summaryrefslogtreecommitdiffstats
path: root/apps/plugins/lua_scripts/tagnav.lua
blob: f62f27973bcc934f47fadb20238abef348775982 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
-- tagnav.lua BILGUS 2018
--[[
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2018 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.
 *
 ****************************************************************************/
]]

--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 sRELOADSEARCH  = "^\"Reload\.\.\.\"%s*%->.+"
local sRELOADMENU    = "\"Reload...\" -> %reload"

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("# CUSTOM 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

            -- copy all existing lines to table we will overwrite existing file
            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)

        -- remove reload menu, we will add it at the end
        replacelines(lines, sRELOADSEARCH, sBLANKLINE)

        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 tagpos = 0

        local function buildtag(e_tagtype)
            if ts_tags[e_tagtype] then
                tagpos = tagpos + 1
                n_cond = (cond[e_tagtype] or COND_OR)
                if tagpos > 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)
            -- add reload menu
            lines_next(sRELOADMENU .. "\n")

        else
            rb.splash(rb.HZ, "Nothing to save")
            return
        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 = "left"
        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] = "",
                [13] = "Save to Tagnav",
                [14] = "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], t_tags)
        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(B) mt[B] = "TNC Bilgus 2018" end,
                [13]  = function(SAVET)
                            menuname = menuname or sDEFAULTMENU
                            menuname = rb.kbd_input(menuname)
                            menuname = string.match(menuname or "", "%w+")
                            if menuname == "" then menuname = nil end
                            if menuname then
                                savedata(sFILEOUT, ts_tags, cond, menuname)
                            end
                        end,
                [14] = 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()