diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index ed911e290..6e01a6f01 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -1,5 +1,6 @@ local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") +local BookList = require("ui/widget/booklist") local ButtonDialog = require("ui/widget/buttondialog") local CheckButton = require("ui/widget/checkbutton") local ConfirmBox = require("ui/widget/confirmbox") @@ -141,9 +142,6 @@ function FileManager:setupLayout() path = self.root_path, focused_path = self.focused_file, show_parent = self.show_parent, - height = Screen:getHeight(), - is_popout = false, - is_borderless = true, file_filter = function(filename) return DocumentRegistry:hasProvider(filename) end, close_callback = function() return self:onClose() end, -- allow left bottom tap gesture, otherwise it is eaten by hidden return button @@ -253,19 +251,19 @@ function FileManager:setupLayout() {}, -- separator } + local book_props if is_file then - self.book_props = nil -- in 'self' to provide access to it in CoverBrowser local has_provider = DocumentRegistry:hasProvider(file) - local has_sidecar = DocSettings:hasSidecarFile(file) + local been_opened = BookList.hasBookBeenOpened(file) local doc_settings_or_file = file - if has_provider or has_sidecar then - self.book_props = file_manager.coverbrowser and file_manager.coverbrowser:getBookInfo(file) - if has_sidecar then - doc_settings_or_file = DocSettings:open(file) - if not self.book_props then + if has_provider or been_opened then + book_props = file_manager.coverbrowser and file_manager.coverbrowser:getBookInfo(file) + if been_opened then + doc_settings_or_file = BookList.getDocSettings(file) + if not book_props then local props = doc_settings_or_file:readSetting("doc_props") - self.book_props = FileManagerBookInfo.extendProps(props, file) - self.book_props.has_cover = true -- to enable "Book cover" button, we do not know if cover exists + book_props = FileManagerBookInfo.extendProps(props, file) + book_props.has_cover = true -- to enable "Book cover" button, we do not know if cover exists end end table.insert(buttons, filemanagerutil.genStatusButtonsRow(doc_settings_or_file, close_dialog_refresh_callback)) @@ -283,12 +281,12 @@ function FileManager:setupLayout() file_manager:showOpenWithDialog(file) end, }, - filemanagerutil.genBookInformationButton(doc_settings_or_file, self.book_props, close_dialog_callback), + filemanagerutil.genBookInformationButton(doc_settings_or_file, book_props, close_dialog_callback), }) if has_provider then table.insert(buttons, { - filemanagerutil.genBookCoverButton(file, self.book_props, close_dialog_callback), - filemanagerutil.genBookDescriptionButton(file, self.book_props, close_dialog_callback), + filemanagerutil.genBookCoverButton(file, book_props, close_dialog_callback), + filemanagerutil.genBookDescriptionButton(file, book_props, close_dialog_callback), }) end if Device:canExecuteScript(file) then @@ -325,7 +323,7 @@ function FileManager:setupLayout() if file_manager.file_dialog_added_buttons ~= nil then for _, row_func in ipairs(file_manager.file_dialog_added_buttons) do - local row = row_func(file, is_file, self.book_props) + local row = row_func(file, is_file, book_props) if row ~= nil then table.insert(buttons, row) end @@ -789,10 +787,6 @@ FileManager.rotate = FileManager.reinit FileManager.onPhysicalKeyboardConnected = FileManager.reinit FileManager.onPhysicalKeyboardDisconnected = FileManager.reinit -function FileManager:getCurrentDir() - return FileManager.instance and FileManager.instance.file_chooser.path -end - function FileManager:onClose() logger.dbg("close filemanager") PluginLoader:finalize() @@ -860,7 +854,7 @@ end function FileManager:openRandomFile(dir) local match_func = function(file) -- documents, not yet opened - return DocumentRegistry:hasProvider(file) and not DocSettings:hasSidecarFile(file) + return DocumentRegistry:hasProvider(file) and not BookList.hasBookBeenOpened(file) end local random_file = filemanagerutil.getRandomFile(dir, match_func) if random_file then @@ -1095,7 +1089,7 @@ function FileManager:showDeleteFileDialog(filepath, post_delete_callback, pre_de end local is_file = isFile(file) local text = (is_file and _("Delete file permanently?") or _("Delete folder permanently?")) .. "\n\n" .. BD.filepath(file) - if is_file and DocSettings:hasSidecarFile(file) then + if is_file and BookList.hasBookBeenOpened(file) then text = text .. "\n\n" .. _("Book settings, highlights and notes will be deleted.") end UIManager:show(ConfirmBox:new{ @@ -1116,6 +1110,7 @@ function FileManager:deleteFile(file, is_file) if is_file then local ok = os.remove(file) if ok then + BookList.resetBookInfoCache(file) DocSettings.updateLocation(file) -- delete sdr ReadHistory:fileDeleted(file) ReadCollection:removeItem(file) @@ -1141,6 +1136,7 @@ function FileManager:deleteSelectedFiles() local file_abs_path = ffiUtil.realpath(orig_file) local ok = file_abs_path and os.remove(file_abs_path) if ok then + BookList.resetBookInfoCache(file_abs_path) DocSettings.updateLocation(file_abs_path) -- delete sdr ok_files[orig_file] = true self.selected_files[orig_file] = nil diff --git a/frontend/apps/filemanager/filemanagerbookinfo.lua b/frontend/apps/filemanager/filemanagerbookinfo.lua index 3de27f6f2..8f9f3d7a5 100644 --- a/frontend/apps/filemanager/filemanagerbookinfo.lua +++ b/frontend/apps/filemanager/filemanagerbookinfo.lua @@ -3,6 +3,7 @@ This module provides a way to display book information (filename and book metada ]] local BD = require("ui/bidi") +local BookList = require("ui/widget/booklist") local ButtonDialog = require("ui/widget/buttondialog") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") @@ -79,8 +80,8 @@ function BookInfo:show(doc_settings_or_file, book_props) doc_settings_or_file = self.ui.doc_settings has_sidecar = true end - if not has_sidecar and DocSettings:hasSidecarFile(file) then - doc_settings_or_file = DocSettings:open(file) + if not has_sidecar and BookList.hasBookBeenOpened(file) then + doc_settings_or_file = BookList.getDocSettings(file) has_sidecar = true end local folder, filename = util.splitFilePathName(file) @@ -247,8 +248,8 @@ function BookInfo:getDocProps(file, book_props, no_open_document) end end - if DocSettings:hasSidecarFile(file) then - local doc_settings = DocSettings:open(file) + if BookList.hasBookBeenOpened(file) then + local doc_settings = BookList.getDocSettings(file) if not book_props then -- Files opened after 20170701 have a "doc_props" setting with -- complete metadata and "doc_pages" with accurate nb of pages @@ -721,7 +722,7 @@ function BookInfo:showNotebookFileDialog(notebook_file, doc_settings_or_file, bo local file = has_sidecar and doc_settings_or_file:readSetting("doc_path") or doc_settings_or_file local function saveNotebookFile(new_notebook_file) if not has_sidecar then - doc_settings_or_file = DocSettings:open(doc_settings_or_file) + doc_settings_or_file = BookList.getDocSettings(doc_settings_or_file) end doc_settings_or_file:saveSetting("notebook_file", new_notebook_file) if not self.is_current_doc then diff --git a/frontend/apps/filemanager/filemanagercollection.lua b/frontend/apps/filemanager/filemanagercollection.lua index 363be595a..d065199c5 100644 --- a/frontend/apps/filemanager/filemanagercollection.lua +++ b/frontend/apps/filemanager/filemanagercollection.lua @@ -1,9 +1,9 @@ local BD = require("ui/bidi") +local BookList = require("ui/widget/booklist") local ButtonDialog = require("ui/widget/buttondialog") local CheckButton = require("ui/widget/checkbutton") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") -local DocSettings = require("docsettings") local InfoMessage = require("ui/widget/infomessage") local InputDialog = require("ui/widget/inputdialog") local Menu = require("ui/widget/menu") @@ -13,10 +13,10 @@ local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local ffiUtil = require("ffi/util") local filemanagerutil = require("apps/filemanager/filemanagerutil") +local util = require("util") local _ = require("gettext") local N_ = _.ngettext local T = ffiUtil.template -local util = require("util") local FileManagerCollection = WidgetContainer:extend{ title = _("Collections"), @@ -63,14 +63,7 @@ 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, + self.coll_menu = BookList:new{ title_bar_left_icon = "appbar.menu", onLeftButtonTap = function() self:showCollDialog() end, onReturn = function() @@ -80,6 +73,7 @@ function FileManagerCollection:onShowColl(collection_name) end, onMenuChoice = self.onMenuChoice, onMenuHold = self.onMenuHold, + ui = self.ui, _manager = self, _recreate_func = function() self:onShowColl(collection_name) end, collection_name = collection_name, @@ -156,7 +150,7 @@ end function FileManagerCollection:onMenuHold(item) local file = item.file self.file_dialog = nil - self.book_props = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file) + local book_props = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file) local function close_dialog_callback() UIManager:close(self.file_dialog) @@ -176,17 +170,17 @@ function FileManagerCollection:onMenuHold(item) local doc_settings_or_file if is_currently_opened then doc_settings_or_file = self.ui.doc_settings - if not self.book_props then - self.book_props = self.ui.doc_props - self.book_props.has_cover = true + if not book_props then + book_props = self.ui.doc_props + book_props.has_cover = true end else - if DocSettings:hasSidecarFile(file) then - doc_settings_or_file = DocSettings:open(file) - if not self.book_props then + if BookList.hasBookBeenOpened(file) then + doc_settings_or_file = BookList.getDocSettings(file) + if not book_props then local props = doc_settings_or_file:readSetting("doc_props") - self.book_props = self.ui.bookinfo.extendProps(props, file) - self.book_props.has_cover = true + book_props = self.ui.bookinfo.extendProps(props, file) + book_props.has_cover = true end else doc_settings_or_file = file @@ -217,11 +211,11 @@ function FileManagerCollection:onMenuHold(item) }) table.insert(buttons, { filemanagerutil.genShowFolderButton(file, close_dialog_menu_callback), - filemanagerutil.genBookInformationButton(doc_settings_or_file, self.book_props, close_dialog_callback), + filemanagerutil.genBookInformationButton(doc_settings_or_file, book_props, close_dialog_callback), }) table.insert(buttons, { - filemanagerutil.genBookCoverButton(file, self.book_props, close_dialog_callback), - filemanagerutil.genBookDescriptionButton(file, self.book_props, close_dialog_callback), + filemanagerutil.genBookCoverButton(file, book_props, close_dialog_callback), + filemanagerutil.genBookDescriptionButton(file, book_props, close_dialog_callback), }) if Device:canExecuteScript(file) then @@ -232,7 +226,7 @@ function FileManagerCollection:onMenuHold(item) if self._manager.file_dialog_added_buttons ~= nil then for _, row_func in ipairs(self._manager.file_dialog_added_buttons) do - local row = row_func(file, true, self.book_props) + local row = row_func(file, true, book_props) if row ~= nil then table.insert(buttons, row) end diff --git a/frontend/apps/filemanager/filemanagerfilesearcher.lua b/frontend/apps/filemanager/filemanagerfilesearcher.lua index d63844b59..6eae03742 100644 --- a/frontend/apps/filemanager/filemanagerfilesearcher.lua +++ b/frontend/apps/filemanager/filemanagerfilesearcher.lua @@ -1,14 +1,13 @@ +local BookList = require("ui/widget/booklist") local ButtonDialog = require("ui/widget/buttondialog") local CheckButton = require("ui/widget/checkbutton") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") -local DocSettings = require("docsettings") local DocumentRegistry = require("document/documentregistry") local FileChooser = require("ui/widget/filechooser") local InfoMessage = require("ui/widget/infomessage") local InputContainer = require("ui/widget/container/inputcontainer") local InputDialog = require("ui/widget/inputdialog") -local Menu = require("ui/widget/menu") local UIManager = require("ui/uimanager") local Utf8Proc = require("ffi/utf8proc") local filemanagerutil = require("apps/filemanager/filemanagerutil") @@ -259,12 +258,8 @@ function FileSearcher:onShowSearchResults(not_cached) return end - self.search_menu = Menu:new{ + self.search_menu = BookList:new{ subtitle = T(_("Query: %1"), FileSearcher.search_string), - covers_fullscreen = true, -- hint for UIManager:_repaint() - is_borderless = true, - is_popout = false, - title_bar_fm_style = true, title_bar_left_icon = "appbar.menu", onLeftButtonTap = function() self:setSelectMode() end, onMenuSelect = self.onMenuSelect, @@ -323,10 +318,10 @@ function FileSearcher:showFileDialog(item) if item.is_file then local is_currently_opened = self.ui.document and self.ui.document.file == file local has_provider = DocumentRegistry:hasProvider(file) - local has_sidecar = DocSettings:hasSidecarFile(file) + local been_opened = BookList.hasBookBeenOpened(file) local doc_settings_or_file = is_currently_opened and self.ui.doc_settings - or (has_sidecar and DocSettings:open(file) or file) - if has_provider or has_sidecar then + or (been_opened and BookList.getDocSettings(file) or file) + if has_provider or been_opened then bookinfo = self.ui.bookinfo:getDocProps(file, nil, true) table.insert(buttons, filemanagerutil.genStatusButtonsRow(doc_settings_or_file, close_dialog_callback)) table.insert(buttons, {}) -- separator diff --git a/frontend/apps/filemanager/filemanagerhistory.lua b/frontend/apps/filemanager/filemanagerhistory.lua index fb0cc54bb..e49773504 100644 --- a/frontend/apps/filemanager/filemanagerhistory.lua +++ b/frontend/apps/filemanager/filemanagerhistory.lua @@ -1,10 +1,9 @@ local BD = require("ui/bidi") +local BookList = require("ui/widget/booklist") local ButtonDialog = require("ui/widget/buttondialog") local CheckButton = require("ui/widget/checkbutton") local ConfirmBox = require("ui/widget/confirmbox") -local DocSettings = require("docsettings") 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") @@ -129,7 +128,7 @@ end function FileManagerHistory:onMenuHold(item) local file = item.file self.file_dialog = nil - self.book_props = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file) + local book_props = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file) local function close_dialog_callback() UIManager:close(self.file_dialog) @@ -146,7 +145,7 @@ function FileManagerHistory:onMenuHold(item) self._manager.statuses_fetched = false end self._manager:updateItemTable() - self._manager.files_updated = true -- sidecar folder may be created/deleted + self._manager.files_updated = true end local function update_callback() self._manager:updateItemTable() @@ -157,17 +156,17 @@ function FileManagerHistory:onMenuHold(item) local doc_settings_or_file if is_currently_opened then doc_settings_or_file = self.ui.doc_settings - if not self.book_props then - self.book_props = self.ui.doc_props - self.book_props.has_cover = true + if not book_props then + book_props = self.ui.doc_props + book_props.has_cover = true end else - if DocSettings:hasSidecarFile(file) then - doc_settings_or_file = DocSettings:open(file) - if not self.book_props then + if BookList.hasBookBeenOpened(file) then + doc_settings_or_file = BookList.getDocSettings(file) + if not book_props then local props = doc_settings_or_file:readSetting("doc_props") - self.book_props = self.ui.bookinfo.extendProps(props, file) - self.book_props.has_cover = true + book_props = self.ui.bookinfo.extendProps(props, file) + book_props.has_cover = true end else doc_settings_or_file = file @@ -206,16 +205,16 @@ function FileManagerHistory:onMenuHold(item) }) table.insert(buttons, { filemanagerutil.genShowFolderButton(file, close_dialog_menu_callback, item.dim), - filemanagerutil.genBookInformationButton(doc_settings_or_file, self.book_props, close_dialog_callback, item.dim), + filemanagerutil.genBookInformationButton(doc_settings_or_file, book_props, close_dialog_callback, item.dim), }) table.insert(buttons, { - filemanagerutil.genBookCoverButton(file, self.book_props, close_dialog_callback, item.dim), - filemanagerutil.genBookDescriptionButton(file, self.book_props, close_dialog_callback, item.dim), + filemanagerutil.genBookCoverButton(file, book_props, close_dialog_callback, item.dim), + filemanagerutil.genBookDescriptionButton(file, book_props, close_dialog_callback, item.dim), }) if self._manager.file_dialog_added_buttons ~= nil then for _, row_func in ipairs(self._manager.file_dialog_added_buttons) do - local row = row_func(file, true, self.book_props) + local row = row_func(file, true, book_props) if row ~= nil then table.insert(buttons, row) end @@ -237,19 +236,13 @@ function FileManagerHistory.getMenuInstance() end function FileManagerHistory:onShowHist(search_info) - self.hist_menu = Menu:new{ - ui = self.ui, - covers_fullscreen = true, -- hint for UIManager:_repaint() - is_borderless = true, - is_popout = false, + self.hist_menu = BookList:new{ title = self.hist_menu_title, - -- 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:showHistDialog() end, onMenuChoice = self.onMenuChoice, onMenuHold = self.onMenuHold, + ui = self.ui, _manager = self, _recreate_func = function() self:onShowHist(search_info) end, } diff --git a/frontend/apps/filemanager/filemanagerutil.lua b/frontend/apps/filemanager/filemanagerutil.lua index 5d0eae224..8533a3501 100644 --- a/frontend/apps/filemanager/filemanagerutil.lua +++ b/frontend/apps/filemanager/filemanagerutil.lua @@ -3,6 +3,7 @@ This module contains miscellaneous helper functions for FileManager ]] local BD = require("ui/bidi") +local BookList = require("ui/widget/booklist") local Device = require("device") local DocSettings = require("docsettings") local Event = require("ui/event") @@ -99,19 +100,14 @@ function filemanagerutil.resetDocumentSettings(file) end doc_settings:makeTrue("docsettings_reset_done") -- for readertypeset block_rendering_mode doc_settings:flush() + BookList.setBookInfoCache(file_abs_path, doc_settings) end end -- Get a document status ("new", "reading", "complete", or "abandoned") function filemanagerutil.getStatus(file) - if DocSettings:hasSidecarFile(file) then - local summary = DocSettings:open(file):readSetting("summary") - if summary and summary.status and summary.status ~= "" then - return summary.status - end - return "reading" - end - return "new" + local book_info = BookList.getBookInfo(file) + return book_info.been_opened and book_info.status or "new" end function filemanagerutil.saveSummary(doc_settings_or_file, summary) @@ -155,7 +151,7 @@ function filemanagerutil.genStatusButtonsRow(doc_settings_or_file, caller_callba callback = function() summary.status = to_status filemanagerutil.saveSummary(doc_settings_or_file, summary) - UIManager:broadcastEvent(Event:new("DocSettingsItemsChanged", file, { summary = summary })) -- for CoverBrowser + BookList.setBookInfoCacheProperty(file, "status", to_status) caller_callback() end, } @@ -176,7 +172,7 @@ function filemanagerutil.genResetSettingsButton(doc_settings_or_file, caller_cal has_sidecar_file = true else file = ffiUtil.realpath(doc_settings_or_file) or doc_settings_or_file - has_sidecar_file = DocSettings:hasSidecarFile(file) + has_sidecar_file = BookList.hasBookBeenOpened(file) end local custom_cover_file = DocSettings:findCustomCoverFile(file) local has_custom_cover_file = custom_cover_file and true or false @@ -205,7 +201,7 @@ function filemanagerutil.genResetSettingsButton(doc_settings_or_file, caller_cal UIManager:broadcastEvent(Event:new("InvalidateMetadataCache", file)) end if data_to_purge.doc_settings then - UIManager:broadcastEvent(Event:new("DocSettingsItemsChanged", file)) -- for CoverBrowser + BookList.setBookInfoCacheProperty(file, "been_opened", false) require("readhistory"):fileSettingsPurged(file) end caller_callback() diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index ecaaed3bf..bb96ef46f 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -5,6 +5,7 @@ It works using data gathered from a document interface. ]]-- local BD = require("ui/bidi") +local BookList = require("ui/widget/booklist") local Device = require("device") local DeviceListener = require("device/devicelistener") local DocCache = require("document/doccache") @@ -814,6 +815,7 @@ function ReaderUI:onClose(full_refresh) self:saveSettings() end if self.document ~= nil then + BookList.setBookInfoCache(self.document.file, self.doc_settings) require("readhistory"):updateLastBookTime(self.tearing_down) -- Serialize the most recently displayed page for later launch DocCache:serialize(self.document.file) diff --git a/frontend/ui/widget/booklist.lua b/frontend/ui/widget/booklist.lua new file mode 100644 index 000000000..34b5ce51e --- /dev/null +++ b/frontend/ui/widget/booklist.lua @@ -0,0 +1,94 @@ +local DocSettings = require("docsettings") +local Menu = require("ui/widget/menu") + +local BookList = Menu:extend{ + covers_fullscreen = true, -- hint for UIManager:_repaint() + is_borderless = true, + is_popout = false, + book_info_cache = {}, -- cache in the base class +} + +function BookList:init() + self.title_bar_fm_style = not self.custom_title_bar + Menu.init(self) +end + +-- BookInfo + +function BookList.setBookInfoCache(file, doc_settings) + local book_info = { + been_opened = true, + status = nil, + pages = nil, + has_annotations = nil, + percent_finished = doc_settings:readSetting("percent_finished"), + } + local summary = doc_settings:readSetting("summary") + book_info.status = summary and summary.status or "reading" + local pages = doc_settings:readSetting("doc_pages") + if pages == nil then + local stats = doc_settings:readSetting("stats") + if stats and stats.pages and stats.pages ~= 0 then -- crengine with statistics disabled stores 0 + pages = stats.pages + end + end + book_info.pages = pages + local annotations = doc_settings:readSetting("annotations") + if annotations then + book_info.has_annotations = #annotations > 0 + else + local highlight = doc_settings:readSetting("highlight") + book_info.has_annotations = highlight and next(highlight) and true + end + BookList.book_info_cache[file] = book_info +end + +function BookList.setBookInfoCacheProperty(file, prop_name, prop_value) + if prop_name == "been_opened" and prop_value == false then + BookList.book_info_cache[file] = { been_opened = false } + else + BookList.book_info_cache[file] = BookList.book_info_cache[file] or {} + BookList.book_info_cache[file][prop_name] = prop_value + BookList.book_info_cache[file].been_opened = true + end +end + +function BookList.resetBookInfoCache(file) + BookList.book_info_cache[file] = nil +end + +function BookList.hasBookInfoCache(file) + local book_info = BookList.book_info_cache[file] + return book_info ~= nil and (book_info.been_opened == false or book_info.status ~= nil) +end + +function BookList.getBookInfo(file) + if not BookList.hasBookInfoCache(file) then + if DocSettings:hasSidecarFile(file) then + BookList.setBookInfoCache(file, DocSettings:open(file)) + else + BookList.book_info_cache[file] = { been_opened = false } + end + end + return BookList.book_info_cache[file] +end + +function BookList.hasBookBeenOpened(file) + local book_info = BookList.book_info_cache[file] + local been_opened = book_info and book_info.been_opened + if been_opened == nil then -- not cached yet + been_opened = DocSettings:hasSidecarFile(file) + BookList.book_info_cache[file] = { been_opened = been_opened } + end + return been_opened +end + +function BookList.getDocSettings(file) + local doc_settings = DocSettings:open(file) + if not BookList.hasBookInfoCache(file) then + BookList.setBookInfoCache(file, doc_settings) + end + return doc_settings +end + +return BookList diff --git a/frontend/ui/widget/bookstatuswidget.lua b/frontend/ui/widget/bookstatuswidget.lua index ae3c9c105..9900366fc 100644 --- a/frontend/ui/widget/bookstatuswidget.lua +++ b/frontend/ui/widget/bookstatuswidget.lua @@ -1,4 +1,5 @@ local Blitbuffer = require("ffi/blitbuffer") +local BookList = require("ui/widget/booklist") local Button = require("ui/widget/button") local CenterContainer = require("ui/widget/container/centercontainer") local Device = require("device") @@ -197,6 +198,7 @@ function BookStatusWidget:genHeader(title) end function BookStatusWidget:onChangeBookStatus(option_name, option_value) + BookList.setBookInfoCacheProperty(self.ui.document.file, "status", option_name[option_value]) self.summary.status = option_name[option_value] self.summary.modified = os.date("%Y-%m-%d", os.time()) self.updated = true diff --git a/frontend/ui/widget/filechooser.lua b/frontend/ui/widget/filechooser.lua index 9f4be134d..904634b22 100644 --- a/frontend/ui/widget/filechooser.lua +++ b/frontend/ui/widget/filechooser.lua @@ -1,16 +1,15 @@ local BD = require("ui/bidi") -local datetime = require("datetime") +local BookList = require("ui/widget/booklist") local Device = require("device") -local DocSettings = require("docsettings") local DocumentRegistry = require("document/documentregistry") local Event = require("ui/event") local FileManagerShortcuts = require("apps/filemanager/filemanagershortcuts") -local filemanagerutil = require("apps/filemanager/filemanagerutil") -local Menu = require("ui/widget/menu") local ReadCollection = require("readcollection") local UIManager = require("ui/uimanager") +local datetime = require("datetime") local ffi = require("ffi") local ffiUtil = require("ffi/util") +local filemanagerutil = require("apps/filemanager/filemanagerutil") local lfs = require("libs/libkoreader-lfs") local sort = require("sort") local util = require("util") @@ -19,7 +18,7 @@ local Screen = Device.screen local T = ffiUtil.template -- NOTE: It's our caller's responsibility to setup a title bar and pass it to us via custom_title_bar (c.f., FileManager) -local FileChooser = Menu:extend{ +local FileChooser = BookList:extend{ path = lfs.currentdir(), show_path = true, parent = nil, @@ -168,18 +167,13 @@ local FileChooser = Menu:extend{ end, cache end, item_func = function(item) - local percent_finished - item.opened = DocSettings:hasSidecarFile(item.path) - if item.opened then - local doc_settings = DocSettings:open(item.path) - percent_finished = doc_settings:readSetting("percent_finished") - end - + local book_info = BookList.getBookInfo(item.path) + item.opened = book_info.been_opened -- smooth 2 decimal points (0.00) instead of 16 decimal points - item.percent_finished = util.round_decimal(percent_finished or 0, 2) + item.percent_finished = util.round_decimal(book_info.percent_finished or 0, 2) end, mandatory_func = function(item) - return item.opened and string.format("%d %%", 100 * item.percent_finished) or "–" + return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–" end, }, percent_unopened_last = { @@ -198,18 +192,13 @@ local FileChooser = Menu:extend{ end, cache end, item_func = function(item) - local percent_finished - item.opened = DocSettings:hasSidecarFile(item.path) - if item.opened then - local doc_settings = DocSettings:open(item.path) - percent_finished = doc_settings:readSetting("percent_finished") - end - + local book_info = BookList.getBookInfo(item.path) + item.opened = book_info.been_opened -- smooth 2 decimal points (0.00) instead of 16 decimal points - item.percent_finished = util.round_decimal(percent_finished or 0, 2) + item.percent_finished = util.round_decimal(book_info.percent_finished or 0, 2) end, mandatory_func = function(item) - return item.opened and string.format("%d %%", 100 * item.percent_finished) or "–" + return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–" end, }, percent_natural = { @@ -235,28 +224,24 @@ local FileChooser = Menu:extend{ return sortfunc, cache end, item_func = function(item) - local percent_finished + local book_info = BookList.getBookInfo(item.path) + item.opened = book_info.been_opened + local percent_finished = book_info.percent_finished local sort_percent - item.opened = DocSettings:hasSidecarFile(item.path) if item.opened then - local doc_settings = DocSettings:open(item.path) - local summary = doc_settings:readSetting("summary") - -- books marked as "finished" or "on hold" should be considered the same as 100% and less than 0% respectively - if summary and summary.status == "complete" then + if book_info.status == "complete" then sort_percent = 1.0 - elseif summary and summary.status == "abandoned" then + elseif book_info.status == "abandoned" then sort_percent = -0.01 end - - percent_finished = doc_settings:readSetting("percent_finished") end -- smooth 2 decimal points (0.00) instead of 16 decimal points item.sort_percent = sort_percent or util.round_decimal(percent_finished or -1, 2) item.percent_finished = percent_finished or 0 end, mandatory_func = function(item) - return item.opened and string.format("%d %%", 100 * item.percent_finished) or "–" + return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–" end, }, }, @@ -288,7 +273,7 @@ function FileChooser:init() if lfs.attributes(self.path, "mode") ~= "directory" then self.path = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir() end - Menu.init(self) -- call parent's init() + BookList.init(self) self:refreshPath() end @@ -350,8 +335,13 @@ function FileChooser:getListItem(dirpath, f, fullpath, attributes, collate) local show_file_in_bold = G_reader_settings:readSetting("show_file_in_bold") item.bidi_wrap_func = BD.filename item.is_file = true + if collate.item_func ~= nil then + collate.item_func(item) + end if show_file_in_bold ~= false then - item.opened = DocSettings:hasSidecarFile(fullpath) + if item.opened == nil then -- could be set in item_func + item.opened = BookList.hasBookBeenOpened(item.path) + end item.bold = item.opened if show_file_in_bold ~= "opened" then item.bold = not item.bold @@ -359,9 +349,6 @@ function FileChooser:getListItem(dirpath, f, fullpath, attributes, collate) end item.dim = self.filemanager and self.filemanager.selected_files and self.filemanager.selected_files[item.path] - if collate.item_func ~= nil then - collate.item_func(item) - end item.mandatory = self:getMenuItemMandatory(item, collate) else -- folder if item.text == "./." then -- added as content of an unreadable directory @@ -370,7 +357,7 @@ function FileChooser:getListItem(dirpath, f, fullpath, attributes, collate) item.text = item.text.."/" item.bidi_wrap_func = BD.directory if collate.can_collate_mixed and collate.item_func ~= nil then - collate.item_func(item) + collate.item_func(item, self) end if dirpath then -- file browser or PathChooser item.mandatory = self:getMenuItemMandatory(item) @@ -450,7 +437,8 @@ function FileChooser:genItemTable(dirs, files, path) end if self.show_current_dir_for_hold then table.insert(item_table, 1, { - text = _("Long-press to choose current folder"), + text = _("Long-press here to choose current folder"), + bold = true, path = path.."/.", }) end @@ -494,7 +482,7 @@ function FileChooser:getMenuItemMandatory(item, collate) end function FileChooser:updateItems(select_number, no_recalculate_dimen) - Menu.updateItems(self, select_number, no_recalculate_dimen) -- call parent's updateItems() + BookList.updateItems(self, select_number, no_recalculate_dimen) -- call parent's updateItems() self.path_items[self.path] = (self.page - 1) * self.perpage + (select_number or 1) end diff --git a/frontend/ui/widget/pathchooser.lua b/frontend/ui/widget/pathchooser.lua index 9bb2d6d36..99e92b99e 100644 --- a/frontend/ui/widget/pathchooser.lua +++ b/frontend/ui/widget/pathchooser.lua @@ -14,10 +14,6 @@ local PathChooser = FileChooser:extend{ title = true, -- or a string -- if let to true, a generic title will be set in init() no_title = false, - is_popout = false, - covers_fullscreen = true, -- set it to false if you set is_popout = true - title_bar_fm_style = true, - is_borderless = true, select_directory = true, -- allow selecting directories select_file = true, -- allow selecting files show_files = true, -- show files, even if select_file=false @@ -28,11 +24,11 @@ local PathChooser = FileChooser:extend{ function PathChooser:init() if self.title == true then -- default title depending on options if self.select_directory and not self.select_file then - self.title = _("Long-press to choose a folder") + self.title = _("Long-press folder's name to choose it") elseif not self.select_directory and self.select_file then - self.title = _("Long-press to choose a file") + self.title = _("Long-press file's name to choose it") else - self.title = _("Long-press to choose") + self.title = _("Long-press item's name to choose it") end end if not self.show_files then diff --git a/plugins/calibre.koplugin/search.lua b/plugins/calibre.koplugin/search.lua index 432ffaa11..cb9d3719c 100644 --- a/plugins/calibre.koplugin/search.lua +++ b/plugins/calibre.koplugin/search.lua @@ -2,6 +2,7 @@ This module implements calibre metadata searching. --]] +local BookList = require("ui/widget/booklist") local CalibreMetadata = require("metadata") local ConfirmBox = require("ui/widget/confirmbox") local DataStorage = require("datastorage") @@ -10,7 +11,6 @@ local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo") local filemanagerutil = require("apps/filemanager/filemanagerutil") local InputDialog = require("ui/widget/inputdialog") local InfoMessage = require("ui/widget/infomessage") -local Menu = require("ui/widget/menu") local Persist = require("persist") local Screen = require("device").screen local UIManager = require("ui/uimanager") @@ -453,11 +453,8 @@ function CalibreSearch:browse(option) end end - self.search_menu = self.search_menu or Menu:new{ - width = Screen:getWidth(), - height = Screen:getHeight(), + self.search_menu = self.search_menu or BookList:new{ parent = nil, - is_borderless = true, onMenuHold = self.onMenuHold, } self.search_menu.paths = {} diff --git a/plugins/calibre.koplugin/wireless.lua b/plugins/calibre.koplugin/wireless.lua index 103dbbdde..9fcc312e4 100644 --- a/plugins/calibre.koplugin/wireless.lua +++ b/plugins/calibre.koplugin/wireless.lua @@ -46,12 +46,9 @@ end -- update the view of the dir if we are currently browsing it. local function updateDir(dir) local FileManager = require("apps/filemanager/filemanager") - if FileManager:getCurrentDir() == dir then - -- getCurrentDir() will return nil (well, nothing, technically) if there isn't an FM instance, so, - -- unless we were passed a nil, this is technically redundant. - if FileManager.instance then - FileManager.instance:reinit(dir) - end + local fc = FileManager.instance and FileManager.instance.file_chooser + if fc and fc.path == dir then + fc:refreshPath() end end diff --git a/plugins/coverbrowser.koplugin/covermenu.lua b/plugins/coverbrowser.koplugin/covermenu.lua index 2ae506eee..dc7a7871c 100644 --- a/plugins/coverbrowser.koplugin/covermenu.lua +++ b/plugins/coverbrowser.koplugin/covermenu.lua @@ -1,4 +1,3 @@ -local DocSettings = require("docsettings") local InfoMessage = require("ui/widget/infomessage") local Menu = require("ui/widget/menu") local UIManager = require("ui/uimanager") @@ -31,43 +30,6 @@ local nb_drawings_since_last_collectgarbage = 0 -- in the real Menu class or instance local CoverMenu = {} -function CoverMenu:updateCache(file, status, do_create, pages) - if do_create then -- create new cache entry if absent - if self.cover_info_cache[file] then return end - local doc_settings = DocSettings:open(file) - -- We can get nb of page in the new 'doc_pages' setting, or from the old 'stats.page' - local doc_pages = doc_settings:readSetting("doc_pages") - if doc_pages then - pages = doc_pages - else - local stats = doc_settings:readSetting("stats") - if stats and stats.pages and stats.pages ~= 0 then -- crengine with statistics disabled stores 0 - pages = stats.pages - end - end - local percent_finished = doc_settings:readSetting("percent_finished") - local summary = doc_settings:readSetting("summary") - status = summary and summary.status - local has_highlight - local annotations = doc_settings:readSetting("annotations") - if annotations then - has_highlight = #annotations > 0 - else - local highlight = doc_settings:readSetting("highlight") - has_highlight = highlight and next(highlight) and true - end - self.cover_info_cache[file] = table.pack(pages, percent_finished, status, has_highlight) -- may be a sparse array - else - if self.cover_info_cache and self.cover_info_cache[file] then - if status then - self.cover_info_cache[file][3] = status - else - self.cover_info_cache[file] = nil - end - end - end -end - function CoverMenu:updateItems(select_number, no_recalculate_dimen) -- As done in Menu:updateItems() local old_dimen = self.dimen and self.dimen:copy() @@ -231,9 +193,6 @@ function CoverMenu:onCloseWidget() -- Propagate a call to free() to all our sub-widgets, to release memory used by their _bb self.item_group:free() - -- Clean any short term cache (used by ListMenu to cache some Doc Settings info) - self.cover_info_cache = nil - -- Force garbage collecting when leaving too -- (delay it a bit so this pause is less noticeable) UIManager:scheduleIn(0.2, function() diff --git a/plugins/coverbrowser.koplugin/listmenu.lua b/plugins/coverbrowser.koplugin/listmenu.lua index 7e5633048..c821d7081 100644 --- a/plugins/coverbrowser.koplugin/listmenu.lua +++ b/plugins/coverbrowser.koplugin/listmenu.lua @@ -2,7 +2,6 @@ local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local CenterContainer = require("ui/widget/container/centercontainer") local Device = require("device") -local DocSettings = require("docsettings") local Font = require("ui/font") local FrameContainer = require("ui/widget/container/framecontainer") local Geom = require("ui/geometry") @@ -282,6 +281,8 @@ function ListMenuItem:update() end end + local book_info = self.menu.getBookInfo(self.filepath) + self.been_opened = book_info.been_opened if bookinfo then -- This book is known self.bookinfo_found = true local cover_bb_used = false @@ -355,21 +356,9 @@ function ListMenuItem:update() -- file type -- pages read / nb of pages (not available for crengine doc not opened) -- Current page / pages are available or more accurate in .sdr/metadata.lua - -- We use a cache (cleaned at end of this browsing session) to store - -- page, percent read and book status from sidecar files, to avoid - -- re-parsing them when re-rendering a visited page - if not self.menu.cover_info_cache then - self.menu.cover_info_cache = {} - end - local pages_str = "" - local pages = bookinfo.pages -- default to those in bookinfo db - local percent_finished, status, has_highlight - if DocSettings:hasSidecarFile(self.filepath) then - self.been_opened = true - self.menu:updateCache(self.filepath, nil, true, pages) -- create new cache entry if absent - pages, percent_finished, status, has_highlight = - unpack(self.menu.cover_info_cache[self.filepath], 1, self.menu.cover_info_cache[self.filepath].n) - end + local pages = book_info.pages or bookinfo.pages -- default to those in bookinfo db + local percent_finished = book_info.percent_finished + local status = book_info.status -- right widget, first line local directory, filename = util.splitFilePathName(self.filepath) -- luacheck: no unused local filename_without_suffix, filetype = filemanagerutil.splitFileNameType(filename) @@ -380,10 +369,11 @@ function ListMenuItem:update() filename_without_suffix = filename fileinfo_str = self.mandatory else - local mark = has_highlight and "\u{2592} " or "" -- "medium shade" + local mark = book_info.has_annotations and "\u{2592} " or "" -- "medium shade" fileinfo_str = mark .. BD.wrap(filetype) .. " " .. BD.wrap(self.mandatory) end -- right widget, second line + local pages_str = "" if status == "complete" or status == "abandoned" then -- Display these instead of the read % if pages then @@ -693,10 +683,6 @@ function ListMenuItem:update() -- Not in db, we're going to fetch some cover self.cover_specs = cover_specs end - -- - if self.do_hint_opened and DocSettings:hasSidecarFile(self.filepath) then - self.been_opened = true - end -- No right widget by default, except in History local wright local wright_width = 0 diff --git a/plugins/coverbrowser.koplugin/main.lua b/plugins/coverbrowser.koplugin/main.lua index 906cb6cc9..46772f587 100644 --- a/plugins/coverbrowser.koplugin/main.lua +++ b/plugins/coverbrowser.koplugin/main.lua @@ -553,7 +553,7 @@ function CoverBrowser.addFileDialogButtons(widget) text = _("Refresh cached book information"), callback = function() local menu = widget.getMenuInstance() - menu:updateCache(file) -- wipe the cache + menu.resetBookInfoCache(file) BookInfoManager:deleteBookInfo(file) UIManager:close(menu.file_dialog) menu:updateItems(1, true) @@ -613,7 +613,6 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) FileChooser._recalculateDimen = _FileChooser__recalculateDimen_orig CoverBrowser.removeFileDialogButtons(FileManager) -- Also clean-up what we added, even if it does not bother original code - FileChooser.updateCache = nil FileChooser._updateItemsBuildUI = nil FileChooser._do_cover_images = nil FileChooser._do_filename_only = nil @@ -626,7 +625,6 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) -- In both mosaic and list modes, replace original methods with those from -- our generic CoverMenu local CoverMenu = require("covermenu") - FileChooser.updateCache = CoverMenu.updateCache FileChooser.updateItems = CoverMenu.updateItems FileChooser.onCloseWidget = CoverMenu.onCloseWidget CoverBrowser.addFileDialogButtons(FileManager) @@ -677,7 +675,6 @@ local function _FileManagerHistory_updateItemTable(self, ...) -- In both mosaic and list modes, replace original methods with those from -- our generic CoverMenu local CoverMenu = require("covermenu") - hist_menu.updateCache = CoverMenu.updateCache hist_menu.updateItems = CoverMenu.updateItems hist_menu.onCloseWidget = CoverMenu.onCloseWidget @@ -755,7 +752,6 @@ local function _FileManagerCollections_updateItemTable(self, ...) -- In both mosaic and list modes, replace original methods with those from -- our generic CoverMenu local CoverMenu = require("covermenu") - coll_menu.updateCache = CoverMenu.updateCache coll_menu.updateItems = CoverMenu.updateItems coll_menu.onCloseWidget = CoverMenu.onCloseWidget @@ -832,23 +828,6 @@ function CoverBrowser:onInvalidateMetadataCache(file) return true end -function CoverBrowser:onDocSettingsItemsChanged(file, doc_settings) - local status -- nil to wipe the covermenu book cache - if doc_settings then - status = doc_settings.summary and doc_settings.summary.status - if not status then return end -- changes not for us - end - if filemanager_display_mode and self.ui.file_chooser then - self.ui.file_chooser:updateCache(file, status) - end - if history_display_mode and self.ui.history and self.ui.history.hist_menu then - self.ui.history.hist_menu:updateCache(file, status) - end - if collection_display_mode and self.ui.collections and self.ui.collections.coll_menu then - self.ui.collections.coll_menu:updateCache(file, status) - end -end - function CoverBrowser:extractBooksInDirectory(path) local Trapper = require("ui/trapper") Trapper:wrap(function() diff --git a/plugins/coverbrowser.koplugin/mosaicmenu.lua b/plugins/coverbrowser.koplugin/mosaicmenu.lua index 39c45bcc1..d06bd1428 100644 --- a/plugins/coverbrowser.koplugin/mosaicmenu.lua +++ b/plugins/coverbrowser.koplugin/mosaicmenu.lua @@ -3,7 +3,6 @@ local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local CenterContainer = require("ui/widget/container/centercontainer") local Device = require("device") -local DocSettings = require("docsettings") local Font = require("ui/font") local FrameContainer = require("ui/widget/container/framecontainer") local Geom = require("ui/geometry") @@ -529,10 +528,6 @@ function MosaicMenuItem:update() else -- file self.file_deleted = self.entry.dim -- entry with deleted file from History or selected file from FM - if self.do_hint_opened and DocSettings:hasSidecarFile(self.filepath) then - self.been_opened = true - end - local bookinfo = BookInfoManager:getBookInfo(self.filepath, self.do_cover_image) if bookinfo and self.do_cover_image and not bookinfo.ignore_cover and not self.file_deleted then @@ -556,26 +551,13 @@ function MosaicMenuItem:update() end end + local book_info = self.menu.getBookInfo(self.filepath) + self.been_opened = book_info.been_opened if bookinfo then -- This book is known - -- Current page / pages are available or more accurate in .sdr/metadata.lua - -- We use a cache (cleaned at end of this browsing session) to store - -- page, percent read and book status from sidecar files, to avoid - -- re-parsing them when re-rendering a visited page - -- This cache is shared with ListMenu, so we need to fill it with the same - -- info here than there, even if we don't need them all here. - if not self.menu.cover_info_cache then - self.menu.cover_info_cache = {} - end - local percent_finished, status - if DocSettings:hasSidecarFile(self.filepath) then - self.been_opened = true - self.menu:updateCache(self.filepath, nil, true, bookinfo.pages) -- create new cache entry if absent - dummy, percent_finished, status = - unpack(self.menu.cover_info_cache[self.filepath], 1, self.menu.cover_info_cache[self.filepath].n) - end - self.percent_finished = percent_finished - self.status = status - self.show_progress_bar = self.status ~= "complete" and BookInfoManager:getSetting("show_progress_in_mosaic") and self.percent_finished + self.percent_finished = book_info.percent_finished + self.status = book_info.status + self.show_progress_bar = self.status ~= "complete" + and BookInfoManager:getSetting("show_progress_in_mosaic") and self.percent_finished local cover_bb_used = false self.bookinfo_found = true