diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index ecabd4eee..6f5119283 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -12,6 +12,7 @@ local ReaderDictionary = EventListener:new{} function ReaderDictionary:onLookupWord(word, box, highlight) self.highlight = highlight self:stardictLookup(word, box) + return true end function ReaderDictionary:stardictLookup(word, box) @@ -47,24 +48,7 @@ end function ReaderDictionary:showDict(results, box) if results and results[1] then - DEBUG("showing quick lookup dictionary window") - local align = "center" - local region = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - if box then - if box.y + box.h/2 < Screen:getHeight()/2 then - region.y = box.y + box.h - region.h = Screen:getHeight() - box.y - box.h - align = "top" - else - region.y = 0 - region.h = box.y - align = "bottom" - end - end + DEBUG("showing quick lookup window") UIManager:show(DictQuickLookup:new{ ui = self.ui, highlight = self.highlight, @@ -72,9 +56,9 @@ function ReaderDictionary:showDict(results, box) results = results, dictionary = self.default_dictionary, width = Screen:getWidth() - Screen:scaleByDPI(80), - height = math.min(region.h*0.7, Screen:getHeight()*0.5), - region = region, - align = align, + word_box = box, + -- differentiate between dict and wiki + wiki = self.wiki, }) end end @@ -82,6 +66,7 @@ end function ReaderDictionary:onUpdateDefaultDict(dict) DEBUG("make default dictionary:", dict) self.default_dictionary = dict + return true end function ReaderDictionary:onReadSettings(config) diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 494abd8e9..c6d869e9f 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -294,8 +294,7 @@ function ReaderHighlight:onHoldRelease() text = _("Highlight"), callback = function() self:saveHighlight() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) + self:onClose() end, }, { @@ -303,39 +302,44 @@ function ReaderHighlight:onHoldRelease() enabled = false, callback = function() self:addNote() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) + self:onClose() end, }, }, { + { + text = _("Wikipedia"), + callback = function() + UIManager:scheduleIn(0.1, function() + self:lookupWikipedia() + self:onClose() + end) + end, + }, { text = _("Translate"), enabled = false, callback = function() self:translate(self.selected_text) - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) - end, - }, - { - text = _("Share"), - enabled = false, - callback = function() - self:shareHighlight() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) + self:onClose() end, }, }, { + { + text = _("Share"), + enabled = false, + callback = function() + self:shareHighlight() + self:onClose() + end, + }, { text = _("More"), enabled = false, callback = function() self:moreAction() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) + self:onClose() end, }, }, @@ -411,6 +415,12 @@ function ReaderHighlight:addNote() DEBUG("add Note") end +function ReaderHighlight:lookupWikipedia() + if self.selected_text then + self.ui:handleEvent(Event:new("LookupWikipedia", self.selected_text.text)) + end +end + function ReaderHighlight:shareHighlight() DEBUG("share highlight") end @@ -436,4 +446,10 @@ function ReaderHighlight:onSaveSettings() self.ui.doc_settings:saveSetting("highlight_drawer", self.view.highlight.saved_drawer) end +function ReaderHighlight:onClose() + UIManager:close(self.highlight_dialog) + -- clear highlighted text + self:handleEvent(Event:new("Tap")) +end + return ReaderHighlight diff --git a/frontend/apps/reader/modules/readerwikipedia.lua b/frontend/apps/reader/modules/readerwikipedia.lua new file mode 100644 index 000000000..17821e181 --- /dev/null +++ b/frontend/apps/reader/modules/readerwikipedia.lua @@ -0,0 +1,51 @@ +local ReaderDictionary = require("apps/reader/modules/readerdictionary") +local EventListener = require("ui/widget/eventlistener") +local Translator = require("ui/translator") +local Wikipedia = require("ui/wikipedia") +local UIManager = require("ui/uimanager") +local Geom = require("ui/geometry") +local Screen = require("ui/screen") +local JSON = require("JSON") +local DEBUG = require("dbg") +local _ = require("gettext") + +-- Wikipedia as a special dictionary +local ReaderWikipedia = ReaderDictionary:new{ + -- identify itself + wiki = true, + no_page = _("No wiki page found."), +} + +function ReaderWikipedia:onLookupWikipedia(word, box) + local lang = Translator:detect(word) or "en" + -- convert "zh-CN" and "zh-TW" to "zh" + lang = lang:match("(.*)-") or lang + local results = {} + local ok, pages = pcall(Wikipedia.wikintro, Wikipedia, word, lang) + if ok and pages then + for pageid, page in pairs(pages) do + local result = { + dict = _("Wikipedia"), + word = page.title, + definition = page.extract or self.no_page, + } + table.insert(results, result) + end + DEBUG("lookup result:", word, results) + self:showDict(results, box) + else + DEBUG("error:", pages) + -- dummy results + results = { + { + dict = _("Wikipedia"), + word = word, + definition = self.no_page, + } + } + DEBUG("dummy result table:", word, results) + self:showDict(results, box) + end +end + +return ReaderWikipedia diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 9bc50c61e..352da8887 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -29,6 +29,7 @@ local ReaderHighlight = require("apps/reader/modules/readerhighlight") local ReaderScreenshot = require("apps/reader/modules/readerscreenshot") local ReaderFrontLight = require("apps/reader/modules/readerfrontlight") 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 ReaderLink = require("apps/reader/modules/readerlink") @@ -142,6 +143,13 @@ function ReaderUI:init() ui = self, document = self.document, }) + -- wikipedia + table.insert(self, ReaderWikipedia:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) -- screenshot controller table.insert(self.active_widgets, ReaderScreenshot:new{ dialog = self.dialog, diff --git a/frontend/ui/translator.lua b/frontend/ui/translator.lua new file mode 100644 index 000000000..357d8c83b --- /dev/null +++ b/frontend/ui/translator.lua @@ -0,0 +1,85 @@ +local socket = require('socket') +local url = require('socket.url') +local http = require('socket.http') +local https = require('ssl.https') +local ltn12 = require('ltn12') +local JSON = require("JSON") +local DEBUG = require("dbg") + +--[[ +-- Translate text using Google Translate. +-- http://translate.google.com/translate_a/t?client=z&ie=UTF-8&oe=UTF-8&hl=en&tl=en&text=hello +--]] + +local Translator = { + trans_servers = { + "http://translate.google.cn", + "http://translate.google.com", + }, + trans_path = "/translate_a/t", + trans_params = { + client = "z", -- client z returns normal JSON result + ie = "UTF-8", + oe = "UTF-8", + hl = "en", + tl = "en", + sl = nil, -- we don't specify source languagae to detect language + }, + default_lang = "en", +} + +function Translator:getTransServer() + return G_reader_settings:readSetting("trans_server") or self.trans_servers[1] +end + +--[[ +-- return decoded JSON table from translate server +--]] +function Translator:loadPage(target_lang, source_lang, text) + local request, sink = {}, {} + local query = "" + self.trans_params.tl = target_lang + self.trans_params.sl = source_lang + for k,v in pairs(self.trans_params) do + query = query .. k .. '=' .. v .. '&' + end + local parsed = url.parse(self:getTransServer()) + parsed.path = self.trans_path + parsed.query = query .. "text=" .. url.escape(text) + + -- HTTP request + request['url'] = url.build(parsed) + request['method'] = 'GET' + request['sink'] = ltn12.sink.table(sink) + DEBUG("request", request) + http.TIMEOUT, https.TIMEOUT = 10, 10 + local httpRequest = parsed.scheme == 'http' and http.request or https.request + local code, headers, status = socket.skip(1, httpRequest(request)) + + -- raise error message when network is unavailable + if headers == nil then + error("Network is unreachable") + end + + local content = table.concat(sink) + if content ~= "" then + local ok, result = pcall(JSON.decode, JSON, content) + if ok and result then + --DEBUG("translate result", result) + return result + else + DEBUG("error:", result) + end + end +end + +function Translator:detect(text) + local result = self:loadPage("en", nil, text) + if result then + local src_lang = result.src + DEBUG("detected language:", src_lang) + return src_lang + end +end + +return Translator diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 071b0e827..fd232affc 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -37,6 +37,8 @@ local DictQuickLookup = InputContainer:new{ content_face = Font:getFace("cfont", DDICT_FONT_SIZE), width = nil, height = nil, + -- box of highlighted word, quick lookup window tries to not hide the word + word_box = nil, title_padding = Screen:scaleByDPI(5), title_margin = Screen:scaleByDPI(2), @@ -85,6 +87,26 @@ function DictQuickLookup:init() end function DictQuickLookup:update() + -- calculate window dimension and try to not hide highlighted word + 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()/2 then + self.region.y = box.y + box.h + self.region.h = Screen:getHeight() - box.y - box.h + self.align = "top" + else + 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) -- dictionary title self.dict_title = FrameContainer:new{ padding = self.title_padding, @@ -150,9 +172,11 @@ function DictQuickLookup:update() { { text = _("Wikipedia"), - enabled = false, callback = function() - self.ui:handleEvent(Event:new("HighlightWiki")) + UIManager:scheduleIn(0.1, function() + self:lookupWikipedia() + self:onClose() + end) end, }, { @@ -225,7 +249,7 @@ function DictQuickLookup:update() } self[1] = WidgetContainer:new{ align = self.align, - dimen = self.region:copy(), + dimen = self.region, FrameContainer:new{ bordersize = 0, padding = Screen:scaleByDPI(5), @@ -336,6 +360,7 @@ function DictQuickLookup:lookupInputWord(hint) self:onClose() self.input_dialog = InputDialog:new{ title = _("Input lookup word"), + input = hint, input_hint = hint or "", input_type = "text", buttons = { @@ -369,7 +394,8 @@ end function DictQuickLookup:inputLookup() local word = self.input_dialog:getInputText() if word and word ~= "" then - self.ui:handleEvent(Event:new("LookupWord", word)) + local event = self.wiki and "LookupWikipedia" or "LookupWord" + self.ui:handleEvent(Event:new(event, word)) end end @@ -378,4 +404,8 @@ function DictQuickLookup:closeInputDialog() UIManager:close(self.input_dialog) end +function DictQuickLookup:lookupWikipedia() + self.ui:handleEvent(Event:new("LookupWikipedia", self.lookupword, self.word_box)) +end + return DictQuickLookup diff --git a/frontend/ui/wikipedia.lua b/frontend/ui/wikipedia.lua new file mode 100644 index 000000000..b75bd9c05 --- /dev/null +++ b/frontend/ui/wikipedia.lua @@ -0,0 +1,84 @@ +local socket = require('socket') +local url = require('socket.url') +local http = require('socket.http') +local https = require('ssl.https') +local ltn12 = require('ltn12') +local JSON = require("JSON") +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 +--]] + +local Wikipedia = { + wiki_server = "http://%s.wikipedia.org", + wiki_path = "/w/api.php", + wiki_params = { + action = "query", + prop = "extracts", + format = "json", + exintro = "", + explaintext = "", + redirects = "", + }, + default_lang = "en", +} + +function Wikipedia:getWikiServer(lang) + return string.format(self.wiki_server, lang or self.default_lang) +end + +--[[ +-- return decoded JSON table from Wikipedia +--]] +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) + + -- HTTP request + request['url'] = url.build(parsed) + request['method'] = 'GET' + request['sink'] = ltn12.sink.table(sink) + DEBUG("request", request) + http.TIMEOUT, https.TIMEOUT = 10, 10 + local httpRequest = parsed.scheme == 'http' and http.request or https.request + local code, headers, status = socket.skip(1, httpRequest(request)) + + -- raise error message when network is unavailable + if headers == nil then + error("Network is unreachable") + end + + local content = table.concat(sink) + if content ~= "" then + local ok, result = pcall(JSON.decode, JSON, content) + if ok and result then + DEBUG("wiki result", result) + return result + else + DEBUG("error:", result) + end + end +end + +-- extract intro passage in wiki page +function Wikipedia:wikintro(text, lang) + local result = self:loadPage(text, lang, true, true) + if result then + local query = result.query + if query then + return query.pages + end + end +end + +return Wikipedia