From 1708fd5e1c4999e16e59e79cb9c3dfb294989357 Mon Sep 17 00:00:00 2001 From: poire-z Date: Tue, 6 Dec 2016 22:15:52 +0100 Subject: [PATCH] Dictionary and wikipedia enhancements (#2393) Stardict: - remove duplicate results - better cleaning of selection - append results from a 2nd query of a 2nd set of dictionaries in data/dict_ext/ Wikipedia: - use the search API for better results - allow viewing the full page content of a result in a bigger window - allow queries for multiple languages - available languages can be set in settings.reader.lua : ["wikipedia_languages"] = {"en", "fr", "it"} - "Wikipedia lookup" added to Tools menu For both: - allow selection of multiple words for a new lookup (so one can actually browse wikipedia) - allow continuous reading with Tap - display "current result / total number of results" Details in #2393 --- .../apps/reader/modules/readerdictionary.lua | 130 +++++++-- .../apps/reader/modules/readerhighlight.lua | 3 +- .../apps/reader/modules/readerwikipedia.lua | 134 +++++++-- frontend/ui/widget/dictquicklookup.lua | 256 ++++++++++++++---- frontend/ui/wikipedia.lua | 57 +++- 5 files changed, 474 insertions(+), 106 deletions(-) diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index 9a93b22e6..e7756892e 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -7,12 +7,14 @@ local Screen = require("device").screen local Device = require("device") local JSON = require("json") local DEBUG = require("dbg") +local util = require("util") local _ = require("gettext") local T = require("ffi/util").template local ReaderDictionary = InputContainer:new{ data_dir = nil, dict_window_list = {}, + lookup_msg = _("Searching dictionary for:\n%1") } function ReaderDictionary:init() @@ -40,7 +42,7 @@ function ReaderDictionary:onLookupWord(word, box, highlight) return true end -local function tidy_markup(results) +local function tidyMarkup(results) local cdata_tag = "" local format_escape = "&[29Ib%+]{(.-)}" for _, result in ipairs(results) do @@ -64,20 +66,97 @@ local function tidy_markup(results) return results end +function ReaderDictionary:cleanSelection(text) + -- Will be used by ReaderWikipedia too + if not text then + return "" + end + -- We do multiple times the same replacements, which is most of the time overkill, + -- but sometimes provices better cleaning + -- Some extremes cases to explain the multiples gsub : + -- + -- Sample epub html: mais, qu’« absolument, on ne peut décidément pas » revenir en arrière + -- Holding on "qu" or "absolument" will make crengine returns: qu’« absolument, + -- We want to only get: absolument + -- + -- Sample epub html: car « l’état, actuel, de notre connaissance » s’y oppose + -- Holding on "état" will make crengine returns: « l’état, + -- We want to get: état + -- + -- Some of these gsub could be removed when crengine does a better job + -- at finding word boundaries + -- + -- Strip some quotations marks + text = string.gsub(text, "\xC2\xAB", '') -- U+00AB << (left double angle quotation mark) + text = string.gsub(text, "\xC2\xBB", '') -- U+00BB >> (right double angle quotation mark) + text = string.gsub(text, "\xE2\x80\x9D", '') -- U+201D '' (right double quotation mark) + text = string.gsub(text, "\xE2\x80\x9C", '') -- U+201C `` (left double quotation mark) + text = string.gsub(text, "\xE2\x80\x94", '') -- U+2014 - (em dash) + text = string.gsub(text, "\xE2\x80\x95", '') -- U+2015 - (horizontal bar) + text = string.gsub(text, "\xC2\xA0", '') -- U+00A0 no-break space + -- Replace some meaningful quotes with ascii quote + text = string.gsub(text, "\xE2\x80\x99", "'") -- U+2019 (right single quotation mark) + -- Strip punctuation characters around selection + -- (this had to be done after the utf8 gsubs above, or it would strip part of these utf8 chars) + text = util.stripePunctuations(text) + -- Strip leading and trailing spaces + text = string.gsub(text, "^%s+", '') + text = string.gsub(text, "%s+$", '') + -- Strip some french grammatical constructs + text = string.gsub(text, "^[LSDMNTlsdmnt]'", '') -- french l' s' t' + text = string.gsub(text, "^[Qq][Uu]'", '') -- french qu' + -- Strip again leading and trailing spaces + text = string.gsub(text, "^%s+", '') + text = string.gsub(text, "%s+$", '') + return text +end + +function ReaderDictionary:onLookupStarted(word) + local text = T(self.lookup_msg, word) + self.lookup_progress_msg = InfoMessage:new{text=text} + UIManager:show(self.lookup_progress_msg) + UIManager:forceRePaint() +end + +function ReaderDictionary:onLookupDone() + if self.lookup_progress_msg then + UIManager:close(self.lookup_progress_msg) + UIManager:forceRePaint() + end + self.lookup_progress_msg = nil +end + function ReaderDictionary:stardictLookup(word, box) DEBUG("lookup word:", word, box) - if word then - word = require("util").stripePunctuations(word) - DEBUG("stripped word:", word) - -- escape quotes and other funny characters in word + -- escape quotes and other funny characters in word + word = self:cleanSelection(word) + DEBUG("stripped word:", word) + if word == "" then + return + end + self:onLookupStarted(word) + local final_results = {} + local seen_results = {} + -- Allow for two sdcv calls : one in the classic data/dict, and + -- another one in data/dict_ext if it exists + -- We could put in data/dict_ext dictionaries with a great number of words + -- but poor definitions as a fall back. If these were in data/dict, + -- they would prevent fuzzy searches in other dictories with better + -- definitions, and masks such results. This way, we can get both. + local dict_dirs = {self.data_dir} + local dict_ext = self.data_dir.."_ext" + if lfs.attributes(dict_ext, "mode") == "directory" then + table.insert(dict_dirs, dict_ext) + end + for _, dict_dir in ipairs(dict_dirs) do local results_str = nil if Device:isAndroid() then local A = require("android") results_str = A.stdout("./sdcv", "--utf8-input", "--utf8-output", - "-nj", word, "--data-dir", self.data_dir) + "-nj", word, "--data-dir", dict_dir) else local std_out = io.popen("./sdcv --utf8-input --utf8-output -nj " - .. ("%q"):format(word) .. " --data-dir " .. self.data_dir, "r") + .. ("%q"):format(word) .. " --data-dir " .. dict_dir, "r") if std_out then results_str = std_out:read("*all") std_out:close() @@ -86,21 +165,33 @@ function ReaderDictionary:stardictLookup(word, box) --DEBUG("result str:", word, results_str) local ok, results = pcall(JSON.decode, results_str) if ok and results then - --DEBUG("lookup result table:", word, results) - self:showDict(word, tidy_markup(results), box) + -- we may get duplicates (sdcv may do multiple queries, + -- in fixed mode then in fuzzy mode), we have to remove them + local h + for _,r in ipairs(results) do + h = r.dict .. r.word .. r.definition + if seen_results[h] == nil then + table.insert(final_results, r) + seen_results[h] = true + end + end else DEBUG("JSON data cannot be decoded", results) - -- dummy results - results = { - { - dict = "", - word = word, - definition = _("No definition found."), - } - } - self:showDict(word, results, box) end end + if #final_results == 0 then + -- dummy results + final_results = { + { + dict = "", + word = word, + definition = _("No definition found."), + } + } + end + self:onLookupDone() + --DEBUG("lookup result table:", word, final_results) + self:showDict(word, tidyMarkup(final_results), box) end function ReaderDictionary:showDict(word, results, box) @@ -118,7 +209,8 @@ function ReaderDictionary:showDict(word, results, box) width = Screen:getWidth() - Screen:scaleBySize(80), word_box = box, -- differentiate between dict and wiki - wiki = self.wiki, + is_wiki = self.is_wiki, + wiki_languages = self.wiki_languages, } table.insert(self.dict_window_list, self.dict_window) UIManager:show(self.dict_window) diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 48eeee124..788534cd1 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -494,8 +494,7 @@ function ReaderHighlight:onHighlightDictLookup() DEBUG("dictionary lookup highlight") self:highlightFromHoldPos() if self.selected_text then - local text = require("util").stripePunctuations(self.selected_text.text) - self.ui:handleEvent(Event:new("LookupWord", text)) + self.ui:handleEvent(Event:new("LookupWord", self.selected_text.text)) end end diff --git a/frontend/apps/reader/modules/readerwikipedia.lua b/frontend/apps/reader/modules/readerwikipedia.lua index 050ecc1e3..b74f9b3c9 100644 --- a/frontend/apps/reader/modules/readerwikipedia.lua +++ b/frontend/apps/reader/modules/readerwikipedia.lua @@ -3,65 +3,147 @@ local Translator = require("ui/translator") local Wikipedia = require("ui/wikipedia") local DEBUG = require("dbg") local _ = require("gettext") +local T = require("ffi/util").template -- Wikipedia as a special dictionary local ReaderWikipedia = ReaderDictionary:extend{ -- identify itself - wiki = true, + is_wiki = true, + wiki_languages = {}, no_page = _("No wiki page found."), + lookup_msg = _("Searching Wikipedia for:\n%1") } --- the super "class" ReaderDictionary has already registers a menu entry --- we should override the init function in ReaderWikipedia function ReaderWikipedia:init() + self.ui.menu:registerToMainMenu(self) end -function ReaderWikipedia:onLookupWikipedia(word, box) - -- set language from book properties - local lang = self.view.document:getProps().language - if lang == nil then - -- or set laguage from KOReader settings - lang = G_reader_settings:readSetting("language") - if lang == nil then - -- or detect language - local ok_translator +function ReaderWikipedia:addToMainMenu(tab_item_table) + table.insert(tab_item_table.plugins, { + text = _("Wikipedia lookup"), + tap_input = { + title = _("Enter words to look up on Wikipedia"), + type = "text", + callback = function(input) + self:onLookupWikipedia(input) + end, + }, + }) +end + +function ReaderWikipedia:initLanguages(word) + if #self.wiki_languages > 0 then -- already done + return + end + -- Fill self.wiki_languages with languages to propose + local wikipedia_languages = G_reader_settings:readSetting("wikipedia_languages") + if type(wikipedia_languages) == "table" and #wikipedia_languages > 0 then + -- use this setting, no need to guess + self.wiki_languages = wikipedia_languages + else + -- guess some languages + self.seen_lang = {} + local addLanguage = function(lang) + if lang and lang ~= "" then + -- convert "zh-CN" and "zh-TW" to "zh" + lang = lang:match("(.*)-") or lang + if lang == "C" then lang="en" end + lang = lang:lower() + if not self.seen_lang[lang] then + table.insert(self.wiki_languages, lang) + self.seen_lang[lang] = true + end + end + end + -- use book and UI languages + addLanguage(self.view.document:getProps().language) + addLanguage(G_reader_settings:readSetting("language")) + if #self.wiki_languages == 0 and word then + -- if no language at all, do a translation of selected word + local ok_translator, lang ok_translator, lang = pcall(Translator.detect, Translator, word) - if not ok_translator then return end + if ok_translator then + addLanguage(lang) + end end + -- add english anyway, so we have at least one language + addLanguage("en") + end +end + +function ReaderWikipedia:onLookupWikipedia(word, box, get_fullpage) + -- word is the text to query. If get_fullpage is true, it is the + -- exact wikipedia page title we want the full page of. + self:initLanguages(word) + -- use first lang from self.wiki_languages, which may have been rotated by DictQuickLookup + local lang = self.wiki_languages[1] + DEBUG("lookup word:", word, box, get_fullpage) + -- no need to clean word if get_fullpage, as it is the exact wikipetia page title + if word and not get_fullpage then + -- escape quotes and other funny characters in word + word = self:cleanSelection(word) + -- no need to lower() word with wikipedia search + end + DEBUG("stripped word:", word) + if word == "" then + return end - -- convert "zh-CN" and "zh-TW" to "zh" - lang = lang:match("(.*)-") or lang - -- strip punctuation characters around selected word - word = string.gsub(word, "^%p+", '') - word = string.gsub(word, "%p+$", '') - -- seems lower case phrase has higher hit rate - word = string.lower(word) + self:onLookupStarted(word) local results = {} - local ok, pages = pcall(Wikipedia.wikintro, Wikipedia, word, lang) + local ok, pages + if get_fullpage then + ok, pages = pcall(Wikipedia.wikifull, Wikipedia, word, lang) + else + ok, pages = pcall(Wikipedia.wikintro, Wikipedia, word, lang) + end if ok and pages then + -- sort pages according to 'index' attribute if present (not present + -- in fullpage results) + local sorted_pages = {} + local has_indexes = false + for pageid, page in pairs(pages) do + if page.index ~= nil then + sorted_pages[page.index+1] = page + has_indexes = true + end + end + if has_indexes then + pages = sorted_pages + end for pageid, page in pairs(pages) do + local definition = page.extract or self.no_page + if page.length then + -- we get 'length' only for intro results + -- let's append it to definition so we know + -- how big/valuable the full page is + local fullkb = math.ceil(page.length/1024) + local more_factor = math.ceil( page.length / (1+definition:len()) ) -- +1 just in case len()=0 + definition = definition .. "\n" .. T(_("(full page : %1 kB, = %2 x this intro length)"), fullkb, more_factor) + end local result = { - dict = _("Wikipedia"), + dict = T(_("Wikipedia %1"), lang:upper()), word = page.title, - definition = page.extract or self.no_page, + definition = definition, + is_fullpage = get_fullpage, } table.insert(results, result) end DEBUG("lookup result:", word, results) - self:showDict(word, results, box) else DEBUG("error:", pages) -- dummy results results = { { - dict = _("Wikipedia"), + dict = T(_("Wikipedia %1"), lang:upper()), word = word, definition = self.no_page, + is_fullpage = get_fullpage, } } DEBUG("dummy result table:", word, results) - self:showDict(word, results, box) end + self:onLookupDone() + self:showDict(word, results, box) end -- override onSaveSettings in ReaderDictionary diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index ea100734f..c70d6fdf0 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -21,6 +21,7 @@ local Event = require("ui/event") local Font = require("ui/font") local DEBUG = require("dbg") local _ = require("gettext") +local T = require("ffi/util").template local Blitbuffer = require("ffi/blitbuffer") --[[ @@ -31,9 +32,11 @@ local DictQuickLookup = InputContainer:new{ lookupword = nil, dictionary = nil, definition = nil, + displayword = nil, + is_wiki = false, + is_fullpage = false, dict_index = 1, title_face = Font:getFace("tfont", 22), - word_face = Font:getFace("tfont", 22), content_face = Font:getFace("cfont", DDICT_FONT_SIZE), width = nil, height = nil, @@ -44,6 +47,9 @@ local DictQuickLookup = InputContainer:new{ title_margin = Screen:scaleBySize(2), word_padding = Screen:scaleBySize(5), word_margin = Screen:scaleBySize(2), + -- alt padding/margin for wiki to compensate for reduced font size + wiki_word_padding = Screen:scaleBySize(7), + wiki_word_margin = Screen:scaleBySize(3), definition_padding = Screen:scaleBySize(2), definition_margin = Screen:scaleBySize(2), button_padding = Screen:scaleBySize(14), @@ -78,67 +84,134 @@ function DictQuickLookup:init() } }, }, - HoldWord = { + -- This was for selection of a single word with simple hold + -- HoldWord = { + -- GestureRange:new{ + -- ges = "hold", + -- range = function() + -- return self.region + -- end, + -- }, + -- -- callback function when HoldWord is handled as args + -- args = function(word) + -- self.ui:handleEvent( + -- -- don't pass self.highlight to subsequent lookup, we want + -- -- the first to be the only one to unhighlight selection + -- -- when closed + -- Event:new("LookupWord", word, self.word_box)) + -- end + -- }, + -- Allow selection of one or more words (see textboxwidget.lua) : + HoldStartText = { GestureRange:new{ ges = "hold", range = function() return self.region end, }, - -- callback function when HoldWord is handled as args - args = function(word) + }, + HoldReleaseText = { + GestureRange:new{ + ges = "hold_release", + range = function() + return self.region + end, + }, + -- callback function when HoldReleaseText is handled as args + args = function(text, hold_duration) + local lookup_target + if hold_duration < 2.0 then + -- do this lookup in the same domain (dict/wikipedia) + lookup_target = self.is_wiki and "LookupWikipedia" or "LookupWord" + else + -- but allow switching domain with a long hold + lookup_target = self.is_wiki and "LookupWord" or "LookupWikipedia" + end self.ui:handleEvent( - Event:new("LookupWord", word, self.word_box, self.highlight)) + -- don't pass self.highlight to subsequent lookup, we want + -- the first to be the only one to unhighlight selection + -- when closed + Event:new(lookup_target, text) + ) end }, } - table.insert(self.dict_bar, - CloseButton:new{ window = self, }) end end function DictQuickLookup:update() local orig_dimen = self.dict_frame and self.dict_frame.dimen or Geom:new{} - -- calculate window dimension and try to not hide highlighted word + -- calculate window dimension self.align = "center" self.region = Geom:new{ x = 0, y = 0, w = Screen:getWidth(), h = Screen:getHeight(), } - if self.word_box then - local box = self.word_box - if box.y + box.h/2 < Screen:getHeight()*0.3 then - self.region.y = box.y + box.h - self.region.h = Screen:getHeight() - box.y - box.h - self.align = "top" - elseif box.y + box.h/2 > Screen:getHeight()*0.7 then - self.region.y = 0 - self.region.h = box.y - self.align = "bottom" + if self.is_fullpage then + -- bigger window if fullpage being shown - this will let + -- some room anyway for footer display (time, battery...) + self.height = Screen:getHeight() + self.width = Screen:getWidth() - Screen:scaleBySize(40) + else + -- smaller window otherwise + -- try to not hide highlighted word + if self.word_box then + local box = self.word_box + if box.y + box.h/2 < Screen:getHeight()*0.3 then + self.region.y = box.y + box.h + self.region.h = Screen:getHeight() - box.y - box.h + self.align = "top" + elseif box.y + box.h/2 > Screen:getHeight()*0.7 then + self.region.y = 0 + self.region.h = box.y + self.align = "bottom" + end end + self.height = math.min(self.region.h*0.7, Screen:getHeight()*0.5) end - self.height = math.min(self.region.h*0.7, Screen:getHeight()*0.5) -- dictionary title + local dict_title_text = TextWidget:new{ + text = self.dictionary, + face = self.title_face, + bold = true, + width = self.width, + } + -- Some different UI tweaks for dict or wiki + local lookup_word_font_size, lookup_word_padding, lookup_word_margin + if self.is_wiki then + -- visual hint : dictionary title left adjusted, Wikipedia title centered + dict_title_text = CenterContainer:new{ + dimen = Geom:new{ + w = self.width, + h = dict_title_text:getSize().h, + }, + dict_title_text + } + -- Wikipedia has longer titles, so use a smaller font + lookup_word_font_size = 18 + lookup_word_padding = self.wiki_word_padding + lookup_word_margin = self.wiki_word_margin + else + -- Usual font size for dictionary + lookup_word_font_size = 22 + lookup_word_padding = self.word_padding + lookup_word_margin = self.word_margin + end self.dict_title = FrameContainer:new{ padding = self.title_padding, margin = self.title_margin, bordersize = 0, - TextWidget:new{ - text = self.dictionary, - face = self.title_face, - bold = true, - width = self.width, - } + dict_title_text } -- lookup word local lookup_word = Button:new{ - padding = self.word_padding, - margin = self.word_margin, + padding = lookup_word_padding, + margin = lookup_word_margin, bordersize = 0, - text = self.lookupword, + text = self.displayword, text_font_face = "tfont", - text_font_size = 22, + text_font_size = lookup_word_font_size, hold_callback = function() self:lookupInputWord(self.lookupword) end, } -- word definition @@ -150,14 +223,27 @@ function DictQuickLookup:update() text = self.definition, face = self.content_face, width = self.width, - height = self.height*0.7, + -- get a bit more height for definition as wiki has one less button raw + height = self.is_fullpage and self.height*0.75 or self.height*0.7, dialog = self, }, } - local button_table = ButtonTable:new{ - width = math.max(self.width, definition:getSize().w), - button_font_face = "cfont", - button_font_size = 20, + -- Different sets of buttons if fullpage or not + local buttons + if self.is_fullpage then + -- Only a single wide close button, get a little more room for + -- closing by taping at bottom (on footer or on this button) + buttons = { + { + { + text = "Close", + callback = function() + UIManager:close(self) + end, + }, + }, + } + else buttons = { { { @@ -185,30 +271,48 @@ function DictQuickLookup:update() }, { { - text = _("Wikipedia"), - enabled = not self.wiki, + -- if dictionary result, do the same search on wikipedia + -- if already wiki, get the full page for the current result + text = self.is_wiki and _("Wikipedia full") or _("Wikipedia"), callback = function() UIManager:scheduleIn(0.1, function() - self:lookupWikipedia() + self:lookupWikipedia(self.is_wiki) -- will get_fullpage if is_wiki end) end, }, + -- Rotate thru available wikipedia languages (disabled if dictionary window) + -- (replace previous unimplemented "Add Note") { - text = _("Add Note"), - enabled = false, + -- if more than one language, enable it and display "current lang > next lang" + -- otherwise, just display current lang + text = self.is_wiki and ( #self.wiki_languages > 1 and self.wiki_languages[1].." > "..self.wiki_languages[2] or self.wiki_languages[1] ) or "-", + enabled = self.is_wiki and #self.wiki_languages > 1, callback = function() - self.ui:handleEvent(Event:new("HighlightAddNote")) + -- rotate wiki_languages + local current_lang = table.remove(self.wiki_languages, 1) + table.insert(self.wiki_languages, current_lang) + UIManager:close(self) + self:lookupWikipedia() end, }, { - text = _("Search"), + text = self.is_wiki and _("Close") or _("Search"), callback = function() - self.ui:handleEvent(Event:new("HighlightSearch")) + if not self.is_wiki then + self.ui:handleEvent(Event:new("HighlightSearch")) + end UIManager:close(self) end, }, }, - }, + } + end + + local button_table = ButtonTable:new{ + width = math.max(self.width, definition:getSize().w), + button_font_face = "cfont", + button_font_size = 20, + buttons = buttons, zero_sep = true, show_parent = self, } @@ -225,6 +329,7 @@ function DictQuickLookup:update() h = self.dict_title:getSize().h }, self.dict_title, + CloseButton:new{ window = self, }, } self.dict_frame = FrameContainer:new{ @@ -318,11 +423,19 @@ function DictQuickLookup:isNextDictAvaiable() end function DictQuickLookup:changeToPrevDict() - self:changeDictionary(self.dict_index - 1) + if self:isPrevDictAvaiable() then + self:changeDictionary(self.dict_index - 1) + elseif #self.results > 1 then -- restart at end if first reached + self:changeDictionary(#self.results) + end end function DictQuickLookup:changeToNextDict() - self:changeDictionary(self.dict_index + 1) + if self:isNextDictAvaiable() then + self:changeDictionary(self.dict_index + 1) + elseif #self.results > 1 then -- restart at first if end reached + self:changeDictionary(1) + end end function DictQuickLookup:changeDictionary(index) @@ -331,6 +444,19 @@ function DictQuickLookup:changeDictionary(index) self.dictionary = self.results[index].dict self.lookupword = self.results[index].word self.definition = self.results[index].definition + self.is_fullpage = self.results[index].is_fullpage + if self.is_fullpage then + self.displayword = self.lookupword + else + -- add "dict_index / nbresults" to displayword, so we know where + -- we're at and what's yet to see + self.displayword = self.lookupword.." "..index.." / "..#self.results + -- add queried word to 1st result's definition, so we can see + -- what was the selected text and if we selected wrong + if index == 1 then + self.definition = self.definition.."\n_______\n"..T(_("(query : %1)"), self.word) + end + end self:update() end @@ -374,10 +500,19 @@ function DictQuickLookup:onTapCloseDict(arg, ges_ev) if ges_ev.pos:notIntersectWith(self.dict_frame.dimen) then self:onClose() return true - elseif not ges_ev.pos:notIntersectWith(self.dict_title.dimen) then + elseif not ges_ev.pos:notIntersectWith(self.dict_title.dimen) and not self.is_wiki then self.ui:handleEvent(Event:new("UpdateDefaultDict", self.dictionary)) return true end + -- Allow for changing dict with tap (tap event will be first + -- processed for scrolling definition by ScrollTextWidget, which + -- will pop it up for us here when it can't scroll anymore). + -- This allow for continuous reading of results' definitions with tap. + if ges_ev.pos.x < Screen:getWidth()/2 then + self:changeToPrevDict() + else + self:changeToNextDict() + end return true end @@ -390,7 +525,11 @@ function DictQuickLookup:onClose() end end if self.highlight then - self.highlight:clear() + -- delay unhighlight of selection, so we can see where we stopped when + -- back from our journey into dictionary or wikipedia + UIManager:scheduleIn(1, function() + self.highlight:clear() + end) end return true end @@ -399,6 +538,12 @@ function DictQuickLookup:onHoldClose() self:onClose() for i = #self.window_list, 1, -1 do local window = self.window_list[i] + -- if one holds a highlight, let's clear it like in onClose() + if window.highlight then + UIManager:scheduleIn(1, function() + window.highlight:clear() + end) + end UIManager:close(window) table.remove(self.window_list, i) end @@ -447,7 +592,7 @@ end function DictQuickLookup:inputLookup() local word = self.input_dialog:getInputText() if word and word ~= "" then - local event = self.wiki and "LookupWikipedia" or "LookupWord" + local event = self.is_wiki and "LookupWikipedia" or "LookupWord" self.ui:handleEvent(Event:new(event, word)) end end @@ -456,8 +601,19 @@ function DictQuickLookup:closeInputDialog() UIManager:close(self.input_dialog) end -function DictQuickLookup:lookupWikipedia() - self.ui:handleEvent(Event:new("LookupWikipedia", self.word, self.word_box)) +function DictQuickLookup:lookupWikipedia(get_fullpage) + local word + if get_fullpage then + -- we use the word of the displayed result's definition, which + -- is the exact title of the full wikipedia page + word = self.lookupword + else + -- we use the original word that was querried + word = self.word + end + -- strange : we need to pass false instead of nil if word_box is nil, + -- otherwise get_fullpage is not passed + self.ui:handleEvent(Event:new("LookupWikipedia", word, self.word_box and self.word_box or false, get_fullpage)) end return DictQuickLookup diff --git a/frontend/ui/wikipedia.lua b/frontend/ui/wikipedia.lua index 349b96d4b..20309f550 100644 --- a/frontend/ui/wikipedia.lua +++ b/frontend/ui/wikipedia.lua @@ -3,7 +3,8 @@ local DEBUG = require("dbg") --[[ -- Query wikipedia using Wikimedia Web API. --- http://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exintro=&explaintext=&redirects=&titles=hello +-- https://en.wikipedia.org/w/api.php?format=jsonfm&action=query&generator=search&gsrnamespace=0&gsrsearch=ereader&gsrlimit=10&prop=extracts&exintro&explaintext&exlimit=max +-- https://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=jsonfm&explaintext=&redirects=&titles=E-reader --]] local Wikipedia = { @@ -13,11 +14,29 @@ local Wikipedia = { action = "query", prop = "extracts", format = "json", - exintro = "", + -- exintro = nil, -- get more than only the intro explaintext = "", redirects = "", + -- title = nil, -- text to lookup, will be added below }, default_lang = "en", + -- Search query for better results + -- see https://www.mediawiki.org/wiki/API:Main_page + wiki_search_params = { + action = "query", + generator = "search", + gsrnamespace = "0", + -- gsrsearch = nil, -- text to lookup, will be added below + gsrlimit = 20, -- max nb of results to get + exlimit = "max", + prop = "extracts|info", -- 'extracts' to get text, 'info' to get full page length + format = "json", + explaintext = "", + exintro = "", + -- We have to use 'exintro=' to get extracts for ALL results + -- (otherwise, we get the full text for only the first result, and + -- no text at all for the others + }, } function Wikipedia:getWikiServer(lang) @@ -36,14 +55,22 @@ function Wikipedia:loadPage(text, lang, intro, plain) local request, sink = {}, {} local query = "" - self.wiki_params.exintro = intro and "" or nil - self.wiki_params.explaintext = plain and "" or nil - for k,v in pairs(self.wiki_params) do - query = query .. k .. '=' .. v .. '&' - end + local parsed = url.parse(self:getWikiServer(lang)) parsed.path = self.wiki_path - parsed.query = query .. "titles=" .. url.escape(text) + if intro == true then -- search query + self.wiki_search_params.explaintext = plain and "" or nil + for k,v in pairs(self.wiki_search_params) do + query = query .. k .. '=' .. v .. '&' + end + parsed.query = query .. "gsrsearch=" .. url.escape(text) + else -- full page content + self.wiki_params.explaintext = plain and "" or nil + for k,v in pairs(self.wiki_params) do + query = query .. k .. '=' .. v .. '&' + end + parsed.query = query .. "titles=" .. url.escape(text) + end -- HTTP request request['url'] = url.build(parsed) @@ -79,7 +106,7 @@ function Wikipedia:loadPage(text, lang, intro, plain) end end --- extract intro passage in wiki page +-- search wikipedia and get intros for results function Wikipedia:wikintro(text, lang) local result = self:loadPage(text, lang, true, true) if result then @@ -90,4 +117,16 @@ function Wikipedia:wikintro(text, lang) end end +-- get full content of a wiki page +function Wikipedia:wikifull(text, lang) + local result = self:loadPage(text, lang, false, true) + if result then + local query = result.query + if query then + return query.pages + end + end +end + + return Wikipedia