From dfe12252bba8cbfe427752ad9ed101002a76fe67 Mon Sep 17 00:00:00 2001 From: William Wilgus Date: Sat, 1 Apr 2023 12:32:47 -0400 Subject: [Feature, Plugin] lastfm_scrobbler_viewer a plugin to view lastfm scrobbler logs uses print cell to give a spreadsheet view of scrobbler logs buffers the whole file if possible otherwise it reads entries from disk rudimentary text searching for columns include / exclude; all/any and case sensitive Change-Id: Id9616e5796658952fba4ea747f596cb77d6f34c0 --- apps/filetypes.c | 1 + apps/filetypes.h | 1 + apps/plugins/CATEGORIES | 1 + apps/plugins/SOURCES | 1 + apps/plugins/keyremap.c | 29 +- apps/plugins/lastfm_scrobbler.c | 2 + apps/plugins/lastfm_scrobbler_viewer.c | 1033 ++++++++++++++++++++++++++++++++ apps/plugins/lib/printcell_helper.c | 297 ++++++--- apps/plugins/lib/printcell_helper.h | 68 ++- apps/plugins/rb_info.c | 35 +- apps/plugins/viewers.config | 1 + 11 files changed, 1358 insertions(+), 111 deletions(-) create mode 100644 apps/plugins/lastfm_scrobbler_viewer.c (limited to 'apps') diff --git a/apps/filetypes.c b/apps/filetypes.c index 1944ee9383..bda7018381 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -129,6 +129,7 @@ static const struct filetype inbuilt_filetypes[] = { { "fmr", FILE_ATTR_FMR }, { "fms", FILE_ATTR_FMS }, #endif + { "log", FILE_ATTR_LOG }, { "lng", FILE_ATTR_LNG }, { "rock", FILE_ATTR_ROCK }, { "lua", FILE_ATTR_LUA }, diff --git a/apps/filetypes.h b/apps/filetypes.h index 5aae772a9c..2886fa2850 100644 --- a/apps/filetypes.h +++ b/apps/filetypes.h @@ -48,6 +48,7 @@ #define FILE_ATTR_FMS 0x1200 /* FM screen skin file */ #define FILE_ATTR_RFMS 0x1300 /* FM screen skin file */ #define FILE_ATTR_OPX 0x1400 /* open plugins shortcut */ +#define FILE_ATTR_LOG 0x1500 /* log file */ #define FILE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ struct filetype { diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index f2ab4843c2..cb0e407d31 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -50,6 +50,7 @@ keybox,apps keyremap,apps lamp,apps lastfm_scrobbler,apps +lastfm_scrobbler_viewer,viewers logo,demos lrcplayer,apps lua,viewers diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 85227d80d7..28a4bc38f5 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -13,6 +13,7 @@ jackpot.c keybox.c keyremap.c lastfm_scrobbler.c +lastfm_scrobbler_viewer.c logo.c lrcplayer.c mosaique.c diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c index f0b36a735e..202d5fcfa4 100644 --- a/apps/plugins/keyremap.c +++ b/apps/plugins/keyremap.c @@ -1422,6 +1422,17 @@ static int list_voice_cb(int list_index, void* data) else rb->talk_spell(name, true); } + else if(data == MENU_ID(M_SETKEYS)) + { + char buf[MAX_MENU_NAME]; + int selcol = printcell_get_column_selected(); + const char* name = printcell_get_column_text(selcol, buf, sizeof(buf)); + long id = P2ID((const unsigned char *)name); + if(id>=0) + rb->talk_id(id, true); + else + rb->talk_spell(name, true); + } else { char buf[MAX_MENU_NAME]; @@ -1606,14 +1617,14 @@ int menu_action_setkeys(int *action, int selected_item, bool* exit, struct gui_s } else { - keyset.view_lastcol = printcell_increment_column(lists, 1, true); + keyset.view_lastcol = printcell_increment_column(1, true); *action = ACTION_NONE; } } } else if (*action == ACTION_STD_CANCEL) { - keyset.view_lastcol = printcell_increment_column(lists, -1, true); + keyset.view_lastcol = printcell_increment_column(-1, true); if (keyset.view_lastcol != keyset.view_columns - 1) { *action = ACTION_NONE; @@ -2038,7 +2049,7 @@ static void synclist_set(int id, int selected_item, int items, int sel_size) rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); rb->gui_synclist_set_nb_items(&lists,items); rb->gui_synclist_select_item(&lists, selected_item); - printcell_enable(&lists, false, false); + printcell_enable(false); if (menu_id == MENU_ID(M_ROOT)) { @@ -2047,15 +2058,16 @@ static void synclist_set(int id, int selected_item, int items, int sel_size) } else if (menu_id == MENU_ID(M_SETKEYS)) { - printcell_enable(&lists, true, true); - keyset.view_columns = printcell_set_columns(&lists, ACTVIEW_HEADER, Icon_Rockbox); - int curcol = printcell_increment_column(&lists, 0, true); + keyset.view_columns = printcell_set_columns(&lists, NULL, + ACTVIEW_HEADER, Icon_Rockbox); + printcell_enable(true); + int curcol = printcell_get_column_selected(); if (keyset.view_lastcol >= keyset.view_columns) keyset.view_lastcol = -1; /* restore column position */ while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol) { - curcol = printcell_increment_column(&lists, 1, true); + curcol = printcell_increment_column(1, true); } keyset.view_lastcol = curcol; } @@ -2065,9 +2077,8 @@ static void synclist_set(int id, int selected_item, int items, int sel_size) PEEK_MENU_ID(id); lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title)); rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered); - /* if (menu_title[0] == '$'){ printcell_enable(&lists, true, true); } */ + /* if (menu_title[0] == '$'){ printcell_enable(true); } */ } - } static void keyremap_set_buffer(void* buffer, size_t buf_size) diff --git a/apps/plugins/lastfm_scrobbler.c b/apps/plugins/lastfm_scrobbler.c index dce6be0d1e..c835533b1f 100644 --- a/apps/plugins/lastfm_scrobbler.c +++ b/apps/plugins/lastfm_scrobbler.c @@ -160,6 +160,8 @@ static int config_settings_menu(void) case 3: rb->set_int("Beep Level", "", UNIT_INT, &gConfig.beeplvl, NULL, 1, 0, 10, NULL); + if (gConfig.beeplvl > 0) + rb->beep_play(1500, 100, 100 * gConfig.beeplvl); case 4: /*sep*/ continue; case 5: diff --git a/apps/plugins/lastfm_scrobbler_viewer.c b/apps/plugins/lastfm_scrobbler_viewer.c new file mode 100644 index 0000000000..8f533ef5f7 --- /dev/null +++ b/apps/plugins/lastfm_scrobbler_viewer.c @@ -0,0 +1,1033 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / + * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2023 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "plugin.h" +#include "lang_enum.h" + +#include "lib/printcell_helper.h" + +#include "lib/configfile.h" +#define CFG_FILE "/lastfm_scrobbler_viewer.cfg" +#define TMP_FILE ""PLUGIN_DATA_DIR "/lscrobbler_viewer_%d.tmp" +#define CFG_VER 1 + +#ifdef ROCKBOX_HAS_LOGF +#define logf rb->logf +#else +#define logf(...) do { } while(0) +#endif + +/*#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMP #MUSICBRAINZ_TRACKID*/ + +#define SCROBBLE_HDR_FMT "$*%d$%s$*%d$%s$*%d$%s$Track#$Length$Rating$TimeStamp$TrackId" +//#define SCROBBLE_HDR "$*128$Artist$*128$Album$*128$Title$Track#$Length$Rating$TimeStamp$TrackId" + +#define SCROBBLER_MIN_COLUMNS (6) /* a valid scrobbler file should have at least this many columns */ + +#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1) +#define CANCEL_CONTEXT_MENU (PLUGIN_OK + 2) +#define QUIT_CONTEXT_MENU (PLUGIN_OK + 3) +/* global lists, for everything */ +static struct gui_synclist lists; + +/* printcell data for the current file */ +struct printcell_data_t { + int view_columns; + int view_lastcol; + + int items_buffered; + int items_total; + int fd_cur; + char *filename; + + char *buf; + size_t buf_size; + off_t buf_used; + char header[PRINTCELL_MAXLINELEN]; + +}; + +enum e_find_type { + FIND_ALL = 0, + FIND_EXCLUDE, + FIND_EXCLUDE_CASE, + FIND_EXCLUDE_ANY, + FIND_INCLUDE, + FIND_INCLUDE_CASE, + FIND_INCLUDE_ANY, + FIND_CUSTOM, +}; + +static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data); +static void pc_data_set_header(struct printcell_data_t *pc_data); + +static void browse_file(char *buf, size_t bufsz) +{ + struct browse_context browse = { + .dirfilter = SHOW_ALL, + .flags = BROWSE_SELECTONLY, + .title = "Select a scrobbler log file", + .icon = Icon_Playlist, + .buf = buf, + .bufsize = bufsz, + .root = "/", + }; + + if (rb->rockbox_browse(&browse) != GO_TO_PREVIOUS) + { + buf[0] = '\0'; + } +} + +static struct plugin_config +{ + bool separator; + bool talk; + int col_width; + uint32_t hidecol_flags; +} gConfig; + +static struct configdata config[] = +{ + {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.separator }, "Cell Separator", NULL}, + {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.talk }, "Voice", NULL}, + {TYPE_INT, 32, LCD_WIDTH, { .int_p = &gConfig.col_width }, "Cell Width", NULL}, + {TYPE_INT, INT_MIN, INT_MAX, { .int_p = &gConfig.hidecol_flags }, "Hidden Columns", NULL}, +}; +const int gCfg_sz = sizeof(config)/sizeof(*config); +/****************** config functions *****************/ +static void config_set_defaults(void) +{ + gConfig.col_width = MIN(LCD_WIDTH, 128); + gConfig.hidecol_flags = 0; + gConfig.separator = true; + gConfig.talk = rb->global_settings->talk_menu; +} + +static void config_save(void) +{ + configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); +} + +static int config_settings_menu(struct printcell_data_t *pc_data) +{ + int selection = 0; + + struct viewport parentvp[NB_SCREENS]; + FOR_NB_SCREENS(l) + { + rb->viewport_set_defaults(&parentvp[l], l); + rb->viewport_set_fullscreen(&parentvp[l], l); + } + + MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_SETTINGS), NULL, + ID2P(LANG_LIST_SEPARATOR), + "Cell Width", + ID2P(LANG_VOICE), + ID2P(VOICE_BLANK), + ID2P(VOICE_BLANK), + ID2P(LANG_CANCEL_0), + ID2P(LANG_SAVE_EXIT)); + + do { + selection=rb->do_menu(&settings_menu,&selection, parentvp, true); + switch(selection) { + + case 0: + rb->set_bool(rb->str(LANG_LIST_SEPARATOR), &gConfig.separator); + break; + case 1: + rb->set_int("Cell Width", "", UNIT_INT, + &gConfig.col_width, NULL, 1, 32, LCD_WIDTH, NULL ); + break; + case 2: + rb->set_bool(rb->str(LANG_VOICE), &gConfig.talk); + break; + case 3: + continue; + case 4: /*sep*/ + continue; + case 5: + return -1; + break; + case 6: + { + pc_data_set_header(pc_data); + synclist_set(0, pc_data->items_total, 1, pc_data); + int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); + if (res >= 0) + { + logf("Cfg saved %s %d bytes", CFG_FILE, gCfg_sz); + return PLUGIN_OK; + } + logf("Cfg FAILED (%d) %s", res, CFG_FILE); + return PLUGIN_ERROR; + } + case MENU_ATTACHED_USB: + return PLUGIN_USB_CONNECTED; + default: + return PLUGIN_OK; + } + } while ( selection >= 0 ); + return 0; +} + +/* open file pass back file descriptor and return file size */ +static size_t file_open(const char *filename, int *fd) +{ + size_t fsize = 0; + + if (filename && fd) + { + *fd = rb->open(filename, O_RDONLY); + if (*fd >= 0) + { + fsize = rb->filesize(*fd); + } + } + return fsize; +} + +static const char* list_get_name_cb(int selected_item, void* data, + char* buf, size_t buf_len) +{ + const int slush_pos = 32; /* entries around the current entry to keep buffered */ + /* keep track of the last position in the buffer */ + static int buf_line_num = 0; + static off_t buf_last_pos = 0; + /* keep track of the last position in the file */ + static int file_line_num = 0; + static off_t file_last_seek = 0; + + if (selected_item < 0) + { + logf("[%s] Reset positions", __func__); + buf_line_num = 0; + buf_last_pos = 0; + file_line_num = 0; + file_last_seek = 0; + return ""; + } + + int line_num; + struct printcell_data_t *pc_data = (struct printcell_data_t*) data; + bool found = false; + + if (pc_data->buf && selected_item < pc_data->items_buffered) + { + int buf_pos; + + if (buf_line_num > selected_item || buf_last_pos >= pc_data->buf_used + || buf_line_num < 0) + { + logf("[%s] Set pos buffer 0", __func__); + buf_line_num = 0; + buf_last_pos = 0; + } + buf_pos = buf_last_pos; + line_num = buf_line_num; + + while (buf_pos < pc_data->buf_used + && line_num < pc_data->items_buffered) + { + size_t len = rb->strlen(&pc_data->buf[buf_pos]); + if(line_num == selected_item) + { + rb->strlcpy(buf, &pc_data->buf[buf_pos], MIN(buf_len, len)); + logf("(%d) in buffer: %s", line_num, buf); + found = true; + break; + } + else + { + buf_pos += len + 1; /* need to go past the NULL terminator */ + line_num++; + + if (buf_line_num + slush_pos < selected_item) + { + logf("[%s] Set pos buffer %d", __func__, line_num); + buf_line_num = line_num; + buf_last_pos = buf_pos; + } + } + } + } + + /* didn't find the item try the file */ + if(!found && pc_data->fd_cur >= 0) + { + int fd = pc_data->fd_cur; + + if (file_line_num < 0 || file_line_num > selected_item) + { + logf("[%s] Set seek file 0", __func__); + file_line_num = 0; + file_last_seek = 0; + } + + rb->lseek(fd, file_last_seek, SEEK_SET); + line_num = file_line_num; + + while ((rb->read_line(fd, buf, buf_len)) > 0) + { + if(buf[0] == '#') + continue; + if(line_num == selected_item) + { + logf("(%d) in file: %s", line_num, buf); + found = true; + break; + } + else + { + line_num++; + + if (file_line_num + slush_pos < selected_item) + { + logf("[%s] Set seek file %d", __func__, line_num); + file_line_num = line_num; + file_last_seek = rb->lseek(fd, 0, SEEK_CUR); + } + } + } + } + + if(!found) + { + logf("(%d) Not Found!", selected_item); + buf_line_num = -1; + file_line_num = -1; + buf[0] = '\0'; + } + return buf; +} + +static int list_voice_cb(int list_index, void* data) +{ + (void) list_index; + struct printcell_data_t *pc_data = (struct printcell_data_t*) data; + if (!gConfig.talk) + return -1; + + int selcol = printcell_get_column_selected(); + char buf[MAX_PATH]; + char* name; + long id; + + if (pc_data->view_lastcol != selcol) + { + name = printcell_get_title_text(selcol, buf, sizeof(buf)); + + id = P2ID((const unsigned char *)name); + + if(id>=0) + rb->talk_id(id, true); + else if (selcol >= 0) + { + switch (selcol) + { + case 0: + rb->talk_id(LANG_ID3_ARTIST, true); + break; + case 1: + rb->talk_id(LANG_ID3_ALBUM, true); + break; + case 2: + rb->talk_id(LANG_ID3_TITLE, true); + break; + case 3: + rb->talk_id(LANG_ID3_TRACKNUM, true); + break; + case 4: + rb->talk_id(LANG_ID3_LENGTH, true); + break; + + default: + rb->talk_spell(name, true); + break; + } + } + else + rb->talk_id(LANG_ALL, true); + + rb->talk_id(VOICE_PAUSE, true); + } + + name = printcell_get_column_text(selcol, buf, sizeof(buf)); + + id = P2ID((const unsigned char *)name); + + if(id>=0) + rb->talk_id(id, true); + else if (selcol >= 0) + rb->talk_spell(name, true); + + return 0; +} + +static enum themable_icons list_icon_cb(int selected_item, void *data) +{ + struct printcell_data_t *pc_data = (struct printcell_data_t*) data; + if (lists.selected_item == selected_item) + { + if (pc_data->view_lastcol < 0) + return Icon_Config; + return Icon_Audio; + } + return Icon_NOICON; +} + +/* load file entries into pc_data buffer, file should already be opened + * and will be closed if the whole file was buffered */ +static int file_load_entries(struct printcell_data_t *pc_data) +{ + int items = 0; + int count = 0; + int buffered = 0; + unsigned int pos = 0; + bool comment = false; + char ch; + int fd = pc_data->fd_cur; + if (fd < 0) + return 0; + + rb->lseek(fd, 0, SEEK_SET); + + while (rb->read(fd, &ch, 1) > 0) + { + if (count++ == 0 && ch == '#') /* skip comments */ + comment = true; + else if (!comment && ch != '\r' && pc_data->buf_size > pos) + pc_data->buf[pos++] = ch; + + if (items == 0 && pos > PRINTCELL_MAXLINELEN * 2) + break; + + if (ch == '\n') + { + if (!comment) + { + pc_data->buf[pos] = '\0'; + if (pc_data->buf_size > pos) + { + pos++; + buffered++; + } + items++; + } + comment = false; + count = 0; + rb->yield(); + } + } + + logf("[%s] items: %d buffered: %d", __func__, items, buffered); + + pc_data->items_total = items; + pc_data->items_buffered = buffered; + pc_data->buf[pos] = '\0'; + pc_data->buf_used = pos; + + if (items == buffered) /* whole file fit into buffer; close file */ + { + rb->close(pc_data->fd_cur); + pc_data->fd_cur = -1; + } + + list_get_name_cb(-1, NULL, NULL, 0); /* prime name cb */ + return items; +} + +static int filter_items(struct printcell_data_t *pc_data, + enum e_find_type find_type, int col) +{ + /* saves filtered items to a temp file and loads it */ + int fd; + bool reload = false; + char buf[PRINTCELL_MAXLINELEN]; + char find_exclude_buf[PRINTCELL_MAXLINELEN]; + int selcol = printcell_get_column_selected(); + char *find_exclude = printcell_get_column_text(selcol, find_exclude_buf, + sizeof(find_exclude_buf)); + const char colsep = '\t'; + int find_len = strlen(find_exclude); + + if (find_type == FIND_CUSTOM || find_len == 0) + { + int option = 0; + struct opt_items find_types[] = { + {"Exclude", -1}, {"Exclude Case Sensitive", -1}, + {"Exclude Any", -1}, {"Include", -1}, + {"Include Case Sensitive", -1}, {"Include Any", -1} + }; + if (rb->set_option("Find Type", &option, INT, + find_types, 6, NULL)) + { + return 0; + } + switch (option) + { + case 0: + find_type = FIND_EXCLUDE; + break; + case 1: + find_type = FIND_EXCLUDE_CASE; + break; + case 2: + find_type = FIND_EXCLUDE_ANY; + break; + case 3: + find_type = FIND_INCLUDE; + break; + case 4: + find_type = FIND_INCLUDE_CASE; + break; + case 5: + find_type = FIND_INCLUDE_ANY; + break; + default: + find_type = FIND_ALL; + break; + } + + /* copy data to beginning of buf */ + rb->memmove(find_exclude_buf, find_exclude, find_len); + find_exclude_buf[find_len] = '\0'; + + if (rb->kbd_input(find_exclude_buf, sizeof(find_exclude_buf), NULL) < 0) + return -1; + find_exclude = find_exclude_buf; + find_len = strlen(find_exclude); + } + + char tmp_filename[MAX_PATH]; + static int tmp_num = 0; + rb->snprintf(tmp_filename, sizeof(tmp_filename), TMP_FILE, tmp_num); + tmp_num++; + + fd = rb->open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); + if(fd >= 0) + { + rb->splash_progress_set_delay(HZ * 5); + for (int i = 0; i < pc_data->items_total; i++) + { + rb->splash_progress(i, pc_data->items_total, "Filtering..."); + const char * data = list_get_name_cb(i, pc_data, buf, sizeof(buf)); + + if (find_type != FIND_ALL) + { + int index = col; + + if (index < 0) + index = 0; + if (find_len > 0) + { + char *bcol = buf; + while (*bcol != '\0' && index > 0) + { + if (*bcol == colsep) + index--; + bcol++; + } + if (index > 0) + continue; + + if (find_type == FIND_EXCLUDE) + { + if (rb->strncasecmp(bcol, find_exclude, find_len) == 0) + { + logf("[%s] exclude [%s]", find_exclude, bcol); + continue; + } + } + else if (find_type == FIND_INCLUDE) + { + if (rb->strncasecmp(bcol, find_exclude, find_len) != 0) + { + logf("%s include %s", find_exclude, bcol); + continue; + } + } + else if (find_type == FIND_EXCLUDE_CASE) + { + if (rb->strncmp(bcol, find_exclude, find_len) == 0) + { + logf("[%s] exclude case [%s]", find_exclude, bcol); + continue; + } + } + else if (find_type == FIND_INCLUDE_CASE) + { + if (rb->strncmp(bcol, find_exclude, find_len) != 0) + { + logf("%s include case %s", find_exclude, bcol); + continue; + } + } + else if (find_type == FIND_EXCLUDE_ANY) + { + bool found = false; + while (*bcol != '\0' && *bcol != colsep) + { + if (rb->strncasecmp(bcol, find_exclude, find_len) == 0) + { + logf("[%s] exclude any [%s]", find_exclude, bcol); + found = true; + break; + } + bcol++; + } + if (found) + continue; + } + else if (find_type == FIND_INCLUDE_ANY) + { + bool found = false; + while (*bcol != '\0' && *bcol != colsep) + { + if (rb->strncasecmp(bcol, find_exclude, find_len) == 0) + { + found = true; + logf("[%s] include any [%s]", find_exclude, bcol); + break; + } + bcol++; + } + if (!found) + continue; + } + } + } + int len = strlen(data); + if (len > 0) + { + logf("writing [%d bytes][%s]", len + 1, data); + rb->write(fd, data, len); + rb->write(fd, "\n", 1); + } + } + reload = true; + } + + if (reload) + { + if (pc_data->fd_cur >= 0) + rb->close(pc_data->fd_cur); + + pc_data->fd_cur = fd; + int items = file_load_entries(pc_data); + if (items >= 0) + { + lists.selected_item = 0; + rb->gui_synclist_set_nb_items(&lists, items); + } + } + logf("Col text '%s'", find_exclude); + logf("text '%s'", find_exclude_buf); + + return pc_data->items_total; +} + +static int scrobbler_context_menu(struct printcell_data_t *pc_data) +{ + struct viewport parentvp[NB_SCREENS]; + FOR_NB_SCREENS(l) + { + rb->viewport_set_defaults(&parentvp[l], l); + rb->viewport_set_fullscreen(&parentvp[l], l); + } + + int col = printcell_get_column_selected(); + int selection = 0; + int items = 0; + uint32_t visible = printcell_get_column_visibility(-1); + bool hide_col = PRINTCELL_COLUMN_IS_VISIBLE(visible, col); + + char namebuf[PRINTCELL_MAXLINELEN]; + enum e_find_type find_type = FIND_ALL; + + char *colname = pc_data->filename; + if (col >= 0) + colname = printcell_get_title_text(col, namebuf, sizeof(namebuf)); + +#define MENUITEM_STRINGLIST_CUSTOM(name, str, callback, ... ) \ + const char *name##_[] = {__VA_ARGS__}; \ + const struct menu_callback_with_desc name##__ = \ + {callback,str, Icon_NOICON}; \ + const struct menu_item_ex name = \ + {MT_RETURN_ID|MENU_HAS_DESC| \ + MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ + { .strings = name##_},{.callback_and_desc = & name##__}}; + + const char *menu_item[4]; + + menu_item[0]= hide_col ? "Hide":"Show"; + menu_item[1]= "Exclude"; + menu_item[2]= "Include"; + menu_item[3]= "Custom Filter"; + + if (col == -1) + { + menu_item[0]= hide_col ? "Hide All":"Show All"; + menu_item[1]= "Open"; + menu_item[2]= "Reload"; + menu_item[3]= ID2P(LANG_SETTINGS); + } + + MENUITEM_STRINGLIST_CUSTOM(context_menu, colname, NULL, + menu_item[0], + menu_item[1], + menu_item[2], + menu_item[3], + ID2P(VOICE_BLANK), + ID2P(LANG_CANCEL_0), + ID2P(LANG_MENU_QUIT)); + +#undef MENUITEM_STRINGLIST_CUSTOM + do { + selection=rb->do_menu(&context_menu,&selection, parentvp, true); + switch(selection) { + + case 0: + { + printcell_set_column_visible(col, !hide_col); + if (hide_col) + { + do + { + col = printcell_increment_column(1, true); + } while (col >= 0 && printcell_get_column_visibility(col) == 1); + pc_data->view_lastcol = col; + } + gConfig.hidecol_flags = printcell_get_column_visibility(-1); + break; + } + case 1: /* Exclude / Open */ + { + if (col == -1)/*Open*/ + { + char buf[MAX_PATH]; + browse_file(buf, sizeof(buf)); + if (rb->file_exists(buf)) + { + rb->strlcpy(pc_data->filename, buf, MAX_PATH); + if (pc_data->fd_cur >= 0) + rb->close(pc_data->fd_cur); + if (file_open(pc_data->filename, &pc_data->fd_cur) > 0) + items = file_load_entries(pc_data); + if (items >= 0) + synclist_set(0, items, 1, pc_data); + } + else + rb->splash(HZ *2, "Error Opening"); + + return CANCEL_CONTEXT_MENU; + } + + find_type = FIND_EXCLUDE; + } + /* fall-through */ + case 2: /* Include / Reload */ + { + if (col == -1) /*Reload*/ + { + if (pc_data->fd_cur >= 0) + rb->close(pc_data->fd_cur); + if (file_open(pc_data->filename, &pc_data->fd_cur) > 0) + items = file_load_entries(pc_data); + if (items >= 0) + rb->gui_synclist_set_nb_items(&lists, items); + return CANCEL_CONTEXT_MENU; + } + + if (find_type == FIND_ALL) + find_type = FIND_INCLUDE; + /* fall-through */ + } + case 3: /*Custom Filter / Settings */ + { + if (col == -1)/*Settings*/ + return config_settings_menu(pc_data); + + if (find_type == FIND_ALL) + find_type = FIND_CUSTOM; + items = filter_items(pc_data, find_type, col); + + if (items >= 0) + rb->gui_synclist_set_nb_items(&lists, items); + break; + } + case 4: /*sep*/ + continue; + case 5: + return CANCEL_CONTEXT_MENU; + break; + case 6: /* Quit */ + return QUIT_CONTEXT_MENU; + break; + case MENU_ATTACHED_USB: + return PLUGIN_USB_CONNECTED; + default: + return PLUGIN_OK; + } + } while ( selection < 0 ); + return 0; +} + +static void cleanup(void *parameter) +{ + struct printcell_data_t *pc_data = (struct printcell_data_t*) parameter; + if (pc_data->fd_cur >= 0) + rb->close(pc_data->fd_cur); + pc_data->fd_cur = -1; +} + +static void menu_action_printcell(int *action, int selected_item, bool* exit, struct gui_synclist *lists) +{ + (void) exit; + struct printcell_data_t *pc_data = (struct printcell_data_t*) lists->data; + if (*action == ACTION_STD_OK) + { + if (selected_item < lists->nb_items) + { + pc_data->view_lastcol = printcell_increment_column(1, true); + *action = ACTION_NONE; + } + } + else if (*action == ACTION_STD_CANCEL) + { + pc_data->view_lastcol = printcell_increment_column(-1, true); + if (pc_data->view_lastcol != pc_data->view_columns - 1) + { + *action = ACTION_NONE; + } + } + else if (*action == ACTION_STD_CONTEXT) + { + int ctxret = scrobbler_context_menu(pc_data); + if (ctxret == QUIT_CONTEXT_MENU) + *exit = true; + } +} + +int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists) +{ + + menu_action_printcell(action, selected_item, exit, lists); + + if (rb->default_event_handler_ex(*action, cleanup, lists->data) == SYS_USB_CONNECTED) + { + *exit = true; + return PLUGIN_USB_CONNECTED; + } + return PLUGIN_OK; +} + +static int count_max_columns(int items, char delimeter, + int expected_cols, struct printcell_data_t *pc_data) +{ + int max_cols = 0; + int cols = 0; + char buf[PRINTCELL_MAXLINELEN]; + for (int i = 0; i < items; i++) + { + const char *txt = list_get_name_cb(i, pc_data, buf, sizeof(buf)); + while (*txt != '\0') + { + if (*txt == delimeter) + { + cols++; + if (cols == expected_cols) + { + max_cols = cols; + break; + } + } + txt++; + } + + if(max_cols < expected_cols && i > 32) + break; + + if (cols > max_cols) + max_cols = cols; + cols = 0; + } + return max_cols; +} + +static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data) +{ + if (items <= 0) + return; + if (selected_item < 0) + selected_item = 0; + + rb->gui_synclist_init(&lists,list_get_name_cb, + pc_data, false, sel_size, NULL); + + rb->gui_synclist_set_icon_callback(&lists, list_icon_cb); + rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); + rb->gui_synclist_set_nb_items(&lists,items); + rb->gui_synclist_select_item(&lists, selected_item); + + struct printcell_settings pcs = {.cell_separator = gConfig.separator, + .title_delimeter = '$', + .text_delimeter = '\t', + .hidecol_flags = gConfig.hidecol_flags}; + + pc_data->view_columns = printcell_set_columns(&lists, &pcs, + pc_data->header, Icon_Rockbox); + printcell_enable(true); + + + int max_cols = count_max_columns(items, pcs.text_delimeter, + SCROBBLER_MIN_COLUMNS, pc_data); + if (max_cols < SCROBBLER_MIN_COLUMNS) /* not a scrobbler file? */ + { + rb->gui_synclist_set_voice_callback(&lists, NULL); + pc_data->view_columns = printcell_set_columns(&lists, NULL, + "$*512$", Icon_Questionmark); + } + + int curcol = printcell_get_column_selected(); + while (curcol >= 0) + curcol = printcell_increment_column(-1, false); + + if (pc_data->view_lastcol >= pc_data->view_columns) + pc_data->view_lastcol = -1; + + /* restore column position */ + while (pc_data->view_lastcol > -1 && curcol != pc_data->view_lastcol) + { + curcol = printcell_increment_column(1, true); + } + pc_data->view_lastcol = curcol; + list_voice_cb(0, pc_data); +} + +static void pc_data_set_header(struct printcell_data_t *pc_data) +{ + int col_w = gConfig.col_width; + rb->snprintf(pc_data->header, PRINTCELL_MAXLINELEN, + SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST), + col_w, rb->str(LANG_ID3_ALBUM), + col_w, rb->str(LANG_ID3_TITLE)); +} + +enum plugin_status plugin_start(const void* parameter) +{ + int ret = PLUGIN_OK; + int selected_item = -1; + int action; + int items = 0; +#if CONFIG_RTC + static char filename[MAX_PATH] = HOME_DIR "/.scrobbler.log"; +#else /* !CONFIG_RTC */ + static char filename[MAX_PATH] = HOME_DIR "/.scrobbler-timeless.log"; +#endif /* CONFIG_RTC */ + bool redraw = true; + bool exit = false; + + static struct printcell_data_t printcell_data; + + if (parameter) + { + rb->strlcpy(filename, (const char*)parameter, MAX_PATH); + filename[MAX_PATH - 1] = '\0'; + } + + if (!rb->file_exists(filename)) + { + browse_file(filename, sizeof(filename)); + if (!rb->file_exists(filename)) + { + rb->splash(HZ, "No Scrobbler file Goodbye."); + return PLUGIN_ERROR; + } + + } + + config_set_defaults(); + if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0) + { + /* If the loading failed, save a new config file */ + config_set_defaults(); + configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); + + rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS)); + } + + rb->memset(&printcell_data, 0, sizeof (struct printcell_data_t)); + printcell_data.fd_cur = -1; + printcell_data.view_lastcol = -1; + + if (rb->file_exists(filename)) + { + if (file_open(filename, &printcell_data.fd_cur) == 0) + printcell_data.fd_cur = -1; + else + { + size_t buf_size; + printcell_data.buf = rb->plugin_get_buffer(&buf_size); + printcell_data.buf_size = buf_size; + printcell_data.buf_used = 0; + printcell_data.filename = filename; + items = file_load_entries(&printcell_data); + } + } + int col_w = gConfig.col_width; + rb->snprintf(printcell_data.header, PRINTCELL_MAXLINELEN, + SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST), + col_w, rb->str(LANG_ID3_ALBUM), + col_w, rb->str(LANG_ID3_TITLE)); + + if (!exit && items > 0) + { + synclist_set(0, items, 1, &printcell_data); + rb->gui_synclist_draw(&lists); + + while (!exit) + { + action = rb->get_action(CONTEXT_LIST, HZ / 10); + + if (action == ACTION_NONE) + { + if (redraw) + { + action = ACTION_REDRAW; + redraw = false; + } + } + else + redraw = true; + + ret = menu_action_cb(&action, selected_item, &exit, &lists); + if (rb->gui_synclist_do_button(&lists, &action)) + continue; + selected_item = rb->gui_synclist_get_sel_pos(&lists); + + } + } + config_save(); + cleanup(&printcell_data); + return ret; +} diff --git a/apps/plugins/lib/printcell_helper.c b/apps/plugins/lib/printcell_helper.c index 42de444c57..48b8b2c9d2 100644 --- a/apps/plugins/lib/printcell_helper.c +++ b/apps/plugins/lib/printcell_helper.c @@ -22,10 +22,6 @@ #include "plugin.h" #include "lib/printcell_helper.h" -#ifndef PRINTCELL_MAX_COLUMNS -#define PRINTCELL_MAX_COLUMNS 16 -#endif - #define COLUMN_ENDLEN 3 #define TITLE_FLAG 0xFF #define SELECTED_FLAG 0x1 @@ -37,48 +33,54 @@ #endif struct printcell_info_t { - int offw[NB_SCREENS]; - int iconw[NB_SCREENS]; - int selcol_offw[NB_SCREENS]; - int totalcolw[NB_SCREENS]; - int firstcolxw[NB_SCREENS]; - uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; - int ncols; - int selcol; - int selcol_index; - char title[PRINTCELL_MAXLINELEN]; - bool separator; + struct gui_synclist *gui_list; /* list to display */ + int offw[NB_SCREENS]; /* padding between column boundries and text */ + int iconw[NB_SCREENS]; /* width of an icon */ + int selcol_offw[NB_SCREENS]; /* offset width calculated for selected item */ + int totalcolw[NB_SCREENS]; /* total width of all columns */ + int firstcolxw[NB_SCREENS]; /* first column x + width, save recalculating */ + int ncols; /* number of columns */ + int selcol; /* selected column (-1 to ncols-1) */ + uint32_t hidecol_flags; /*bits 0-31 set bit to 1 to hide a column (1<= ncols || j >= (PRINTCELL_MAXLINELEN - 1)) + if (i >= ncols || j >= (bufsz - 1)) break; } ch = *dsp_text++; @@ -146,23 +148,39 @@ static inline void set_cell_width(struct viewport *vp, int max_w, int new_w) static inline int printcells(struct screen *display, char* buffer, uint16_t *sidx, struct line_desc *linedes, struct viewport *vp, int vp_w, int separator, - int x, int y, int offw, int selected_flag, bool scroll) + int x, int y, int offw, int selected_flag, int last_col, + bool scroll, bool is_title) { /* Internal function prints remaining cells */ int text_offset = offw + offw; - int ncols = printcell.ncols; int screen = display->screen_type; int height = linedes->height; int selsep = (selected_flag == 0) ? 0: separator; uint16_t *screencolwidth = printcell.colw[screen]; - for(int i = 1; i < ncols; i++) + for(int i = 1; i <= last_col; i++) { int ny = y; int nw = screencolwidth[i] + text_offset; + int offx = 0; + + if (i == last_col || x + nw >= vp_w - offw + 1) + { /* not enough space for next column use up excess */ + if (nw < (vp_w - x)) + { + if (is_title) + offx = ((vp_w - x) - nw) / 2; + nw = vp_w - x; + } + } + + if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i)) + nw = 0; + int nx = x + nw; char *buftext; - if (nx > 0 && x < vp_w) + + if (nx > 0 && nw > offw && x < vp_w) { set_cell_width(vp, vp_w, nx); @@ -173,11 +191,15 @@ static inline int printcells(struct screen *display, char* buffer, } else { + if (vp_w < x + text_offset) + { + scroll = false; + } linedes->scroll = scroll; linedes->separator_height = separator; } buftext = &buffer[sidx[i]]; - display->put_line(x + offw, ny, linedes, "$t", buftext); + display->put_line(x + offw + offx, ny, linedes, "$t", buftext); vp->width += COLUMN_ENDLEN + 1; draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height); } @@ -200,6 +222,10 @@ static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth for (int i = printcell.selcol - 1; i >= 0; i--) { int cw = screencolwidth[i] + text_offset; + + if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i)) + cw = 0; + if (i == 0) cw += screenicnwidth; if (offset > 0 || cw > maxw) @@ -257,15 +283,30 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) separator = 1; int nx = x; + int last_col = printcell.ncols - 1; + int hidden_w = 0; int nw, colxw; - + char *buftext; printcell_buffer[0] = '\0'; - parse_dsptext(printcell.ncols, dsp_text, printcell_buffer, sidx); - char *buftext = &printcell_buffer[sidx[0]]; + uint16_t *screencolwidth = printcell.colw[screen]; + if (printcell.hidecol_flags > 0) + { + for (int i = 0; i < printcell.ncols; i++) + { + if (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i)) + last_col = i; + else + hidden_w += (screencolwidth[i] + text_offset); + } + } if (is_title) { + parse_dsptext(printcell.titlesep, printcell.ncols, dsp_text, + printcell_buffer, sizeof(printcell_buffer), sidx); + + buftext = &printcell_buffer[sidx[0]]; /* set to first column text */ int sbwidth = 0; if (rb->global_settings->scrollbar == SCROLLBAR_LEFT) sbwidth = rb->global_settings->scrollbar_width; @@ -278,6 +319,9 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) nx -= printcell.selcol_offw[screen]; nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; + + if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, 0)) + nw = printcell.iconw[screen] - 1; nw += sbwidth; colxw = nx + nw; @@ -301,20 +345,21 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) } else { + parse_dsptext(printcell.colsep, printcell.ncols, dsp_text, + printcell_buffer, sizeof(printcell_buffer), sidx); + + buftext = &printcell_buffer[sidx[0]]; /* set to first column text */ int cursor = Icon_NOICON; nx -= printcell.selcol_offw[screen]; if (selected_flag & SELECTED_FLAG) { - if (printcell.selcol >= 0) - printcell.selcol_index = sidx[printcell.selcol]; /* save the item offset*/ - else - printcell.selcol_index = -1; - cursor = Icon_Cursor; /* limit length of selection if columns don't reach end */ int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen]; maxw += text_offset * printcell.ncols; + maxw -= hidden_w; + if (vp_w > maxw) vp->width = maxw; /* display a blank line first to draw selector across all cells */ @@ -364,7 +409,7 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) nx += nw; /* display remaining cells */ printcells(display, printcell_buffer, sidx, linedes, vp, vp_w, separator, - nx, y, col_offset_width, selected_flag, scroll_items); + nx, y, col_offset_width, selected_flag, last_col, scroll_items, is_title); /* draw a line at the bottom of the list */ if (separator > 0 && line == list->nb_items - 1) @@ -377,14 +422,17 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) vp->width = vp_w; } -void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator) +void printcell_enable(bool enable) { - printcell.separator = separator; + if (!printcell.gui_list) + return; + struct gui_synclist *gui_list = printcell.gui_list; #ifdef HAVE_LCD_COLOR static int list_sep_color = INT_MIN; if (enable) { - list_sep_color = rb->global_settings->list_separator_color; + if (list_sep_color == INT_MIN) + list_sep_color = rb->global_settings->list_separator_color; rb->global_settings->list_separator_color = rb->global_settings->fg_color; gui_list->callback_draw_item = printcell_listdraw_fn; } @@ -404,8 +452,11 @@ void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator } -int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap) +int printcell_increment_column(int increment, bool wrap) { + if (!printcell.gui_list) + return -1; + struct gui_synclist *gui_list = printcell.gui_list; int item = printcell.selcol + increment; int imin = -1; int imax = printcell.ncols - 1; @@ -425,36 +476,94 @@ int printcell_increment_column(struct gui_synclist *gui_list, int increment, boo FOR_NB_SCREENS(n) /* offset needs recalculated */ printcell.selcol_offw[n] = 0; printcell.selcol = item; - printcell.selcol_index = 0; + rb->gui_synclist_draw(gui_list); + rb->gui_synclist_speak_item(gui_list); } return item; } +int printcell_get_column_selected(void) +{ + if (!printcell.gui_list) + return -1; + return printcell.selcol; +} + +uint32_t printcell_get_column_visibility(int col) +{ + if (col >= 0) + return (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, col) ? 0:1); + else /* return flag of all columns */ + return printcell.hidecol_flags; +} + +void printcell_set_column_visible(int col, bool visible) +{ + /* visible columns have 0 for the column bit hidden columns have the bit set */ + if (col >= 0) + { + if (visible) + printcell.hidecol_flags &= ~(PRINTCELL_COLUMN_FLAG(col)); + else + printcell.hidecol_flags |= PRINTCELL_COLUMN_FLAG(col); + } + else + { + if (visible) /* set to everything visible */ + printcell.hidecol_flags = 0; + else /* set to everything hidden */ + printcell.hidecol_flags = ((uint32_t)-1); + } +} + int printcell_set_columns(struct gui_synclist *gui_list, - char * title, enum themable_icons icon) + struct printcell_settings * pcs, + char * title, enum themable_icons icon) { + if (title == NULL) title = "$PRINTCELL NOT SETUP"; + + if (pcs == NULL) /* DEFAULTS */ + { +#if LCD_DEPTH > 1 + /* If line sep is set to automatic then outline cells */ + bool sep = (rb->global_settings->list_separator_height < 0); +#else + bool sep = (rb->global_settings->cursor_style == 0); +#endif + pcs = &(struct printcell_settings){ .cell_separator = sep, + .title_delimeter = '$', + .text_delimeter = '$', + .hidecol_flags = 0}; + } + uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */ int width, height, user_minwidth; int i = 0; - int j = 0; - int ch = '$'; /* first column $ is optional */ - + size_t j = 0; rb->memset(&printcell, 0, sizeof(struct printcell_info_t)); + printcell.gui_list = gui_list; + printcell.separator = pcs->cell_separator; + printcell.titlesep = pcs->title_delimeter; + printcell.colsep = pcs->text_delimeter; + printcell.hidecol_flags = pcs->hidecol_flags; + + int ch = printcell.titlesep; /* first column $ is optional */ + FOR_NB_SCREENS(n) { rb->screens[n]->getstringsize("W", &width, &height); printcell.offw[n] = width; /* set column text offset */ } - if (*title == '$') + if (*title == printcell.titlesep) title++; do { - if (ch == '$') + if (ch == printcell.titlesep) { printcell.title[j++] = ch; user_minwidth = 0; @@ -466,7 +575,7 @@ int printcell_set_columns(struct gui_synclist *gui_list, user_minwidth = 10*user_minwidth + *title - '0'; title++; } - if (*title != '$') /* user forgot $ or wants to display '*' */ + if (*title != printcell.titlesep) /* user forgot titlesep or wants to display '*' */ { title = dspst; user_minwidth = 0; @@ -476,17 +585,25 @@ int printcell_set_columns(struct gui_synclist *gui_list, } sidx[i] = j; - if (*title == '$') /* $$ escaped user must want to display $*/ - printcell.title[j++] = *title++; - while (*title != '\0' && *title != '$' && j < PRINTCELL_MAXLINELEN - 1) + while (*title != '\0' + && *title != printcell.titlesep + && j < PRINTCELL_MAXLINELEN - 1) + { printcell.title[j++] = *title++; + } FOR_NB_SCREENS(n) { - rb->screens[n]->getstringsize(&printcell.title[sidx[i]], &width, &height); + rb->screens[n]->getstringsize(&printcell.title[sidx[i]], + &width, &height); + if (width < user_minwidth) width = user_minwidth; + + if (width > LCD_WIDTH) + width = LCD_WIDTH; + printcell.colw[n][i] = width; printcell.totalcolw[n] += width; } @@ -498,37 +615,67 @@ int printcell_set_columns(struct gui_synclist *gui_list, printcell.ncols = i; printcell.title[j] = '\0'; printcell.selcol = -1; - printcell.selcol_index = 0; rb->gui_synclist_set_title(gui_list, printcell.title, icon); return printcell.ncols; } -char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz) +char *printcell_get_title_text(int selcol, char *buf, size_t bufsz) { - int selected = gui_list->selected_item; - int index = printcell.selcol_index - 1; + /* note offsets are calculated everytime this function is called + * shouldn't be used in hot code paths */ + int index = 0; + buf[0] = '\0'; + if (selcol < 0) /* return entire string incld col formatting '$'*/ + return printcell.title; + + if (selcol < printcell.ncols) + { + uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/ + parse_dsptext(printcell.titlesep, selcol + 1, printcell.title, buf, bufsz, sidx); + index = sidx[selcol]; + } + return &buf[index]; +} - if (index < 0) - index = 0; +char *printcell_get_column_text(int selcol, char *buf, size_t bufsz) +{ + int index = 0; char *bpos; + struct gui_synclist *gui_list = printcell.gui_list; - if (gui_list->callback_get_item_name(selected, gui_list->data, buf, bufsz) == buf) + if (gui_list && gui_list->callback_draw_item == printcell_listdraw_fn) { - bpos = &buf[index]; - if (printcell.selcol < 0) /* return entire string incld col formatting '$'*/ - return bpos; - while(bpos < &buf[bufsz - 1]) + int col = selcol; + int item = gui_list->selected_item; + void *data = gui_list->data; + + if (col < printcell.ncols + && gui_list->callback_get_item_name(item, data, buf, bufsz) == buf) { - if (*bpos == '$' || *bpos == '\0') - goto success; - bpos++; + bpos = buf; + if (col < 0) /* return entire string incld col formatting '$'*/ + { + return bpos; + } + bpos++; /* Skip sep/NULL */ + + while(bpos < &buf[bufsz - 1]) + { + if (*bpos == printcell.colsep || *bpos == '\0') + { + if (col-- == 0) + goto success; + index = bpos - buf + 1; /* Skip sep/NULL */ + } + bpos++; + } } } /*failure*/ - bpos = buf; - index = 0; + bpos = buf; + index = 0; success: - *bpos = '\0'; - return &buf[index]; + *bpos = '\0'; + return &buf[index]; } diff --git a/apps/plugins/lib/printcell_helper.h b/apps/plugins/lib/printcell_helper.h index adc98e5a5f..f58e73c0a5 100644 --- a/apps/plugins/lib/printcell_helper.h +++ b/apps/plugins/lib/printcell_helper.h @@ -21,25 +21,67 @@ #ifndef _PRINTCELL_LIST_H_ #define _PRINTCELL_LIST_H_ +#ifndef PRINTCELL_MAX_COLUMNS +#define PRINTCELL_MAX_COLUMNS 16 /* Max 32 (hidecol_flags)*/ +#endif + #define PRINTCELL_MAXLINELEN MAX_PATH +#define PC_COL_FLAG(col) ((uint32_t)(col >= 0 \ + && col < PRINTCELL_MAX_COLUMNS) ? 1u<menuid == MENU_ID(M_TESTPUT)) { synclist_set(cur->menuid, 0, cur->items, 1); -#if LCD_DEPTH > 1 - /* If line sep is set to automatic then outline cells */ - bool showlinesep = (rb->global_settings->list_separator_height < 0); -#else - bool showlinesep = (rb->global_settings->cursor_style == 0); -#endif - printcell_enable(lists, true, showlinesep); + printcell_enable(true); //lists->callback_draw_item = test_listdraw_fn; } else { - printcell_enable(lists, false, false); + printcell_enable(false); synclist_set(cur->menuid, 1, cur->items, 1); } rb->gui_synclist_draw(lists); @@ -473,7 +479,7 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli if (lists->data == MENU_ID(M_TESTPUT)) { //lists->callback_draw_item = NULL; - printcell_enable(lists, false, false); + printcell_enable(false); } if (lists->data != MENU_ID(M_ROOT)) { @@ -505,7 +511,8 @@ static void synclist_set(char* menu_id, int selected_item, int items, int sel_si menu_id, false, sel_size, NULL); if (menu_id == MENU_ID(M_TESTPUT)) { - testput_cols = printcell_set_columns(&lists, TESTPUT_HEADER, Icon_Rockbox); + testput_cols = printcell_set_columns(&lists, NULL, + TESTPUT_HEADER, Icon_Rockbox); } else { @@ -562,6 +569,6 @@ enum plugin_status plugin_start(const void* parameter) selected_item = rb->gui_synclist_get_sel_pos(&lists); } } - + printcell_enable(false); return ret; } diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index 8d266966d0..c0b76d45d5 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -105,4 +105,5 @@ shopper,viewers/shopper,1 lnk,viewers/windows_lnk,- #ifdef HAVE_TAGCACHE *,demos/pictureflow,- +log,viewers/lastfm_scrobbler_viewer,4 #endif -- cgit v1.2.3