summaryrefslogtreecommitdiff
path: root/apps/plugins/lua/include_lua/printtable.lua
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lua/include_lua/printtable.lua')
-rw-r--r--apps/plugins/lua/include_lua/printtable.lua385
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]]
23if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
24
25require("actions") -- Contains rb.actions & rb.contexts
26
27local _clr = require("color")
28local _print = require("print")
29local _timer = require("timer")
30
31-- Button definitions --
32local EXIT_BUTTON = rb.PLA_EXIT
33local CANCEL_BUTTON = rb.actions.PLA_CANCEL
34local DOWN_BUTTON = rb.actions.PLA_DOWN
35local DOWNR_BUTTON = rb.actions.PLA_DOWN_REPEAT
36local EXIT_BUTTON = rb.actions.PLA_EXIT
37local LEFT_BUTTON = rb.actions.PLA_LEFT
38local LEFTR_BUTTON = rb.actions.PLA_LEFT_REPEAT
39local RIGHT_BUTTON = rb.actions.PLA_RIGHT
40local RIGHTR_BUTTON = rb.actions.PLA_RIGHT_REPEAT
41local SEL_BUTTON = rb.actions.PLA_SELECT
42local SELREL_BUTTON = rb.actions.PLA_SELECT_REL
43local SELR_BUTTON = rb.actions.PLA_SELECT_REPEAT
44local UP_BUTTON = rb.actions.PLA_UP
45local UPR_BUTTON = rb.actions.PLA_UP_REPEAT
46
47-- clamps value to >= min and <= max
48local 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
61end
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
75local 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)
128end -- 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
146function 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
385end --print_table