From 785eb5f3ef5f9a1d46a7faa70e0b5a37ad09b6ed Mon Sep 17 00:00:00 2001 From: hius07 <62179190+hius07@users.noreply.github.com> Date: Sat, 4 Dec 2021 19:37:00 +0200 Subject: [PATCH] Bookmark search (#8504) From bookmark list, main menu and with a gesture. --- .../apps/reader/modules/readerbookmark.lua | 299 +++++++++++------- frontend/dispatcher.lua | 2 + frontend/ui/elements/reader_menu_order.lua | 1 + 3 files changed, 179 insertions(+), 123 deletions(-) diff --git a/frontend/apps/reader/modules/readerbookmark.lua b/frontend/apps/reader/modules/readerbookmark.lua index 0c247679f..6a574cb7a 100644 --- a/frontend/apps/reader/modules/readerbookmark.lua +++ b/frontend/apps/reader/modules/readerbookmark.lua @@ -9,7 +9,9 @@ local Geom = require("ui/geometry") local GestureRange = require("ui/gesturerange") local InputContainer = require("ui/widget/container/inputcontainer") local InputDialog = require("ui/widget/inputdialog") +local LineWidget = require("ui/widget/linewidget") local Menu = require("ui/widget/menu") +local Size = require("ui/size") local TextViewer = require("ui/widget/textviewer") local UIManager = require("ui/uimanager") local Utf8Proc = require("ffi/utf8proc") @@ -174,6 +176,15 @@ function ReaderBookmark:addToMainMenu(menu_items) }, }, } + menu_items.bookmark_search = { + text = _("Bookmark search"), + enabled_func = function() + return self:hasBookmarks() + end, + callback = function() + self:onSearchBookmark() + end, + } end function ReaderBookmark:enableBookmarkBrowsingMode() @@ -385,37 +396,33 @@ function ReaderBookmark:updateHighlightsIfNeeded() self.ui.doc_settings:saveSetting("bookmarks_version", 20200615) end -function ReaderBookmark:onShowBookmark() +function ReaderBookmark:onShowBookmark(match_table) self.select_mode = false - self.filtered_mode = false + self.filtered_mode = match_table and true or false self:updateHighlightsIfNeeded() -- build up item_table local item_table = {} local is_reverse_sorting = G_reader_settings:nilOrTrue("bookmarks_items_reverse_sorting") + local curr_page = self.ui.rolling and self.ui.document:getXPointer() or self.ui.paging.current_page + curr_page = self:getBookmarkPageString(curr_page) local num = #self.bookmarks + 1 - for i, v in ipairs(self.bookmarks) do - local is_auto_text + 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 - is_auto_text = true v.text = self:getBookmarkAutoText(v) - else - is_auto_text = self:isBookmarkAutoText(v) end - -- bookmarks are internally sorted by descending page numbers - local k = is_reverse_sorting and i or num - i - item_table[k] = util.tableDeepCopy(v) - if v.highlighted then - if is_auto_text then - item_table[k].type = "highlight" - else - item_table[k].type = "note" + local item = util.tableDeepCopy(v) + 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 = DISPLAY_PREFIX[item.type] .. item.text_orig + item.mandatory = self:getBookmarkPageString(item.page) + if item.mandatory == curr_page then + item.bold = true end - else - item_table[k].type = "bookmark" + table.insert(item_table, item) end - item_table[k].text_orig = v.text or v.notes - item_table[k].text = DISPLAY_PREFIX[item_table[k].type] .. item_table[k].text_orig - item_table[k].mandatory = self:getBookmarkPageString(v.page) end local items_per_page = G_reader_settings:readSetting("bookmarks_items_per_page") @@ -424,7 +431,7 @@ function ReaderBookmark:onShowBookmark() local show_separator = G_reader_settings:isTrue("bookmarks_items_show_separator") local bm_menu = Menu:new{ - title = _("Bookmarks"), + title = self.filtered_mode and _("Bookmarks (search results)") or _("Bookmarks"), item_table = item_table, is_borderless = true, is_popout = false, @@ -491,7 +498,7 @@ function ReaderBookmark:onShowBookmark() table.remove(item_table, i) end end - bm_menu:switchItemTable(self.filtered_mode and _("Bookmarks (filtered)") or _("Bookmarks"), item_table, -1) + bm_menu:switchItemTable(bookmark.filtered_mode and _("Bookmarks (search results)") or _("Bookmarks"), item_table, -1) end, other_buttons_first = true, other_buttons = { @@ -526,7 +533,7 @@ function ReaderBookmark:onShowBookmark() end end self.select_mode = false - bm_menu:switchItemTable(self.filtered_mode and _("Bookmarks (filtered)") or _("Bookmarks"), item_table) + bm_menu:switchItemTable(bookmark.filtered_mode and _("Bookmarks (search results)") or _("Bookmarks"), item_table) end, }, { @@ -604,91 +611,10 @@ function ReaderBookmark:onShowBookmark() end, }, { - text = _("Filter bookmarks"), + text = _("Search bookmarks"), callback = function() UIManager:close(self.textviewer) - local input_dialog, check_button_bookmark, check_button_highlight, check_button_note - input_dialog = InputDialog:new{ - title = _("Filter bookmarks"), - input_hint = _("(containing text)"), - buttons = { - { - { - text = _("Close"), - callback = function() - UIManager:close(input_dialog) - end, - }, - { - text = _("Apply"), - is_enter_default = true, - callback = function() - if check_button_bookmark.checked - or check_button_highlight.checked - or check_button_note.checked then - local search_str = input_dialog:getInputText() - local is_search_str = false - if search_str ~= "" then - is_search_str = true - search_str = Utf8Proc.lowercase(util.fixUtf8(search_str, "?")) - end - for i = #item_table, 1, -1 do - local bm_item = item_table[i] - if (check_button_bookmark.checked and bm_item.type == "bookmark") - or (check_button_highlight.checked and bm_item.type == "highlight") - or (check_button_note.checked and bm_item.type == "note") then - if is_search_str then - local bm_text = bm_item.notes .. bm_item.text - bm_text = Utf8Proc.lowercase(util.fixUtf8(bm_text, "?")) - if not bm_text:find(search_str) then - table.remove(item_table, i) - end - end - else - table.remove(item_table, i) - end - end - UIManager:close(input_dialog) - bm_menu:switchItemTable(_("Bookmarks (filtered)"), item_table) - self.filtered_mode = true - end - end, - }, - }, - }, - } - check_button_highlight = CheckButton:new{ - text = " " .. DISPLAY_PREFIX["highlight"] .. _("highlights"), - checked = true, - parent = input_dialog, - max_width = input_dialog._input_widget.width, - callback = function() - check_button_highlight:toggleCheck() - end, - } - input_dialog:addWidget(check_button_highlight) - check_button_note = CheckButton:new{ - text = " " .. DISPLAY_PREFIX["note"] .. _("notes"), - checked = true, - parent = input_dialog, - max_width = input_dialog._input_widget.width, - callback = function() - check_button_note:toggleCheck() - end, - } - input_dialog:addWidget(check_button_note) - check_button_bookmark = CheckButton:new{ - text = " " .. DISPLAY_PREFIX["bookmark"] .. _("page bookmarks"), - checked = true, - parent = input_dialog, - max_width = input_dialog._input_widget.width, - callback = function() - check_button_bookmark:toggleCheck() - end, - } - input_dialog:addWidget(check_button_bookmark) - UIManager:show(input_dialog) - input_dialog:onShowKeyboard() + bookmark:onSearchBookmark(bm_menu) end, }, }, @@ -910,35 +836,28 @@ function ReaderBookmark:renameBookmark(item, from_highlight) local value = self.input:getInputValue() if value == "" then -- blank input resets the 'text' field to auto-text value = self:getBookmarkAutoText(bookmark) - if bookmark.type == "note" then - bookmark.type = "highlight" - end - else - if bookmark.type == "highlight" then - bookmark.type = "note" - end end + bookmark.text = value or bookmark.notes for __, bm in ipairs(self.bookmarks) do if bookmark.datetime == bm.datetime and bookmark.page == bm.page then bm.text = value - bookmark.text_orig = value or bookmark.notes - bookmark.text = bookmark.text_orig -- A bookmark isn't necessarily a highlight (it doesn't have pboxes) if bookmark.pboxes then local setting = G_reader_settings:readSetting("save_document") if setting ~= "disable" then - self.ui.document:updateHighlightContents(bookmark.page, bookmark, value or bookmark.notes) + self.ui.document:updateHighlightContents(bookmark.page, bookmark, bookmark.text) end end - UIManager:close(self.input) - if not from_highlight then - bookmark.text = DISPLAY_PREFIX[bookmark.type] .. bookmark.text - self.refresh() - end break end end UIManager:close(self.input) + if not from_highlight then + bookmark.type = self:getBookmarkType(bookmark) + bookmark.text_orig = bookmark.text + bookmark.text = DISPLAY_PREFIX[bookmark.type] .. bookmark.text + self.refresh() + end end, }, } @@ -948,6 +867,127 @@ function ReaderBookmark:renameBookmark(item, from_highlight) self.input:onShowKeyboard() end +function ReaderBookmark:onSearchBookmark(bm_menu) + local input_dialog + local check_button_case, separator, check_button_bookmark, check_button_highlight, check_button_note + input_dialog = InputDialog:new{ + title = _("Search bookmarks"), + buttons = { + { + { + text = _("Cancel"), + callback = function() + UIManager:close(input_dialog) + end, + }, + { + text = _("Search"), + is_enter_default = true, + callback = function() + local search_str = input_dialog:getInputText() + if not check_button_case.checked then + search_str = Utf8Proc.lowercase(util.fixUtf8(search_str, "?")) + end + local match_table = { + search_str = search_str, + bookmark = check_button_bookmark.checked, + highlight = check_button_highlight.checked, + note = check_button_note.checked, + case_sensitive = check_button_case.checked, + } + UIManager:close(input_dialog) + if bm_menu then -- from bookmark list + for i = #bm_menu.item_table, 1, -1 do + if not self:doesBookmarkMatchTable(bm_menu.item_table[i], match_table) then + table.remove(bm_menu.item_table, i) + end + end + bm_menu:switchItemTable(_("Bookmarks (search results)"), bm_menu.item_table) + self.filtered_mode = true + else -- from main menu + self:onShowBookmark(match_table) + end + end, + }, + }, + }, + } + check_button_case = CheckButton:new{ + text = " " .. _("Case sensitive"), + checked = false, + parent = input_dialog, + max_width = input_dialog._input_widget.width, + callback = function() + check_button_case:toggleCheck() + end, + } + input_dialog:addWidget(check_button_case) + separator = CenterContainer:new{ + dimen = Geom:new{ + w = input_dialog._input_widget.width, + h = 2 * Size.span.vertical_large, + }, + LineWidget:new{ + background = Blitbuffer.COLOR_DARK_GRAY, + dimen = Geom:new{ + w = input_dialog._input_widget.width, + h = Size.line.medium, + } + }, + } + input_dialog:addWidget(separator) + check_button_highlight = CheckButton:new{ + text = " " .. DISPLAY_PREFIX["highlight"] .. _("highlights"), + checked = true, + parent = input_dialog, + max_width = input_dialog._input_widget.width, + callback = function() + check_button_highlight:toggleCheck() + end, + } + input_dialog:addWidget(check_button_highlight) + check_button_note = CheckButton:new{ + text = " " .. DISPLAY_PREFIX["note"] .. _("notes"), + checked = true, + parent = input_dialog, + max_width = input_dialog._input_widget.width, + callback = function() + check_button_note:toggleCheck() + end, + } + input_dialog:addWidget(check_button_note) + check_button_bookmark = CheckButton:new{ + text = " " .. DISPLAY_PREFIX["bookmark"] .. _("page bookmarks"), + checked = true, + parent = input_dialog, + max_width = input_dialog._input_widget.width, + callback = function() + check_button_bookmark:toggleCheck() + end, + } + input_dialog:addWidget(check_button_bookmark) + + UIManager:show(input_dialog) + input_dialog:onShowKeyboard() +end + +function ReaderBookmark:doesBookmarkMatchTable(item, match_table) + if match_table[item.type] then + if match_table.search_str == "" then + return true + else + local text = item.notes + if item.text then -- search in the highlighted text and in the note + text = text .. "\u{FFFF}" .. item.text + end + if not match_table.case_sensitive then + text = Utf8Proc.lowercase(util.fixUtf8(text, "?")) + end + return text:find(match_table.search_str) + end + end +end + function ReaderBookmark:toggleBookmark(pn_or_xp) local index = self:getDogearBookmarkIndex(pn_or_xp) if index then @@ -1087,8 +1127,20 @@ function ReaderBookmark:getNumberOfHighlightsAndNotes() return highlights, notes 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 not self.ui.document.info.has_pages then + if self.ui.rolling then if self.ui.pagemap and self.ui.pagemap:wantsPageLabels() then page = self.ui.pagemap:getXPointerPageLabel(page, true) else @@ -1117,7 +1169,8 @@ end --- Check if the 'text' field has not been edited manually function ReaderBookmark:isBookmarkAutoText(bookmark) - return (bookmark.text == nil) or (bookmark.text == self:getBookmarkAutoText(bookmark, true)) + return (bookmark.text == nil) or (bookmark.text == bookmark.notes) + or (bookmark.text == self:getBookmarkAutoText(bookmark, true)) end function ReaderBookmark:isHighlightAutoText(item) diff --git a/frontend/dispatcher.lua b/frontend/dispatcher.lua index 4966a6c72..c7aa5bed0 100644 --- a/frontend/dispatcher.lua +++ b/frontend/dispatcher.lua @@ -118,6 +118,7 @@ local settingsList = { clear_location_history = {category="none", event="ClearLocationStack", arg=true, title=_("Clear location history"), reader=true, separator=true}, toc = {category="none", event="ShowToc", title=_("Table of contents"), reader=true}, bookmarks = {category="none", event="ShowBookmark", title=_("Bookmarks"), reader=true}, + bookmark_search = {category="none", event="SearchBookmark", title=_("Bookmark search"), reader=true}, book_status = {category="none", event="ShowBookStatus", title=_("Book status"), reader=true}, book_info = {category="none", event="ShowBookInfo", title=_("Book information"), reader=true}, book_description = {category="none", event="ShowBookDescription", title=_("Book description"), reader=true}, @@ -278,6 +279,7 @@ local dispatcher_menu_order = { "toc", "bookmarks", + "bookmark_search", "book_status", "book_info", diff --git a/frontend/ui/elements/reader_menu_order.lua b/frontend/ui/elements/reader_menu_order.lua index 58bff2f83..3ecf88a0c 100644 --- a/frontend/ui/elements/reader_menu_order.lua +++ b/frontend/ui/elements/reader_menu_order.lua @@ -187,6 +187,7 @@ local order = { "----------------------------", "find_book_in_calibre_catalog", "fulltext_search", + "bookmark_search", }, filemanager = {}, main = {