lua add demo scripts, atexit handler, gui_scrollbar_draw

Change-Id: Ie8794e8a487f73952dae43e036787b6972fdbbee
This commit is contained in:
William Wilgus 2019-07-26 01:30:00 -05:00
parent 60c5a29408
commit 90118f14cf
22 changed files with 2374 additions and 22 deletions

View file

@ -0,0 +1,116 @@
--dbgettags.lua Bilgus 2018
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 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.
*
****************************************************************************/
]]
require("actions")
local CANCEL_BUTTON = rb.actions.PLA_CANCEL
local sINVALIDDATABASE = "Invalid Database"
local sERROROPENING = "Error opening"
-- tag cache header
local sTCVERSION = string.char(0x0F)
local sTCHEADER = string.reverse("TCH" .. sTCVERSION)
local DATASZ = 4 -- int32_t
local TCHSIZE = 3 * DATASZ -- 3 x int32_t
local function bytesLE_n(str)
str = str or ""
local tbyte={str:byte(1, -1)}
local bpos = 1
local num = 0
for k = 1,#tbyte do -- (k = #t, 1, -1 for BE)
num = num + tbyte[k] * bpos
bpos = bpos * 256
end
return num
end
-- uses database files to retrieve database tags
-- adds all unique tags into a lua table
function get_tags(filename, hstr)
if not filename then return end
hstr = hstr or filename
local file = io.open('/' .. filename or "", "r") --read
if not file then rb.splash(100, sERROROPENING .. " " .. filename) return end
local fsz = file:seek("end")
local posln = 0
local tag_len = TCHSIZE
local idx
local function readchrs(count)
if posln >= fsz then return nil end
file:seek("set", posln)
posln = posln + count
return file:read(count)
end
local tagcache_header = readchrs(DATASZ) or ""
local tagcache_sz = readchrs(DATASZ) or ""
local tagcache_entries = readchrs(DATASZ) or ""
if tagcache_header ~= sTCHEADER or
bytesLE_n(tagcache_sz) ~= (fsz - TCHSIZE) then
rb.splash(100, sINVALIDDATABASE .. " " .. filename)
return
end
-- local tag_entries = bytesLE_n(tagcache_entries)
local ftable = {}
table.insert(ftable, 1, hstr)
local tline = #ftable + 1
ftable[tline] = ""
local str = ""
while true do
tag_len = bytesLE_n(readchrs(DATASZ))
readchrs(DATASZ) -- idx = bytesLE_n(readchrs(DATASZ))
str = readchrs(tag_len) or ""
str = string.match(str, "(%Z+)%z")
if str then
if ftable[tline - 1] ~= str then -- Remove dupes
ftable[tline] = str
tline = tline + 1
end
elseif posln >= fsz then
break
end
if rb.get_plugin_action(0) == CANCEL_BUTTON then
break
end
end
file:close()
return ftable
end -- get_tags

View file

