diff options
author | William Wilgus <wilgus.william@gmail.com> | 2021-12-15 00:37:04 -0500 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2022-01-01 23:56:51 -0500 |
commit | a7703e4926075ee41269ca96d1806444107e65f2 (patch) | |
tree | a9b5c6a5df97267c71b0c1ad0613ca0be0d66c4d | |
parent | edc68b06574b57684a7fad4263dd59eed7f65f2f (diff) | |
download | rockbox-a7703e4926.tar.gz rockbox-a7703e4926.zip |
gui lists add callback for owner drawn items
allow the guts of gui_sync_list to be used with owner drawn items
WIP
printcell_helper--
goal: allow data to be displayed in a spreadsheet format with an easy to use interface
printcell_set_columns(gui_synclist, title, icon)
sets title and calculates cell widths
each column is identified by '$' character
ex 3 columns title = "Col1$Col2$Col3"
also accepts $*WIDTH$
ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3
printcell_enable(gui_synclist, enable)
sets the printcell function enabled
After setting the columns and enabling the printcell function
items can be added to the list like normal
column items are supplied delimited by '$'
ex item = "Item1$item2$item3"
they will be placed in cells defined by set_columns and scroll if the cell is too small
--Fixed for 1 bit & 2 bit displays
Change-Id: I49bd7903005d7a54e93af4379b0cdea63c860656
-rw-r--r-- | apps/gui/bitmap/list.c | 132 | ||||
-rw-r--r-- | apps/gui/list.c | 1 | ||||
-rw-r--r-- | apps/gui/list.h | 30 | ||||
-rw-r--r-- | apps/plugins/lib/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/lib/printcell_helper.c | 493 | ||||
-rw-r--r-- | apps/plugins/lib/printcell_helper.h | 45 | ||||
-rw-r--r-- | apps/plugins/rb_info.c | 110 |
7 files changed, 763 insertions, 49 deletions
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index 194f4c008b..1b051cd800 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c @@ -90,11 +90,57 @@ static int list_icon_width(enum screen_type screen) return get_icon_width(screen) + ICON_PADDING * 2; } -static bool draw_title(struct screen *display, struct gui_synclist *list) +static void _default_listdraw_fn(struct list_putlineinfo_t *list_info) +{ + struct screen *display = list_info->display; + int x = list_info->x; + int y = list_info->y; + int item_indent = list_info->item_indent; + int item_offset = list_info->item_offset; + int icon = list_info->icon; + bool is_selected = list_info->is_selected; + bool is_title = list_info->is_title; + bool show_cursor = list_info->show_cursor; + bool have_icons = list_info->have_icons; + struct line_desc *linedes = list_info->linedes; + char *dsp_text = list_info->dsp_text; + + if (is_title) + { + if (have_icons) + display->put_line(x, y, linedes, "$"ICON_PADDING_S"I$t", + icon, dsp_text); + else + display->put_line(x, y, linedes, "$t", dsp_text); + } + else if (show_cursor && have_icons) + { + /* the list can have both, one of or neither of cursor and item icons, + * if both don't apply icon padding twice between the icons */ + display->put_line(x, y, + linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t", + item_indent, is_selected ? Icon_Cursor : Icon_NOICON, + icon, item_offset, dsp_text); + } + else if (show_cursor || have_icons) + { + display->put_line(x, y, linedes, "$*s$"ICON_PADDING_S"I$*t", item_indent, + show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon, + item_offset, dsp_text); + } + else + { + display->put_line(x, y, linedes, "$*s$*t", item_indent, item_offset, dsp_text); + } +} + +static bool draw_title(struct screen *display, + struct gui_synclist *list, + list_draw_item *callback_draw_item) { const int screen = display->screen_type; struct viewport *title_text_vp = &title_text[screen]; - struct line_desc line = LINE_DESC_DEFINIT; + struct line_desc linedes = LINE_DESC_DEFINIT; if (sb_set_title_text(list->title, list->title_icon, screen)) return false; /* the sbs is handling the title */ @@ -102,29 +148,39 @@ static bool draw_title(struct screen *display, struct gui_synclist *list) if (!list_display_title(list, screen)) return false; *title_text_vp = *(list->parent[screen]); - line.height = list->line_height[screen]; - title_text_vp->height = line.height; + linedes.height = list->line_height[screen]; + title_text_vp->height = linedes.height; #if LCD_DEPTH > 1 /* XXX: Do we want to support the separator on remote displays? */ if (display->screen_type == SCREEN_MAIN && global_settings.list_separator_height != 0) - line.separator_height = abs(global_settings.list_separator_height) + linedes.separator_height = abs(global_settings.list_separator_height) + (lcd_get_dpi() > 200 ? 2 : 1); #endif #ifdef HAVE_LCD_COLOR if (list->title_color >= 0) - line.style |= (STYLE_COLORED|list->title_color); + linedes.style |= (STYLE_COLORED|list->title_color); #endif - line.scroll = true; + linedes.scroll = true; display->set_viewport(title_text_vp); + int icon = list->title_icon; + int icon_w = list_icon_width(display->screen_type); + bool have_icons = false; + if (icon != Icon_NOICON && global_settings.show_icons) + have_icons = true; - if (list->title_icon != Icon_NOICON && global_settings.show_icons) - put_line(display, 0, 0, &line, "$"ICON_PADDING_S"I$t", - list->title_icon, list->title); - else - put_line(display, 0, 0, &line, "$t", list->title); + struct list_putlineinfo_t list_info = + { + .x = 0, .y = 0, .item_indent = 0, .item_offset = 0, + .line = -1, .icon = icon, .icon_width = icon_w, + .display = display, .vp = title_text_vp, .linedes = &linedes, .list = list, + .dsp_text = list->title, + .is_selected = false, .is_title = true, .show_cursor = false, + .have_icons = have_icons + }; + callback_draw_item(&list_info); return true; } @@ -133,6 +189,8 @@ void list_draw(struct screen *display, struct gui_synclist *list) { int start, end, item_offset, i; const int screen = display->screen_type; + list_draw_item *callback_draw_item; + const int list_start_item = list->start_item[screen]; const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT); const bool scrollbar_in_right = (global_settings.scrollbar == SCROLLBAR_RIGHT); @@ -145,11 +203,16 @@ void list_draw(struct screen *display, struct gui_synclist *list) struct viewport *list_text_vp = &list_text[screen]; int indent = 0; + if (list->callback_draw_item != NULL) + callback_draw_item = list->callback_draw_item; + else + callback_draw_item = _default_listdraw_fn; + struct viewport * last_vp = display->set_viewport(parent); display->clear_viewport(); display->scroll_stop_viewport(list_text_vp); *list_text_vp = *parent; - if ((show_title = draw_title(display, list))) + if ((show_title = draw_title(display, list, callback_draw_item))) { int title_height = title_text[screen].height; list_text_vp->y += title_height; @@ -244,6 +307,16 @@ void list_draw(struct screen *display, struct gui_synclist *list) } display->set_viewport(list_text_vp); + int icon_w = list_icon_width(screen); + int character_width = display->getcharwidth(); + + struct list_putlineinfo_t list_info = + { + .x = 0, .y = 0, .vp = list_text_vp, .list = list, + .icon_width = icon_w, .is_title = false, .show_cursor = show_cursor, + .have_icons = have_icons, .linedes = &linedes, .display = display + }; + for (i=start; i<end && i<list->nb_items; i++) { /* do the text */ @@ -251,7 +324,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) unsigned const char *s; char entry_buffer[MAX_PATH]; unsigned char *entry_name; - int text_pos = 0; + const int text_pos = 0; /* UNUSED */ int line = i - start; int line_indent = 0; int style = STYLE_DEFAULT; @@ -268,9 +341,9 @@ void list_draw(struct screen *display, struct gui_synclist *list) if (line_indent) { if (global_settings.show_icons) - line_indent *= list_icon_width(screen); + line_indent *= icon_w; else - line_indent *= display->getcharwidth(); + line_indent *= character_width; } line_indent += indent; @@ -345,27 +418,22 @@ void list_draw(struct screen *display, struct gui_synclist *list) } } #endif - linedes.style = style; linedes.scroll = is_selected ? true : list->scroll_all; linedes.line = i % list->selected_size; icon = list->callback_get_item_icon ? list->callback_get_item_icon(i, list->data) : Icon_NOICON; - /* the list can have both, one of or neither of cursor and item icons, - * if both don't apply icon padding twice between the icons */ - if (show_cursor && have_icons) - put_line(display, 0, line * linedes.height + draw_offset, - &linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t", - line_indent, is_selected ? Icon_Cursor : Icon_NOICON, - icon, item_offset, entry_name); - else if (show_cursor || have_icons) - put_line(display, 0, line * linedes.height + draw_offset, - &linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent, - show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon, - item_offset, entry_name); - else - put_line(display, 0, line * linedes.height + draw_offset, - &linedes, "$*s$*t", line_indent, item_offset, entry_name); + + + list_info.y = line * linedes.height + draw_offset; + list_info.is_selected = is_selected; + list_info.item_indent = line_indent; + list_info.line = i; + list_info.icon = icon; + list_info.dsp_text = entry_name; + list_info.item_offset = item_offset; + + callback_draw_item(&list_info); } display->set_viewport(parent); display->update_viewport(); diff --git a/apps/gui/list.c b/apps/gui/list.c index 8ff075da7e..a8055c4581 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -150,6 +150,7 @@ void gui_synclist_init(struct gui_synclist * gui_list, gui_list->callback_get_item_icon = NULL; gui_list->callback_get_item_name = callback_get_item_name; gui_list->callback_speak_item = NULL; + gui_list->callback_draw_item = NULL; gui_list->nb_items = 0; gui_list->selected_item = 0; #ifdef HAVE_TOUCHSCREEN diff --git a/apps/gui/list.h b/apps/gui/list.h index 64ff3e3fdd..1f910577a1 100644 --- a/apps/gui/list.h +++ b/apps/gui/list.h @@ -69,6 +69,35 @@ typedef enum themable_icons list_get_icon(int selected_item, void * data); typedef const char * list_get_name(int selected_item, void * data, char * buffer, size_t buffer_len); /* + * Draw callback + * - display : functions supplied depends on the screen call originated from (typ: MAIN) + * - list_info : a pointer to an internal struct containing item display information + */ +/* owner drawn lists need to know this info */ +struct list_putlineinfo_t { + int x; + int y; + int item_indent; + int item_offset; + int line; + + int icon; + int icon_width; + + struct screen *display; + struct viewport *vp; + struct line_desc *linedes; + struct gui_synclist * list; + char *dsp_text; + + bool is_selected; + bool is_title; + bool show_cursor; + bool have_icons; +}; + +typedef void list_draw_item(struct list_putlineinfo_t *list_info); +/* * Voice callback * - selected_item : an integer that tells the number of the item to speak * - data : a void pointer to the data you gave to the list when you @@ -133,6 +162,7 @@ struct gui_synclist list_get_icon *callback_get_item_icon; list_get_name *callback_get_item_name; list_speak_item *callback_speak_item; + list_draw_item *callback_draw_item; /* The data that will be passed to the callback function YOU implement */ void * data; diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES index 1cd092f8df..bff9017404 100644 --- a/apps/plugins/lib/SOURCES +++ b/apps/plugins/lib/SOURCES @@ -14,6 +14,7 @@ rgb_hsv.c highscore.c simple_viewer.c display_text.c +printcell_helper.c strncpy.c stdio_compat.c 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +/* spreadsheet cells for rockbox lists */ +#include "plugin.h" +#include "lib/printcell_helper.h" + +#ifndef PRINTCELL_MAX_COLUMNS +#define PRINTCELL_MAX_COLUMNS 16 +#endif + +#define COLUMN_ENDLEN 3 +#define TITLE_FLAG 0xFF +#define SELECTED_FLAG 0x1 + +#if LCD_DEPTH == 1 +#define BAR_WIDTH (1) +#else +#define BAR_WIDTH (COLUMN_ENDLEN) +#endif + +struct printcell_info_t { + int offw[NB_SCREENS]; + int iconw[NB_SCREENS]; + int selcol_offw[NB_SCREENS]; + int totalcolw[NB_SCREENS]; + uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; + int ncols; + int selcol; + int selcol_index; + char title[PRINTCELL_MAXLINELEN]; + bool separator; +}; +static struct printcell_info_t printcell; + +static void parse_dsptext(int ncols, const char *dsp_text, char* buffer, uint16_t *sidx) +{ + /*Internal function loads sidx with split offsets indexing + the buffer of null terminated strings, splits on '$' + _assumptions_: + dsp_text[len - 1] = \0, + buffer[PRINTCELL_MAXLINELEN], + sidx[PRINTCELL_MAX_COLUMNS] + */ + int i = 0; + int j = 0; + int ch = '$'; /* first column $ is optional */ + if (*dsp_text == '$') + dsp_text++; + /* add null to the start of the text buffer */ + buffer[j++] = '\0'; + do + { + if (ch == '$') + { + sidx[i] = j; + if (*dsp_text == '$') /* $$ escaped user must want to display $*/ + buffer[j++] = *dsp_text++; + while (*dsp_text != '\0' && *dsp_text != '$' && j < PRINTCELL_MAXLINELEN - 1) + buffer[j++] = *dsp_text++; + buffer[j++] = '\0'; + i++; + if (i >= ncols || j >= (PRINTCELL_MAXLINELEN - 1)) + break; + } + ch = *dsp_text++; + } while (ch != '\0'); + while (i < ncols) + sidx[i++] = 0; /* point to null */ +} + +static void draw_selector(struct screen *display, struct line_desc *linedes, + int selected_flag, int selected_col, + int separator_height, int x, int y, int w, int h) +{ + /* Internal function draws the currently selected items row & column styling */ + if (!(separator_height > 0 || (selected_flag & SELECTED_FLAG))) + return; + y--; + h++; + int linestyle = linedes->style & _STYLE_DECO_MASK; + bool invert = (selected_flag == SELECTED_FLAG && linestyle >= STYLE_COLORBAR); + if (invert || (linestyle & STYLE_INVERT) == STYLE_INVERT) + { + display->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); + } + + if (selected_col == printcell.selcol) + { + if (selected_flag & SELECTED_FLAG) + { + /* expand left and right bars to show selected column */ + display->fillrect(x, y, BAR_WIDTH, h); + display->fillrect(x + w - BAR_WIDTH + 1, y, BAR_WIDTH, h); + display->set_drawmode(DRMODE_FG); + } + else + { + /* only draw left and right bars */ + display->drawrect(x + 1, y, 1, h); + display->drawrect(x + w - 1, y, 1, h); + return; + } + } + /* draw whole rect outline */ + display->drawrect(x + 1, y, w - 1, h); +} + +static inline void set_cell_width(struct viewport *vp, int max_w, int new_w) +{ + /* Internal function sets cell width if less than the max width */ + if (new_w > max_w) + vp->width = max_w; + else + vp->width = new_w; + vp->width -= COLUMN_ENDLEN; +} + +static inline int printcells(struct screen *display, char* buffer, uint16_t *sidx, + struct line_desc *linedes, struct viewport *vp, int vp_w, + int separator, int x, int y, int offw, int selected_flag) +{ + /* Internal function prints remaining cells */ + int text_offset = offw + offw; + int ncols = printcell.ncols; + int screen = display->screen_type; + int height = linedes->height; + int selsep = (selected_flag == 0) ? 0: separator; + uint16_t *screencolwidth = printcell.colw[screen]; + + for(int i = 1; i < ncols; i++) + { + int ny = y; + int nw = screencolwidth[i] + text_offset; + int nx = x + nw; + char *buftext; + if (nx > 0 && x < vp_w) + { + set_cell_width(vp, vp_w, nx); + + if (i == printcell.selcol) + linedes->separator_height = selsep; + else + linedes->separator_height = separator; + + buftext = &buffer[sidx[i]]; + display->put_line(x + offw, ny, linedes, "$t", buftext); + vp->width += COLUMN_ENDLEN + 1; + draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height); + } + x = nx; + } + return x; +} + +static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth) +{ + /* Internal function determine how many of the previous colums can be shown */ + uint16_t *screencolwidth = printcell.colw[screen]; + int screenicnwidth = printcell.iconw[screen]; + int offset = 0; + int selcellw = screencolwidth[printcell.selcol] + text_offset; + int maxw = vp_w - (sbwidth + selcellw + 1); + + for (int i = printcell.selcol - 1; i >= 0; i--) + { + int cw = screencolwidth[i] + text_offset; + if (i == 0) + cw += screenicnwidth; + if (offset > 0 || cw > maxw) + offset += cw; /* can not display this cell -- everything left goes here too */ + else + maxw -= cw; /* can display this cell subtract from the max width */ + } + return offset; +} + +static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info) +{ +/* Internal function callback from the list, draws items as they are requested */ +#define ICON_PADDING 1 +#define ICON_PADDING_S "1" + struct screen *display = list_info->display; + int screen = display->screen_type; + int col_offset_width = printcell.offw[screen]; + int text_offset = col_offset_width + col_offset_width; + + static char printcell_buffer[PRINTCELL_MAXLINELEN]; + static uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/ + + int offset_pos = list_info->list->offset_position[screen]; + int x = list_info->x - offset_pos; + int y = list_info->y; + int line_indent = list_info->item_indent; + int item_offset = list_info->item_offset; + int icon = list_info->icon; + int icon_w = list_info->icon_width; + bool is_title = list_info->is_title; + bool is_selected = list_info->is_selected; + bool show_cursor = list_info->show_cursor; + bool have_icons = list_info->have_icons; + struct line_desc *linedes = list_info->linedes; + char *dsp_text = list_info->dsp_text; + struct viewport *vp = list_info->vp; + + int selected_flag = ((is_selected || is_title) ? + (is_title ? TITLE_FLAG : SELECTED_FLAG) : 0); + int vp_w = vp->width;/* save for restore */ + int saved_separator_height = linedes->separator_height;/* save for restore */ + + linedes->separator_height = 0; + int separator = saved_separator_height; + + if (printcell.separator || (selected_flag & SELECTED_FLAG)) + separator = 1; + + int nx = x; + int nw, colxw; + + printcell_buffer[0] = '\0'; + parse_dsptext(printcell.ncols, dsp_text, printcell_buffer, sidx); + char *buftext = &printcell_buffer[sidx[0]]; + uint16_t *screencolwidth = printcell.colw[screen]; + + if (is_title) + { + int sbwidth = 0; + if (rb->global_settings->scrollbar == SCROLLBAR_LEFT) + sbwidth = rb->global_settings->scrollbar_width; + + printcell.iconw[screen] = have_icons ? ICON_PADDING + icon_w : 0; + + if (printcell.selcol_offw[screen] == 0 && printcell.selcol > 0) + printcell.selcol_offw[screen] = calcvisible(screen, vp_w, text_offset, sbwidth); + nx -= printcell.selcol_offw[screen]; + + nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; + nw += sbwidth; + + colxw = nx + nw; + if (colxw > 0) + { + set_cell_width(vp, vp_w, colxw); + linedes->separator_height = separator; + + if (have_icons) + { + display->put_line(nx + (COLUMN_ENDLEN/2), y, linedes, + "$"ICON_PADDING_S"I$t", icon, buftext); + } + else + { + display->put_line(nx + col_offset_width, y, linedes, "$t", buftext); + } + } + } + else + { + int cursor = Icon_NOICON; + nx -= printcell.selcol_offw[screen]; + + if (selected_flag & SELECTED_FLAG) + { + printcell.selcol_index = sidx[printcell.selcol]; /* save the item offset*/ + cursor = Icon_Cursor; + /* limit length of selection if columns don't reach end */ + int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen]; + maxw += text_offset * printcell.ncols; + if (vp_w > maxw) + vp->width = maxw; + /* display a blank line first to draw selector across all cells */ + display->put_line(x, y, linedes, "$t", ""); + } + + nw = screencolwidth[0] + printcell.iconw[screen] + text_offset; + colxw = nx + nw; + if (colxw > 0) + { + set_cell_width(vp, vp_w, colxw); + if (printcell.selcol == 0 && selected_flag == 0) + linedes->separator_height = 0; + else + linedes->separator_height = separator; + + if (show_cursor && have_icons) + { + /* the list can have both, one of or neither of cursor and item icons, + * if both don't apply icon padding twice between the icons */ + display->put_line(nx, y, + linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t", + line_indent, cursor, icon, item_offset, buftext); + } + else if (show_cursor || have_icons) + { + display->put_line(nx, y, linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent, + show_cursor ? cursor:icon, item_offset, buftext); + } + else + { + display->put_line(nx + col_offset_width, y, linedes, + "$*s$*t", line_indent, item_offset, buftext); + } + } + } + + if (colxw > 0) /* draw selector for first column (title or items) */ + { + vp->width += COLUMN_ENDLEN + 1; + draw_selector(display, linedes, selected_flag, 0, + separator, nx, y, nw, linedes->height); + } + nx += nw; + /* display remaining cells */ + printcells(display, printcell_buffer, sidx, linedes, + vp, vp_w, separator, nx, y, col_offset_width, selected_flag); + /* restore settings */ + linedes->separator_height = saved_separator_height; + display->set_drawmode(DRMODE_FG); + vp->width = vp_w; +} + +void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator) +{ + printcell.separator = separator; +#ifdef HAVE_LCD_COLOR + static int list_sep_color = INT_MIN; + if (enable) + { + list_sep_color = rb->global_settings->list_separator_color; + rb->global_settings->list_separator_color = rb->global_settings->fg_color; + gui_list->callback_draw_item = printcell_listdraw_fn; + } + else + { + gui_list->callback_draw_item = NULL; + if (list_sep_color != INT_MIN) + rb->global_settings->list_separator_color = list_sep_color; + list_sep_color = INT_MIN; + } +#else + if (enable) + gui_list->callback_draw_item = printcell_listdraw_fn; + else + gui_list->callback_draw_item = NULL; +#endif + +} + +int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap) +{ + int item = printcell.selcol + increment; + int imin = -1; + int imax = printcell.ncols - 1; + if(wrap) + { + imin = imax; + imax = -1; + } + + if (item < -1) + item = imin; + else if (item >= printcell.ncols) + item = imax; + + if (item != printcell.selcol) + { + FOR_NB_SCREENS(n) /* offset needs recalculated */ + printcell.selcol_offw[n] = 0; + printcell.selcol = item; + printcell.selcol_index = 0; + rb->gui_synclist_draw(gui_list); + } + return item; +} + +int printcell_set_columns(struct gui_synclist *gui_list, + char * title, enum themable_icons icon) +{ + if (title == NULL) + title = "$PRINTCELL NOT SETUP"; + uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */ + int width, height, user_minwidth; + int i = 0; + int j = 0; + int ch = '$'; /* first column $ is optional */ + + rb->memset(&printcell, 0, sizeof(struct printcell_info_t)); + + FOR_NB_SCREENS(n) + { + rb->screens[n]->getstringsize("W", &width, &height); + printcell.offw[n] = width; /* set column text offset */ + } + + if (*title == '$') + title++; + do + { + if (ch == '$') + { + printcell.title[j++] = ch; + user_minwidth = 0; + if (*title == '*')/* user wants a minimum size for this column */ + { + char *dspst = title++; /* store starting position in case this is wrong */ + while(isdigit(*title)) + { + user_minwidth = 10*user_minwidth + *title - '0'; + title++; + } + if (*title != '$') /* user forgot $ or wants to display '*' */ + { + title = dspst; + user_minwidth = 0; + } + else + title++; + } + + sidx[i] = j; + if (*title == '$') /* $$ escaped user must want to display $*/ + printcell.title[j++] = *title++; + + while (*title != '\0' && *title != '$' && j < PRINTCELL_MAXLINELEN - 1) + printcell.title[j++] = *title++; + + FOR_NB_SCREENS(n) + { + rb->screens[n]->getstringsize(&printcell.title[sidx[i]], &width, &height); + if (width < user_minwidth) + width = user_minwidth; + printcell.colw[n][i] = width; + printcell.totalcolw[n] += width; + } + if (++i >= PRINTCELL_MAX_COLUMNS - 1) + break; + } + ch = *title++; + } while (ch != '\0'); + printcell.ncols = i; + printcell.title[j] = '\0'; + printcell.selcol = -1; + printcell.selcol_index = 0; + + rb->gui_synclist_set_title(gui_list, printcell.title, icon); + return printcell.ncols; +} + +char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz) +{ + int selected = gui_list->selected_item; + int index = printcell.selcol_index - 1; + + if (index < 0) + index = 0; + char *bpos; + + if (gui_list->callback_get_item_name(selected, gui_list->data, buf, bufsz) == buf) + { + bpos = &buf[index]; + if (printcell.selcol < 0) /* return entire string incld col formatting '$'*/ + return bpos; + while(bpos < &buf[bufsz - 1]) + { + if (*bpos == '$' || *bpos == '\0') + goto success; + bpos++; + } + } +/*failure*/ + bpos = buf; + index = 0; +success: + *bpos = '\0'; + return &buf[index]; +} diff --git a/apps/plugins/lib/printcell_helper.h b/apps/plugins/lib/printcell_helper.h new file mode 100644 index 0000000000..adc98e5a5f --- /dev/null +++ b/apps/plugins/lib/printcell_helper.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _PRINTCELL_LIST_H_ +#define _PRINTCELL_LIST_H_ +#define PRINTCELL_MAXLINELEN MAX_PATH + +/* sets the printcell function enabled */ +void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator); + +/* sets title and calculates cell widths each column is identified by '$' character + ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$ + ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3 + returns number of columns +*/ +int printcell_set_columns(struct gui_synclist *gui_list, + char * title, enum themable_icons icon); + +/* increments the current selected column negative increment is allowed + returns the selected column + range: -1(no selection) to ncols - 1 */ +int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap); + +/* return the text of currently selected column buffer should be sized + * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */ +char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz); +#endif /*_PRINTCELL_LIST_H_*/ diff --git a/apps/plugins/rb_info.c b/apps/plugins/rb_info.c index f82c80c0cf..e0ec117dfb 100644 --- a/apps/plugins/rb_info.c +++ b/apps/plugins/rb_info.c @@ -29,8 +29,9 @@ #include "lib/action_helper.h" #include "lib/button_helper.h" #include "lib/pluginlib_actions.h" +#include "lib/printcell_helper.h" -#define MENU_ID(x) (((void*)&"RPBUTACNGX\0" + x)) +#define MENU_ID(x) (((void*)&"RPBUTACNGSX\0" + x)) enum { M_ROOT = 0, M_PATHS, @@ -41,6 +42,7 @@ enum { M_CONTEXTS, M_ACTTEST, M_PLUGINS, + M_TESTPUT, M_EXIT, M_LAST_ITEM //ITEM COUNT }; @@ -96,7 +98,8 @@ static const struct paths paths[] = { {"Fm Presets", ""FMPRESET_PATH}, {"MAX_PATH", ""MACROVAL(MAX_PATH)" bytes"}, }; - +#define TESTPUT_HEADER "$*64$col1$col2$*128$col3$col4$col5$col6$*64$col7$col8" +static int testput_cols = 0; struct mainmenu { const char *name; void *menuid; int items;}; static struct mainmenu mainmenu[M_LAST_ITEM] = { #define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)COUNT} @@ -109,6 +112,7 @@ MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER), MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ), MENU_ITEM(M_ACTTEST, "Action test", 3), MENU_ITEM(M_PLUGINS, ID2P(LANG_PLUGINS), MENU_ID_PLUGINS_ITEMS), +MENU_ITEM(M_TESTPUT, "Printcell test", 36), MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0), #undef MENU_ITEM }; @@ -125,8 +129,16 @@ static const struct mainmenu *mainitem(int selected_item) static void cleanup(void *parameter) { (void)parameter; +} +#if 0 +static enum themable_icons menu_icon_cb(int selected_item, void * data) +{ + (void)data; + (void)selected_item; + return Icon_NOICON; } +#endif static const char *menu_plugin_name_cb(int selected_item, void* data, char* buf, size_t buf_len) @@ -247,7 +259,7 @@ static const char* list_get_name_cb(int selected_item, void* data, buf[0] = '\0'; if (data == MENU_ID(M_ROOT)) return mainitem(selected_item)->name; - else if (selected_item == 0) /*header text*/ + else if (selected_item == 0 && data != MENU_ID(M_TESTPUT)) /*header text*/ return mainitem(main_last_sel)->name; else if (selected_item >= mainitem(main_last_sel)->items - 1) return ID2P(LANG_BACK); @@ -286,6 +298,11 @@ static const char* list_get_name_cb(int selected_item, void* data, { return menu_plugin_name_cb(selected_item - 1, data, buf, buf_len); } + else if (data == MENU_ID(M_TESTPUT)) + { + rb->snprintf(buf, buf_len, "put_line item: [ %d ]$Text %d$Text LONGER TEST text %d $4$5$6$7$8$9", selected_item, 1, 2); + return buf; + } return buf; } @@ -331,16 +348,38 @@ static int list_voice_cb(int list_index, void* data) return 0; } -int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclist *lists) +int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists) { - if (lists->data == MENU_ID(M_ACTTEST)) + if (lists->data == MENU_ID(M_TESTPUT) && (selected_item < (mainitem(M_TESTPUT)->items) - 1)/*back*/) + { + if (*action == ACTION_STD_OK) + { + printcell_increment_column(lists, 1, true); + *action = ACTION_NONE; + } + else if (*action == ACTION_STD_CANCEL) + { + if (printcell_increment_column(lists, -1, true) != testput_cols - 1) + { + *action = ACTION_NONE; + } + } + else if (*action == ACTION_STD_CONTEXT) + { + char buf[PRINTCELL_MAXLINELEN]; + char* bufp = buf; + bufp = printcell_get_selected_column_text(lists, bufp, PRINTCELL_MAXLINELEN); + rb->splashf(HZ * 2, "Item: %s", bufp); + } + } + else if (lists->data == MENU_ID(M_ACTTEST)) { if (selected_item == 2) /* context */ { int ctx = m_test.context; - if (action == ACTION_STD_OK) + if (*action == ACTION_STD_OK) m_test.context++; - else if (action == ACTION_STD_CANCEL) + else if (*action == ACTION_STD_CANCEL) m_test.context--; if (m_test.context < 0) @@ -353,7 +392,7 @@ int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclis goto default_handler; } - if (action == ACTION_STD_OK) + if (*action == ACTION_STD_OK) { if (selected_item == 1 || selected_item == 3) { @@ -364,7 +403,7 @@ int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclis } else if (lists->data == MENU_ID(M_BTNTEST)) { - if (action == ACTION_STD_OK) + if (*action == ACTION_STD_OK) { if (selected_item == 1 || selected_item == 2) { @@ -373,18 +412,38 @@ int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclis } } } - if (action == ACTION_STD_OK) +/* common */ + if (*action == ACTION_STD_OK) { if (lists->data == MENU_ID(M_ROOT)) { rb->memset(&m_test, 0, sizeof(struct menu_test_t)); const struct mainmenu *cur = mainitem(selected_item); + if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT)) *exit = true; else { main_last_sel = selected_item; - synclist_set(cur->menuid, 1, cur->items, 1); + + if (cur->menuid == MENU_ID(M_TESTPUT)) + { + //rb->gui_list_screen_scroll_out_of_view(true); + synclist_set(cur->menuid, 0, cur->items, 1); +#if LCD_DEPTH > 1 + /* If line sep is set to automatic then outline cells */ + bool showlinesep = (rb->global_settings->list_separator_height < 0); +#else + bool showlinesep = (rb->global_settings->cursor_style == 0); +#endif + printcell_enable(lists, true, showlinesep); + //lists->callback_draw_item = test_listdraw_fn; + } + else + { + printcell_enable(lists, false, false); + synclist_set(cur->menuid, 1, cur->items, 1); + } rb->gui_synclist_draw(lists); } } @@ -394,7 +453,11 @@ int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclis } else if (selected_item >= (mainitem(main_last_sel)->items) - 1)/*back*/ { - action = ACTION_STD_CANCEL; + *action = ACTION_STD_CANCEL; + } + else if (lists->data == MENU_ID(M_TESTPUT)) + { + } else if (lists->data == MENU_ID(M_ACTIONS) || lists->data == MENU_ID(M_CONTEXTS)) @@ -406,8 +469,14 @@ int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclis rb->button_get(true); } } - if (action == ACTION_STD_CANCEL) + if (*action == ACTION_STD_CANCEL) { + if (lists->data == MENU_ID(M_TESTPUT)) + { + //rb->gui_list_screen_scroll_out_of_view(false); + //lists->callback_draw_item = NULL; + printcell_enable(lists, false, false); + } if (lists->data != MENU_ID(M_ROOT)) { const struct mainmenu *mainm = &mainmenu[0]; @@ -418,7 +487,7 @@ int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclis *exit = true; } default_handler: - if (rb->default_event_handler_ex(action, cleanup, NULL) == SYS_USB_CONNECTED) + if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED) { *exit = true; return PLUGIN_USB_CONNECTED; @@ -436,7 +505,14 @@ static void synclist_set(char* menu_id, int selected_item, int items, int sel_si list_voice_cb(0, menu_id); rb->gui_synclist_init(&lists,list_get_name_cb, menu_id, false, sel_size, NULL); - + if (menu_id == MENU_ID(M_TESTPUT)) + { + testput_cols = printcell_set_columns(&lists, TESTPUT_HEADER, Icon_Rockbox); + } + else + { + rb->gui_synclist_set_title(&lists, NULL,-1); + } rb->gui_synclist_set_icon_callback(&lists,NULL); rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); rb->gui_synclist_set_nb_items(&lists,items); @@ -460,7 +536,7 @@ enum plugin_status plugin_start(const void* parameter) /* add header and back item to each submenu */ for (int i = 1; i < M_LAST_ITEM; i++) mainmenu[i].items += 2; - + mainmenu[M_TESTPUT].items -= 1; if (!exit) { const struct mainmenu *mainm = &mainmenu[0]; @@ -483,10 +559,10 @@ enum plugin_status plugin_start(const void* parameter) } else redraw = true; + ret = menu_action_cb(&action, selected_item, &exit, &lists); if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD)) continue; selected_item = rb->gui_synclist_get_sel_pos(&lists); - ret = menu_action_cb(action, selected_item, &exit, &lists); } } |