diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index b5c03b382..cbf535e1e 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -292,7 +292,7 @@ function FileManager:setupLayout() table.insert(buttons, {}) -- separator table.insert(buttons, { filemanagerutil.genResetSettingsButton(doc_settings_or_file, close_dialog_refresh_callback), - filemanagerutil.genAddRemoveFavoritesButton(file, close_dialog_callback), + file_manager.collections:genAddToCollectionButton(file, close_dialog_callback), }) end table.insert(buttons, { @@ -515,11 +515,17 @@ function FileManager:tapPlus() local title, buttons if self.select_mode then + local function toggle_select_mode_callback() + self:onToggleSelectMode() + end local select_count = util.tableSize(self.selected_files) local actions_enabled = select_count > 0 title = actions_enabled and T(N_("1 file selected", "%1 files selected", select_count), select_count) or _("No files selected") buttons = { + { + self.collections:genAddToCollectionButton(self.selected_files, close_dialog_callback, toggle_select_mode_callback), + }, { { text = _("Show selected files list"), diff --git a/frontend/apps/filemanager/filemanagercollection.lua b/frontend/apps/filemanager/filemanagercollection.lua index 4775916ff..887ab5b3b 100644 --- a/frontend/apps/filemanager/filemanagercollection.lua +++ b/frontend/apps/filemanager/filemanagercollection.lua @@ -1,19 +1,26 @@ local BD = require("ui/bidi") local ButtonDialog = require("ui/widget/buttondialog") +local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") local DocSettings = require("docsettings") -local DocumentRegistry = require("document/documentregistry") local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo") +local InfoMessage = require("ui/widget/infomessage") +local InputDialog = require("ui/widget/inputdialog") local Menu = require("ui/widget/menu") local ReadCollection = require("readcollection") +local SortWidget = require("ui/widget/sortwidget") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local Screen = require("device").screen local filemanagerutil = require("apps/filemanager/filemanagerutil") local _ = require("gettext") +local T = require("ffi/util").template +local util = require("util") local FileManagerCollection = WidgetContainer:extend{ - title = _("Favorites"), + title = _("Collections"), + default_collection_title = _("Favorites"), + checkmark = "\u{2713}", } function FileManagerCollection:init() @@ -21,21 +28,73 @@ function FileManagerCollection:init() end function FileManagerCollection:addToMainMenu(menu_items) + menu_items.favorites = { + text = self.default_collection_title, + callback = function() + self:onShowColl() + end, + } menu_items.collections = { text = self.title, callback = function() - self:onShowColl() + self:onShowCollList() end, } end -function FileManagerCollection:updateItemTable() +-- collection + +function FileManagerCollection:getCollectionTitle(collection_name) + return collection_name == ReadCollection.default_collection_name + and self.default_collection_title -- favorites + or collection_name +end + +function FileManagerCollection:onShowColl(collection_name) + collection_name = collection_name or ReadCollection.default_collection_name + self.coll_menu = Menu:new{ + ui = self.ui, + covers_fullscreen = true, -- hint for UIManager:_repaint() + is_borderless = true, + is_popout = false, + -- item and book cover thumbnail dimensions in Mosaic and Detailed list display modes + -- must be equal in File manager, History and Collection windows to avoid image scaling + title_bar_fm_style = true, + title_bar_left_icon = "appbar.menu", + onLeftButtonTap = function() self:showCollDialog() end, + onMenuChoice = self.onMenuChoice, + onMenuHold = self.onMenuHold, + onSetRotationMode = self.MenuSetRotationModeHandler, + _manager = self, + collection_name = collection_name, + } + self.coll_menu.close_callback = function() + if self.files_updated then + if self.ui.file_chooser then + self.ui.file_chooser:refreshPath() + end + self.files_updated = nil + end + UIManager:close(self.coll_menu) + self.coll_menu = nil + end + self:updateItemTable() + UIManager:show(self.coll_menu) + return true +end + +function FileManagerCollection:updateItemTable(show_last_item) local item_table = {} for _, item in pairs(ReadCollection.coll[self.coll_menu.collection_name]) do table.insert(item_table, item) end - table.sort(item_table, function(v1, v2) return v1.order < v2.order end) - self.coll_menu:switchItemTable(self.title, item_table, -1) + if #item_table > 1 then + table.sort(item_table, function(v1, v2) return v1.order < v2.order end) + end + local title = self:getCollectionTitle(self.coll_menu.collection_name) + title = T("%1 (%2)", title, #item_table) + local item_number = show_last_item and #item_table or -1 + self.coll_menu:switchItemTable(title, item_table, item_number) end function FileManagerCollection:onMenuChoice(item) @@ -92,7 +151,7 @@ function FileManagerCollection:onMenuHold(item) table.insert(buttons, { filemanagerutil.genResetSettingsButton(doc_settings_or_file, close_dialog_update_callback, is_currently_opened), { - text = _("Remove from favorites"), + text = _("Remove from collection"), callback = function() UIManager:close(self.collfile_dialog) ReadCollection:removeItem(file, self.collection_name) @@ -124,79 +183,37 @@ function FileManagerCollection:onMenuHold(item) return true end -function FileManagerCollection:MenuSetRotationModeHandler(rotation) - if rotation ~= nil and rotation ~= Screen:getRotationMode() then - UIManager:close(self._manager.coll_menu) - if self._manager.ui.view and self._manager.ui.view.onSetRotationMode then - self._manager.ui.view:onSetRotationMode(rotation) - elseif self._manager.ui.onSetRotationMode then - self._manager.ui:onSetRotationMode(rotation) - else - Screen:setRotationMode(rotation) - end - self._manager:onShowColl() - end - return true -end - -function FileManagerCollection:onShowColl(collection_name) - collection_name = collection_name or ReadCollection.default_collection_name - self.coll_menu = Menu:new{ - ui = self.ui, - covers_fullscreen = true, -- hint for UIManager:_repaint() - is_borderless = true, - is_popout = false, - -- item and book cover thumbnail dimensions in Mosaic and Detailed list display modes - -- must be equal in File manager, History and Collection windows to avoid image scaling - title_bar_fm_style = true, - title_bar_left_icon = "appbar.menu", - onLeftButtonTap = function() self:showCollDialog() end, - onMenuChoice = self.onMenuChoice, - onMenuHold = self.onMenuHold, - onSetRotationMode = self.MenuSetRotationModeHandler, - _manager = self, - collection_name = collection_name, - } - self.coll_menu.close_callback = function() - if self.files_updated then - if self.ui.file_chooser then - self.ui.file_chooser:refreshPath() - end - self.files_updated = nil - end - UIManager:close(self.coll_menu) - self.coll_menu = nil - end - self:updateItemTable() - UIManager:show(self.coll_menu) - return true -end - function FileManagerCollection:showCollDialog() local coll_dialog local buttons = { {{ - text = _("Sort favorites"), + text = _("Collections"), + callback = function() + UIManager:close(coll_dialog) + self.coll_menu.close_callback() + self:onShowCollList() + end, + }}, + {}, -- separator + {{ + text = _("Arrange books in collection"), callback = function() UIManager:close(coll_dialog) self:sortCollection() end, }}, {{ - text = _("Add a book to favorites"), + text = _("Add a book to collection"), callback = function() UIManager:close(coll_dialog) local PathChooser = require("ui/widget/pathchooser") local path_chooser = PathChooser:new{ path = G_reader_settings:readSetting("home_dir"), select_directory = false, - file_filter = function(file) - return DocumentRegistry:hasProvider(file) - end, onConfirm = function(file) - if not ReadCollection:hasFile(file) then + if not ReadCollection:isFileInCollection(file, self.coll_menu.collection_name) then ReadCollection:addItem(file, self.coll_menu.collection_name) - self:updateItemTable() + self:updateItemTable(true) -- show added item end end, } @@ -205,19 +222,20 @@ function FileManagerCollection:showCollDialog() }}, } if self.ui.document then - local has_file = ReadCollection:hasFile(self.ui.document.file) + local file = self.ui.document.file + local is_in_collection = ReadCollection:isFileInCollection(file, self.coll_menu.collection_name) table.insert(buttons, {{ text_func = function() - return has_file and _("Remove current book from favorites") or _("Add current book to favorites") + return is_in_collection and _("Remove current book from collection") or _("Add current book to collection") end, callback = function() UIManager:close(coll_dialog) - if has_file then - ReadCollection:removeItem(self.ui.document.file) + if is_in_collection then + ReadCollection:removeItem(file, self.coll_menu.collection_name) else - ReadCollection:addItem(self.ui.document.file, self.coll_menu.collection_name) + ReadCollection:addItem(file, self.coll_menu.collection_name) end - self:updateItemTable() + self:updateItemTable(not is_in_collection) end, }}) end @@ -228,12 +246,10 @@ function FileManagerCollection:showCollDialog() end function FileManagerCollection:sortCollection() - local item_table = ReadCollection:getOrderedCollection(self.coll_menu.collection_name) - local SortWidget = require("ui/widget/sortwidget") local sort_widget sort_widget = SortWidget:new{ - title = _("Sort favorites"), - item_table = item_table, + title = _("Arrange books in collection"), + item_table = ReadCollection:getOrderedCollection(self.coll_menu.collection_name), callback = function() ReadCollection:updateCollectionOrder(self.coll_menu.collection_name, sort_widget.item_table) self:updateItemTable() @@ -242,10 +258,341 @@ function FileManagerCollection:sortCollection() UIManager:show(sort_widget) end +function FileManagerCollection:MenuSetRotationModeHandler(rotation) + if rotation ~= nil and rotation ~= Screen:getRotationMode() then + UIManager:close(self._manager.coll_menu) + if self._manager.ui.view and self._manager.ui.view.onSetRotationMode then + self._manager.ui.view:onSetRotationMode(rotation) + elseif self._manager.ui.onSetRotationMode then + self._manager.ui:onSetRotationMode(rotation) + else + Screen:setRotationMode(rotation) + end + self._manager:onShowColl() + end + return true +end + function FileManagerCollection:onBookMetadataChanged() if self.coll_menu then self.coll_menu:updateItems() end end +-- collection list + +function FileManagerCollection:onShowCollList(file_or_files, caller_callback, no_dialog) + self.selected_colections = nil + if file_or_files then -- select mode + if type(file_or_files) == "string" then -- checkmark collections containing the file + self.selected_colections = ReadCollection:getCollectionsWithFile(file_or_files) + else -- do not checkmark any + self.selected_colections = {} + end + end + self.coll_list = Menu:new{ + subtitle = "", + covers_fullscreen = true, + is_borderless = true, + is_popout = false, + title_bar_fm_style = true, + title_bar_left_icon = file_or_files and "check" or "appbar.menu", + onLeftButtonTap = function() self:showCollListDialog(caller_callback, no_dialog) end, + onMenuChoice = self.onCollListChoice, + onMenuHold = self.onCollListHold, + onSetRotationMode = self.MenuSetRotationModeHandler, + _manager = self, + } + self.coll_list.close_callback = function(force_close) + if force_close or self.selected_colections == nil then + UIManager:close(self.coll_list) + self.coll_list = nil + end + end + self:updateCollListItemTable(true) -- init + UIManager:show(self.coll_list) + return true +end + +function FileManagerCollection:updateCollListItemTable(do_init, item_number) + local item_table + if do_init then + item_table = {} + for name, coll in pairs(ReadCollection.coll) do + local mandatory + if self.selected_colections then + mandatory = self.selected_colections[name] and self.checkmark or " " + self.coll_list.items_mandatory_font_size = self.coll_list.font_size + else + mandatory = util.tableSize(coll) + end + table.insert(item_table, { + text = self:getCollectionTitle(name), + mandatory = mandatory, + name = name, + order = ReadCollection.coll_order[name], + }) + end + if #item_table > 1 then + table.sort(item_table, function(v1, v2) return v1.order < v2.order end) + end + else + item_table = self.coll_list.item_table + end + local title = T(_("Collections (%1)"), #item_table) + local subtitle + if self.selected_colections then + local selected_nb = util.tableSize(self.selected_colections) + subtitle = self.selected_colections and T(_("Selected collections: %1"), selected_nb) + if do_init and selected_nb > 0 then -- show first collection containing the long-pressed book + for i, item in ipairs(item_table) do + if self.selected_colections[item.name] then + item_number = i + break + end + end + end + end + self.coll_list:switchItemTable(title, item_table, item_number or -1, nil, subtitle) +end + +function FileManagerCollection:onCollListChoice(item) + if self._manager.selected_colections then + if item.mandatory == self._manager.checkmark then + self.item_table[item.idx].mandatory = " " + self._manager.selected_colections[item.name] = nil + else + self.item_table[item.idx].mandatory = self._manager.checkmark + self._manager.selected_colections[item.name] = true + end + self._manager:updateCollListItemTable() + else + self._manager:onShowColl(item.name) + end +end + +function FileManagerCollection:onCollListHold(item) + if item.name == ReadCollection.default_collection_name -- Favorites non-editable + or self._manager.selected_colections then -- select mode + return + end + + local button_dialog + local buttons = { + { + { + text = _("Remove collection"), + callback = function() + UIManager:close(button_dialog) + self._manager:removeCollection(item) + end + }, + { + text = _("Rename collection"), + callback = function() + UIManager:close(button_dialog) + self._manager:renameCollection(item) + end + }, + }, + } + button_dialog = ButtonDialog:new{ + title = item.text, + title_align = "center", + buttons = buttons, + } + UIManager:show(button_dialog) + return true +end + +function FileManagerCollection:showCollListDialog(caller_callback, no_dialog) + if no_dialog then + caller_callback() + self.coll_list.close_callback(true) + return + end + + local button_dialog, buttons + local new_collection_button = { + { + text = _("New collection"), + callback = function() + UIManager:close(button_dialog) + self:addCollection() + end, + }, + } + if self.selected_colections then -- select mode + buttons = { + new_collection_button, + {}, -- separator + { + { + text = _("Deselect all"), + callback = function() + UIManager:close(button_dialog) + for name in pairs(self.selected_colections) do + self.selected_colections[name] = nil + end + self:updateCollListItemTable(true) + end, + }, + { + text = _("Select all"), + callback = function() + UIManager:close(button_dialog) + for name in pairs(ReadCollection.coll) do + self.selected_colections[name] = true + end + self:updateCollListItemTable(true) + end, + }, + }, + { + { + text = _("Apply selection"), + callback = function() + UIManager:close(button_dialog) + caller_callback() + self.coll_list.close_callback(true) + end, + }, + }, + } + else + buttons = { + new_collection_button, + { + { + text = _("Arrange collections"), + callback = function() + UIManager:close(button_dialog) + self:sortCollections() + end, + }, + }, + } + end + button_dialog = ButtonDialog:new{ + buttons = buttons, + } + UIManager:show(button_dialog) +end + +function FileManagerCollection:editCollectionName(editCallback, old_name) + local input_dialog + input_dialog = InputDialog:new{ + title = _("Enter collection name"), + input = old_name, + input_hint = old_name, + buttons = {{ + { + text = _("Cancel"), + id = "close", + callback = function() + UIManager:close(input_dialog) + end, + }, + { + text = _("Save"), + callback = function() + local new_name = input_dialog:getInputText() + if new_name == "" or new_name == old_name then return end + if ReadCollection.coll[new_name] then + UIManager:show(InfoMessage:new{ + text = T(_("Collection already exists: %1"), new_name), + }) + else + UIManager:close(input_dialog) + editCallback(new_name) + end + end, + }, + }}, + } + UIManager:show(input_dialog) + input_dialog:onShowKeyboard() +end + +function FileManagerCollection:addCollection() + local editCallback = function(name) + ReadCollection:addCollection(name) + local mandatory + if self.selected_colections then + self.selected_colections[name] = true + mandatory = self.checkmark + else + mandatory = 0 + end + table.insert(self.coll_list.item_table, { + text = name, + mandatory = mandatory, + name = name, + order = ReadCollection.coll_order[name], + }) + self:updateCollListItemTable(false, #self.coll_list.item_table) -- show added item + end + self:editCollectionName(editCallback) +end + +function FileManagerCollection:renameCollection(item) + local editCallback = function(name) + ReadCollection:renameCollection(item.name, name) + self.coll_list.item_table[item.idx].text = name + self.coll_list.item_table[item.idx].name = name + self:updateCollListItemTable() + end + self:editCollectionName(editCallback, item.name) +end + +function FileManagerCollection:removeCollection(item) + UIManager:show(ConfirmBox:new{ + text = _("Remove collection?") .. "\n\n" .. item.text, + ok_text = _("Remove"), + ok_callback = function() + ReadCollection:removeCollection(item.name) + table.remove(self.coll_list.item_table, item.idx) + self:updateCollListItemTable() + end, + }) +end + +function FileManagerCollection:sortCollections() + local sort_widget + sort_widget = SortWidget:new{ + title = _("Arrange collections"), + item_table = util.tableDeepCopy(self.coll_list.item_table), + callback = function() + ReadCollection:updateCollectionListOrder(sort_widget.item_table) + self:updateCollListItemTable(true) -- init + end, + } + UIManager:show(sort_widget) +end + +-- external + +function FileManagerCollection:genAddToCollectionButton(file_or_files, caller_pre_callback, caller_post_callback, button_disabled) + return { + text = _("Add to collection"), + enabled = not button_disabled, + callback = function() + if caller_pre_callback then + caller_pre_callback() + end + local caller_callback = function() + if type(file_or_files) == "string" then + ReadCollection:addRemoveItemMultiple(file_or_files, self.selected_colections) + else -- selected files + ReadCollection:addItemsMultiple(file_or_files, self.selected_colections) + end + if caller_post_callback then + caller_post_callback() + end + end + self:onShowCollList(file_or_files, caller_callback) + end, + } +end + return FileManagerCollection diff --git a/frontend/apps/filemanager/filemanagerfilesearcher.lua b/frontend/apps/filemanager/filemanagerfilesearcher.lua index 1b56dbd31..540d92f32 100644 --- a/frontend/apps/filemanager/filemanagerfilesearcher.lua +++ b/frontend/apps/filemanager/filemanagerfilesearcher.lua @@ -265,7 +265,7 @@ function FileSearcher:onMenuSelect(item) table.insert(buttons, {}) -- separator table.insert(buttons, { filemanagerutil.genResetSettingsButton(file, close_dialog_callback, is_currently_opened), - filemanagerutil.genAddRemoveFavoritesButton(file, close_dialog_callback), + self.ui.collections:genAddToCollectionButton(file, close_dialog_callback), }) end table.insert(buttons, { diff --git a/frontend/apps/filemanager/filemanagerhistory.lua b/frontend/apps/filemanager/filemanagerhistory.lua index d7b9f8fb3..5593e1436 100644 --- a/frontend/apps/filemanager/filemanagerhistory.lua +++ b/frontend/apps/filemanager/filemanagerhistory.lua @@ -6,6 +6,7 @@ local DocSettings = require("docsettings") local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo") local InputDialog = require("ui/widget/inputdialog") local Menu = require("ui/widget/menu") +local ReadCollection = require("readcollection") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local Screen = require("device").screen @@ -79,6 +80,8 @@ function FileManagerHistory:updateItemTable() local subtitle = "" if self.search_string then subtitle = T(_("Search results (%1)"), #item_table) + elseif self.selected_colections then + subtitle = T(_("Filtered by collections (%1)"), #item_table) elseif self.filter ~= "all" then subtitle = T(_("Status: %1 (%2)"), filter_text[self.filter]:lower(), #item_table) end @@ -101,6 +104,13 @@ function FileManagerHistory:isItemMatch(item) end end end + if self.selected_colections then + for name in pairs(self.selected_colections) do + if not ReadCollection:isFileInCollection(item.file, name) then + return false + end + end + end return self.filter == "all" or item.status == self.filter end @@ -168,7 +178,7 @@ function FileManagerHistory:onMenuHold(item) end table.insert(buttons, { filemanagerutil.genResetSettingsButton(doc_settings_or_file, close_dialog_update_callback, is_currently_opened), - filemanagerutil.genAddRemoveFavoritesButton(file, close_dialog_callback, item.dim), + self._manager.ui.collections:genAddToCollectionButton(file, close_dialog_callback, nil, item.dim), }) table.insert(buttons, { { @@ -251,6 +261,7 @@ function FileManagerHistory:onShowHist(search_info) self.case_sensitive = search_info.case_sensitive else self.search_string = nil + self.selected_colections = nil end self.filter = G_reader_settings:readSetting("history_filter", "all") self.is_frozen = G_reader_settings:isTrue("history_freeze_finished_books") @@ -289,6 +300,7 @@ function FileManagerHistory:showHistDialog() self.filter = filter if filter == "all" then -- reset all filters self.search_string = nil + self.selected_colections = nil end self:updateItemTable() end, @@ -304,6 +316,19 @@ function FileManagerHistory:showHistDialog() genFilterButton("abandoned"), genFilterButton("complete"), }) + table.insert(buttons, { + { + text = _("Filter by collections"), + callback = function() + UIManager:close(hist_dialog) + local caller_callback = function() + self.selected_colections = self.ui.collections.selected_colections + self:updateItemTable() + end + self.ui.collections:onShowCollList({}, caller_callback, true) -- do not select any, no dialog to apply + end, + }, + }) table.insert(buttons, { { text = _("Search in filename and book metadata"), diff --git a/frontend/apps/filemanager/filemanagerutil.lua b/frontend/apps/filemanager/filemanagerutil.lua index 713c381a2..9b1fea163 100644 --- a/frontend/apps/filemanager/filemanagerutil.lua +++ b/frontend/apps/filemanager/filemanagerutil.lua @@ -234,25 +234,6 @@ function filemanagerutil.genResetSettingsButton(doc_settings_or_file, caller_cal } end -function filemanagerutil.genAddRemoveFavoritesButton(file, caller_callback, button_disabled) - local ReadCollection = require("readcollection") - local has_file = ReadCollection:hasFile(file) - return { - text_func = function() - return has_file and _("Remove from favorites") or _("Add to favorites") - end, - enabled = not button_disabled, - callback = function() - caller_callback() - if has_file then - ReadCollection:removeItem(file) - else - ReadCollection:addItem(file) - end - end, - } -end - function filemanagerutil.genShowFolderButton(file, caller_callback, button_disabled) return { text = _("Show folder"), diff --git a/frontend/dispatcher.lua b/frontend/dispatcher.lua index 3afb94687..591c8f6ec 100644 --- a/frontend/dispatcher.lua +++ b/frontend/dispatcher.lua @@ -53,7 +53,8 @@ local settingsList = { open_previous_document = {category="none", event="OpenLastDoc", title=_("Open previous document"), general=true}, history = {category="none", event="ShowHist", title=_("History"), general=true}, history_search = {category="none", event="SearchHistory", title=_("History search"), general=true}, - favorites = {category="none", event="ShowColl", arg="favorites", title=_("Favorites"), general=true}, + favorites = {category="none", event="ShowColl", title=_("Favorites"), general=true}, + collections = {category="none", event="ShowCollList", title=_("Collections"), general=true}, filemanager = {category="none", event="Home", title=_("File browser"), general=true, separator=true}, ---- dictionary_lookup = {category="none", event="ShowDictionaryLookup", title=_("Dictionary lookup"), general=true}, @@ -273,6 +274,7 @@ local dispatcher_menu_order = { "history", "history_search", "favorites", + "collections", "filemanager", ---- "dictionary_lookup", @@ -714,7 +716,7 @@ function Dispatcher:_sortActions(caller, location, settings, touchmenu_instance) local SortWidget = require("ui/widget/sortwidget") local sort_widget sort_widget = SortWidget:new{ - title = _("Sort"), + title = _("Arrange actions"), item_table = display_list, callback = function() if location[settings] and next(location[settings]) ~= nil then @@ -983,7 +985,7 @@ function Dispatcher:addSubMenu(caller, menu, location, settings) end menu[#menu].separator = true table.insert(menu, { - text = _("Sort"), + text = _("Arrange actions"), checked_func = function() return location[settings] ~= nil and location[settings].settings ~= nil diff --git a/frontend/readcollection.lua b/frontend/readcollection.lua index 5dd9ec185..047fb1c39 100644 --- a/frontend/readcollection.lua +++ b/frontend/readcollection.lua @@ -8,11 +8,14 @@ local util = require("util") local collection_file = DataStorage:getSettingsDir() .. "/collection.lua" local ReadCollection = { - coll = {}, + coll = nil, -- hash table + coll_order = nil, -- hash table last_read_time = 0, default_collection_name = "favorites", } +-- read, write + local function buildEntry(file, order, mandatory) file = FFIUtil.realpath(file) if not file then return end @@ -41,6 +44,7 @@ function ReadCollection:_read() end logger.dbg("ReadCollection: reading from collection file") self.coll = {} + self.coll_order = {} for coll_name, collection in pairs(collections.data) do local coll = {} for _, v in ipairs(collection) do @@ -50,14 +54,23 @@ function ReadCollection:_read() end end self.coll[coll_name] = coll + if not collection.settings then -- favorites, first run + collection.settings = { order = 1 } + end + self.coll_order[coll_name] = collection.settings.order end end function ReadCollection:write(collection_name) local collections = LuaSettings:open(collection_file) + for coll_name in pairs(collections.data) do + if not self.coll[coll_name] then + collections:delSetting(coll_name) + end + end for coll_name, coll in pairs(self.coll) do if not collection_name or coll_name == collection_name then - local data = {} + local data = { settings = { order = self.coll_order[coll_name] } } for _, item in pairs(coll) do table.insert(data, { file = item.file, order = item.order }) end @@ -68,20 +81,32 @@ function ReadCollection:write(collection_name) collections:flush() end -function ReadCollection:getFileCollectionName(file, collection_name) +-- info + +function ReadCollection:isFileInCollection(file, collection_name) file = FFIUtil.realpath(file) or file - for coll_name, coll in pairs(self.coll) do - if not collection_name or coll_name == collection_name then - if coll[file] then - return coll_name, file - end + return self.coll[collection_name][file] and true or false +end + +function ReadCollection:isFileInCollections(file) + file = FFIUtil.realpath(file) or file + for _, coll in pairs(self.coll) do + if coll[file] then + return true end end + return false end -function ReadCollection:hasFile(file, collection_name) - local coll_name = self:getFileCollectionName(file, collection_name) - return coll_name and true or false +function ReadCollection:getCollectionsWithFile(file) + file = FFIUtil.realpath(file) or file + local collections = {} + for coll_name, coll in pairs(self.coll) do + if coll[file] then + collections[coll_name] = true + end + end + return collections end function ReadCollection:getCollectionMaxOrder(collection_name) @@ -94,61 +119,74 @@ function ReadCollection:getCollectionMaxOrder(collection_name) return max_order end -function ReadCollection:getOrderedCollection(collection_name) - local ordered_coll = {} - for _, item in pairs(self.coll[collection_name]) do - table.insert(ordered_coll, item) - end - table.sort(ordered_coll, function(v1, v2) return v1.order < v2.order end) - return ordered_coll -end - -function ReadCollection:updateCollectionOrder(collection_name, ordered_coll) - local coll = self.coll[collection_name] - for i, item in ipairs(ordered_coll) do - coll[item.file].order = i - end - self:write(collection_name) -end +-- manage items function ReadCollection:addItem(file, collection_name) - collection_name = collection_name or self.default_collection_name local max_order = self:getCollectionMaxOrder(collection_name) local item = buildEntry(file, max_order + 1) self.coll[collection_name][item.file] = item self:write(collection_name) end -function ReadCollection:addItems(files, collection_name) -- files = { filepath = true, } - collection_name = collection_name or self.default_collection_name - local coll = self.coll[collection_name] - local max_order = self:getCollectionMaxOrder(collection_name) - local do_write - for file in pairs(files) do - if not self:hasFile(file) then - max_order = max_order + 1 - local item = buildEntry(file, max_order) - coll[item.file] = item - do_write = true +function ReadCollection:addRemoveItemMultiple(file, collections_to_add) + file = FFIUtil.realpath(file) or file + for coll_name, coll in pairs(self.coll) do + if collections_to_add[coll_name] then + if not coll[file] then + local max_order = self:getCollectionMaxOrder(coll_name) + coll[file] = buildEntry(file, max_order + 1) + end + else + if coll[file] then + coll[file] = nil + end end end - if do_write then - self:write(collection_name) + self:write() +end + +function ReadCollection:addItemsMultiple(files, collections_to_add) + for file in pairs(files) do + file = FFIUtil.realpath(file) or file + for coll_name in pairs(collections_to_add) do + local coll = self.coll[coll_name] + if not coll[file] then + local max_order = self:getCollectionMaxOrder(coll_name) + coll[file] = buildEntry(file, max_order + 1) + end + end end + self:write() end -function ReadCollection:removeItem(file, collection_name, no_write) - local coll_name, file_name = self:getFileCollectionName(file, collection_name) - if coll_name then - self.coll[coll_name][file_name] = nil - if not no_write then - self:write(coll_name) +function ReadCollection:removeItem(file, collection_name, no_write) -- FM: delete file; FMColl: remove file + file = FFIUtil.realpath(file) or file + if collection_name then + if self.coll[collection_name][file] then + self.coll[collection_name][file] = nil + if not no_write then + self:write(collection_name) + end + return true + end + else + local do_write + for _, coll in pairs(self.coll) do + if coll[file] then + coll[file] = nil + do_write = true + end + end + if do_write then + if not no_write then + self:write() + end + return true end - return true end end -function ReadCollection:removeItems(files) -- files = { filepath = true, } +function ReadCollection:removeItems(files) -- FM: delete files local do_write for file in pairs(files) do if self:removeItem(file, nil, true) then @@ -160,12 +198,12 @@ function ReadCollection:removeItems(files) -- files = { filepath = true, } end end -function ReadCollection:removeItemsByPath(path) +function ReadCollection:removeItemsByPath(path) -- FM: delete folder local do_write for coll_name, coll in pairs(self.coll) do for file_name in pairs(coll) do if util.stringStartsWith(file_name, path) then - self.coll[coll_name][file_name] = nil + coll[file_name] = nil do_write = true end end @@ -185,21 +223,29 @@ function ReadCollection:_updateItem(coll_name, file_name, new_filepath, new_path coll[item.file] = item end -function ReadCollection:updateItem(file, new_filepath) - local coll_name, file_name = self:getFileCollectionName(file) - if coll_name then - self:_updateItem(coll_name, file_name, new_filepath) - self:write(coll_name) +function ReadCollection:updateItem(file, new_filepath) -- FM: rename file, move file + file = FFIUtil.realpath(file) or file + local do_write + for coll_name, coll in pairs(self.coll) do + if coll[file] then + self:_updateItem(coll_name, file, new_filepath) + do_write = true + end + end + if do_write then + self:write() end end -function ReadCollection:updateItems(files, new_path) -- files = { filepath = true, } +function ReadCollection:updateItems(files, new_path) -- FM: move files local do_write for file in pairs(files) do - local coll_name, file_name = self:getFileCollectionName(file) - if coll_name then - self:_updateItem(coll_name, file_name, nil, new_path) - do_write = true + file = FFIUtil.realpath(file) or file + for coll_name, coll in pairs(self.coll) do + if coll[file] then + self:_updateItem(coll_name, file, nil, new_path) + do_write = true + end end end if do_write then @@ -207,7 +253,7 @@ function ReadCollection:updateItems(files, new_path) -- files = { filepath = tru end end -function ReadCollection:updateItemsByPath(path, new_path) +function ReadCollection:updateItemsByPath(path, new_path) -- FM: rename folder, move folder local len = #path local do_write for coll_name, coll in pairs(self.coll) do @@ -223,6 +269,58 @@ function ReadCollection:updateItemsByPath(path, new_path) end end +function ReadCollection:getOrderedCollection(collection_name) + local ordered_coll = {} + for _, item in pairs(self.coll[collection_name]) do + table.insert(ordered_coll, item) + end + table.sort(ordered_coll, function(v1, v2) return v1.order < v2.order end) + return ordered_coll +end + +function ReadCollection:updateCollectionOrder(collection_name, ordered_coll) + local coll = self.coll[collection_name] + for i, item in ipairs(ordered_coll) do + coll[item.file].order = i + end + self:write(collection_name) +end + +-- manage collections + +function ReadCollection:addCollection(coll_name) + local max_order = 0 + for _, order in pairs(self.coll_order) do + if max_order < order then + max_order = order + end + end + self.coll_order[coll_name] = max_order + 1 + self.coll[coll_name] = {} + self:write(coll_name) +end + +function ReadCollection:renameCollection(coll_name, new_name) + self.coll_order[new_name] = self.coll_order[coll_name] + self.coll[new_name] = self.coll[coll_name] + self.coll_order[coll_name] = nil + self.coll[coll_name] = nil + self:write(new_name) +end + +function ReadCollection:removeCollection(coll_name) + self.coll_order[coll_name] = nil + self.coll[coll_name] = nil + self:write() +end + +function ReadCollection:updateCollectionListOrder(ordered_coll) + for i, item in ipairs(ordered_coll) do + self.coll_order[item.name] = i + end + self:write() +end + ReadCollection:_read() return ReadCollection diff --git a/frontend/ui/elements/filemanager_menu_order.lua b/frontend/ui/elements/filemanager_menu_order.lua index 543e5507c..1f0cdb28e 100644 --- a/frontend/ui/elements/filemanager_menu_order.lua +++ b/frontend/ui/elements/filemanager_menu_order.lua @@ -168,6 +168,7 @@ local order = { "history", "open_last_document", "----------------------------", + "favorites", "collections", "----------------------------", "mass_storage_actions", -- if Device:canToggleMassStorage() diff --git a/frontend/ui/elements/reader_menu_order.lua b/frontend/ui/elements/reader_menu_order.lua index d64d5f0e6..ef685a124 100644 --- a/frontend/ui/elements/reader_menu_order.lua +++ b/frontend/ui/elements/reader_menu_order.lua @@ -227,6 +227,7 @@ local order = { "history", "open_previous_document", "----------------------------", + "favorites", "collections", "----------------------------", "book_status", diff --git a/plugins/coverbrowser.koplugin/main.lua b/plugins/coverbrowser.koplugin/main.lua index 6a3b2bc27..528943145 100644 --- a/plugins/coverbrowser.koplugin/main.lua +++ b/plugins/coverbrowser.koplugin/main.lua @@ -145,7 +145,7 @@ function CoverBrowser:addToMainMenu(menu_items) sub_item_table = history_sub_item_table, }) table.insert(sub_item_table, { - text = _("Favorites display mode"), + text = _("Collections display mode"), enabled_func = function() return not BookInfoManager:getSetting("unified_display_mode") end, @@ -364,7 +364,7 @@ function CoverBrowser:addToMainMenu(menu_items) end, }, { - text = _("Show hint for book status in favorites"), + text = _("Show hint for book status in collections"), checked_func = function() return BookInfoManager:getSetting("collections_hint_opened") end, callback = function() BookInfoManager:toggleSetting("collections_hint_opened")