@ -0,0 +1,107 @@
--Bilgus 12-2016
--revisited 8-2019
require "actions"
require "buttons"
require "sound"
require "audio"
TIMEOUT = 0
local SOUND_VOLUME = rb.sound_settings.SOUND_VOLUME
rb.sound_settings = nil
package.loaded["sound_defines"] = nil
function say_msg(message, timeout)
rb.splash(1, message)
rb.sleep(timeout * rb.HZ)
end
function say_value(value,message,timeout)
local message = string.format(message .. "%d", value)
say_msg(message, timeout)
end
function ShowMainMenu() -- we invoke this function every time we want to display the main menu of the script
local s = 0
local mult = 1
local unit = " Minutes"
while s == 0 or s == 5 do -- don't exit of program until user selects Exit
if mult < 1 then
mult = 1
s = 0
end
mainmenu = {"More", mult * 1 .. unit, mult * 5 .. unit, mult * 10 .. unit, mult * 15 .. unit, "Less", "Exit"} -- define the items of the menu
s = rb.do_menu("Reduce volume + sleep over", mainmenu, s, false) -- actually tell Rockbox to draw the menu
-- In the line above: "Test" is the title of the menu, mainmenu is an array with the items
-- of the menu, nil is a null value that needs to be there, and the last parameter is
-- whether the theme should be drawn on the menu or not.
-- the variable s will hold the index of the selected item on the menu.
-- the index is zero based. This means that the first item is 0, the second one is 1, etc.
if s == 0 then mult = mult + 1
elseif s == 1 then TIMEOUT = mult
elseif s == 2 then TIMEOUT = mult * 5
elseif s == 3 then TIMEOUT = mult * 10
elseif s == 4 then TIMEOUT = mult * 15
elseif s == 5 then mult = mult - 1 -- User selected to exit
elseif s == 6 then os.exit() -- User selected to exit
elseif s == -2 then os.exit() -- -2 index is returned from do_menu() when user presses the key to exit the menu (on iPods, it's the left key).
-- In this case, user probably wants to exit (or go back to last menu).
else rb.splash(2 * rb.HZ, "Error! Selected index: " .. s) -- something strange happened. The program shows this message when
-- the selected item is not on the index from 0 to 3 (in this case), and displays
-- the selected index. Having this type of error handling is not
-- required, but it might be nice to have Especially while you're still
-- developing the plugin.
end
end
end
ShowMainMenu()
rb.set_sleeptimer_duration(TIMEOUT)
rb.lcd_clear_display()
rb.lcd_update()
local volume = rb.sound_current(SOUND_VOLUME)
local vol_min = rb.sound_min(SOUND_VOLUME)
local volsteps = -(vol_min - volume)
local seconds = (TIMEOUT * 60) / volsteps
local sec_left = (TIMEOUT * 60)
local hb = 0
local action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
if rb.audio_status() == 1 then
while ((volume > vol_min) and (action ~= rb.actions.ACTION_STD_CANCEL)) do
rb.lcd_clear_display()
say_value(volume,sec_left .. " Sec, Volume: ", 1)
local i = seconds * 2
while ((i > 0) and (action ~= rb.actions.ACTION_STD_CANCEL)) do
i = i - 1
rb.lcd_drawline(hb, 1, hb, 1)
rb.lcd_update()
if hb >= rb.LCD_WIDTH then
hb = 0
rb.lcd_clear_display()
say_value(volume,sec_left .. " Sec, Volume: ", 1)
end
hb = hb + 1
rb.sleep(rb.HZ / 2)
action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
rb.yield()
end
volume = volume - 1
rb.sound_set(SOUND_VOLUME, volume);
sec_left = sec_left - seconds
end
rb.audio_stop()
rb.lcd_clear_display()
rb.lcd_update()
os.exit(1, "Playback Stopped")
else
rb.lcd_clear_display()
rb.lcd_update()
os.exit(2, "Nothing is playing")
end

View file

@ -0,0 +1,190 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 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.
*
****************************************************************************/
]]
if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end
require("printtable")
local _lcd = require("lcd")
local _timer = require("timer")
--------------------------------------------------------------------------------
--[[ returns a sorted tables of directories and (another) of files
-- path is the starting path; norecurse == true.. only that path will be searched
-- findfile & finddir are definable search functions
-- if not defined all files/dirs are returned if false is passed.. none
-- or you can provide your own function see below..
-- f_t and d_t allow you to pass your own tables for re-use but isn't necessary
]]
local function get_files(path, norecurse, finddir, findfile, f_t, d_t)
local quit = false
local files = f_t or {}
local dirs = d_t or {}
local function f_filedir(name)
--default find function
-- example: return name:find(".mp3", 1, true) ~= nil
if name:len() <= 2 and (name == "." or name == "..") then
return false
end
return true
end
local function d_filedir(name)
--default discard function
return false
end
if finddir == nil then
finddir = f_filedir
elseif type(finddir) ~= "function" then
finddir = d_filedir
end
if findfile == nil then
findfile = f_filedir
elseif type(findfile) ~= "function" then
findfile = d_filedir
end
local function _get_files(path, cancelbtn)
local sep = ""
if string.sub(path, - 1) ~= "/" then sep = "/" end
for fname, isdir in luadir.dir(path) do
if isdir and finddir(fname) then
table.insert(dirs, path .. sep ..fname)
elseif not isdir and findfile(fname) then
table.insert(files, path .. sep ..fname)
end
if rb.get_plugin_action(0) == cancelbtn then
return true
end
end
end
local function cmp_alphanum (op1, op2)
local type1= type(op1)
local type2 = type(op2)
if type1 ~= type2 then
return type1 < type2
else
if type1 == "string" then
op1 = op1:upper()
op2 = op2:upper()
end
return op1 < op2
end
end
_lcd:splashf(1, "Searching for Files")
table.insert(dirs, path) -- root
for key,value in pairs(dirs) do
--luadir.dir may error out so we need to do the call protected
_, quit = pcall(_get_files, value, CANCEL_BUTTON)
if quit == true or norecurse then
break;
end
end
table.sort(files, cmp_alphanum)
table.sort(dirs, cmp_alphanum)
return dirs, files
end -- get_files
--------------------------------------------------------------------------------
-- uses print_table and get_files to display simple file browser
function file_choose(dir, title)
local dstr, hstr = ""
if not title then
dstr = "%d items found in %0d.%02d seconds"
else
hstr = title
end
-- returns whole seconds and remainder
local function tick2seconds(ticks)
local secs = (ticks / rb.HZ)
local csecs = (ticks - (secs * rb.HZ))
return secs, csecs
end
local norecurse = true
local f_finddir = nil -- function to match directories; nil all, false none
local f_findfile = nil -- function to match files; nil all, false none
local p_settings = {wrap = true, hasheader = true}
local timer
local files = {}
local dirs = {}
local item = 1
_lcd:clear()
while item > 0 do
if not title then
timer = _timer()
end
dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, dirs, files)
local parentdir = dirs[1]
for i = 1, #dirs do
dirs[i] = "\t" .. dirs[i]
end
for i = 1, #files do
table.insert(dirs, "\t" .. files[i])
end
for i=1, #files do files[i] = nil end -- empty table for reuse
if not title then
hstr = string.format(dstr, #dirs - 1, tick2seconds(timer:stop()))
end
table.insert(dirs, 1, hstr)
item = print_table(dirs, #dirs, p_settings)
-- If item was selected follow directory or return filename
if item > 0 then
dir = string.gsub(dirs[item], "%c+","")
if not rb.dir_exists("/" .. dir) then
return dir
end
end
if dir == parentdir then
dir = dir:sub(1, dir:match(".*()/") - 1)
if dir == "" then dir = "/" end
end
for i=1, #dirs do dirs[i] = nil end -- empty table for reuse
end
end -- file_choose
--------------------------------------------------------------------------------

View file

@ -0,0 +1,79 @@
--[[
__________ __ ___.
Open \______ \ ____ ____ | | _\_ |__ _______ ___
Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
\/ \/ \/ \/ \/
$Id$
Example Lua File Viewer script
Copyright (C) 2017 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.
]]--
require("actions") -- Contains rb.actions & rb.contexts
-- require("buttons") -- Contains rb.buttons -- not needed for this example
--local _timer = require("timer")
--local _clr = require("color") -- clrset, clrinc provides device independent colors
local _lcd = require("lcd") -- lcd helper functions
--local _print = require("print") -- advanced text printing
--local _img = require("image") -- image manipulation save, rotate, resize, tile, new, load
--local _blit = require("blit") -- handy list of blit operations
--local _draw = require("draw") -- draw all the things (primitives)
--local _math = require("math_ex") -- missing math sine cosine, sqrt, clamp functions
local scrpath = rb.current_path()--rb.PLUGIN_DIR .. "/demos/lua_scripts/"
package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path
require("printmenu") --menu
require("filebrowse") -- file browser
require("fileviewers") -- fileviewer, hexviewer
rb.actions = nil
package.loaded["actions"] = nil
-- uses print_table to display a menu
function main_menu()
local mt = {
[1] = "Rocklua File View Example",
[2] = "File View",
[3] = "File Hex View",
[4] = "Simple Browser",
[5] = "Exit"
}
local ft = {
[0] = exit_now, --if user cancels do this function
[1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot
[2] = function(VIEWF) -- view file
print_file_increment(file_choose("/", "Choose File"))
end,
[3] = function(VHEXF) -- view hex
print_file_hex(file_choose("/", "Choose File"), 8)
end,
[4] = function(BROWS) -- file browser
_lcd:splashf(rb.HZ, "%s", file_choose("/") or "None")
end,
[5] = function(EXIT_) return true end
}
print_menu(mt, ft)
end
function exit_now()
_lcd:update()
os.exit()
end -- exit_now
main_menu()
exit_now()

View file

@ -0,0 +1,465 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 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.
*
****************************************************************************/
]]
if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end
require("printtable")
local _clr = require("color")
local _lcd = require("lcd")
local _print = require("print")
local _timer = require("timer")
require("actions")
local CANCEL_BUTTON = rb.actions.PLA_CANCEL
--------------------------------------------------------------------------------
-- builds an index of byte position of every line at each bufsz increment
-- in filename; bufsz == 1 would be every line; saves to filename.ext.idx_ext
-- lnbyte should be nil for text files and number of bytes per line for binary
local function build_file_index(filename, idx_ext, bufsz, lnbyte)
if not filename then return end
local file = io.open('/' .. filename, "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
local fsz = file:seek("end")
local fsz_kb = fsz / 1024
local count
local ltable = {0} --first index is the beginning of the file
local timer = _timer()
local fread
_lcd:splashf(100, "Indexing file %d Kb", (fsz / 1024))
if lnbyte then
fread = function(f) return f:read(lnbyte) end
else
lnbyte = -1
fread = function(f) return f:read("*l") end
end
file:seek("set", 0)
for i = 1, fsz do
if i % bufsz == 0 then
local loc = file:seek()
ltable[#ltable + 1] = loc
_lcd:splashf(1, "Parsing %d of %d Kb", loc / 1024, fsz_kb)
end
if rb.get_plugin_action(0) == CANCEL_BUTTON then
return
end
if not fread(file) then
count = i
break
end
end
local fileidx = io.open('/' .. filename .. idx_ext, "w+") -- write/erase
if fileidx then
fileidx:write(fsz .. "\n")
fileidx:write(count .. "\n")
fileidx:write(bufsz .. "\n")
fileidx:write(lnbyte .. "\n")
fileidx:write(table.concat(ltable, "\n"))
fileidx:close()
_lcd:splashf(100, "Finished in %d seconds", timer.stop() / rb.HZ)
collectgarbage("collect")
else
error("unable to save index file")
end
end -- build_file_index
--------------------------------------------------------------------------------
--- returns size of original file, total lines buffersize, and table filled
-- with line offsets in index file -> filename
local function load_index_file(filename)
local filesz, count, bufsz, lnbyte
local ltable
local fileidx = io.open('/' .. filename, "r") --read
if fileidx then
local idx = -3
ltable = {}
fileidx:seek("set", 0)
for line in fileidx:lines() do
if idx == -3 then
filesz = tonumber(line)
elseif idx == -2 then
count = tonumber(line)
elseif idx == -1 then
bufsz = tonumber(line)
elseif idx == 0 then
lnbyte = tonumber(line)
else
ltable[idx] = tonumber(line)
end
idx = idx + 1
end
fileidx:close()
end
return lnbyte, filesz, count, bufsz, ltable
end -- load_index_file
--------------------------------------------------------------------------------
-- creates a fixed index with fixed line lengths, perfect for viewing hex files
-- not so great for reading text files but works as a fallback
local function load_fixed_index(bytesperline, filesz, bufsz)
local lnbyte = bytesperline
local count = (filesz + lnbyte - 1) / lnbyte + 1
local idx_t = {} -- build index
for i = 0, filesz, bufsz do
idx_t[#idx_t + 1] = lnbyte * i
end
return lnbyte, filesz, count, bufsz, idx_t
end -- load_fixed_index
--------------------------------------------------------------------------------
-- uses print_table to display a whole file
function print_file(filename, maxlinelen, settings)
if not filename then return end
local file = io.open('/' .. filename or "", "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
maxlinelen = 33
local hstr = filename
local ftable = {}
table.insert(ftable, 1, hstr)
local tline = #ftable + 1
local remln = maxlinelen
local posln = 1
for line in file:lines() do
if line then
if maxlinelen then
if line == "" then
ftable[tline] = ftable[tline] or ""
tline = tline + 1
remln = maxlinelen
else
line = line:match("%w.+") or ""
end
local linelen = line:len()
while linelen > 0 do
local fsp = line:find("%s", posln + remln - 5) or 0x0
fsp = fsp - (posln + remln)
if fsp >= 0 then
local fspr = fsp
fsp = line:find("%s", posln + remln) or linelen
fsp = fsp - (posln + remln)
if math.abs(fspr) < fsp then fsp = fspr end
end
if fsp > 5 or fsp < -5 then fsp = 0 end
local str = line:sub(posln, posln + remln + fsp)
local slen = str:len()
ftable[tline] = ftable[tline] or ""
ftable[tline] = ftable[tline] .. str
linelen = linelen - slen
if linelen > 0 then
tline = tline + 1
posln = posln + slen
remln = maxlinelen
--loop continues
else
ftable[tline] = ftable[tline] .. " "
remln = maxlinelen - slen
posln = 1
--loop ends
end
end
else
ftable[#ftable + 1] = line
end
end
end
file:close()
_lcd:clear()
_print.clear()
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
settings.msel = true
end
settings.hasheader = true
settings.co_routine = nil
local sel =
print_table(ftable, #ftable, settings)
_lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", "))
ftable = nil
end -- print_file
--------------------------------------------------------------------------------
-- uses print_table to display a portion of a file
function print_file_increment(filename, settings)
if not filename then return end
local file = io.open('/' .. filename, "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
local fsz = file:seek("end")
local bsz = 1023
--if small file do it the easier way and load whole file to table
if fsz < 60 * 1024 then
file:close()
print_file(filename, settings)
return
end
local ext = ".idx"
local lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext)
if not idx_t or fsz ~= filesz then -- build file index
build_file_index(filename, ext, bsz)
lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext)
end
-- if invalid or user canceled creation fallback to a fixed index
if not idx_t or fsz ~= filesz or count <= 0 then
_lcd:splashf(rb.HZ * 5, "Unable to read file index %s", filename .. ext)
lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(32, fsz, bsz)
end
if not idx_t or fsz ~= filesz or count <= 0 then
_lcd:splashf(rb.HZ * 5, "Unable to load file %s", filename)
return
end
local hstr = filename
local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values
-- this allows them to be garbage collected as space is needed
-- rebuilds when needed
local ovf = 0
local lpos = 1
local timer = _timer()
file:seek("set", 0)
function print_co()
while true do
collectgarbage("step")
file_t[1] = hstr --position 1 is ALWAYS header/title
for i = 1, bufsz + ovf do
file_t[lpos + i] = file:read ("*l")
end
ovf = 0
lpos = lpos + bufsz
local bpos = coroutine.yield()
if bpos <= lpos then -- roll over or scroll up
bpos = (bpos - bufsz) + bpos % bufsz
timer:check(true)
end
lpos = bpos - bpos % bufsz
if lpos < 1 then
lpos = 1
elseif lpos > count - bufsz then -- partial fill
ovf = count - bufsz - lpos
end
--get position in file of the nearest indexed line
file:seek("set", idx_t[bpos / bufsz + 1])
-- on really large files if it has been more than 10 minutes
-- since the user scrolled up the screen wipe out the prior
-- items to free memory
if lpos % 5000 == 0 and timer:check() > rb.HZ * 600 then
for i = 1, lpos - 100 do
file_t[i] = nil
end
end
end
end
co = coroutine.create(print_co)
_lcd:clear()
_print.clear()
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
end
settings.hasheader = true
settings.co_routine = co
settings.msel = false
table.insert(file_t, 1, hstr) --position 1 is header/title
local sel =
print_table(file_t, count, settings)
file:close()
idx_t = nil
file_t = nil
return sel
end --print_file_increment
--------------------------------------------------------------------------------
function print_file_hex(filename, bytesperline, settings)
if not filename then return end
local file = io.open('/' .. filename, "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
local hstr = filename
local bpl = bytesperline
local fsz = file:seek("end")
--[[
local filesz = file:seek("end")
local bufsz = 1023
local lnbyte = bytesperline
local count = (filesz + lnbyte - 1) / lnbyte + 1
local idx_t = {} -- build index
for i = 0, filesz, bufsz do
idx_t[#idx_t + 1] = lnbyte * i
end]]
local lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(bpl, fsz, 1023)
local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values
-- this allows them to be garbage collected as space is needed
-- rebuilds when needed
local ovf = 0
local lpos = 1
local timer = _timer()
file:seek("set", 0)
function hex_co()
while true do
collectgarbage("step")
file_t[1] = hstr --position 1 is ALWAYS header/title
for i = 1, bufsz + ovf do
local pos = file:seek()
local s = file:read (lnbyte)
if not s then -- EOF
file_t[lpos + i] = ""
break;
end
local s_len = s:len()
if s_len > 0 then
local fmt = "0x%04X: " .. string.rep("%02X ", s_len)
local schrs = " " .. s:gsub("(%c)", " . ")
file_t[lpos + i] = string.format(fmt, pos, s:byte(1, s_len)) ..
schrs
else
file_t[lpos + i] = string.format("0x%04X: ", pos)
end
end
ovf = 0
lpos = lpos + bufsz
local bpos = coroutine.yield()
if bpos < lpos then -- roll over or scroll up
bpos = (bpos - bufsz) + bpos % bufsz
timer:check(true)
end
lpos = bpos - bpos % bufsz
if lpos < 1 then
lpos = 1
elseif lpos > count - bufsz then -- partial fill
ovf = count - bufsz - lpos
end
--get position in file of the nearest indexed line
file:seek("set", idx_t[bpos / bufsz + 1])
-- on really large files if it has been more than 10 minutes
-- since the user scrolled up the screen wipe out the prior
-- items to free memory
if lpos % 10000 == 0 and timer:check() > rb.HZ * 600 then
for i = 1, lpos - 100 do
file_t[i] = nil
end
end
end
end
co = coroutine.create(hex_co)
local function repl(char)
local ret = ""
if char:sub(1,2) == "0x" then
return string.format("%dd:", tonumber(char:sub(3, -2), 16))
else
return string.format("%03d ", tonumber(char, 16))
end
end
_lcd:clear()
_print.clear()
local sel, start, vcur = 1
table.insert(file_t, 1, hstr) --position 1 is header/title
if not settings then
settings = {}
settings.justify = "left"
settings.wrap = true
settings.msel = false
settings.hfgc = _clr.set( 0, 000, 000, 000)
settings.hbgc = _clr.set(-1, 255, 255, 255)
settings.ifgc = _clr.set(-1, 255, 255, 255)
settings.ibgc = _clr.set( 0, 000, 000, 000)
settings.iselc = _clr.set( 1, 000, 200, 100)
end
settings.hasheader = true
settings.co_routine = co
settings.start = start
settings.curpos = vcur
while sel > 0 do
settings.start = start
settings.curpos = vcur
sel, start, vcur = print_table(file_t, count, settings)
if sel > 1 and file_t[sel] then -- flips between hex and decimal
local s = file_t[sel]
if s:sub(-1) == "\b" then
file_t[sel] = nil
ovf = -(bufsz - 1)
coroutine.resume(co, sel) --rebuild this item
else
s = s:gsub("(0x%x+:)", repl) .. "\b"
file_t[sel] = s:gsub("(%x%x%s)", repl) .. "\b"
end
end
end
file:close()
idx_t = nil
file_t = nil
return sel
end -- print_file_hex
--------------------------------------------------------------------------------

View file

@ -0,0 +1,24 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# $Id$
#
LUASCR_SRCDIR := $(APPSDIR)/plugins/lua_scripts
LUASCR_BUILDDIR := $(BUILDDIR)/apps/plugins/lua_scripts
LUASCRS := $(wildcard $(LUASCR_SRCDIR)/*.lua)
#DUMMY := $(info [${LUASCRS}])
DUMMY : all
all: $(subst $(LUASCR_SRCDIR)/,$(LUASCR_BUILDDIR)/,$(LUASCRS))
$(LUASCR_BUILDDIR)/%.lua: $(LUASCR_SRCDIR)/%.lua | $(LUASCR_BUILDDIR)
$(call PRINTS,CP $(subst $(LUASCR_SRCDIR)/,,$<))cp $< $@
$(LUASCR_BUILDDIR):
$(call PRINTS,MKDIR $@)mkdir -p $(LUASCR_BUILDDIR)/

View file

@ -0,0 +1,304 @@
--RB LUA show all global variables; BILGUS
require "actions"
require "audio"
require "buttons"
require "color"
require "draw"
require "image"
require "lcd"
require "math_ex"
require "pcm"
require "playlist"
require "print"
--require "settings" --uses a lot of memory
require "sound"
collectgarbage("collect")
local sDumpFile = "/rb-lua_functions.txt"
local filehandle
local function a2m_m2a(addr_member)
--turns members into addresses; addresses back into members
return addr_member
end
local function dtTag(sType)
--convert named type; 'number'.. to short type '[n]...'
--if '?' supplied print out datatype key; number = [n]...
local retType = "?"
local typ = {
["nil"] = "nil",
["boolean"] = "b",
["number"] = "n",
["string"] = "s",
["userdata"] = "u",
["function"] = "f",
["thread"] = "thr",
["table"] = "t"
}
if sType == "?" then retType = "Datatypes: " end
for k,v in pairs(typ) do
if sType == k then
retType = v break
elseif (sType == "?") then
retType = retType .. " [" ..v.. "] = " .. k
end
end
return " [" ..retType.. "] "
end
local function tableByName(tName)
--find the longest match possible to an actual table
--Name comes in as (table) tName.var so we can pass back out the name found PITA
--returns the table found (key and value)
local ld = {}
local sMatch = ""
local kMatch = nil
local vMatch = nil
----FUNCTIONS for tableByName -----------------------------------------------------
local function search4Str(n, k, v)
local sKey = tostring(k)
if string.find (n, sKey,1,true) then
if sKey:len() > sMatch:len() then sMatch = sKey kMatch = k vMatch = v end
--find the longest match we can
end
end
----END FUNCTIONS for tableByName -------------------------------------------------
if tName.val ~= nil and tName.val ~= "" then
for k, v in pairs(_G) do
--_G check both since some tables are only in _G or package.loaded
search4Str(tName.val, k, v)
end
for k, v in pairs(package.loaded) do --package.loaded
search4Str(tName.val, k, v)
end
if not string.find (sMatch, "_G",1,true) then sMatch = "_G." .. sMatch end
-- put the root _G in if not exist
if kMatch and vMatch then ld[kMatch] = vMatch tName.val = sMatch return ld end
end
tName.val = "_G"
return package.loaded --Not Found return default
end
local function dump_Tables(tBase, sFunc, tSeen, tRet)
--Based on: http://www.lua.org/cgi-bin/demo?globals
--Recurse through tBase tables copying all found Tables
local sSep=""
local ld={}
local tNameBuf = {}
local sName
if sFunc ~= "" then sSep = "." end
for k, v in pairs(tBase) do
k = tostring(k)
tNameBuf[1] = sFunc
tNameBuf[2] = sSep
tNameBuf[3] = k
if k ~= "loaded" and type(v) == "table" and not tSeen[v] then
tSeen[v]=sFunc
sName = table.concat(tNameBuf)
tRet[sName] = a2m_m2a(v) --place all keys into ld[i]=value
dump_Tables(v, sName, tSeen, tRet)
elseif type(v) == "table" and not tSeen[v] then
tSeen[v]=sFunc
tRet[table.concat(tNameBuf)] = a2m_m2a(v) -- dump 'loaded' table
for k1, v1 in pairs(v) do
if not _G[k1] and type(v1) == "table" and not tSeen[v1] then
-- dump tables that are loaded but not global
tSeen[v1]=sFunc
tNameBuf[3] = k1
sName = table.concat(tNameBuf)
tRet[sName] = a2m_m2a(v1) --place all keys into ld[i]=value
dump_Tables(v1, sName, tSeen, tRet)
end
end
end
end
end
local function dump_Functions(tBase)
--Based on: http://www.lua.org/cgi-bin/demo?globals
--We already recursed through tBase copying all found tables
--we look up the table by name and then (ab)use a2m_m2a() to load the address
--after finding the table by address in tBase we will
--put the table address of tFuncs in its place
local tFuncBuf = {}
for k,v in pairs(tBase) do
local tTable = a2m_m2a(v)
local tFuncs = {}
for key, val in pairs(tTable) do
if key ~= "loaded" then
tFuncBuf[1] = dtTag(type(val))
tFuncBuf[2] = tostring(key)
tFuncs[table.concat(tFuncBuf)]= val
--put the name and value in our tFuncs table
end
end
tBase[k] = a2m_m2a(tFuncs) -- copy the address back to tBase
end
end
local function get_common_branches(t, tRet)
--load t 'names(values)' into keys
--strip off long paths then iterate value if it exists
--local tRet={}
local sBranch = ""
local tName = {}
for k in pairs(t) do
tName["val"]=k
tableByName(tName)
sBranch = tName.val
if tRet[sBranch] == nil then
tRet[sBranch] = 1 --first instance of this branch
else
tRet[sBranch] = tRet[sBranch] + 1
end
end
end
local function pairsByPairs (t, tkSorted)
--tkSorted should be an already sorted (i)table with t[keys] in the values
--https://www.lua.org/pil/19.3.html
--!!Note: table sort default function does not like numbers as [KEY]!!
--see *sortbyKeys*cmp_alphanum*
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if tkSorted[i] == nil then return nil
else return tkSorted[i], t[tkSorted[i]]
end
end
return iter
end
local function sortbyKeys(t, tkSorted)
--loads keys of (t) into values of tkSorted
--and then sorts them
--tkSorted has integer keys (see ipairs)
----FUNCTIONS for sortByKeys -------------
local cmp_alphanum = function (op1, op2)
local type1= type(op1)
local type2 = type(op2)
if type1 ~= type2 then
return type1 < type2
else
return op1 < op2
end
end
----END FUNCTIONS for sortByKeys ---------
for n in pairs(t) do table.insert(tkSorted, n) end
table.sort(tkSorted, cmp_alphanum)--table.sort(tkSorted)
end
local function funcprint(tBuf, strName, value)
local sType = type(value)
local sVal = ""
local sHex = ""
tBuf[#tBuf + 1] = "\t"
tBuf[#tBuf + 1] = strName
if nil ~= string.find (";string;number;userdata;boolean;", sType, 1, true) then
--If any of the above types print the contents of variable
sVal = tostring(value)
if type(value) == "number" then
sHex = " = 0x" .. string.format("%x", value)
else
sHex = ""
sVal = string.gsub(sVal, "\n", "\\n") --replace newline with \n
end
tBuf[#tBuf + 1] = " : "
tBuf[#tBuf + 1] = sVal
tBuf[#tBuf + 1] = sHex
end
tBuf[#tBuf + 1] = "\r\n"
end
local function errorHandler( err )
filehandle:write(" ERROR:" .. err .. "\n")
end
------------MAIN----------------------------------------------------------------
local _NIL = nil
local tSeen= {}
local tcBase = {}
local tkSortCbase = {}
local tMods= {}
local tkSortMods = {}
local tWriteBuf = {}
local n = 0 -- count of how many items were found
filehandle = io.open(sDumpFile, "w+") --overwrite
tWriteBuf[#tWriteBuf + 1] = "*Loaded Modules* \n"
xpcall( function()
dump_Tables(tableByName({["val"] = "_G"}),"", tSeen, tMods)
--you can put a table name here if you just wanted to display
--only its items, ex. "os" or "rb" or "io"
--However, it has to be accessible directly from _G
--so "rb.actions" wouldn't return anything since its technically
--enumerated through _G.rb
end , errorHandler )
tSeen = nil
xpcall( function()dump_Functions(tMods)end , errorHandler )
get_common_branches(tMods, tcBase)
sortbyKeys(tcBase, tkSortCbase)
sortbyKeys(tMods, tkSortMods)
for k, v in pairsByPairs(tcBase, tkSortCbase ) do
n = n + 1
if n ~= 1 then
tWriteBuf[#tWriteBuf + 1] = ", "
end
tWriteBuf[#tWriteBuf + 1] = tostring(k)
if n >= 3 then -- split loaded modules to multiple lines
n = 0
tWriteBuf[#tWriteBuf + 1] = "\r\n"
end
if #tWriteBuf > 25 then
filehandle:write(table.concat(tWriteBuf))
for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table
end
end
tcBase= nil tkSortCbase= nil
tWriteBuf[#tWriteBuf + 1] = "\r\n"
tWriteBuf[#tWriteBuf + 1] = dtTag("?")
tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n"
tWriteBuf[#tWriteBuf + 1] = "Functions: \r\n"
n = 0
for key, val in pairsByPairs(tMods, tkSortMods) do
local tkSorted = {}
local tFuncs = a2m_m2a(val)
sortbyKeys(tFuncs, tkSorted)
tWriteBuf[#tWriteBuf + 1] = "\r\n"
tWriteBuf[#tWriteBuf + 1] = tostring(key)
tWriteBuf[#tWriteBuf + 1] = "\r\n"
for k, v in pairsByPairs(tFuncs, tkSorted) do
n = n + 1
funcprint(tWriteBuf, k,v)
if #tWriteBuf > 25 then
filehandle:write(table.concat(tWriteBuf))
for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table
end
end
end
tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n"
tWriteBuf[#tWriteBuf + 1] = n
tWriteBuf[#tWriteBuf + 1] = " Items Found \r\n"
filehandle:write(table.concat(tWriteBuf))
for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- empty table
filehandle:close()
rb.splash(rb.HZ * 5, n .. " Items dumped to : " .. sDumpFile)
--rb.splash(500, collectgarbage("count"))

View file

@ -0,0 +1,83 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 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.
*
****************************************************************************/
]]
if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
require("printtable")
local _clr = require("color")
local _LCD = rb.lcd_framebuffer()
--------------------------------------------------------------------------------
-- displays text in menu_t calls function in same indice of func_t when selected
function print_menu(menu_t, func_t, selected, settings, copy_screen)
local i, start, vcur, screen_img
if selected then vcur = selected + 1 end
if vcur and vcur <= 1 then vcur = 2 end
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
settings.hfgc = _clr.set( 0, 000, 000, 000)
settings.hbgc = _clr.set(-1, 255, 255, 255)
settings.ifgc = _clr.set(-1, 000, 255, 060)
settings.ibgc = _clr.set( 0, 000, 000, 000)
settings.iselc = _clr.set( 1, 000, 200, 100)
settings.default = true
end
settings.hasheader = true
settings.co_routine = nil
settings.msel = false
settings.start = start
settings.curpos = vcur
while not i or i > 0 do
if copy_screen == true then
--make a copy of screen for restoration
screen_img = screen_img or rb.new_image()
screen_img:copy(_LCD)
else
screen_img = nil
end
_LCD:clear(settings.ibgc)
settings.start = start
settings.curpos = vcur
i, start, vcur = print_table(menu_t, #menu_t, settings)
--vcur = vcur + 1
collectgarbage("collect")
if copy_screen == true then _LCD:copy(screen_img) end
if func_t and func_t[i] then
if func_t[i](i, menu_t) == true then break end
else
break
end
end
if settings.default == true then settings = nil end
return screen_img
end

View file

@ -0,0 +1,344 @@
-- BILGUS 2018
--local scrpath = rb.current_path()"
--package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path
require("printmenu")
require("printtable")
require("dbgettags")
local _print = require("print")
rb.actions = nil
package.loaded["actions"] = nil
local sERROROPENING = "Error opening"
local sERRORMENUENTRY = "Error finding menu entry"
local sBLANKLINE = "##sBLANKLINE##"
local sDEFAULTMENU = "customfilter"
local sFILEOUT = "/.rockbox/tagnavi_custom.config"
local sFILEHEADER = "#! rockbox/tagbrowser/2.0"
local sMENUSTART = "%menu_start \"custom\" \"Database\""
local sMENUTITLE = "title = \"fmt_title\""
local TAG_ARTIST, TAG_ALBARTIST, TAG_ALBUM, TAG_GENRE, TAG_COMPOSER = 1, 2, 3, 4, 5
local ts_TAGTYPE = {"Artist", "AlbumArtist", "Album", "Genre", "Composer"}
local ts_DBPATH = {"database_0.tcd", "database_7.tcd", "database_1.tcd", "database_2.tcd", "database_5.tcd"}
local COND_OR, COND_AND, COND_NOR, COND_NAND = 1, 2, 3, 4
local ts_CONDITIONALS = {"OR", "AND", "!, OR", "!, AND"}
local ts_CONDSYMBOLS = {"|", "&", "|", "&"}
local ts_YESNO = {"", "Yes", "No"}
local s_OVERWRITE = "Overwrite"
local s_EXISTS = "Exists"
local function question(tInquiry, start)
settings = {}
settings.justify = "center"
settings.wrap = true
settings.msel = false
settings.hasheader = true
settings.co_routine = nil
settings.curpos = start or 1
local sel = print_table(tInquiry, #tInquiry, settings)
return sel
end
local function find_linepos(t_lines, search, startline)
startline = startline or 1
for i = startline, #t_lines do
if string.match (t_lines[i], search) then
return i
end
end
return -1
end
local function replacelines(t_lines, search, replace, startline)
startline = startline or 1
repcount = 0
for i = startline, #t_lines do
if string.match (t_lines[i], search) then
t_lines[i] = replace
repcount = repcount + 1
end
end
return repcount
end
local function replaceemptylines(t_lines, replace, startline)
startline = startline or 1
replace = replace or nil
repcount = 0
for i = startline, #t_lines do
if t_lines[i] == "" then
t_lines[i] = replace
repcount = repcount + 1
end
end
return repcount
end
local function checkexistingmenu(t_lines, menuname)
local pos = find_linepos(t_lines, "^\"" .. menuname .. "\"%s*%->.+")
local sel = 0
if pos > 0 then
ts_YESNO[1] = menuname .. " " .. s_EXISTS .. ", " .. s_OVERWRITE .. "?"
sel = question(ts_YESNO, 3)
if sel == 3 then
pos = nil
elseif sel < 2 then
pos = 0
end
else
pos = nil
end
return pos
end
local function savedata(filename, ts_tags, cond, menuname)
menuname = menuname or sDEFAULTMENU
local lines = {}
local curline = 0
local function lines_next(str, pos)
pos = pos or #lines + 1
lines[pos] = str or ""
curline = pos
end
local function lines_append(str, pos)
pos = pos or curline or #lines
lines[pos] = lines[pos] .. str or ""
end
if rb.file_exists(filename) ~= true then
lines_next(sFILEHEADER)
lines_next("#")
lines_next("# MAIN MENU")
lines_next(sMENUSTART)
else
local file = io.open(filename, "r") -- read
if not file then
rb.splash(rb.HZ, "Error opening" .. " " .. filename)
return
end
for line in file:lines() do
lines_next(line)
end
file:close()
end
local menupos = find_linepos(lines, sMENUSTART)
if menupos < 1 then
rb.splash(rb.HZ, sERRORMENUENTRY)
return
end
replaceemptylines(lines, sBLANKLINE, menupos)
local existmenupos = checkexistingmenu(lines, menuname)
if existmenupos and existmenupos < 1 then return end -- user canceled
local lastcond = ""
local n_cond = COND_OR
local tags, tagtype
local function buildtag(e_tagtype)
if ts_tags[e_tagtype] then
n_cond = (cond[e_tagtype] or COND_OR)
if e_tagtype > 1 then
lines_append(" " .. ts_CONDSYMBOLS[n_cond])
end
tags = ts_tags[e_tagtype]
tagtype = string.lower(ts_TAGTYPE[e_tagtype])
if n_cond <= COND_AND then
lines_append(" " .. tagtype)
lines_append(" @ \"".. table.concat(tags, "|") .. "\"")
else
for k = 1, #tags do
lines_append(" " .. tagtype)
lines_append(" !~ \"".. tags[k] .. "\"")
if k < #tags then lines_append(" &") end
end
end
end
end
if ts_tags[TAG_ARTIST] or ts_tags[TAG_ALBARTIST] or ts_tags[TAG_ALBUM] or
ts_tags[TAG_GENRE] or ts_tags[TAG_COMPOSER] then
lines_next("\"" .. menuname .. "\" -> " .. sMENUTITLE .. " ?", existmenupos)
buildtag(TAG_ARTIST)
buildtag(TAG_ALBARTIST)
buildtag(TAG_ALBUM)
buildtag(TAG_GENRE)
buildtag(TAG_COMPOSER)
lines_next("\n")
else
rb.splash(rb.HZ, "Nothing to save")
end
local file = io.open(filename, "w+") -- overwrite
if not file then
rb.splash(rb.HZ, "Error writing " .. filename)
return
end
for i = 1, #lines do
if lines[i] and lines[i] ~= sBLANKLINE then
file:write(lines[i], "\n")
end
end
file:close()
end
-- uses print_table to display database tags
local function print_tags(ftable, settings, t_selected)
if not s_cond then s_sep = "|" end
ftable = ftable or {}
if t_selected then
for k = 1, #t_selected do
ftable[t_selected[k]] = ftable[t_selected[k]] .. "\0"
end
end
rb.lcd_clear_display()
_print.clear()
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
settings.msel = true
end
settings.hasheader = true
settings.co_routine = nil
local sel = print_table(ftable, #ftable, settings)
--_lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", "))
local selected = {}
local str = ""
for k = 1,#sel do
str = ftable[sel[k]] or ""
selected[#selected + 1] = string.sub(str, 1, -2) -- REMOVE \0
end
ftable = nil
if #sel == 0 then
return nil, nil
end
return sel, selected
end -- print_tags
-- uses print_table to display a menu
function main_menu()
local menuname = sDEFAULTMENU
local t_tags
local ts_tags = {}
local cond = {}
local sel = {}
local mt = {
[1] = "TagNav Customizer",
[2] = "", --ts_CONDITIONALS[cond[TAG_ARTIST] or COND_OR],
[3] = ts_TAGTYPE[TAG_ARTIST],
[4] = ts_CONDITIONALS[cond[TAG_ALBARTIST] or COND_OR],
[5] = ts_TAGTYPE[TAG_ALBARTIST],
[6] = ts_CONDITIONALS[cond[TAG_ALBUM] or COND_OR],
[7] = ts_TAGTYPE[TAG_ALBUM],
[8] = ts_CONDITIONALS[cond[TAG_GENRE] or COND_OR],
[9] = ts_TAGTYPE[TAG_GENRE],
[10] = ts_CONDITIONALS[cond[TAG_COMPOSER] or COND_OR],
[11] = ts_TAGTYPE[TAG_COMPOSER],
[12] = "Save to Tagnav",
[13] = "Exit"
}
local function sel_cond(item, item_mt)
cond[item] = cond[item] or 1
cond[item] = cond[item] + 1
if cond[item] > #ts_CONDITIONALS then cond[item] = 1 end
mt[item_mt] = ts_CONDITIONALS[cond[item]]
end
local function sel_tag(item, item_mt, t_tags)
t_tags = get_tags(rb.ROCKBOX_DIR .. "/" .. ts_DBPATH[item], ts_TAGTYPE[item])
sel[item], ts_tags[item] = print_tags(t_tags, nil, sel[item])
if ts_tags[item] then
mt[item_mt] = ts_TAGTYPE[item] .. " [" .. #sel[item] .. "]"
else
mt[item_mt] = ts_TAGTYPE[item]
end
end
local ft = {
[0] = exit_now, --if user cancels do this function
[1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot
[2] = function(ARTCOND)
sel_cond(TAG_ARTIST, ARTCOND)
end,
[3] = function(ART)
sel_tag(TAG_ARTIST, ART, t_tags)
end,
[4] = function(ALBARTCOND)
sel_cond(TAG_ALBARTIST, ALBARTCOND)
end,
[5] = function(ALBART)
sel_tag(TAG_ALBARTIST, ALBART, t_tags)
end,
[6] = function(ALBCOND)
sel_cond(TAG_ALBUM, ALBCOND)
end,
[7] = function(ALB)
sel_tag(TAG_ALBUM, ALB, t_tags)
end,
[8] = function(GENRECOND)
sel_cond(TAG_GENRE, GENRECOND)
end,
[9] = function(GENRE)
sel_tag(TAG_GENRE, GENRE, t_tags)
end,
[10] = function(COMPCOND)
sel_cond(TAG_COMPOSER, COMPCOND)
end,
[11] = function(COMP)
sel_tag(TAG_COMPOSER, COMP, t_tags)
end,
[12] = function(SAVET)
menuname = menuname or sDEFAULTMENU
menuname = rb.kbd_input(menuname)
menuname = string.match(menuname, "%w+")
if menuname == "" then menuname = nil end
menuname = menuname or sDEFAULTMENU
savedata(sFILEOUT, ts_tags, cond, menuname)
end,
[13] = function(EXIT_) return true end
}
print_menu(mt, ft, 2) --start at item 2
end
function exit_now()
rb.lcd_update()
os.exit()
end -- exit_now
main_menu()
exit_now()