summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2023-04-01 12:32:47 -0400
committerWilliam Wilgus <me.theuser@yahoo.com>2023-04-16 23:50:24 -0400
commitdfe12252bba8cbfe427752ad9ed101002a76fe67 (patch)
treeef0dd104dff37aac82966a4977f035487966d8fd
parent43b0fba75d4312230793f2747700371362204bb6 (diff)
downloadrockbox-dfe12252bb.tar.gz
rockbox-dfe12252bb.zip
[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
-rw-r--r--apps/filetypes.c1
-rw-r--r--apps/filetypes.h1
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/keyremap.c29
-rw-r--r--apps/plugins/lastfm_scrobbler.c2
-rw-r--r--apps/plugins/lastfm_scrobbler_viewer.c1033
-rw-r--r--apps/plugins/lib/printcell_helper.c297
-rw-r--r--apps/plugins/lib/printcell_helper.h68
-rw-r--r--apps/plugins/rb_info.c35
-rw-r--r--apps/plugins/viewers.config1
11 files changed, 1358 insertions, 111 deletions
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<<col#) */
+ uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; /* width of title text
+ or MIN(or user defined width / screen width) */
+ char title[PRINTCELL_MAXLINELEN]; /* title buffer */
+ char titlesep; /* character that separates title column items (ex '$') */
+ char colsep; /* character that separates text column items (ex ',') */
+ bool separator; /* draw grid */
};
+
static struct printcell_info_t printcell;
-static void parse_dsptext(int ncols, const char *dsp_text, char* buffer, uint16_t *sidx)
+static void parse_dsptext(char splitchr, int ncols, const char *dsp_text,
+ char* buffer, size_t bufsz, uint16_t *sidx)
{
/*Internal function loads sidx with split offsets indexing
- the buffer of null terminated strings, splits on '$'
+ the buffer of null terminated strings, splits on 'splitchr'
_assumptions_:
dsp_text[len - 1] = \0,
- buffer[PRINTCELL_MAXLINELEN],
sidx[PRINTCELL_MAX_COLUMNS]
*/
int i = 0;
- int j = 0;
- int ch = '$'; /* first column $ is optional */
- if (*dsp_text == '$')
+ size_t j = 0;
+ int ch = splitchr; /* first column $ is optional */
+ if (*dsp_text == splitchr)
dsp_text++;
/* add null to the start of the text buffer */
buffer[j++] = '\0';
do
{
- if (ch == '$')
+ if (ch == splitchr)
{
- sidx[i] = j;
- if (*dsp_text == '$') /* $$ escaped user must want to display $*/
- buffer[j++] = *dsp_text++;
- while (*dsp_text != '\0' && *dsp_text != '$' && j < PRINTCELL_MAXLINELEN - 1)
+ sidx[i] = j; /* save start index and copy next column to the buffer */
+ while (*dsp_text != '\0' && *dsp_text != splitchr && j < (bufsz - 1))
+ {
buffer[j++] = *dsp_text++;
+ }
buffer[j++] = '\0';
+
i++;
- if (i >= 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<<col : -1u)
+
+#define PRINTCELL_COLUMN_IS_VISIBLE(flag, col) ((flag & PC_COL_FLAG(col)) == 0)
+#define PRINTCELL_COLUMN_FLAG(col) (PC_COL_FLAG(col))
-/* sets the printcell function enabled */
-void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator);
+struct printcell_settings
+{
+ bool cell_separator;
+ char title_delimeter;
+ char text_delimeter;
+ uint32_t hidecol_flags;
+};
-/* sets title and calculates cell widths each column is identified by '$' character
- ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$
- ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3
- returns number of columns
+/* Printcell initialization - Sets title and calculates cell widths
+* by default each column is identified by '$' character
+* ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$
+* ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3
+* supplying struct printcell_settings pcs allows changing default settings
+* supply NULL to use defaults
+*
+* Returns number of columns
*/
-int printcell_set_columns(struct gui_synclist *gui_list,
- char * title, enum themable_icons icon);
+int printcell_set_columns(struct gui_synclist *gui_list,
+ struct printcell_settings * pcs,
+ char * title, enum themable_icons icon);
+
+/* Sets the printcell function enabled (use after initializing with set_column)
+ * Note you should call printcell_enable(false) if the list might be reused */
+void printcell_enable(bool enable);
-/* increments the current selected column negative increment is allowed
- returns the selected column
+/* Increments the current selected column negative increment is allowed
+ returns the selected column
range: -1(no selection) to ncols - 1 */
-int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap);
+int printcell_increment_column(int increment, bool wrap);
+
+/* Return index of the currently selected column (-1 to ncols - 1) */
+int printcell_get_column_selected(void);
+
+/* Return the text of currently selected column buffer should be sized
+ * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */
+char *printcell_get_column_text(int selcol, char *buf, size_t bufsz);
-/* return the text of currently selected column buffer should be sized
+/* Return the text of currently selected column title should be sized
* for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */
-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);
+
+
+/* Hide or show a specified column - supply col = -1 to affect all columns */
+void printcell_set_column_visible(int col, bool visible);
+
+/* Return visibility of a specified column
+* returns (1 visible or 0 hidden)
+* if supply col == -1 a flag with visibility of all columns will be returned
+* NOTE: flag denotes a hidden column by a 1 in the column bit (1 << col#)
+* PRINTCELL_COLUMN_IS_VISIBLE(flag,col) macro will convert to bool
+*/
+uint32_t printcell_get_column_visibility(int col);
#endif /*_PRINTCELL_LIST_H_*/
diff --git a/apps/plugins/rb_info.c b/apps/plugins/rb_info.c
index f123a623d2..a89cc658cc 100644
--- a/apps/plugins/rb_info.c
+++ b/apps/plugins/rb_info.c
@@ -335,6 +335,17 @@ static int list_voice_cb(int list_index, void* data)
rb->talk_spell(name, true);
}
}
+ else if (data == MENU_ID(M_TESTPUT))
+ {
+ char buf[64];
+ const char* name = printcell_get_column_text(printcell_get_column_selected(),
+ 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[64];
@@ -354,12 +365,12 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
{
if (*action == ACTION_STD_OK)
{
- printcell_increment_column(lists, 1, true);
+ printcell_increment_column(1, true);
*action = ACTION_NONE;
}
else if (*action == ACTION_STD_CANCEL)
{
- if (printcell_increment_column(lists, -1, true) != testput_cols - 1)
+ if (printcell_increment_column(-1, true) != testput_cols - 1)
{
*action = ACTION_NONE;
}
@@ -368,7 +379,8 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
{
char buf[PRINTCELL_MAXLINELEN];
char* bufp = buf;
- bufp = printcell_get_selected_column_text(lists, bufp, PRINTCELL_MAXLINELEN);
+ int selcol = printcell_get_column_selected();
+ bufp = printcell_get_column_text(selcol, bufp, PRINTCELL_MAXLINELEN);
rb->splashf(HZ * 2, "Item: %s", bufp);
}
}
@@ -429,18 +441,12 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
if (cur->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