From 98d92d37ab6996aaed0b1ae42050d425157d7e26 Mon Sep 17 00:00:00 2001 From: Wim de With Date: Thu, 23 Nov 2023 14:48:33 +0100 Subject: [PATCH] Refactor FileManager collation for easier patching --- .../filemanager/filemanagerfilesearcher.lua | 2 +- frontend/apps/filemanager/filemanagermenu.lua | 38 +-- frontend/ui/widget/filechooser.lua | 241 ++++++++++++------ 3 files changed, 180 insertions(+), 101 deletions(-) diff --git a/frontend/apps/filemanager/filemanagerfilesearcher.lua b/frontend/apps/filemanager/filemanagerfilesearcher.lua index 4f7c74947..270bee95f 100644 --- a/frontend/apps/filemanager/filemanagerfilesearcher.lua +++ b/frontend/apps/filemanager/filemanagerfilesearcher.lua @@ -124,7 +124,7 @@ function FileSearcher:getList() ["/proc"] = true, ["/sys"] = true, } - local collate = G_reader_settings:readSetting("collate") + local collate = FileChooser:getCollate() local search_string = self.search_string if search_string ~= "*" then -- one * to show all files if not self.case_sensitive then diff --git a/frontend/apps/filemanager/filemanagermenu.lua b/frontend/apps/filemanager/filemanagermenu.lua index 70a742f33..d004e650d 100644 --- a/frontend/apps/filemanager/filemanagermenu.lua +++ b/frontend/apps/filemanager/filemanagermenu.lua @@ -443,12 +443,12 @@ To: self.menu_items.sort_mixed = { text = _("Folders and files mixed"), enabled_func = function() - local collate = G_reader_settings:readSetting("collate") - return not FileChooser.isCollateNotForMixed(collate) + local collate = FileChooser:getCollate() + return collate.can_collate_mixed end, checked_func = function() - local collate = G_reader_settings:readSetting("collate") - return not FileChooser.isCollateNotForMixed(collate) and G_reader_settings:isTrue("collate_mixed") + local collate = FileChooser:getCollate() + return collate.can_collate_mixed and G_reader_settings:isTrue("collate_mixed") end, callback = function() G_reader_settings:flipNilOrFalse("collate_mixed") @@ -851,37 +851,27 @@ dbg:guard(FileManagerMenu, 'setUpdateItemTable', end) function FileManagerMenu:getSortingMenuTable() - local collates = { - { _("name"), "strcoll" }, - { _("name (natural sorting)"), "natural" }, - { _("last read date"), "access" }, - { _("date modified"), "date" }, - { _("size"), "size" }, - { _("type"), "type" }, - { _("percent – unopened first"), "percent_unopened_first" }, - { _("percent – unopened last"), "percent_unopened_last" }, - } local sub_item_table = {} - for i, v in ipairs(collates) do + for k, v in pairs(self.ui.file_chooser.collates) do table.insert(sub_item_table, { - text = v[1], + text = v.text, + menu_order = v.menu_order, checked_func = function() - return v[2] == G_reader_settings:readSetting("collate", "strcoll") + local _, id = self.ui.file_chooser:getCollate() + return k == id end, callback = function() - G_reader_settings:saveSetting("collate", v[2]) + G_reader_settings:saveSetting("collate", k) + self.ui.file_chooser:clearSortingCache() self.ui.file_chooser:refreshPath() end, }) end + table.sort(sub_item_table, function(a, b) return a.menu_order < b.menu_order end) return { text_func = function() - local collate = G_reader_settings:readSetting("collate") - for i, v in ipairs(collates) do - if v[2] == collate then - return T(_("Sort by: %1"), v[1]) - end - end + local collate = self.ui.file_chooser:getCollate() + return T(_("Sort by: %1"), collate.text) end, sub_item_table = sub_item_table, } diff --git a/frontend/ui/widget/filechooser.lua b/frontend/ui/widget/filechooser.lua index 53bbf8d98..2e08d0b71 100644 --- a/frontend/ui/widget/filechooser.lua +++ b/frontend/ui/widget/filechooser.lua @@ -75,6 +75,141 @@ local FileChooser = Menu:extend{ }, path_items = nil, -- hash, store last browsed location (item index) for each path goto_letter = true, + collates = { + strcoll = { + text = _("name"), + menu_order = 10, + can_collate_mixed = true, + init_sort_func = function(cache) + return function(a, b) + return ffiUtil.strcoll(a.text, b.text) + end, cache + end, + }, + natural = { + text = _("name (natural sorting)"), + menu_order = 20, + can_collate_mixed = true, + init_sort_func = function(cache) + local natsort + natsort, cache = sort.natsort_cmp(cache) + return function(a, b) + return natsort(a.text, b.text) + end, cache + end + }, + access = { + text = _("last read date"), + menu_order = 30, + can_collate_mixed = true, + init_sort_func = function(cache) + return function(a, b) + return a.attr.access > b.attr.access + end, cache + end, + mandatory_func = function(item) + return datetime.secondsToDateTime(item.attr.access) + end, + }, + date = { + text = _("date modified"), + menu_order = 40, + can_collate_mixed = true, + init_sort_func = function(cache) + return function(a, b) + return a.attr.modification > b.attr.modification + end, cache + end, + mandatory_func = function(item) + return datetime.secondsToDateTime(item.attr.modification) + end, + }, + size = { + text = _("size"), + menu_order = 50, + can_collate_mixed = false, + init_sort_func = function(cache) + return function(a, b) + return a.attr.size < b.attr.size + end, cache + end, + }, + type = { + text = _("type"), + menu_order = 60, + can_collate_mixed = false, + init_sort_func = function(cache) + return function(a, b) + if (a.suffix or b.suffix) and a.suffix ~= b.suffix then + return ffiUtil.strcoll(a.suffix, b.suffix) + end + return ffiUtil.strcoll(a.text, b.text) + end, cache + end, + item_func = function(item) + item.suffix = util.getFileNameSuffix(item.text) + return item + end, + }, + percent_unopened_first = { + text = _("percent - unopened first"), + menu_order = 70, + can_collate_mixed = false, + init_sort_func = function(cache) + return function(a, b) + if a.opened == b.opened then + if a.opened then + return a.percent_finished < b.percent_finished + end + return ffiUtil.strcoll(a.text, b.text) + end + return b.opened + end, cache + end, + item_func = function(item) + local percent_finished + item.opened = DocSettings:hasSidecarFile(item.fullpath) + if item.opened then + local doc_settings = DocSettings:open(item.fullpath) + percent_finished = doc_settings:readSetting("percent_finished") + end + item.percent_finished = percent_finished or 0 + return item + end, + mandatory_func = function(item) + return item.opened and string.format("%d %%", 100 * item.percent_finished) or "–" + end, + }, + percent_unopened_last = { + text = _("percent - unopened last"), + menu_order = 80, + can_collate_mixed = false, + init_sort_func = function(cache) + return function(a, b) + if a.opened == b.opened then + if a.opened then + return a.percent_finished < b.percent_finished + end + return ffiUtil.strcoll(a.text, b.text) + end + return a.opened + end, cache + end, + item_func = function(item) + local percent_finished + item.opened = DocSettings:hasSidecarFile(item.fullpath) + if item.opened then + local doc_settings = DocSettings:open(item.fullpath) + percent_finished = doc_settings:readSetting("percent_finished") + end + item.percent_finished = percent_finished or 0 + return item + end, + mandatory_func = function(item) + return item.opened and string.format("%d %%", 100 * item.percent_finished) or "–" + end, + }, + }, } -- Cache of content we knew of for directories that are not readable @@ -163,70 +298,31 @@ function FileChooser.getListItem(f, filename, attributes, collate) if G_reader_settings:readSetting("show_file_in_bold") ~= false then item.opened = DocSettings:hasSidecarFile(filename) end - if collate == "type" then - item.suffix = util.getFileNameSuffix(f) - elseif collate == "percent_unopened_first" or collate == "percent_unopened_last" then - local percent_finished - item.opened = DocSettings:hasSidecarFile(filename) - if item.opened then - local doc_settings = DocSettings:open(filename) - percent_finished = doc_settings:readSetting("percent_finished") - end - item.percent_finished = percent_finished or 0 + if collate.item_func ~= nil then + item = collate.item_func(item) end end return item end +function FileChooser:getCollate() + local collate_id = G_reader_settings:readSetting("collate", "strcoll") + local collate = self.collates[collate_id] + if collate ~= nil then + return collate, collate_id + else + G_reader_settings:saveSetting("collate", "strcoll") + return self.collates.strcoll, "strcoll" + end +end + function FileChooser:getSortingFunction(collate, reverse_collate) local sorting - if collate == "strcoll" then - sorting = function(a, b) - return ffiUtil.strcoll(a.text, b.text) - end - elseif collate == "natural" then - local natsort - -- Only keep the cache if we're an *instance* of FileChooser - if self ~= FileChooser then - natsort, self.natsort_cache = sort.natsort_cmp(self.natsort_cache) - else - natsort = sort.natsort_cmp() - end - sorting = function(a, b) - return natsort(a.text, b.text) - end - elseif collate == "access" then - sorting = function(a, b) - return a.attr.access > b.attr.access - end - elseif collate == "date" then - sorting = function(a, b) - return a.attr.modification > b.attr.modification - end - elseif collate == "size" then - sorting = function(a, b) - return a.attr.size < b.attr.size - end - elseif collate == "type" then - sorting = function(a, b) - if (a.suffix or b.suffix) and a.suffix ~= b.suffix then - return ffiUtil.strcoll(a.suffix, b.suffix) - end - return ffiUtil.strcoll(a.text, b.text) - end - else -- collate == "percent_unopened_first" or collate == "percent_unopened_last" - sorting = function(a, b) - if a.opened == b.opened then - if a.opened then - return a.percent_finished < b.percent_finished - end - return ffiUtil.strcoll(a.text, b.text) - end - if collate == "percent_unopened_first" then - return b.opened - end - return a.opened - end + -- Only keep the cache if we're an *instance* of FileChooser + if self ~= FileChooser then + sorting, self.sort_cache = collate.init_sort_func(self.sort_cache) + else + sorting = collate.init_sort_func() end if reverse_collate then @@ -237,27 +333,25 @@ function FileChooser:getSortingFunction(collate, reverse_collate) return sorting end +function FileChooser:clearSortingCache() + self.sort_cache = nil +end + function FileChooser:genItemTableFromPath(path) - local collate = G_reader_settings:readSetting("collate", "strcoll") + local collate = self:getCollate() local dirs, files = self:getList(path, collate) return self:genItemTable(dirs, files, path) end -function FileChooser.isCollateNotForMixed(collate) - return collate == "size" or collate == "type" - or collate == "percent_unopened_first" or collate == "percent_unopened_last" -end - function FileChooser:genItemTable(dirs, files, path) - local collate = G_reader_settings:readSetting("collate") + local collate = self:getCollate() local collate_mixed = G_reader_settings:isTrue("collate_mixed") local reverse_collate = G_reader_settings:isTrue("reverse_collate") local sorting = self:getSortingFunction(collate, reverse_collate) - local collate_not_for_mixed = self.isCollateNotForMixed(collate) - if collate_not_for_mixed or not collate_mixed then + if not collate.can_collate_mixed or not collate_mixed then table.sort(files, sorting) - if collate_not_for_mixed then -- keep folders sorted by name not reversed - sorting = self:getSortingFunction("strcoll") + if not collate.can_collate_mixed then -- keep folders sorted by name not reversed + sorting = self:getSortingFunction(self.collates.strcoll) end table.sort(dirs, sorting) end @@ -310,7 +404,7 @@ function FileChooser:genItemTable(dirs, files, path) table.insert(item_table, file_item) end - if not collate_not_for_mixed and collate_mixed then + if collate.can_collate_mixed and collate_mixed then table.sort(item_table, sorting) end @@ -347,13 +441,8 @@ end function FileChooser:getMenuItemMandatory(item, collate) local text if collate then -- file - -- display the sorting parameter in mandatory - if collate == "access" then - text = datetime.secondsToDateTime(item.attr.access) - elseif collate == "date" then - text = datetime.secondsToDateTime(item.attr.modification) - elseif collate == "percent_unopened_first" or collate == "percent_unopened_last" then - text = item.opened and string.format("%d %%", 100 * item.percent_finished) or "–" + if collate.mandatory_func ~= nil then + text = collate.mandatory_func(item) else text = util.getFriendlySize(item.attr.size or 0) end