diff options
author | William Wilgus <wilgus.william@gmail.com> | 2023-04-01 12:32:47 -0400 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2023-04-16 23:50:24 -0400 |
commit | dfe12252bba8cbfe427752ad9ed101002a76fe67 (patch) | |
tree | ef0dd104dff37aac82966a4977f035487966d8fd | |
parent | 43b0fba75d4312230793f2747700371362204bb6 (diff) | |
download | rockbox-dfe12252bba8cbfe427752ad9ed101002a76fe67.tar.gz rockbox-dfe12252bba8cbfe427752ad9ed101002a76fe67.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.c | 1 | ||||
-rw-r--r-- | apps/filetypes.h | 1 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/keyremap.c | 29 | ||||
-rw-r--r-- | apps/plugins/lastfm_scrobbler.c | 2 | ||||
-rw-r--r-- | apps/plugins/lastfm_scrobbler_viewer.c | 1033 | ||||
-rw-r--r-- | apps/plugins/lib/printcell_helper.c | 297 | ||||
-rw-r--r-- | apps/plugins/lib/printcell_helper.h | 68 | ||||
-rw-r--r-- | apps/plugins/rb_info.c | 35 | ||||
-rw-r--r-- | apps/plugins/viewers.config | 1 |
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[] = { | |||
129 | { "fmr", FILE_ATTR_FMR }, | 129 | { "fmr", FILE_ATTR_FMR }, |
130 | { "fms", FILE_ATTR_FMS }, | 130 | { "fms", FILE_ATTR_FMS }, |
131 | #endif | 131 | #endif |
132 | { "log", FILE_ATTR_LOG }, | ||
132 | { "lng", FILE_ATTR_LNG }, | 133 | { "lng", FILE_ATTR_LNG }, |
133 | { "rock", FILE_ATTR_ROCK }, | 134 | { "rock", FILE_ATTR_ROCK }, |
134 | { "lua", FILE_ATTR_LUA }, | 135 | { "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 @@ | |||
48 | #define FILE_ATTR_FMS 0x1200 /* FM screen skin file */ | 48 | #define FILE_ATTR_FMS 0x1200 /* FM screen skin file */ |
49 | #define FILE_ATTR_RFMS 0x1300 /* FM screen skin file */ | 49 | #define FILE_ATTR_RFMS 0x1300 /* FM screen skin file */ |
50 | #define FILE_ATTR_OPX 0x1400 /* open plugins shortcut */ | 50 | #define FILE_ATTR_OPX 0x1400 /* open plugins shortcut */ |
51 | #define FILE_ATTR_LOG 0x1500 /* log file */ | ||
51 | #define FILE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ | 52 | #define FILE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */ |
52 | 53 | ||
53 | struct filetype { | 54 | 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 | |||
50 | keyremap,apps | 50 | keyremap,apps |
51 | lamp,apps | 51 | lamp,apps |
52 | lastfm_scrobbler,apps | 52 | lastfm_scrobbler,apps |
53 | lastfm_scrobbler_viewer,viewers | ||
53 | logo,demos | 54 | logo,demos |
54 | lrcplayer,apps | 55 | lrcplayer,apps |
55 | lua,viewers | 56 | 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 | |||
13 | keybox.c | 13 | keybox.c |
14 | keyremap.c | 14 | keyremap.c |
15 | lastfm_scrobbler.c | 15 | lastfm_scrobbler.c |
16 | lastfm_scrobbler_viewer.c | ||
16 | logo.c | 17 | logo.c |
17 | lrcplayer.c | 18 | lrcplayer.c |
18 | mosaique.c | 19 | 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) | |||
1422 | else | 1422 | else |
1423 | rb->talk_spell(name, true); | 1423 | rb->talk_spell(name, true); |
1424 | } | 1424 | } |
1425 | else if(data == MENU_ID(M_SETKEYS)) | ||
1426 | { | ||
1427 | char buf[MAX_MENU_NAME]; | ||
1428 | int selcol = printcell_get_column_selected(); | ||
1429 | const char* name = printcell_get_column_text(selcol, buf, sizeof(buf)); | ||
1430 | long id = P2ID((const unsigned char *)name); | ||
1431 | if(id>=0) | ||
1432 | rb->talk_id(id, true); | ||
1433 | else | ||
1434 | rb->talk_spell(name, true); | ||
1435 | } | ||
1425 | else | 1436 | else |
1426 | { | 1437 | { |
1427 | char buf[MAX_MENU_NAME]; | 1438 | char buf[MAX_MENU_NAME]; |
@@ -1606,14 +1617,14 @@ int menu_action_setkeys(int *action, int selected_item, bool* exit, struct gui_s | |||
1606 | } | 1617 | } |
1607 | else | 1618 | else |
1608 | { | 1619 | { |
1609 | keyset.view_lastcol = printcell_increment_column(lists, 1, true); | 1620 | keyset.view_lastcol = printcell_increment_column(1, true); |
1610 | *action = ACTION_NONE; | 1621 | *action = ACTION_NONE; |
1611 | } | 1622 | } |
1612 | } | 1623 | } |
1613 | } | 1624 | } |
1614 | else if (*action == ACTION_STD_CANCEL) | 1625 | else if (*action == ACTION_STD_CANCEL) |
1615 | { | 1626 | { |
1616 | keyset.view_lastcol = printcell_increment_column(lists, -1, true); | 1627 | keyset.view_lastcol = printcell_increment_column(-1, true); |
1617 | if (keyset.view_lastcol != keyset.view_columns - 1) | 1628 | if (keyset.view_lastcol != keyset.view_columns - 1) |
1618 | { | 1629 | { |
1619 | *action = ACTION_NONE; | 1630 | *action = ACTION_NONE; |
@@ -2038,7 +2049,7 @@ static void synclist_set(int id, int selected_item, int items, int sel_size) | |||
2038 | rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); | 2049 | rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); |
2039 | rb->gui_synclist_set_nb_items(&lists,items); | 2050 | rb->gui_synclist_set_nb_items(&lists,items); |
2040 | rb->gui_synclist_select_item(&lists, selected_item); | 2051 | rb->gui_synclist_select_item(&lists, selected_item); |
2041 | printcell_enable(&lists, false, false); | 2052 | printcell_enable(false); |
2042 | 2053 | ||
2043 | if (menu_id == MENU_ID(M_ROOT)) | 2054 | if (menu_id == MENU_ID(M_ROOT)) |
2044 | { | 2055 | { |
@@ -2047,15 +2058,16 @@ static void synclist_set(int id, int selected_item, int items, int sel_size) | |||
2047 | } | 2058 | } |
2048 | else if (menu_id == MENU_ID(M_SETKEYS)) | 2059 | else if (menu_id == MENU_ID(M_SETKEYS)) |
2049 | { | 2060 | { |
2050 | printcell_enable(&lists, true, true); | 2061 | keyset.view_columns = printcell_set_columns(&lists, NULL, |
2051 | keyset.view_columns = printcell_set_columns(&lists, ACTVIEW_HEADER, Icon_Rockbox); | 2062 | ACTVIEW_HEADER, Icon_Rockbox); |
2052 | int curcol = printcell_increment_column(&lists, 0, true); | 2063 | printcell_enable(true); |
2064 | int curcol = printcell_get_column_selected(); | ||
2053 | if (keyset.view_lastcol >= keyset.view_columns) | 2065 | if (keyset.view_lastcol >= keyset.view_columns) |
2054 | keyset.view_lastcol = -1; | 2066 | keyset.view_lastcol = -1; |
2055 | /* restore column position */ | 2067 | /* restore column position */ |
2056 | while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol) | 2068 | while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol) |
2057 | { | 2069 | { |
2058 | curcol = printcell_increment_column(&lists, 1, true); | 2070 | curcol = printcell_increment_column(1, true); |
2059 | } | 2071 | } |
2060 | keyset.view_lastcol = curcol; | 2072 | keyset.view_lastcol = curcol; |
2061 | } | 2073 | } |
@@ -2065,9 +2077,8 @@ static void synclist_set(int id, int selected_item, int items, int sel_size) | |||
2065 | PEEK_MENU_ID(id); | 2077 | PEEK_MENU_ID(id); |
2066 | lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title)); | 2078 | lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title)); |
2067 | rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered); | 2079 | rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered); |
2068 | /* if (menu_title[0] == '$'){ printcell_enable(&lists, true, true); } */ | 2080 | /* if (menu_title[0] == '$'){ printcell_enable(true); } */ |
2069 | } | 2081 | } |
2070 | |||
2071 | } | 2082 | } |
2072 | 2083 | ||
2073 | static void keyremap_set_buffer(void* buffer, size_t buf_size) | 2084 | 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) | |||
160 | case 3: | 160 | case 3: |
161 | rb->set_int("Beep Level", "", UNIT_INT, | 161 | rb->set_int("Beep Level", "", UNIT_INT, |
162 | &gConfig.beeplvl, NULL, 1, 0, 10, NULL); | 162 | &gConfig.beeplvl, NULL, 1, 0, 10, NULL); |
163 | if (gConfig.beeplvl > 0) | ||
164 | rb->beep_play(1500, 100, 100 * gConfig.beeplvl); | ||
163 | case 4: /*sep*/ | 165 | case 4: /*sep*/ |
164 | continue; | 166 | continue; |
165 | case 5: | 167 | 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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / | ||
5 | * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2023 William Wilgus | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "plugin.h" | ||
23 | #include "lang_enum.h" | ||
24 | |||
25 | #include "lib/printcell_helper.h" | ||
26 | |||
27 | #include "lib/configfile.h" | ||
28 | #define CFG_FILE "/lastfm_scrobbler_viewer.cfg" | ||
29 | #define TMP_FILE ""PLUGIN_DATA_DIR "/lscrobbler_viewer_%d.tmp" | ||
30 | #define CFG_VER 1 | ||
31 | |||
32 | #ifdef ROCKBOX_HAS_LOGF | ||
33 | #define logf rb->logf | ||
34 | #else | ||
35 | #define logf(...) do { } while(0) | ||
36 | #endif | ||
37 | |||
38 | /*#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMP #MUSICBRAINZ_TRACKID*/ | ||
39 | |||
40 | #define SCROBBLE_HDR_FMT "$*%d$%s$*%d$%s$*%d$%s$Track#$Length$Rating$TimeStamp$TrackId" | ||
41 | //#define SCROBBLE_HDR "$*128$Artist$*128$Album$*128$Title$Track#$Length$Rating$TimeStamp$TrackId" | ||
42 | |||
43 | #define SCROBBLER_MIN_COLUMNS (6) /* a valid scrobbler file should have at least this many columns */ | ||
44 | |||
45 | #define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1) | ||
46 | #define CANCEL_CONTEXT_MENU (PLUGIN_OK + 2) | ||
47 | #define QUIT_CONTEXT_MENU (PLUGIN_OK + 3) | ||
48 | /* global lists, for everything */ | ||
49 | static struct gui_synclist lists; | ||
50 | |||
51 | /* printcell data for the current file */ | ||
52 | struct printcell_data_t { | ||
53 | int view_columns; | ||
54 | int view_lastcol; | ||
55 | |||
56 | int items_buffered; | ||
57 | int items_total; | ||
58 | int fd_cur; | ||
59 | char *filename; | ||
60 | |||
61 | char *buf; | ||
62 | size_t buf_size; | ||
63 | off_t buf_used; | ||
64 | char header[PRINTCELL_MAXLINELEN]; | ||
65 | |||
66 | }; | ||
67 | |||
68 | enum e_find_type { | ||
69 | FIND_ALL = 0, | ||
70 | FIND_EXCLUDE, | ||
71 | FIND_EXCLUDE_CASE, | ||
72 | FIND_EXCLUDE_ANY, | ||
73 | FIND_INCLUDE, | ||
74 | FIND_INCLUDE_CASE, | ||
75 | FIND_INCLUDE_ANY, | ||
76 | FIND_CUSTOM, | ||
77 | }; | ||
78 | |||
79 | static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data); | ||
80 | static void pc_data_set_header(struct printcell_data_t *pc_data); | ||
81 | |||
82 | static void browse_file(char *buf, size_t bufsz) | ||
83 | { | ||
84 | struct browse_context browse = { | ||
85 | .dirfilter = SHOW_ALL, | ||
86 | .flags = BROWSE_SELECTONLY, | ||
87 | .title = "Select a scrobbler log file", | ||
88 | .icon = Icon_Playlist, | ||
89 | .buf = buf, | ||
90 | .bufsize = bufsz, | ||
91 | .root = "/", | ||
92 | }; | ||
93 | |||
94 | if (rb->rockbox_browse(&browse) != GO_TO_PREVIOUS) | ||
95 | { | ||
96 | buf[0] = '\0'; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static struct plugin_config | ||
101 | { | ||
102 | bool separator; | ||
103 | bool talk; | ||
104 | int col_width; | ||
105 | uint32_t hidecol_flags; | ||
106 | } gConfig; | ||
107 | |||
108 | static struct configdata config[] = | ||
109 | { | ||
110 | {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.separator }, "Cell Separator", NULL}, | ||
111 | {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.talk }, "Voice", NULL}, | ||
112 | {TYPE_INT, 32, LCD_WIDTH, { .int_p = &gConfig.col_width }, "Cell Width", NULL}, | ||
113 | {TYPE_INT, INT_MIN, INT_MAX, { .int_p = &gConfig.hidecol_flags }, "Hidden Columns", NULL}, | ||
114 | }; | ||
115 | const int gCfg_sz = sizeof(config)/sizeof(*config); | ||
116 | /****************** config functions *****************/ | ||
117 | static void config_set_defaults(void) | ||
118 | { | ||
119 | gConfig.col_width = MIN(LCD_WIDTH, 128); | ||
120 | gConfig.hidecol_flags = 0; | ||
121 | gConfig.separator = true; | ||
122 | gConfig.talk = rb->global_settings->talk_menu; | ||
123 | } | ||
124 | |||
125 | static void config_save(void) | ||
126 | { | ||
127 | configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); | ||
128 | } | ||
129 | |||
130 | static int config_settings_menu(struct printcell_data_t *pc_data) | ||
131 | { | ||
132 | int selection = 0; | ||
133 | |||
134 | struct viewport parentvp[NB_SCREENS]; | ||
135 | FOR_NB_SCREENS(l) | ||
136 | { | ||
137 | rb->viewport_set_defaults(&parentvp[l], l); | ||
138 | rb->viewport_set_fullscreen(&parentvp[l], l); | ||
139 | } | ||
140 | |||
141 | MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_SETTINGS), NULL, | ||
142 | ID2P(LANG_LIST_SEPARATOR), | ||
143 | "Cell Width", | ||
144 | ID2P(LANG_VOICE), | ||
145 | ID2P(VOICE_BLANK), | ||
146 | ID2P(VOICE_BLANK), | ||
147 | ID2P(LANG_CANCEL_0), | ||
148 | ID2P(LANG_SAVE_EXIT)); | ||
149 | |||
150 | do { | ||
151 | selection=rb->do_menu(&settings_menu,&selection, parentvp, true); | ||
152 | switch(selection) { | ||
153 | |||
154 | case 0: | ||
155 | rb->set_bool(rb->str(LANG_LIST_SEPARATOR), &gConfig.separator); | ||
156 | break; | ||
157 | case 1: | ||
158 | rb->set_int("Cell Width", "", UNIT_INT, | ||
159 | &gConfig.col_width, NULL, 1, 32, LCD_WIDTH, NULL ); | ||
160 | break; | ||
161 | case 2: | ||
162 | rb->set_bool(rb->str(LANG_VOICE), &gConfig.talk); | ||
163 | break; | ||
164 | case 3: | ||
165 | continue; | ||
166 | case 4: /*sep*/ | ||
167 | continue; | ||
168 | case 5: | ||
169 | return -1; | ||
170 | break; | ||
171 | case 6: | ||
172 | { | ||
173 | pc_data_set_header(pc_data); | ||
174 | synclist_set(0, pc_data->items_total, 1, pc_data); | ||
175 | int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); | ||
176 | if (res >= 0) | ||
177 | { | ||
178 | logf("Cfg saved %s %d bytes", CFG_FILE, gCfg_sz); | ||
179 | return PLUGIN_OK; | ||
180 | } | ||
181 | logf("Cfg FAILED (%d) %s", res, CFG_FILE); | ||
182 | return PLUGIN_ERROR; | ||
183 | } | ||
184 | case MENU_ATTACHED_USB: | ||
185 | return PLUGIN_USB_CONNECTED; | ||
186 | default: | ||
187 | return PLUGIN_OK; | ||
188 | } | ||
189 | } while ( selection >= 0 ); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | /* open file pass back file descriptor and return file size */ | ||
194 | static size_t file_open(const char *filename, int *fd) | ||
195 | { | ||
196 | size_t fsize = 0; | ||
197 | |||
198 | if (filename && fd) | ||
199 | { | ||
200 | *fd = rb->open(filename, O_RDONLY); | ||
201 | if (*fd >= 0) | ||
202 | { | ||
203 | fsize = rb->filesize(*fd); | ||
204 | } | ||
205 | } | ||
206 | return fsize; | ||
207 | } | ||
208 | |||
209 | static const char* list_get_name_cb(int selected_item, void* data, | ||
210 | char* buf, size_t buf_len) | ||
211 | { | ||
212 | const int slush_pos = 32; /* entries around the current entry to keep buffered */ | ||
213 | /* keep track of the last position in the buffer */ | ||
214 | static int buf_line_num = 0; | ||
215 | static off_t buf_last_pos = 0; | ||
216 | /* keep track of the last position in the file */ | ||
217 | static int file_line_num = 0; | ||
218 | static off_t file_last_seek = 0; | ||
219 | |||
220 | if (selected_item < 0) | ||
221 | { | ||
222 | logf("[%s] Reset positions", __func__); | ||
223 | buf_line_num = 0; | ||
224 | buf_last_pos = 0; | ||
225 | file_line_num = 0; | ||
226 | file_last_seek = 0; | ||
227 | return ""; | ||
228 | } | ||
229 | |||
230 | int line_num; | ||
231 | struct printcell_data_t *pc_data = (struct printcell_data_t*) data; | ||
232 | bool found = false; | ||
233 | |||
234 | if (pc_data->buf && selected_item < pc_data->items_buffered) | ||
235 | { | ||
236 | int buf_pos; | ||
237 | |||
238 | if (buf_line_num > selected_item || buf_last_pos >= pc_data->buf_used | ||
239 | || buf_line_num < 0) | ||
240 | { | ||
241 | logf("[%s] Set pos buffer 0", __func__); | ||
242 | buf_line_num = 0; | ||
243 | buf_last_pos = 0; | ||
244 | } | ||
245 | buf_pos = buf_last_pos; | ||
246 | line_num = buf_line_num; | ||
247 | |||
248 | while (buf_pos < pc_data->buf_used | ||
249 | && line_num < pc_data->items_buffered) | ||
250 | { | ||
251 | size_t len = rb->strlen(&pc_data->buf[buf_pos]); | ||
252 | if(line_num == selected_item) | ||
253 | { | ||
254 | rb->strlcpy(buf, &pc_data->buf[buf_pos], MIN(buf_len, len)); | ||
255 | logf("(%d) in buffer: %s", line_num, buf); | ||
256 | found = true; | ||
257 | break; | ||
258 | } | ||
259 | else | ||
260 | { | ||
261 | buf_pos += len + 1; /* need to go past the NULL terminator */ | ||
262 | line_num++; | ||
263 | |||
264 | if (buf_line_num + slush_pos < selected_item) | ||
265 | { | ||
266 | logf("[%s] Set pos buffer %d", __func__, line_num); | ||
267 | buf_line_num = line_num; | ||
268 | buf_last_pos = buf_pos; | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* didn't find the item try the file */ | ||
275 | if(!found && pc_data->fd_cur >= 0) | ||
276 | { | ||
277 | int fd = pc_data->fd_cur; | ||
278 | |||
279 | if (file_line_num < 0 || file_line_num > selected_item) | ||
280 | { | ||
281 | logf("[%s] Set seek file 0", __func__); | ||
282 | file_line_num = 0; | ||
283 | file_last_seek = 0; | ||
284 | } | ||
285 | |||
286 | rb->lseek(fd, file_last_seek, SEEK_SET); | ||
287 | line_num = file_line_num; | ||
288 | |||
289 | while ((rb->read_line(fd, buf, buf_len)) > 0) | ||
290 | { | ||
291 | if(buf[0] == '#') | ||
292 | continue; | ||
293 | if(line_num == selected_item) | ||
294 | { | ||
295 | logf("(%d) in file: %s", line_num, buf); | ||
296 | found = true; | ||
297 | break; | ||
298 | } | ||
299 | else | ||
300 | { | ||
301 | line_num++; | ||
302 | |||
303 | if (file_line_num + slush_pos < selected_item) | ||
304 | { | ||
305 | logf("[%s] Set seek file %d", __func__, line_num); | ||
306 | file_line_num = line_num; | ||
307 | file_last_seek = rb->lseek(fd, 0, SEEK_CUR); | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | |||
313 | if(!found) | ||
314 | { | ||
315 | logf("(%d) Not Found!", selected_item); | ||
316 | buf_line_num = -1; | ||
317 | file_line_num = -1; | ||
318 | buf[0] = '\0'; | ||
319 | } | ||
320 | return buf; | ||
321 | } | ||
322 | |||
323 | static int list_voice_cb(int list_index, void* data) | ||
324 | { | ||
325 | (void) list_index; | ||
326 | struct printcell_data_t *pc_data = (struct printcell_data_t*) data; | ||
327 | if (!gConfig.talk) | ||
328 | return -1; | ||
329 | |||
330 | int selcol = printcell_get_column_selected(); | ||
331 | char buf[MAX_PATH]; | ||
332 | char* name; | ||
333 | long id; | ||
334 | |||
335 | if (pc_data->view_lastcol != selcol) | ||
336 | { | ||
337 | name = printcell_get_title_text(selcol, buf, sizeof(buf)); | ||
338 | |||
339 | id = P2ID((const unsigned char *)name); | ||
340 | |||
341 | if(id>=0) | ||
342 | rb->talk_id(id, true); | ||
343 | else if (selcol >= 0) | ||
344 | { | ||
345 | switch (selcol) | ||
346 | { | ||
347 | case 0: | ||
348 | rb->talk_id(LANG_ID3_ARTIST, true); | ||
349 | break; | ||
350 | case 1: | ||
351 | rb->talk_id(LANG_ID3_ALBUM, true); | ||
352 | break; | ||
353 | case 2: | ||
354 | rb->talk_id(LANG_ID3_TITLE, true); | ||
355 | break; | ||
356 | case 3: | ||
357 | rb->talk_id(LANG_ID3_TRACKNUM, true); | ||
358 | break; | ||
359 | case 4: | ||
360 | rb->talk_id(LANG_ID3_LENGTH, true); | ||
361 | break; | ||
362 | |||
363 | default: | ||
364 | rb->talk_spell(name, true); | ||
365 | break; | ||
366 | } | ||
367 | } | ||
368 | else | ||
369 | rb->talk_id(LANG_ALL, true); | ||
370 | |||
371 | rb->talk_id(VOICE_PAUSE, true); | ||
372 | } | ||
373 | |||
374 | name = printcell_get_column_text(selcol, buf, sizeof(buf)); | ||
375 | |||
376 | id = P2ID((const unsigned char *)name); | ||
377 | |||
378 | if(id>=0) | ||
379 | rb->talk_id(id, true); | ||
380 | else if (selcol >= 0) | ||
381 | rb->talk_spell(name, true); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static enum themable_icons list_icon_cb(int selected_item, void *data) | ||
387 | { | ||
388 | struct printcell_data_t *pc_data = (struct printcell_data_t*) data; | ||
389 | if (lists.selected_item == selected_item) | ||
390 | { | ||
391 | if (pc_data->view_lastcol < 0) | ||
392 | return Icon_Config; | ||
393 | return Icon_Audio; | ||
394 | } | ||
395 | return Icon_NOICON; | ||
396 | } | ||
397 | |||
398 | /* load file entries into pc_data buffer, file should already be opened | ||
399 | * and will be closed if the whole file was buffered */ | ||
400 | static int file_load_entries(struct printcell_data_t *pc_data) | ||
401 | { | ||
402 | int items = 0; | ||
403 | int count = 0; | ||
404 | int buffered = 0; | ||
405 | unsigned int pos = 0; | ||
406 | bool comment = false; | ||
407 | char ch; | ||
408 | int fd = pc_data->fd_cur; | ||
409 | if (fd < 0) | ||
410 | return 0; | ||
411 | |||
412 | rb->lseek(fd, 0, SEEK_SET); | ||
413 | |||
414 | while (rb->read(fd, &ch, 1) > 0) | ||
415 | { | ||
416 | if (count++ == 0 && ch == '#') /* skip comments */ | ||
417 | comment = true; | ||
418 | else if (!comment && ch != '\r' && pc_data->buf_size > pos) | ||
419 | pc_data->buf[pos++] = ch; | ||
420 | |||
421 | if (items == 0 && pos > PRINTCELL_MAXLINELEN * 2) | ||
422 | break; | ||
423 | |||
424 | if (ch == '\n') | ||
425 | { | ||
426 | if (!comment) | ||
427 | { | ||
428 | pc_data->buf[pos] = '\0'; | ||
429 | if (pc_data->buf_size > pos) | ||
430 | { | ||
431 | pos++; | ||
432 | buffered++; | ||
433 | } | ||
434 | items++; | ||
435 | } | ||
436 | comment = false; | ||
437 | count = 0; | ||
438 | rb->yield(); | ||
439 | } | ||
440 | } | ||
441 | |||
442 | logf("[%s] items: %d buffered: %d", __func__, items, buffered); | ||
443 | |||
444 | pc_data->items_total = items; | ||
445 | pc_data->items_buffered = buffered; | ||
446 | pc_data->buf[pos] = '\0'; | ||
447 | pc_data->buf_used = pos; | ||
448 | |||
449 | if (items == buffered) /* whole file fit into buffer; close file */ | ||
450 | { | ||
451 | rb->close(pc_data->fd_cur); | ||
452 | pc_data->fd_cur = -1; | ||
453 | } | ||
454 | |||
455 | list_get_name_cb(-1, NULL, NULL, 0); /* prime name cb */ | ||
456 | return items; | ||
457 | } | ||
458 | |||
459 | static int filter_items(struct printcell_data_t *pc_data, | ||
460 | enum e_find_type find_type, int col) | ||
461 | { | ||
462 | /* saves filtered items to a temp file and loads it */ | ||
463 | int fd; | ||
464 | bool reload = false; | ||
465 | char buf[PRINTCELL_MAXLINELEN]; | ||
466 | char find_exclude_buf[PRINTCELL_MAXLINELEN]; | ||
467 | int selcol = printcell_get_column_selected(); | ||
468 | char *find_exclude = printcell_get_column_text(selcol, find_exclude_buf, | ||
469 | sizeof(find_exclude_buf)); | ||
470 | const char colsep = '\t'; | ||
471 | int find_len = strlen(find_exclude); | ||
472 | |||
473 | if (find_type == FIND_CUSTOM || find_len == 0) | ||
474 | { | ||
475 | int option = 0; | ||
476 | struct opt_items find_types[] = { | ||
477 | {"Exclude", -1}, {"Exclude Case Sensitive", -1}, | ||
478 | {"Exclude Any", -1}, {"Include", -1}, | ||
479 | {"Include Case Sensitive", -1}, {"Include Any", -1} | ||
480 | }; | ||
481 | if (rb->set_option("Find Type", &option, INT, | ||
482 | find_types, 6, NULL)) | ||
483 | { | ||
484 | return 0; | ||
485 | } | ||
486 | switch (option) | ||
487 | { | ||
488 | case 0: | ||
489 | find_type = FIND_EXCLUDE; | ||
490 | break; | ||
491 | case 1: | ||
492 | find_type = FIND_EXCLUDE_CASE; | ||
493 | break; | ||
494 | case 2: | ||
495 | find_type = FIND_EXCLUDE_ANY; | ||
496 | break; | ||
497 | case 3: | ||
498 | find_type = FIND_INCLUDE; | ||
499 | break; | ||
500 | case 4: | ||
501 | find_type = FIND_INCLUDE_CASE; | ||
502 | break; | ||
503 | case 5: | ||
504 | find_type = FIND_INCLUDE_ANY; | ||
505 | break; | ||
506 | default: | ||
507 | find_type = FIND_ALL; | ||
508 | break; | ||
509 | } | ||
510 | |||
511 | /* copy data to beginning of buf */ | ||
512 | rb->memmove(find_exclude_buf, find_exclude, find_len); | ||
513 | find_exclude_buf[find_len] = '\0'; | ||
514 | |||
515 | if (rb->kbd_input(find_exclude_buf, sizeof(find_exclude_buf), NULL) < 0) | ||
516 | return -1; | ||
517 | find_exclude = find_exclude_buf; | ||
518 | find_len = strlen(find_exclude); | ||
519 | } | ||
520 | |||
521 | char tmp_filename[MAX_PATH]; | ||
522 | static int tmp_num = 0; | ||
523 | rb->snprintf(tmp_filename, sizeof(tmp_filename), TMP_FILE, tmp_num); | ||
524 | tmp_num++; | ||
525 | |||
526 | fd = rb->open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); | ||
527 | if(fd >= 0) | ||
528 | { | ||
529 | rb->splash_progress_set_delay(HZ * 5); | ||
530 | for (int i = 0; i < pc_data->items_total; i++) | ||
531 | { | ||
532 | rb->splash_progress(i, pc_data->items_total, "Filtering..."); | ||
533 | const char * data = list_get_name_cb(i, pc_data, buf, sizeof(buf)); | ||
534 | |||
535 | if (find_type != FIND_ALL) | ||
536 | { | ||
537 | int index = col; | ||
538 | |||
539 | if (index < 0) | ||
540 | index = 0; | ||
541 | if (find_len > 0) | ||
542 | { | ||
543 | char *bcol = buf; | ||
544 | while (*bcol != '\0' && index > 0) | ||
545 | { | ||
546 | if (*bcol == colsep) | ||
547 | index--; | ||
548 | bcol++; | ||
549 | } | ||
550 | if (index > 0) | ||
551 | continue; | ||
552 | |||
553 | if (find_type == FIND_EXCLUDE) | ||
554 | { | ||
555 | if (rb->strncasecmp(bcol, find_exclude, find_len) == 0) | ||
556 | { | ||
557 | logf("[%s] exclude [%s]", find_exclude, bcol); | ||
558 | continue; | ||
559 | } | ||
560 | } | ||
561 | else if (find_type == FIND_INCLUDE) | ||
562 | { | ||
563 | if (rb->strncasecmp(bcol, find_exclude, find_len) != 0) | ||
564 | { | ||
565 | logf("%s include %s", find_exclude, bcol); | ||
566 | continue; | ||
567 | } | ||
568 | } | ||
569 | else if (find_type == FIND_EXCLUDE_CASE) | ||
570 | { | ||
571 | if (rb->strncmp(bcol, find_exclude, find_len) == 0) | ||
572 | { | ||
573 | logf("[%s] exclude case [%s]", find_exclude, bcol); | ||
574 | continue; | ||
575 | } | ||
576 | } | ||
577 | else if (find_type == FIND_INCLUDE_CASE) | ||
578 | { | ||
579 | if (rb->strncmp(bcol, find_exclude, find_len) != 0) | ||
580 | { | ||
581 | logf("%s include case %s", find_exclude, bcol); | ||
582 | continue; | ||
583 | } | ||
584 | } | ||
585 | else if (find_type == FIND_EXCLUDE_ANY) | ||
586 | { | ||
587 | bool found = false; | ||
588 | while (*bcol != '\0' && *bcol != colsep) | ||
589 | { | ||
590 | if (rb->strncasecmp(bcol, find_exclude, find_len) == 0) | ||
591 | { | ||
592 | logf("[%s] exclude any [%s]", find_exclude, bcol); | ||
593 | found = true; | ||
594 | break; | ||
595 | } | ||
596 | bcol++; | ||
597 | } | ||
598 | if (found) | ||
599 | continue; | ||
600 | } | ||
601 | else if (find_type == FIND_INCLUDE_ANY) | ||
602 | { | ||
603 | bool found = false; | ||
604 | while (*bcol != '\0' && *bcol != colsep) | ||
605 | { | ||
606 | if (rb->strncasecmp(bcol, find_exclude, find_len) == 0) | ||
607 | { | ||
608 | found = true; | ||
609 | logf("[%s] include any [%s]", find_exclude, bcol); | ||
610 | break; | ||
611 | } | ||
612 | bcol++; | ||
613 | } | ||
614 | if (!found) | ||
615 | continue; | ||
616 | } | ||
617 | } | ||
618 | } | ||
619 | int len = strlen(data); | ||
620 | if (len > 0) | ||
621 | { | ||
622 | logf("writing [%d bytes][%s]", len + 1, data); | ||
623 | rb->write(fd, data, len); | ||
624 | rb->write(fd, "\n", 1); | ||
625 | } | ||
626 | } | ||
627 | reload = true; | ||
628 | } | ||
629 | |||
630 | if (reload) | ||
631 | { | ||
632 | if (pc_data->fd_cur >= 0) | ||
633 | rb->close(pc_data->fd_cur); | ||
634 | |||
635 | pc_data->fd_cur = fd; | ||
636 | int items = file_load_entries(pc_data); | ||
637 | if (items >= 0) | ||
638 | { | ||
639 | lists.selected_item = 0; | ||
640 | rb->gui_synclist_set_nb_items(&lists, items); | ||
641 | } | ||
642 | } | ||
643 | logf("Col text '%s'", find_exclude); | ||
644 | logf("text '%s'", find_exclude_buf); | ||
645 | |||
646 | return pc_data->items_total; | ||
647 | } | ||
648 | |||
649 | static int scrobbler_context_menu(struct printcell_data_t *pc_data) | ||
650 | { | ||
651 | struct viewport parentvp[NB_SCREENS]; | ||
652 | FOR_NB_SCREENS(l) | ||
653 | { | ||
654 | rb->viewport_set_defaults(&parentvp[l], l); | ||
655 | rb->viewport_set_fullscreen(&parentvp[l], l); | ||
656 | } | ||
657 | |||
658 | int col = printcell_get_column_selected(); | ||
659 | int selection = 0; | ||
660 | int items = 0; | ||
661 | uint32_t visible = printcell_get_column_visibility(-1); | ||
662 | bool hide_col = PRINTCELL_COLUMN_IS_VISIBLE(visible, col); | ||
663 | |||
664 | char namebuf[PRINTCELL_MAXLINELEN]; | ||
665 | enum e_find_type find_type = FIND_ALL; | ||
666 | |||
667 | char *colname = pc_data->filename; | ||
668 | if (col >= 0) | ||
669 | colname = printcell_get_title_text(col, namebuf, sizeof(namebuf)); | ||
670 | |||
671 | #define MENUITEM_STRINGLIST_CUSTOM(name, str, callback, ... ) \ | ||
672 | const char *name##_[] = {__VA_ARGS__}; \ | ||
673 | const struct menu_callback_with_desc name##__ = \ | ||
674 | {callback,str, Icon_NOICON}; \ | ||
675 | const struct menu_item_ex name = \ | ||
676 | {MT_RETURN_ID|MENU_HAS_DESC| \ | ||
677 | MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ | ||
678 | { .strings = name##_},{.callback_and_desc = & name##__}}; | ||
679 | |||
680 | const char *menu_item[4]; | ||
681 | |||
682 | menu_item[0]= hide_col ? "Hide":"Show"; | ||
683 | menu_item[1]= "Exclude"; | ||
684 | menu_item[2]= "Include"; | ||
685 | menu_item[3]= "Custom Filter"; | ||
686 | |||
687 | if (col == -1) | ||
688 | { | ||
689 | menu_item[0]= hide_col ? "Hide All":"Show All"; | ||
690 | menu_item[1]= "Open"; | ||
691 | menu_item[2]= "Reload"; | ||
692 | menu_item[3]= ID2P(LANG_SETTINGS); | ||
693 | } | ||
694 | |||
695 | MENUITEM_STRINGLIST_CUSTOM(context_menu, colname, NULL, | ||
696 | menu_item[0], | ||
697 | menu_item[1], | ||
698 | menu_item[2], | ||
699 | menu_item[3], | ||
700 | ID2P(VOICE_BLANK), | ||
701 | ID2P(LANG_CANCEL_0), | ||
702 | ID2P(LANG_MENU_QUIT)); | ||
703 | |||
704 | #undef MENUITEM_STRINGLIST_CUSTOM | ||
705 | do { | ||
706 | selection=rb->do_menu(&context_menu,&selection, parentvp, true); | ||
707 | switch(selection) { | ||
708 | |||
709 | case 0: | ||
710 | { | ||
711 | printcell_set_column_visible(col, !hide_col); | ||
712 | if (hide_col) | ||
713 | { | ||
714 | do | ||
715 | { | ||
716 | col = printcell_increment_column(1, true); | ||
717 | } while (col >= 0 && printcell_get_column_visibility(col) == 1); | ||
718 | pc_data->view_lastcol = col; | ||
719 | } | ||
720 | gConfig.hidecol_flags = printcell_get_column_visibility(-1); | ||
721 | break; | ||
722 | } | ||
723 | case 1: /* Exclude / Open */ | ||
724 | { | ||
725 | if (col == -1)/*Open*/ | ||
726 | { | ||
727 | char buf[MAX_PATH]; | ||
728 | browse_file(buf, sizeof(buf)); | ||
729 | if (rb->file_exists(buf)) | ||
730 | { | ||
731 | rb->strlcpy(pc_data->filename, buf, MAX_PATH); | ||
732 | if (pc_data->fd_cur >= 0) | ||
733 | rb->close(pc_data->fd_cur); | ||
734 | if (file_open(pc_data->filename, &pc_data->fd_cur) > 0) | ||
735 | items = file_load_entries(pc_data); | ||
736 | if (items >= 0) | ||
737 | synclist_set(0, items, 1, pc_data); | ||
738 | } | ||
739 | else | ||
740 | rb->splash(HZ *2, "Error Opening"); | ||
741 | |||
742 | return CANCEL_CONTEXT_MENU; | ||
743 | } | ||
744 | |||
745 | find_type = FIND_EXCLUDE; | ||
746 | } | ||
747 | /* fall-through */ | ||
748 | case 2: /* Include / Reload */ | ||
749 | { | ||
750 | if (col == -1) /*Reload*/ | ||
751 | { | ||
752 | if (pc_data->fd_cur >= 0) | ||
753 | rb->close(pc_data->fd_cur); | ||
754 | if (file_open(pc_data->filename, &pc_data->fd_cur) > 0) | ||
755 | items = file_load_entries(pc_data); | ||
756 | if (items >= 0) | ||
757 | rb->gui_synclist_set_nb_items(&lists, items); | ||
758 | return CANCEL_CONTEXT_MENU; | ||
759 | } | ||
760 | |||
761 | if (find_type == FIND_ALL) | ||
762 | find_type = FIND_INCLUDE; | ||
763 | /* fall-through */ | ||
764 | } | ||
765 | case 3: /*Custom Filter / Settings */ | ||
766 | { | ||
767 | if (col == -1)/*Settings*/ | ||
768 | return config_settings_menu(pc_data); | ||
769 | |||
770 | if (find_type == FIND_ALL) | ||
771 | find_type = FIND_CUSTOM; | ||
772 | items = filter_items(pc_data, find_type, col); | ||
773 | |||
774 | if (items >= 0) | ||
775 | rb->gui_synclist_set_nb_items(&lists, items); | ||
776 | break; | ||
777 | } | ||
778 | case 4: /*sep*/ | ||
779 | continue; | ||
780 | case 5: | ||
781 | return CANCEL_CONTEXT_MENU; | ||
782 | break; | ||
783 | case 6: /* Quit */ | ||
784 | return QUIT_CONTEXT_MENU; | ||
785 | break; | ||
786 | case MENU_ATTACHED_USB: | ||
787 | return PLUGIN_USB_CONNECTED; | ||
788 | default: | ||
789 | return PLUGIN_OK; | ||
790 | } | ||
791 | } while ( selection < 0 ); | ||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static void cleanup(void *parameter) | ||
796 | { | ||
797 | struct printcell_data_t *pc_data = (struct printcell_data_t*) parameter; | ||
798 | if (pc_data->fd_cur >= 0) | ||
799 | rb->close(pc_data->fd_cur); | ||
800 | pc_data->fd_cur = -1; | ||
801 | } | ||
802 | |||
803 | static void menu_action_printcell(int *action, int selected_item, bool* exit, struct gui_synclist *lists) | ||
804 | { | ||
805 | (void) exit; | ||
806 | struct printcell_data_t *pc_data = (struct printcell_data_t*) lists->data; | ||
807 | if (*action == ACTION_STD_OK) | ||
808 | { | ||
809 | if (selected_item < lists->nb_items) | ||
810 | { | ||
811 | pc_data->view_lastcol = printcell_increment_column(1, true); | ||
812 | *action = ACTION_NONE; | ||
813 | } | ||
814 | } | ||
815 | else if (*action == ACTION_STD_CANCEL) | ||
816 | { | ||
817 | pc_data->view_lastcol = printcell_increment_column(-1, true); | ||
818 | if (pc_data->view_lastcol != pc_data->view_columns - 1) | ||
819 | { | ||
820 | *action = ACTION_NONE; | ||
821 | } | ||
822 | } | ||
823 | else if (*action == ACTION_STD_CONTEXT) | ||
824 | { | ||
825 | int ctxret = scrobbler_context_menu(pc_data); | ||
826 | if (ctxret == QUIT_CONTEXT_MENU) | ||
827 | *exit = true; | ||
828 | } | ||
829 | } | ||
830 | |||
831 | int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists) | ||
832 | { | ||
833 | |||
834 | menu_action_printcell(action, selected_item, exit, lists); | ||
835 | |||
836 | if (rb->default_event_handler_ex(*action, cleanup, lists->data) == SYS_USB_CONNECTED) | ||
837 | { | ||
838 | *exit = true; | ||
839 | return PLUGIN_USB_CONNECTED; | ||
840 | } | ||
841 | return PLUGIN_OK; | ||
842 | } | ||
843 | |||
844 | static int count_max_columns(int items, char delimeter, | ||
845 | int expected_cols, struct printcell_data_t *pc_data) | ||
846 | { | ||
847 | int max_cols = 0; | ||
848 | int cols = 0; | ||
849 | char buf[PRINTCELL_MAXLINELEN]; | ||
850 | for (int i = 0; i < items; i++) | ||
851 | { | ||
852 | const char *txt = list_get_name_cb(i, pc_data, buf, sizeof(buf)); | ||
853 | while (*txt != '\0') | ||
854 | { | ||
855 | if (*txt == delimeter) | ||
856 | { | ||
857 | cols++; | ||
858 | if (cols == expected_cols) | ||
859 | { | ||
860 | max_cols = cols; | ||
861 | break; | ||
862 | } | ||
863 | } | ||
864 | txt++; | ||
865 | } | ||
866 | |||
867 | if(max_cols < expected_cols && i > 32) | ||
868 | break; | ||
869 | |||
870 | if (cols > max_cols) | ||
871 | max_cols = cols; | ||
872 | cols = 0; | ||
873 | } | ||
874 | return max_cols; | ||
875 | } | ||
876 | |||
877 | static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data) | ||
878 | { | ||
879 | if (items <= 0) | ||
880 | return; | ||
881 | if (selected_item < 0) | ||
882 | selected_item = 0; | ||
883 | |||
884 | rb->gui_synclist_init(&lists,list_get_name_cb, | ||
885 | pc_data, false, sel_size, NULL); | ||
886 | |||
887 | rb->gui_synclist_set_icon_callback(&lists, list_icon_cb); | ||
888 | rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); | ||
889 | rb->gui_synclist_set_nb_items(&lists,items); | ||
890 | rb->gui_synclist_select_item(&lists, selected_item); | ||
891 | |||
892 | struct printcell_settings pcs = {.cell_separator = gConfig.separator, | ||
893 | .title_delimeter = '$', | ||
894 | .text_delimeter = '\t', | ||
895 | .hidecol_flags = gConfig.hidecol_flags}; | ||
896 | |||
897 | pc_data->view_columns = printcell_set_columns(&lists, &pcs, | ||
898 | pc_data->header, Icon_Rockbox); | ||
899 | printcell_enable(true); | ||
900 | |||
901 | |||
902 | int max_cols = count_max_columns(items, pcs.text_delimeter, | ||
903 | SCROBBLER_MIN_COLUMNS, pc_data); | ||
904 | if (max_cols < SCROBBLER_MIN_COLUMNS) /* not a scrobbler file? */ | ||
905 | { | ||
906 | rb->gui_synclist_set_voice_callback(&lists, NULL); | ||
907 | pc_data->view_columns = printcell_set_columns(&lists, NULL, | ||
908 | "$*512$", Icon_Questionmark); | ||
909 | } | ||
910 | |||
911 | int curcol = printcell_get_column_selected(); | ||
912 | while (curcol >= 0) | ||
913 | curcol = printcell_increment_column(-1, false); | ||
914 | |||
915 | if (pc_data->view_lastcol >= pc_data->view_columns) | ||
916 | pc_data->view_lastcol = -1; | ||
917 | |||
918 | /* restore column position */ | ||
919 | while (pc_data->view_lastcol > -1 && curcol != pc_data->view_lastcol) | ||
920 | { | ||
921 | curcol = printcell_increment_column(1, true); | ||
922 | } | ||
923 | pc_data->view_lastcol = curcol; | ||
924 | list_voice_cb(0, pc_data); | ||
925 | } | ||
926 | |||
927 | static void pc_data_set_header(struct printcell_data_t *pc_data) | ||
928 | { | ||
929 | int col_w = gConfig.col_width; | ||
930 | rb->snprintf(pc_data->header, PRINTCELL_MAXLINELEN, | ||
931 | SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST), | ||
932 | col_w, rb->str(LANG_ID3_ALBUM), | ||
933 | col_w, rb->str(LANG_ID3_TITLE)); | ||
934 | } | ||
935 | |||
936 | enum plugin_status plugin_start(const void* parameter) | ||
937 | { | ||
938 | int ret = PLUGIN_OK; | ||
939 | int selected_item = -1; | ||
940 | int action; | ||
941 | int items = 0; | ||
942 | #if CONFIG_RTC | ||
943 | static char filename[MAX_PATH] = HOME_DIR "/.scrobbler.log"; | ||
944 | #else /* !CONFIG_RTC */ | ||
945 | static char filename[MAX_PATH] = HOME_DIR "/.scrobbler-timeless.log"; | ||
946 | #endif /* CONFIG_RTC */ | ||
947 | bool redraw = true; | ||
948 | bool exit = false; | ||
949 | |||
950 | static struct printcell_data_t printcell_data; | ||
951 | |||
952 | if (parameter) | ||
953 | { | ||
954 | rb->strlcpy(filename, (const char*)parameter, MAX_PATH); | ||
955 | filename[MAX_PATH - 1] = '\0'; | ||
956 | } | ||
957 | |||
958 | if (!rb->file_exists(filename)) | ||
959 | { | ||
960 | browse_file(filename, sizeof(filename)); | ||
961 | if (!rb->file_exists(filename)) | ||
962 | { | ||
963 | rb->splash(HZ, "No Scrobbler file Goodbye."); | ||
964 | return PLUGIN_ERROR; | ||
965 | } | ||
966 | |||
967 | } | ||
968 | |||
969 | config_set_defaults(); | ||
970 | if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0) | ||
971 | { | ||
972 | /* If the loading failed, save a new config file */ | ||
973 | config_set_defaults(); | ||
974 | configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); | ||
975 | |||
976 | rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS)); | ||
977 | } | ||
978 | |||
979 | rb->memset(&printcell_data, 0, sizeof (struct printcell_data_t)); | ||
980 | printcell_data.fd_cur = -1; | ||
981 | printcell_data.view_lastcol = -1; | ||
982 | |||
983 | if (rb->file_exists(filename)) | ||
984 | { | ||
985 | if (file_open(filename, &printcell_data.fd_cur) == 0) | ||
986 | printcell_data.fd_cur = -1; | ||
987 | else | ||
988 | { | ||
989 | size_t buf_size; | ||
990 | printcell_data.buf = rb->plugin_get_buffer(&buf_size); | ||
991 | printcell_data.buf_size = buf_size; | ||
992 | printcell_data.buf_used = 0; | ||
993 | printcell_data.filename = filename; | ||
994 | items = file_load_entries(&printcell_data); | ||
995 | } | ||
996 | } | ||
997 | int col_w = gConfig.col_width; | ||
998 | rb->snprintf(printcell_data.header, PRINTCELL_MAXLINELEN, | ||
999 | SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST), | ||
1000 | col_w, rb->str(LANG_ID3_ALBUM), | ||
1001 | col_w, rb->str(LANG_ID3_TITLE)); | ||
1002 | |||
1003 | if (!exit && items > 0) | ||
1004 | { | ||
1005 | synclist_set(0, items, 1, &printcell_data); | ||
1006 | rb->gui_synclist_draw(&lists); | ||
1007 | |||
1008 | while (!exit) | ||
1009 | { | ||
1010 | action = rb->get_action(CONTEXT_LIST, HZ / 10); | ||
1011 | |||
1012 | if (action == ACTION_NONE) | ||
1013 | { | ||
1014 | if (redraw) | ||
1015 | { | ||
1016 | action = ACTION_REDRAW; | ||
1017 | redraw = false; | ||
1018 | } | ||
1019 | } | ||
1020 | else | ||
1021 | redraw = true; | ||
1022 | |||
1023 | ret = menu_action_cb(&action, selected_item, &exit, &lists); | ||
1024 | if (rb->gui_synclist_do_button(&lists, &action)) | ||
1025 | continue; | ||
1026 | selected_item = rb->gui_synclist_get_sel_pos(&lists); | ||
1027 | |||
1028 | } | ||
1029 | } | ||
1030 | config_save(); | ||
1031 | cleanup(&printcell_data); | ||
1032 | return ret; | ||
1033 | } | ||
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 @@ | |||
22 | #include "plugin.h" | 22 | #include "plugin.h" |
23 | #include "lib/printcell_helper.h" | 23 | #include "lib/printcell_helper.h" |
24 | 24 | ||
25 | #ifndef PRINTCELL_MAX_COLUMNS | ||
26 | #define PRINTCELL_MAX_COLUMNS 16 | ||
27 | #endif | ||
28 | |||
29 | #define COLUMN_ENDLEN 3 | 25 | #define COLUMN_ENDLEN 3 |
30 | #define TITLE_FLAG 0xFF | 26 | #define TITLE_FLAG 0xFF |
31 | #define SELECTED_FLAG 0x1 | 27 | #define SELECTED_FLAG 0x1 |
@@ -37,48 +33,54 @@ | |||
37 | #endif | 33 | #endif |
38 | 34 | ||
39 | struct printcell_info_t { | 35 | struct printcell_info_t { |
40 | int offw[NB_SCREENS]; | 36 | struct gui_synclist *gui_list; /* list to display */ |
41 | int iconw[NB_SCREENS]; | 37 | int offw[NB_SCREENS]; /* padding between column boundries and text */ |
42 | int selcol_offw[NB_SCREENS]; | 38 | int iconw[NB_SCREENS]; /* width of an icon */ |
43 | int totalcolw[NB_SCREENS]; | 39 | int selcol_offw[NB_SCREENS]; /* offset width calculated for selected item */ |
44 | int firstcolxw[NB_SCREENS]; | 40 | int totalcolw[NB_SCREENS]; /* total width of all columns */ |
45 | uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; | 41 | int firstcolxw[NB_SCREENS]; /* first column x + width, save recalculating */ |
46 | int ncols; | 42 | int ncols; /* number of columns */ |
47 | int selcol; | 43 | int selcol; /* selected column (-1 to ncols-1) */ |
48 | int selcol_index; | 44 | uint32_t hidecol_flags; /*bits 0-31 set bit to 1 to hide a column (1<<col#) */ |
49 | char title[PRINTCELL_MAXLINELEN]; | 45 | uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; /* width of title text |
50 | bool separator; | 46 | or MIN(or user defined width / screen width) */ |
47 | char title[PRINTCELL_MAXLINELEN]; /* title buffer */ | ||
48 | char titlesep; /* character that separates title column items (ex '$') */ | ||
49 | char colsep; /* character that separates text column items (ex ',') */ | ||
50 | bool separator; /* draw grid */ | ||
51 | }; | 51 | }; |
52 | |||
52 | static struct printcell_info_t printcell; | 53 | static struct printcell_info_t printcell; |
53 | 54 | ||
54 | static void parse_dsptext(int ncols, const char *dsp_text, char* buffer, uint16_t *sidx) | 55 | static void parse_dsptext(char splitchr, int ncols, const char *dsp_text, |
56 | char* buffer, size_t bufsz, uint16_t *sidx) | ||
55 | { | 57 | { |
56 | /*Internal function loads sidx with split offsets indexing | 58 | /*Internal function loads sidx with split offsets indexing |
57 | the buffer of null terminated strings, splits on '$' | 59 | the buffer of null terminated strings, splits on 'splitchr' |
58 | _assumptions_: | 60 | _assumptions_: |
59 | dsp_text[len - 1] = \0, | 61 | dsp_text[len - 1] = \0, |
60 | buffer[PRINTCELL_MAXLINELEN], | ||
61 | sidx[PRINTCELL_MAX_COLUMNS] | 62 | sidx[PRINTCELL_MAX_COLUMNS] |
62 | */ | 63 | */ |
63 | int i = 0; | 64 | int i = 0; |
64 | int j = 0; | 65 | size_t j = 0; |
65 | int ch = '$'; /* first column $ is optional */ | 66 | int ch = splitchr; /* first column $ is optional */ |
66 | if (*dsp_text == '$') | 67 | if (*dsp_text == splitchr) |
67 | dsp_text++; | 68 | dsp_text++; |
68 | /* add null to the start of the text buffer */ | 69 | /* add null to the start of the text buffer */ |
69 | buffer[j++] = '\0'; | 70 | buffer[j++] = '\0'; |
70 | do | 71 | do |
71 | { | 72 | { |
72 | if (ch == '$') | 73 | if (ch == splitchr) |
73 | { | 74 | { |
74 | sidx[i] = j; | 75 | sidx[i] = j; /* save start index and copy next column to the buffer */ |
75 | if (*dsp_text == '$') /* $$ escaped user must want to display $*/ | 76 | while (*dsp_text != '\0' && *dsp_text != splitchr && j < (bufsz - 1)) |
76 | buffer[j++] = *dsp_text++; | 77 | { |
77 | while (*dsp_text != '\0' && *dsp_text != '$' && j < PRINTCELL_MAXLINELEN - 1) | ||
78 | buffer[j++] = *dsp_text++; | 78 | buffer[j++] = *dsp_text++; |
79 | } | ||
79 | buffer[j++] = '\0'; | 80 | buffer[j++] = '\0'; |
81 | |||
80 | i++; | 82 | i++; |
81 | if (i >= ncols || j >= (PRINTCELL_MAXLINELEN - 1)) | 83 | if (i >= ncols || j >= (bufsz - 1)) |
82 | break; | 84 | break; |
83 | } | 85 | } |
84 | ch = *dsp_text++; | 86 | ch = *dsp_text++; |
@@ -146,23 +148,39 @@ static inline void set_cell_width(struct viewport *vp, int max_w, int new_w) | |||
146 | static inline int printcells(struct screen *display, char* buffer, | 148 | static inline int printcells(struct screen *display, char* buffer, |
147 | uint16_t *sidx, struct line_desc *linedes, | 149 | uint16_t *sidx, struct line_desc *linedes, |
148 | struct viewport *vp, int vp_w, int separator, | 150 | struct viewport *vp, int vp_w, int separator, |
149 | int x, int y, int offw, int selected_flag, bool scroll) | 151 | int x, int y, int offw, int selected_flag, int last_col, |
152 | bool scroll, bool is_title) | ||
150 | { | 153 | { |
151 | /* Internal function prints remaining cells */ | 154 | /* Internal function prints remaining cells */ |
152 | int text_offset = offw + offw; | 155 | int text_offset = offw + offw; |
153 | int ncols = printcell.ncols; | ||
154 | int screen = display->screen_type; | 156 | int screen = display->screen_type; |
155 | int height = linedes->height; | 157 | int height = linedes->height; |
156 | int selsep = (selected_flag == 0) ? 0: separator; | 158 | int selsep = (selected_flag == 0) ? 0: separator; |
157 | uint16_t *screencolwidth = printcell.colw[screen]; | 159 | uint16_t *screencolwidth = printcell.colw[screen]; |
158 | 160 | ||
159 | for(int i = 1; i < ncols; i++) | 161 | for(int i = 1; i <= last_col; i++) |
160 | { | 162 | { |
161 | int ny = y; | 163 | int ny = y; |
162 | int nw = screencolwidth[i] + text_offset; | 164 | int nw = screencolwidth[i] + text_offset; |
165 | int offx = 0; | ||
166 | |||
167 | if (i == last_col || x + nw >= vp_w - offw + 1) | ||
168 | { /* not enough space for next column use up excess */ | ||
169 | if (nw < (vp_w - x)) | ||
170 | { | ||
171 | if (is_title) | ||
172 | offx = ((vp_w - x) - nw) / 2; | ||
173 | nw = vp_w - x; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i)) | ||
178 | nw = 0; | ||
179 | |||
163 | int nx = x + nw; | 180 | int nx = x + nw; |
164 | char *buftext; | 181 | char *buftext; |
165 | if (nx > 0 && x < vp_w) | 182 | |
183 | if (nx > 0 && nw > offw && x < vp_w) | ||
166 | { | 184 | { |
167 | set_cell_width(vp, vp_w, nx); | 185 | set_cell_width(vp, vp_w, nx); |
168 | 186 | ||
@@ -173,11 +191,15 @@ static inline int printcells(struct screen *display, char* buffer, | |||
173 | } | 191 | } |
174 | else | 192 | else |
175 | { | 193 | { |
194 | if (vp_w < x + text_offset) | ||
195 | { | ||
196 | scroll = false; | ||
197 | } | ||
176 | linedes->scroll = scroll; | 198 | linedes->scroll = scroll; |
177 | linedes->separator_height = separator; | 199 | linedes->separator_height = separator; |
178 | } | 200 | } |
179 | buftext = &buffer[sidx[i]]; | 201 | buftext = &buffer[sidx[i]]; |
180 | display->put_line(x + offw, ny, linedes, "$t", buftext); | 202 | display->put_line(x + offw + offx, ny, linedes, "$t", buftext); |
181 | vp->width += COLUMN_ENDLEN + 1; | 203 | vp->width += COLUMN_ENDLEN + 1; |
182 | draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height); | 204 | draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height); |
183 | } | 205 | } |
@@ -200,6 +222,10 @@ static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth | |||
200 | for (int i = printcell.selcol - 1; i >= 0; i--) | 222 | for (int i = printcell.selcol - 1; i >= 0; i--) |
201 | { | 223 | { |
202 | int cw = screencolwidth[i] + text_offset; | 224 | int cw = screencolwidth[i] + text_offset; |
225 | |||
226 | if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i)) | ||
227 | cw = 0; | ||
228 | |||
203 | if (i == 0) | 229 | if (i == 0) |
204 | cw += screenicnwidth; | 230 | cw += screenicnwidth; |
205 | if (offset > 0 || cw > maxw) | 231 | if (offset > 0 || cw > maxw) |
@@ -257,15 +283,30 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) | |||
257 | separator = 1; | 283 | separator = 1; |
258 | 284 | ||
259 | int nx = x; | 285 | int nx = x; |
286 | int last_col = printcell.ncols - 1; | ||
287 | int hidden_w = 0; | ||
260 | int nw, colxw; | 288 | int nw, colxw; |
261 | 289 | char *buftext; | |
262 | printcell_buffer[0] = '\0'; | 290 | printcell_buffer[0] = '\0'; |
263 | parse_dsptext(printcell.ncols, dsp_text, printcell_buffer, sidx); | 291 | |
264 | char *buftext = &printcell_buffer[sidx[0]]; | ||
265 | uint16_t *screencolwidth = printcell.colw[screen]; | 292 | uint16_t *screencolwidth = printcell.colw[screen]; |
293 | if (printcell.hidecol_flags > 0) | ||
294 | { | ||
295 | for (int i = 0; i < printcell.ncols; i++) | ||
296 | { | ||
297 | if (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i)) | ||
298 | last_col = i; | ||
299 | else | ||
300 | hidden_w += (screencolwidth[i] + text_offset); | ||
301 | } | ||
302 | } | ||
266 | 303 | ||
267 | if (is_title) | 304 | if (is_title) |
268 | { | 305 | { |
306 | parse_dsptext(printcell.titlesep, printcell.ncols, dsp_text, | ||
307 | printcell_buffer, sizeof(printcell_buffer), sidx); | ||
308 | |||
309 | buftext = &printcell_buffer[sidx[0]]; /* set to first column text */ | ||
269 | int sbwidth = 0; | 310 | int sbwidth = 0; |
270 | if (rb->global_settings->scrollbar == SCROLLBAR_LEFT) | 311 | if (rb->global_settings->scrollbar == SCROLLBAR_LEFT) |
271 | sbwidth = rb->global_settings->scrollbar_width; | 312 | sbwidth = rb->global_settings->scrollbar_width; |
@@ -278,6 +319,9 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) | |||
278 | nx -= printcell.selcol_offw[screen]; | 319 | nx -= printcell.selcol_offw[screen]; |
279 | 320 | ||
280 | nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; | 321 | nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; |
322 | |||
323 | if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, 0)) | ||
324 | nw = printcell.iconw[screen] - 1; | ||
281 | nw += sbwidth; | 325 | nw += sbwidth; |
282 | 326 | ||
283 | colxw = nx + nw; | 327 | colxw = nx + nw; |
@@ -301,20 +345,21 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) | |||
301 | } | 345 | } |
302 | else | 346 | else |
303 | { | 347 | { |
348 | parse_dsptext(printcell.colsep, printcell.ncols, dsp_text, | ||
349 | printcell_buffer, sizeof(printcell_buffer), sidx); | ||
350 | |||
351 | buftext = &printcell_buffer[sidx[0]]; /* set to first column text */ | ||
304 | int cursor = Icon_NOICON; | 352 | int cursor = Icon_NOICON; |
305 | nx -= printcell.selcol_offw[screen]; | 353 | nx -= printcell.selcol_offw[screen]; |
306 | 354 | ||
307 | if (selected_flag & SELECTED_FLAG) | 355 | if (selected_flag & SELECTED_FLAG) |
308 | { | 356 | { |
309 | if (printcell.selcol >= 0) | ||
310 | printcell.selcol_index = sidx[printcell.selcol]; /* save the item offset*/ | ||
311 | else | ||
312 | printcell.selcol_index = -1; | ||
313 | |||
314 | cursor = Icon_Cursor; | 357 | cursor = Icon_Cursor; |
315 | /* limit length of selection if columns don't reach end */ | 358 | /* limit length of selection if columns don't reach end */ |
316 | int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen]; | 359 | int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen]; |
317 | maxw += text_offset * printcell.ncols; | 360 | maxw += text_offset * printcell.ncols; |
361 | maxw -= hidden_w; | ||
362 | |||
318 | if (vp_w > maxw) | 363 | if (vp_w > maxw) |
319 | vp->width = maxw; | 364 | vp->width = maxw; |
320 | /* display a blank line first to draw selector across all cells */ | 365 | /* 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) | |||
364 | nx += nw; | 409 | nx += nw; |
365 | /* display remaining cells */ | 410 | /* display remaining cells */ |
366 | printcells(display, printcell_buffer, sidx, linedes, vp, vp_w, separator, | 411 | printcells(display, printcell_buffer, sidx, linedes, vp, vp_w, separator, |
367 | nx, y, col_offset_width, selected_flag, scroll_items); | 412 | nx, y, col_offset_width, selected_flag, last_col, scroll_items, is_title); |
368 | 413 | ||
369 | /* draw a line at the bottom of the list */ | 414 | /* draw a line at the bottom of the list */ |
370 | if (separator > 0 && line == list->nb_items - 1) | 415 | if (separator > 0 && line == list->nb_items - 1) |
@@ -377,14 +422,17 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) | |||
377 | vp->width = vp_w; | 422 | vp->width = vp_w; |
378 | } | 423 | } |
379 | 424 | ||
380 | void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator) | 425 | void printcell_enable(bool enable) |
381 | { | 426 | { |
382 | printcell.separator = separator; | 427 | if (!printcell.gui_list) |
428 | return; | ||
429 | struct gui_synclist *gui_list = printcell.gui_list; | ||
383 | #ifdef HAVE_LCD_COLOR | 430 | #ifdef HAVE_LCD_COLOR |
384 | static int list_sep_color = INT_MIN; | 431 | static int list_sep_color = INT_MIN; |
385 | if (enable) | 432 | if (enable) |
386 | { | 433 | { |
387 | list_sep_color = rb->global_settings->list_separator_color; | 434 | if (list_sep_color == INT_MIN) |
435 | list_sep_color = rb->global_settings->list_separator_color; | ||
388 | rb->global_settings->list_separator_color = rb->global_settings->fg_color; | 436 | rb->global_settings->list_separator_color = rb->global_settings->fg_color; |
389 | gui_list->callback_draw_item = printcell_listdraw_fn; | 437 | gui_list->callback_draw_item = printcell_listdraw_fn; |
390 | } | 438 | } |
@@ -404,8 +452,11 @@ void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator | |||
404 | 452 | ||
405 | } | 453 | } |
406 | 454 | ||
407 | int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap) | 455 | int printcell_increment_column(int increment, bool wrap) |
408 | { | 456 | { |
457 | if (!printcell.gui_list) | ||
458 | return -1; | ||
459 | struct gui_synclist *gui_list = printcell.gui_list; | ||
409 | int item = printcell.selcol + increment; | 460 | int item = printcell.selcol + increment; |
410 | int imin = -1; | 461 | int imin = -1; |
411 | int imax = printcell.ncols - 1; | 462 | int imax = printcell.ncols - 1; |
@@ -425,36 +476,94 @@ int printcell_increment_column(struct gui_synclist *gui_list, int increment, boo | |||
425 | FOR_NB_SCREENS(n) /* offset needs recalculated */ | 476 | FOR_NB_SCREENS(n) /* offset needs recalculated */ |
426 | printcell.selcol_offw[n] = 0; | 477 | printcell.selcol_offw[n] = 0; |
427 | printcell.selcol = item; | 478 | printcell.selcol = item; |
428 | printcell.selcol_index = 0; | 479 | |
429 | rb->gui_synclist_draw(gui_list); | 480 | rb->gui_synclist_draw(gui_list); |
481 | rb->gui_synclist_speak_item(gui_list); | ||
430 | } | 482 | } |
431 | return item; | 483 | return item; |
432 | } | 484 | } |
433 | 485 | ||
486 | int printcell_get_column_selected(void) | ||
487 | { | ||
488 | if (!printcell.gui_list) | ||
489 | return -1; | ||
490 | return printcell.selcol; | ||
491 | } | ||
492 | |||
493 | uint32_t printcell_get_column_visibility(int col) | ||
494 | { | ||
495 | if (col >= 0) | ||
496 | return (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, col) ? 0:1); | ||
497 | else /* return flag of all columns */ | ||
498 | return printcell.hidecol_flags; | ||
499 | } | ||
500 | |||
501 | void printcell_set_column_visible(int col, bool visible) | ||
502 | { | ||
503 | /* visible columns have 0 for the column bit hidden columns have the bit set */ | ||
504 | if (col >= 0) | ||
505 | { | ||
506 | if (visible) | ||
507 | printcell.hidecol_flags &= ~(PRINTCELL_COLUMN_FLAG(col)); | ||
508 | else | ||
509 | printcell.hidecol_flags |= PRINTCELL_COLUMN_FLAG(col); | ||
510 | } | ||
511 | else | ||
512 | { | ||
513 | if (visible) /* set to everything visible */ | ||
514 | printcell.hidecol_flags = 0; | ||
515 | else /* set to everything hidden */ | ||
516 | printcell.hidecol_flags = ((uint32_t)-1); | ||
517 | } | ||
518 | } | ||
519 | |||
434 | int printcell_set_columns(struct gui_synclist *gui_list, | 520 | int printcell_set_columns(struct gui_synclist *gui_list, |
435 | char * title, enum themable_icons icon) | 521 | struct printcell_settings * pcs, |
522 | char * title, enum themable_icons icon) | ||
436 | { | 523 | { |
524 | |||
437 | if (title == NULL) | 525 | if (title == NULL) |
438 | title = "$PRINTCELL NOT SETUP"; | 526 | title = "$PRINTCELL NOT SETUP"; |
527 | |||
528 | if (pcs == NULL) /* DEFAULTS */ | ||
529 | { | ||
530 | #if LCD_DEPTH > 1 | ||
531 | /* If line sep is set to automatic then outline cells */ | ||
532 | bool sep = (rb->global_settings->list_separator_height < 0); | ||
533 | #else | ||
534 | bool sep = (rb->global_settings->cursor_style == 0); | ||
535 | #endif | ||
536 | pcs = &(struct printcell_settings){ .cell_separator = sep, | ||
537 | .title_delimeter = '$', | ||
538 | .text_delimeter = '$', | ||
539 | .hidecol_flags = 0}; | ||
540 | } | ||
541 | |||
439 | uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */ | 542 | uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */ |
440 | int width, height, user_minwidth; | 543 | int width, height, user_minwidth; |
441 | int i = 0; | 544 | int i = 0; |
442 | int j = 0; | 545 | size_t j = 0; |
443 | int ch = '$'; /* first column $ is optional */ | ||
444 | |||
445 | rb->memset(&printcell, 0, sizeof(struct printcell_info_t)); | 546 | rb->memset(&printcell, 0, sizeof(struct printcell_info_t)); |
446 | 547 | ||
548 | printcell.gui_list = gui_list; | ||
549 | printcell.separator = pcs->cell_separator; | ||
550 | printcell.titlesep = pcs->title_delimeter; | ||
551 | printcell.colsep = pcs->text_delimeter; | ||
552 | printcell.hidecol_flags = pcs->hidecol_flags; | ||
553 | |||
554 | int ch = printcell.titlesep; /* first column $ is optional */ | ||
555 | |||
447 | FOR_NB_SCREENS(n) | 556 | FOR_NB_SCREENS(n) |
448 | { | 557 | { |
449 | rb->screens[n]->getstringsize("W", &width, &height); | 558 | rb->screens[n]->getstringsize("W", &width, &height); |
450 | printcell.offw[n] = width; /* set column text offset */ | 559 | printcell.offw[n] = width; /* set column text offset */ |
451 | } | 560 | } |
452 | 561 | ||
453 | if (*title == '$') | 562 | if (*title == printcell.titlesep) |
454 | title++; | 563 | title++; |
455 | do | 564 | do |
456 | { | 565 | { |
457 | if (ch == '$') | 566 | if (ch == printcell.titlesep) |
458 | { | 567 | { |
459 | printcell.title[j++] = ch; | 568 | printcell.title[j++] = ch; |
460 | user_minwidth = 0; | 569 | user_minwidth = 0; |
@@ -466,7 +575,7 @@ int printcell_set_columns(struct gui_synclist *gui_list, | |||
466 | user_minwidth = 10*user_minwidth + *title - '0'; | 575 | user_minwidth = 10*user_minwidth + *title - '0'; |
467 | title++; | 576 | title++; |
468 | } | 577 | } |
469 | if (*title != '$') /* user forgot $ or wants to display '*' */ | 578 | if (*title != printcell.titlesep) /* user forgot titlesep or wants to display '*' */ |
470 | { | 579 | { |
471 | title = dspst; | 580 | title = dspst; |
472 | user_minwidth = 0; | 581 | user_minwidth = 0; |
@@ -476,17 +585,25 @@ int printcell_set_columns(struct gui_synclist *gui_list, | |||
476 | } | 585 | } |
477 | 586 | ||
478 | sidx[i] = j; | 587 | sidx[i] = j; |
479 | if (*title == '$') /* $$ escaped user must want to display $*/ | ||
480 | printcell.title[j++] = *title++; | ||
481 | 588 | ||
482 | while (*title != '\0' && *title != '$' && j < PRINTCELL_MAXLINELEN - 1) | 589 | while (*title != '\0' |
590 | && *title != printcell.titlesep | ||
591 | && j < PRINTCELL_MAXLINELEN - 1) | ||
592 | { | ||
483 | printcell.title[j++] = *title++; | 593 | printcell.title[j++] = *title++; |
594 | } | ||
484 | 595 | ||
485 | FOR_NB_SCREENS(n) | 596 | FOR_NB_SCREENS(n) |
486 | { | 597 | { |
487 | rb->screens[n]->getstringsize(&printcell.title[sidx[i]], &width, &height); | 598 | rb->screens[n]->getstringsize(&printcell.title[sidx[i]], |
599 | &width, &height); | ||
600 | |||
488 | if (width < user_minwidth) | 601 | if (width < user_minwidth) |
489 | width = user_minwidth; | 602 | width = user_minwidth; |
603 | |||
604 | if (width > LCD_WIDTH) | ||
605 | width = LCD_WIDTH; | ||
606 | |||
490 | printcell.colw[n][i] = width; | 607 | printcell.colw[n][i] = width; |
491 | printcell.totalcolw[n] += width; | 608 | printcell.totalcolw[n] += width; |
492 | } | 609 | } |
@@ -498,37 +615,67 @@ int printcell_set_columns(struct gui_synclist *gui_list, | |||
498 | printcell.ncols = i; | 615 | printcell.ncols = i; |
499 | printcell.title[j] = '\0'; | 616 | printcell.title[j] = '\0'; |
500 | printcell.selcol = -1; | 617 | printcell.selcol = -1; |
501 | printcell.selcol_index = 0; | ||
502 | 618 | ||
503 | rb->gui_synclist_set_title(gui_list, printcell.title, icon); | 619 | rb->gui_synclist_set_title(gui_list, printcell.title, icon); |
504 | return printcell.ncols; | 620 | return printcell.ncols; |
505 | } | 621 | } |
506 | 622 | ||
507 | char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz) | 623 | char *printcell_get_title_text(int selcol, char *buf, size_t bufsz) |
508 | { | 624 | { |
509 | int selected = gui_list->selected_item; | 625 | /* note offsets are calculated everytime this function is called |
510 | int index = printcell.selcol_index - 1; | 626 | * shouldn't be used in hot code paths */ |
627 | int index = 0; | ||
628 | buf[0] = '\0'; | ||
629 | if (selcol < 0) /* return entire string incld col formatting '$'*/ | ||
630 | return printcell.title; | ||
631 | |||
632 | if (selcol < printcell.ncols) | ||
633 | { | ||
634 | uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/ | ||
635 | parse_dsptext(printcell.titlesep, selcol + 1, printcell.title, buf, bufsz, sidx); | ||
636 | index = sidx[selcol]; | ||
637 | } | ||
638 | return &buf[index]; | ||
639 | } | ||
511 | 640 | ||
512 | if (index < 0) | 641 | char *printcell_get_column_text(int selcol, char *buf, size_t bufsz) |
513 | index = 0; | 642 | { |
643 | int index = 0; | ||
514 | char *bpos; | 644 | char *bpos; |
645 | struct gui_synclist *gui_list = printcell.gui_list; | ||
515 | 646 | ||
516 | if (gui_list->callback_get_item_name(selected, gui_list->data, buf, bufsz) == buf) | 647 | if (gui_list && gui_list->callback_draw_item == printcell_listdraw_fn) |
517 | { | 648 | { |
518 | bpos = &buf[index]; | 649 | int col = selcol; |
519 | if (printcell.selcol < 0) /* return entire string incld col formatting '$'*/ | 650 | int item = gui_list->selected_item; |
520 | return bpos; | 651 | void *data = gui_list->data; |
521 | while(bpos < &buf[bufsz - 1]) | 652 | |
653 | if (col < printcell.ncols | ||
654 | && gui_list->callback_get_item_name(item, data, buf, bufsz) == buf) | ||
522 | { | 655 | { |
523 | if (*bpos == '$' || *bpos == '\0') | 656 | bpos = buf; |
524 | goto success; | 657 | if (col < 0) /* return entire string incld col formatting '$'*/ |
525 | bpos++; | 658 | { |
659 | return bpos; | ||
660 | } | ||
661 | bpos++; /* Skip sep/NULL */ | ||
662 | |||
663 | while(bpos < &buf[bufsz - 1]) | ||
664 | { | ||
665 | if (*bpos == printcell.colsep || *bpos == '\0') | ||
666 | { | ||
667 | if (col-- == 0) | ||
668 | goto success; | ||
669 | index = bpos - buf + 1; /* Skip sep/NULL */ | ||
670 | } | ||
671 | bpos++; | ||
672 | } | ||
526 | } | 673 | } |
527 | } | 674 | } |
528 | /*failure*/ | 675 | /*failure*/ |
529 | bpos = buf; | 676 | bpos = buf; |
530 | index = 0; | 677 | index = 0; |
531 | success: | 678 | success: |
532 | *bpos = '\0'; | 679 | *bpos = '\0'; |
533 | return &buf[index]; | 680 | return &buf[index]; |
534 | } | 681 | } |
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 @@ | |||
21 | 21 | ||
22 | #ifndef _PRINTCELL_LIST_H_ | 22 | #ifndef _PRINTCELL_LIST_H_ |
23 | #define _PRINTCELL_LIST_H_ | 23 | #define _PRINTCELL_LIST_H_ |
24 | #ifndef PRINTCELL_MAX_COLUMNS | ||
25 | #define PRINTCELL_MAX_COLUMNS 16 /* Max 32 (hidecol_flags)*/ | ||
26 | #endif | ||
27 | |||
24 | #define PRINTCELL_MAXLINELEN MAX_PATH | 28 | #define PRINTCELL_MAXLINELEN MAX_PATH |
29 | #define PC_COL_FLAG(col) ((uint32_t)(col >= 0 \ | ||
30 | && col < PRINTCELL_MAX_COLUMNS) ? 1u<<col : -1u) | ||
31 | |||
32 | #define PRINTCELL_COLUMN_IS_VISIBLE(flag, col) ((flag & PC_COL_FLAG(col)) == 0) | ||
33 | #define PRINTCELL_COLUMN_FLAG(col) (PC_COL_FLAG(col)) | ||
25 | 34 | ||
26 | /* sets the printcell function enabled */ | 35 | struct printcell_settings |
27 | void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator); | 36 | { |
37 | bool cell_separator; | ||
38 | char title_delimeter; | ||
39 | char text_delimeter; | ||
40 | uint32_t hidecol_flags; | ||
41 | }; | ||
28 | 42 | ||
29 | /* sets title and calculates cell widths each column is identified by '$' character | 43 | /* Printcell initialization - Sets title and calculates cell widths |
30 | ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$ | 44 | * by default each column is identified by '$' character |
31 | ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3 | 45 | * ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$ |
32 | returns number of columns | 46 | * ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3 |
47 | * supplying struct printcell_settings pcs allows changing default settings | ||
48 | * supply NULL to use defaults | ||
49 | * | ||
50 | * Returns number of columns | ||
33 | */ | 51 | */ |
34 | int printcell_set_columns(struct gui_synclist *gui_list, | 52 | int printcell_set_columns(struct gui_synclist *gui_list, |
35 | char * title, enum themable_icons icon); | 53 | struct printcell_settings * pcs, |
54 | char * title, enum themable_icons icon); | ||
55 | |||
56 | /* Sets the printcell function enabled (use after initializing with set_column) | ||
57 | * Note you should call printcell_enable(false) if the list might be reused */ | ||
58 | void printcell_enable(bool enable); | ||
36 | 59 | ||
37 | /* increments the current selected column negative increment is allowed | 60 | /* Increments the current selected column negative increment is allowed |
38 | returns the selected column | 61 | returns the selected column |
39 | range: -1(no selection) to ncols - 1 */ | 62 | range: -1(no selection) to ncols - 1 */ |
40 | int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap); | 63 | int printcell_increment_column(int increment, bool wrap); |
64 | |||
65 | /* Return index of the currently selected column (-1 to ncols - 1) */ | ||
66 | int printcell_get_column_selected(void); | ||
67 | |||
68 | /* Return the text of currently selected column buffer should be sized | ||
69 | * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */ | ||
70 | char *printcell_get_column_text(int selcol, char *buf, size_t bufsz); | ||
41 | 71 | ||
42 | /* return the text of currently selected column buffer should be sized | 72 | /* Return the text of currently selected column title should be sized |
43 | * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */ | 73 | * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */ |
44 | char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz); | 74 | char *printcell_get_title_text(int selcol, char *buf, size_t bufsz); |
75 | |||
76 | |||
77 | /* Hide or show a specified column - supply col = -1 to affect all columns */ | ||
78 | void printcell_set_column_visible(int col, bool visible); | ||
79 | |||
80 | /* Return visibility of a specified column | ||
81 | * returns (1 visible or 0 hidden) | ||
82 | * if supply col == -1 a flag with visibility of all columns will be returned | ||
83 | * NOTE: flag denotes a hidden column by a 1 in the column bit (1 << col#) | ||
84 | * PRINTCELL_COLUMN_IS_VISIBLE(flag,col) macro will convert to bool | ||
85 | */ | ||
86 | uint32_t printcell_get_column_visibility(int col); | ||
45 | #endif /*_PRINTCELL_LIST_H_*/ | 87 | #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) | |||
335 | rb->talk_spell(name, true); | 335 | rb->talk_spell(name, true); |
336 | } | 336 | } |
337 | } | 337 | } |
338 | else if (data == MENU_ID(M_TESTPUT)) | ||
339 | { | ||
340 | char buf[64]; | ||
341 | const char* name = printcell_get_column_text(printcell_get_column_selected(), | ||
342 | buf, sizeof(buf)); | ||
343 | long id = P2ID((const unsigned char *)name); | ||
344 | if(id>=0) | ||
345 | rb->talk_id(id, true); | ||
346 | else | ||
347 | rb->talk_spell(name, true); | ||
348 | } | ||
338 | else | 349 | else |
339 | { | 350 | { |
340 | char buf[64]; | 351 | char buf[64]; |
@@ -354,12 +365,12 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli | |||
354 | { | 365 | { |
355 | if (*action == ACTION_STD_OK) | 366 | if (*action == ACTION_STD_OK) |
356 | { | 367 | { |
357 | printcell_increment_column(lists, 1, true); | 368 | printcell_increment_column(1, true); |
358 | *action = ACTION_NONE; | 369 | *action = ACTION_NONE; |
359 | } | 370 | } |
360 | else if (*action == ACTION_STD_CANCEL) | 371 | else if (*action == ACTION_STD_CANCEL) |
361 | { | 372 | { |
362 | if (printcell_increment_column(lists, -1, true) != testput_cols - 1) | 373 | if (printcell_increment_column(-1, true) != testput_cols - 1) |
363 | { | 374 | { |
364 | *action = ACTION_NONE; | 375 | *action = ACTION_NONE; |
365 | } | 376 | } |
@@ -368,7 +379,8 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli | |||
368 | { | 379 | { |
369 | char buf[PRINTCELL_MAXLINELEN]; | 380 | char buf[PRINTCELL_MAXLINELEN]; |
370 | char* bufp = buf; | 381 | char* bufp = buf; |
371 | bufp = printcell_get_selected_column_text(lists, bufp, PRINTCELL_MAXLINELEN); | 382 | int selcol = printcell_get_column_selected(); |
383 | bufp = printcell_get_column_text(selcol, bufp, PRINTCELL_MAXLINELEN); | ||
372 | rb->splashf(HZ * 2, "Item: %s", bufp); | 384 | rb->splashf(HZ * 2, "Item: %s", bufp); |
373 | } | 385 | } |
374 | } | 386 | } |
@@ -429,18 +441,12 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli | |||
429 | if (cur->menuid == MENU_ID(M_TESTPUT)) | 441 | if (cur->menuid == MENU_ID(M_TESTPUT)) |
430 | { | 442 | { |
431 | synclist_set(cur->menuid, 0, cur->items, 1); | 443 | synclist_set(cur->menuid, 0, cur->items, 1); |
432 | #if LCD_DEPTH > 1 | 444 | printcell_enable(true); |
433 | /* If line sep is set to automatic then outline cells */ | ||
434 | bool showlinesep = (rb->global_settings->list_separator_height < 0); | ||
435 | #else | ||
436 | bool showlinesep = (rb->global_settings->cursor_style == 0); | ||
437 | #endif | ||
438 | printcell_enable(lists, true, showlinesep); | ||
439 | //lists->callback_draw_item = test_listdraw_fn; | 445 | //lists->callback_draw_item = test_listdraw_fn; |
440 | } | 446 | } |
441 | else | 447 | else |
442 | { | 448 | { |
443 | printcell_enable(lists, false, false); | 449 | printcell_enable(false); |
444 | synclist_set(cur->menuid, 1, cur->items, 1); | 450 | synclist_set(cur->menuid, 1, cur->items, 1); |
445 | } | 451 | } |
446 | rb->gui_synclist_draw(lists); | 452 | rb->gui_synclist_draw(lists); |
@@ -473,7 +479,7 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli | |||
473 | if (lists->data == MENU_ID(M_TESTPUT)) | 479 | if (lists->data == MENU_ID(M_TESTPUT)) |
474 | { | 480 | { |
475 | //lists->callback_draw_item = NULL; | 481 | //lists->callback_draw_item = NULL; |
476 | printcell_enable(lists, false, false); | 482 | printcell_enable(false); |
477 | } | 483 | } |
478 | if (lists->data != MENU_ID(M_ROOT)) | 484 | if (lists->data != MENU_ID(M_ROOT)) |
479 | { | 485 | { |
@@ -505,7 +511,8 @@ static void synclist_set(char* menu_id, int selected_item, int items, int sel_si | |||
505 | menu_id, false, sel_size, NULL); | 511 | menu_id, false, sel_size, NULL); |
506 | if (menu_id == MENU_ID(M_TESTPUT)) | 512 | if (menu_id == MENU_ID(M_TESTPUT)) |
507 | { | 513 | { |
508 | testput_cols = printcell_set_columns(&lists, TESTPUT_HEADER, Icon_Rockbox); | 514 | testput_cols = printcell_set_columns(&lists, NULL, |
515 | TESTPUT_HEADER, Icon_Rockbox); | ||
509 | } | 516 | } |
510 | else | 517 | else |
511 | { | 518 | { |
@@ -562,6 +569,6 @@ enum plugin_status plugin_start(const void* parameter) | |||
562 | selected_item = rb->gui_synclist_get_sel_pos(&lists); | 569 | selected_item = rb->gui_synclist_get_sel_pos(&lists); |
563 | } | 570 | } |
564 | } | 571 | } |
565 | 572 | printcell_enable(false); | |
566 | return ret; | 573 | return ret; |
567 | } | 574 | } |
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 | |||
105 | lnk,viewers/windows_lnk,- | 105 | lnk,viewers/windows_lnk,- |
106 | #ifdef HAVE_TAGCACHE | 106 | #ifdef HAVE_TAGCACHE |
107 | *,demos/pictureflow,- | 107 | *,demos/pictureflow,- |
108 | log,viewers/lastfm_scrobbler_viewer,4 | ||
108 | #endif | 109 | #endif |