Files
koreader/plugins/coverbrowser.koplugin/main.lua
poire-z 850be52177 Keep some menus open when Tap or Hold (#4189)
TouchMenu: added options to menu items with the following defaults:
    keep_menu_open = false
    hold_keep_menu_open = true
So, default for Tap callback is to close menu, and for Hold callback
to keep menu open.
In both cases, provide the TouchMenu instance as the 1st argument to
the callback functions (instead of a refresh_menu_func I added in #3941)
so the callback can do more things, like closing, refreshing,
changing menu items text and re-ordering...

ReaderZooming: show symbol for default (like it was done for
ReaderFont, ReaderHyphenation...)
TextEditor plugin: update the previously opened files list in real
time, so the menu can be kept open and used as the TextEditor main
interface.
SSH plugin: keep menu open and update the Start/Stop state in real time
ReadTimer plugin: tried to do what feels right (but I don't use it)

Also remove forgotten cp in the move/paste file code
2018-09-04 23:55:58 +02:00

602 lines
28 KiB
Lua

local InputContainer = require("ui/widget/container/inputcontainer")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local _ = require("gettext")
local BookInfoManager = require("bookinfomanager")
--[[
This plugin provides additional display modes to file browsers (File Manager
and History).
It does that by dynamically replacing some methods code to their classes
or instances.
--]]
-- We need to save the original methods early here as locals.
-- For some reason, saving them as attributes in init() does not allow
-- us to get back to classic mode
local FileChooser = require("ui/widget/filechooser")
local _FileChooser__recalculateDimen_orig = FileChooser._recalculateDimen
local _FileChooser_updateItems_orig = FileChooser.updateItems
local _FileChooser_onCloseWidget_orig = FileChooser.onCloseWidget
local _FileChooser_onSwipe_orig = FileChooser.onSwipe
local FileManagerHistory = require("apps/filemanager/filemanagerhistory")
local _FileManagerHistory_updateItemTable_orig = FileManagerHistory.updateItemTable
local FileManager = require("apps/filemanager/filemanager")
local _FileManager_tapPlus_orig = FileManager.tapPlus
-- Available display modes
local DISPLAY_MODES = {
-- nil or "" -- classic : filename only
mosaic_image = true, -- 3x3 grid covers with images
mosaic_text = true, -- 3x3 grid covers text only
list_image_meta = true, -- image with metadata (title/authors)
list_only_meta = true, -- metadata with no image
list_image_filename = true, -- image with filename (no metadata)
}
-- Store some states as locals, to be permanent across instantiations
local init_done = false
local filemanager_display_mode = false -- not initialized yet
local history_display_mode = false -- not initialized yet
local CoverBrowser = InputContainer:new{
name = "coverbrowser",
}
function CoverBrowser:init()
self.full_featured = true
-- (Could be set to false for some platforms to provide a fallback
-- option with only a menu for managing a few core settings)
self.ui.menu:registerToMainMenu(self)
if not self.full_featured then -- nothing else than menu registration
return
end
if init_done then -- things already patched according to current modes
return
end
-- Set up default display modes on first launch
if not G_reader_settings:isTrue("coverbrowser_initial_default_setup_done") then
-- Only if no display mode has been set yet
if not BookInfoManager:getSetting("filemanager_display_mode")
and not BookInfoManager:getSetting("history_display_mode") then
logger.info("CoverBrowser: setting default display modes")
BookInfoManager:saveSetting("filemanager_display_mode", "list_image_meta")
BookInfoManager:saveSetting("history_display_mode", "mosaic_image")
end
G_reader_settings:saveSetting("coverbrowser_initial_default_setup_done", true)
end
self:setupFileManagerDisplayMode(BookInfoManager:getSetting("filemanager_display_mode"))
self:setupHistoryDisplayMode(BookInfoManager:getSetting("history_display_mode"))
init_done = true
BookInfoManager:closeDbConnection() -- will be re-opened if needed
end
function CoverBrowser:addToMainMenu(menu_items)
-- We add it only to FileManager menu
if self.ui.view then -- Reader
return
end
-- Items available even if not full_featured
-- (settings used by core, that fit in this menu)
local generic_items = {
{
text_func = function()
local current_state = _("Show new files in bold")
if G_reader_settings:readSetting("show_file_in_bold") == "opened" then
current_state = _("Show opened files in bold")
elseif G_reader_settings:readSetting("show_file_in_bold") == false then
current_state = _("Show files in bold") -- with checkmark unchecked
end
if self.full_featured then
-- Inform that this settings applies only to classic file chooser
current_state = _("(Classic mode) ") .. current_state
end
return current_state
end,
checked_func = function() return G_reader_settings:readSetting("show_file_in_bold") ~= false end,
sub_item_table = {
{
text = _("Don't show files in bold"),
checked_func = function() return G_reader_settings:readSetting("show_file_in_bold") == false end,
callback = function()
G_reader_settings:saveSetting("show_file_in_bold", false)
self:refreshFileManagerInstance()
end,
},
{
text = _("Show opened files in bold"),
checked_func = function() return G_reader_settings:readSetting("show_file_in_bold") == "opened" end,
callback = function()
G_reader_settings:saveSetting("show_file_in_bold", "opened")
self:refreshFileManagerInstance()
end,
},
{
text = _("Show new (not yet opened) files in bold"),
checked_func = function()
return G_reader_settings:readSetting("show_file_in_bold") ~= false and G_reader_settings:readSetting("show_file_in_bold") ~= "opened"
end,
callback = function()
G_reader_settings:delSetting("show_file_in_bold")
self:refreshFileManagerInstance()
end,
},
},
separator = true,
},
{
text = _("Shorten home directory to ~"),
checked_func = function() return G_reader_settings:readSetting("home_dir_display_name") end,
callback = function()
if G_reader_settings:readSetting("home_dir_display_name") then
G_reader_settings:delSetting("home_dir_display_name")
if FileManager.instance then FileManager.instance:reinit() end
else
G_reader_settings:saveSetting("home_dir_display_name", "~")
if FileManager.instance then FileManager.instance:reinit() end
end
end,
},
{
text = _("Auto-remove deleted or purged items from history"),
checked_func = function() return G_reader_settings:readSetting("autoremove_deleted_items_from_history") end,
callback = function() G_reader_settings:flipNilOrFalse("autoremove_deleted_items_from_history") end,
},
{
text = _("Show filename in Open last/previous menu items"),
checked_func = function() return G_reader_settings:readSetting("open_last_menu_show_filename") end,
callback = function() G_reader_settings:flipNilOrFalse("open_last_menu_show_filename") end,
},
}
if not self.full_featured then
-- Make the generic items directly as 1st level items,
-- and use alternate name for main menu, not mentionning
-- "display mode" that are not available
menu_items.filemanager_display_mode = {
text = _("File browser settings"),
sub_item_table = generic_items
}
return
end
menu_items.filemanager_display_mode = {
text = _("Display mode"),
sub_item_table = {
-- selecting these does not close menu, which may be nice
-- so one can see how they look below the menu
{
text = _("Classic (filename only)"),
checked_func = function() return not filemanager_display_mode end,
callback = function()
self:setupFileManagerDisplayMode("")
end,
},
{
text = _("Mosaic with cover images"),
checked_func = function() return filemanager_display_mode == "mosaic_image" end,
callback = function()
self:setupFileManagerDisplayMode("mosaic_image")
end,
},
{
text = _("Mosaic with text covers"),
checked_func = function() return filemanager_display_mode == "mosaic_text" end,
callback = function()
self:setupFileManagerDisplayMode("mosaic_text")
end,
},
{
text = _("Detailed list with cover images and metadata"),
checked_func = function() return filemanager_display_mode == "list_image_meta" end,
callback = function()
self:setupFileManagerDisplayMode("list_image_meta")
end,
},
{
text = _("Detailed list with metadata, no images"),
checked_func = function() return filemanager_display_mode == "list_only_meta" end,
callback = function()
self:setupFileManagerDisplayMode("list_only_meta")
end,
},
{
text = _("Detailed list with cover images and filenames"),
checked_func = function() return filemanager_display_mode == "list_image_filename" end,
callback = function()
self:setupFileManagerDisplayMode("list_image_filename")
end,
separator = true,
},
-- Plug the same choices for History here as a submenu
-- (Any other suitable place for that ?)
{
text = _("History display mode"),
sub_item_table = {
{
text = _("Classic (filename only)"),
checked_func = function() return not history_display_mode end,
callback = function()
self:setupHistoryDisplayMode("")
end,
},
{
text = _("Mosaic with cover images"),
checked_func = function() return history_display_mode == "mosaic_image" end,
callback = function()
self:setupHistoryDisplayMode("mosaic_image")
end,
},
{
text = _("Mosaic with text covers"),
checked_func = function() return history_display_mode == "mosaic_text" end,
callback = function()
self:setupHistoryDisplayMode("mosaic_text")
end,
},
{
text = _("Detailed list with cover images and metadata"),
checked_func = function() return history_display_mode == "list_image_meta" end,
callback = function()
self:setupHistoryDisplayMode("list_image_meta")
end,
},
{
text = _("Detailed list with metadata, no images"),
checked_func = function() return history_display_mode == "list_only_meta" end,
callback = function()
self:setupHistoryDisplayMode("list_only_meta")
end,
},
{
text = _("Detailed list with cover images and filenames"),
checked_func = function() return history_display_mode == "list_image_filename" end,
callback = function()
self:setupHistoryDisplayMode("list_image_filename")
end,
separator = true,
},
},
separator = true,
},
-- Misc settings
{
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 = _("Append series metadata to authors"),
checked_func = function() return BookInfoManager:getSetting("append_series_to_authors") end,
callback = function()
if BookInfoManager:getSetting("append_series_to_authors") then
BookInfoManager:saveSetting("append_series_to_authors", false)
else
BookInfoManager:saveSetting("append_series_to_authors", true)
end
self:refreshFileManagerInstance()
end,
},
{
text = _("Append series metadata to title"),
checked_func = function() return BookInfoManager:getSetting("append_series_to_title") end,
callback = function()
if BookInfoManager:getSetting("append_series_to_title") then
BookInfoManager:saveSetting("append_series_to_title", false)
else
BookInfoManager:saveSetting("append_series_to_title", true)
end
self:refreshFileManagerInstance()
end,
},
-- generic_items will be inserted here
},
},
{
text = _("Book info cache management"),
sub_item_table = {
{
text_func = function() -- add current db size to menu text
local sstr = BookInfoManager:getDbSize()
return _("Current cache size: ") .. sstr
end,
-- no callback, only for information
},
{
text = _("Prune cache of removed books"),
keep_menu_open = true,
callback = function()
local ConfirmBox = require("ui/widget/confirmbox")
UIManager:close(self.file_dialog)
UIManager:show(ConfirmBox:new{
-- Checking file existences is quite fast, but deleting entries is slow.
text = _("Are you sure that you want to prune cache of removed books?\n(This may take a while.)"),
ok_text = _("Prune cache"),
ok_callback = function()
local InfoMessage = require("ui/widget/infomessage")
local msg = InfoMessage:new{ text = _("Pruning cache of removed books…") }
UIManager:show(msg)
UIManager:nextTick(function()
local summary = BookInfoManager:removeNonExistantEntries()
UIManager:close(msg)
UIManager:show( InfoMessage:new{ text = summary } )
end)
end
})
end,
},
{
text = _("Compact cache database"),
keep_menu_open = true,
callback = function()
local ConfirmBox = require("ui/widget/confirmbox")
UIManager:close(self.file_dialog)
UIManager:show(ConfirmBox:new{
text = _("Are you sure that you want to compact cache database?\n(This may take a while.)"),
ok_text = _("Compact database"),
ok_callback = function()
local InfoMessage = require("ui/widget/infomessage")
local msg = InfoMessage:new{ text = _("Compacting cache database…") }
UIManager:show(msg)
UIManager:nextTick(function()
local summary = BookInfoManager:compactDb()
UIManager:close(msg)
UIManager:show( InfoMessage:new{ text = summary } )
end)
end
})
end,
},
{
text = _("Delete cache database"),
keep_menu_open = true,
callback = function()
local ConfirmBox = require("ui/widget/confirmbox")
UIManager:close(self.file_dialog)
UIManager:show(ConfirmBox:new{
text = _("Are you sure that you want to delete cover and metadata cache?\n(This will also reset your display mode settings.)"),
ok_text = _("Purge"),
ok_callback = function()
BookInfoManager:deleteDb()
end
})
end,
},
},
},
},
}
-- Finally, insert the generic items at end of "Other settings" submenu
local sub_item_table = menu_items.filemanager_display_mode.sub_item_table
local generic_items_target = sub_item_table[#sub_item_table-1].sub_item_table -- second to last
for _, item in pairs(generic_items) do
table.insert(generic_items_target, item)
end
end
function CoverBrowser:refreshFileManagerInstance(cleanup, post_init)
local fm = FileManager.instance
if fm then
local fc = fm.file_chooser
if cleanup then -- clean instance properties we may have set
if fc.onFileHold_orig then
-- remove our onFileHold that extended file_dialog with new buttons
fc.onFileHold = fc.onFileHold_orig
fc.onFileHold_orig = nil
fc.onFileHold_ours = nil
end
end
if filemanager_display_mode then
if post_init then
-- FileBrowser was initialized in classic mode, but we changed
-- display mode: items per page may have changed, and we want
-- to re-position on the focused_file
fc:_recalculateDimen()
fc:changeToPath(fc.path, fc.prev_focused_path)
else
fc:updateItems()
end
else -- classic file_chooser needs this for a full redraw
fc:refreshPath()
end
end
end
function CoverBrowser:setupFileManagerDisplayMode(display_mode)
if not DISPLAY_MODES[display_mode] then
display_mode = nil -- unknow mode, fallback to classic
end
if init_done and display_mode == filemanager_display_mode then -- no change
return
end
if init_done then -- save new mode in db
BookInfoManager:saveSetting("filemanager_display_mode", display_mode)
end
-- remember current mode in module variable
filemanager_display_mode = display_mode
logger.dbg("CoverBrowser: setting FileManager 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
if not display_mode then -- classic mode
-- Put back original methods
FileChooser.updateItems = _FileChooser_updateItems_orig
FileChooser.onCloseWidget = _FileChooser_onCloseWidget_orig
FileChooser.onSwipe = _FileChooser_onSwipe_orig
FileChooser._recalculateDimen = _FileChooser__recalculateDimen_orig
FileManager.tapPlus = _FileManager_tapPlus_orig
-- Also clean-up what we added, even if it does not bother original code
FileChooser._updateItemsBuildUI = nil
FileChooser._do_cover_images = nil
FileChooser._do_filename_only = nil
FileChooser._do_hint_opened = nil
self:refreshFileManagerInstance(true)
return
end
-- In both mosaic and list modes, replace original methods with those from
-- our generic CoverMenu
local CoverMenu = require("covermenu")
FileChooser.updateItems = CoverMenu.updateItems
FileChooser.onCloseWidget = CoverMenu.onCloseWidget
FileChooser.onSwipe = CoverMenu.onSwipe
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")
FileChooser._recalculateDimen = MosaicMenu._recalculateDimen
FileChooser._updateItemsBuildUI = MosaicMenu._updateItemsBuildUI
-- Set MosaicMenu behaviour:
FileChooser._do_cover_images = display_mode ~= "mosaic_text"
FileChooser._do_hint_opened = true -- dogear at bottom
-- One could override default 3x3 grid here (put that as settings ?)
-- FileChooser.nb_cols_portrait = 4
-- FileChooser.nb_rows_portrait = 4
-- FileChooser.nb_cols_landscape = 6
-- FileChooser.nb_rows_landscape = 3
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")
FileChooser._recalculateDimen = ListMenu._recalculateDimen
FileChooser._updateItemsBuildUI = ListMenu._updateItemsBuildUI
-- Set ListMenu behaviour:
FileChooser._do_cover_images = display_mode ~= "list_only_meta"
FileChooser._do_filename_only = display_mode == "list_image_filename"
FileChooser._do_hint_opened = true -- dogear at bottom
end
-- Replace this FileManager method with the one from CoverMenu
-- (but first, make the original method saved here as local available
-- to CoverMenu)
CoverMenu._FileManager_tapPlus_orig = _FileManager_tapPlus_orig
FileManager.tapPlus = CoverMenu.tapPlus
if init_done then
self:refreshFileManagerInstance()
else
-- If KOReader has started directly to FileManager, the FileManager
-- instance is being init()'ed and there is no FileManager.instance yet,
-- but there'll be one at next tick.
UIManager:nextTick(function()
self:refreshFileManagerInstance(false, true)
end)
end
end
local function _FileManagerHistory_updateItemTable(self)
-- 'self' here is the single FileManagerHistory instance
-- FileManagerHistory has just created a new instance of Menu as 'hist_menu'
-- at each display of History. Soon after instantiation, this method
-- is called. The first time it is called, we replace some methods.
local display_mode = self.display_mode
local hist_menu = self.hist_menu
if not hist_menu._coverbrowser_overridden then
hist_menu._coverbrowser_overridden = true
-- In both mosaic and list modes, replace original methods with those from
-- our generic CoverMenu
local CoverMenu = require("covermenu")
hist_menu.updateItems = CoverMenu.updateItems
hist_menu.onCloseWidget = CoverMenu.onCloseWidget
hist_menu.onSwipe = CoverMenu.onSwipe
-- Also replace original onMenuHold (it will use original method, so remember it)
hist_menu.onMenuHold_orig = hist_menu.onMenuHold
hist_menu.onMenuHold = CoverMenu.onHistoryMenuHold
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")
hist_menu._recalculateDimen = MosaicMenu._recalculateDimen
hist_menu._updateItemsBuildUI = MosaicMenu._updateItemsBuildUI
-- Set MosaicMenu behaviour:
hist_menu._do_cover_images = display_mode ~= "mosaic_text"
-- no need for do_hint_opened with History
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")
hist_menu._recalculateDimen = ListMenu._recalculateDimen
hist_menu._updateItemsBuildUI = ListMenu._updateItemsBuildUI
-- Set ListMenu behaviour:
hist_menu._do_cover_images = display_mode ~= "list_only_meta"
hist_menu._do_filename_only = display_mode == "list_image_filename"
-- no need for do_hint_opened with History
end
hist_menu._do_hint_opened = BookInfoManager:getSetting("history_hint_opened")
end
-- And do now what the original does
_FileManagerHistory_updateItemTable_orig(self)
end
function CoverBrowser:setupHistoryDisplayMode(display_mode)
if not DISPLAY_MODES[display_mode] then
display_mode = nil -- unknow mode, fallback to classic
end
if init_done and display_mode == history_display_mode then -- no change
return
end
if init_done then -- save new mode in db
BookInfoManager:saveSetting("history_display_mode", display_mode)
end
-- remember current mode in module variable
history_display_mode = display_mode
logger.dbg("CoverBrowser: setting History 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 FileManagerHistory method
if not display_mode then -- classic mode
-- Put back original methods
FileManagerHistory.updateItemTable = _FileManagerHistory_updateItemTable_orig
FileManagerHistory.display_mode = nil
else
-- Replace original method with the one defined above
FileManagerHistory.updateItemTable = _FileManagerHistory_updateItemTable
-- And let it know which display_mode we should use
FileManagerHistory.display_mode = display_mode
end
end
return CoverBrowser