diff options
Diffstat (limited to 'apps/plugins/lib/printcell_helper.c')
-rw-r--r-- | apps/plugins/lib/printcell_helper.c | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/apps/plugins/lib/printcell_helper.c b/apps/plugins/lib/printcell_helper.c new file mode 100644 index 0000000000..27a4b0fc95 --- /dev/null +++ b/apps/plugins/lib/printcell_helper.c | |||
@@ -0,0 +1,493 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 by 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 | /* spreadsheet cells for rockbox lists */ | ||
22 | #include "plugin.h" | ||
23 | #include "lib/printcell_helper.h" | ||
24 | |||
25 | #ifndef PRINTCELL_MAX_COLUMNS | ||
26 | #define PRINTCELL_MAX_COLUMNS 16 | ||
27 | #endif | ||
28 | |||
29 | #define COLUMN_ENDLEN 3 | ||
30 | #define TITLE_FLAG 0xFF | ||
31 | #define SELECTED_FLAG 0x1 | ||
32 | |||
33 | #if LCD_DEPTH == 1 | ||
34 | #define BAR_WIDTH (1) | ||
35 | #else | ||
36 | #define BAR_WIDTH (COLUMN_ENDLEN) | ||
37 | #endif | ||
38 | |||
39 | struct printcell_info_t { | ||
40 | int offw[NB_SCREENS]; | ||
41 | int iconw[NB_SCREENS]; | ||
42 | int selcol_offw[NB_SCREENS]; | ||
43 | int totalcolw[NB_SCREENS]; | ||
44 | uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; | ||
45 | int ncols; | ||
46 | int selcol; | ||
47 | int selcol_index; | ||
48 | char title[PRINTCELL_MAXLINELEN]; | ||
49 | bool separator; | ||
50 | }; | ||
51 | static struct printcell_info_t printcell; | ||
52 | |||
53 | static void parse_dsptext(int ncols, const char *dsp_text, char* buffer, uint16_t *sidx) | ||
54 | { | ||
55 | /*Internal function loads sidx with split offsets indexing | ||
56 | the buffer of null terminated strings, splits on '$' | ||
57 | _assumptions_: | ||
58 | dsp_text[len - 1] = \0, | ||
59 | buffer[PRINTCELL_MAXLINELEN], | ||
60 | sidx[PRINTCELL_MAX_COLUMNS] | ||
61 | */ | ||
62 | int i = 0; | ||
63 | int j = 0; | ||
64 | int ch = '$'; /* first column $ is optional */ | ||
65 | if (*dsp_text == '$') | ||
66 | dsp_text++; | ||
67 | /* add null to the start of the text buffer */ | ||
68 | buffer[j++] = '\0'; | ||
69 | do | ||
70 | { | ||
71 | if (ch == '$') | ||
72 | { | ||
73 | sidx[i] = j; | ||
74 | if (*dsp_text == '$') /* $$ escaped user must want to display $*/ | ||
75 | buffer[j++] = *dsp_text++; | ||
76 | while (*dsp_text != '\0' && *dsp_text != '$' && j < PRINTCELL_MAXLINELEN - 1) | ||
77 | buffer[j++] = *dsp_text++; | ||
78 | buffer[j++] = '\0'; | ||
79 | i++; | ||
80 | if (i >= ncols || j >= (PRINTCELL_MAXLINELEN - 1)) | ||
81 | break; | ||
82 | } | ||
83 | ch = *dsp_text++; | ||
84 | } while (ch != '\0'); | ||
85 | while (i < ncols) | ||
86 | sidx[i++] = 0; /* point to null */ | ||
87 | } | ||
88 | |||
89 | static void draw_selector(struct screen *display, struct line_desc *linedes, | ||
90 | int selected_flag, int selected_col, | ||
91 | int separator_height, int x, int y, int w, int h) | ||
92 | { | ||
93 | /* Internal function draws the currently selected items row & column styling */ | ||
94 | if (!(separator_height > 0 || (selected_flag & SELECTED_FLAG))) | ||
95 | return; | ||
96 | y--; | ||
97 | h++; | ||
98 | int linestyle = linedes->style & _STYLE_DECO_MASK; | ||
99 | bool invert = (selected_flag == SELECTED_FLAG && linestyle >= STYLE_COLORBAR); | ||
100 | if (invert || (linestyle & STYLE_INVERT) == STYLE_INVERT) | ||
101 | { | ||
102 | display->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); | ||
103 | } | ||
104 | |||
105 | if (selected_col == printcell.selcol) | ||
106 | { | ||
107 | if (selected_flag & SELECTED_FLAG) | ||
108 | { | ||
109 | /* expand left and right bars to show selected column */ | ||
110 | display->fillrect(x, y, BAR_WIDTH, h); | ||
111 | display->fillrect(x + w - BAR_WIDTH + 1, y, BAR_WIDTH, h); | ||
112 | display->set_drawmode(DRMODE_FG); | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | /* only draw left and right bars */ | ||
117 | display->drawrect(x + 1, y, 1, h); | ||
118 | display->drawrect(x + w - 1, y, 1, h); | ||
119 | return; | ||
120 | } | ||
121 | } | ||
122 | /* draw whole rect outline */ | ||
123 | display->drawrect(x + 1, y, w - 1, h); | ||
124 | } | ||
125 | |||
126 | static inline void set_cell_width(struct viewport *vp, int max_w, int new_w) | ||
127 | { | ||
128 | /* Internal function sets cell width if less than the max width */ | ||
129 | if (new_w > max_w) | ||
130 | vp->width = max_w; | ||
131 | else | ||
132 | vp->width = new_w; | ||
133 | vp->width -= COLUMN_ENDLEN; | ||
134 | } | ||
135 | |||
136 | static inline int printcells(struct screen *display, char* buffer, uint16_t *sidx, | ||
137 | struct line_desc *linedes, struct viewport *vp, int vp_w, | ||
138 | int separator, int x, int y, int offw, int selected_flag) | ||
139 | { | ||
140 | /* Internal function prints remaining cells */ | ||
141 | int text_offset = offw + offw; | ||
142 | int ncols = printcell.ncols; | ||
143 | int screen = display->screen_type; | ||
144 | int height = linedes->height; | ||
145 | int selsep = (selected_flag == 0) ? 0: separator; | ||
146 | uint16_t *screencolwidth = printcell.colw[screen]; | ||
147 | |||
148 | for(int i = 1; i < ncols; i++) | ||
149 | { | ||
150 | int ny = y; | ||
151 | int nw = screencolwidth[i] + text_offset; | ||
152 | int nx = x + nw; | ||
153 | char *buftext; | ||
154 | if (nx > 0 && x < vp_w) | ||
155 | { | ||
156 | set_cell_width(vp, vp_w, nx); | ||
157 | |||
158 | if (i == printcell.selcol) | ||
159 | linedes->separator_height = selsep; | ||
160 | else | ||
161 | linedes->separator_height = separator; | ||
162 | |||
163 | buftext = &buffer[sidx[i]]; | ||
164 | display->put_line(x + offw, ny, linedes, "$t", buftext); | ||
165 | vp->width += COLUMN_ENDLEN + 1; | ||
166 | draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height); | ||
167 | } | ||
168 | x = nx; | ||
169 | } | ||
170 | return x; | ||
171 | } | ||
172 | |||
173 | static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth) | ||
174 | { | ||
175 | /* Internal function determine how many of the previous colums can be shown */ | ||
176 | uint16_t *screencolwidth = printcell.colw[screen]; | ||
177 | int screenicnwidth = printcell.iconw[screen]; | ||
178 | int offset = 0; | ||
179 | int selcellw = screencolwidth[printcell.selcol] + text_offset; | ||
180 | int maxw = vp_w - (sbwidth + selcellw + 1); | ||
181 | |||
182 | for (int i = printcell.selcol - 1; i >= 0; i--) | ||
183 | { | ||
184 | int cw = screencolwidth[i] + text_offset; | ||
185 | if (i == 0) | ||
186 | cw += screenicnwidth; | ||
187 | if (offset > 0 || cw > maxw) | ||
188 | offset += cw; /* can not display this cell -- everything left goes here too */ | ||
189 | else | ||
190 | maxw -= cw; /* can display this cell subtract from the max width */ | ||
191 | } | ||
192 | return offset; | ||
193 | } | ||
194 | |||
195 | static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) | ||
196 | { | ||
197 | /* Internal function callback from the list, draws items as they are requested */ | ||
198 | #define ICON_PADDING 1 | ||
199 | #define ICON_PADDING_S "1" | ||
200 | struct screen *display = list_info->display; | ||
201 | int screen = display->screen_type; | ||
202 | int col_offset_width = printcell.offw[screen]; | ||
203 | int text_offset = col_offset_width + col_offset_width; | ||
204 | |||
205 | static char printcell_buffer[PRINTCELL_MAXLINELEN]; | ||
206 | static uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/ | ||
207 | |||
208 | int offset_pos = list_info->list->offset_position[screen]; | ||
209 | int x = list_info->x - offset_pos; | ||
210 | int y = list_info->y; | ||
211 | int line_indent = list_info->item_indent; | ||
212 | int item_offset = list_info->item_offset; | ||
213 | int icon = list_info->icon; | ||
214 | int icon_w = list_info->icon_width; | ||
215 | bool is_title = list_info->is_title; | ||
216 | bool is_selected = list_info->is_selected; | ||
217 | bool show_cursor = list_info->show_cursor; | ||
218 | bool have_icons = list_info->have_icons; | ||
219 | struct line_desc *linedes = list_info->linedes; | ||
220 | char *dsp_text = list_info->dsp_text; | ||
221 | struct viewport *vp = list_info->vp; | ||
222 | |||
223 | int selected_flag = ((is_selected || is_title) ? | ||
224 | (is_title ? TITLE_FLAG : SELECTED_FLAG) : 0); | ||
225 | int vp_w = vp->width;/* save for restore */ | ||
226 | int saved_separator_height = linedes->separator_height;/* save for restore */ | ||
227 | |||
228 | linedes->separator_height = 0; | ||
229 | int separator = saved_separator_height; | ||
230 | |||
231 | if (printcell.separator || (selected_flag & SELECTED_FLAG)) | ||
232 | separator = 1; | ||
233 | |||
234 | int nx = x; | ||
235 | int nw, colxw; | ||
236 | |||
237 | printcell_buffer[0] = '\0'; | ||
238 | parse_dsptext(printcell.ncols, dsp_text, printcell_buffer, sidx); | ||
239 | char *buftext = &printcell_buffer[sidx[0]]; | ||
240 | uint16_t *screencolwidth = printcell.colw[screen]; | ||
241 | |||
242 | if (is_title) | ||
243 | { | ||
244 | int sbwidth = 0; | ||
245 | if (rb->global_settings->scrollbar == SCROLLBAR_LEFT) | ||
246 | sbwidth = rb->global_settings->scrollbar_width; | ||
247 | |||
248 | printcell.iconw[screen] = have_icons ? ICON_PADDING + icon_w : 0; | ||
249 | |||
250 | if (printcell.selcol_offw[screen] == 0 && printcell.selcol > 0) | ||
251 | printcell.selcol_offw[screen] = calcvisible(screen, vp_w, text_offset, sbwidth); | ||
252 | nx -= printcell.selcol_offw[screen]; | ||
253 | |||
254 | nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; | ||
255 | nw += sbwidth; | ||
256 | |||
257 | colxw = nx + nw; | ||
258 | if (colxw > 0) | ||
259 | { | ||
260 | set_cell_width(vp, vp_w, colxw); | ||
261 | linedes->separator_height = separator; | ||
262 | |||
263 | if (have_icons) | ||
264 | { | ||
265 | display->put_line(nx + (COLUMN_ENDLEN/2), y, linedes, | ||
266 | "$"ICON_PADDING_S"I$t", icon, buftext); | ||
267 | } | ||
268 | else | ||
269 | { | ||
270 | display->put_line(nx + col_offset_width, y, linedes, "$t", buftext); | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | else | ||
275 | { | ||
276 | int cursor = Icon_NOICON; | ||
277 | nx -= printcell.selcol_offw[screen]; | ||
278 | |||
279 | if (selected_flag & SELECTED_FLAG) | ||
280 | { | ||
281 | printcell.selcol_index = sidx[printcell.selcol]; /* save the item offset*/ | ||
282 | cursor = Icon_Cursor; | ||
283 | /* limit length of selection if columns don't reach end */ | ||
284 | int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen]; | ||
285 | maxw += text_offset * printcell.ncols; | ||
286 | if (vp_w > maxw) | ||
287 | vp->width = maxw; | ||
288 | /* display a blank line first to draw selector across all cells */ | ||
289 | display->put_line(x, y, linedes, "$t", ""); | ||
290 | } | ||
291 | |||
292 | nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; | ||
293 | colxw = nx + nw; | ||
294 | if (colxw > 0) | ||
295 | { | ||
296 | set_cell_width(vp, vp_w, colxw); | ||
297 | if (printcell.selcol == 0 && selected_flag == 0) | ||
298 | linedes->separator_height = 0; | ||
299 | else | ||
300 | linedes->separator_height = separator; | ||
301 | |||
302 | if (show_cursor && have_icons) | ||
303 | { | ||
304 | /* the list can have both, one of or neither of cursor and item icons, | ||
305 | * if both don't apply icon padding twice between the icons */ | ||
306 | display->put_line(nx, y, | ||
307 | linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t", | ||
308 | line_indent, cursor, icon, item_offset, buftext); | ||
309 | } | ||
310 | else if (show_cursor || have_icons) | ||
311 | { | ||
312 | display->put_line(nx, y, linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent, | ||
313 | show_cursor ? cursor:icon, item_offset, buftext); | ||
314 | } | ||
315 | else | ||
316 | { | ||
317 | display->put_line(nx + col_offset_width, y, linedes, | ||
318 | "$*s$*t", line_indent, item_offset, buftext); | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | if (colxw > 0) /* draw selector for first column (title or items) */ | ||
324 | { | ||
325 | vp->width += COLUMN_ENDLEN + 1; | ||
326 | draw_selector(display, linedes, selected_flag, 0, | ||
327 | separator, nx, y, nw, linedes->height); | ||
328 | } | ||
329 | nx += nw; | ||
330 | /* display remaining cells */ | ||
331 | printcells(display, printcell_buffer, sidx, linedes, | ||
332 | vp, vp_w, separator, nx, y, col_offset_width, selected_flag); | ||
333 | /* restore settings */ | ||
334 | linedes->separator_height = saved_separator_height; | ||
335 | display->set_drawmode(DRMODE_FG); | ||
336 | vp->width = vp_w; | ||
337 | } | ||
338 | |||
339 | void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator) | ||
340 | { | ||
341 | printcell.separator = separator; | ||
342 | #ifdef HAVE_LCD_COLOR | ||
343 | static int list_sep_color = INT_MIN; | ||
344 | if (enable) | ||
345 | { | ||
346 | list_sep_color = rb->global_settings->list_separator_color; | ||
347 | rb->global_settings->list_separator_color = rb->global_settings->fg_color; | ||
348 | gui_list->callback_draw_item = printcell_listdraw_fn; | ||
349 | } | ||
350 | else | ||
351 | { | ||
352 | gui_list->callback_draw_item = NULL; | ||
353 | if (list_sep_color != INT_MIN) | ||
354 | rb->global_settings->list_separator_color = list_sep_color; | ||
355 | list_sep_color = INT_MIN; | ||
356 | } | ||
357 | #else | ||
358 | if (enable) | ||
359 | gui_list->callback_draw_item = printcell_listdraw_fn; | ||
360 | else | ||
361 | gui_list->callback_draw_item = NULL; | ||
362 | #endif | ||
363 | |||
364 | } | ||
365 | |||
366 | int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap) | ||
367 | { | ||
368 | int item = printcell.selcol + increment; | ||
369 | int imin = -1; | ||
370 | int imax = printcell.ncols - 1; | ||
371 | if(wrap) | ||
372 | { | ||
373 | imin = imax; | ||
374 | imax = -1; | ||
375 | } | ||
376 | |||
377 | if (item < -1) | ||
378 | item = imin; | ||
379 | else if (item >= printcell.ncols) | ||
380 | item = imax; | ||
381 | |||
382 | if (item != printcell.selcol) | ||
383 | { | ||
384 | FOR_NB_SCREENS(n) /* offset needs recalculated */ | ||
385 | printcell.selcol_offw[n] = 0; | ||
386 | printcell.selcol = item; | ||
387 | printcell.selcol_index = 0; | ||
388 | rb->gui_synclist_draw(gui_list); | ||
389 | } | ||
390 | return item; | ||
391 | } | ||
392 | |||
393 | int printcell_set_columns(struct gui_synclist *gui_list, | ||
394 | char * title, enum themable_icons icon) | ||
395 | { | ||
396 | if (title == NULL) | ||
397 | title = "$PRINTCELL NOT SETUP"; | ||
398 | uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */ | ||
399 | int width, height, user_minwidth; | ||
400 | int i = 0; | ||
401 | int j = 0; | ||
402 | int ch = '$'; /* first column $ is optional */ | ||
403 | |||
404 | rb->memset(&printcell, 0, sizeof(struct printcell_info_t)); | ||
405 | |||
406 | FOR_NB_SCREENS(n) | ||
407 | { | ||
408 | rb->screens[n]->getstringsize("W", &width, &height); | ||
409 | printcell.offw[n] = width; /* set column text offset */ | ||
410 | } | ||
411 | |||
412 | if (*title == '$') | ||
413 | title++; | ||
414 | do | ||
415 | { | ||
416 | if (ch == '$') | ||
417 | { | ||
418 | printcell.title[j++] = ch; | ||
419 | user_minwidth = 0; | ||
420 | if (*title == '*')/* user wants a minimum size for this column */ | ||
421 | { | ||
422 | char *dspst = title++; /* store starting position in case this is wrong */ | ||
423 | while(isdigit(*title)) | ||
424 | { | ||
425 | user_minwidth = 10*user_minwidth + *title - '0'; | ||
426 | title++; | ||
427 | } | ||
428 | if (*title != '$') /* user forgot $ or wants to display '*' */ | ||
429 | { | ||
430 | title = dspst; | ||
431 | user_minwidth = 0; | ||
432 | } | ||
433 | else | ||
434 | title++; | ||
435 | } | ||
436 | |||
437 | sidx[i] = j; | ||
438 | if (*title == '$') /* $$ escaped user must want to display $*/ | ||
439 | printcell.title[j++] = *title++; | ||
440 | |||
441 | while (*title != '\0' && *title != '$' && j < PRINTCELL_MAXLINELEN - 1) | ||
442 | printcell.title[j++] = *title++; | ||
443 | |||
444 | FOR_NB_SCREENS(n) | ||
445 | { | ||
446 | rb->screens[n]->getstringsize(&printcell.title[sidx[i]], &width, &height); | ||
447 | if (width < user_minwidth) | ||
448 | width = user_minwidth; | ||
449 | printcell.colw[n][i] = width; | ||
450 | printcell.totalcolw[n] += width; | ||
451 | } | ||
452 | if (++i >= PRINTCELL_MAX_COLUMNS - 1) | ||
453 | break; | ||
454 | } | ||
455 | ch = *title++; | ||
456 | } while (ch != '\0'); | ||
457 | printcell.ncols = i; | ||
458 | printcell.title[j] = '\0'; | ||
459 | printcell.selcol = -1; | ||
460 | printcell.selcol_index = 0; | ||
461 | |||
462 | rb->gui_synclist_set_title(gui_list, printcell.title, icon); | ||
463 | return printcell.ncols; | ||
464 | } | ||
465 | |||
466 | char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz) | ||
467 | { | ||
468 | int selected = gui_list->selected_item; | ||
469 | int index = printcell.selcol_index - 1; | ||
470 | |||
471 | if (index < 0) | ||
472 | index = 0; | ||
473 | char *bpos; | ||
474 | |||
475 | if (gui_list->callback_get_item_name(selected, gui_list->data, buf, bufsz) == buf) | ||
476 | { | ||
477 | bpos = &buf[index]; | ||
478 | if (printcell.selcol < 0) /* return entire string incld col formatting '$'*/ | ||
479 | return bpos; | ||
480 | while(bpos < &buf[bufsz - 1]) | ||
481 | { | ||
482 | if (*bpos == '$' || *bpos == '\0') | ||
483 | goto success; | ||
484 | bpos++; | ||
485 | } | ||
486 | } | ||
487 | /*failure*/ | ||
488 | bpos = buf; | ||
489 | index = 0; | ||
490 | success: | ||
491 | *bpos = '\0'; | ||
492 | return &buf[index]; | ||
493 | } | ||