diff --git a/base b/base index af763e714..5a83d4aad 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit af763e714395a9bf1a2356f7fb99a020c0387684 +Subproject commit 5a83d4aaded90420306380613ccdc4613a53a9ce diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index b1b5829d0..530a8a91c 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -111,19 +111,24 @@ function ReaderHighlight:onSetDimensions(dimen) end function ReaderHighlight:clear() + if self.ui.document.info.has_pages then + self.view.highlight.temp = {} + else + self.ui.document:clearSelection() + end + UIManager:setDirty(self.dialog, "partial") if self.hold_pos then - if self.ui.document.info.has_pages then - self.view.highlight.temp[self.hold_pos.page] = nil - else - self.ui.document:clearSelection() - end self.hold_pos = nil self.selected_text = nil - UIManager:setDirty(self.dialog, "partial") return true end end +function ReaderHighlight:onClearHighlight() + self:clear() + return true +end + function ReaderHighlight:onTap(arg, ges) if not self:clear() then if self.ui.document.info.has_pages then @@ -333,11 +338,10 @@ function ReaderHighlight:onHoldRelease() }, { { - text = _("Share"), - enabled = false, + text = _("Search"), callback = function() - self:shareHighlight() - self:onClose() + self:onHighlightSearch() + UIManager:close(self.highlight_dialog) end, }, { @@ -357,16 +361,20 @@ function ReaderHighlight:onHoldRelease() return true end -function ReaderHighlight:onHighlight() +function ReaderHighlight:highlightFromHoldPos() if self.hold_pos then if not self.selected_text then self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.hold_pos) DEBUG("selected text:", self.selected_text) end - self:saveHighlight() end end +function ReaderHighlight:onHighlight() + self:highlightFromHoldPos() + self:saveHighlight() +end + function ReaderHighlight:saveHighlight() DEBUG("save highlight") local page = self.hold_pos.page @@ -427,6 +435,14 @@ function ReaderHighlight:lookupWikipedia() end end +function ReaderHighlight:onHighlightSearch() + DEBUG("search highlight") + self:highlightFromHoldPos() + if self.selected_text then + self.ui:handleEvent(Event:new("ShowSearchDialog", self.selected_text.text)) + end +end + function ReaderHighlight:shareHighlight() DEBUG("share highlight") end diff --git a/frontend/apps/reader/modules/readersearch.lua b/frontend/apps/reader/modules/readersearch.lua new file mode 100644 index 000000000..ce35ea661 --- /dev/null +++ b/frontend/apps/reader/modules/readersearch.lua @@ -0,0 +1,100 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local ButtonDialog = require("ui/widget/buttondialog") +local UIManager = require("ui/uimanager") +local Geom = require("ui/geometry") +local Screen = require("ui/screen") +local DEBUG = require("dbg") +local _ = require("gettext") + +local ReaderSearch = InputContainer:new{ + direction = 0, -- 0 for search forward, 1 for search backward + case_insensitive = 1, -- default to case insensitive +} + +function ReaderSearch:init() + self.ui.menu:registerToMainMenu(self) +end + +function ReaderSearch:addToMainMenu(tab_item_table) + table.insert(tab_item_table.plugins, { + text = _("Fulltext search"), + tap_input = { + title = _("Input text to search for"), + type = "text", + callback = function(input) + self:onShowSearchDialog(input) + end, + }, + }) +end + +function ReaderSearch:onShowSearchDialog(text) + local do_search = function(search_func, text, param) + return function() + local res = search_func(self, text, param) + if res then + self.ui.link:onGotoLink(res[1].start) + end + end + end + self.search_dialog = ButtonDialog:new{ + alpha = 0.5, + buttons = { + { + { + text = "|<", + callback = do_search(self.searchFromStart, text), + }, + { + text = "<", + callback = do_search(self.searchNext, text, 1), + }, + { + text = ">", + callback = do_search(self.searchNext, text, 0), + }, + { + text = ">|", + callback = do_search(self.searchFromEnd, text), + }, + } + }, + tap_close_callback = function() + DEBUG("highlight clear") + self.ui.highlight:clear() + end, + } + local res = do_search(self.searchFromCurrent, text, 0)() + UIManager:show(self.search_dialog) + UIManager:setDirty(self.dialog, "partial") + return true +end + +function ReaderSearch:search(pattern, origin) + local direction = self.direction + local case = self.case_insensitive + return self.ui.document:findText(pattern, origin, direction, case) +end + +function ReaderSearch:searchFromStart(pattern) + self.direction = 0 + return self:search(pattern, -1) +end + +function ReaderSearch:searchFromEnd(pattern) + self.direction = 1 + return self:search(pattern, -1) +end + +function ReaderSearch:searchFromCurrent(pattern, direction) + self.direction = direction + return self:search(pattern, 0) +end + +-- ignore current page and search next occurrence +function ReaderSearch:searchNext(pattern, direction) + self.direction = direction + return self:search(pattern, 1) +end + +return ReaderSearch diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 2cd9d5c5c..cf73ece7e 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -37,9 +37,10 @@ local ReaderDictionary = require("apps/reader/modules/readerdictionary") local ReaderWikipedia = require("apps/reader/modules/readerwikipedia") local ReaderHyphenation = require("apps/reader/modules/readerhyphenation") local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindicator") +local FileManagerHistory = require("apps/filemanager/filemanagerhistory") +local ReaderSearch = require("apps/reader/modules/readersearch") local ReaderLink = require("apps/reader/modules/readerlink") local PluginLoader = require("apps/reader/pluginloader") -local FileManagerHistory = require("apps/filemanager/filemanagerhistory") --[[ This is an abstraction for a reader interface @@ -280,6 +281,12 @@ function ReaderUI:init() ui = self }) end + -- fulltext search + self:registerModule("search", ReaderSearch:new{ + dialog = self.dialog, + view = self.view, + ui = self + }) -- koreader plugins for _,module in ipairs(PluginLoader:loadPlugins()) do diff --git a/frontend/document/credocument.lua b/frontend/document/credocument.lua index 6e471615b..c644696cf 100644 --- a/frontend/document/credocument.lua +++ b/frontend/document/credocument.lua @@ -312,6 +312,7 @@ function CreDocument:setFontFace(new_font_face) end function CreDocument:clearSelection() + DEBUG("clear selection") self._document:clearSelection() end @@ -418,6 +419,11 @@ function CreDocument:setStatusLineProp(prop) self._document:setStringProperty("window.status.line", prop) end +function CreDocument:findText(pattern, origin, reverse, caseInsensitive) + DEBUG("CreDocument: find text", pattern, origin, reverse, caseInsensitive) + return self._document:findText(pattern, origin, reverse, caseInsensitive) +end + function CreDocument:register(registry) registry:addProvider("txt", "application/txt", self) registry:addProvider("epub", "application/epub", self) diff --git a/frontend/document/document.lua b/frontend/document/document.lua index f274b6dda..c77de744d 100644 --- a/frontend/document/document.lua +++ b/frontend/document/document.lua @@ -217,6 +217,10 @@ function Document:getCoverPageImage() return nil end +function Document:findText() + return nil +end + function Document:getFullPageHash(pageno, zoom, rotation, gamma, render_mode) return "renderpg|"..self.file.."|"..self.mod_time.."|"..pageno.."|" ..zoom.."|"..rotation.."|"..gamma.."|"..render_mode diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 8dc856750..ec2e247fb 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -187,10 +187,10 @@ function DictQuickLookup:update() end, }, { - text = _("More"), - enabled = false, + text = _("Search"), callback = function() - self.ui:handleEvent(Event:new("HighlightMore")) + self.ui:handleEvent(Event:new("HighlightSearch")) + UIManager:close(self) end, }, }, diff --git a/spec/unit/readersearch_spec.lua b/spec/unit/readersearch_spec.lua new file mode 100644 index 000000000..7c982277c --- /dev/null +++ b/spec/unit/readersearch_spec.lua @@ -0,0 +1,94 @@ +require("commonrequire") +local DocumentRegistry = require("document/documentregistry") +local ReaderUI = require("apps/reader/readerui") +local DEBUG = require("dbg") + +local sample_epub = "spec/front/unit/data/juliet.epub" + +describe("Readersearch module", function() + describe("search API for EPUB documents", function() + local doc, search, rolling + setup(function() + local readerui = ReaderUI:new{ + document = DocumentRegistry:openDocument(sample_epub), + } + doc = readerui.document + search = readerui.search + rolling = readerui.rolling + end) + it("should search backward", function() + rolling:gotoPage(10) + assert.truthy(search:searchFromCurrent("Verona", 1)) + for i = 1, 100, 10 do + rolling:gotoPage(i) + local words = search:searchFromCurrent("Verona", 1) + if words then + for _, word in ipairs(words) do + local pageno = doc:getPageFromXPointer(word.start) + --DEBUG("found at pageno", pageno) + assert.truthy(pageno <= i) + end + end + end + end) + it("should search forward", function() + rolling:gotoPage(10) + assert.truthy(search:searchFromCurrent("Verona", 0)) + for i = 1, 100, 10 do + rolling:gotoPage(i) + local words = search:searchFromCurrent("Verona", 0) + if words then + for _, word in ipairs(words) do + local pageno = doc:getPageFromXPointer(word.start) + --DEBUG("found at pageno", pageno) + assert.truthy(pageno >= i) + end + end + end + end) + it("should find the first occurrence", function() + for i = 10, 100, 10 do + rolling:gotoPage(i) + local words = search:searchFromStart("Verona") + assert.truthy(words) + local pageno = doc:getPageFromXPointer(words[1].start) + assert.are.equal(8, pageno) + end + for i = 1, 5, 1 do + rolling:gotoPage(i) + local words = search:searchFromStart("Verona") + assert(words == nil) + end + end) + it("should find the last occurrence", function() + for i = 100, 200, 10 do + rolling:gotoPage(i) + local words = search:searchFromEnd("Verona") + assert.truthy(words) + local pageno = doc:getPageFromXPointer(words[1].start) + assert.are.equal(208, pageno) + end + for i = 230, 235, 1 do + rolling:gotoPage(i) + local words = search:searchFromEnd("Verona") + assert(words == nil) + end + end) + it("should find all occurrences", function() + local count = 0 + rolling:gotoPage(1) + local words = search:searchFromCurrent("Verona", 0) + while words do + count = count + #words + for _, word in ipairs(words) do + --DEBUG("found word", word.start) + end + doc:gotoXPointer(words[1].start) + words = search:searchNext("Verona", 0) + end + assert.are.equal(13, count) + end) + end) + describe("search API for PDF documents", function() + end) +end)