diff --git a/frontend/apps/filemanager/filemanagerutil.lua b/frontend/apps/filemanager/filemanagerutil.lua index 9b1fea163..04fa0e1d6 100644 --- a/frontend/apps/filemanager/filemanagerutil.lua +++ b/frontend/apps/filemanager/filemanagerutil.lua @@ -73,11 +73,18 @@ end -- Purge doc settings except kept function filemanagerutil.resetDocumentSettings(file) local settings_to_keep = { + annotations = true, + annotations_paging = true, + annotations_rolling = true, bookmarks = true, + bookmarks_paging = true, + bookmarks_rolling = true, bookmarks_sorted_20220106 = true, bookmarks_version = true, cre_dom_version = true, highlight = true, + highlight_paging = true, + highlight_rolling = true, highlights_imported = true, last_page = true, last_xpointer = true, diff --git a/frontend/apps/reader/modules/readerannotation.lua b/frontend/apps/reader/modules/readerannotation.lua new file mode 100644 index 000000000..45da875e4 --- /dev/null +++ b/frontend/apps/reader/modules/readerannotation.lua @@ -0,0 +1,425 @@ +local WidgetContainer = require("ui/widget/container/widgetcontainer") +local logger = require("logger") +local _ = require("gettext") +local T = require("ffi/util").template + +local ReaderAnnotation = WidgetContainer:extend{ + annotations = nil, -- array sorted by annotation position order, ascending +} + +-- build, read, save + +function ReaderAnnotation:buildAnnotation(bm, highlights, init) + -- bm: associated single bookmark ; highlights: tables with all highlights + local note = bm.text + if note == "" then + note = nil + end + local chapter = bm.chapter + local hl, pageno = self:getHighlightByDatetime(highlights, bm.datetime) + if init then + if note and self.ui.bookmark:isBookmarkAutoText(bm) then + note = nil + end + if chapter == nil then + chapter = self.ui.toc:getTocTitleByPage(bm.page) + end + pageno = self.ui.paging and bm.page or self.document:getPageFromXPointer(bm.page) + end + if not hl then -- page bookmark or orphaned bookmark + hl = {} + if bm.highlighted then -- orphaned bookmark + hl.drawer = self.view.highlight.saved_drawer + hl.color = self.view.highlight.saved_color + if self.ui.paging then + if bm.pos0.page == bm.pos1.page then + hl.pboxes = self.document:getPageBoxesFromPositions(bm.page, bm.pos0, bm.pos1) + else -- multi-page highlight, restore the first box only + hl.pboxes = self.document:getPageBoxesFromPositions(bm.page, bm.pos0, bm.pos0) + end + end + end + end + if self.ui.paging then + -- old single-page reflow highlights do not have page in position + if not bm.pos0.page then + bm.pos0.page = bm.page + bm.pos1.page = bm.page + end + end + return { -- annotation + datetime = bm.datetime, -- creation time, not changeable + drawer = hl.drawer, -- highlight drawer + color = hl.color, -- highlight color + text = bm.notes, -- highlighted text, editable + text_edited = hl.edited, -- true if highlighted text has been edited + note = note, -- user's note, editable + chapter = chapter, -- book chapter title + pageno = pageno, -- book page number + page = bm.page, -- highlight location, xPointer or number (pdf) + pos0 = bm.pos0, -- highlight start position, xPointer (== page) or table (pdf) + pos1 = bm.pos1, -- highlight end position, xPointer or table (pdf) + pboxes = hl.pboxes, -- pdf pboxes, used only and changeable by addMarkupAnnotation + ext = hl.ext, -- pdf multi-page highlight + } +end + +function ReaderAnnotation:getHighlightByDatetime(highlights, datetime) + for pageno, page_highlights in pairs(highlights) do + for _, highlight in ipairs(page_highlights) do + if highlight.datetime == datetime then + return highlight, pageno + end + end + end +end + +function ReaderAnnotation:getAnnotationsFromBookmarksHighlights(bookmarks, highlights, init) + local annotations = {} + for i = #bookmarks, 1, -1 do + table.insert(annotations, self:buildAnnotation(bookmarks[i], highlights, init)) + end + if init then + self:sortItems(annotations) + end + return annotations +end + +function ReaderAnnotation:onReadSettings(config) + local annotations = config:readSetting("annotations") + if annotations then + -- KOHighlights may set this key when it has merged annotations from different sources: + -- we want to make sure they are updated and sorted + local needs_update = config:isTrue("annotations_externally_modified") + local needs_sort -- if incompatible annotations were built of old highlights/bookmarks + -- Annotation formats in crengine and mupdf are incompatible. + local has_annotations = #annotations > 0 + local annotations_type = has_annotations and type(annotations[1].page) + if self.ui.rolling and annotations_type ~= "string" then -- incompatible format loaded, or empty + if has_annotations then -- backup incompatible format if not empty + config:saveSetting("annotations_paging", annotations) + end + -- load compatible format + annotations = config:readSetting("annotations_rolling") or {} + config:delSetting("annotations_rolling") + needs_sort = true + elseif self.ui.paging and annotations_type ~= "number" then + if has_annotations then + config:saveSetting("annotations_rolling", annotations) + end + annotations = config:readSetting("annotations_paging") or {} + config:delSetting("annotations_paging") + needs_sort = true + end + self.annotations = annotations + if needs_update or needs_sort then + if self.ui.rolling then + self.ui:registerPostInitCallback(function() + self:updatedAnnotations(needs_update, needs_sort) + end) + else + self:updatedAnnotations(needs_update, needs_sort) + end + config:delSetting("annotations_externally_modified") + end + else -- first run + if self.ui.rolling then + self.ui:registerPostInitCallback(function() + self:migrateToAnnotations(config) + end) + else + self:migrateToAnnotations(config) + end + end +end + +function ReaderAnnotation:migrateToAnnotations(config) + local bookmarks = config:readSetting("bookmarks") or {} + local highlights = config:readSetting("highlight") or {} + + if config:hasNot("highlights_imported") then + -- before 2014, saved highlights were not added to bookmarks when they were created. + for page, hls in pairs(highlights) do + for _, hl in ipairs(hls) do + local hl_page = self.ui.paging and page or hl.pos0 + -- highlights saved by some old versions don't have pos0 field + -- we just ignore those highlights + if hl_page then + local item = { + datetime = hl.datetime, + highlighted = true, + notes = hl.text, + page = hl_page, + pos0 = hl.pos0, + pos1 = hl.pos1, + } + if self.ui.paging then + item.pos0.page = page + item.pos1.page = page + end + table.insert(bookmarks, item) + end + end + end + end + + -- Bookmarks/highlights formats in crengine and mupdf are incompatible. + local has_bookmarks = #bookmarks > 0 + local bookmarks_type = has_bookmarks and type(bookmarks[1].page) + if self.ui.rolling then + if bookmarks_type == "string" then -- compatible format loaded, check for incompatible old backup + if config:has("bookmarks_paging") then -- save incompatible old backup + local bookmarks_paging = config:readSetting("bookmarks_paging") + local highlights_paging = config:readSetting("highlight_paging") + local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks_paging, highlights_paging) + config:saveSetting("annotations_paging", annotations) + config:delSetting("bookmarks_paging") + config:delSetting("highlight_paging") + end + else -- incompatible format loaded, or empty + if has_bookmarks then -- save incompatible format if not empty + local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights) + config:saveSetting("annotations_paging", annotations) + end + -- load compatible format + bookmarks = config:readSetting("bookmarks_rolling") or {} + highlights = config:readSetting("highlight_rolling") or {} + config:delSetting("bookmarks_rolling") + config:delSetting("highlight_rolling") + end + else -- self.ui.paging + if bookmarks_type == "number" then + if config:has("bookmarks_rolling") then + local bookmarks_rolling = config:readSetting("bookmarks_rolling") + local highlights_rolling = config:readSetting("highlight_rolling") + local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks_rolling, highlights_rolling) + config:saveSetting("annotations_rolling", annotations) + config:delSetting("bookmarks_rolling") + config:delSetting("highlight_rolling") + end + else + if has_bookmarks then + local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights) + config:saveSetting("annotations_rolling", annotations) + end + bookmarks = config:readSetting("bookmarks_paging") or {} + highlights = config:readSetting("highlight_paging") or {} + config:delSetting("bookmarks_paging") + config:delSetting("highlight_paging") + end + end + + self.annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights, true) +end + +function ReaderAnnotation:onDocumentRerendered() + self.needs_update = true +end + +function ReaderAnnotation:onCloseDocument() + self:updatePageNumbers() +end + +function ReaderAnnotation:onSaveSettings() + self:updatePageNumbers() + self.ui.doc_settings:saveSetting("annotations", self.annotations) +end + +-- items handling + +function ReaderAnnotation:updatePageNumbers() + if self.needs_update and self.ui.rolling then -- triggered by ReaderRolling on document layout change + for _, item in ipairs(self.annotations) do + item.pageno = self.document:getPageFromXPointer(item.page) + end + end + self.needs_update = nil +end + +function ReaderAnnotation:sortItems(items) + if #items > 1 then + local sort_func = self.ui.rolling and function(a, b) return self:isItemInPositionOrderRolling(a, b) end + or function(a, b) return self:isItemInPositionOrderPaging(a, b) end + table.sort(items, sort_func) + end +end + +function ReaderAnnotation:updatedAnnotations(needs_update, needs_sort) + if needs_update then + self.needs_update = true + self:updatePageNumbers() + needs_sort = true + end + if needs_sort then + self:sortItems(self.annotations) + end +end + +function ReaderAnnotation:updateItemByXPointer(item) + -- called by ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade() + local chapter = self.ui.toc:getTocTitleByPage(item.page) + if chapter == "" then + chapter = nil + end + if not item.drawer then -- page bookmark + item.text = chapter and T(_("in %1"), chapter) or nil + end + item.chapter = chapter + item.pageno = self.document:getPageFromXPointer(item.page) +end + +function ReaderAnnotation:isItemInPositionOrderRolling(a, b) + local a_page = self.document:getPageFromXPointer(a.page) + local b_page = self.document:getPageFromXPointer(b.page) + if a_page == b_page then -- both items in the same page + if a.drawer and b.drawer then -- both items are highlights, compare positions + local compare_xp = self.document:compareXPointers(a.page, b.page) + if compare_xp then + if compare_xp == 0 then -- both highlights with the same start, compare ends + compare_xp = self.document:compareXPointers(a.pos1, b.pos1) + if compare_xp then + return compare_xp > 0 + end + logger.warn("Invalid xpointer in highlight:", a.pos1, b.pos1) + return true + end + return compare_xp > 0 + end + -- if compare_xp is nil, some xpointer is invalid and "a" will be sorted first to page 1 + logger.warn("Invalid xpointer in highlight:", a.page, b.page) + return true + end + return not a.drawer -- have page bookmarks before highlights + end + return a_page < b_page +end + +function ReaderAnnotation:isItemInPositionOrderPaging(a, b) + if a.page == b.page then -- both items in the same page + if a.drawer and b.drawer then -- both items are highlights, compare positions + local is_reflow = self.document.configurable.text_wrap -- save reflow mode + self.document.configurable.text_wrap = 0 -- native positions + -- sort start and end positions of each highlight + local a_start, a_end, b_start, b_end, result + if self.document:comparePositions(a.pos0, a.pos1) > 0 then + a_start, a_end = a.pos0, a.pos1 + else + a_start, a_end = a.pos1, a.pos0 + end + if self.document:comparePositions(b.pos0, b.pos1) > 0 then + b_start, b_end = b.pos0, b.pos1 + else + b_start, b_end = b.pos1, b.pos0 + end + -- compare start positions + local compare_pos = self.document:comparePositions(a_start, b_start) + if compare_pos == 0 then -- both highlights with the same start, compare ends + result = self.document:comparePositions(a_end, b_end) > 0 + else + result = compare_pos > 0 + end + self.document.configurable.text_wrap = is_reflow -- restore reflow mode + return result + end + return not a.drawer -- have page bookmarks before highlights + end + return a.page < b.page +end + +function ReaderAnnotation:getItemIndex(item, no_binary) + local doesMatch + if item.datetime then + doesMatch = function(a, b) + return a.datetime == b.datetime + end + else + if self.ui.rolling then + doesMatch = function(a, b) + if a.text ~= b.text or a.pos0 ~= b.pos0 or a.pos1 ~= b.pos1 then + return false + end + return true + end + else + doesMatch = function(a, b) + if a.text ~= b.text or a.pos0.page ~= b.pos0.page + or a.pos0.x ~= b.pos0.x or a.pos1.x ~= b.pos1.x + or a.pos0.y ~= b.pos0.y or a.pos1.y ~= b.pos1.y then + return false + end + return true + end + end + end + + if not no_binary then + local isInOrder = self.ui.rolling and self.isItemInPositionOrderRolling or self.isItemInPositionOrderPaging + local _start, _end, _middle = 1, #self.annotations + while _start <= _end do + _middle = bit.rshift(_start + _end, 1) + local v = self.annotations[_middle] + if doesMatch(item, v) then + return _middle + elseif isInOrder(self, item, v) then + _end = _middle - 1 + else + _start = _middle + 1 + end + end + end + + for i, v in ipairs(self.annotations) do + if doesMatch(item, v) then + return i + end + end +end + +function ReaderAnnotation:getInsertionIndex(item) + local isInOrder = self.ui.rolling and self.isItemInPositionOrderRolling or self.isItemInPositionOrderPaging + local _start, _end, _middle, direction = 1, #self.annotations, 1, 0 + while _start <= _end do + _middle = bit.rshift(_start + _end, 1) + if isInOrder(self, item, self.annotations[_middle]) then + _end, direction = _middle - 1, 0 + else + _start, direction = _middle + 1, 1 + end + end + return _middle + direction +end + +function ReaderAnnotation:addItem(item) + item.datetime = os.date("%Y-%m-%d %H:%M:%S") + item.pageno = self.ui.paging and item.page or self.document:getPageFromXPointer(item.page) + local index = self:getInsertionIndex(item) + table.insert(self.annotations, index, item) + return index +end + +-- info + +function ReaderAnnotation:hasAnnotations() + return #self.annotations > 0 +end + +function ReaderAnnotation:getNumberOfAnnotations() + return #self.annotations +end + +function ReaderAnnotation:getNumberOfHighlightsAndNotes() -- for Statistics plugin + local highlights = 0 + local notes = 0 + for _, item in ipairs(self.annotations) do + if item.drawer then + if item.note then + notes = notes + 1 + else + highlights = highlights + 1 + end + end + end + return highlights, notes +end + +return ReaderAnnotation diff --git a/frontend/apps/reader/modules/readerbookmark.lua b/frontend/apps/reader/modules/readerbookmark.lua index 216537047..1f328e82d 100644 --- a/frontend/apps/reader/modules/readerbookmark.lua +++ b/frontend/apps/reader/modules/readerbookmark.lua @@ -17,7 +17,6 @@ local SpinWidget = require("ui/widget/spinwidget") local TextViewer = require("ui/widget/textviewer") local UIManager = require("ui/uimanager") local Utf8Proc = require("ffi/utf8proc") -local logger = require("logger") local util = require("util") local _ = require("gettext") local N_ = _.ngettext @@ -26,12 +25,11 @@ local T = require("ffi/util").template local ReaderBookmark = InputContainer:extend{ bookmarks_items_per_page_default = 14, - bookmarks = nil, -- mark the type of a bookmark with a symbol + non-expandable space display_prefix = { highlight = "\u{2592}\u{2002}", -- "medium shade" - note = "\u{F040}\u{2002}", -- "pencil" - bookmark = "\u{F097}\u{2002}", -- "empty bookmark" + note = "\u{F040}\u{2002}", -- "pencil" + bookmark = "\u{F097}\u{2002}", -- "empty bookmark" }, } @@ -51,6 +49,7 @@ function ReaderBookmark:init() G_reader_settings:saveSetting("bookmarks_items_font_size", items_font_size) end end + self.items_text = G_reader_settings:readSetting("bookmarks_items_text_type", "note") self.ui.menu:registerToMainMenu(self) -- NOP our own gesture handling @@ -67,6 +66,27 @@ end ReaderBookmark.onPhysicalKeyboardConnected = ReaderBookmark.registerKeyEvents +function ReaderBookmark:genItemTextMenuItem(type, get_string) + local text_type = { + text = _("highlighted text"), + all = _("highlighted text and note"), + note = _("note, or highlighted text"), + } + if get_string then + return text_type[type] + end + return { + text = text_type[type], + checked_func = function() + return self.items_text == type + end, + callback = function() + self.items_text = type + G_reader_settings:saveSetting("bookmarks_items_text_type", type) + end, + } +end + function ReaderBookmark:addToMainMenu(menu_items) menu_items.bookmarks = { text = _("Bookmarks"), @@ -91,7 +111,7 @@ function ReaderBookmark:addToMainMenu(menu_items) return self.ui.paging.bookmark_flipping_mode end, callback = function(touchmenu_instance) - self:toggleBookmarkBrowsingMode() + self.ui.paging:onToggleBookmarkFlipping() touchmenu_instance:closeMenu() end, } @@ -155,6 +175,7 @@ function ReaderBookmark:addToMainMenu(menu_items) callback = function() G_reader_settings:flipNilOrFalse("bookmarks_items_multilines_show_more_text") end, + separator = true, }, { text = _("Show separator between items"), @@ -166,21 +187,23 @@ function ReaderBookmark:addToMainMenu(menu_items) end, }, { - text = _("Sort by largest page number"), - checked_func = function() - return G_reader_settings:nilOrTrue("bookmarks_items_reverse_sorting") - end, - callback = function() - G_reader_settings:flipNilOrTrue("bookmarks_items_reverse_sorting") + text_func = function() + local curr_type = G_reader_settings:readSetting("bookmarks_items_text_type", "note") + return T(_("Show in items: %1"), self:genItemTextMenuItem(curr_type, true)) end, + sub_item_table = { + self:genItemTextMenuItem("text"), + self:genItemTextMenuItem("all"), + self:genItemTextMenuItem("note"), + }, }, { - text = _("Add page number / timestamp to bookmark"), + text = _("Sort by largest page number"), checked_func = function() - return G_reader_settings:nilOrTrue("bookmarks_items_auto_text") + return G_reader_settings:nilOrTrue("bookmarks_items_reverse_sorting") end, callback = function() - G_reader_settings:flipNilOrTrue("bookmarks_items_auto_text") + G_reader_settings:flipNilOrTrue("bookmarks_items_reverse_sorting") end, }, }, @@ -188,7 +211,7 @@ function ReaderBookmark:addToMainMenu(menu_items) menu_items.bookmark_search = { text = _("Bookmark search"), enabled_func = function() - return self:hasBookmarks() + return self.ui.annotation:hasAnnotations() end, callback = function() self:onSearchBookmark() @@ -196,248 +219,328 @@ function ReaderBookmark:addToMainMenu(menu_items) } end -function ReaderBookmark:toggleBookmarkBrowsingMode() - self.ui:handleEvent(Event:new("ToggleBookmarkFlipping")) +-- page bookmarks, dogear + +function ReaderBookmark:onToggleBookmark() + self:toggleBookmark() + self.view.footer:onUpdateFooter(self.view.footer_visible) + self.view.dogear:onSetDogearVisibility(not self.view.dogear_visible) + UIManager:setDirty(self.view.dialog, "ui") + return true end -function ReaderBookmark:isBookmarkInPositionOrder(a, b) - if self.ui.paging then - if a.page == b.page then -- both bookmarks in the same page - if a.highlighted and b.highlighted then -- both are highlights, compare positions - local is_reflow = self.ui.document.configurable.text_wrap -- save reflow mode - -- reflow mode didn't set page in positions (in older bookmarks) - if not a.pos0.page then - a.pos0.page = a.page - a.pos1.page = a.page - end - if not b.pos0.page then - b.pos0.page = b.page - b.pos1.page = b.page - end - self.ui.document.configurable.text_wrap = 0 -- native positions - -- sort start and end positions of each highlight - local compare_pos, a_start, a_end, b_start, b_end, result - compare_pos = self.ui.document:comparePositions(a.pos0, a.pos1) > 0 - a_start = compare_pos and a.pos0 or a.pos1 - a_end = compare_pos and a.pos1 or a.pos0 - compare_pos = self.ui.document:comparePositions(b.pos0, b.pos1) > 0 - b_start = compare_pos and b.pos0 or b.pos1 - b_end = compare_pos and b.pos1 or b.pos0 - -- compare start positions - compare_pos = self.ui.document:comparePositions(a_start, b_start) - if compare_pos == 0 then -- both highlights with the same start, compare ends - result = self.ui.document:comparePositions(a_end, b_end) < 0 - else - result = compare_pos < 0 - end - self.ui.document.configurable.text_wrap = is_reflow -- restore reflow mode - return result - end - return a.highlighted -- have page bookmarks before highlights +function ReaderBookmark:toggleBookmark(pageno) + local pn_or_xp, item + if pageno then + if self.ui.rolling then + pn_or_xp = self.ui.document:getPageXPointer(pageno) + else + pn_or_xp = pageno end - return a.page > b.page else - local a_page = self.ui.document:getPageFromXPointer(a.page) - local b_page = self.ui.document:getPageFromXPointer(b.page) - if a_page == b_page then -- both bookmarks in the same page - local compare_xp = self.ui.document:compareXPointers(a.page, b.page) - if compare_xp then - if compare_xp == 0 then -- both bookmarks with the same start - if a.highlighted and b.highlighted then -- both are highlights, compare ends - compare_xp = self.ui.document:compareXPointers(a.pos1, b.pos1) - if compare_xp then - return compare_xp < 0 - end - logger.warn("Invalid xpointer in highlight:", a.pos1, b.pos1) - return - end - return a.highlighted -- have page bookmarks before highlights - end - return compare_xp < 0 - end - -- if compare_xp is nil, some xpointer is invalid and will be sorted first to page 1 - logger.warn("Invalid xpointer in highlight:", a.page, b.page) + pn_or_xp = self:getCurrentPageNumber() + end + local index = self:getDogearBookmarkIndex(pn_or_xp) + if index then + item = table.remove(self.ui.annotation.annotations, index) + else + local text + local chapter = self.ui.toc:getTocTitleByPage(pn_or_xp) + if chapter == "" then + chapter = nil + else + -- @translators In which chapter title (%1) a note is found. + text = T(_("in %1"), chapter) end - return a_page > b_page + item = { + page = pn_or_xp, + text = text, + chapter = chapter, + } + self.ui.annotation:addItem(item) end + self.ui:handleEvent(Event:new("AnnotationsModified", { item })) +end + +function ReaderBookmark:setDogearVisibility(pn_or_xp) + local visible = self:isPageBookmarked(pn_or_xp) + self.view.dogear:onSetDogearVisibility(visible) +end + +function ReaderBookmark:isPageBookmarked(pn_or_xp) + local page = pn_or_xp or self:getCurrentPageNumber() + return self:getDogearBookmarkIndex(page) and true or false end function ReaderBookmark:isBookmarkInPageOrder(a, b) local a_page = self:getBookmarkPageNumber(a) local b_page = self:getBookmarkPageNumber(b) - if a_page == b_page then -- have bookmarks before highlights - return a.highlighted + if a_page == b_page then -- have page bookmarks before highlights + return not a.drawer + end + return a_page < b_page +end + +function ReaderBookmark:getDogearBookmarkIndex(pn_or_xp) + local doesMatch + if self.ui.paging then + doesMatch = function(p1, p2) + return p1 == p2 + end + else + doesMatch = function(p1, p2) + return self.ui.document:getPageFromXPointer(p1) == self.ui.document:getPageFromXPointer(p2) + end + end + local _middle + local _start, _end = 1, #self.ui.annotation.annotations + while _start <= _end do + _middle = bit.rshift(_start + _end, 1) + local v = self.ui.annotation.annotations[_middle] + if not v.drawer and doesMatch(v.page, pn_or_xp) then + return _middle + elseif self:isBookmarkInPageOrder({page = pn_or_xp}, v) then + _end = _middle - 1 + else + _start = _middle + 1 + end end - return a_page > b_page end -function ReaderBookmark:isBookmarkInReversePageOrder(a, b) - -- The way this is used (by getNextBookmarkedPage(), iterating bookmarks - -- in reverse order), we want to skip highlights, but also the current - -- page: so we do not do any "a.page == b.page" check (not even with - -- a reverse logic than the one from above function). - return self:getBookmarkPageNumber(a) < self:getBookmarkPageNumber(b) +-- remove, update bookmark + +function ReaderBookmark:removeItem(item) + local index = self.ui.annotation:getItemIndex(item) + if item.pos0 then + self.ui.highlight:deleteHighlight(index) -- will call ReaderBookmark:removeItemByIndex() + else -- dogear bookmark, update it in case we removed a bookmark for current page + self:removeItemByIndex(index) + self:setDogearVisibility(self:getCurrentPageNumber()) + end end -function ReaderBookmark:isBookmarkPageInPageOrder(a, b) - return a > self:getBookmarkPageNumber(b) +function ReaderBookmark:removeItemByIndex(index) + local item = self.ui.annotation.annotations[index] + local item_type = self.getBookmarkType(item) + if item_type == "highlight" then + self.ui:handleEvent(Event:new("AnnotationsModified", { item, nb_highlights_added = -1 })) + elseif item_type == "note" then + self.ui:handleEvent(Event:new("AnnotationsModified", { item, nb_notes_added = -1 })) + end + table.remove(self.ui.annotation.annotations, index) + self.view.footer:onUpdateFooter(self.view.footer_visible) end -function ReaderBookmark:isBookmarkPageInReversePageOrder(a, b) - return a < self:getBookmarkPageNumber(b) +function ReaderBookmark:deleteItemNote(item) + local index = self.ui.annotation:getItemIndex(item) + self.ui.annotation.annotations[index].note = nil + self.ui:handleEvent(Event:new("AnnotationsModified", { item, nb_highlights_added = 1, nb_notes_added = -1 })) end -function ReaderBookmark:fixBookmarkSort(config) - -- for backward compatibility, since previously bookmarks for credocuments - -- are not well sorted. We need to do a whole sorting for at least once. - -- 20220106: accurate sorting with isBookmarkInPositionOrder - if config:hasNot("bookmarks_sorted_20220106") then - table.sort(self.bookmarks, function(a, b) - return self:isBookmarkInPositionOrder(a, b) - end) +-- navigation + +function ReaderBookmark:onPageUpdate(pageno) + local pn_or_xp = self.ui.paging and pageno or self.ui.document:getXPointer() + self:setDogearVisibility(pn_or_xp) +end + +function ReaderBookmark:onPosUpdate(pos) + local pn_or_xp = self.ui.document:getXPointer() + self:setDogearVisibility(pn_or_xp) +end + +function ReaderBookmark:gotoBookmark(pn_or_xp, marker_xp) + if pn_or_xp then + local event = self.ui.paging and "GotoPage" or "GotoXPointer" + self.ui:handleEvent(Event:new(event, pn_or_xp, marker_xp)) end end -function ReaderBookmark:importSavedHighlight(config) - local textmarks = config:readSetting("highlight") or {} - -- import saved highlight once, because from now on highlight are added to - -- bookmarks when they are created. - if config:hasNot("highlights_imported") then - for page, marks in pairs(textmarks) do - for _, mark in ipairs(marks) do - local mark_page = self.ui.paging and page or mark.pos0 - -- highlights saved by some old versions don't have pos0 field - -- we just ignore those highlights - if mark_page then - self:addBookmark({ - page = mark_page, - datetime = mark.datetime, - notes = mark.text, - highlighted = true, - }) - end - end +function ReaderBookmark:getNextBookmarkedPage(pn_or_xp, page_bookmark_only) + local pageno = self:getBookmarkPageNumber({page = pn_or_xp}) + for i = 1, #self.ui.annotation.annotations do + local item = self.ui.annotation.annotations[i] + if (not page_bookmark_only or not item.drawer) and pageno < self:getBookmarkPageNumber(item) then + return item.page end end end -function ReaderBookmark:updateHighlightsIfNeeded(config) - -- adds "chapter" property to highlights and bookmarks already saved in the document - local version = config:readSetting("bookmarks_version") or 0 - if version >= 20200615 then - return - end - for page, highlights in pairs(self.view.highlight.saved) do - for _, highlight in ipairs(highlights) do - local pn_or_xp = self.ui.paging and page or highlight.pos0 - highlight.chapter = self.ui.toc:getTocTitleByPage(pn_or_xp) +function ReaderBookmark:getPreviousBookmarkedPage(pn_or_xp, page_bookmark_only) + local pageno = self:getBookmarkPageNumber({page = pn_or_xp}) + for i = #self.ui.annotation.annotations, 1, -1 do + local item = self.ui.annotation.annotations[i] + if (not page_bookmark_only or not item.drawer) and pageno > self:getBookmarkPageNumber(item) then + return item.page end end - for _, bookmark in ipairs(self.bookmarks) do - local pn_or_xp = (self.ui.rolling and bookmark.pos0) and bookmark.pos0 or bookmark.page - bookmark.chapter = self.ui.toc:getTocTitleByPage(pn_or_xp) - end end -function ReaderBookmark:onReadSettings(config) - self.bookmarks = config:readSetting("bookmarks", {}) - -- Bookmark formats in crengine and mupdf are incompatible. - -- Backup bookmarks when the document is opened with incompatible engine. - if #self.bookmarks > 0 then - local bookmarks_type = type(self.bookmarks[1].page) - if self.ui.rolling and bookmarks_type == "number" then - config:saveSetting("bookmarks_paging", self.bookmarks) - self.bookmarks = config:readSetting("bookmarks_rolling", {}) - config:saveSetting("bookmarks", self.bookmarks) - config:delSetting("bookmarks_rolling") - elseif self.ui.paging and bookmarks_type == "string" then - config:saveSetting("bookmarks_rolling", self.bookmarks) - self.bookmarks = config:readSetting("bookmarks_paging", {}) - config:saveSetting("bookmarks", self.bookmarks) - config:delSetting("bookmarks_paging") +function ReaderBookmark:getFirstBookmarkedPage(pn_or_xp) + if #self.ui.annotation.annotations > 0 then + local pageno = self:getBookmarkPageNumber({page = pn_or_xp}) + local item = self.ui.annotation.annotations[1] + if pageno > self:getBookmarkPageNumber(item) then + return item.page end - else - if self.ui.rolling and config:has("bookmarks_rolling") then - self.bookmarks = config:readSetting("bookmarks_rolling") - config:delSetting("bookmarks_rolling") - elseif self.ui.paging and config:has("bookmarks_paging") then - self.bookmarks = config:readSetting("bookmarks_paging") - config:delSetting("bookmarks_paging") + end +end + +function ReaderBookmark:getLastBookmarkedPage(pn_or_xp) + if #self.ui.annotation.annotations > 0 then + local pageno = self:getBookmarkPageNumber({page = pn_or_xp}) + local item = self.ui.annotation.annotations[#self.ui.annotation.annotations] + if pageno < self:getBookmarkPageNumber(item) then + return item.page end end - -- need to do this after initialization because checking xpointer - -- may cause segfaults before credocuments are inited. - self.ui:registerPostInitCallback(function() - self:fixBookmarkSort(config) - self:importSavedHighlight(config) - self:updateHighlightsIfNeeded(config) - end) end -function ReaderBookmark:onSaveSettings() - self.ui.doc_settings:saveSetting("bookmarks", self.bookmarks) - self.ui.doc_settings:saveSetting("bookmarks_version", 20200615) - self.ui.doc_settings:makeTrue("bookmarks_sorted_20220106") - self.ui.doc_settings:makeTrue("highlights_imported") +function ReaderBookmark:onGotoPreviousBookmark(pn_or_xp) + self:gotoBookmark(self:getPreviousBookmarkedPage(pn_or_xp)) + return true end -function ReaderBookmark:onToggleBookmark() - self:toggleBookmark() - self.view.footer:onUpdateFooter(self.view.footer_visible) - self.view.dogear:onSetDogearVisibility(not self.view.dogear_visible) - UIManager:setDirty(self.view.dialog, "ui") +function ReaderBookmark:onGotoNextBookmark(pn_or_xp) + self:gotoBookmark(self:getNextBookmarkedPage(pn_or_xp)) return true end -function ReaderBookmark:isPageBookmarked(pn_or_xp) - local page = pn_or_xp or self:getCurrentPageNumber() - return self:getDogearBookmarkIndex(page) and true or false +function ReaderBookmark:onGotoPreviousBookmarkFromPage(add_current_location_to_stack) + if add_current_location_to_stack ~= false then -- nil or true + self.ui.link:addCurrentLocationToStack() + end + local pn_or_xp = self:getCurrentPageNumber() + self:gotoBookmark(self:getPreviousBookmarkedPage(pn_or_xp)) + return true end -function ReaderBookmark:setDogearVisibility(pn_or_xp) - self.view.dogear:onSetDogearVisibility(self:isPageBookmarked(pn_or_xp)) +function ReaderBookmark:onGotoNextBookmarkFromPage(add_current_location_to_stack) + if add_current_location_to_stack ~= false then -- nil or true + self.ui.link:addCurrentLocationToStack() + end + local pn_or_xp = self:getCurrentPageNumber() + self:gotoBookmark(self:getNextBookmarkedPage(pn_or_xp)) + return true end -function ReaderBookmark:onPageUpdate(pageno) - local pn_or_xp = self.ui.paging and pageno or self.ui.document:getXPointer() - self:setDogearVisibility(pn_or_xp) +function ReaderBookmark:onGotoFirstBookmark(add_current_location_to_stack) + if add_current_location_to_stack ~= false then -- nil or true + self.ui.link:addCurrentLocationToStack() + end + local pn_or_xp = self:getCurrentPageNumber() + self:gotoBookmark(self:getFirstBookmarkedPage(pn_or_xp)) + return true end -function ReaderBookmark:onPosUpdate(pos) - self:setDogearVisibility(self.ui.document:getXPointer()) +function ReaderBookmark:onGotoLastBookmark(add_current_location_to_stack) + if add_current_location_to_stack ~= false then -- nil or true + self.ui.link:addCurrentLocationToStack() + end + local pn_or_xp = self:getCurrentPageNumber() + self:gotoBookmark(self:getLastBookmarkedPage(pn_or_xp)) + return true end -function ReaderBookmark:gotoBookmark(pn_or_xp, marker_xp) - if pn_or_xp then - local event = self.ui.paging and "GotoPage" or "GotoXPointer" - self.ui:handleEvent(Event:new(event, pn_or_xp, marker_xp)) +-- bookmarks misc info, helpers + +function ReaderBookmark:getCurrentPageNumber() + return self.ui.paging and self.view.state.page or self.ui.document:getXPointer() +end + +function ReaderBookmark:getBookmarkPageNumber(bookmark) + return self.ui.paging and bookmark.page or self.ui.document:getPageFromXPointer(bookmark.page) +end + +function ReaderBookmark.getBookmarkType(bookmark) + if bookmark.drawer then + if bookmark.note then + return "note" + end + return "highlight" + end + return "bookmark" +end + +function ReaderBookmark:getLatestBookmark() + local latest_bookmark, latest_bookmark_idx + local latest_bookmark_datetime = "0" + for i, v in ipairs(self.ui.annotation.annotations) do + if v.datetime > latest_bookmark_datetime then + latest_bookmark_datetime = v.datetime + latest_bookmark = v + latest_bookmark_idx = i + end + end + return latest_bookmark, latest_bookmark_idx +end + +function ReaderBookmark:getBookmarkedPages() + local pages = {} + for _, bm in ipairs(self.ui.annotation.annotations) do + local page = self:getBookmarkPageNumber(bm) + local btype = self.getBookmarkType(bm) + if not pages[page] then + pages[page] = {} + end + if not pages[page][btype] then + pages[page][btype] = true + end end + return pages end +function ReaderBookmark:getBookmarkPageString(page) + if self.ui.rolling then + if self.ui.pagemap and self.ui.pagemap:wantsPageLabels() then + page = self.ui.pagemap:getXPointerPageLabel(page, true) + else + page = self.ui.document:getPageFromXPointer(page) + if self.ui.document:hasHiddenFlows() then + local flow = self.ui.document:getPageFlow(page) + page = self.ui.document:getPageNumberInFlow(page) + if flow > 0 then + page = T("[%1]%2", page, flow) + end + end + end + end + return tostring(page) +end + +function ReaderBookmark:isBookmarkAutoText(bookmark) + -- old bookmarks only + if bookmark.text == "" or bookmark.text == bookmark.notes then + return true + end + local page = self:getBookmarkPageString(bookmark.page) + local auto_text = T(_("Page %1 %2 @ %3"), page, bookmark.notes, bookmark.datetime) + return bookmark.text == auto_text +end + +-- bookmark list, dialogs + function ReaderBookmark:onShowBookmark(match_table) self.show_edited_only = nil self.select_mode = false self.filtered_mode = match_table and true or false + -- build up item_table local item_table = {} - local is_reverse_sorting = G_reader_settings:nilOrTrue("bookmarks_items_reverse_sorting") + local is_reverse_sorting = G_reader_settings:nilOrTrue("bookmarks_items_reverse_sorting") -- page numbers descending local curr_page_num = self:getCurrentPageNumber() local curr_page_string = self:getBookmarkPageString(curr_page_num) - local curr_page_index = self:getBookmarkInsertionIndexBinary({page = curr_page_num}) - 1 - local num = #self.bookmarks + 1 - curr_page_index = is_reverse_sorting and curr_page_index or num - curr_page_index + local curr_page_index = self.ui.annotation:getInsertionIndex({page = curr_page_num}) + local num = #self.ui.annotation.annotations + 1 + curr_page_index = is_reverse_sorting and num - curr_page_index or curr_page_index local curr_page_index_filtered = curr_page_index - for i = 1, #self.bookmarks do - -- bookmarks are internally sorted by descending page numbers - local v = self.bookmarks[is_reverse_sorting and i or num - i] - if v.text == nil or v.text == "" then - v.text = self:getBookmarkAutoText(v) - end + for i = 1, #self.ui.annotation.annotations do + local v = self.ui.annotation.annotations[is_reverse_sorting and num - i or i] local item = util.tableDeepCopy(v) - item.type = self:getBookmarkType(item) + item.text_orig = item.text or "" + item.type = self.getBookmarkType(item) if not match_table or self:doesBookmarkMatchTable(item, match_table) then - item.text_orig = item.text or item.notes - item.text = self.display_prefix[item.type] .. item.text_orig + item.text = self:getBookmarkItemText(item) item.mandatory = self:getBookmarkPageString(item.page) if (not is_reverse_sorting and i >= curr_page_index) or (is_reverse_sorting and i <= curr_page_index) then item.after_curr_page = true @@ -511,14 +614,11 @@ function ReaderBookmark:onShowBookmark(match_table) end function bm_menu:onMenuHold(item) - local bm_view = bookmark._getDialogHeader(item) .. "\n\n" - if item.type == "bookmark" then - bm_view = bm_view .. item.text - else - bm_view = bm_view .. bookmark.display_prefix["highlight"] .. item.notes - if item.type == "note" then - bm_view = bm_view .. "\n\n" .. item.text - end + local bm_view = bookmark:_getDialogHeader(item) .. "\n\n" + local prefix = item.type == "bookmark" and bookmark.display_prefix["bookmark"] or bookmark.display_prefix["highlight"] + bm_view = bm_view .. prefix .. item.text_orig + if item.note then + bm_view = bm_view .. "\n\n" .. bookmark.display_prefix["note"] .. item.note end local not_select_mode = not self.select_mode and not bookmark.ui.highlight.select_mode local textviewer @@ -530,7 +630,7 @@ function ReaderBookmark:onShowBookmark(match_table) { { text = _("Reset text"), - enabled = item.highlighted and not_select_mode and item.edited or false, + enabled = item.drawer and not_select_mode and item.text_edited or false, callback = function() UIManager:close(textviewer) bookmark:setHighlightedText(item) @@ -542,7 +642,7 @@ function ReaderBookmark:onShowBookmark(match_table) }, { text = _("Edit text"), - enabled = item.highlighted and not_select_mode or false, + enabled = item.drawer and not_select_mode or false, callback = function() UIManager:close(textviewer) bookmark:editHighlightedText(item) @@ -558,7 +658,7 @@ function ReaderBookmark:onShowBookmark(match_table) text = _("Remove this bookmark?"), ok_text = _("Remove"), ok_callback = function() - bookmark:removeHighlight(item) + bookmark:removeItem(item) table.remove(item_table, item.idx) bm_menu:switchItemTable(nil, item_table, -1) UIManager:close(textviewer) @@ -567,7 +667,7 @@ function ReaderBookmark:onShowBookmark(match_table) end, }, { - text = bookmark:getBookmarkNote(item) and _("Edit note") or _("Add note"), + text = item.note and _("Edit note") or _("Add note"), enabled = not self.select_mode, callback = function() bookmark:setBookmarkNote(item) @@ -676,18 +776,17 @@ function ReaderBookmark:onShowBookmark(match_table) end, }, { - text = _("Reset"), - enabled = G_reader_settings:isFalse("bookmarks_items_auto_text") - and actions_enabled and not bookmark.ui.highlight.select_mode, + text = _("Delete note"), + enabled = actions_enabled, callback = function() UIManager:show(ConfirmBox:new{ - text = _("Reset page number / timestamp?"), - ok_text = _("Reset"), + text = _("Delete bookmark notes?"), + ok_text = _("Delete"), ok_callback = function() UIManager:close(bm_dialog) for _, v in ipairs(item_table) do if v.dim then - bookmark:removeBookmark(v, true) -- reset_auto_text_only=true + bookmark:deleteItemNote(v) end end bm_menu:onClose() @@ -716,7 +815,7 @@ function ReaderBookmark:onShowBookmark(match_table) UIManager:close(bm_dialog) for i = #item_table, 1, -1 do if item_table[i].dim then - bookmark:removeHighlight(item_table[i]) + bookmark:removeItem(item_table[i]) table.remove(item_table, i) end end @@ -811,7 +910,7 @@ function ReaderBookmark:onShowBookmark(match_table) callback = function() UIManager:close(bm_dialog) local _, idx = bookmark:getLatestBookmark() - idx = is_reverse_sorting and idx or #item_table - idx + 1 + idx = is_reverse_sorting and #item_table - idx + 1 or idx bm_menu:switchItemTable(nil, item_table, idx) bm_menu:onMenuHold(item_table[idx]) end, @@ -855,7 +954,6 @@ function ReaderBookmark:onShowBookmark(match_table) self.refresh = function() bm_menu:updateItems() - self:onSaveSettings() end bm_menu:switchItemTable(nil, item_table, curr_page_index_filtered) @@ -863,186 +961,55 @@ function ReaderBookmark:onShowBookmark(match_table) return true end -function ReaderBookmark:isBookmarkMatch(item, pn_or_xp) - if self.ui.paging then - return item.page == pn_or_xp - else - return self.ui.document:getPageFromXPointer(item.page) == self.ui.document:getPageFromXPointer(pn_or_xp) +function ReaderBookmark:getBookmarkItemText(item) + if item.type == "highlight" or self.items_text == "text" then + return self.display_prefix[item.type] .. item.text_orig end -end - -function ReaderBookmark:getDogearBookmarkIndex(pn_or_xp) - local _middle - local _start, _end = 1, #self.bookmarks - while _start <= _end do - _middle = math.floor((_start + _end)/2) - local v = self.bookmarks[_middle] - if not v.highlighted and self:isBookmarkMatch(v, pn_or_xp) then - return _middle - elseif self:isBookmarkInPageOrder({page = pn_or_xp}, v) then - _end = _middle - 1 - else - _start = _middle + 1 - end - end -end - -function ReaderBookmark:isBookmarkSame(item1, item2) - if item1.notes ~= item2.notes then return false end - if self.ui.paging then - return item1.pos0 and item1.pos1 and item2.pos0 and item2.pos1 - and item1.pos0.page == item2.pos0.page - and item1.pos0.x == item2.pos0.x and item1.pos0.y == item2.pos0.y - and item1.pos1.x == item2.pos1.x and item1.pos1.y == item2.pos1.y - else - return item1.page == item2.page - and item1.pos0 == item2.pos0 and item1.pos1 == item2.pos1 - end -end - -function ReaderBookmark:getBookmarkIndexFullScan(item) - for i, v in ipairs(self.bookmarks) do - if item.datetime == v.datetime then - return i - end + if item.type == "note" and self.items_text == "note" then + return self.display_prefix["note"] .. item.note end -end - -function ReaderBookmark:getBookmarkIndexBinarySearch(item) - local _start, _end, _middle = 1, #self.bookmarks - while _start <= _end do - _middle = bit.rshift(_start + _end, 1) - local v = self.bookmarks[_middle] - if item.datetime == v.datetime and item.page == v.page then - return _middle - elseif self:isBookmarkInPositionOrder(item, v) then - _end = _middle - 1 - else - _start = _middle + 1 - end + local text + if item.type == "bookmark" then + text = self.display_prefix["bookmark"] + else -- it is a note, but we show the "highlight" prefix before the highlighted text + text = self.display_prefix["highlight"] end -end - -function ReaderBookmark:getBookmarkInsertionIndexBinary(item) - local _start, _end, _middle, direction = 1, #self.bookmarks, 1, 0 - while _start <= _end do - _middle = bit.rshift(_start + _end, 1) - if self:isBookmarkInPositionOrder(item, self.bookmarks[_middle]) then - _end, direction = _middle - 1, 0 - else - _start, direction = _middle + 1, 1 - end - end - return _middle + direction -end - -function ReaderBookmark:addBookmark(item) - local index = self:getBookmarkInsertionIndexBinary(item) - table.insert(self.bookmarks, index, item) - self.ui:handleEvent(Event:new("BookmarkAdded", item)) - self.view.footer:onUpdateFooter(self.view.footer_visible) -end - -function ReaderBookmark:isBookmarkAdded(item) - -- binary search of sorted bookmarks (without check of datetime, for dictquicklookup) - local _middle - local _start, _end = 1, #self.bookmarks - while _start <= _end do - _middle = math.floor((_start + _end)/2) - if self:isBookmarkSame(item, self.bookmarks[_middle]) then - return true - end - if self:isBookmarkInPageOrder(item, self.bookmarks[_middle]) then - _end = _middle - 1 - else - _start = _middle + 1 - end - end - return false -end - -function ReaderBookmark:removeHighlight(item) - if item.pos0 then - self.ui:handleEvent(Event:new("Unhighlight", item)) - else -- dogear bookmark, update it in case we removed a bookmark for current page - self:removeBookmark(item) - self:setDogearVisibility(self:getCurrentPageNumber()) - end -end - -function ReaderBookmark:removeBookmark(item, reset_auto_text_only) - -- If we haven't found item in binary search, it may be because there are multiple - -- bookmarks on the same page, and the above binary search decided to - -- not search on one side of one it found on page, where item could be. - -- Fallback to do a full scan. - local index = self:getBookmarkIndexBinarySearch(item) or self:getBookmarkIndexFullScan(item) - local bookmark = self.bookmarks[index] - if reset_auto_text_only then - if self:isBookmarkAutoText(bookmark) then - bookmark.text = nil - end - else - local bookmark_type = item.type or self:getBookmarkType(bookmark) - if bookmark_type == "highlight" then - self.ui:handleEvent(Event:new("DelHighlight")) - elseif bookmark_type == "note" then - self.ui:handleEvent(Event:new("DelNote")) - end - self.ui:handleEvent(Event:new("BookmarkRemoved", bookmark)) - table.remove(self.bookmarks, index) - self.view.footer:onUpdateFooter(self.view.footer_visible) + if self.items_text == "all" or self.items_text == "note" then + text = text .. item.text_orig end -end - -function ReaderBookmark:updateBookmark(item) - -- Called from Highlights when changing highlight boundaries (positions). - -- Binary search cannot be used. - local index = self:getBookmarkIndexFullScan(item) - local v = self.bookmarks[index] - local bookmark_before = util.tableDeepCopy(v) - local is_auto_text_before = self:isBookmarkAutoText(v) - v.page = item.updated_highlight.pos0 - v.pos0 = item.updated_highlight.pos0 - v.pos1 = item.updated_highlight.pos1 - v.notes = item.updated_highlight.text - v.datetime = item.updated_highlight.datetime - v.chapter = item.updated_highlight.chapter - if is_auto_text_before then - v.text = self:getBookmarkAutoText(v) + if item.note then + text = text .. "\u{2002}" .. self.display_prefix["note"] .. item.note end - self.ui:handleEvent(Event:new("BookmarkUpdated", v, bookmark_before)) - self:onSaveSettings() + return text end -function ReaderBookmark._getDialogHeader(bookmark) - return T(_("Page: %1"), bookmark.mandatory) .. " " .. T(_("Time: %1"), bookmark.datetime) +function ReaderBookmark:_getDialogHeader(bookmark) + local page_str = bookmark.mandatory or self:getBookmarkPageString(bookmark.page) + return T(_("Page: %1"), page_str) .. " " .. T(_("Time: %1"), bookmark.datetime) end -function ReaderBookmark:setBookmarkNote(item, from_highlight, is_new_note, new_text) - local bookmark +function ReaderBookmark:setBookmarkNote(item_or_index, is_new_note, new_note) + local item, index + local from_highlight = type(item_or_index) == "number" if from_highlight then - local bm = self.bookmarks[self:getBookmarkIndexFullScan(item)] - if bm.text == nil or bm.text == "" then - bm.text = self:getBookmarkAutoText(bm) - end - bookmark = util.tableDeepCopy(bm) - bookmark.type = self:getBookmarkType(bookmark) - bookmark.text_orig = bm.text or bm.notes - bookmark.mandatory = self:getBookmarkPageString(bm.page) + index = item_or_index else - bookmark = item + item = item_or_index -- in item_table + index = (self.filtered_mode or self.show_edited_only) and self.ui.annotation:getItemIndex(item) or item.idx end - local input_text = self:getBookmarkNote(bookmark) and bookmark.text_orig or nil - if new_text then + local annotation = self.ui.annotation.annotations[index] + local type_before = item and item.type or self.getBookmarkType(annotation) + local input_text = annotation.note + if new_note then if input_text then - input_text = input_text .. "\n\n" .. new_text + input_text = input_text .. "\n\n" .. new_note else - input_text = new_text + input_text = new_note end end self.input = InputDialog:new{ title = _("Edit note"), - description = " " .. self._getDialogHeader(bookmark), + description = " " .. self:_getDialogHeader(annotation), input = input_text, allow_newline = true, add_scroll_buttons = true, @@ -1054,16 +1021,15 @@ function ReaderBookmark:setBookmarkNote(item, from_highlight, is_new_note, new_t id = "close", callback = function() UIManager:close(self.input) - if is_new_note then -- "Add note" cancelled, remove saved highlight - local index = self:getBookmarkIndexBinarySearch(bookmark) or self:getBookmarkIndexFullScan(bookmark) - self:removeHighlight(self.bookmarks[index]) + if is_new_note then -- "Add note" called from highlight dialog and cancelled, remove saved highlight + self:removeItemByIndex(index) end end, }, { - text = _("Paste"), -- insert highlighted text (auto-text) + text = _("Paste"), -- insert highlighted text callback = function() - self.input._input_widget:addChars(bookmark.text_orig) + self.input._input_widget:addChars(annotation.text) end, }, { @@ -1071,27 +1037,22 @@ function ReaderBookmark:setBookmarkNote(item, from_highlight, is_new_note, new_t is_enter_default = true, callback = function() local value = self.input:getInputText() - if value == "" then -- blank input resets the 'text' field to auto-text - value = self:getBookmarkAutoText(bookmark) + if value == "" then -- blank input deletes note + value = nil end - bookmark.text = value or bookmark.notes - local bookmark_type = bookmark.type - bookmark.type = self:getBookmarkType(bookmark) - if bookmark_type ~= bookmark.type then - if bookmark_type == "highlight" then - self.ui:handleEvent(Event:new("DelHighlight")) - self.ui:handleEvent(Event:new("AddNote")) + annotation.note = value + local type_after = self.getBookmarkType(annotation) + if type_before ~= type_after then + if type_before == "highlight" then + self.ui:handleEvent(Event:new("AnnotationsModified", + { annotation, nb_highlights_added = -1, nb_notes_added = 1 })) else - self.ui:handleEvent(Event:new("AddHighlight")) - self.ui:handleEvent(Event:new("DelNote")) + self.ui:handleEvent(Event:new("AnnotationsModified", + { annotation, nb_highlights_added = 1, nb_notes_added = -1 })) end end - local index = self:getBookmarkIndexBinarySearch(bookmark) or self:getBookmarkIndexFullScan(bookmark) - local bm = self.bookmarks[index] - bm.text = value - self.ui:handleEvent(Event:new("BookmarkEdited", bm)) - if bookmark.highlighted then - self.ui.highlight:writePdfAnnotation("content", bookmark.page, bookmark, bookmark.text) + if annotation.drawer then + self.ui.highlight:writePdfAnnotation("content", annotation, value) end UIManager:close(self.input) if from_highlight then @@ -1099,8 +1060,9 @@ function ReaderBookmark:setBookmarkNote(item, from_highlight, is_new_note, new_t UIManager:setDirty(self.dialog, "ui") -- refresh note marker end else - bookmark.text_orig = bookmark.text - bookmark.text = self.display_prefix[bookmark.type] .. bookmark.text + item.note = value + item.type = type_after + item.text = self:getBookmarkItemText(item) self.refresh() end end, @@ -1116,8 +1078,8 @@ function ReaderBookmark:editHighlightedText(item) local input_dialog input_dialog = InputDialog:new{ title = _("Edit highlighted text"), - description = " " .. self._getDialogHeader(item), - input = item.notes, + description = " " .. self:_getDialogHeader(item), + input = item.text_orig, allow_newline = true, add_scroll_buttons = true, use_available_height = true, @@ -1156,24 +1118,14 @@ function ReaderBookmark:setHighlightedText(item, text) text = self.ui.document:getTextFromPositions(item.pos0, item.pos1).text end end - -- highlight - local hl = self.ui.highlight:getHighlightByDatetime(item.datetime) - hl.text = text - hl.edited = edited - -- bookmark - local index = self:getBookmarkIndexBinarySearch(item) or self:getBookmarkIndexFullScan(item) - local bm = self.bookmarks[index] - local is_auto_text_before = self:isBookmarkAutoText(bm) - bm.notes = text - if is_auto_text_before then - bm.text = self:getBookmarkAutoText(bm) - end - bm.edited = edited + -- annotation + local index = (self.filtered_mode or self.show_edited_only) and self.ui.annotation:getItemIndex(item) or item.idx + self.ui.annotation.annotations[index].text = text + self.ui.annotation.annotations[index].text_edited = edited -- item table - item.notes = text - item.text_orig = bm.text or text - item.text = self.display_prefix[item.type] .. item.text_orig - item.edited = edited + item.text_orig = text + item.text = self.display_prefix[item.type] .. (item.note or text) + item.text_edited = edited if edited then self.refresh() end @@ -1276,7 +1228,7 @@ end function ReaderBookmark:filterByEditedText(bm_menu) self.show_edited_only = true for i = #bm_menu.item_table, 1, -1 do - if not bm_menu.item_table[i].edited then + if not bm_menu.item_table[i].text_edited then table.remove(bm_menu.item_table, i) end end @@ -1298,14 +1250,13 @@ end function ReaderBookmark:doesBookmarkMatchTable(item, match_table) if match_table.drawer then -- filter by highlight style - return item.highlighted - and match_table.drawer == self.ui.highlight:getHighlightByDatetime(item.datetime).drawer + return match_table.drawer == item.drawer end if match_table[item.type] then if match_table.search_str then - local text = item.notes - if item.text then -- search in the highlighted text and in the note - text = text .. "\u{FFFF}" .. item.text + local text = item.text_orig + if item.note then -- search in the highlighted text and in the note + text = text .. "\u{FFFF}" .. item.note end if not match_table.case_sensitive then text = Utf8Proc.lowercase(util.fixUtf8(text, "?")) @@ -1316,248 +1267,4 @@ function ReaderBookmark:doesBookmarkMatchTable(item, match_table) end end -function ReaderBookmark:toggleBookmark(pageno) - local pn_or_xp - if pageno then - if self.ui.rolling then - pn_or_xp = self.ui.document:getPageXPointer(pageno) - else - pn_or_xp = pageno - end - else - pn_or_xp = self:getCurrentPageNumber() - end - local index = self:getDogearBookmarkIndex(pn_or_xp) - if index then - self.ui:handleEvent(Event:new("BookmarkRemoved", self.bookmarks[index])) - table.remove(self.bookmarks, index) - else - -- build notes from TOC - local notes = self.ui.toc:getTocTitleByPage(pn_or_xp) - local chapter_name = notes - if notes ~= "" then - -- @translators In which chapter title (%1) a note is found. - notes = T(_("in %1"), notes) - end - self:addBookmark({ - page = pn_or_xp, - datetime = os.date("%Y-%m-%d %H:%M:%S"), - notes = notes, - chapter = chapter_name, - }) - end -end - -function ReaderBookmark:getPreviousBookmarkedPage(pn_or_xp) - logger.dbg("go to next bookmark from", pn_or_xp) - for i = 1, #self.bookmarks do - if self:isBookmarkInPageOrder({page = pn_or_xp}, self.bookmarks[i]) then - return self.bookmarks[i].page - end - end -end - -function ReaderBookmark:getNextBookmarkedPage(pn_or_xp) - logger.dbg("go to next bookmark from", pn_or_xp) - for i = #self.bookmarks, 1, -1 do - if self:isBookmarkInReversePageOrder({page = pn_or_xp}, self.bookmarks[i]) then - return self.bookmarks[i].page - end - end -end - -function ReaderBookmark:getPreviousBookmarkedPageFromPage(pn_or_xp) - logger.dbg("go to next bookmark from", pn_or_xp) - for i = 1, #self.bookmarks do - if self:isBookmarkPageInPageOrder(pn_or_xp, self.bookmarks[i]) then - return self.bookmarks[i].page - end - end -end - -function ReaderBookmark:getNextBookmarkedPageFromPage(pn_or_xp) - logger.dbg("go to next bookmark from", pn_or_xp) - for i = #self.bookmarks, 1, -1 do - if self:isBookmarkPageInReversePageOrder(pn_or_xp, self.bookmarks[i]) then - return self.bookmarks[i].page - end - end -end - -function ReaderBookmark:getFirstBookmarkedPageFromPage(pn_or_xp) - if #self.bookmarks > 0 then - local first = #self.bookmarks - if self:isBookmarkPageInPageOrder(pn_or_xp, self.bookmarks[first]) then - return self.bookmarks[first].page - end - end -end - -function ReaderBookmark:getLastBookmarkedPageFromPage(pn_or_xp) - if #self.bookmarks > 0 then - local last = 1 - if self:isBookmarkPageInReversePageOrder(pn_or_xp, self.bookmarks[last]) then - return self.bookmarks[last].page - end - end -end - -function ReaderBookmark:onGotoPreviousBookmark(pn_or_xp) - self:gotoBookmark(self:getPreviousBookmarkedPage(pn_or_xp)) - return true -end - -function ReaderBookmark:onGotoNextBookmark(pn_or_xp) - self:gotoBookmark(self:getNextBookmarkedPage(pn_or_xp)) - return true -end - -function ReaderBookmark:onGotoNextBookmarkFromPage(add_current_location_to_stack) - if add_current_location_to_stack ~= false then -- nil or true - self.ui.link:addCurrentLocationToStack() - end - self:gotoBookmark(self:getNextBookmarkedPageFromPage(self.ui:getCurrentPage())) - return true -end - -function ReaderBookmark:onGotoPreviousBookmarkFromPage(add_current_location_to_stack) - if add_current_location_to_stack ~= false then -- nil or true - self.ui.link:addCurrentLocationToStack() - end - self:gotoBookmark(self:getPreviousBookmarkedPageFromPage(self.ui:getCurrentPage())) - return true -end - -function ReaderBookmark:onGotoFirstBookmark(add_current_location_to_stack) - if add_current_location_to_stack ~= false then -- nil or true - self.ui.link:addCurrentLocationToStack() - end - self:gotoBookmark(self:getFirstBookmarkedPageFromPage(self.ui:getCurrentPage())) - return true -end - -function ReaderBookmark:onGotoLastBookmark(add_current_location_to_stack) - if add_current_location_to_stack ~= false then -- nil or true - self.ui.link:addCurrentLocationToStack() - end - self:gotoBookmark(self:getLastBookmarkedPageFromPage(self.ui:getCurrentPage())) - return true -end - -function ReaderBookmark:getLatestBookmark() - local latest_bookmark, latest_bookmark_idx - local latest_bookmark_datetime = "0" - for i, v in ipairs(self.bookmarks) do - if v.datetime > latest_bookmark_datetime then - latest_bookmark_datetime = v.datetime - latest_bookmark = v - latest_bookmark_idx = i - end - end - return latest_bookmark, latest_bookmark_idx -end - -function ReaderBookmark:hasBookmarks() - return self.bookmarks and #self.bookmarks > 0 -end - -function ReaderBookmark:getNumberOfBookmarks() - return self.bookmarks and #self.bookmarks or 0 -end - -function ReaderBookmark:getNumberOfHighlightsAndNotes() -- for Statistics plugin - local highlights = 0 - local notes = 0 - for _, v in ipairs(self.bookmarks) do - local bm_type = self:getBookmarkType(v) - if bm_type == "highlight" then - highlights = highlights + 1 - elseif bm_type == "note" then - notes = notes + 1 - end - end - return highlights, notes -end - -function ReaderBookmark:getCurrentPageNumber() - return self.ui.paging and self.view.state.page or self.ui.document:getXPointer() -end - -function ReaderBookmark:getBookmarkPageNumber(bookmark) - return self.ui.paging and bookmark.page or self.ui.document:getPageFromXPointer(bookmark.page) -end - -function ReaderBookmark:getBookmarkType(bookmark) - if bookmark.highlighted then - if self:isBookmarkAutoText(bookmark) then - return "highlight" - else - return "note" - end - else - return "bookmark" - end -end - -function ReaderBookmark:getBookmarkPageString(page) - if self.ui.rolling then - if self.ui.pagemap and self.ui.pagemap:wantsPageLabels() then - page = self.ui.pagemap:getXPointerPageLabel(page, true) - else - page = self.ui.document:getPageFromXPointer(page) - if self.ui.document:hasHiddenFlows() then - local flow = self.ui.document:getPageFlow(page) - page = self.ui.document:getPageNumberInFlow(page) - if flow > 0 then - page = T("[%1]%2", page, flow) - end - end - end - end - return tostring(page) -end - -function ReaderBookmark:getBookmarkedPages() - local pages = {} - for _, bm in ipairs(self.bookmarks) do - local page = self:getBookmarkPageNumber(bm) - local btype = self:getBookmarkType(bm) - if not pages[page] then - pages[page] = {} - end - if not pages[page][btype] then - pages[page][btype] = true - end - end - return pages -end - -function ReaderBookmark:getBookmarkAutoText(bookmark, force_auto_text) - if G_reader_settings:nilOrTrue("bookmarks_items_auto_text") or force_auto_text then - local page = self:getBookmarkPageString(bookmark.page) - return T(_("Page %1 %2 @ %3"), page, bookmark.notes, bookmark.datetime) - else - -- When not auto_text, and 'text' would be identical to 'notes', leave 'text' be nil - return nil - end -end - ---- Check if the 'text' field has not been edited manually -function ReaderBookmark:isBookmarkAutoText(bookmark) - return (bookmark.text == nil) or (bookmark.text == "") or (bookmark.text == bookmark.notes) - or (bookmark.text == self:getBookmarkAutoText(bookmark, true)) -end - -function ReaderBookmark:getBookmarkNote(item) - for _, bm in ipairs(self.bookmarks) do - if item.datetime == bm.datetime then - return not self:isBookmarkAutoText(bm) and bm.text - end - end -end - -function ReaderBookmark:getBookmarkForHighlight(item) - return self.bookmarks[self:getBookmarkIndexFullScan(item)] -end - return ReaderBookmark diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index 0a9fcd5e5..f23507ac6 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -219,7 +219,7 @@ local footerTextGeneratorMap = { bookmark_count = function(footer) local symbol_type = footer.settings.item_prefix local prefix = symbol_prefix[symbol_type].bookmark_count - local bookmark_count = footer.ui.bookmark:getNumberOfBookmarks() + local bookmark_count = footer.ui.annotation:getNumberOfAnnotations() if footer.settings.all_at_once and footer.settings.hide_empty_generators and bookmark_count == 0 then return "" end diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index e80998717..77579c401 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -7,6 +7,7 @@ local Geom = require("ui/geometry") local InfoMessage = require("ui/widget/infomessage") local InputContainer = require("ui/widget/container/inputcontainer") local Notification = require("ui/widget/notification") +local RadioButtonWidget = require("ui/widget/radiobuttonwidget") local TextViewer = require("ui/widget/textviewer") local Translator = require("ui/translator") local UIManager = require("ui/uimanager") @@ -73,11 +74,11 @@ function ReaderHighlight:init() ["02_highlight"] = function(this) return { text = _("Highlight"), + enabled = this.hold_pos ~= nil, callback = function() this:saveHighlight(true) this:onClose() end, - enabled = this.hold_pos ~= nil, } end, ["03_copy"] = function(this) @@ -96,11 +97,11 @@ function ReaderHighlight:init() ["04_add_note"] = function(this) return { text = _("Add note"), + enabled = this.hold_pos ~= nil, callback = function() this:addNote() this:onClose() end, - enabled = this.hold_pos ~= nil, } end, -- then information lookup functions, putting on the left those that @@ -127,11 +128,11 @@ function ReaderHighlight:init() end, } end, - ["07_translate"] = function(this, page, index) + ["07_translate"] = function(this, index) return { text = _("Translate"), callback = function() - this:translate(this.selected_text, page, index) + this:translate(index) -- We don't call this:onClose(), so one can still see -- the highlighted text when moving the translated -- text window, and also if NetworkMgr:promptWifiOn() @@ -429,7 +430,7 @@ function ReaderHighlight:addToMainMenu(menu_items) self.view.highlight.lighten_factor = spin.value UIManager:setDirty(self.dialog, "ui") if touchmenu_instance then touchmenu_instance:updateItems() end - end + end, } UIManager:show(spin_widget) end, @@ -455,7 +456,7 @@ function ReaderHighlight:addToMainMenu(menu_items) }, }) end - UIManager:show(require("ui/widget/radiobuttonwidget"):new{ + UIManager:show(RadioButtonWidget:new{ title_text = _("Note marker"), width_factor = 0.5, keep_shown_on_apply = true, @@ -475,7 +476,7 @@ function ReaderHighlight:addToMainMenu(menu_items) }) end, }) - if self.document.info.has_pages then + if self.ui.paging then menu_items.panel_zoom_options = { text = _("Panel zoom (manga/comic)"), sub_item_table = self:genPanelZoomMenu(), @@ -563,7 +564,7 @@ function ReaderHighlight:addToMainMenu(menu_items) callback = function(spin) G_reader_settings:saveSetting("highlight_long_hold_threshold_s", spin.value) if touchmenu_instance then touchmenu_instance:updateItems() end - end + end, } UIManager:show(items) end, @@ -671,6 +672,7 @@ function ReaderHighlight:clear(clear_id) end self.is_word_selection = false self.selected_text_start_xpointer = nil + self.gest_pos = nil if self.hold_pos then self.hold_pos = nil self.selected_text = nil @@ -693,8 +695,8 @@ function ReaderHighlight:onTapSelectModeIcon() cancel_text = _("Close"), ok_callback = function() self.select_mode = false - self:deleteHighlight(self.highlight_page, self.highlight_idx) - end + self:deleteHighlight(self.highlight_idx) + end, }) return true end @@ -706,7 +708,7 @@ function ReaderHighlight:onTap(_, ges) -- ReaderHighlight:clear can only return true if self.hold_pos was set anyway. local cleared = self.hold_pos and self:clear() -- We only care about potential taps on existing highlights, not on taps that closed a highlight menu. - if not cleared and ges then + if not cleared and ges and self.ui.annotation:hasAnnotations() then if self.ui.paging then return self:onTapPageSavedHighlight(ges) else @@ -720,31 +722,24 @@ function ReaderHighlight:onTapPageSavedHighlight(ges) local pos = self.view:screenToPageTransform(ges.pos) local highlights_tapped = {} for _, page in ipairs(pages) do - local items = self.view:getPageSavedHighlights(page) - if items then - for i, item in ipairs(items) do - local boxes = self.ui.document:getPageBoxesFromPositions(page, item.pos0, item.pos1) - if boxes then - for _, box in ipairs(boxes) do - if inside_box(pos, box) then - logger.dbg("Tap on highlight") - local hl_page, hl_i - if item.parent then -- multi-page highlight - hl_page, hl_i = unpack(item.parent) - else - hl_page, hl_i = page, i - end - if self.select_mode then - if hl_page == self.highlight_page and hl_i == self.highlight_idx then - -- tap on the first fragment: abort select mode, clear highlight - self.select_mode = false - self:deleteHighlight(hl_page, hl_i) - return true - end - else - table.insert(highlights_tapped, {hl_page, hl_i}) - break + local items = self:getPageSavedHighlights(page) + for i, item in ipairs(items) do + local boxes = self.ui.document:getPageBoxesFromPositions(page, item.pos0, item.pos1) + if boxes then + for __, box in ipairs(boxes) do + if inside_box(pos, box) then + logger.dbg("Tap on highlight") + local hl_i = item.parent or i -- parent exists in multi-page highlight only + if self.select_mode then + if hl_i == self.highlight_idx then + -- tap on the first fragment: abort select mode, clear highlight + self.select_mode = false + self:deleteHighlight(hl_i) + return true end + else + table.insert(highlights_tapped, hl_i) + break end end end @@ -761,7 +756,6 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges) -- showing menu...). We might want to cache these boxes per page (and -- clear that cache when page layout change or highlights are added -- or removed). - local cur_view_top, cur_view_bottom local pos = self.view:screenToPageTransform(ges.pos) -- NOTE: By now, pos.page is set, but if a highlight spans across multiple pages, -- it's stored under the hash of its *starting* point, @@ -770,46 +764,39 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges) -- because pos.page isn't super accurate in continuous mode -- (it's the page number for what's it the topleft corner of the screen, -- i.e., often a bit earlier)... + -- Even in page mode, it's safer to use pos and ui.dimen.h + -- than pages' xpointers pos, even if ui.dimen.h is a bit + -- larger than pages' heights + local cur_view_top = self.document:getCurrentPos() + local cur_view_bottom + if self.view.view_mode == "page" and self.document:getVisiblePageCount() > 1 then + cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h + else + cur_view_bottom = cur_view_top + self.ui.dimen.h + end local highlights_tapped = {} - for page, items in pairs(self.view.highlight.saved) do - if items then - for i = 1, #items do - local item = items[i] - local pos0, pos1 = item.pos0, item.pos1 - -- document:getScreenBoxesFromPositions() is expensive, so we - -- first check this item is on current page - if not cur_view_top then - -- Even in page mode, it's safer to use pos and ui.dimen.h - -- than pages' xpointers pos, even if ui.dimen.h is a bit - -- larger than pages' heights - cur_view_top = self.ui.document:getCurrentPos() - if self.view.view_mode == "page" and self.ui.document:getVisiblePageCount() > 1 then - cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h - else - cur_view_bottom = cur_view_top + self.ui.dimen.h - end - end - local spos0 = self.ui.document:getPosFromXPointer(pos0) - local spos1 = self.ui.document:getPosFromXPointer(pos1) - local start_pos = math.min(spos0, spos1) - local end_pos = math.max(spos0, spos1) - if start_pos <= cur_view_bottom and end_pos >= cur_view_top then - local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true - if boxes then - for index, box in pairs(boxes) do - if inside_box(pos, box) then - logger.dbg("Tap on highlight") - if self.select_mode then - if page == self.highlight_page and i == self.highlight_idx then - -- tap on the first fragment: abort select mode, clear highlight - self.select_mode = false - self:deleteHighlight(page, i) - return true - end - else - table.insert(highlights_tapped, {page, i}) - break + for hl_i, item in ipairs(self.ui.annotation.annotations) do + if item.drawer then + -- document:getScreenBoxesFromPositions() is expensive, so we + -- first check this item is on current page + local start_pos = self.document:getPosFromXPointer(item.pos0) + local end_pos = self.document:getPosFromXPointer(item.pos1) + if start_pos <= cur_view_bottom and end_pos >= cur_view_top then + local boxes = self.ui.document:getScreenBoxesFromPositions(item.pos0, item.pos1, true) -- get_segments=true + if boxes then + for _, box in ipairs(boxes) do + if inside_box(pos, box) then + logger.dbg("Tap on highlight") + if self.select_mode then + if hl_i == self.highlight_idx then + -- tap on the first fragment: abort select mode, clear highlight + self.select_mode = false + self:deleteHighlight(hl_i) + return true end + else + table.insert(highlights_tapped, hl_i) + break end end end @@ -822,10 +809,9 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges) end end -function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_char) - if self.ui.paging then return end - local highlight = self.view.highlight.saved[page][index] - local highlight_time = highlight.datetime +function ReaderHighlight:updateHighlight(index, side, direction, move_by_char) + local highlight = self.ui.annotation.annotations[index] + local highlight_before = util.tableDeepCopy(highlight) local highlight_beginning = highlight.pos0 local highlight_end = highlight.pos1 if side == 0 then -- we move pos0 @@ -846,7 +832,10 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c if updated_highlight_beginning then local order = self.ui.document:compareXPointers(updated_highlight_beginning, highlight_end) if order and order > 0 then -- only if beginning did not go past end - self.view.highlight.saved[page][index].pos0 = updated_highlight_beginning + highlight.pos0 = updated_highlight_beginning + highlight.page = updated_highlight_beginning + highlight.chapter = self.ui.toc:getTocTitleByPage(updated_highlight_beginning) + highlight.pageno = self.document:getPageFromXPointer(updated_highlight_beginning) end end else -- we move pos1 @@ -867,23 +856,16 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c if updated_highlight_end then local order = self.ui.document:compareXPointers(highlight_beginning, updated_highlight_end) if order and order > 0 then -- only if end did not go back past beginning - self.view.highlight.saved[page][index].pos1 = updated_highlight_end + highlight.pos1 = updated_highlight_end end end end - local new_beginning = self.view.highlight.saved[page][index].pos0 - local new_end = self.view.highlight.saved[page][index].pos1 + local new_beginning = highlight.pos0 + local new_end = highlight.pos1 local new_text = self.ui.document:getTextFromXPointers(new_beginning, new_end) - local new_chapter = self.ui.toc:getTocTitleByPage(new_beginning) - self.view.highlight.saved[page][index].text = cleanupSelectedText(new_text) - self.view.highlight.saved[page][index].chapter = new_chapter - local new_highlight = self.view.highlight.saved[page][index] - self.ui.bookmark:updateBookmark({ - page = highlight_beginning, - datetime = highlight_time, - updated_highlight = new_highlight - }) + highlight.text = cleanupSelectedText(new_text) + self.ui:handleEvent(Event:new("AnnotationsModified", { highlight, highlight_before })) if side == 0 then -- Ensure we show the page with the new beginning of highlight if not self.ui.document:isXPointerInCurrentPage(new_beginning) then @@ -909,20 +891,15 @@ end function ReaderHighlight:showChooseHighlightDialog(highlights) if #highlights == 1 then - local page, index = unpack(highlights[1]) - local item = self.view.highlight.saved[page][index] - local bookmark_note = self.ui.bookmark:getBookmarkNote({datetime = item.datetime}) - self:showHighlightNoteOrDialog(page, index, bookmark_note) + self:showHighlightNoteOrDialog(highlights[1]) else -- overlapped highlights local dialog local buttons = {} - for i, v in ipairs(highlights) do - local page, index = unpack(v) - local item = self.view.highlight.saved[page][index] - local bookmark_note = self.ui.bookmark:getBookmarkNote({datetime = item.datetime}) + for i, index in ipairs(highlights) do + local item = self.ui.annotation.annotations[index] buttons[i] = {{ - text = (bookmark_note and self.ui.bookmark.display_prefix["note"] - or self.ui.bookmark.display_prefix["highlight"]) .. item.text, + text = (item.note and self.ui.bookmark.display_prefix["note"] + or self.ui.bookmark.display_prefix["highlight"]) .. item.text, align = "left", avoid_text_truncation = false, font_face = "smallinfofont", @@ -930,7 +907,7 @@ function ReaderHighlight:showChooseHighlightDialog(highlights) font_bold = false, callback = function() UIManager:close(dialog) - self:showHighlightNoteOrDialog(page, index, bookmark_note) + self:showHighlightNoteOrDialog(index) end, }} end @@ -942,7 +919,8 @@ function ReaderHighlight:showChooseHighlightDialog(highlights) return true end -function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note) +function ReaderHighlight:showHighlightNoteOrDialog(index) + local bookmark_note = self.ui.annotation.annotations[index].note if bookmark_note then local textviewer textviewer = TextViewer:new{ @@ -957,14 +935,14 @@ function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note) text = _("Delete highlight"), callback = function() UIManager:close(textviewer) - self:deleteHighlight(page, index) + self:deleteHighlight(index) end, }, { text = _("Edit highlight"), callback = function() UIManager:close(textviewer) - self:onShowHighlightDialog(page, index, false) + self:onShowHighlightDialog(index) end, }, }, @@ -972,17 +950,18 @@ function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note) } UIManager:show(textviewer) else - self:onShowHighlightDialog(page, index, true) + self:onShowHighlightDialog(index) end end -function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) +function ReaderHighlight:onShowHighlightDialog(index) + local item = self.ui.annotation.annotations[index] local buttons = { { { text = _("Delete"), callback = function() - self:deleteHighlight(page, index) + self:deleteHighlight(index) UIManager:close(self.edit_highlight_dialog) self.edit_highlight_dialog = nil end, @@ -990,15 +969,15 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) { text = C_("Highlight", "Style"), callback = function() - self:editHighlightStyle(page, index) + self:editHighlightStyle(index) UIManager:close(self.edit_highlight_dialog) self.edit_highlight_dialog = nil end, }, { - text = is_auto_text and _("Add note") or _("Edit note"), + text = item.note and _("Edit note") or _("Add note"), callback = function() - self:editHighlight(page, index) + self:editHighlight(index) UIManager:close(self.edit_highlight_dialog) self.edit_highlight_dialog = nil end, @@ -1006,8 +985,8 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) { text = "…", callback = function() - self.selected_text = self.view.highlight.saved[page][index] - self:onShowHighlightMenu(page, index) + self.selected_text = util.tableDeepCopy(item) + self:onShowHighlightMenu(index) UIManager:close(self.edit_highlight_dialog) self.edit_highlight_dialog = nil end, @@ -1016,7 +995,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) } if self.ui.rolling then - local enabled = not self.view.highlight.saved[page][index].edited + local enabled = not item.text_edited local start_prev = "◁▒▒" local start_next = "▷▒▒" local end_prev = "▒▒◁" @@ -1031,10 +1010,10 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) text = start_prev, enabled = enabled, callback = function() - self:updateHighlight(page, index, 0, -1, false) + self:updateHighlight(index, 0, -1, false) end, hold_callback = function() - self:updateHighlight(page, index, 0, -1, true) + self:updateHighlight(index, 0, -1, true) return true end }, @@ -1042,10 +1021,10 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) text = start_next, enabled = enabled, callback = function() - self:updateHighlight(page, index, 0, 1, false) + self:updateHighlight(index, 0, 1, false) end, hold_callback = function() - self:updateHighlight(page, index, 0, 1, true) + self:updateHighlight(index, 0, 1, true) return true end }, @@ -1053,26 +1032,26 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) text = end_prev, enabled = enabled, callback = function() - self:updateHighlight(page, index, 1, -1, false) + self:updateHighlight(index, 1, -1, false) end, hold_callback = function() - self:updateHighlight(page, index, 1, -1, true) + self:updateHighlight(index, 1, -1, true) end }, { text = end_next, enabled = enabled, callback = function() - self:updateHighlight(page, index, 1, 1, false) + self:updateHighlight(index, 1, 1, false) end, hold_callback = function() - self:updateHighlight(page, index, 1, 1, true) + self:updateHighlight(index, 1, 1, true) end } }) end self.edit_highlight_dialog = ButtonDialog:new{ - buttons = buttons + buttons = buttons, } UIManager:show(self.edit_highlight_dialog) return true @@ -1090,7 +1069,7 @@ function ReaderHighlight:removeFromHighlightDialog(idx) return button end -function ReaderHighlight:onShowHighlightMenu(page, index) +function ReaderHighlight:onShowHighlightMenu(index) if not self.selected_text then return end @@ -1099,7 +1078,7 @@ function ReaderHighlight:onShowHighlightMenu(page, index) local columns = 2 for idx, fn_button in ffiUtil.orderedPairs(self._highlight_buttons) do - local button = fn_button(self, page, index) + local button = fn_button(self, index) if not button.show_in_highlight_dialog_func or button.show_in_highlight_dialog_func() then if #highlight_buttons[#highlight_buttons] >= columns then table.insert(highlight_buttons, {}) @@ -1198,12 +1177,12 @@ function ReaderHighlight:_resetHoldTimer(clear) end function ReaderHighlight:onTogglePanelZoomSetting(arg, ges) - if not self.document.info.has_pages then return end + if self.ui.rolling then return end self.panel_zoom_enabled = not self.panel_zoom_enabled end function ReaderHighlight:onToggleFallbackTextSelection(arg, ges) - if not self.document.info.has_pages then return end + if self.ui.rolling then return end self.panel_zoom_fallback_to_text_selection = not self.panel_zoom_fallback_to_text_selection end @@ -1229,7 +1208,7 @@ function ReaderHighlight:onPanelZoom(arg, ges) end function ReaderHighlight:onHold(arg, ges) - if self.document.info.has_pages and self.panel_zoom_enabled then + if self.ui.paging and self.panel_zoom_enabled then local res = self:onPanelZoom(arg, ges) if res or not self.panel_zoom_fallback_to_text_selection then return res @@ -1548,20 +1527,20 @@ function ReaderHighlight:viewSelectionHTML(debug_view, no_css_files_buttons) end end -function ReaderHighlight:translate(selected_text, page, index) +function ReaderHighlight:translate(index) if self.ui.rolling then -- Extend the selected text to include any punctuation at start or end, -- which may give a better translation with the added context. - local extended_text = self.ui.document:extendXPointersToSentenceSegment(selected_text.pos0, selected_text.pos1) + local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1) if extended_text then - selected_text = extended_text + self.selected_text = extended_text end end - if #selected_text.text > 0 then - self:onTranslateText(selected_text.text, page, index) + if #self.selected_text.text > 0 then + self:onTranslateText(self.selected_text.text, index) -- or we will do OCR elseif self.hold_pos then - local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text) + local text = self.ui.document:getOCRText(self.hold_pos.page, self.selected_text) logger.dbg("OCRed text:", text) if text and text ~= "" then self:onTranslateText(text) @@ -1572,14 +1551,9 @@ function ReaderHighlight:translate(selected_text, page, index) end end end -dbg:guard(ReaderHighlight, "translate", - function(self, selected_text) - assert(selected_text ~= nil, - "translate must not be called with nil selected_text!") - end) -function ReaderHighlight:onTranslateText(text, page, index) - Translator:showTranslation(text, true, nil, nil, true, page, index) +function ReaderHighlight:onTranslateText(text, index) + Translator:showTranslation(text, true, nil, nil, true, index) end function ReaderHighlight:onTranslateCurrentPage() @@ -1661,7 +1635,7 @@ function ReaderHighlight:onHoldRelease() self:addNote() self:onClose() elseif default_highlight_action == "translate" then - self:translate(self.selected_text) + self:translate() elseif default_highlight_action == "wikipedia" then self:lookupWikipedia() self:onClose() @@ -1751,137 +1725,45 @@ function ReaderHighlight:highlightFromHoldPos() end end -function ReaderHighlight:onHighlight() - self:saveHighlight() -end - -function ReaderHighlight:onUnhighlight(bookmark_item) - local page - local sel_text - local sel_pos0 - local datetime - local idx - if bookmark_item then -- called from Bookmarks menu onHold - page = bookmark_item.page - sel_text = bookmark_item.notes - sel_pos0 = bookmark_item.pos0 - datetime = bookmark_item.datetime - else -- called from DictQuickLookup Unhighlight button - --- @fixme: is this self.hold_pos access safe? - page = self.hold_pos.page - sel_text = cleanupSelectedText(self.selected_text.text) - sel_pos0 = self.selected_text.pos0 - end - if self.ui.paging then -- We can safely use page - -- As we may have changed spaces and hyphens handling in the extracted - -- text over the years, check text identities with them removed - local sel_text_cleaned = sel_text:gsub("[ -]", ""):gsub("\u{00AD}", "") - for index = 1, #self.view.highlight.saved[page] do - local highlight = self.view.highlight.saved[page][index] - -- pos0 are tables and can't be compared directly, except when from - -- DictQuickLookup where these are the same object. - -- If bookmark_item provided, just check datetime - if ( (datetime == nil and highlight.pos0 == sel_pos0) or - (datetime ~= nil and highlight.datetime == datetime) ) then - if highlight.text:gsub("[ -]", ""):gsub("\u{00AD}", "") == sel_text_cleaned then - idx = index - break - end - end - end - else -- page is a xpointer - -- The original page could be found in bookmark_item.text, but - -- no more if it has been renamed: we need to loop through all - -- highlights on all page slots - for p, highlights in pairs(self.view.highlight.saved) do - for index = 1, #highlights do - local highlight = highlights[index] - -- pos0 are strings and can be compared directly - if highlight.text == sel_text and ( - (datetime == nil and highlight.pos0 == sel_pos0) or - (datetime ~= nil and highlight.datetime == datetime)) then - page = p -- this is the original page slot - idx = index - break - end - end - if idx then - break - end - end - end - if idx then - logger.dbg("found highlight to delete on page", page, idx) - self:deleteHighlight(page, idx, bookmark_item) - return true - end -end - -function ReaderHighlight:getHighlightByDatetime(datetime) - for page, highlights in pairs(self.view.highlight.saved) do - for _, highlight in ipairs(highlights) do - if highlight.datetime == datetime then - return highlight - end - end - end -end - -function ReaderHighlight:getHighlightBookmarkItem() +function ReaderHighlight:saveHighlight(extend_to_sentence) + logger.dbg("save highlight") if self.hold_pos and not self.selected_text then self:highlightFromHoldPos() end if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then - return { - page = self.ui.paging and self.selected_text.pos0.page or self.selected_text.pos0, - pos0 = self.selected_text.pos0, - pos1 = self.selected_text.pos1, - notes = cleanupSelectedText(self.selected_text.text), - highlighted = true, - } - end -end - -function ReaderHighlight:saveHighlight(extend_to_sentence) - self.ui:handleEvent(Event:new("AddHighlight")) - logger.dbg("save highlight") - if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then - if extend_to_sentence and self.ui.rolling then - local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1) - if extended_text then - self.selected_text = extended_text + local pg_or_xp + if self.ui.rolling then + if extend_to_sentence then + local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1) + if extended_text then + self.selected_text = extended_text + end end + pg_or_xp = self.selected_text.pos0 + else + pg_or_xp = self.selected_text.pos0.page end - local page = self.ui.paging and self.selected_text.pos0.page or self.ui.document:getPageFromXPointer(self.selected_text.pos0) - if not self.view.highlight.saved[page] then - self.view.highlight.saved[page] = {} - end - local datetime = os.date("%Y-%m-%d %H:%M:%S") - local pg_or_xp = self.ui.paging and page or self.selected_text.pos0 - local chapter_name = self.ui.toc:getTocTitleByPage(pg_or_xp) - local hl_item = { - datetime = datetime, - text = cleanupSelectedText(self.selected_text.text), + local item = { + page = self.ui.paging and self.selected_text.pos0.page or self.selected_text.pos0, pos0 = self.selected_text.pos0, pos1 = self.selected_text.pos1, - pboxes = self.selected_text.pboxes, - ext = self.selected_text.ext, + text = cleanupSelectedText(self.selected_text.text), drawer = self.view.highlight.saved_drawer, - chapter = chapter_name, + chapter = self.ui.toc:getTocTitleByPage(pg_or_xp), } - table.insert(self.view.highlight.saved[page], hl_item) - local bookmark_item = self:getHighlightBookmarkItem() - if bookmark_item then - bookmark_item.datetime = datetime - bookmark_item.chapter = chapter_name - self.ui.bookmark:addBookmark(bookmark_item) + if self.ui.paging then + item.pboxes = self.selected_text.pboxes + item.ext = self.selected_text.ext + self:writePdfAnnotation("save", item) end - self:writePdfAnnotation("save", page, hl_item) - return page, #self.view.highlight.saved[page] + local index = self.ui.annotation:addItem(item) + self.view.footer:onUpdateFooter(self.view.footer_visible) + self.ui:handleEvent(Event:new("AnnotationsModified", { item, nb_highlights_added = 1 })) + return index end end -function ReaderHighlight:writePdfAnnotation(action, page, item, content) +function ReaderHighlight:writePdfAnnotation(action, item, content) if self.ui.rolling or G_reader_settings:readSetting("save_document") == "disable" then return end @@ -1897,24 +1779,10 @@ function ReaderHighlight:writePdfAnnotation(action, page, item, content) end local can_write if item.pos0.page == item.pos1.page then -- single-page highlight - local item_ - if item.pboxes then - item_ = item - else -- called from bookmarks to write bookmark note to annotation - for _, hl in ipairs(self.view.highlight.saved[page]) do - if hl.datetime == item.datetime then - item_ = {pboxes = hl.pboxes} - break - end - end - end - can_write = doAction(action, page, item_, content) + can_write = doAction(action, item.pos0.page, item, content) else -- multi-page highlight - local is_reflow = self.ui.document.configurable.text_wrap for hl_page = item.pos0.page, item.pos1.page do - self.ui.document.configurable.text_wrap = 0 local hl_part = self:getSavedExtendedHighlightPage(item, hl_page) - self.ui.document.configurable.text_wrap = is_reflow can_write = doAction(action, hl_page, hl_part, content) if can_write == false then break end if action == "save" then -- update pboxes from quadpoints @@ -1934,14 +1802,6 @@ If you wish your highlights to be saved in the document, just move it to a writa end end -function ReaderHighlight:addNote(text) - local page, index = self:saveHighlight(true) - if text then self:clear() end - self:editHighlight(page, index, true, text) - UIManager:close(self.edit_highlight_dialog) - self.edit_highlight_dialog = nil -end - function ReaderHighlight:lookupWikipedia() if self.selected_text then self.ui:handleEvent(Event:new("LookupWikipedia", cleanupSelectedText(self.selected_text.text))) @@ -1970,59 +1830,48 @@ function ReaderHighlight:onHighlightDictLookup() end end -function ReaderHighlight:deleteHighlight(page, i, bookmark_item) - logger.dbg("delete highlight", page, i) - -- The per-page table is a pure array - local removed = table.remove(self.view.highlight.saved[page], i) - -- But the main, outer table is a hash, so clear the table for this page if there are no longer any highlights on it - if #self.view.highlight.saved[page] == 0 then - self.view.highlight.saved[page] = nil - end - if bookmark_item then - self.ui.bookmark:removeBookmark(bookmark_item) - else - self.ui.bookmark:removeBookmark({ - page = self.ui.paging and page or removed.pos0, - datetime = removed.datetime, - }) - end - self:writePdfAnnotation("delete", page, removed) +function ReaderHighlight:deleteHighlight(index) + logger.dbg("delete highlight", index) + local item = self.ui.annotation.annotations[index] + self:writePdfAnnotation("delete", item) + self.ui.bookmark:removeItemByIndex(index) UIManager:setDirty(self.dialog, "ui") end -function ReaderHighlight:editHighlight(page, i, is_new_note, text) - local item = self.view.highlight.saved[page][i] - self.ui.bookmark:setBookmarkNote({ - page = self.ui.paging and page or item.pos0, - datetime = item.datetime, - }, true, is_new_note, text) +function ReaderHighlight:addNote(text) + local index = self:saveHighlight(true) + if text then -- called from Translator to save translation to note + self:clear() + end + self:editHighlight(index, true, text) + UIManager:close(self.edit_highlight_dialog) + self.edit_highlight_dialog = nil +end + +function ReaderHighlight:editHighlight(index, is_new_note, text) + self.ui.bookmark:setBookmarkNote(index, is_new_note, text) end -function ReaderHighlight:editHighlightStyle(page, i) - local item = self.view.highlight.saved[page][i] +function ReaderHighlight:editHighlightStyle(index) + local item = self.ui.annotation.annotations[index] local apply_drawer = function(drawer) - self:writePdfAnnotation("delete", page, item) + self:writePdfAnnotation("delete", item) item.drawer = drawer if self.ui.paging then - self:writePdfAnnotation("save", page, item) - local bm_note = self.ui.bookmark:getBookmarkNote(item) - if bm_note then - self:writePdfAnnotation("content", page, item, bm_note) + self:writePdfAnnotation("save", item) + if item.note then + self:writePdfAnnotation("content", item, item.note) end end UIManager:setDirty(self.dialog, "ui") - self.ui:handleEvent(Event:new("BookmarkUpdated", - self.ui.bookmark:getBookmarkForHighlight({ - page = self.ui.paging and page or item.pos0, - datetime = item.datetime, - }))) + self.ui:handleEvent(Event:new("AnnotationsModified", { item })) end self:showHighlightStyleDialog(apply_drawer, item.drawer) end function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer) local default_drawer, keep_shown_on_apply - if item_drawer then -- called from editHighlightStyle + if item_drawer then -- called from ReaderHighlight:editHighlightStyle() default_drawer = self.view.highlight.saved_drawer or G_reader_settings:readSetting("highlight_drawing_style", "lighten") keep_shown_on_apply = true @@ -2037,7 +1886,6 @@ function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer) }, }) end - local RadioButtonWidget = require("ui/widget/radiobuttonwidget") UIManager:show(RadioButtonWidget:new{ title_text = _("Highlight style"), width_factor = 0.5, @@ -2051,14 +1899,14 @@ function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer) end function ReaderHighlight:startSelection() - self.highlight_page, self.highlight_idx = self:saveHighlight() + self.highlight_idx = self:saveHighlight() self.select_mode = true end function ReaderHighlight:extendSelection() -- item1 - starting fragment (saved), item2 - ending fragment (currently selected) -- new extended highlight includes item1, item2 and the text between them - local item1 = self.view.highlight.saved[self.highlight_page][self.highlight_idx] + local item1 = self.ui.annotation.annotations[self.highlight_idx] local item2_pos0, item2_pos1 = self.selected_text.pos0, self.selected_text.pos1 -- getting starting and ending positions, text and pboxes of extended highlight local new_pos0, new_pos1, new_text, new_pboxes, ext @@ -2106,7 +1954,7 @@ function ReaderHighlight:extendSelection() -- true to draw new_text = self.ui.document:getTextFromXPointers(new_pos0, new_pos1, true) end - self:deleteHighlight(self.highlight_page, self.highlight_idx) -- starting fragment + self:deleteHighlight(self.highlight_idx) -- starting fragment self.selected_text = { text = new_text, pos0 = new_pos0, @@ -2126,7 +1974,10 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page) local page_boxes = self.ui.document:getTextBoxes(page) if page == pos0.page then -- first page (from the start of highlight to the end of the page) - item.pos0 = pos0 + item.pos0 = { + x = pos0.x, + y = pos0.y, + } item.pos1 = { x = page_boxes[#page_boxes][#page_boxes[#page_boxes]].x1, y = page_boxes[#page_boxes][#page_boxes[#page_boxes]].y1, @@ -2147,7 +1998,10 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page) x = page_boxes[1][1].x0, y = page_boxes[1][1].y0, } - item.pos1 = pos1 + item.pos1 = { + x = pos1.x, + y = pos1.y, + } end item.pos0.page = page item.pos1.page = page @@ -2159,44 +2013,53 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page) return item end --- Returns one page of saved multi-page highlight +-- Returns the list of highlights in page. +-- The list includes full single-page highlights and parts of multi-page highlights. -- (For pdf documents only) -function ReaderHighlight:getSavedExtendedHighlightPage(hl_or_bm, page, index) - local highlight - if hl_or_bm.ext then - highlight = hl_or_bm - else -- called from bookmark, need to find the corresponding highlight - for _, hl in ipairs(self.view.highlight.saved[hl_or_bm.page]) do - if hl.datetime == hl_or_bm.datetime then - highlight = hl - break +function ReaderHighlight:getPageSavedHighlights(page) + local highlights = {} + for index, highlight in ipairs(self.ui.annotation.annotations) do + if highlight.drawer and highlight.pos0.page <= page and page <= highlight.pos1.page then + if highlight.ext then -- multi-page highlight + local item = self:getSavedExtendedHighlightPage(highlight, page, index) + table.insert(highlights, item) + else + table.insert(highlights, highlight) end end end - local item = {} - item.datetime = highlight.datetime - item.drawer = highlight.drawer - item.pos0 = highlight.ext[page].pos0 - item.pos0.zoom = highlight.pos0.zoom + return highlights +end + +-- Returns one page of saved multi-page highlight +-- (For pdf documents only) +function ReaderHighlight:getSavedExtendedHighlightPage(highlight, page, index) + local item = { + datetime = highlight.datetime, + drawer = highlight.drawer, + text = highlight.text, + page = highlight.page, + pos0 = highlight.ext[page].pos0, + pos1 = highlight.ext[page].pos1, + pboxes = highlight.ext[page].pboxes, + parent = index, + } + item.pos0.zoom = highlight.pos0.zoom item.pos0.rotation = highlight.pos0.rotation - item.pos1 = highlight.ext[page].pos1 - item.pos1.zoom = highlight.pos0.zoom + item.pos1.zoom = highlight.pos0.zoom item.pos1.rotation = highlight.pos0.rotation - item.pboxes = highlight.ext[page].pboxes - item.parent = {highlight.pos0.page, index} return item end function ReaderHighlight:onReadSettings(config) self.view.highlight.saved_drawer = config:readSetting("highlight_drawer") or G_reader_settings:readSetting("highlight_drawing_style") or self.view.highlight.saved_drawer - self.view.highlight.disabled = G_reader_settings:has("default_highlight_action") - and G_reader_settings:readSetting("default_highlight_action") == "nothing" + self.view.highlight.disabled = G_reader_settings:readSetting("default_highlight_action") == "nothing" self.allow_corner_scroll = G_reader_settings:nilOrTrue("highlight_corner_scroll") -- panel zoom settings isn't supported in EPUB - if self.document.info.has_pages then + if self.ui.paging then 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}) @@ -2231,6 +2094,8 @@ function ReaderHighlight:onClose() self:clear() end +-- dpad/keys support + function ReaderHighlight:onHighlightPress() if self._current_indicator_pos then if not self._start_indicator_highlight then diff --git a/frontend/apps/reader/modules/readerlink.lua b/frontend/apps/reader/modules/readerlink.lua index 1c8b52174..88ce4b29f 100644 --- a/frontend/apps/reader/modules/readerlink.lua +++ b/frontend/apps/reader/modules/readerlink.lua @@ -3,7 +3,7 @@ ReaderLink is an abstraction for document-specific link interfaces. ]] local BD = require("ui/bidi") -local ButtonDialogTitle = require("ui/widget/buttondialogtitle") +local ButtonDialog = require("ui/widget/buttondialog") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") local DocumentRegistry = require("document/documentregistry") @@ -118,7 +118,7 @@ function ReaderLink:init() end) if G_reader_settings:isTrue("opening_page_location_stack") then -- Add location at book opening to stack - self.ui:registerPostReadyCallback(function() + self.ui:registerPostReaderReadyCallback(function() self:addCurrentLocationToStack() end) end @@ -858,7 +858,7 @@ end function ReaderLink:onGoToExternalLink(link_url) local buttons, title = self:getButtonsForExternalLinkDialog(link_url) - self.external_link_dialog = ButtonDialogTitle:new{ + self.external_link_dialog = ButtonDialog:new{ title = title, buttons = buttons, } diff --git a/frontend/apps/reader/modules/readerrolling.lua b/frontend/apps/reader/modules/readerrolling.lua index 91270f46f..205c72cff 100644 --- a/frontend/apps/reader/modules/readerrolling.lua +++ b/frontend/apps/reader/modules/readerrolling.lua @@ -100,7 +100,7 @@ function ReaderRolling:init() self.valid_cache_rendering_hash = self.ui.document:getDocumentRenderingHash(false) end end) - table.insert(self.ui.postReaderCallback, function() + table.insert(self.ui.postReaderReadyCallback, function() self:updatePos() -- Disable crengine internal history, with required redraw self.ui.document:enableInternalHistory(false) @@ -234,7 +234,7 @@ function ReaderRolling:onReadSettings(config) -- And check if we can migrate to a newest DOM version after -- the book is loaded (unless the user told us not to). if config:nilOrFalse("cre_keep_old_dom_version") then - self.ui:registerPostReadyCallback(function() + self.ui:registerPostReaderReadyCallback(function() self:checkXPointersAndProposeDOMVersionUpgrade() end) end @@ -1029,9 +1029,9 @@ function ReaderRolling:onUpdatePos(force) if self.batched_update_count > 0 then return end - if self.ui.postReaderCallback ~= nil then -- ReaderUI:init() not yet done + if self.ui.postReaderReadyCallback ~= nil then -- ReaderUI:init() not yet done -- Don't schedule any updatePos as long as ReaderUI:init() is - -- not finished (one will be called in the ui.postReaderCallback + -- not finished (one will be called in the ui.postReaderReadyCallback -- we have set above) to avoid multiple refreshes. return true end @@ -1129,7 +1129,7 @@ function ReaderRolling:onRedrawCurrentView() end function ReaderRolling:onSetDimensions(dimen) - if self.ui.postReaderCallback ~= nil then + if self.ui.postReaderReadyCallback ~= nil then -- ReaderUI:init() not yet done: just set document dimensions self.ui.document:setViewDimen(Screen:getSize()) -- (what's done in the following else is done elsewhere by @@ -1445,25 +1445,12 @@ function ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade() local applyFuncToXPointersSlots = function(func) -- Last position func(self, "xpointer", "last position in book") - -- Bookmarks - if self.ui.bookmark and self.ui.bookmark.bookmarks and #self.ui.bookmark.bookmarks > 0 then + -- Annotations + if self.ui.annotation and self.ui.annotation.annotations and #self.ui.annotation.annotations > 0 then local slots = { "page", "pos0", "pos1" } - for _, bookmark in ipairs(self.ui.bookmark.bookmarks) do + for _, item in ipairs(self.ui.annotation.annotations) do for _, slot in ipairs(slots) do - func(bookmark, slot, bookmark.notes or "bookmark") - end - end - end - -- Highlights - if self.view.highlight and self.view.highlight.saved then - local slots = { "pos0", "pos1" } - for page, items in pairs(self.view.highlight.saved) do - if items and #items > 0 then - for _, highlight in ipairs(items) do - for _, slot in ipairs(slots) do - func(highlight, slot, highlight.text or "highlight") - end - end + func(item, slot, item.text or "annotation") end end end @@ -1509,6 +1496,9 @@ function ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade() local new_xp = normalized_xpointers[xp] if new_xp then obj[slot] = new_xp + if slot == "page" then + self.ui.annotation:updateItemByXPointer(obj) + end else -- Let lost/not-found XPointer be. There is a small chance that -- it will be found (it it was made before the boxing code moved diff --git a/frontend/apps/reader/modules/readerthumbnail.lua b/frontend/apps/reader/modules/readerthumbnail.lua index e29c13562..2922c3f21 100644 --- a/frontend/apps/reader/modules/readerthumbnail.lua +++ b/frontend/apps/reader/modules/readerthumbnail.lua @@ -186,11 +186,11 @@ function ReaderThumbnail:removeFromCache(hash_subs, remove_only_non_matching) return nb_removed, size_removed end -function ReaderThumbnail:resetCachedPagesForBookmarks(...) +function ReaderThumbnail:resetCachedPagesForBookmarks(annotations) -- Multiple bookmarks may be provided local start_page, end_page - for i = 1, select("#", ...) do - local bm = select(i, ...) + for i = 1, #annotations do + local bm = annotations[i] if self.ui.rolling then -- Look at all properties that may be xpointers for _, k in ipairs({"page", "pos0", "pos1"}) do @@ -537,9 +537,6 @@ end ReaderThumbnail.onDocumentRerendered = ReaderThumbnail.resetCache ReaderThumbnail.onDocumentPartiallyRerendered = ReaderThumbnail.resetCache -- Emitted When adding/removing/updating bookmarks and highlights -ReaderThumbnail.onBookmarkAdded = ReaderThumbnail.resetCachedPagesForBookmarks -ReaderThumbnail.onBookmarkRemoved = ReaderThumbnail.resetCachedPagesForBookmarks -ReaderThumbnail.onBookmarkUpdated = ReaderThumbnail.resetCachedPagesForBookmarks -ReaderThumbnail.onBookmarkEdited = ReaderThumbnail.resetCachedPagesForBookmarks +ReaderThumbnail.onAnnotationsModified = ReaderThumbnail.resetCachedPagesForBookmarks return ReaderThumbnail diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index 5ae97354c..37deb112f 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -95,7 +95,6 @@ function ReaderView:init() temp_drawer = "invert", temp = {}, saved_drawer = "lighten", - saved = {}, indicator = nil, -- geom: non-touch highlight position indicator: {x = 50, y=50} } self.page_states = {} @@ -522,6 +521,7 @@ function ReaderView:drawTempHighlight(bb, x, y) end function ReaderView:drawSavedHighlight(bb, x, y) + if #self.ui.annotation.annotations == 0 then return end if self.ui.paging then self:drawPageSavedHighlight(bb, x, y) else @@ -529,45 +529,18 @@ function ReaderView:drawSavedHighlight(bb, x, y) end end --- Returns the list of highlights in page. --- The list includes full single-page highlights and parts of multi-page highlights. -function ReaderView:getPageSavedHighlights(page) - local highlights = {} - local is_reflow = self.document.configurable.text_wrap - self.document.configurable.text_wrap = 0 - for page_num, page_highlights in pairs(self.highlight.saved) do - for i, highlight in ipairs(page_highlights) do - -- old single-page reflow highlights do not have page in position - local pos0_page = highlight.pos0.page or page_num - local pos1_page = highlight.pos1.page or page_num - if pos0_page <= page and page <= pos1_page then - if pos0_page == pos1_page then -- single-page highlight - table.insert(highlights, highlight) - else -- multi-page highlight - local item = self.ui.highlight:getSavedExtendedHighlightPage(highlight, page, i) - table.insert(highlights, item) - end - end - end - end - self.document.configurable.text_wrap = is_reflow - return highlights -end - function ReaderView:drawPageSavedHighlight(bb, x, y) local pages = self:getCurrentPageList() for _, page in ipairs(pages) do - local items = self:getPageSavedHighlights(page) + local items = self.ui.highlight:getPageSavedHighlights(page) for _, item in ipairs(items) do local boxes = self.document:getPageBoxesFromPositions(page, item.pos0, item.pos1) if boxes then - local drawer = item.drawer or self.highlight.saved_drawer - local draw_note_mark = self.highlight.note_mark and - self.ui.bookmark:getBookmarkNote({datetime = item.datetime}) + local draw_note_mark = item.note and self.highlight.note_mark for _, box in ipairs(boxes) do local rect = self:pageToScreenTransform(page, box) if rect then - self:drawHighlightRect(bb, x, y, rect, drawer, draw_note_mark) + self:drawHighlightRect(bb, x, y, rect, item.drawer, draw_note_mark) if draw_note_mark and self.highlight.note_mark == "sidemark" then draw_note_mark = false -- side mark in the first line only end @@ -583,48 +556,38 @@ function ReaderView:drawXPointerSavedHighlight(bb, x, y) -- showing menu...). We might want to cache these boxes per page (and -- clear that cache when page layout change or highlights are added -- or removed). - local cur_view_top, cur_view_bottom - for _, items in pairs(self.highlight.saved) do - if items then - for j = 1, #items do - local item = items[j] - local pos0, pos1 = item.pos0, item.pos1 - -- document:getScreenBoxesFromPositions() is expensive, so we - -- first check this item is on current page - if not cur_view_top then - -- Even in page mode, it's safer to use pos and ui.dimen.h - -- than pages' xpointers pos, even if ui.dimen.h is a bit - -- larger than pages' heights - cur_view_top = self.document:getCurrentPos() - if self.view_mode == "page" and self.document:getVisiblePageCount() > 1 then - cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h - else - cur_view_bottom = cur_view_top + self.ui.dimen.h - end - end - local spos0 = self.document:getPosFromXPointer(pos0) - local spos1 = self.document:getPosFromXPointer(pos1) - local start_pos = math.min(spos0, spos1) - local end_pos = math.max(spos0, spos1) - if start_pos <= cur_view_bottom and end_pos >= cur_view_top then - local boxes = self.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true - if boxes then - local drawer = item.drawer or self.highlight.saved_drawer - local draw_note_mark = self.highlight.note_mark and - self.ui.bookmark:getBookmarkNote({datetime = item.datetime}) - for _, box in ipairs(boxes) do - if box.h ~= 0 then - self:drawHighlightRect(bb, x, y, box, drawer, draw_note_mark) - if draw_note_mark and self.highlight.note_mark == "sidemark" then - draw_note_mark = false -- side mark in the first line only - end + -- Even in page mode, it's safer to use pos and ui.dimen.h + -- than pages' xpointers pos, even if ui.dimen.h is a bit + -- larger than pages' heights + local cur_view_top = self.document:getCurrentPos() + local cur_view_bottom + if self.view_mode == "page" and self.document:getVisiblePageCount() > 1 then + cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h + else + cur_view_bottom = cur_view_top + self.ui.dimen.h + end + for _, item in ipairs(self.ui.annotation.annotations) do + if item.drawer then + -- document:getScreenBoxesFromPositions() is expensive, so we + -- first check if this item is on current page + local start_pos = self.document:getPosFromXPointer(item.pos0) + local end_pos = self.document:getPosFromXPointer(item.pos1) + if start_pos <= cur_view_bottom and end_pos >= cur_view_top then + local boxes = self.document:getScreenBoxesFromPositions(item.pos0, item.pos1, true) -- get_segments=true + if boxes then + local draw_note_mark = item.note and self.highlight.note_mark + for _, box in ipairs(boxes) do + if box.h ~= 0 then + self:drawHighlightRect(bb, x, y, box, item.drawer, draw_note_mark) + if draw_note_mark and self.highlight.note_mark == "sidemark" then + draw_note_mark = false -- side mark in the first line only end - end -- end for each box - end -- end if boxes + end + end end - end -- end for each highlight + end end - end -- end for all saved highlight + end end function ReaderView:drawHighlightRect(bb, _x, _y, rect, drawer, draw_note_mark) @@ -914,40 +877,6 @@ function ReaderView:onReadSettings(config) self:resetLayout() local page_scroll = config:readSetting("kopt_page_scroll") or self.document.configurable.page_scroll self.page_scroll = page_scroll == 1 and true or false - self.highlight.saved = config:readSetting("highlight", {}) - -- Highlight formats in crengine and mupdf are incompatible. - -- Backup highlights when the document is opened with incompatible engine. - local page, page_highlights - while true do -- remove empty tables for pages without highlights and get the first page with highlights - page, page_highlights = next(self.highlight.saved) - if not page or #page_highlights > 0 then - break -- we're done (there is none, or there is some usable) - else - self.highlight.saved[page] = nil -- clean it up while we're at it, and find another one - end - end - if page_highlights then - local highlight_type = type(page_highlights[1].pos0) - if self.ui.rolling and highlight_type == "table" then - config:saveSetting("highlight_paging", self.highlight.saved) - self.highlight.saved = config:readSetting("highlight_rolling", {}) - config:saveSetting("highlight", self.highlight.saved) - config:delSetting("highlight_rolling") - elseif self.ui.paging and highlight_type == "string" then - config:saveSetting("highlight_rolling", self.highlight.saved) - self.highlight.saved = config:readSetting("highlight_paging", {}) - config:saveSetting("highlight", self.highlight.saved) - config:delSetting("highlight_paging") - end - else - if self.ui.rolling and config:has("highlight_rolling") then - self.highlight.saved = config:readSetting("highlight_rolling") - config:delSetting("highlight_rolling") - elseif self.ui.paging and config:has("highlight_paging") then - self.highlight.saved = config:readSetting("highlight_paging") - config:delSetting("highlight_paging") - end - end self.inverse_reading_order = config:isTrue("inverse_reading_order") or G_reader_settings:isTrue("inverse_reading_order") self.page_overlap_enable = config:isTrue("show_overlap_enable") or G_reader_settings:isTrue("page_overlap_enable") or G_defaults:readSetting("DSHOWOVERLAP") self.page_overlap_style = config:readSetting("page_overlap_style") or G_reader_settings:readSetting("page_overlap_style") or "dim" @@ -1107,7 +1036,6 @@ function ReaderView:onSaveSettings() if G_reader_settings:nilOrFalse("lock_rotation") then self.document.configurable.rotation_mode = Screen:getRotationMode() -- will be saved by ReaderConfig end - self.ui.doc_settings:saveSetting("highlight", self.highlight.saved) self.ui.doc_settings:saveSetting("inverse_reading_order", self.inverse_reading_order) self.ui.doc_settings:saveSetting("show_overlap_enable", self.page_overlap_enable) self.ui.doc_settings:saveSetting("page_overlap_style", self.page_overlap_style) diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 708c39bff..abc346a3a 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -24,6 +24,7 @@ local LanguageSupport = require("languagesupport") local Notification = require("ui/widget/notification") local PluginLoader = require("pluginloader") local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindicator") +local ReaderAnnotation = require("apps/reader/modules/readerannotation") local ReaderBack = require("apps/reader/modules/readerback") local ReaderBookmark = require("apps/reader/modules/readerbookmark") local ReaderConfig = require("apps/reader/modules/readerconfig") @@ -83,7 +84,7 @@ local ReaderUI = InputContainer:extend{ password = nil, postInitCallback = nil, - postReaderCallback = nil, + postReaderReadyCallback = nil, } function ReaderUI:registerModule(name, ui_module, always_active) @@ -102,8 +103,8 @@ function ReaderUI:registerPostInitCallback(callback) table.insert(self.postInitCallback, callback) end -function ReaderUI:registerPostReadyCallback(callback) - table.insert(self.postReaderCallback, callback) +function ReaderUI:registerPostReaderReadyCallback(callback) + table.insert(self.postReaderReadyCallback, callback) end function ReaderUI:init() @@ -116,7 +117,7 @@ function ReaderUI:init() Device:setIgnoreInput(true) -- Avoid ANRs on Android with unprocessed events. self.postInitCallback = {} - self.postReaderCallback = {} + self.postReaderReadyCallback = {} -- if we are not the top level dialog ourselves, it must be given in the table if not self.dialog then self.dialog = self @@ -182,6 +183,12 @@ function ReaderUI:init() view = self.view, ui = self }) + self:registerModule("annotation", ReaderAnnotation:new{ + dialog = self.dialog, + view = self.view, + ui = self, + document = self.document, + }) -- reader goto controller -- "goto" being a dirty keyword in Lua? self:registerModule("gotopage", ReaderGoto:new{ @@ -491,10 +498,10 @@ function ReaderUI:init() -- Need the same event for PDF document self:handleEvent(Event:new("ReaderReady", self.doc_settings)) - for _,v in ipairs(self.postReaderCallback) do + for _,v in ipairs(self.postReaderReadyCallback) do v() end - self.postReaderCallback = nil + self.postReaderReadyCallback = nil Device:setIgnoreInput(false) -- Allow processing of events (on Android). Input:inhibitInputUntil(0.2) @@ -584,6 +591,7 @@ end function ReaderUI:showReader(file, provider, seamless) logger.dbg("show reader ui") + file = ffiUtil.realpath(file) if lfs.attributes(file, "mode") ~= "file" then UIManager:show(InfoMessage:new{ text = T(_("File '%1' does not exist."), BD.filepath(file)) diff --git a/frontend/ui/translator.lua b/frontend/ui/translator.lua index 6ae338cf3..054dc1e50 100644 --- a/frontend/ui/translator.lua +++ b/frontend/ui/translator.lua @@ -503,14 +503,14 @@ Show translated text in TextViewer, with alternate translations @string source_lang[opt="auto"] (`"en"`, `"fr"`, `…`) or `"auto"` to auto-detect source language @string target_lang[opt] (`"en"`, `"fr"`, `…`) --]] -function Translator:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) +function Translator:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index) if Device:hasClipboard() then Device.input.setClipboardText(text) end local NetworkMgr = require("ui/network/manager") if NetworkMgr:willRerunWhenOnline(function() - self:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) + self:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index) end) then return end @@ -519,11 +519,11 @@ function Translator:showTranslation(text, detailed_view, source_lang, target_lan -- translation service query. local Trapper = require("ui/trapper") Trapper:wrap(function() - self:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) + self:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index) end) end -function Translator:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) +function Translator:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index) if not target_lang then target_lang = self:getTargetLanguage() end @@ -632,8 +632,8 @@ function Translator:_showTranslation(text, detailed_view, source_lang, target_la UIManager:close(textviewer) UIManager:close(ui.highlight.highlight_dialog) ui.highlight.highlight_dialog = nil - if page then - ui.highlight:editHighlight(page, index, false, text_main) + if index then + ui.highlight:editHighlight(index, false, text_main) else ui.highlight:addNote(text_main) end @@ -645,8 +645,8 @@ function Translator:_showTranslation(text, detailed_view, source_lang, target_la UIManager:close(textviewer) UIManager:close(ui.highlight.highlight_dialog) ui.highlight.highlight_dialog = nil - if page then - ui.highlight:editHighlight(page, index, false, text_all) + if index then + ui.highlight:editHighlight(index, false, text_all) else ui.highlight:addNote(text_all) end diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 45722e40c..7ae794f97 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -65,11 +65,6 @@ local DictQuickLookup = InputContainer:extend{ rotated_update_wiki_languages_on_close = nil, } -local highlight_strings = { - highlight =_("Highlight"), - unhighlight = _("Unhighlight"), -} - function DictQuickLookup.getWikiSaveEpubDefaultDir() local dir = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir() if dir:sub(-1) ~= "/" then @@ -438,19 +433,13 @@ function DictQuickLookup:init() }, { id = "highlight", - text = self:getHighlightText(), + text = _("Highlight"), enabled = not self:isDocless() and self.highlight ~= nil, callback = function() - if self:getHighlightText() == highlight_strings.highlight then - self.ui:handleEvent(Event:new("Highlight")) - else - self.ui:handleEvent(Event:new("Unhighlight")) - end + self.save_highlight = not self.save_highlight -- Just update, repaint and refresh *this* button local this = self.button_table:getButtonById("highlight") - if not this then return end - this:enableDisable(self.highlight ~= nil) - this:setText(self:getHighlightText(), this.width) + this:setText(self.save_highlight and _("Unhighlight") or _("Highlight"), this.width) this:refresh() end, }, @@ -953,22 +942,6 @@ function DictQuickLookup:onShow() return true end -function DictQuickLookup:getHighlightedItem() - if self:isDocless() then return end - return self.ui.highlight:getHighlightBookmarkItem() -end - -function DictQuickLookup:getHighlightText() - local item = self:getHighlightedItem() - if not item then - return highlight_strings.highlight, false - elseif self.ui.bookmark:isBookmarkAdded(item) then - return highlight_strings.unhighlight, false - else - return highlight_strings.highlight, true - end -end - function DictQuickLookup:isPrevDictAvaiable() return self.dict_index > 1 end @@ -1150,13 +1123,19 @@ function DictQuickLookup:onClose(no_clear) self.ui:handleEvent(Event:new("UpdateWikiLanguages", self.wiki_languages)) end end - if self.highlight and not no_clear then - -- delay unhighlight of selection, so we can see where we stopped when - -- back from our journey into dictionary or wikipedia - local clear_id = self.highlight:getClearId() - UIManager:scheduleIn(0.5, function() - self.highlight:clear(clear_id) - end) + + if self.save_highlight then + self.highlight:saveHighlight() + self.highlight:clear() + else + if self.highlight and not no_clear then + -- delay unhighlight of selection, so we can see where we stopped when + -- back from our journey into dictionary or wikipedia + local clear_id = self.highlight:getClearId() + UIManager:scheduleIn(0.5, function() + self.highlight:clear(clear_id) + end) + end end return true end diff --git a/plugins/coverbrowser.koplugin/covermenu.lua b/plugins/coverbrowser.koplugin/covermenu.lua index 1a2b868be..e31db80a1 100644 --- a/plugins/coverbrowser.koplugin/covermenu.lua +++ b/plugins/coverbrowser.koplugin/covermenu.lua @@ -57,8 +57,14 @@ function CoverMenu:updateCache(file, status, do_create, pages) local percent_finished = doc_settings:readSetting("percent_finished") local summary = doc_settings:readSetting("summary") status = summary and summary.status - local highlight = doc_settings:readSetting("highlight") - local has_highlight = highlight and next(highlight) and true + 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 diff --git a/plugins/exporter.koplugin/clip.lua b/plugins/exporter.koplugin/clip.lua index d6c87f3cb..39b9bbaab 100644 --- a/plugins/exporter.koplugin/clip.lua +++ b/plugins/exporter.koplugin/clip.lua @@ -235,6 +235,23 @@ function MyClipping:getImage(image) end end +function MyClipping:parseAnnotations(annotations, book) + for _, item in ipairs(annotations) do + if item.drawer then + local clipping = { + sort = "highlight", + page = item.pageno, + time = self:getTime(item.datetime), + text = self:getText(item.text), + note = self:getText(item.note), + chapter = item.chapter, + drawer = item.drawer, + } + table.insert(book, { clipping }) + end + end +end + function MyClipping:parseHighlight(highlights, bookmarks, book) --DEBUG("book", book.file) @@ -249,13 +266,14 @@ function MyClipping:parseHighlight(highlights, bookmarks, book) local orphan_highlights = {} for page, items in pairs(highlights) do for _, item in ipairs(items) do - local clipping = {} - clipping.page = page - clipping.sort = "highlight" - clipping.time = self:getTime(item.datetime or "") - clipping.text = self:getText(item.text) - clipping.chapter = item.chapter - clipping.drawer = item.drawer + local clipping = { + sort = "highlight", + page = page, + time = self:getTime(item.datetime or ""), + text = self:getText(item.text), + chapter = item.chapter, + drawer = item.drawer, + } local bookmark_found = false for _, bookmark in pairs(bookmarks) do if bookmark.datetime == item.datetime then @@ -316,9 +334,13 @@ end function MyClipping:getClippingsFromBook(clippings, doc_path) local doc_settings = DocSettings:open(doc_path) - local highlights = doc_settings:readSetting("highlight") - if not highlights then return end - local bookmarks = doc_settings:readSetting("bookmarks") + local highlights, bookmarks + local annotations = doc_settings:readSetting("annotations") + if annotations == nil then + highlights = doc_settings:readSetting("highlight") + if highlights == nil then return end + bookmarks = doc_settings:readSetting("bookmarks") + end local props = doc_settings:readSetting("doc_props") props = FileManagerBookInfo.extendProps(props, doc_path) local title, author = self:getTitleAuthor(doc_path, props) @@ -326,8 +348,13 @@ function MyClipping:getClippingsFromBook(clippings, doc_path) file = doc_path, title = title, author = author, + number_of_pages = doc_settings:readSetting("doc_pages"), } - self:parseHighlight(highlights, bookmarks, clippings[title]) + if annotations then + self:parseAnnotations(annotations, clippings[title]) + else + self:parseHighlight(highlights, bookmarks, clippings[title]) + end end function MyClipping:parseHistory() @@ -361,7 +388,7 @@ function MyClipping:parseCurrentDoc(view) output_filename = util.getSafeFilename(title), number_of_pages = view.document.info.number_of_pages, } - self:parseHighlight(view.highlight.saved, view.ui.bookmark.bookmarks, clippings[title]) + self:parseAnnotations(view.ui.annotation.annotations, clippings[title]) return clippings end diff --git a/plugins/exporter.koplugin/main.lua b/plugins/exporter.koplugin/main.lua index 766a7d3c5..311448aa7 100644 --- a/plugins/exporter.koplugin/main.lua +++ b/plugins/exporter.koplugin/main.lua @@ -156,6 +156,7 @@ end --- Parse and export highlights from the currently opened document. function Exporter:exportCurrentNotes() + self.ui.annotation:updatePageNumbers() local clippings = self:getDocumentClippings() self:exportClippings(clippings) end diff --git a/plugins/statistics.koplugin/main.lua b/plugins/statistics.koplugin/main.lua index f97ed28dd..31a27de0c 100644 --- a/plugins/statistics.koplugin/main.lua +++ b/plugins/statistics.koplugin/main.lua @@ -187,7 +187,7 @@ function ReaderStatistics:initData() self.data.pages = self.document:getPageCount() -- Update these numbers to what's actually stored in the settings - self.data.highlights, self.data.notes = self.ui.bookmark:getNumberOfHighlightsAndNotes() + self.data.highlights, self.data.notes = self.ui.annotation:getNumberOfHighlightsAndNotes() self.id_curr_book = self:getIdBookDB() self.book_read_pages, self.book_read_time = self:getPageTimeTotalStats(self.id_curr_book) if self.book_read_pages > 0 then @@ -2727,27 +2727,14 @@ function ReaderStatistics:onCloseDocument() self:insertDB() end -function ReaderStatistics:onAddHighlight() +function ReaderStatistics:onAnnotationsModified(annotations) if self.settings.is_enabled then - self.data.highlights = self.data.highlights + 1 - end -end - -function ReaderStatistics:onDelHighlight() - if self.settings.is_enabled then - self.data.highlights = self.data.highlights - 1 - end -end - -function ReaderStatistics:onAddNote() - if self.settings.is_enabled then - self.data.notes = self.data.notes + 1 - end -end - -function ReaderStatistics:onDelNote() - if self.settings.is_enabled then - self.data.notes = self.data.notes - 1 + if annotations.nb_highlights_added then + self.data.highlights = self.data.highlights + annotations.nb_highlights_added + end + if annotations.nb_notes_added then + self.data.notes = self.data.notes + annotations.nb_notes_added + end end end diff --git a/spec/unit/readerbookmark_spec.lua b/spec/unit/readerbookmark_spec.lua index 0dc22fc4e..190a3e0f8 100644 --- a/spec/unit/readerbookmark_spec.lua +++ b/spec/unit/readerbookmark_spec.lua @@ -20,7 +20,7 @@ describe("ReaderBookmark module", function() readerui.highlight:onHoldPan(nil, { pos = pos1 }) readerui.highlight:onHoldRelease() assert.truthy(readerui.highlight.highlight_dialog) - readerui.highlight:onHighlight() + readerui.highlight:saveHighlight() UIManager:nextTick(function() UIManager:close(readerui.highlight.highlight_dialog) UIManager:close(readerui) @@ -67,17 +67,6 @@ describe("ReaderBookmark module", function() UIManager:show(readerui) readerui.rolling:onGotoPage(10) end) - it("should compare bookmarks properly", function() - assert.truthy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }, - { notes = 'foo', page = 1, pos0 = 0, pos1 = 2, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }, - { notes = 'bar', page = 1, pos0 = 0, pos1 = 2, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo0', page = 1, pos0 = 0, pos1 = 0, }, - { notes = 'foo', page = 1, pos0 = 0, pos1 = 2, })) - end) it("should show dogear after toggling non-bookmarked page", function() assert.falsy(readerui.view.dogear_visible) toggler_dogear(readerui) @@ -90,7 +79,7 @@ describe("ReaderBookmark module", function() Screen:shot("screenshots/reader_bookmark_nodogear_epub.png") assert.falsy(readerui.view.dogear_visible) end) - it("should sort bookmarks with descending page numbers", function() + it("should sort bookmarks with ascending page numbers", function() local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45} for _, page in ipairs(pages) do readerui.rolling:onGotoPage(page) @@ -99,11 +88,11 @@ describe("ReaderBookmark module", function() readerui.bookmark:onShowBookmark() show_bookmark_menu(readerui) Screen:shot("screenshots/reader_bookmark_10marks_epub.png") - assert.are.same(10, #readerui.bookmark.bookmarks) + assert.are.same(10, #readerui.annotation.annotations) + assert.are.same(15, readerui.document:getPageFromXPointer(readerui.annotation.annotations[4].page)) end) it("should keep descending page numbers after removing bookmarks", function() local pages = {1, 30, 10, 40, 20} - readerui.bookmark.bookmarks = {} for _, page in ipairs(pages) do readerui.rolling:onGotoPage(page) toggler_dogear(readerui) @@ -111,7 +100,7 @@ describe("ReaderBookmark module", function() readerui.bookmark:onShowBookmark() show_bookmark_menu(readerui) Screen:shot("screenshots/reader_bookmark_5marks_epub.png") - assert.are.same(5, #readerui.bookmark.bookmarks) + assert.are.same(5, #readerui.annotation.annotations) end) it("should add bookmark by highlighting", function() highlight_text(readerui, @@ -120,18 +109,18 @@ describe("ReaderBookmark module", function() readerui.bookmark:onShowBookmark() show_bookmark_menu(readerui) Screen:shot("screenshots/reader_bookmark_6marks_epub.png") - assert.are.same(6, #readerui.bookmark.bookmarks) + assert.are.same(6, #readerui.annotation.annotations) end) it("should get previous bookmark for certain page", function() local xpointer = readerui.document:getXPointer() local bm_xpointer = readerui.bookmark:getPreviousBookmarkedPage(xpointer) - assert.are.same(6, #readerui.bookmark.bookmarks) - assert.are.same(1, readerui.document:getPageFromXPointer(bm_xpointer)) + assert.are.same(6, #readerui.annotation.annotations) + assert.are.same(5, readerui.document:getPageFromXPointer(bm_xpointer)) end) it("should get next bookmark for certain page", function() local xpointer = readerui.document:getXPointer() local bm_xpointer = readerui.bookmark:getNextBookmarkedPage(xpointer) - assert.are.same(20, readerui.document:getPageFromXPointer(bm_xpointer)) + assert.are.same(15, readerui.document:getPageFromXPointer(bm_xpointer)) end) end) @@ -154,34 +143,6 @@ describe("ReaderBookmark module", function() UIManager:show(readerui) readerui.paging:onGotoPage(10) end) - it("should does bookmark comparison properly", function() - assert.truthy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', pos0 = { page = 1 , x = 2, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, }, - { notes = 'foo', pos0 = { page = 1 , x = 2, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }, - { notes = 'foo', page = 1, pos1 = 2, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }, - { notes = 'foo', page = 1, pos0 = 2, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', pos0 = { page = 1 , x = 2, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, }, - { notes = 'foo', pos0 = { page = 2 , x = 2, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', pos0 = { page = 1 , x = 1, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, }, - { notes = 'foo', pos0 = { page = 1 , x = 2, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, })) - assert.falsy(readerui.bookmark:isBookmarkSame( - { notes = 'foo', pos0 = { page = 1 , x = 1, y = 3}, - pos1 = { page = 1, x = 20, y = 3 }, }, - { notes = 'foo', pos0 = { page = 1 , x = 1, y = 3}, - pos1 = { page = 1, x = 20, y = 2 }, })) - end) it("should show dogear after toggling non-bookmarked page", function() toggler_dogear(readerui) Screen:shot("screenshots/reader_bookmark_dogear_pdf.png") @@ -192,7 +153,7 @@ describe("ReaderBookmark module", function() Screen:shot("screenshots/reader_bookmark_nodogear_pdf.png") assert.truthy(not readerui.view.dogear_visible) end) - it("should sort bookmarks with descending page numbers", function() + it("should sort bookmarks with ascending page numbers", function() local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45} for _, page in ipairs(pages) do readerui.paging:onGotoPage(page) @@ -201,7 +162,8 @@ describe("ReaderBookmark module", function() readerui.bookmark:onShowBookmark() show_bookmark_menu(readerui) Screen:shot("screenshots/reader_bookmark_10marks_pdf.png") - assert.are.same(10, #readerui.bookmark.bookmarks) + assert.are.same(10, #readerui.annotation.annotations) + assert.are.same(15, readerui.annotation.annotations[4].page) end) it("should keep descending page numbers after removing bookmarks", function() local pages = {1, 30, 10, 40, 20} @@ -212,14 +174,14 @@ describe("ReaderBookmark module", function() readerui.bookmark:onShowBookmark() show_bookmark_menu(readerui) Screen:shot("screenshots/reader_bookmark_5marks_pdf.png") - assert.are.same(5, #readerui.bookmark.bookmarks) + assert.are.same(5, #readerui.annotation.annotations) end) it("should add bookmark by highlighting", function() highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) readerui.bookmark:onShowBookmark() show_bookmark_menu(readerui) Screen:shot("screenshots/reader_bookmark_6marks_pdf.png") - assert.are.same(6, #readerui.bookmark.bookmarks) + assert.are.same(6, #readerui.annotation.annotations) end) it("should get previous bookmark for certain page", function() assert.are.same(5, readerui.bookmark:getPreviousBookmarkedPage(10)) @@ -227,31 +189,5 @@ describe("ReaderBookmark module", function() it("should get next bookmark for certain page", function() assert.are.same(15, readerui.bookmark:getNextBookmarkedPage(10)) end) - it("should search/add bookmarks properly", function() - -- clear bookmarks created by previous tests - readerui.bookmark.bookmarks = {} - local p1 = { x = 0, y = 0, page = 100 } - local bm1 = { notes = 'foo', page = 10, - pos0 = { x = 0, y = 0, page = 100 }, pos1 = p1, } - assert.falsy(readerui.bookmark:isBookmarkAdded(bm1)) - readerui.bookmark:addBookmark(bm1) - assert.are.same(readerui.bookmark.bookmarks, {bm1}) - - local bm2 = { notes = 'foo', page = 1, - pos0 = { x = 0, y = 0, page = 1 }, pos1 = p1, } - assert.falsy(readerui.bookmark:isBookmarkAdded(bm2)) - readerui.bookmark:addBookmark(bm2) - assert.are.same({bm1, bm2}, readerui.bookmark.bookmarks) - - local bm3 = { notes = 'foo', page = 5, - pos0 = { x = 0, y = 0, page = 5 }, pos1 = p1, } - assert.falsy(readerui.bookmark:isBookmarkAdded(bm3)) - readerui.bookmark:addBookmark(bm3) - assert.are.same({bm1, bm3, bm2}, readerui.bookmark.bookmarks) - - assert.truthy(readerui.bookmark:isBookmarkAdded(bm1)) - assert.truthy(readerui.bookmark:isBookmarkAdded(bm2)) - assert.truthy(readerui.bookmark:isBookmarkAdded(bm3)) - end) end) end) diff --git a/spec/unit/readerhighlight_spec.lua b/spec/unit/readerhighlight_spec.lua index ae1ff4432..e7f7c8d55 100644 --- a/spec/unit/readerhighlight_spec.lua +++ b/spec/unit/readerhighlight_spec.lua @@ -17,7 +17,7 @@ describe("Readerhighlight module", function() readerui.highlight:onHold(nil, { pos = pos0 }) readerui.highlight:onHoldRelease() - readerui.highlight:onHighlight() + readerui.highlight:saveHighlight() assert.spy(s).was_called() assert.spy(s).was_called_with(match.is_ref(readerui.languagesupport), @@ -50,7 +50,7 @@ describe("Readerhighlight module", function() assert.truthy(readerui.highlight.highlight_dialog) assert.truthy(UIManager._window_stack[next_slot].widget == readerui.highlight.highlight_dialog) - readerui.highlight:onHighlight() + readerui.highlight:saveHighlight() UIManager:scheduleIn(1, function() UIManager:close(readerui.highlight.highlight_dialog) UIManager:close(readerui) @@ -64,7 +64,7 @@ describe("Readerhighlight module", function() readerui.highlight:onHold(nil, { pos = pos0 }) readerui.highlight:onHoldPan(nil, { pos = pos1 }) readerui.highlight:onHoldRelease() - readerui.highlight:onHighlight() + readerui.highlight:saveHighlight() readerui.highlight:clear() UIManager:close(readerui.highlight.highlight_dialog) readerui.highlight:onTap(nil, { pos = pos2 }) @@ -106,12 +106,13 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} end) it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 400, y = 70 }) Screen:shot("screenshots/reader_highlight_single_word_epub.png") assert.spy(selection_spy).was_called() - assert.truthy(readerui.view.highlight.saved[page]) + assert.truthy(#readerui.annotation.annotations == 1) end) it("should highlight text", function() highlight_text(readerui, @@ -119,7 +120,7 @@ describe("Readerhighlight module", function() Geom:new{ x = 400, y = 170 }) Screen:shot("screenshots/reader_highlight_text_epub.png") assert.spy(selection_spy).was_called() - assert.truthy(readerui.view.highlight.saved[page]) + assert.truthy(#readerui.annotation.annotations == 1) end) it("should response on tap gesture", function() tap_highlight_text(readerui, @@ -154,6 +155,7 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} end) it("should response on tap gesture #nocov", function() tap_highlight_text(readerui, @@ -165,10 +167,12 @@ describe("Readerhighlight module", function() it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) Screen:shot("screenshots/reader_highlight_single_word_pdf.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should highlight text", function() highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 }) Screen:shot("screenshots/reader_highlight_text_pdf.png") + assert.truthy(#readerui.annotation.annotations == 1) end) end) describe("for scanned page without text layer", function() @@ -179,6 +183,7 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} end) it("should respond to tap gesture #nocov", function() tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 }) @@ -187,10 +192,12 @@ describe("Readerhighlight module", function() it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should highlight text", function() highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) Screen:shot("screenshots/reader_highlight_text_pdf_scanned.png") + assert.truthy(#readerui.annotation.annotations == 1) end) end) describe("for reflowed page", function() @@ -202,6 +209,7 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} readerui.document.configurable.text_wrap = 0 UIManager:close(readerui) -- close to flush settings -- We haven't torn it down yet @@ -214,10 +222,12 @@ describe("Readerhighlight module", function() it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should highlight text", function() highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) Screen:shot("screenshots/reader_highlight_text_pdf_reflowed.png") + assert.truthy(#readerui.annotation.annotations == 1) end) end) end) @@ -247,6 +257,7 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} end) it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) @@ -255,6 +266,7 @@ describe("Readerhighlight module", function() it("should highlight text", function() highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 }) Screen:shot("screenshots/reader_highlight_text_pdf_scroll.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should response on tap gesture", function() tap_highlight_text(readerui, @@ -262,6 +274,7 @@ describe("Readerhighlight module", function() Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 }) Screen:shot("screenshots/reader_tap_highlight_text_pdf_scroll.png") + assert.truthy(#readerui.annotation.annotations == 1) end) end) describe("for scanned page without text layer", function() @@ -273,14 +286,17 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} end) it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned_scroll.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should highlight text", function() highlight_text(readerui, Geom:new{x = 192, y = 186}, Geom:new{x = 280, y = 186}) Screen:shot("screenshots/reader_highlight_text_pdf_scanned_scroll.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should response on tap gesture", function() tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 }) @@ -296,6 +312,7 @@ describe("Readerhighlight module", function() end) after_each(function() readerui.highlight:clear() + readerui.annotation.annotations = {} readerui.document.configurable.text_wrap = 0 UIManager:close(readerui) -- close to flush settings -- We haven't torn it down yet @@ -304,10 +321,12 @@ describe("Readerhighlight module", function() it("should highlight single word", function() highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed_scroll.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should highlight text", function() highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) Screen:shot("screenshots/reader_highlight_text_pdf_reflowed_scroll.png") + assert.truthy(#readerui.annotation.annotations == 1) end) it("should response on tap gesture", function() tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })