diff --git a/frontend/apps/filemanager/filemanagercollection.lua b/frontend/apps/filemanager/filemanagercollection.lua index 4775916ff..1df36e1cb 100644 --- a/frontend/apps/filemanager/filemanagercollection.lua +++ b/frontend/apps/filemanager/filemanagercollection.lua @@ -1,19 +1,27 @@ 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() @@ -22,20 +30,66 @@ end function FileManagerCollection:addToMainMenu(menu_items) menu_items.collections = { - text = self.title, + text = self.default_collection_title, callback = function() self:onShowColl() 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 +146,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 +178,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 = _("Sort 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 +217,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 +241,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 = _("Sort 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 +253,269 @@ 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) + self.selected_colections = nil + if file_or_files then + -- mark collections containing the file; do not mark any collection if group of selected files passed + self.selected_colections = type(file_or_files) == "string" and ReadCollection:getCollectionsWithFile(file_or_files) or {} + 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:onLeftButtonTap(file_or_files) 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:onLeftButtonTap(file_or_files) + if file_or_files then + 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 + self.coll_list.close_callback(true) + else + local button_dialog + local buttons = { + { + { + text = _("Sort collections"), + callback = function() + UIManager:close(button_dialog) + self:sortCollections() + end, + }, + }, + { + { + text = _("New collection"), + callback = function() + UIManager:close(button_dialog) + self:addCollection() + end, + }, + }, + } + button_dialog = ButtonDialog:new{ + buttons = buttons, + } + UIManager:show(button_dialog) + end +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) + table.insert(self.coll_list.item_table, { + text = name, + mandatory = 0, + 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 = _("Sort 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 + return FileManagerCollection