mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Multiple collections (#11693)
This commit is contained in:
@@ -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"),
|
||||
|
||||
@@ -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.collections = {
|
||||
text = self.title,
|
||||
menu_items.favorites = {
|
||||
text = self.default_collection_title,
|
||||
callback = function()
|
||||
self:onShowColl()
|
||||
end,
|
||||
}
|
||||
menu_items.collections = {
|
||||
text = self.title,
|
||||
callback = function()
|
||||
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,6 +183,81 @@ function FileManagerCollection:onMenuHold(item)
|
||||
return true
|
||||
end
|
||||
|
||||
function FileManagerCollection:showCollDialog()
|
||||
local coll_dialog
|
||||
local buttons = {
|
||||
{{
|
||||
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 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,
|
||||
onConfirm = function(file)
|
||||
if not ReadCollection:isFileInCollection(file, self.coll_menu.collection_name) then
|
||||
ReadCollection:addItem(file, self.coll_menu.collection_name)
|
||||
self:updateItemTable(true) -- show added item
|
||||
end
|
||||
end,
|
||||
}
|
||||
UIManager:show(path_chooser)
|
||||
end,
|
||||
}},
|
||||
}
|
||||
if self.ui.document then
|
||||
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 is_in_collection and _("Remove current book from collection") or _("Add current book to collection")
|
||||
end,
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
if is_in_collection then
|
||||
ReadCollection:removeItem(file, self.coll_menu.collection_name)
|
||||
else
|
||||
ReadCollection:addItem(file, self.coll_menu.collection_name)
|
||||
end
|
||||
self:updateItemTable(not is_in_collection)
|
||||
end,
|
||||
}})
|
||||
end
|
||||
coll_dialog = ButtonDialog:new{
|
||||
buttons = buttons,
|
||||
}
|
||||
UIManager:show(coll_dialog)
|
||||
end
|
||||
|
||||
function FileManagerCollection:sortCollection()
|
||||
local sort_widget
|
||||
sort_widget = SortWidget:new{
|
||||
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()
|
||||
end
|
||||
}
|
||||
UIManager:show(sort_widget)
|
||||
end
|
||||
|
||||
function FileManagerCollection:MenuSetRotationModeHandler(rotation)
|
||||
if rotation ~= nil and rotation ~= Screen:getRotationMode() then
|
||||
UIManager:close(self._manager.coll_menu)
|
||||
@@ -139,113 +273,326 @@ function FileManagerCollection:MenuSetRotationModeHandler(rotation)
|
||||
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"),
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
self:sortCollection()
|
||||
end,
|
||||
}},
|
||||
{{
|
||||
text = _("Add a book to favorites"),
|
||||
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
|
||||
ReadCollection:addItem(file, self.coll_menu.collection_name)
|
||||
self:updateItemTable()
|
||||
end
|
||||
end,
|
||||
}
|
||||
UIManager:show(path_chooser)
|
||||
end,
|
||||
}},
|
||||
}
|
||||
if self.ui.document then
|
||||
local has_file = ReadCollection:hasFile(self.ui.document.file)
|
||||
table.insert(buttons, {{
|
||||
text_func = function()
|
||||
return has_file and _("Remove current book from favorites") or _("Add current book to favorites")
|
||||
end,
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
if has_file then
|
||||
ReadCollection:removeItem(self.ui.document.file)
|
||||
else
|
||||
ReadCollection:addItem(self.ui.document.file, self.coll_menu.collection_name)
|
||||
end
|
||||
self:updateItemTable()
|
||||
end,
|
||||
}})
|
||||
end
|
||||
coll_dialog = ButtonDialog:new{
|
||||
buttons = buttons,
|
||||
}
|
||||
UIManager:show(coll_dialog)
|
||||
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,
|
||||
callback = function()
|
||||
ReadCollection:updateCollectionOrder(self.coll_menu.collection_name, sort_widget.item_table)
|
||||
self:updateItemTable()
|
||||
end
|
||||
}
|
||||
UIManager:show(sort_widget)
|
||||
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
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
end
|
||||
end
|
||||
return self.coll[collection_name][file] and true or 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: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: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
|
||||
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
|
||||
self:write()
|
||||
end
|
||||
|
||||
function ReadCollection:addItemsMultiple(files, collections_to_add)
|
||||
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
|
||||
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
|
||||
if do_write then
|
||||
self:write(collection_name)
|
||||
self:write()
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
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)
|
||||
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,20 +223,12 @@ 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)
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:updateItems(files, new_path) -- files = { filepath = true, }
|
||||
function ReadCollection:updateItem(file, new_filepath) -- FM: rename file, move file
|
||||
file = FFIUtil.realpath(file) or file
|
||||
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)
|
||||
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
|
||||
@@ -207,7 +237,23 @@ function ReadCollection:updateItems(files, new_path) -- files = { filepath = tru
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:updateItemsByPath(path, new_path)
|
||||
function ReadCollection:updateItems(files, new_path) -- FM: move files
|
||||
local do_write
|
||||
for file in pairs(files) do
|
||||
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
|
||||
self:write()
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@@ -168,6 +168,7 @@ local order = {
|
||||
"history",
|
||||
"open_last_document",
|
||||
"----------------------------",
|
||||
"favorites",
|
||||
"collections",
|
||||
"----------------------------",
|
||||
"mass_storage_actions", -- if Device:canToggleMassStorage()
|
||||
|
||||
@@ -227,6 +227,7 @@ local order = {
|
||||
"history",
|
||||
"open_previous_document",
|
||||
"----------------------------",
|
||||
"favorites",
|
||||
"collections",
|
||||
"----------------------------",
|
||||
"book_status",
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user