mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
[feat] Favorites: organize book into collections (#5527)
View, add, remove, sort, open book to/from collections. For now, only one collection named Favorites.
This commit is contained in:
@@ -9,6 +9,7 @@ local DocumentRegistry = require("document/documentregistry")
|
||||
local Event = require("ui/event")
|
||||
local FileChooser = require("ui/widget/filechooser")
|
||||
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
|
||||
local FileManagerCollection = require("apps/filemanager/filemanagercollection")
|
||||
local FileManagerConverter = require("apps/filemanager/filemanagerconverter")
|
||||
local FileManagerFileSearcher = require("apps/filemanager/filemanagerfilesearcher")
|
||||
local FileManagerHistory = require("apps/filemanager/filemanagerhistory")
|
||||
@@ -23,6 +24,7 @@ local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local InputDialog = require("ui/widget/inputdialog")
|
||||
local MultiConfirmBox = require("ui/widget/multiconfirmbox")
|
||||
local PluginLoader = require("pluginloader")
|
||||
local ReadCollection = require("readcollection")
|
||||
local ReaderDeviceStatus = require("apps/reader/modules/readerdevicestatus")
|
||||
local ReaderDictionary = require("apps/reader/modules/readerdictionary")
|
||||
local ReaderGesture = require("apps/reader/modules/readergesture")
|
||||
@@ -288,25 +290,17 @@ function FileManager:init()
|
||||
},
|
||||
-- a little hack to get visual functionality grouping
|
||||
{},
|
||||
{
|
||||
}
|
||||
if lfs.attributes(file, "mode") == "file" then
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = _("Open with…"),
|
||||
enabled = lfs.attributes(file, "mode") == "file" and (DocumentRegistry:getProviders(file) == nil
|
||||
or #(DocumentRegistry:getProviders(file)) > 1),
|
||||
enabled = DocumentRegistry:getProviders(file) == nil or #(DocumentRegistry:getProviders(file)) > 1,
|
||||
callback = function()
|
||||
UIManager:close(self.file_dialog)
|
||||
self:showSetProviderButtons(file, FileManager.instance, ReaderUI)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Convert"),
|
||||
enabled = lfs.attributes(file, "mode") == "file"
|
||||
and FileManagerConverter:isSupported(file),
|
||||
callback = function()
|
||||
UIManager:close(self.file_dialog)
|
||||
FileManagerConverter:showConvertButtons(file, self)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Book information"),
|
||||
enabled = FileManagerBookInfo:isSupported(file),
|
||||
@@ -314,9 +308,41 @@ function FileManager:init()
|
||||
FileManagerBookInfo:show(file)
|
||||
UIManager:close(self.file_dialog)
|
||||
end,
|
||||
}
|
||||
})
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text_func = function()
|
||||
if ReadCollection:checkItemExist(file) then
|
||||
return _("Remove from favorites")
|
||||
else
|
||||
return _("Add to favorites")
|
||||
end
|
||||
end,
|
||||
enabled = DocumentRegistry:getProviders(file) ~= nil,
|
||||
callback = function()
|
||||
if ReadCollection:checkItemExist(file) then
|
||||
ReadCollection:removeItem(file)
|
||||
else
|
||||
ReadCollection:addItem(file)
|
||||
end
|
||||
UIManager:close(self.file_dialog)
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
if FileManagerConverter:isSupported(file) then
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = _("Convert"),
|
||||
enabled = true,
|
||||
callback = function()
|
||||
UIManager:close(self.file_dialog)
|
||||
FileManagerConverter:showConvertButtons(file, self)
|
||||
end,
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
if lfs.attributes(file, "mode") == "directory" then
|
||||
local realpath = util.realpath(file)
|
||||
table.insert(buttons, {
|
||||
@@ -361,6 +387,9 @@ function FileManager:init()
|
||||
table.insert(self, FileManagerHistory:new{
|
||||
ui = self,
|
||||
})
|
||||
table.insert(self, FileManagerCollection:new{
|
||||
ui = self,
|
||||
})
|
||||
table.insert(self, FileManagerFileSearcher:new{ ui = self })
|
||||
table.insert(self, FileManagerShortcuts:new{ ui = self })
|
||||
table.insert(self, ReaderDictionary:new{ ui = self })
|
||||
@@ -695,10 +724,11 @@ function FileManager:pasteHere(file)
|
||||
self:moveFile(DocSettings:getSidecarDir(orig), dest) -- dest is always a directory
|
||||
end
|
||||
if self:moveFile(orig, dest) then
|
||||
--update history
|
||||
-- Update history and collections.
|
||||
local dest_file = string.format("%s/%s", dest, util.basename(orig))
|
||||
require("readhistory"):updateItemByPath(orig, dest_file)
|
||||
--update last open file
|
||||
ReadCollection:updateItemByPath(orig, dest_file)
|
||||
-- Update last open file.
|
||||
if G_reader_settings:readSetting("lastfile") == orig then
|
||||
G_reader_settings:saveSetting("lastfile", dest_file)
|
||||
end
|
||||
@@ -764,7 +794,7 @@ function FileManager:createFolder(curr_folder, new_folder)
|
||||
end
|
||||
|
||||
function FileManager:deleteFile(file)
|
||||
local ok, err
|
||||
local ok, err, is_dir
|
||||
local file_abs_path = util.realpath(file)
|
||||
if file_abs_path == nil then
|
||||
UIManager:show(InfoMessage:new{
|
||||
@@ -778,6 +808,7 @@ function FileManager:deleteFile(file)
|
||||
ok, err = os.remove(file_abs_path)
|
||||
else
|
||||
ok, err = util.purgeDir(file_abs_path)
|
||||
is_dir = true
|
||||
end
|
||||
if ok and not err then
|
||||
if is_doc then
|
||||
@@ -789,6 +820,7 @@ function FileManager:deleteFile(file)
|
||||
end
|
||||
doc_settings:purge()
|
||||
end
|
||||
ReadCollection:removeItemByPath(file, is_dir)
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = util.template(_("Deleted %1"), file),
|
||||
timeout = 2,
|
||||
@@ -804,6 +836,7 @@ function FileManager:renameFile(file)
|
||||
if util.basename(file) ~= self.rename_dialog:getInputText() then
|
||||
local dest = util.joinPath(util.dirname(file), self.rename_dialog:getInputText())
|
||||
if self:moveFile(file, dest) then
|
||||
ReadCollection:updateItemByPath(file, dest)
|
||||
if lfs.attributes(dest, "mode") == "file" then
|
||||
local doc = require("docsettings")
|
||||
local move_history = true;
|
||||
|
||||
124
frontend/apps/filemanager/filemanagercollection.lua
Normal file
124
frontend/apps/filemanager/filemanagercollection.lua
Normal file
@@ -0,0 +1,124 @@
|
||||
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
|
||||
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local Menu = require("ui/widget/menu")
|
||||
local ReadCollection = require("readcollection")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local Screen = require("device").screen
|
||||
local _ = require("gettext")
|
||||
|
||||
local FileManagerCollection = InputContainer:extend{
|
||||
coll_menu_title = _("Favorites"),
|
||||
}
|
||||
|
||||
function FileManagerCollection:init()
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
function FileManagerCollection:addToMainMenu(menu_items)
|
||||
menu_items.collections = {
|
||||
text = self.coll_menu_title,
|
||||
callback = function()
|
||||
self:onShowColl("favorites")
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
function FileManagerCollection:updateItemTable()
|
||||
-- Try to stay on current page.
|
||||
local select_number = nil
|
||||
if self.coll_menu.page and self.coll_menu.perpage then
|
||||
select_number = (self.coll_menu.page - 1) * self.coll_menu.perpage + 1
|
||||
end
|
||||
self.coll_menu:switchItemTable(self.coll_menu_title,
|
||||
ReadCollection:prepareList(self.coll_menu.collection), select_number)
|
||||
end
|
||||
|
||||
function FileManagerCollection:onMenuHold(item)
|
||||
self.collfile_dialog = nil
|
||||
local buttons = {
|
||||
{
|
||||
{
|
||||
text = _("Sort"),
|
||||
callback = function()
|
||||
UIManager:close(self.collfile_dialog)
|
||||
local item_table = {}
|
||||
for i=1, #self._manager.coll_menu.item_table do
|
||||
table.insert(item_table, {text = self._manager.coll_menu.item_table[i].text, label = self._manager.coll_menu.item_table[i].file})
|
||||
end
|
||||
local SortWidget = require("ui/widget/sortwidget")
|
||||
local sort_item
|
||||
sort_item = SortWidget:new{
|
||||
title = _("Sort favorites"),
|
||||
item_table = item_table,
|
||||
callback = function()
|
||||
local new_order_table = {}
|
||||
for i=1, #sort_item.item_table do
|
||||
table.insert(new_order_table, {
|
||||
file = sort_item.item_table[i].label,
|
||||
order = i
|
||||
})
|
||||
end
|
||||
ReadCollection:writeCollection(new_order_table, self._manager.coll_menu.collection)
|
||||
self._manager:updateItemTable()
|
||||
end
|
||||
}
|
||||
UIManager:show(sort_item)
|
||||
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Remove from collection"),
|
||||
callback = function()
|
||||
ReadCollection:removeItem(item.file, self._manager.coll_menu.collection)
|
||||
self._manager:updateItemTable()
|
||||
UIManager:close(self.collfile_dialog)
|
||||
end,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
text = _("Book information"),
|
||||
enabled = FileManagerBookInfo:isSupported(item.file),
|
||||
callback = function()
|
||||
FileManagerBookInfo:show(item.file)
|
||||
UIManager:close(self.collfile_dialog)
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
self.collfile_dialog = ButtonDialogTitle:new{
|
||||
title = item.text:match("([^/]+)$"),
|
||||
title_align = "center",
|
||||
buttons = buttons,
|
||||
}
|
||||
UIManager:show(self.collfile_dialog)
|
||||
return true
|
||||
end
|
||||
|
||||
function FileManagerCollection:onShowColl(collection)
|
||||
self.coll_menu = Menu:new{
|
||||
ui = self.ui,
|
||||
width = Screen:getWidth(),
|
||||
height = Screen:getHeight(),
|
||||
covers_fullscreen = true, -- hint for UIManager:_repaint()
|
||||
is_borderless = true,
|
||||
is_popout = false,
|
||||
onMenuHold = self.onMenuHold,
|
||||
_manager = self,
|
||||
collection = collection,
|
||||
}
|
||||
self:updateItemTable()
|
||||
self.coll_menu.close_callback = function()
|
||||
-- Close it at next tick so it stays displayed
|
||||
-- while a book is opening (avoids a transient
|
||||
-- display of the underlying File Browser)
|
||||
UIManager:nextTick(function()
|
||||
UIManager:close(self.coll_menu)
|
||||
end)
|
||||
end
|
||||
UIManager:show(self.coll_menu)
|
||||
return true
|
||||
end
|
||||
|
||||
return FileManagerCollection
|
||||
@@ -11,6 +11,7 @@ local DocSettings = require("docsettings")
|
||||
local DocumentRegistry = require("document/documentregistry")
|
||||
local Event = require("ui/event")
|
||||
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
|
||||
local FileManagerCollection = require("apps/filemanager/filemanagercollection")
|
||||
local FileManagerHistory = require("apps/filemanager/filemanagerhistory")
|
||||
local FileManagerFileSearcher = require("apps/filemanager/filemanagerfilesearcher")
|
||||
local FileManagerShortcuts = require("apps/filemanager/filemanagershortcuts")
|
||||
@@ -351,6 +352,11 @@ function ReaderUI:init()
|
||||
dialog = self.dialog,
|
||||
ui = self,
|
||||
})
|
||||
-- collections/favorites view
|
||||
self:registerModule("collections", FileManagerCollection:new{
|
||||
dialog = self.dialog,
|
||||
ui = self,
|
||||
})
|
||||
-- book info
|
||||
self:registerModule("bookinfo", FileManagerBookInfo:new{
|
||||
dialog = self.dialog,
|
||||
|
||||
159
frontend/readcollection.lua
Normal file
159
frontend/readcollection.lua
Normal file
@@ -0,0 +1,159 @@
|
||||
local DataStorage = require("datastorage")
|
||||
local LuaSettings = require("luasettings")
|
||||
local getFriendlySize = require("util").getFriendlySize
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local realpath = require("ffi/util").realpath
|
||||
local util = require("util")
|
||||
|
||||
local DEFAULT_COLLECTION_NAME = "favorites"
|
||||
local collection_file = DataStorage:getSettingsDir() .. "/collection.lua"
|
||||
|
||||
local ReadCollection = {}
|
||||
|
||||
function ReadCollection:read(collection_name)
|
||||
if not collection_name then collection_name = DEFAULT_COLLECTION_NAME end
|
||||
local collections = LuaSettings:open(collection_file)
|
||||
local coll = collections:readSetting(collection_name) or {}
|
||||
local coll_max_item = 0
|
||||
|
||||
for _, v in pairs(coll) do
|
||||
if v.order > coll_max_item then
|
||||
coll_max_item = v.order
|
||||
end
|
||||
end
|
||||
return coll, coll_max_item
|
||||
end
|
||||
|
||||
function ReadCollection:readAllCollection()
|
||||
local collection = LuaSettings:open(collection_file)
|
||||
if collection and collection.data then
|
||||
return collection.data
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:prepareList(collection_name)
|
||||
local data = self:read(collection_name)
|
||||
local list = {}
|
||||
for _, v in pairs(data) do
|
||||
local file_exists = lfs.attributes(v.file, "mode") == "file"
|
||||
table.insert(list, {
|
||||
order = v.order,
|
||||
text = v.file:gsub(".*/", ""),
|
||||
file = realpath(v.file) or v.file, -- keep orig file path of deleted files
|
||||
dim = not file_exists, -- "dim", as expected by Menu
|
||||
mandatory = file_exists and getFriendlySize(lfs.attributes(v.file, "size") or 0),
|
||||
callback = function()
|
||||
local ReaderUI = require("apps/reader/readerui")
|
||||
ReaderUI:showReader(v.file)
|
||||
end
|
||||
})
|
||||
end
|
||||
table.sort(list, function(v1,v2)
|
||||
return v1.order < v2.order
|
||||
end)
|
||||
return list
|
||||
end
|
||||
|
||||
function ReadCollection:removeItemByPath(path, is_dir)
|
||||
local dir
|
||||
local should_write = false
|
||||
if is_dir then
|
||||
path = path .. "/"
|
||||
end
|
||||
local coll = self:readAllCollection()
|
||||
for i, _ in pairs(coll) do
|
||||
local single_collection = coll[i]
|
||||
for item = #single_collection, 1, -1 do
|
||||
if not is_dir and single_collection[item].file == path then
|
||||
should_write = true
|
||||
table.remove(single_collection, item)
|
||||
elseif is_dir then
|
||||
dir = util.splitFilePathName(single_collection[item].file)
|
||||
if dir == path then
|
||||
should_write = true
|
||||
table.remove(single_collection, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if should_write then
|
||||
local collection = LuaSettings:open(collection_file)
|
||||
collection.data = coll
|
||||
collection:flush()
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:updateItemByPath(old_path, new_path)
|
||||
local is_dir = false
|
||||
local dir, file
|
||||
if lfs.attributes(new_path, "mode") == "directory" then
|
||||
is_dir = true
|
||||
old_path = old_path .. "/"
|
||||
end
|
||||
local should_write = false
|
||||
local coll = self:readAllCollection()
|
||||
for i, j in pairs(coll) do
|
||||
for k, v in pairs(j) do
|
||||
if not is_dir and v.file == old_path then
|
||||
should_write = true
|
||||
coll[i][k].file = new_path
|
||||
elseif is_dir then
|
||||
dir, file = util.splitFilePathName(v.file)
|
||||
if dir == old_path then
|
||||
should_write = true
|
||||
coll[i][k].file = string.format("%s/%s", new_path, file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if should_write then
|
||||
local collection = LuaSettings:open(collection_file)
|
||||
collection.data = coll
|
||||
collection:flush()
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:removeItem(item, collection_name)
|
||||
local coll = self:read(collection_name)
|
||||
for k, v in pairs(coll) do
|
||||
if v.file == item then
|
||||
table.remove(coll, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
self:writeCollection(coll, collection_name)
|
||||
end
|
||||
|
||||
function ReadCollection:writeCollection(coll_items, collection_name)
|
||||
if not collection_name then collection_name = DEFAULT_COLLECTION_NAME end
|
||||
local collection = LuaSettings:open(collection_file)
|
||||
collection:saveSetting(collection_name, coll_items)
|
||||
collection:flush()
|
||||
end
|
||||
|
||||
function ReadCollection:addItem(file, collection_name)
|
||||
local coll, coll_max_item = self:read(collection_name)
|
||||
coll_max_item = coll_max_item + 1
|
||||
local collection_item =
|
||||
{
|
||||
file = file,
|
||||
order = coll_max_item
|
||||
}
|
||||
table.insert(coll, collection_item)
|
||||
self:writeCollection(coll, collection_name)
|
||||
end
|
||||
|
||||
function ReadCollection:checkItemExist(item, collection_name)
|
||||
local coll = self:read(collection_name)
|
||||
for _, v in pairs(coll) do
|
||||
if v.file == item then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return ReadCollection
|
||||
|
||||
@@ -130,6 +130,8 @@ local order = {
|
||||
"history",
|
||||
"open_last_document",
|
||||
"----------------------------",
|
||||
"collections",
|
||||
"----------------------------",
|
||||
"system_statistics",
|
||||
"mass_storage_actions",
|
||||
"----------------------------",
|
||||
|
||||
@@ -151,6 +151,8 @@ local order = {
|
||||
"history",
|
||||
"open_previous_document",
|
||||
"----------------------------",
|
||||
"collections",
|
||||
"----------------------------",
|
||||
"book_status",
|
||||
"book_info",
|
||||
"----------------------------",
|
||||
|
||||
@@ -406,7 +406,7 @@ function SortWidget:_populateItems()
|
||||
height = self.item_height,
|
||||
width = self.item_width,
|
||||
text = self.item_table[idx].text,
|
||||
lable = self.item_table[idx].label,
|
||||
label = self.item_table[idx].label,
|
||||
invert = invert_status,
|
||||
index = idx,
|
||||
show_parent = self,
|
||||
|
||||
@@ -267,7 +267,8 @@ function BookInfoManager:getBookInfo(filepath, get_cover)
|
||||
-- files with unknown book extension. If not a supported extension,
|
||||
-- returns a bookinfo like-object enough for a correct display and
|
||||
-- to not trigger extraction, so we don't clutter DB with such files.
|
||||
if not DocumentRegistry:hasProvider(filepath) then
|
||||
local is_directory = lfs.attributes(filepath, "mode") == "directory"
|
||||
if is_directory or not DocumentRegistry:hasProvider(filepath) then
|
||||
return {
|
||||
directory = directory,
|
||||
filename = filename,
|
||||
@@ -277,6 +278,8 @@ function BookInfoManager:getBookInfo(filepath, get_cover)
|
||||
has_cover = nil,
|
||||
ignore_meta = "Y",
|
||||
ignore_cover = "Y",
|
||||
-- for CoverMenu to *not* extend the onHold dialog:
|
||||
_is_directory = is_directory,
|
||||
-- for ListMenu to show the filename *with* suffix:
|
||||
_no_provider = true
|
||||
}
|
||||
|
||||
@@ -204,8 +204,8 @@ function CoverMenu:updateItems(select_number)
|
||||
self.onFileHold_orig(self, file)
|
||||
|
||||
local bookinfo = BookInfoManager:getBookInfo(file)
|
||||
if not bookinfo then
|
||||
-- If no bookinfo (yet) about this file, let the original dialog be
|
||||
if not bookinfo or bookinfo._is_directory then
|
||||
-- If no bookinfo (yet) about this file, or it's a directory, let the original dialog be
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -220,7 +220,7 @@ function CoverMenu:updateItems(select_number)
|
||||
UIManager:clearRenderStack()
|
||||
|
||||
-- Replace Book information callback to use directly our bookinfo
|
||||
orig_buttons[4][3].callback = function()
|
||||
orig_buttons[4][2].callback = function()
|
||||
FileManagerBookInfo:show(file, bookinfo)
|
||||
UIManager:close(self.file_dialog)
|
||||
end
|
||||
@@ -237,7 +237,7 @@ function CoverMenu:updateItems(select_number)
|
||||
end
|
||||
|
||||
-- Add some new buttons to original buttons set
|
||||
table.insert(orig_buttons, {
|
||||
table.insert(orig_buttons[5], 1,
|
||||
{ -- Mark the book as read/unread
|
||||
text_func = function()
|
||||
-- If the book has a cache entry, it means it has a sidecar file, and it *may* have the info we need.
|
||||
@@ -289,11 +289,8 @@ function CoverMenu:updateItems(select_number)
|
||||
UIManager:close(self.file_dialog)
|
||||
self:updateItems()
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
-- Move the "Convert" button ([4][2]) to the left of the "Mark as..." button [5][1] we've just added
|
||||
table.insert(orig_buttons[5], 1, table.remove(orig_buttons[4], 2))
|
||||
}
|
||||
)
|
||||
|
||||
-- Keep on adding new buttons
|
||||
table.insert(orig_buttons, {
|
||||
@@ -521,6 +518,124 @@ function CoverMenu:onHistoryMenuHold(item)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Similar to onFileHold setup just above, but for Collections,
|
||||
-- which is plugged in main.lua _FileManagerCollections_updateItemTable()
|
||||
function CoverMenu:onCollectionsMenuHold(item)
|
||||
-- Call original function: it will create a ButtonDialog
|
||||
-- and store it as self.collfile_dialog, and UIManager:show() it.
|
||||
self.onMenuHold_orig(self, item)
|
||||
local file = item.file
|
||||
|
||||
local bookinfo = BookInfoManager:getBookInfo(file)
|
||||
if not bookinfo then
|
||||
-- If no bookinfo (yet) about this file, let the original dialog be
|
||||
return true
|
||||
end
|
||||
|
||||
-- Remember some of this original ButtonDialogTitle properties
|
||||
local orig_title = self.collfile_dialog.title
|
||||
local orig_title_align = self.collfile_dialog.title_align
|
||||
local orig_buttons = self.collfile_dialog.buttons
|
||||
-- Close original ButtonDialog (it has not yet been painted
|
||||
-- on screen, so we won't see it)
|
||||
UIManager:close(self.collfile_dialog)
|
||||
UIManager:clearRenderStack()
|
||||
|
||||
-- Replace Book information callback to use directly our bookinfo
|
||||
orig_buttons[2][1].callback = function()
|
||||
FileManagerBookInfo:show(file, bookinfo)
|
||||
UIManager:close(self.collfile_dialog)
|
||||
end
|
||||
|
||||
-- Add some new buttons to original buttons set
|
||||
table.insert(orig_buttons, {
|
||||
{ -- Allow user to view real size cover in ImageViewer
|
||||
text = _("View full size cover"),
|
||||
enabled = bookinfo.has_cover and true or false,
|
||||
callback = function()
|
||||
local document = DocumentRegistry:openDocument(file)
|
||||
if document then
|
||||
if document.loadDocument then -- needed for crengine
|
||||
document:loadDocument(false) -- load only metadata
|
||||
end
|
||||
local cover_bb = document:getCoverPageImage()
|
||||
if cover_bb then
|
||||
local imgviewer = ImageViewer:new{
|
||||
image = cover_bb,
|
||||
with_title_bar = false,
|
||||
fullscreen = true,
|
||||
}
|
||||
UIManager:show(imgviewer)
|
||||
else
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("No cover image available."),
|
||||
})
|
||||
end
|
||||
UIManager:close(self.collfile_dialog)
|
||||
DocumentRegistry:closeDocument(file)
|
||||
end
|
||||
end,
|
||||
},
|
||||
{ -- Allow user to directly view description in TextViewer
|
||||
text = bookinfo.description and _("View book description") or _("No book description"),
|
||||
enabled = bookinfo.description and true or false,
|
||||
callback = function()
|
||||
local description = require("util").htmlToPlainTextIfHtml(bookinfo.description)
|
||||
local textviewer = TextViewer:new{
|
||||
title = bookinfo.title,
|
||||
text = description,
|
||||
}
|
||||
UIManager:close(self.collfile_dialog)
|
||||
UIManager:show(textviewer)
|
||||
end,
|
||||
},
|
||||
})
|
||||
table.insert(orig_buttons, {
|
||||
{ -- Allow user to ignore some offending cover image
|
||||
text = bookinfo.ignore_cover and _("Unignore cover") or _("Ignore cover"),
|
||||
enabled = bookinfo.has_cover and true or false,
|
||||
callback = function()
|
||||
BookInfoManager:setBookInfoProperties(file, {
|
||||
["ignore_cover"] = not bookinfo.ignore_cover and 'Y' or false,
|
||||
})
|
||||
UIManager:close(self.collfile_dialog)
|
||||
self:updateItems()
|
||||
end,
|
||||
},
|
||||
{ -- Allow user to ignore some bad metadata (filename will be used instead)
|
||||
text = bookinfo.ignore_meta and _("Unignore metadata") or _("Ignore metadata"),
|
||||
enabled = bookinfo.has_meta and true or false,
|
||||
callback = function()
|
||||
BookInfoManager:setBookInfoProperties(file, {
|
||||
["ignore_meta"] = not bookinfo.ignore_meta and 'Y' or false,
|
||||
})
|
||||
UIManager:close(self.collfile_dialog)
|
||||
self:updateItems()
|
||||
end,
|
||||
},
|
||||
})
|
||||
table.insert(orig_buttons, {
|
||||
{ -- Allow a new extraction (multiple interruptions, book replaced)...
|
||||
text = _("Refresh cached book information"),
|
||||
enabled = bookinfo and true or false,
|
||||
callback = function()
|
||||
BookInfoManager:deleteBookInfo(file)
|
||||
UIManager:close(self.collfile_dialog)
|
||||
self:updateItems()
|
||||
end,
|
||||
},
|
||||
})
|
||||
-- Create the new ButtonDialog, and let UIManager show it
|
||||
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
|
||||
self.collfile_dialog = ButtonDialogTitle:new{
|
||||
title = orig_title,
|
||||
title_align = orig_title_align,
|
||||
buttons = orig_buttons,
|
||||
}
|
||||
UIManager:show(self.collfile_dialog)
|
||||
return true
|
||||
end
|
||||
|
||||
function CoverMenu:onCloseWidget()
|
||||
-- Due to close callback in FileManagerHistory:onShowHist, we may be called
|
||||
-- multiple times (witnessed that with print(debug.traceback())
|
||||
|
||||
@@ -23,6 +23,9 @@ local _FileChooser_onCloseWidget_orig = FileChooser.onCloseWidget
|
||||
local FileManagerHistory = require("apps/filemanager/filemanagerhistory")
|
||||
local _FileManagerHistory_updateItemTable_orig = FileManagerHistory.updateItemTable
|
||||
|
||||
local FileManagerCollection = require("apps/filemanager/filemanagercollection")
|
||||
local _FileManagerCollection_updateItemTable_orig = FileManagerCollection.updateItemTable
|
||||
|
||||
local FileManager = require("apps/filemanager/filemanager")
|
||||
local _FileManager_tapPlus_orig = FileManager.tapPlus
|
||||
|
||||
@@ -40,6 +43,7 @@ local DISPLAY_MODES = {
|
||||
local init_done = false
|
||||
local filemanager_display_mode = false -- not initialized yet
|
||||
local history_display_mode = false -- not initialized yet
|
||||
local collection_display_mode = false -- not initialized yet
|
||||
local series_mode = nil -- defaults to not display series
|
||||
|
||||
local CoverBrowser = InputContainer:new{
|
||||
@@ -75,6 +79,7 @@ function CoverBrowser:init()
|
||||
|
||||
self:setupFileManagerDisplayMode(BookInfoManager:getSetting("filemanager_display_mode"))
|
||||
self:setupHistoryDisplayMode(BookInfoManager:getSetting("history_display_mode"))
|
||||
self:setupCollectionDisplayMode(BookInfoManager:getSetting("collection_display_mode"))
|
||||
series_mode = BookInfoManager:getSetting("series_mode")
|
||||
|
||||
init_done = true
|
||||
@@ -265,6 +270,54 @@ function CoverBrowser:addToMainMenu(menu_items)
|
||||
separator = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
text = _("Favorites display mode"),
|
||||
sub_item_table = {
|
||||
{
|
||||
text = _("Classic (filename only)"),
|
||||
checked_func = function() return not collection_display_mode end,
|
||||
callback = function()
|
||||
self:setupCollectionDisplayMode("")
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Mosaic with cover images"),
|
||||
checked_func = function() return collection_display_mode == "mosaic_image" end,
|
||||
callback = function()
|
||||
self:setupCollectionDisplayMode("mosaic_image")
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Mosaic with text covers"),
|
||||
checked_func = function() return collection_display_mode == "mosaic_text" end,
|
||||
callback = function()
|
||||
self:setupCollectionDisplayMode("mosaic_text")
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Detailed list with cover images and metadata"),
|
||||
checked_func = function() return collection_display_mode == "list_image_meta" end,
|
||||
callback = function()
|
||||
self:setupCollectionDisplayMode("list_image_meta")
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Detailed list with metadata, no images"),
|
||||
checked_func = function() return collection_display_mode == "list_only_meta" end,
|
||||
callback = function()
|
||||
self:setupCollectionDisplayMode("list_only_meta")
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Detailed list with cover images and filenames"),
|
||||
checked_func = function() return collection_display_mode == "list_image_filename" end,
|
||||
callback = function()
|
||||
self:setupCollectionDisplayMode("list_image_filename")
|
||||
end,
|
||||
separator = true,
|
||||
},
|
||||
},
|
||||
separator = true,
|
||||
},
|
||||
-- Misc settings
|
||||
@@ -272,28 +325,45 @@ function CoverBrowser:addToMainMenu(menu_items)
|
||||
text = _("Other settings"),
|
||||
sub_item_table = {
|
||||
{
|
||||
text = _("Show hint for books with description"),
|
||||
checked_func = function() return not BookInfoManager:getSetting("no_hint_description") end,
|
||||
callback = function()
|
||||
if BookInfoManager:getSetting("no_hint_description") then
|
||||
BookInfoManager:saveSetting("no_hint_description", false)
|
||||
else
|
||||
BookInfoManager:saveSetting("no_hint_description", true)
|
||||
end
|
||||
self:refreshFileManagerInstance()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Show hint for opened books in history"),
|
||||
checked_func = function() return BookInfoManager:getSetting("history_hint_opened") end,
|
||||
callback = function()
|
||||
if BookInfoManager:getSetting("history_hint_opened") then
|
||||
BookInfoManager:saveSetting("history_hint_opened", false)
|
||||
else
|
||||
BookInfoManager:saveSetting("history_hint_opened", true)
|
||||
end
|
||||
self:refreshFileManagerInstance()
|
||||
end,
|
||||
text = _("Display hints"),
|
||||
sub_item_table = {
|
||||
{
|
||||
text = _("Show hint for books with description"),
|
||||
checked_func = function() return not BookInfoManager:getSetting("no_hint_description") end,
|
||||
callback = function()
|
||||
if BookInfoManager:getSetting("no_hint_description") then
|
||||
BookInfoManager:saveSetting("no_hint_description", false)
|
||||
else
|
||||
BookInfoManager:saveSetting("no_hint_description", true)
|
||||
end
|
||||
self:refreshFileManagerInstance()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Show hint for opened books in history"),
|
||||
checked_func = function() return BookInfoManager:getSetting("history_hint_opened") end,
|
||||
callback = function()
|
||||
if BookInfoManager:getSetting("history_hint_opened") then
|
||||
BookInfoManager:saveSetting("history_hint_opened", false)
|
||||
else
|
||||
BookInfoManager:saveSetting("history_hint_opened", true)
|
||||
end
|
||||
self:refreshFileManagerInstance()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Show hint for opened books in favorites"),
|
||||
checked_func = function() return BookInfoManager:getSetting("collections_hint_opened") end,
|
||||
callback = function()
|
||||
if BookInfoManager:getSetting("collections_hint_opened") then
|
||||
BookInfoManager:saveSetting("collections_hint_opened", false)
|
||||
else
|
||||
BookInfoManager:saveSetting("collections_hint_opened", true)
|
||||
end
|
||||
self:refreshFileManagerInstance()
|
||||
end,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
text = _("Series "),
|
||||
@@ -666,4 +736,82 @@ function CoverBrowser:setupHistoryDisplayMode(display_mode)
|
||||
end
|
||||
end
|
||||
|
||||
local function _FileManagerCollections_updateItemTable(self)
|
||||
-- 'self' here is the single FileManagerCollections instance
|
||||
-- FileManagerCollections has just created a new instance of Menu as 'coll_menu'
|
||||
-- at each display of Collection/Favorites. Soon after instantiation, this method
|
||||
-- is called. The first time it is called, we replace some methods.
|
||||
local display_mode = self.display_mode
|
||||
local coll_menu = self.coll_menu
|
||||
|
||||
if not coll_menu._coverbrowser_overridden then
|
||||
coll_menu._coverbrowser_overridden = true
|
||||
|
||||
-- In both mosaic and list modes, replace original methods with those from
|
||||
-- our generic CoverMenu
|
||||
local CoverMenu = require("covermenu")
|
||||
coll_menu.updateItems = CoverMenu.updateItems
|
||||
coll_menu.onCloseWidget = CoverMenu.onCloseWidget
|
||||
-- Also replace original onMenuHold (it will use original method, so remember it)
|
||||
coll_menu.onMenuHold_orig = coll_menu.onMenuHold
|
||||
coll_menu.onMenuHold = CoverMenu.onCollectionsMenuHold
|
||||
|
||||
if display_mode == "mosaic_image" or display_mode == "mosaic_text" then -- mosaic mode
|
||||
-- Replace some other original methods with those from our MosaicMenu
|
||||
local MosaicMenu = require("mosaicmenu")
|
||||
coll_menu._recalculateDimen = MosaicMenu._recalculateDimen
|
||||
coll_menu._updateItemsBuildUI = MosaicMenu._updateItemsBuildUI
|
||||
-- Set MosaicMenu behaviour:
|
||||
coll_menu._do_cover_images = display_mode ~= "mosaic_text"
|
||||
|
||||
elseif display_mode == "list_image_meta" or display_mode == "list_only_meta" or
|
||||
display_mode == "list_image_filename" then -- list modes
|
||||
-- Replace some other original methods with those from our ListMenu
|
||||
local ListMenu = require("listmenu")
|
||||
coll_menu._recalculateDimen = ListMenu._recalculateDimen
|
||||
coll_menu._updateItemsBuildUI = ListMenu._updateItemsBuildUI
|
||||
-- Set ListMenu behaviour:
|
||||
coll_menu._do_cover_images = display_mode ~= "list_only_meta"
|
||||
coll_menu._do_filename_only = display_mode == "list_image_filename"
|
||||
|
||||
end
|
||||
coll_menu._do_hint_opened = BookInfoManager:getSetting("collections_hint_opened")
|
||||
end
|
||||
|
||||
-- And do now what the original does
|
||||
_FileManagerCollection_updateItemTable_orig(self)
|
||||
end
|
||||
|
||||
|
||||
function CoverBrowser:setupCollectionDisplayMode(display_mode)
|
||||
if not DISPLAY_MODES[display_mode] then
|
||||
display_mode = nil -- unknow mode, fallback to classic
|
||||
end
|
||||
if init_done and display_mode == collection_display_mode then -- no change
|
||||
return
|
||||
end
|
||||
if init_done then -- save new mode in db
|
||||
BookInfoManager:saveSetting("collection_display_mode", display_mode)
|
||||
end
|
||||
-- remember current mode in module variable
|
||||
collection_display_mode = display_mode
|
||||
logger.dbg("CoverBrowser: setting Collection display mode to:", display_mode or "classic")
|
||||
|
||||
if not init_done and not display_mode then
|
||||
return -- starting in classic mode, nothing to patch
|
||||
end
|
||||
|
||||
-- We only need to replace one FileManagerCollection method
|
||||
if not display_mode then -- classic mode
|
||||
-- Put back original methods
|
||||
FileManagerCollection.updateItemTable = _FileManagerCollection_updateItemTable_orig
|
||||
FileManagerCollection.display_mode = nil
|
||||
else
|
||||
-- Replace original method with the one defined above
|
||||
FileManagerCollection.updateItemTable = _FileManagerCollections_updateItemTable
|
||||
-- And let it know which display_mode we should use
|
||||
FileManagerCollection.display_mode = display_mode
|
||||
end
|
||||
end
|
||||
|
||||
return CoverBrowser
|
||||
|
||||
Reference in New Issue
Block a user