diff --git a/frontend/apps/filemanager/filemanagermenu.lua b/frontend/apps/filemanager/filemanagermenu.lua index fbdeb6687..bec7cc0e2 100644 --- a/frontend/apps/filemanager/filemanagermenu.lua +++ b/frontend/apps/filemanager/filemanagermenu.lua @@ -933,8 +933,6 @@ function FileManagerMenu:getStartWithMenuTable() end function FileManagerMenu:exitOrRestart(callback, force) - UIManager:close(self.menu_container) - -- Only restart sets a callback, which suits us just fine for this check ;) if callback and not force and not Device:isStartupScriptUpToDate() then UIManager:show(ConfirmBox:new{ @@ -947,6 +945,7 @@ function FileManagerMenu:exitOrRestart(callback, force) return end + UIManager:close(self.menu_container) self.ui:onClose() if callback then callback() diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index db60f77ac..872bfb1bf 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -342,6 +342,11 @@ end function ReaderHighlight:onReaderReady() self:setupTouchZones() + if self.ui.paging and G_reader_settings:isTrue("highlight_write_into_pdf_notify") then + UIManager:show(Notification:new{ + text = T(_("Write highlights into PDF: %1"), self.highlight_write_into_pdf and _("on") or _("off")), + }) + end end local highlight_style = { @@ -389,34 +394,34 @@ function ReaderHighlight:addToMainMenu(menu_items) end -- main menu Typeset + local star = " ★" + local hl_sub_item_table = {} menu_items.highlight_options = { - text = _("Highlight style"), - sub_item_table = {}, + text = _("Highlights"), + sub_item_table = hl_sub_item_table, } - for i, v in ipairs(highlight_style) do - table.insert(menu_items.highlight_options.sub_item_table, { + for _, v in ipairs(highlight_style) do + local style_text, style = unpack(v) + table.insert(hl_sub_item_table, { text_func = function() - local text = v[1] - if v[2] == (G_reader_settings:readSetting("highlight_drawing_style") or self._fallback_drawer) then - text = text .. " ★" - end - return text + return style == (G_reader_settings:readSetting("highlight_drawing_style") or self._fallback_drawer) + and style_text .. star or style_text end, checked_func = function() - return self.view.highlight.saved_drawer == v[2] + return self.view.highlight.saved_drawer == style end, radio = true, callback = function() - self.view.highlight.saved_drawer = v[2] + self.view.highlight.saved_drawer = style end, hold_callback = function(touchmenu_instance) - G_reader_settings:saveSetting("highlight_drawing_style", v[2]) - if touchmenu_instance then touchmenu_instance:updateItems() end + G_reader_settings:saveSetting("highlight_drawing_style", style) + touchmenu_instance:updateItems() end, - separator = i == #highlight_style, }) end - table.insert(menu_items.highlight_options.sub_item_table, { + hl_sub_item_table[#highlight_style].separator = true + table.insert(hl_sub_item_table, { text_func = function() local saved_color = self.view.highlight.saved_color local text @@ -429,7 +434,7 @@ function ReaderHighlight:addToMainMenu(menu_items) text = text or saved_color -- nonstandard color local default_color = G_reader_settings:readSetting("highlight_color") or self._fallback_color if saved_color == default_color then - text = text .. " ★" + text = text .. star end return T(_("Highlight color: %1"), text) end, @@ -449,7 +454,7 @@ function ReaderHighlight:addToMainMenu(menu_items) touchmenu_instance:updateItems() end, }) - table.insert(menu_items.highlight_options.sub_item_table, { + table.insert(hl_sub_item_table, { text_func = function() return T(_("Gray highlight opacity: %1"), G_reader_settings:readSetting("highlight_lighten_factor", 0.2)) end, @@ -457,14 +462,13 @@ function ReaderHighlight:addToMainMenu(menu_items) return self.view.highlight.saved_drawer == "lighten" end, callback = function(touchmenu_instance) - local curr_val = G_reader_settings:readSetting("highlight_lighten_factor") local spin_widget = SpinWidget:new{ - value = curr_val, + value = G_reader_settings:readSetting("highlight_lighten_factor"), value_min = 0, value_max = 1, precision = "%.2f", value_step = 0.1, - value_hold_step = 0.25, + value_hold_step = 0.2, default_value = 0.2, keep_shown_on_apply = true, title_text = _("Gray highlight opacity"), @@ -473,18 +477,18 @@ function ReaderHighlight:addToMainMenu(menu_items) G_reader_settings:saveSetting("highlight_lighten_factor", spin.value) self.view.highlight.lighten_factor = spin.value UIManager:setDirty(self.dialog, "ui") - if touchmenu_instance then touchmenu_instance:updateItems() end + touchmenu_instance:updateItems() end, } UIManager:show(spin_widget) end, }) - table.insert(menu_items.highlight_options.sub_item_table, { + table.insert(hl_sub_item_table, { text_func = function() local notemark = self.view.highlight.note_mark or "none" for __, v in ipairs(note_mark) do if v[2] == notemark then - return T(_("Note marker: %1"), string.lower(v[1])) + return T(_("Note marker: %1"), v[1]:lower()) end end end, @@ -515,13 +519,13 @@ function ReaderHighlight:addToMainMenu(menu_items) end self.view:setupNoteMarkPosition() UIManager:setDirty(self.dialog, "ui") - if touchmenu_instance then touchmenu_instance:updateItems() end + touchmenu_instance:updateItems() end, }) end, separator = true, }) - table.insert(menu_items.highlight_options.sub_item_table, { + table.insert(hl_sub_item_table, { text = _("Apply current style and color to all highlights"), callback = function() UIManager:show(ConfirmBox:new{ @@ -546,7 +550,135 @@ function ReaderHighlight:addToMainMenu(menu_items) end, }) end, + separator = self.ui.paging and true, }) + if self.document.is_pdf then + table.insert(hl_sub_item_table, { + text_func = function() + local text = self.highlight_write_into_pdf and _("on") or _("off") + if (not self.highlight_write_into_pdf) == (not G_reader_settings:isTrue("highlight_write_into_pdf")) then + text = text .. star + end + return T(_("Write highlights into PDF: %1"), text) + end, + sub_item_table = { + { + text_func = function() + local text = _("On") + return G_reader_settings:isTrue("highlight_write_into_pdf") and text .. star or text + end, + checked_func = function() + return self.highlight_write_into_pdf + end, + radio = true, + callback = function() + if self.document:_checkIfWritable() then + self.highlight_write_into_pdf = true + if G_reader_settings:readSetting("document_metadata_folder") == "hash" then + UIManager:show(InfoMessage:new{ + text = _("Warning: Book metadata location is set to hash-based storage. Writing highlights into a PDF modifies the file which may change the partial hash, resulting in its metadata (e.g., highlights and progress) being unlinked and lost."), + icon = "notice-warning", + }) + end + else + UIManager:show(InfoMessage:new{ + text = _([[ +Highlights in this document will be saved in the settings file, but they won't be written in the document itself because the file is in a read-only location. + +If you wish your highlights to be saved in the document, just move it to a writable directory first.]]), + }) + end + end, + hold_callback = function(touchmenu_instance) + G_reader_settings:makeTrue("highlight_write_into_pdf") + touchmenu_instance:updateItems() + end, + }, + { + text_func = function() + local text = _("Off") + return G_reader_settings:hasNot("highlight_write_into_pdf") and text .. star or text + end, + checked_func = function() + return not self.highlight_write_into_pdf + end, + radio = true, + callback = function() + self.highlight_write_into_pdf = false + end, + hold_callback = function(touchmenu_instance) + G_reader_settings:delSetting("highlight_write_into_pdf") + touchmenu_instance:updateItems() + end, + }, + { + text = _("Show reminder on book opening"), + checked_func = function() + return G_reader_settings:isTrue("highlight_write_into_pdf_notify") + end, + callback = function() + G_reader_settings:flipNilOrFalse("highlight_write_into_pdf_notify") + end, + separator = true, + }, + { + text = _("Write all highlights into pdf file"), + enabled_func = function() + return self.highlight_write_into_pdf and self.ui.annotation:getNumberOfHighlightsAndNotes() > 0 + end, + callback = function() + UIManager:show(ConfirmBox:new{ + text = _("Are you sure you want to write all KOReader highlights into pdf file?"), + icon = "texture-box", + ok_callback = function() + local count = 0 + for _, item in ipairs(self.ui.annotation.annotations) do + if item.drawer then + count = count + 1 + self:writePdfAnnotation("delete", item) + self:writePdfAnnotation("save", item) + if item.note then + self:writePdfAnnotation("content", item, item.note) + end + end + end + UIManager:show(Notification:new{ + text = T(N_("1 highlight written into pdf file", + "%1 highlights written into pdf file", count), count), + }) + end, + }) + end, + }, + { + text = _("Delete all highlights from pdf file"), + enabled_func = function() + return self.highlight_write_into_pdf and self.ui.annotation:getNumberOfHighlightsAndNotes() > 0 + end, + callback = function() + UIManager:show(ConfirmBox:new{ + text = _("Are you sure you want to delete all KOReader highlights from pdf file?"), + icon = "texture-box", + ok_callback = function() + local count = 0 + for _, item in ipairs(self.ui.annotation.annotations) do + if item.drawer then + count = count + 1 + self:writePdfAnnotation("delete", item) + end + end + UIManager:show(Notification:new{ + text = T(N_("1 highlight deleted from pdf file", + "%1 highlights deleted from pdf file", count), count), + }) + end, + }) + end, + }, + }, + }) + end + if self.ui.paging then menu_items.panel_zoom_options = { text = _("Panel zoom (manga/comic)"), @@ -1938,42 +2070,30 @@ function ReaderHighlight:saveHighlight(extend_to_sentence) end function ReaderHighlight:writePdfAnnotation(action, item, content) - if self.ui.rolling or G_reader_settings:readSetting("save_document") == "disable" then + if self.ui.rolling or not self.highlight_write_into_pdf then return end logger.dbg("write to pdf document", action, item) local function doAction(action_, page_, item_, content_) if action_ == "save" then - return self.ui.document:saveHighlight(page_, item_) + self.document:saveHighlight(page_, item_) elseif action_ == "delete" then - return self.ui.document:deleteHighlight(page_, item_) + self.document:deleteHighlight(page_, item_) elseif action_ == "content" then - return self.ui.document:updateHighlightContents(page_, item_, content_) + self.document:updateHighlightContents(page_, item_, content_) end end - local can_write if item.pos0.page == item.pos1.page then -- single-page highlight - can_write = doAction(action, item.pos0.page, item, content) + doAction(action, item.pos0.page, item, content) else -- multi-page highlight for hl_page = item.pos0.page, item.pos1.page do local hl_part = self:getSavedExtendedHighlightPage(item, hl_page) - can_write = doAction(action, hl_page, hl_part, content) - if can_write == false then break end + doAction(action, hl_page, hl_part, content) if action == "save" then -- update pboxes from quadpoints item.ext[hl_page].pboxes = hl_part.pboxes end end end - if can_write == false and not self.warned_once then - self.warned_once = true - UIManager:show(InfoMessage:new{ - text = _([[ -Highlights in this document will be saved in the settings file, but they won't be written in the document itself because the file is in a read-only location. - -If you wish your highlights to be saved in the document, just move it to a writable directory first.]]), - timeout = 5, - }) - end end function ReaderHighlight:lookupWikipedia() @@ -2298,6 +2418,13 @@ function ReaderHighlight:onReadSettings(config) -- panel zoom settings isn't supported in EPUB if self.ui.paging then + if self.document.is_pdf and self.document:_checkIfWritable() then + if config:has("highlight_write_into_pdf") then + self.highlight_write_into_pdf = config:isTrue("highlight_write_into_pdf") -- true or false + else + self.highlight_write_into_pdf = G_reader_settings:readSetting("highlight_write_into_pdf") -- true or nil + end + end local ext = util.getFileNameSuffix(self.ui.document.file) G_reader_settings:initializeExtSettings("panel_zoom_enabled", {cbz = true, cbt = true}) G_reader_settings:initializeExtSettings("panel_zoom_fallback_to_text_selection", {pdf = true}) @@ -2321,6 +2448,7 @@ end function ReaderHighlight:onSaveSettings() self.ui.doc_settings:saveSetting("highlight_drawer", self.view.highlight.saved_drawer) self.ui.doc_settings:saveSetting("highlight_color", self.view.highlight.saved_color) + self.ui.doc_settings:saveSetting("highlight_write_into_pdf", self.highlight_write_into_pdf) self.ui.doc_settings:saveSetting("panel_zoom_enabled", self.panel_zoom_enabled) end diff --git a/frontend/apps/reader/modules/readermenu.lua b/frontend/apps/reader/modules/readermenu.lua index eaf6c0403..2b81ccc1f 100644 --- a/frontend/apps/reader/modules/readermenu.lua +++ b/frontend/apps/reader/modules/readermenu.lua @@ -363,8 +363,6 @@ function ReaderMenu:saveDocumentSettingsAsDefault() end function ReaderMenu:exitOrRestart(callback, force) - if self.menu_container then self:onTapCloseMenu() end - -- Only restart sets a callback, which suits us just fine for this check ;) if callback and not force and not Device:isStartupScriptUpToDate() then UIManager:show(ConfirmBox:new{ @@ -377,36 +375,13 @@ function ReaderMenu:exitOrRestart(callback, force) return end + self:onTapCloseMenu() UIManager:nextTick(function() self.ui:onClose() - if callback ~= nil then - -- show an empty widget so that the callback always happens - local Widget = require("ui/widget/widget") - local widget = Widget:new{ - width = Screen:getWidth(), - height = Screen:getHeight(), - } - UIManager:show(widget) - local waiting = function(waiting) - -- if we don't do this you can get a situation where either the - -- program won't exit due to remaining widgets until they're - -- dismissed or if the callback forces all widgets to close, - -- that the save document ConfirmBox is also closed - if self.ui and self.ui.document and self.ui.document:isEdited() then - logger.dbg("waiting for save settings") - UIManager:scheduleIn(1, function() waiting(waiting) end) - else - callback() - UIManager:close(widget) - end - end - UIManager:scheduleIn(1, function() waiting(waiting) end) + if callback then + callback() end end) - local FileManager = require("apps/filemanager/filemanager") - if FileManager.instance then - FileManager.instance:onClose() - end end function ReaderMenu:onShowMenu(tab_index) diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index 06f05ed42..97a6be095 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -1103,10 +1103,7 @@ end function ReaderView:onSaveSettings() if self.ui.paging then - if self.document:isEdited() and G_reader_settings:readSetting("save_document") ~= "always" then - -- Either "disable" (and the current tiles will be wrong) or "prompt" (but the - -- prompt will happen later, too late to catch "Don't save"), so force cached - -- tiles to be ignored on next opening. + if self.document:isEdited() and not self.ui.highlight.highlight_write_into_pdf then self.document:resetTileCacheValidity() end self.ui.doc_settings:saveSetting("tile_cache_validity_ts", self.document:getTileCacheValidity()) diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 719181c09..1753aa224 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -5,7 +5,6 @@ It works using data gathered from a document interface. ]]-- local BD = require("ui/bidi") -local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") local DeviceListener = require("device/devicelistener") local DocCache = require("document/doccache") @@ -775,41 +774,10 @@ function ReaderUI:closeDocument() self.document = nil end -function ReaderUI:notifyCloseDocument() - self:handleEvent(Event:new("CloseDocument")) - if self.document:isEdited() then - local setting = G_reader_settings:readSetting("save_document") - if setting == "always" then - self:closeDocument() - elseif setting == "disable" then - self.document:discardChange() - self:closeDocument() - else - UIManager:show(ConfirmBox:new{ - text = _("Write highlights into this PDF?"), - ok_text = _("Write"), - dismissable = false, - ok_callback = function() - self:closeDocument() - end, - cancel_callback = function() - self.document:discardChange() - self:closeDocument() - end, - }) - end - else - self:closeDocument() - end -end - function ReaderUI:onClose(full_refresh) logger.dbg("closing reader") PluginLoader:finalize() Device:notifyBookState(nil, nil) - if full_refresh == nil then - full_refresh = true - end -- if self.dialog is us, we'll have our onFlushSettings() called -- by UIManager:close() below, so avoid double save if self.dialog ~= self then @@ -820,9 +788,13 @@ function ReaderUI:onClose(full_refresh) -- Serialize the most recently displayed page for later launch DocCache:serialize(self.document.file) logger.dbg("closing document") - self:notifyCloseDocument() + self:handleEvent(Event:new("CloseDocument")) + if self.document:isEdited() and not self.highlight.highlight_write_into_pdf then + self.document:discardChange() + end + self:closeDocument() end - UIManager:close(self.dialog, full_refresh and "full") + UIManager:close(self.dialog, full_refresh ~= false and "full") end function ReaderUI:onCloseWidget() diff --git a/frontend/device/devicelistener.lua b/frontend/device/devicelistener.lua index 509c79ee6..b541b26e1 100755 --- a/frontend/device/devicelistener.lua +++ b/frontend/device/devicelistener.lua @@ -359,6 +359,10 @@ function DeviceListener:onRequestUSBMS() MassStorage:start(false) end +function DeviceListener:onExit() + self.ui.menu:exitOrRestart() +end + function DeviceListener:onRestart() self.ui.menu:exitOrRestart(function() UIManager:restartKOReader() end) end @@ -375,10 +379,6 @@ function DeviceListener:onRequestPowerOff() UIManager:askForPowerOff() end -function DeviceListener:onExit(callback) - self.ui.menu:exitOrRestart(callback) -end - function DeviceListener:onFullRefresh() if self.ui and self.ui.view then self.ui:handleEvent(Event:new("UpdateFooter", self.ui.view.footer_visible)) diff --git a/frontend/ui/data/onetime_migration.lua b/frontend/ui/data/onetime_migration.lua index e97c3f8d1..8d76bdae2 100644 --- a/frontend/ui/data/onetime_migration.lua +++ b/frontend/ui/data/onetime_migration.lua @@ -10,7 +10,7 @@ local util = require("util") local _ = require("gettext") -- Date at which the last migration snippet was added -local CURRENT_MIGRATION_DATE = 20240911 +local CURRENT_MIGRATION_DATE = 20240914 -- Retrieve the date of the previous migration, if any local last_migration_date = G_reader_settings:readSetting("last_migration_date", 0) @@ -708,5 +708,19 @@ if last_migration_date < 20240911 then G_defaults:flush() end +-- 20240914, Write highlights to PDF: revisited, https://github.com/koreader/koreader/pull/12509 +if last_migration_date < 20240914 then + logger.info("Performing one-time migration for 20240914") + + local setting = G_reader_settings:readSetting("save_document") + if setting == "always" then + G_reader_settings:makeTrue("highlight_write_into_pdf") + elseif setting == "prompt" then + G_reader_settings:makeTrue("highlight_write_into_pdf") + G_reader_settings:makeTrue("highlight_write_into_pdf_notify") + end + G_reader_settings:delSetting("save_document") +end + -- We're done, store the current migration date G_reader_settings:saveSetting("last_migration_date", CURRENT_MIGRATION_DATE) diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index 815e8a7d6..074b33f42 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -566,7 +566,7 @@ local metadata_folder_help_table = { } local metadata_folder_help_text = table.concat(metadata_folder_help_table, "\n") -local hash_filemod_warn = T(_("%1 requires calculating partial file hashes of documents which may slow down file browser navigation. Any file modifications (such as embedding annotations into PDF files or downloading from calibre) may change the partial hash, thereby losing track of any highlights, bookmarks, and progress data. Embedding PDF annotations is currently set to \"%s\" and can be disabled at (⚙ → Document → Save Document (write highlights into PDF))."), metadata_folder_str.hash) +local hash_filemod_warn = T(_("%1 requires calculating partial file hashes of documents which may slow down file browser navigation. Any file modifications (such as embedding annotations into PDF files or downloading from calibre) may change the partial hash, thereby losing track of any highlights, bookmarks, and progress data. Embedding PDF annotations can be set at menu Typeset → Highlights → Write highlights into PDF."), metadata_folder_str.hash) local leaving_hash_sdr_warn = _("Warning: You currently have documents with hash-based metadata. Until this metadata is moved by opening those documents, or deleted, file browser navigation may remain slower.") local function genMetadataFolderMenuItem(value) @@ -581,8 +581,7 @@ local function genMetadataFolderMenuItem(value) G_reader_settings:saveSetting("document_metadata_folder", value) if value == "hash" then DocSettings.setIsHashLocationEnabled(true) - local save_document_setting = G_reader_settings:readSetting("save_document") - UIManager:show(InfoMessage:new{ text = string.format(hash_filemod_warn, save_document_setting), icon = "notice-warning" }) + UIManager:show(InfoMessage:new{ text = hash_filemod_warn, icon = "notice-warning" }) else DocSettings.setIsHashLocationEnabled(nil) -- reset if DocSettings.isHashLocationEnabled() then @@ -666,26 +665,6 @@ common_settings.document_auto_save = { separator = true, } -common_settings.document_save = { - text = _("Save document (write highlights into PDF)"), - sub_item_table = { - genGenericMenuEntry(_("Prompt"), "save_document", "prompt", "prompt"), -- set "save_document" to "prompt" - { - text = _("Always"), - checked_func = function() - return G_reader_settings:readSetting("save_document") == "always" - end, - callback = function() - if G_reader_settings:readSetting("document_metadata_folder") == "hash" then - UIManager:show(InfoMessage:new{ text = _("Warning: Book metadata location is set to hash-based storage. Writing highlights into a PDF modifies the file which may change the partial hash, resulting in its metadata (e.g., highlights and progress) being unlinked and lost."), icon = "notice-warning" }) - end - G_reader_settings:saveSetting("save_document", "always") - end, - }, - genGenericMenuEntry(_("Disable"), "save_document", "disable"), - }, -} - common_settings.document_end_action = { text = _("End of document action"), sub_item_table = { diff --git a/frontend/ui/elements/filemanager_menu_order.lua b/frontend/ui/elements/filemanager_menu_order.lua index 9084a87be..2ff56a296 100644 --- a/frontend/ui/elements/filemanager_menu_order.lua +++ b/frontend/ui/elements/filemanager_menu_order.lua @@ -40,7 +40,6 @@ local order = { "document_metadata_location", "document_metadata_location_move", "document_auto_save", - "document_save", "document_end_action", "language_support", }, diff --git a/frontend/ui/elements/reader_menu_order.lua b/frontend/ui/elements/reader_menu_order.lua index 5062ee5a5..a052f931d 100644 --- a/frontend/ui/elements/reader_menu_order.lua +++ b/frontend/ui/elements/reader_menu_order.lua @@ -87,7 +87,6 @@ local order = { document = { "document_metadata_location", "document_auto_save", - "document_save", "document_end_action", "language_support", "----------------------------",