diff options
Diffstat (limited to 'apps/plugins/lua/include_lua/printtable.lua')
-rw-r--r-- | apps/plugins/lua/include_lua/printtable.lua | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/apps/plugins/lua/include_lua/printtable.lua b/apps/plugins/lua/include_lua/printtable.lua new file mode 100644 index 0000000000..24c4f73b0a --- /dev/null +++ b/apps/plugins/lua/include_lua/printtable.lua | |||
@@ -0,0 +1,385 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2017 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
24 | |||
25 | require("actions") -- Contains rb.actions & rb.contexts | ||
26 | |||
27 | local _clr = require("color") | ||
28 | local _print = require("print") | ||
29 | local _timer = require("timer") | ||
30 | |||
31 | -- Button definitions -- | ||
32 | local EXIT_BUTTON = rb.PLA_EXIT | ||
33 | local CANCEL_BUTTON = rb.actions.PLA_CANCEL | ||
34 | local DOWN_BUTTON = rb.actions.PLA_DOWN | ||
35 | local DOWNR_BUTTON = rb.actions.PLA_DOWN_REPEAT | ||
36 | local EXIT_BUTTON = rb.actions.PLA_EXIT | ||
37 | local LEFT_BUTTON = rb.actions.PLA_LEFT | ||
38 | local LEFTR_BUTTON = rb.actions.PLA_LEFT_REPEAT | ||
39 | local RIGHT_BUTTON = rb.actions.PLA_RIGHT | ||
40 | local RIGHTR_BUTTON = rb.actions.PLA_RIGHT_REPEAT | ||
41 | local SEL_BUTTON = rb.actions.PLA_SELECT | ||
42 | local SELREL_BUTTON = rb.actions.PLA_SELECT_REL | ||
43 | local SELR_BUTTON = rb.actions.PLA_SELECT_REPEAT | ||
44 | local UP_BUTTON = rb.actions.PLA_UP | ||
45 | local UPR_BUTTON = rb.actions.PLA_UP_REPEAT | ||
46 | |||
47 | -- clamps value to >= min and <= max | ||
48 | local function clamp(iVal, iMin, iMax) | ||
49 | if iMin > iMax then | ||
50 | local swap = iMin | ||
51 | iMin, iMax = iMax, swap | ||
52 | end | ||
53 | |||
54 | if iVal < iMin then | ||
55 | return iMin | ||
56 | elseif iVal < iMax then | ||
57 | return iVal | ||
58 | end | ||
59 | |||
60 | return iMax | ||
61 | end | ||
62 | |||
63 | -------------------------------------------------------------------------------- | ||
64 | --[[ cursor style button routine | ||
65 | -- left / right are x, xi is increment xir is increment when repeat | ||
66 | -- up / down are y, yi is increment yir is increment when repeat | ||
67 | -- cancel is returned as 0,1 | ||
68 | -- select as 0, 1, 2, 3 (none, pressed, repeat, relesed) | ||
69 | -- x_chg and y_chg are the amount x or y changed | ||
70 | -- timeout == nil or -1 loop waits indefinitely till button is pressed | ||
71 | -- time since last button press is returned in ticks.. | ||
72 | -- make xi, xir, yi, yir negative to flip direction... | ||
73 | ]] | ||
74 | |||
75 | local function dpad(x, xi, xir, y, yi, yir, timeout) | ||
76 | _timer("dpad") -- start a persistant timer; keeps time between button events | ||
77 | if timeout == nil then timeout = -1 end | ||
78 | local cancel, select = 0, 0 | ||
79 | local x_chg, y_chg = 0, 0 | ||
80 | local button | ||
81 | while true do | ||
82 | button = rb.get_plugin_action(timeout) | ||
83 | |||
84 | if button == CANCEL_BUTTON then | ||
85 | cancel = 1 | ||
86 | break; | ||
87 | elseif button == EXIT_BUTTON then | ||
88 | cancel = 1 | ||
89 | break; | ||
90 | elseif button == SEL_BUTTON then | ||
91 | select = 1 | ||
92 | timeout = timeout + 1 | ||
93 | elseif button == SELR_BUTTON then | ||
94 | select = 2 | ||
95 | timeout = timeout + 1 | ||
96 | elseif button == SELREL_BUTTON then | ||
97 | select = -1 | ||
98 | timeout = timeout + 1 | ||
99 | elseif button == LEFT_BUTTON then | ||
100 | x_chg = x_chg - xi | ||
101 | elseif button == LEFTR_BUTTON then | ||
102 | x_chg = x_chg - xir | ||
103 | elseif button == RIGHT_BUTTON then | ||
104 | x_chg = x_chg + xi | ||
105 | elseif button == RIGHTR_BUTTON then | ||
106 | x_chg = x_chg + xir | ||
107 | elseif button == UP_BUTTON then | ||
108 | y_chg = y_chg + yi | ||
109 | elseif button == UPR_BUTTON then | ||
110 | y_chg = y_chg + yir | ||
111 | elseif button == DOWN_BUTTON then | ||
112 | y_chg = y_chg - yi | ||
113 | elseif button == DOWNR_BUTTON then | ||
114 | y_chg = y_chg - yir | ||
115 | elseif timeout >= 0 then--and rb.button_queue_count() < 1 then | ||
116 | break; | ||
117 | end | ||
118 | |||
119 | if x_chg ~= 0 or y_chg ~= 0 then | ||
120 | timeout = timeout + 1 | ||
121 | end | ||
122 | end | ||
123 | |||
124 | x = x + x_chg | ||
125 | y = y + y_chg | ||
126 | |||
127 | return cancel, select, x_chg, x, y_chg, y, _timer.check("dpad", true) | ||
128 | end -- dpad | ||
129 | |||
130 | |||
131 | |||
132 | -------------------------------------------------------------------------------- | ||
133 | --[[ prints a scrollable table to the screen; | ||
134 | -- requires a contiguous table with only strings; | ||
135 | -- 1st item in table is the title if hasheader == true | ||
136 | -- returns select item indice if NOT m_sel.. | ||
137 | -- if m_sel == true a table of selected indices are returned ]] | ||
138 | -------------------------------------------------------------------------------- | ||
139 | -- SECOND MODE OF OPERATION -- if co_routine is defined... | ||
140 | -- prints values returned from a resumable factory in a coroutine this allows | ||
141 | -- very large files etc to be displayed.. the downside is it takes time | ||
142 | -- to load data when scrolling also NO multiple selection is allowed | ||
143 | -- table is passed along with the final count t_count | ||
144 | -------------------------------------------------------------------------------- | ||
145 | |||
146 | function print_table(t, t_count, settings) | ||
147 | -- (table, t_count, {hasheader, wrap, m_sel, start, curpos, justify, co_routine}) | ||
148 | |||
149 | if type(t) ~= "table" then | ||
150 | rb.splash(rb.HZ * 5, "table expected got ".. type(t)) | ||
151 | return | ||
152 | end | ||
153 | |||
154 | local wrap, justify, start, curpos, co_routine, hasheader, m_sel | ||
155 | local header_fgc, header_bgc, item_fgc, item_bgc, item_selc | ||
156 | do | ||
157 | local s = settings or _print.get_settings() | ||
158 | wrap, justify = s.wrap, s.justify | ||
159 | start, curpos = s.start, s.curpos | ||
160 | co_routine = s.co_routine | ||
161 | hasheader = s.hasheader | ||
162 | m_sel = false | ||
163 | if co_routine == nil then | ||
164 | --no multi select in incremental mode | ||
165 | m_sel = s.msel | ||
166 | end | ||
167 | header_fgc = s.hfgc or _clr.set( 0, 000, 000, 000) | ||
168 | header_bgc = s.hbgc or _clr.set(-1, 255, 255, 255) | ||
169 | item_fgc = s.ifgc or _clr.set(-1, 000, 255, 060) | ||
170 | item_bgc = s.ibgc or _clr.set( 0, 000, 000, 000) | ||
171 | item_selc = s.iselc or _clr.set( 1, 000, 200, 100) | ||
172 | end | ||
173 | |||
174 | local table_p, line, maxline | ||
175 | |||
176 | local function set_vsb() end -- forward declaration; initialized below | ||
177 | |||
178 | local function init_position(acc_ticks, acc_steps) | ||
179 | if not acc_ticks then acc_ticks = 15 end-- accelerate scroll every this many ticks | ||
180 | if not acc_steps then acc_steps = 5 end -- default steps for an accelerated scroll | ||
181 | |||
182 | return {row = 1, row_scrl= acc_steps, | ||
183 | col = 0, col_scrl = acc_steps, | ||
184 | vcursor = 1, vcursor_min = 1, | ||
185 | acc_ticks = acc_ticks, | ||
186 | acc_steps = acc_steps} | ||
187 | end | ||
188 | |||
189 | local function set_accel(time, scrl, t_p) | ||
190 | if time < t_p.acc_ticks then -- accelerate scroll | ||
191 | scrl = scrl + 1 | ||
192 | else | ||
193 | scrl = t_p.acc_steps | ||
194 | end | ||
195 | return scrl | ||
196 | end | ||
197 | |||
198 | --adds or removes \0 from end of table entry to mark selected items | ||
199 | local function select_item(item) | ||
200 | if item < 1 then item = 1 end | ||
201 | if not t[item] then return end | ||
202 | if t[item]:sub(-1) == "\0" then | ||
203 | t[item] = t[item]:sub(1, -2) -- de-select | ||
204 | else | ||
205 | t[item] = t[item] .. "\0" -- select | ||
206 | end | ||
207 | end | ||
208 | |||
209 | -- displays header text at top | ||
210 | local function disp_header(hstr) | ||
211 | local header = header or hstr | ||
212 | local opts = _print.opt.get() | ||
213 | _print.opt.overflow("none") -- don't scroll header; colors change | ||
214 | _print.opt.color(header_fgc, header_bgc) | ||
215 | _print.opt.line(1) | ||
216 | |||
217 | _print.f() | ||
218 | local line = _print.f(header) | ||
219 | |||
220 | _print.opt.set(opts) | ||
221 | _print.opt.line(2) | ||
222 | return 2 | ||
223 | end | ||
224 | |||
225 | -- gets user input to select items, quit, scroll | ||
226 | local function get_input(t_p) | ||
227 | set_vsb(t_p.row + t_p.vcursor - 1)--t_p.row) | ||
228 | rb.lcd_update() | ||
229 | |||
230 | local quit, select, x_chg, xi, y_chg, yi, timeb = | ||
231 | dpad(t_p.col, -1, -t_p.col_scrl, t_p.row, -1, -t_p.row_scrl) | ||
232 | |||
233 | t_p.vcursor = t_p.vcursor + y_chg | ||
234 | |||
235 | if t_p.vcursor > maxline or t_p.vcursor < t_p.vcursor_min then | ||
236 | t_p.row = yi | ||
237 | end | ||
238 | |||
239 | if wrap == true and (y_chg == 1 or y_chg == -1) then | ||
240 | |||
241 | -- wraps list, stops at end if accelerated | ||
242 | if t_p.row < t_p.vcursor_min - 1 then | ||
243 | t_p.row = t_count - maxline + 1 | ||
244 | t_p.vcursor = maxline | ||
245 | elseif t_p.row + maxline - 1 > t_count then | ||
246 | t_p.row, t_p.vcursor = t_p.vcursor_min - 1, t_p.vcursor_min - 1 | ||
247 | end | ||
248 | end | ||
249 | |||
250 | t_p.row = clamp(t_p.row, 1, math.max(t_count - maxline + 1, 1)) | ||
251 | t_p.vcursor = clamp(t_p.vcursor, t_p.vcursor_min, maxline) | ||
252 | |||
253 | if x_chg ~= 0 then | ||
254 | |||
255 | if x_chg ~= 1 and x_chg ~= -1 then --stop at the center if accelerated | ||
256 | if (t_p.col <= 0 and xi > 0) or (t_p.col >= 0 and xi < 0) then | ||
257 | xi = 0 | ||
258 | end | ||
259 | end | ||
260 | t_p.col = xi | ||
261 | |||
262 | t_p.col_scrl = set_accel(timeb, t_p.col_scrl, t_p) | ||
263 | |||
264 | elseif y_chg ~= 0 then | ||
265 | --t_p.col = 0 -- reset column to the beginning | ||
266 | _print.clear() | ||
267 | _print.opt.sel_line(t_p.vcursor) | ||
268 | |||
269 | t_p.row_scrl = set_accel(timeb, t_p.row_scrl, t_p) | ||
270 | |||
271 | end | ||
272 | |||
273 | if select > 0 and timeb > 15 then --select may be sent multiple times | ||
274 | if m_sel == true then | ||
275 | select_item(t_p.row + t_p.vcursor - 1) | ||
276 | else | ||
277 | return -1, 0, 0, (t_p.row + t_p.vcursor - 1) | ||
278 | end | ||
279 | end | ||
280 | if quit > 0 then return -2, 0, 0, 0 end | ||
281 | return t_p.row, x_chg, y_chg, 0 | ||
282 | end | ||
283 | |||
284 | -- displays the actual table | ||
285 | local function display_table(table_p, col_c, row_c, sel) | ||
286 | local i = table_p.row | ||
287 | while i >= 1 and i <= t_count do | ||
288 | |||
289 | -- only print if beginning or user scrolled up/down | ||
290 | if row_c ~= 0 then | ||
291 | |||
292 | if t[i] == nil and co_routine then | ||
293 | --value has been garbage collected or not created yet | ||
294 | coroutine.resume(co_routine, i) | ||
295 | end | ||
296 | |||
297 | if t[i] == nil then | ||
298 | rb.splash(1, string.format("ERROR %d is nil", i)) | ||
299 | t[i] = "???" | ||
300 | if rb.get_plugin_action(10) == CANCEL_BUTTON then return 0 end | ||
301 | end | ||
302 | |||
303 | if m_sel == true and t[i]:sub(-1) == "\0" then | ||
304 | _print.opt.sel_line(line) | ||
305 | end | ||
306 | |||
307 | if i == 1 and hasheader == true then | ||
308 | line = disp_header(t[1]) | ||
309 | else | ||
310 | line = _print.f("%s", tostring(t[i])) | ||
311 | end | ||
312 | |||
313 | end | ||
314 | |||
315 | i = i + 1 -- important! | ||
316 | |||
317 | if line == 1 or i > t_count or col_c ~= 0 then | ||
318 | _print.opt.column(table_p.col) | ||
319 | i, col_c, row_c, sel = get_input(table_p) | ||
320 | end | ||
321 | |||
322 | rb.button_clear_queue() -- keep the button queue from overflowing | ||
323 | end | ||
324 | return sel | ||
325 | end -- display_table | ||
326 | --============================================================================-- | ||
327 | |||
328 | _print.opt.defaults() | ||
329 | _print.opt.autoupdate(false) | ||
330 | _print.opt.color(item_fgc, item_bgc, item_selc) | ||
331 | |||
332 | table_p = init_position(15, 5) | ||
333 | line, maxline = _print.opt.area(5, 1, rb.LCD_WIDTH - 10, rb.LCD_HEIGHT - 2) | ||
334 | maxline = math.min(maxline, t_count) | ||
335 | |||
336 | -- allow user to start at a position other than the beginning | ||
337 | if start ~= nil then table_p.row = clamp(start, 1, t_count + 1) end | ||
338 | |||
339 | if hasheader == true then | ||
340 | table_p.vcursor_min = 2 -- lowest selectable item | ||
341 | table_p.vcursor = 2 | ||
342 | end | ||
343 | |||
344 | table_p.vcursor = curpos or table_p.vcursor_min | ||
345 | |||
346 | if table_p.vcursor < 1 or table_p.vcursor > maxline then | ||
347 | table_p.vcursor = table_p.vcursor_min | ||
348 | end | ||
349 | |||
350 | _print.opt.sel_line(table_p.vcursor) | ||
351 | _print.opt.overflow("manual") | ||
352 | _print.opt.justify(justify) | ||
353 | |||
354 | -- initialize vertical scrollbar | ||
355 | set_vsb(); do | ||
356 | local vsb =_print.opt.get() | ||
357 | if rb.LCD_DEPTH == 2 then -- invert 2-bit screens | ||
358 | vsb.fg_pattern = 3 - vsb.fg_pattern | ||
359 | vsb.bg_pattern = 3 - vsb.bg_pattern | ||
360 | end | ||
361 | |||
362 | set_vsb = function (item) | ||
363 | if t_count > (maxline or t_count) then | ||
364 | rb.set_viewport(vsb) | ||
365 | item = item or 0 | ||
366 | local m = maxline / 2 + 1 | ||
367 | rb.gui_scrollbar_draw(vsb.width - 5, vsb.y, 5, vsb.height, | ||
368 | t_count, math.max(0, item - m), | ||
369 | math.min(item + m, t_count), 0) | ||
370 | end | ||
371 | end | ||
372 | end -- set_vsb | ||
373 | local selected = display_table(table_p, 0, 1, 0) | ||
374 | |||
375 | _print.opt.defaults() | ||
376 | |||
377 | if m_sel == true then -- walk the table to get selected items | ||
378 | selected = {} | ||
379 | for i = 1, t_count do | ||
380 | if t[i]:sub(-1) == "\0" then table.insert(selected, i) end | ||
381 | end | ||
382 | end | ||
383 | --rb.splash(100, string.format("#1 %d, %d, %d", row, vcursor_pos, sel)) | ||
384 | return selected, table_p.row, table_p.vcursor | ||
385 | end --print_table | ||