diff --git a/defaults.lua b/defaults.lua index b496a3888..1ed04ad2f 100644 --- a/defaults.lua +++ b/defaults.lua @@ -69,26 +69,26 @@ DTAP_ZONE_BOOKMARK = {x = 7/8, y = 0, w = 1/8, h = 1/8} DTAP_ZONE_FLIPPING = {x = 0, y = 0, w = 1/8, h = 1/8} -- koptreader config defaults -DKOPTREADER_CONFIG_FONT_SIZE = 1.0 -- range from 0.1 to 3.0 -DKOPTREADER_CONFIG_TEXT_WRAP = 0 -- 1 = on, 0 = off -DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual -DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable -DKOPTREADER_CONFIG_DEFECT_SIZE = 1.0 -- range from 0.0 to 3.0 -DKOPTREADER_CONFIG_PAGE_MARGIN = 0.10 -- range from 0.0 to 1.0 -DKOPTREADER_CONFIG_LINE_SPACING = 1.2 -- range from 0.5 to 2.0 -DKOPTREADER_CONFIG_RENDER_QUALITY = 1.0 -- range from 0.5 to 2.0 -DKOPTREADER_CONFIG_AUTO_STRAIGHTEN = 0 -- range from 0 to 10 -DKOPTREADER_CONFIG_JUSTIFICATION = 3 -- -1 = auto, 0 = left, 1 = center, 2 = right, 3 = full -DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4 -DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0 +DKOPTREADER_CONFIG_FONT_SIZE = 1.0 -- range from 0.1 to 3.0 +DKOPTREADER_CONFIG_TEXT_WRAP = 0 -- 1 = on, 0 = off +DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual +DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable +DKOPTREADER_CONFIG_DEFECT_SIZE = 1.0 -- range from 0.0 to 3.0 +DKOPTREADER_CONFIG_PAGE_MARGIN = 0.10 -- range from 0.0 to 1.0 +DKOPTREADER_CONFIG_LINE_SPACING = 1.2 -- range from 0.5 to 2.0 +DKOPTREADER_CONFIG_RENDER_QUALITY = 1.0 -- range from 0.5 to 2.0 +DKOPTREADER_CONFIG_AUTO_STRAIGHTEN = 0 -- range from 0 to 10 +DKOPTREADER_CONFIG_JUSTIFICATION = 3 -- -1 = auto, 0 = left, 1 = center, 2 = right, 3 = full +DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4 +DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0 -- word spacing for reflow DKOPTREADER_CONFIG_WORD_SAPCINGS = {0.05, -1, 0.375} -- range from 0.05 to 0.5 -DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING = -1 -- range from 0.05 to 0.5 +DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING = -1 -- range from 0.05 to 0.5 -- document languages for OCR DKOPTREADER_CONFIG_DOC_LANGS_TEXT = {"English", "Chinese"} -DKOPTREADER_CONFIG_DOC_LANGS_CODE = {"eng", "chi_sim"} -- language code, make sure you have corresponding training data -DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE = "eng" -- that have filenames starting with the language codes +DKOPTREADER_CONFIG_DOC_LANGS_CODE = {"eng", "chi_sim"} -- language code, make sure you have corresponding training data +DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE = "eng" -- that have filenames starting with the language codes -- crereader font sizes -- feel free to add more entries in this list @@ -170,5 +170,5 @@ DDICT_FONT_SIZE = 20 --DPICVIEWER_PAGE_MODE_ENABLE = false ---DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off ---DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees +--DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off +--DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index 5447ed04a..6741d5d6b 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -17,175 +17,175 @@ local DEBUG = require("dbg") local _ = require("gettext") local FileManager = InputContainer:extend{ - title = _("FileManager"), - width = Screen:getWidth(), - height = Screen:getHeight(), - root_path = lfs.currentdir(), - -- our own size - dimen = Geom:new{ w = 400, h = 600 }, - onExit = function() end, + title = _("FileManager"), + width = Screen:getWidth(), + height = Screen:getHeight(), + root_path = lfs.currentdir(), + -- our own size + dimen = Geom:new{ w = 400, h = 600 }, + onExit = function() end, } function FileManager:init() - local exclude_dirs = {"%.sdr$"} - - self.show_parent = self.show_parent or self - - self.banner = VerticalGroup:new{ - TextWidget:new{ - face = Font:getFace("tfont", 24), - text = self.title, - }, - VerticalSpan:new{ width = Screen:scaleByDPI(10) } - } - - local g_show_hidden = G_reader_settings:readSetting("show_hidden") - local show_hidden = g_show_hidden == nil and DSHOWHIDDENFILES or g_show_hidden - local file_chooser = FileChooser:new{ - -- remeber to adjust the height when new item is added to the group - path = self.root_path, - show_parent = self.show_parent, - show_hidden = show_hidden, - height = Screen:getHeight() - self.banner:getSize().h, - is_popout = false, - is_borderless = true, - has_close_button = true, - dir_filter = function(dirname) - for _, pattern in ipairs(exclude_dirs) do - if dirname:match(pattern) then return end - end - return true - end, - file_filter = function(filename) - if DocumentRegistry:getProvider(filename) then - return true - end - end - } - self.file_chooser = file_chooser - - function file_chooser:onFileSelect(file) - showReaderUI(file) - return true - end - - local copyFile = function(file) self:copyFile(file) end - local pasteHere = function(file) self:pasteHere(file) end - local cutFile = function(file) self:cutFile(file) end - local deleteFile = function(file) self:deleteFile(file) end - local fileManager = self - - function file_chooser:onFileHold(file) - --DEBUG("hold file", file) - self.file_dialog = ButtonDialog:new{ - buttons = { - { - { - text = _("Copy"), - callback = function() - copyFile(file) - UIManager:close(self.file_dialog) - end, - }, - { - text = _("Paste"), - enabled = fileManager.clipboard and true or false, - callback = function() - pasteHere(file) - self:refreshPath() - UIManager:close(self.file_dialog) - end, - }, - }, - { - { - text = _("Cut"), - callback = function() - cutFile(file) - UIManager:close(self.file_dialog) - end, - }, - { - text = _("Delete"), - callback = function() - local path = util.realpath(file) - deleteFile(file) - self:refreshPath() - UIManager:close(self.file_dialog) - end, - }, - }, - }, - } - UIManager:show(self.file_dialog) - return true - end - - self.layout = VerticalGroup:new{ - self.banner, - file_chooser, - } - - local fm_ui = FrameContainer:new{ - padding = 0, - bordersize = 0, - background = 0, - self.layout, - } - - self[1] = fm_ui - - self.menu = FileManagerMenu:new{ - ui = self - } - table.insert(self, self.menu) - table.insert(self, FileManagerHistory:new{ - ui = self, - menu = self.menu - }) - - self:handleEvent(Event:new("SetDimensions", self.dimen)) + local exclude_dirs = {"%.sdr$"} + + self.show_parent = self.show_parent or self + + self.banner = VerticalGroup:new{ + TextWidget:new{ + face = Font:getFace("tfont", 24), + text = self.title, + }, + VerticalSpan:new{ width = Screen:scaleByDPI(10) } + } + + local g_show_hidden = G_reader_settings:readSetting("show_hidden") + local show_hidden = g_show_hidden == nil and DSHOWHIDDENFILES or g_show_hidden + local file_chooser = FileChooser:new{ + -- remeber to adjust the height when new item is added to the group + path = self.root_path, + show_parent = self.show_parent, + show_hidden = show_hidden, + height = Screen:getHeight() - self.banner:getSize().h, + is_popout = false, + is_borderless = true, + has_close_button = true, + dir_filter = function(dirname) + for _, pattern in ipairs(exclude_dirs) do + if dirname:match(pattern) then return end + end + return true + end, + file_filter = function(filename) + if DocumentRegistry:getProvider(filename) then + return true + end + end + } + self.file_chooser = file_chooser + + function file_chooser:onFileSelect(file) + showReaderUI(file) + return true + end + + local copyFile = function(file) self:copyFile(file) end + local pasteHere = function(file) self:pasteHere(file) end + local cutFile = function(file) self:cutFile(file) end + local deleteFile = function(file) self:deleteFile(file) end + local fileManager = self + + function file_chooser:onFileHold(file) + --DEBUG("hold file", file) + self.file_dialog = ButtonDialog:new{ + buttons = { + { + { + text = _("Copy"), + callback = function() + copyFile(file) + UIManager:close(self.file_dialog) + end, + }, + { + text = _("Paste"), + enabled = fileManager.clipboard and true or false, + callback = function() + pasteHere(file) + self:refreshPath() + UIManager:close(self.file_dialog) + end, + }, + }, + { + { + text = _("Cut"), + callback = function() + cutFile(file) + UIManager:close(self.file_dialog) + end, + }, + { + text = _("Delete"), + callback = function() + local path = util.realpath(file) + deleteFile(file) + self:refreshPath() + UIManager:close(self.file_dialog) + end, + }, + }, + }, + } + UIManager:show(self.file_dialog) + return true + end + + self.layout = VerticalGroup:new{ + self.banner, + file_chooser, + } + + local fm_ui = FrameContainer:new{ + padding = 0, + bordersize = 0, + background = 0, + self.layout, + } + + self[1] = fm_ui + + self.menu = FileManagerMenu:new{ + ui = self + } + table.insert(self, self.menu) + table.insert(self, FileManagerHistory:new{ + ui = self, + menu = self.menu + }) + + self:handleEvent(Event:new("SetDimensions", self.dimen)) end function FileManager:toggleHiddenFiles() - self.file_chooser:toggleHiddenFiles() - G_reader_settings:saveSetting("show_hidden", self.file_chooser.show_hidden) + self.file_chooser:toggleHiddenFiles() + G_reader_settings:saveSetting("show_hidden", self.file_chooser.show_hidden) end function FileManager:onClose() - UIManager:close(self) - if self.onExit then - self:onExit() - end - return true + UIManager:close(self) + if self.onExit then + self:onExit() + end + return true end function FileManager:copyFile(file) - self.cutfile = false - self.clipboard = file + self.cutfile = false + self.clipboard = file end function FileManager:cutFile(file) - self.cutfile = true - self.clipboard = file + self.cutfile = true + self.clipboard = file end function FileManager:pasteHere(file) - if self.clipboard then - file = util.realpath(file) - local orig = util.realpath(self.clipboard) - local dest = lfs.attributes(file, "mode") == "directory" and - file or file:match("(.*/)") - if self.cutfile then - util.execute("/bin/mv", orig, dest) - else - util.execute("/bin/cp", "-r", orig, dest) - end - end + if self.clipboard then + file = util.realpath(file) + local orig = util.realpath(self.clipboard) + local dest = lfs.attributes(file, "mode") == "directory" and + file or file:match("(.*/)") + if self.cutfile then + util.execute("/bin/mv", orig, dest) + else + util.execute("/bin/cp", "-r", orig, dest) + end + end end function FileManager:deleteFile(file) - util.execute("/bin/rm", "-r", util.realpath(file)) + util.execute("/bin/rm", "-r", util.realpath(file)) end return FileManager diff --git a/frontend/apps/filemanager/filemanagerhistory.lua b/frontend/apps/filemanager/filemanagerhistory.lua index b025ff08c..ef8ae144e 100644 --- a/frontend/apps/filemanager/filemanagerhistory.lua +++ b/frontend/apps/filemanager/filemanagerhistory.lua @@ -11,103 +11,103 @@ local _ = require("gettext") local history_dir = "./history/" local FileManagerHistory = InputContainer:extend{ - hist_menu_title = _("History"), + hist_menu_title = _("History"), } function FileManagerHistory:init() - self.ui.menu:registerToMainMenu(self) + self.ui.menu:registerToMainMenu(self) end function FileManagerHistory:onSetDimensions(dimen) - self.dimen = dimen + self.dimen = dimen end function FileManagerHistory:onMenuHold(item) - self.histfile_dialog = ButtonDialog:new{ - buttons = { - { - { - text = _("Delete"), - callback = function() - os.remove(history_dir..item.histfile) - self._manager:updateItemTable() - UIManager:close(self.histfile_dialog) - end, - }, - }, - }, - } - UIManager:show(self.histfile_dialog) - return true + self.histfile_dialog = ButtonDialog:new{ + buttons = { + { + { + text = _("Delete"), + callback = function() + os.remove(history_dir..item.histfile) + self._manager:updateItemTable() + UIManager:close(self.histfile_dialog) + end, + }, + }, + }, + } + UIManager:show(self.histfile_dialog) + return true end function FileManagerHistory:onShowHist() - local menu_container = CenterContainer:new{ - dimen = Screen:getSize(), - } + local menu_container = CenterContainer:new{ + dimen = Screen:getSize(), + } - self.hist_menu = Menu:new{ - ui = self.ui, - width = Screen:getWidth()-50, - height = Screen:getHeight()-50, - show_parent = menu_container, - onMenuHold = self.onMenuHold, - _manager = self, - } - self:updateItemTable() + self.hist_menu = Menu:new{ + ui = self.ui, + width = Screen:getWidth()-50, + height = Screen:getHeight()-50, + show_parent = menu_container, + onMenuHold = self.onMenuHold, + _manager = self, + } + self:updateItemTable() - table.insert(menu_container, self.hist_menu) + table.insert(menu_container, self.hist_menu) - self.hist_menu.close_callback = function() - UIManager:close(menu_container) - end + self.hist_menu.close_callback = function() + UIManager:close(menu_container) + end - UIManager:show(menu_container) - return true + UIManager:show(menu_container) + return true end function FileManagerHistory:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.main, { - text = self.hist_menu_title, - callback = function() - self:onShowHist() - end, - }) + -- insert table to main reader menu + table.insert(tab_item_table.main, { + text = self.hist_menu_title, + callback = function() + self:onShowHist() + end, + }) end function FileManagerHistory:updateItemTable() - function readHistDir(re) - local sorted_files = {} - for f in lfs.dir(history_dir) do - local path = history_dir..f - if lfs.attributes(path, "mode") == "file" then - table.insert(sorted_files, {file = f, date = lfs.attributes(path, "modification")}) - end - end - table.sort(sorted_files, function(v1,v2) return v1.date > v2.date end) - for _, v in pairs(sorted_files) do - table.insert(re, { - dir = DocSettings:getPathFromHistory(v.file), - name = DocSettings:getNameFromHistory(v.file), - histfile = v.file, - }) - end - end + function readHistDir(re) + local sorted_files = {} + for f in lfs.dir(history_dir) do + local path = history_dir..f + if lfs.attributes(path, "mode") == "file" then + table.insert(sorted_files, {file = f, date = lfs.attributes(path, "modification")}) + end + end + table.sort(sorted_files, function(v1,v2) return v1.date > v2.date end) + for _, v in pairs(sorted_files) do + table.insert(re, { + dir = DocSettings:getPathFromHistory(v.file), + name = DocSettings:getNameFromHistory(v.file), + histfile = v.file, + }) + end + end - self.hist = {} - local last_files = {} - readHistDir(last_files) - for _,v in pairs(last_files) do - table.insert(self.hist, { - text = v.name, - histfile = v.histfile, - callback = function() - showReaderUI(v.dir .. "/" .. v.name) - end - }) - end - self.hist_menu:swithItemTable(self.hist_menu_title, self.hist) + self.hist = {} + local last_files = {} + readHistDir(last_files) + for _,v in pairs(last_files) do + table.insert(self.hist, { + text = v.name, + histfile = v.histfile, + callback = function() + showReaderUI(v.dir .. "/" .. v.name) + end + }) + end + self.hist_menu:swithItemTable(self.hist_menu_title, self.hist) end return FileManagerHistory diff --git a/frontend/apps/filemanager/filemanagermenu.lua b/frontend/apps/filemanager/filemanagermenu.lua index 64af5d676..9dfe2ff41 100644 --- a/frontend/apps/filemanager/filemanagermenu.lua +++ b/frontend/apps/filemanager/filemanagermenu.lua @@ -12,143 +12,143 @@ local Language = require("ui/language") local _ = require("gettext") local FileManagerMenu = InputContainer:extend{ - tab_item_table = nil, - registered_widgets = {}, + tab_item_table = nil, + registered_widgets = {}, } function FileManagerMenu:init() - self.tab_item_table = { - main = { - icon = "resources/icons/appbar.pokeball.png", - }, - home = { - icon = "resources/icons/appbar.home.png", - callback = function() - UIManager:close(self.menu_container) - self.ui:onClose() - end, - }, - } - self.registered_widgets = {} - - if Device:hasKeyboard() then - self.key_events = { - ShowMenu = { { "Menu" }, doc = _("show menu") }, - } - end + self.tab_item_table = { + main = { + icon = "resources/icons/appbar.pokeball.png", + }, + home = { + icon = "resources/icons/appbar.home.png", + callback = function() + UIManager:close(self.menu_container) + self.ui:onClose() + end, + }, + } + self.registered_widgets = {} + + if Device:hasKeyboard() then + self.key_events = { + ShowMenu = { { "Menu" }, doc = _("show menu") }, + } + end end function FileManagerMenu:initGesListener() - self.ges_events = { - TapShowMenu = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, - y = 0, - w = Screen:getWidth()*3/4, - h = Screen:getHeight()/4, - } - } - }, - } + self.ges_events = { + TapShowMenu = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, + y = 0, + w = Screen:getWidth()*3/4, + h = Screen:getHeight()/4, + } + } + }, + } end function FileManagerMenu:setUpdateItemTable() - for _, widget in pairs(self.registered_widgets) do - widget:addToMainMenu(self.tab_item_table) - end - - table.insert(self.tab_item_table.main, { - text = _("Toggle hidden files"), - callback = function() - self.ui:toggleHiddenFiles() - end - }) - - if Device:hasFrontlight() then - ReaderFrontLight:addToMainMenu(self.tab_item_table) - end - - table.insert(self.tab_item_table.main, { - text = _("Help"), - callback = function() - UIManager:show(InfoMessage:new{ - text = _("Please report bugs to https://github.com/koreader/ koreader/issues, Click at the bottom of the page for more options"), - }) - end - }) - table.insert(self.tab_item_table.main, { - text = _("Version"), - callback = function() - UIManager:show(InfoMessage:new{ - text = io.open("git-rev", "r"):read(), - }) - end - }) - table.insert(self.tab_item_table.main, Language:getLangMenuTable()) + for _, widget in pairs(self.registered_widgets) do + widget:addToMainMenu(self.tab_item_table) + end + + table.insert(self.tab_item_table.main, { + text = _("Toggle hidden files"), + callback = function() + self.ui:toggleHiddenFiles() + end + }) + + if Device:hasFrontlight() then + ReaderFrontLight:addToMainMenu(self.tab_item_table) + end + + table.insert(self.tab_item_table.main, { + text = _("Help"), + callback = function() + UIManager:show(InfoMessage:new{ + text = _("Please report bugs to https://github.com/koreader/ koreader/issues, Click at the bottom of the page for more options"), + }) + end + }) + table.insert(self.tab_item_table.main, { + text = _("Version"), + callback = function() + UIManager:show(InfoMessage:new{ + text = io.open("git-rev", "r"):read(), + }) + end + }) + table.insert(self.tab_item_table.main, Language:getLangMenuTable()) end function FileManagerMenu:onShowMenu() - if #self.tab_item_table.main == 0 then - self:setUpdateItemTable() - end - - local menu_container = CenterContainer:new{ - ignore = "height", - dimen = Screen:getSize(), - } - - local main_menu = nil - if Device:isTouchDevice() then - main_menu = TouchMenu:new{ - width = Screen:getWidth(), - tab_item_table = { - self.tab_item_table.main, - self.tab_item_table.home, - }, - show_parent = menu_container, - } - else - main_menu = Menu:new{ - title = _("File manager menu"), - item_table = {}, - width = Screen:getWidth() - 100, - } - - for _,item_table in pairs(self.tab_item_table) do - for k,v in ipairs(item_table) do - table.insert(main_menu.item_table, v) - end - end - end - - main_menu.close_callback = function () - UIManager:close(menu_container) - end - - menu_container[1] = main_menu - -- maintain a reference to menu_container - self.menu_container = menu_container - UIManager:show(menu_container) - - return true + if #self.tab_item_table.main == 0 then + self:setUpdateItemTable() + end + + local menu_container = CenterContainer:new{ + ignore = "height", + dimen = Screen:getSize(), + } + + local main_menu = nil + if Device:isTouchDevice() then + main_menu = TouchMenu:new{ + width = Screen:getWidth(), + tab_item_table = { + self.tab_item_table.main, + self.tab_item_table.home, + }, + show_parent = menu_container, + } + else + main_menu = Menu:new{ + title = _("File manager menu"), + item_table = {}, + width = Screen:getWidth() - 100, + } + + for _,item_table in pairs(self.tab_item_table) do + for k,v in ipairs(item_table) do + table.insert(main_menu.item_table, v) + end + end + end + + main_menu.close_callback = function () + UIManager:close(menu_container) + end + + menu_container[1] = main_menu + -- maintain a reference to menu_container + self.menu_container = menu_container + UIManager:show(menu_container) + + return true end function FileManagerMenu:onTapShowMenu() - self:onShowMenu() - return true + self:onShowMenu() + return true end function FileManagerMenu:onSetDimensions(dimen) - -- update listening according to new screen dimen - if Device:isTouchDevice() then - self:initGesListener() - end + -- update listening according to new screen dimen + if Device:isTouchDevice() then + self:initGesListener() + end end function FileManagerMenu:registerToMainMenu(widget) - table.insert(self.registered_widgets, widget) + table.insert(self.registered_widgets, widget) end return FileManagerMenu diff --git a/frontend/cache.lua b/frontend/cache.lua index f7487ae05..4d4312706 100644 --- a/frontend/cache.lua +++ b/frontend/cache.lua @@ -2,98 +2,98 @@ A global LRU cache ]]-- local function calcFreeMem() - local meminfo = io.open("/proc/meminfo", "r") - local freemem = 0 - if meminfo then - for line in meminfo:lines() do - local free, buffer, cached, n - free, n = line:gsub("^MemFree:%s-(%d+) kB", "%1") - if n ~= 0 then freemem = freemem + tonumber(free)*1024 end - buffer, n = line:gsub("^Buffers:%s-(%d+) kB", "%1") - if n ~= 0 then freemem = freemem + tonumber(buffer)*1024 end - cached, n = line:gsub("^Cached:%s-(%d+) kB", "%1") - if n ~= 0 then freemem = freemem + tonumber(cached)*1024 end - end - meminfo:close() - end - return freemem + local meminfo = io.open("/proc/meminfo", "r") + local freemem = 0 + if meminfo then + for line in meminfo:lines() do + local free, buffer, cached, n + free, n = line:gsub("^MemFree:%s-(%d+) kB", "%1") + if n ~= 0 then freemem = freemem + tonumber(free)*1024 end + buffer, n = line:gsub("^Buffers:%s-(%d+) kB", "%1") + if n ~= 0 then freemem = freemem + tonumber(buffer)*1024 end + cached, n = line:gsub("^Cached:%s-(%d+) kB", "%1") + if n ~= 0 then freemem = freemem + tonumber(cached)*1024 end + end + meminfo:close() + end + return freemem end local function calcCacheMemSize() - local min = DGLOBAL_CACHE_SIZE_MINIMUM - local max = DGLOBAL_CACHE_SIZE_MAXIMUM - local calc = calcFreeMem()*(DGLOBAL_CACHE_FREE_PROPORTION or 0) - return math.min(max, math.max(min, calc)) + local min = DGLOBAL_CACHE_SIZE_MINIMUM + local max = DGLOBAL_CACHE_SIZE_MAXIMUM + local calc = calcFreeMem()*(DGLOBAL_CACHE_FREE_PROPORTION or 0) + return math.min(max, math.max(min, calc)) end local Cache = { - -- cache configuration: - max_memsize = calcCacheMemSize(), - -- cache state: - current_memsize = 0, - -- associative cache - cache = {}, - -- this will hold the LRU order of the cache - cache_order = {} + -- cache configuration: + max_memsize = calcCacheMemSize(), + -- cache state: + current_memsize = 0, + -- associative cache + cache = {}, + -- this will hold the LRU order of the cache + cache_order = {} } function Cache:new(o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o + o = o or {} + setmetatable(o, self) + self.__index = self + return o end function Cache:insert(key, object) - -- guarantee that we have enough memory in cache - if(object.size > self.max_memsize) then - -- we're not allowed to claim this much at all - error("too much memory claimed") - end - -- delete objects that least recently used - -- (they are at the end of the cache_order array) - while self.current_memsize + object.size > self.max_memsize do - local removed_key = table.remove(self.cache_order) - self.current_memsize = self.current_memsize - self.cache[removed_key].size - self.cache[removed_key]:onFree() - self.cache[removed_key] = nil - end - -- insert new object in front of the LRU order - table.insert(self.cache_order, 1, key) - self.cache[key] = object - self.current_memsize = self.current_memsize + object.size + -- guarantee that we have enough memory in cache + if(object.size > self.max_memsize) then + -- we're not allowed to claim this much at all + error("too much memory claimed") + end + -- delete objects that least recently used + -- (they are at the end of the cache_order array) + while self.current_memsize + object.size > self.max_memsize do + local removed_key = table.remove(self.cache_order) + self.current_memsize = self.current_memsize - self.cache[removed_key].size + self.cache[removed_key]:onFree() + self.cache[removed_key] = nil + end + -- insert new object in front of the LRU order + table.insert(self.cache_order, 1, key) + self.cache[key] = object + self.current_memsize = self.current_memsize + object.size end function Cache:check(key) - if self.cache[key] then - if self.cache_order[1] ~= key then - -- put key in front of the LRU list - for k, v in ipairs(self.cache_order) do - if v == key then - table.remove(self.cache_order, k) - end - end - table.insert(self.cache_order, 1, key) - end - return self.cache[key] - end + if self.cache[key] then + if self.cache_order[1] ~= key then + -- put key in front of the LRU list + for k, v in ipairs(self.cache_order) do + if v == key then + table.remove(self.cache_order, k) + end + end + table.insert(self.cache_order, 1, key) + end + return self.cache[key] + end end function Cache:willAccept(size) - -- we only allow single objects to fill 75% of the cache - if size*4 < self.max_memsize*3 then - return true - end + -- we only allow single objects to fill 75% of the cache + if size*4 < self.max_memsize*3 then + return true + end end -- blank the cache function Cache:clear() - for k, _ in pairs(self.cache) do - self.cache[k]:onFree() - end - self.cache = {} - self.cache_order = {} - self.current_memsize = 0 + for k, _ in pairs(self.cache) do + self.cache[k]:onFree() + end + self.cache = {} + self.cache_order = {} + self.current_memsize = 0 end return Cache diff --git a/frontend/cacheitem.lua b/frontend/cacheitem.lua index 21c60465f..2ca58928c 100644 --- a/frontend/cacheitem.lua +++ b/frontend/cacheitem.lua @@ -3,14 +3,14 @@ Inheritable abstraction for cache items --]] local CacheItem = { - size = 64, -- some reasonable default for simple Lua values / small tables + size = 64, -- some reasonable default for simple Lua values / small tables } function CacheItem:new(o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o + o = o or {} + setmetatable(o, self) + self.__index = self + return o end function CacheItem:onFree() diff --git a/frontend/dbg.lua b/frontend/dbg.lua index e9a52dfc0..7b19fa2e6 100644 --- a/frontend/dbg.lua +++ b/frontend/dbg.lua @@ -1,45 +1,45 @@ local DocSettings = require("docsettings") -- for dump method local Dbg = { - is_on = false, - ev_log = nil, + is_on = false, + ev_log = nil, } local Dbg_mt = {} local function LvDEBUG(lv, ...) - local line = "" - for i,v in ipairs({...}) do - if type(v) == "table" then - line = line .. " " .. DocSettings:dump(v, lv) - else - line = line .. " " .. tostring(v) - end - end - print("#"..line) + local line = "" + for i,v in ipairs({...}) do + if type(v) == "table" then + line = line .. " " .. DocSettings:dump(v, lv) + else + line = line .. " " .. tostring(v) + end + end + print("#"..line) end function Dbg_mt.__call(dbg, ...) - if dbg.is_on then LvDEBUG(math.huge, ...) end + if dbg.is_on then LvDEBUG(math.huge, ...) end end function Dbg:turnOn() - self.is_on = true + self.is_on = true - -- create or clear ev log file - os.execute("echo > ev.log") - self.ev_log = io.open("ev.log", "w") + -- create or clear ev log file + os.execute("echo > ev.log") + self.ev_log = io.open("ev.log", "w") end function Dbg:logEv(ev) - local log = ev.type.."|"..ev.code.."|" - ..ev.value.."|"..ev.time.sec.."|"..ev.time.usec.."\n" - self.ev_log:write(log) - self.ev_log:flush() + local log = ev.type.."|"..ev.code.."|" + ..ev.value.."|"..ev.time.sec.."|"..ev.time.usec.."\n" + self.ev_log:write(log) + self.ev_log:flush() end function Dbg:traceback() - LvDEBUG(math.huge, debug.traceback()) + LvDEBUG(math.huge, debug.traceback()) end setmetatable(Dbg, Dbg_mt) diff --git a/frontend/docsettings.lua b/frontend/docsettings.lua index 01844b8ef..d9625d471 100644 --- a/frontend/docsettings.lua +++ b/frontend/docsettings.lua @@ -2,133 +2,133 @@ local DocSettings = {} -- lfs function DocSettings:getHistoryPath(fullpath) - local i = #fullpath - 1 - -- search for last slash - while i > 0 do - if fullpath:sub(i,i) == "/" then - break - end - i = i - 1 - end - -- construct path to configuration file in history dir - local filename = fullpath:sub(i+1, -1) - local basename = fullpath:sub(1, i) - return "./history/["..basename:gsub("/","#").."] "..filename..".lua" + local i = #fullpath - 1 + -- search for last slash + while i > 0 do + if fullpath:sub(i,i) == "/" then + break + end + i = i - 1 + end + -- construct path to configuration file in history dir + local filename = fullpath:sub(i+1, -1) + local basename = fullpath:sub(1, i) + return "./history/["..basename:gsub("/","#").."] "..filename..".lua" end function DocSettings:getPathFromHistory(hist_name) - -- 1. select everything included in brackets - local s = string.match(hist_name,"%b[]") - -- 2. crop the bracket-sign from both sides - -- 3. and finally replace decorative signs '#' to dir-char '/' - return string.gsub(string.sub(s,2,-3),"#","/") + -- 1. select everything included in brackets + local s = string.match(hist_name,"%b[]") + -- 2. crop the bracket-sign from both sides + -- 3. and finally replace decorative signs '#' to dir-char '/' + return string.gsub(string.sub(s,2,-3),"#","/") end function DocSettings:getNameFromHistory(hist_name) - -- at first, search for path length - local s = string.len(string.match(hist_name,"%b[]")) - -- and return the rest of string without 4 last characters (".lua") - return string.sub(hist_name, s+2, -5) + -- at first, search for path length + local s = string.len(string.match(hist_name,"%b[]")) + -- and return the rest of string without 4 last characters (".lua") + return string.sub(hist_name, s+2, -5) end function DocSettings:open(docfile) - local conf_path = nil - if docfile == ".reader" then - -- we handle reader setting as special case - conf_path = "settings.reader.lua" - else - if lfs.attributes("./history","mode") ~= "directory" then - lfs.mkdir("history") - end - conf_path = self:getHistoryPath(docfile) - end - -- construct settings obj - local new = { file = conf_path, data = {} } - local ok, stored = pcall(dofile, new.file) - if not ok then - -- try legacy conf path, for backward compatibility. this also - -- takes care of reader legacy setting - ok, stored = pcall(dofile, docfile..".kpdfview.lua") - end - if ok then - new.data = stored - end - return setmetatable(new, { __index = DocSettings}) + local conf_path = nil + if docfile == ".reader" then + -- we handle reader setting as special case + conf_path = "settings.reader.lua" + else + if lfs.attributes("./history","mode") ~= "directory" then + lfs.mkdir("history") + end + conf_path = self:getHistoryPath(docfile) + end + -- construct settings obj + local new = { file = conf_path, data = {} } + local ok, stored = pcall(dofile, new.file) + if not ok then + -- try legacy conf path, for backward compatibility. this also + -- takes care of reader legacy setting + ok, stored = pcall(dofile, docfile..".kpdfview.lua") + end + if ok then + new.data = stored + end + return setmetatable(new, { __index = DocSettings}) end function DocSettings:readSetting(key) - return self.data[key] + return self.data[key] end function DocSettings:saveSetting(key, value) - self.data[key] = value + self.data[key] = value end function DocSettings:delSetting(key) - self.data[key] = nil + self.data[key] = nil end function DocSettings:dump(data, max_lv) - local out = {} - self:_serialize(data, out, 0, max_lv) - return table.concat(out) + local out = {} + self:_serialize(data, out, 0, max_lv) + return table.concat(out) end -- simple serialization function, won't do uservalues, functions, loops function DocSettings:_serialize(what, outt, indent, max_lv) - if not max_lv then - max_lv = math.huge - end + if not max_lv then + max_lv = math.huge + end - if indent > max_lv then - return - end + if indent > max_lv then + return + end - if type(what) == "table" then - local didrun = false - table.insert(outt, "{") - for k, v in pairs(what) do - if didrun then - table.insert(outt, ",") - end - table.insert(outt, "\n") - table.insert(outt, string.rep("\t", indent+1)) - table.insert(outt, "[") - self:_serialize(k, outt, indent+1, max_lv) - table.insert(outt, "] = ") - self:_serialize(v, outt, indent+1, max_lv) - didrun = true - end - if didrun then - table.insert(outt, "\n") - table.insert(outt, string.rep("\t", indent)) - end - table.insert(outt, "}") - elseif type(what) == "string" then - table.insert(outt, string.format("%q", what)) - elseif type(what) == "number" or type(what) == "boolean" then - table.insert(outt, tostring(what)) - end + if type(what) == "table" then + local didrun = false + table.insert(outt, "{") + for k, v in pairs(what) do + if didrun then + table.insert(outt, ",") + end + table.insert(outt, "\n") + table.insert(outt, string.rep("\t", indent+1)) + table.insert(outt, "[") + self:_serialize(k, outt, indent+1, max_lv) + table.insert(outt, "] = ") + self:_serialize(v, outt, indent+1, max_lv) + didrun = true + end + if didrun then + table.insert(outt, "\n") + table.insert(outt, string.rep("\t", indent)) + end + table.insert(outt, "}") + elseif type(what) == "string" then + table.insert(outt, string.format("%q", what)) + elseif type(what) == "number" or type(what) == "boolean" then + table.insert(outt, tostring(what)) + end end function DocSettings:flush() - -- write a serialized version of the data table - if not self.file then - return - end - local f_out = io.open(self.file, "w") - if f_out ~= nil then - os.setlocale('C', 'numeric') - local out = {"-- we can read Lua syntax here!\nreturn "} - self:_serialize(self.data, out, 0) - table.insert(out, "\n") - f_out:write(table.concat(out)) - f_out:close() - end + -- write a serialized version of the data table + if not self.file then + return + end + local f_out = io.open(self.file, "w") + if f_out ~= nil then + os.setlocale('C', 'numeric') + local out = {"-- we can read Lua syntax here!\nreturn "} + self:_serialize(self.data, out, 0) + table.insert(out, "\n") + f_out:write(table.concat(out)) + f_out:close() + end end function DocSettings:close() - self:flush() + self:flush() end return DocSettings diff --git a/frontend/document/credocument.lua b/frontend/document/credocument.lua index 44412b528..44fdb2c88 100644 --- a/frontend/document/credocument.lua +++ b/frontend/document/credocument.lua @@ -10,140 +10,140 @@ local DEBUG = require("dbg") -- TBD: DrawContext local CreDocument = Document:new{ - -- this is defined in kpvcrlib/crengine/crengine/include/lvdocview.h - SCROLL_VIEW_MODE = 0, - PAGE_VIEW_MODE = 1, - - _document = false, - engine_initilized = false, - - line_space_percent = 100, - default_font = G_reader_settings:readSetting("cre_font") or "FreeSerif", - header_font = G_reader_settings:readSetting("header_font") or "FreeSans", - fallback_font = G_reader_settings:readSetting("fallback_font") or "Droid Sans Fallback", - default_css = "./data/cr3.css", - options = CreOptions, + -- this is defined in kpvcrlib/crengine/crengine/include/lvdocview.h + SCROLL_VIEW_MODE = 0, + PAGE_VIEW_MODE = 1, + + _document = false, + engine_initilized = false, + + line_space_percent = 100, + default_font = G_reader_settings:readSetting("cre_font") or "FreeSerif", + header_font = G_reader_settings:readSetting("header_font") or "FreeSans", + fallback_font = G_reader_settings:readSetting("fallback_font") or "Droid Sans Fallback", + default_css = "./data/cr3.css", + options = CreOptions, } -- NuPogodi, 20.05.12: inspect the zipfile content function CreDocument.zipContentExt(self, fname) - local outfile = "./data/zip_content" - local s = "" - os.execute("unzip ".."-l \""..fname.."\" > "..outfile) - local i = 1 - if io.open(outfile,"r") then - for lines in io.lines(outfile) do - if i == 4 then s = lines break else i = i + 1 end - end - end - -- return the extention - return string.lower(string.match(s, ".+%.([^.]+)")) + local outfile = "./data/zip_content" + local s = "" + os.execute("unzip ".."-l \""..fname.."\" > "..outfile) + local i = 1 + if io.open(outfile,"r") then + for lines in io.lines(outfile) do + if i == 4 then s = lines break else i = i + 1 end + end + end + -- return the extention + return string.lower(string.match(s, ".+%.([^.]+)")) end function CreDocument:engineInit() - if not engine_initilized then - -- initialize cache - cre.initCache(1024*1024*64) + if not engine_initilized then + -- initialize cache + cre.initCache(1024*1024*64) - -- we need to initialize the CRE font list - local fonts = Font:getFontList() - for _k, _v in ipairs(fonts) do - if _v ~= "Dingbats.cff" and _v ~= "StandardSymL.cff" then - local ok, err = pcall(cre.registerFont, Font.fontdir..'/'.._v) - if not ok then - DEBUG(err) - end - end - end + -- we need to initialize the CRE font list + local fonts = Font:getFontList() + for _k, _v in ipairs(fonts) do + if _v ~= "Dingbats.cff" and _v ~= "StandardSymL.cff" then + local ok, err = pcall(cre.registerFont, Font.fontdir..'/'.._v) + if not ok then + DEBUG(err) + end + end + end - engine_initilized = true - end + engine_initilized = true + end end function CreDocument:init() - require "libs/libkoreader-cre" - self:engineInit() - self.configurable:loadDefaults(self.options) - - local ok - local file_type = string.lower(string.match(self.file, ".+%.([^.]+)")) - if file_type == "zip" then - -- NuPogodi, 20.05.12: read the content of zip-file - -- and return extention of the 1st file - file_type = self:zipContentExt(self.file) - end - -- these two format use the same css file - if file_type == "html" then - file_type = "htm" - end - -- if native css-file doesn't exist, one needs to use default cr3.css - if not io.open("./data/"..file_type..".css") then - file_type = "cr3" - end - self.default_css = "./data/"..file_type..".css" - - -- @TODO check the default view_mode to a global user configurable - -- variable 22.12 2012 (houqp) - ok, self._document = pcall(cre.newDocView, - Screen:getWidth(), Screen:getHeight(), self.PAGE_VIEW_MODE - ) - if not ok then - self.error_message = self.doc -- will contain error message - return - end - - -- adjust font sizes according to screen dpi - self._document:adjustFontSizes(Screen:getDPI()) - - -- set fallback font face - self._document:setStringProperty("crengine.font.fallback.face", self.fallback_font) - - self.is_open = true - self.info.has_pages = false - self:_readMetadata() - self.info.configurable = true + require "libs/libkoreader-cre" + self:engineInit() + self.configurable:loadDefaults(self.options) + + local ok + local file_type = string.lower(string.match(self.file, ".+%.([^.]+)")) + if file_type == "zip" then + -- NuPogodi, 20.05.12: read the content of zip-file + -- and return extention of the 1st file + file_type = self:zipContentExt(self.file) + end + -- these two format use the same css file + if file_type == "html" then + file_type = "htm" + end + -- if native css-file doesn't exist, one needs to use default cr3.css + if not io.open("./data/"..file_type..".css") then + file_type = "cr3" + end + self.default_css = "./data/"..file_type..".css" + + -- @TODO check the default view_mode to a global user configurable + -- variable 22.12 2012 (houqp) + ok, self._document = pcall(cre.newDocView, + Screen:getWidth(), Screen:getHeight(), self.PAGE_VIEW_MODE + ) + if not ok then + self.error_message = self.doc -- will contain error message + return + end + + -- adjust font sizes according to screen dpi + self._document:adjustFontSizes(Screen:getDPI()) + + -- set fallback font face + self._document:setStringProperty("crengine.font.fallback.face", self.fallback_font) + + self.is_open = true + self.info.has_pages = false + self:_readMetadata() + self.info.configurable = true end function CreDocument:loadDocument() - self._document:loadDocument(self.file) - if not self.info.has_pages then - self.info.doc_height = self._document:getFullHeight() - end - if Device:getModel() ~= "KindleDXG" then - self:setVisiblePageCount(1) - end + self._document:loadDocument(self.file) + if not self.info.has_pages then + self.info.doc_height = self._document:getFullHeight() + end + if Device:getModel() ~= "KindleDXG" then + self:setVisiblePageCount(1) + end end function CreDocument:close() - self._document:saveDefaults() - Document.close(self) + self._document:saveDefaults() + Document.close(self) end function CreDocument:getPageCount() - return self._document:getPages() + return self._document:getPages() end function CreDocument:getWordFromPosition(pos) - local word_box = self._document:getWordFromPosition(pos.x, pos.y) - local text_range = self._document:getTextFromPositions(pos.x, pos.y, pos.x, pos.y) - if word_box.word then - return { - word = text_range.text == "" and word_box.word or text_range.text, - page = self._document:getCurrentPage(), - sbox = Geom:new{ - x = word_box.x0, y = word_box.y0, - w = word_box.x1 - word_box.x0, - h = word_box.y1 - word_box.y0, - } - } - end + local word_box = self._document:getWordFromPosition(pos.x, pos.y) + local text_range = self._document:getTextFromPositions(pos.x, pos.y, pos.x, pos.y) + if word_box.word then + return { + word = text_range.text == "" and word_box.word or text_range.text, + page = self._document:getCurrentPage(), + sbox = Geom:new{ + x = word_box.x0, y = word_box.y0, + w = word_box.x1 - word_box.x0, + h = word_box.y1 - word_box.y0, + } + } + end end function CreDocument:getTextFromPositions(pos0, pos1) - local text_range = self._document:getTextFromPositions(pos0.x, pos0.y, pos1.x, pos1.y) - DEBUG("CreDocument: get text range", text_range) - local line_boxes = self:getScreenBoxesFromPositions(text_range.pos0, text_range.pos1) - return { + local text_range = self._document:getTextFromPositions(pos0.x, pos0.y, pos1.x, pos1.y) + DEBUG("CreDocument: get text range", text_range) + local line_boxes = self:getScreenBoxesFromPositions(text_range.pos0, text_range.pos1) + return { text = text_range.text, pos0 = text_range.pos0, pos1 = text_range.pos1, @@ -152,38 +152,38 @@ function CreDocument:getTextFromPositions(pos0, pos1) end function CreDocument:getScreenBoxesFromPositions(pos0, pos1) - local line_boxes = {} - if pos0 and pos1 then - local word_boxes = self._document:getWordBoxesFromPositions(pos0, pos1) - --DEBUG("word boxes", word_boxes) - for i = 1, #word_boxes do - local line_box = word_boxes[i] - table.insert(line_boxes, Geom:new{ - x = line_box.x0, y = line_box.y0, - w = line_box.x1 - line_box.x0, - h = line_box.y1 - line_box.y0, - }) - end - --DEBUG("line boxes", line_boxes) - end - return line_boxes + local line_boxes = {} + if pos0 and pos1 then + local word_boxes = self._document:getWordBoxesFromPositions(pos0, pos1) + --DEBUG("word boxes", word_boxes) + for i = 1, #word_boxes do + local line_box = word_boxes[i] + table.insert(line_boxes, Geom:new{ + x = line_box.x0, y = line_box.y0, + w = line_box.x1 - line_box.x0, + h = line_box.y1 - line_box.y0, + }) + end + --DEBUG("line boxes", line_boxes) + end + return line_boxes end function CreDocument:drawCurrentView(target, x, y, rect, pos) - tile_bb = Blitbuffer.new(rect.w, rect.h) - self._document:drawCurrentPage(tile_bb) - target:blitFrom(tile_bb, x, y, 0, 0, rect.w, rect.h) - tile_bb:free() + tile_bb = Blitbuffer.new(rect.w, rect.h) + self._document:drawCurrentPage(tile_bb) + target:blitFrom(tile_bb, x, y, 0, 0, rect.w, rect.h) + tile_bb:free() end function CreDocument:drawCurrentViewByPos(target, x, y, rect, pos) - self._document:gotoPos(pos) - self:drawCurrentView(target, x, y, rect) + self._document:gotoPos(pos) + self:drawCurrentView(target, x, y, rect) end function CreDocument:drawCurrentViewByPage(target, x, y, rect, page) - self._document:gotoPage(page) - self:drawCurrentView(target, x, y, rect) + self._document:gotoPage(page) + self:drawCurrentView(target, x, y, rect) end function CreDocument:hintPage(pageno, zoom, rotation) @@ -196,182 +196,182 @@ function CreDocument:renderPage(pageno, rect, zoom, rotation) end function CreDocument:gotoXPointer(xpointer) - DEBUG("CreDocument: goto xpointer", xpointer) - self._document:gotoXPointer(xpointer) + DEBUG("CreDocument: goto xpointer", xpointer) + self._document:gotoXPointer(xpointer) end function CreDocument:getXPointer() - return self._document:getXPointer() + return self._document:getXPointer() end function CreDocument:getPosFromXPointer(xp) - return self._document:getPosFromXPointer(xp) + return self._document:getPosFromXPointer(xp) end function CreDocument:getPageFromXPointer(xp) - return self._document:getPageFromXPointer(xp) + return self._document:getPageFromXPointer(xp) end function CreDocument:getFontFace() - return self._document:getFontFace() + return self._document:getFontFace() end function CreDocument:getCurrentPos() - return self._document:getCurrentPos() + return self._document:getCurrentPos() end function CreDocument:getPageLinks() - return self._document:getPageLinks() + return self._document:getPageLinks() end function CreDocument:getLinkFromPosition(pos) - return self._document:getLinkFromPosition(pos.x, pos.y) + return self._document:getLinkFromPosition(pos.x, pos.y) end function Document:gotoPos(pos) - DEBUG("CreDocument: goto position", pos) - self._document:gotoPos(pos) + DEBUG("CreDocument: goto position", pos) + self._document:gotoPos(pos) end function CreDocument:gotoPage(page) - DEBUG("CreDocument: goto page", page) - self._document:gotoPage(page) + DEBUG("CreDocument: goto page", page) + self._document:gotoPage(page) end function CreDocument:gotoLink(link) - DEBUG("CreDocument: goto link", link) - self._document:gotoLink(link) + DEBUG("CreDocument: goto link", link) + self._document:gotoLink(link) end function CreDocument:goBack() - DEBUG("CreDocument: go back") - self._document:goBack() + DEBUG("CreDocument: go back") + self._document:goBack() end function CreDocument:goForward(link) - DEBUG("CreDocument: go forward") - self._document:goForward() + DEBUG("CreDocument: go forward") + self._document:goForward() end function CreDocument:getCurrentPage() - return self._document:getCurrentPage() + return self._document:getCurrentPage() end function CreDocument:setFontFace(new_font_face) - if new_font_face then - DEBUG("CreDocument: set font face", new_font_face) - self._document:setFontFace(new_font_face) - end + if new_font_face then + DEBUG("CreDocument: set font face", new_font_face) + self._document:setFontFace(new_font_face) + end end function CreDocument:clearSelection() - self._document:clearSelection() + self._document:clearSelection() end function CreDocument:getFontSize() - return self._document:getFontSize() + return self._document:getFontSize() end function CreDocument:setFontSize(new_font_size) - if new_font_size then - DEBUG("CreDocument: set font size", new_font_size) - self._document:setFontSize(new_font_size) - end + if new_font_size then + DEBUG("CreDocument: set font size", new_font_size) + self._document:setFontSize(new_font_size) + end end function CreDocument:setViewMode(new_mode) - if new_mode then - DEBUG("CreDocument: set view mode", new_mode) - if new_mode == "scroll" then - self._document:setViewMode(self.SCROLL_VIEW_MODE) - else - self._document:setViewMode(self.PAGE_VIEW_MODE) - end - end + if new_mode then + DEBUG("CreDocument: set view mode", new_mode) + if new_mode == "scroll" then + self._document:setViewMode(self.SCROLL_VIEW_MODE) + else + self._document:setViewMode(self.PAGE_VIEW_MODE) + end + end end function CreDocument:setHeaderFont(new_font) - if new_font then - DEBUG("CreDocument: set header font", new_font) - self._document:setHeaderFont(new_font) - end + if new_font then + DEBUG("CreDocument: set header font", new_font) + self._document:setHeaderFont(new_font) + end end function CreDocument:zoomFont(delta) - DEBUG("CreDocument: zoom font", delta) - self._document:zoomFont(delta) + DEBUG("CreDocument: zoom font", delta) + self._document:zoomFont(delta) end function CreDocument:setInterlineSpacePercent(percent) - DEBUG("CreDocument: set interline space", percent) - self._document:setDefaultInterlineSpace(percent) + DEBUG("CreDocument: set interline space", percent) + self._document:setDefaultInterlineSpace(percent) end function CreDocument:toggleFontBolder() - DEBUG("CreDocument: toggle font bolder") - self._document:toggleFontBolder() + DEBUG("CreDocument: toggle font bolder") + self._document:toggleFontBolder() end function CreDocument:setGammaIndex(index) - DEBUG("CreDocument: set gamma index", index) - cre.setGammaIndex(index) + DEBUG("CreDocument: set gamma index", index) + cre.setGammaIndex(index) end function CreDocument:setStyleSheet(new_css) - DEBUG("CreDocument: set style sheet", new_css) - self._document:setStyleSheet(new_css) + DEBUG("CreDocument: set style sheet", new_css) + self._document:setStyleSheet(new_css) end function CreDocument:setEmbeddedStyleSheet(toggle) - DEBUG("CreDocument: set embedded style sheet", toggle) - self._document:setEmbeddedStyleSheet(toggle) + DEBUG("CreDocument: set embedded style sheet", toggle) + self._document:setEmbeddedStyleSheet(toggle) end function CreDocument:setPageMargins(left, top, right, bottom) - DEBUG("CreDocument: set page margins", left, top, right, bottom) - self._document:setPageMargins(left, top, right, bottom) + DEBUG("CreDocument: set page margins", left, top, right, bottom) + self._document:setPageMargins(left, top, right, bottom) end function CreDocument:setFloatingPunctuation(enabled) - DEBUG("CreDocument: set floating punctuation", enabled) - self._document:setIntProperty("crengine.style.floating.punctuation.enabled", enabled) + DEBUG("CreDocument: set floating punctuation", enabled) + self._document:setIntProperty("crengine.style.floating.punctuation.enabled", enabled) end function CreDocument:setVisiblePageCount(new_count) - DEBUG("CreDocument: set visible page count", new_count) - self._document:setVisiblePageCount(new_count) + DEBUG("CreDocument: set visible page count", new_count) + self._document:setVisiblePageCount(new_count) end function CreDocument:setBatteryState(state) - DEBUG("CreDocument: set battery state", state) - self._document:setBatteryState(state) + DEBUG("CreDocument: set battery state", state) + self._document:setBatteryState(state) end function CreDocument:isXPointerInCurrentPage(xp) - DEBUG("CreDocument: check in page", xp) - return self._document:isXPointerInCurrentPage(xp) + DEBUG("CreDocument: check in page", xp) + return self._document:isXPointerInCurrentPage(xp) end function CreDocument:setStatusLineProp(prop) - DEBUG("CreDocument: set status line property", prop) - self._document:setStringProperty("window.status.line", prop) + DEBUG("CreDocument: set status line property", prop) + self._document:setStringProperty("window.status.line", prop) end function CreDocument:register(registry) - registry:addProvider("txt", "application/txt", self) - registry:addProvider("epub", "application/epub", self) - registry:addProvider("fb2", "application/fb2", self) - registry:addProvider("html", "application/html", self) - registry:addProvider("htm", "application/htm", self) - registry:addProvider("zip", "application/zip", self) - registry:addProvider("rtf", "application/rtf", self) - registry:addProvider("mobi", "application/mobi", self) - registry:addProvider("prc", "application/prc", self) - registry:addProvider("azw", "application/azw", self) - registry:addProvider("chm", "application/chm", self) - registry:addProvider("pdb", "application/pdb", self) - registry:addProvider("doc", "application/doc", self) - registry:addProvider("tcr", "application/tcr", self) + registry:addProvider("txt", "application/txt", self) + registry:addProvider("epub", "application/epub", self) + registry:addProvider("fb2", "application/fb2", self) + registry:addProvider("html", "application/html", self) + registry:addProvider("htm", "application/htm", self) + registry:addProvider("zip", "application/zip", self) + registry:addProvider("rtf", "application/rtf", self) + registry:addProvider("mobi", "application/mobi", self) + registry:addProvider("prc", "application/prc", self) + registry:addProvider("azw", "application/azw", self) + registry:addProvider("chm", "application/chm", self) + registry:addProvider("pdb", "application/pdb", self) + registry:addProvider("doc", "application/doc", self) + registry:addProvider("tcr", "application/tcr", self) end return CreDocument diff --git a/frontend/document/djvudocument.lua b/frontend/document/djvudocument.lua index ac916ae0b..b0aba2aea 100644 --- a/frontend/document/djvudocument.lua +++ b/frontend/document/djvudocument.lua @@ -7,113 +7,113 @@ local Configurable = require("ui/reader/configurable") local DrawContext = require("ffi/drawcontext") local DjvuDocument = Document:new{ - _document = false, - -- libdjvulibre manages its own additional cache, default value is hard written in c module. - djvulibre_cache_size = nil, - dc_null = DrawContext.new(), - options = KoptOptions, - koptinterface = nil, + _document = false, + -- libdjvulibre manages its own additional cache, default value is hard written in c module. + djvulibre_cache_size = nil, + dc_null = DrawContext.new(), + options = KoptOptions, + koptinterface = nil, } -- check DjVu magic string to validate local function validDjvuFile(filename) - f = io.open(filename, "r") - if not f then return false end - local magic = f:read(8) - f:close() - if not magic or magic ~= "AT&TFORM" then return false end - return true + f = io.open(filename, "r") + if not f then return false end + local magic = f:read(8) + f:close() + if not magic or magic ~= "AT&TFORM" then return false end + return true end function DjvuDocument:init() - local djvu = require("libs/libkoreader-djvu") - self.koptinterface = require("document/koptinterface") - self.configurable:loadDefaults(self.options) - if not validDjvuFile(self.file) then - self.error_message = "Not a valid DjVu file" - return - end - - local ok - ok, self._document = pcall(djvu.openDocument, self.file, self.djvulibre_cache_size) - if not ok then - self.error_message = self.doc -- will contain error message - return - end - self.is_open = true - self.info.has_pages = true - self.info.configurable = true - self:_readMetadata() + local djvu = require("libs/libkoreader-djvu") + self.koptinterface = require("document/koptinterface") + self.configurable:loadDefaults(self.options) + if not validDjvuFile(self.file) then + self.error_message = "Not a valid DjVu file" + return + end + + local ok + ok, self._document = pcall(djvu.openDocument, self.file, self.djvulibre_cache_size) + if not ok then + self.error_message = self.doc -- will contain error message + return + end + self.is_open = true + self.info.has_pages = true + self.info.configurable = true + self:_readMetadata() end function DjvuDocument:invertTextYAxel(pageno, text_table) - local _, height = self.doc:getOriginalPageSize(pageno) - for _,text in pairs(text_table) do - for _,line in ipairs(text) do - line.y0, line.y1 = (height - line.y1), (height - line.y0) - end - end - return text_table + local _, height = self.doc:getOriginalPageSize(pageno) + for _,text in pairs(text_table) do + for _,line in ipairs(text) do + line.y0, line.y1 = (height - line.y1), (height - line.y0) + end + end + return text_table end function DjvuDocument:getPageTextBoxes(pageno) - return self._document:getPageText(pageno) + return self._document:getPageText(pageno) end function DjvuDocument:getWordFromPosition(spos) - return self.koptinterface:getWordFromPosition(self, spos) + return self.koptinterface:getWordFromPosition(self, spos) end function DjvuDocument:getTextFromPositions(spos0, spos1) - return self.koptinterface:getTextFromPositions(self, spos0, spos1) + return self.koptinterface:getTextFromPositions(self, spos0, spos1) end function DjvuDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1) - return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1) + return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1) end function DjvuDocument:getOCRWord(pageno, wbox) - return self.koptinterface:getOCRWord(self, pageno, wbox) + return self.koptinterface:getOCRWord(self, pageno, wbox) end function DjvuDocument:getOCRText(pageno, tboxes) - return self.koptinterface:getOCRText(self, pageno, tboxes) + return self.koptinterface:getOCRText(self, pageno, tboxes) end function DjvuDocument:getPageRegions(pageno) - return self.koptinterface:getPageRegions(self, pageno) + return self.koptinterface:getPageRegions(self, pageno) end function DjvuDocument:getUsedBBox(pageno) - -- djvu does not support usedbbox, so fake it. - local used = {} - local native_dim = self:getNativePageDimensions(pageno) - used.x0, used.y0, used.x1, used.y1 = 0, 0, native_dim.w, native_dim.h - return used + -- djvu does not support usedbbox, so fake it. + local used = {} + local native_dim = self:getNativePageDimensions(pageno) + used.x0, used.y0, used.x1, used.y1 = 0, 0, native_dim.w, native_dim.h + return used end function DjvuDocument:getPageBBox(pageno) - return self.koptinterface:getPageBBox(self, pageno) + return self.koptinterface:getPageBBox(self, pageno) end function DjvuDocument:getPageDimensions(pageno, zoom, rotation) - return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation) + return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation) end function DjvuDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) - return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode) + return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode) end function DjvuDocument:hintPage(pageno, zoom, rotation, gamma, render_mode) - return self.koptinterface:hintPage(self, pageno, zoom, rotation, gamma, render_mode) + return self.koptinterface:hintPage(self, pageno, zoom, rotation, gamma, render_mode) end function DjvuDocument:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) - return self.koptinterface:drawPage(self, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) + return self.koptinterface:drawPage(self, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) end function DjvuDocument:register(registry) - registry:addProvider("djvu", "application/djvu", self) + registry:addProvider("djvu", "application/djvu", self) end return DjvuDocument diff --git a/frontend/document/document.lua b/frontend/document/document.lua index c5ec407cc..91e0451dc 100644 --- a/frontend/document/document.lua +++ b/frontend/document/document.lua @@ -11,55 +11,55 @@ local DEBUG = require("dbg") This is an abstract interface to a document ]]-- local Document = { - -- file name - file = nil, - - info = { - -- whether the document is pageable - has_pages = false, - -- whether words can be provided - has_words = false, - -- whether hyperlinks can be provided - has_hyperlinks = false, - -- whether (native to format) annotations can be provided - has_annotations = false, - - -- whether pages can be rotated - is_rotatable = false, - - number_of_pages = 0, - -- if not pageable, length of the document in pixels - doc_height = 0, - - -- other metadata - title = "", - author = "", - date = "" - }, - - links = {}, - - GAMMA_NO_GAMMA = 1.0, - - -- override bbox from orignal page's getUsedBBox - bbox = {}, - - -- flag to show whether the document was opened successfully - is_open = false, - error_message = nil, - - -- flag to show that the document needs to be unlocked by a password - is_locked = false, - - configurable = Configurable, + -- file name + file = nil, + + info = { + -- whether the document is pageable + has_pages = false, + -- whether words can be provided + has_words = false, + -- whether hyperlinks can be provided + has_hyperlinks = false, + -- whether (native to format) annotations can be provided + has_annotations = false, + + -- whether pages can be rotated + is_rotatable = false, + + number_of_pages = 0, + -- if not pageable, length of the document in pixels + doc_height = 0, + + -- other metadata + title = "", + author = "", + date = "" + }, + + links = {}, + + GAMMA_NO_GAMMA = 1.0, + + -- override bbox from orignal page's getUsedBBox + bbox = {}, + + -- flag to show whether the document was opened successfully + is_open = false, + error_message = nil, + + -- flag to show that the document needs to be unlocked by a password + is_locked = false, + + configurable = Configurable, } function Document:new(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - if o.init then o:init() end - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + if o.init then o:init() end + return o end -- override this method to open a document @@ -68,195 +68,195 @@ end -- this might be overridden by a document implementation function Document:unlock(password) - -- return true instead when the password provided unlocked the document - return false + -- return true instead when the password provided unlocked the document + return false end -- this might be overridden by a document implementation function Document:close() - if self.is_open then - self.is_open = false - self._document:close() - end + if self.is_open then + self.is_open = false + self._document:close() + end end -- this might be overridden by a document implementation function Document:getNativePageDimensions(pageno) - local hash = "pgdim|"..self.file.."|"..pageno - local cached = Cache:check(hash) - if cached then - return cached[1] - end - local page = self._document:openPage(pageno) - local page_size_w, page_size_h = page:getSize(self.dc_null) - local page_size = Geom:new{ w = page_size_w, h = page_size_h } - Cache:insert(hash, CacheItem:new{ page_size }) - page:close() - return page_size + local hash = "pgdim|"..self.file.."|"..pageno + local cached = Cache:check(hash) + if cached then + return cached[1] + end + local page = self._document:openPage(pageno) + local page_size_w, page_size_h = page:getSize(self.dc_null) + local page_size = Geom:new{ w = page_size_w, h = page_size_h } + Cache:insert(hash, CacheItem:new{ page_size }) + page:close() + return page_size end function Document:_readMetadata() - self.info.number_of_pages = self._document:getPages() - return true + self.info.number_of_pages = self._document:getPages() + return true end function Document:getPageCount() - return self.info.number_of_pages + return self.info.number_of_pages end -- calculates page dimensions function Document:getPageDimensions(pageno, zoom, rotation) - local native_dimen = self:getNativePageDimensions(pageno):copy() - if rotation == 90 or rotation == 270 then - -- switch orientation - native_dimen.w, native_dimen.h = native_dimen.h, native_dimen.w - end - native_dimen:scaleBy(zoom) - --DEBUG("dimen for pageno", pageno, "zoom", zoom, "rotation", rotation, "is", native_dimen) - return native_dimen + local native_dimen = self:getNativePageDimensions(pageno):copy() + if rotation == 90 or rotation == 270 then + -- switch orientation + native_dimen.w, native_dimen.h = native_dimen.h, native_dimen.w + end + native_dimen:scaleBy(zoom) + --DEBUG("dimen for pageno", pageno, "zoom", zoom, "rotation", rotation, "is", native_dimen) + return native_dimen end function Document:getPageBBox(pageno) - local bbox = self.bbox[pageno] -- exact - if bbox ~= nil then - --DEBUG("bbox from", pageno) - return bbox - else - local oddEven = Math.oddEven(pageno) - bbox = self.bbox[oddEven] -- odd/even - end - if bbox ~= nil then -- last used up to this page - --DEBUG("bbox from", oddEven) - return bbox - else - for i = 0,pageno do - bbox = self.bbox[ pageno - i ] - if bbox ~= nil then - --DEBUG("bbox from", pageno - i) - return bbox - end - end - end - if bbox == nil then -- fallback bbox - bbox = self:getUsedBBox(pageno) - --DEBUG("bbox from ORIGINAL page") - end - --DEBUG("final bbox", bbox) - return bbox + local bbox = self.bbox[pageno] -- exact + if bbox ~= nil then + --DEBUG("bbox from", pageno) + return bbox + else + local oddEven = Math.oddEven(pageno) + bbox = self.bbox[oddEven] -- odd/even + end + if bbox ~= nil then -- last used up to this page + --DEBUG("bbox from", oddEven) + return bbox + else + for i = 0,pageno do + bbox = self.bbox[ pageno - i ] + if bbox ~= nil then + --DEBUG("bbox from", pageno - i) + return bbox + end + end + end + if bbox == nil then -- fallback bbox + bbox = self:getUsedBBox(pageno) + --DEBUG("bbox from ORIGINAL page") + end + --DEBUG("final bbox", bbox) + return bbox end --[[ This method returns pagesize if bbox is corrupted --]] function Document:getUsedBBoxDimensions(pageno, zoom, rotation) - local bbox = self:getPageBBox(pageno) - -- clipping page bbox - if bbox.x0 < 0 then bbox.x0 = 0 end - if bbox.y0 < 0 then bbox.y0 = 0 end - if bbox.x1 < 0 then bbox.x1 = 0 end - if bbox.y1 < 0 then bbox.y1 = 0 end - local ubbox_dimen = nil - if (bbox.x0 > bbox.x1) or (bbox.y0 > bbox.y1) then - -- if document's bbox info is corrupted, we use the page size - ubbox_dimen = self:getPageDimensions(pageno, zoom, rotation) - else - ubbox_dimen = Geom:new{ - x = bbox.x0, - y = bbox.y0, - w = bbox.x1 - bbox.x0, - h = bbox.y1 - bbox.y0, - } - if zoom ~= 1 then - ubbox_dimen:transformByScale(zoom) - end - end - return ubbox_dimen + local bbox = self:getPageBBox(pageno) + -- clipping page bbox + if bbox.x0 < 0 then bbox.x0 = 0 end + if bbox.y0 < 0 then bbox.y0 = 0 end + if bbox.x1 < 0 then bbox.x1 = 0 end + if bbox.y1 < 0 then bbox.y1 = 0 end + local ubbox_dimen = nil + if (bbox.x0 > bbox.x1) or (bbox.y0 > bbox.y1) then + -- if document's bbox info is corrupted, we use the page size + ubbox_dimen = self:getPageDimensions(pageno, zoom, rotation) + else + ubbox_dimen = Geom:new{ + x = bbox.x0, + y = bbox.y0, + w = bbox.x1 - bbox.x0, + h = bbox.y1 - bbox.y0, + } + if zoom ~= 1 then + ubbox_dimen:transformByScale(zoom) + end + end + return ubbox_dimen end function Document:getToc() - return self._document:getToc() + return self._document:getToc() end function Document:getPageLinks(pageno) - return nil + return nil end function Document:getLinkFromPosition(pageno, pos) - return nil + return nil end function Document:getTextBoxes(pageno) - return nil + return nil end function Document:getOCRWord(pageno, rect) - return nil + return nil end function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) - local hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode - local page_size = self:getPageDimensions(pageno, zoom, rotation) - -- this will be the size we actually render - local size = page_size - -- we prefer to render the full page, if it fits into cache - if not Cache:willAccept(size.w * size.h / 2) then - -- whole page won't fit into cache - DEBUG("rendering only part of the page") - -- TODO: figure out how to better segment the page - if not rect then - DEBUG("aborting, since we do not have a specification for that part") - -- required part not given, so abort - return - end - -- only render required part - hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode.."|"..tostring(rect) - size = rect - end - - -- prepare cache item with contained blitbuffer - local tile = TileCacheItem:new{ - size = size.w * size.h / 2 + 64, -- estimation - excerpt = size, - pageno = pageno, - bb = Blitbuffer.new(size.w, size.h) - } - - -- create a draw context - local dc = DrawContext.new() - - dc:setRotate(rotation) - -- correction of rotation - if rotation == 90 then - dc:setOffset(page_size.w, 0) - elseif rotation == 180 then - dc:setOffset(page_size.w, page_size.h) - elseif rotation == 270 then - dc:setOffset(0, page_size.h) - end - dc:setZoom(zoom) - - if gamma ~= self.GAMMA_NO_GAMMA then - --DEBUG("gamma correction: ", gamma) - dc:setGamma(gamma) - end - - -- render - local page = self._document:openPage(pageno) - page:draw(dc, tile.bb, size.x, size.y, render_mode) - page:close() - Cache:insert(hash, tile) - - return tile + local hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode + local page_size = self:getPageDimensions(pageno, zoom, rotation) + -- this will be the size we actually render + local size = page_size + -- we prefer to render the full page, if it fits into cache + if not Cache:willAccept(size.w * size.h / 2) then + -- whole page won't fit into cache + DEBUG("rendering only part of the page") + -- TODO: figure out how to better segment the page + if not rect then + DEBUG("aborting, since we do not have a specification for that part") + -- required part not given, so abort + return + end + -- only render required part + hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode.."|"..tostring(rect) + size = rect + end + + -- prepare cache item with contained blitbuffer + local tile = TileCacheItem:new{ + size = size.w * size.h / 2 + 64, -- estimation + excerpt = size, + pageno = pageno, + bb = Blitbuffer.new(size.w, size.h) + } + + -- create a draw context + local dc = DrawContext.new() + + dc:setRotate(rotation) + -- correction of rotation + if rotation == 90 then + dc:setOffset(page_size.w, 0) + elseif rotation == 180 then + dc:setOffset(page_size.w, page_size.h) + elseif rotation == 270 then + dc:setOffset(0, page_size.h) + end + dc:setZoom(zoom) + + if gamma ~= self.GAMMA_NO_GAMMA then + --DEBUG("gamma correction: ", gamma) + dc:setGamma(gamma) + end + + -- render + local page = self._document:openPage(pageno) + page:draw(dc, tile.bb, size.x, size.y, render_mode) + page:close() + Cache:insert(hash, tile) + + return tile end -- a hint for the cache engine to paint a full page to the cache -- TODO: this should trigger a background operation function Document:hintPage(pageno, zoom, rotation, gamma, render_mode) - local hash_full_page = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode - if not Cache:check(hash_full_page) then - DEBUG("hinting page", pageno) - self:renderPage(pageno, nil, zoom, rotation, gamma, render_mode) - end + local hash_full_page = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode + if not Cache:check(hash_full_page) then + DEBUG("hinting page", pageno) + self:renderPage(pageno, nil, zoom, rotation, gamma, render_mode) + end end --[[ @@ -268,59 +268,59 @@ Draw page content to blitbuffer. @rect: visible_area inside document page --]] function Document:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) - local hash_full_page = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode - local hash_excerpt = hash_full_page.."|"..tostring(rect) - local tile = Cache:check(hash_full_page) - if not tile then - tile = Cache:check(hash_excerpt) - if not tile then - DEBUG("rendering") - tile = self:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) - end - end - DEBUG("now painting", tile, rect) - target:blitFrom(tile.bb, - x, y, - rect.x - tile.excerpt.x, - rect.y - tile.excerpt.y, - rect.w, rect.h) + local hash_full_page = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode + local hash_excerpt = hash_full_page.."|"..tostring(rect) + local tile = Cache:check(hash_full_page) + if not tile then + tile = Cache:check(hash_excerpt) + if not tile then + DEBUG("rendering") + tile = self:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) + end + end + DEBUG("now painting", tile, rect) + target:blitFrom(tile.bb, + x, y, + rect.x - tile.excerpt.x, + rect.y - tile.excerpt.y, + rect.w, rect.h) end function Document:getPageText(pageno) - -- is this worth caching? not done yet. - local page = self._document:openPage(pageno) - local text = page:getPageText() - page:close() - return text + -- is this worth caching? not done yet. + local page = self._document:openPage(pageno) + local text = page:getPageText() + page:close() + return text end function Document:saveHighlight(pageno, item) - return nil + return nil end --[[ helper functions --]] function Document:logMemoryUsage(pageno) - local status_file = io.open("/proc/self/status", "r") - local log_file = io.open("mem_usage_log.txt", "a+") - local data = -1 - if status_file then - for line in status_file:lines() do - local s, n - s, n = line:gsub("VmData:%s-(%d+) kB", "%1") - if n ~= 0 then data = tonumber(s) end - if data ~= -1 then break end - end - status_file:close() - end - if log_file then - if log_file:seek("end") == 0 then -- write the header only once - log_file:write("PAGE\tMEM\n") - end - log_file:write(string.format("%s\t%s\n", pageno, data)) - log_file:close() - end + local status_file = io.open("/proc/self/status", "r") + local log_file = io.open("mem_usage_log.txt", "a+") + local data = -1 + if status_file then + for line in status_file:lines() do + local s, n + s, n = line:gsub("VmData:%s-(%d+) kB", "%1") + if n ~= 0 then data = tonumber(s) end + if data ~= -1 then break end + end + status_file:close() + end + if log_file then + if log_file:seek("end") == 0 then -- write the header only once + log_file:write("PAGE\tMEM\n") + end + log_file:write(string.format("%s\t%s\n", pageno, data)) + log_file:close() + end end return Document diff --git a/frontend/document/documentregistry.lua b/frontend/document/documentregistry.lua index 21ae9969a..50e7fae89 100644 --- a/frontend/document/documentregistry.lua +++ b/frontend/document/documentregistry.lua @@ -2,21 +2,21 @@ This is a registry for document providers ]]-- local DocumentRegistry = { - providers = { } + providers = { } } function DocumentRegistry:addProvider(extension, mimetype, provider) - table.insert(self.providers, { extension = extension, mimetype = mimetype, provider = provider }) + table.insert(self.providers, { extension = extension, mimetype = mimetype, provider = provider }) end function DocumentRegistry:getProvider(file) - -- TODO: some implementation based on mime types? - local extension = string.lower(string.match(file, ".+%.([^.]+)") or "") - for _, provider in ipairs(self.providers) do - if extension == provider.extension then - return provider.provider - end - end + -- TODO: some implementation based on mime types? + local extension = string.lower(string.match(file, ".+%.([^.]+)") or "") + for _, provider in ipairs(self.providers) do + if extension == provider.extension then + return provider.provider + end + end end function DocumentRegistry:openDocument(file) diff --git a/frontend/document/koptinterface.lua b/frontend/document/koptinterface.lua index d4e115d9a..212798073 100644 --- a/frontend/document/koptinterface.lua +++ b/frontend/document/koptinterface.lua @@ -8,167 +8,167 @@ local DEBUG = require("dbg") local KOPTContext = require("ffi/koptcontext") local KoptInterface = { - ocrengine = "ocrengine", - tessocr_data = "data", - ocr_lang = "eng", - ocr_type = 3, -- default 0, for more accuracy use 3 - last_context_size = nil, - default_context_size = 1024*1024, - screen_dpi = Screen:getDPI(), + ocrengine = "ocrengine", + tessocr_data = "data", + ocr_lang = "eng", + ocr_type = 3, -- default 0, for more accuracy use 3 + last_context_size = nil, + default_context_size = 1024*1024, + screen_dpi = Screen:getDPI(), } local ContextCacheItem = CacheItem:new{} function ContextCacheItem:onFree() - if self.kctx.free then - DEBUG("free koptcontext", self.kctx) - self.kctx:free() - end + if self.kctx.free then + DEBUG("free koptcontext", self.kctx) + self.kctx:free() + end end local OCREngine = CacheItem:new{} function OCREngine:onFree() - if self.ocrengine.freeOCR then - DEBUG("free OCREngine", self.ocrengine) - self.ocrengine:freeOCR() - end + if self.ocrengine.freeOCR then + DEBUG("free OCREngine", self.ocrengine) + self.ocrengine:freeOCR() + end end function KoptInterface:waitForContext(kc) - -- if koptcontext is being processed in background thread - -- the isPreCache will return 1. - while kc and kc:isPreCache() == 1 do - DEBUG("waiting for background rendering") - util.usleep(100000) - end - return kc + -- if koptcontext is being processed in background thread + -- the isPreCache will return 1. + while kc and kc:isPreCache() == 1 do + DEBUG("waiting for background rendering") + util.usleep(100000) + end + return kc end --[[ get reflow context --]] function KoptInterface:createContext(doc, pageno, bbox) - -- Now koptcontext keeps track of its dst bitmap reflowed by libk2pdfopt. - -- So there is no need to check background context when creating new context. - local kc = KOPTContext.new() - local screen_size = Screen:getSize() - local lang = doc.configurable.doc_language - if lang == "chi_sim" or lang == "chi_tra" or - lang == "jpn" or lang == "kor" then - kc:setCJKChar() - end - kc:setLanguage(lang) - kc:setTrim(doc.configurable.trim_page) - kc:setWrap(doc.configurable.text_wrap) - kc:setIndent(doc.configurable.detect_indent) - kc:setColumns(doc.configurable.max_columns) - kc:setDeviceDim(screen_size.w, screen_size.h) - kc:setDeviceDPI(self.screen_dpi) - kc:setStraighten(doc.configurable.auto_straighten) - kc:setJustification(doc.configurable.justification) - kc:setWritingDirection(doc.configurable.writing_direction) - kc:setZoom(doc.configurable.font_size) - kc:setMargin(doc.configurable.page_margin) - kc:setQuality(doc.configurable.quality) - kc:setContrast(doc.configurable.contrast) - kc:setDefectSize(doc.configurable.defect_size) - kc:setLineSpacing(doc.configurable.line_spacing) - kc:setWordSpacing(doc.configurable.word_spacing) - if bbox then - if bbox.x0 >= bbox.x1 or bbox.y0 >= bbox.y1 then - local page_size = Document.getNativePageDimensions(doc, pageno) - bbox.x0, bbox.y0 = 0, 0 - bbox.x1, bbox.y1 = page_size.w, page_size.h - end - kc:setBBox(bbox.x0, bbox.y0, bbox.x1, bbox.y1) - end - if DEBUG.is_on then kc:setDebug() end - return kc + -- Now koptcontext keeps track of its dst bitmap reflowed by libk2pdfopt. + -- So there is no need to check background context when creating new context. + local kc = KOPTContext.new() + local screen_size = Screen:getSize() + local lang = doc.configurable.doc_language + if lang == "chi_sim" or lang == "chi_tra" or + lang == "jpn" or lang == "kor" then + kc:setCJKChar() + end + kc:setLanguage(lang) + kc:setTrim(doc.configurable.trim_page) + kc:setWrap(doc.configurable.text_wrap) + kc:setIndent(doc.configurable.detect_indent) + kc:setColumns(doc.configurable.max_columns) + kc:setDeviceDim(screen_size.w, screen_size.h) + kc:setDeviceDPI(self.screen_dpi) + kc:setStraighten(doc.configurable.auto_straighten) + kc:setJustification(doc.configurable.justification) + kc:setWritingDirection(doc.configurable.writing_direction) + kc:setZoom(doc.configurable.font_size) + kc:setMargin(doc.configurable.page_margin) + kc:setQuality(doc.configurable.quality) + kc:setContrast(doc.configurable.contrast) + kc:setDefectSize(doc.configurable.defect_size) + kc:setLineSpacing(doc.configurable.line_spacing) + kc:setWordSpacing(doc.configurable.word_spacing) + if bbox then + if bbox.x0 >= bbox.x1 or bbox.y0 >= bbox.y1 then + local page_size = Document.getNativePageDimensions(doc, pageno) + bbox.x0, bbox.y0 = 0, 0 + bbox.x1, bbox.y1 = page_size.w, page_size.h + end + kc:setBBox(bbox.x0, bbox.y0, bbox.x1, bbox.y1) + end + if DEBUG.is_on then kc:setDebug() end + return kc end function KoptInterface:getContextHash(doc, pageno, bbox) - local screen_size = Screen:getSize() - local screen_size_hash = screen_size.w.."|"..screen_size.h - local bbox_hash = bbox.x0.."|"..bbox.y0.."|"..bbox.x1.."|"..bbox.y1 - return doc.file.."|"..pageno.."|"..doc.configurable:hash("|").."|"..bbox_hash.."|"..screen_size_hash + local screen_size = Screen:getSize() + local screen_size_hash = screen_size.w.."|"..screen_size.h + local bbox_hash = bbox.x0.."|"..bbox.y0.."|"..bbox.x1.."|"..bbox.y1 + return doc.file.."|"..pageno.."|"..doc.configurable:hash("|").."|"..bbox_hash.."|"..screen_size_hash end function KoptInterface:getPageBBox(doc, pageno) - if doc.configurable.text_wrap ~= 1 and doc.configurable.trim_page == 1 then - -- auto bbox finding - return self:getAutoBBox(doc, pageno) - elseif doc.configurable.text_wrap ~= 1 and doc.configurable.trim_page == 2 then - -- semi-auto bbox finding - return self:getSemiAutoBBox(doc, pageno) - else - -- get saved manual bbox - return Document.getPageBBox(doc, pageno) - end + if doc.configurable.text_wrap ~= 1 and doc.configurable.trim_page == 1 then + -- auto bbox finding + return self:getAutoBBox(doc, pageno) + elseif doc.configurable.text_wrap ~= 1 and doc.configurable.trim_page == 2 then + -- semi-auto bbox finding + return self:getSemiAutoBBox(doc, pageno) + else + -- get saved manual bbox + return Document.getPageBBox(doc, pageno) + end end --[[ auto detect bbox --]] -function KoptInterface:getAutoBBox(doc, pageno) - local native_size = Document.getNativePageDimensions(doc, pageno) - local bbox = { - x0 = 0, y0 = 0, - x1 = native_size.w, - y1 = native_size.h, - } - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "autobbox|"..context_hash - local cached = Cache:check(hash) - if not cached then - local page = doc._document:openPage(pageno) - local kc = self:createContext(doc, pageno, bbox) - --DEBUGBT() - --DEBUG("getAutoBBox:native page size", native_size) - local x0, y0, x1, y1 = page:getAutoBBox(kc) - local w, h = native_size.w, native_size.h - if (x1 - x0)/w > 0.1 or (y1 - y0)/h > 0.1 then - bbox.x0, bbox.y0, bbox.x1, bbox.y1 = x0, y0, x1, y1 - --DEBUG("getAutoBBox:auto detected bbox", bbox) - else - bbox = Document.getPageBBox(doc, pageno) - --DEBUG("getAutoBBox:use manual bbox", bbox) - end - Cache:insert(hash, CacheItem:new{ autobbox = bbox }) - page:close() - kc:free() - return bbox - else - return cached.autobbox - end +function KoptInterface:getAutoBBox(doc, pageno) + local native_size = Document.getNativePageDimensions(doc, pageno) + local bbox = { + x0 = 0, y0 = 0, + x1 = native_size.w, + y1 = native_size.h, + } + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "autobbox|"..context_hash + local cached = Cache:check(hash) + if not cached then + local page = doc._document:openPage(pageno) + local kc = self:createContext(doc, pageno, bbox) + --DEBUGBT() + --DEBUG("getAutoBBox:native page size", native_size) + local x0, y0, x1, y1 = page:getAutoBBox(kc) + local w, h = native_size.w, native_size.h + if (x1 - x0)/w > 0.1 or (y1 - y0)/h > 0.1 then + bbox.x0, bbox.y0, bbox.x1, bbox.y1 = x0, y0, x1, y1 + --DEBUG("getAutoBBox:auto detected bbox", bbox) + else + bbox = Document.getPageBBox(doc, pageno) + --DEBUG("getAutoBBox:use manual bbox", bbox) + end + Cache:insert(hash, CacheItem:new{ autobbox = bbox }) + page:close() + kc:free() + return bbox + else + return cached.autobbox + end end --[[ detect bbox within user restricted bbox --]] function KoptInterface:getSemiAutoBBox(doc, pageno) - -- use manual bbox - local bbox = Document.getPageBBox(doc, pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "semiautobbox|"..context_hash - local cached = Cache:check(hash) - if not cached then - local page = doc._document:openPage(pageno) - local kc = self:createContext(doc, pageno, bbox) - local auto_bbox = {} - auto_bbox.x0, auto_bbox.y0, auto_bbox.x1, auto_bbox.y1 = page:getAutoBBox(kc) - auto_bbox.x0 = auto_bbox.x0 + bbox.x0 - auto_bbox.y0 = auto_bbox.y0 + bbox.y0 - auto_bbox.x1 = auto_bbox.x1 + bbox.x0 - auto_bbox.y1 = auto_bbox.y1 + bbox.y0 - DEBUG("Semi-auto detected bbox", auto_bbox) - page:close() - Cache:insert(hash, CacheItem:new{ semiautobbox = auto_bbox }) - kc:free() - return auto_bbox - else - return cached.semiautobbox - end + -- use manual bbox + local bbox = Document.getPageBBox(doc, pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "semiautobbox|"..context_hash + local cached = Cache:check(hash) + if not cached then + local page = doc._document:openPage(pageno) + local kc = self:createContext(doc, pageno, bbox) + local auto_bbox = {} + auto_bbox.x0, auto_bbox.y0, auto_bbox.x1, auto_bbox.y1 = page:getAutoBBox(kc) + auto_bbox.x0 = auto_bbox.x0 + bbox.x0 + auto_bbox.y0 = auto_bbox.y0 + bbox.y0 + auto_bbox.x1 = auto_bbox.x1 + bbox.x0 + auto_bbox.y1 = auto_bbox.y1 + bbox.y0 + DEBUG("Semi-auto detected bbox", auto_bbox) + page:close() + Cache:insert(hash, CacheItem:new{ semiautobbox = auto_bbox }) + kc:free() + return auto_bbox + else + return cached.semiautobbox + end end --[[ @@ -177,65 +177,65 @@ new context and reflow the src page immediatly, or wait background thread for reflowed context. --]] function KoptInterface:getCachedContext(doc, pageno) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - if not cached then - -- If kctx is not cached, create one and get reflowed bmp in foreground. - local kc = self:createContext(doc, pageno, bbox) - local page = doc._document:openPage(pageno) - -- reflow page - --local secs, usecs = util.gettime() - page:reflow(kc, 0) - page:close() - --local nsecs, nusecs = util.gettime() - --local dur = nsecs - secs + (nusecs - usecs) / 1000000 - --DEBUG("Reflow duration:", dur) - --self:logReflowDuration(pageno, dur) - local fullwidth, fullheight = kc:getPageDim() - DEBUG("reflowed page", pageno, "fullwidth:", fullwidth, "fullheight:", fullheight) - self.last_context_size = fullwidth * fullheight + 128 -- estimation - Cache:insert(kctx_hash, ContextCacheItem:new{ - size = self.last_context_size, - kctx = kc - }) - return kc - else - -- wait for background thread - local kc = self:waitForContext(cached.kctx) - local fullwidth, fullheight = kc:getPageDim() - self.last_context_size = fullwidth * fullheight + 128 -- estimation - return kc - end + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + if not cached then + -- If kctx is not cached, create one and get reflowed bmp in foreground. + local kc = self:createContext(doc, pageno, bbox) + local page = doc._document:openPage(pageno) + -- reflow page + --local secs, usecs = util.gettime() + page:reflow(kc, 0) + page:close() + --local nsecs, nusecs = util.gettime() + --local dur = nsecs - secs + (nusecs - usecs) / 1000000 + --DEBUG("Reflow duration:", dur) + --self:logReflowDuration(pageno, dur) + local fullwidth, fullheight = kc:getPageDim() + DEBUG("reflowed page", pageno, "fullwidth:", fullwidth, "fullheight:", fullheight) + self.last_context_size = fullwidth * fullheight + 128 -- estimation + Cache:insert(kctx_hash, ContextCacheItem:new{ + size = self.last_context_size, + kctx = kc + }) + return kc + else + -- wait for background thread + local kc = self:waitForContext(cached.kctx) + local fullwidth, fullheight = kc:getPageDim() + self.last_context_size = fullwidth * fullheight + 128 -- estimation + return kc + end end --[[ get page dimensions --]] function KoptInterface:getPageDimensions(doc, pageno, zoom, rotation) - if doc.configurable.text_wrap == 1 then - return self:getRFPageDimensions(doc, pageno, zoom, rotation) - else - return Document.getPageDimensions(doc, pageno, zoom, rotation) - end + if doc.configurable.text_wrap == 1 then + return self:getRFPageDimensions(doc, pageno, zoom, rotation) + else + return Document.getPageDimensions(doc, pageno, zoom, rotation) + end end --[[ get reflowed page dimensions --]] function KoptInterface:getRFPageDimensions(doc, pageno, zoom, rotation) - local kc = self:getCachedContext(doc, pageno) - local fullwidth, fullheight = kc:getPageDim() - return Geom:new{ w = fullwidth, h = fullheight } + local kc = self:getCachedContext(doc, pageno) + local fullwidth, fullheight = kc:getPageDim() + return Geom:new{ w = fullwidth, h = fullheight } end function KoptInterface:renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode) - if doc.configurable.text_wrap == 1 then - return self:renderreflowedPage(doc, pageno, rect, zoom, rotation, render_mode) - else - return Document.renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode) - end + if doc.configurable.text_wrap == 1 then + return self:renderreflowedPage(doc, pageno, rect, zoom, rotation, render_mode) + else + return Document.renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode) + end end --[[ @@ -243,43 +243,43 @@ inherited from common document interface render reflowed page into tile cache. --]] function KoptInterface:renderreflowedPage(doc, pageno, rect, zoom, rotation, render_mode) - doc.render_mode = render_mode - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local renderpg_hash = "renderpg|"..context_hash - - local cached = Cache:check(renderpg_hash) - if not cached then - -- do the real reflowing if kctx is not been cached yet - local kc = self:getCachedContext(doc, pageno) - local fullwidth, fullheight = kc:getPageDim() - if not Cache:willAccept(fullwidth * fullheight / 2) then - -- whole page won't fit into cache - error("aborting, since we don't have enough cache for this page") - end - local page = doc._document:openPage(pageno) - -- prepare cache item with contained blitbuffer - local tile = TileCacheItem:new{ - size = fullwidth * fullheight / 2 + 64, -- estimation - excerpt = Geom:new{ w = fullwidth, h = fullheight }, - pageno = pageno, - bb = Blitbuffer.new(fullwidth, fullheight) - } - page:rfdraw(kc, tile.bb) - page:close() - Cache:insert(renderpg_hash, tile) - return tile - else - return cached - end + doc.render_mode = render_mode + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local renderpg_hash = "renderpg|"..context_hash + + local cached = Cache:check(renderpg_hash) + if not cached then + -- do the real reflowing if kctx is not been cached yet + local kc = self:getCachedContext(doc, pageno) + local fullwidth, fullheight = kc:getPageDim() + if not Cache:willAccept(fullwidth * fullheight / 2) then + -- whole page won't fit into cache + error("aborting, since we don't have enough cache for this page") + end + local page = doc._document:openPage(pageno) + -- prepare cache item with contained blitbuffer + local tile = TileCacheItem:new{ + size = fullwidth * fullheight / 2 + 64, -- estimation + excerpt = Geom:new{ w = fullwidth, h = fullheight }, + pageno = pageno, + bb = Blitbuffer.new(fullwidth, fullheight) + } + page:rfdraw(kc, tile.bb) + page:close() + Cache:insert(renderpg_hash, tile) + return tile + else + return cached + end end function KoptInterface:hintPage(doc, pageno, zoom, rotation, gamma, render_mode) - if doc.configurable.text_wrap == 1 then - self:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode) - else - Document.hintPage(doc, pageno, zoom, rotation, gamma, render_mode) - end + if doc.configurable.text_wrap == 1 then + self:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode) + else + Document.hintPage(doc, pageno, zoom, rotation, gamma, render_mode) + end end --[[ @@ -289,31 +289,31 @@ in context. subsequent usage of this context should wait for the precache flag off by calling self:waitForContext(kctx) --]] function KoptInterface:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - if not cached then - local kc = self:createContext(doc, pageno, bbox) - local page = doc._document:openPage(pageno) - DEBUG("hinting page", pageno, "in background") - -- reflow will return immediately and running in background thread - kc:setPreCache() - page:reflow(kc, 0) - page:close() - Cache:insert(kctx_hash, ContextCacheItem:new{ - size = self.last_context_size or self.default_context_size, - kctx = kc, - }) - end + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + if not cached then + local kc = self:createContext(doc, pageno, bbox) + local page = doc._document:openPage(pageno) + DEBUG("hinting page", pageno, "in background") + -- reflow will return immediately and running in background thread + kc:setPreCache() + page:reflow(kc, 0) + page:close() + Cache:insert(kctx_hash, ContextCacheItem:new{ + size = self.last_context_size or self.default_context_size, + kctx = kc, + }) + end end function KoptInterface:drawPage(doc, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) - if doc.configurable.text_wrap == 1 then - self:drawReflowedPage(doc, target, x, y, rect, pageno, zoom, rotation, render_mode) - else - Document.drawPage(doc, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) - end + if doc.configurable.text_wrap == 1 then + self:drawReflowedPage(doc, target, x, y, rect, pageno, zoom, rotation, render_mode) + else + Document.drawPage(doc, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) + end end --[[ @@ -321,13 +321,13 @@ inherited from common document interface draw cached tile pixels into target blitbuffer. --]] function KoptInterface:drawReflowedPage(doc, target, x, y, rect, pageno, zoom, rotation, render_mode) - local tile = self:renderPage(doc, pageno, rect, zoom, rotation, render_mode) - --DEBUG("now painting", tile, rect) - target:blitFrom(tile.bb, - x, y, - rect.x - tile.excerpt.x, - rect.y - tile.excerpt.y, - rect.w, rect.h) + local tile = self:renderPage(doc, pageno, rect, zoom, rotation, render_mode) + --DEBUG("now painting", tile, rect) + target:blitFrom(tile.bb, + x, y, + rect.x - tile.excerpt.x, + rect.y - tile.excerpt.y, + rect.w, rect.h) end --[[ @@ -335,66 +335,66 @@ extract text boxes in a PDF/Djvu page returned boxes are in native page coordinates zoomed at 1.0 --]] function KoptInterface:getTextBoxes(doc, pageno) - local text = doc:getPageTextBoxes(pageno) - if text and #text > 1 then - return text - -- if we have no text in original page then we will reuse native word boxes - -- in reflow mode and find text boxes from scratch in non-reflow mode - else - if doc.configurable.text_wrap == 1 then - return self:getNativeTextBoxes(doc, pageno) - else - return self:getNativeTextBoxesFromScratch(doc, pageno) - end - end + local text = doc:getPageTextBoxes(pageno) + if text and #text > 1 then + return text + -- if we have no text in original page then we will reuse native word boxes + -- in reflow mode and find text boxes from scratch in non-reflow mode + else + if doc.configurable.text_wrap == 1 then + return self:getNativeTextBoxes(doc, pageno) + else + return self:getNativeTextBoxesFromScratch(doc, pageno) + end + end end --[[ get text boxes in reflowed page via rectmaps in koptcontext --]] function KoptInterface:getReflowedTextBoxes(doc, pageno) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "rfpgboxes|"..context_hash - local cached = Cache:check(hash) - if not cached then - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - if cached then - local kc = self:waitForContext(cached.kctx) - --kc:setDebug() - local fullwidth, fullheight = kc:getPageDim() - local boxes = kc:getReflowedWordBoxes(0, 0, fullwidth, fullheight) - Cache:insert(hash, CacheItem:new{ rfpgboxes = boxes }) - return boxes - end - else - return cached.rfpgboxes - end + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "rfpgboxes|"..context_hash + local cached = Cache:check(hash) + if not cached then + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + if cached then + local kc = self:waitForContext(cached.kctx) + --kc:setDebug() + local fullwidth, fullheight = kc:getPageDim() + local boxes = kc:getReflowedWordBoxes(0, 0, fullwidth, fullheight) + Cache:insert(hash, CacheItem:new{ rfpgboxes = boxes }) + return boxes + end + else + return cached.rfpgboxes + end end --[[ get text boxes in native page via rectmaps in koptcontext --]] function KoptInterface:getNativeTextBoxes(doc, pageno) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "nativepgboxes|"..context_hash - local cached = Cache:check(hash) - if not cached then - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - if cached then - local kc = self:waitForContext(cached.kctx) - --kc:setDebug() - local fullwidth, fullheight = kc:getPageDim() - local boxes = kc:getNativeWordBoxes(0, 0, fullwidth, fullheight) - Cache:insert(hash, CacheItem:new{ nativepgboxes = boxes }) - return boxes - end - else - return cached.nativepgboxes - end + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "nativepgboxes|"..context_hash + local cached = Cache:check(hash) + if not cached then + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + if cached then + local kc = self:waitForContext(cached.kctx) + --kc:setDebug() + local fullwidth, fullheight = kc:getPageDim() + local boxes = kc:getNativeWordBoxes(0, 0, fullwidth, fullheight) + Cache:insert(hash, CacheItem:new{ nativepgboxes = boxes }) + return boxes + end + else + return cached.nativepgboxes + end end --[[ @@ -402,26 +402,26 @@ get text boxes in reflowed page via optical method, i.e. OCR pre-processing in Tesseract and Leptonica. --]] function KoptInterface:getReflowedTextBoxesFromScratch(doc, pageno) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "scratchrfpgboxes|"..context_hash - local cached = Cache:check(hash) - if not cached then - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - if cached then - local reflowed_kc = self:waitForContext(cached.kctx) - local fullwidth, fullheight = reflowed_kc:getPageDim() - local kc = self:createContext(doc, pageno) - kc:copyDestBMP(reflowed_kc) - local boxes = kc:getNativeWordBoxes(0, 0, fullwidth, fullheight) - Cache:insert(hash, CacheItem:new{ scratchrfpgboxes = boxes }) - kc:free() - return boxes - end - else - return cached.scratchrfpgboxes - end + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "scratchrfpgboxes|"..context_hash + local cached = Cache:check(hash) + if not cached then + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + if cached then + local reflowed_kc = self:waitForContext(cached.kctx) + local fullwidth, fullheight = reflowed_kc:getPageDim() + local kc = self:createContext(doc, pageno) + kc:copyDestBMP(reflowed_kc) + local boxes = kc:getNativeWordBoxes(0, 0, fullwidth, fullheight) + Cache:insert(hash, CacheItem:new{ scratchrfpgboxes = boxes }) + kc:free() + return boxes + end + else + return cached.scratchrfpgboxes + end end --[[ @@ -429,260 +429,260 @@ get text boxes in native page via optical method, i.e. OCR pre-processing in Tesseract and Leptonica. --]] function KoptInterface:getNativeTextBoxesFromScratch(doc, pageno) - local hash = "scratchnativepgboxes|"..doc.file.."|"..pageno - local cached = Cache:check(hash) - if not cached then - local page_size = Document.getNativePageDimensions(doc, pageno) - local bbox = { - x0 = 0, y0 = 0, - x1 = page_size.w, - y1 = page_size.h, - } - local kc = self:createContext(doc, pageno, bbox) - kc:setZoom(1.0) - local page = doc._document:openPage(pageno) - page:getPagePix(kc) - local boxes = kc:getNativeWordBoxes(0, 0, page_size.w, page_size.h) - Cache:insert(hash, CacheItem:new{ scratchnativepgboxes = boxes }) - page:close() - kc:free() - return boxes - else - return cached.scratchnativepgboxes - end + local hash = "scratchnativepgboxes|"..doc.file.."|"..pageno + local cached = Cache:check(hash) + if not cached then + local page_size = Document.getNativePageDimensions(doc, pageno) + local bbox = { + x0 = 0, y0 = 0, + x1 = page_size.w, + y1 = page_size.h, + } + local kc = self:createContext(doc, pageno, bbox) + kc:setZoom(1.0) + local page = doc._document:openPage(pageno) + page:getPagePix(kc) + local boxes = kc:getNativeWordBoxes(0, 0, page_size.w, page_size.h) + Cache:insert(hash, CacheItem:new{ scratchnativepgboxes = boxes }) + page:close() + kc:free() + return boxes + else + return cached.scratchnativepgboxes + end end --[[ get page regions in native page via optical method, --]] function KoptInterface:getPageRegions(doc, pageno) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "pageregions|"..context_hash - local cached = Cache:check(hash) - if not cached then - local page_size = Document.getNativePageDimensions(doc, pageno) - local bbox = { - x0 = 0, y0 = 0, - x1 = page_size.w, - y1 = page_size.h, - } - local kc = self:createContext(doc, pageno, bbox) - kc:setZoom(1.0) - local page = doc._document:openPage(pageno) - page:getPagePix(kc) - local regions = kc:getPageRegions() - Cache:insert(hash, CacheItem:new{ pageregions = regions }) - page:close() - kc:free() - return regions - else - return cached.pageregions - end + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "pageregions|"..context_hash + local cached = Cache:check(hash) + if not cached then + local page_size = Document.getNativePageDimensions(doc, pageno) + local bbox = { + x0 = 0, y0 = 0, + x1 = page_size.w, + y1 = page_size.h, + } + local kc = self:createContext(doc, pageno, bbox) + kc:setZoom(1.0) + local page = doc._document:openPage(pageno) + page:getPagePix(kc) + local regions = kc:getPageRegions() + Cache:insert(hash, CacheItem:new{ pageregions = regions }) + page:close() + kc:free() + return regions + else + return cached.pageregions + end end --[[ get word from OCR providing selected word box --]] function KoptInterface:getOCRWord(doc, pageno, wbox) - if not Cache:check(self.ocrengine) then - Cache:insert(self.ocrengine, OCREngine:new{ ocrengine = KOPTContext.new() }) - end - if doc.configurable.text_wrap == 1 then - return self:getReflewOCRWord(doc, pageno, wbox.sbox) - else - return self:getNativeOCRWord(doc, pageno, wbox.sbox) - end + if not Cache:check(self.ocrengine) then + Cache:insert(self.ocrengine, OCREngine:new{ ocrengine = KOPTContext.new() }) + end + if doc.configurable.text_wrap == 1 then + return self:getReflewOCRWord(doc, pageno, wbox.sbox) + else + return self:getNativeOCRWord(doc, pageno, wbox.sbox) + end end --[[ get word from OCR in reflew page --]] function KoptInterface:getReflewOCRWord(doc, pageno, rect) - self.ocr_lang = doc.configurable.doc_language - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local hash = "rfocrword|"..context_hash..rect.x..rect.y..rect.w..rect.h - local cached = Cache:check(hash) - if not cached then - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - if cached then - local kc = self:waitForContext(cached.kctx) - local ok, word = pcall( - kc.getTOCRWord, kc, - rect.x, rect.y, rect.w, rect.h, - self.tessocr_data, self.ocr_lang, self.ocr_type, 0, 1) - Cache:insert(hash, CacheItem:new{ rfocrword = word }) - return word - end - else - return cached.rfocrword - end + self.ocr_lang = doc.configurable.doc_language + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "rfocrword|"..context_hash..rect.x..rect.y..rect.w..rect.h + local cached = Cache:check(hash) + if not cached then + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + if cached then + local kc = self:waitForContext(cached.kctx) + local ok, word = pcall( + kc.getTOCRWord, kc, + rect.x, rect.y, rect.w, rect.h, + self.tessocr_data, self.ocr_lang, self.ocr_type, 0, 1) + Cache:insert(hash, CacheItem:new{ rfocrword = word }) + return word + end + else + return cached.rfocrword + end end --[[ get word from OCR in native page --]] function KoptInterface:getNativeOCRWord(doc, pageno, rect) - self.ocr_lang = doc.configurable.doc_language - local hash = "ocrword|"..doc.file.."|"..pageno..rect.x..rect.y..rect.w..rect.h - local cached = Cache:check(hash) - if not cached then - local bbox = { - x0 = rect.x - math.floor(rect.h * 0.3), - y0 = rect.y - math.floor(rect.h * 0.3), - x1 = rect.x + rect.w + math.floor(rect.h * 0.3), - y1 = rect.y + rect.h + math.floor(rect.h * 0.3), - } - local kc = self:createContext(doc, pageno, bbox) - kc:setZoom(30/rect.h) - local page = doc._document:openPage(pageno) - page:getPagePix(kc) - local word_w, word_h = kc:getPageDim() - local ok, word = pcall( - kc.getTOCRWord, kc, - 0, 0, word_w, word_h, - self.tessocr_data, self.ocr_lang, self.ocr_type, 0, 1) - Cache:insert(hash, CacheItem:new{ ocrword = word }) - page:close() - kc:free() - return word - else - return cached.ocrword - end + self.ocr_lang = doc.configurable.doc_language + local hash = "ocrword|"..doc.file.."|"..pageno..rect.x..rect.y..rect.w..rect.h + local cached = Cache:check(hash) + if not cached then + local bbox = { + x0 = rect.x - math.floor(rect.h * 0.3), + y0 = rect.y - math.floor(rect.h * 0.3), + x1 = rect.x + rect.w + math.floor(rect.h * 0.3), + y1 = rect.y + rect.h + math.floor(rect.h * 0.3), + } + local kc = self:createContext(doc, pageno, bbox) + kc:setZoom(30/rect.h) + local page = doc._document:openPage(pageno) + page:getPagePix(kc) + local word_w, word_h = kc:getPageDim() + local ok, word = pcall( + kc.getTOCRWord, kc, + 0, 0, word_w, word_h, + self.tessocr_data, self.ocr_lang, self.ocr_type, 0, 1) + Cache:insert(hash, CacheItem:new{ ocrword = word }) + page:close() + kc:free() + return word + else + return cached.ocrword + end end --[[ get text from OCR providing selected text boxes --]] function KoptInterface:getOCRText(doc, pageno, tboxes) - if not Cache:check(self.ocrengine) then - Cache:insert(self.ocrengine, OCREngine:new{ ocrengine = KOPTContext.new() }) - end - DEBUG("Not implemented yet") + if not Cache:check(self.ocrengine) then + Cache:insert(self.ocrengine, OCREngine:new{ ocrengine = KOPTContext.new() }) + end + DEBUG("Not implemented yet") end --[[ get index of nearest word box around pos --]] local function inside_box(box, pos) - local x, y = pos.x, pos.y - if box.x0 <= x and box.y0 <= y and box.x1 >= x and box.y1 >= y then - return true - end - return false + local x, y = pos.x, pos.y + if box.x0 <= x and box.y0 <= y and box.x1 >= x and box.y1 >= y then + return true + end + return false end local function box_distance(box, pos) - if inside_box(box, pos) then - return 0 - else - local x0, y0 = pos.x, pos.y - local x1, y1 = (box.x0 + box.x1) / 2, (box.y0 + box.y1) / 2 - return (x0 - x1)*(x0 - x1) + (y0 - y1)*(y0 - y1) - end + if inside_box(box, pos) then + return 0 + else + local x0, y0 = pos.x, pos.y + local x1, y1 = (box.x0 + box.x1) / 2, (box.y0 + box.y1) / 2 + return (x0 - x1)*(x0 - x1) + (y0 - y1)*(y0 - y1) + end end local function getWordBoxIndices(boxes, pos) - local m, n = 1, 1 - for i = 1, #boxes do - for j = 1, #boxes[i] do - if box_distance(boxes[i][j], pos) < box_distance(boxes[m][n], pos) then - m, n = i, j - end - end - end - return m, n + local m, n = 1, 1 + for i = 1, #boxes do + for j = 1, #boxes[i] do + if box_distance(boxes[i][j], pos) < box_distance(boxes[m][n], pos) then + m, n = i, j + end + end + end + return m, n end --[[ get word and word box around pos --]] function KoptInterface:getWordFromBoxes(boxes, pos) - if not pos or #boxes == 0 then return {} end - local i, j = getWordBoxIndices(boxes, pos) - local lb = boxes[i] - local wb = boxes[i][j] - if lb and wb then - local box = Geom:new{ - x = wb.x0, y = lb.y0, - w = wb.x1 - wb.x0, - h = lb.y1 - lb.y0, - } - return { - word = wb.word, - box = box, - } - end + if not pos or #boxes == 0 then return {} end + local i, j = getWordBoxIndices(boxes, pos) + local lb = boxes[i] + local wb = boxes[i][j] + if lb and wb then + local box = Geom:new{ + x = wb.x0, y = lb.y0, + w = wb.x1 - wb.x0, + h = lb.y1 - lb.y0, + } + return { + word = wb.word, + box = box, + } + end end --[[ get text and text boxes between pos0 and pos1 --]] function KoptInterface:getTextFromBoxes(boxes, pos0, pos1) - if not pos0 or not pos1 or #boxes == 0 then return {} end + if not pos0 or not pos1 or #boxes == 0 then return {} end local line_text = "" local line_boxes = {} local i_start, j_start = getWordBoxIndices(boxes, pos0) local i_stop, j_stop = getWordBoxIndices(boxes, pos1) if i_start == i_stop and j_start > j_stop or i_start > i_stop then - i_start, i_stop = i_stop, i_start - j_start, j_stop = j_stop, j_start + i_start, i_stop = i_stop, i_start + j_start, j_stop = j_stop, j_start end for i = i_start, i_stop do - if i_start == i_stop and #boxes[i] == 0 then break end - -- insert line words - local j0 = i > i_start and 1 or j_start - local j1 = i < i_stop and #boxes[i] or j_stop - for j = j0, j1 do - local word = boxes[i][j].word - if word then - -- if last character of this word is an ascii char then append a space - local space = (word:match("[%z\194-\244][\128-\191]*$") or j == j1) - and "" or " " - line_text = line_text..word..space - end - end - -- insert line box - local lb = boxes[i] - if i > i_start and i < i_stop then - local line_box = Geom:new{ - x = lb.x0, y = lb.y0, - w = lb.x1 - lb.x0, - h = lb.y1 - lb.y0, - } - table.insert(line_boxes, line_box) - elseif i == i_start and i < i_stop then - local wb = boxes[i][j_start] - local line_box = Geom:new{ - x = wb.x0, y = lb.y0, - w = lb.x1 - wb.x0, - h = lb.y1 - lb.y0, - } - table.insert(line_boxes, line_box) - elseif i > i_start and i == i_stop then - local wb = boxes[i][j_stop] - local line_box = Geom:new{ - x = lb.x0, y = lb.y0, - w = wb.x1 - lb.x0, - h = lb.y1 - lb.y0, - } - table.insert(line_boxes, line_box) - elseif i == i_start and i == i_stop then - local wb_start = boxes[i][j_start] - local wb_stop = boxes[i][j_stop] - local line_box = Geom:new{ - x = wb_start.x0, y = lb.y0, - w = wb_stop.x1 - wb_start.x0, - h = lb.y1 - lb.y0, - } - table.insert(line_boxes, line_box) - end + if i_start == i_stop and #boxes[i] == 0 then break end + -- insert line words + local j0 = i > i_start and 1 or j_start + local j1 = i < i_stop and #boxes[i] or j_stop + for j = j0, j1 do + local word = boxes[i][j].word + if word then + -- if last character of this word is an ascii char then append a space + local space = (word:match("[%z\194-\244][\128-\191]*$") or j == j1) + and "" or " " + line_text = line_text..word..space + end + end + -- insert line box + local lb = boxes[i] + if i > i_start and i < i_stop then + local line_box = Geom:new{ + x = lb.x0, y = lb.y0, + w = lb.x1 - lb.x0, + h = lb.y1 - lb.y0, + } + table.insert(line_boxes, line_box) + elseif i == i_start and i < i_stop then + local wb = boxes[i][j_start] + local line_box = Geom:new{ + x = wb.x0, y = lb.y0, + w = lb.x1 - wb.x0, + h = lb.y1 - lb.y0, + } + table.insert(line_boxes, line_box) + elseif i > i_start and i == i_stop then + local wb = boxes[i][j_stop] + local line_box = Geom:new{ + x = lb.x0, y = lb.y0, + w = wb.x1 - lb.x0, + h = lb.y1 - lb.y0, + } + table.insert(line_boxes, line_box) + elseif i == i_start and i == i_stop then + local wb_start = boxes[i][j_start] + local wb_stop = boxes[i][j_stop] + local line_box = Geom:new{ + x = wb_start.x0, y = lb.y0, + w = wb_stop.x1 - wb_start.x0, + h = lb.y1 - lb.y0, + } + table.insert(line_boxes, line_box) + end end return { - text = line_text, - boxes = line_boxes, + text = line_text, + boxes = line_boxes, } end @@ -690,233 +690,233 @@ end get word and word box from doc position ]]-- function KoptInterface:getWordFromPosition(doc, pos) - local text_boxes = self:getTextBoxes(doc, pos.page) - if text_boxes then - if doc.configurable.text_wrap == 1 then - return self:getWordFromReflowPosition(doc, text_boxes, pos) - else - return self:getWordFromNativePosition(doc, text_boxes, pos) - end - end + local text_boxes = self:getTextBoxes(doc, pos.page) + if text_boxes then + if doc.configurable.text_wrap == 1 then + return self:getWordFromReflowPosition(doc, text_boxes, pos) + else + return self:getWordFromNativePosition(doc, text_boxes, pos) + end + end end local function getBoxRelativePosition(s_box, l_box) - local pos_rel = {} - local s_box_center = s_box:center() - pos_rel.x = (s_box_center.x - l_box.x)/l_box.w - pos_rel.y = (s_box_center.y - l_box.y)/l_box.h - return pos_rel + local pos_rel = {} + local s_box_center = s_box:center() + pos_rel.x = (s_box_center.x - l_box.x)/l_box.w + pos_rel.y = (s_box_center.y - l_box.y)/l_box.h + return pos_rel end --[[ get word and word box from position in reflowed page ]]-- function KoptInterface:getWordFromReflowPosition(doc, boxes, pos) - local pageno = pos.page - - local scratch_reflowed_page_boxes = self:getReflowedTextBoxesFromScratch(doc, pageno) - local scratch_reflowed_word_box = self:getWordFromBoxes(scratch_reflowed_page_boxes, pos) - --DEBUG("word box from scratch", scratch_reflowed_word_box) - - local reflowed_page_boxes = self:getReflowedTextBoxes(doc, pageno) - local reflowed_word_box = self:getWordFromBoxes(reflowed_page_boxes, pos) - --DEBUG("word box from reflow", reflowed_word_box) - - local reflowed_pos_abs = scratch_reflowed_word_box.box:center() - local reflowed_pos_rel = getBoxRelativePosition(scratch_reflowed_word_box.box, reflowed_word_box.box) - --DEBUG("word box absolote center", reflowed_pos_abs) - --DEBUG("word box relative center", reflowed_pos_rel) - - local native_pos = self:reflowToNativePosTransform(doc, pageno, reflowed_pos_abs, reflowed_pos_rel) - local native_word_box = self:getWordFromBoxes(boxes, native_pos) - - local word_box = { - word = native_word_box.word, - pbox = native_word_box.box, -- box on page - sbox = scratch_reflowed_word_box.box, -- box on screen - pos = native_pos, - } - return word_box + local pageno = pos.page + + local scratch_reflowed_page_boxes = self:getReflowedTextBoxesFromScratch(doc, pageno) + local scratch_reflowed_word_box = self:getWordFromBoxes(scratch_reflowed_page_boxes, pos) + --DEBUG("word box from scratch", scratch_reflowed_word_box) + + local reflowed_page_boxes = self:getReflowedTextBoxes(doc, pageno) + local reflowed_word_box = self:getWordFromBoxes(reflowed_page_boxes, pos) + --DEBUG("word box from reflow", reflowed_word_box) + + local reflowed_pos_abs = scratch_reflowed_word_box.box:center() + local reflowed_pos_rel = getBoxRelativePosition(scratch_reflowed_word_box.box, reflowed_word_box.box) + --DEBUG("word box absolote center", reflowed_pos_abs) + --DEBUG("word box relative center", reflowed_pos_rel) + + local native_pos = self:reflowToNativePosTransform(doc, pageno, reflowed_pos_abs, reflowed_pos_rel) + local native_word_box = self:getWordFromBoxes(boxes, native_pos) + + local word_box = { + word = native_word_box.word, + pbox = native_word_box.box, -- box on page + sbox = scratch_reflowed_word_box.box, -- box on screen + pos = native_pos, + } + return word_box end --[[ get word and word box from position in native page ]]-- function KoptInterface:getWordFromNativePosition(doc, boxes, pos) - local native_word_box = self:getWordFromBoxes(boxes, pos) - local word_box = { - word = native_word_box.word, - pbox = native_word_box.box, -- box on page - sbox = native_word_box.box, -- box on screen - pos = pos, - } - return word_box + local native_word_box = self:getWordFromBoxes(boxes, pos) + local word_box = { + word = native_word_box.word, + pbox = native_word_box.box, -- box on page + sbox = native_word_box.box, -- box on screen + pos = pos, + } + return word_box end --[[ get link from position in screen page ]]-- function KoptInterface:getLinkFromPosition(doc, pageno, pos) - local function inside_box(pos, box) - if pos then - local x, y = pos.x, pos.y - if box.x <= x and box.y <= y - and box.x + box.w >= x - and box.y + box.h >= y then - return true - end - end - end - local page_links = doc:getPageLinks(pageno) - if page_links then - if doc.configurable.text_wrap == 1 then - pos = self:reflowToNativePosTransform(doc, pageno, pos, {x=0.5, y=0.5}) - end - for i = 1, #page_links do - local link = page_links[i] - -- enlarge tappable link box - local lbox = Geom:new{ - x = link.x0 - Screen:scaleByDPI(15), - y = link.y0 - Screen:scaleByDPI(15), - w = link.x1 - link.x0 + Screen:scaleByDPI(30), - h = link.y1 - link.y0 + Screen:scaleByDPI(30) - } - if inside_box(pos, lbox) and link.page then - return link - end - end - end + local function inside_box(pos, box) + if pos then + local x, y = pos.x, pos.y + if box.x <= x and box.y <= y + and box.x + box.w >= x + and box.y + box.h >= y then + return true + end + end + end + local page_links = doc:getPageLinks(pageno) + if page_links then + if doc.configurable.text_wrap == 1 then + pos = self:reflowToNativePosTransform(doc, pageno, pos, {x=0.5, y=0.5}) + end + for i = 1, #page_links do + local link = page_links[i] + -- enlarge tappable link box + local lbox = Geom:new{ + x = link.x0 - Screen:scaleByDPI(15), + y = link.y0 - Screen:scaleByDPI(15), + w = link.x1 - link.x0 + Screen:scaleByDPI(30), + h = link.y1 - link.y0 + Screen:scaleByDPI(30) + } + if inside_box(pos, lbox) and link.page then + return link + end + end + end end --[[ transform position in native page to reflowed page ]]-- function KoptInterface:nativeToReflowPosTransform(doc, pageno, pos) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - local kc = self:waitForContext(cached.kctx) - --DEBUG("transform native pos", pos) - local rpos = {} - rpos.x, rpos.y = kc:nativeToReflowPosTransform(pos.x, pos.y) - --DEBUG("transformed reflowed pos", rpos) - return rpos + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + local kc = self:waitForContext(cached.kctx) + --DEBUG("transform native pos", pos) + local rpos = {} + rpos.x, rpos.y = kc:nativeToReflowPosTransform(pos.x, pos.y) + --DEBUG("transformed reflowed pos", rpos) + return rpos end --[[ transform position in reflowed page to native page ]]-- function KoptInterface:reflowToNativePosTransform(doc, pageno, abs_pos, rel_pos) - local bbox = doc:getPageBBox(pageno) - local context_hash = self:getContextHash(doc, pageno, bbox) - local kctx_hash = "kctx|"..context_hash - local cached = Cache:check(kctx_hash) - local kc = self:waitForContext(cached.kctx) - --kc:setDebug() - --DEBUG("transform reflowed pos", abs_pos, rel_pos) - local npos = {} - npos.x, npos.y = kc:reflowToNativePosTransform(abs_pos.x, abs_pos.y, rel_pos.x, rel_pos.y) - --DEBUG("transformed native pos", npos) - return npos + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local kctx_hash = "kctx|"..context_hash + local cached = Cache:check(kctx_hash) + local kc = self:waitForContext(cached.kctx) + --kc:setDebug() + --DEBUG("transform reflowed pos", abs_pos, rel_pos) + local npos = {} + npos.x, npos.y = kc:reflowToNativePosTransform(abs_pos.x, abs_pos.y, rel_pos.x, rel_pos.y) + --DEBUG("transformed native pos", npos) + return npos end --[[ get text and text boxes from screen positions --]] function KoptInterface:getTextFromPositions(doc, pos0, pos1) - local text_boxes = self:getTextBoxes(doc, pos0.page) - if text_boxes then - if doc.configurable.text_wrap == 1 then - return self:getTextFromReflowPositions(doc, text_boxes, pos0, pos1) - else - return self:getTextFromNativePositions(doc, text_boxes, pos0, pos1) - end - end + local text_boxes = self:getTextBoxes(doc, pos0.page) + if text_boxes then + if doc.configurable.text_wrap == 1 then + return self:getTextFromReflowPositions(doc, text_boxes, pos0, pos1) + else + return self:getTextFromNativePositions(doc, text_boxes, pos0, pos1) + end + end end --[[ get text and text boxes from screen positions for reflowed page ]]-- function KoptInterface:getTextFromReflowPositions(doc, native_boxes, pos0, pos1) - local pageno = pos0.page - - local scratch_reflowed_page_boxes = self:getReflowedTextBoxesFromScratch(doc, pageno) - local reflowed_page_boxes = self:getReflowedTextBoxes(doc, pageno) - - local scratch_reflowed_word_box0 = self:getWordFromBoxes(scratch_reflowed_page_boxes, pos0) - local reflowed_word_box0 = self:getWordFromBoxes(reflowed_page_boxes, pos0) - local scratch_reflowed_word_box1 = self:getWordFromBoxes(scratch_reflowed_page_boxes, pos1) - local reflowed_word_box1 = self:getWordFromBoxes(reflowed_page_boxes, pos1) - - local reflowed_pos_abs0 = scratch_reflowed_word_box0.box:center() - local reflowed_pos_rel0 = getBoxRelativePosition(scratch_reflowed_word_box0.box, reflowed_word_box0.box) - local reflowed_pos_abs1 = scratch_reflowed_word_box1.box:center() - local reflowed_pos_rel1 = getBoxRelativePosition(scratch_reflowed_word_box1.box, reflowed_word_box1.box) - --DEBUG("absolute positions", reflowed_pos_abs0, reflowed_pos_abs1) - --DEBUG("relative positions", reflowed_pos_rel0, reflowed_pos_rel1) - - local native_pos0 = self:reflowToNativePosTransform(doc, pageno, reflowed_pos_abs0, reflowed_pos_rel0) - local native_pos1 = self:reflowToNativePosTransform(doc, pageno, reflowed_pos_abs1, reflowed_pos_rel1) - --DEBUG("native positions", native_pos0, native_pos1) - - local reflowed_text_boxes = self:getTextFromBoxes(reflowed_page_boxes, pos0, pos1) - local native_text_boxes = self:getTextFromBoxes(native_boxes, native_pos0, native_pos1) - local text_boxes = { - text = native_text_boxes.text, - pboxes = native_text_boxes.boxes, -- boxes on page - sboxes = reflowed_text_boxes.boxes, -- boxes on screen - pos0 = native_pos0, - pos1 = native_pos1 - } - return text_boxes + local pageno = pos0.page + + local scratch_reflowed_page_boxes = self:getReflowedTextBoxesFromScratch(doc, pageno) + local reflowed_page_boxes = self:getReflowedTextBoxes(doc, pageno) + + local scratch_reflowed_word_box0 = self:getWordFromBoxes(scratch_reflowed_page_boxes, pos0) + local reflowed_word_box0 = self:getWordFromBoxes(reflowed_page_boxes, pos0) + local scratch_reflowed_word_box1 = self:getWordFromBoxes(scratch_reflowed_page_boxes, pos1) + local reflowed_word_box1 = self:getWordFromBoxes(reflowed_page_boxes, pos1) + + local reflowed_pos_abs0 = scratch_reflowed_word_box0.box:center() + local reflowed_pos_rel0 = getBoxRelativePosition(scratch_reflowed_word_box0.box, reflowed_word_box0.box) + local reflowed_pos_abs1 = scratch_reflowed_word_box1.box:center() + local reflowed_pos_rel1 = getBoxRelativePosition(scratch_reflowed_word_box1.box, reflowed_word_box1.box) + --DEBUG("absolute positions", reflowed_pos_abs0, reflowed_pos_abs1) + --DEBUG("relative positions", reflowed_pos_rel0, reflowed_pos_rel1) + + local native_pos0 = self:reflowToNativePosTransform(doc, pageno, reflowed_pos_abs0, reflowed_pos_rel0) + local native_pos1 = self:reflowToNativePosTransform(doc, pageno, reflowed_pos_abs1, reflowed_pos_rel1) + --DEBUG("native positions", native_pos0, native_pos1) + + local reflowed_text_boxes = self:getTextFromBoxes(reflowed_page_boxes, pos0, pos1) + local native_text_boxes = self:getTextFromBoxes(native_boxes, native_pos0, native_pos1) + local text_boxes = { + text = native_text_boxes.text, + pboxes = native_text_boxes.boxes, -- boxes on page + sboxes = reflowed_text_boxes.boxes, -- boxes on screen + pos0 = native_pos0, + pos1 = native_pos1 + } + return text_boxes end --[[ get text and text boxes from screen positions for native page ]]-- function KoptInterface:getTextFromNativePositions(doc, native_boxes, pos0, pos1) - local native_text_boxes = self:getTextFromBoxes(native_boxes, pos0, pos1) - local text_boxes = { - text = native_text_boxes.text, - pboxes = native_text_boxes.boxes, -- boxes on page - sboxes = native_text_boxes.boxes, -- boxes on screen - pos0 = pos0, - pos1 = pos1, - } - return text_boxes + local native_text_boxes = self:getTextFromBoxes(native_boxes, pos0, pos1) + local text_boxes = { + text = native_text_boxes.text, + pboxes = native_text_boxes.boxes, -- boxes on page + sboxes = native_text_boxes.boxes, -- boxes on screen + pos0 = pos0, + pos1 = pos1, + } + return text_boxes end --[[ get text boxes from page positions --]] function KoptInterface:getPageBoxesFromPositions(doc, pageno, ppos0, ppos1) - if not ppos0 or not ppos1 then return end - if doc.configurable.text_wrap == 1 then - local spos0 = self:nativeToReflowPosTransform(doc, pageno, ppos0) - local spos1 = self:nativeToReflowPosTransform(doc, pageno, ppos1) - local page_boxes = self:getReflowedTextBoxes(doc, pageno) - local text_boxes = self:getTextFromBoxes(page_boxes, spos0, spos1) - return text_boxes.boxes - else - local page_boxes = self:getTextBoxes(doc, pageno) - local text_boxes = self:getTextFromBoxes(page_boxes, ppos0, ppos1) - return text_boxes.boxes - end + if not ppos0 or not ppos1 then return end + if doc.configurable.text_wrap == 1 then + local spos0 = self:nativeToReflowPosTransform(doc, pageno, ppos0) + local spos1 = self:nativeToReflowPosTransform(doc, pageno, ppos1) + local page_boxes = self:getReflowedTextBoxes(doc, pageno) + local text_boxes = self:getTextFromBoxes(page_boxes, spos0, spos1) + return text_boxes.boxes + else + local page_boxes = self:getTextBoxes(doc, pageno) + local text_boxes = self:getTextFromBoxes(page_boxes, ppos0, ppos1) + return text_boxes.boxes + end end --[[ helper functions --]] function KoptInterface:logReflowDuration(pageno, dur) - local file = io.open("reflow_dur_log.txt", "a+") - if file then - if file:seek("end") == 0 then -- write the header only once - file:write("PAGE\tDUR\n") - end - file:write(string.format("%s\t%s\n", pageno, dur)) - file:close() - end + local file = io.open("reflow_dur_log.txt", "a+") + if file then + if file:seek("end") == 0 then -- write the header only once + file:write("PAGE\tDUR\n") + end + file:write(string.format("%s\t%s\n", pageno, dur)) + file:close() + end end return KoptInterface diff --git a/frontend/document/pdfdocument.lua b/frontend/document/pdfdocument.lua index e7e9fa8df..6677aebda 100644 --- a/frontend/document/pdfdocument.lua +++ b/frontend/document/pdfdocument.lua @@ -8,211 +8,211 @@ local ffi = require("ffi") ffi.cdef[[ typedef struct fz_point_s fz_point; struct fz_point_s { - float x, y; + float x, y; }; typedef enum { - FZ_ANNOT_TEXT, - FZ_ANNOT_LINK, - FZ_ANNOT_FREETEXT, - FZ_ANNOT_LINE, - FZ_ANNOT_SQUARE, - FZ_ANNOT_CIRCLE, - FZ_ANNOT_POLYGON, - FZ_ANNOT_POLYLINE, - FZ_ANNOT_HIGHLIGHT, - FZ_ANNOT_UNDERLINE, - FZ_ANNOT_SQUIGGLY, - FZ_ANNOT_STRIKEOUT, - FZ_ANNOT_STAMP, - FZ_ANNOT_CARET, - FZ_ANNOT_INK, - FZ_ANNOT_POPUP, - FZ_ANNOT_FILEATTACHMENT, - FZ_ANNOT_SOUND, - FZ_ANNOT_MOVIE, - FZ_ANNOT_WIDGET, - FZ_ANNOT_SCREEN, - FZ_ANNOT_PRINTERMARK, - FZ_ANNOT_TRAPNET, - FZ_ANNOT_WATERMARK, - FZ_ANNOT_3D + FZ_ANNOT_TEXT, + FZ_ANNOT_LINK, + FZ_ANNOT_FREETEXT, + FZ_ANNOT_LINE, + FZ_ANNOT_SQUARE, + FZ_ANNOT_CIRCLE, + FZ_ANNOT_POLYGON, + FZ_ANNOT_POLYLINE, + FZ_ANNOT_HIGHLIGHT, + FZ_ANNOT_UNDERLINE, + FZ_ANNOT_SQUIGGLY, + FZ_ANNOT_STRIKEOUT, + FZ_ANNOT_STAMP, + FZ_ANNOT_CARET, + FZ_ANNOT_INK, + FZ_ANNOT_POPUP, + FZ_ANNOT_FILEATTACHMENT, + FZ_ANNOT_SOUND, + FZ_ANNOT_MOVIE, + FZ_ANNOT_WIDGET, + FZ_ANNOT_SCREEN, + FZ_ANNOT_PRINTERMARK, + FZ_ANNOT_TRAPNET, + FZ_ANNOT_WATERMARK, + FZ_ANNOT_3D } fz_annot_type; ]] local PdfDocument = Document:new{ - _document = false, - -- muPDF manages its own additional cache - mupdf_cache_size = 5 * 1024 * 1024, - dc_null = DrawContext.new(), - options = KoptOptions, - koptinterface = nil, - annot_revision = 0, + _document = false, + -- muPDF manages its own additional cache + mupdf_cache_size = 5 * 1024 * 1024, + dc_null = DrawContext.new(), + options = KoptOptions, + koptinterface = nil, + annot_revision = 0, } function PdfDocument:init() - local pdf = require("libs/libkoreader-pdf") - self.koptinterface = require("document/koptinterface") - self.configurable:loadDefaults(self.options) - local ok - ok, self._document = pcall(pdf.openDocument, self.file, self.mupdf_cache_size) - if not ok then - self.error_message = self.doc -- will contain error message - return - end - self.is_open = true - self.info.has_pages = true - self.info.configurable = true - if self._document:needsPassword() then - self.is_locked = true - else - self:_readMetadata() - end + local pdf = require("libs/libkoreader-pdf") + self.koptinterface = require("document/koptinterface") + self.configurable:loadDefaults(self.options) + local ok + ok, self._document = pcall(pdf.openDocument, self.file, self.mupdf_cache_size) + if not ok then + self.error_message = self.doc -- will contain error message + return + end + self.is_open = true + self.info.has_pages = true + self.info.configurable = true + if self._document:needsPassword() then + self.is_locked = true + else + self:_readMetadata() + end end function PdfDocument:unlock(password) - if not self._document:authenticatePassword(password) then - self._document:close() - return false, "wrong password" - end - self.is_locked = false - return self:_readMetadata() + if not self._document:authenticatePassword(password) then + self._document:close() + return false, "wrong password" + end + self.is_locked = false + return self:_readMetadata() end function PdfDocument:getPageTextBoxes(pageno) - local page = self._document:openPage(pageno) - local text = page:getPageText() - page:close() - return text + local page = self._document:openPage(pageno) + local text = page:getPageText() + page:close() + return text end function PdfDocument:getWordFromPosition(spos) - return self.koptinterface:getWordFromPosition(self, spos) + return self.koptinterface:getWordFromPosition(self, spos) end function PdfDocument:getTextFromPositions(spos0, spos1) - return self.koptinterface:getTextFromPositions(self, spos0, spos1) + return self.koptinterface:getTextFromPositions(self, spos0, spos1) end function PdfDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1) - return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1) + return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1) end function PdfDocument:getOCRWord(pageno, wbox) - return self.koptinterface:getOCRWord(self, pageno, wbox) + return self.koptinterface:getOCRWord(self, pageno, wbox) end function PdfDocument:getOCRText(pageno, tboxes) - return self.koptinterface:getOCRText(self, pageno, tboxes) + return self.koptinterface:getOCRText(self, pageno, tboxes) end function PdfDocument:getPageRegions(pageno) - return self.koptinterface:getPageRegions(self, pageno) + return self.koptinterface:getPageRegions(self, pageno) end function PdfDocument:getUsedBBox(pageno) - local hash = "pgubbox|"..self.file.."|"..pageno - local cached = Cache:check(hash) - if cached then - return cached.ubbox - end - local page = self._document:openPage(pageno) - local used = {} - used.x0, used.y0, used.x1, used.y1 = page:getUsedBBox() - local pwidth, pheight = page:getSize(self.dc_null) - -- clamp to page BBox - if used.x0 < 0 then used.x0 = 0 end - if used.x1 > pwidth then used.x1 = pwidth end - if used.y0 < 0 then used.y0 = 0 end - if used.y1 > pheight then used.y1 = pheight end - --@TODO give size for cacheitem? 02.12 2012 (houqp) - Cache:insert(hash, CacheItem:new{ - ubbox = used, - }) - page:close() - return used + local hash = "pgubbox|"..self.file.."|"..pageno + local cached = Cache:check(hash) + if cached then + return cached.ubbox + end + local page = self._document:openPage(pageno) + local used = {} + used.x0, used.y0, used.x1, used.y1 = page:getUsedBBox() + local pwidth, pheight = page:getSize(self.dc_null) + -- clamp to page BBox + if used.x0 < 0 then used.x0 = 0 end + if used.x1 > pwidth then used.x1 = pwidth end + if used.y0 < 0 then used.y0 = 0 end + if used.y1 > pheight then used.y1 = pheight end + --@TODO give size for cacheitem? 02.12 2012 (houqp) + Cache:insert(hash, CacheItem:new{ + ubbox = used, + }) + page:close() + return used end function PdfDocument:getPageLinks(pageno) - local hash = "pglinks|"..self.file.."|"..pageno - local cached = Cache:check(hash) - if cached then - return cached.links - end - local page = self._document:openPage(pageno) - local links = page:getPageLinks() - Cache:insert(hash, CacheItem:new{ - links = links, - }) - page:close() - return links + local hash = "pglinks|"..self.file.."|"..pageno + local cached = Cache:check(hash) + if cached then + return cached.links + end + local page = self._document:openPage(pageno) + local links = page:getPageLinks() + Cache:insert(hash, CacheItem:new{ + links = links, + }) + page:close() + return links end function PdfDocument:saveHighlight(pageno, item) - self.annot_revision = self.annot_revision + 1 - local n = #item.pboxes - local quadpoints = ffi.new("fz_point[?]", 4*n) - for i=1, n do - quadpoints[4*i-4].x = item.pboxes[i].x + item.pboxes[i].w - quadpoints[4*i-4].y = item.pboxes[i].y + item.pboxes[i].h - quadpoints[4*i-3].x = item.pboxes[i].x - quadpoints[4*i-3].y = item.pboxes[i].y + item.pboxes[i].h - quadpoints[4*i-2].x = item.pboxes[i].x - quadpoints[4*i-2].y = item.pboxes[i].y - quadpoints[4*i-1].x = item.pboxes[i].x + item.pboxes[i].w - quadpoints[4*i-1].y = item.pboxes[i].y - end - local page = self._document:openPage(pageno) - local annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT - if item.drawer == "lighten" then - annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT - elseif item.drawer == "underscore" then - annot_type = ffi.C.FZ_ANNOT_UNDERLINE - elseif item.drawer == "strikeout" then - annot_type = ffi.C.FZ_ANNOT_STRIKEOUT - end - page:addMarkupAnnotation(quadpoints, 4*n, annot_type) - page:close() + self.annot_revision = self.annot_revision + 1 + local n = #item.pboxes + local quadpoints = ffi.new("fz_point[?]", 4*n) + for i=1, n do + quadpoints[4*i-4].x = item.pboxes[i].x + item.pboxes[i].w + quadpoints[4*i-4].y = item.pboxes[i].y + item.pboxes[i].h + quadpoints[4*i-3].x = item.pboxes[i].x + quadpoints[4*i-3].y = item.pboxes[i].y + item.pboxes[i].h + quadpoints[4*i-2].x = item.pboxes[i].x + quadpoints[4*i-2].y = item.pboxes[i].y + quadpoints[4*i-1].x = item.pboxes[i].x + item.pboxes[i].w + quadpoints[4*i-1].y = item.pboxes[i].y + end + local page = self._document:openPage(pageno) + local annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT + if item.drawer == "lighten" then + annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT + elseif item.drawer == "underscore" then + annot_type = ffi.C.FZ_ANNOT_UNDERLINE + elseif item.drawer == "strikeout" then + annot_type = ffi.C.FZ_ANNOT_STRIKEOUT + end + page:addMarkupAnnotation(quadpoints, 4*n, annot_type) + page:close() end function PdfDocument:writeDocument() - self._document:writeDocument(self.file) + self._document:writeDocument(self.file) end function PdfDocument:close() - if self.annot_revision ~= 0 then - self:writeDocument() - end - Document.close(self) + if self.annot_revision ~= 0 then + self:writeDocument() + end + Document.close(self) end function PdfDocument:getLinkFromPosition(pageno, pos) - return self.koptinterface:getLinkFromPosition(self, pageno, pos) + return self.koptinterface:getLinkFromPosition(self, pageno, pos) end function PdfDocument:getPageBBox(pageno) - return self.koptinterface:getPageBBox(self, pageno) + return self.koptinterface:getPageBBox(self, pageno) end function PdfDocument:getPageDimensions(pageno, zoom, rotation) - return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation) + return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation) end function PdfDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) - return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode) + return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode) end function PdfDocument:hintPage(pageno, zoom, rotation, gamma, render_mode) - return self.koptinterface:hintPage(self, pageno, zoom, rotation, gamma, render_mode) + return self.koptinterface:hintPage(self, pageno, zoom, rotation, gamma, render_mode) end function PdfDocument:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) - return self.koptinterface:drawPage(self, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) + return self.koptinterface:drawPage(self, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) end function PdfDocument:register(registry) - registry:addProvider("pdf", "application/pdf", self) - registry:addProvider("cbz", "application/cbz", self) - registry:addProvider("xps", "application/xps", self) + registry:addProvider("pdf", "application/pdf", self) + registry:addProvider("cbz", "application/cbz", self) + registry:addProvider("xps", "application/xps", self) end return PdfDocument diff --git a/frontend/document/picdocument.lua b/frontend/document/picdocument.lua index dd98684c0..042e32578 100644 --- a/frontend/document/picdocument.lua +++ b/frontend/document/picdocument.lua @@ -2,31 +2,31 @@ local Document = require("document/document") local DrawContext = require("ffi/drawcontext") local PicDocument = Document:new{ - _document = false, - dc_null = DrawContext.new() + _document = false, + dc_null = DrawContext.new() } function PicDocument:init() - require "libs/libkoreader-pic" - ok, self._document = pcall(pic.openDocument, self.file) - if not ok then - self.error_message = "failed to open jpeg image" - return - end + require "libs/libkoreader-pic" + ok, self._document = pcall(pic.openDocument, self.file) + if not ok then + self.error_message = "failed to open jpeg image" + return + end - self.info.has_pages = true - self.info.configurable = false + self.info.has_pages = true + self.info.configurable = false - self:readMetadata() + self:readMetadata() end function PicDocument:readMetadata() - self.info.number_of_pages = 1 + self.info.number_of_pages = 1 end function PicDocument:register(registry) - registry:addProvider("jpeg", "application/jpeg", self) - registry:addProvider("jpg", "application/jpeg", self) + registry:addProvider("jpeg", "application/jpeg", self) + registry:addProvider("jpg", "application/jpeg", self) end return PicDocument diff --git a/frontend/document/tilecacheitem.lua b/frontend/document/tilecacheitem.lua index 72f347d4a..a532d52d2 100644 --- a/frontend/document/tilecacheitem.lua +++ b/frontend/document/tilecacheitem.lua @@ -4,10 +4,10 @@ local DEBUG = require("dbg") local TileCacheItem = CacheItem:new{} function TileCacheItem:onFree() - if self.bb.free then - DEBUG("free blitbuffer", self.bb) - self.bb:free() - end + if self.bb.free then + DEBUG("free blitbuffer", self.bb) + self.bb:free() + end end return TileCacheItem diff --git a/frontend/gettext.lua b/frontend/gettext.lua index dd2891078..dd240f174 100644 --- a/frontend/gettext.lua +++ b/frontend/gettext.lua @@ -4,11 +4,11 @@ local GetText = {} local GetText_mt = {} function GetText_mt.__call(gettext, string) - return lua_gettext.translate(string) + return lua_gettext.translate(string) end function GetText.changeLang(new_lang) - lua_gettext.change_lang(new_lang) + lua_gettext.change_lang(new_lang) end setmetatable(GetText, GetText_mt) diff --git a/frontend/optmath.lua b/frontend/optmath.lua index b6a787565..2742b1bfe 100644 --- a/frontend/optmath.lua +++ b/frontend/optmath.lua @@ -5,42 +5,42 @@ Simple math helper function local Math = {} function Math.roundAwayFromZero(num) - if num > 0 then - return math.ceil(num) - else - return math.floor(num) - end + if num > 0 then + return math.ceil(num) + else + return math.floor(num) + end end function Math.round(num) - return math.floor(num + 0.5) + return math.floor(num + 0.5) end function Math.oddEven(number) - if number % 2 == 1 then - return "odd" - else - return "even" - end + if number % 2 == 1 then + return "odd" + else + return "even" + end end local function tmin_max(tab, func, op) - if #tab == 0 then return nil, nil end - local index, value = 1, tab[1] + if #tab == 0 then return nil, nil end + local index, value = 1, tab[1] for i = 2, #tab do - if func then - if func(value, tab[i]) then - index, value = i, tab[i] - end - elseif op == "min" then - if value > tab[i] then - index, value = i, tab[i] - end - elseif op == "max" then - if value < tab[i] then - index, value = i, tab[i] - end - end + if func then + if func(value, tab[i]) then + index, value = i, tab[i] + end + elseif op == "min" then + if value > tab[i] then + index, value = i, tab[i] + end + elseif op == "max" then + if value < tab[i] then + index, value = i, tab[i] + end + end end return index, value end @@ -50,7 +50,7 @@ Return the minimum element of a table. The optional argument func specifies a one-argument ordering function. ]]-- function Math.tmin(tab, func) - return tmin_max(tab, func, "min") + return tmin_max(tab, func, "min") end --[[ @@ -58,7 +58,7 @@ Return the maximum element of a table. The optional argument func specifies a one-argument ordering function. ]]-- function Math.tmax(tab, func) - return tmin_max(tab, func, "max") + return tmin_max(tab, func, "max") end return Math diff --git a/frontend/ui/data/creoptions.lua b/frontend/ui/data/creoptions.lua index adbfbf1fd..ccb3a752a 100644 --- a/frontend/ui/data/creoptions.lua +++ b/frontend/ui/data/creoptions.lua @@ -5,144 +5,144 @@ local _ = require("gettext") -- add multiply operator to Aa dict local Aa = setmetatable({"Aa"}, { - __mul = function(t, mul) - local new = {} - for i = 1, mul do - for _, v in ipairs(t) do table.insert(new, v) end - end - return new - end + __mul = function(t, mul) + local new = {} + for i = 1, mul do + for _, v in ipairs(t) do table.insert(new, v) end + end + return new + end }) local CreOptions = { - prefix = 'copt', - { - icon = "resources/icons/appbar.transform.rotate.right.large.png", - options = { - { - name = "screen_mode", - name_text = S.SCREEN_MODE, - toggle = {S.PORTRAIT, S.LANDSCAPE}, - alternate = false, - args = {"portrait", "landscape"}, - default_arg = "portrait", - current_func = function() return Screen:getScreenMode() end, - event = "ChangeScreenMode", - } - } - }, - { - icon = "resources/icons/appbar.column.two.large.png", - options = { - { - name = "line_spacing", - name_text = S.LINE_SPACING, - toggle = {S.DECREASE, S.INCREASE}, - alternate = false, - args = {"decrease", "increase"}, - default_arg = "decrease", - event = "ChangeLineSpace", - }, - { - name = "page_margins", - name_text = S.PAGE_MARGIN, - toggle = {S.SMALL, S.MEDIUM, S.LARGE}, - values = { - DCREREADER_CONFIG_MARGIN_SIZES_SMALL, - DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, - DCREREADER_CONFIG_MARGIN_SIZES_LARGE, - }, - default_value = DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, - args = { - DCREREADER_CONFIG_MARGIN_SIZES_SMALL, - DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, - DCREREADER_CONFIG_MARGIN_SIZES_LARGE, - }, - event = "SetPageMargins", - }, - } - }, - { - icon = "resources/icons/appbar.text.size.large.png", - options = { - { - name = "font_size", - item_text = Aa * #DCREREADER_CONFIG_FONT_SIZES, - item_align_center = 1.0, - spacing = 15, - item_font_size = DCREREADER_CONFIG_FONT_SIZES, - values = DCREREADER_CONFIG_FONT_SIZES, - default_value = DCREREADER_CONFIG_DEFAULT_FONT_SIZE, - args = DCREREADER_CONFIG_FONT_SIZES, - event = "SetFontSize", - }, - { - name = "font_fine_tune", - name_text = S.FONTSIZE_FINE_TUNING, - toggle = {S.DECREASE, S.INCREASE}, - event = "ChangeSize", - args = {"decrease", "increase"}, - alternate = false, - height = 60, - } - } - }, - { - icon = "resources/icons/appbar.grade.b.large.png", - options = { - { - name = "font_weight", - name_text = S.FONT_WEIGHT, - toggle = {S.TOGGLE_BOLD}, - default_arg = nil, - event = "ToggleFontBolder", - }, - { - name = "font_gamma", - name_text = S.CONTRAST, - toggle = {S.DECREASE, S.INCREASE}, - alternate = false, - args = {"decrease", "increase"}, - default_arg = "increase", - event = "ChangeFontGamma", - } - } - }, - { - icon = "resources/icons/appbar.settings.large.png", - options = { - { - name = "view_mode", - name_text = S.VIEW_MODE, - toggle = {S.VIEW_SCROLL, S.VIEW_PAGE}, - values = {1, 0}, - default_value = 0, - args = {"scroll", "page"}, - default_arg = "page", - event = "SetViewMode", - }, - { - name = "status_line", - name_text = S.PROGRESS_BAR, - toggle = {S.FULL, S.MINI}, - values = {0, 1}, - default_value = DCREREADER_PROGRESS_BAR, - args = {0, 1}, - default_arg = DCREREADER_PROGRESS_BAR, - event = "SetStatusLine", - }, - { - name = "embedded_css", - name_text = S.EMBEDDED_STYLE, - toggle = {S.ON, S.OFF}, - values = {1, 0}, - default_value = 1, - args = {true, false}, - default_arg = nil, - event = "ToggleEmbeddedStyleSheet", - }, - }, - }, + prefix = 'copt', + { + icon = "resources/icons/appbar.transform.rotate.right.large.png", + options = { + { + name = "screen_mode", + name_text = S.SCREEN_MODE, + toggle = {S.PORTRAIT, S.LANDSCAPE}, + alternate = false, + args = {"portrait", "landscape"}, + default_arg = "portrait", + current_func = function() return Screen:getScreenMode() end, + event = "ChangeScreenMode", + } + } + }, + { + icon = "resources/icons/appbar.column.two.large.png", + options = { + { + name = "line_spacing", + name_text = S.LINE_SPACING, + toggle = {S.DECREASE, S.INCREASE}, + alternate = false, + args = {"decrease", "increase"}, + default_arg = "decrease", + event = "ChangeLineSpace", + }, + { + name = "page_margins", + name_text = S.PAGE_MARGIN, + toggle = {S.SMALL, S.MEDIUM, S.LARGE}, + values = { + DCREREADER_CONFIG_MARGIN_SIZES_SMALL, + DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, + DCREREADER_CONFIG_MARGIN_SIZES_LARGE, + }, + default_value = DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, + args = { + DCREREADER_CONFIG_MARGIN_SIZES_SMALL, + DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, + DCREREADER_CONFIG_MARGIN_SIZES_LARGE, + }, + event = "SetPageMargins", + }, + } + }, + { + icon = "resources/icons/appbar.text.size.large.png", + options = { + { + name = "font_size", + item_text = Aa * #DCREREADER_CONFIG_FONT_SIZES, + item_align_center = 1.0, + spacing = 15, + item_font_size = DCREREADER_CONFIG_FONT_SIZES, + values = DCREREADER_CONFIG_FONT_SIZES, + default_value = DCREREADER_CONFIG_DEFAULT_FONT_SIZE, + args = DCREREADER_CONFIG_FONT_SIZES, + event = "SetFontSize", + }, + { + name = "font_fine_tune", + name_text = S.FONTSIZE_FINE_TUNING, + toggle = {S.DECREASE, S.INCREASE}, + event = "ChangeSize", + args = {"decrease", "increase"}, + alternate = false, + height = 60, + } + } + }, + { + icon = "resources/icons/appbar.grade.b.large.png", + options = { + { + name = "font_weight", + name_text = S.FONT_WEIGHT, + toggle = {S.TOGGLE_BOLD}, + default_arg = nil, + event = "ToggleFontBolder", + }, + { + name = "font_gamma", + name_text = S.CONTRAST, + toggle = {S.DECREASE, S.INCREASE}, + alternate = false, + args = {"decrease", "increase"}, + default_arg = "increase", + event = "ChangeFontGamma", + } + } + }, + { + icon = "resources/icons/appbar.settings.large.png", + options = { + { + name = "view_mode", + name_text = S.VIEW_MODE, + toggle = {S.VIEW_SCROLL, S.VIEW_PAGE}, + values = {1, 0}, + default_value = 0, + args = {"scroll", "page"}, + default_arg = "page", + event = "SetViewMode", + }, + { + name = "status_line", + name_text = S.PROGRESS_BAR, + toggle = {S.FULL, S.MINI}, + values = {0, 1}, + default_value = DCREREADER_PROGRESS_BAR, + args = {0, 1}, + default_arg = DCREREADER_PROGRESS_BAR, + event = "SetStatusLine", + }, + { + name = "embedded_css", + name_text = S.EMBEDDED_STYLE, + toggle = {S.ON, S.OFF}, + values = {1, 0}, + default_value = 1, + args = {true, false}, + default_arg = nil, + event = "ToggleEmbeddedStyleSheet", + }, + }, + }, } return CreOptions diff --git a/frontend/ui/data/koptoptions.lua b/frontend/ui/data/koptoptions.lua index 3ef7ec97d..0d78890dd 100644 --- a/frontend/ui/data/koptoptions.lua +++ b/frontend/ui/data/koptoptions.lua @@ -4,222 +4,222 @@ local S = require("ui/data/strings") local _ = require("gettext") local KoptOptions = { - prefix = 'kopt', - { - icon = "resources/icons/appbar.transform.rotate.right.large.png", - options = { - { - name = "screen_mode", - name_text = S.SCREEN_MODE, - toggle = {S.PORTRAIT, S.LANDSCAPE}, - alternate = false, - args = {"portrait", "landscape"}, - default_arg = "portrait", - current_func = function() return Screen:getScreenMode() end, - event = "SetScreenMode", - } - } - }, - { - icon = "resources/icons/appbar.crop.large.png", - options = { - { - name = "trim_page", - name_text = S.PAGE_CROP, - width = 225, - toggle = {S.MANUAL, S.AUTO, S.SEMIAUTO}, - alternate = false, - values = {0, 1, 2}, - default_value = DKOPTREADER_CONFIG_TRIM_PAGE, - event = "PageCrop", - args = {"manual", "auto", "semi-auto"}, - } - } - }, - { - icon = "resources/icons/appbar.column.two.large.png", - options = { - { - name = "page_scroll", - name_text = S.SCROLL_MODE, - toggle = {S.ON, S.OFF}, - values = {1, 0}, - default_value = DSCROLL_MODE, - event = "ToggleScrollMode", - args = {true, false}, - }, - { - name = "full_screen", - name_text = S.PROGRESS_BAR, - toggle = {S.OFF, S.ON}, - values = {1, 0}, - default_value = DFULL_SCREEN, - event = "SetFullScreen", - args = {true, false}, - }, - { - name = "page_margin", - name_text = S.PAGE_MARGIN, - toggle = {S.SMALL, S.MEDIUM, S.LARGE}, - values = {0.05, 0.10, 0.15}, - default_value = DKOPTREADER_CONFIG_PAGE_MARGIN, - event = "MarginUpdate", - }, - { - name = "line_spacing", - name_text = S.LINE_SPACING, - toggle = {S.SMALL, S.MEDIUM, S.LARGE}, - values = {1.0, 1.2, 1.4}, - default_value = DKOPTREADER_CONFIG_LINE_SPACING, - }, - } - }, - { - icon = "resources/icons/appbar.text.size.large.png", - options = { - { - name = "font_size", - item_text = {"Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa"}, - item_align_center = 1.0, - spacing = 15, - height = 60, - item_font_size = {22,24,28,32,34,36,38,42,46,50}, - values = {0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.6, 2.0, 4.0}, - default_value = DKOPTREADER_CONFIG_FONT_SIZE, - event = "FontSizeUpdate", - }, - { - name = "font_fine_tune", - name_text = S.FONTSIZE_FINE_TUNING, - toggle = {S.DECREASE, S.INCREASE}, - values = {-0.05, 0.05}, - default_value = 0.05, - event = "FineTuningFontSize", - args = {-0.05, 0.05}, - alternate = false, - height = 60, - } - } - }, - { - icon = "resources/icons/appbar.grade.b.large.png", - options = { - { - name = "contrast", - name_text = S.CONTRAST, - name_align_right = 0.2, - item_text = {S.LIGHTEST , S.LIGHTER, S.DEFAULT, S.DARKER, S.DARKEST}, - item_font_size = 18, - item_align_center = 0.8, - values = {2.0, 1.5, 1.0, 0.5, 0.2}, - default_value = DKOPTREADER_CONFIG_CONTRAST, - event = "GammaUpdate", - args = {0.5, 0.8, 1.0, 2.0, 4.0}, - } - } - }, - { - icon = "resources/icons/appbar.settings.large.png", - options = { - { - name = "text_wrap", - name_text = _("Reflow"), - toggle = {S.ON, S.OFF}, - values = {1, 0}, - default_value = DKOPTREADER_CONFIG_TEXT_WRAP, - events = { - { - event = "RedrawCurrentPage", - }, - { - event = "RestoreZoomMode", - }, - { - event = "InitScrollPageStates", - }, - } - }, - { - name="doc_language", - name_text = S.DOC_LANG, - toggle = DKOPTREADER_CONFIG_DOC_LANGS_TEXT, - values = DKOPTREADER_CONFIG_DOC_LANGS_CODE, - default_value = DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE, - event = "DocLangUpdate", - args = DKOPTREADER_CONFIG_DOC_LANGS_CODE, - }, - { - name = "word_spacing", - name_text = S.WORD_GAP, - toggle = {S.SMALL, S.AUTO, S.LARGE}, - values = DKOPTREADER_CONFIG_WORD_SAPCINGS, - default_value = DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING, - }, - { - name = "writing_direction", - name_text = S.WRITING_DIR, - toggle = {S.LTR, S.RTL, S.TBRTL}, - values = {0, 1, 2}, - default_value = 0, - }, - { - name = "quality", - name_text = S.RENDER_QUALITY, - toggle = {S.LOW, S.DEFAULT, S.HIGH}, - values={0.5, 1.0, 1.5}, - default_value = DKOPTREADER_CONFIG_RENDER_QUALITY, - }, - { - name = "max_columns", - name_text = S.COLUMNS, - item_icons = { - "resources/icons/appbar.column.one.png", - "resources/icons/appbar.column.two.png", - "resources/icons/appbar.column.three.png", - }, - values = {1,2,3}, - default_value = DKOPTREADER_CONFIG_MAX_COLUMNS, - }, - { - name = "justification", - name_text = S.TEXT_ALIGN, - item_icons = { - "resources/icons/appbar.align.auto.png", - "resources/icons/appbar.align.left.png", - "resources/icons/appbar.align.center.png", - "resources/icons/appbar.align.right.png", - "resources/icons/appbar.align.justify.png", - }, - values = {-1,0,1,2,3}, - default_value = DKOPTREADER_CONFIG_JUSTIFICATION, - }, - { - name = "defect_size", - name_text = S.DEFECT_SIZE, - toggle = {S.SMALL, S.MEDIUM, S.LARGE}, - values = {1.0, 3.0, 5.0}, - default_value = DKOPTREADER_CONFIG_DEFECT_SIZE, - event = "DefectSizeUpdate", - show = false, - }, - { - name = "auto_straighten", - name_text = S.AUTO_STRAIGHTEN, - toggle = {S.ZERO_DEG, S.FIVE_DEG, S.TEN_DEG}, - values = {0, 5, 10}, - default_value = DKOPTREADER_CONFIG_AUTO_STRAIGHTEN, - show = false, - }, - { - name = "detect_indent", - name_text = S.INDENTATION, - toggle = {S.ON, S.OFF}, - values = {1, 0}, - default_value = DKOPTREADER_CONFIG_DETECT_INDENT, - show = false, - }, - } - }, + prefix = 'kopt', + { + icon = "resources/icons/appbar.transform.rotate.right.large.png", + options = { + { + name = "screen_mode", + name_text = S.SCREEN_MODE, + toggle = {S.PORTRAIT, S.LANDSCAPE}, + alternate = false, + args = {"portrait", "landscape"}, + default_arg = "portrait", + current_func = function() return Screen:getScreenMode() end, + event = "SetScreenMode", + } + } + }, + { + icon = "resources/icons/appbar.crop.large.png", + options = { + { + name = "trim_page", + name_text = S.PAGE_CROP, + width = 225, + toggle = {S.MANUAL, S.AUTO, S.SEMIAUTO}, + alternate = false, + values = {0, 1, 2}, + default_value = DKOPTREADER_CONFIG_TRIM_PAGE, + event = "PageCrop", + args = {"manual", "auto", "semi-auto"}, + } + } + }, + { + icon = "resources/icons/appbar.column.two.large.png", + options = { + { + name = "page_scroll", + name_text = S.SCROLL_MODE, + toggle = {S.ON, S.OFF}, + values = {1, 0}, + default_value = DSCROLL_MODE, + event = "ToggleScrollMode", + args = {true, false}, + }, + { + name = "full_screen", + name_text = S.PROGRESS_BAR, + toggle = {S.OFF, S.ON}, + values = {1, 0}, + default_value = DFULL_SCREEN, + event = "SetFullScreen", + args = {true, false}, + }, + { + name = "page_margin", + name_text = S.PAGE_MARGIN, + toggle = {S.SMALL, S.MEDIUM, S.LARGE}, + values = {0.05, 0.10, 0.15}, + default_value = DKOPTREADER_CONFIG_PAGE_MARGIN, + event = "MarginUpdate", + }, + { + name = "line_spacing", + name_text = S.LINE_SPACING, + toggle = {S.SMALL, S.MEDIUM, S.LARGE}, + values = {1.0, 1.2, 1.4}, + default_value = DKOPTREADER_CONFIG_LINE_SPACING, + }, + } + }, + { + icon = "resources/icons/appbar.text.size.large.png", + options = { + { + name = "font_size", + item_text = {"Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa"}, + item_align_center = 1.0, + spacing = 15, + height = 60, + item_font_size = {22,24,28,32,34,36,38,42,46,50}, + values = {0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.6, 2.0, 4.0}, + default_value = DKOPTREADER_CONFIG_FONT_SIZE, + event = "FontSizeUpdate", + }, + { + name = "font_fine_tune", + name_text = S.FONTSIZE_FINE_TUNING, + toggle = {S.DECREASE, S.INCREASE}, + values = {-0.05, 0.05}, + default_value = 0.05, + event = "FineTuningFontSize", + args = {-0.05, 0.05}, + alternate = false, + height = 60, + } + } + }, + { + icon = "resources/icons/appbar.grade.b.large.png", + options = { + { + name = "contrast", + name_text = S.CONTRAST, + name_align_right = 0.2, + item_text = {S.LIGHTEST , S.LIGHTER, S.DEFAULT, S.DARKER, S.DARKEST}, + item_font_size = 18, + item_align_center = 0.8, + values = {2.0, 1.5, 1.0, 0.5, 0.2}, + default_value = DKOPTREADER_CONFIG_CONTRAST, + event = "GammaUpdate", + args = {0.5, 0.8, 1.0, 2.0, 4.0}, + } + } + }, + { + icon = "resources/icons/appbar.settings.large.png", + options = { + { + name = "text_wrap", + name_text = _("Reflow"), + toggle = {S.ON, S.OFF}, + values = {1, 0}, + default_value = DKOPTREADER_CONFIG_TEXT_WRAP, + events = { + { + event = "RedrawCurrentPage", + }, + { + event = "RestoreZoomMode", + }, + { + event = "InitScrollPageStates", + }, + } + }, + { + name="doc_language", + name_text = S.DOC_LANG, + toggle = DKOPTREADER_CONFIG_DOC_LANGS_TEXT, + values = DKOPTREADER_CONFIG_DOC_LANGS_CODE, + default_value = DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE, + event = "DocLangUpdate", + args = DKOPTREADER_CONFIG_DOC_LANGS_CODE, + }, + { + name = "word_spacing", + name_text = S.WORD_GAP, + toggle = {S.SMALL, S.AUTO, S.LARGE}, + values = DKOPTREADER_CONFIG_WORD_SAPCINGS, + default_value = DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING, + }, + { + name = "writing_direction", + name_text = S.WRITING_DIR, + toggle = {S.LTR, S.RTL, S.TBRTL}, + values = {0, 1, 2}, + default_value = 0, + }, + { + name = "quality", + name_text = S.RENDER_QUALITY, + toggle = {S.LOW, S.DEFAULT, S.HIGH}, + values={0.5, 1.0, 1.5}, + default_value = DKOPTREADER_CONFIG_RENDER_QUALITY, + }, + { + name = "max_columns", + name_text = S.COLUMNS, + item_icons = { + "resources/icons/appbar.column.one.png", + "resources/icons/appbar.column.two.png", + "resources/icons/appbar.column.three.png", + }, + values = {1,2,3}, + default_value = DKOPTREADER_CONFIG_MAX_COLUMNS, + }, + { + name = "justification", + name_text = S.TEXT_ALIGN, + item_icons = { + "resources/icons/appbar.align.auto.png", + "resources/icons/appbar.align.left.png", + "resources/icons/appbar.align.center.png", + "resources/icons/appbar.align.right.png", + "resources/icons/appbar.align.justify.png", + }, + values = {-1,0,1,2,3}, + default_value = DKOPTREADER_CONFIG_JUSTIFICATION, + }, + { + name = "defect_size", + name_text = S.DEFECT_SIZE, + toggle = {S.SMALL, S.MEDIUM, S.LARGE}, + values = {1.0, 3.0, 5.0}, + default_value = DKOPTREADER_CONFIG_DEFECT_SIZE, + event = "DefectSizeUpdate", + show = false, + }, + { + name = "auto_straighten", + name_text = S.AUTO_STRAIGHTEN, + toggle = {S.ZERO_DEG, S.FIVE_DEG, S.TEN_DEG}, + values = {0, 5, 10}, + default_value = DKOPTREADER_CONFIG_AUTO_STRAIGHTEN, + show = false, + }, + { + name = "detect_indent", + name_text = S.INDENTATION, + toggle = {S.ON, S.OFF}, + values = {1, 0}, + default_value = DKOPTREADER_CONFIG_DETECT_INDENT, + show = false, + }, + } + }, } return KoptOptions diff --git a/frontend/ui/device.lua b/frontend/ui/device.lua index b5674e528..735b6ec43 100644 --- a/frontend/ui/device.lua +++ b/frontend/ui/device.lua @@ -6,229 +6,229 @@ local Screen = require("ui/device/screen") -- lfs local Device = { - screen_saver_mode = false, - charging_mode = false, - survive_screen_saver = false, - touch_dev = nil, - model = nil, - firmware_rev = nil, - powerd = nil, - has_no_keyboard = nil, - is_touch_device = nil, - has_front_light = nil, - screen = Screen + screen_saver_mode = false, + charging_mode = false, + survive_screen_saver = false, + touch_dev = nil, + model = nil, + firmware_rev = nil, + powerd = nil, + has_no_keyboard = nil, + is_touch_device = nil, + has_front_light = nil, + screen = Screen } Screen.device = Device function Set (list) - local set = {} - for _, l in ipairs(list) do set[l] = true end - return set + local set = {} + for _, l in ipairs(list) do set[l] = true end + return set end function Device:getModel() - if self.model then return self.model end - if util.isEmulated() then - self.model = "Emulator" - return self.model - end - self.model = nil - local kindle_sn = io.open("/proc/usid", "r") - if kindle_sn then - local kindle_devcode = string.sub(kindle_sn:read(),3,4) - kindle_sn:close() - -- NOTE: Update me when new models come out :) - local k2_set = Set { "02", "03" } - local dx_set = Set { "04", "05" } - local dxg_set = Set { "09" } - local k3_set = Set { "08", "06", "0A" } - local k4_set = Set { "0E", "23" } - local touch_set = Set { "0F", "11", "10", "12" } - local pw_set = Set { "24", "1B", "1D", "1F", "1C", "20" } - local pw2_set = Set { "D4", "5A", "D5", "D7", "D8", "F2" } - - if k2_set[kindle_devcode] then - self.model = "Kindle2" - elseif dx_set[kindle_devcode] then - self.model = "Kindle2" - elseif dxg_set[kindle_devcode] then - self.model = "Kindle2" - elseif k3_set[kindle_devcode] then - self.model = "Kindle3" - elseif k4_set[kindle_devcode] then - self.model = "Kindle4" - elseif touch_set[kindle_devcode] then - self.model = "KindleTouch" - elseif pw_set[kindle_devcode] then - self.model = "KindlePaperWhite" - elseif pw2_set[kindle_devcode] then - self.model = "KindlePaperWhite2" - end - else - local kg_test_fd = lfs.attributes("/bin/kobo_config.sh") - if kg_test_fd then - local std_out = io.popen("/bin/kobo_config.sh", "r") - local codename = std_out:read() - self.model = "Kobo_" .. codename - local version_file = io.open("/mnt/onboard/.kobo/version", "r") - self.firmware_rev = string.sub(version_file:read(),24,28) - version_file:close() - end - end - return self.model + if self.model then return self.model end + if util.isEmulated() then + self.model = "Emulator" + return self.model + end + self.model = nil + local kindle_sn = io.open("/proc/usid", "r") + if kindle_sn then + local kindle_devcode = string.sub(kindle_sn:read(),3,4) + kindle_sn:close() + -- NOTE: Update me when new models come out :) + local k2_set = Set { "02", "03" } + local dx_set = Set { "04", "05" } + local dxg_set = Set { "09" } + local k3_set = Set { "08", "06", "0A" } + local k4_set = Set { "0E", "23" } + local touch_set = Set { "0F", "11", "10", "12" } + local pw_set = Set { "24", "1B", "1D", "1F", "1C", "20" } + local pw2_set = Set { "D4", "5A", "D5", "D7", "D8", "F2" } + + if k2_set[kindle_devcode] then + self.model = "Kindle2" + elseif dx_set[kindle_devcode] then + self.model = "Kindle2" + elseif dxg_set[kindle_devcode] then + self.model = "Kindle2" + elseif k3_set[kindle_devcode] then + self.model = "Kindle3" + elseif k4_set[kindle_devcode] then + self.model = "Kindle4" + elseif touch_set[kindle_devcode] then + self.model = "KindleTouch" + elseif pw_set[kindle_devcode] then + self.model = "KindlePaperWhite" + elseif pw2_set[kindle_devcode] then + self.model = "KindlePaperWhite2" + end + else + local kg_test_fd = lfs.attributes("/bin/kobo_config.sh") + if kg_test_fd then + local std_out = io.popen("/bin/kobo_config.sh", "r") + local codename = std_out:read() + self.model = "Kobo_" .. codename + local version_file = io.open("/mnt/onboard/.kobo/version", "r") + self.firmware_rev = string.sub(version_file:read(),24,28) + version_file:close() + end + end + return self.model end function Device:getFirmVer() - if not self.model then self:getModel() end - return self.firmware_rev + if not self.model then self:getModel() end + return self.firmware_rev end function Device:isKindle4() - return (self:getModel() == "Kindle4") + return (self:getModel() == "Kindle4") end function Device:isKindle3() - return (self:getModel() == "Kindle3") + return (self:getModel() == "Kindle3") end function Device:isKindle2() - return (self:getModel() == "Kindle2") + return (self:getModel() == "Kindle2") end function Device:isKobo() - return string.find(self:getModel(),"Kobo_") == 1 + return string.find(self:getModel(),"Kobo_") == 1 end function Device:hasNoKeyboard() - if self.has_no_keyboard ~= nil then return self.has_no_keyboard end - local model = self:getModel() - self.has_no_keyboard = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") - or (model == "KindleTouch") or self:isKobo() - return self.has_no_keyboard + if self.has_no_keyboard ~= nil then return self.has_no_keyboard end + local model = self:getModel() + self.has_no_keyboard = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") + or (model == "KindleTouch") or self:isKobo() + return self.has_no_keyboard end function Device:hasKeyboard() - return not self:hasNoKeyboard() + return not self:hasNoKeyboard() end function Device:isTouchDevice() - if self.is_touch_device ~= nil then return self.is_touch_device end - local model = self:getModel() - self.is_touch_device = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") - or (model == "KindleTouch") or self:isKobo() or util.isEmulated() - return self.is_touch_device + if self.is_touch_device ~= nil then return self.is_touch_device end + local model = self:getModel() + self.is_touch_device = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") + or (model == "KindleTouch") or self:isKobo() or util.isEmulated() + return self.is_touch_device end function Device:hasFrontlight() - if self.has_front_light ~= nil then return self.has_front_light end - local model = self:getModel() - self.has_front_light = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") - or (model == "Kobo_dragon") or (model == "Kobo_kraken") or (model == "Kobo_phoenix") - or util.isEmulated() - return self.has_front_light + if self.has_front_light ~= nil then return self.has_front_light end + local model = self:getModel() + self.has_front_light = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") + or (model == "Kobo_dragon") or (model == "Kobo_kraken") or (model == "Kobo_phoenix") + or util.isEmulated() + return self.has_front_light end function Device:setTouchInputDev(dev) - self.touch_dev = dev + self.touch_dev = dev end function Device:getTouchInputDev() - return self.touch_dev + return self.touch_dev end function Device:intoScreenSaver() - --os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt") - if self.charging_mode == false and self.screen_saver_mode == false then - self.screen:saveCurrentBB() - --UIManager:show(InfoMessage:new{ - --text = "Going into screensaver... ", - --timeout = 2, - --}) - --util.sleep(1) - --os.execute("killall -cont cvm") - self.screen_saver_mode = true - end + --os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt") + if self.charging_mode == false and self.screen_saver_mode == false then + self.screen:saveCurrentBB() + --UIManager:show(InfoMessage:new{ + --text = "Going into screensaver... ", + --timeout = 2, + --}) + --util.sleep(1) + --os.execute("killall -cont cvm") + self.screen_saver_mode = true + end end function Device:outofScreenSaver() - --os.execute("echo 'screensaver out' >> /mnt/us/event_test.txt") - if self.screen_saver_mode == true and self.charging_mode == false then - -- wait for native system update screen before we recover saved - -- Blitbuffer. - util.usleep(1500000) - --os.execute("killall -stop cvm") - self.screen:restoreFromSavedBB() - self.screen:refresh(0) - self.survive_screen_saver = true - end - self.screen_saver_mode = false + --os.execute("echo 'screensaver out' >> /mnt/us/event_test.txt") + if self.screen_saver_mode == true and self.charging_mode == false then + -- wait for native system update screen before we recover saved + -- Blitbuffer. + util.usleep(1500000) + --os.execute("killall -stop cvm") + self.screen:restoreFromSavedBB() + self.screen:refresh(0) + self.survive_screen_saver = true + end + self.screen_saver_mode = false end function Device:prepareSuspend() -- currently only used for kobo devices - local powerd = self:getPowerDevice() - if powerd ~= nil then - powerd.fl:sleep() - end - self.screen:refresh(0) - self.screen_saver_mode = true + local powerd = self:getPowerDevice() + if powerd ~= nil then + powerd.fl:sleep() + end + self.screen:refresh(0) + self.screen_saver_mode = true end function Device:Suspend() -- currently only used for kobo devices - os.execute("./kobo_suspend.sh") + os.execute("./kobo_suspend.sh") end function Device:Resume() -- currently only used for kobo devices - os.execute("echo 0 > /sys/power/state-extended") - self.screen:refresh(0) - local powerd = self:getPowerDevice() - if powerd ~= nil then - powerd.fl:restore() - end - self.screen_saver_mode = false + os.execute("echo 0 > /sys/power/state-extended") + self.screen:refresh(0) + local powerd = self:getPowerDevice() + if powerd ~= nil then + powerd.fl:restore() + end + self.screen_saver_mode = false end function Device:usbPlugIn() - --os.execute("echo 'usb in' >> /mnt/us/event_test.txt") - if self.charging_mode == false and self.screen_saver_mode == false then - self.screen:saveCurrentBB() - --UIManager:show(InfoMessage:new{ - --text = "Going into USB mode... ", - --timeout = 2, - --}) - --util.sleep(1) - --os.execute("killall -cont cvm") - end - self.charging_mode = true + --os.execute("echo 'usb in' >> /mnt/us/event_test.txt") + if self.charging_mode == false and self.screen_saver_mode == false then + self.screen:saveCurrentBB() + --UIManager:show(InfoMessage:new{ + --text = "Going into USB mode... ", + --timeout = 2, + --}) + --util.sleep(1) + --os.execute("killall -cont cvm") + end + self.charging_mode = true end function Device:usbPlugOut() - --os.execute("echo 'usb out' >> /mnt/us/event_test.txt") - if self.charging_mode == true and self.screen_saver_mode == false then - --util.usleep(1500000) - --os.execute("killall -stop cvm") - self.screen:restoreFromSavedBB() - self.screen:refresh(0) - end + --os.execute("echo 'usb out' >> /mnt/us/event_test.txt") + if self.charging_mode == true and self.screen_saver_mode == false then + --util.usleep(1500000) + --os.execute("killall -stop cvm") + self.screen:restoreFromSavedBB() + self.screen:refresh(0) + end - --@TODO signal filemanager for file changes 13.06 2012 (houqp) - self.charging_mode = false + --@TODO signal filemanager for file changes 13.06 2012 (houqp) + self.charging_mode = false end function Device:getPowerDevice() - if self.powerd ~= nil then - return self.powerd - else - local model = self:getModel() - if model == "KindleTouch" or model == "KindlePaperWhite" or model == "KindlePaperWhite2" then - self.powerd = KindlePowerD:new{model = model} - elseif self:isKobo() then - self.powerd = KoboPowerD:new() - else -- emulated FrontLight - self.powerd = BasePowerD:new() - end - end - return self.powerd + if self.powerd ~= nil then + return self.powerd + else + local model = self:getModel() + if model == "KindleTouch" or model == "KindlePaperWhite" or model == "KindlePaperWhite2" then + self.powerd = KindlePowerD:new{model = model} + elseif self:isKobo() then + self.powerd = KoboPowerD:new() + else -- emulated FrontLight + self.powerd = BasePowerD:new() + end + end + return self.powerd end return Device diff --git a/frontend/ui/device/basepowerd.lua b/frontend/ui/device/basepowerd.lua index 20f26cafe..2ce343b24 100644 --- a/frontend/ui/device/basepowerd.lua +++ b/frontend/ui/device/basepowerd.lua @@ -1,20 +1,20 @@ local BasePowerD = { - fl_min = 0, -- min frontlight intensity - fl_max = 10, -- max frontlight intensity - flIntensity = nil, -- frontlight intensity - battCapacity = nil, -- battery capacity - model = nil, -- device model - - capacity_pulled_count = 0, - capacity_cached_count = 10, + fl_min = 0, -- min frontlight intensity + fl_max = 10, -- max frontlight intensity + flIntensity = nil, -- frontlight intensity + battCapacity = nil, -- battery capacity + model = nil, -- device model + + capacity_pulled_count = 0, + capacity_cached_count = 10, } function BasePowerD:new(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - if o.init then o:init() end - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + if o.init then o:init() end + return o end function BasePowerD:init() end @@ -26,39 +26,39 @@ function BasePowerD:suspendHW() end function BasePowerD:wakeUpHW() end function BasePowerD:read_int_file(file) - local f = io.open(file, "r") - local sysint = tonumber(f:read("*all"):match("%d+")) - f:close() - return sysint + local f = io.open(file, "r") + local sysint = tonumber(f:read("*all"):match("%d+")) + f:close() + return sysint end function BasePowerD:setIntensity(intensity) - intensity = intensity < self.fl_min and self.fl_min or intensity - intensity = intensity > self.fl_max and self.fl_max or intensity - self.flIntensity = intensity - self:setIntensityHW() + intensity = intensity < self.fl_min and self.fl_min or intensity + intensity = intensity > self.fl_max and self.fl_max or intensity + self.flIntensity = intensity + self:setIntensityHW() end function BasePowerD:getCapacity() - if capacity_pulled_count == capacity_cached_count then - capacity_pulled_count = 0 - return self:getCapacityHW() - else - capacity_pulled_count = capacity_pulled_count + 1 - return self.battCapacity or self:getCapacityHW() - end + if capacity_pulled_count == capacity_cached_count then + capacity_pulled_count = 0 + return self:getCapacityHW() + else + capacity_pulled_count = capacity_pulled_count + 1 + return self.battCapacity or self:getCapacityHW() + end end function BasePowerD:isCharging() - return self:isChargingHW() + return self:isChargingHW() end function BasePowerD:suspend() - return self:suspendHW() + return self:suspendHW() end function BasePowerD:wakeUp() - return self:wakeUpHW() + return self:wakeUpHW() end return BasePowerD diff --git a/frontend/ui/device/kindlepowerd.lua b/frontend/ui/device/kindlepowerd.lua index cd5c8b5da..9cfe8fa2d 100644 --- a/frontend/ui/device/kindlepowerd.lua +++ b/frontend/ui/device/kindlepowerd.lua @@ -2,85 +2,85 @@ local BasePowerD = require("ui/device/basepowerd") -- liblipclua, see require below local KindlePowerD = BasePowerD:new{ - fl_min = 0, fl_max = 24, - kpw1_frontlight = "/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity", - kpw2_frontlight = "/sys/class/backlight/max77696-bl/brightness", - kt_kpw_capacity = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity", - kpw_charging = "/sys/devices/platform/aplite_charger.0/charging", - kt_charging = "/sys/devices/platform/fsl-usb2-udc/charging", - - flIntensity = nil, - battCapacity = nil, - is_charging = nil, - lipc_handle = nil, + fl_min = 0, fl_max = 24, + kpw1_frontlight = "/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity", + kpw2_frontlight = "/sys/class/backlight/max77696-bl/brightness", + kt_kpw_capacity = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity", + kpw_charging = "/sys/devices/platform/aplite_charger.0/charging", + kt_charging = "/sys/devices/platform/fsl-usb2-udc/charging", + + flIntensity = nil, + battCapacity = nil, + is_charging = nil, + lipc_handle = nil, } function KindlePowerD:new(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - if o.init then o:init(o.model) end - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + if o.init then o:init(o.model) end + return o end function KindlePowerD:init(model) - local lipc = require("liblipclua") - if lipc then - self.lipc_handle = lipc.init("com.github.koreader") - end - - if model == "KindleTouch" then - self.batt_capacity_file = self.kt_kpw_capacity - self.is_charging_file = self.kt_charging - elseif model == "KindlePaperWhite" then - self.fl_intensity_file = self.kpw1_frontlight - self.batt_capacity_file = self.kt_kpw_capacity - self.is_charging_file = self.kpw_charging - elseif model == "KindlePaperWhite2" then - self.fl_intensity_file = self.kpw2_frontlight - self.batt_capacity_file = self.kt_kpw_capacity - self.is_charging_file = self.kpw_charging - end - if self.lipc_handle then - self.flIntensity = self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity") - else - self.flIntensity = self:read_int_file(self.fl_intensity_file) - end + local lipc = require("liblipclua") + if lipc then + self.lipc_handle = lipc.init("com.github.koreader") + end + + if model == "KindleTouch" then + self.batt_capacity_file = self.kt_kpw_capacity + self.is_charging_file = self.kt_charging + elseif model == "KindlePaperWhite" then + self.fl_intensity_file = self.kpw1_frontlight + self.batt_capacity_file = self.kt_kpw_capacity + self.is_charging_file = self.kpw_charging + elseif model == "KindlePaperWhite2" then + self.fl_intensity_file = self.kpw2_frontlight + self.batt_capacity_file = self.kt_kpw_capacity + self.is_charging_file = self.kpw_charging + end + if self.lipc_handle then + self.flIntensity = self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity") + else + self.flIntensity = self:read_int_file(self.fl_intensity_file) + end end function KindlePowerD:toggleFrontlight() - local sysint = self:read_int_file(self.fl_intensity_file) - if sysint == 0 then - self:setIntensity(self.flIntensity) - else - os.execute("echo -n 0 > " .. self.fl_intensity_file) - end + local sysint = self:read_int_file(self.fl_intensity_file) + if sysint == 0 then + self:setIntensity(self.flIntensity) + else + os.execute("echo -n 0 > " .. self.fl_intensity_file) + end end function KindlePowerD:setIntensityHW() - if self.lipc_handle ~= nil then - self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", self.flIntensity) - else - os.execute("echo -n ".. self.flIntensity .." > " .. self.fl_intensity_file) - end + if self.lipc_handle ~= nil then + self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", self.flIntensity) + else + os.execute("echo -n ".. self.flIntensity .." > " .. self.fl_intensity_file) + end end function KindlePowerD:getCapacityHW() - if self.lipc_handle ~= nil then - self.battCapacity = self.lipc_handle:get_int_property("com.lab126.powerd", "battLevel") - else - self.battCapacity = self:read_int_file(self.batt_capacity_file) - end - return self.battCapacity + if self.lipc_handle ~= nil then + self.battCapacity = self.lipc_handle:get_int_property("com.lab126.powerd", "battLevel") + else + self.battCapacity = self:read_int_file(self.batt_capacity_file) + end + return self.battCapacity end function KindlePowerD:isChargingHW() - if self.lipc_handle ~= nil then - self.is_charging = self.lipc_handle:get_int_property("com.lab126.powerd", "isCharging") - else - self.is_charging = self:read_int_file(self.is_charging_file) - end - return self.is_charging == 1 + if self.lipc_handle ~= nil then + self.is_charging = self.lipc_handle:get_int_property("com.lab126.powerd", "isCharging") + else + self.is_charging = self:read_int_file(self.is_charging_file) + end + return self.is_charging == 1 end return KindlePowerD diff --git a/frontend/ui/device/kobopowerd.lua b/frontend/ui/device/kobopowerd.lua index cfdecf7c0..fef464d17 100644 --- a/frontend/ui/device/kobopowerd.lua +++ b/frontend/ui/device/kobopowerd.lua @@ -1,41 +1,41 @@ local BasePowerD = require("ui/device/basepowerd") local KoboPowerD = BasePowerD:new{ - fl_min = 1, fl_max = 100, - flIntensity = 20, - restore_settings = true, - fl = nil, - - batt_capacity_file = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/capacity", - is_charging_file = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/charge_now", - battCapacity = nil, - is_charging = nil, + fl_min = 1, fl_max = 100, + flIntensity = 20, + restore_settings = true, + fl = nil, + + batt_capacity_file = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/capacity", + is_charging_file = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/charge_now", + battCapacity = nil, + is_charging = nil, } function KoboPowerD:init() - self.fl = kobolight.open() + self.fl = kobolight.open() end function KoboPowerD:toggleFrontlight() - if self.fl ~= nil then - self.fl:toggle() - end + if self.fl ~= nil then + self.fl:toggle() + end end function KoboPowerD:setIntensityHW() - if self.fl ~= nil then - self.fl:setBrightness(self.flIntensity) - end + if self.fl ~= nil then + self.fl:setBrightness(self.flIntensity) + end end function KoboPowerD:getCapacityHW() - self.battCapacity = self:read_int_file(self.batt_capacity_file) - return self.battCapacity + self.battCapacity = self:read_int_file(self.batt_capacity_file) + return self.battCapacity end function KoboPowerD:isChargingHW() - self.is_charging = self:read_int_file(self.is_charging_file) - return self.is_charging == 1 + self.is_charging = self:read_int_file(self.is_charging_file) + return self.is_charging == 1 end return KoboPowerD diff --git a/frontend/ui/device/screen.lua b/frontend/ui/device/screen.lua index 2ad926765..5bfebc0c5 100644 --- a/frontend/ui/device/screen.lua +++ b/frontend/ui/device/screen.lua @@ -29,131 +29,131 @@ Codes for rotation modes: local Screen = { - cur_rotation_mode = 0, - native_rotation_mode = nil, - blitbuffer_rotation_mode = 0, + cur_rotation_mode = 0, + native_rotation_mode = nil, + blitbuffer_rotation_mode = 0, - bb = nil, - saved_bb = nil, + bb = nil, + saved_bb = nil, - fb = einkfb.open("/dev/fb0"), - -- will be set upon loading by Device class: - device = nil, + fb = einkfb.open("/dev/fb0"), + -- will be set upon loading by Device class: + device = nil, } function Screen:init() - self.bb = self.fb.bb - self.blitbuffer_rotation_mode = self.bb:getRotation() - -- asking the framebuffer for orientation is error prone, - -- so we do this simple heuristic (for now) - if self:getWidth() > self:getHeight() then - self.native_rotation_mode = 1 - else - self.native_rotation_mode = 0 - end - self.cur_rotation_mode = self.native_rotation_mode + self.bb = self.fb.bb + self.blitbuffer_rotation_mode = self.bb:getRotation() + -- asking the framebuffer for orientation is error prone, + -- so we do this simple heuristic (for now) + if self:getWidth() > self:getHeight() then + self.native_rotation_mode = 1 + else + self.native_rotation_mode = 0 + end + self.cur_rotation_mode = self.native_rotation_mode end function Screen:refresh(refresh_type, waveform_mode, x, y, w, h) - self.fb:refresh(refresh_type, waveform_mode, x, y, w, h) + self.fb:refresh(refresh_type, waveform_mode, x, y, w, h) end function Screen:getSize() - return Geom:new{w = self.bb:getWidth(), h = self.bb:getHeight()} + return Geom:new{w = self.bb:getWidth(), h = self.bb:getHeight()} end function Screen:getWidth() - return self.bb:getWidth() + return self.bb:getWidth() end function Screen:getHeight() - return self.bb:getHeight() + return self.bb:getHeight() end function Screen:getDPI() - if(self.device:getModel() == "KindlePaperWhite") - or (self.device:getModel() == "Kobo_kraken") - or (self.device:getModel() == "Kobo_phoenix") then - return 212 - elseif self.device:getModel() == "Kobo_dragon" then - return 265 - elseif self.device:getModel() == "Kobo_pixie" then - return 200 - else - return 167 - end + if(self.device:getModel() == "KindlePaperWhite") + or (self.device:getModel() == "Kobo_kraken") + or (self.device:getModel() == "Kobo_phoenix") then + return 212 + elseif self.device:getModel() == "Kobo_dragon" then + return 265 + elseif self.device:getModel() == "Kobo_pixie" then + return 200 + else + return 167 + end end function Screen:scaleByDPI(px) - return math.floor(px * self:getDPI()/167) + return math.floor(px * self:getDPI()/167) end function Screen:rescaleByDPI(px) - return math.ceil(px * 167/self:getDPI()) + return math.ceil(px * 167/self:getDPI()) end function Screen:getRotationMode() - return self.cur_rotation_mode + return self.cur_rotation_mode end function Screen:getScreenMode() - if self:getWidth() > self:getHeight() then - return "landscape" - else - return "portrait" - end + if self:getWidth() > self:getHeight() then + return "landscape" + else + return "portrait" + end end function Screen:setRotationMode(mode) - self.fb.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode)) - self.cur_rotation_mode = mode + self.fb.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode)) + self.cur_rotation_mode = mode end function Screen:setScreenMode(mode) - if mode == "portrait" then - if self.cur_rotation_mode ~= 0 then - self:setRotationMode(0) - end - elseif mode == "landscape" then - if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then - self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3) - elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then - self:setRotationMode((self.cur_rotation_mode + 2) % 4) - end - end + if mode == "portrait" then + if self.cur_rotation_mode ~= 0 then + self:setRotationMode(0) + end + elseif mode == "landscape" then + if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then + self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3) + elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then + self:setRotationMode((self.cur_rotation_mode + 2) % 4) + end + end end function Screen:saveCurrentBB() - local width, height = self:getWidth(), self:getHeight() + local width, height = self:getWidth(), self:getHeight() - if not self.saved_bb then - self.saved_bb = Blitbuffer.new(width, height) - end - if self.saved_bb:getWidth() ~= width then - self.saved_bb:free() - self.saved_bb = Blitbuffer.new(width, height) - end - self.saved_bb:blitFullFrom(self.bb) + if not self.saved_bb then + self.saved_bb = Blitbuffer.new(width, height) + end + if self.saved_bb:getWidth() ~= width then + self.saved_bb:free() + self.saved_bb = Blitbuffer.new(width, height) + end + self.saved_bb:blitFullFrom(self.bb) end function Screen:restoreFromSavedBB() - self:restoreFromBB(self.saved_bb) - -- free data - self.saved_bb = nil + self:restoreFromBB(self.saved_bb) + -- free data + self.saved_bb = nil end function Screen:getCurrentScreenBB() - local bb = Blitbuffer.new(self:getWidth(), self:getHeight()) - bb:blitFullFrom(self.bb) - return bb + local bb = Blitbuffer.new(self:getWidth(), self:getHeight()) + bb:blitFullFrom(self.bb) + return bb end function Screen:restoreFromBB(bb) - if bb then - self.bb:blitFullFrom(bb) - else - DEBUG("Got nil bb in restoreFromSavedBB!") - end + if bb then + self.bb:blitFullFrom(bb) + else + DEBUG("Got nil bb in restoreFromSavedBB!") + end end return Screen diff --git a/frontend/ui/event.lua b/frontend/ui/event.lua index 63c8806ad..fcc20c93e 100644 --- a/frontend/ui/event.lua +++ b/frontend/ui/event.lua @@ -10,13 +10,13 @@ below. local Event = {} function Event:new(name, ...) - local o = { - handler = "on"..name, - args = {...} - } - setmetatable(o, self) - self.__index = self - return o + local o = { + handler = "on"..name, + args = {...} + } + setmetatable(o, self) + self.__index = self + return o end return Event diff --git a/frontend/ui/font.lua b/frontend/ui/font.lua index b5dbea66d..a0d0bd010 100644 --- a/frontend/ui/font.lua +++ b/frontend/ui/font.lua @@ -2,102 +2,102 @@ local Screen = require("ui/screen") local DEBUG = require("dbg") local Font = { - fontmap = { - -- default font for menu contents - cfont = "droid/DroidSans.ttf", - -- default font for title - --tfont = "NimbusSanL-BoldItal.cff", - tfont = "droid/DroidSans.ttf", - -- default font for footer - ffont = "droid/DroidSans.ttf", - - -- default font for reading position info - rifont = "droid/DroidSans.ttf", - - -- default font for pagination display - pgfont = "droid/DroidSans.ttf", - - -- selectmenu: font for item shortcut - scfont = "droid/DroidSansMono.ttf", - - -- help page: font for displaying keys - hpkfont = "droid/DroidSansMono.ttf", - -- font for displaying help messages - hfont = "droid/DroidSans.ttf", - - -- font for displaying input content - -- we have to use mono here for better distance controlling - infont = "droid/DroidSansMono.ttf", - - -- font for info messages - infofont = "droid/DroidSans.ttf", - }, - fallbacks = { - [1] = "droid/DroidSansFallback.ttf", - [2] = "droid/DroidSans.ttf", - [3] = "freefont/FreeSans.ttf", - }, - - fontdir = os.getenv("FONTDIR") or "./fonts", - - -- face table - faces = {}, + fontmap = { + -- default font for menu contents + cfont = "droid/DroidSans.ttf", + -- default font for title + --tfont = "NimbusSanL-BoldItal.cff", + tfont = "droid/DroidSans.ttf", + -- default font for footer + ffont = "droid/DroidSans.ttf", + + -- default font for reading position info + rifont = "droid/DroidSans.ttf", + + -- default font for pagination display + pgfont = "droid/DroidSans.ttf", + + -- selectmenu: font for item shortcut + scfont = "droid/DroidSansMono.ttf", + + -- help page: font for displaying keys + hpkfont = "droid/DroidSansMono.ttf", + -- font for displaying help messages + hfont = "droid/DroidSans.ttf", + + -- font for displaying input content + -- we have to use mono here for better distance controlling + infont = "droid/DroidSansMono.ttf", + + -- font for info messages + infofont = "droid/DroidSans.ttf", + }, + fallbacks = { + [1] = "droid/DroidSansFallback.ttf", + [2] = "droid/DroidSans.ttf", + [3] = "freefont/FreeSans.ttf", + }, + + fontdir = os.getenv("FONTDIR") or "./fonts", + + -- face table + faces = {}, } function Font:getFace(font, size) - if not font then - -- default to content font - font = self.cfont - end - - local size = Screen:scaleByDPI(size) - - local face = self.faces[font..size] - -- build face if not found - if not face then - local realname = self.fontmap[font] - if not realname then - realname = font - end - realname = self.fontdir.."/"..realname - ok, face = pcall(freetype.newFace, realname, size) - if not ok then - DEBUG("#! Font "..font.." ("..realname..") not supported: "..face) - return nil - end - self.faces[font..size] = face - --DEBUG("getFace, found: "..realname.." size:"..size) - end - return { size = size, ftface = face, hash = font..size } + if not font then + -- default to content font + font = self.cfont + end + + local size = Screen:scaleByDPI(size) + + local face = self.faces[font..size] + -- build face if not found + if not face then + local realname = self.fontmap[font] + if not realname then + realname = font + end + realname = self.fontdir.."/"..realname + ok, face = pcall(freetype.newFace, realname, size) + if not ok then + DEBUG("#! Font "..font.." ("..realname..") not supported: "..face) + return nil + end + self.faces[font..size] = face + --DEBUG("getFace, found: "..realname.." size:"..size) + end + return { size = size, ftface = face, hash = font..size } end function Font:_readList(target, dir, effective_dir) - for f in lfs.dir(dir) do - if lfs.attributes(dir.."/"..f, "mode") == "directory" and f ~= "." and f ~= ".." then - self:_readList(target, dir.."/"..f, effective_dir..f.."/") - else - local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "") - if file_type == "ttf" or file_type == "cff" or file_type == "otf" then - table.insert(target, effective_dir..f) - end - end - end + for f in lfs.dir(dir) do + if lfs.attributes(dir.."/"..f, "mode") == "directory" and f ~= "." and f ~= ".." then + self:_readList(target, dir.."/"..f, effective_dir..f.."/") + else + local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "") + if file_type == "ttf" or file_type == "cff" or file_type == "otf" then + table.insert(target, effective_dir..f) + end + end + end end function Font:getFontList() - fontlist = {} - self:_readList(fontlist, self.fontdir, "") - table.sort(fontlist) - return fontlist + fontlist = {} + self:_readList(fontlist, self.fontdir, "") + table.sort(fontlist) + return fontlist end function Font:update() - for _k, _v in ipairs(self.faces) do - _v:done() - end - self.faces = {} - clearGlyphCache() + for _k, _v in ipairs(self.faces) do + _v:done() + end + self.faces = {} + clearGlyphCache() end return Font diff --git a/frontend/ui/geometry.lua b/frontend/ui/geometry.lua index c7aeb433b..3a00247a6 100644 --- a/frontend/ui/geometry.lua +++ b/frontend/ui/geometry.lua @@ -12,48 +12,48 @@ just use it on simple tables that have x, y and/or w, h or define your own types using this as a metatable ]]-- local Geom = { - x = 0, - y = 0, - w = 0, - h = 0 + x = 0, + y = 0, + w = 0, + h = 0 } function Geom:new(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + return o end function Geom:copy() - local n = Geom:new() - n.x = self.x - n.y = self.y - n.w = self.w - n.h = self.h - return n + local n = Geom:new() + n.x = self.x + n.y = self.y + n.w = self.w + n.h = self.h + return n end function Geom:__tostring() - return self.w.."x"..self.h.."+"..self.x.."+"..self.y + return self.w.."x"..self.h.."+"..self.x.."+"..self.y end --[[ offset rectangle or point by relative values ]]-- function Geom:offsetBy(dx, dy) - self.x = self.x + dx - self.y = self.y + dy - return self + self.x = self.x + dx + self.y = self.y + dy + return self end --[[ offset rectangle or point to certain coordinates ]]-- function Geom:offsetTo(x, y) - self.x = x - self.y = y - return self + self.x = x + self.y = y + return self end --[[ @@ -62,29 +62,29 @@ scale rectangle (grow to bottom and to the right) or dimension if a single factor is given, it is applied to both width and height ]]-- function Geom:scaleBy(zx, zy) - self.w = self.w * zx - self.h = self.h * (zy or zx) - return self + self.w = self.w * zx + self.h = self.h * (zy or zx) + return self end --[[ this method also takes care of x and y ]]-- function Geom:transformByScale(zx, zy) - self.x = self.x * zx - self.y = self.y * (zx or zy) - self:scaleBy(zx, zy) + self.x = self.x * zx + self.y = self.y * (zx or zy) + self:scaleBy(zx, zy) end --[[ return size of geom ]]-- function Geom:sizeof() - if not self.w or not self.h then - return 0 - else - return self.w * self.h - end + if not self.w or not self.h then + return 0 + else + return self.w * self.h + end end --[[ @@ -93,9 +93,9 @@ enlarges or shrinks dimensions or rectangles note that for rectangles the offset stays the same ]]-- function Geom:changeSizeBy(dw, dh) - self.w = self.w + dw - self.h = self.h + dh - return self + self.w = self.w + dw + self.h = self.h + dh + return self end --[[ @@ -104,25 +104,25 @@ return the outer rectangle that contains both us and a given rectangle works for rectangles, dimensions and points ]]-- function Geom:combine(rect_b) - local combined = self:copy() - if not rect_b or rect_b:sizeof() == 0 then return combined end - if combined.x > rect_b.x then - combined.x = rect_b.x - end - if combined.y > rect_b.y then - combined.y = rect_b.y - end - if self.x + self.w > rect_b.x + rect_b.w then - combined.w = self.x + self.w - combined.x - else - combined.w = rect_b.x + rect_b.w - combined.x - end - if self.y + self.h > rect_b.y + rect_b.h then - combined.h = self.y + self.h - combined.y - else - combined.h = rect_b.y + rect_b.h - combined.y - end - return combined + local combined = self:copy() + if not rect_b or rect_b:sizeof() == 0 then return combined end + if combined.x > rect_b.x then + combined.x = rect_b.x + end + if combined.y > rect_b.y then + combined.y = rect_b.y + end + if self.x + self.w > rect_b.x + rect_b.w then + combined.w = self.x + self.w - combined.x + else + combined.w = rect_b.x + rect_b.w - combined.x + end + if self.y + self.h > rect_b.y + rect_b.h then + combined.h = self.y + self.h - combined.y + else + combined.h = rect_b.y + rect_b.h - combined.y + end + return combined end --[[ @@ -131,47 +131,47 @@ returns a rectangle for the part that we and a given rectangle share TODO: what happens if there is no rectangle shared? currently behaviour is undefined. ]]-- function Geom:intersect(rect_b) - -- make a copy of self - local intersected = self:copy() - if self.x < rect_b.x then - intersected.x = rect_b.x - end - if self.y < rect_b.y then - intersected.y = rect_b.y - end - if self.x + self.w < rect_b.x + rect_b.w then - intersected.w = self.x + self.w - intersected.x - else - intersected.w = rect_b.x + rect_b.w - intersected.x - end - if self.y + self.h < rect_b.y + rect_b.h then - intersected.h = self.y + self.h - intersected.y - else - intersected.h = rect_b.y + rect_b.h - intersected.y - end - return intersected + -- make a copy of self + local intersected = self:copy() + if self.x < rect_b.x then + intersected.x = rect_b.x + end + if self.y < rect_b.y then + intersected.y = rect_b.y + end + if self.x + self.w < rect_b.x + rect_b.w then + intersected.w = self.x + self.w - intersected.x + else + intersected.w = rect_b.x + rect_b.w - intersected.x + end + if self.y + self.h < rect_b.y + rect_b.h then + intersected.h = self.y + self.h - intersected.y + else + intersected.h = rect_b.y + rect_b.h - intersected.y + end + return intersected end --[[ return true if self does not share any area with rect_b ]]-- function Geom:notIntersectWith(rect_b) - if (self.x >= (rect_b.x + rect_b.w)) - or (self.y >= (rect_b.y + rect_b.h)) - or (rect_b.x >= (self.x + self.w)) - or (rect_b.y >= (self.y + self.h)) then - return true - end - return false + if (self.x >= (rect_b.x + rect_b.w)) + or (self.y >= (rect_b.y + rect_b.h)) + or (rect_b.x >= (self.x + self.w)) + or (rect_b.y >= (self.y + self.h)) then + return true + end + return false end --[[ set size of dimension or rectangle to size of given dimension/rectangle ]]-- function Geom:setSizeTo(rect_b) - self.w = rect_b.w - self.h = rect_b.h - return self + self.w = rect_b.w + self.h = rect_b.h + return self end --[[ @@ -181,14 +181,14 @@ works for dimensions, too for points, it is basically an equality check ]]-- function Geom:contains(rect_b) - if self.x <= rect_b.x - and self.y <= rect_b.y - and self.x + self.w >= rect_b.x + rect_b.w - and self.y + self.h >= rect_b.y + rect_b.h - then - return true - end - return false + if self.x <= rect_b.x + and self.y <= rect_b.y + and self.x + self.w >= rect_b.x + rect_b.w + and self.y + self.h >= rect_b.y + rect_b.h + then + return true + end + return false end --[[ @@ -197,48 +197,48 @@ check for equality works for rectangles, points, dimensions ]]-- function Geom:__eq(rect_b) - if self.x == rect_b.x - and self.y == rect_b.y - and self:equalSize(rect_b) - then - return true - end - return false + if self.x == rect_b.x + and self.y == rect_b.y + and self:equalSize(rect_b) + then + return true + end + return false end --[[ check size of dimension/rectangle for equality ]]-- function Geom:equalSize(rect_b) - if self.w == rect_b.w - and self.h == rect_b.h - then - return true - end - return false + if self.w == rect_b.w + and self.h == rect_b.h + then + return true + end + return false end --[[ check if our size is smaller than the size of the given dimension/rectangle ]]-- function Geom:__lt(rect_b) - DEBUG("lt:",self,rect_b) - if self.w < rect_b.w and self.h < rect_b.h then + DEBUG("lt:",self,rect_b) + if self.w < rect_b.w and self.h < rect_b.h then DEBUG("lt+") - return true - end + return true + end DEBUG("lt-") - return false + return false end --[[ check if our size is smaller or equal the size of the given dimension/rectangle ]]-- function Geom:__le(rect_b) - if self.w <= rect_b.w and self.h <= rect_b.h then - return true - end - return false + if self.w <= rect_b.w and self.h <= rect_b.h then + return true + end + return false end --[[ @@ -249,92 +249,92 @@ this can also be called with dx=0 and dy=0, which will fit the current rectangle into the given rectangle ]]-- function Geom:offsetWithin(rect_b, dx, dy) - -- check size constraints and shrink us when we're too big - if self.w > rect_b.w then - self.w = rect_b.w - end - if self.h > rect_b.h then - self.h = rect_b.h - end - -- offset - self.x = self.x + dx - self.y = self.y + dy - -- check offsets - if self.x < rect_b.x then - self.x = rect_b.x - end - if self.y < rect_b.y then - self.y = rect_b.y - end - if self.x + self.w > rect_b.x + rect_b.w then - self.x = rect_b.x + rect_b.w - self.w - end - if self.y + self.h > rect_b.y + rect_b.h then - self.y = rect_b.y + rect_b.h - self.h - end + -- check size constraints and shrink us when we're too big + if self.w > rect_b.w then + self.w = rect_b.w + end + if self.h > rect_b.h then + self.h = rect_b.h + end + -- offset + self.x = self.x + dx + self.y = self.y + dy + -- check offsets + if self.x < rect_b.x then + self.x = rect_b.x + end + if self.y < rect_b.y then + self.y = rect_b.y + end + if self.x + self.w > rect_b.x + rect_b.w then + self.x = rect_b.x + rect_b.w - self.w + end + if self.y + self.h > rect_b.y + rect_b.h then + self.y = rect_b.y + rect_b.h - self.h + end end --[[ center the current rectangle at position x and y of a given rectangle ]]-- function Geom:centerWithin(rect_b, x, y) - -- check size constraints and shrink us when we're too big - if self.w > rect_b.w then - self.w = rect_b.w - end - if self.h > rect_b.h then - self.h = rect_b.h - end - -- place to center - self.x = x - self.w/2 - self.y = y - self.h/2 - -- check boundary - if self.x < rect_b.x then - self.x = rect_b.x - end - if self.y < rect_b.y then - self.y = rect_b.y - end - if self.x + self.w > rect_b.x + rect_b.w then - self.x = rect_b.x + rect_b.w - self.w - end - if self.y + self.h > rect_b.y + rect_b.h then - self.y = rect_b.y + rect_b.h - self.h - end + -- check size constraints and shrink us when we're too big + if self.w > rect_b.w then + self.w = rect_b.w + end + if self.h > rect_b.h then + self.h = rect_b.h + end + -- place to center + self.x = x - self.w/2 + self.y = y - self.h/2 + -- check boundary + if self.x < rect_b.x then + self.x = rect_b.x + end + if self.y < rect_b.y then + self.y = rect_b.y + end + if self.x + self.w > rect_b.x + rect_b.w then + self.x = rect_b.x + rect_b.w - self.w + end + if self.y + self.h > rect_b.y + rect_b.h then + self.y = rect_b.y + rect_b.h - self.h + end end function Geom:shrinkInside(rect_b, dx, dy) - self:offsetBy(dx, dy) - return self:intersect(rect_b) + self:offsetBy(dx, dy) + return self:intersect(rect_b) end --[[ return the Euclidean distance between two geoms ]]-- function Geom:distance(geom) - return math.sqrt(math.pow(self.x - geom.x, 2) + math.pow(self.y - geom.y, 2)) + return math.sqrt(math.pow(self.x - geom.x, 2) + math.pow(self.y - geom.y, 2)) end --[[ return the midpoint of two geoms ]]-- function Geom:midpoint(geom) - return Geom:new{ - x = (self.x + geom.x) / 2, - y = (self.y + geom.y) / 2, - w = 0, h = 0, - } + return Geom:new{ + x = (self.x + geom.x) / 2, + y = (self.y + geom.y) / 2, + w = 0, h = 0, + } end --[[ return center point in this geom ]]-- function Geom:center() - return Geom:new{ - x = self.x + self.w / 2, - y = self.y + self.h / 2, - w = 0, h = 0, - } + return Geom:new{ + x = self.x + self.w / 2, + y = self.y + self.h / 2, + w = 0, h = 0, + } end return Geom diff --git a/frontend/ui/gesturedetector.lua b/frontend/ui/gesturedetector.lua index f15133987..4265796cf 100644 --- a/frontend/ui/gesturedetector.lua +++ b/frontend/ui/gesturedetector.lua @@ -5,34 +5,34 @@ local DEBUG = require("dbg") --[[ Current detectable gestures: - * tap - * pan - * hold - * swipe - * pinch - * spread - * rotate - * hold_pan - * double_tap - * inward_pan - * outward_pan - * pan_release - * hold_release - * two_finger_tap - * two_finger_pan - * two_finger_swipe - * two_finger_pan_release + * tap + * pan + * hold + * swipe + * pinch + * spread + * rotate + * hold_pan + * double_tap + * inward_pan + * outward_pan + * pan_release + * hold_release + * two_finger_tap + * two_finger_pan + * two_finger_swipe + * two_finger_pan_release You change the state machine by feeding it touch events, i.e. calling GestureDetector:feedEvent(tev). a touch event should have following format: tev = { - slot = 1, - id = 46, - x = 0, - y = 1, - timev = TimeVal:new{...}, + slot = 1, + id = 46, + x = 0, + y = 1, + timev = TimeVal:new{...}, } Don't confuse tev with raw evs from kernel, tev is build according to ev. @@ -42,103 +42,103 @@ feed a touch release event to it. --]] local GestureDetector = { - -- must be initialized with the Input singleton class - input = nil, - -- all the time parameters are in us - DOUBLE_TAP_INTERVAL = 300 * 1000, - TWO_FINGER_TAP_DURATION = 300 * 1000, - HOLD_INTERVAL = 500 * 1000, - SWIPE_INTERVAL = 900 * 1000, - -- distance parameters - DOUBLE_TAP_DISTANCE = 50 * Screen:getDPI() / 167, - TWO_FINGER_TAP_REGION = 20 * Screen:getDPI() / 167, - PAN_THRESHOLD = 50 * Screen:getDPI() / 167, - -- pinch/spread direction table - DIRECTION_TABLE = { - east = "horizontal", - west = "horizontal", - north = "vertical", - south = "vertical", - northeast = "diagonal", - northwest = "diagonal", - southeast = "diagonal", - southwest = "diagonal", - }, - -- states are stored in separated slots - states = {}, - track_ids = {}, - tev_stacks = {}, - -- latest feeded touch event in each slots - last_tevs = {}, - first_tevs = {}, - -- detecting status on each slots - detectings = {}, - -- for single/double tap - last_taps = {}, + -- must be initialized with the Input singleton class + input = nil, + -- all the time parameters are in us + DOUBLE_TAP_INTERVAL = 300 * 1000, + TWO_FINGER_TAP_DURATION = 300 * 1000, + HOLD_INTERVAL = 500 * 1000, + SWIPE_INTERVAL = 900 * 1000, + -- distance parameters + DOUBLE_TAP_DISTANCE = 50 * Screen:getDPI() / 167, + TWO_FINGER_TAP_REGION = 20 * Screen:getDPI() / 167, + PAN_THRESHOLD = 50 * Screen:getDPI() / 167, + -- pinch/spread direction table + DIRECTION_TABLE = { + east = "horizontal", + west = "horizontal", + north = "vertical", + south = "vertical", + northeast = "diagonal", + northwest = "diagonal", + southeast = "diagonal", + southwest = "diagonal", + }, + -- states are stored in separated slots + states = {}, + track_ids = {}, + tev_stacks = {}, + -- latest feeded touch event in each slots + last_tevs = {}, + first_tevs = {}, + -- detecting status on each slots + detectings = {}, + -- for single/double tap + last_taps = {}, } function GestureDetector:feedEvent(tevs) - repeat - local tev = table.remove(tevs) - if tev then - --DEBUG("tev fed|",tev.timev.sec,"|",tev.timev.usec,"|",tev.x,"|",tev.y,"|",tev.id,"| Evt",tev.slot) - local slot = tev.slot - if not self.states[slot] then - self:clearState(slot) -- initiate state - end - local ges = self.states[slot](self, tev) - if tev.id ~= -1 then - self.last_tevs[slot] = tev - end - -- return no more than one gesture - if ges then return ges end - end - until tev == nil + repeat + local tev = table.remove(tevs) + if tev then + --DEBUG("tev fed|",tev.timev.sec,"|",tev.timev.usec,"|",tev.x,"|",tev.y,"|",tev.id,"| Evt",tev.slot) + local slot = tev.slot + if not self.states[slot] then + self:clearState(slot) -- initiate state + end + local ges = self.states[slot](self, tev) + if tev.id ~= -1 then + self.last_tevs[slot] = tev + end + -- return no more than one gesture + if ges then return ges end + end + until tev == nil end function GestureDetector:deepCopyEv(tev) - return { - x = tev.x, - y = tev.y, - id = tev.id, - slot = tev.slot, - timev = TimeVal:new{ - sec = tev.timev.sec, - usec = tev.timev.usec, - } - } + return { + x = tev.x, + y = tev.y, + id = tev.id, + slot = tev.slot, + timev = TimeVal:new{ + sec = tev.timev.sec, + usec = tev.timev.usec, + } + } end --[[ tap2 is the later tap --]] function GestureDetector:isDoubleTap(tap1, tap2) - local tv_diff = tap2.timev - tap1.timev - return ( - math.abs(tap1.x - tap2.x) < self.DOUBLE_TAP_DISTANCE and - math.abs(tap1.y - tap2.y) < self.DOUBLE_TAP_DISTANCE and - (tv_diff.sec == 0 and (tv_diff.usec) < self.DOUBLE_TAP_INTERVAL) - ) + local tv_diff = tap2.timev - tap1.timev + return ( + math.abs(tap1.x - tap2.x) < self.DOUBLE_TAP_DISTANCE and + math.abs(tap1.y - tap2.y) < self.DOUBLE_TAP_DISTANCE and + (tv_diff.sec == 0 and (tv_diff.usec) < self.DOUBLE_TAP_INTERVAL) + ) end function GestureDetector:isTwoFingerTap() - if self.last_tevs[0] == nil or self.last_tevs[1] == nil then - return false - end - local x_diff0 = math.abs(self.last_tevs[0].x - self.first_tevs[0].x) - local x_diff1 = math.abs(self.last_tevs[1].x - self.first_tevs[1].x) - local y_diff0 = math.abs(self.last_tevs[0].y - self.first_tevs[0].y) - local y_diff1 = math.abs(self.last_tevs[1].y - self.first_tevs[1].y) - local tv_diff0 = self.last_tevs[0].timev - self.first_tevs[0].timev - local tv_diff1 = self.last_tevs[1].timev - self.first_tevs[1].timev - return ( - x_diff0 < self.TWO_FINGER_TAP_REGION and - x_diff1 < self.TWO_FINGER_TAP_REGION and - y_diff0 < self.TWO_FINGER_TAP_REGION and - y_diff1 < self.TWO_FINGER_TAP_REGION and - tv_diff0.sec == 0 and tv_diff0.usec < self.TWO_FINGER_TAP_DURATION and - tv_diff1.sec == 0 and tv_diff1.usec < self.TWO_FINGER_TAP_DURATION - ) + if self.last_tevs[0] == nil or self.last_tevs[1] == nil then + return false + end + local x_diff0 = math.abs(self.last_tevs[0].x - self.first_tevs[0].x) + local x_diff1 = math.abs(self.last_tevs[1].x - self.first_tevs[1].x) + local y_diff0 = math.abs(self.last_tevs[0].y - self.first_tevs[0].y) + local y_diff1 = math.abs(self.last_tevs[1].y - self.first_tevs[1].y) + local tv_diff0 = self.last_tevs[0].timev - self.first_tevs[0].timev + local tv_diff1 = self.last_tevs[1].timev - self.first_tevs[1].timev + return ( + x_diff0 < self.TWO_FINGER_TAP_REGION and + x_diff1 < self.TWO_FINGER_TAP_REGION and + y_diff0 < self.TWO_FINGER_TAP_REGION and + y_diff1 < self.TWO_FINGER_TAP_REGION and + tv_diff0.sec == 0 and tv_diff0.usec < self.TWO_FINGER_TAP_DURATION and + tv_diff1.sec == 0 and tv_diff1.usec < self.TWO_FINGER_TAP_DURATION + ) end --[[ @@ -146,43 +146,43 @@ compare last_pan with first_tev in this slot return pan direction and distance --]] function GestureDetector:getPath(slot) - local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x - local y_diff = self.last_tevs[slot].y - self.first_tevs[slot].y - local direction = nil - local distance = math.sqrt(x_diff*x_diff + y_diff*y_diff) - if x_diff == 0 and y_diff == 0 then - else - local v_direction = y_diff < 0 and "north" or "south" - local h_direction = x_diff < 0 and "west" or "east" - if math.abs(y_diff) > 0.577*math.abs(x_diff) - and math.abs(y_diff) < 1.732*math.abs(x_diff) then - direction = v_direction..h_direction - elseif (math.abs(x_diff) > math.abs(y_diff)) then - direction = h_direction - else - direction = v_direction - end - end - return direction, distance + local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x + local y_diff = self.last_tevs[slot].y - self.first_tevs[slot].y + local direction = nil + local distance = math.sqrt(x_diff*x_diff + y_diff*y_diff) + if x_diff == 0 and y_diff == 0 then + else + local v_direction = y_diff < 0 and "north" or "south" + local h_direction = x_diff < 0 and "west" or "east" + if math.abs(y_diff) > 0.577*math.abs(x_diff) + and math.abs(y_diff) < 1.732*math.abs(x_diff) then + direction = v_direction..h_direction + elseif (math.abs(x_diff) > math.abs(y_diff)) then + direction = h_direction + else + direction = v_direction + end + end + return direction, distance end function GestureDetector:isSwipe(slot) - if not self.first_tevs[slot] or not self.last_tevs[slot] then return end - local tv_diff = self.first_tevs[slot].timev - self.last_tevs[slot].timev - if (tv_diff.sec == 0) and (tv_diff.usec < self.SWIPE_INTERVAL) then - local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x - local y_diff = self.last_tevs[slot].y - self.first_tevs[slot].y - if x_diff ~= 0 or y_diff ~= 0 then - return true - end - end + if not self.first_tevs[slot] or not self.last_tevs[slot] then return end + local tv_diff = self.first_tevs[slot].timev - self.last_tevs[slot].timev + if (tv_diff.sec == 0) and (tv_diff.usec < self.SWIPE_INTERVAL) then + local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x + local y_diff = self.last_tevs[slot].y - self.first_tevs[slot].y + if x_diff ~= 0 or y_diff ~= 0 then + return true + end + end end function GestureDetector:getRotate(orig_point, start_point, end_point) - local a = orig_point:distance(start_point) - local b = orig_point:distance(end_point) - local c = start_point:distance(end_point) - return math.acos((a*a + b*b - c*c)/(2*a*b))*180/math.pi + local a = orig_point:distance(start_point) + local b = orig_point:distance(end_point) + local c = start_point:distance(end_point) + return math.acos((a*a + b*b - c*c)/(2*a*b))*180/math.pi end --[[ @@ -190,391 +190,391 @@ Warning! this method won't update self.state, you need to do it in each state method! --]] function GestureDetector:switchState(state_new, tev, param) - --@TODO do we need to check whether state is valid? (houqp) - return self[state_new](self, tev, param) + --@TODO do we need to check whether state is valid? (houqp) + return self[state_new](self, tev, param) end function GestureDetector:clearState(slot) - self.states[slot] = self.initialState - self.detectings[slot] = false - self.first_tevs[slot] = nil - self.last_tevs[slot] = nil + self.states[slot] = self.initialState + self.detectings[slot] = false + self.first_tevs[slot] = nil + self.last_tevs[slot] = nil end function GestureDetector:clearStates() - self:clearState(0) - self:clearState(1) + self:clearState(0) + self:clearState(1) end function GestureDetector:initialState(tev) - local slot = tev.slot - if tev.id then - -- an event ends - if tev.id == -1 then - self.detectings[slot] = false - else - self.track_ids[slot] = tev.id - if tev.x and tev.y then - -- user starts a new touch motion - if not self.detectings[slot] then - self.detectings[slot] = true - self.first_tevs[slot] = self:deepCopyEv(tev) - -- default to tap state - return self:switchState("tapState", tev) - end - end - end - end + local slot = tev.slot + if tev.id then + -- an event ends + if tev.id == -1 then + self.detectings[slot] = false + else + self.track_ids[slot] = tev.id + if tev.x and tev.y then + -- user starts a new touch motion + if not self.detectings[slot] then + self.detectings[slot] = true + self.first_tevs[slot] = self:deepCopyEv(tev) + -- default to tap state + return self:switchState("tapState", tev) + end + end + end + end end --[[ this method handles both single and double tap --]] function GestureDetector:tapState(tev) - DEBUG("in tap state...") - local slot = tev.slot - if tev.id == -1 then - -- end of tap event - if self.detectings[0] and self.detectings[1] then - if self:isTwoFingerTap() then - local pos0 = Geom:new{ - x = self.last_tevs[0].x, - y = self.last_tevs[0].y, - w = 0, h = 0, - } - local pos1 = Geom:new{ - x = self.last_tevs[1].x, - y = self.last_tevs[1].y, - w = 0, h = 0, - } - local tap_span = pos0:distance(pos1) - DEBUG("two-finger tap detected with span", tap_span) - self:clearStates() - return { - ges = "two_finger_tap", - pos = pos0:midpoint(pos1), - span = tap_span, - time = tev.timev, - } - else - self:clearState(slot) - end - elseif self.last_tevs[slot] ~= nil then - return self:handleDoubleTap(tev) - else - -- last tev in this slot is cleared by last two finger tap - self:clearState(slot) - return { - ges = "tap", - pos = Geom:new{ - x = tev.x, - y = tev.y, - w = 0, h = 0, - }, - time = tev.timev, - } - end - else - return self:handleNonTap(tev) - end + DEBUG("in tap state...") + local slot = tev.slot + if tev.id == -1 then + -- end of tap event + if self.detectings[0] and self.detectings[1] then + if self:isTwoFingerTap() then + local pos0 = Geom:new{ + x = self.last_tevs[0].x, + y = self.last_tevs[0].y, + w = 0, h = 0, + } + local pos1 = Geom:new{ + x = self.last_tevs[1].x, + y = self.last_tevs[1].y, + w = 0, h = 0, + } + local tap_span = pos0:distance(pos1) + DEBUG("two-finger tap detected with span", tap_span) + self:clearStates() + return { + ges = "two_finger_tap", + pos = pos0:midpoint(pos1), + span = tap_span, + time = tev.timev, + } + else + self:clearState(slot) + end + elseif self.last_tevs[slot] ~= nil then + return self:handleDoubleTap(tev) + else + -- last tev in this slot is cleared by last two finger tap + self:clearState(slot) + return { + ges = "tap", + pos = Geom:new{ + x = tev.x, + y = tev.y, + w = 0, h = 0, + }, + time = tev.timev, + } + end + else + return self:handleNonTap(tev) + end end function GestureDetector:handleDoubleTap(tev) - local slot = tev.slot - local ges_ev = { - -- default to single tap - ges = "tap", - pos = Geom:new{ - x = self.last_tevs[slot].x, - y = self.last_tevs[slot].y, - w = 0, h = 0, - }, - time = tev.timev, - } - -- cur_tap is used for double tap detection - local cur_tap = { - x = tev.x, - y = tev.y, - timev = tev.timev, - } - - if self.last_taps[slot] ~= nil and - self:isDoubleTap(self.last_taps[slot], cur_tap) then - -- it is a double tap - self:clearState(slot) - ges_ev.ges = "double_tap" - self.last_taps[slot] = nil - DEBUG("double tap detected in slot", slot) - return ges_ev - end - - -- set current tap to last tap - self.last_taps[slot] = cur_tap - - DEBUG("set up tap timer") - -- deadline should be calculated by adding current tap time and the interval - local deadline = cur_tap.timev + TimeVal:new{ - sec = 0, - usec = not self.input.disable_double_tap and self.DOUBLE_TAP_INTERVAL or 0, - } - self.input:setTimeout(function() - DEBUG("in tap timer", self.last_taps[slot] ~= nil) - -- double tap will set last_tap to nil so if it is not, then - -- user must only tapped once - if self.last_taps[slot] ~= nil then - self.last_taps[slot] = nil - -- we are using closure here - DEBUG("single tap detected in slot", slot) - return ges_ev - end - end, deadline) - -- we are already at the end of touch event - -- so reset the state - self:clearState(slot) + local slot = tev.slot + local ges_ev = { + -- default to single tap + ges = "tap", + pos = Geom:new{ + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, + w = 0, h = 0, + }, + time = tev.timev, + } + -- cur_tap is used for double tap detection + local cur_tap = { + x = tev.x, + y = tev.y, + timev = tev.timev, + } + + if self.last_taps[slot] ~= nil and + self:isDoubleTap(self.last_taps[slot], cur_tap) then + -- it is a double tap + self:clearState(slot) + ges_ev.ges = "double_tap" + self.last_taps[slot] = nil + DEBUG("double tap detected in slot", slot) + return ges_ev + end + + -- set current tap to last tap + self.last_taps[slot] = cur_tap + + DEBUG("set up tap timer") + -- deadline should be calculated by adding current tap time and the interval + local deadline = cur_tap.timev + TimeVal:new{ + sec = 0, + usec = not self.input.disable_double_tap and self.DOUBLE_TAP_INTERVAL or 0, + } + self.input:setTimeout(function() + DEBUG("in tap timer", self.last_taps[slot] ~= nil) + -- double tap will set last_tap to nil so if it is not, then + -- user must only tapped once + if self.last_taps[slot] ~= nil then + self.last_taps[slot] = nil + -- we are using closure here + DEBUG("single tap detected in slot", slot) + return ges_ev + end + end, deadline) + -- we are already at the end of touch event + -- so reset the state + self:clearState(slot) end function GestureDetector:handleNonTap(tev) - local slot = tev.slot - if self.states[slot] ~= self.tapState then - -- switched from other state, probably from initialState - -- we return nil in this case - self.states[slot] = self.tapState - DEBUG("set up hold timer") - local deadline = tev.timev + TimeVal:new{ - sec = 0, usec = self.HOLD_INTERVAL - } - self.input:setTimeout(function() - if self.states[slot] == self.tapState then - -- timer set in tapState, so we switch to hold - DEBUG("hold gesture detected in slot", slot) - return self:switchState("holdState", tev, true) - end - end, deadline) - --DEBUG("handle non-tap", tev) - return { - ges = "touch", - pos = Geom:new{ - x = tev.x, - y = tev.y, - w = 0, h = 0, - }, - time = tev.timev, - } - else - -- it is not end of touch event, see if we need to switch to - -- other states - if (tev.x and math.abs(tev.x - self.first_tevs[slot].x) >= self.PAN_THRESHOLD) or - (tev.y and math.abs(tev.y - self.first_tevs[slot].y) >= self.PAN_THRESHOLD) then - -- if user's finger moved long enough in X or - -- Y distance, we switch to pan state - return self:switchState("panState", tev) - end - end + local slot = tev.slot + if self.states[slot] ~= self.tapState then + -- switched from other state, probably from initialState + -- we return nil in this case + self.states[slot] = self.tapState + DEBUG("set up hold timer") + local deadline = tev.timev + TimeVal:new{ + sec = 0, usec = self.HOLD_INTERVAL + } + self.input:setTimeout(function() + if self.states[slot] == self.tapState then + -- timer set in tapState, so we switch to hold + DEBUG("hold gesture detected in slot", slot) + return self:switchState("holdState", tev, true) + end + end, deadline) + --DEBUG("handle non-tap", tev) + return { + ges = "touch", + pos = Geom:new{ + x = tev.x, + y = tev.y, + w = 0, h = 0, + }, + time = tev.timev, + } + else + -- it is not end of touch event, see if we need to switch to + -- other states + if (tev.x and math.abs(tev.x - self.first_tevs[slot].x) >= self.PAN_THRESHOLD) or + (tev.y and math.abs(tev.y - self.first_tevs[slot].y) >= self.PAN_THRESHOLD) then + -- if user's finger moved long enough in X or + -- Y distance, we switch to pan state + return self:switchState("panState", tev) + end + end end function GestureDetector:panState(tev) - DEBUG("in pan state...") - local slot = tev.slot - if tev.id == -1 then - -- end of pan, signal swipe gesture if necessary - if self:isSwipe(slot) then - if self.detectings[0] and self.detectings[1] then - local ges_ev = self:handleTwoFingerPan(tev) - self:clearStates() - if ges_ev then - if ges_ev.ges == "two_finger_pan" then - ges_ev.ges = "two_finger_swipe" - elseif ges_ev.ges == "inward_pan" then - ges_ev.ges = "pinch" - elseif ges_ev.ges == "outward_pan" then - ges_ev.ges = "spread" - end - DEBUG(ges_ev.ges, ges_ev.direction, ges_ev.distance, "detected") - end - return ges_ev - else - return self:handleSwipe(tev) - end - else -- if end of pan is not swipe then it must be pan release. - return self:handlePanRelease(tev) - end - else - if self.states[slot] ~= self.panState then - self.states[slot] = self.panState - end - return self:handlePan(tev) - end + DEBUG("in pan state...") + local slot = tev.slot + if tev.id == -1 then + -- end of pan, signal swipe gesture if necessary + if self:isSwipe(slot) then + if self.detectings[0] and self.detectings[1] then + local ges_ev = self:handleTwoFingerPan(tev) + self:clearStates() + if ges_ev then + if ges_ev.ges == "two_finger_pan" then + ges_ev.ges = "two_finger_swipe" + elseif ges_ev.ges == "inward_pan" then + ges_ev.ges = "pinch" + elseif ges_ev.ges == "outward_pan" then + ges_ev.ges = "spread" + end + DEBUG(ges_ev.ges, ges_ev.direction, ges_ev.distance, "detected") + end + return ges_ev + else + return self:handleSwipe(tev) + end + else -- if end of pan is not swipe then it must be pan release. + return self:handlePanRelease(tev) + end + else + if self.states[slot] ~= self.panState then + self.states[slot] = self.panState + end + return self:handlePan(tev) + end end function GestureDetector:handleSwipe(tev) - local slot = tev.slot - local swipe_direction, swipe_distance = self:getPath(slot) - local start_pos = Geom:new{ - x = self.first_tevs[slot].x, - y = self.first_tevs[slot].y, - w = 0, h = 0, - } - DEBUG("swipe", swipe_direction, swipe_distance, "detected in slot", slot) - self:clearState(slot) - return { - ges = "swipe", - -- use first pan tev coordination as swipe start point - pos = start_pos, - direction = swipe_direction, - distance = swipe_distance, - time = tev.timev, - } + local slot = tev.slot + local swipe_direction, swipe_distance = self:getPath(slot) + local start_pos = Geom:new{ + x = self.first_tevs[slot].x, + y = self.first_tevs[slot].y, + w = 0, h = 0, + } + DEBUG("swipe", swipe_direction, swipe_distance, "detected in slot", slot) + self:clearState(slot) + return { + ges = "swipe", + -- use first pan tev coordination as swipe start point + pos = start_pos, + direction = swipe_direction, + distance = swipe_distance, + time = tev.timev, + } end function GestureDetector:handlePan(tev) - local slot = tev.slot - if self.detectings[0] and self.detectings[1] then - return self:handleTwoFingerPan(tev) - else - local pan_direction, pan_distance = self:getPath(slot) - local pan_ev = { - ges = "pan", - relative = { - -- default to pan 0 - x = 0, - y = 0, - }, - pos = nil, - direction = pan_direction, - distance = pan_distance, - time = tev.timev, - } - pan_ev.relative.x = tev.x - self.first_tevs[slot].x - pan_ev.relative.y = tev.y - self.first_tevs[slot].y - pan_ev.pos = Geom:new{ - x = self.last_tevs[slot].x, - y = self.last_tevs[slot].y, - w = 0, h = 0, - } - --DEBUG(pan_ev.ges, pan_ev, "detected") - return pan_ev - end + local slot = tev.slot + if self.detectings[0] and self.detectings[1] then + return self:handleTwoFingerPan(tev) + else + local pan_direction, pan_distance = self:getPath(slot) + local pan_ev = { + ges = "pan", + relative = { + -- default to pan 0 + x = 0, + y = 0, + }, + pos = nil, + direction = pan_direction, + distance = pan_distance, + time = tev.timev, + } + pan_ev.relative.x = tev.x - self.first_tevs[slot].x + pan_ev.relative.y = tev.y - self.first_tevs[slot].y + pan_ev.pos = Geom:new{ + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, + w = 0, h = 0, + } + --DEBUG(pan_ev.ges, pan_ev, "detected") + return pan_ev + end end function GestureDetector:handleTwoFingerPan(tev) - -- triggering slot - local tslot = tev.slot - -- reference slot - local rslot = tslot and 0 or 1 - local tpan_dir, tpan_dis = self:getPath(tslot) - local tstart_pos = Geom:new{ - x = self.first_tevs[tslot].x, - y = self.first_tevs[tslot].y, - w = 0, h = 0, - } - local tend_pos = Geom:new{ - x = self.last_tevs[tslot].x, - y = self.last_tevs[tslot].y, - w = 0, h = 0, - } - local rstart_pos = Geom:new{ - x = self.first_tevs[rslot].x, - y = self.first_tevs[rslot].y, - w = 0, h = 0, - } - if self.states[rslot] == self.panState then - local rpan_dir, rpan_dis = self:getPath(rslot) - local rend_pos = Geom:new{ - x = self.last_tevs[rslot].x, - y = self.last_tevs[rslot].y, - w = 0, h = 0, - } - local start_distance = tstart_pos:distance(rstart_pos) - local end_distance = tend_pos:distance(rend_pos) - local ges_ev = { - ges = "two_finger_pan", - -- use midpoint of tstart and rstart as swipe start point - pos = tstart_pos:midpoint(rstart_pos), - distance = tpan_dis + rpan_dis, - direction = tpan_dir, - time = tev.timev, - } - if tpan_dir ~= rpan_dir then - if start_distance > end_distance then - ges_ev.ges = "inward_pan" - else - ges_ev.ges = "outward_pan" - end - ges_ev.direction = self.DIRECTION_TABLE[tpan_dir] - end - DEBUG(ges_ev.ges, ges_ev.direction, ges_ev.distance, "detected") - return ges_ev - elseif self.states[rslot] == self.holdState then - local angle = self:getRotate(rstart_pos, tstart_pos, tend_pos) - DEBUG("rotate", angle, "detected") - return { - ges = "rotate", - pos = rstart_pos, - angle = angle, - time = tev.timev, - } - end + -- triggering slot + local tslot = tev.slot + -- reference slot + local rslot = tslot and 0 or 1 + local tpan_dir, tpan_dis = self:getPath(tslot) + local tstart_pos = Geom:new{ + x = self.first_tevs[tslot].x, + y = self.first_tevs[tslot].y, + w = 0, h = 0, + } + local tend_pos = Geom:new{ + x = self.last_tevs[tslot].x, + y = self.last_tevs[tslot].y, + w = 0, h = 0, + } + local rstart_pos = Geom:new{ + x = self.first_tevs[rslot].x, + y = self.first_tevs[rslot].y, + w = 0, h = 0, + } + if self.states[rslot] == self.panState then + local rpan_dir, rpan_dis = self:getPath(rslot) + local rend_pos = Geom:new{ + x = self.last_tevs[rslot].x, + y = self.last_tevs[rslot].y, + w = 0, h = 0, + } + local start_distance = tstart_pos:distance(rstart_pos) + local end_distance = tend_pos:distance(rend_pos) + local ges_ev = { + ges = "two_finger_pan", + -- use midpoint of tstart and rstart as swipe start point + pos = tstart_pos:midpoint(rstart_pos), + distance = tpan_dis + rpan_dis, + direction = tpan_dir, + time = tev.timev, + } + if tpan_dir ~= rpan_dir then + if start_distance > end_distance then + ges_ev.ges = "inward_pan" + else + ges_ev.ges = "outward_pan" + end + ges_ev.direction = self.DIRECTION_TABLE[tpan_dir] + end + DEBUG(ges_ev.ges, ges_ev.direction, ges_ev.distance, "detected") + return ges_ev + elseif self.states[rslot] == self.holdState then + local angle = self:getRotate(rstart_pos, tstart_pos, tend_pos) + DEBUG("rotate", angle, "detected") + return { + ges = "rotate", + pos = rstart_pos, + angle = angle, + time = tev.timev, + } + end end function GestureDetector:handlePanRelease(tev) - local slot = tev.slot - local release_pos = Geom:new{ - x = self.last_tevs[slot].x, - y = self.last_tevs[slot].y, - w = 0, h = 0, - } - local pan_ev = { - ges = "pan_release", - pos = release_pos, - time = tev.timev, - } - if self.detectings[0] and self.detectings[1] then - DEBUG("two finger pan release detected") - pan_ev.ges = "two_finger_pan_release" - self:clearStates() - else - DEBUG("pan release detected in slot", slot) - self:clearState(slot) - end - return pan_ev + local slot = tev.slot + local release_pos = Geom:new{ + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, + w = 0, h = 0, + } + local pan_ev = { + ges = "pan_release", + pos = release_pos, + time = tev.timev, + } + if self.detectings[0] and self.detectings[1] then + DEBUG("two finger pan release detected") + pan_ev.ges = "two_finger_pan_release" + self:clearStates() + else + DEBUG("pan release detected in slot", slot) + self:clearState(slot) + end + return pan_ev end function GestureDetector:holdState(tev, hold) - DEBUG("in hold state...") - local slot = tev.slot - -- when we switch to hold state, we pass additional param "hold" - if tev.id ~= -1 and hold and self.last_tevs[slot].x and self.last_tevs[slot].y then - self.states[slot] = self.holdState - return { - ges = "hold", - pos = Geom:new{ - x = self.last_tevs[slot].x, - y = self.last_tevs[slot].y, - w = 0, h = 0, - }, - time = tev.timev, - } - elseif tev.id == -1 and self.last_tevs[slot] ~= nil then - -- end of hold, signal hold release - DEBUG("hold_release detected in slot", slot) - local last_x = self.last_tevs[slot].x - local last_y = self.last_tevs[slot].y - self:clearState(slot) - return { - ges = "hold_release", - pos = Geom:new{ - x = last_x, - y = last_y, - w = 0, h = 0, - }, - time = tev.timev, - } - elseif (tev.x and math.abs(tev.x - self.first_tevs[slot].x) >= self.PAN_THRESHOLD) or - (tev.y and math.abs(tev.y - self.first_tevs[slot].y) >= self.PAN_THRESHOLD) then - local ges_ev = self:handlePan(tev) - if ges_ev ~= nil then ges_ev.ges = "hold_pan" end - return ges_ev - end + DEBUG("in hold state...") + local slot = tev.slot + -- when we switch to hold state, we pass additional param "hold" + if tev.id ~= -1 and hold and self.last_tevs[slot].x and self.last_tevs[slot].y then + self.states[slot] = self.holdState + return { + ges = "hold", + pos = Geom:new{ + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, + w = 0, h = 0, + }, + time = tev.timev, + } + elseif tev.id == -1 and self.last_tevs[slot] ~= nil then + -- end of hold, signal hold release + DEBUG("hold_release detected in slot", slot) + local last_x = self.last_tevs[slot].x + local last_y = self.last_tevs[slot].y + self:clearState(slot) + return { + ges = "hold_release", + pos = Geom:new{ + x = last_x, + y = last_y, + w = 0, h = 0, + }, + time = tev.timev, + } + elseif (tev.x and math.abs(tev.x - self.first_tevs[slot].x) >= self.PAN_THRESHOLD) or + (tev.y and math.abs(tev.y - self.first_tevs[slot].y) >= self.PAN_THRESHOLD) then + local ges_ev = self:handlePan(tev) + if ges_ev ~= nil then ges_ev.ges = "hold_pan" end + return ges_ev + end end --[[ @@ -584,82 +584,82 @@ end @return adjusted gesture. --]] function GestureDetector:adjustGesCoordinate(ges) - if Screen.cur_rotation_mode == 1 then - -- in landscape mode rotated 270 - if ges.pos then - ges.pos.x, ges.pos.y = (Screen:getWidth() - ges.pos.y), (ges.pos.x) - end - if ges.ges == "swipe" or ges.ges == "pan" - or ges.ges == "two_finger_swipe" - or ges.ges == "two_finger_pan" then - if ges.direction == "north" then - ges.direction = "east" - elseif ges.direction == "south" then - ges.direction = "west" - elseif ges.direction == "east" then - ges.direction = "south" - elseif ges.direction == "west" then - ges.direction = "north" - elseif ges.direction == "northeast" then - ges.direction = "southeast" - elseif ges.direction == "northwest" then - ges.direction = "northeast" - elseif ges.direction == "southeast" then - ges.direction = "southwest" - elseif ges.direction == "southwest" then - ges.direction = "northwest" - end - if ges.relative then - ges.relative.x, ges.relative.y = -ges.relative.y, ges.relative.x - end - elseif ges.ges == "pinch" or ges.ges == "spread" - or ges.ges == "inward_pan" - or ges.ges == "outward_pan" then - if ges.direction == "horizontal" then - ges.direction = "vertical" - elseif ges.direction == "vertical" then - ges.direction = "horizontal" - end - end - elseif Screen.cur_rotation_mode == 3 then - -- in landscape mode rotated 90 - if ges.pos then - ges.pos.x, ges.pos.y = (ges.pos.y), (Screen:getHeight() - ges.pos.x) - end - if ges.ges == "swipe" or ges.ges == "pan" - or ges.ges == "two_finger_swipe" - or ges.ges == "two_finger_pan" then - if ges.direction == "north" then - ges.direction = "west" - elseif ges.direction == "south" then - ges.direction = "east" - elseif ges.direction == "east" then - ges.direction = "north" - elseif ges.direction == "west" then - ges.direction = "south" - elseif ges.direction == "northeast" then - ges.direction = "northwest" - elseif ges.direction == "northwest" then - ges.direction = "southeast" - elseif ges.direction == "southeast" then - ges.direction = "northeast" - elseif ges.direction == "southwest" then - ges.direction = "southeast" - end - if ges.relative then - ges.relative.x, ges.relative.y = ges.relative.y, -ges.relative.x - end - elseif ges.ges == "pinch" or ges.ges == "spread" - or ges.ges == "inward_pan" - or ges.ges == "outward_pan" then - if ges.direction == "horizontal" then - ges.direction = "vertical" - elseif ges.direction == "vertical" then - ges.direction = "horizontal" - end - end - end - return ges + if Screen.cur_rotation_mode == 1 then + -- in landscape mode rotated 270 + if ges.pos then + ges.pos.x, ges.pos.y = (Screen:getWidth() - ges.pos.y), (ges.pos.x) + end + if ges.ges == "swipe" or ges.ges == "pan" + or ges.ges == "two_finger_swipe" + or ges.ges == "two_finger_pan" then + if ges.direction == "north" then + ges.direction = "east" + elseif ges.direction == "south" then + ges.direction = "west" + elseif ges.direction == "east" then + ges.direction = "south" + elseif ges.direction == "west" then + ges.direction = "north" + elseif ges.direction == "northeast" then + ges.direction = "southeast" + elseif ges.direction == "northwest" then + ges.direction = "northeast" + elseif ges.direction == "southeast" then + ges.direction = "southwest" + elseif ges.direction == "southwest" then + ges.direction = "northwest" + end + if ges.relative then + ges.relative.x, ges.relative.y = -ges.relative.y, ges.relative.x + end + elseif ges.ges == "pinch" or ges.ges == "spread" + or ges.ges == "inward_pan" + or ges.ges == "outward_pan" then + if ges.direction == "horizontal" then + ges.direction = "vertical" + elseif ges.direction == "vertical" then + ges.direction = "horizontal" + end + end + elseif Screen.cur_rotation_mode == 3 then + -- in landscape mode rotated 90 + if ges.pos then + ges.pos.x, ges.pos.y = (ges.pos.y), (Screen:getHeight() - ges.pos.x) + end + if ges.ges == "swipe" or ges.ges == "pan" + or ges.ges == "two_finger_swipe" + or ges.ges == "two_finger_pan" then + if ges.direction == "north" then + ges.direction = "west" + elseif ges.direction == "south" then + ges.direction = "east" + elseif ges.direction == "east" then + ges.direction = "north" + elseif ges.direction == "west" then + ges.direction = "south" + elseif ges.direction == "northeast" then + ges.direction = "northwest" + elseif ges.direction == "northwest" then + ges.direction = "southeast" + elseif ges.direction == "southeast" then + ges.direction = "northeast" + elseif ges.direction == "southwest" then + ges.direction = "southeast" + end + if ges.relative then + ges.relative.x, ges.relative.y = ges.relative.y, -ges.relative.x + end + elseif ges.ges == "pinch" or ges.ges == "spread" + or ges.ges == "inward_pan" + or ges.ges == "outward_pan" then + if ges.direction == "horizontal" then + ges.direction = "vertical" + elseif ges.direction == "vertical" then + ges.direction = "horizontal" + end + end + end + return ges end return GestureDetector diff --git a/frontend/ui/gesturerange.lua b/frontend/ui/gesturerange.lua index ed5ae7ab9..b1c1ca092 100644 --- a/frontend/ui/gesturerange.lua +++ b/frontend/ui/gesturerange.lua @@ -1,50 +1,50 @@ local TimeVal = require("ui/timeval") local GestureRange = { - ges = nil, - -- spatial range limits the gesture emitting position - range = nil, - -- temproal range limits the gesture emitting rate - rate = nil, - -- span limits of this gesture - scale = nil, + ges = nil, + -- spatial range limits the gesture emitting position + range = nil, + -- temproal range limits the gesture emitting rate + rate = nil, + -- span limits of this gesture + scale = nil, } function GestureRange:new(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + return o end function GestureRange:match(gs) - if gs.ges ~= self.ges then - return false - end - if self.range then - if not self.range:contains(gs.pos) then - return false - end - end - if self.rate then - local last_time = self.last_time or TimeVal:new{} - if gs.time - last_time > TimeVal:new{usec = 1000000 / self.rate} then - self.last_time = gs.time - else - return false - end - end - if self.scale then - if self.scale[1] > gs.span or self.scale[2] < gs.span then - return false - end - end - if self.direction then - if self.direction ~= gs.direction then - return false - end - end - return true + if gs.ges ~= self.ges then + return false + end + if self.range then + if not self.range:contains(gs.pos) then + return false + end + end + if self.rate then + local last_time = self.last_time or TimeVal:new{} + if gs.time - last_time > TimeVal:new{usec = 1000000 / self.rate} then + self.last_time = gs.time + else + return false + end + end + if self.scale then + if self.scale[1] > gs.span or self.scale[2] < gs.span then + return false + end + end + if self.direction then + if self.direction ~= gs.direction then + return false + end + end + return true end return GestureRange diff --git a/frontend/ui/input.lua b/frontend/ui/input.lua index ba229bf07..9a48c4190 100644 --- a/frontend/ui/input.lua +++ b/frontend/ui/input.lua @@ -45,23 +45,23 @@ an interface for key presses local Key = {} function Key:new(key, modifiers) - local o = { key = key, modifiers = modifiers } - - -- we're a hash map, too - o[key] = true - for mod, pressed in pairs(modifiers) do - if pressed then - o[mod] = true - end - end - - setmetatable(o, self) - self.__index = self - return o + local o = { key = key, modifiers = modifiers } + + -- we're a hash map, too + o[key] = true + for mod, pressed in pairs(modifiers) do + if pressed then + o[mod] = true + end + end + + setmetatable(o, self) + self.__index = self + return o end function Key:__tostring() - return table.concat(self:getSequence(), "-") + return table.concat(self:getSequence(), "-") end --[[ @@ -72,13 +72,13 @@ store this as configuration data (configurable shortcuts) ]] function Key:getSequence() - local seq = {} - for mod, pressed in pairs(self.modifiers) do - if pressed then - table.insert(seq, mod) - end - end - table.insert(seq, self.key) + local seq = {} + for mod, pressed in pairs(self.modifiers) do + if pressed then + table.insert(seq, mod) + end + end + table.insert(seq, self.key) end --[[ @@ -95,317 +95,317 @@ Key:match({ "Alt", "K" }) -- match Alt-K Key:match({ "Alt", { "K", "L" }}) -- match Alt-K _or_ Alt-L ]] function Key:match(sequence) - local mod_keys = {} -- a hash table for checked modifiers - for _, key in ipairs(sequence) do - if type(key) == "table" then - local found = false - for _, variant in ipairs(key) do - if self[variant] then - found = true - break - end - end - if not found then - -- one of the needed keys is not pressed - return false - end - elseif not self[key] then - -- needed key not pressed - return false - elseif self.modifiers[key] ~= nil then - -- checked key is a modifier key - mod_keys[key] = true - end - end - - for mod, pressed in pairs(self.modifiers) do - if pressed and not mod_keys[mod] then - -- additional modifier keys are pressed, don't match - return false - end - end - - return true + local mod_keys = {} -- a hash table for checked modifiers + for _, key in ipairs(sequence) do + if type(key) == "table" then + local found = false + for _, variant in ipairs(key) do + if self[variant] then + found = true + break + end + end + if not found then + -- one of the needed keys is not pressed + return false + end + elseif not self[key] then + -- needed key not pressed + return false + elseif self.modifiers[key] ~= nil then + -- checked key is a modifier key + mod_keys[key] = true + end + end + + for mod, pressed in pairs(self.modifiers) do + if pressed and not mod_keys[mod] then + -- additional modifier keys are pressed, don't match + return false + end + end + + return true end --[[ an interface to get input events ]] local Input = { - event_map = {}, - modifiers = {}, - rotation_map = { - [0] = {}, - [1] = { Up = "Right", Right = "Down", Down = "Left", Left = "Up" }, - [2] = { Up = "Down", Right = "Left", Down = "Up", Left = "Right" }, - [3] = { Up = "Left", Right = "Up", Down = "Right", Left = "Down" } - }, - timer_callbacks = {}, - disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP, + event_map = {}, + modifiers = {}, + rotation_map = { + [0] = {}, + [1] = { Up = "Right", Right = "Down", Down = "Left", Left = "Up" }, + [2] = { Up = "Down", Right = "Left", Down = "Up", Left = "Right" }, + [3] = { Up = "Left", Right = "Up", Down = "Right", Left = "Down" } + }, + timer_callbacks = {}, + disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP, } function Input:initKeyMap() - self.event_map = { - [2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5", [7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0", - [16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T", [21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P", - [30] = "A", [31] = "S", [32] = "D", [33] = "F", [34] = "G", [35] = "H", [36] = "J", [37] = "K", [38] = "L", [14] = "Del", - [44] = "Z", [45] = "X", [46] = "C", [47] = "V", [48] = "B", [49] = "N", [50] = "M", [52] = ".", [53] = "/", -- only KDX - - [28] = "Enter", - [29] = "ScreenKB", -- K[4] - [42] = "Shift", - [56] = "Alt", - [57] = " ", - [90] = "AA", -- KDX - [91] = "Back", -- KDX - [92] = "Press", -- KDX - [94] = "Sym", -- KDX - [98] = "Home", -- KDX - [102] = "Home", -- K[3] & k[4] - [104] = "LPgBack", -- K[3] only - [103] = "Up", -- K[3] & k[4] - [105] = "Left", - [106] = "Right", - [108] = "Down", -- K[3] & k[4] - [109] = "RPgBack", - [114] = "VMinus", - [115] = "VPlus", - [122] = "Up", -- KDX - [123] = "Down", -- KDX - [124] = "RPgFwd", -- KDX - [126] = "Sym", -- K[3] - [139] = "Menu", - [158] = "Back", -- K[3] & K[4] - [190] = "AA", -- K[3] - [191] = "RPgFwd", -- K[3] & k[4] - [193] = "LPgFwd", -- K[3] only - [194] = "Press", -- K[3] & k[4] - } - self.sdl_event_map = { - [10] = "1", [11] = "2", [12] = "3", [13] = "4", [14] = "5", [15] = "6", [16] = "7", [17] = "8", [18] = "9", [19] = "0", - [24] = "Q", [25] = "W", [26] = "E", [27] = "R", [28] = "T", [29] = "Y", [30] = "U", [31] = "I", [32] = "O", [33] = "P", - [38] = "A", [39] = "S", [40] = "D", [41] = "F", [42] = "G", [43] = "H", [44] = "J", [45] = "K", [46] = "L", - [52] = "Z", [53] = "X", [54] = "C", [55] = "V", [56] = "B", [57] = "N", [58] = "M", - - [22] = "Back", -- Backspace - [36] = "Enter", -- Enter - [50] = "Shift", -- left shift - [60] = ".", - [61] = "/", - [62] = "Sym", -- right shift key - [64] = "Alt", -- left alt - [65] = " ", -- Spacebar - [67] = "Menu", -- F[1] - [72] = "LPgBack", -- F[6] - [73] = "LPgFwd", -- F[7] - [95] = "VPlus", -- F[11] - [96] = "VMinus", -- F[12] - [105] = "AA", -- right alt key - [110] = "Home", -- Home - [111] = "Up", -- arrow up - [112] = "RPgBack", -- normal PageUp - [113] = "Left", -- arrow left - [114] = "Right", -- arrow right - [115] = "Press", -- End (above arrows) - [116] = "Down", -- arrow down - [117] = "RPgFwd", -- normal PageDown - [119] = "Del", -- Delete - } - self.modifiers = { - Alt = false, - Shift = false - } - -- these groups are just helpers: - self.group = { - Cursor = { "Up", "Down", "Left", "Right" }, - PgFwd = { "RPgFwd", "LPgFwd" }, - PgBack = { "RPgBack", "LPgBack" }, - Alphabet = { - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" - }, - AlphaNumeric = { - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" - }, - Numeric = { - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" - }, - Text = { - " ", ".", "/", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" - }, - Any = { - " ", ".", "/", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "Up", "Down", "Left", "Right", "Press", - "Back", "Enter", "Sym", "AA", "Menu", "Home", "Del", - "LPgBack", "RPgBack", "LPgFwd", "RPgFwd" - } - } + self.event_map = { + [2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5", [7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0", + [16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T", [21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P", + [30] = "A", [31] = "S", [32] = "D", [33] = "F", [34] = "G", [35] = "H", [36] = "J", [37] = "K", [38] = "L", [14] = "Del", + [44] = "Z", [45] = "X", [46] = "C", [47] = "V", [48] = "B", [49] = "N", [50] = "M", [52] = ".", [53] = "/", -- only KDX + + [28] = "Enter", + [29] = "ScreenKB", -- K[4] + [42] = "Shift", + [56] = "Alt", + [57] = " ", + [90] = "AA", -- KDX + [91] = "Back", -- KDX + [92] = "Press", -- KDX + [94] = "Sym", -- KDX + [98] = "Home", -- KDX + [102] = "Home", -- K[3] & k[4] + [104] = "LPgBack", -- K[3] only + [103] = "Up", -- K[3] & k[4] + [105] = "Left", + [106] = "Right", + [108] = "Down", -- K[3] & k[4] + [109] = "RPgBack", + [114] = "VMinus", + [115] = "VPlus", + [122] = "Up", -- KDX + [123] = "Down", -- KDX + [124] = "RPgFwd", -- KDX + [126] = "Sym", -- K[3] + [139] = "Menu", + [158] = "Back", -- K[3] & K[4] + [190] = "AA", -- K[3] + [191] = "RPgFwd", -- K[3] & k[4] + [193] = "LPgFwd", -- K[3] only + [194] = "Press", -- K[3] & k[4] + } + self.sdl_event_map = { + [10] = "1", [11] = "2", [12] = "3", [13] = "4", [14] = "5", [15] = "6", [16] = "7", [17] = "8", [18] = "9", [19] = "0", + [24] = "Q", [25] = "W", [26] = "E", [27] = "R", [28] = "T", [29] = "Y", [30] = "U", [31] = "I", [32] = "O", [33] = "P", + [38] = "A", [39] = "S", [40] = "D", [41] = "F", [42] = "G", [43] = "H", [44] = "J", [45] = "K", [46] = "L", + [52] = "Z", [53] = "X", [54] = "C", [55] = "V", [56] = "B", [57] = "N", [58] = "M", + + [22] = "Back", -- Backspace + [36] = "Enter", -- Enter + [50] = "Shift", -- left shift + [60] = ".", + [61] = "/", + [62] = "Sym", -- right shift key + [64] = "Alt", -- left alt + [65] = " ", -- Spacebar + [67] = "Menu", -- F[1] + [72] = "LPgBack", -- F[6] + [73] = "LPgFwd", -- F[7] + [95] = "VPlus", -- F[11] + [96] = "VMinus", -- F[12] + [105] = "AA", -- right alt key + [110] = "Home", -- Home + [111] = "Up", -- arrow up + [112] = "RPgBack", -- normal PageUp + [113] = "Left", -- arrow left + [114] = "Right", -- arrow right + [115] = "Press", -- End (above arrows) + [116] = "Down", -- arrow down + [117] = "RPgFwd", -- normal PageDown + [119] = "Del", -- Delete + } + self.modifiers = { + Alt = false, + Shift = false + } + -- these groups are just helpers: + self.group = { + Cursor = { "Up", "Down", "Left", "Right" }, + PgFwd = { "RPgFwd", "LPgFwd" }, + PgBack = { "RPgBack", "LPgBack" }, + Alphabet = { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + }, + AlphaNumeric = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + }, + Numeric = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" + }, + Text = { + " ", ".", "/", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + }, + Any = { + " ", ".", "/", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "Up", "Down", "Left", "Right", "Press", + "Back", "Enter", "Sym", "AA", "Menu", "Home", "Del", + "LPgBack", "RPgBack", "LPgFwd", "RPgFwd" + } + } end function Input:initTouchState() - self.cur_slot = 0 - self.MTSlots = {} - self.ev_slots = { - [0] = { - slot = 0, - } - } + self.cur_slot = 0 + self.MTSlots = {} + self.ev_slots = { + [0] = { + slot = 0, + } + } end function Input:init() - if Device:hasKeyboard() then - self:initKeyMap() - end - if Device:isTouchDevice() then - self:initTouchState() - end - -- set up fake event map - self.event_map[10000] = "IntoSS" -- go into screen saver - self.event_map[10001] = "OutOfSS" -- go out of screen saver - self.event_map[10020] = "Charging" - self.event_map[10021] = "NotCharging" - - if util.isEmulated() then - self:initKeyMap() - os.remove("/tmp/emu_event") - os.execute("mkfifo /tmp/emu_event") - input.open("/tmp/emu_event") - -- SDL key codes - self.event_map = self.sdl_event_map - else - local dev_mod = Device:getModel() - if not Device:isKobo() then - input.open("fake_events") - end - if dev_mod == "KindlePaperWhite" then - print(_("Auto-detected Kindle PaperWhite")) - Device:setTouchInputDev("/dev/input/event0") - input.open("/dev/input/event0") - elseif dev_mod == "KindlePaperWhite2" then - print(_("Auto-detected Kindle PaperWhite")) - Device:setTouchInputDev("/dev/input/event1") - input.open("/dev/input/event1") - elseif dev_mod == "KindleTouch" then - -- event0 in KindleTouch is "WM8962 Beep Generator" (useless) - -- event1 in KindleTouch is "imx-yoshi Headset" (useless) - Device:setTouchInputDev("/dev/input/event3") - input.open("/dev/input/event2") -- Home button - input.open("/dev/input/event3") -- touchscreen - -- KT does have one key! - self.event_map[102] = "Home" - -- update event hook - function Input:eventAdjustHook(ev) - if ev.type == EV_ABS then - --@TODO handle coordinates properly after - --screen rotate. (houqp) - if ev.code == ABS_MT_POSITION_X then - ev.value = Math.round(ev.value * (600/4095)) - elseif ev.code == ABS_MT_POSITION_Y then - ev.value = Math.round(ev.value * (800/4095)) - end - end - return ev - end - print(_("Auto-detected Kindle Touch")) - elseif Device:isKobo() then - local firm_rev = Device:getFirmVer() - input.open("/dev/input/event1") - Device:setTouchInputDev("/dev/input/event1") - input.open("/dev/input/event0") -- Light button and sleep slider - print(_("Auto-detected Kobo")) - print(_("Device model=")) - print(_(dev_mod)) - print(_("Firmware revision")) - print(_(firm_rev)) - print(_("Screen height =")) - print(_(Screen:getHeight())) - print(_("Screen width =")) - print(_(Screen:getWidth())) - self:adjustKoboEventMap() - if dev_mod ~= 'Kobo_trilogy' then - function Input:eventAdjustHook(ev) - if ev.type == EV_ABS then - if ev.code == ABS_X then - ev.code = ABS_Y - elseif ev.code == ABS_Y then - ev.code = ABS_X - -- We always have to substract from the physical x, - -- regardless of the orientation - if (Screen:getWidth() 0 then - -- we don't block if there is any timer, set wait to 10us - while #self.timer_callbacks > 0 do - ok, ev = pcall(input.waitForEvent, 100) - if ok then break end - local tv_now = TimeVal:now() - if ((not timeout_us and not timeout_s) or tv_now < wait_deadline) then - -- check whether timer is up - if tv_now >= self.timer_callbacks[1].deadline then - local touch_ges = self.timer_callbacks[1].callback() - table.remove(self.timer_callbacks, 1) - if touch_ges then - -- Do we really need to clear all setTimeout after - -- decided a gesture? FIXME - Input.timer_callbacks = {} - return Event:new("Gesture", - GestureDetector:adjustGesCoordinate(touch_ges) - ) - end -- EOF if touch_ges - end -- EOF if deadline reached - else - break - end -- EOF if not exceed wait timeout - end -- while #timer_callbacks > 0 - else - ok, ev = pcall(input.waitForEvent, timeout_us) - end -- EOF if #timer_callbacks > 0 - if ok then - break - end - if ev == "Waiting for input failed: timeout\n" then - -- don't report an error on timeout - ev = nil - break - elseif ev == "application forced to quit" then - os.exit(0) - end - --DEBUG("got error waiting for events:", ev) - if ev ~= "Waiting for input failed: 4\n" then - -- we only abort if the error is not EINTR - break - end - end - - if ok and ev then - if Dbg.is_on and ev then - Dbg:logEv(ev) - end - ev = self:eventAdjustHook(ev) - if ev.type == EV_KEY then - return self:handleKeyBoardEv(ev) - elseif ev.type == EV_ABS or ev.type == EV_SYN then - return self:handleTouchEv(ev) - else - -- some other kind of event that we do not know yet - return Event:new("GenericInput", ev) - end - elseif not ok and ev then - return Event:new("InputError", ev) - end + -- wrapper for input.waitForEvents that will retry for some cases + local ok, ev + local wait_deadline = TimeVal:now() + TimeVal:new{ + sec = timeout_s, + usec = timeout_us + } + while true do + if #self.timer_callbacks > 0 then + -- we don't block if there is any timer, set wait to 10us + while #self.timer_callbacks > 0 do + ok, ev = pcall(input.waitForEvent, 100) + if ok then break end + local tv_now = TimeVal:now() + if ((not timeout_us and not timeout_s) or tv_now < wait_deadline) then + -- check whether timer is up + if tv_now >= self.timer_callbacks[1].deadline then + local touch_ges = self.timer_callbacks[1].callback() + table.remove(self.timer_callbacks, 1) + if touch_ges then + -- Do we really need to clear all setTimeout after + -- decided a gesture? FIXME + Input.timer_callbacks = {} + return Event:new("Gesture", + GestureDetector:adjustGesCoordinate(touch_ges) + ) + end -- EOF if touch_ges + end -- EOF if deadline reached + else + break + end -- EOF if not exceed wait timeout + end -- while #timer_callbacks > 0 + else + ok, ev = pcall(input.waitForEvent, timeout_us) + end -- EOF if #timer_callbacks > 0 + if ok then + break + end + if ev == "Waiting for input failed: timeout\n" then + -- don't report an error on timeout + ev = nil + break + elseif ev == "application forced to quit" then + os.exit(0) + end + --DEBUG("got error waiting for events:", ev) + if ev ~= "Waiting for input failed: 4\n" then + -- we only abort if the error is not EINTR + break + end + end + + if ok and ev then + if Dbg.is_on and ev then + Dbg:logEv(ev) + end + ev = self:eventAdjustHook(ev) + if ev.type == EV_KEY then + return self:handleKeyBoardEv(ev) + elseif ev.type == EV_ABS or ev.type == EV_SYN then + return self:handleTouchEv(ev) + else + -- some other kind of event that we do not know yet + return Event:new("GenericInput", ev) + end + elseif not ok and ev then + return Event:new("InputError", ev) + end end --[[ helper function for formatting sequence definitions for output ]] function Input:sequenceToString(sequence) - local modifiers = {} - local keystring = {"",""} -- first entries reserved for modifier specification - for _, key in ipairs(sequence) do - if type(key) == "table" then - local alternatives = {} - for _, alternative in ipairs(key) do - table.insert(alternatives, alternative) - end - table.insert(keystring, "{") - table.insert(keystring, table.concat(alternatives, "|")) - table.insert(keystring, "}") - elseif self.modifiers[key] ~= nil then - table.insert(modifiers, key) - else - table.insert(keystring, key) - end - end - if #modifiers then - keystring[1] = table.concat(modifiers, "-") - keystring[2] = "-" - end - return table.concat(keystring) + local modifiers = {} + local keystring = {"",""} -- first entries reserved for modifier specification + for _, key in ipairs(sequence) do + if type(key) == "table" then + local alternatives = {} + for _, alternative in ipairs(key) do + table.insert(alternatives, alternative) + end + table.insert(keystring, "{") + table.insert(keystring, table.concat(alternatives, "|")) + table.insert(keystring, "}") + elseif self.modifiers[key] ~= nil then + table.insert(modifiers, key) + else + table.insert(keystring, key) + end + end + if #modifiers then + keystring[1] = table.concat(modifiers, "-") + keystring[2] = "-" + end + return table.concat(keystring) end -- initialize the GestureDectector diff --git a/frontend/ui/language.lua b/frontend/ui/language.lua index 812361d2a..d9a625d16 100644 --- a/frontend/ui/language.lua +++ b/frontend/ui/language.lua @@ -7,47 +7,47 @@ local _ = require("gettext") Language = {} function Language:changeLanguage(lang_locale) - _.changeLang(lang_locale) - G_reader_settings:saveSetting("language", lang_locale) - UIManager:show(InfoMessage:new{ - text = _("Please restart reader for new language setting to take effect."), - timeout = 3, - }) + _.changeLang(lang_locale) + G_reader_settings:saveSetting("language", lang_locale) + UIManager:show(InfoMessage:new{ + text = _("Please restart reader for new language setting to take effect."), + timeout = 3, + }) end function Language:genLanguageSubItem(lang, lang_locale) - return { - text = lang, - callback = function() - self:changeLanguage(lang_locale) - end - } + return { + text = lang, + callback = function() + self:changeLanguage(lang_locale) + end + } end function Language:getLangMenuTable() - -- cache menu table - if not self.LangMenuTable then - self.LangMenuTable = { - text = _("Language"), - -- NOTE: language with no translation are commented out for now - sub_item_table = { - self:genLanguageSubItem("English", "C"), - self:genLanguageSubItem("čeština", "cs_CZ"), - self:genLanguageSubItem("Deutsch", "de"), - self:genLanguageSubItem("français", "fr"), - --self:genLanguageSubItem("magyar", "hu"), - self:genLanguageSubItem("Italiano", "it_IT"), - self:genLanguageSubItem("Polski", "pl"), - self:genLanguageSubItem("Português do Brasil", "pt_BR"), - self:genLanguageSubItem("Русский язык", "ru"), - --self:genLanguageSubItem("svenska", "sv"), - self:genLanguageSubItem("Türkçe", "tr"), - --self:genLanguageSubItem("Tiếng Việt", "vi"), - self:genLanguageSubItem("简体中文", "zh_CN"), - } - } - end - return self.LangMenuTable + -- cache menu table + if not self.LangMenuTable then + self.LangMenuTable = { + text = _("Language"), + -- NOTE: language with no translation are commented out for now + sub_item_table = { + self:genLanguageSubItem("English", "C"), + self:genLanguageSubItem("čeština", "cs_CZ"), + self:genLanguageSubItem("Deutsch", "de"), + self:genLanguageSubItem("français", "fr"), + --self:genLanguageSubItem("magyar", "hu"), + self:genLanguageSubItem("Italiano", "it_IT"), + self:genLanguageSubItem("Polski", "pl"), + self:genLanguageSubItem("Português do Brasil", "pt_BR"), + self:genLanguageSubItem("Русский язык", "ru"), + --self:genLanguageSubItem("svenska", "sv"), + self:genLanguageSubItem("Türkçe", "tr"), + --self:genLanguageSubItem("Tiếng Việt", "vi"), + self:genLanguageSubItem("简体中文", "zh_CN"), + } + } + end + return self.LangMenuTable end return Language diff --git a/frontend/ui/reader/configurable.lua b/frontend/ui/reader/configurable.lua index 6fac977e7..2b91e3160 100644 --- a/frontend/ui/reader/configurable.lua +++ b/frontend/ui/reader/configurable.lua @@ -1,49 +1,66 @@ local Configurable = {} +function Configurable:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +function Configurable:reset() + for key,value in pairs(self) do + if type(value) == "number" or type(value) == "string" then + self[key] = nil + end + end +end + function Configurable:hash(sep) - local hash = "" - local excluded = {multi_threads = true,} - for key,value in pairs(self) do - if type(value) == "number" or type(value) == "string" - and not excluded[key] then - hash = hash..sep..value - end - end - return hash + local hash = "" + local excluded = {multi_threads = true,} + for key,value in pairs(self) do + if type(value) == "number" or type(value) == "string" then + hash = hash..sep..value + end + end + return hash end function Configurable:loadDefaults(config_options) - for i=1,#config_options do - local options = config_options[i].options - for j=1,#config_options[i].options do - local key = config_options[i].options[j].name - self[key] = config_options[i].options[j].default_value - if not self[key] then - self[key] = config_options[i].options[j].default_arg - end - end - end + -- reset configurable before loading new options + self:reset() + for i=1,#config_options do + local options = config_options[i].options + for j=1,#config_options[i].options do + local key = config_options[i].options[j].name + self[key] = config_options[i].options[j].default_value + if not self[key] then + self[key] = config_options[i].options[j].default_arg + end + end + end end function Configurable:loadSettings(settings, prefix) - for key,value in pairs(self) do - if type(value) == "number" or type(value) == "string" - or type(value) == "table" then - local saved_value = settings:readSetting(prefix..key) - self[key] = (saved_value == nil) and self[key] or saved_value - --Debug("Configurable:loadSettings", "key", key, "saved value", saved_value,"Configurable.key", self[key]) - end - end - --Debug("loaded config:", dump(Configurable)) + for key,value in pairs(self) do + if type(value) == "number" or type(value) == "string" + or type(value) == "table" then + local saved_value = settings:readSetting(prefix..key) + self[key] = (saved_value == nil) and self[key] or saved_value + --Debug("Configurable:loadSettings", "key", key, "saved value", + --saved_value,"Configurable.key", self[key]) + end + end + --Debug("loaded config:", dump(Configurable)) end function Configurable:saveSettings(settings, prefix) - for key,value in pairs(self) do - if type(value) == "number" or type(value) == "string" - or type(value) == "table" then - settings:saveSetting(prefix..key, value) - end - end + for key,value in pairs(self) do + if type(value) == "number" or type(value) == "string" + or type(value) == "table" then + settings:saveSetting(prefix..key, value) + end + end end return Configurable diff --git a/frontend/ui/reader/readeractivityindicator.lua b/frontend/ui/reader/readeractivityindicator.lua index 14ca25b33..976aafad7 100644 --- a/frontend/ui/reader/readeractivityindicator.lua +++ b/frontend/ui/reader/readeractivityindicator.lua @@ -5,43 +5,43 @@ local Device = require("ui/device") local ReaderActivityIndicator = EventListener:new{} function ReaderActivityIndicator:init() - local dev_mod = Device:getModel() - if dev_mod == "KindlePaperWhite" or dev_mod == "KindlePaperWhite2" or dev_mod == "KindleTouch" then - require "liblipclua" - self.lipc_handle = lipc.init("com.github.koreader.activityindicator") - end + local dev_mod = Device:getModel() + if dev_mod == "KindlePaperWhite" or dev_mod == "KindlePaperWhite2" or dev_mod == "KindleTouch" then + require "liblipclua" + self.lipc_handle = lipc.init("com.github.koreader.activityindicator") + end end -function ReaderActivityIndicator:onStartActivityIndicator() - if self.lipc_handle then - -- check if activity indicator is needed - if self.document.configurable.text_wrap == 1 then - -- start indicator depends on pillow being enabled - self.lipc_handle:set_string_property( - "com.lab126.pillow", "activityIndicator", - '{"activityIndicator":{ \ - "action":"start","timeout":10000, \ - "clientId":"com.github.koreader.activityindicator", \ - "priority":true}}') - self.indicator_started = true - end - end - return true +function ReaderActivityIndicator:onStartActivityIndicator() + if self.lipc_handle then + -- check if activity indicator is needed + if self.document.configurable.text_wrap == 1 then + -- start indicator depends on pillow being enabled + self.lipc_handle:set_string_property( + "com.lab126.pillow", "activityIndicator", + '{"activityIndicator":{ \ + "action":"start","timeout":10000, \ + "clientId":"com.github.koreader.activityindicator", \ + "priority":true}}') + self.indicator_started = true + end + end + return true end function ReaderActivityIndicator:onStopActivityIndicator() - if self.lipc_handle and self.indicator_started then - -- stop indicator depends on pillow being enabled - self.lipc_handle:set_string_property( - "com.lab126.pillow", "activityIndicator", - '{"activityIndicator":{ \ - "action":"stop","timeout":10000, \ - "clientId":"com.github.koreader.activityindicator", \ - "priority":true}}') - self.indicator_started = false - util.usleep(1000000) - end - return true + if self.lipc_handle and self.indicator_started then + -- stop indicator depends on pillow being enabled + self.lipc_handle:set_string_property( + "com.lab126.pillow", "activityIndicator", + '{"activityIndicator":{ \ + "action":"stop","timeout":10000, \ + "clientId":"com.github.koreader.activityindicator", \ + "priority":true}}') + self.indicator_started = false + util.usleep(1000000) + end + return true end return ReaderActivityIndicator diff --git a/frontend/ui/reader/readerbookmark.lua b/frontend/ui/reader/readerbookmark.lua index 56132babd..ec491042e 100644 --- a/frontend/ui/reader/readerbookmark.lua +++ b/frontend/ui/reader/readerbookmark.lua @@ -11,218 +11,218 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderBookmark = InputContainer:new{ - bm_menu_title = _("Bookmarks"), - bookmarks = nil, + bm_menu_title = _("Bookmarks"), + bookmarks = nil, } function ReaderBookmark:init() - if Device:hasKeyboard() then - self.key_events = { - ShowBookmark = { - { "B" }, - doc = _("show bookmarks") }, - } - end - if Device:isTouchDevice() then - self.ges_events = { - ShowBookmark = { - GestureRange:new{ - ges = "two_finger_swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - }, - direction = "west" - } - }, - } - end - self.ui.menu:registerToMainMenu(self) + if Device:hasKeyboard() then + self.key_events = { + ShowBookmark = { + { "B" }, + doc = _("show bookmarks") }, + } + end + if Device:isTouchDevice() then + self.ges_events = { + ShowBookmark = { + GestureRange:new{ + ges = "two_finger_swipe", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + }, + direction = "west" + } + }, + } + end + self.ui.menu:registerToMainMenu(self) end function ReaderBookmark:onReadSettings(config) - self.bookmarks = config:readSetting("bookmarks") or {} + self.bookmarks = config:readSetting("bookmarks") or {} end function ReaderBookmark:onSaveSettings() - self.ui.doc_settings:saveSetting("bookmarks", self.bookmarks) + self.ui.doc_settings:saveSetting("bookmarks", self.bookmarks) end function ReaderBookmark:onToggleBookmark() - local pn_or_xp = nil - if self.ui.document.info.has_pages then - pn_or_xp = self.view.state.page - else - pn_or_xp = self.ui.document:getXPointer() - end - self:toggleBookmark(pn_or_xp) - self.view.dogear_visible = not self.view.dogear_visible - UIManager:setDirty(self.view.dialog, "partial") - return true + local pn_or_xp = nil + if self.ui.document.info.has_pages then + pn_or_xp = self.view.state.page + else + pn_or_xp = self.ui.document:getXPointer() + end + self:toggleBookmark(pn_or_xp) + self.view.dogear_visible = not self.view.dogear_visible + UIManager:setDirty(self.view.dialog, "partial") + return true end function ReaderBookmark:setDogearVisibility(pn_or_xp) - if self:isBookmarked(pn_or_xp) then - self.ui:handleEvent(Event:new("SetDogearVisibility", true)) - else - self.ui:handleEvent(Event:new("SetDogearVisibility", false)) - end + if self:isBookmarked(pn_or_xp) then + self.ui:handleEvent(Event:new("SetDogearVisibility", true)) + else + self.ui:handleEvent(Event:new("SetDogearVisibility", false)) + end end function ReaderBookmark:onPageUpdate(pageno) - if self.ui.document.info.has_pages then - self:setDogearVisibility(pageno) - else - -- FIXME: this is a dirty hack to prevent crash in isXPointerInCurrentPage - if pageno ~= 1 then - self:setDogearVisibility("dummy") - end - end + if self.ui.document.info.has_pages then + self:setDogearVisibility(pageno) + else + -- FIXME: this is a dirty hack to prevent crash in isXPointerInCurrentPage + if pageno ~= 1 then + self:setDogearVisibility("dummy") + end + end end function ReaderBookmark:onPosUpdate(pos) - self:setDogearVisibility("dummy") + self:setDogearVisibility("dummy") end function ReaderBookmark:onShowBookmark() - -- build up item_table - for k, v in ipairs(self.bookmarks) do - local page = v.page - -- for CREngine, bookmark page is xpointer - if type(page) == "string" then - page = self.ui.document:getPageFromXPointer(v.page) - end - v.text = "Page "..page.." "..v.notes.." @ "..v.datetime - end - - local menu_container = CenterContainer:new{ - dimen = Screen:getSize(), - } - - local bm_menu = Menu:new{ - title = "Bookmarks", - item_table = self.bookmarks, - width = Screen:getWidth()-50, - height = Screen:getHeight()-50, - show_parent = menu_container, - } - - table.insert(menu_container, bm_menu) - - -- buid up menu widget method as closure - local doc = self.ui.document - local view = self.view - local sendEv = function(ev) - self.ui:handleEvent(ev) - end - function bm_menu:onMenuChoice(item) - if doc.info.has_pages then - sendEv(Event:new("PageUpdate", item.page)) - elseif view.view_mode == "page" then - sendEv(Event:new("PageUpdate", doc:getPageFromXPointer(item.page))) - else - sendEv(Event:new("PosUpdate", doc:getPosFromXPointer(item.page))) - end - end - - bm_menu.close_callback = function() - UIManager:close(menu_container) - end - - UIManager:show(menu_container) - return true + -- build up item_table + for k, v in ipairs(self.bookmarks) do + local page = v.page + -- for CREngine, bookmark page is xpointer + if type(page) == "string" then + page = self.ui.document:getPageFromXPointer(v.page) + end + v.text = "Page "..page.." "..v.notes.." @ "..v.datetime + end + + local menu_container = CenterContainer:new{ + dimen = Screen:getSize(), + } + + local bm_menu = Menu:new{ + title = "Bookmarks", + item_table = self.bookmarks, + width = Screen:getWidth()-50, + height = Screen:getHeight()-50, + show_parent = menu_container, + } + + table.insert(menu_container, bm_menu) + + -- buid up menu widget method as closure + local doc = self.ui.document + local view = self.view + local sendEv = function(ev) + self.ui:handleEvent(ev) + end + function bm_menu:onMenuChoice(item) + if doc.info.has_pages then + sendEv(Event:new("PageUpdate", item.page)) + elseif view.view_mode == "page" then + sendEv(Event:new("PageUpdate", doc:getPageFromXPointer(item.page))) + else + sendEv(Event:new("PosUpdate", doc:getPosFromXPointer(item.page))) + end + end + + bm_menu.close_callback = function() + UIManager:close(menu_container) + end + + UIManager:show(menu_container) + return true end function ReaderBookmark:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.navi, { - text = self.bm_menu_title, - callback = function() - self:onShowBookmark() - end, - }) + -- insert table to main reader menu + table.insert(tab_item_table.navi, { + text = self.bm_menu_title, + callback = function() + self:onShowBookmark() + end, + }) end function ReaderBookmark:isBookmarked(pn_or_xp) - for k,v in ipairs(self.bookmarks) do - if (type(pn_or_xp) == "number" and v.page == pn_or_xp) or - (type(pn_or_xp) == "string" and self.ui.document:isXPointerInCurrentPage(v.page)) then - return true - end - end - return false + for k,v in ipairs(self.bookmarks) do + if (type(pn_or_xp) == "number" and v.page == pn_or_xp) or + (type(pn_or_xp) == "string" and self.ui.document:isXPointerInCurrentPage(v.page)) then + return true + end + end + return false end function ReaderBookmark:addBookmark(pn_or_xp) - -- build notes from TOC - local notes = self.ui.toc:getTocTitleByPage(pn_or_xp) - if notes ~= "" then - notes = "in "..notes - end - mark_item = { - page = pn_or_xp, - datetime = os.date("%Y-%m-%d %H:%M:%S"), - notes = notes, - } - table.insert(self.bookmarks, mark_item) - table.sort(self.bookmarks, function(a,b) - return self:isBookmarkInSequence(a, b) - end) - return true + -- build notes from TOC + local notes = self.ui.toc:getTocTitleByPage(pn_or_xp) + if notes ~= "" then + notes = "in "..notes + end + mark_item = { + page = pn_or_xp, + datetime = os.date("%Y-%m-%d %H:%M:%S"), + notes = notes, + } + table.insert(self.bookmarks, mark_item) + table.sort(self.bookmarks, function(a,b) + return self:isBookmarkInSequence(a, b) + end) + return true end function ReaderBookmark:isBookmarkInSequence(a, b) - return a.page < b.page + return a.page < b.page end function ReaderBookmark:toggleBookmark(pn_or_xp) - for k,v in ipairs(self.bookmarks) do - if (type(pn_or_xp) == "number" and v.page == pn_or_xp) or - (type(pn_or_xp) == "string" and self.ui.document:isXPointerInCurrentPage(v.page)) then - table.remove(self.bookmarks, k) - return - end - end - self:addBookmark(pn_or_xp) + for k,v in ipairs(self.bookmarks) do + if (type(pn_or_xp) == "number" and v.page == pn_or_xp) or + (type(pn_or_xp) == "string" and self.ui.document:isXPointerInCurrentPage(v.page)) then + table.remove(self.bookmarks, k) + return + end + end + self:addBookmark(pn_or_xp) end function ReaderBookmark:getPreviousBookmarkedPage(pn_or_xp) - for i = #self.bookmarks, 1, -1 do - if pn_or_xp > self.bookmarks[i].page then - return self.bookmarks[i].page - end - end + for i = #self.bookmarks, 1, -1 do + if pn_or_xp > self.bookmarks[i].page then + return self.bookmarks[i].page + end + end end function ReaderBookmark:getNextBookmarkedPage(pn_or_xp) - for i = 1, #self.bookmarks do - if pn_or_xp < self.bookmarks[i].page then - return self.bookmarks[i].page - end - end + for i = 1, #self.bookmarks do + if pn_or_xp < self.bookmarks[i].page then + return self.bookmarks[i].page + end + end end function ReaderBookmark:onGotoPreviousBookmark(pn_or_xp) - self:GotoBookmark(self:getPreviousBookmarkedPage(pn_or_xp)) - return true + 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 + self:GotoBookmark(self:getNextBookmarkedPage(pn_or_xp)) + return true end function ReaderBookmark:GotoBookmark(pn_or_xp) - if type(pn_or_xp) == "string" then - if self.view.view_mode == "page" then - self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(pn_or_xp))) - else - self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(pn_or_xp))) - end - elseif type(pn_or_xp) == "number" then - self.ui:handleEvent(Event:new("PageUpdate", pn_or_xp)) - end + if type(pn_or_xp) == "string" then + if self.view.view_mode == "page" then + self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(pn_or_xp))) + else + self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(pn_or_xp))) + end + elseif type(pn_or_xp) == "number" then + self.ui:handleEvent(Event:new("PageUpdate", pn_or_xp)) + end end return ReaderBookmark diff --git a/frontend/ui/reader/readerconfig.lua b/frontend/ui/reader/readerconfig.lua index d4f32bbf9..92f325508 100644 --- a/frontend/ui/reader/readerconfig.lua +++ b/frontend/ui/reader/readerconfig.lua @@ -9,87 +9,87 @@ local UIManager = require("ui/uimanager") local _ = require("gettext") local ReaderConfig = InputContainer:new{ - last_panel_index = 1, + last_panel_index = 1, } function ReaderConfig:init() - if Device:hasKeyboard() then - self.key_events = { - ShowConfigMenu = { { "AA" }, doc = _("show config dialog") }, - } - end - if Device:isTouchDevice() then - self:initGesListener() - end + if Device:hasKeyboard() then + self.key_events = { + ShowConfigMenu = { { "AA" }, doc = _("show config dialog") }, + } + end + if Device:isTouchDevice() then + self:initGesListener() + end end function ReaderConfig:initGesListener() - self.ges_events = { - TapShowConfigMenu = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_CONFIG.x, - y = Screen:getHeight()*DTAP_ZONE_CONFIG.y, - w = Screen:getWidth()*DTAP_ZONE_CONFIG.w, - h = Screen:getHeight()*DTAP_ZONE_CONFIG.h, - } - } - } - } + self.ges_events = { + TapShowConfigMenu = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_CONFIG.x, + y = Screen:getHeight()*DTAP_ZONE_CONFIG.y, + w = Screen:getWidth()*DTAP_ZONE_CONFIG.w, + h = Screen:getHeight()*DTAP_ZONE_CONFIG.h, + } + } + } + } end function ReaderConfig:onShowConfigMenu() - self.config_dialog = ConfigDialog:new{ - dimen = self.dimen:copy(), - ui = self.ui, - configurable = self.configurable, - config_options = self.options, - is_always_active = true, - close_callback = function() self:onCloseCallback() end, - } - self.ui:handleEvent(Event:new("DisableHinting")) - -- show last used panel when opening config dialog - self.config_dialog:onShowConfigPanel(self.last_panel_index) - UIManager:show(self.config_dialog) + self.config_dialog = ConfigDialog:new{ + dimen = self.dimen:copy(), + ui = self.ui, + configurable = self.configurable, + config_options = self.options, + is_always_active = true, + close_callback = function() self:onCloseCallback() end, + } + self.ui:handleEvent(Event:new("DisableHinting")) + -- show last used panel when opening config dialog + self.config_dialog:onShowConfigPanel(self.last_panel_index) + UIManager:show(self.config_dialog) - return true + return true end function ReaderConfig:onTapShowConfigMenu() - self:onShowConfigMenu() - return true + self:onShowConfigMenu() + return true end function ReaderConfig:onSetDimensions(dimen) - if Device:isTouchDevice() then - self:initGesListener() - end - -- since we cannot redraw config_dialog with new size, we close - -- the old one on screen size change - if self.config_dialog then - self.config_dialog:closeDialog() - end + if Device:isTouchDevice() then + self:initGesListener() + end + -- since we cannot redraw config_dialog with new size, we close + -- the old one on screen size change + if self.config_dialog then + self.config_dialog:closeDialog() + end end function ReaderConfig:onCloseCallback() - self.last_panel_index = self.config_dialog.panel_index - self.ui:handleEvent(Event:new("RestoreHinting")) + self.last_panel_index = self.config_dialog.panel_index + self.ui:handleEvent(Event:new("RestoreHinting")) end -- event handler for readercropping function ReaderConfig:onCloseConfig() - self.config_dialog:closeDialog() + self.config_dialog:closeDialog() end function ReaderConfig:onReadSettings(config) - self.configurable:loadSettings(config, self.options.prefix.."_") - self.last_panel_index = config:readSetting("config_panel_index") or 1 + self.configurable:loadSettings(config, self.options.prefix.."_") + self.last_panel_index = config:readSetting("config_panel_index") or 1 end function ReaderConfig:onSaveSettings() - self.configurable:saveSettings(self.ui.doc_settings, self.options.prefix.."_") - self.ui.doc_settings:saveSetting("config_panel_index", self.last_panel_index) + self.configurable:saveSettings(self.ui.doc_settings, self.options.prefix.."_") + self.ui.doc_settings:saveSetting("config_panel_index", self.last_panel_index) end return ReaderConfig diff --git a/frontend/ui/reader/readercoptlistener.lua b/frontend/ui/reader/readercoptlistener.lua index 8e8bf894d..910fc00c0 100644 --- a/frontend/ui/reader/readercoptlistener.lua +++ b/frontend/ui/reader/readercoptlistener.lua @@ -4,29 +4,29 @@ local Event = require("ui/event") local ReaderCoptListener = EventListener:new{} function ReaderCoptListener:onReadSettings(config) - local embedded_css = config:readSetting("copt_embedded_css") - local toggle_embedded_css = embedded_css == 0 and false or true - table.insert(self.ui.postInitCallback, function() + local embedded_css = config:readSetting("copt_embedded_css") + local toggle_embedded_css = embedded_css == 0 and false or true + table.insert(self.ui.postInitCallback, function() self.ui:handleEvent(Event:new("ToggleEmbeddedStyleSheet", toggle_embedded_css)) end) - - local view_mode = config:readSetting("copt_view_mode") - if view_mode == 0 then - table.insert(self.ui.postInitCallback, function() - self.ui:handleEvent(Event:new("SetViewMode", "page")) - end) - elseif view_mode == 1 then - table.insert(self.ui.postInitCallback, function() - self.ui:handleEvent(Event:new("SetViewMode", "scroll")) - end) - end - - local status_line = config:readSetting("copt_status_line") or DCREREADER_PROGRESS_BAR - self.document:setStatusLineProp(status_line) + + local view_mode = config:readSetting("copt_view_mode") + if view_mode == 0 then + table.insert(self.ui.postInitCallback, function() + self.ui:handleEvent(Event:new("SetViewMode", "page")) + end) + elseif view_mode == 1 then + table.insert(self.ui.postInitCallback, function() + self.ui:handleEvent(Event:new("SetViewMode", "scroll")) + end) + end + + local status_line = config:readSetting("copt_status_line") or DCREREADER_PROGRESS_BAR + self.document:setStatusLineProp(status_line) end function ReaderCoptListener:onSetFontSize(font_size) - self.document.configurable.font_size = font_size + self.document.configurable.font_size = font_size end return ReaderCoptListener diff --git a/frontend/ui/reader/readercropping.lua b/frontend/ui/reader/readercropping.lua index ef7637c40..439176474 100644 --- a/frontend/ui/reader/readercropping.lua +++ b/frontend/ui/reader/readercropping.lua @@ -15,149 +15,149 @@ local Math = require("optmath") local DEBUG = require("dbg") local PageCropDialog = VerticalGroup:new{ - ok_text = "OK", - cancel_text = "Cancel", - ok_callback = function() end, - cancel_callback = function() end, - button_width = math.floor(Screen:scaleByDPI(70)), + ok_text = "OK", + cancel_text = "Cancel", + ok_callback = function() end, + cancel_callback = function() end, + button_width = math.floor(Screen:scaleByDPI(70)), } function PageCropDialog:init() - local horizontal_group = HorizontalGroup:new{} - local ok_button = Button:new{ - text = self.ok_text, - callback = self.ok_callback, - width = self.button_width, - bordersize = 2, - radius = 7, - text_font_face = "cfont", - text_font_size = 20, - } - local cancel_button = Button:new{ - text = self.cancel_text, - callback = self.cancel_callback, - width = self.button_width, - bordersize = 2, - radius = 7, - text_font_face = "cfont", - text_font_size = 20, - } - local ok_container = RightContainer:new{ - dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12}, - ok_button, - } - local cancel_container = LeftContainer:new{ - dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12}, - cancel_button, - } - table.insert(horizontal_group, ok_container) - table.insert(horizontal_group, HorizontalSpan:new{ width = Screen:getWidth()*0.34}) - table.insert(horizontal_group, cancel_container) - self[2] = FrameContainer:new{ - horizontal_group, - background = 0, - bordersize = 0, - padding = 0, - } + local horizontal_group = HorizontalGroup:new{} + local ok_button = Button:new{ + text = self.ok_text, + callback = self.ok_callback, + width = self.button_width, + bordersize = 2, + radius = 7, + text_font_face = "cfont", + text_font_size = 20, + } + local cancel_button = Button:new{ + text = self.cancel_text, + callback = self.cancel_callback, + width = self.button_width, + bordersize = 2, + radius = 7, + text_font_face = "cfont", + text_font_size = 20, + } + local ok_container = RightContainer:new{ + dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12}, + ok_button, + } + local cancel_container = LeftContainer:new{ + dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12}, + cancel_button, + } + table.insert(horizontal_group, ok_container) + table.insert(horizontal_group, HorizontalSpan:new{ width = Screen:getWidth()*0.34}) + table.insert(horizontal_group, cancel_container) + self[2] = FrameContainer:new{ + horizontal_group, + background = 0, + bordersize = 0, + padding = 0, + } end local ReaderCropping = InputContainer:new{} function ReaderCropping:onPageCrop(mode) - if mode == "auto" then return end - self.ui:handleEvent(Event:new("CloseConfig")) - -- backup original view dimen - self.orig_view_dimen = Geom:new{w = self.view.dimen.w, h = self.view.dimen.h} - -- backup original view bgcolor - self.orig_view_bgcolor = self.view.outer_page_color - self.view.outer_page_color = 7 -- gray bgcolor - -- backup original zoom mode as cropping use "page" zoom mode - self.orig_zoom_mode = self.view.zoom_mode - -- backup original page scroll - self.orig_page_scroll = self.view.page_scroll - self.view.page_scroll = false - -- backup and disable original hinting state - self.ui:handleEvent(Event:new("DisableHinting")) - -- backup original reflow mode as cropping use non-reflow mode - self.orig_reflow_mode = self.document.configurable.text_wrap - if self.orig_reflow_mode == 1 then - self.document.configurable.text_wrap = 0 - -- if we are in reflow mode, then we are already in page - -- mode, just force readerview to recalculate visible_area - self.view:recalculate() - else - self.ui:handleEvent(Event:new("SetZoomMode", "page", "cropping")) - end - self.ui:handleEvent(Event:new("SetDimensions", - Geom:new{w = Screen:getWidth(), h = Screen:getHeight()*11/12}) - ) - self.bbox_widget = BBoxWidget:new{ - crop = self, - ui = self.ui, - view = self.view, - document = self.document, - } - self.crop_dialog = PageCropDialog:new{ - self.bbox_widget, - ok_callback = function() self:onConfirmPageCrop() end, - cancel_callback = function() self:onCancelPageCrop() end, - } - UIManager:show(self.crop_dialog) - return true + if mode == "auto" then return end + self.ui:handleEvent(Event:new("CloseConfig")) + -- backup original view dimen + self.orig_view_dimen = Geom:new{w = self.view.dimen.w, h = self.view.dimen.h} + -- backup original view bgcolor + self.orig_view_bgcolor = self.view.outer_page_color + self.view.outer_page_color = 7 -- gray bgcolor + -- backup original zoom mode as cropping use "page" zoom mode + self.orig_zoom_mode = self.view.zoom_mode + -- backup original page scroll + self.orig_page_scroll = self.view.page_scroll + self.view.page_scroll = false + -- backup and disable original hinting state + self.ui:handleEvent(Event:new("DisableHinting")) + -- backup original reflow mode as cropping use non-reflow mode + self.orig_reflow_mode = self.document.configurable.text_wrap + if self.orig_reflow_mode == 1 then + self.document.configurable.text_wrap = 0 + -- if we are in reflow mode, then we are already in page + -- mode, just force readerview to recalculate visible_area + self.view:recalculate() + else + self.ui:handleEvent(Event:new("SetZoomMode", "page", "cropping")) + end + self.ui:handleEvent(Event:new("SetDimensions", + Geom:new{w = Screen:getWidth(), h = Screen:getHeight()*11/12}) + ) + self.bbox_widget = BBoxWidget:new{ + crop = self, + ui = self.ui, + view = self.view, + document = self.document, + } + self.crop_dialog = PageCropDialog:new{ + self.bbox_widget, + ok_callback = function() self:onConfirmPageCrop() end, + cancel_callback = function() self:onCancelPageCrop() end, + } + UIManager:show(self.crop_dialog) + return true end function ReaderCropping:onConfirmPageCrop() - --DEBUG("new bbox", new_bbox) - UIManager:close(self.crop_dialog) - local new_bbox = self.bbox_widget:getModifiedPageBBox() - self.ui:handleEvent(Event:new("BBoxUpdate", new_bbox)) - local pageno = self.view.state.page - self.document.bbox[pageno] = new_bbox - self.document.bbox[Math.oddEven(pageno)] = new_bbox - self:exitPageCrop(true) - return true + --DEBUG("new bbox", new_bbox) + UIManager:close(self.crop_dialog) + local new_bbox = self.bbox_widget:getModifiedPageBBox() + self.ui:handleEvent(Event:new("BBoxUpdate", new_bbox)) + local pageno = self.view.state.page + self.document.bbox[pageno] = new_bbox + self.document.bbox[Math.oddEven(pageno)] = new_bbox + self:exitPageCrop(true) + return true end function ReaderCropping:onCancelPageCrop() - UIManager:close(self.crop_dialog) - self:exitPageCrop(false) - return true + UIManager:close(self.crop_dialog) + self:exitPageCrop(false) + return true end function ReaderCropping:exitPageCrop(confirmed) - -- restore hinting state - self.ui:handleEvent(Event:new("RestoreHinting")) - -- restore page scroll - self.view.page_scroll = self.orig_page_scroll - -- restore view bgcolor - self.view.outer_page_color = self.orig_view_bgcolor - -- restore reflow mode - self.document.configurable.text_wrap = self.orig_reflow_mode - -- restore view dimens - self.ui:handleEvent(Event:new("RestoreDimensions", self.orig_view_dimen)) - self.view:recalculate() - -- Exiting should have the same look and feel with entering. - if self.orig_reflow_mode == 1 then - self.ui:handleEvent(Event:new("RestoreZoomMode")) - else - if confirmed then - -- if original zoom mode is not "content", set zoom mode to "contentwidth" - self.ui:handleEvent(Event:new("SetZoomMode", - self.orig_zoom_mode:find("content") and self.orig_zoom_mode or "contentwidth")) - self.ui:handleEvent(Event:new("InitScrollPageStates")) - else - self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) - end - end - UIManager.repaint_all = true + -- restore hinting state + self.ui:handleEvent(Event:new("RestoreHinting")) + -- restore page scroll + self.view.page_scroll = self.orig_page_scroll + -- restore view bgcolor + self.view.outer_page_color = self.orig_view_bgcolor + -- restore reflow mode + self.document.configurable.text_wrap = self.orig_reflow_mode + -- restore view dimens + self.ui:handleEvent(Event:new("RestoreDimensions", self.orig_view_dimen)) + self.view:recalculate() + -- Exiting should have the same look and feel with entering. + if self.orig_reflow_mode == 1 then + self.ui:handleEvent(Event:new("RestoreZoomMode")) + else + if confirmed then + -- if original zoom mode is not "content", set zoom mode to "contentwidth" + self.ui:handleEvent(Event:new("SetZoomMode", + self.orig_zoom_mode:find("content") and self.orig_zoom_mode or "contentwidth")) + self.ui:handleEvent(Event:new("InitScrollPageStates")) + else + self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) + end + end + UIManager.repaint_all = true end function ReaderCropping:onReadSettings(config) - self.document.bbox = config:readSetting("bbox") + self.document.bbox = config:readSetting("bbox") end function ReaderCropping:onSaveSettings() - self.ui.doc_settings:saveSetting("bbox", self.document.bbox) + self.ui.doc_settings:saveSetting("bbox", self.document.bbox) end return ReaderCropping diff --git a/frontend/ui/reader/readerdictionary.lua b/frontend/ui/reader/readerdictionary.lua index 3f6f2ab7a..beb8abcfc 100644 --- a/frontend/ui/reader/readerdictionary.lua +++ b/frontend/ui/reader/readerdictionary.lua @@ -9,69 +9,69 @@ local DEBUG = require("dbg") local ReaderDictionary = EventListener:new{} function ReaderDictionary:onLookupWord(highlight, word, box) - self.highlight = highlight - self:stardictLookup(word, box) + self.highlight = highlight + self:stardictLookup(word, box) end function ReaderDictionary:stardictLookup(word, box) - DEBUG("lookup word:", word, box) - if word then - -- strip punctuation characters around selected word - word = string.gsub(word, "^%p+", '') - word = string.gsub(word, "%p+$", '') - DEBUG("stripped word:", word) - -- escape quotes and other funny characters in word - local std_out = io.popen("./sdcv --utf8-input --utf8-output -nj "..("%q"):format(word), "r") - local results_str = nil - if std_out then results_str = std_out:read("*all") end - if results_str then - --DEBUG("result str:", word, results_str) - local ok, results = pcall(JSON.decode, JSON, results_str) - --DEBUG("lookup result table:", word, results) - self:showDict(results, box) - end - end + DEBUG("lookup word:", word, box) + if word then + -- strip punctuation characters around selected word + word = string.gsub(word, "^%p+", '') + word = string.gsub(word, "%p+$", '') + DEBUG("stripped word:", word) + -- escape quotes and other funny characters in word + local std_out = io.popen("./sdcv --utf8-input --utf8-output -nj "..("%q"):format(word), "r") + local results_str = nil + if std_out then results_str = std_out:read("*all") end + if results_str then + --DEBUG("result str:", word, results_str) + local ok, results = pcall(JSON.decode, JSON, results_str) + --DEBUG("lookup result table:", word, results) + self:showDict(results, box) + end + end end function ReaderDictionary:showDict(results, box) - if results and results[1] and box then - DEBUG("showing quick lookup dictionary window") - local align = nil - local region = Geom:new{x = 0, w = Screen:getWidth()} - 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 - UIManager:show(DictQuickLookup:new{ - ui = self.ui, - highlight = self.highlight, - dialog = self.dialog, - 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, - }) - end + if results and results[1] and box then + DEBUG("showing quick lookup dictionary window") + local align = nil + local region = Geom:new{x = 0, w = Screen:getWidth()} + 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 + UIManager:show(DictQuickLookup:new{ + ui = self.ui, + highlight = self.highlight, + dialog = self.dialog, + 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, + }) + end end function ReaderDictionary:onUpdateDefaultDict(dict) - DEBUG("make default dictionary:", dict) - self.default_dictionary = dict + DEBUG("make default dictionary:", dict) + self.default_dictionary = dict end function ReaderDictionary:onReadSettings(config) - self.default_dictionary = config:readSetting("default_dictionary") + self.default_dictionary = config:readSetting("default_dictionary") end function ReaderDictionary:onSaveSettings() - self.ui.doc_settings:saveSetting("default_dictionary", self.default_dictionary) + self.ui.doc_settings:saveSetting("default_dictionary", self.default_dictionary) end return ReaderDictionary diff --git a/frontend/ui/reader/readerdogear.lua b/frontend/ui/reader/readerdogear.lua index 531ae028c..616455e0d 100644 --- a/frontend/ui/reader/readerdogear.lua +++ b/frontend/ui/reader/readerdogear.lua @@ -11,54 +11,54 @@ local Event = require("ui/event") local ReaderDogear = InputContainer:new{} function ReaderDogear:init() - local widget = ImageWidget:new{ - file = "resources/icons/dogear.png", - } - self[1] = RightContainer:new{ - dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, - widget, - } - if Device:isTouchDevice() then - self.ges_events = { - Tap = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x, - y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y, - w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w, - h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h - } - } - }, - Hold = { - GestureRange:new{ - ges = "hold", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x, - y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y, - w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w, - h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h - } - } - } - } - end + local widget = ImageWidget:new{ + file = "resources/icons/dogear.png", + } + self[1] = RightContainer:new{ + dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, + widget, + } + if Device:isTouchDevice() then + self.ges_events = { + Tap = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x, + y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y, + w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w, + h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h + } + } + }, + Hold = { + GestureRange:new{ + ges = "hold", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x, + y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y, + w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w, + h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h + } + } + } + } + end end function ReaderDogear:onTap() - self.ui:handleEvent(Event:new("ToggleBookmark")) - return true + self.ui:handleEvent(Event:new("ToggleBookmark")) + return true end function ReaderDogear:onHold() - self.ui:handleEvent(Event:new("ToggleBookmarkFlipping")) - return true + self.ui:handleEvent(Event:new("ToggleBookmarkFlipping")) + return true end function ReaderDogear:onSetDogearVisibility(visible) - self.view.dogear_visible = visible - return true + self.view.dogear_visible = visible + return true end return ReaderDogear diff --git a/frontend/ui/reader/readerflipping.lua b/frontend/ui/reader/readerflipping.lua index 24f0b5551..b2d9be9bf 100644 --- a/frontend/ui/reader/readerflipping.lua +++ b/frontend/ui/reader/readerflipping.lua @@ -8,37 +8,37 @@ local Screen = require("ui/screen") local Event = require("ui/event") local ReaderFlipping = InputContainer:new{ - orig_reflow_mode = 0, + orig_reflow_mode = 0, } function ReaderFlipping:init() - local widget = ImageWidget:new{ - file = "resources/icons/appbar.book.open.png", - } - self[1] = LeftContainer:new{ - dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, - widget, - } - if Device:isTouchDevice() then - self.ges_events = { - Tap = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_FLIPPING.x, - y = Screen:getHeight()*DTAP_ZONE_FLIPPING.y, - w = Screen:getWidth()*DTAP_ZONE_FLIPPING.w, - h = Screen:getHeight()*DTAP_ZONE_FLIPPING.h - } - } - } - } - end + local widget = ImageWidget:new{ + file = "resources/icons/appbar.book.open.png", + } + self[1] = LeftContainer:new{ + dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, + widget, + } + if Device:isTouchDevice() then + self.ges_events = { + Tap = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_FLIPPING.x, + y = Screen:getHeight()*DTAP_ZONE_FLIPPING.y, + w = Screen:getWidth()*DTAP_ZONE_FLIPPING.w, + h = Screen:getHeight()*DTAP_ZONE_FLIPPING.h + } + } + } + } + end end function ReaderFlipping:onTap() - self.ui:handleEvent(Event:new("TogglePageFlipping")) - return true + self.ui:handleEvent(Event:new("TogglePageFlipping")) + return true end return ReaderFlipping diff --git a/frontend/ui/reader/readerfont.lua b/frontend/ui/reader/readerfont.lua index dc74764b9..ffd6954ea 100644 --- a/frontend/ui/reader/readerfont.lua +++ b/frontend/ui/reader/readerfont.lua @@ -12,220 +12,220 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderFont = InputContainer:new{ - font_face = nil, - font_size = nil, - line_space_percent = nil, - font_menu_title = _("Change font"), - face_table = nil, - -- default gamma from crengine's lvfntman.cpp - gamma_index = nil, + font_face = nil, + font_size = nil, + line_space_percent = nil, + font_menu_title = _("Change font"), + face_table = nil, + -- default gamma from crengine's lvfntman.cpp + gamma_index = nil, } function ReaderFont:init() - if Device:hasKeyboard() then - -- add shortcut for keyboard - self.key_events = { - ShowFontMenu = { {"F"}, doc = _("show font menu") }, - IncreaseSize = { - { "Shift", Input.group.PgFwd }, - doc = _("increase font size"), - event = "ChangeSize", args = "increase" }, - DecreaseSize = { - { "Shift", Input.group.PgBack }, - doc = _("decrease font size"), - event = "ChangeSize", args = "decrease" }, - IncreaseLineSpace = { - { "Alt", Input.group.PgFwd }, - doc = _("increase line space"), - event = "ChangeLineSpace", args = "increase" }, - DecreaseLineSpace = { - { "Alt", Input.group.PgBack }, - doc = _("decrease line space"), - event = "ChangeLineSpace", args = "decrease" }, - } - end - -- build face_table for menu - self.face_table = {} - local face_list = cre.getFontFaces() - for k,v in ipairs(face_list) do - table.insert(self.face_table, { - text = v, - callback = function() - self:setFont(v) - end - }) - face_list[k] = {text = v} - end - self.ui.menu:registerToMainMenu(self) + if Device:hasKeyboard() then + -- add shortcut for keyboard + self.key_events = { + ShowFontMenu = { {"F"}, doc = _("show font menu") }, + IncreaseSize = { + { "Shift", Input.group.PgFwd }, + doc = _("increase font size"), + event = "ChangeSize", args = "increase" }, + DecreaseSize = { + { "Shift", Input.group.PgBack }, + doc = _("decrease font size"), + event = "ChangeSize", args = "decrease" }, + IncreaseLineSpace = { + { "Alt", Input.group.PgFwd }, + doc = _("increase line space"), + event = "ChangeLineSpace", args = "increase" }, + DecreaseLineSpace = { + { "Alt", Input.group.PgBack }, + doc = _("decrease line space"), + event = "ChangeLineSpace", args = "decrease" }, + } + end + -- build face_table for menu + self.face_table = {} + local face_list = cre.getFontFaces() + for k,v in ipairs(face_list) do + table.insert(self.face_table, { + text = v, + callback = function() + self:setFont(v) + end + }) + face_list[k] = {text = v} + end + self.ui.menu:registerToMainMenu(self) end function ReaderFont:onSetDimensions(dimen) - self.dimen = dimen + self.dimen = dimen end function ReaderFont:onReadSettings(config) - self.font_face = config:readSetting("font_face") - if not self.font_face then - self.font_face = self.ui.document.default_font - end - self.ui.document:setFontFace(self.font_face) - - self.header_font_face = config:readSetting("header_font_face") - if not self.header_font_face then - self.header_font_face = self.ui.document.header_font - end - self.ui.document:setHeaderFont(self.header_font_face) - - self.font_size = config:readSetting("font_size") - if not self.font_size then - --@TODO change this! 12.01 2013 (houqp) - self.font_size = DCREREADER_CONFIG_DEFAULT_FONT_SIZE - end - self.ui.document:setFontSize(Screen:scaleByDPI(self.font_size)) - - self.line_space_percent = config:readSetting("line_space_percent") - if not self.line_space_percent then - self.line_space_percent = 100 - else - self.ui.document:setInterlineSpacePercent(self.line_space_percent) - end - - self.gamma_index = config:readSetting("gamma_index") - if not self.gamma_index then - self.gamma_index = 15 - end - self.ui.document:setGammaIndex(self.gamma_index) - - -- Dirty hack: we have to add folloing call in order to set - -- m_is_rendered(member of LVDocView) to true. Otherwise position inside - -- document will be reset to 0 on first view render. - -- So far, I don't know why this call will alter the value of m_is_rendered. - table.insert(self.ui.postInitCallback, function() - self.ui:handleEvent(Event:new("UpdatePos")) - end) + self.font_face = config:readSetting("font_face") + if not self.font_face then + self.font_face = self.ui.document.default_font + end + self.ui.document:setFontFace(self.font_face) + + self.header_font_face = config:readSetting("header_font_face") + if not self.header_font_face then + self.header_font_face = self.ui.document.header_font + end + self.ui.document:setHeaderFont(self.header_font_face) + + self.font_size = config:readSetting("font_size") + if not self.font_size then + --@TODO change this! 12.01 2013 (houqp) + self.font_size = DCREREADER_CONFIG_DEFAULT_FONT_SIZE + end + self.ui.document:setFontSize(Screen:scaleByDPI(self.font_size)) + + self.line_space_percent = config:readSetting("line_space_percent") + if not self.line_space_percent then + self.line_space_percent = 100 + else + self.ui.document:setInterlineSpacePercent(self.line_space_percent) + end + + self.gamma_index = config:readSetting("gamma_index") + if not self.gamma_index then + self.gamma_index = 15 + end + self.ui.document:setGammaIndex(self.gamma_index) + + -- Dirty hack: we have to add folloing call in order to set + -- m_is_rendered(member of LVDocView) to true. Otherwise position inside + -- document will be reset to 0 on first view render. + -- So far, I don't know why this call will alter the value of m_is_rendered. + table.insert(self.ui.postInitCallback, function() + self.ui:handleEvent(Event:new("UpdatePos")) + end) end function ReaderFont:onShowFontMenu() - -- build menu widget - local main_menu = Menu:new{ - title = self.font_menu_title, - item_table = self.face_table, - width = Screen:getWidth() - 100, - } - -- build container - local menu_container = CenterContainer:new{ - main_menu, - dimen = Screen:getSize(), - } - main_menu.close_callback = function () - UIManager:close(menu_container) - end - -- show menu - UIManager:show(menu_container) - return true + -- build menu widget + local main_menu = Menu:new{ + title = self.font_menu_title, + item_table = self.face_table, + width = Screen:getWidth() - 100, + } + -- build container + local menu_container = CenterContainer:new{ + main_menu, + dimen = Screen:getSize(), + } + main_menu.close_callback = function () + UIManager:close(menu_container) + end + -- show menu + UIManager:show(menu_container) + return true end --[[ - UpdatePos event is used to tell ReaderRolling to update pos. + UpdatePos event is used to tell ReaderRolling to update pos. --]] function ReaderFont:onChangeSize(direction) - local delta = direction == "decrease" and -1 or 1 - self.font_size = self.font_size + delta - self.ui:handleEvent(Event:new("SetFontSize", self.font_size)) - return true + local delta = direction == "decrease" and -1 or 1 + self.font_size = self.font_size + delta + self.ui:handleEvent(Event:new("SetFontSize", self.font_size)) + return true end function ReaderFont:onSetFontSize(new_size) - if new_size > 72 then new_size = 72 end - if new_size < 12 then new_size = 12 end - - self.font_size = new_size - UIManager:show(Notification:new{ - text = _("Set font size to ")..self.font_size, - timeout = 1, - }) - self.ui.document:setFontSize(Screen:scaleByDPI(new_size)) - self.ui:handleEvent(Event:new("UpdatePos")) - - return true + if new_size > 72 then new_size = 72 end + if new_size < 12 then new_size = 12 end + + self.font_size = new_size + UIManager:show(Notification:new{ + text = _("Set font size to ")..self.font_size, + timeout = 1, + }) + self.ui.document:setFontSize(Screen:scaleByDPI(new_size)) + self.ui:handleEvent(Event:new("UpdatePos")) + + return true end function ReaderFont:onChangeLineSpace(direction) - local msg = "" - if direction == "decrease" then - self.line_space_percent = self.line_space_percent - 10 - -- NuPogodi, 15.05.12: reduce lowest space_percent to 80 - self.line_space_percent = math.max(self.line_space_percent, 80) - msg = _("Decrease line space to ") - else - self.line_space_percent = self.line_space_percent + 10 - self.line_space_percent = math.min(self.line_space_percent, 200) - msg = _("Increase line space to ") - end - UIManager:show(Notification:new{ - text = msg..self.line_space_percent.."%", - timeout = 1, - }) - self.ui.document:setInterlineSpacePercent(self.line_space_percent) - self.ui:handleEvent(Event:new("UpdatePos")) - - return true + local msg = "" + if direction == "decrease" then + self.line_space_percent = self.line_space_percent - 10 + -- NuPogodi, 15.05.12: reduce lowest space_percent to 80 + self.line_space_percent = math.max(self.line_space_percent, 80) + msg = _("Decrease line space to ") + else + self.line_space_percent = self.line_space_percent + 10 + self.line_space_percent = math.min(self.line_space_percent, 200) + msg = _("Increase line space to ") + end + UIManager:show(Notification:new{ + text = msg..self.line_space_percent.."%", + timeout = 1, + }) + self.ui.document:setInterlineSpacePercent(self.line_space_percent) + self.ui:handleEvent(Event:new("UpdatePos")) + + return true end function ReaderFont:onToggleFontBolder() - self.ui.document:toggleFontBolder() - self.ui:handleEvent(Event:new("UpdatePos")) - return true + self.ui.document:toggleFontBolder() + self.ui:handleEvent(Event:new("UpdatePos")) + return true end function ReaderFont:onChangeFontGamma(direction) - local msg = "" - if direction == "increase" then - cre.setGammaIndex(self.gamma_index+2) - msg = _("Increase gamma to ") - elseif direction == "decrease" then - cre.setGammaIndex(self.gamma_index-2) - msg = _("Decrease gamma to ") - end - self.gamma_index = cre.getGammaIndex() - UIManager:show(Notification:new{ - text = msg..self.gamma_index, - timeout = 1 - }) - self.ui:handleEvent(Event:new("RedrawCurrentView")) - return true + local msg = "" + if direction == "increase" then + cre.setGammaIndex(self.gamma_index+2) + msg = _("Increase gamma to ") + elseif direction == "decrease" then + cre.setGammaIndex(self.gamma_index-2) + msg = _("Decrease gamma to ") + end + self.gamma_index = cre.getGammaIndex() + UIManager:show(Notification:new{ + text = msg..self.gamma_index, + timeout = 1 + }) + self.ui:handleEvent(Event:new("RedrawCurrentView")) + return true end function ReaderFont:onSaveSettings() - self.ui.doc_settings:saveSetting("font_face", self.font_face) - self.ui.doc_settings:saveSetting("header_font_face", self.header_font_face) - self.ui.doc_settings:saveSetting("font_size", self.font_size) - self.ui.doc_settings:saveSetting("line_space_percent", self.line_space_percent) - self.ui.doc_settings:saveSetting("gamma_index", self.gamma_index) + self.ui.doc_settings:saveSetting("font_face", self.font_face) + self.ui.doc_settings:saveSetting("header_font_face", self.header_font_face) + self.ui.doc_settings:saveSetting("font_size", self.font_size) + self.ui.doc_settings:saveSetting("line_space_percent", self.line_space_percent) + self.ui.doc_settings:saveSetting("gamma_index", self.gamma_index) end function ReaderFont:setFont(face) - if face and self.font_face ~= face then - self.font_face = face - UIManager:show(Notification:new{ - text = _("Redrawing with font ")..face, - timeout = 1, - }) - - self.ui.document:setFontFace(face) - -- signal readerrolling to update pos in new height - self.ui:handleEvent(Event:new("UpdatePos")) - - UIManager:close(msg) - end + if face and self.font_face ~= face then + self.font_face = face + UIManager:show(Notification:new{ + text = _("Redrawing with font ")..face, + timeout = 1, + }) + + self.ui.document:setFontFace(face) + -- signal readerrolling to update pos in new height + self.ui:handleEvent(Event:new("UpdatePos")) + + UIManager:close(msg) + end end function ReaderFont:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.typeset, { - text = self.font_menu_title, - sub_item_table = self.face_table, - }) + -- insert table to main reader menu + table.insert(tab_item_table.typeset, { + text = self.font_menu_title, + sub_item_table = self.face_table, + }) end return ReaderFont diff --git a/frontend/ui/reader/readerfooter.lua b/frontend/ui/reader/readerfooter.lua index 1a69c4ed5..ca97caf3b 100644 --- a/frontend/ui/reader/readerfooter.lua +++ b/frontend/ui/reader/readerfooter.lua @@ -16,136 +16,136 @@ local Font = require("ui/font") local DEBUG = require("dbg") local ReaderFooter = InputContainer:new{ - visible = true, - pageno = nil, - pages = nil, - progress_percentage = 0.0, - progress_text = "0 / 0", - show_time = false, - bar_width = 0.85, - text_width = 0.15, - text_font_face = "ffont", - text_font_size = 14, - height = 19, + visible = true, + pageno = nil, + pages = nil, + progress_percentage = 0.0, + progress_text = "0 / 0", + show_time = false, + bar_width = 0.85, + text_width = 0.15, + text_font_face = "ffont", + text_font_size = 14, + height = 19, } function ReaderFooter:init() - self.progress_bar = ProgressWidget:new{ - width = math.floor(Screen:getWidth()*(self.bar_width-0.02)), - height = 7, - percentage = self.progress_percentage, - } - self.progress_text = TextWidget:new{ - text = self.progress_text, - face = Font:getFace(self.text_font_face, self.text_font_size), - } - local _, text_height = self.progress_text:getSize() - local horizontal_group = HorizontalGroup:new{} - local bar_container = RightContainer:new{ - dimen = Geom:new{w = Screen:getWidth()*self.bar_width, h = self.height}, - self.progress_bar, - } - local text_container = CenterContainer:new{ - dimen = Geom:new{w = Screen:getWidth()*self.text_width, h = self.height}, - self.progress_text, - } - table.insert(horizontal_group, bar_container) - table.insert(horizontal_group, text_container) - self[1] = BottomContainer:new{ - dimen = Screen:getSize(), - FrameContainer:new{ - horizontal_group, - background = 0, - bordersize = 0, - padding = 0, - } - } - self.dimen = self[1]:getSize() - self.pageno = self.view.state.page - self.pages = self.view.document.info.number_of_pages - self:updateFooterPage() - if Device:isTouchDevice() then - self.ges_events = { - TapFooter = { - GestureRange:new{ - ges = "tap", - range = self[1]:contentRange(), - }, - }, - HoldFooter = { - GestureRange:new{ - ges = "hold", - range = self[1]:contentRange(), - }, - }, - } - end + self.progress_bar = ProgressWidget:new{ + width = math.floor(Screen:getWidth()*(self.bar_width-0.02)), + height = 7, + percentage = self.progress_percentage, + } + self.progress_text = TextWidget:new{ + text = self.progress_text, + face = Font:getFace(self.text_font_face, self.text_font_size), + } + local _, text_height = self.progress_text:getSize() + local horizontal_group = HorizontalGroup:new{} + local bar_container = RightContainer:new{ + dimen = Geom:new{w = Screen:getWidth()*self.bar_width, h = self.height}, + self.progress_bar, + } + local text_container = CenterContainer:new{ + dimen = Geom:new{w = Screen:getWidth()*self.text_width, h = self.height}, + self.progress_text, + } + table.insert(horizontal_group, bar_container) + table.insert(horizontal_group, text_container) + self[1] = BottomContainer:new{ + dimen = Screen:getSize(), + FrameContainer:new{ + horizontal_group, + background = 0, + bordersize = 0, + padding = 0, + } + } + self.dimen = self[1]:getSize() + self.pageno = self.view.state.page + self.pages = self.view.document.info.number_of_pages + self:updateFooterPage() + if Device:isTouchDevice() then + self.ges_events = { + TapFooter = { + GestureRange:new{ + ges = "tap", + range = self[1]:contentRange(), + }, + }, + HoldFooter = { + GestureRange:new{ + ges = "hold", + range = self[1]:contentRange(), + }, + }, + } + end end function ReaderFooter:updateFooterPage() - if type(self.pageno) ~= "number" then return end - self.progress_bar.percentage = self.pageno / self.pages - - if self.show_time then - self.progress_text.text = os.date("%H:%M") - else - self.progress_text.text = string.format("%d / %d", self.pageno, self.pages) - end + if type(self.pageno) ~= "number" then return end + self.progress_bar.percentage = self.pageno / self.pages + + if self.show_time then + self.progress_text.text = os.date("%H:%M") + else + self.progress_text.text = string.format("%d / %d", self.pageno, self.pages) + end end function ReaderFooter:updateFooterPos() - if type(self.position) ~= "number" then return end - self.progress_bar.percentage = self.position / self.doc_height - - if self.show_time then - self.progress_text.text = os.date("%H:%M") - else - self.progress_text.text = string.format("%1.f", self.progress_bar.percentage*100).."%" - end + if type(self.position) ~= "number" then return end + self.progress_bar.percentage = self.position / self.doc_height + + if self.show_time then + self.progress_text.text = os.date("%H:%M") + else + self.progress_text.text = string.format("%1.f", self.progress_bar.percentage*100).."%" + end end function ReaderFooter:onPageUpdate(pageno) - self.pageno = pageno - self.pages = self.view.document.info.number_of_pages - self:updateFooterPage() + self.pageno = pageno + self.pages = self.view.document.info.number_of_pages + self:updateFooterPage() end function ReaderFooter:onPosUpdate(pos) - self.position = pos - self.doc_height = self.view.document.info.doc_height - self:updateFooterPos() + self.position = pos + self.doc_height = self.view.document.info.doc_height + self:updateFooterPos() end function ReaderFooter:onTapFooter(arg, ges) - if self.view.flipping_visible then - local pos = ges.pos - local dimen = self.progress_bar.dimen - local percentage = (pos.x - dimen.x)/dimen.w - self.ui:handleEvent(Event:new("GotoPercentage", percentage)) - else - self.show_time = not self.show_time - end - if self.pageno then - self:updateFooterPage() - else - self:updateFooterPos() - end - UIManager:setDirty(self.view.dialog, "partial") - -- consume this tap when footer is visible - if self.view.footer_visible then - return true - end + if self.view.flipping_visible then + local pos = ges.pos + local dimen = self.progress_bar.dimen + local percentage = (pos.x - dimen.x)/dimen.w + self.ui:handleEvent(Event:new("GotoPercentage", percentage)) + else + self.show_time = not self.show_time + end + if self.pageno then + self:updateFooterPage() + else + self:updateFooterPos() + end + UIManager:setDirty(self.view.dialog, "partial") + -- consume this tap when footer is visible + if self.view.footer_visible then + return true + end end function ReaderFooter:onHoldFooter(arg, ges) - self.ui:handleEvent(Event:new("ShowGotoDialog")) - return true + self.ui:handleEvent(Event:new("ShowGotoDialog")) + return true end function ReaderFooter:onSetStatusLine(status_line) - self.view.footer_visible = status_line == 1 and true or false - self.ui.document:setStatusLineProp(status_line) - self.ui:handleEvent(Event:new("UpdatePos")) + self.view.footer_visible = status_line == 1 and true or false + self.ui.document:setStatusLineProp(status_line) + self.ui:handleEvent(Event:new("UpdatePos")) end return ReaderFooter diff --git a/frontend/ui/reader/readergoto.lua b/frontend/ui/reader/readergoto.lua index 2b269388f..9f2a01759 100644 --- a/frontend/ui/reader/readergoto.lua +++ b/frontend/ui/reader/readergoto.lua @@ -7,86 +7,86 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderGoto = InputContainer:new{ - goto_menu_title = _("Go To"), - goto_dialog_title = _("Go to Page or Location"), + goto_menu_title = _("Go To"), + goto_dialog_title = _("Go to Page or Location"), } function ReaderGoto:init() - self.ui.menu:registerToMainMenu(self) + self.ui.menu:registerToMainMenu(self) end function ReaderGoto:addToMainMenu(tab_item_table) - -- insert goto command to main reader menu - table.insert(tab_item_table.navi, { - text = self.goto_menu_title, - callback = function() - self:onShowGotoDialog() - end, - }) + -- insert goto command to main reader menu + table.insert(tab_item_table.navi, { + text = self.goto_menu_title, + callback = function() + self:onShowGotoDialog() + end, + }) end function ReaderGoto:onShowGotoDialog() - DEBUG("show goto dialog") - self.goto_dialog = InputDialog:new{ - title = self.goto_dialog_title, - input_hint = "(1 - "..self.document:getPageCount()..")", - buttons = { - { - { - text = _("Cancel"), - enabled = true, - callback = function() - self:close() - end, - }, - { - text = _("Page"), - enabled = self.document.info.has_pages, - callback = function() - self:gotoPage() - end, - }, - { - text = _("Location"), - enabled = not self.document.info.has_pages, - callback = function() - self:gotoLocation() - end, - }, - }, - }, - input_type = "number", - enter_callback = self.document.info.has_pages - and function() self:gotoPage() end - or function() self:gotoLocation() end, - width = Screen:getWidth() * 0.8, - height = Screen:getHeight() * 0.2, - } - self.goto_dialog:onShowKeyboard() - UIManager:show(self.goto_dialog) + DEBUG("show goto dialog") + self.goto_dialog = InputDialog:new{ + title = self.goto_dialog_title, + input_hint = "(1 - "..self.document:getPageCount()..")", + buttons = { + { + { + text = _("Cancel"), + enabled = true, + callback = function() + self:close() + end, + }, + { + text = _("Page"), + enabled = self.document.info.has_pages, + callback = function() + self:gotoPage() + end, + }, + { + text = _("Location"), + enabled = not self.document.info.has_pages, + callback = function() + self:gotoLocation() + end, + }, + }, + }, + input_type = "number", + enter_callback = self.document.info.has_pages + and function() self:gotoPage() end + or function() self:gotoLocation() end, + width = Screen:getWidth() * 0.8, + height = Screen:getHeight() * 0.2, + } + self.goto_dialog:onShowKeyboard() + UIManager:show(self.goto_dialog) end function ReaderGoto:close() - self.goto_dialog:onClose() - UIManager:close(self.goto_dialog) + self.goto_dialog:onClose() + UIManager:close(self.goto_dialog) end function ReaderGoto:gotoPage() - local number = tonumber(self.goto_dialog:getInputText()) - if number then - self.ui:handleEvent(Event:new("GotoPage", number)) - end - self:close() - return true + local number = tonumber(self.goto_dialog:getInputText()) + if number then + self.ui:handleEvent(Event:new("GotoPage", number)) + end + self:close() + return true end function ReaderGoto:gotoLocation() - local number = tonumber(self.goto_dialog:getInputText()) - if number then - self.ui:handleEvent(Event:new("GotoPage", number)) - end - self:close() - return true + local number = tonumber(self.goto_dialog:getInputText()) + if number then + self.ui:handleEvent(Event:new("GotoPage", number)) + end + self:close() + return true end return ReaderGoto diff --git a/frontend/ui/reader/readerhighlight.lua b/frontend/ui/reader/readerhighlight.lua index d3ebc261d..3be7f8229 100644 --- a/frontend/ui/reader/readerhighlight.lua +++ b/frontend/ui/reader/readerhighlight.lua @@ -12,405 +12,405 @@ local _ = require("gettext") local ReaderHighlight = InputContainer:new{} function ReaderHighlight:init() - if Device:hasKeyboard() then - self.key_events = { - ShowToc = { - { "." }, - doc = _("highlight text") }, - } - end - self.ui.menu:registerToMainMenu(self) + if Device:hasKeyboard() then + self.key_events = { + ShowToc = { + { "." }, + doc = _("highlight text") }, + } + end + self.ui.menu:registerToMainMenu(self) end function ReaderHighlight:initGesListener() - self.ges_events = { - Tap = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight() - } - } - }, - Hold = { - GestureRange:new{ - ges = "hold", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight() - } - } - }, - HoldRelease = { - GestureRange:new{ - ges = "hold_release", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight() - } - } - }, - HoldPan = { - GestureRange:new{ - ges = "hold_pan", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight() - }, - rate = 2.0, - } - }, - } + self.ges_events = { + Tap = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight() + } + } + }, + Hold = { + GestureRange:new{ + ges = "hold", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight() + } + } + }, + HoldRelease = { + GestureRange:new{ + ges = "hold_release", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight() + } + } + }, + HoldPan = { + GestureRange:new{ + ges = "hold_pan", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight() + }, + rate = 2.0, + } + }, + } end function ReaderHighlight:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.typeset, { - text_func = function() - return _("Set highlight drawer ").."( "..self.view.highlight.saved_drawer.." )" - end, - sub_item_table = self:genHighlightDrawerMenu(), - }) + -- insert table to main reader menu + table.insert(tab_item_table.typeset, { + text_func = function() + return _("Set highlight drawer ").."( "..self.view.highlight.saved_drawer.." )" + end, + sub_item_table = self:genHighlightDrawerMenu(), + }) end function ReaderHighlight:genHighlightDrawerMenu() - return { - { - text = _("Lighten"), - callback = function() - self.view.highlight.saved_drawer = "lighten" - end - }, - { - text = _("Underscore"), - callback = function() - self.view.highlight.saved_drawer = "underscore" - end - }, - { - text = _("Invert"), - callback = function() - self.view.highlight.saved_drawer = "invert" - end - }, - } + return { + { + text = _("Lighten"), + callback = function() + self.view.highlight.saved_drawer = "lighten" + end + }, + { + text = _("Underscore"), + callback = function() + self.view.highlight.saved_drawer = "underscore" + end + }, + { + text = _("Invert"), + callback = function() + self.view.highlight.saved_drawer = "invert" + end + }, + } end function ReaderHighlight:onSetDimensions(dimen) - -- update listening according to new screen dimen - if Device:isTouchDevice() then - self:initGesListener() - end + -- update listening according to new screen dimen + if Device:isTouchDevice() then + self:initGesListener() + end end function ReaderHighlight:onTap(arg, ges) - 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 - UIManager:setDirty(self.dialog, "partial") - return true - end - if self.ui.document.info.has_pages then - return self:onTapPageSavedHighlight(ges) - else - return self:onTapXPointerSavedHighlight(ges) - end + 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 + UIManager:setDirty(self.dialog, "partial") + return true + end + if self.ui.document.info.has_pages then + return self:onTapPageSavedHighlight(ges) + else + return self:onTapXPointerSavedHighlight(ges) + end end local function inside_box(pos, box) - if pos then - local x, y = pos.x, pos.y - if box.x <= x and box.y <= y - and box.x + box.w >= x - and box.y + box.h >= y then - return true - end - end + if pos then + local x, y = pos.x, pos.y + if box.x <= x and box.y <= y + and box.x + box.w >= x + and box.y + box.h >= y then + return true + end + end end function ReaderHighlight:onTapPageSavedHighlight(ges) - local pages = self.view:getCurrentPageList() - local pos = self.view:screenToPageTransform(ges.pos) - for key, page in pairs(pages) do - local items = self.view.highlight.saved[page] - if not items then items = {} end - for i = 1, #items do - local pos0, pos1 = items[i].pos0, items[i].pos1 - local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1) - if boxes then - for index, box in pairs(boxes) do - if inside_box(pos, box) then - DEBUG("Tap on hightlight") - return self:onShowHighlightDialog(page, i) - end - end - end - end - end + local pages = self.view:getCurrentPageList() + local pos = self.view:screenToPageTransform(ges.pos) + for key, page in pairs(pages) do + local items = self.view.highlight.saved[page] + if not items then items = {} end + for i = 1, #items do + local pos0, pos1 = items[i].pos0, items[i].pos1 + local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1) + if boxes then + for index, box in pairs(boxes) do + if inside_box(pos, box) then + DEBUG("Tap on hightlight") + return self:onShowHighlightDialog(page, i) + end + end + end + end + end end function ReaderHighlight:onTapXPointerSavedHighlight(ges) - local pos = self.view:screenToPageTransform(ges.pos) - for page, _ in pairs(self.view.highlight.saved) do - local items = self.view.highlight.saved[page] - if not items then items = {} end - for i = 1, #items do - local pos0, pos1 = items[i].pos0, items[i].pos1 - local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1) - if boxes then - for index, box in pairs(boxes) do - if inside_box(pos, box) then - DEBUG("Tap on hightlight") - return self:onShowHighlightDialog(page, i) - end - end - end - end - end + local pos = self.view:screenToPageTransform(ges.pos) + for page, _ in pairs(self.view.highlight.saved) do + local items = self.view.highlight.saved[page] + if not items then items = {} end + for i = 1, #items do + local pos0, pos1 = items[i].pos0, items[i].pos1 + local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1) + if boxes then + for index, box in pairs(boxes) do + if inside_box(pos, box) then + DEBUG("Tap on hightlight") + return self:onShowHighlightDialog(page, i) + end + end + end + end + end end function ReaderHighlight:onShowHighlightDialog(page, index) - self.edit_highlight_dialog = ButtonDialog:new{ - buttons = { - { - { - text = _("Delete"), - callback = function() - self:deleteHighlight(page, index) - UIManager:close(self.edit_highlight_dialog) - end, - }, - { - text = _("Edit"), - enabled = false, - callback = function() - self:editHighlight() - UIManager:close(self.edit_highlight_dialog) - end, - }, - }, - }, - } - UIManager:show(self.edit_highlight_dialog) - return true + self.edit_highlight_dialog = ButtonDialog:new{ + buttons = { + { + { + text = _("Delete"), + callback = function() + self:deleteHighlight(page, index) + UIManager:close(self.edit_highlight_dialog) + end, + }, + { + text = _("Edit"), + enabled = false, + callback = function() + self:editHighlight() + UIManager:close(self.edit_highlight_dialog) + end, + }, + }, + }, + } + UIManager:show(self.edit_highlight_dialog) + return true end function ReaderHighlight:onHold(arg, ges) - self.hold_pos = self.view:screenToPageTransform(ges.pos) - DEBUG("hold position in page", self.hold_pos) - if not self.hold_pos then - DEBUG("not inside page area") - return true - end + self.hold_pos = self.view:screenToPageTransform(ges.pos) + DEBUG("hold position in page", self.hold_pos) + if not self.hold_pos then + DEBUG("not inside page area") + return true + end - local ok, word = pcall(self.ui.document.getWordFromPosition, self.ui.document, self.hold_pos) - if ok and word then - DEBUG("selected word:", word) - self.selected_word = word - if self.ui.document.info.has_pages then - local boxes = {} - table.insert(boxes, self.selected_word.sbox) - self.view.highlight.temp[self.hold_pos.page] = boxes - end - UIManager:setDirty(self.dialog, "partial") - end - return true + local ok, word = pcall(self.ui.document.getWordFromPosition, self.ui.document, self.hold_pos) + if ok and word then + DEBUG("selected word:", word) + self.selected_word = word + if self.ui.document.info.has_pages then + local boxes = {} + table.insert(boxes, self.selected_word.sbox) + self.view.highlight.temp[self.hold_pos.page] = boxes + end + UIManager:setDirty(self.dialog, "partial") + end + return true end function ReaderHighlight:onHoldPan(arg, ges) - if self.hold_pos == nil then - DEBUG("no previous hold position") - return true - end - self.holdpan_pos = self.view:screenToPageTransform(ges.pos) - DEBUG("holdpan position in page", self.holdpan_pos) - self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.holdpan_pos) - DEBUG("selected text:", self.selected_text) - if self.selected_text then - self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes - -- remove selected word if hold moves out of word box - if not self.selected_text.sboxes or #self.selected_text.sboxes == 0 then - self.selected_word = nil - elseif self.selected_word and not self.selected_word.sbox:contains(self.selected_text.sboxes[1]) or - #self.selected_text.sboxes > 1 then - self.selected_word = nil - end - end - UIManager:setDirty(self.dialog, "partial") + if self.hold_pos == nil then + DEBUG("no previous hold position") + return true + end + self.holdpan_pos = self.view:screenToPageTransform(ges.pos) + DEBUG("holdpan position in page", self.holdpan_pos) + self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.holdpan_pos) + DEBUG("selected text:", self.selected_text) + if self.selected_text then + self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes + -- remove selected word if hold moves out of word box + if not self.selected_text.sboxes or #self.selected_text.sboxes == 0 then + self.selected_word = nil + elseif self.selected_word and not self.selected_word.sbox:contains(self.selected_text.sboxes[1]) or + #self.selected_text.sboxes > 1 then + self.selected_word = nil + end + end + UIManager:setDirty(self.dialog, "partial") end function ReaderHighlight:lookup(selected_word) - -- if we extracted text directly - if selected_word.word then - local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox) - self.ui:handleEvent(Event:new("LookupWord", self, selected_word.word, word_box)) - -- or we will do OCR - elseif selected_word.sbox and self.hold_pos then - local word = self.ui.document:getOCRWord(self.hold_pos.page, selected_word) - DEBUG("OCRed word:", word) - local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox) - self.ui:handleEvent(Event:new("LookupWord", self, word, word_box)) - end + -- if we extracted text directly + if selected_word.word then + local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox) + self.ui:handleEvent(Event:new("LookupWord", self, selected_word.word, word_box)) + -- or we will do OCR + elseif selected_word.sbox and self.hold_pos then + local word = self.ui.document:getOCRWord(self.hold_pos.page, selected_word) + DEBUG("OCRed word:", word) + local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox) + self.ui:handleEvent(Event:new("LookupWord", self, word, word_box)) + end end function ReaderHighlight:translate(selected_text) - if selected_text.text ~= "" then - self.ui:handleEvent(Event:new("TranslateText", self, selected_text.text)) - -- or we will do OCR - else - local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text) - DEBUG("OCRed text:", text) - self.ui:handleEvent(Event:new("TranslateText", self, text)) - end + if selected_text.text ~= "" then + self.ui:handleEvent(Event:new("TranslateText", self, selected_text.text)) + -- or we will do OCR + else + local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text) + DEBUG("OCRed text:", text) + self.ui:handleEvent(Event:new("TranslateText", self, text)) + end end function ReaderHighlight:onHoldRelease(arg, ges) - if self.selected_word then - self:lookup(self.selected_word) - self.selected_word = nil - elseif self.selected_text then - DEBUG("show highlight dialog") - self.highlight_dialog = ButtonDialog:new{ - buttons = { - { - { - text = _("Highlight"), - callback = function() - self:saveHighlight() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) - end, - }, - { - text = _("Add Note"), - enabled = false, - callback = function() - self:addNote() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) - end, - }, - }, - { - { - text = _("Translate"), - 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")) - end, - }, - }, - { - { - text = _("More"), - enabled = false, - callback = function() - self:moreAction() - UIManager:close(self.highlight_dialog) - self:handleEvent(Event:new("Tap")) - end, - }, - }, - }, - tap_close_callback = function() self:handleEvent(Event:new("Tap")) end, - } - UIManager:show(self.highlight_dialog) - end - return true + if self.selected_word then + self:lookup(self.selected_word) + self.selected_word = nil + elseif self.selected_text then + DEBUG("show highlight dialog") + self.highlight_dialog = ButtonDialog:new{ + buttons = { + { + { + text = _("Highlight"), + callback = function() + self:saveHighlight() + UIManager:close(self.highlight_dialog) + self:handleEvent(Event:new("Tap")) + end, + }, + { + text = _("Add Note"), + enabled = false, + callback = function() + self:addNote() + UIManager:close(self.highlight_dialog) + self:handleEvent(Event:new("Tap")) + end, + }, + }, + { + { + text = _("Translate"), + 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")) + end, + }, + }, + { + { + text = _("More"), + enabled = false, + callback = function() + self:moreAction() + UIManager:close(self.highlight_dialog) + self:handleEvent(Event:new("Tap")) + end, + }, + }, + }, + tap_close_callback = function() self:handleEvent(Event:new("Tap")) end, + } + UIManager:show(self.highlight_dialog) + end + return true end function ReaderHighlight:saveHighlight() - DEBUG("save highlight") - local page = self.hold_pos.page - if self.hold_pos and self.selected_text then - if not self.view.highlight.saved[page] then - self.view.highlight.saved[page] = {} - end - local hl_item = {} - hl_item["text"] = self.selected_text.text - hl_item["pos0"] = self.selected_text.pos0 - hl_item["pos1"] = self.selected_text.pos1 - hl_item["pboxes"] = self.selected_text.pboxes - hl_item["datetime"] = os.date("%Y-%m-%d %H:%M:%S") - hl_item["drawer"] = self.view.highlight.saved_drawer - table.insert(self.view.highlight.saved[page], hl_item) - if self.selected_text.text ~= "" then - self:exportToClippings(page, hl_item) - end - if self.selected_text.pboxes then - self:exportToDocument(page, hl_item) - end - end - --DEBUG("saved hightlights", self.view.highlight.saved[page]) + DEBUG("save highlight") + local page = self.hold_pos.page + if self.hold_pos and self.selected_text then + if not self.view.highlight.saved[page] then + self.view.highlight.saved[page] = {} + end + local hl_item = {} + hl_item["text"] = self.selected_text.text + hl_item["pos0"] = self.selected_text.pos0 + hl_item["pos1"] = self.selected_text.pos1 + hl_item["pboxes"] = self.selected_text.pboxes + hl_item["datetime"] = os.date("%Y-%m-%d %H:%M:%S") + hl_item["drawer"] = self.view.highlight.saved_drawer + table.insert(self.view.highlight.saved[page], hl_item) + if self.selected_text.text ~= "" then + self:exportToClippings(page, hl_item) + end + if self.selected_text.pboxes then + self:exportToDocument(page, hl_item) + end + end + --DEBUG("saved hightlights", self.view.highlight.saved[page]) end function ReaderHighlight:exportToClippings(page, item) - DEBUG("export highlight to clippings", item) - local clippings = io.open("/mnt/us/documents/My Clippings.txt", "a+") - if clippings and item.text then - local current_locale = os.setlocale() - os.setlocale("C") - clippings:write(self.document.file:gsub("(.*/)(.*)", "%2").."\n") - clippings:write("- Koreader Highlight Page "..page.." ") - clippings:write("| Added on "..os.date("%A, %b %d, %Y %I:%M:%S %p\n\n")) - clippings:write(item["text"].."\n") - clippings:write("==========\n") - clippings:close() - os.setlocale(current_locale) - end + DEBUG("export highlight to clippings", item) + local clippings = io.open("/mnt/us/documents/My Clippings.txt", "a+") + if clippings and item.text then + local current_locale = os.setlocale() + os.setlocale("C") + clippings:write(self.document.file:gsub("(.*/)(.*)", "%2").."\n") + clippings:write("- Koreader Highlight Page "..page.." ") + clippings:write("| Added on "..os.date("%A, %b %d, %Y %I:%M:%S %p\n\n")) + clippings:write(item["text"].."\n") + clippings:write("==========\n") + clippings:close() + os.setlocale(current_locale) + end end function ReaderHighlight:exportToDocument(page, item) - DEBUG("export highlight to document", item) - self.ui.document:saveHighlight(page, item) + DEBUG("export highlight to document", item) + self.ui.document:saveHighlight(page, item) end function ReaderHighlight:addNote() - DEBUG("add Note") + DEBUG("add Note") end function ReaderHighlight:shareHighlight() - DEBUG("share highlight") + DEBUG("share highlight") end function ReaderHighlight:moreAction() - DEBUG("more action") + DEBUG("more action") end function ReaderHighlight:deleteHighlight(page, i) - DEBUG("delete highlight") - table.remove(self.view.highlight.saved[page], i) + DEBUG("delete highlight") + table.remove(self.view.highlight.saved[page], i) end function ReaderHighlight:editHighlight() - DEBUG("edit highlight") + DEBUG("edit highlight") end function ReaderHighlight:onReadSettings(config) - self.view.highlight.saved_drawer = config:readSetting("highlight_drawer") or self.view.highlight.saved_drawer + self.view.highlight.saved_drawer = config:readSetting("highlight_drawer") or self.view.highlight.saved_drawer end function ReaderHighlight:onSaveSettings() - self.ui.doc_settings:saveSetting("highlight_drawer", self.view.highlight.saved_drawer) + self.ui.doc_settings:saveSetting("highlight_drawer", self.view.highlight.saved_drawer) end return ReaderHighlight diff --git a/frontend/ui/reader/readerhinting.lua b/frontend/ui/reader/readerhinting.lua index ce6a1d0f4..c8abd2601 100644 --- a/frontend/ui/reader/readerhinting.lua +++ b/frontend/ui/reader/readerhinting.lua @@ -1,37 +1,37 @@ local EventListener = require("ui/widget/eventlistener") local ReaderHinting = EventListener:new{ - hinting_states = {} + hinting_states = {} } function ReaderHinting:onHintPage() - if not self.view.hinting then return true end - for i=1, DHINTCOUNT do - if self.view.state.page + i <= self.ui.document.info.number_of_pages then - self.ui.document:hintPage( - self.view.state.page + i, - self.zoom:getZoom(self.view.state.page + i), - self.view.state.rotation, - self.view.state.gamma, - self.view.render_mode) - end - end - return true + if not self.view.hinting then return true end + for i=1, DHINTCOUNT do + if self.view.state.page + i <= self.ui.document.info.number_of_pages then + self.ui.document:hintPage( + self.view.state.page + i, + self.zoom:getZoom(self.view.state.page + i), + self.view.state.rotation, + self.view.state.gamma, + self.view.render_mode) + end + end + return true end function ReaderHinting:onSetHinting(hinting) - self.view.hinting = hinting + self.view.hinting = hinting end function ReaderHinting:onDisableHinting() - table.insert(self.hinting_states, self.view.hinting) - self.view.hinting = false - return true + table.insert(self.hinting_states, self.view.hinting) + self.view.hinting = false + return true end function ReaderHinting:onRestoreHinting() - self.view.hinting = table.remove(self.hinting_states) - return true + self.view.hinting = table.remove(self.hinting_states) + return true end return ReaderHinting diff --git a/frontend/ui/reader/readerhyphenation.lua b/frontend/ui/reader/readerhyphenation.lua index bbf1a2daa..20455dec5 100644 --- a/frontend/ui/reader/readerhyphenation.lua +++ b/frontend/ui/reader/readerhyphenation.lua @@ -4,52 +4,52 @@ local InfoMessage = require("ui/widget/infomessage") local _ = require("gettext") local ReaderHyphenation = InputContainer:new{ - hyph_menu_title = _("Hyphenation"), - hyph_table = nil, - cur_hyph_idx = nil, + hyph_menu_title = _("Hyphenation"), + hyph_table = nil, + cur_hyph_idx = nil, } function ReaderHyphenation:_changeSel(k) - if self.cur_hyph_idx then - self.hyph_table[self.cur_hyph_idx].selected = false - end - self.hyph_table[k].selected = true - self.cur_hyph_idx = k + if self.cur_hyph_idx then + self.hyph_table[self.cur_hyph_idx].selected = false + end + self.hyph_table[k].selected = true + self.cur_hyph_idx = k end function ReaderHyphenation:init() - self.hyph_table = {} - self.hyph_alg = cre.getSelectedHyphDict() - for k,v in ipairs(cre.getHyphDictList()) do - if v == self.hyph_alg then - self.cur_hyph_idx = k - end - table.insert(self.hyph_table, { - text = v, - callback = function() - self.hyph_alg = v - UIManager:show(InfoMessage:new{ - text = _("Change Hyphenation to ")..v, - }) - self:_changeSel(k) - cre.setHyphDictionary(v) - end - }) - end - self.ui.menu:registerToMainMenu(self) + self.hyph_table = {} + self.hyph_alg = cre.getSelectedHyphDict() + for k,v in ipairs(cre.getHyphDictList()) do + if v == self.hyph_alg then + self.cur_hyph_idx = k + end + table.insert(self.hyph_table, { + text = v, + callback = function() + self.hyph_alg = v + UIManager:show(InfoMessage:new{ + text = _("Change Hyphenation to ")..v, + }) + self:_changeSel(k) + cre.setHyphDictionary(v) + end + }) + end + self.ui.menu:registerToMainMenu(self) end function ReaderHyphenation:onReadSettings(config) - local hyph_alg = config:readSetting("hyph_alg") - if hyph_alg then - cre.setHyphDictionary(hyph_alg) - end - self.hyph_alg = cre.getSelectedHyphDict() - for k,v in ipairs(self.hyph_table) do - if v.text == self.hyph_alg then - self:_changeSel(k) - end - end + local hyph_alg = config:readSetting("hyph_alg") + if hyph_alg then + cre.setHyphDictionary(hyph_alg) + end + self.hyph_alg = cre.getSelectedHyphDict() + for k,v in ipairs(self.hyph_table) do + if v.text == self.hyph_alg then + self:_changeSel(k) + end + end end function ReaderHyphenation:onSaveSettings() @@ -57,11 +57,11 @@ function ReaderHyphenation:onSaveSettings() end function ReaderHyphenation:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.typeset, { - text = self.hyph_menu_title, - sub_item_table = self.hyph_table, - }) + -- insert table to main reader menu + table.insert(tab_item_table.typeset, { + text = self.hyph_menu_title, + sub_item_table = self.hyph_table, + }) end return ReaderHyphenation diff --git a/frontend/ui/reader/readerkoptlistener.lua b/frontend/ui/reader/readerkoptlistener.lua index 6eec15888..c4cd75913 100644 --- a/frontend/ui/reader/readerkoptlistener.lua +++ b/frontend/ui/reader/readerkoptlistener.lua @@ -4,56 +4,56 @@ local Event = require("ui/event") local ReaderKoptListener = EventListener:new{} function ReaderKoptListener:setZoomMode(zoom_mode) - if self.document.configurable.text_wrap == 1 then - -- in reflow mode only "page" zoom mode is valid so override any other zoom mode - self.ui:handleEvent(Event:new("SetZoomMode", "page", "koptlistener")) - else - self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode, "koptlistener")) - end + if self.document.configurable.text_wrap == 1 then + -- in reflow mode only "page" zoom mode is valid so override any other zoom mode + self.ui:handleEvent(Event:new("SetZoomMode", "page", "koptlistener")) + else + self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode, "koptlistener")) + end end function ReaderKoptListener:onReadSettings(config) - -- normal zoom mode is zoom mode used in non-reflow mode. - self.normal_zoom_mode = config:readSetting("normal_zoom_mode") or "page" - self:setZoomMode(self.normal_zoom_mode) + -- normal zoom mode is zoom mode used in non-reflow mode. + self.normal_zoom_mode = config:readSetting("normal_zoom_mode") or "page" + self:setZoomMode(self.normal_zoom_mode) end function ReaderKoptListener:onSaveSettings() - self.ui.doc_settings:saveSetting("normal_zoom_mode", self.normal_zoom_mode) + self.ui.doc_settings:saveSetting("normal_zoom_mode", self.normal_zoom_mode) end function ReaderKoptListener:onRestoreZoomMode() - -- "RestoreZoomMode" event is sent when reflow mode on/off is toggled - self:setZoomMode(self.normal_zoom_mode) - return true + -- "RestoreZoomMode" event is sent when reflow mode on/off is toggled + self:setZoomMode(self.normal_zoom_mode) + return true end function ReaderKoptListener:onSetZoomMode(zoom_mode, orig) - if orig == "koptlistener" then return end - -- capture zoom mode set outside of koptlistener which should always be normal zoom mode - self.normal_zoom_mode = zoom_mode - self:setZoomMode(self.normal_zoom_mode) + if orig == "koptlistener" then return end + -- capture zoom mode set outside of koptlistener which should always be normal zoom mode + self.normal_zoom_mode = zoom_mode + self:setZoomMode(self.normal_zoom_mode) end function ReaderKoptListener:onFineTuningFontSize(delta) - self.document.configurable.font_size = self.document.configurable.font_size + delta + self.document.configurable.font_size = self.document.configurable.font_size + delta end function ReaderKoptListener:onZoomUpdate(zoom) - -- an exceptional case is reflow mode - if self.document.configurable.text_wrap == 1 then - self.view.state.zoom = 1.0 - end + -- an exceptional case is reflow mode + if self.document.configurable.text_wrap == 1 then + self.view.state.zoom = 1.0 + end end -- misc koptoption handler function ReaderKoptListener:onDocLangUpdate(lang) - if lang == "chi_sim" or lang == "chi_tra" or - lang == "jpn" or lang == "kor" then - self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[1] - else - self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[3] - end + if lang == "chi_sim" or lang == "chi_tra" or + lang == "jpn" or lang == "kor" then + self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[1] + else + self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[3] + end end return ReaderKoptListener diff --git a/frontend/ui/reader/readerlink.lua b/frontend/ui/reader/readerlink.lua index 1d7764cad..f6ca63853 100644 --- a/frontend/ui/reader/readerlink.lua +++ b/frontend/ui/reader/readerlink.lua @@ -7,100 +7,100 @@ local Event = require("ui/event") local DEBUG = require("dbg") local ReaderLink = InputContainer:new{ - link_states = {} + link_states = {} } function ReaderLink:init() - if Device:isTouchDevice() then - self:initGesListener() - end + if Device:isTouchDevice() then + self:initGesListener() + end end function ReaderLink:onReadSettings(config) - -- called when loading new document - self.link_states = {} + -- called when loading new document + self.link_states = {} end function ReaderLink:initGesListener() - if Device:isTouchDevice() then - self.ges_events = { - Tap = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight() - } - } - }, - Swipe = { - GestureRange:new{ - ges = "swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - } - end + if Device:isTouchDevice() then + self.ges_events = { + Tap = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight() + } + } + }, + Swipe = { + GestureRange:new{ + ges = "swipe", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + } + end end function ReaderLink:onSetDimensions(dimen) - -- update listening according to new screen dimen - if Device:isTouchDevice() then - self:initGesListener() - end + -- update listening according to new screen dimen + if Device:isTouchDevice() then + self:initGesListener() + end end function ReaderLink:onTap(arg, ges) - if self.ui.document.info.has_pages then - local pos = self.view:screenToPageTransform(ges.pos) - if pos then - local link = self.ui.document:getLinkFromPosition(pos.page, pos) - if link then - return self:onGotoLink(link) - end - end - else - local link = self.ui.document:getLinkFromPosition(ges.pos) - if link ~= "" then - return self:onGotoLink(link) - end - end + if self.ui.document.info.has_pages then + local pos = self.view:screenToPageTransform(ges.pos) + if pos then + local link = self.ui.document:getLinkFromPosition(pos.page, pos) + if link then + return self:onGotoLink(link) + end + end + else + local link = self.ui.document:getLinkFromPosition(ges.pos) + if link ~= "" then + return self:onGotoLink(link) + end + end end function ReaderLink:onGotoLink(link) - if self.ui.document.info.has_pages then - table.insert(self.link_states, self.view.state.page) - self.ui:handleEvent(Event:new("PageUpdate", link.page + 1)) - else - table.insert(self.link_states, self.ui.document:getXPointer()) - self.document:gotoLink(link) - self.ui:handleEvent(Event:new("UpdateXPointer")) - end - return true + if self.ui.document.info.has_pages then + table.insert(self.link_states, self.view.state.page) + self.ui:handleEvent(Event:new("PageUpdate", link.page + 1)) + else + table.insert(self.link_states, self.ui.document:getXPointer()) + self.document:gotoLink(link) + self.ui:handleEvent(Event:new("UpdateXPointer")) + end + return true end function ReaderLink:onSwipe(arg, ges) - if ges.direction == "east" then - if self.ui.document.info.has_pages then - local last_page = table.remove(self.link_states) - if last_page then - self.ui:handleEvent(Event:new("PageUpdate", last_page)) - return true - end - else - local last_xp = table.remove(self.link_states) - if last_xp then - self.ui.document:gotoXPointer(last_xp) - self.ui:handleEvent(Event:new("UpdateXPointer")) - return true - end - end - end + if ges.direction == "east" then + if self.ui.document.info.has_pages then + local last_page = table.remove(self.link_states) + if last_page then + self.ui:handleEvent(Event:new("PageUpdate", last_page)) + return true + end + else + local last_xp = table.remove(self.link_states) + if last_xp then + self.ui.document:gotoXPointer(last_xp) + self.ui:handleEvent(Event:new("UpdateXPointer")) + return true + end + end + end end return ReaderLink diff --git a/frontend/ui/reader/readermenu.lua b/frontend/ui/reader/readermenu.lua index d41ff313c..e32c3bd19 100644 --- a/frontend/ui/reader/readermenu.lua +++ b/frontend/ui/reader/readermenu.lua @@ -13,146 +13,146 @@ local Language = require("ui/language") local _ = require("gettext") local ReaderMenu = InputContainer:new{ - tab_item_table = nil, - registered_widgets = {}, + tab_item_table = nil, + registered_widgets = {}, } function ReaderMenu:init() - self.tab_item_table = { - main = { - icon = "resources/icons/appbar.pokeball.png", - }, - navi = { - icon = "resources/icons/appbar.page.corner.bookmark.png", - }, - typeset = { - icon = "resources/icons/appbar.page.text.png", - }, - home = { - icon = "resources/icons/appbar.home.png", - callback = function() - self.ui:handleEvent(Event:new("RestoreScreenMode", - G_reader_settings:readSetting("screen_mode") or "portrait")) - UIManager:close(self.menu_container) - self.ui:onClose() - end, - }, - } - self.registered_widgets = {} - - if Device:hasKeyboard() then - self.key_events = { - ShowMenu = { { "Menu" }, doc = _("show menu") }, - } - end + self.tab_item_table = { + main = { + icon = "resources/icons/appbar.pokeball.png", + }, + navi = { + icon = "resources/icons/appbar.page.corner.bookmark.png", + }, + typeset = { + icon = "resources/icons/appbar.page.text.png", + }, + home = { + icon = "resources/icons/appbar.home.png", + callback = function() + self.ui:handleEvent(Event:new("RestoreScreenMode", + G_reader_settings:readSetting("screen_mode") or "portrait")) + UIManager:close(self.menu_container) + self.ui:onClose() + end, + }, + } + self.registered_widgets = {} + + if Device:hasKeyboard() then + self.key_events = { + ShowMenu = { { "Menu" }, doc = _("show menu") }, + } + end end function ReaderMenu:initGesListener() - self.ges_events = { - TapShowMenu = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_MENU.x, - y = Screen:getHeight()*DTAP_ZONE_MENU.y, - w = Screen:getWidth()*DTAP_ZONE_MENU.w, - h = Screen:getHeight()*DTAP_ZONE_MENU.h - } - } - }, - } + self.ges_events = { + TapShowMenu = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_MENU.x, + y = Screen:getHeight()*DTAP_ZONE_MENU.y, + w = Screen:getWidth()*DTAP_ZONE_MENU.w, + h = Screen:getHeight()*DTAP_ZONE_MENU.h + } + } + }, + } end function ReaderMenu:setUpdateItemTable() - for _, widget in pairs(self.registered_widgets) do - widget:addToMainMenu(self.tab_item_table) - end - - table.insert(self.tab_item_table.main, { - text = _("Help"), - callback = function() - UIManager:show(InfoMessage:new{ - text = _("Please report bugs to https://github.com/koreader/ koreader/issues, Click at the bottom of the page for more options"), - }) - end - }) - table.insert(self.tab_item_table.main, { - text = _("Version"), - callback = function() - UIManager:show(InfoMessage:new{ - text = io.open("git-rev", "r"):read(), - }) - end - }) - table.insert(self.tab_item_table.main, Language:getLangMenuTable()) + for _, widget in pairs(self.registered_widgets) do + widget:addToMainMenu(self.tab_item_table) + end + + table.insert(self.tab_item_table.main, { + text = _("Help"), + callback = function() + UIManager:show(InfoMessage:new{ + text = _("Please report bugs to https://github.com/koreader/ koreader/issues, Click at the bottom of the page for more options"), + }) + end + }) + table.insert(self.tab_item_table.main, { + text = _("Version"), + callback = function() + UIManager:show(InfoMessage:new{ + text = io.open("git-rev", "r"):read(), + }) + end + }) + table.insert(self.tab_item_table.main, Language:getLangMenuTable()) end function ReaderMenu:onShowReaderMenu() - if #self.tab_item_table.main == 0 then - self:setUpdateItemTable() - end - - local menu_container = CenterContainer:new{ - ignore = "height", - dimen = Screen:getSize(), - } - - local main_menu = nil - if Device:isTouchDevice() then - main_menu = TouchMenu:new{ - width = Screen:getWidth(), - tab_item_table = { - self.tab_item_table.navi, - self.tab_item_table.typeset, - self.tab_item_table.main, - self.tab_item_table.home, - }, - show_parent = menu_container, - } - else - main_menu = Menu:new{ - title = _("Document menu"), - item_table = {}, - width = Screen:getWidth() - 100, - } - - for _,item_table in pairs(self.tab_item_table) do - for k,v in ipairs(item_table) do - table.insert(main_menu.item_table, v) - end - end - end - - main_menu.close_callback = function () - UIManager:close(menu_container) - end - - menu_container[1] = main_menu - -- maintain a reference to menu_container - self.menu_container = menu_container - UIManager:show(menu_container) - - return true + if #self.tab_item_table.main == 0 then + self:setUpdateItemTable() + end + + local menu_container = CenterContainer:new{ + ignore = "height", + dimen = Screen:getSize(), + } + + local main_menu = nil + if Device:isTouchDevice() then + main_menu = TouchMenu:new{ + width = Screen:getWidth(), + tab_item_table = { + self.tab_item_table.navi, + self.tab_item_table.typeset, + self.tab_item_table.main, + self.tab_item_table.home, + }, + show_parent = menu_container, + } + else + main_menu = Menu:new{ + title = _("Document menu"), + item_table = {}, + width = Screen:getWidth() - 100, + } + + for _,item_table in pairs(self.tab_item_table) do + for k,v in ipairs(item_table) do + table.insert(main_menu.item_table, v) + end + end + end + + main_menu.close_callback = function () + UIManager:close(menu_container) + end + + menu_container[1] = main_menu + -- maintain a reference to menu_container + self.menu_container = menu_container + UIManager:show(menu_container) + + return true end function ReaderMenu:onTapShowMenu() - self.ui:handleEvent(Event:new("ShowConfigMenu")) - self.ui:handleEvent(Event:new("ShowReaderMenu")) - return true + self.ui:handleEvent(Event:new("ShowConfigMenu")) + self.ui:handleEvent(Event:new("ShowReaderMenu")) + return true end function ReaderMenu:onSetDimensions(dimen) - -- update listening according to new screen dimen - if Device:isTouchDevice() then - self:initGesListener() - end + -- update listening according to new screen dimen + if Device:isTouchDevice() then + self:initGesListener() + end end function ReaderMenu:onSaveSettings() end function ReaderMenu:registerToMainMenu(widget) - table.insert(self.registered_widgets, widget) + table.insert(self.registered_widgets, widget) end return ReaderMenu diff --git a/frontend/ui/reader/readerpaging.lua b/frontend/ui/reader/readerpaging.lua index 48d7f48a7..48e9297fa 100644 --- a/frontend/ui/reader/readerpaging.lua +++ b/frontend/ui/reader/readerpaging.lua @@ -11,143 +11,143 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderPaging = InputContainer:new{ - current_page = 0, - number_of_pages = 0, - last_pan_relative_y = 0, - visible_area = nil, - page_area = nil, - show_overlap_enable = nil, - overlap = Screen:scaleByDPI(DOVERLAPPIXELS), - - page_flipping_mode = false, - bookmark_flipping_mode = false, - flip_steps = {0,1,2,5,10,20,50,100} + current_page = 0, + number_of_pages = 0, + last_pan_relative_y = 0, + visible_area = nil, + page_area = nil, + show_overlap_enable = nil, + overlap = Screen:scaleByDPI(DOVERLAPPIXELS), + + page_flipping_mode = false, + bookmark_flipping_mode = false, + flip_steps = {0,1,2,5,10,20,50,100} } function ReaderPaging:init() - if Device:hasKeyboard() then - self.key_events = { - GotoNextPage = { - {Input.group.PgFwd}, doc = "go to next page", - event = "GotoPageRel", args = 1 }, - GotoPrevPage = { - {Input.group.PgBack}, doc = "go to previous page", - event = "GotoPageRel", args = -1 }, - - GotoFirst = { - {"1"}, doc = "go to start", event = "GotoPercent", args = 0}, - Goto11 = { - {"2"}, doc = "go to 11%", event = "GotoPercent", args = 11}, - Goto22 = { - {"3"}, doc = "go to 22%", event = "GotoPercent", args = 22}, - Goto33 = { - {"4"}, doc = "go to 33%", event = "GotoPercent", args = 33}, - Goto44 = { - {"5"}, doc = "go to 44%", event = "GotoPercent", args = 44}, - Goto55 = { - {"6"}, doc = "go to 55%", event = "GotoPercent", args = 55}, - Goto66 = { - {"7"}, doc = "go to 66%", event = "GotoPercent", args = 66}, - Goto77 = { - {"8"}, doc = "go to 77%", event = "GotoPercent", args = 77}, - Goto88 = { - {"9"}, doc = "go to 88%", event = "GotoPercent", args = 88}, - GotoLast = { - {"0"}, doc = "go to end", event = "GotoPercent", args = 100}, - } - end - self.number_of_pages = self.ui.document.info.number_of_pages - self.ui.menu:registerToMainMenu(self) + if Device:hasKeyboard() then + self.key_events = { + GotoNextPage = { + {Input.group.PgFwd}, doc = "go to next page", + event = "GotoPageRel", args = 1 }, + GotoPrevPage = { + {Input.group.PgBack}, doc = "go to previous page", + event = "GotoPageRel", args = -1 }, + + GotoFirst = { + {"1"}, doc = "go to start", event = "GotoPercent", args = 0}, + Goto11 = { + {"2"}, doc = "go to 11%", event = "GotoPercent", args = 11}, + Goto22 = { + {"3"}, doc = "go to 22%", event = "GotoPercent", args = 22}, + Goto33 = { + {"4"}, doc = "go to 33%", event = "GotoPercent", args = 33}, + Goto44 = { + {"5"}, doc = "go to 44%", event = "GotoPercent", args = 44}, + Goto55 = { + {"6"}, doc = "go to 55%", event = "GotoPercent", args = 55}, + Goto66 = { + {"7"}, doc = "go to 66%", event = "GotoPercent", args = 66}, + Goto77 = { + {"8"}, doc = "go to 77%", event = "GotoPercent", args = 77}, + Goto88 = { + {"9"}, doc = "go to 88%", event = "GotoPercent", args = 88}, + GotoLast = { + {"0"}, doc = "go to end", event = "GotoPercent", args = 100}, + } + end + self.number_of_pages = self.ui.document.info.number_of_pages + self.ui.menu:registerToMainMenu(self) end -- This method will be called in onSetDimensions handler function ReaderPaging:initGesListener() - self.ges_events = { - TapForward = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_FORWARD.x, - y = Screen:getHeight()*DTAP_ZONE_FORWARD.y, - w = Screen:getWidth()*DTAP_ZONE_FORWARD.w, - h = Screen:getHeight()*DTAP_ZONE_FORWARD.h, - } - } - }, - TapBackward = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x, - y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y, - w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w, - h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h, - } - } - }, - Swipe = { - GestureRange:new{ - ges = "swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - Pan = { - GestureRange:new{ - ges = "pan", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - }, - rate = 4.0, - } - }, - PanRelease = { - GestureRange:new{ - ges = "pan_release", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - }, - } - }, - } + self.ges_events = { + TapForward = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_FORWARD.x, + y = Screen:getHeight()*DTAP_ZONE_FORWARD.y, + w = Screen:getWidth()*DTAP_ZONE_FORWARD.w, + h = Screen:getHeight()*DTAP_ZONE_FORWARD.h, + } + } + }, + TapBackward = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x, + y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y, + w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w, + h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h, + } + } + }, + Swipe = { + GestureRange:new{ + ges = "swipe", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + Pan = { + GestureRange:new{ + ges = "pan", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + }, + rate = 4.0, + } + }, + PanRelease = { + GestureRange:new{ + ges = "pan_release", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + }, + } + }, + } end function ReaderPaging:onReadSettings(config) - self.page_positions = config:readSetting("page_positions") or {} - self:gotoPage(config:readSetting("last_page") or 1) - self.show_overlap_enable = config:readSetting("show_overlap_enable") - if self.show_overlap_enable == nil then - self.show_overlap_enable = DSHOWOVERLAP - end + self.page_positions = config:readSetting("page_positions") or {} + self:gotoPage(config:readSetting("last_page") or 1) + self.show_overlap_enable = config:readSetting("show_overlap_enable") + if self.show_overlap_enable == nil then + self.show_overlap_enable = DSHOWOVERLAP + end end function ReaderPaging:onSaveSettings() - self.ui.doc_settings:saveSetting("page_positions", self.page_positions) - self.ui.doc_settings:saveSetting("last_page", self:getTopPage()) - self.ui.doc_settings:saveSetting("percent_finished", self.current_page/self.number_of_pages) - self.ui.doc_settings:saveSetting("show_overlap_enable", self.show_overlap_enable) + self.ui.doc_settings:saveSetting("page_positions", self.page_positions) + self.ui.doc_settings:saveSetting("last_page", self:getTopPage()) + self.ui.doc_settings:saveSetting("percent_finished", self.current_page/self.number_of_pages) + self.ui.doc_settings:saveSetting("show_overlap_enable", self.show_overlap_enable) end function ReaderPaging:addToMainMenu(tab_item_table) - if self.ui.document.info.has_pages then - table.insert(tab_item_table.typeset, { - text_func = function() - return self.show_overlap_enable and - _("Turn off page overlap") or - _("Turn on page overlap") - end, - callback = function() - self.show_overlap_enable = not self.show_overlap_enable - end - }) - end + if self.ui.document.info.has_pages then + table.insert(tab_item_table.typeset, { + text_func = function() + return self.show_overlap_enable and + _("Turn off page overlap") or + _("Turn on page overlap") + end, + callback = function() + self.show_overlap_enable = not self.show_overlap_enable + end + }) + end end --[[ @@ -157,610 +157,610 @@ certain page. With the position information on each page whenever users change f page margin or line spacing or close and reopen the book, the page view will be roughly the same. --]] function ReaderPaging:setPagePosition(page, pos) - DEBUG("set page position", pos) - self.page_positions[page] = pos + DEBUG("set page position", pos) + self.page_positions[page] = pos end --[[ Get reading position on certain page --]] function ReaderPaging:getPagePosition(page) - -- Page number ought to be integer, somehow I notice that with - -- fractional page number the reader runs silently well, but the - -- number won't fit to retrieve page position. - page = math.floor(page) - DEBUG("get page position", self.page_positions[page]) - return self.page_positions[page] or 0 + -- Page number ought to be integer, somehow I notice that with + -- fractional page number the reader runs silently well, but the + -- number won't fit to retrieve page position. + page = math.floor(page) + DEBUG("get page position", self.page_positions[page]) + return self.page_positions[page] or 0 end function ReaderPaging:onTapForward() - self:onPagingRel(1) - return true + self:onPagingRel(1) + return true end function ReaderPaging:onTapBackward() - self:onPagingRel(-1) - return true + self:onPagingRel(-1) + return true end function ReaderPaging:onTogglePageFlipping() - self.view.flipping_visible = not self.view.flipping_visible - self.page_flipping_mode = self.view.flipping_visible - self.flipping_page = self.current_page - - if self.page_flipping_mode then - self:updateOriginalPage(self.current_page) - self:enterFlippingMode() - else - self:updateOriginalPage(nil) - self:exitFlippingMode() - end - self.view:resetLayout() - self.ui:handleEvent(Event:new("SetHinting", not self.page_flipping_mode)) - self.ui:handleEvent(Event:new("ReZoom")) - UIManager:setDirty(self.view.dialog, "partial") + self.view.flipping_visible = not self.view.flipping_visible + self.page_flipping_mode = self.view.flipping_visible + self.flipping_page = self.current_page + + if self.page_flipping_mode then + self:updateOriginalPage(self.current_page) + self:enterFlippingMode() + else + self:updateOriginalPage(nil) + self:exitFlippingMode() + end + self.view:resetLayout() + self.ui:handleEvent(Event:new("SetHinting", not self.page_flipping_mode)) + self.ui:handleEvent(Event:new("ReZoom")) + UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:onToggleBookmarkFlipping() - self.bookmark_flipping_mode = not self.bookmark_flipping_mode - - if self.bookmark_flipping_mode then - self.orig_flipping_mode = self.view.flipping_visible - self.orig_dogear_mode = self.view.dogear_visible - - self.view.flipping_visible = true - self.view.dogear_visible = true - self.bm_flipping_orig_page = self.current_page - self:enterFlippingMode() - else - self.view.flipping_visible = self.orig_flipping_mode - self.view.dogear_visible = self.orig_dogear_mode - self:exitFlippingMode() - self:gotoPage(self.bm_flipping_orig_page) - end - self.view:resetLayout() - self.ui:handleEvent(Event:new("SetHinting", not self.bookmark_flipping_mode)) - self.ui:handleEvent(Event:new("ReZoom")) - UIManager:setDirty(self.view.dialog, "partial") + self.bookmark_flipping_mode = not self.bookmark_flipping_mode + + if self.bookmark_flipping_mode then + self.orig_flipping_mode = self.view.flipping_visible + self.orig_dogear_mode = self.view.dogear_visible + + self.view.flipping_visible = true + self.view.dogear_visible = true + self.bm_flipping_orig_page = self.current_page + self:enterFlippingMode() + else + self.view.flipping_visible = self.orig_flipping_mode + self.view.dogear_visible = self.orig_dogear_mode + self:exitFlippingMode() + self:gotoPage(self.bm_flipping_orig_page) + end + self.view:resetLayout() + self.ui:handleEvent(Event:new("SetHinting", not self.bookmark_flipping_mode)) + self.ui:handleEvent(Event:new("ReZoom")) + UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:enterFlippingMode() - self.orig_reflow_mode = self.view.document.configurable.text_wrap - self.orig_footer_mode = self.view.footer_visible - self.orig_scroll_mode = self.view.page_scroll - self.orig_zoom_mode = self.view.zoom_mode - DEBUG("store zoom mode", self.orig_zoom_mode) - self.DGESDETECT_DISABLE_DOUBLE_TAP = DGESDETECT_DISABLE_DOUBLE_TAP - - self.view.document.configurable.text_wrap = 0 - self.view.page_scroll = false - self.view.footer_visible = true - Input.disable_double_tap = false - DGESDETECT_DISABLE_DOUBLE_TAP = false - self.ui:handleEvent(Event:new("SetZoomMode", "page")) + self.orig_reflow_mode = self.view.document.configurable.text_wrap + self.orig_footer_mode = self.view.footer_visible + self.orig_scroll_mode = self.view.page_scroll + self.orig_zoom_mode = self.view.zoom_mode + DEBUG("store zoom mode", self.orig_zoom_mode) + self.DGESDETECT_DISABLE_DOUBLE_TAP = DGESDETECT_DISABLE_DOUBLE_TAP + + self.view.document.configurable.text_wrap = 0 + self.view.page_scroll = false + self.view.footer_visible = true + Input.disable_double_tap = false + DGESDETECT_DISABLE_DOUBLE_TAP = false + self.ui:handleEvent(Event:new("SetZoomMode", "page")) end function ReaderPaging:exitFlippingMode() - self.view.document.configurable.text_wrap = self.orig_reflow_mode - self.view.page_scroll = self.orig_scroll_mode - self.view.footer_visible = self.orig_footer_mode - DGESDETECT_DISABLE_DOUBLE_TAP = self.DGESDETECT_DISABLE_DOUBLE_TAP - Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP - DEBUG("restore zoom mode", self.orig_zoom_mode) - self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) + self.view.document.configurable.text_wrap = self.orig_reflow_mode + self.view.page_scroll = self.orig_scroll_mode + self.view.footer_visible = self.orig_footer_mode + DGESDETECT_DISABLE_DOUBLE_TAP = self.DGESDETECT_DISABLE_DOUBLE_TAP + Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP + DEBUG("restore zoom mode", self.orig_zoom_mode) + self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) end function ReaderPaging:updateOriginalPage(page) - self.original_page = page + self.original_page = page end function ReaderPaging:updateFlippingPage(page) - self.flipping_page = page + self.flipping_page = page end function ReaderPaging:pageFlipping(flipping_page, flipping_ges) - local whole = self.number_of_pages - local steps = #self.flip_steps - local stp_proportion = flipping_ges.distance / Screen:getWidth() - local abs_proportion = flipping_ges.distance / Screen:getHeight() - if flipping_ges.direction == "east" then - self:gotoPage(flipping_page - self.flip_steps[math.ceil(steps*stp_proportion)]) - elseif flipping_ges.direction == "west" then - self:gotoPage(flipping_page + self.flip_steps[math.ceil(steps*stp_proportion)]) - elseif flipping_ges.direction == "south" then - self:gotoPage(flipping_page - math.floor(whole*abs_proportion)) - elseif flipping_ges.direction == "north" then - self:gotoPage(flipping_page + math.floor(whole*abs_proportion)) - end - UIManager:setDirty(self.view.dialog, "partial") + local whole = self.number_of_pages + local steps = #self.flip_steps + local stp_proportion = flipping_ges.distance / Screen:getWidth() + local abs_proportion = flipping_ges.distance / Screen:getHeight() + if flipping_ges.direction == "east" then + self:gotoPage(flipping_page - self.flip_steps[math.ceil(steps*stp_proportion)]) + elseif flipping_ges.direction == "west" then + self:gotoPage(flipping_page + self.flip_steps[math.ceil(steps*stp_proportion)]) + elseif flipping_ges.direction == "south" then + self:gotoPage(flipping_page - math.floor(whole*abs_proportion)) + elseif flipping_ges.direction == "north" then + self:gotoPage(flipping_page + math.floor(whole*abs_proportion)) + end + UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges) - if flipping_ges.direction == "east" then - self.ui:handleEvent(Event:new("GotoPreviousBookmark", flipping_page)) - elseif flipping_ges.direction == "west" then - self.ui:handleEvent(Event:new("GotoNextBookmark", flipping_page)) - end - UIManager:setDirty(self.view.dialog, "partial") + if flipping_ges.direction == "east" then + self.ui:handleEvent(Event:new("GotoPreviousBookmark", flipping_page)) + elseif flipping_ges.direction == "west" then + self.ui:handleEvent(Event:new("GotoNextBookmark", flipping_page)) + end + UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:onSwipe(arg, ges) - if self.bookmark_flipping_mode then - self:bookmarkFlipping(self.current_page, ges) - elseif self.page_flipping_mode and self.original_page then - self:gotoPage(self.original_page) - elseif ges.direction == "west" then - self:onPagingRel(1) - elseif ges.direction == "east" then - self:onPagingRel(-1) - else - UIManager.full_refresh = true - end - return true + if self.bookmark_flipping_mode then + self:bookmarkFlipping(self.current_page, ges) + elseif self.page_flipping_mode and self.original_page then + self:gotoPage(self.original_page) + elseif ges.direction == "west" then + self:onPagingRel(1) + elseif ges.direction == "east" then + self:onPagingRel(-1) + else + UIManager.full_refresh = true + end + return true end function ReaderPaging:onPan(arg, ges) - if self.bookmark_flipping_mode then - return true - elseif self.page_flipping_mode then - if self.view.zoom_mode == "page" then - self:pageFlipping(self.flipping_page, ges) - else - self.view:PanningStart(-ges.relative.x, -ges.relative.y) - end - elseif ges.direction == "north" or ges.direction == "south" then - self:onPanningRel(self.last_pan_relative_y - ges.relative.y) - self.last_pan_relative_y = ges.relative.y - end - return true + if self.bookmark_flipping_mode then + return true + elseif self.page_flipping_mode then + if self.view.zoom_mode == "page" then + self:pageFlipping(self.flipping_page, ges) + else + self.view:PanningStart(-ges.relative.x, -ges.relative.y) + end + elseif ges.direction == "north" or ges.direction == "south" then + self:onPanningRel(self.last_pan_relative_y - ges.relative.y) + self.last_pan_relative_y = ges.relative.y + end + return true end function ReaderPaging:onPanRelease(arg, ges) - if self.page_flipping_mode then - if self.view.zoom_mode == "page" then - self:updateFlippingPage(self.current_page) - else - self.view:PanningStop() - end - else - self.last_pan_relative_y = 0 - UIManager.full_refresh = true - end + if self.page_flipping_mode then + if self.view.zoom_mode == "page" then + self:updateFlippingPage(self.current_page) + else + self.view:PanningStop() + end + else + self.last_pan_relative_y = 0 + UIManager.full_refresh = true + end end function ReaderPaging:onZoomModeUpdate(new_mode) - -- we need to remember zoom mode to handle page turn event - self.zoom_mode = new_mode + -- we need to remember zoom mode to handle page turn event + self.zoom_mode = new_mode end function ReaderPaging:onPageUpdate(new_page_no, orig) - self.current_page = new_page_no - if orig ~= "scrolling" then - self.ui:handleEvent(Event:new("InitScrollPageStates", orig)) - end + self.current_page = new_page_no + if orig ~= "scrolling" then + self.ui:handleEvent(Event:new("InitScrollPageStates", orig)) + end end function ReaderPaging:onViewRecalculate(visible_area, page_area) - -- we need to remember areas to handle page turn event - self.visible_area = visible_area:copy() - self.page_area = page_area + -- we need to remember areas to handle page turn event + self.visible_area = visible_area:copy() + self.page_area = page_area end function ReaderPaging:onGotoPercent(percent) - DEBUG("goto document offset in percent:", percent) - local dest = math.floor(self.number_of_pages * percent / 100) - if dest < 1 then dest = 1 end - if dest > self.number_of_pages then - dest = self.number_of_pages - end - self:gotoPage(dest) - return true + DEBUG("goto document offset in percent:", percent) + local dest = math.floor(self.number_of_pages * percent / 100) + if dest < 1 then dest = 1 end + if dest > self.number_of_pages then + dest = self.number_of_pages + end + self:gotoPage(dest) + return true end function ReaderPaging:onPagingRel(diff) - if self.view.page_scroll then - self:onScrollPageRel(diff) - else - self:onGotoPageRel(diff) - end - self:setPagePosition(self:getTopPage(), self:getTopPosition()) - return true + if self.view.page_scroll then + self:onScrollPageRel(diff) + else + self:onGotoPageRel(diff) + end + self:setPagePosition(self:getTopPage(), self:getTopPosition()) + return true end function ReaderPaging:onPanningRel(diff) - if self.view.page_scroll then - self:onScrollPanRel(diff) - end - self:setPagePosition(self:getTopPage(), self:getTopPosition()) - return true + if self.view.page_scroll then + self:onScrollPanRel(diff) + end + self:setPagePosition(self:getTopPage(), self:getTopPosition()) + return true end --[[ Get read percentage on current page. --]] function ReaderPaging:getTopPosition() - if self.view.page_scroll then - local state = self.view.page_states[1] - return (state.visible_area.y - state.page_area.y)/state.page_area.h - else - return 0 - end + if self.view.page_scroll then + local state = self.view.page_states[1] + return (state.visible_area.y - state.page_area.y)/state.page_area.h + else + return 0 + end end --[[ Get page number of the page drawn at the very top part of the screen. --]] function ReaderPaging:getTopPage() - if self.view.page_scroll then - local state = self.view.page_states[1] - return state and state.page or self.current_page - else - return self.current_page - end + if self.view.page_scroll then + local state = self.view.page_states[1] + return state and state.page or self.current_page + else + return self.current_page + end end function ReaderPaging:onInitScrollPageStates(orig) - --DEBUG.traceback() - DEBUG("init scroll page states", orig) - if self.view.page_scroll and self.view.state.page then - self.orig_page = self.current_page - self.view.page_states = {} - local blank_area = Geom:new{} - blank_area:setSizeTo(self.view.dimen) - while blank_area.h > 0 do - local offset = Geom:new{} - -- caculate position in current page - if self.current_page == self.orig_page then - local page_area = self.view:getPageArea( - self.view.state.page, - self.view.state.zoom, - self.view.state.rotation) - offset.y = page_area.h * self:getPagePosition(self.current_page) - end - local state = self:getNextPageState(blank_area, offset) - --DEBUG("init new state", state) - table.insert(self.view.page_states, state) - if blank_area.h > 0 then - blank_area.h = blank_area.h - self.view.page_gap.height - end - if blank_area.h > 0 then - self:gotoPage(self.current_page + 1, "scrolling") - end - end - self:gotoPage(self.orig_page, "scrolling") - end - return true + --DEBUG.traceback() + DEBUG("init scroll page states", orig) + if self.view.page_scroll and self.view.state.page then + self.orig_page = self.current_page + self.view.page_states = {} + local blank_area = Geom:new{} + blank_area:setSizeTo(self.view.dimen) + while blank_area.h > 0 do + local offset = Geom:new{} + -- caculate position in current page + if self.current_page == self.orig_page then + local page_area = self.view:getPageArea( + self.view.state.page, + self.view.state.zoom, + self.view.state.rotation) + offset.y = page_area.h * self:getPagePosition(self.current_page) + end + local state = self:getNextPageState(blank_area, offset) + --DEBUG("init new state", state) + table.insert(self.view.page_states, state) + if blank_area.h > 0 then + blank_area.h = blank_area.h - self.view.page_gap.height + end + if blank_area.h > 0 then + self:gotoPage(self.current_page + 1, "scrolling") + end + end + self:gotoPage(self.orig_page, "scrolling") + end + return true end function ReaderPaging:onUpdateScrollPageRotation(rotation) - for _, state in ipairs(self.view.page_states) do - state.rotation = rotation - end - return true + for _, state in ipairs(self.view.page_states) do + state.rotation = rotation + end + return true end function ReaderPaging:onUpdateScrollPageGamma(gamma) - for _, state in ipairs(self.view.page_states) do - state.gamma = gamma - end - return true + for _, state in ipairs(self.view.page_states) do + state.gamma = gamma + end + return true end function ReaderPaging:getNextPageState(blank_area, offset) - local page_area = self.view:getPageArea( - self.view.state.page, - self.view.state.zoom, - self.view.state.rotation) - local visible_area = Geom:new{x = 0, y = 0} - visible_area.w, visible_area.h = blank_area.w, blank_area.h - visible_area.x, visible_area.y = page_area.x, page_area.y - visible_area = visible_area:shrinkInside(page_area, offset.x, offset.y) - -- shrink blank area by the height of visible area - blank_area.h = blank_area.h - visible_area.h - return { - page = self.view.state.page, - zoom = self.view.state.zoom, - rotation = self.view.state.rotation, - gamma = self.view.state.gamma, - offset = Geom:new{ x = self.view.state.offset.x, y = 0}, - visible_area = visible_area, - page_area = page_area, - } + local page_area = self.view:getPageArea( + self.view.state.page, + self.view.state.zoom, + self.view.state.rotation) + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x, visible_area.y = page_area.x, page_area.y + visible_area = visible_area:shrinkInside(page_area, offset.x, offset.y) + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + return { + page = self.view.state.page, + zoom = self.view.state.zoom, + rotation = self.view.state.rotation, + gamma = self.view.state.gamma, + offset = Geom:new{ x = self.view.state.offset.x, y = 0}, + visible_area = visible_area, + page_area = page_area, + } end function ReaderPaging:getPrevPageState(blank_area, offset) - local page_area = self.view:getPageArea( - self.view.state.page, - self.view.state.zoom, - self.view.state.rotation) - local visible_area = Geom:new{x = 0, y = 0} - visible_area.w, visible_area.h = blank_area.w, blank_area.h - visible_area.x = page_area.x - visible_area.y = page_area.y + page_area.h - visible_area.h - visible_area = visible_area:shrinkInside(page_area, offset.x, offset.y) - -- shrink blank area by the height of visible area - blank_area.h = blank_area.h - visible_area.h - return { - page = self.view.state.page, - zoom = self.view.state.zoom, - rotation = self.view.state.rotation, - gamma = self.view.state.gamma, - offset = Geom:new{ x = self.view.state.offset.x, y = 0}, - visible_area = visible_area, - page_area = page_area, - } + local page_area = self.view:getPageArea( + self.view.state.page, + self.view.state.zoom, + self.view.state.rotation) + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x = page_area.x + visible_area.y = page_area.y + page_area.h - visible_area.h + visible_area = visible_area:shrinkInside(page_area, offset.x, offset.y) + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + return { + page = self.view.state.page, + zoom = self.view.state.zoom, + rotation = self.view.state.rotation, + gamma = self.view.state.gamma, + offset = Geom:new{ x = self.view.state.offset.x, y = 0}, + visible_area = visible_area, + page_area = page_area, + } end function ReaderPaging:updateTopPageState(state, blank_area, offset) - local visible_area = Geom:new{x = 0, y = 0} - visible_area.w, visible_area.h = blank_area.w, blank_area.h - visible_area.x, visible_area.y = state.visible_area.x, state.visible_area.y - if state.page == self.number_of_pages then - visible_area:offsetWithin(state.page_area, offset.x, offset.y) - else - visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y) - end - -- shrink blank area by the height of visible area - blank_area.h = blank_area.h - visible_area.h - state.visible_area = visible_area - return state + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x, visible_area.y = state.visible_area.x, state.visible_area.y + if state.page == self.number_of_pages then + visible_area:offsetWithin(state.page_area, offset.x, offset.y) + else + visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y) + end + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + state.visible_area = visible_area + return state end function ReaderPaging:updateBottomPageState(state, blank_area, offset) - local visible_area = Geom:new{x = 0, y = 0} - visible_area.w, visible_area.h = blank_area.w, blank_area.h - visible_area.x = state.page_area.x - visible_area.y = state.visible_area.y + state.visible_area.h - visible_area.h - if state.page == 1 then - visible_area:offsetWithin(state.page_area, offset.x, offset.y) - else - visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y) - end - -- shrink blank area by the height of visible area - blank_area.h = blank_area.h - visible_area.h - state.visible_area = visible_area - return state + local visible_area = Geom:new{x = 0, y = 0} + visible_area.w, visible_area.h = blank_area.w, blank_area.h + visible_area.x = state.page_area.x + visible_area.y = state.visible_area.y + state.visible_area.h - visible_area.h + if state.page == 1 then + visible_area:offsetWithin(state.page_area, offset.x, offset.y) + else + visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y) + end + -- shrink blank area by the height of visible area + blank_area.h = blank_area.h - visible_area.h + state.visible_area = visible_area + return state end function ReaderPaging:genPageStatesFromTop(top_page_state, blank_area, offset) - -- Offset should always be greater than 0 - -- otherwise if offset is less than 0 the height of blank area will be - -- larger than 0 even if page area is much larger than visible area, - -- which will trigger the drawing of next page leaving part of current - -- page undrawn. This should also be true for generating from bottom. - if offset.y < 0 then offset.y = 0 end - local state = self:updateTopPageState(top_page_state, blank_area, offset) - --DEBUG("updated state", state) - local page_states = {} - if state.visible_area.h > 0 then - table.insert(page_states, state) - end - --DEBUG("blank area", blank_area) - local current_page = state.page - while blank_area.h > 0 do - blank_area.h = blank_area.h - self.view.page_gap.height - if blank_area.h > 0 then - if self.current_page == self.number_of_pages then break end - self:gotoPage(current_page + 1, "scrolling") - current_page = current_page + 1 - local state = self:getNextPageState(blank_area, Geom:new{}) - --DEBUG("new state", state) - table.insert(page_states, state) - end - end - return page_states + -- Offset should always be greater than 0 + -- otherwise if offset is less than 0 the height of blank area will be + -- larger than 0 even if page area is much larger than visible area, + -- which will trigger the drawing of next page leaving part of current + -- page undrawn. This should also be true for generating from bottom. + if offset.y < 0 then offset.y = 0 end + local state = self:updateTopPageState(top_page_state, blank_area, offset) + --DEBUG("updated state", state) + local page_states = {} + if state.visible_area.h > 0 then + table.insert(page_states, state) + end + --DEBUG("blank area", blank_area) + local current_page = state.page + while blank_area.h > 0 do + blank_area.h = blank_area.h - self.view.page_gap.height + if blank_area.h > 0 then + if self.current_page == self.number_of_pages then break end + self:gotoPage(current_page + 1, "scrolling") + current_page = current_page + 1 + local state = self:getNextPageState(blank_area, Geom:new{}) + --DEBUG("new state", state) + table.insert(page_states, state) + end + end + return page_states end function ReaderPaging:genPageStatesFromBottom(bottom_page_state, blank_area, offset) - -- scroll up offset should always be less than 0 - if offset.y > 0 then offset.y = 0 end - local state = self:updateBottomPageState(bottom_page_state, blank_area, offset) - --DEBUG("updated state", state) - local page_states = {} - if state.visible_area.h > 0 then - table.insert(page_states, state) - end - --DEBUG("blank area", blank_area) - local current_page = state.page - while blank_area.h > 0 do - blank_area.h = blank_area.h - self.view.page_gap.height - if blank_area.h > 0 then - if self.current_page == 1 then break end - self:gotoPage(current_page - 1, "scrolling") - current_page = current_page - 1 - local state = self:getPrevPageState(blank_area, Geom:new{}) - --DEBUG("new state", state) - table.insert(page_states, 1, state) - end - end - return page_states + -- scroll up offset should always be less than 0 + if offset.y > 0 then offset.y = 0 end + local state = self:updateBottomPageState(bottom_page_state, blank_area, offset) + --DEBUG("updated state", state) + local page_states = {} + if state.visible_area.h > 0 then + table.insert(page_states, state) + end + --DEBUG("blank area", blank_area) + local current_page = state.page + while blank_area.h > 0 do + blank_area.h = blank_area.h - self.view.page_gap.height + if blank_area.h > 0 then + if self.current_page == 1 then break end + self:gotoPage(current_page - 1, "scrolling") + current_page = current_page - 1 + local state = self:getPrevPageState(blank_area, Geom:new{}) + --DEBUG("new state", state) + table.insert(page_states, 1, state) + end + end + return page_states end function ReaderPaging:onScrollPanRel(diff) - DEBUG("pan relative height:", diff) - local blank_area = Geom:new{} - blank_area:setSizeTo(self.view.dimen) - if diff > 0 then - local first_page_state = table.remove(self.view.page_states, 1) - local offset = Geom:new{ - x = 0, - y = diff, - } - self.view.page_states = self:genPageStatesFromTop(first_page_state, blank_area, offset) - end - if diff < 0 then - local last_page_state = table.remove(self.view.page_states) - local offset = Geom:new{ - x = 0, - y = diff - } - self.view.page_states = self:genPageStatesFromBottom(last_page_state, blank_area, offset) - end - -- update current pageno to the very last part in current view - self:gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling") - - UIManager:setDirty(self.view.dialog, "fast") + DEBUG("pan relative height:", diff) + local blank_area = Geom:new{} + blank_area:setSizeTo(self.view.dimen) + if diff > 0 then + local first_page_state = table.remove(self.view.page_states, 1) + local offset = Geom:new{ + x = 0, + y = diff, + } + self.view.page_states = self:genPageStatesFromTop(first_page_state, blank_area, offset) + end + if diff < 0 then + local last_page_state = table.remove(self.view.page_states) + local offset = Geom:new{ + x = 0, + y = diff + } + self.view.page_states = self:genPageStatesFromBottom(last_page_state, blank_area, offset) + end + -- update current pageno to the very last part in current view + self:gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling") + + UIManager:setDirty(self.view.dialog, "fast") end function ReaderPaging:onScrollPageRel(diff) - DEBUG("scroll relative page:", diff) - local blank_area = Geom:new{} - blank_area:setSizeTo(self.view.dimen) - if diff > 0 then - local last_page_state = table.remove(self.view.page_states) - local offset = Geom:new{ - x = 0, - y = last_page_state.visible_area.h - self.overlap - } - self.view.page_states = self:genPageStatesFromTop(last_page_state, blank_area, offset) - end - if diff < 0 then - local first_page_state = table.remove(self.view.page_states, 1) - local offset = Geom:new{ - x = 0, - y = -first_page_state.visible_area.h + self.overlap - } - self.view.page_states = self:genPageStatesFromBottom(first_page_state, blank_area, offset) - end - -- update current pageno to the very last part in current view - self:gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling") - UIManager:setDirty(self.view.dialog) + DEBUG("scroll relative page:", diff) + local blank_area = Geom:new{} + blank_area:setSizeTo(self.view.dimen) + if diff > 0 then + local last_page_state = table.remove(self.view.page_states) + local offset = Geom:new{ + x = 0, + y = last_page_state.visible_area.h - self.overlap + } + self.view.page_states = self:genPageStatesFromTop(last_page_state, blank_area, offset) + end + if diff < 0 then + local first_page_state = table.remove(self.view.page_states, 1) + local offset = Geom:new{ + x = 0, + y = -first_page_state.visible_area.h + self.overlap + } + self.view.page_states = self:genPageStatesFromBottom(first_page_state, blank_area, offset) + end + -- update current pageno to the very last part in current view + self:gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling") + UIManager:setDirty(self.view.dialog) end function ReaderPaging:onGotoPageRel(diff) - DEBUG("goto relative page:", diff) - local new_va = self.visible_area:copy() - local x_pan_off, y_pan_off = 0, 0 - - if self.zoom_mode == "free" then - -- do nothing in free zoom mode - elseif self.zoom_mode:find("width") then - y_pan_off = self.visible_area.h * diff - elseif self.zoom_mode:find("height") then - x_pan_off = self.visible_area.w * diff - else - -- must be fit content or page zoom mode - if self.visible_area.w == self.page_area.w then - y_pan_off = self.visible_area.h * diff - else - x_pan_off = self.visible_area.w * diff - end - end - -- adjust offset to help with page turn decision - -- we dont take overlap into account here yet, otherwise new_va will - -- always intersect with page_area - x_pan_off = Math.roundAwayFromZero(x_pan_off) - y_pan_off = Math.roundAwayFromZero(y_pan_off) - new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) - new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) - - if new_va:notIntersectWith(self.page_area) then - -- view area out of page area, do a page turn - self:gotoPage(self.current_page + diff) - -- if we are going back to previous page, reset - -- view area to bottom of previous page - if x_pan_off < 0 then - self.view:PanningUpdate(self.page_area.w, 0) - elseif y_pan_off < 0 then - self.view:PanningUpdate(0, self.page_area.h) - end - -- reset dim_area - --self.view.dim_area.h = 0 - --self.view.dim_area.w = 0 - -- - else - -- not end of page yet, goto next view - -- adjust panning step according to overlap - if x_pan_off > self.overlap then - -- moving to next view, move view - x_pan_off = x_pan_off - self.overlap - elseif x_pan_off < -self.overlap then - x_pan_off = x_pan_off + self.overlap - end - if y_pan_off > self.overlap then - y_pan_off = y_pan_off - self.overlap - elseif y_pan_off < -self.overlap then - y_pan_off = y_pan_off + self.overlap - end - -- we have to calculate again to count into overlap - new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) - new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) - -- fit new view area into page area - new_va:offsetWithin(self.page_area, 0, 0) - -- calculate panning offsets - local panned_x = new_va.x - self.visible_area.x - local panned_y = new_va.y - self.visible_area.y - -- adjust for crazy float point overflow... - if math.abs(panned_x) < 1 then - panned_x = 0 - end - if math.abs(panned_y) < 1 then - panned_y = 0 - end - -- singal panning update - self.view:PanningUpdate(panned_x, panned_y) - -- update dime area in ReaderView - if self.show_overlap_enable then - self.view.dim_area.h = new_va.h - math.abs(panned_y) - self.view.dim_area.w = new_va.w - math.abs(panned_x) - if panned_y < 0 then - self.view.dim_area.y = new_va.y - panned_y - else - self.view.dim_area.y = 0 - end - if panned_x < 0 then - self.view.dim_area.x = new_va.x - panned_x - else - self.view.dim_area.x = 0 - end - end - -- update self.visible_area - self.visible_area = new_va - end - - return true + DEBUG("goto relative page:", diff) + local new_va = self.visible_area:copy() + local x_pan_off, y_pan_off = 0, 0 + + if self.zoom_mode == "free" then + -- do nothing in free zoom mode + elseif self.zoom_mode:find("width") then + y_pan_off = self.visible_area.h * diff + elseif self.zoom_mode:find("height") then + x_pan_off = self.visible_area.w * diff + else + -- must be fit content or page zoom mode + if self.visible_area.w == self.page_area.w then + y_pan_off = self.visible_area.h * diff + else + x_pan_off = self.visible_area.w * diff + end + end + -- adjust offset to help with page turn decision + -- we dont take overlap into account here yet, otherwise new_va will + -- always intersect with page_area + x_pan_off = Math.roundAwayFromZero(x_pan_off) + y_pan_off = Math.roundAwayFromZero(y_pan_off) + new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) + new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) + + if new_va:notIntersectWith(self.page_area) then + -- view area out of page area, do a page turn + self:gotoPage(self.current_page + diff) + -- if we are going back to previous page, reset + -- view area to bottom of previous page + if x_pan_off < 0 then + self.view:PanningUpdate(self.page_area.w, 0) + elseif y_pan_off < 0 then + self.view:PanningUpdate(0, self.page_area.h) + end + -- reset dim_area + --self.view.dim_area.h = 0 + --self.view.dim_area.w = 0 + -- + else + -- not end of page yet, goto next view + -- adjust panning step according to overlap + if x_pan_off > self.overlap then + -- moving to next view, move view + x_pan_off = x_pan_off - self.overlap + elseif x_pan_off < -self.overlap then + x_pan_off = x_pan_off + self.overlap + end + if y_pan_off > self.overlap then + y_pan_off = y_pan_off - self.overlap + elseif y_pan_off < -self.overlap then + y_pan_off = y_pan_off + self.overlap + end + -- we have to calculate again to count into overlap + new_va.x = Math.roundAwayFromZero(self.visible_area.x+x_pan_off) + new_va.y = Math.roundAwayFromZero(self.visible_area.y+y_pan_off) + -- fit new view area into page area + new_va:offsetWithin(self.page_area, 0, 0) + -- calculate panning offsets + local panned_x = new_va.x - self.visible_area.x + local panned_y = new_va.y - self.visible_area.y + -- adjust for crazy float point overflow... + if math.abs(panned_x) < 1 then + panned_x = 0 + end + if math.abs(panned_y) < 1 then + panned_y = 0 + end + -- singal panning update + self.view:PanningUpdate(panned_x, panned_y) + -- update dime area in ReaderView + if self.show_overlap_enable then + self.view.dim_area.h = new_va.h - math.abs(panned_y) + self.view.dim_area.w = new_va.w - math.abs(panned_x) + if panned_y < 0 then + self.view.dim_area.y = new_va.y - panned_y + else + self.view.dim_area.y = 0 + end + if panned_x < 0 then + self.view.dim_area.x = new_va.x - panned_x + else + self.view.dim_area.x = 0 + end + end + -- update self.visible_area + self.visible_area = new_va + end + + return true end function ReaderPaging:onRedrawCurrentPage() - self.ui:handleEvent(Event:new("PageUpdate", self.current_page)) - return true + self.ui:handleEvent(Event:new("PageUpdate", self.current_page)) + return true end function ReaderPaging:onSetDimensions() - -- update listening according to new screen dimen - if Device:isTouchDevice() then - self:initGesListener() - end + -- update listening according to new screen dimen + if Device:isTouchDevice() then + self:initGesListener() + end end -- wrapper for bounds checking function ReaderPaging:gotoPage(number, orig) - --DEBUG.traceback() - if number == self.current_page or not number then - return true - end - if number > self.number_of_pages - or number < 1 then - DEBUG("wrong page number: "..number.."!") - return false - end - DEBUG("going to page number", number) - - -- this is an event to allow other controllers to be aware of this change - self.ui:handleEvent(Event:new("PageUpdate", number, orig)) - return true + --DEBUG.traceback() + if number == self.current_page or not number then + return true + end + if number > self.number_of_pages + or number < 1 then + DEBUG("wrong page number: "..number.."!") + return false + end + DEBUG("going to page number", number) + + -- this is an event to allow other controllers to be aware of this change + self.ui:handleEvent(Event:new("PageUpdate", number, orig)) + return true end function ReaderPaging:onGotoPage(number) - self:gotoPage(number) - return true + self:gotoPage(number) + return true end function ReaderPaging:onGotoPercentage(percentage) - if percentage < 0 then percentage = 0 end - if percentage > 1 then percentage = 1 end - self:gotoPage(math.floor(percentage*self.number_of_pages)) - return true + if percentage < 0 then percentage = 0 end + if percentage > 1 then percentage = 1 end + self:gotoPage(math.floor(percentage*self.number_of_pages)) + return true end return ReaderPaging diff --git a/frontend/ui/reader/readerpanning.lua b/frontend/ui/reader/readerpanning.lua index 8b1008c7b..1a5286728 100644 --- a/frontend/ui/reader/readerpanning.lua +++ b/frontend/ui/reader/readerpanning.lua @@ -4,48 +4,48 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderPanning = InputContainer:new{ - -- defaults - panning_steps = { - normal = 50, - alt = 25, - shift = 10, - altshift = 5 - }, + -- defaults + panning_steps = { + normal = 50, + alt = 25, + shift = 10, + altshift = 5 + }, } function ReaderPanning:init() - if Device:isTouchDevice() then - else - self.key_events = { - -- these will all generate the same event, just with different arguments - MoveUp = { - { "Up" }, doc = _("move visible area up"), - event = "Panning", args = {0, -1} }, - MoveDown = { - { "Down" }, doc = _("move visible area down"), - event = "Panning", args = {0, 1} }, - MoveLeft = { - { "Left" }, doc = _("move visible area left"), - event = "Panning", args = {-1, 0} }, - MoveRight = { - { "Right" }, doc = _("move visible area right"), - event = "Panning", args = {1, 0} }, - } - end + if Device:isTouchDevice() then + else + self.key_events = { + -- these will all generate the same event, just with different arguments + MoveUp = { + { "Up" }, doc = _("move visible area up"), + event = "Panning", args = {0, -1} }, + MoveDown = { + { "Down" }, doc = _("move visible area down"), + event = "Panning", args = {0, 1} }, + MoveLeft = { + { "Left" }, doc = _("move visible area left"), + event = "Panning", args = {-1, 0} }, + MoveRight = { + { "Right" }, doc = _("move visible area right"), + event = "Panning", args = {1, 0} }, + } + end end function ReaderPanning:onSetDimensions(dimensions) - self.dimen = dimensions + self.dimen = dimensions end function ReaderPanning:onPanning(args, key) - local dx, dy = unpack(args) - DEBUG("key =", key) - -- for now, bounds checking/calculation is done in the view - self.view:PanningUpdate( - dx * self.panning_steps.normal * self.dimen.w / 100, - dy * self.panning_steps.normal * self.dimen.h / 100) - return true + local dx, dy = unpack(args) + DEBUG("key =", key) + -- for now, bounds checking/calculation is done in the view + self.view:PanningUpdate( + dx * self.panning_steps.normal * self.dimen.w / 100, + dy * self.panning_steps.normal * self.dimen.h / 100) + return true end return ReaderPanning diff --git a/frontend/ui/reader/readerrolling.lua b/frontend/ui/reader/readerrolling.lua index 4b23980d1..4de85a432 100644 --- a/frontend/ui/reader/readerrolling.lua +++ b/frontend/ui/reader/readerrolling.lua @@ -11,363 +11,363 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderRolling = InputContainer:new{ - old_doc_height = nil, - old_page = nil, - current_pos = 0, - -- only used for page view mode - current_page= nil, - doc_height = nil, - panning_steps = ReaderPanning.panning_steps, - show_overlap_enable = true, - overlap = 20, + old_doc_height = nil, + old_page = nil, + current_pos = 0, + -- only used for page view mode + current_page= nil, + doc_height = nil, + panning_steps = ReaderPanning.panning_steps, + show_overlap_enable = true, + overlap = 20, } function ReaderRolling:init() - if Device:hasKeyboard() then - self.key_events = { - GotoNextView = { - { Input.group.PgFwd }, - doc = _("go to next view"), - event = "GotoViewRel", args = 1 - }, - GotoPrevView = { - { Input.group.PgBack }, - doc = _("go to previous view"), - event = "GotoViewRel", args = -1 - }, - MoveUp = { - { "Up" }, - doc = _("move view up"), - event = "Panning", args = {0, -1} - }, - MoveDown = { - { "Down" }, - doc = _("move view down"), - event = "Panning", args = {0, 1} - }, - GotoFirst = { - {"1"}, doc = _("go to start"), event = "GotoPercent", args = 0}, - Goto11 = { - {"2"}, doc = _("go to 11%"), event = "GotoPercent", args = 11}, - Goto22 = { - {"3"}, doc = _("go to 22%"), event = "GotoPercent", args = 22}, - Goto33 = { - {"4"}, doc = _("go to 33%"), event = "GotoPercent", args = 33}, - Goto44 = { - {"5"}, doc = _("go to 44%"), event = "GotoPercent", args = 44}, - Goto55 = { - {"6"}, doc = _("go to 55%"), event = "GotoPercent", args = 55}, - Goto66 = { - {"7"}, doc = _("go to 66%"), event = "GotoPercent", args = 66}, - Goto77 = { - {"8"}, doc = _("go to 77%"), event = "GotoPercent", args = 77}, - Goto88 = { - {"9"}, doc = _("go to 88%"), event = "GotoPercent", args = 88}, - GotoLast = { - {"0"}, doc = _("go to end"), event = "GotoPercent", args = 100}, - } - end - - table.insert(self.ui.postInitCallback, function() - self.doc_height = self.ui.document.info.doc_height - self.old_doc_height = self.doc_height - self.old_page = self.ui.document.info.number_of_pages - end) + if Device:hasKeyboard() then + self.key_events = { + GotoNextView = { + { Input.group.PgFwd }, + doc = _("go to next view"), + event = "GotoViewRel", args = 1 + }, + GotoPrevView = { + { Input.group.PgBack }, + doc = _("go to previous view"), + event = "GotoViewRel", args = -1 + }, + MoveUp = { + { "Up" }, + doc = _("move view up"), + event = "Panning", args = {0, -1} + }, + MoveDown = { + { "Down" }, + doc = _("move view down"), + event = "Panning", args = {0, 1} + }, + GotoFirst = { + {"1"}, doc = _("go to start"), event = "GotoPercent", args = 0}, + Goto11 = { + {"2"}, doc = _("go to 11%"), event = "GotoPercent", args = 11}, + Goto22 = { + {"3"}, doc = _("go to 22%"), event = "GotoPercent", args = 22}, + Goto33 = { + {"4"}, doc = _("go to 33%"), event = "GotoPercent", args = 33}, + Goto44 = { + {"5"}, doc = _("go to 44%"), event = "GotoPercent", args = 44}, + Goto55 = { + {"6"}, doc = _("go to 55%"), event = "GotoPercent", args = 55}, + Goto66 = { + {"7"}, doc = _("go to 66%"), event = "GotoPercent", args = 66}, + Goto77 = { + {"8"}, doc = _("go to 77%"), event = "GotoPercent", args = 77}, + Goto88 = { + {"9"}, doc = _("go to 88%"), event = "GotoPercent", args = 88}, + GotoLast = { + {"0"}, doc = _("go to end"), event = "GotoPercent", args = 100}, + } + end + + table.insert(self.ui.postInitCallback, function() + self.doc_height = self.ui.document.info.doc_height + self.old_doc_height = self.doc_height + self.old_page = self.ui.document.info.number_of_pages + end) end -- This method will be called in onSetDimensions handler function ReaderRolling:initGesListener() - self.ges_events = { - TapForward = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_FORWARD.x, - y = Screen:getHeight()*DTAP_ZONE_FORWARD.y, - w = Screen:getWidth()*DTAP_ZONE_FORWARD.w, - h = Screen:getHeight()*DTAP_ZONE_FORWARD.h, - } - } - }, - TapBackward = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x, - y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y, - w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w, - h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h, - } - } - }, - Swipe = { - GestureRange:new{ - ges = "swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - Pan = { - GestureRange:new{ - ges = "pan", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - }, - rate = 4.0, - } - }, - } + self.ges_events = { + TapForward = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_FORWARD.x, + y = Screen:getHeight()*DTAP_ZONE_FORWARD.y, + w = Screen:getWidth()*DTAP_ZONE_FORWARD.w, + h = Screen:getHeight()*DTAP_ZONE_FORWARD.h, + } + } + }, + TapBackward = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x, + y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y, + w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w, + h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h, + } + } + }, + Swipe = { + GestureRange:new{ + ges = "swipe", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + Pan = { + GestureRange:new{ + ges = "pan", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + }, + rate = 4.0, + } + }, + } end function ReaderRolling:onReadSettings(config) - local soe = config:readSetting("show_overlap_enable") - if not soe then - self.show_overlap_enable = soe - end - local last_xp = config:readSetting("last_xpointer") - if last_xp then - table.insert(self.ui.postInitCallback, function() - self:gotoXPointer(last_xp) - -- we have to do a real jump in self.ui.document._document to - -- update status information in CREngine. - self.ui.document:gotoXPointer(last_xp) - end) - end - -- we read last_percent just for backward compatibility - if not last_xp then - local last_per = config:readSetting("last_percent") - if last_per then - table.insert(self.ui.postInitCallback, function() - self:gotoPercent(last_per) - -- we have to do a real pos change in self.ui.document._document - -- to update status information in CREngine. - self.ui.document:gotoPos(self.current_pos) - end) - end - end - if self.view.view_mode == "page" then - self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage())) - end + local soe = config:readSetting("show_overlap_enable") + if not soe then + self.show_overlap_enable = soe + end + local last_xp = config:readSetting("last_xpointer") + if last_xp then + table.insert(self.ui.postInitCallback, function() + self:gotoXPointer(last_xp) + -- we have to do a real jump in self.ui.document._document to + -- update status information in CREngine. + self.ui.document:gotoXPointer(last_xp) + end) + end + -- we read last_percent just for backward compatibility + if not last_xp then + local last_per = config:readSetting("last_percent") + if last_per then + table.insert(self.ui.postInitCallback, function() + self:gotoPercent(last_per) + -- we have to do a real pos change in self.ui.document._document + -- to update status information in CREngine. + self.ui.document:gotoPos(self.current_pos) + end) + end + end + if self.view.view_mode == "page" then + self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage())) + end end function ReaderRolling:onSaveSettings() - -- remove last_percent config since its deprecated - self.ui.doc_settings:saveSetting("last_percent", nil) - self.ui.doc_settings:saveSetting("last_xpointer", self.ui.document:getXPointer()) - self.ui.doc_settings:saveSetting("percent_finished", self:getLastPercent()) + -- remove last_percent config since its deprecated + self.ui.doc_settings:saveSetting("last_percent", nil) + self.ui.doc_settings:saveSetting("last_xpointer", self.ui.document:getXPointer()) + self.ui.doc_settings:saveSetting("percent_finished", self:getLastPercent()) end function ReaderRolling:getLastPercent() - if self.view.view_mode == "page" then - return self.current_page / self.old_page - else - -- FIXME: the calculated percent is not accurate in "scroll" mode. - return self.ui.document:getPosFromXPointer( - self.ui.document:getXPointer()) / self.doc_height - end + if self.view.view_mode == "page" then + return self.current_page / self.old_page + else + -- FIXME: the calculated percent is not accurate in "scroll" mode. + return self.ui.document:getPosFromXPointer( + self.ui.document:getXPointer()) / self.doc_height + end end function ReaderRolling:onTapForward() - self:onGotoViewRel(1) - return true + self:onGotoViewRel(1) + return true end function ReaderRolling:onTapBackward() - self:onGotoViewRel(-1) - return true + self:onGotoViewRel(-1) + return true end function ReaderRolling:onSwipe(arg, ges) - if ges.direction == "west" or ges.direction == "north" then - self:onGotoViewRel(1) - elseif ges.direction == "east" or ges.direction == "south" then - self:onGotoViewRel(-1) - end - return true + if ges.direction == "west" or ges.direction == "north" then + self:onGotoViewRel(1) + elseif ges.direction == "east" or ges.direction == "south" then + self:onGotoViewRel(-1) + end + return true end function ReaderRolling:onPan(arg, ges) - if self.view.view_mode == "scroll" then - if ges.direction == "north" then - self:gotoPos(self.current_pos + ges.distance) - elseif ges.direction == "south" then - self:gotoPos(self.current_pos - ges.distance) - end - end - return true + if self.view.view_mode == "scroll" then + if ges.direction == "north" then + self:gotoPos(self.current_pos + ges.distance) + elseif ges.direction == "south" then + self:gotoPos(self.current_pos - ges.distance) + end + end + return true end function ReaderRolling:onPosUpdate(new_pos) - self.current_pos = new_pos - self:updateBatteryState() + self.current_pos = new_pos + self:updateBatteryState() end function ReaderRolling:onPageUpdate(new_page) - self.current_page = new_page - self:updateBatteryState() + self.current_page = new_page + self:updateBatteryState() end function ReaderRolling:onResume() - self:updateBatteryState() + self:updateBatteryState() end function ReaderRolling:onNotCharging() - self:updateBatteryState() + self:updateBatteryState() end function ReaderRolling:onGotoPercent(percent) - DEBUG("goto document offset in percent:", percent) - self:gotoPercent(percent) - return true + DEBUG("goto document offset in percent:", percent) + self:gotoPercent(percent) + return true end function ReaderRolling:onGotoViewRel(diff) - DEBUG("goto relative screen:", diff, ", in mode: ", self.view.view_mode) - if self.view.view_mode == "scroll" then - local pan_diff = diff * self.ui.dimen.h - if self.show_overlap_enable then - if pan_diff > self.overlap then - pan_diff = pan_diff - self.overlap - elseif pan_diff < -self.overlap then - pan_diff = pan_diff + self.overlap - end - end - self:gotoPos(self.current_pos + pan_diff) - elseif self.view.view_mode == "page" then - self:gotoPage(self.current_page + diff) - end - return true + DEBUG("goto relative screen:", diff, ", in mode: ", self.view.view_mode) + if self.view.view_mode == "scroll" then + local pan_diff = diff * self.ui.dimen.h + if self.show_overlap_enable then + if pan_diff > self.overlap then + pan_diff = pan_diff - self.overlap + elseif pan_diff < -self.overlap then + pan_diff = pan_diff + self.overlap + end + end + self:gotoPos(self.current_pos + pan_diff) + elseif self.view.view_mode == "page" then + self:gotoPage(self.current_page + diff) + end + return true end function ReaderRolling:onPanning(args, key) - --@TODO disable panning in page view_mode? 22.12 2012 (houqp) - local _, dy = unpack(args) - DEBUG("key =", key) - self:gotoPos(self.current_pos + dy * self.panning_steps.normal) - return true + --@TODO disable panning in page view_mode? 22.12 2012 (houqp) + local _, dy = unpack(args) + DEBUG("key =", key) + self:gotoPos(self.current_pos + dy * self.panning_steps.normal) + return true end function ReaderRolling:onZoom() - --@TODO re-read doc_height info after font or lineheight changes 05.06 2012 (houqp) - self:updatePos() + --@TODO re-read doc_height info after font or lineheight changes 05.06 2012 (houqp) + self:updatePos() end --[[ - remember to signal this event when the document has been zoomed, - font has been changed, or line height has been changed. + remember to signal this event when the document has been zoomed, + font has been changed, or line height has been changed. --]] function ReaderRolling:onUpdatePos() - UIManager:scheduleIn(0.1, function () self:updatePos() end) - return true + UIManager:scheduleIn(0.1, function () self:updatePos() end) + return true end function ReaderRolling:updatePos() - -- reread document height - self.ui.document:_readMetadata() - -- update self.current_pos if the height of document has been changed. - local new_height = self.ui.document.info.doc_height - local new_page = self.ui.document.info.number_of_pages - if self.old_doc_height ~= new_height or self.old_page ~= new_page then - self:gotoXPointer(self.ui.document:getXPointer()) - self.old_doc_height = new_height - self.old_page = new_page - self.ui:handleEvent(Event:new("UpdateToc")) - end - UIManager.repaint_all = true + -- reread document height + self.ui.document:_readMetadata() + -- update self.current_pos if the height of document has been changed. + local new_height = self.ui.document.info.doc_height + local new_page = self.ui.document.info.number_of_pages + if self.old_doc_height ~= new_height or self.old_page ~= new_page then + self:gotoXPointer(self.ui.document:getXPointer()) + self.old_doc_height = new_height + self.old_page = new_page + self.ui:handleEvent(Event:new("UpdateToc")) + end + UIManager.repaint_all = true end function ReaderRolling:onUpdateXPointer() - local xp = self.ui.document:getXPointer() - if self.view.view_mode == "page" then - self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(xp))) - else - self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(xp))) - end - return true + local xp = self.ui.document:getXPointer() + if self.view.view_mode == "page" then + self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(xp))) + else + self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(xp))) + end + return true end function ReaderRolling:onChangeViewMode() - self.ui.document:_readMetadata() - self.old_doc_height = self.ui.document.info.doc_height - self.old_page = self.ui.document.info.number_of_pages - self.ui:handleEvent(Event:new("UpdateToc")) - self:gotoXPointer(self.ui.document:getXPointer()) - if self.view.view_mode == "scroll" then - self.current_pos = self.ui.document:getCurrentPos() - else - self.current_page = self.ui.document:getCurrentPage() - end - return true + self.ui.document:_readMetadata() + self.old_doc_height = self.ui.document.info.doc_height + self.old_page = self.ui.document.info.number_of_pages + self.ui:handleEvent(Event:new("UpdateToc")) + self:gotoXPointer(self.ui.document:getXPointer()) + if self.view.view_mode == "scroll" then + self.current_pos = self.ui.document:getCurrentPos() + else + self.current_page = self.ui.document:getCurrentPage() + end + return true end function ReaderRolling:onRedrawCurrentView() - if self.view.view_mode == "page" then - self.ui:handleEvent(Event:new("PageUpdate", self.current_page)) - else - self.ui:handleEvent(Event:new("PosUpdate", self.current_pos)) - end - return true + if self.view.view_mode == "page" then + self.ui:handleEvent(Event:new("PageUpdate", self.current_page)) + else + self.ui:handleEvent(Event:new("PosUpdate", self.current_pos)) + end + return true end function ReaderRolling:onSetDimensions() - -- update listening according to new screen dimen - if Device:isTouchDevice() then - self:initGesListener() - end + -- update listening according to new screen dimen + if Device:isTouchDevice() then + self:initGesListener() + end end function ReaderRolling:onChangeScreenMode(mode) - self.ui:handleEvent(Event:new("SetScreenMode", mode)) - self:onChangeViewMode() + self.ui:handleEvent(Event:new("SetScreenMode", mode)) + self:onChangeViewMode() end --[[ - PosUpdate event is used to signal other widgets that pos has been changed. + PosUpdate event is used to signal other widgets that pos has been changed. --]] function ReaderRolling:gotoPos(new_pos) - if new_pos == self.current_pos then return end - if new_pos < 0 then new_pos = 0 end - if new_pos > self.doc_height then new_pos = self.doc_height end - -- adjust dim_area according to new_pos - if self.view.view_mode ~= "page" and self.show_overlap_enable then - local panned_step = new_pos - self.current_pos - self.view.dim_area.x = 0 - self.view.dim_area.h = self.ui.dimen.h - math.abs(panned_step) - self.view.dim_area.w = self.ui.dimen.w - if panned_step < 0 then - self.view.dim_area.y = self.ui.dimen.h - self.view.dim_area.h - elseif panned_step > 0 then - self.view.dim_area.y = 0 - end - end - self.ui:handleEvent(Event:new("PosUpdate", new_pos)) + if new_pos == self.current_pos then return end + if new_pos < 0 then new_pos = 0 end + if new_pos > self.doc_height then new_pos = self.doc_height end + -- adjust dim_area according to new_pos + if self.view.view_mode ~= "page" and self.show_overlap_enable then + local panned_step = new_pos - self.current_pos + self.view.dim_area.x = 0 + self.view.dim_area.h = self.ui.dimen.h - math.abs(panned_step) + self.view.dim_area.w = self.ui.dimen.w + if panned_step < 0 then + self.view.dim_area.y = self.ui.dimen.h - self.view.dim_area.h + elseif panned_step > 0 then + self.view.dim_area.y = 0 + end + end + self.ui:handleEvent(Event:new("PosUpdate", new_pos)) end function ReaderRolling:gotoPage(new_page) - self.ui.document:gotoPage(new_page) - self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage())) + self.ui.document:gotoPage(new_page) + self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage())) end function ReaderRolling:gotoXPointer(xpointer) - if self.view.view_mode == "page" then - self:gotoPage(self.ui.document:getPageFromXPointer(xpointer)) - else - self:gotoPos(self.ui.document:getPosFromXPointer(xpointer)) - end + if self.view.view_mode == "page" then + self:gotoPage(self.ui.document:getPageFromXPointer(xpointer)) + else + self:gotoPos(self.ui.document:getPosFromXPointer(xpointer)) + end end function ReaderRolling:gotoPercent(new_percent) - self:gotoPos(new_percent * self.doc_height / 10000) + self:gotoPos(new_percent * self.doc_height / 10000) end function ReaderRolling:onGotoPage(number) - self:gotoPage(number) - return true + self:gotoPage(number) + return true end --[[ @@ -375,20 +375,20 @@ currently we don't need to get page links on each page/pos update since we can check link on the fly when tapping on the screen --]] function ReaderRolling:updatePageLink() - DEBUG("update page link") - local links = self.ui.document:getPageLinks() - self.view.links = links + DEBUG("update page link") + local links = self.ui.document:getPageLinks() + self.view.links = links end function ReaderRolling:updateBatteryState() - DEBUG("update battery state") - if self.view.view_mode == "page" then - local powerd = Device:getPowerDevice() - local state = powerd:isCharging() and -1 or powerd:getCapacity() - if state then - self.ui.document:setBatteryState(state) - end - end + DEBUG("update battery state") + if self.view.view_mode == "page" then + local powerd = Device:getPowerDevice() + local state = powerd:isCharging() and -1 or powerd:getCapacity() + if state then + self.ui.document:setBatteryState(state) + end + end end return ReaderRolling diff --git a/frontend/ui/reader/readerrotation.lua b/frontend/ui/reader/readerrotation.lua index 7cb1ae1d0..4711ce105 100644 --- a/frontend/ui/reader/readerrotation.lua +++ b/frontend/ui/reader/readerrotation.lua @@ -7,72 +7,72 @@ local GestureRange = require("ui/gesturerange") local _ = require("gettext") local ReaderRotation = InputContainer:new{ - ROTATE_ANGLE_THRESHOLD = 15, - current_rotation = 0 + ROTATE_ANGLE_THRESHOLD = 15, + current_rotation = 0 } function ReaderRotation:init() - if Device:hasKeyboard() then - self.key_events = { - -- these will all generate the same event, just with different arguments - RotateLeft = { - {"J"}, - doc = _("rotate left by 90 degrees"), - event = "Rotate", args = -90 }, - RotateRight = { - {"K"}, - doc = _("rotate right by 90 degrees"), - event = "Rotate", args = 90 }, - } - end - if Device:isTouchDevice() then - self.ges_events = { - RotateGes = { - GestureRange:new{ - ges = "rotate", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - TwoFingerPanRelease = { - GestureRange:new{ - ges = "two_finger_pan_release", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - } - } - end + if Device:hasKeyboard() then + self.key_events = { + -- these will all generate the same event, just with different arguments + RotateLeft = { + {"J"}, + doc = _("rotate left by 90 degrees"), + event = "Rotate", args = -90 }, + RotateRight = { + {"K"}, + doc = _("rotate right by 90 degrees"), + event = "Rotate", args = 90 }, + } + end + if Device:isTouchDevice() then + self.ges_events = { + RotateGes = { + GestureRange:new{ + ges = "rotate", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + TwoFingerPanRelease = { + GestureRange:new{ + ges = "two_finger_pan_release", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + } + end end -- TODO: reset rotation on new document, maybe on new page? function ReaderRotation:onRotate(rotate_by) - self.current_rotation = (self.current_rotation + rotate_by) % 360 - self.ui:handleEvent(Event:new("RotationUpdate", self.current_rotation)) - return true + self.current_rotation = (self.current_rotation + rotate_by) % 360 + self.ui:handleEvent(Event:new("RotationUpdate", self.current_rotation)) + return true end function ReaderRotation:onRotateGes(arg, ges) - self.rotate_angle = ges.angle - return true + self.rotate_angle = ges.angle + return true end function ReaderRotation:onTwoFingerPanRelease(arg, ges) - if self.rotate_angle and self.rotate_angle > self.ROTATE_ANGLE_THRESHOLD then - if Screen:getScreenMode() == "portrait" then - self.ui:handleEvent(Event:new("SetScreenMode", "landscape")) - else - self.ui:handleEvent(Event:new("SetScreenMode", "portrait")) - end - self.rotate_angle = nil - end + if self.rotate_angle and self.rotate_angle > self.ROTATE_ANGLE_THRESHOLD then + if Screen:getScreenMode() == "portrait" then + self.ui:handleEvent(Event:new("SetScreenMode", "landscape")) + else + self.ui:handleEvent(Event:new("SetScreenMode", "portrait")) + end + self.rotate_angle = nil + end end return ReaderRotation diff --git a/frontend/ui/reader/readerscreenshot.lua b/frontend/ui/reader/readerscreenshot.lua index 32c030dcf..1b1638c09 100644 --- a/frontend/ui/reader/readerscreenshot.lua +++ b/frontend/ui/reader/readerscreenshot.lua @@ -10,36 +10,36 @@ local DEBUG = require("dbg") local ReaderScreenshot = InputContainer:new{} function ReaderScreenshot:init() - local diagonal = math.sqrt( - math.pow(Screen:getWidth(), 2) + - math.pow(Screen:getHeight(), 2) - ) - self.ges_events = { - Screenshot = { - GestureRange:new{ - ges = "two_finger_tap", - scale = {diagonal - Screen:scaleByDPI(100), diagonal}, - rate = 1.0, - } - }, - } + local diagonal = math.sqrt( + math.pow(Screen:getWidth(), 2) + + math.pow(Screen:getHeight(), 2) + ) + self.ges_events = { + Screenshot = { + GestureRange:new{ + ges = "two_finger_tap", + scale = {diagonal - Screen:scaleByDPI(100), diagonal}, + rate = 1.0, + } + }, + } end function ReaderScreenshot:onScreenshot() - if Device:getModel() ~= 'Kobo_phoenix' then - os.execute("screenshot") - else Screen.bb:invert() - local screenshot_name = os.date("screenshots/Screenshot_%Y-%B-%d_%Hh%M.pam") - UIManager:show(InfoMessage:new{ - text = _("Writing screen to ")..screenshot_name, - timeout = 2, - }) - Screen.bb:writePAM(screenshot_name) - DEBUG(screenshot_name) - Screen.bb:invert() - end - UIManager.full_refresh = true - return true + if Device:getModel() ~= 'Kobo_phoenix' then + os.execute("screenshot") + else Screen.bb:invert() + local screenshot_name = os.date("screenshots/Screenshot_%Y-%B-%d_%Hh%M.pam") + UIManager:show(InfoMessage:new{ + text = _("Writing screen to ")..screenshot_name, + timeout = 2, + }) + Screen.bb:writePAM(screenshot_name) + DEBUG(screenshot_name) + Screen.bb:invert() + end + UIManager.full_refresh = true + return true end return ReaderScreenshot diff --git a/frontend/ui/reader/readertoc.lua b/frontend/ui/reader/readertoc.lua index 9bd7d6794..bfc6c752e 100644 --- a/frontend/ui/reader/readertoc.lua +++ b/frontend/ui/reader/readertoc.lua @@ -10,135 +10,135 @@ local Event = require("ui/event") local _ = require("gettext") local ReaderToc = InputContainer:new{ - toc = nil, - toc_menu_title = _("Table of contents"), + toc = nil, + toc_menu_title = _("Table of contents"), } function ReaderToc:init() - if Device:hasKeyboard() then - self.key_events = { - ShowToc = { - { "T" }, - doc = _("show Table of Content menu") }, - } - end - if Device:isTouchDevice() then - self.ges_events = { - ShowToc = { - GestureRange:new{ - ges = "two_finger_swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - }, - direction = "east" - } - }, - } - end - self.ui.menu:registerToMainMenu(self) + if Device:hasKeyboard() then + self.key_events = { + ShowToc = { + { "T" }, + doc = _("show Table of Content menu") }, + } + end + if Device:isTouchDevice() then + self.ges_events = { + ShowToc = { + GestureRange:new{ + ges = "two_finger_swipe", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + }, + direction = "east" + } + }, + } + end + self.ui.menu:registerToMainMenu(self) end function ReaderToc:cleanUpTocTitle(title) - return (title:gsub("\13", "")) + return (title:gsub("\13", "")) end function ReaderToc:onSetDimensions(dimen) - self.dimen = dimen + self.dimen = dimen end function ReaderToc:onUpdateToc() - self.toc = nil - return true + self.toc = nil + return true end function ReaderToc:fillToc() - self.toc = self.ui.document:getToc() + self.toc = self.ui.document:getToc() end -- _getTocTitleByPage wrapper, so specific reader -- can tranform pageno according its need function ReaderToc:getTocTitleByPage(pn_or_xp) - local page = pn_or_xp - if type(pn_or_xp) == "string" then - page = self.ui.document:getPageFromXPointer(pn_or_xp) - end - return self:_getTocTitleByPage(page) + local page = pn_or_xp + if type(pn_or_xp) == "string" then + page = self.ui.document:getPageFromXPointer(pn_or_xp) + end + return self:_getTocTitleByPage(page) end function ReaderToc:_getTocTitleByPage(pageno) - if not self.toc then - -- build toc when needed. - self:fillToc() - end - - -- no table of content - if #self.toc == 0 then - return "" - end - - local pre_entry = self.toc[1] - for _k,_v in ipairs(self.toc) do - if _v.page > pageno then - break - end - pre_entry = _v - end - return self:cleanUpTocTitle(pre_entry.title) + if not self.toc then + -- build toc when needed. + self:fillToc() + end + + -- no table of content + if #self.toc == 0 then + return "" + end + + local pre_entry = self.toc[1] + for _k,_v in ipairs(self.toc) do + if _v.page > pageno then + break + end + pre_entry = _v + end + return self:cleanUpTocTitle(pre_entry.title) end function ReaderToc:getTocTitleOfCurrentPage() - return self:getTocTitleByPage(self.pageno) + return self:getTocTitleByPage(self.pageno) end function ReaderToc:onShowToc() - if not self.toc then - self:fillToc() - end - -- build menu items - if #self.toc > 0 and not self.toc[1].text then - for _,v in ipairs(self.toc) do - v.text = (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title) - v.mandatory = v.page - end - end - - local menu_container = CenterContainer:new{ - dimen = Screen:getSize(), - } - - local toc_menu = Menu:new{ - title = _("Table of Contents"), - item_table = self.toc, - ui = self.ui, - width = Screen:getWidth()-50, - height = Screen:getHeight()-50, - show_parent = menu_container, - } - - table.insert(menu_container, toc_menu) - - function toc_menu:onMenuChoice(item) - self.ui:handleEvent(Event:new("PageUpdate", item.page)) - end - - toc_menu.close_callback = function() - UIManager:close(menu_container) - end - - UIManager:show(menu_container) - return true + if not self.toc then + self:fillToc() + end + -- build menu items + if #self.toc > 0 and not self.toc[1].text then + for _,v in ipairs(self.toc) do + v.text = (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title) + v.mandatory = v.page + end + end + + local menu_container = CenterContainer:new{ + dimen = Screen:getSize(), + } + + local toc_menu = Menu:new{ + title = _("Table of Contents"), + item_table = self.toc, + ui = self.ui, + width = Screen:getWidth()-50, + height = Screen:getHeight()-50, + show_parent = menu_container, + } + + table.insert(menu_container, toc_menu) + + function toc_menu:onMenuChoice(item) + self.ui:handleEvent(Event:new("PageUpdate", item.page)) + end + + toc_menu.close_callback = function() + UIManager:close(menu_container) + end + + UIManager:show(menu_container) + return true end function ReaderToc:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.navi, { - text = self.toc_menu_title, - callback = function() - self:onShowToc() - end, - }) + -- insert table to main reader menu + table.insert(tab_item_table.navi, { + text = self.toc_menu_title, + callback = function() + self:onShowToc() + end, + }) end return ReaderToc diff --git a/frontend/ui/reader/readertypeset.lua b/frontend/ui/reader/readertypeset.lua index 17d2f0dd0..98e9cfc52 100644 --- a/frontend/ui/reader/readertypeset.lua +++ b/frontend/ui/reader/readertypeset.lua @@ -6,121 +6,121 @@ local _ = require("gettext") -- lfs local ReaderTypeset = InputContainer:new{ - css_menu_title = _("Set render style"), - css = nil, - internal_css = true, + css_menu_title = _("Set render style"), + css = nil, + internal_css = true, } function ReaderTypeset:init() - self.ui.menu:registerToMainMenu(self) + self.ui.menu:registerToMainMenu(self) end function ReaderTypeset:onReadSettings(config) - self.css = config:readSetting("css") - if self.css and self.css ~= "" then - self.ui.document:setStyleSheet(self.css) - else - self.ui.document:setStyleSheet(self.ui.document.default_css) - self.css = self.ui.document.default_css - end + self.css = config:readSetting("css") + if self.css and self.css ~= "" then + self.ui.document:setStyleSheet(self.css) + else + self.ui.document:setStyleSheet(self.ui.document.default_css) + self.css = self.ui.document.default_css + end - -- default to enable embedded css - self.embedded_css = config:readSetting("embedded_css") or true - self.ui.document:setEmbeddedStyleSheet(self.embedded_css and 1 or 0) - - -- set page margins - self:onSetPageMargins(config:readSetting("copt_page_margins") or DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM) + -- default to enable embedded css + self.embedded_css = config:readSetting("embedded_css") or true + self.ui.document:setEmbeddedStyleSheet(self.embedded_css and 1 or 0) + + -- set page margins + self:onSetPageMargins(config:readSetting("copt_page_margins") or DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM) end function ReaderTypeset:onSaveSettings() - self.ui.doc_settings:saveSetting("css", self.css) - self.ui.doc_settings:saveSetting("embedded_css", self.embedded_css) + self.ui.doc_settings:saveSetting("css", self.css) + self.ui.doc_settings:saveSetting("embedded_css", self.embedded_css) end function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle) - self:toggleEmbeddedStyleSheet(toggle) - return true + self:toggleEmbeddedStyleSheet(toggle) + return true end function ReaderTypeset:genStyleSheetMenu() - local file_list = { - { - text = _("clear all external styles"), - callback = function() - self:setStyleSheet(nil) - end - }, - { - text = _("Auto"), - callback = function() - self:setStyleSheet(self.ui.document.default_css) - end - }, - } - for f in lfs.dir("./data") do - if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then - table.insert(file_list, { - text = f, - callback = function() - self:setStyleSheet("./data/"..f) - end - }) - end - end - return file_list + local file_list = { + { + text = _("clear all external styles"), + callback = function() + self:setStyleSheet(nil) + end + }, + { + text = _("Auto"), + callback = function() + self:setStyleSheet(self.ui.document.default_css) + end + }, + } + for f in lfs.dir("./data") do + if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then + table.insert(file_list, { + text = f, + callback = function() + self:setStyleSheet("./data/"..f) + end + }) + end + end + return file_list end function ReaderTypeset:setStyleSheet(new_css) - if new_css ~= self.css then - --DEBUG("setting css to ", new_css) - self.css = new_css - if new_css == nil then - new_css = "" - end - self.ui.document:setStyleSheet(new_css) - self.ui:handleEvent(Event:new("UpdatePos")) - end + if new_css ~= self.css then + --DEBUG("setting css to ", new_css) + self.css = new_css + if new_css == nil then + new_css = "" + end + self.ui.document:setStyleSheet(new_css) + self.ui:handleEvent(Event:new("UpdatePos")) + end end function ReaderTypeset:setEmbededStyleSheetOnly() - if self.css ~= nil then - -- clear applied css - self.ui.document:setStyleSheet("") - self.ui.document:setEmbeddedStyleSheet(1) - self.css = nil - self.ui:handleEvent(Event:new("UpdatePos")) - end + if self.css ~= nil then + -- clear applied css + self.ui.document:setStyleSheet("") + self.ui.document:setEmbeddedStyleSheet(1) + self.css = nil + self.ui:handleEvent(Event:new("UpdatePos")) + end end function ReaderTypeset:toggleEmbeddedStyleSheet(toggle) - if not toggle then - self.embedded_css = false - self:setStyleSheet(self.ui.document.default_css) - self.ui.document:setEmbeddedStyleSheet(0) - else - self.embedded_css = true - --self:setStyleSheet(self.ui.document.default_css) - self.ui.document:setEmbeddedStyleSheet(1) - end - self.ui:handleEvent(Event:new("UpdatePos")) + if not toggle then + self.embedded_css = false + self:setStyleSheet(self.ui.document.default_css) + self.ui.document:setEmbeddedStyleSheet(0) + else + self.embedded_css = true + --self:setStyleSheet(self.ui.document.default_css) + self.ui.document:setEmbeddedStyleSheet(1) + end + self.ui:handleEvent(Event:new("UpdatePos")) end function ReaderTypeset:addToMainMenu(tab_item_table) - -- insert table to main reader menu - table.insert(tab_item_table.typeset, { - text = self.css_menu_title, - sub_item_table = self:genStyleSheetMenu(), - }) + -- insert table to main reader menu + table.insert(tab_item_table.typeset, { + text = self.css_menu_title, + sub_item_table = self:genStyleSheetMenu(), + }) end function ReaderTypeset:onSetPageMargins(margins) - local left = Screen:scaleByDPI(margins[1]) - local top = Screen:scaleByDPI(margins[2]) - local right = Screen:scaleByDPI(margins[3]) - local bottom = Screen:scaleByDPI(margins[4]) - self.ui.document:setPageMargins(left, top, right, bottom) - self.ui:handleEvent(Event:new("UpdatePos")) - return true + local left = Screen:scaleByDPI(margins[1]) + local top = Screen:scaleByDPI(margins[2]) + local right = Screen:scaleByDPI(margins[3]) + local bottom = Screen:scaleByDPI(margins[4]) + self.ui.document:setPageMargins(left, top, right, bottom) + self.ui:handleEvent(Event:new("UpdatePos")) + return true end return ReaderTypeset diff --git a/frontend/ui/reader/readerview.lua b/frontend/ui/reader/readerview.lua index ebd9f6808..2a9c9264b 100644 --- a/frontend/ui/reader/readerview.lua +++ b/frontend/ui/reader/readerview.lua @@ -9,632 +9,632 @@ local Event = require("ui/event") local DEBUG = require("dbg") local ReaderView = OverlapGroup:new{ - document = nil, - - -- single page state - state = { - page = nil, - pos = 0, - zoom = 1.0, - rotation = 0, - gamma = 1.0, - offset = nil, - bbox = nil, - }, - outer_page_color = DOUTER_PAGE_COLOR, - -- hightlight with "lighten" or "underscore" or "invert" - highlight = { - lighten_color = 0.2, -- color range [0.0, 1.0] - temp_drawer = "invert", - temp = {}, - saved_drawer = "lighten", - saved = {}, - }, - highlight_visible = true, - -- PDF/DjVu continuous paging - page_scroll = nil, - page_bgcolor = DBACKGROUND_COLOR, - page_states = {}, - scroll_mode = "vertical", - page_gap = { - width = Screen:scaleByDPI(8), - height = Screen:scaleByDPI(8), - color = 8, - }, - -- DjVu page rendering mode (used in djvu.c:drawPage()) - render_mode = DRENDER_MODE, -- default to COLOR - -- Crengine view mode - view_mode = DCREREADER_VIEW_MODE, -- default to page mode - hinting = true, - - -- visible area within current viewing page - visible_area = Geom:new{x = 0, y = 0}, - -- dimen for current viewing page - page_area = Geom:new{}, - -- dimen for area to dim - dim_area = Geom:new{w = 0, h = 0}, - -- has footer - footer_visible = nil, - -- has dogear - dogear_visible = false, - -- in flipping state - flipping_visible = false, - - -- auto save settings after turning pages - auto_save_paging_count = 0, + document = nil, + + -- single page state + state = { + page = nil, + pos = 0, + zoom = 1.0, + rotation = 0, + gamma = 1.0, + offset = nil, + bbox = nil, + }, + outer_page_color = DOUTER_PAGE_COLOR, + -- hightlight with "lighten" or "underscore" or "invert" + highlight = { + lighten_color = 0.2, -- color range [0.0, 1.0] + temp_drawer = "invert", + temp = {}, + saved_drawer = "lighten", + saved = {}, + }, + highlight_visible = true, + -- PDF/DjVu continuous paging + page_scroll = nil, + page_bgcolor = DBACKGROUND_COLOR, + page_states = {}, + scroll_mode = "vertical", + page_gap = { + width = Screen:scaleByDPI(8), + height = Screen:scaleByDPI(8), + color = 8, + }, + -- DjVu page rendering mode (used in djvu.c:drawPage()) + render_mode = DRENDER_MODE, -- default to COLOR + -- Crengine view mode + view_mode = DCREREADER_VIEW_MODE, -- default to page mode + hinting = true, + + -- visible area within current viewing page + visible_area = Geom:new{x = 0, y = 0}, + -- dimen for current viewing page + page_area = Geom:new{}, + -- dimen for area to dim + dim_area = Geom:new{w = 0, h = 0}, + -- has footer + footer_visible = nil, + -- has dogear + dogear_visible = false, + -- in flipping state + flipping_visible = false, + + -- auto save settings after turning pages + auto_save_paging_count = 0, } function ReaderView:init() - self:resetLayout() + self:resetLayout() end function ReaderView:resetLayout() - self.dogear = ReaderDogear:new{ - view = self, - ui = self.ui, - } - self.footer = ReaderFooter:new{ - view = self, - ui = self.ui, - visible = self.footer_visible, - } - self.flipping = ReaderFlipping:new{ - view = self, - ui = self.ui, - } - self[1] = self.dogear - self[2] = self.footer - self[3] = self.flipping + self.dogear = ReaderDogear:new{ + view = self, + ui = self.ui, + } + self.footer = ReaderFooter:new{ + view = self, + ui = self.ui, + visible = self.footer_visible, + } + self.flipping = ReaderFlipping:new{ + view = self, + ui = self.ui, + } + self[1] = self.dogear + self[2] = self.footer + self[3] = self.flipping end function ReaderView:paintTo(bb, x, y) - DEBUG("painting", self.visible_area, "to", x, y) - if self.page_scroll then - self:drawPageBackground(bb, x, y) - else - self:drawPageSurround(bb, x, y) - end - - -- draw page content - if self.ui.document.info.has_pages then - if self.page_scroll then - self:drawScrollPages(bb, x, y) - else - self:drawSinglePage(bb, x, y) - end - else - if self.view_mode == "page" then - self:drawPageView(bb, x, y) - elseif self.view_mode == "scroll" then - self:drawScrollView(bb, x, y) - end - end - - -- dim last read area - if self.document.view_mode ~= "page" - and self.dim_area.w ~= 0 and self.dim_area.h ~= 0 then - bb:dimRect( - self.dim_area.x, self.dim_area.y, - self.dim_area.w, self.dim_area.h - ) - end - -- draw saved highlight - if self.highlight_visible then - self:drawSavedHighlight(bb, x, y) - end - -- draw temporary highlight - if self.highlight.temp then - self:drawTempHighlight(bb, x, y) - end - -- paint dogear - if self.dogear_visible then - self.dogear:paintTo(bb, x, y) - end - -- paint footer - if self.footer_visible then - self.footer:paintTo(bb, x, y) - end - -- paint flipping - if self.flipping_visible then - self.flipping:paintTo(bb, x, y) - end - -- stop activity indicator - self.ui:handleEvent(Event:new("StopActivityIndicator")) + DEBUG("painting", self.visible_area, "to", x, y) + if self.page_scroll then + self:drawPageBackground(bb, x, y) + else + self:drawPageSurround(bb, x, y) + end + + -- draw page content + if self.ui.document.info.has_pages then + if self.page_scroll then + self:drawScrollPages(bb, x, y) + else + self:drawSinglePage(bb, x, y) + end + else + if self.view_mode == "page" then + self:drawPageView(bb, x, y) + elseif self.view_mode == "scroll" then + self:drawScrollView(bb, x, y) + end + end + + -- dim last read area + if self.document.view_mode ~= "page" + and self.dim_area.w ~= 0 and self.dim_area.h ~= 0 then + bb:dimRect( + self.dim_area.x, self.dim_area.y, + self.dim_area.w, self.dim_area.h + ) + end + -- draw saved highlight + if self.highlight_visible then + self:drawSavedHighlight(bb, x, y) + end + -- draw temporary highlight + if self.highlight.temp then + self:drawTempHighlight(bb, x, y) + end + -- paint dogear + if self.dogear_visible then + self.dogear:paintTo(bb, x, y) + end + -- paint footer + if self.footer_visible then + self.footer:paintTo(bb, x, y) + end + -- paint flipping + if self.flipping_visible then + self.flipping:paintTo(bb, x, y) + end + -- stop activity indicator + self.ui:handleEvent(Event:new("StopActivityIndicator")) end --[[ Given coordinates on the screen return position in original page ]]-- function ReaderView:screenToPageTransform(pos) - if self.ui.document.info.has_pages then - if self.page_scroll then - return self:getScrollPagePosition(pos) - else - return self:getSinglePagePosition(pos) - end - else - pos.page = self.ui.document:getCurrentPage() - local last_y = self.ui.document:getCurrentPos() - DEBUG("document has no pages at", pos) - return pos - end + if self.ui.document.info.has_pages then + if self.page_scroll then + return self:getScrollPagePosition(pos) + else + return self:getSinglePagePosition(pos) + end + else + pos.page = self.ui.document:getCurrentPage() + local last_y = self.ui.document:getCurrentPos() + DEBUG("document has no pages at", pos) + return pos + end end --[[ Given rectangle in original page return rectangle on the screen ]]-- function ReaderView:pageToScreenTransform(page, rect) - if self.ui.document.info.has_pages then - if self.page_scroll then - return self:getScrollPageRect(page, rect) - else - return self:getSinglePageRect(rect) - end - else - return rect - end + if self.ui.document.info.has_pages then + if self.page_scroll then + return self:getScrollPageRect(page, rect) + else + return self:getSinglePageRect(rect) + end + else + return rect + end end function ReaderView:drawPageBackground(bb, x, y) - bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.page_bgcolor) + bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.page_bgcolor) end function ReaderView:drawPageSurround(bb, x, y) - if self.dimen.h > self.visible_area.h then - bb:paintRect(x, y, self.dimen.w, self.state.offset.y, self.outer_page_color) - bb:paintRect(x, y + self.dimen.h - self.state.offset.y - 1, - self.dimen.w, self.state.offset.y + 1, self.outer_page_color) - end - if self.dimen.w > self.visible_area.w then - bb:paintRect(x, y, self.state.offset.x, self.dimen.h, self.outer_page_color) - bb:paintRect(x + self.dimen.w - self.state.offset.x - 1, y, - self.state.offset.x + 1, self.dimen.h, self.outer_page_color) - end + if self.dimen.h > self.visible_area.h then + bb:paintRect(x, y, self.dimen.w, self.state.offset.y, self.outer_page_color) + bb:paintRect(x, y + self.dimen.h - self.state.offset.y - 1, + self.dimen.w, self.state.offset.y + 1, self.outer_page_color) + end + if self.dimen.w > self.visible_area.w then + bb:paintRect(x, y, self.state.offset.x, self.dimen.h, self.outer_page_color) + bb:paintRect(x + self.dimen.w - self.state.offset.x - 1, y, + self.state.offset.x + 1, self.dimen.h, self.outer_page_color) + end end function ReaderView:drawScrollPages(bb, x, y) - local pos = Geom:new{x = x , y = y} - for page, state in ipairs(self.page_states) do - self.ui.document:drawPage( - bb, - pos.x + state.offset.x, - pos.y + state.offset.y, - state.visible_area, - state.page, - state.zoom, - state.rotation, - state.gamma, - self.render_mode) - pos.y = pos.y + state.visible_area.h - -- draw page gap if not the last part - if page ~= #self.page_states then - self:drawPageGap(bb, pos.x, pos.y) - pos.y = pos.y + self.page_gap.height - end - end - UIManager:scheduleIn(0, function() - self.ui:handleEvent(Event:new("HintPage", self.hinting)) - end) + local pos = Geom:new{x = x , y = y} + for page, state in ipairs(self.page_states) do + self.ui.document:drawPage( + bb, + pos.x + state.offset.x, + pos.y + state.offset.y, + state.visible_area, + state.page, + state.zoom, + state.rotation, + state.gamma, + self.render_mode) + pos.y = pos.y + state.visible_area.h + -- draw page gap if not the last part + if page ~= #self.page_states then + self:drawPageGap(bb, pos.x, pos.y) + pos.y = pos.y + self.page_gap.height + end + end + UIManager:scheduleIn(0, function() + self.ui:handleEvent(Event:new("HintPage", self.hinting)) + end) end function ReaderView:getCurrentPageList() - local pages = {} - if self.ui.document.info.has_pages then - if self.page_scroll then - for _, state in ipairs(self.page_states) do - table.insert(pages, state.page) - end - else - table.insert(pages, self.state.page) - end - end - return pages + local pages = {} + if self.ui.document.info.has_pages then + if self.page_scroll then + for _, state in ipairs(self.page_states) do + table.insert(pages, state.page) + end + else + table.insert(pages, self.state.page) + end + end + return pages end function ReaderView:getScrollPagePosition(pos) - local x_s, y_s = pos.x, pos.y - local x_p, y_p = nil, nil - for _, state in ipairs(self.page_states) do - if y_s < state.visible_area.h + state.offset.y then - y_p = (state.visible_area.y + y_s - state.offset.y) / state.zoom - x_p = (state.visible_area.x + x_s - state.offset.x) / state.zoom - return { - x = x_p, - y = y_p, - page = state.page, - zoom = state.zoom, - rotation = state.rotation, - } - else - y_s = y_s - state.visible_area.h - self.page_gap.height - end - end + local x_s, y_s = pos.x, pos.y + local x_p, y_p = nil, nil + for _, state in ipairs(self.page_states) do + if y_s < state.visible_area.h + state.offset.y then + y_p = (state.visible_area.y + y_s - state.offset.y) / state.zoom + x_p = (state.visible_area.x + x_s - state.offset.x) / state.zoom + return { + x = x_p, + y = y_p, + page = state.page, + zoom = state.zoom, + rotation = state.rotation, + } + else + y_s = y_s - state.visible_area.h - self.page_gap.height + end + end end function ReaderView:getScrollPageRect(page, rect_p) - local rect_s = Geom:new{} - for _, state in ipairs(self.page_states) do - local trans_p = Geom:new(rect_p):copy() - trans_p:transformByScale(state.zoom, state.zoom) - if page == state.page and state.visible_area:contains(trans_p) then - rect_s.x = rect_s.x + state.offset.x + trans_p.x - state.visible_area.x - rect_s.y = rect_s.y + state.offset.y + trans_p.y - state.visible_area.y - rect_s.w = trans_p.w - rect_s.h = trans_p.h - return rect_s - end - rect_s.y = rect_s.y + state.visible_area.h + self.page_gap.height - end + local rect_s = Geom:new{} + for _, state in ipairs(self.page_states) do + local trans_p = Geom:new(rect_p):copy() + trans_p:transformByScale(state.zoom, state.zoom) + if page == state.page and state.visible_area:contains(trans_p) then + rect_s.x = rect_s.x + state.offset.x + trans_p.x - state.visible_area.x + rect_s.y = rect_s.y + state.offset.y + trans_p.y - state.visible_area.y + rect_s.w = trans_p.w + rect_s.h = trans_p.h + return rect_s + end + rect_s.y = rect_s.y + state.visible_area.h + self.page_gap.height + end end function ReaderView:drawPageGap(bb, x, y) - if self.scroll_mode == "vertical" then - bb:paintRect(x, y, self.dimen.w, self.page_gap.height, self.page_gap.color) - elseif self.scroll_mode == "horizontal" then - bb:paintRect(x, y, self.page_gap.width, self.dimen.h, self.page_gap.color) - end + if self.scroll_mode == "vertical" then + bb:paintRect(x, y, self.dimen.w, self.page_gap.height, self.page_gap.color) + elseif self.scroll_mode == "horizontal" then + bb:paintRect(x, y, self.page_gap.width, self.dimen.h, self.page_gap.color) + end end function ReaderView:drawSinglePage(bb, x, y) - self.ui.document:drawPage( - bb, - x + self.state.offset.x, - y + self.state.offset.y, - self.visible_area, - self.state.page, - self.state.zoom, - self.state.rotation, - self.state.gamma, - self.render_mode) - UIManager:scheduleIn(0, function() - self.ui:handleEvent(Event:new("HintPage", self.hinting)) - end) + self.ui.document:drawPage( + bb, + x + self.state.offset.x, + y + self.state.offset.y, + self.visible_area, + self.state.page, + self.state.zoom, + self.state.rotation, + self.state.gamma, + self.render_mode) + UIManager:scheduleIn(0, function() + self.ui:handleEvent(Event:new("HintPage", self.hinting)) + end) end function ReaderView:getSinglePagePosition(pos) - local x_s, y_s = pos.x, pos.y - return { - x = (self.visible_area.x + x_s - self.state.offset.x) / self.state.zoom, - y = (self.visible_area.y + y_s - self.state.offset.y) / self.state.zoom, - page = self.state.page, - zoom = self.state.zoom, - rotation = self.state.rotation, - } + local x_s, y_s = pos.x, pos.y + return { + x = (self.visible_area.x + x_s - self.state.offset.x) / self.state.zoom, + y = (self.visible_area.y + y_s - self.state.offset.y) / self.state.zoom, + page = self.state.page, + zoom = self.state.zoom, + rotation = self.state.rotation, + } end function ReaderView:getSinglePageRect(rect_p) - local rect_s = Geom:new{} - local trans_p = Geom:new(rect_p):copy() - trans_p:transformByScale(self.state.zoom, self.state.zoom) - if self.visible_area:contains(trans_p) then - rect_s.x = self.state.offset.x + trans_p.x - self.visible_area.x - rect_s.y = self.state.offset.y + trans_p.y - self.visible_area.y - rect_s.w = trans_p.w - rect_s.h = trans_p.h - return rect_s - end + local rect_s = Geom:new{} + local trans_p = Geom:new(rect_p):copy() + trans_p:transformByScale(self.state.zoom, self.state.zoom) + if self.visible_area:contains(trans_p) then + rect_s.x = self.state.offset.x + trans_p.x - self.visible_area.x + rect_s.y = self.state.offset.y + trans_p.y - self.visible_area.y + rect_s.w = trans_p.w + rect_s.h = trans_p.h + return rect_s + end end function ReaderView:drawPageView(bb, x, y) - self.ui.document:drawCurrentViewByPage( - bb, - x + self.state.offset.x, - y + self.state.offset.y, - self.visible_area, - self.state.page) + self.ui.document:drawCurrentViewByPage( + bb, + x + self.state.offset.x, + y + self.state.offset.y, + self.visible_area, + self.state.page) end function ReaderView:drawScrollView(bb, x, y) - self.ui.document:drawCurrentViewByPos( - bb, - x + self.state.offset.x, - y + self.state.offset.y, - self.visible_area, - self.state.pos) + self.ui.document:drawCurrentViewByPos( + bb, + x + self.state.offset.x, + y + self.state.offset.y, + self.visible_area, + self.state.pos) end function ReaderView:drawTempHighlight(bb, x, y) - for page, boxes in pairs(self.highlight.temp) do - for i = 1, #boxes do - local rect = self:pageToScreenTransform(page, boxes[i]) - if rect then - self:drawHighlightRect(bb, x, y, rect, self.highlight.temp_drawer) - end - end - end + for page, boxes in pairs(self.highlight.temp) do + for i = 1, #boxes do + local rect = self:pageToScreenTransform(page, boxes[i]) + if rect then + self:drawHighlightRect(bb, x, y, rect, self.highlight.temp_drawer) + end + end + end end function ReaderView:drawSavedHighlight(bb, x, y) - if self.ui.document.info.has_pages then - self:drawPageSavedHighlight(bb, x, y) - else - self:drawXPointerSavedHighlight(bb, x, y) - end + if self.ui.document.info.has_pages then + self:drawPageSavedHighlight(bb, x, y) + else + self:drawXPointerSavedHighlight(bb, x, y) + end end function ReaderView:drawPageSavedHighlight(bb, x, y) - local pages = self:getCurrentPageList() - for _, page in pairs(pages) do - local items = self.highlight.saved[page] - if not items then items = {} end - for i = 1, #items do - local item = items[i] - local pos0, pos1 = item.pos0, item.pos1 - local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1) - if boxes then - for _, box in pairs(boxes) do - local rect = self:pageToScreenTransform(page, box) - if rect then - self:drawHighlightRect(bb, x, y, rect, item.drawer or self.highlight.saved_drawer) - end - end -- end for each box - end -- end if boxes - end -- end for each highlight - end -- end for each page + local pages = self:getCurrentPageList() + for _, page in pairs(pages) do + local items = self.highlight.saved[page] + if not items then items = {} end + for i = 1, #items do + local item = items[i] + local pos0, pos1 = item.pos0, item.pos1 + local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1) + if boxes then + for _, box in pairs(boxes) do + local rect = self:pageToScreenTransform(page, box) + if rect then + self:drawHighlightRect(bb, x, y, rect, item.drawer or self.highlight.saved_drawer) + end + end -- end for each box + end -- end if boxes + end -- end for each highlight + end -- end for each page end function ReaderView:drawXPointerSavedHighlight(bb, x, y) - for page, _ in pairs(self.highlight.saved) do - local items = self.highlight.saved[page] - if not items then items = {} end - for j = 1, #items do - local item = items[j] - local pos0, pos1 = item.pos0, item.pos1 - local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1) - if boxes then - for _, box in pairs(boxes) do - local rect = self:pageToScreenTransform(page, box) - if rect then - self:drawHighlightRect(bb, x, y, rect, item.drawer or self.highlight.saved_drawer) - end - end -- end for each box - end -- end if boxes - end -- end for each highlight - end -- end for all saved highlight + for page, _ in pairs(self.highlight.saved) do + local items = self.highlight.saved[page] + if not items then items = {} end + for j = 1, #items do + local item = items[j] + local pos0, pos1 = item.pos0, item.pos1 + local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1) + if boxes then + for _, box in pairs(boxes) do + local rect = self:pageToScreenTransform(page, box) + if rect then + self:drawHighlightRect(bb, x, y, rect, item.drawer or self.highlight.saved_drawer) + end + end -- end for each box + end -- end if boxes + end -- end for each highlight + end -- end for all saved highlight end function ReaderView:drawHighlightRect(bb, x, y, rect, drawer) - local x, y, w, h = rect.x, rect.y, rect.w, rect.h - - if drawer == "underscore" then - self.highlight.line_width = self.highlight.line_width or 2 - self.highlight.line_color = self.highlight.line_color or 5 - bb:paintRect(x, y+h-1, w, - self.highlight.line_width, - self.highlight.line_color) - elseif drawer == "lighten" then - bb:lightenRect(x, y, w, h, self.highlight.lighten_color) - elseif drawer == "invert" then - bb:invertRect(x, y, w, h) - end + local x, y, w, h = rect.x, rect.y, rect.w, rect.h + + if drawer == "underscore" then + self.highlight.line_width = self.highlight.line_width or 2 + self.highlight.line_color = self.highlight.line_color or 5 + bb:paintRect(x, y+h-1, w, + self.highlight.line_width, + self.highlight.line_color) + elseif drawer == "lighten" then + bb:lightenRect(x, y, w, h, self.highlight.lighten_color) + elseif drawer == "invert" then + bb:invertRect(x, y, w, h) + end end function ReaderView:getPageArea(page, zoom, rotation) - if self.use_bbox then - return self.ui.document:getUsedBBoxDimensions(page, zoom, rotation) - else - return self.ui.document:getPageDimensions(page, zoom, rotation) - end + if self.use_bbox then + return self.ui.document:getUsedBBoxDimensions(page, zoom, rotation) + else + return self.ui.document:getPageDimensions(page, zoom, rotation) + end end --[[ This method is supposed to be only used by ReaderPaging --]] function ReaderView:recalculate() - local page_size = nil - if self.ui.document.info.has_pages and self.state.page then - self.page_area = self:getPageArea( - self.state.page, - self.state.zoom, - self.state.rotation) - -- starts from left top of page_area - self.visible_area.x = self.page_area.x - self.visible_area.y = self.page_area.y - -- reset our size - self.visible_area:setSizeTo(self.dimen) - -- and recalculate it according to page size - self.visible_area:offsetWithin(self.page_area, 0, 0) - -- clear dim area - self.dim_area.w = 0 - self.dim_area.h = 0 - self.ui:handleEvent( - Event:new("ViewRecalculate", self.visible_area, self.page_area)) - else - self.visible_area:setSizeTo(self.dimen) - end - self.state.offset = Geom:new{x = 0, y = 0} - if self.dimen.h > self.visible_area.h then - self.state.offset.y = (self.dimen.h - self.visible_area.h) / 2 - end - if self.dimen.w > self.visible_area.w then - self.state.offset.x = (self.dimen.w - self.visible_area.w) / 2 - end - -- flag a repaint so self:paintTo will be called - UIManager:setDirty(self.dialog) + local page_size = nil + if self.ui.document.info.has_pages and self.state.page then + self.page_area = self:getPageArea( + self.state.page, + self.state.zoom, + self.state.rotation) + -- starts from left top of page_area + self.visible_area.x = self.page_area.x + self.visible_area.y = self.page_area.y + -- reset our size + self.visible_area:setSizeTo(self.dimen) + -- and recalculate it according to page size + self.visible_area:offsetWithin(self.page_area, 0, 0) + -- clear dim area + self.dim_area.w = 0 + self.dim_area.h = 0 + self.ui:handleEvent( + Event:new("ViewRecalculate", self.visible_area, self.page_area)) + else + self.visible_area:setSizeTo(self.dimen) + end + self.state.offset = Geom:new{x = 0, y = 0} + if self.dimen.h > self.visible_area.h then + self.state.offset.y = (self.dimen.h - self.visible_area.h) / 2 + end + if self.dimen.w > self.visible_area.w then + self.state.offset.x = (self.dimen.w - self.visible_area.w) / 2 + end + -- flag a repaint so self:paintTo will be called + UIManager:setDirty(self.dialog) end function ReaderView:PanningUpdate(dx, dy) - DEBUG("pan by", dx, dy) - local old = self.visible_area:copy() - self.visible_area:offsetWithin(self.page_area, dx, dy) - if self.visible_area ~= old then - -- flag a repaint - UIManager:setDirty(self.dialog) - DEBUG("on pan: page_area", self.page_area) - DEBUG("on pan: visible_area", self.visible_area) - self.ui:handleEvent( - Event:new("ViewRecalculate", self.visible_area, self.page_area)) - end - return true + DEBUG("pan by", dx, dy) + local old = self.visible_area:copy() + self.visible_area:offsetWithin(self.page_area, dx, dy) + if self.visible_area ~= old then + -- flag a repaint + UIManager:setDirty(self.dialog) + DEBUG("on pan: page_area", self.page_area) + DEBUG("on pan: visible_area", self.visible_area) + self.ui:handleEvent( + Event:new("ViewRecalculate", self.visible_area, self.page_area)) + end + return true end function ReaderView:PanningStart(x, y) - DEBUG("panning start", x, y) - if not self.panning_visible_area then - self.panning_visible_area = self.visible_area:copy() - end - self.visible_area = self.panning_visible_area:copy() - self.visible_area:offsetWithin(self.page_area, x, y) - self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) - UIManager:setDirty(self.dialog) + DEBUG("panning start", x, y) + if not self.panning_visible_area then + self.panning_visible_area = self.visible_area:copy() + end + self.visible_area = self.panning_visible_area:copy() + self.visible_area:offsetWithin(self.page_area, x, y) + self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) + UIManager:setDirty(self.dialog) end function ReaderView:PanningStop() - self.panning_visible_area = nil + self.panning_visible_area = nil end function ReaderView:SetZoomCenter(x, y) - local old = self.visible_area:copy() - self.visible_area:centerWithin(self.page_area, x, y) - if self.visible_area ~= old then - self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) - UIManager:setDirty(self.dialog) - end + local old = self.visible_area:copy() + self.visible_area:centerWithin(self.page_area, x, y) + if self.visible_area ~= old then + self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) + UIManager:setDirty(self.dialog) + end end function ReaderView:onSetScreenMode(new_mode, rotation) - if new_mode == "landscape" or new_mode == "portrait" then - self.screen_mode = new_mode - if rotation ~= nil then - Screen:setRotationMode(rotation) - else - Screen:setScreenMode(new_mode) - end - self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) - self.ui:handleEvent(Event:new("InitScrollPageStates")) - end - self.cur_rotation_mode = Screen.cur_rotation_mode - return true + if new_mode == "landscape" or new_mode == "portrait" then + self.screen_mode = new_mode + if rotation ~= nil then + Screen:setRotationMode(rotation) + else + Screen:setScreenMode(new_mode) + end + self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) + self.ui:handleEvent(Event:new("InitScrollPageStates")) + end + self.cur_rotation_mode = Screen.cur_rotation_mode + return true end -- for returning to FileManager function ReaderView:onRestoreScreenMode(old_mode) - if old_mode == "landscape" or old_mode == "portrait" then - Screen:setScreenMode(old_mode) - self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) - end - return true + if old_mode == "landscape" or old_mode == "portrait" then + Screen:setScreenMode(old_mode) + self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) + end + return true end function ReaderView:onSetDimensions(dimensions) - self:resetLayout() - self.dimen = dimensions - -- recalculate view - self:recalculate() + self:resetLayout() + self.dimen = dimensions + -- recalculate view + self:recalculate() end function ReaderView:onRestoreDimensions(dimensions) - --DEBUG("restore dimen", dimensions) - self:resetLayout() - self.dimen = dimensions - -- recalculate view - self:recalculate() + --DEBUG("restore dimen", dimensions) + self:resetLayout() + self.dimen = dimensions + -- recalculate view + self:recalculate() end function ReaderView:onSetFullScreen(full_screen) - self.footer_visible = not full_screen - self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) + self.footer_visible = not full_screen + self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) end function ReaderView:onToggleScrollMode(page_scroll) - self.page_scroll = page_scroll - self:recalculate() - self.ui:handleEvent(Event:new("InitScrollPageStates")) + self.page_scroll = page_scroll + self:recalculate() + self.ui:handleEvent(Event:new("InitScrollPageStates")) end function ReaderView:onReadSettings(config) - self.render_mode = config:readSetting("render_mode") or 0 - local screen_mode = config:readSetting("screen_mode") or "portrait" - if screen_mode then - Screen:setScreenMode(screen_mode) - self:onSetScreenMode(screen_mode, config:readSetting("rotation_mode")) - end - self.state.gamma = config:readSetting("gamma") or DGLOBALGAMMA - local full_screen = config:readSetting("kopt_full_screen") or self.document.configurable.full_screen - local status_line = config:readSetting("copt_status_line") or self.document.configurable.status_line - self.footer_visible = (full_screen == 0 or status_line == 1) and true or false - 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") or {} + self.render_mode = config:readSetting("render_mode") or 0 + local screen_mode = config:readSetting("screen_mode") or "portrait" + if screen_mode then + Screen:setScreenMode(screen_mode) + self:onSetScreenMode(screen_mode, config:readSetting("rotation_mode")) + end + self.state.gamma = config:readSetting("gamma") or DGLOBALGAMMA + local full_screen = config:readSetting("kopt_full_screen") or self.document.configurable.full_screen + local status_line = config:readSetting("copt_status_line") or self.document.configurable.status_line + self.footer_visible = (full_screen == 0 or status_line == 1) and true or false + 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") or {} end function ReaderView:onPageUpdate(new_page_no) - self.state.page = new_page_no - self:recalculate() - self.highlight.temp = {} - self:autoSaveSettings() + self.state.page = new_page_no + self:recalculate() + self.highlight.temp = {} + self:autoSaveSettings() end function ReaderView:onPosUpdate(new_pos) - self.state.pos = new_pos - self:recalculate() - self.highlight.temp = {} - self:autoSaveSettings() + self.state.pos = new_pos + self:recalculate() + self.highlight.temp = {} + self:autoSaveSettings() end function ReaderView:onZoomUpdate(zoom) - self.state.zoom = zoom - self:recalculate() - self.highlight.temp = {} + self.state.zoom = zoom + self:recalculate() + self.highlight.temp = {} end function ReaderView:onBBoxUpdate(bbox) - self.use_bbox = bbox and true or false + self.use_bbox = bbox and true or false end function ReaderView:onRotationUpdate(rotation) - self.state.rotation = rotation - self:recalculate() + self.state.rotation = rotation + self:recalculate() end function ReaderView:onGammaUpdate(gamma) - self.state.gamma = gamma - if self.page_scroll then - self.ui:handleEvent(Event:new("UpdateScrollPageGamma", gamma)) - end + self.state.gamma = gamma + if self.page_scroll then + self.ui:handleEvent(Event:new("UpdateScrollPageGamma", gamma)) + end end function ReaderView:onFontSizeUpdate() - self.ui:handleEvent(Event:new("ReZoom")) + self.ui:handleEvent(Event:new("ReZoom")) end function ReaderView:onDefectSizeUpdate() - self.ui:handleEvent(Event:new("ReZoom")) + self.ui:handleEvent(Event:new("ReZoom")) end function ReaderView:onPageCrop() - self.ui:handleEvent(Event:new("ReZoom")) + self.ui:handleEvent(Event:new("ReZoom")) end function ReaderView:onMarginUpdate() - self.ui:handleEvent(Event:new("ReZoom")) + self.ui:handleEvent(Event:new("ReZoom")) end function ReaderView:onSetViewMode(new_mode) - self.view_mode = new_mode - self.ui.document:setViewMode(new_mode) - self.ui:handleEvent(Event:new("ChangeViewMode")) - return true + self.view_mode = new_mode + self.ui.document:setViewMode(new_mode) + self.ui:handleEvent(Event:new("ChangeViewMode")) + return true end function ReaderView:onSaveSettings() - self.ui.doc_settings:saveSetting("render_mode", self.render_mode) - self.ui.doc_settings:saveSetting("screen_mode", self.screen_mode) - self.ui.doc_settings:saveSetting("rotation_mode", self.cur_rotation_mode) - self.ui.doc_settings:saveSetting("gamma", self.state.gamma) - self.ui.doc_settings:saveSetting("highlight", self.highlight.saved) + self.ui.doc_settings:saveSetting("render_mode", self.render_mode) + self.ui.doc_settings:saveSetting("screen_mode", self.screen_mode) + self.ui.doc_settings:saveSetting("rotation_mode", self.cur_rotation_mode) + self.ui.doc_settings:saveSetting("gamma", self.state.gamma) + self.ui.doc_settings:saveSetting("highlight", self.highlight.saved) end function ReaderView:autoSaveSettings() - if DAUTO_SAVE_PAGING_COUNT then - if self.auto_save_paging_count == DAUTO_SAVE_PAGING_COUNT then - self.ui:saveSettings() - self.auto_save_paging_count = 0 - else - self.auto_save_paging_count = self.auto_save_paging_count + 1 - end - end + if DAUTO_SAVE_PAGING_COUNT then + if self.auto_save_paging_count == DAUTO_SAVE_PAGING_COUNT then + self.ui:saveSettings() + self.auto_save_paging_count = 0 + else + self.auto_save_paging_count = self.auto_save_paging_count + 1 + end + end end return ReaderView diff --git a/frontend/ui/reader/readerzooming.lua b/frontend/ui/reader/readerzooming.lua index 57194ddc5..a721e7180 100644 --- a/frontend/ui/reader/readerzooming.lua +++ b/frontend/ui/reader/readerzooming.lua @@ -9,315 +9,315 @@ local DEBUG = require("dbg") local _ = require("gettext") local ReaderZooming = InputContainer:new{ - zoom = 1.0, - -- default to nil so we can trigger ZoomModeUpdate events on start up - zoom_mode = nil, - DEFAULT_ZOOM_MODE = "page", - current_page = 1, - rotation = 0 + zoom = 1.0, + -- default to nil so we can trigger ZoomModeUpdate events on start up + zoom_mode = nil, + DEFAULT_ZOOM_MODE = "page", + current_page = 1, + rotation = 0 } function ReaderZooming:init() - if Device:hasKeyboard() then - self.key_events = { - ZoomIn = { - { "Shift", Input.group.PgFwd }, - doc = _("zoom in"), - event = "Zoom", args = "in" - }, - ZoomOut = { - { "Shift", Input.group.PgBack }, - doc = _("zoom out"), - event = "Zoom", args = "out" - }, - ZoomToFitPage = { - { "A" }, - doc = _("zoom to fit page"), - event = "SetZoomMode", args = "page" - }, - ZoomToFitContent = { - { "Shift", "A" }, - doc = _("zoom to fit content"), - event = "SetZoomMode", args = "content" - }, - ZoomToFitPageWidth = { - { "S" }, - doc = _("zoom to fit page width"), - event = "SetZoomMode", args = "pagewidth" - }, - ZoomToFitContentWidth = { - { "Shift", "S" }, - doc = _("zoom to fit content width"), - event = "SetZoomMode", args = "contentwidth" - }, - ZoomToFitPageHeight = { - { "D" }, - doc = _("zoom to fit page height"), - event = "SetZoomMode", args = "pageheight" - }, - ZoomToFitContentHeight = { - { "Shift", "D" }, - doc = _("zoom to fit content height"), - event = "SetZoomMode", args = "contentheight" - }, - } - end - if Device:isTouchDevice() then - self.ges_events = { - Spread = { - GestureRange:new{ - ges = "spread", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - Pinch = { - GestureRange:new{ - ges = "pinch", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - ToggleFreeZoom = { - GestureRange:new{ - ges = "double_tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - }, - } - end - self.ui.menu:registerToMainMenu(self) + if Device:hasKeyboard() then + self.key_events = { + ZoomIn = { + { "Shift", Input.group.PgFwd }, + doc = _("zoom in"), + event = "Zoom", args = "in" + }, + ZoomOut = { + { "Shift", Input.group.PgBack }, + doc = _("zoom out"), + event = "Zoom", args = "out" + }, + ZoomToFitPage = { + { "A" }, + doc = _("zoom to fit page"), + event = "SetZoomMode", args = "page" + }, + ZoomToFitContent = { + { "Shift", "A" }, + doc = _("zoom to fit content"), + event = "SetZoomMode", args = "content" + }, + ZoomToFitPageWidth = { + { "S" }, + doc = _("zoom to fit page width"), + event = "SetZoomMode", args = "pagewidth" + }, + ZoomToFitContentWidth = { + { "Shift", "S" }, + doc = _("zoom to fit content width"), + event = "SetZoomMode", args = "contentwidth" + }, + ZoomToFitPageHeight = { + { "D" }, + doc = _("zoom to fit page height"), + event = "SetZoomMode", args = "pageheight" + }, + ZoomToFitContentHeight = { + { "Shift", "D" }, + doc = _("zoom to fit content height"), + event = "SetZoomMode", args = "contentheight" + }, + } + end + if Device:isTouchDevice() then + self.ges_events = { + Spread = { + GestureRange:new{ + ges = "spread", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + Pinch = { + GestureRange:new{ + ges = "pinch", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + ToggleFreeZoom = { + GestureRange:new{ + ges = "double_tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, + } + end + self.ui.menu:registerToMainMenu(self) end function ReaderZooming:onReadSettings(config) - -- @TODO config file from old code base uses globalzoom_mode - -- instead of zoom_mode, we need to handle this imcompatibility - -- 04.12 2012 (houqp) - local zoom_mode = config:readSetting("zoom_mode") - if not zoom_mode then - zoom_mode = self.DEFAULT_ZOOM_MODE - end - self:setZoomMode(zoom_mode) + -- @TODO config file from old code base uses globalzoom_mode + -- instead of zoom_mode, we need to handle this imcompatibility + -- 04.12 2012 (houqp) + local zoom_mode = config:readSetting("zoom_mode") + if not zoom_mode then + zoom_mode = self.DEFAULT_ZOOM_MODE + end + self:setZoomMode(zoom_mode) end function ReaderZooming:onSaveSettings() - self.ui.doc_settings:saveSetting("zoom_mode", self.zoom_mode) + self.ui.doc_settings:saveSetting("zoom_mode", self.zoom_mode) end function ReaderZooming:onSpread(arg, ges) - if ges.direction == "horizontal" then - self:genSetZoomModeCallBack("contentwidth")() - elseif ges.direction == "vertical" then - self:genSetZoomModeCallBack("contentheight")() - elseif ges.direction == "diagonal" then - self:genSetZoomModeCallBack("content")() - end - return true + if ges.direction == "horizontal" then + self:genSetZoomModeCallBack("contentwidth")() + elseif ges.direction == "vertical" then + self:genSetZoomModeCallBack("contentheight")() + elseif ges.direction == "diagonal" then + self:genSetZoomModeCallBack("content")() + end + return true end function ReaderZooming:onPinch(arg, ges) - if ges.direction == "diagonal" then - self:genSetZoomModeCallBack("page")() - elseif ges.direction == "horizontal" then - self:genSetZoomModeCallBack("pagewidth")() - elseif ges.direction == "vertical" then - self:genSetZoomModeCallBack("pageheight")() - end - return true + if ges.direction == "diagonal" then + self:genSetZoomModeCallBack("page")() + elseif ges.direction == "horizontal" then + self:genSetZoomModeCallBack("pagewidth")() + elseif ges.direction == "vertical" then + self:genSetZoomModeCallBack("pageheight")() + end + return true end function ReaderZooming:onToggleFreeZoom(arg, ges) - if self.zoom_mode ~= "free" then - self.orig_zoom = self.zoom - self.orig_zoom_mode = self.zoom_mode - local xpos, ypos - self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos) - DEBUG("zoom center", self.zoom, xpos, ypos) - self.ui:handleEvent(Event:new("SetZoomMode", "free")) - if xpos == nil or ypos == nil then - xpos = ges.pos.x * self.zoom / self.orig_zoom - ypos = ges.pos.y * self.zoom / self.orig_zoom - end - self.view:SetZoomCenter(xpos, ypos) - else - self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode or "page")) - end + if self.zoom_mode ~= "free" then + self.orig_zoom = self.zoom + self.orig_zoom_mode = self.zoom_mode + local xpos, ypos + self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos) + DEBUG("zoom center", self.zoom, xpos, ypos) + self.ui:handleEvent(Event:new("SetZoomMode", "free")) + if xpos == nil or ypos == nil then + xpos = ges.pos.x * self.zoom / self.orig_zoom + ypos = ges.pos.y * self.zoom / self.orig_zoom + end + self.view:SetZoomCenter(xpos, ypos) + else + self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode or "page")) + end end function ReaderZooming:onSetDimensions(dimensions) - -- we were resized - self.dimen = dimensions - self:setZoom() + -- we were resized + self.dimen = dimensions + self:setZoom() end function ReaderZooming:onRestoreDimensions(dimensions) - -- we were resized - self.dimen = dimensions - self:setZoom() + -- we were resized + self.dimen = dimensions + self:setZoom() end function ReaderZooming:onRotationUpdate(rotation) - self.rotation = rotation - self:setZoom() + self.rotation = rotation + self:setZoom() end function ReaderZooming:onZoom(direction) - DEBUG("zoom", direction) - if direction == "in" then - self.zoom = self.zoom * 1.333333 - elseif direction == "out" then - self.zoom = self.zoom * 0.75 - end - DEBUG("zoom is now at", self.zoom) - self:onSetZoomMode("free") - self.view:onZoomUpdate(self.zoom) - return true + DEBUG("zoom", direction) + if direction == "in" then + self.zoom = self.zoom * 1.333333 + elseif direction == "out" then + self.zoom = self.zoom * 0.75 + end + DEBUG("zoom is now at", self.zoom) + self:onSetZoomMode("free") + self.view:onZoomUpdate(self.zoom) + return true end function ReaderZooming:onSetZoomMode(new_mode) - self.view.zoom_mode = new_mode - if self.zoom_mode ~= new_mode then - DEBUG("setting zoom mode to", new_mode) - self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode)) - self.zoom_mode = new_mode - self:setZoom() - end + self.view.zoom_mode = new_mode + if self.zoom_mode ~= new_mode then + DEBUG("setting zoom mode to", new_mode) + self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode)) + self.zoom_mode = new_mode + self:setZoom() + end end function ReaderZooming:onPageUpdate(new_page_no) - self.current_page = new_page_no - self:setZoom() + self.current_page = new_page_no + self:setZoom() end function ReaderZooming:onReZoom() - self:setZoom() - self.ui:handleEvent(Event:new("InitScrollPageStates")) - return true + self:setZoom() + self.ui:handleEvent(Event:new("InitScrollPageStates")) + return true end function ReaderZooming:getZoom(pageno) - -- check if we're in bbox mode and work on bbox if that's the case - local zoom = nil - local page_size = {} - if self.zoom_mode == "content" - or self.zoom_mode == "contentwidth" - or self.zoom_mode == "contentheight" then - local ubbox_dimen = self.ui.document:getUsedBBoxDimensions(pageno, 1) - --self.view:handleEvent(Event:new("BBoxUpdate", page_size)) - self.view:onBBoxUpdate(ubbox_dimen) - page_size = ubbox_dimen - else - -- otherwise, operate on full page - self.view:onBBoxUpdate(nil) - page_size = self.ui.document:getNativePageDimensions(pageno) - --page_size = self.ui.document:getPageDimensions(pageno, 1, 0) - end - -- calculate zoom value: - local zoom_w = self.dimen.w / page_size.w - local zoom_h = self.dimen.h / page_size.h - if self.rotation % 180 ~= 0 then - -- rotated by 90 or 270 degrees - zoom_w = self.dimen.w / page_size.h - zoom_h = self.dimen.h / page_size.w - end - if self.zoom_mode == "content" or self.zoom_mode == "page" then - if zoom_w < zoom_h then - zoom = zoom_w - else - zoom = zoom_h - end - elseif self.zoom_mode == "contentwidth" or self.zoom_mode == "pagewidth" then - zoom = zoom_w - elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then - zoom = zoom_h - elseif self.zoom_mode == "free" then - zoom = self.zoom - end - return zoom + -- check if we're in bbox mode and work on bbox if that's the case + local zoom = nil + local page_size = {} + if self.zoom_mode == "content" + or self.zoom_mode == "contentwidth" + or self.zoom_mode == "contentheight" then + local ubbox_dimen = self.ui.document:getUsedBBoxDimensions(pageno, 1) + --self.view:handleEvent(Event:new("BBoxUpdate", page_size)) + self.view:onBBoxUpdate(ubbox_dimen) + page_size = ubbox_dimen + else + -- otherwise, operate on full page + self.view:onBBoxUpdate(nil) + page_size = self.ui.document:getNativePageDimensions(pageno) + --page_size = self.ui.document:getPageDimensions(pageno, 1, 0) + end + -- calculate zoom value: + local zoom_w = self.dimen.w / page_size.w + local zoom_h = self.dimen.h / page_size.h + if self.rotation % 180 ~= 0 then + -- rotated by 90 or 270 degrees + zoom_w = self.dimen.w / page_size.h + zoom_h = self.dimen.h / page_size.w + end + if self.zoom_mode == "content" or self.zoom_mode == "page" then + if zoom_w < zoom_h then + zoom = zoom_w + else + zoom = zoom_h + end + elseif self.zoom_mode == "contentwidth" or self.zoom_mode == "pagewidth" then + zoom = zoom_w + elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then + zoom = zoom_h + elseif self.zoom_mode == "free" then + zoom = self.zoom + end + return zoom end function ReaderZooming:getRegionalZoomCenter(pageno, pos) - local p_pos = self.view:getSinglePagePosition(pos) - local page_size = self.ui.document:getNativePageDimensions(pageno) - local pos_x = p_pos.x / page_size.w / p_pos.zoom - local pos_y = p_pos.y / page_size.h / p_pos.zoom - local regions = self.ui.document:getPageRegions(pageno) - DEBUG("get page regions", regions) - local margin = self.ui.document.configurable.page_margin * Screen:getDPI() - for i = 1, #regions do - if regions[i].x0 <= pos_x and pos_x <= regions[i].x1 - and regions[i].y0 <= pos_y and pos_y <= regions[i].y1 then - local zoom = 1/(regions[i].x1 - regions[i].x0) - zoom = zoom/(1 + 3*margin/zoom/page_size.w) - local xpos = (regions[i].x0 + regions[i].x1)/2 * zoom * page_size.w - local ypos = p_pos.y / p_pos.zoom * zoom - return zoom, xpos, ypos - end - end - return 2 + local p_pos = self.view:getSinglePagePosition(pos) + local page_size = self.ui.document:getNativePageDimensions(pageno) + local pos_x = p_pos.x / page_size.w / p_pos.zoom + local pos_y = p_pos.y / page_size.h / p_pos.zoom + local regions = self.ui.document:getPageRegions(pageno) + DEBUG("get page regions", regions) + local margin = self.ui.document.configurable.page_margin * Screen:getDPI() + for i = 1, #regions do + if regions[i].x0 <= pos_x and pos_x <= regions[i].x1 + and regions[i].y0 <= pos_y and pos_y <= regions[i].y1 then + local zoom = 1/(regions[i].x1 - regions[i].x0) + zoom = zoom/(1 + 3*margin/zoom/page_size.w) + local xpos = (regions[i].x0 + regions[i].x1)/2 * zoom * page_size.w + local ypos = p_pos.y / p_pos.zoom * zoom + return zoom, xpos, ypos + end + end + return 2 end function ReaderZooming:setZoom() - if not self.dimen then - self.dimen = self.ui.dimen - end - self.zoom = self:getZoom(self.current_page) - self.ui:handleEvent(Event:new("ZoomUpdate", self.zoom)) + if not self.dimen then + self.dimen = self.ui.dimen + end + self.zoom = self:getZoom(self.current_page) + self.ui:handleEvent(Event:new("ZoomUpdate", self.zoom)) end function ReaderZooming:genSetZoomModeCallBack(mode) - return function() - self:setZoomMode(mode) - end + return function() + self:setZoomMode(mode) + end end function ReaderZooming:setZoomMode(mode) - self.ui:handleEvent(Event:new("SetZoomMode", mode)) - self.ui:handleEvent(Event:new("InitScrollPageStates")) + self.ui:handleEvent(Event:new("SetZoomMode", mode)) + self.ui:handleEvent(Event:new("InitScrollPageStates")) end function ReaderZooming:addToMainMenu(tab_item_table) - if self.ui.document.info.has_pages then - table.insert(tab_item_table.typeset, { - text = _("Switch zoom mode"), - sub_item_table = { - { - text = _("Zoom to fit content width"), - callback = self:genSetZoomModeCallBack("contentwidth") - }, - { - text = _("Zoom to fit content height"), - callback = self:genSetZoomModeCallBack("contentheight") - }, - { - text = _("Zoom to fit page width"), - callback = self:genSetZoomModeCallBack("pagewidth") - }, - { - text = _("Zoom to fit page height"), - callback = self:genSetZoomModeCallBack("pageheight") - }, - { - text = _("Zoom to fit content"), - callback = self:genSetZoomModeCallBack("content") - }, - { - text = _("Zoom to fit page"), - callback = self:genSetZoomModeCallBack("page") - }, - } - }) - end + if self.ui.document.info.has_pages then + table.insert(tab_item_table.typeset, { + text = _("Switch zoom mode"), + sub_item_table = { + { + text = _("Zoom to fit content width"), + callback = self:genSetZoomModeCallBack("contentwidth") + }, + { + text = _("Zoom to fit content height"), + callback = self:genSetZoomModeCallBack("contentheight") + }, + { + text = _("Zoom to fit page width"), + callback = self:genSetZoomModeCallBack("pagewidth") + }, + { + text = _("Zoom to fit page height"), + callback = self:genSetZoomModeCallBack("pageheight") + }, + { + text = _("Zoom to fit content"), + callback = self:genSetZoomModeCallBack("content") + }, + { + text = _("Zoom to fit page"), + callback = self:genSetZoomModeCallBack("page") + }, + } + }) + end end return ReaderZooming diff --git a/frontend/ui/readerui.lua b/frontend/ui/readerui.lua index 69aa11c7d..b3ad7a2a7 100644 --- a/frontend/ui/readerui.lua +++ b/frontend/ui/readerui.lua @@ -39,256 +39,256 @@ it works using data gathered from a document interface ]]-- local ReaderUI = InputContainer:new{ - key_events = { - Close = { { "Home" }, - doc = _("close document"), event = "Close" }, - }, - active_widgets = {}, + key_events = { + Close = { { "Home" }, + doc = _("close document"), event = "Close" }, + }, + active_widgets = {}, - -- our own size - dimen = Geom:new{ w = 400, h = 600 }, - -- if we have a parent container, it must be referenced for now - dialog = nil, + -- our own size + dimen = Geom:new{ w = 400, h = 600 }, + -- if we have a parent container, it must be referenced for now + dialog = nil, - -- the document interface - document = nil, + -- the document interface + document = nil, - -- initial page or percent inside document on opening - start_pos = nil, - -- password for document unlock - password = nil, + -- initial page or percent inside document on opening + start_pos = nil, + -- password for document unlock + password = nil, - postInitCallback = nil, + postInitCallback = nil, } function ReaderUI:init() - self.postInitCallback = {} - -- if we are not the top level dialog ourselves, it must be given in the table - if not self.dialog then - self.dialog = self - end + self.postInitCallback = {} + -- if we are not the top level dialog ourselves, it must be given in the table + if not self.dialog then + self.dialog = self + end - if Device:hasKeyboard() then - self.key_events.Back = { - { "Back" }, doc = _("close document"), - event = "Close" } - end + if Device:hasKeyboard() then + self.key_events.Back = { + { "Back" }, doc = _("close document"), + event = "Close" } + end - self.doc_settings = DocSettings:open(self.document.file) + self.doc_settings = DocSettings:open(self.document.file) - -- a view container (so it must be child #1!) - self[1] = ReaderView:new{ - dialog = self.dialog, - dimen = self.dimen, - ui = self, - document = self.document, - } - -- reader menu controller - -- hold reference to menu widget - self.menu = ReaderMenu:new{ - view = self[1], - ui = self - } - -- link - table.insert(self, ReaderLink:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - -- text highlight - table.insert(self, ReaderHighlight:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - -- menu widget should be registered after link widget and highlight widget - -- so that taps on link and highlight areas won't popup reader menu - table.insert(self, self.menu) - -- rotation controller - table.insert(self, ReaderRotation:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - -- Table of content controller - -- hold reference to bm widget - self.toc = ReaderToc:new{ - dialog = self.dialog, - view = self[1], - ui = self - } - table.insert(self, self.toc) - -- bookmark controller - table.insert(self, ReaderBookmark:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - -- reader goto controller - table.insert(self, ReaderGoto:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - -- dictionary - table.insert(self, ReaderDictionary:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - -- screenshot controller - table.insert(self.active_widgets, ReaderScreenshot:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - -- frontlight controller - if Device:hasFrontlight() then - table.insert(self, ReaderFrontLight:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - end - -- configuable controller - if self.document.info.configurable then - -- config panel controller - table.insert(self, ReaderConfig:new{ - configurable = self.document.configurable, - options = self.document.options, - dialog = self.dialog, - view = self[1], - ui = self - }) - if not self.document.info.has_pages then - -- cre option controller - table.insert(self, ReaderCoptListener:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - end - end - -- for page specific controller - if self.document.info.has_pages then - -- cropping controller - table.insert(self, ReaderCropping:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - -- paging controller - table.insert(self, ReaderPaging:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - -- zooming controller - local zoom = ReaderZooming:new{ - dialog = self.dialog, - view = self[1], - ui = self - } - table.insert(self, zoom) - -- panning controller - table.insert(self, ReaderPanning:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - -- hinting controller - table.insert(self, ReaderHinting:new{ - dialog = self.dialog, - zoom = zoom, - view = self[1], - ui = self, - document = self.document, - }) - else - -- make sure we load document first before calling any callback - table.insert(self.postInitCallback, function() - self.document:loadDocument() - end) - -- typeset controller - table.insert(self, ReaderTypeset:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - -- font menu - self.font = ReaderFont:new{ - dialog = self.dialog, - view = self[1], - ui = self - } - table.insert(self, self.font) -- hold reference to font menu - -- hyphenation menu - self.hyphenation = ReaderHyphenation:new{ - dialog = self.dialog, - view = self[1], - ui = self - } - table.insert(self, self.hyphenation) -- hold reference to hyphenation menu - -- rolling controller - table.insert(self, ReaderRolling:new{ - dialog = self.dialog, - view = self[1], - ui = self - }) - end - -- configuable controller - if self.document.info.configurable then - if self.document.info.has_pages then - -- kopt option controller - table.insert(self, ReaderKoptListener:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - end - -- activity indicator - table.insert(self, ReaderActivityIndicator:new{ - dialog = self.dialog, - view = self[1], - ui = self, - document = self.document, - }) - end - --DEBUG(self.doc_settings) - -- we only read settings after all the widgets are initialized - self:handleEvent(Event:new("ReadSettings", self.doc_settings)) + -- a view container (so it must be child #1!) + self[1] = ReaderView:new{ + dialog = self.dialog, + dimen = self.dimen, + ui = self, + document = self.document, + } + -- reader menu controller + -- hold reference to menu widget + self.menu = ReaderMenu:new{ + view = self[1], + ui = self + } + -- link + table.insert(self, ReaderLink:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + -- text highlight + table.insert(self, ReaderHighlight:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + -- menu widget should be registered after link widget and highlight widget + -- so that taps on link and highlight areas won't popup reader menu + table.insert(self, self.menu) + -- rotation controller + table.insert(self, ReaderRotation:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + -- Table of content controller + -- hold reference to bm widget + self.toc = ReaderToc:new{ + dialog = self.dialog, + view = self[1], + ui = self + } + table.insert(self, self.toc) + -- bookmark controller + table.insert(self, ReaderBookmark:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + -- reader goto controller + table.insert(self, ReaderGoto:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + -- dictionary + table.insert(self, ReaderDictionary:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + -- screenshot controller + table.insert(self.active_widgets, ReaderScreenshot:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + -- frontlight controller + if Device:hasFrontlight() then + table.insert(self, ReaderFrontLight:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + end + -- configuable controller + if self.document.info.configurable then + -- config panel controller + table.insert(self, ReaderConfig:new{ + configurable = self.document.configurable, + options = self.document.options, + dialog = self.dialog, + view = self[1], + ui = self + }) + if not self.document.info.has_pages then + -- cre option controller + table.insert(self, ReaderCoptListener:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + end + end + -- for page specific controller + if self.document.info.has_pages then + -- cropping controller + table.insert(self, ReaderCropping:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + -- paging controller + table.insert(self, ReaderPaging:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + -- zooming controller + local zoom = ReaderZooming:new{ + dialog = self.dialog, + view = self[1], + ui = self + } + table.insert(self, zoom) + -- panning controller + table.insert(self, ReaderPanning:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + -- hinting controller + table.insert(self, ReaderHinting:new{ + dialog = self.dialog, + zoom = zoom, + view = self[1], + ui = self, + document = self.document, + }) + else + -- make sure we load document first before calling any callback + table.insert(self.postInitCallback, function() + self.document:loadDocument() + end) + -- typeset controller + table.insert(self, ReaderTypeset:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + -- font menu + self.font = ReaderFont:new{ + dialog = self.dialog, + view = self[1], + ui = self + } + table.insert(self, self.font) -- hold reference to font menu + -- hyphenation menu + self.hyphenation = ReaderHyphenation:new{ + dialog = self.dialog, + view = self[1], + ui = self + } + table.insert(self, self.hyphenation) -- hold reference to hyphenation menu + -- rolling controller + table.insert(self, ReaderRolling:new{ + dialog = self.dialog, + view = self[1], + ui = self + }) + end + -- configuable controller + if self.document.info.configurable then + if self.document.info.has_pages then + -- kopt option controller + table.insert(self, ReaderKoptListener:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + end + -- activity indicator + table.insert(self, ReaderActivityIndicator:new{ + dialog = self.dialog, + view = self[1], + ui = self, + document = self.document, + }) + end + --DEBUG(self.doc_settings) + -- we only read settings after all the widgets are initialized + self:handleEvent(Event:new("ReadSettings", self.doc_settings)) - for _,v in ipairs(self.postInitCallback) do - v() - end + for _,v in ipairs(self.postInitCallback) do + v() + end end function ReaderUI:onSetDimensions(dimen) - self.dimen = dimen + self.dimen = dimen end function ReaderUI:saveSettings() - self:handleEvent(Event:new("SaveSettings")) - self.doc_settings:flush() + self:handleEvent(Event:new("SaveSettings")) + self.doc_settings:flush() end function ReaderUI:onClose() - DEBUG("closing reader") - self:saveSettings() - if self.document ~= nil then - self.document:close() - self.document = nil - self.start_pos = nil - end - UIManager:close(self.dialog) - return true + DEBUG("closing reader") + self:saveSettings() + if self.document ~= nil then + self.document:close() + self.document = nil + self.start_pos = nil + end + UIManager:close(self.dialog) + return true end return ReaderUI diff --git a/frontend/ui/rendertext.lua b/frontend/ui/rendertext.lua index d93c66e38..2b2b753c5 100644 --- a/frontend/ui/rendertext.lua +++ b/frontend/ui/rendertext.lua @@ -10,170 +10,170 @@ TODO: all these functions should probably be methods on Face objects local RenderText = {} local GlyphCache = Cache:new{ - max_memsize = 512*1024, - current_memsize = 0, - cache = {}, - -- this will hold the LRU order of the cache - cache_order = {} + max_memsize = 512*1024, + current_memsize = 0, + cache = {}, + -- this will hold the LRU order of the cache + cache_order = {} } -- iterator over UTF8 encoded characters in a string local function utf8Chars(input) - local function read_next_glyph(input, pos) - if string.len(input) < pos then return nil end - local value = string.byte(input, pos) - if bit.band(value, 0x80) == 0 then - -- TODO: check valid ranges - return pos+1, value, string.sub(input, pos, pos) - elseif bit.band(value, 0xC0) == 0x80 -- invalid, continuation - or bit.band(value, 0xF8) == 0xF8 -- 5-or-more byte sequence, illegal due to RFC3629 - then - return pos+1, 0xFFFD, "\xFF\xFD" - else - local glyph, bytes_left - if bit.band(value, 0xE0) == 0xC0 then - glyph = bit.band(value, 0x1F) - bytes_left = 1 - elseif bit.band(value, 0xF0) == 0xE0 then - glyph = bit.band(value, 0x0F) - bytes_left = 2 - elseif bit.band(value, 0xF8) == 0xF0 then - glyph = bit.band(value, 0x07) - bytes_left = 3 - else - return pos+1, 0xFFFD, "\xFF\xFD" - end - if string.len(input) < (pos + bytes_left - 1) then - return pos+1, 0xFFFD, "\xFF\xFD" - end - for i = pos+1, pos + bytes_left do - value = string.byte(input, i) - if bit.band(value, 0xC0) == 0x80 then - glyph = bit.bor(bit.lshift(glyph, 6), bit.band(value, 0x3F)) - else - return i+1, 0xFFFD, "\xFF\xFD" - end - end - -- TODO: check for valid ranges here! - return pos+bytes_left+1, glyph, string.sub(input, pos, pos+bytes_left) - end - end - return read_next_glyph, input, 1 + local function read_next_glyph(input, pos) + if string.len(input) < pos then return nil end + local value = string.byte(input, pos) + if bit.band(value, 0x80) == 0 then + -- TODO: check valid ranges + return pos+1, value, string.sub(input, pos, pos) + elseif bit.band(value, 0xC0) == 0x80 -- invalid, continuation + or bit.band(value, 0xF8) == 0xF8 -- 5-or-more byte sequence, illegal due to RFC3629 + then + return pos+1, 0xFFFD, "\xFF\xFD" + else + local glyph, bytes_left + if bit.band(value, 0xE0) == 0xC0 then + glyph = bit.band(value, 0x1F) + bytes_left = 1 + elseif bit.band(value, 0xF0) == 0xE0 then + glyph = bit.band(value, 0x0F) + bytes_left = 2 + elseif bit.band(value, 0xF8) == 0xF0 then + glyph = bit.band(value, 0x07) + bytes_left = 3 + else + return pos+1, 0xFFFD, "\xFF\xFD" + end + if string.len(input) < (pos + bytes_left - 1) then + return pos+1, 0xFFFD, "\xFF\xFD" + end + for i = pos+1, pos + bytes_left do + value = string.byte(input, i) + if bit.band(value, 0xC0) == 0x80 then + glyph = bit.bor(bit.lshift(glyph, 6), bit.band(value, 0x3F)) + else + return i+1, 0xFFFD, "\xFF\xFD" + end + end + -- TODO: check for valid ranges here! + return pos+bytes_left+1, glyph, string.sub(input, pos, pos+bytes_left) + end + end + return read_next_glyph, input, 1 end function RenderText:getGlyph(face, charcode, bold, bgcolor, fgcolor) - if bgcolor == nil then bgcolor = 0.0 end - if fgcolor == nil then fgcolor = 1.0 end - local hash = "glyph|"..face.hash.."|"..charcode.."|"..(bold and 1 or 0).."|"..bgcolor.."|"..fgcolor - local glyph = GlyphCache:check(hash) - if glyph then - -- cache hit - return glyph[1] - end - local rendered_glyph = face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold) - if face.ftface:checkGlyph(charcode) == 0 then - for index, font in pairs(Font.fallbacks) do - -- rescale face size by DPI since it will be scaled in getFace again - local fb_face = Font:getFace(font, Screen:rescaleByDPI(face.size)) - if fb_face.ftface:checkGlyph(charcode) ~= 0 then - rendered_glyph = fb_face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold) - --DEBUG("fallback to font", font) - break - end - end - end - if not rendered_glyph then - DEBUG("error rendering glyph (charcode=", charcode, ") for face", face) - return - end - glyph = CacheItem:new{rendered_glyph} - glyph.size = glyph[1].bb:getWidth() * glyph[1].bb:getHeight() / 2 + 32 - GlyphCache:insert(hash, glyph) - return rendered_glyph + if bgcolor == nil then bgcolor = 0.0 end + if fgcolor == nil then fgcolor = 1.0 end + local hash = "glyph|"..face.hash.."|"..charcode.."|"..(bold and 1 or 0).."|"..bgcolor.."|"..fgcolor + local glyph = GlyphCache:check(hash) + if glyph then + -- cache hit + return glyph[1] + end + local rendered_glyph = face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold) + if face.ftface:checkGlyph(charcode) == 0 then + for index, font in pairs(Font.fallbacks) do + -- rescale face size by DPI since it will be scaled in getFace again + local fb_face = Font:getFace(font, Screen:rescaleByDPI(face.size)) + if fb_face.ftface:checkGlyph(charcode) ~= 0 then + rendered_glyph = fb_face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold) + --DEBUG("fallback to font", font) + break + end + end + end + if not rendered_glyph then + DEBUG("error rendering glyph (charcode=", charcode, ") for face", face) + return + end + glyph = CacheItem:new{rendered_glyph} + glyph.size = glyph[1].bb:getWidth() * glyph[1].bb:getHeight() / 2 + 32 + GlyphCache:insert(hash, glyph) + return rendered_glyph end function RenderText:getSubTextByWidth(text, face, width, kerning, bold) - local pen_x = 0 - local prevcharcode = 0 - local char_list = {} - for _, charcode, uchar in utf8Chars(text) do - if pen_x < width then - local glyph = self:getGlyph(face, charcode, bold) - if kerning and prevcharcode then - local kern = face.ftface:getKerning(prevcharcode, charcode) - pen_x = pen_x + kern - end - pen_x = pen_x + glyph.ax - if pen_x <= width then - prevcharcode = charcode - table.insert(char_list, uchar) - else - break - end - end - end - return table.concat(char_list) + local pen_x = 0 + local prevcharcode = 0 + local char_list = {} + for _, charcode, uchar in utf8Chars(text) do + if pen_x < width then + local glyph = self:getGlyph(face, charcode, bold) + if kerning and prevcharcode then + local kern = face.ftface:getKerning(prevcharcode, charcode) + pen_x = pen_x + kern + end + pen_x = pen_x + glyph.ax + if pen_x <= width then + prevcharcode = charcode + table.insert(char_list, uchar) + else + break + end + end + end + return table.concat(char_list) end function RenderText:sizeUtf8Text(x, width, face, text, kerning, bold) - if not text then - DEBUG("sizeUtf8Text called without text"); - return - end + if not text then + DEBUG("sizeUtf8Text called without text"); + return + end - -- may still need more adaptive pen placement when kerning, - -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html - local pen_x = 0 - local pen_y_top = 0 - local pen_y_bottom = 0 - local prevcharcode = 0 - for _, charcode, uchar in utf8Chars(text) do - if pen_x < (width - x) then - local glyph = self:getGlyph(face, charcode, bold) - if kerning and (prevcharcode ~= 0) then - pen_x = pen_x + (face.ftface):getKerning(prevcharcode, charcode) - end - pen_x = pen_x + glyph.ax - pen_y_top = math.max(pen_y_top, glyph.t) - pen_y_bottom = math.max(pen_y_bottom, glyph.bb:getHeight() - glyph.t) - --DEBUG("ax:"..glyph.ax.." t:"..glyph.t.." r:"..glyph.r.." h:"..glyph.bb:getHeight().." w:"..glyph.bb:getWidth().." yt:"..pen_y_top.." yb:"..pen_y_bottom) - prevcharcode = charcode - end -- if pen_x < (width - x) - end - return { x = pen_x, y_top = pen_y_top, y_bottom = pen_y_bottom} + -- may still need more adaptive pen placement when kerning, + -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html + local pen_x = 0 + local pen_y_top = 0 + local pen_y_bottom = 0 + local prevcharcode = 0 + for _, charcode, uchar in utf8Chars(text) do + if pen_x < (width - x) then + local glyph = self:getGlyph(face, charcode, bold) + if kerning and (prevcharcode ~= 0) then + pen_x = pen_x + (face.ftface):getKerning(prevcharcode, charcode) + end + pen_x = pen_x + glyph.ax + pen_y_top = math.max(pen_y_top, glyph.t) + pen_y_bottom = math.max(pen_y_bottom, glyph.bb:getHeight() - glyph.t) + --DEBUG("ax:"..glyph.ax.." t:"..glyph.t.." r:"..glyph.r.." h:"..glyph.bb:getHeight().." w:"..glyph.bb:getWidth().." yt:"..pen_y_top.." yb:"..pen_y_bottom) + prevcharcode = charcode + end -- if pen_x < (width - x) + end + return { x = pen_x, y_top = pen_y_top, y_bottom = pen_y_bottom} end function RenderText:renderUtf8Text(buffer, x, y, face, text, kerning, bold, bgcolor, fgcolor, width) - if not text then - DEBUG("renderUtf8Text called without text"); - return 0 - end + if not text then + DEBUG("renderUtf8Text called without text"); + return 0 + end - -- may still need more adaptive pen placement when kerning, - -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html - local pen_x = 0 - local prevcharcode = 0 - local text_width = buffer:getWidth() - x - if width and width < text_width then - text_width = width - end - for _, charcode, uchar in utf8Chars(text) do - if pen_x < text_width then - local glyph = self:getGlyph(face, charcode, bold, bgcolor, fgcolor) - if kerning and (prevcharcode ~= 0) then - pen_x = pen_x + face.ftface:getKerning(prevcharcode, charcode) - end - buffer:addblitFrom( - glyph.bb, - x + pen_x + glyph.l, y - glyph.t, - 0, 0, - glyph.bb:getWidth(), glyph.bb:getHeight(), 1) - pen_x = pen_x + glyph.ax - prevcharcode = charcode - end -- if pen_x < text_width - end + -- may still need more adaptive pen placement when kerning, + -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html + local pen_x = 0 + local prevcharcode = 0 + local text_width = buffer:getWidth() - x + if width and width < text_width then + text_width = width + end + for _, charcode, uchar in utf8Chars(text) do + if pen_x < text_width then + local glyph = self:getGlyph(face, charcode, bold, bgcolor, fgcolor) + if kerning and (prevcharcode ~= 0) then + pen_x = pen_x + face.ftface:getKerning(prevcharcode, charcode) + end + buffer:addblitFrom( + glyph.bb, + x + pen_x + glyph.l, y - glyph.t, + 0, 0, + glyph.bb:getWidth(), glyph.bb:getHeight(), 1) + pen_x = pen_x + glyph.ax + prevcharcode = charcode + end -- if pen_x < text_width + end - return pen_x + return pen_x end return RenderText diff --git a/frontend/ui/timeval.lua b/frontend/ui/timeval.lua index baa489d94..e95eeabe0 100644 --- a/frontend/ui/timeval.lua +++ b/frontend/ui/timeval.lua @@ -1,104 +1,104 @@ local TimeVal = { - sec = 0, - usec = 0, + sec = 0, + usec = 0, } function TimeVal:new(o) - local o = o or {} - if o.sec == nil then - o.sec = 0 - end - if o.usec == nil then - o.usec = 0 - elseif o.usec > 1000000 then - o.sec = o.sec + maht.floor(o.usec/1000000) - o.usec = o.usec % 1000000 - end - setmetatable(o, self) - self.__index = self - return o + local o = o or {} + if o.sec == nil then + o.sec = 0 + end + if o.usec == nil then + o.usec = 0 + elseif o.usec > 1000000 then + o.sec = o.sec + maht.floor(o.usec/1000000) + o.usec = o.usec % 1000000 + end + setmetatable(o, self) + self.__index = self + return o end function TimeVal:__lt(time_b) - if self.sec < time_b.sec then - return true - elseif self.sec > time_b.sec then - return false - else - -- self.sec == time_b.sec - if self.usec < time_b.usec then - return true - else - return false - end - end + if self.sec < time_b.sec then + return true + elseif self.sec > time_b.sec then + return false + else + -- self.sec == time_b.sec + if self.usec < time_b.usec then + return true + else + return false + end + end end function TimeVal:__le(time_b) - if self.sec < time_b.sec then - return true - elseif self.sec > time_b.sec then - return false - else - -- self.sec == time_b.sec - if self.usec > time_b.usec then - return false - else - return true - end - end + if self.sec < time_b.sec then + return true + elseif self.sec > time_b.sec then + return false + else + -- self.sec == time_b.sec + if self.usec > time_b.usec then + return false + else + return true + end + end end function TimeVal:__eq(time_b) - if self.sec == time_b.sec and self.usec == time_b.usec then - return true - else - return false - end + if self.sec == time_b.sec and self.usec == time_b.usec then + return true + else + return false + end end function TimeVal:__sub(time_b) - local diff = TimeVal:new{} + local diff = TimeVal:new{} - diff.sec = self.sec - time_b.sec - diff.usec = self.usec - time_b.usec + diff.sec = self.sec - time_b.sec + diff.usec = self.usec - time_b.usec - if diff.sec < 0 and diff.usec > 0 then - diff.sec = diff.sec + 1 - diff.usec = diff.usec - 1000000 - elseif diff.sec > 0 and diff.usec < 0 then - diff.sec = diff.sec - 1 - diff.usec = diff.usec + 1000000 - end + if diff.sec < 0 and diff.usec > 0 then + diff.sec = diff.sec + 1 + diff.usec = diff.usec - 1000000 + elseif diff.sec > 0 and diff.usec < 0 then + diff.sec = diff.sec - 1 + diff.usec = diff.usec + 1000000 + end - return diff + return diff end function TimeVal:__add(time_b) - local sum = TimeVal:new{} + local sum = TimeVal:new{} - sum.sec = self.sec + time_b.sec - sum.usec = self.usec + time_b.usec - if sum.usec > 1000000 then - sum.usec = sum.usec - 1000000 - sum.sec = sum.sec + 1 - end + sum.sec = self.sec + time_b.sec + sum.usec = self.usec + time_b.usec + if sum.usec > 1000000 then + sum.usec = sum.usec - 1000000 + sum.sec = sum.sec + 1 + end - if sum.sec < 0 and sum.usec > 0 then - sum.sec = sum.sec + 1 - sum.usec = sum.usec - 1000000 - elseif sum.sec > 0 and sum.usec < 0 then - sum.sec = sum.sec - 1 - sum.usec = sum.usec + 1000000 - end + if sum.sec < 0 and sum.usec > 0 then + sum.sec = sum.sec + 1 + sum.usec = sum.usec - 1000000 + elseif sum.sec > 0 and sum.usec < 0 then + sum.sec = sum.sec - 1 + sum.usec = sum.usec + 1000000 + end - return sum + return sum end function TimeVal:now() - local sec, usec = util.gettime() - return TimeVal:new{sec = sec, usec = usec} + local sec, usec = util.gettime() + return TimeVal:new{sec = sec, usec = usec} end return TimeVal diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 13dc07dc8..253a2d48f 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -10,304 +10,304 @@ Screen:init() -- initialize the input handling Input:init() -local WAVEFORM_MODE_INIT = 0x0 -- Screen goes to white (clears) -local WAVEFORM_MODE_DU = 0x1 -- Grey->white/grey->black -local WAVEFORM_MODE_GC16 = 0x2 -- High fidelity (flashing) -local WAVEFORM_MODE_GC4 = WAVEFORM_MODE_GC16 -- For compatibility -local WAVEFORM_MODE_GC16_FAST = 0x3 -- Medium fidelity -local WAVEFORM_MODE_A2 = 0x4 -- Faster but even lower fidelity -local WAVEFORM_MODE_GL16 = 0x5 -- High fidelity from white transition -local WAVEFORM_MODE_GL16_FAST = 0x6 -- Medium fidelity from white transition -local WAVEFORM_MODE_AUTO = 0x101 +local WAVEFORM_MODE_INIT = 0x0 -- Screen goes to white (clears) +local WAVEFORM_MODE_DU = 0x1 -- Grey->white/grey->black +local WAVEFORM_MODE_GC16 = 0x2 -- High fidelity (flashing) +local WAVEFORM_MODE_GC4 = WAVEFORM_MODE_GC16 -- For compatibility +local WAVEFORM_MODE_GC16_FAST = 0x3 -- Medium fidelity +local WAVEFORM_MODE_A2 = 0x4 -- Faster but even lower fidelity +local WAVEFORM_MODE_GL16 = 0x5 -- High fidelity from white transition +local WAVEFORM_MODE_GL16_FAST = 0x6 -- Medium fidelity from white transition +local WAVEFORM_MODE_AUTO = 0x101 -- there is only one instance of this local UIManager = { - default_refresh_type = 0, -- 0 for partial refresh, 1 for full refresh - default_waveform_mode = WAVEFORM_MODE_GC16, -- high fidelity waveform - fast_waveform_mode = WAVEFORM_MODE_A2, - -- force to repaint all the widget is stack, will be reset to false - -- after each ui loop - repaint_all = false, - -- force to do full refresh, will be reset to false - -- after each ui loop - full_refresh = false, - -- force to do patial refresh, will be reset to false - -- after each ui loop - patial_refresh = false, - -- trigger a full refresh when counter reaches FULL_REFRESH_COUNT - FULL_REFRESH_COUNT = DRCOUNTMAX, - refresh_count = 0, + default_refresh_type = 0, -- 0 for partial refresh, 1 for full refresh + default_waveform_mode = WAVEFORM_MODE_GC16, -- high fidelity waveform + fast_waveform_mode = WAVEFORM_MODE_A2, + -- force to repaint all the widget is stack, will be reset to false + -- after each ui loop + repaint_all = false, + -- force to do full refresh, will be reset to false + -- after each ui loop + full_refresh = false, + -- force to do patial refresh, will be reset to false + -- after each ui loop + patial_refresh = false, + -- trigger a full refresh when counter reaches FULL_REFRESH_COUNT + FULL_REFRESH_COUNT = DRCOUNTMAX, + refresh_count = 0, - _running = true, - _window_stack = {}, - _execution_stack = {}, - _dirty = {} + _running = true, + _window_stack = {}, + _execution_stack = {}, + _dirty = {} } -- register & show a widget function UIManager:show(widget, x, y) - -- put widget on top of stack - table.insert(self._window_stack, {x = x or 0, y = y or 0, widget = widget}) - -- and schedule it to be painted - self:setDirty(widget) - -- tell the widget that it is shown now - widget:handleEvent(Event:new("Show")) - -- check if this widget disables double tap gesture - if widget.disable_double_tap then - Input.disable_double_tap = true - end + -- put widget on top of stack + table.insert(self._window_stack, {x = x or 0, y = y or 0, widget = widget}) + -- and schedule it to be painted + self:setDirty(widget) + -- tell the widget that it is shown now + widget:handleEvent(Event:new("Show")) + -- check if this widget disables double tap gesture + if widget.disable_double_tap then + Input.disable_double_tap = true + end end -- unregister a widget function UIManager:close(widget) - Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP - local dirty = false - for i = #self._window_stack, 1, -1 do - if self._window_stack[i].widget == widget then - table.remove(self._window_stack, i) - dirty = true - elseif self._window_stack[i].widget.disable_double_tap then - Input.disable_double_tap = true - end - end - if dirty then - -- schedule remaining widgets to be painted - for i = 1, #self._window_stack do - self:setDirty(self._window_stack[i].widget) - end - end + Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP + local dirty = false + for i = #self._window_stack, 1, -1 do + if self._window_stack[i].widget == widget then + table.remove(self._window_stack, i) + dirty = true + elseif self._window_stack[i].widget.disable_double_tap then + Input.disable_double_tap = true + end + end + if dirty then + -- schedule remaining widgets to be painted + for i = 1, #self._window_stack do + self:setDirty(self._window_stack[i].widget) + end + end end -- schedule an execution task function UIManager:schedule(time, action) - table.insert(self._execution_stack, { time = time, action = action }) + table.insert(self._execution_stack, { time = time, action = action }) end -- schedule task in a certain amount of seconds (fractions allowed) from now function UIManager:scheduleIn(seconds, action) - local when = { util.gettime() } - local s = math.floor(seconds) - local usecs = (seconds - s) * 1000000 - when[1] = when[1] + s - when[2] = when[2] + usecs - if when[2] > 1000000 then - when[1] = when[1] + 1 - when[2] = when[2] - 1000000 - end - self:schedule(when, action) + local when = { util.gettime() } + local s = math.floor(seconds) + local usecs = (seconds - s) * 1000000 + when[1] = when[1] + s + when[2] = when[2] + usecs + if when[2] > 1000000 then + when[1] = when[1] + 1 + when[2] = when[2] - 1000000 + end + self:schedule(when, action) end -- register a widget to be repainted function UIManager:setDirty(widget, refresh_type) - -- "auto": request full refresh - -- "full": force full refresh - -- "partial": partial refresh - if not refresh_type then - refresh_type = "auto" - end - self._dirty[widget] = refresh_type + -- "auto": request full refresh + -- "full": force full refresh + -- "partial": partial refresh + if not refresh_type then + refresh_type = "auto" + end + self._dirty[widget] = refresh_type end -- signal to quit function UIManager:quit() - self._running = false + self._running = false end -- transmit an event to registered widgets function UIManager:sendEvent(event) - -- top level widget has first access to the event - if self._window_stack[#self._window_stack].widget:handleEvent(event) then - return - end + -- top level widget has first access to the event + if self._window_stack[#self._window_stack].widget:handleEvent(event) then + return + end - -- if the event is not consumed, active widgets can access it - for _, widget in ipairs(self._window_stack) do - if widget.widget.is_always_active then - if widget.widget:handleEvent(event) then return end - end - if widget.widget.active_widgets then - for _, active_widget in ipairs(widget.widget.active_widgets) do - if active_widget:handleEvent(event) then return end - end - end - end + -- if the event is not consumed, active widgets can access it + for _, widget in ipairs(self._window_stack) do + if widget.widget.is_always_active then + if widget.widget:handleEvent(event) then return end + end + if widget.widget.active_widgets then + for _, active_widget in ipairs(widget.widget.active_widgets) do + if active_widget:handleEvent(event) then return end + end + end + end end function UIManager:checkTasks() - local now = { util.gettime() } + local now = { util.gettime() } - -- check if we have timed events in our queue and search next one - local wait_until = nil - local all_tasks_checked - repeat - all_tasks_checked = true - for i = #self._execution_stack, 1, -1 do - local task = self._execution_stack[i] - if not task.time - or task.time[1] < now[1] - or task.time[1] == now[1] and task.time[2] < now[2] then - -- task is pending to be executed right now. do it. - task.action() - -- and remove from table - table.remove(self._execution_stack, i) - -- start loop again, since new tasks might be on the - -- queue now - all_tasks_checked = false - elseif not wait_until - or wait_until[1] > task.time[1] - or wait_until[1] == task.time[1] and wait_until[2] > task.time[2] then - -- task is to be run in the future _and_ is scheduled - -- earlier than the tasks we looked at already - -- so adjust to the currently examined task instead. - wait_until = task.time - end - end - until all_tasks_checked - return wait_until + -- check if we have timed events in our queue and search next one + local wait_until = nil + local all_tasks_checked + repeat + all_tasks_checked = true + for i = #self._execution_stack, 1, -1 do + local task = self._execution_stack[i] + if not task.time + or task.time[1] < now[1] + or task.time[1] == now[1] and task.time[2] < now[2] then + -- task is pending to be executed right now. do it. + task.action() + -- and remove from table + table.remove(self._execution_stack, i) + -- start loop again, since new tasks might be on the + -- queue now + all_tasks_checked = false + elseif not wait_until + or wait_until[1] > task.time[1] + or wait_until[1] == task.time[1] and wait_until[2] > task.time[2] then + -- task is to be run in the future _and_ is scheduled + -- earlier than the tasks we looked at already + -- so adjust to the currently examined task instead. + wait_until = task.time + end + end + until all_tasks_checked + return wait_until end -- this is the main loop of the UI controller -- it is intended to manage input events and delegate -- them to dialogs function UIManager:run() - self._running = true - while self._running do - local now = { util.gettime() } - local wait_until = self:checkTasks() + self._running = true + while self._running do + local now = { util.gettime() } + local wait_until = self:checkTasks() - --DEBUG("---------------------------------------------------") - --DEBUG("exec stack", self._execution_stack) - --DEBUG("window stack", self._window_stack) - --DEBUG("dirty stack", self._dirty) - --DEBUG("---------------------------------------------------") + --DEBUG("---------------------------------------------------") + --DEBUG("exec stack", self._execution_stack) + --DEBUG("window stack", self._window_stack) + --DEBUG("dirty stack", self._dirty) + --DEBUG("---------------------------------------------------") - -- stop when we have no window to show - if #self._window_stack == 0 then - DEBUG("no dialog left to show, would loop endlessly") - return nil - end + -- stop when we have no window to show + if #self._window_stack == 0 then + DEBUG("no dialog left to show, would loop endlessly") + return nil + end - -- repaint dirty widgets - local dirty = false - local request_full_refresh = false - local force_full_refresh = false - local force_patial_refresh = false - local force_fast_refresh = false - for _, widget in ipairs(self._window_stack) do - if self.repaint_all or self._dirty[widget.widget] then - widget.widget:paintTo(Screen.bb, widget.x, widget.y) - if self._dirty[widget.widget] == "auto" then - request_full_refresh = true - end - if self._dirty[widget.widget] == "full" then - force_full_refresh = true - end - if self._dirty[widget.widget] == "partial" then - force_patial_refresh = true - end - if self._dirty[widget.widget] == "fast" then - force_fast_refresh = true - end - -- and remove from list after painting - self._dirty[widget.widget] = nil - -- trigger repaint - dirty = true - end - end - - if self.full_refresh then - dirty = true - force_full_refresh = true - end + -- repaint dirty widgets + local dirty = false + local request_full_refresh = false + local force_full_refresh = false + local force_patial_refresh = false + local force_fast_refresh = false + for _, widget in ipairs(self._window_stack) do + if self.repaint_all or self._dirty[widget.widget] then + widget.widget:paintTo(Screen.bb, widget.x, widget.y) + if self._dirty[widget.widget] == "auto" then + request_full_refresh = true + end + if self._dirty[widget.widget] == "full" then + force_full_refresh = true + end + if self._dirty[widget.widget] == "partial" then + force_patial_refresh = true + end + if self._dirty[widget.widget] == "fast" then + force_fast_refresh = true + end + -- and remove from list after painting + self._dirty[widget.widget] = nil + -- trigger repaint + dirty = true + end + end + + if self.full_refresh then + dirty = true + force_full_refresh = true + end - if self.patial_refresh then - dirty = true - force_patial_refresh = true - end - - self.repaint_all = false - self.full_refresh = false - self.patial_refresh = false - - local refresh_type = self.default_refresh_type - local waveform_mode = self.default_waveform_mode - if dirty then - if force_patial_refresh or force_fast_refresh then - refresh_type = 0 - elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then - refresh_type = 1 - end - if force_fast_refresh then - waveform_mode = self.fast_waveform_mode - end - if self.update_region_func then - local update_region = self.update_region_func() - -- in some rare cases update region has 1 pixel offset - Screen:refresh(refresh_type, waveform_mode, - update_region.x-1, update_region.y-1, - update_region.w+2, update_region.h+2) - else - Screen:refresh(refresh_type, waveform_mode) - end - if self.refresh_type == 1 then - self.refresh_count = 0 - elseif not force_patial_refresh and not force_full_refresh then - self.refresh_count = (self.refresh_count + 1)%self.FULL_REFRESH_COUNT - end - self.update_region_func = nil - end + if self.patial_refresh then + dirty = true + force_patial_refresh = true + end + + self.repaint_all = false + self.full_refresh = false + self.patial_refresh = false + + local refresh_type = self.default_refresh_type + local waveform_mode = self.default_waveform_mode + if dirty then + if force_patial_refresh or force_fast_refresh then + refresh_type = 0 + elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then + refresh_type = 1 + end + if force_fast_refresh then + waveform_mode = self.fast_waveform_mode + end + if self.update_region_func then + local update_region = self.update_region_func() + -- in some rare cases update region has 1 pixel offset + Screen:refresh(refresh_type, waveform_mode, + update_region.x-1, update_region.y-1, + update_region.w+2, update_region.h+2) + else + Screen:refresh(refresh_type, waveform_mode) + end + if self.refresh_type == 1 then + self.refresh_count = 0 + elseif not force_patial_refresh and not force_full_refresh then + self.refresh_count = (self.refresh_count + 1)%self.FULL_REFRESH_COUNT + end + self.update_region_func = nil + end - self:checkTasks() + self:checkTasks() - -- wait for next event - -- note that we will skip that if in the meantime we have tasks that are ready to run - local input_event = nil - if not wait_until then - -- no pending task, wait endlessly - input_event = Input:waitEvent() - elseif wait_until[1] > now[1] - or wait_until[1] == now[1] and wait_until[2] > now[2] then - local wait_for = { s = wait_until[1] - now[1], us = wait_until[2] - now[2] } - if wait_for.us < 0 then - wait_for.s = wait_for.s - 1 - wait_for.us = 1000000 + wait_for.us - end - -- wait until next task is pending - input_event = Input:waitEvent(wait_for.us, wait_for.s) - end + -- wait for next event + -- note that we will skip that if in the meantime we have tasks that are ready to run + local input_event = nil + if not wait_until then + -- no pending task, wait endlessly + input_event = Input:waitEvent() + elseif wait_until[1] > now[1] + or wait_until[1] == now[1] and wait_until[2] > now[2] then + local wait_for = { s = wait_until[1] - now[1], us = wait_until[2] - now[2] } + if wait_for.us < 0 then + wait_for.s = wait_for.s - 1 + wait_for.us = 1000000 + wait_for.us + end + -- wait until next task is pending + input_event = Input:waitEvent(wait_for.us, wait_for.s) + end - -- delegate input_event to handler - if input_event then - --DEBUG("in ui.lua:", input_event) - if input_event == "IntoSS" then - Device:intoScreenSaver() - elseif input_event == "OutOfSS" then - Device:outofScreenSaver() - elseif input_event == "Charging" then - Device:usbPlugIn() - elseif input_event == "NotCharging" then - Device:usbPlugOut() - self:sendEvent(Event:new("NotCharging")) - elseif input_event == "Light" then - Device:getPowerDevice():toggleFrontlight() - elseif (input_event == "Power" and not Device.screen_saver_mode) - or input_event == "Suspend" then - local InfoMessage = require("ui/widget/infomessage") - self:show(InfoMessage:new{ - text = _("Standby"), - timeout = 1, - }) - Device:prepareSuspend() - self:scheduleIn(0.5, function() Device:Suspend() end) - elseif (input_event == "Power" and Device.screen_saver_mode) - or input_event == "Resume" then - Device:Resume() - self:sendEvent(Event:new("Resume")) - else - self:sendEvent(input_event) - end - end - end + -- delegate input_event to handler + if input_event then + --DEBUG("in ui.lua:", input_event) + if input_event == "IntoSS" then + Device:intoScreenSaver() + elseif input_event == "OutOfSS" then + Device:outofScreenSaver() + elseif input_event == "Charging" then + Device:usbPlugIn() + elseif input_event == "NotCharging" then + Device:usbPlugOut() + self:sendEvent(Event:new("NotCharging")) + elseif input_event == "Light" then + Device:getPowerDevice():toggleFrontlight() + elseif (input_event == "Power" and not Device.screen_saver_mode) + or input_event == "Suspend" then + local InfoMessage = require("ui/widget/infomessage") + self:show(InfoMessage:new{ + text = _("Standby"), + timeout = 1, + }) + Device:prepareSuspend() + self:scheduleIn(0.5, function() Device:Suspend() end) + elseif (input_event == "Power" and Device.screen_saver_mode) + or input_event == "Resume" then + Device:Resume() + self:sendEvent(Event:new("Resume")) + else + self:sendEvent(input_event) + end + end + end end return UIManager diff --git a/frontend/ui/widget/bboxwidget.lua b/frontend/ui/widget/bboxwidget.lua index 3f4045295..cb28f5536 100644 --- a/frontend/ui/widget/bboxwidget.lua +++ b/frontend/ui/widget/bboxwidget.lua @@ -11,212 +11,212 @@ local DEBUG = require("dbg") BBoxWidget shows a bbox for page cropping ]] local BBoxWidget = InputContainer:new{ - page_bbox = nil, - screen_bbox = nil, - linesize = 2, - fine_factor = 10, + page_bbox = nil, + screen_bbox = nil, + linesize = 2, + fine_factor = 10, } function BBoxWidget:init() - self.page_bbox = self.document:getPageBBox(self.view.state.page) - --DEBUG("used page bbox on page", self.view.state.page, self.page_bbox) - if Device:isTouchDevice() then - self.ges_events = { - TapAdjust = { - GestureRange:new{ - ges = "tap", - range = self.view.dimen, - } - }, - SwipeAdjust = { - GestureRange:new{ - ges = "swipe", - range = self.view.dimen, - } - }, - HoldAdjust = { - GestureRange:new{ - ges = "hold", - range = self.view.dimen, - } - }, - ConfirmAdjust = { - GestureRange:new{ - ges = "double_tap", - range = self.view.dimen, - } - } - } - end + self.page_bbox = self.document:getPageBBox(self.view.state.page) + --DEBUG("used page bbox on page", self.view.state.page, self.page_bbox) + if Device:isTouchDevice() then + self.ges_events = { + TapAdjust = { + GestureRange:new{ + ges = "tap", + range = self.view.dimen, + } + }, + SwipeAdjust = { + GestureRange:new{ + ges = "swipe", + range = self.view.dimen, + } + }, + HoldAdjust = { + GestureRange:new{ + ges = "hold", + range = self.view.dimen, + } + }, + ConfirmAdjust = { + GestureRange:new{ + ges = "double_tap", + range = self.view.dimen, + } + } + } + end end function BBoxWidget:getSize() - return self.view.dimen + return self.view.dimen end function BBoxWidget:paintTo(bb, x, y) - -- As getScreenBBox uses view states, screen_bbox initialization is postponed. - self.screen_bbox = self.screen_bbox or self:getScreenBBox(self.page_bbox) - local bbox = self.screen_bbox - -- top edge - bb:invertRect(bbox.x0 + self.linesize, bbox.y0, bbox.x1 - bbox.x0, self.linesize) - -- bottom edge - bb:invertRect(bbox.x0 + self.linesize, bbox.y1, bbox.x1 - bbox.x0 - self.linesize, self.linesize) - -- left edge - bb:invertRect(bbox.x0, bbox.y0, self.linesize, bbox.y1 - bbox.y0 + self.linesize) - -- right edge - bb:invertRect(bbox.x1, bbox.y0 + self.linesize, self.linesize, bbox.y1 - bbox.y0) + -- As getScreenBBox uses view states, screen_bbox initialization is postponed. + self.screen_bbox = self.screen_bbox or self:getScreenBBox(self.page_bbox) + local bbox = self.screen_bbox + -- top edge + bb:invertRect(bbox.x0 + self.linesize, bbox.y0, bbox.x1 - bbox.x0, self.linesize) + -- bottom edge + bb:invertRect(bbox.x0 + self.linesize, bbox.y1, bbox.x1 - bbox.x0 - self.linesize, self.linesize) + -- left edge + bb:invertRect(bbox.x0, bbox.y0, self.linesize, bbox.y1 - bbox.y0 + self.linesize) + -- right edge + bb:invertRect(bbox.x1, bbox.y0 + self.linesize, self.linesize, bbox.y1 - bbox.y0) end -- transform page bbox to screen bbox function BBoxWidget:getScreenBBox(page_bbox) - local bbox = {} - local scale = self.view.state.zoom - local screen_offset = self.view.state.offset - --DEBUG("screen offset in page_to_screen", screen_offset) - bbox.x0 = Math.round(page_bbox.x0 * scale + screen_offset.x) - bbox.y0 = Math.round(page_bbox.y0 * scale + screen_offset.y) - bbox.x1 = Math.round(page_bbox.x1 * scale + screen_offset.x) - bbox.y1 = Math.round(page_bbox.y1 * scale + screen_offset.y) - return bbox + local bbox = {} + local scale = self.view.state.zoom + local screen_offset = self.view.state.offset + --DEBUG("screen offset in page_to_screen", screen_offset) + bbox.x0 = Math.round(page_bbox.x0 * scale + screen_offset.x) + bbox.y0 = Math.round(page_bbox.y0 * scale + screen_offset.y) + bbox.x1 = Math.round(page_bbox.x1 * scale + screen_offset.x) + bbox.y1 = Math.round(page_bbox.y1 * scale + screen_offset.y) + return bbox end -- transform screen bbox to page bbox function BBoxWidget:getPageBBox(screen_bbox) - local bbox = {} - local scale = self.view.state.zoom - local screen_offset = self.view.state.offset - --DEBUG("screen offset in screen_to_page", screen_offset) - bbox.x0 = Math.round((screen_bbox.x0 - screen_offset.x) / scale) - bbox.y0 = Math.round((screen_bbox.y0 - screen_offset.y) / scale) - bbox.x1 = Math.round((screen_bbox.x1 - screen_offset.x) / scale) - bbox.y1 = Math.round((screen_bbox.y1 - screen_offset.y) / scale) - return bbox + local bbox = {} + local scale = self.view.state.zoom + local screen_offset = self.view.state.offset + --DEBUG("screen offset in screen_to_page", screen_offset) + bbox.x0 = Math.round((screen_bbox.x0 - screen_offset.x) / scale) + bbox.y0 = Math.round((screen_bbox.y0 - screen_offset.y) / scale) + bbox.x1 = Math.round((screen_bbox.x1 - screen_offset.x) / scale) + bbox.y1 = Math.round((screen_bbox.y1 - screen_offset.y) / scale) + return bbox end function BBoxWidget:inPageArea(ges) - local offset = self.view.state.offset - local page_area = self.view.page_area - local page_dimen = Geom:new{ x = offset.x, y = offset.y, h = page_area.h, w = page_area.w} - return not ges.pos:notIntersectWith(page_dimen) + local offset = self.view.state.offset + local page_area = self.view.page_area + local page_dimen = Geom:new{ x = offset.x, y = offset.y, h = page_area.h, w = page_area.w} + return not ges.pos:notIntersectWith(page_dimen) end function BBoxWidget:adjustScreenBBox(ges, relative) - --DEBUG("adjusting crop bbox with pos", ges.pos) - if not self:inPageArea(ges) then return end - local bbox = self.screen_bbox - local upper_left = Geom:new{ x = bbox.x0, y = bbox.y0} - local upper_right = Geom:new{ x = bbox.x1, y = bbox.y0} - local bottom_left = Geom:new{ x = bbox.x0, y = bbox.y1} - local bottom_right = Geom:new{ x = bbox.x1, y = bbox.y1} - local upper_center = Geom:new{ x = (bbox.x0 + bbox.x1) / 2, y = bbox.y0} - local bottom_center = Geom:new{ x = (bbox.x0 + bbox.x1) / 2, y = bbox.y1} - local right_center = Geom:new{ x = bbox.x1, y = (bbox.y0 + bbox.y1) / 2} - local left_center = Geom:new{ x = bbox.x0, y = (bbox.y0 + bbox.y1) / 2} - local anchors = { - upper_left, upper_center, upper_right, - left_center, right_center, - bottom_left, bottom_center, bottom_right, - } - local _, nearest = Math.tmin(anchors, function(a,b) - return a:distance(ges.pos) > b:distance(ges.pos) - end) - --DEBUG("nearest anchor", nearest) - if nearest == upper_left then - upper_left.x = ges.pos.x - upper_left.y = ges.pos.y - elseif nearest == bottom_right then - bottom_right.x = ges.pos.x - bottom_right.y = ges.pos.y - elseif nearest == upper_right then - bottom_right.x = ges.pos.x - upper_left.y = ges.pos.y - elseif nearest == bottom_left then - upper_left.x = ges.pos.x - bottom_right.y = ges.pos.y - elseif nearest == upper_center then - if relative then - local delta = 0 - if ges.direction == "north" then - delta = -ges.distance / self.fine_factor - elseif ges.direction == "south" then - delta = ges.distance / self.fine_factor - end - upper_left.y = upper_left.y + delta - else - upper_left.y = ges.pos.y - end - elseif nearest == right_center then - if relative then - local delta = 0 - if ges.direction == "west" then - delta = -ges.distance / self.fine_factor - elseif ges.direction == "east" then - delta = ges.distance / self.fine_factor - end - bottom_right.x = bottom_right.x + delta - else - bottom_right.x = ges.pos.x - end - elseif nearest == bottom_center then - if relative then - local delta = 0 - if ges.direction == "north" then - delta = -ges.distance / self.fine_factor - elseif ges.direction == "south" then - delta = ges.distance / self.fine_factor - end - bottom_right.y = bottom_right.y + delta - else - bottom_right.y = ges.pos.y - end - elseif nearest == left_center then - if relative then - local delta = 0 - if ges.direction == "west" then - delta = -ges.distance / self.fine_factor - elseif ges.direction == "east" then - delta = ges.distance / self.fine_factor - end - upper_left.x = upper_left.x + delta - else - upper_left.x = ges.pos.x - end - end - self.screen_bbox = { - x0 = Math.round(upper_left.x), - y0 = Math.round(upper_left.y), - x1 = Math.round(bottom_right.x), - y1 = Math.round(bottom_right.y) - } + --DEBUG("adjusting crop bbox with pos", ges.pos) + if not self:inPageArea(ges) then return end + local bbox = self.screen_bbox + local upper_left = Geom:new{ x = bbox.x0, y = bbox.y0} + local upper_right = Geom:new{ x = bbox.x1, y = bbox.y0} + local bottom_left = Geom:new{ x = bbox.x0, y = bbox.y1} + local bottom_right = Geom:new{ x = bbox.x1, y = bbox.y1} + local upper_center = Geom:new{ x = (bbox.x0 + bbox.x1) / 2, y = bbox.y0} + local bottom_center = Geom:new{ x = (bbox.x0 + bbox.x1) / 2, y = bbox.y1} + local right_center = Geom:new{ x = bbox.x1, y = (bbox.y0 + bbox.y1) / 2} + local left_center = Geom:new{ x = bbox.x0, y = (bbox.y0 + bbox.y1) / 2} + local anchors = { + upper_left, upper_center, upper_right, + left_center, right_center, + bottom_left, bottom_center, bottom_right, + } + local _, nearest = Math.tmin(anchors, function(a,b) + return a:distance(ges.pos) > b:distance(ges.pos) + end) + --DEBUG("nearest anchor", nearest) + if nearest == upper_left then + upper_left.x = ges.pos.x + upper_left.y = ges.pos.y + elseif nearest == bottom_right then + bottom_right.x = ges.pos.x + bottom_right.y = ges.pos.y + elseif nearest == upper_right then + bottom_right.x = ges.pos.x + upper_left.y = ges.pos.y + elseif nearest == bottom_left then + upper_left.x = ges.pos.x + bottom_right.y = ges.pos.y + elseif nearest == upper_center then + if relative then + local delta = 0 + if ges.direction == "north" then + delta = -ges.distance / self.fine_factor + elseif ges.direction == "south" then + delta = ges.distance / self.fine_factor + end + upper_left.y = upper_left.y + delta + else + upper_left.y = ges.pos.y + end + elseif nearest == right_center then + if relative then + local delta = 0 + if ges.direction == "west" then + delta = -ges.distance / self.fine_factor + elseif ges.direction == "east" then + delta = ges.distance / self.fine_factor + end + bottom_right.x = bottom_right.x + delta + else + bottom_right.x = ges.pos.x + end + elseif nearest == bottom_center then + if relative then + local delta = 0 + if ges.direction == "north" then + delta = -ges.distance / self.fine_factor + elseif ges.direction == "south" then + delta = ges.distance / self.fine_factor + end + bottom_right.y = bottom_right.y + delta + else + bottom_right.y = ges.pos.y + end + elseif nearest == left_center then + if relative then + local delta = 0 + if ges.direction == "west" then + delta = -ges.distance / self.fine_factor + elseif ges.direction == "east" then + delta = ges.distance / self.fine_factor + end + upper_left.x = upper_left.x + delta + else + upper_left.x = ges.pos.x + end + end + self.screen_bbox = { + x0 = Math.round(upper_left.x), + y0 = Math.round(upper_left.y), + x1 = Math.round(bottom_right.x), + y1 = Math.round(bottom_right.y) + } - UIManager.repaint_all = true + UIManager.repaint_all = true end function BBoxWidget:getModifiedPageBBox() - return self:getPageBBox(self.screen_bbox) + return self:getPageBBox(self.screen_bbox) end function BBoxWidget:onTapAdjust(arg, ges) - self:adjustScreenBBox(ges) - return true + self:adjustScreenBBox(ges) + return true end function BBoxWidget:onSwipeAdjust(arg, ges) - self:adjustScreenBBox(ges, true) - return true + self:adjustScreenBBox(ges, true) + return true end function BBoxWidget:onHoldAdjust(arg, ges) - self:adjustScreenBBox(ges) - return true + self:adjustScreenBBox(ges) + return true end function BBoxWidget:onConfirmAdjust(arg, ges) - if self:inPageArea(ges) then - self.ui:handleEvent(Event:new("ConfirmPageCrop")) - end - return true + if self:inPageArea(ges) then + self.ui:handleEvent(Event:new("ConfirmPageCrop")) + end + return true end return BBoxWidget diff --git a/frontend/ui/widget/button.lua b/frontend/ui/widget/button.lua index 28eed3d0c..eb7415b4a 100644 --- a/frontend/ui/widget/button.lua +++ b/frontend/ui/widget/button.lua @@ -13,135 +13,135 @@ local _ = require("gettext") a button widget that shows text or a icon and handles callback when tapped --]] local Button = InputContainer:new{ - text = nil, -- mandatory - icon = nil, - preselect = false, - callback = nil, - enabled = true, - margin = 0, - bordersize = 3, - background = 0, - radius = 15, - padding = 2, - width = nil, - text_font_face = "cfont", - text_font_size = 20, + text = nil, -- mandatory + icon = nil, + preselect = false, + callback = nil, + enabled = true, + margin = 0, + bordersize = 3, + background = 0, + radius = 15, + padding = 2, + width = nil, + text_font_face = "cfont", + text_font_size = 20, } function Button:init() - if self.text then - self.label_widget = TextWidget:new{ - text = self.text, - bgcolor = 0.0, - fgcolor = self.enabled and 1.0 or 0.5, - bold = true, - face = Font:getFace(self.text_font_face, self.text_font_size) - } - else - self.label_widget = ImageWidget:new{ - file = self.icon, - dim = self.enabled, - } - end - local widget_size = self.label_widget:getSize() - if self.width == nil then - self.width = widget_size.w - end - -- set FrameContainer content - self[1] = FrameContainer:new{ - margin = self.margin, - bordersize = self.bordersize, - background = self.background, - radius = self.radius, - padding = self.padding, - CenterContainer:new{ - dimen = Geom:new{ - w = self.width, - h = widget_size.h - }, - self.label_widget, - } - } - if self.preselect then - self[1].color = 15 - else - self[1].color = 5 - end - self.dimen = self[1]:getSize() - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = _("Tap Button"), - }, - } - end + if self.text then + self.label_widget = TextWidget:new{ + text = self.text, + bgcolor = 0.0, + fgcolor = self.enabled and 1.0 or 0.5, + bold = true, + face = Font:getFace(self.text_font_face, self.text_font_size) + } + else + self.label_widget = ImageWidget:new{ + file = self.icon, + dim = self.enabled, + } + end + local widget_size = self.label_widget:getSize() + if self.width == nil then + self.width = widget_size.w + end + -- set FrameContainer content + self[1] = FrameContainer:new{ + margin = self.margin, + bordersize = self.bordersize, + background = self.background, + radius = self.radius, + padding = self.padding, + CenterContainer:new{ + dimen = Geom:new{ + w = self.width, + h = widget_size.h + }, + self.label_widget, + } + } + if self.preselect then + self[1].color = 15 + else + self[1].color = 5 + end + self.dimen = self[1]:getSize() + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = _("Tap Button"), + }, + } + end end function Button:onFocus() - self[1].color = 15 - return true + self[1].color = 15 + return true end function Button:onUnfocus() - self[1].color = 5 - return true + self[1].color = 5 + return true end function Button:enable() - self.enabled = true - if self.text then - self.label_widget.fgcolor = self.enabled and 1.0 or 0.5 - else - self.label_widget.dim = not self.enabled - end + self.enabled = true + if self.text then + self.label_widget.fgcolor = self.enabled and 1.0 or 0.5 + else + self.label_widget.dim = not self.enabled + end end function Button:disable() - self.enabled = false - if self.text then - self.label_widget.fgcolor = self.enabled and 1.0 or 0.5 - else - self.label_widget.dim = not self.enabled - end + self.enabled = false + if self.text then + self.label_widget.fgcolor = self.enabled and 1.0 or 0.5 + else + self.label_widget.dim = not self.enabled + end end function Button:enableDisable(enable) - if enable then - self:enable() - else - self:disable() - end + if enable then + self:enable() + else + self:disable() + end end function Button:hide() - if self.icon then - self.label_widget.hide = true - end + if self.icon then + self.label_widget.hide = true + end end function Button:show() - if self.icon then - self.label_widget.hide = false - end + if self.icon then + self.label_widget.hide = false + end end function Button:showHide(show) - if show then - self:show() - else - self:hide() - end + if show then + self:show() + else + self:hide() + end end function Button:onTapSelect() - if self.enabled then - self.callback() - end - return true + if self.enabled then + self.callback() + end + return true end return Button diff --git a/frontend/ui/widget/buttondialog.lua b/frontend/ui/widget/buttondialog.lua index 28183a502..611b8369c 100644 --- a/frontend/ui/widget/buttondialog.lua +++ b/frontend/ui/widget/buttondialog.lua @@ -11,49 +11,49 @@ local UIManager = require("ui/uimanager") local _ = require("gettext") local ButtonDialog = InputContainer:new{ - buttons = nil, - tap_close_callback = nil, + buttons = nil, + tap_close_callback = nil, } function ButtonDialog:init() - if Device:hasKeyboard() then - self.key_events = { - AnyKeyPressed = { { Input.group.Any }, - seqtext = "any key", doc = _("close dialog") } - } - else - self.ges_events.TapClose = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - } - end - self[1] = CenterContainer:new{ - dimen = Screen:getSize(), - FrameContainer:new{ - ButtonTable:new{ - width = Screen:getWidth()*0.9, - buttons = self.buttons, - }, - background = 0, - bordersize = 2, - radius = 7, - padding = 2, - } - } + if Device:hasKeyboard() then + self.key_events = { + AnyKeyPressed = { { Input.group.Any }, + seqtext = "any key", doc = _("close dialog") } + } + else + self.ges_events.TapClose = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + end + self[1] = CenterContainer:new{ + dimen = Screen:getSize(), + FrameContainer:new{ + ButtonTable:new{ + width = Screen:getWidth()*0.9, + buttons = self.buttons, + }, + background = 0, + bordersize = 2, + radius = 7, + padding = 2, + } + } end function ButtonDialog:onTapClose() - UIManager:close(self) - if self.tap_close_callback then - self.tap_close_callback() - end - return true + UIManager:close(self) + if self.tap_close_callback then + self.tap_close_callback() + end + return true end return ButtonDialog diff --git a/frontend/ui/widget/buttontable.lua b/frontend/ui/widget/buttontable.lua index 328513a54..36ead3d11 100644 --- a/frontend/ui/widget/buttontable.lua +++ b/frontend/ui/widget/buttontable.lua @@ -7,72 +7,72 @@ local Screen = require("ui/screen") local Geom = require("ui/geometry") local ButtonTable = VerticalGroup:new{ - width = Screen:getWidth(), - buttons = { - { - {text="OK", enabled=true, callback=nil}, - {text="Cancel", enabled=false, callback=nil}, - }, - }, - sep_width = Screen:scaleByDPI(1), - padding = Screen:scaleByDPI(2), + width = Screen:getWidth(), + buttons = { + { + {text="OK", enabled=true, callback=nil}, + {text="Cancel", enabled=false, callback=nil}, + }, + }, + sep_width = Screen:scaleByDPI(1), + padding = Screen:scaleByDPI(2), - zero_sep = false, - button_font_face = "cfont", - button_font_size = 20, + zero_sep = false, + button_font_face = "cfont", + button_font_size = 20, } function ButtonTable:init() - --local vertical_group = VerticalGroup:new{} - if self.zero_sep then - self:addHorizontalSep() - end - for i = 1, #self.buttons do - local horizontal_group = HorizontalGroup:new{} - local line = self.buttons[i] - local sizer_space = self.sep_width * (#line - 1) + 2 - for j = 1, #line do - local button = Button:new{ - text = line[j].text, - enabled = line[j].enabled, - callback = line[j].callback, - width = (self.width - sizer_space)/#line, - bordersize = 0, - margin = 0, - padding = 0, - text_font_face = self.button_font_face, - text_font_size = self.button_font_size, - } - local button_dim = button:getSize() - local vertical_sep = LineWidget:new{ - background = 8, - dimen = Geom:new{ - w = self.sep_width, - h = button_dim.h, - } - } - table.insert(horizontal_group, button) - if j < #line then - table.insert(horizontal_group, vertical_sep) - end - end -- end for each button - table.insert(self, horizontal_group) - if i < #self.buttons then - self:addHorizontalSep() - end - end -- end for each button line + --local vertical_group = VerticalGroup:new{} + if self.zero_sep then + self:addHorizontalSep() + end + for i = 1, #self.buttons do + local horizontal_group = HorizontalGroup:new{} + local line = self.buttons[i] + local sizer_space = self.sep_width * (#line - 1) + 2 + for j = 1, #line do + local button = Button:new{ + text = line[j].text, + enabled = line[j].enabled, + callback = line[j].callback, + width = (self.width - sizer_space)/#line, + bordersize = 0, + margin = 0, + padding = 0, + text_font_face = self.button_font_face, + text_font_size = self.button_font_size, + } + local button_dim = button:getSize() + local vertical_sep = LineWidget:new{ + background = 8, + dimen = Geom:new{ + w = self.sep_width, + h = button_dim.h, + } + } + table.insert(horizontal_group, button) + if j < #line then + table.insert(horizontal_group, vertical_sep) + end + end -- end for each button + table.insert(self, horizontal_group) + if i < #self.buttons then + self:addHorizontalSep() + end + end -- end for each button line end function ButtonTable:addHorizontalSep() - table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) }) - table.insert(self, LineWidget:new{ - background = 8, - dimen = Geom:new{ - w = self.width, - h = self.sep_width, - } - }) - table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) }) + table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) }) + table.insert(self, LineWidget:new{ + background = 8, + dimen = Geom:new{ + w = self.width, + h = self.sep_width, + } + }) + table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) }) end return ButtonTable diff --git a/frontend/ui/widget/closebutton.lua b/frontend/ui/widget/closebutton.lua index a1710fa8b..a7e247b64 100644 --- a/frontend/ui/widget/closebutton.lua +++ b/frontend/ui/widget/closebutton.lua @@ -11,35 +11,35 @@ local Font = require("ui/font") a button widget that shows an "×" and handles closing window when tapped --]] local CloseButton = InputContainer:new{ - align = "right", - window = nil, + align = "right", + window = nil, } function CloseButton:init() - local text_widget = TextWidget:new{ - text = "×", - face = Font:getFace("cfont", 32), - } - self[1] = FrameContainer:new{ - bordersize = 0, - padding = 0, - text_widget - } - - self.dimen = text_widget:getSize():copy() + local text_widget = TextWidget:new{ + text = "×", + face = Font:getFace("cfont", 32), + } + self[1] = FrameContainer:new{ + bordersize = 0, + padding = 0, + text_widget + } + + self.dimen = text_widget:getSize():copy() - self.ges_events.Close = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = "Tap on close button", - } + self.ges_events.Close = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = "Tap on close button", + } end function CloseButton:onClose() - self.window:onClose() - return true + self.window:onClose() + return true end return CloseButton diff --git a/frontend/ui/widget/configdialog.lua b/frontend/ui/widget/configdialog.lua index 06268a14e..ef6501dd8 100644 --- a/frontend/ui/widget/configdialog.lua +++ b/frontend/ui/widget/configdialog.lua @@ -26,363 +26,363 @@ local _ = require("gettext") local MenuBarItem = InputContainer:new{} function MenuBarItem:init() - self.dimen = self[1]:getSize() - -- we need this table per-instance, so we declare it here - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = _("Select Menu Item"), - }, - } - else - self.active_key_events = { - Select = { {"Press"}, doc = _("chose selected item") }, - } - end + self.dimen = self[1]:getSize() + -- we need this table per-instance, so we declare it here + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = _("Select Menu Item"), + }, + } + else + self.active_key_events = { + Select = { {"Press"}, doc = _("chose selected item") }, + } + end end function MenuBarItem:onTapSelect() - UIManager:scheduleIn(0.0, function() self:invert(true) end) - UIManager:scheduleIn(0.1, function() - UIManager:sendEvent(Event:new("ShowConfigPanel", self.index)) - end) - UIManager:scheduleIn(0.5, function() self:invert(false) end) - return true + UIManager:scheduleIn(0.0, function() self:invert(true) end) + UIManager:scheduleIn(0.1, function() + UIManager:sendEvent(Event:new("ShowConfigPanel", self.index)) + end) + UIManager:scheduleIn(0.5, function() self:invert(false) end) + return true end function MenuBarItem:invert(invert) - self[1].invert = invert - UIManager.update_region_func = function() - DEBUG("update icon region", self[1].dimen) - return self[1].dimen - end - UIManager:setDirty(self.config, "full") + self[1].invert = invert + UIManager.update_region_func = function() + DEBUG("update icon region", self[1].dimen) + return self[1].dimen + end + UIManager:setDirty(self.config, "full") end local OptionTextItem = InputContainer:new{} function OptionTextItem:init() - local text_widget = self[1] - - self[1] = UnderlineContainer:new{ - text_widget, - padding = self.padding, - color = self.color, - } - self.dimen = self[1]:getSize() - -- we need this table per-instance, so we declare it here - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = _("Select Option Item"), - }, - } - else - self.active_key_events = { - Select = { {"Press"}, doc = _("chose selected item") }, - } - end + local text_widget = self[1] + + self[1] = UnderlineContainer:new{ + text_widget, + padding = self.padding, + color = self.color, + } + self.dimen = self[1]:getSize() + -- we need this table per-instance, so we declare it here + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = _("Select Option Item"), + }, + } + else + self.active_key_events = { + Select = { {"Press"}, doc = _("chose selected item") }, + } + end end function OptionTextItem:onTapSelect() - for _, item in pairs(self.items) do - item[1].color = 0 - end - self[1].color = 15 - self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) - UIManager:setDirty(self.config, "partial") - return true + for _, item in pairs(self.items) do + item[1].color = 0 + end + self[1].color = 15 + self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) + UIManager:setDirty(self.config, "partial") + return true end local OptionIconItem = InputContainer:new{} function OptionIconItem:init() - self.dimen = self.icon:getSize() - self[1] = UnderlineContainer:new{ - self.icon, - padding = self.padding, - color = self.color, - } - -- we need this table per-instance, so we declare it here - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = _("Select Option Item"), - }, - } - end + self.dimen = self.icon:getSize() + self[1] = UnderlineContainer:new{ + self.icon, + padding = self.padding, + color = self.color, + } + -- we need this table per-instance, so we declare it here + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = _("Select Option Item"), + }, + } + end end function OptionIconItem:onTapSelect() - for _, item in pairs(self.items) do - --item[1][1].invert = false - item[1].color = 0 - end - --self[1][1].invert = true - self[1].color = 15 - self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) - UIManager:setDirty(self.config, "partial") - return true + for _, item in pairs(self.items) do + --item[1][1].invert = false + item[1].color = 0 + end + --self[1][1].invert = true + self[1].color = 15 + self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) + UIManager:setDirty(self.config, "partial") + return true end local ConfigOption = CenterContainer:new{} function ConfigOption:init() - local default_name_font_size = 20 - local default_item_font_size = 16 - local default_items_spacing = 30 - local default_option_height = 50 - local default_option_padding = 15 - local vertical_group = VerticalGroup:new{} - table.insert(vertical_group, VerticalSpan:new{ - width = Screen:scaleByDPI(default_option_padding), - }) - for c = 1, #self.options do - if self.options[c].show ~= false then - local name_align = self.options[c].name_align_right and self.options[c].name_align_right or 0.33 - local item_align = self.options[c].item_align_center and self.options[c].item_align_center or 0.66 - local name_font_face = self.options[c].name_font_face and self.options[c].name_font_face or "cfont" - local name_font_size = self.options[c].name_font_size and self.options[c].name_font_size or default_name_font_size - local item_font_face = self.options[c].item_font_face and self.options[c].item_font_face or "cfont" - local item_font_size = self.options[c].item_font_size and self.options[c].item_font_size or default_item_font_size - local option_height = Screen:scaleByDPI(self.options[c].height and self.options[c].height or default_option_height) - local items_spacing = HorizontalSpan:new{ - width = Screen:scaleByDPI(self.options[c].spacing and self.options[c].spacing or default_items_spacing) - } - local horizontal_group = HorizontalGroup:new{} - if self.options[c].name_text then - local option_name_container = RightContainer:new{ - dimen = Geom:new{ w = Screen:getWidth()*name_align, h = option_height}, - } - local option_name = TextWidget:new{ - text = self.options[c].name_text, - face = Font:getFace(name_font_face, name_font_size), - } - table.insert(option_name_container, option_name) - table.insert(horizontal_group, option_name_container) - end - - if self.options[c].widget == "ProgressWidget" then - local widget_container = CenterContainer:new{ - dimen = Geom:new{w = Screen:getWidth()*self.options[c].widget_align_center, h = option_height} - } - local widget = ProgressWidget:new{ - width = self.options[c].width, - height = self.options[c].height, - percentage = self.options[c].percentage, - } - table.insert(widget_container, widget) - table.insert(horizontal_group, widget_container) - end - - local option_items_container = CenterContainer:new{ - dimen = Geom:new{w = Screen:getWidth()*item_align, h = option_height} - } - local option_items_group = HorizontalGroup:new{} - local option_items_fixed = false - local option_items = {} - if type(self.options[c].item_font_size) == "table" then - option_items_group.align = "bottom" - option_items_fixed = true - end - -- make current index according to configurable table - local current_item = nil - local function value_diff(val1, val2, name) - if type(val1) ~= type(val2) then - error("different data types in option", name) - end - if type(val1) == "number" then - return math.abs(val1 - val2) - elseif type(val1) == "string" then - return val1 == val2 and 0 or 1 - end - end - if self.options[c].name then - if self.options[c].values then - -- check if current value is stored in configurable or calculated in runtime - local val = self.options[c].current_func and self.options[c].current_func() - or self.config.configurable[self.options[c].name] - local min_diff = nil - if type(val) == "table" then - min_diff = value_diff(val[1], self.options[c].values[1][1]) - else - min_diff = value_diff(val, self.options[c].values[1]) - end - - local diff = nil - for index, val_ in pairs(self.options[c].values) do - local diff = nil - if type(val) == "table" then - diff = value_diff(val[1], val_[1]) - else - diff = value_diff(val, val_) - end - if val == val_ then - current_item = index - break - end - if diff <= min_diff then - min_diff = diff - current_item = index - end - end - elseif self.options[c].args then - -- check if current arg is stored in configurable or calculated in runtime - local arg = self.options[c].current_func and self.options[c].current_func() - or self.config.configurable[self.options[c].name] - for idx, arg_ in pairs(self.options[c].args) do - if arg_ == arg then - current_item = idx - break - end - end - end - end - if self.options[c].item_text then - for d = 1, #self.options[c].item_text do - local option_item = nil - if option_items_fixed then - option_item = OptionTextItem:new{ - FixedTextWidget:new{ - text = self.options[c].item_text[d], - face = Font:getFace(item_font_face, item_font_size[d]), - }, - padding = 3, - color = d == current_item and 15 or 0, - } - else - option_item = OptionTextItem:new{ - TextWidget:new{ - text = self.options[c].item_text[d], - face = Font:getFace(item_font_face, item_font_size), - }, - padding = -3, - color = d == current_item and 15 or 0, - } - end - option_items[d] = option_item - option_item.items = option_items - option_item.name = self.options[c].name - option_item.values = self.options[c].values - option_item.args = self.options[c].args - option_item.event = self.options[c].event - option_item.current_item = d - option_item.config = self.config - table.insert(option_items_group, option_item) - if d ~= #self.options[c].item_text then - table.insert(option_items_group, items_spacing) - end - end - end - - if self.options[c].item_icons then - for d = 1, #self.options[c].item_icons do - local option_item = OptionIconItem:new{ - icon = ImageWidget:new{ - file = self.options[c].item_icons[d] - }, - padding = -2, - color = d == current_item and 15 or 0, - } - option_items[d] = option_item - option_item.items = option_items - option_item.name = self.options[c].name - option_item.values = self.options[c].values - option_item.args = self.options[c].args - option_item.event = self.options[c].event - option_item.current_item = d - option_item.config = self.config - table.insert(option_items_group, option_item) - if d ~= #self.options[c].item_icons then - table.insert(option_items_group, items_spacing) - end - end - end - - if self.options[c].toggle then - local switch = ToggleSwitch:new{ - width = Screen:scaleByDPI(self.options[c].width or 216), - name = self.options[c].name, - toggle = self.options[c].toggle, - alternate = self.options[c].alternate, - values = self.options[c].values, - args = self.options[c].args, - event = self.options[c].event, - events = self.options[c].events, - config = self.config, - } - local position = current_item - switch:setPosition(position) - table.insert(option_items_group, switch) - end - - table.insert(option_items_container, option_items_group) - table.insert(horizontal_group, option_items_container) - table.insert(vertical_group, horizontal_group) - end -- if - end -- for - table.insert(vertical_group, VerticalSpan:new{ width = default_option_padding }) - self[1] = vertical_group - self.dimen = vertical_group:getSize() + local default_name_font_size = 20 + local default_item_font_size = 16 + local default_items_spacing = 30 + local default_option_height = 50 + local default_option_padding = 15 + local vertical_group = VerticalGroup:new{} + table.insert(vertical_group, VerticalSpan:new{ + width = Screen:scaleByDPI(default_option_padding), + }) + for c = 1, #self.options do + if self.options[c].show ~= false then + local name_align = self.options[c].name_align_right and self.options[c].name_align_right or 0.33 + local item_align = self.options[c].item_align_center and self.options[c].item_align_center or 0.66 + local name_font_face = self.options[c].name_font_face and self.options[c].name_font_face or "cfont" + local name_font_size = self.options[c].name_font_size and self.options[c].name_font_size or default_name_font_size + local item_font_face = self.options[c].item_font_face and self.options[c].item_font_face or "cfont" + local item_font_size = self.options[c].item_font_size and self.options[c].item_font_size or default_item_font_size + local option_height = Screen:scaleByDPI(self.options[c].height and self.options[c].height or default_option_height) + local items_spacing = HorizontalSpan:new{ + width = Screen:scaleByDPI(self.options[c].spacing and self.options[c].spacing or default_items_spacing) + } + local horizontal_group = HorizontalGroup:new{} + if self.options[c].name_text then + local option_name_container = RightContainer:new{ + dimen = Geom:new{ w = Screen:getWidth()*name_align, h = option_height}, + } + local option_name = TextWidget:new{ + text = self.options[c].name_text, + face = Font:getFace(name_font_face, name_font_size), + } + table.insert(option_name_container, option_name) + table.insert(horizontal_group, option_name_container) + end + + if self.options[c].widget == "ProgressWidget" then + local widget_container = CenterContainer:new{ + dimen = Geom:new{w = Screen:getWidth()*self.options[c].widget_align_center, h = option_height} + } + local widget = ProgressWidget:new{ + width = self.options[c].width, + height = self.options[c].height, + percentage = self.options[c].percentage, + } + table.insert(widget_container, widget) + table.insert(horizontal_group, widget_container) + end + + local option_items_container = CenterContainer:new{ + dimen = Geom:new{w = Screen:getWidth()*item_align, h = option_height} + } + local option_items_group = HorizontalGroup:new{} + local option_items_fixed = false + local option_items = {} + if type(self.options[c].item_font_size) == "table" then + option_items_group.align = "bottom" + option_items_fixed = true + end + -- make current index according to configurable table + local current_item = nil + local function value_diff(val1, val2, name) + if type(val1) ~= type(val2) then + error("different data types in option", name) + end + if type(val1) == "number" then + return math.abs(val1 - val2) + elseif type(val1) == "string" then + return val1 == val2 and 0 or 1 + end + end + if self.options[c].name then + if self.options[c].values then + -- check if current value is stored in configurable or calculated in runtime + local val = self.options[c].current_func and self.options[c].current_func() + or self.config.configurable[self.options[c].name] + local min_diff = nil + if type(val) == "table" then + min_diff = value_diff(val[1], self.options[c].values[1][1]) + else + min_diff = value_diff(val, self.options[c].values[1]) + end + + local diff = nil + for index, val_ in pairs(self.options[c].values) do + local diff = nil + if type(val) == "table" then + diff = value_diff(val[1], val_[1]) + else + diff = value_diff(val, val_) + end + if val == val_ then + current_item = index + break + end + if diff <= min_diff then + min_diff = diff + current_item = index + end + end + elseif self.options[c].args then + -- check if current arg is stored in configurable or calculated in runtime + local arg = self.options[c].current_func and self.options[c].current_func() + or self.config.configurable[self.options[c].name] + for idx, arg_ in pairs(self.options[c].args) do + if arg_ == arg then + current_item = idx + break + end + end + end + end + if self.options[c].item_text then + for d = 1, #self.options[c].item_text do + local option_item = nil + if option_items_fixed then + option_item = OptionTextItem:new{ + FixedTextWidget:new{ + text = self.options[c].item_text[d], + face = Font:getFace(item_font_face, item_font_size[d]), + }, + padding = 3, + color = d == current_item and 15 or 0, + } + else + option_item = OptionTextItem:new{ + TextWidget:new{ + text = self.options[c].item_text[d], + face = Font:getFace(item_font_face, item_font_size), + }, + padding = -3, + color = d == current_item and 15 or 0, + } + end + option_items[d] = option_item + option_item.items = option_items + option_item.name = self.options[c].name + option_item.values = self.options[c].values + option_item.args = self.options[c].args + option_item.event = self.options[c].event + option_item.current_item = d + option_item.config = self.config + table.insert(option_items_group, option_item) + if d ~= #self.options[c].item_text then + table.insert(option_items_group, items_spacing) + end + end + end + + if self.options[c].item_icons then + for d = 1, #self.options[c].item_icons do + local option_item = OptionIconItem:new{ + icon = ImageWidget:new{ + file = self.options[c].item_icons[d] + }, + padding = -2, + color = d == current_item and 15 or 0, + } + option_items[d] = option_item + option_item.items = option_items + option_item.name = self.options[c].name + option_item.values = self.options[c].values + option_item.args = self.options[c].args + option_item.event = self.options[c].event + option_item.current_item = d + option_item.config = self.config + table.insert(option_items_group, option_item) + if d ~= #self.options[c].item_icons then + table.insert(option_items_group, items_spacing) + end + end + end + + if self.options[c].toggle then + local switch = ToggleSwitch:new{ + width = Screen:scaleByDPI(self.options[c].width or 216), + name = self.options[c].name, + toggle = self.options[c].toggle, + alternate = self.options[c].alternate, + values = self.options[c].values, + args = self.options[c].args, + event = self.options[c].event, + events = self.options[c].events, + config = self.config, + } + local position = current_item + switch:setPosition(position) + table.insert(option_items_group, switch) + end + + table.insert(option_items_container, option_items_group) + table.insert(horizontal_group, option_items_container) + table.insert(vertical_group, horizontal_group) + end -- if + end -- for + table.insert(vertical_group, VerticalSpan:new{ width = default_option_padding }) + self[1] = vertical_group + self.dimen = vertical_group:getSize() end local ConfigPanel = FrameContainer:new{ background = 0, bordersize = 0, } function ConfigPanel:init() - local config_options = self.config_dialog.config_options - local default_option = config_options.default_options and config_options.default_options - or config_options[1].options - local panel = ConfigOption:new{ - options = self.index and config_options[self.index].options or default_option, - config = self.config_dialog, - } - self.dimen = panel:getSize() - table.insert(self, panel) + local config_options = self.config_dialog.config_options + local default_option = config_options.default_options and config_options.default_options + or config_options[1].options + local panel = ConfigOption:new{ + options = self.index and config_options[self.index].options or default_option, + config = self.config_dialog, + } + self.dimen = panel:getSize() + table.insert(self, panel) end local MenuBar = FrameContainer:new{ background = 0, } function MenuBar:init() - local config_options = self.config_dialog.config_options - local menu_items = {} - local icons_width = 0 - local icons_height = 0 - for c = 1, #config_options do - local menu_icon = ImageWidget:new{ - file = config_options[c].icon - } - local icon_dimen = menu_icon:getSize() - icons_width = icons_width + icon_dimen.w - icons_height = icon_dimen.h > icons_height and icon_dimen.h or icons_height - - menu_items[c] = MenuBarItem:new{ - menu_icon, - index = c, - config = self.config_dialog, - } - end - - local spacing = HorizontalSpan:new{ - width = (Screen:getWidth() - icons_width) / (#menu_items+1) - } - - local menu_bar = HorizontalGroup:new{} - - for c = 1, #menu_items do - table.insert(menu_bar, spacing) - table.insert(menu_bar, menu_items[c]) - end - table.insert(menu_bar, spacing) - - self.dimen = Geom:new{ w = Screen:getWidth(), h = icons_height} - table.insert(self, menu_bar) + local config_options = self.config_dialog.config_options + local menu_items = {} + local icons_width = 0 + local icons_height = 0 + for c = 1, #config_options do + local menu_icon = ImageWidget:new{ + file = config_options[c].icon + } + local icon_dimen = menu_icon:getSize() + icons_width = icons_width + icon_dimen.w + icons_height = icon_dimen.h > icons_height and icon_dimen.h or icons_height + + menu_items[c] = MenuBarItem:new{ + menu_icon, + index = c, + config = self.config_dialog, + } + end + + local spacing = HorizontalSpan:new{ + width = (Screen:getWidth() - icons_width) / (#menu_items+1) + } + + local menu_bar = HorizontalGroup:new{} + + for c = 1, #menu_items do + table.insert(menu_bar, spacing) + table.insert(menu_bar, menu_items[c]) + end + table.insert(menu_bar, spacing) + + self.dimen = Geom:new{ w = Screen:getWidth(), h = icons_height} + table.insert(self, menu_bar) end --[[ @@ -405,122 +405,122 @@ Widget that displays config menubar and config panel --]] local ConfigDialog = InputContainer:new{ - --is_borderless = false, - panel_index = 1, + --is_borderless = false, + panel_index = 1, } function ConfigDialog:init() - ------------------------------------------ - -- start to set up widget layout --------- - ------------------------------------------ - self.config_menubar = MenuBar:new{ - config_dialog = self, - } - self:update() - ------------------------------------------ - -- start to set up input event callback -- - ------------------------------------------ - if Device:isTouchDevice() then - self.ges_events.TapCloseMenu = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - } - else - -- set up keyboard events - self.key_events.Close = { {"Back"}, doc = _("close config menu") } - -- we won't catch presses to "Right" - self.key_events.FocusRight = nil - end - self.key_events.Select = { {"Press"}, doc = _("select current menu item") } + ------------------------------------------ + -- start to set up widget layout --------- + ------------------------------------------ + self.config_menubar = MenuBar:new{ + config_dialog = self, + } + self:update() + ------------------------------------------ + -- start to set up input event callback -- + ------------------------------------------ + if Device:isTouchDevice() then + self.ges_events.TapCloseMenu = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + else + -- set up keyboard events + self.key_events.Close = { {"Back"}, doc = _("close config menu") } + -- we won't catch presses to "Right" + self.key_events.FocusRight = nil + end + self.key_events.Select = { {"Press"}, doc = _("select current menu item") } end function ConfigDialog:updateConfigPanel(index) - + end function ConfigDialog:update() - self.config_panel = ConfigPanel:new{ - index = self.panel_index, - config_dialog = self, - } - self.dialog_frame = FrameContainer:new{ - background = 0, - VerticalGroup:new{ - self.config_panel, - self.config_menubar, - }, - } - - self[1] = BottomContainer:new{ - dimen = Screen:getSize(), - self.dialog_frame, - } + self.config_panel = ConfigPanel:new{ + index = self.panel_index, + config_dialog = self, + } + self.dialog_frame = FrameContainer:new{ + background = 0, + VerticalGroup:new{ + self.config_panel, + self.config_menubar, + }, + } + + self[1] = BottomContainer:new{ + dimen = Screen:getSize(), + self.dialog_frame, + } end function ConfigDialog:onShowConfigPanel(index) - self.panel_index = index - self:update() - UIManager.repaint_all = true - return true + self.panel_index = index + self:update() + UIManager.repaint_all = true + return true end function ConfigDialog:onConfigChoice(option_name, option_value) - --DEBUG("config option value", option_name, option_value) - self.configurable[option_name] = option_value - self.ui:handleEvent(Event:new("StartActivityIndicator")) - return true + --DEBUG("config option value", option_name, option_value) + self.configurable[option_name] = option_value + self.ui:handleEvent(Event:new("StartActivityIndicator")) + return true end function ConfigDialog:onConfigEvent(option_event, option_arg) - --DEBUG("config option event", option_event, option_arg) - self.ui:handleEvent(Event:new(option_event, option_arg)) - return true + --DEBUG("config option event", option_event, option_arg) + self.ui:handleEvent(Event:new(option_event, option_arg)) + return true end function ConfigDialog:onConfigEvents(option_events, arg_index) - --DEBUG("config option events", option_events, arg_index) - for i=1, #option_events do - option_events[i].args = option_events[i].args or {} - self.ui:handleEvent(Event:new(option_events[i].event, option_events[i].args[arg_index])) - end - return true + --DEBUG("config option events", option_events, arg_index) + for i=1, #option_events do + option_events[i].args = option_events[i].args or {} + self.ui:handleEvent(Event:new(option_events[i].event, option_events[i].args[arg_index])) + end + return true end function ConfigDialog:onConfigChoose(values, name, event, args, events, position) - UIManager:scheduleIn(0.05, function() - if values then - self:onConfigChoice(name, values[position]) - end - if event then - args = args or {} - self:onConfigEvent(event, args[position]) - end - if events then - self:onConfigEvents(events, position) - end - UIManager.repaint_all = true - end) + UIManager:scheduleIn(0.05, function() + if values then + self:onConfigChoice(name, values[position]) + end + if event then + args = args or {} + self:onConfigEvent(event, args[position]) + end + if events then + self:onConfigEvents(events, position) + end + UIManager.repaint_all = true + end) end function ConfigDialog:closeDialog() - UIManager:close(self) - if self.close_callback then - self.close_callback() - end - return true + UIManager:close(self) + if self.close_callback then + self.close_callback() + end + return true end function ConfigDialog:onTapCloseMenu(arg, ges_ev) - if ges_ev.pos:notIntersectWith(self.dialog_frame.dimen) then - self:closeDialog() - return true - end + if ges_ev.pos:notIntersectWith(self.dialog_frame.dimen) then + self:closeDialog() + return true + end end return ConfigDialog diff --git a/frontend/ui/widget/confirmbox.lua b/frontend/ui/widget/confirmbox.lua index efff673d2..86cc44c40 100644 --- a/frontend/ui/widget/confirmbox.lua +++ b/frontend/ui/widget/confirmbox.lua @@ -20,86 +20,86 @@ local _ = require("gettext") Widget that shows a message and OK/Cancel buttons ]] local ConfirmBox = FocusManager:new{ - text = _("no text"), - width = nil, - ok_text = _("OK"), - cancel_text = _("Cancel"), - ok_callback = function() end, - cancel_callback = function() end, + text = _("no text"), + width = nil, + ok_text = _("OK"), + cancel_text = _("Cancel"), + ok_callback = function() end, + cancel_callback = function() end, } function ConfirmBox:init() - -- calculate box width on the fly if not given - if not self.width then - self.width = Screen:getWidth() - 200 - end - -- build bottons - self.key_events.Close = { {{"Home","Back"}}, doc = _("cancel") } - self.key_events.Select = { {{"Enter","Press"}}, doc = _("chose selected option") } + -- calculate box width on the fly if not given + if not self.width then + self.width = Screen:getWidth() - 200 + end + -- build bottons + self.key_events.Close = { {{"Home","Back"}}, doc = _("cancel") } + self.key_events.Select = { {{"Enter","Press"}}, doc = _("chose selected option") } - local ok_button = Button:new{ - text = self.ok_text, - callback = function() - self.ok_callback() - UIManager:close(self) - end, - } - local cancel_button = Button:new{ - text = self.cancel_text, - preselect = true, - callback = function() - self.cancel_callback() - UIManager:close(self) - end, - } + local ok_button = Button:new{ + text = self.ok_text, + callback = function() + self.ok_callback() + UIManager:close(self) + end, + } + local cancel_button = Button:new{ + text = self.cancel_text, + preselect = true, + callback = function() + self.cancel_callback() + UIManager:close(self) + end, + } - self.layout = { { ok_button, cancel_button } } - self.selected.x = 2 -- Cancel is default + self.layout = { { ok_button, cancel_button } } + self.selected.x = 2 -- Cancel is default - self[1] = CenterContainer:new{ - dimen = Screen:getSize(), - FrameContainer:new{ - margin = 2, - background = 0, - padding = 10, - HorizontalGroup:new{ - ImageWidget:new{ - file = "resources/info-i.png" - }, - HorizontalSpan:new{ width = 10 }, - VerticalGroup:new{ - align = "left", - TextBoxWidget:new{ - text = self.text, - face = Font:getFace("cfont", 30), - width = self.width, - }, - VerticalSpan:new{ width = 10 }, - HorizontalGroup:new{ - ok_button, - HorizontalSpan:new{ width = 10 }, - cancel_button, - } - } - } - } - } + self[1] = CenterContainer:new{ + dimen = Screen:getSize(), + FrameContainer:new{ + margin = 2, + background = 0, + padding = 10, + HorizontalGroup:new{ + ImageWidget:new{ + file = "resources/info-i.png" + }, + HorizontalSpan:new{ width = 10 }, + VerticalGroup:new{ + align = "left", + TextBoxWidget:new{ + text = self.text, + face = Font:getFace("cfont", 30), + width = self.width, + }, + VerticalSpan:new{ width = 10 }, + HorizontalGroup:new{ + ok_button, + HorizontalSpan:new{ width = 10 }, + cancel_button, + } + } + } + } + } end function ConfirmBox:onClose() - UIManager:close(self) - return true + UIManager:close(self) + return true end function ConfirmBox:onSelect() - DEBUG("selected:", self.selected.x) - if self.selected.x == 1 then - self:ok_callback() - else - self:cancel_callback() - end - UIManager:close(self) - return true + DEBUG("selected:", self.selected.x) + if self.selected.x == 1 then + self:ok_callback() + else + self:cancel_callback() + end + UIManager:close(self) + return true end return ConfirmBox diff --git a/frontend/ui/widget/container/bottomcontainer.lua b/frontend/ui/widget/container/bottomcontainer.lua index 26f3b9085..123b4be29 100644 --- a/frontend/ui/widget/container/bottomcontainer.lua +++ b/frontend/ui/widget/container/bottomcontainer.lua @@ -8,24 +8,24 @@ dimensions local BottomContainer = WidgetContainer:new() function BottomContainer:paintTo(bb, x, y) - local contentSize = self[1]:getSize() - if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then - -- throw error? paint to scrap buffer and blit partially? - -- for now, we ignore this - end - self[1]:paintTo(bb, - x + math.floor((self.dimen.w - contentSize.w)/2), - y + (self.dimen.h - contentSize.h)) + local contentSize = self[1]:getSize() + if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then + -- throw error? paint to scrap buffer and blit partially? + -- for now, we ignore this + end + self[1]:paintTo(bb, + x + math.floor((self.dimen.w - contentSize.w)/2), + y + (self.dimen.h - contentSize.h)) end function BottomContainer:contentRange() - local contentSize = self[1]:getSize() - return Geom:new{ - x = (self.dimen.x or 0) + math.floor((self.dimen.w - contentSize.w)/2), - y = (self.dimen.y or 0) + self.dimen.h - contentSize.h, - w = contentSize.w, - h = contentSize.h - } + local contentSize = self[1]:getSize() + return Geom:new{ + x = (self.dimen.x or 0) + math.floor((self.dimen.w - contentSize.w)/2), + y = (self.dimen.y or 0) + self.dimen.h - contentSize.h, + w = contentSize.w, + h = contentSize.h + } end return BottomContainer diff --git a/frontend/ui/widget/container/centercontainer.lua b/frontend/ui/widget/container/centercontainer.lua index c3f1dfea2..afa054ea3 100644 --- a/frontend/ui/widget/container/centercontainer.lua +++ b/frontend/ui/widget/container/centercontainer.lua @@ -6,20 +6,20 @@ CenterContainer centers its content (1 widget) within its own dimensions local CenterContainer = WidgetContainer:new() function CenterContainer:paintTo(bb, x, y) - local contentSize = self[1]:getSize() - if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then - -- throw error? paint to scrap buffer and blit partially? - -- for now, we ignore this - end - local x_pos = x - local y_pos = y - if self.ignore ~= "height" then - y_pos = y + math.floor((self.dimen.h - contentSize.h)/2) - end - if self.ignore ~= "width" then - x_pos = x + math.floor((self.dimen.w - contentSize.w)/2) - end - self[1]:paintTo(bb, x_pos, y_pos) + local contentSize = self[1]:getSize() + if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then + -- throw error? paint to scrap buffer and blit partially? + -- for now, we ignore this + end + local x_pos = x + local y_pos = y + if self.ignore ~= "height" then + y_pos = y + math.floor((self.dimen.h - contentSize.h)/2) + end + if self.ignore ~= "width" then + x_pos = x + math.floor((self.dimen.w - contentSize.w)/2) + end + self[1]:paintTo(bb, x_pos, y_pos) end return CenterContainer diff --git a/frontend/ui/widget/container/framecontainer.lua b/frontend/ui/widget/container/framecontainer.lua index b32bb3f6c..44f12ea37 100644 --- a/frontend/ui/widget/container/framecontainer.lua +++ b/frontend/ui/widget/container/framecontainer.lua @@ -6,56 +6,56 @@ A FrameContainer is some graphics content (1 widget) that is surrounded by a frame --]] local FrameContainer = WidgetContainer:new{ - background = nil, - color = 15, - margin = 0, - radius = 0, - bordersize = 2, - padding = 5, - width = nil, - height = nil, - invert = false, + background = nil, + color = 15, + margin = 0, + radius = 0, + bordersize = 2, + padding = 5, + width = nil, + height = nil, + invert = false, } function FrameContainer:getSize() - local content_size = self[1]:getSize() - return Geom:new{ - w = content_size.w + ( self.margin + self.bordersize + self.padding ) * 2, - h = content_size.h + ( self.margin + self.bordersize + self.padding ) * 2 - } + local content_size = self[1]:getSize() + return Geom:new{ + w = content_size.w + ( self.margin + self.bordersize + self.padding ) * 2, + h = content_size.h + ( self.margin + self.bordersize + self.padding ) * 2 + } end function FrameContainer:paintTo(bb, x, y) - local my_size = self:getSize() - self.dimen = Geom:new{ - x = x, y = y, - w = my_size.w, - h = my_size.h - } - local container_width = self.width or my_size.w - local container_height = self.height or my_size.h + local my_size = self:getSize() + self.dimen = Geom:new{ + x = x, y = y, + w = my_size.w, + h = my_size.h + } + local container_width = self.width or my_size.w + local container_height = self.height or my_size.h - --@TODO get rid of margin here? 13.03 2013 (houqp) - if self.background then - bb:paintRoundedRect(x, y, container_width, container_height, - self.background, self.radius) - end - if self.bordersize > 0 then - bb:paintBorder(x + self.margin, y + self.margin, - container_width - self.margin * 2, - container_height - self.margin * 2, - self.bordersize, self.color, self.radius) - end - if self[1] then - self[1]:paintTo(bb, - x + self.margin + self.bordersize + self.padding, - y + self.margin + self.bordersize + self.padding) - end - if self.invert then - bb:invertRect(x + self.bordersize, y + self.bordersize, - container_width - 2*self.bordersize, - container_height - 2*self.bordersize) - end + --@TODO get rid of margin here? 13.03 2013 (houqp) + if self.background then + bb:paintRoundedRect(x, y, container_width, container_height, + self.background, self.radius) + end + if self.bordersize > 0 then + bb:paintBorder(x + self.margin, y + self.margin, + container_width - self.margin * 2, + container_height - self.margin * 2, + self.bordersize, self.color, self.radius) + end + if self[1] then + self[1]:paintTo(bb, + x + self.margin + self.bordersize + self.padding, + y + self.margin + self.bordersize + self.padding) + end + if self.invert then + bb:invertRect(x + self.bordersize, y + self.bordersize, + container_width - 2*self.bordersize, + container_height - 2*self.bordersize) + end end return FrameContainer diff --git a/frontend/ui/widget/container/inputcontainer.lua b/frontend/ui/widget/container/inputcontainer.lua index 0ed9b9812..07ee89094 100644 --- a/frontend/ui/widget/container/inputcontainer.lua +++ b/frontend/ui/widget/container/inputcontainer.lua @@ -8,60 +8,60 @@ an InputContainer is an WidgetContainer that handles input events an example for a key_event is this: - PanBy20 = { - { "Shift", Input.group.Cursor }, - seqtext = "Shift+Cursor", - doc = "pan by 20px", - event = "Pan", args = 20, is_inactive = true, - }, - PanNormal = { - { Input.group.Cursor }, - seqtext = "Cursor", - doc = "pan by 10 px", event = "Pan", args = 10, - }, - Quit = { {"Home"} }, + PanBy20 = { + { "Shift", Input.group.Cursor }, + seqtext = "Shift+Cursor", + doc = "pan by 20px", + event = "Pan", args = 20, is_inactive = true, + }, + PanNormal = { + { Input.group.Cursor }, + seqtext = "Cursor", + doc = "pan by 10 px", event = "Pan", args = 10, + }, + Quit = { {"Home"} }, it is suggested to reference configurable sequences from another table and store that table as configuration setting --]] local InputContainer = WidgetContainer:new{ - vertical_align = "top", + vertical_align = "top", } function InputContainer:_init() - -- we need to do deep copy here - local new_key_events = {} - if self.key_events then - for k,v in pairs(self.key_events) do - new_key_events[k] = v - end - end - self.key_events = new_key_events + -- we need to do deep copy here + local new_key_events = {} + if self.key_events then + for k,v in pairs(self.key_events) do + new_key_events[k] = v + end + end + self.key_events = new_key_events - local new_ges_events = {} - if self.ges_events then - for k,v in pairs(self.ges_events) do - new_ges_events[k] = v - end - end - self.ges_events = new_ges_events + local new_ges_events = {} + if self.ges_events then + for k,v in pairs(self.ges_events) do + new_ges_events[k] = v + end + end + self.ges_events = new_ges_events - if not self.dimen then - self.dimen = Geom:new{} - end + if not self.dimen then + self.dimen = Geom:new{} + end end function InputContainer:paintTo(bb, x, y) - self.dimen.x = x - self.dimen.y = y - if self[1] then - if self.vertical_align == "center" then - local content_size = self[1]:getSize() - self[1]:paintTo(bb, x, y + math.floor((self.dimen.h - content_size.h)/2)) - else - self[1]:paintTo(bb, x, y) - end - end + self.dimen.x = x + self.dimen.y = y + if self[1] then + if self.vertical_align == "center" then + local content_size = self[1]:getSize() + self[1]:paintTo(bb, x, y + math.floor((self.dimen.h - content_size.h)/2)) + else + self[1]:paintTo(bb, x, y) + end + end end --[[ @@ -69,28 +69,28 @@ the following handler handles keypresses and checks if they lead to a command. if this is the case, we retransmit another event within ourselves --]] function InputContainer:onKeyPress(key) - for name, seq in pairs(self.key_events) do - if not seq.is_inactive then - for _, oneseq in ipairs(seq) do - if key:match(oneseq) then - local eventname = seq.event or name - return self:handleEvent(Event:new(eventname, seq.args, key)) - end - end - end - end + for name, seq in pairs(self.key_events) do + if not seq.is_inactive then + for _, oneseq in ipairs(seq) do + if key:match(oneseq) then + local eventname = seq.event or name + return self:handleEvent(Event:new(eventname, seq.args, key)) + end + end + end + end end function InputContainer:onGesture(ev) - for name, gsseq in pairs(self.ges_events) do - for _, gs_range in ipairs(gsseq) do - --DEBUG("gs_range", gs_range) - if gs_range:match(ev) then - local eventname = gsseq.event or name - return self:handleEvent(Event:new(eventname, gsseq.args, ev)) - end - end - end + for name, gsseq in pairs(self.ges_events) do + for _, gs_range in ipairs(gsseq) do + --DEBUG("gs_range", gs_range) + if gs_range:match(ev) then + local eventname = gsseq.event or name + return self:handleEvent(Event:new(eventname, gsseq.args, ev)) + end + end + end end return InputContainer diff --git a/frontend/ui/widget/container/leftcontainer.lua b/frontend/ui/widget/container/leftcontainer.lua index 608004228..227845c09 100644 --- a/frontend/ui/widget/container/leftcontainer.lua +++ b/frontend/ui/widget/container/leftcontainer.lua @@ -6,12 +6,12 @@ LeftContainer aligns its content (1 widget) at the left of its own dimensions local LeftContainer = WidgetContainer:new() function LeftContainer:paintTo(bb, x, y) - local contentSize = self[1]:getSize() - if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then - -- throw error? paint to scrap buffer and blit partially? - -- for now, we ignore this - end - self[1]:paintTo(bb, x , y + math.floor((self.dimen.h - contentSize.h)/2)) + local contentSize = self[1]:getSize() + if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then + -- throw error? paint to scrap buffer and blit partially? + -- for now, we ignore this + end + self[1]:paintTo(bb, x , y + math.floor((self.dimen.h - contentSize.h)/2)) end return LeftContainer diff --git a/frontend/ui/widget/container/rightcontainer.lua b/frontend/ui/widget/container/rightcontainer.lua index eabcd9a55..51b96df1f 100644 --- a/frontend/ui/widget/container/rightcontainer.lua +++ b/frontend/ui/widget/container/rightcontainer.lua @@ -6,14 +6,14 @@ RightContainer aligns its content (1 widget) at the right of its own dimensions local RightContainer = WidgetContainer:new() function RightContainer:paintTo(bb, x, y) - local contentSize = self[1]:getSize() - if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then - -- throw error? paint to scrap buffer and blit partially? - -- for now, we ignore this - end - self[1]:paintTo(bb, - x + (self.dimen.w - contentSize.w), - y + math.floor((self.dimen.h - contentSize.h)/2)) + local contentSize = self[1]:getSize() + if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then + -- throw error? paint to scrap buffer and blit partially? + -- for now, we ignore this + end + self[1]:paintTo(bb, + x + (self.dimen.w - contentSize.w), + y + math.floor((self.dimen.h - contentSize.h)/2)) end return RightContainer diff --git a/frontend/ui/widget/container/underlinecontainer.lua b/frontend/ui/widget/container/underlinecontainer.lua index 6faf6d1a2..6cff80fcf 100644 --- a/frontend/ui/widget/container/underlinecontainer.lua +++ b/frontend/ui/widget/container/underlinecontainer.lua @@ -7,36 +7,36 @@ a line under its child node --]] local UnderlineContainer = WidgetContainer:new{ - linesize = 2, - padding = 1, - color = 0, - vertical_align = "top", + linesize = 2, + padding = 1, + color = 0, + vertical_align = "top", } function UnderlineContainer:getSize() - return self:getContentSize() + return self:getContentSize() end function UnderlineContainer:getContentSize() - local contentSize = self[1]:getSize() - return Geom:new{ - w = contentSize.w, - h = contentSize.h + self.linesize + self.padding - } + local contentSize = self[1]:getSize() + return Geom:new{ + w = contentSize.w, + h = contentSize.h + self.linesize + self.padding + } end function UnderlineContainer:paintTo(bb, x, y) - local container_size = self:getSize() - local content_size = self:getContentSize() - local p_y = y - if self.vertical_align == "center" then - p_y = math.floor((container_size.h - content_size.h) / 2) + y - elseif self.vertical_align == "bottom" then - p_y = (container_size.h - content_size.h) + y - end - self[1]:paintTo(bb, x, p_y) - bb:paintRect(x, y + container_size.h - self.linesize, - container_size.w, self.linesize, self.color) + local container_size = self:getSize() + local content_size = self:getContentSize() + local p_y = y + if self.vertical_align == "center" then + p_y = math.floor((container_size.h - content_size.h) / 2) + y + elseif self.vertical_align == "bottom" then + p_y = (container_size.h - content_size.h) + y + end + self[1]:paintTo(bb, x, p_y) + bb:paintRect(x, y + container_size.h - self.linesize, + container_size.w, self.linesize, self.color) end return UnderlineContainer diff --git a/frontend/ui/widget/container/widgetcontainer.lua b/frontend/ui/widget/container/widgetcontainer.lua index 4ee53cf3d..0ce8a1703 100644 --- a/frontend/ui/widget/container/widgetcontainer.lua +++ b/frontend/ui/widget/container/widgetcontainer.lua @@ -7,83 +7,83 @@ WidgetContainer is a container for another Widget local WidgetContainer = Widget:new() function WidgetContainer:init() - if not self.dimen then - self.dimen = Geom:new{} - end - if not self.dimen.w then - self.dimen.w = self[1].getSize().w - end - if not self.dimen.h then - self.dimen.h = self[1].getSize().h - end + if not self.dimen then + self.dimen = Geom:new{} + end + if not self.dimen.w then + self.dimen.w = self[1].getSize().w + end + if not self.dimen.h then + self.dimen.h = self[1].getSize().h + end end function WidgetContainer:getSize() - if self.dimen then - -- fixed size - return self.dimen - elseif self[1] then - -- return size of first child widget - return self[1]:getSize() - else - return Geom:new{ w = 0, h = 0 } - end + if self.dimen then + -- fixed size + return self.dimen + elseif self[1] then + -- return size of first child widget + return self[1]:getSize() + else + return Geom:new{ w = 0, h = 0 } + end end --[[ delete all child widgets --]] function WidgetContainer:clear() - while table.remove(self) do end + while table.remove(self) do end end function WidgetContainer:paintTo(bb, x, y) - -- default to pass request to first child widget - if self[1] then - x = x + (self.dimen.x or 0) - y = y + (self.dimen.y or 0) - if self.align == "top" then - local contentSize = self[1]:getSize() - self[1]:paintTo(bb, - x + math.floor((self.dimen.w - contentSize.w)/2), y) - elseif self.align == "bottom" then - local contentSize = self[1]:getSize() - self[1]:paintTo(bb, - x + math.floor((self.dimen.w - contentSize.w)/2), - y + (self.dimen.h - contentSize.h)) - else - return self[1]:paintTo(bb, x, y) - end - end + -- default to pass request to first child widget + if self[1] then + x = x + (self.dimen.x or 0) + y = y + (self.dimen.y or 0) + if self.align == "top" then + local contentSize = self[1]:getSize() + self[1]:paintTo(bb, + x + math.floor((self.dimen.w - contentSize.w)/2), y) + elseif self.align == "bottom" then + local contentSize = self[1]:getSize() + self[1]:paintTo(bb, + x + math.floor((self.dimen.w - contentSize.w)/2), + y + (self.dimen.h - contentSize.h)) + else + return self[1]:paintTo(bb, x, y) + end + end end function WidgetContainer:propagateEvent(event) - -- propagate to children - for _, widget in ipairs(self) do - if widget:handleEvent(event) then - -- stop propagating when an event handler returns true - return true - end - end - return false + -- propagate to children + for _, widget in ipairs(self) do + if widget:handleEvent(event) then + -- stop propagating when an event handler returns true + return true + end + end + return false end --[[ Containers will pass events to children or react on them themselves --]] function WidgetContainer:handleEvent(event) - if not self:propagateEvent(event) then - -- call our own standard event handler - return Widget.handleEvent(self, event) - else - return true - end + if not self:propagateEvent(event) then + -- call our own standard event handler + return Widget.handleEvent(self, event) + else + return true + end end function WidgetContainer:free() - for _, widget in ipairs(self) do - if widget.free then widget:free() end - end + for _, widget in ipairs(self) do + if widget.free then widget:free() end + end end return WidgetContainer diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index f6d115de4..b5688adff 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -24,280 +24,280 @@ local _ = require("gettext") Display quick lookup word definition ]] local DictQuickLookup = InputContainer:new{ - results = nil, - lookupword = nil, - dictionary = nil, - definition = nil, - 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, - - title_padding = Screen:scaleByDPI(5), - title_margin = Screen:scaleByDPI(2), - word_padding = Screen:scaleByDPI(2), - word_margin = Screen:scaleByDPI(2), - definition_padding = Screen:scaleByDPI(2), - definition_margin = Screen:scaleByDPI(2), - button_padding = Screen:scaleByDPI(14), + results = nil, + lookupword = nil, + dictionary = nil, + definition = nil, + 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, + + title_padding = Screen:scaleByDPI(5), + title_margin = Screen:scaleByDPI(2), + word_padding = Screen:scaleByDPI(2), + word_margin = Screen:scaleByDPI(2), + definition_padding = Screen:scaleByDPI(2), + definition_margin = Screen:scaleByDPI(2), + button_padding = Screen:scaleByDPI(14), } function DictQuickLookup:init() - self:changeToDefaultDict() - if Device:isTouchDevice() then - self.ges_events = { - TapCloseDict = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - }, - }, - Swipe = { - GestureRange:new{ - ges = "swipe", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - }, - }, - } - table.insert(self.dict_bar, - CloseButton:new{ - window = self, - }) - end + self:changeToDefaultDict() + if Device:isTouchDevice() then + self.ges_events = { + TapCloseDict = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + }, + }, + Swipe = { + GestureRange:new{ + ges = "swipe", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + }, + }, + } + table.insert(self.dict_bar, + CloseButton:new{ + window = self, + }) + end end function DictQuickLookup:update() - -- dictionary title - 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 - self.button_padding, - } - } - -- lookup word - local lookup_word = FrameContainer:new{ - padding = self.word_padding, - margin = self.word_margin, - bordersize = 0, - TextBoxWidget:new{ - text = self.lookupword, - face = self.word_face, - bold = true, - width = self.width, - }, - } - -- word definition - local definition = FrameContainer:new{ - padding = self.definition_padding, - margin = self.definition_margin, - bordersize = 0, - ScrollTextWidget:new{ - text = self.definition, - face = self.content_face, - width = self.width, - height = 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, - buttons = { - { - { - text = _("<<"), - enabled = self:isPrevDictAvaiable(), - callback = function() - self:changeToPrevDict() - end, - }, - { - text = _(">>"), - enabled = self:isNextDictAvaiable(), - callback = function() - self:changeToNextDict() - end, - }, - }, - { - { - text = _("Highlight"), - enabled = false, - callback = function() - self.ui:handleEvent(Event:new("Highlight")) - end, - }, - { - text = _("Add Note"), - enabled = false, - callback = function() - self.ui:handleEvent(Event:new("AddNote")) - end, - }, - }, - }, - zero_sep = true, - } - local title_bar = LineWidget:new{ - --background = 8, - dimen = Geom:new{ - w = button_table:getSize().w + self.button_padding, - h = Screen:scaleByDPI(2), - } - } - - self.dict_bar = OverlapGroup:new{ - dimen = {w = button_table:getSize().w, h = self.dict_title:getSize().h}, - self.dict_title, - } - - self.dict_frame = FrameContainer:new{ - radius = 8, - bordersize = 3, - padding = 0, - margin = 0, - background = 0, - VerticalGroup:new{ - align = "left", - self.dict_bar, - title_bar, - -- word - CenterContainer:new{ - dimen = Geom:new{ - w = title_bar:getSize().w, - h = lookup_word:getSize().h, - }, - lookup_word, - }, - -- definition - CenterContainer:new{ - dimen = Geom:new{ - w = title_bar:getSize().w, - h = definition:getSize().h, - }, - definition, - }, - -- buttons - CenterContainer:new{ - dimen = Geom:new{ - w = title_bar:getSize().w, - h = button_table:getSize().h, - }, - button_table, - } - } - } - self[1] = WidgetContainer:new{ - align = self.align, - dimen = self.region:copy(), - FrameContainer:new{ - bordersize = 0, - padding = Screen:scaleByDPI(5), - self.dict_frame, - } - } - UIManager.repaint_all = true - UIManager.full_refresh = true + -- dictionary title + 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 - self.button_padding, + } + } + -- lookup word + local lookup_word = FrameContainer:new{ + padding = self.word_padding, + margin = self.word_margin, + bordersize = 0, + TextBoxWidget:new{ + text = self.lookupword, + face = self.word_face, + bold = true, + width = self.width, + }, + } + -- word definition + local definition = FrameContainer:new{ + padding = self.definition_padding, + margin = self.definition_margin, + bordersize = 0, + ScrollTextWidget:new{ + text = self.definition, + face = self.content_face, + width = self.width, + height = 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, + buttons = { + { + { + text = _("<<"), + enabled = self:isPrevDictAvaiable(), + callback = function() + self:changeToPrevDict() + end, + }, + { + text = _(">>"), + enabled = self:isNextDictAvaiable(), + callback = function() + self:changeToNextDict() + end, + }, + }, + { + { + text = _("Highlight"), + enabled = false, + callback = function() + self.ui:handleEvent(Event:new("Highlight")) + end, + }, + { + text = _("Add Note"), + enabled = false, + callback = function() + self.ui:handleEvent(Event:new("AddNote")) + end, + }, + }, + }, + zero_sep = true, + } + local title_bar = LineWidget:new{ + --background = 8, + dimen = Geom:new{ + w = button_table:getSize().w + self.button_padding, + h = Screen:scaleByDPI(2), + } + } + + self.dict_bar = OverlapGroup:new{ + dimen = {w = button_table:getSize().w, h = self.dict_title:getSize().h}, + self.dict_title, + } + + self.dict_frame = FrameContainer:new{ + radius = 8, + bordersize = 3, + padding = 0, + margin = 0, + background = 0, + VerticalGroup:new{ + align = "left", + self.dict_bar, + title_bar, + -- word + CenterContainer:new{ + dimen = Geom:new{ + w = title_bar:getSize().w, + h = lookup_word:getSize().h, + }, + lookup_word, + }, + -- definition + CenterContainer:new{ + dimen = Geom:new{ + w = title_bar:getSize().w, + h = definition:getSize().h, + }, + definition, + }, + -- buttons + CenterContainer:new{ + dimen = Geom:new{ + w = title_bar:getSize().w, + h = button_table:getSize().h, + }, + button_table, + } + } + } + self[1] = WidgetContainer:new{ + align = self.align, + dimen = self.region:copy(), + FrameContainer:new{ + bordersize = 0, + padding = Screen:scaleByDPI(5), + self.dict_frame, + } + } + UIManager.repaint_all = true + UIManager.full_refresh = true end function DictQuickLookup:isPrevDictAvaiable() - return self.dict_index > 1 + return self.dict_index > 1 end function DictQuickLookup:isNextDictAvaiable() - return self.dict_index < #self.results + return self.dict_index < #self.results end function DictQuickLookup:changeToPrevDict() - self:changeDictionary(self.dict_index - 1) + self:changeDictionary(self.dict_index - 1) end function DictQuickLookup:changeToNextDict() - self:changeDictionary(self.dict_index + 1) + self:changeDictionary(self.dict_index + 1) end function DictQuickLookup:changeDictionary(index) - self.dict_index = index - self.dictionary = self.results[index].dict - self.lookupword = self.results[index].word - self.definition = self.results[index].definition - - local orig_dimen = self.dict_frame and self.dict_frame.dimen or Geom:new{} - self:update() + self.dict_index = index + self.dictionary = self.results[index].dict + self.lookupword = self.results[index].word + self.definition = self.results[index].definition + + local orig_dimen = self.dict_frame and self.dict_frame.dimen or Geom:new{} + self:update() - UIManager.update_region_func = function() - local update_region = self.dict_frame.dimen:combine(orig_dimen) - DEBUG("update region", update_region) - return update_region - end + UIManager.update_region_func = function() + local update_region = self.dict_frame.dimen:combine(orig_dimen) + DEBUG("update region", update_region) + return update_region + end end -function DictQuickLookup:changeToDefaultDict() - if self.dictionary then - -- dictionaries that have definition of the first word(accurate word) - -- excluding Fuzzy queries. - local n_accurate_dicts = nil - local default_word = self.results[1].word - for i=1, #self.results do - if self.results[i].word == default_word then - n_accurate_dicts = i - else - break - end - end - -- change to dictionary specified by self.dictionary - for i=1, n_accurate_dicts do - if self.results[i].dict == self.dictionary then - self:changeDictionary(i) - break - end - -- cannot find definition in default dictionary - if i == n_accurate_dicts then - self:changeDictionary(1) - end - end - else - self:changeDictionary(1) - end +function DictQuickLookup:changeToDefaultDict() + if self.dictionary then + -- dictionaries that have definition of the first word(accurate word) + -- excluding Fuzzy queries. + local n_accurate_dicts = nil + local default_word = self.results[1].word + for i=1, #self.results do + if self.results[i].word == default_word then + n_accurate_dicts = i + else + break + end + end + -- change to dictionary specified by self.dictionary + for i=1, n_accurate_dicts do + if self.results[i].dict == self.dictionary then + self:changeDictionary(i) + break + end + -- cannot find definition in default dictionary + if i == n_accurate_dicts then + self:changeDictionary(1) + end + end + else + self:changeDictionary(1) + end end function DictQuickLookup:onAnyKeyPressed() - -- triggered by our defined key events - UIManager:close(self) - return true + -- triggered by our defined key events + UIManager:close(self) + return true end 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 - self.ui:handleEvent(Event:new("UpdateDefaultDict", self.dictionary)) - return true - end - return true + 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 + self.ui:handleEvent(Event:new("UpdateDefaultDict", self.dictionary)) + return true + end + return true end function DictQuickLookup:onClose() - UIManager:close(self) - self.highlight:handleEvent(Event:new("Tap")) - return true + UIManager:close(self) + self.highlight:handleEvent(Event:new("Tap")) + return true end return DictQuickLookup diff --git a/frontend/ui/widget/eventlistener.lua b/frontend/ui/widget/eventlistener.lua index 9e4304b0e..c4e1d1522 100644 --- a/frontend/ui/widget/eventlistener.lua +++ b/frontend/ui/widget/eventlistener.lua @@ -8,17 +8,17 @@ will call a method "onEventName" for an event with name local EventListener = {} function EventListener:new(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - if o.init then o:init() end - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + if o.init then o:init() end + return o end function EventListener:handleEvent(event) - if self[event.handler] then - return self[event.handler](self, unpack(event.args)) - end + if self[event.handler] then + return self[event.handler](self, unpack(event.args)) + end end return EventListener diff --git a/frontend/ui/widget/filechooser.lua b/frontend/ui/widget/filechooser.lua index eac2c8cb5..2d0d1bff3 100644 --- a/frontend/ui/widget/filechooser.lua +++ b/frontend/ui/widget/filechooser.lua @@ -5,97 +5,97 @@ local DEBUG = require("dbg") -- lfs local FileChooser = Menu:extend{ - height = Screen:getHeight(), - width = Screen:getWidth(), - no_title = true, - path = lfs.currentdir(), - parent = nil, - show_hidden = nil, - show_filesize = DSHOWFILESIZE, - filter = function(filename) return true end, + height = Screen:getHeight(), + width = Screen:getWidth(), + no_title = true, + path = lfs.currentdir(), + parent = nil, + show_hidden = nil, + show_filesize = DSHOWFILESIZE, + filter = function(filename) return true end, } function FileChooser:init() - self.item_table = self:genItemTableFromPath(self.path) - Menu.init(self) -- call parent's init() + self.item_table = self:genItemTableFromPath(self.path) + Menu.init(self) -- call parent's init() end function FileChooser:genItemTableFromPath(path) - local dirs = {} - local files = {} + local dirs = {} + local files = {} - for f in lfs.dir(self.path) do - if self.show_hidden or not string.match(f, "^%.[^.]") then - local filename = self.path.."/"..f - local filemode = lfs.attributes(filename, "mode") - if filemode == "directory" and f ~= "." and f~=".." then - if self.dir_filter(filename) then - table.insert(dirs, f) - end - elseif filemode == "file" then - if self.file_filter(filename) then - table.insert(files, f) - end - end - end - end - table.sort(dirs) - if path ~= "/" then table.insert(dirs, 1, "..") end - table.sort(files) + for f in lfs.dir(self.path) do + if self.show_hidden or not string.match(f, "^%.[^.]") then + local filename = self.path.."/"..f + local filemode = lfs.attributes(filename, "mode") + if filemode == "directory" and f ~= "." and f~=".." then + if self.dir_filter(filename) then + table.insert(dirs, f) + end + elseif filemode == "file" then + if self.file_filter(filename) then + table.insert(files, f) + end + end + end + end + table.sort(dirs) + if path ~= "/" then table.insert(dirs, 1, "..") end + table.sort(files) - local item_table = {} - for _, dir in ipairs(dirs) do - table.insert(item_table, { text = dir.."/", path = self.path.."/"..dir }) - end - for _, file in ipairs(files) do - local full_path = self.path.."/"..file - if self.show_filesize then - local sstr = string.format("%4.1fM",lfs.attributes(full_path, "size")/1024/1024) - table.insert(item_table, { text = file, mandatory = sstr, path = full_path }) - else - table.insert(item_table, { text = file, path = full_path }) - end - end + local item_table = {} + for _, dir in ipairs(dirs) do + table.insert(item_table, { text = dir.."/", path = self.path.."/"..dir }) + end + for _, file in ipairs(files) do + local full_path = self.path.."/"..file + if self.show_filesize then + local sstr = string.format("%4.1fM",lfs.attributes(full_path, "size")/1024/1024) + table.insert(item_table, { text = file, mandatory = sstr, path = full_path }) + else + table.insert(item_table, { text = file, path = full_path }) + end + end - return item_table + return item_table end function FileChooser:changeToPath(path) - path = util.realpath(path) - self.path = path - self:refreshPath() + path = util.realpath(path) + self.path = path + self:refreshPath() end function FileChooser:refreshPath() - self:swithItemTable(nil, self:genItemTableFromPath(self.path)) + self:swithItemTable(nil, self:genItemTableFromPath(self.path)) end function FileChooser:toggleHiddenFiles() - self.show_hidden = not self.show_hidden - self:swithItemTable(nil, self:genItemTableFromPath(self.path)) + self.show_hidden = not self.show_hidden + self:swithItemTable(nil, self:genItemTableFromPath(self.path)) end function FileChooser:onMenuSelect(item) - if lfs.attributes(item.path, "mode") == "directory" then - self:changeToPath(item.path) - else - self:onFileSelect(item.path) - end - return true + if lfs.attributes(item.path, "mode") == "directory" then + self:changeToPath(item.path) + else + self:onFileSelect(item.path) + end + return true end function FileChooser:onMenuHold(item) - self:onFileHold(item.path) - return true + self:onFileHold(item.path) + return true end function FileChooser:onFileSelect(file) - UIManager:close(self) - return true + UIManager:close(self) + return true end function FileChooser:onFileHold(file) - return true + return true end return FileChooser diff --git a/frontend/ui/widget/fixedtextwidget.lua b/frontend/ui/widget/fixedtextwidget.lua index 925dfe0b1..668dd789e 100644 --- a/frontend/ui/widget/fixedtextwidget.lua +++ b/frontend/ui/widget/fixedtextwidget.lua @@ -9,21 +9,21 @@ FixedTextWidget local FixedTextWidget = TextWidget:new{} function FixedTextWidget:getSize() - local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold) - if not tsize then - return Geom:new{} - end - self._length = tsize.x - self._height = self.face.size - return Geom:new{ - w = self._length, - h = self._height, - } + local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold) + if not tsize then + return Geom:new{} + end + self._length = tsize.x + self._height = self.face.size + return Geom:new{ + w = self._length, + h = self._height, + } end function FixedTextWidget:paintTo(bb, x, y) - RenderText:renderUtf8Text(bb, x, y+self._height, self.face, self.text, true, self.bold, - self.bgcolor, self.fgcolor) + RenderText:renderUtf8Text(bb, x, y+self._height, self.face, self.text, true, self.bold, + self.bgcolor, self.fgcolor) end return FixedTextWidget diff --git a/frontend/ui/widget/focusmanager.lua b/frontend/ui/widget/focusmanager.lua index 8de7d2532..79c0571c1 100644 --- a/frontend/ui/widget/focusmanager.lua +++ b/frontend/ui/widget/focusmanager.lua @@ -8,10 +8,10 @@ Wrapper Widget that manages focus for a whole dialog supports a 2D model of active elements e.g.: - layout = { - { textinput, textinput }, - { okbutton, cancelbutton } - } + layout = { + { textinput, textinput }, + { okbutton, cancelbutton } + } this is a dialog with 2 rows. in the top row, there is the single (!) widget . when the focus is in this @@ -26,76 +26,76 @@ but notice that this does _not_ do the layout for you, it rather defines an abstract layout. ]] local FocusManager = InputContainer:new{ - selected = nil, -- defaults to x=1, y=1 - layout = nil, -- mandatory - movement_allowed = { x = true, y = true } + selected = nil, -- defaults to x=1, y=1 + layout = nil, -- mandatory + movement_allowed = { x = true, y = true } } function FocusManager:init() - self.selected = { x = 1, y = 1 } - self.key_events = { - -- these will all generate the same event, just with different arguments - FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} }, - FocusDown = { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} }, - FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} }, - FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} }, - } + self.selected = { x = 1, y = 1 } + self.key_events = { + -- these will all generate the same event, just with different arguments + FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} }, + FocusDown = { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} }, + FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} }, + FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} }, + } end function FocusManager:onFocusMove(args) - local dx, dy = unpack(args) - - if (dx ~= 0 and not self.movement_allowed.x) - or (dy ~= 0 and not self.movement_allowed.y) then - return true - end - - if not self.layout or not self.layout[self.selected.y] or not self.layout[self.selected.y][self.selected.x] then - return true - end - local current_item = self.layout[self.selected.y][self.selected.x] - while true do - if self.selected.x + dx > #self.layout[self.selected.y] - or self.selected.x + dx < 1 then - break -- abort when we run into horizontal borders - end - - -- move cyclic in vertical direction - if self.selected.y + dy > #self.layout then - if not self:onWrapLast() then - break - end - elseif self.selected.y + dy < 1 then - if not self:onWrapFirst() then - break - end - else - self.selected.y = self.selected.y + dy - end - self.selected.x = self.selected.x + dx - - if self.layout[self.selected.y][self.selected.x] ~= current_item - or not self.layout[self.selected.y][self.selected.x].is_inactive then - -- we found a different object to focus - current_item:handleEvent(Event:new("Unfocus")) - self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus")) - -- trigger a repaint (we need to be the registered widget!) - UIManager:setDirty(self, "partial") - break - end - end - - return true + local dx, dy = unpack(args) + + if (dx ~= 0 and not self.movement_allowed.x) + or (dy ~= 0 and not self.movement_allowed.y) then + return true + end + + if not self.layout or not self.layout[self.selected.y] or not self.layout[self.selected.y][self.selected.x] then + return true + end + local current_item = self.layout[self.selected.y][self.selected.x] + while true do + if self.selected.x + dx > #self.layout[self.selected.y] + or self.selected.x + dx < 1 then + break -- abort when we run into horizontal borders + end + + -- move cyclic in vertical direction + if self.selected.y + dy > #self.layout then + if not self:onWrapLast() then + break + end + elseif self.selected.y + dy < 1 then + if not self:onWrapFirst() then + break + end + else + self.selected.y = self.selected.y + dy + end + self.selected.x = self.selected.x + dx + + if self.layout[self.selected.y][self.selected.x] ~= current_item + or not self.layout[self.selected.y][self.selected.x].is_inactive then + -- we found a different object to focus + current_item:handleEvent(Event:new("Unfocus")) + self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus")) + -- trigger a repaint (we need to be the registered widget!) + UIManager:setDirty(self, "partial") + break + end + end + + return true end function FocusManager:onWrapFirst() - self.selected.y = #self.layout - return true + self.selected.y = #self.layout + return true end function FocusManager:onWrapLast() - self.selected.y = 1 - return true + self.selected.y = 1 + return true end return FocusManager diff --git a/frontend/ui/widget/horizontalgroup.lua b/frontend/ui/widget/horizontalgroup.lua index 7309fd52d..66d80f910 100644 --- a/frontend/ui/widget/horizontalgroup.lua +++ b/frontend/ui/widget/horizontalgroup.lua @@ -4,58 +4,58 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer") A Layout widget that puts objects besides each others --]] local HorizontalGroup = WidgetContainer:new{ - align = "center", - _size = nil, + align = "center", + _size = nil, } function HorizontalGroup:getSize() - if not self._size then - self._size = { w = 0, h = 0 } - self._offsets = { } - for i, widget in ipairs(self) do - local w_size = widget:getSize() - self._offsets[i] = { - x = self._size.w, - y = w_size.h - } - self._size.w = self._size.w + w_size.w - if w_size.h > self._size.h then - self._size.h = w_size.h - end - end - end - return self._size + if not self._size then + self._size = { w = 0, h = 0 } + self._offsets = { } + for i, widget in ipairs(self) do + local w_size = widget:getSize() + self._offsets[i] = { + x = self._size.w, + y = w_size.h + } + self._size.w = self._size.w + w_size.w + if w_size.h > self._size.h then + self._size.h = w_size.h + end + end + end + return self._size end function HorizontalGroup:paintTo(bb, x, y) - local size = self:getSize() - - for i, widget in ipairs(self) do - if self.align == "center" then - widget:paintTo(bb, - x + self._offsets[i].x, - y + math.floor((size.h - self._offsets[i].y) / 2)) - elseif self.align == "top" then - widget:paintTo(bb, x + self._offsets[i].x, y) - elseif self.align == "bottom" then - widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y) - end - end + local size = self:getSize() + + for i, widget in ipairs(self) do + if self.align == "center" then + widget:paintTo(bb, + x + self._offsets[i].x, + y + math.floor((size.h - self._offsets[i].y) / 2)) + elseif self.align == "top" then + widget:paintTo(bb, x + self._offsets[i].x, y) + elseif self.align == "bottom" then + widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y) + end + end end function HorizontalGroup:clear() - self:free() - WidgetContainer.clear(self) + self:free() + WidgetContainer.clear(self) end function HorizontalGroup:resetLayout() - self._size = nil - self._offsets = {} + self._size = nil + self._offsets = {} end function HorizontalGroup:free() - self:resetLayout() - WidgetContainer.free(self) + self:resetLayout() + WidgetContainer.free(self) end return HorizontalGroup diff --git a/frontend/ui/widget/horizontalspan.lua b/frontend/ui/widget/horizontalspan.lua index f36bad312..2bb5cdd65 100644 --- a/frontend/ui/widget/horizontalspan.lua +++ b/frontend/ui/widget/horizontalspan.lua @@ -4,11 +4,11 @@ local Widget = require("ui/widget/widget") Dummy Widget that reserves horizontal space --]] local HorizontalSpan = Widget:new{ - width = 0, + width = 0, } function HorizontalSpan:getSize() - return {w = self.width, h = 0} + return {w = self.width, h = 0} end return HorizontalSpan diff --git a/frontend/ui/widget/iconbutton.lua b/frontend/ui/widget/iconbutton.lua index 155dda63d..33e0d167b 100644 --- a/frontend/ui/widget/iconbutton.lua +++ b/frontend/ui/widget/iconbutton.lua @@ -7,51 +7,51 @@ local UIManager = require("ui/uimanager") Button with a big icon image! Designed for touch device --]] local IconButton = InputContainer:new{ - icon_file = "resources/info-confirm.png", - dimen = nil, - -- show_parent is used for UIManager:setDirty, so we can trigger repaint - show_parent = nil, - callback = function() end, + icon_file = "resources/info-confirm.png", + dimen = nil, + -- show_parent is used for UIManager:setDirty, so we can trigger repaint + show_parent = nil, + callback = function() end, } function IconButton:init() - self.image = ImageWidget:new{ - file = self.icon_file - } + self.image = ImageWidget:new{ + file = self.icon_file + } - self.show_parent = self.show_parent or self - self.dimen = self.image:getSize() + self.show_parent = self.show_parent or self + self.dimen = self.image:getSize() - self:initGesListener() + self:initGesListener() - self[1] = self.image + self[1] = self.image end function IconButton:initGesListener() - self.ges_events = { - TapClickButton = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - } - }, - } + self.ges_events = { + TapClickButton = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + } + }, + } end function IconButton:onTapClickButton() - self.image.invert = true - UIManager:setDirty(self.show_parent, "partial") - -- make sure button reacts before doing callback - UIManager:scheduleIn(0.1, function() - self.callback() - self.image.invert = false - UIManager:setDirty(self.show_parent, "partial") - end) - return true + self.image.invert = true + UIManager:setDirty(self.show_parent, "partial") + -- make sure button reacts before doing callback + UIManager:scheduleIn(0.1, function() + self.callback() + self.image.invert = false + UIManager:setDirty(self.show_parent, "partial") + end) + return true end function IconButton:onSetDimensions(new_dimen) - self.dimen = new_dimen + self.dimen = new_dimen end return IconButton diff --git a/frontend/ui/widget/imagewidget.lua b/frontend/ui/widget/imagewidget.lua index 1aa3c0d82..5f42aa28d 100644 --- a/frontend/ui/widget/imagewidget.lua +++ b/frontend/ui/widget/imagewidget.lua @@ -6,51 +6,51 @@ local Geom = require("ui/geometry") ImageWidget shows an image from a file --]] local ImageWidget = Widget:new{ - file = nil, - invert = nil, - dim = nil, - hide = nil, - _bb = nil + file = nil, + invert = nil, + dim = nil, + hide = nil, + _bb = nil } function ImageWidget:_render() - local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "") - if itype == "jpeg" or itype == "jpg" then - self._bb = Image:fromJPEG(self.file) - elseif itype == "png" then - self._bb = Image:fromPNG(self.file) - end + local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "") + if itype == "jpeg" or itype == "jpg" then + self._bb = Image:fromJPEG(self.file) + elseif itype == "png" then + self._bb = Image:fromPNG(self.file) + end end function ImageWidget:getSize() - if not self._bb then - self:_render() - end - return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() } + if not self._bb then + self:_render() + end + return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() } end function ImageWidget:paintTo(bb, x, y) - local size = self:getSize() - self.dimen = Geom:new{ - x = x, y = y, - w = size.w, - h = size.h - } - if self.hide then return end - bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h) - if self.invert then - bb:invertRect(x, y, size.w, size.h) - end - if self.dim then - bb:dimRect(x, y, size.w, size.h) - end + local size = self:getSize() + self.dimen = Geom:new{ + x = x, y = y, + w = size.w, + h = size.h + } + if self.hide then return end + bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h) + if self.invert then + bb:invertRect(x, y, size.w, size.h) + end + if self.dim then + bb:dimRect(x, y, size.w, size.h) + end end function ImageWidget:free() - if self._bb then - self._bb:free() - self._bb = nil - end + if self._bb then + self._bb:free() + self._bb = nil + end end return ImageWidget diff --git a/frontend/ui/widget/infomessage.lua b/frontend/ui/widget/infomessage.lua index c65c31e72..5f61d962d 100644 --- a/frontend/ui/widget/infomessage.lua +++ b/frontend/ui/widget/infomessage.lua @@ -20,68 +20,68 @@ Widget that displays an informational message it vanishes on key press or after a given timeout ]] local InfoMessage = InputContainer:new{ - face = Font:getFace("infofont", 25), - text = "", - timeout = nil, -- in seconds + face = Font:getFace("infofont", 25), + text = "", + timeout = nil, -- in seconds } function InfoMessage:init() - if Device:hasKeyboard() then - self.key_events = { - AnyKeyPressed = { { Input.group.Any }, - seqtext = "any key", doc = _("close dialog") } - } - end - if Device:isTouchDevice() then - self.ges_events.TapClose = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - } - end - -- we construct the actual content here because self.text is only available now - self[1] = CenterContainer:new{ - dimen = Screen:getSize(), - FrameContainer:new{ - margin = 2, - background = 0, - HorizontalGroup:new{ - align = "center", - ImageWidget:new{ - file = "resources/info-i.png" - }, - HorizontalSpan:new{ width = 10 }, - TextBoxWidget:new{ - text = self.text, - face = self.face - } - } - } - } + if Device:hasKeyboard() then + self.key_events = { + AnyKeyPressed = { { Input.group.Any }, + seqtext = "any key", doc = _("close dialog") } + } + end + if Device:isTouchDevice() then + self.ges_events.TapClose = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + end + -- we construct the actual content here because self.text is only available now + self[1] = CenterContainer:new{ + dimen = Screen:getSize(), + FrameContainer:new{ + margin = 2, + background = 0, + HorizontalGroup:new{ + align = "center", + ImageWidget:new{ + file = "resources/info-i.png" + }, + HorizontalSpan:new{ width = 10 }, + TextBoxWidget:new{ + text = self.text, + face = self.face + } + } + } + } end function InfoMessage:onShow() - -- triggered by the UIManager after we got successfully shown (not yet painted) - if self.timeout then - UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end) - end - return true + -- triggered by the UIManager after we got successfully shown (not yet painted) + if self.timeout then + UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end) + end + return true end function InfoMessage:onAnyKeyPressed() - -- triggered by our defined key events - UIManager:close(self) - return true + -- triggered by our defined key events + UIManager:close(self) + return true end function InfoMessage:onTapClose() - UIManager:close(self) - return true + UIManager:close(self) + return true end return InfoMessage diff --git a/frontend/ui/widget/inputdialog.lua b/frontend/ui/widget/inputdialog.lua index b564dff07..9dd211deb 100644 --- a/frontend/ui/widget/inputdialog.lua +++ b/frontend/ui/widget/inputdialog.lua @@ -12,112 +12,112 @@ local UIManager = require("ui/uimanager") local Screen = require("ui/screen") local InputDialog = InputContainer:new{ - title = "", - input = "", - input_hint = "", - buttons = nil, - input_type = nil, - enter_callback = nil, - - width = nil, - height = nil, - - title_face = Font:getFace("tfont", 22), - input_face = Font:getFace("cfont", 20), - - title_padding = Screen:scaleByDPI(5), - title_margin = Screen:scaleByDPI(2), - input_padding = Screen:scaleByDPI(10), - input_margin = Screen:scaleByDPI(10), - button_padding = Screen:scaleByDPI(14), + title = "", + input = "", + input_hint = "", + buttons = nil, + input_type = nil, + enter_callback = nil, + + width = nil, + height = nil, + + title_face = Font:getFace("tfont", 22), + input_face = Font:getFace("cfont", 20), + + title_padding = Screen:scaleByDPI(5), + title_margin = Screen:scaleByDPI(2), + input_padding = Screen:scaleByDPI(10), + input_margin = Screen:scaleByDPI(10), + button_padding = Screen:scaleByDPI(14), } function InputDialog:init() - self.title = FrameContainer:new{ - padding = self.title_padding, - margin = self.title_margin, - bordersize = 0, - TextWidget:new{ - text = self.title, - face = self.title_face, - width = self.width, - } - } - self.input = InputText:new{ - text = self.input, - hint = self.input_hint, - face = self.input_face, - width = self.width * 0.9, - input_type = self.input_type, - enter_callback = self.enter_callback, - scroll = false, - parent = self, - } - local button_table = ButtonTable:new{ - width = self.width, - button_font_face = "cfont", - button_font_size = 20, - buttons = self.buttons, - zero_sep = true, - } - local title_bar = LineWidget:new{ - --background = 8, - dimen = Geom:new{ - w = button_table:getSize().w + self.button_padding, - h = Screen:scaleByDPI(2), - } - } - - self.dialog_frame = FrameContainer:new{ - radius = 8, - bordersize = 3, - padding = 0, - margin = 0, - background = 0, - VerticalGroup:new{ - align = "left", - self.title, - title_bar, - -- input - CenterContainer:new{ - dimen = Geom:new{ - w = title_bar:getSize().w, - h = self.input:getSize().h, - }, - self.input, - }, - -- buttons - CenterContainer:new{ - dimen = Geom:new{ - w = title_bar:getSize().w, - h = button_table:getSize().h, - }, - button_table, - } - } - } - - self[1] = CenterContainer:new{ - dimen = Geom:new{ - w = Screen:getWidth(), - h = Screen:getHeight() - self.input:getKeyboardDimen().h, - }, - self.dialog_frame, - } - UIManager.repaint_all = true - UIManager.full_refresh = true + self.title = FrameContainer:new{ + padding = self.title_padding, + margin = self.title_margin, + bordersize = 0, + TextWidget:new{ + text = self.title, + face = self.title_face, + width = self.width, + } + } + self.input = InputText:new{ + text = self.input, + hint = self.input_hint, + face = self.input_face, + width = self.width * 0.9, + input_type = self.input_type, + enter_callback = self.enter_callback, + scroll = false, + parent = self, + } + local button_table = ButtonTable:new{ + width = self.width, + button_font_face = "cfont", + button_font_size = 20, + buttons = self.buttons, + zero_sep = true, + } + local title_bar = LineWidget:new{ + --background = 8, + dimen = Geom:new{ + w = button_table:getSize().w + self.button_padding, + h = Screen:scaleByDPI(2), + } + } + + self.dialog_frame = FrameContainer:new{ + radius = 8, + bordersize = 3, + padding = 0, + margin = 0, + background = 0, + VerticalGroup:new{ + align = "left", + self.title, + title_bar, + -- input + CenterContainer:new{ + dimen = Geom:new{ + w = title_bar:getSize().w, + h = self.input:getSize().h, + }, + self.input, + }, + -- buttons + CenterContainer:new{ + dimen = Geom:new{ + w = title_bar:getSize().w, + h = button_table:getSize().h, + }, + button_table, + } + } + } + + self[1] = CenterContainer:new{ + dimen = Geom:new{ + w = Screen:getWidth(), + h = Screen:getHeight() - self.input:getKeyboardDimen().h, + }, + self.dialog_frame, + } + UIManager.repaint_all = true + UIManager.full_refresh = true end function InputDialog:onShowKeyboard() - self.input:onShowKeyboard() + self.input:onShowKeyboard() end function InputDialog:getInputText() - return self.input:getText() + return self.input:getText() end function InputDialog:onClose() - self.input:onCloseKeyboard() + self.input:onCloseKeyboard() end return InputDialog diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 8eb11ec61..2e9e7afd0 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -8,149 +8,149 @@ local Screen = require("ui/screen") local UIManager = require("ui/uimanager") local InputText = InputContainer:new{ - text = "", - hint = "demo hint", - charlist = {}, -- table to store input string - charpos = 1, - input_type = nil, - - width = nil, - height = nil, - face = Font:getFace("cfont", 22), - - padding = 5, - margin = 5, - bordersize = 2, - - parent = nil, -- parent dialog that will be set dirty - scroll = false, + text = "", + hint = "demo hint", + charlist = {}, -- table to store input string + charpos = 1, + input_type = nil, + + width = nil, + height = nil, + face = Font:getFace("cfont", 22), + + padding = 5, + margin = 5, + bordersize = 2, + + parent = nil, -- parent dialog that will be set dirty + scroll = false, } function InputText:init() - self:StringToCharlist(self.text) - self:initTextBox() - self:initKeyboard() + self:StringToCharlist(self.text) + self:initTextBox() + self:initKeyboard() end function InputText:initTextBox() - local bgcolor = nil - local fgcolor = nil - if self.text == "" then - self.text = self.hint - bgcolor = 0.0 - fgcolor = 0.5 - else - bgcolor = 0.0 - fgcolor = 1.0 - end - local text_widget = nil - if self.scroll then - text_widget = ScrollTextWidget:new{ - text = self.text, - face = self.face, - bgcolor = bgcolor, - fgcolor = fgcolor, - width = self.width, - height = self.height, - } - else - text_widget = TextBoxWidget:new{ - text = self.text, - face = self.face, - bgcolor = bgcolor, - fgcolor = fgcolor, - width = self.width, - height = self.height, - } - end - self[1] = FrameContainer:new{ - bordersize = self.bordersize, - padding = self.padding, - margin = self.margin, - text_widget, - } - self.dimen = self[1]:getSize() + local bgcolor = nil + local fgcolor = nil + if self.text == "" then + self.text = self.hint + bgcolor = 0.0 + fgcolor = 0.5 + else + bgcolor = 0.0 + fgcolor = 1.0 + end + local text_widget = nil + if self.scroll then + text_widget = ScrollTextWidget:new{ + text = self.text, + face = self.face, + bgcolor = bgcolor, + fgcolor = fgcolor, + width = self.width, + height = self.height, + } + else + text_widget = TextBoxWidget:new{ + text = self.text, + face = self.face, + bgcolor = bgcolor, + fgcolor = fgcolor, + width = self.width, + height = self.height, + } + end + self[1] = FrameContainer:new{ + bordersize = self.bordersize, + padding = self.padding, + margin = self.margin, + text_widget, + } + self.dimen = self[1]:getSize() end function InputText:initKeyboard() - local keyboard_layout = 2 - if self.input_type == "number" then - keyboard_layout = 3 - end - self.keyboard = VirtualKeyboard:new{ - layout = keyboard_layout, - inputbox = self, - width = Screen:getWidth(), - height = math.max(Screen:getWidth(), Screen:getHeight())*0.33, - } + local keyboard_layout = 2 + if self.input_type == "number" then + keyboard_layout = 3 + end + self.keyboard = VirtualKeyboard:new{ + layout = keyboard_layout, + inputbox = self, + width = Screen:getWidth(), + height = math.max(Screen:getWidth(), Screen:getHeight())*0.33, + } end function InputText:onShowKeyboard() - UIManager:show(self.keyboard) + UIManager:show(self.keyboard) end function InputText:onCloseKeyboard() - UIManager:close(self.keyboard) + UIManager:close(self.keyboard) end function InputText:getKeyboardDimen() - return self.keyboard.dimen + return self.keyboard.dimen end function InputText:addChar(char) - if self.enter_callback and char == '\n' then - UIManager:scheduleIn(0.3, function() self.enter_callback() end) - return - end - table.insert(self.charlist, self.charpos, char) - self.charpos = self.charpos + 1 - self.text = self:CharlistToString() - self:initTextBox() - UIManager:setDirty(self.parent, "partial") + if self.enter_callback and char == '\n' then + UIManager:scheduleIn(0.3, function() self.enter_callback() end) + return + end + table.insert(self.charlist, self.charpos, char) + self.charpos = self.charpos + 1 + self.text = self:CharlistToString() + self:initTextBox() + UIManager:setDirty(self.parent, "partial") end function InputText:delChar() - if self.charpos == 1 then return end - self.charpos = self.charpos - 1 - table.remove(self.charlist, self.charpos) - self.text = self:CharlistToString() - self:initTextBox() - UIManager:setDirty(self.parent, "partial") + if self.charpos == 1 then return end + self.charpos = self.charpos - 1 + table.remove(self.charlist, self.charpos) + self.text = self:CharlistToString() + self:initTextBox() + UIManager:setDirty(self.parent, "partial") end function InputText:getText() - return self.text + return self.text end function InputText:setText(text) - self:StringToCharlist(text) - self:initTextBox() - UIManager:setDirty(self.parent, "partial") + self:StringToCharlist(text) + self:initTextBox() + UIManager:setDirty(self.parent, "partial") end function InputText:StringToCharlist(text) - if text == nil then return end - -- clear - self.charlist = {} - self.charpos = 1 - local prevcharcode, charcode = 0 - for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do - charcode = util.utf8charcode(uchar) - if prevcharcode then -- utf8 - self.charlist[#self.charlist+1] = uchar - end - prevcharcode = charcode - end - self.text = self:CharlistToString() - self.charpos = #self.charlist+1 + if text == nil then return end + -- clear + self.charlist = {} + self.charpos = 1 + local prevcharcode, charcode = 0 + for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do + charcode = util.utf8charcode(uchar) + if prevcharcode then -- utf8 + self.charlist[#self.charlist+1] = uchar + end + prevcharcode = charcode + end + self.text = self:CharlistToString() + self.charpos = #self.charlist+1 end function InputText:CharlistToString() - local s, i = "" - for i=1, #self.charlist do - s = s .. self.charlist[i] - end - return s + local s, i = "" + for i=1, #self.charlist do + s = s .. self.charlist[i] + end + return s end return InputText diff --git a/frontend/ui/widget/linewidget.lua b/frontend/ui/widget/linewidget.lua index 25d5282e2..c5f3395a8 100644 --- a/frontend/ui/widget/linewidget.lua +++ b/frontend/ui/widget/linewidget.lua @@ -1,33 +1,33 @@ local Widget = require("ui/widget/widget") local LineWidget = Widget:new{ - style = "solid", - background = 15, - dimen = nil, - --@TODO replay dirty hack here 13.03 2013 (houqp) - empty_segments = nil, + style = "solid", + background = 15, + dimen = nil, + --@TODO replay dirty hack here 13.03 2013 (houqp) + empty_segments = nil, } function LineWidget:paintTo(bb, x, y) - if self.style == "dashed" then - for i = 0, self.dimen.w - 20, 20 do - bb:paintRect(x + i, y, - 16, self.dimen.h, self.background) - end - else - if self.empty_segments then - bb:paintRect(x, y, - self.empty_segments[1].s, - self.dimen.h, - self.background) - bb:paintRect(x + self.empty_segments[1].e, y, - self.dimen.w - x - self.empty_segments[1].e, - self.dimen.h, - self.background) - else - bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.background) - end - end + if self.style == "dashed" then + for i = 0, self.dimen.w - 20, 20 do + bb:paintRect(x + i, y, + 16, self.dimen.h, self.background) + end + else + if self.empty_segments then + bb:paintRect(x, y, + self.empty_segments[1].s, + self.dimen.h, + self.background) + bb:paintRect(x + self.empty_segments[1].e, y, + self.dimen.w - x - self.empty_segments[1].e, + self.dimen.h, + self.background) + else + bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.background) + end + end end return LineWidget diff --git a/frontend/ui/widget/menu.lua b/frontend/ui/widget/menu.lua index 17706dc43..8216b61f7 100644 --- a/frontend/ui/widget/menu.lua +++ b/frontend/ui/widget/menu.lua @@ -29,49 +29,49 @@ local _ = require("gettext") Widget that displays a shortcut icon for menu item --]] local ItemShortCutIcon = WidgetContainer:new{ - dimen = Geom:new{ w = 22, h = 22 }, - key = nil, - bordersize = 2, - radius = 0, - style = "square" + dimen = Geom:new{ w = 22, h = 22 }, + key = nil, + bordersize = 2, + radius = 0, + style = "square" } function ItemShortCutIcon:init() - if not self.key then - return - end - - local radius = 0 - local background = 0 - if self.style == "rounded_corner" then - radius = math.floor(self.width/2) - elseif self.style == "grey_square" then - background = 3 - end - - --@TODO calculate font size by icon size 01.05 2012 (houqp) - local sc_face = nil - if self.key:len() > 1 then - sc_face = Font:getFace("ffont", 14) - else - sc_face = Font:getFace("scfont", 22) - end - - self[1] = FrameContainer:new{ - padding = 0, - bordersize = self.bordersize, - radius = radius, - background = background, - dimen = self.dimen, - CenterContainer:new{ - dimen = self.dimen, - TextWidget:new{ - text = self.key, - face = sc_face, - bgcolor = background/15, - }, - }, - } + if not self.key then + return + end + + local radius = 0 + local background = 0 + if self.style == "rounded_corner" then + radius = math.floor(self.width/2) + elseif self.style == "grey_square" then + background = 3 + end + + --@TODO calculate font size by icon size 01.05 2012 (houqp) + local sc_face = nil + if self.key:len() > 1 then + sc_face = Font:getFace("ffont", 14) + else + sc_face = Font:getFace("scfont", 22) + end + + self[1] = FrameContainer:new{ + padding = 0, + bordersize = self.bordersize, + radius = radius, + background = background, + dimen = self.dimen, + CenterContainer:new{ + dimen = self.dimen, + TextWidget:new{ + text = self.key, + face = sc_face, + bgcolor = background/15, + }, + }, + } end --[[ @@ -79,601 +79,601 @@ NOTICE: @menu entry must be provided in order to close the menu --]] local MenuCloseButton = InputContainer:new{ - align = "right", - menu = nil, - dimen = Geom:new{}, + align = "right", + menu = nil, + dimen = Geom:new{}, } function MenuCloseButton:init() - self[1] = TextWidget:new{ - text = "×", - face = Font:getFace("cfont", 32), - } - - local text_size = self[1]:getSize() - self.dimen.w, self.dimen.h = text_size.w*2, text_size.h*2 - - self.ges_events.Close = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = "Close menu", - } + self[1] = TextWidget:new{ + text = "×", + face = Font:getFace("cfont", 32), + } + + local text_size = self[1]:getSize() + self.dimen.w, self.dimen.h = text_size.w*2, text_size.h*2 + + self.ges_events.Close = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = "Close menu", + } end function MenuCloseButton:onClose() - self.menu:onClose() - return true + self.menu:onClose() + return true end --[[ Widget that displays an item for menu --]] local MenuItem = InputContainer:new{ - text = nil, - show_parent = nil, - detail = nil, - face = Font:getFace("cfont", 30), - info_face = Font:getFace("infont", 15), - dimen = nil, - shortcut = nil, - shortcut_style = "square", - _underline_container = nil, + text = nil, + show_parent = nil, + detail = nil, + face = Font:getFace("cfont", 30), + info_face = Font:getFace("infont", 15), + dimen = nil, + shortcut = nil, + shortcut_style = "square", + _underline_container = nil, } function MenuItem:init() - local shortcut_icon_dimen = Geom:new() - if self.shortcut then - shortcut_icon_dimen.w = math.floor(self.dimen.h*4/5) - shortcut_icon_dimen.h = shortcut_icon_dimen.w - end - - self.detail = self.text - -- 15 for HorizontalSpan, - self.content_width = self.dimen.w - shortcut_icon_dimen.w - 15 - - -- we need this table per-instance, so we declare it here - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = "Select Menu Item", - }, - HoldSelect = { - GestureRange:new{ - ges = "hold", - range = self.dimen, - }, - doc = "Hold Menu Item", - }, - } - end - if Device:hasKeyboard() then - self.active_key_events = { - Select = { {"Press"}, doc = "chose selected item" }, - } - end - - local mandatory = self.mandatory and ""..self.mandatory.." " or "" - local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, ""..mandatory, true).x - - w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, self.text, true).x - if w + mandatory_w >= self.content_width then - if Device:isTouchDevice() then - else - self.active_key_events.ShowItemDetail = { - {"Right"}, doc = "show item detail" - } - end - local indicator = " >> " - local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, indicator, true).x - self.text = RenderText:getSubTextByWidth(self.text, self.face, - self.content_width - indicator_w - mandatory_w, true) .. indicator - end - - local text_container = LeftContainer:new{ - dimen = Geom:new{w = self.content_width, h = self.dimen.h}, - TextWidget:new{ - text = self.text, - face = self.face, - } - } - - local mandatory_container = RightContainer:new{ - dimen = Geom:new{w = self.content_width, h = self.dimen.h}, - TextWidget:new{ - text = mandatory, - face = self.info_face, - } - } - - self._underline_container = UnderlineContainer:new{ - dimen = Geom:new{ - w = self.content_width, - h = self.dimen.h - }, - HorizontalGroup:new{ - align = "center", - OverlapGroup:new{ - dimen = Geom:new{w = self.content_width, h = self.dimen.h}, - text_container, - mandatory_container, - }, - } - } - - self[1] = FrameContainer:new{ - bordersize = 0, - padding = 0, - HorizontalGroup:new{ - HorizontalSpan:new{ width = 5 }, - ItemShortCutIcon:new{ - dimen = shortcut_icon_dimen, - key = self.shortcut, - radius = shortcut_icon_r, - style = self.shortcut_style, - }, - HorizontalSpan:new{ width = 10 }, - self._underline_container - } - } + local shortcut_icon_dimen = Geom:new() + if self.shortcut then + shortcut_icon_dimen.w = math.floor(self.dimen.h*4/5) + shortcut_icon_dimen.h = shortcut_icon_dimen.w + end + + self.detail = self.text + -- 15 for HorizontalSpan, + self.content_width = self.dimen.w - shortcut_icon_dimen.w - 15 + + -- we need this table per-instance, so we declare it here + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = "Select Menu Item", + }, + HoldSelect = { + GestureRange:new{ + ges = "hold", + range = self.dimen, + }, + doc = "Hold Menu Item", + }, + } + end + if Device:hasKeyboard() then + self.active_key_events = { + Select = { {"Press"}, doc = "chose selected item" }, + } + end + + local mandatory = self.mandatory and ""..self.mandatory.." " or "" + local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, ""..mandatory, true).x + + w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, self.text, true).x + if w + mandatory_w >= self.content_width then + if Device:isTouchDevice() then + else + self.active_key_events.ShowItemDetail = { + {"Right"}, doc = "show item detail" + } + end + local indicator = " >> " + local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, indicator, true).x + self.text = RenderText:getSubTextByWidth(self.text, self.face, + self.content_width - indicator_w - mandatory_w, true) .. indicator + end + + local text_container = LeftContainer:new{ + dimen = Geom:new{w = self.content_width, h = self.dimen.h}, + TextWidget:new{ + text = self.text, + face = self.face, + } + } + + local mandatory_container = RightContainer:new{ + dimen = Geom:new{w = self.content_width, h = self.dimen.h}, + TextWidget:new{ + text = mandatory, + face = self.info_face, + } + } + + self._underline_container = UnderlineContainer:new{ + dimen = Geom:new{ + w = self.content_width, + h = self.dimen.h + }, + HorizontalGroup:new{ + align = "center", + OverlapGroup:new{ + dimen = Geom:new{w = self.content_width, h = self.dimen.h}, + text_container, + mandatory_container, + }, + } + } + + self[1] = FrameContainer:new{ + bordersize = 0, + padding = 0, + HorizontalGroup:new{ + HorizontalSpan:new{ width = 5 }, + ItemShortCutIcon:new{ + dimen = shortcut_icon_dimen, + key = self.shortcut, + radius = shortcut_icon_r, + style = self.shortcut_style, + }, + HorizontalSpan:new{ width = 10 }, + self._underline_container + } + } end function MenuItem:onFocus() - self._underline_container.color = 15 - self.key_events = self.active_key_events - return true + self._underline_container.color = 15 + self.key_events = self.active_key_events + return true end function MenuItem:onUnfocus() - self._underline_container.color = 0 - self.key_events = {} - return true + self._underline_container.color = 0 + self.key_events = {} + return true end function MenuItem:onShowItemDetail() - UIManager:show(InfoMessage:new{ - text=self.detail, - }) - return true + UIManager:show(InfoMessage:new{ + text=self.detail, + }) + return true end function MenuItem:onTapSelect() - self[1].invert = true - UIManager:setDirty(self.show_parent, "partial") - UIManager:scheduleIn(0.1, function() - self[1].invert = false - UIManager:setDirty(self.show_parent, "partial") - self.menu:onMenuSelect(self.table) - end) - return true + self[1].invert = true + UIManager:setDirty(self.show_parent, "partial") + UIManager:scheduleIn(0.1, function() + self[1].invert = false + UIManager:setDirty(self.show_parent, "partial") + self.menu:onMenuSelect(self.table) + end) + return true end function MenuItem:onHoldSelect() - self[1].invert = true - UIManager:setDirty(self.show_parent, "partial") - UIManager:scheduleIn(0.1, function() - self[1].invert = false - UIManager:setDirty(self.show_parent, "partial") - self.menu:onMenuHold(self.table) - end) - return true + self[1].invert = true + UIManager:setDirty(self.show_parent, "partial") + UIManager:scheduleIn(0.1, function() + self[1].invert = false + UIManager:setDirty(self.show_parent, "partial") + self.menu:onMenuHold(self.table) + end) + return true end --[[ Widget that displays menu --]] local Menu = FocusManager:new{ - show_parent = nil, - -- face for displaying item contents - cface = Font:getFace("cfont", 24), - -- face for menu title - tface = Font:getFace("tfont", 26), - -- face for paging info display - fface = Font:getFace("ffont", 16), - -- font for item shortcut - sface = Font:getFace("scfont", 20), - - title = "No Title", - -- default width and height - width = 500, - -- height will be calculated according to item number if not given - height = nil, - dimen = Geom:new{}, - item_table = {}, - item_shortcuts = { - "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", - "A", "S", "D", "F", "G", "H", "J", "K", "L", "Del", - "Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Enter", - }, - item_table_stack = nil, - is_enable_shortcut = true, - - item_dimen = nil, - page = 1, - - item_group = nil, - page_info = nil, - - -- set this to true to not paint as popup menu - is_borderless = false, - -- if you want to embed the menu widget into another widget, set - -- this to false - is_popout = true, - -- set this to true to add close button - has_close_button = true, - -- close_callback is a function, which is executed when menu is closed - -- it is usually set by the widget which creates the menu - close_callback = nil + show_parent = nil, + -- face for displaying item contents + cface = Font:getFace("cfont", 24), + -- face for menu title + tface = Font:getFace("tfont", 26), + -- face for paging info display + fface = Font:getFace("ffont", 16), + -- font for item shortcut + sface = Font:getFace("scfont", 20), + + title = "No Title", + -- default width and height + width = 500, + -- height will be calculated according to item number if not given + height = nil, + dimen = Geom:new{}, + item_table = {}, + item_shortcuts = { + "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", + "A", "S", "D", "F", "G", "H", "J", "K", "L", "Del", + "Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Enter", + }, + item_table_stack = nil, + is_enable_shortcut = true, + + item_dimen = nil, + page = 1, + + item_group = nil, + page_info = nil, + + -- set this to true to not paint as popup menu + is_borderless = false, + -- if you want to embed the menu widget into another widget, set + -- this to false + is_popout = true, + -- set this to true to add close button + has_close_button = true, + -- close_callback is a function, which is executed when menu is closed + -- it is usually set by the widget which creates the menu + close_callback = nil } function Menu:_recalculateDimen() - self.dimen.w = self.width - -- if height not given, dynamically calculate it - self.dimen.h = self.height or (#self.item_table + 2) * Screen:scaleByDPI(36) - if self.dimen.h > Screen:getHeight() then - self.dimen.h = Screen:getHeight() - end - self.item_dimen = Geom:new{ - w = self.dimen.w, - h = Screen:scaleByDPI(46), -- hardcoded for now - } - self.perpage = math.floor((self.dimen.h - self.dimen.x) / self.item_dimen.h) - 2 - self.page_num = math.ceil(#self.item_table / self.perpage) + self.dimen.w = self.width + -- if height not given, dynamically calculate it + self.dimen.h = self.height or (#self.item_table + 2) * Screen:scaleByDPI(36) + if self.dimen.h > Screen:getHeight() then + self.dimen.h = Screen:getHeight() + end + self.item_dimen = Geom:new{ + w = self.dimen.w, + h = Screen:scaleByDPI(46), -- hardcoded for now + } + self.perpage = math.floor((self.dimen.h - self.dimen.x) / self.item_dimen.h) - 2 + self.page_num = math.ceil(#self.item_table / self.perpage) end function Menu:init() - self.show_parent = self.show_parent or self - self.item_table_stack = {} - self:_recalculateDimen() - self.page = 1 - - ----------------------------------- - -- start to set up widget layout -- - ----------------------------------- - self.menu_title = TextWidget:new{ - align = "center", - text = self.title, - face = self.tface, - } - -- group for title bar - self.title_bar = OverlapGroup:new{ - dimen = {w = self.dimen.w, h = self.menu_title:getSize().h}, - self.menu_title, - } - -- group for items - self.item_group = VerticalGroup:new{} - -- group for page info - self.page_info_left_chev = Button:new{ - icon = "resources/icons/appbar.chevron.left.png", - callback = function() self:onPrevPage() end, - bordersize = 0, - } - self.page_info_right_chev = Button:new{ - icon = "resources/icons/appbar.chevron.right.png", - callback = function() self:onNextPage() end, - bordersize = 0, - } - self.page_info_left_chev:hide() - self.page_info_right_chev:hide() - self.page_info_text = TextWidget:new{ - text = "", - face = self.fface, - } - self.page_info = HorizontalGroup:new{ - self.page_info_left_chev, - self.page_info_text, - self.page_info_right_chev - } - - local header = self.title_bar - local body = self.item_group - local footer = BottomContainer:new{ - dimen = self.dimen:copy(), - self.page_info, - } - - local content = nil - if self.no_title then - content = OverlapGroup:new{ - dimen = self.dimen:copy(), - VerticalGroup:new{ - align = "left", - body, - }, - footer - } - else - content = OverlapGroup:new{ - dimen = self.dimen:copy(), - VerticalGroup:new{ - align = "left", - header, - body, - }, - footer - } - end - - self[1] = FrameContainer:new{ - background = 0, - bordersize = self.is_borderless and 0 or 2, - padding = 0, - margin = 0, - radius = math.floor(self.dimen.w/20), - content - } - - ------------------------------------------ - -- start to set up input event callback -- - ------------------------------------------ - if Device:isTouchDevice() then - if self.has_close_button then - table.insert(self.title_bar, - MenuCloseButton:new{ - menu = self, - }) - end - -- watch for outer region if it's a self contained widget - if self.is_popout then - self.ges_events.TapCloseAllMenus = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - } - end - self.ges_events.Swipe = { - GestureRange:new{ - ges = "swipe", - range = self.dimen, - } - } - end - if Device:hasKeyboard() then - -- set up keyboard events - self.key_events.Close = { {"Back"}, doc = _("close menu") } - self.key_events.NextPage = { - {Input.group.PgFwd}, doc = _("goto next page of the menu") - } - self.key_events.PrevPage = { - {Input.group.PgBack}, doc = _("goto previous page of the menu") - } - -- we won't catch presses to "Right", leave that to MenuItem. - self.key_events.FocusRight = nil - -- shortcut icon is not needed for touch device - if self.is_enable_shortcut then - self.key_events.SelectByShortCut = { {self.item_shortcuts} } - end - self.key_events.Select = { - {"Press"}, doc = _("select current menu item") - } - end - - - if #self.item_table > 0 then - -- if the table is not yet initialized, this call - -- must be done manually: - self:updateItems(1) - end + self.show_parent = self.show_parent or self + self.item_table_stack = {} + self:_recalculateDimen() + self.page = 1 + + ----------------------------------- + -- start to set up widget layout -- + ----------------------------------- + self.menu_title = TextWidget:new{ + align = "center", + text = self.title, + face = self.tface, + } + -- group for title bar + self.title_bar = OverlapGroup:new{ + dimen = {w = self.dimen.w, h = self.menu_title:getSize().h}, + self.menu_title, + } + -- group for items + self.item_group = VerticalGroup:new{} + -- group for page info + self.page_info_left_chev = Button:new{ + icon = "resources/icons/appbar.chevron.left.png", + callback = function() self:onPrevPage() end, + bordersize = 0, + } + self.page_info_right_chev = Button:new{ + icon = "resources/icons/appbar.chevron.right.png", + callback = function() self:onNextPage() end, + bordersize = 0, + } + self.page_info_left_chev:hide() + self.page_info_right_chev:hide() + self.page_info_text = TextWidget:new{ + text = "", + face = self.fface, + } + self.page_info = HorizontalGroup:new{ + self.page_info_left_chev, + self.page_info_text, + self.page_info_right_chev + } + + local header = self.title_bar + local body = self.item_group + local footer = BottomContainer:new{ + dimen = self.dimen:copy(), + self.page_info, + } + + local content = nil + if self.no_title then + content = OverlapGroup:new{ + dimen = self.dimen:copy(), + VerticalGroup:new{ + align = "left", + body, + }, + footer + } + else + content = OverlapGroup:new{ + dimen = self.dimen:copy(), + VerticalGroup:new{ + align = "left", + header, + body, + }, + footer + } + end + + self[1] = FrameContainer:new{ + background = 0, + bordersize = self.is_borderless and 0 or 2, + padding = 0, + margin = 0, + radius = math.floor(self.dimen.w/20), + content + } + + ------------------------------------------ + -- start to set up input event callback -- + ------------------------------------------ + if Device:isTouchDevice() then + if self.has_close_button then + table.insert(self.title_bar, + MenuCloseButton:new{ + menu = self, + }) + end + -- watch for outer region if it's a self contained widget + if self.is_popout then + self.ges_events.TapCloseAllMenus = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + end + self.ges_events.Swipe = { + GestureRange:new{ + ges = "swipe", + range = self.dimen, + } + } + end + if Device:hasKeyboard() then + -- set up keyboard events + self.key_events.Close = { {"Back"}, doc = _("close menu") } + self.key_events.NextPage = { + {Input.group.PgFwd}, doc = _("goto next page of the menu") + } + self.key_events.PrevPage = { + {Input.group.PgBack}, doc = _("goto previous page of the menu") + } + -- we won't catch presses to "Right", leave that to MenuItem. + self.key_events.FocusRight = nil + -- shortcut icon is not needed for touch device + if self.is_enable_shortcut then + self.key_events.SelectByShortCut = { {self.item_shortcuts} } + end + self.key_events.Select = { + {"Press"}, doc = _("select current menu item") + } + end + + + if #self.item_table > 0 then + -- if the table is not yet initialized, this call + -- must be done manually: + self:updateItems(1) + end end function Menu:updateItems(select_number) - -- self.layout must be updated for focusmanager - self.layout = {} - self.item_group:clear() - self.page_info:resetLayout() - self:_recalculateDimen() - - -- default to select the first item - if not select_number then - select_number = 1 - end - - for c = 1, self.perpage do - -- calculate index in item_table - local i = (self.page - 1) * self.perpage + c - if i <= #self.item_table then - local item_shortcut = nil - local shortcut_style = "square" - if self.is_enable_shortcut then - -- give different shortcut_style to keys in different - -- lines of keyboard - if c >= 11 and c <= 20 then - --shortcut_style = "rounded_corner" - shortcut_style = "grey_square" - end - item_shortcut = self.item_shortcuts[c] - if item_shortcut == "Enter" then - item_shortcut = "Ent" - end - end - local item_tmp = MenuItem:new{ - show_parent = self.show_parent, - text = self.item_table[i].text, - mandatory = self.item_table[i].mandatory, - face = self.cface, - dimen = self.item_dimen:new(), - shortcut = item_shortcut, - shortcut_style = shortcut_style, - table = self.item_table[i], - menu = self, - } - table.insert(self.item_group, item_tmp) - -- this is for focus manager - table.insert(self.layout, {item_tmp}) - end -- if i <= self.items - end -- for c=1, self.perpage - if self.item_group[1] then - if not Device:isTouchDevice() then - -- only draw underline for nontouch device - -- reset focus manager accordingly - self.selected = { x = 1, y = select_number } - -- set focus to requested menu item - self.item_group[select_number]:onFocus() - end - -- update page information - self.page_info_text.text = _("page ")..self.page.."/"..self.page_num - self.page_info_left_chev:showHide(self.page_num > 1) - self.page_info_right_chev:showHide(self.page_num > 1) - self.page_info_left_chev:enableDisable(self.page > 1) - self.page_info_right_chev:enableDisable(self.page < self.page_num) - else - self.page_info_text.text = _("no choices available") - end - - -- FIXME: this is a dirty hack to clear previous menus - UIManager.repaint_all = true - --UIManager:setDirty(self) + -- self.layout must be updated for focusmanager + self.layout = {} + self.item_group:clear() + self.page_info:resetLayout() + self:_recalculateDimen() + + -- default to select the first item + if not select_number then + select_number = 1 + end + + for c = 1, self.perpage do + -- calculate index in item_table + local i = (self.page - 1) * self.perpage + c + if i <= #self.item_table then + local item_shortcut = nil + local shortcut_style = "square" + if self.is_enable_shortcut then + -- give different shortcut_style to keys in different + -- lines of keyboard + if c >= 11 and c <= 20 then + --shortcut_style = "rounded_corner" + shortcut_style = "grey_square" + end + item_shortcut = self.item_shortcuts[c] + if item_shortcut == "Enter" then + item_shortcut = "Ent" + end + end + local item_tmp = MenuItem:new{ + show_parent = self.show_parent, + text = self.item_table[i].text, + mandatory = self.item_table[i].mandatory, + face = self.cface, + dimen = self.item_dimen:new(), + shortcut = item_shortcut, + shortcut_style = shortcut_style, + table = self.item_table[i], + menu = self, + } + table.insert(self.item_group, item_tmp) + -- this is for focus manager + table.insert(self.layout, {item_tmp}) + end -- if i <= self.items + end -- for c=1, self.perpage + if self.item_group[1] then + if not Device:isTouchDevice() then + -- only draw underline for nontouch device + -- reset focus manager accordingly + self.selected = { x = 1, y = select_number } + -- set focus to requested menu item + self.item_group[select_number]:onFocus() + end + -- update page information + self.page_info_text.text = _("page ")..self.page.."/"..self.page_num + self.page_info_left_chev:showHide(self.page_num > 1) + self.page_info_right_chev:showHide(self.page_num > 1) + self.page_info_left_chev:enableDisable(self.page > 1) + self.page_info_right_chev:enableDisable(self.page < self.page_num) + else + self.page_info_text.text = _("no choices available") + end + + -- FIXME: this is a dirty hack to clear previous menus + UIManager.repaint_all = true + --UIManager:setDirty(self) end function Menu:swithItemTable(new_title, new_item_table) - if self.menu_title then - self.menu_title.text = new_title - end - self.page = 1 - self.item_table = new_item_table - self:updateItems(1) + if self.menu_title then + self.menu_title.text = new_title + end + self.page = 1 + self.item_table = new_item_table + self:updateItems(1) end function Menu:onSelectByShortCut(_, keyevent) - for k,v in ipairs(self.item_shortcuts) do - if k > self.perpage then - break - elseif v == keyevent.key then - if self.item_table[(self.page-1)*self.perpage + k] then - self:onMenuSelect(self.item_table[(self.page-1)*self.perpage + k]) - end - break - end - end - return true + for k,v in ipairs(self.item_shortcuts) do + if k > self.perpage then + break + elseif v == keyevent.key then + if self.item_table[(self.page-1)*self.perpage + k] then + self:onMenuSelect(self.item_table[(self.page-1)*self.perpage + k]) + end + break + end + end + return true end function Menu:onWrapFirst() - if self.page > 1 then - self.page = self.page - 1 - local end_position = self.perpage - if self.page == self.page_num then - end_position = #self.item_table % self.perpage - end - self:updateItems(end_position) - end - return false + if self.page > 1 then + self.page = self.page - 1 + local end_position = self.perpage + if self.page == self.page_num then + end_position = #self.item_table % self.perpage + end + self:updateItems(end_position) + end + return false end function Menu:onWrapLast() - if self.page < self.page_num then - self:onNextPage() - end - return false + if self.page < self.page_num then + self:onNextPage() + end + return false end --[[ override this function to process the item selected in a different manner ]]-- function Menu:onMenuSelect(item) - if item.sub_item_table == nil then - self.close_callback() - self:onMenuChoice(item) - else - -- save menu title for later resume - self.item_table.title = self.title - table.insert(self.item_table_stack, self.item_table) - self:swithItemTable(item.text, item.sub_item_table) - end - return true + if item.sub_item_table == nil then + self.close_callback() + self:onMenuChoice(item) + else + -- save menu title for later resume + self.item_table.title = self.title + table.insert(self.item_table_stack, self.item_table) + self:swithItemTable(item.text, item.sub_item_table) + end + return true end --[[ - default to call item callback - override this function to handle the choice + default to call item callback + override this function to handle the choice --]] function Menu:onMenuChoice(item) - if item.callback then - item.callback() - end - return true + if item.callback then + item.callback() + end + return true end --[[ override this function to process the item hold in a different manner ]]-- function Menu:onMenuHold(item) - return true + return true end function Menu:onNextPage() - if self.page < self.page_num then - self.page = self.page + 1 - self:updateItems(1) - elseif self.page == self.page_num then - -- on the last page, we check if we're on the last item - local end_position = #self.item_table % self.perpage - if end_position == 0 then - end_position = self.perpage - end - if end_position ~= self.selected.y then - self:updateItems(end_position) - end - end - return true + if self.page < self.page_num then + self.page = self.page + 1 + self:updateItems(1) + elseif self.page == self.page_num then + -- on the last page, we check if we're on the last item + local end_position = #self.item_table % self.perpage + if end_position == 0 then + end_position = self.perpage + end + if end_position ~= self.selected.y then + self:updateItems(end_position) + end + end + return true end function Menu:onPrevPage() - if self.page > 1 then - self.page = self.page - 1 - end - self:updateItems(1) - return true + if self.page > 1 then + self.page = self.page - 1 + end + self:updateItems(1) + return true end function Menu:onSelect() - self:onMenuSelect(self.item_table[(self.page-1)*self.perpage+self.selected.y]) - return true + self:onMenuSelect(self.item_table[(self.page-1)*self.perpage+self.selected.y]) + return true end function Menu:onClose() - local table_length = #self.item_table_stack - if table_length == 0 then - self:onCloseAllMenus() - else - -- back to parent menu - parent_item_table = table.remove(self.item_table_stack, table_length) - self:swithItemTable(parent_item_table.title, parent_item_table) - end - return true + local table_length = #self.item_table_stack + if table_length == 0 then + self:onCloseAllMenus() + else + -- back to parent menu + parent_item_table = table.remove(self.item_table_stack, table_length) + self:swithItemTable(parent_item_table.title, parent_item_table) + end + return true end function Menu:onCloseAllMenus() - UIManager:close(self) - if self.close_callback then - self.close_callback() - end - return true + UIManager:close(self) + if self.close_callback then + self.close_callback() + end + return true end function Menu:onTapCloseAllMenus(arg, ges_ev) - if ges_ev.pos:notIntersectWith(self.dimen) then - self:onCloseAllMenus() - return true - end + if ges_ev.pos:notIntersectWith(self.dimen) then + self:onCloseAllMenus() + return true + end end function Menu:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then - self:onNextPage() - elseif ges_ev.direction == "east" then - self:onPrevPage() - end + if ges_ev.direction == "west" then + self:onNextPage() + elseif ges_ev.direction == "east" then + self:onPrevPage() + end end return Menu diff --git a/frontend/ui/widget/notification.lua b/frontend/ui/widget/notification.lua index 7414b743b..8b888889b 100644 --- a/frontend/ui/widget/notification.lua +++ b/frontend/ui/widget/notification.lua @@ -14,50 +14,50 @@ local Screen = require("ui/screen") Widget that displays a tiny notification on top of screen --]] local Notification = InputContainer:new{ - face = Font:getFace("infofont", 20), - text = "Null Message", - timeout = nil, + face = Font:getFace("infofont", 20), + text = "Null Message", + timeout = nil, } function Notification:init() - if Device:hasKeyboard() then - self.key_events = { - AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" } - } - end - -- we construct the actual content here because self.text is only available now - self[1] = CenterContainer:new{ - dimen = Geom:new{ - w = Screen:getWidth(), - h = Screen:getHeight()/10, - }, - ignore = "height", - FrameContainer:new{ - background = 0, - radius = 0, - HorizontalGroup:new{ - align = "center", - TextBoxWidget:new{ - text = self.text, - face = self.face, - } - } - } - } + if Device:hasKeyboard() then + self.key_events = { + AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" } + } + end + -- we construct the actual content here because self.text is only available now + self[1] = CenterContainer:new{ + dimen = Geom:new{ + w = Screen:getWidth(), + h = Screen:getHeight()/10, + }, + ignore = "height", + FrameContainer:new{ + background = 0, + radius = 0, + HorizontalGroup:new{ + align = "center", + TextBoxWidget:new{ + text = self.text, + face = self.face, + } + } + } + } end function Notification:onShow() - -- triggered by the UIManager after we got successfully shown (not yet painted) - if self.timeout then - UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end) - end - return true + -- triggered by the UIManager after we got successfully shown (not yet painted) + if self.timeout then + UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end) + end + return true end function Notification:onAnyKeyPressed() - -- triggered by our defined key events - UIManager:close(self) - return true + -- triggered by our defined key events + UIManager:close(self) + return true end return Notification diff --git a/frontend/ui/widget/overlapgroup.lua b/frontend/ui/widget/overlapgroup.lua index c4918bb55..e8b2e1f42 100644 --- a/frontend/ui/widget/overlapgroup.lua +++ b/frontend/ui/widget/overlapgroup.lua @@ -4,48 +4,48 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer") A Layout widget that puts objects above each other --]] local OverlapGroup = WidgetContainer:new{ - _size = nil, + _size = nil, } function OverlapGroup:getSize() - if not self._size then - self._size = {w = 0, h = 0} - self._offsets = { x = math.huge, y = math.huge } - for i, widget in ipairs(self) do - local w_size = widget:getSize() - if self._size.h < w_size.h then - self._size.h = w_size.h - end - if self._size.w < w_size.w then - self._size.w = w_size.w - end - end - end + if not self._size then + self._size = {w = 0, h = 0} + self._offsets = { x = math.huge, y = math.huge } + for i, widget in ipairs(self) do + local w_size = widget:getSize() + if self._size.h < w_size.h then + self._size.h = w_size.h + end + if self._size.w < w_size.w then + self._size.w = w_size.w + end + end + end - if self.dimen.w then - self._size.w = self.dimen.w - end - if self.dimen.h then - self._size.h = self.dimen.h - end + if self.dimen.w then + self._size.w = self.dimen.w + end + if self.dimen.h then + self._size.h = self.dimen.h + end - return self._size + return self._size end function OverlapGroup:paintTo(bb, x, y) - local size = self:getSize() + local size = self:getSize() - for i, wget in ipairs(self) do - local wget_size = wget:getSize() - if wget.align == "right" then - wget:paintTo(bb, x+size.w-wget_size.w, y) - elseif wget.align == "center" then - wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y) - else - -- default to left - wget:paintTo(bb, x, y) - end - end + for i, wget in ipairs(self) do + local wget_size = wget:getSize() + if wget.align == "right" then + wget:paintTo(bb, x+size.w-wget_size.w, y) + elseif wget.align == "center" then + wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y) + else + -- default to left + wget:paintTo(bb, x, y) + end + end end return OverlapGroup diff --git a/frontend/ui/widget/progresswidget.lua b/frontend/ui/widget/progresswidget.lua index 9042d6a6d..dafd9c246 100644 --- a/frontend/ui/widget/progresswidget.lua +++ b/frontend/ui/widget/progresswidget.lua @@ -5,39 +5,39 @@ local Geom = require("ui/geometry") ProgressWidget shows a progress bar --]] local ProgressWidget = Widget:new{ - width = nil, - height = nil, - margin_h = 3, - margin_v = 1, - radius = 2, - bordersize = 1, - bordercolor = 15, - bgcolor = 0, - rectcolor = 10, - percentage = nil, + width = nil, + height = nil, + margin_h = 3, + margin_v = 1, + radius = 2, + bordersize = 1, + bordercolor = 15, + bgcolor = 0, + rectcolor = 10, + percentage = nil, } function ProgressWidget:getSize() - return { w = self.width, h = self.height } + return { w = self.width, h = self.height } end function ProgressWidget:paintTo(bb, x, y) - local my_size = self:getSize() - self.dimen = Geom:new{ - x = x, y = y, - w = my_size.w, - h = my_size.h - } - bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius) - bb:paintBorder(x, y, my_size.w, my_size.h, - self.bordersize, self.bordercolor, self.radius) - bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize, - (my_size.w-2*self.margin_h)*self.percentage, - (my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor) + local my_size = self:getSize() + self.dimen = Geom:new{ + x = x, y = y, + w = my_size.w, + h = my_size.h + } + bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius) + bb:paintBorder(x, y, my_size.w, my_size.h, + self.bordersize, self.bordercolor, self.radius) + bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize, + (my_size.w-2*self.margin_h)*self.percentage, + (my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor) end function ProgressWidget:setPercentage(percentage) - self.percentage = percentage + self.percentage = percentage end return ProgressWidget diff --git a/frontend/ui/widget/rectspan.lua b/frontend/ui/widget/rectspan.lua index d02ffc6bb..9bc1d4dff 100644 --- a/frontend/ui/widget/rectspan.lua +++ b/frontend/ui/widget/rectspan.lua @@ -4,12 +4,12 @@ local Widget = require("ui/widget/widget") Dummy Widget that reserves vertical and horizontal space ]] local RectSpan = Widget:new{ - width = 0, - hright = 0, + width = 0, + hright = 0, } function RectSpan:getSize() - return {w = self.width, h = self.height} + return {w = self.width, h = self.height} end return RectSpan diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index f61a0c15f..bdb45fbb4 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -13,73 +13,73 @@ local Device = require("ui/device") Text widget with vertical scroll bar --]] local ScrollTextWidget = InputContainer:new{ - text = nil, - face = nil, - bgcolor = 0.0, -- [0.0, 1.0] - fgcolor = 1.0, -- [0.0, 1.0] - width = 400, - height = 20, - scroll_bar_width = Screen:scaleByDPI(6), - text_scroll_span = Screen:scaleByDPI(6), - dialog = nil, + text = nil, + face = nil, + bgcolor = 0.0, -- [0.0, 1.0] + fgcolor = 1.0, -- [0.0, 1.0] + width = 400, + height = 20, + scroll_bar_width = Screen:scaleByDPI(6), + text_scroll_span = Screen:scaleByDPI(6), + dialog = nil, } function ScrollTextWidget:init() - self.text_widget = TextBoxWidget:new{ - text = self.text, - face = self.face, - bgcolor = self.bgcolor, - fgcolor = self.fgcolor, - width = self.width - self.scroll_bar_width - self.text_scroll_span, - height = self.height - } - local visible_line_count = self.text_widget:getVisLineCount() - local total_line_count = self.text_widget:getAllLineCount() - self.v_scroll_bar = VerticalScrollBar:new{ - enable = visible_line_count < total_line_count, - low = 0, - high = visible_line_count/total_line_count, - width = Screen:scaleByDPI(6), - height = self.height, - } - local horizontal_group = HorizontalGroup:new{} - table.insert(horizontal_group, self.text_widget) - table.insert(horizontal_group, HorizontalSpan:new{width = Screen:scaleByDPI(6)}) - table.insert(horizontal_group, self.v_scroll_bar) - self[1] = horizontal_group - self.dimen = Geom:new(self[1]:getSize()) - if Device:isTouchDevice() then - self.ges_events = { - Swipe = { - GestureRange:new{ - ges = "swipe", - range = self.dimen, - }, - }, - } - end + self.text_widget = TextBoxWidget:new{ + text = self.text, + face = self.face, + bgcolor = self.bgcolor, + fgcolor = self.fgcolor, + width = self.width - self.scroll_bar_width - self.text_scroll_span, + height = self.height + } + local visible_line_count = self.text_widget:getVisLineCount() + local total_line_count = self.text_widget:getAllLineCount() + self.v_scroll_bar = VerticalScrollBar:new{ + enable = visible_line_count < total_line_count, + low = 0, + high = visible_line_count/total_line_count, + width = Screen:scaleByDPI(6), + height = self.height, + } + local horizontal_group = HorizontalGroup:new{} + table.insert(horizontal_group, self.text_widget) + table.insert(horizontal_group, HorizontalSpan:new{width = Screen:scaleByDPI(6)}) + table.insert(horizontal_group, self.v_scroll_bar) + self[1] = horizontal_group + self.dimen = Geom:new(self[1]:getSize()) + if Device:isTouchDevice() then + self.ges_events = { + Swipe = { + GestureRange:new{ + ges = "swipe", + range = self.dimen, + }, + }, + } + end end function ScrollTextWidget:updateScrollBar(text) - local virtual_line_num = text:getVirtualLineNum() - local visible_line_count = text:getVisLineCount() - local all_line_count = text:getAllLineCount() - self.v_scroll_bar:set( - (virtual_line_num - 1) / all_line_count, - (virtual_line_num - 1 + visible_line_count) / all_line_count - ) + local virtual_line_num = text:getVirtualLineNum() + local visible_line_count = text:getVisLineCount() + local all_line_count = text:getAllLineCount() + self.v_scroll_bar:set( + (virtual_line_num - 1) / all_line_count, + (virtual_line_num - 1 + visible_line_count) / all_line_count + ) end function ScrollTextWidget:onSwipe(arg, ges) - if ges.direction == "north" then - self.text_widget:scrollDown() - self:updateScrollBar(self.text_widget) - elseif ges.direction == "south" then - self.text_widget:scrollUp() - self:updateScrollBar(self.text_widget) - end - UIManager:setDirty(self.dialog, "partial") - return true + if ges.direction == "north" then + self.text_widget:scrollDown() + self:updateScrollBar(self.text_widget) + elseif ges.direction == "south" then + self.text_widget:scrollUp() + self:updateScrollBar(self.text_widget) + end + UIManager:setDirty(self.dialog, "partial") + return true end return ScrollTextWidget diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index edf7b6c37..cd67c04c5 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -9,59 +9,59 @@ local Geom = require("ui/geometry") A TextWidget that handles long text wrapping --]] local TextBoxWidget = Widget:new{ - text = nil, - face = nil, - bold = nil, - bgcolor = 0.0, -- [0.0, 1.0] - fgcolor = 1.0, -- [0.0, 1.0] - width = 400, -- in pixels - height = nil, - first_line = 1, - virtual_line = 1, -- used by scroll bar - line_height = 0.3, -- in em - v_list = nil, - _bb = nil, - _length = 0, + text = nil, + face = nil, + bold = nil, + bgcolor = 0.0, -- [0.0, 1.0] + fgcolor = 1.0, -- [0.0, 1.0] + width = 400, -- in pixels + height = nil, + first_line = 1, + virtual_line = 1, -- used by scroll bar + line_height = 0.3, -- in em + v_list = nil, + _bb = nil, + _length = 0, } function TextBoxWidget:init() - local v_list = nil - if self.height then - v_list = self:_getCurrentVerticalList() - else - v_list = self:_getVerticalList() - end - self:_render(v_list) + local v_list = nil + if self.height then + v_list = self:_getCurrentVerticalList() + else + v_list = self:_getVerticalList() + end + self:_render(v_list) end function TextBoxWidget:_wrapGreedyAlg(h_list) - local cur_line_width = 0 - local cur_line = {} - local v_list = {} + local cur_line_width = 0 + local cur_line = {} + local v_list = {} - for k,w in ipairs(h_list) do - cur_line_width = cur_line_width + w.width - if w.word == "\n" then - if cur_line_width > 0 then - -- hard line break - table.insert(v_list, cur_line) - cur_line = {} - cur_line_width = 0 - end - elseif cur_line_width > self.width then - -- wrap to next line - table.insert(v_list, cur_line) - cur_line = {} - cur_line_width = w.width - table.insert(cur_line, w) - else - table.insert(cur_line, w) - end - end - -- handle last line - table.insert(v_list, cur_line) + for k,w in ipairs(h_list) do + cur_line_width = cur_line_width + w.width + if w.word == "\n" then + if cur_line_width > 0 then + -- hard line break + table.insert(v_list, cur_line) + cur_line = {} + cur_line_width = 0 + end + elseif cur_line_width > self.width then + -- wrap to next line + table.insert(v_list, cur_line) + cur_line = {} + cur_line_width = w.width + table.insert(cur_line, w) + else + table.insert(cur_line, w) + end + end + -- handle last line + table.insert(v_list, cur_line) - return v_list + return v_list end --[[ @@ -77,199 +77,199 @@ License: MIT/X11 Source: http://snippets.luacode.org/snippets/String_splitting_130 --]] function string:gsplit(pattern, capture) - pattern = pattern and tostring(pattern) or '%s+' - if (''):find(pattern) then - error('pattern matches empty string!', 2) - end - return coroutine.wrap(function() - local index = 1 - repeat - local first, last = self:find(pattern, index) - if first and last then - if index < first then - coroutine.yield(self:sub(index, first - 1)) - end - if capture then - coroutine.yield(self:sub(first, last)) - end - index = last + 1 - else - if index <= #self then - coroutine.yield(self:sub(index)) - end - break - end - until index > #self - end) + pattern = pattern and tostring(pattern) or '%s+' + if (''):find(pattern) then + error('pattern matches empty string!', 2) + end + return coroutine.wrap(function() + local index = 1 + repeat + local first, last = self:find(pattern, index) + if first and last then + if index < first then + coroutine.yield(self:sub(index, first - 1)) + end + if capture then + coroutine.yield(self:sub(first, last)) + end + index = last + 1 + else + if index <= #self then + coroutine.yield(self:sub(index)) + end + break + end + until index > #self + end) end function TextBoxWidget:_getVerticalList(alg) - if self.vertical_list then - return self.vertical_list - end - -- build horizontal list - local h_list = {} - local line_count = 0 - for line in self.text:gsplit("\n", true) do - for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do - for word in words:gsplit("%s+", true) do - for w in word:gsplit("%p+", true) do - local word_box = {} - word_box.word = w - word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, w, true, self.bold).x - table.insert(h_list, word_box) - end - end - end - if line:sub(-1) == "\n" then table.insert(h_list, {word = '\n', width = 0}) end - end + if self.vertical_list then + return self.vertical_list + end + -- build horizontal list + local h_list = {} + local line_count = 0 + for line in self.text:gsplit("\n", true) do + for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do + for word in words:gsplit("%s+", true) do + for w in word:gsplit("%p+", true) do + local word_box = {} + word_box.word = w + word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, w, true, self.bold).x + table.insert(h_list, word_box) + end + end + end + if line:sub(-1) == "\n" then table.insert(h_list, {word = '\n', width = 0}) end + end - -- @TODO check alg here 25.04 2012 (houqp) - -- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp) - self.vertical_list = self:_wrapGreedyAlg(h_list) - return self.vertical_list + -- @TODO check alg here 25.04 2012 (houqp) + -- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp) + self.vertical_list = self:_wrapGreedyAlg(h_list) + return self.vertical_list end function TextBoxWidget:_getCurrentVerticalList() - local line_height = (1 + self.line_height) * self.face.size - local v_list = self:_getVerticalList() - local current_v_list = {} - local height = 0 - for i = self.first_line, #v_list do - if height < self.height - line_height then - table.insert(current_v_list, v_list[i]) - height = height + line_height - else - break - end - end - return current_v_list + local line_height = (1 + self.line_height) * self.face.size + local v_list = self:_getVerticalList() + local current_v_list = {} + local height = 0 + for i = self.first_line, #v_list do + if height < self.height - line_height then + table.insert(current_v_list, v_list[i]) + height = height + line_height + else + break + end + end + return current_v_list end function TextBoxWidget:_getPreviousVerticalList() - local line_height = (1 + self.line_height) * self.face.size - local v_list = self:_getVerticalList() - local previous_v_list = {} - local height = 0 - if self.first_line == 1 then - return self:_getCurrentVerticalList() - end - self.virtual_line = self.first_line - for i = self.first_line - 1, 1, -1 do - if height < self.height - line_height then - table.insert(previous_v_list, 1, v_list[i]) - height = height + line_height - self.virtual_line = self.virtual_line - 1 - else - break - end - end - for i = self.first_line, #v_list do - if height < self.height - line_height then - table.insert(previous_v_list, v_list[i]) - height = height + line_height - else - break - end - end - if self.first_line > #previous_v_list then - self.first_line = self.first_line - #previous_v_list - else - self.first_line = 1 - end - return previous_v_list + local line_height = (1 + self.line_height) * self.face.size + local v_list = self:_getVerticalList() + local previous_v_list = {} + local height = 0 + if self.first_line == 1 then + return self:_getCurrentVerticalList() + end + self.virtual_line = self.first_line + for i = self.first_line - 1, 1, -1 do + if height < self.height - line_height then + table.insert(previous_v_list, 1, v_list[i]) + height = height + line_height + self.virtual_line = self.virtual_line - 1 + else + break + end + end + for i = self.first_line, #v_list do + if height < self.height - line_height then + table.insert(previous_v_list, v_list[i]) + height = height + line_height + else + break + end + end + if self.first_line > #previous_v_list then + self.first_line = self.first_line - #previous_v_list + else + self.first_line = 1 + end + return previous_v_list end function TextBoxWidget:_getNextVerticalList() - local line_height = (1 + self.line_height) * self.face.size - local v_list = self:_getVerticalList() - local current_v_list = self:_getCurrentVerticalList() - local next_v_list = {} - local height = 0 - if self.first_line + #current_v_list > #v_list then - return current_v_list - end - self.virtual_line = self.first_line - for i = self.first_line + #current_v_list, #v_list do - if height < self.height - line_height then - table.insert(next_v_list, v_list[i]) - height = height + line_height - self.virtual_line = self.virtual_line + 1 - else - break - end - end - self.first_line = self.first_line + #current_v_list - return next_v_list + local line_height = (1 + self.line_height) * self.face.size + local v_list = self:_getVerticalList() + local current_v_list = self:_getCurrentVerticalList() + local next_v_list = {} + local height = 0 + if self.first_line + #current_v_list > #v_list then + return current_v_list + end + self.virtual_line = self.first_line + for i = self.first_line + #current_v_list, #v_list do + if height < self.height - line_height then + table.insert(next_v_list, v_list[i]) + height = height + line_height + self.virtual_line = self.virtual_line + 1 + else + break + end + end + self.first_line = self.first_line + #current_v_list + return next_v_list end function TextBoxWidget:_render(v_list) - local font_height = self.face.size - local line_height_px = self.line_height * font_height - local space_w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x - local h = (font_height + line_height_px) * #v_list - self._bb = Blitbuffer.new(self.width, h) - local y = font_height - local pen_x = 0 - for _,l in ipairs(v_list) do - pen_x = 0 - for _,w in ipairs(l) do - --@TODO Don't use kerning for monospaced fonts. (houqp) - -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree - RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, w.word, true, self.bold, self.bgcolor, self.fgcolor) - pen_x = pen_x + w.width - end - y = y + line_height_px + font_height - end --- -- if text is shorter than one line, shrink to text's width --- if #v_list == 1 then --- self.width = pen_x --- end + local font_height = self.face.size + local line_height_px = self.line_height * font_height + local space_w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x + local h = (font_height + line_height_px) * #v_list + self._bb = Blitbuffer.new(self.width, h) + local y = font_height + local pen_x = 0 + for _,l in ipairs(v_list) do + pen_x = 0 + for _,w in ipairs(l) do + --@TODO Don't use kerning for monospaced fonts. (houqp) + -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree + RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, w.word, true, self.bold, self.bgcolor, self.fgcolor) + pen_x = pen_x + w.width + end + y = y + line_height_px + font_height + end +-- -- if text is shorter than one line, shrink to text's width +-- if #v_list == 1 then +-- self.width = pen_x +-- end end function TextBoxWidget:getVirtualLineNum() - return self.virtual_line + return self.virtual_line end function TextBoxWidget:getAllLineCount() - local v_list = self:_getVerticalList() - return #v_list + local v_list = self:_getVerticalList() + return #v_list end function TextBoxWidget:getVisLineCount() - local line_height = (1 + self.line_height) * self.face.size - return math.floor(self.height / line_height) + local line_height = (1 + self.line_height) * self.face.size + return math.floor(self.height / line_height) end function TextBoxWidget:scrollDown() - local next_v_list = self:_getNextVerticalList() - self:free() - self:_render(next_v_list) + local next_v_list = self:_getNextVerticalList() + self:free() + self:_render(next_v_list) end function TextBoxWidget:scrollUp() - local previous_v_list = self:_getPreviousVerticalList() - self:free() - self:_render(previous_v_list) + local previous_v_list = self:_getPreviousVerticalList() + self:free() + self:_render(previous_v_list) end function TextBoxWidget:getSize() - if self.width and self.height then - return Geom:new{ w = self.width, h = self.height} - else - return Geom:new{ w = self.width, h = self._bb:getHeight()} - end + if self.width and self.height then + return Geom:new{ w = self.width, h = self.height} + else + return Geom:new{ w = self.width, h = self._bb:getHeight()} + end end function TextBoxWidget:paintTo(bb, x, y) - bb:blitFrom(self._bb, x, y, 0, 0, self.width, self._bb:getHeight()) + bb:blitFrom(self._bb, x, y, 0, 0, self.width, self._bb:getHeight()) end function TextBoxWidget:free() - if self._bb then - self._bb:free() - self._bb = nil - end + if self._bb then + self._bb:free() + self._bb = nil + end end return TextBoxWidget diff --git a/frontend/ui/widget/textwidget.lua b/frontend/ui/widget/textwidget.lua index 46ece35cb..393b14dc4 100644 --- a/frontend/ui/widget/textwidget.lua +++ b/frontend/ui/widget/textwidget.lua @@ -7,55 +7,55 @@ local Geom = require("ui/geometry") A TextWidget puts a string on a single line --]] local TextWidget = Widget:new{ - text = nil, - face = nil, - bold = nil, - bgcolor = 0.0, -- [0.0, 1.0] - fgcolor = 1.0, -- [0.0, 1.0] - _bb = nil, - _length = 0, - _height = 0, - _maxlength = 1200, + text = nil, + face = nil, + bold = nil, + bgcolor = 0.0, -- [0.0, 1.0] + fgcolor = 1.0, -- [0.0, 1.0] + _bb = nil, + _length = 0, + _height = 0, + _maxlength = 1200, } --function TextWidget:_render() - --local h = self.face.size * 1.3 - --self._bb = Blitbuffer.new(self._maxlength, h) - --self._length = RenderText:renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, true, self.bold) + --local h = self.face.size * 1.3 + --self._bb = Blitbuffer.new(self._maxlength, h) + --self._length = RenderText:renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, true, self.bold) --end function TextWidget:getSize() - --if not self._bb then - --self:_render() - --end - --return { w = self._length, h = self._bb:getHeight() } - local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold) - if not tsize then - return Geom:new{} - end - self._length = tsize.x - self._height = self.face.size * 1.5 - return Geom:new{ - w = self._length, - h = self._height, - } + --if not self._bb then + --self:_render() + --end + --return { w = self._length, h = self._bb:getHeight() } + local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold) + if not tsize then + return Geom:new{} + end + self._length = tsize.x + self._height = self.face.size * 1.5 + return Geom:new{ + w = self._length, + h = self._height, + } end function TextWidget:paintTo(bb, x, y) - --if not self._bb then - --self:_render() - --end - --bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight()) - --@TODO Don't use kerning for monospaced fonts. (houqp) - RenderText:renderUtf8Text(bb, x, y+self._height*0.7, self.face, self.text, true, self.bold, - self.bgcolor, self.fgcolor, self.width) + --if not self._bb then + --self:_render() + --end + --bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight()) + --@TODO Don't use kerning for monospaced fonts. (houqp) + RenderText:renderUtf8Text(bb, x, y+self._height*0.7, self.face, self.text, true, self.bold, + self.bgcolor, self.fgcolor, self.width) end function TextWidget:free() - if self._bb then - self._bb:free() - self._bb = nil - end + if self._bb then + self._bb:free() + self._bb = nil + end end return TextWidget diff --git a/frontend/ui/widget/toggleswitch.lua b/frontend/ui/widget/toggleswitch.lua index f9bf7dad7..e52fd468f 100644 --- a/frontend/ui/widget/toggleswitch.lua +++ b/frontend/ui/widget/toggleswitch.lua @@ -14,126 +14,126 @@ local DEBUG = require("dbg") local _ = require("gettext") local ToggleLabel = TextWidget:new{ - bold = true, - bgcolor = 0, - fgcolor = 1, + bold = true, + bgcolor = 0, + fgcolor = 1, } function ToggleLabel:paintTo(bb, x, y) - RenderText:renderUtf8Text(bb, x, y+self._height*0.75, self.face, self.text, true, self.bold, self.bgcolor, self.fgcolor) + RenderText:renderUtf8Text(bb, x, y+self._height*0.75, self.face, self.text, true, self.bold, self.bgcolor, self.fgcolor) end local ToggleSwitch = InputContainer:new{ - width = Screen:scaleByDPI(216), - height = Screen:scaleByDPI(30), - bgcolor = 0, -- unfoused item color - fgcolor = 7, -- focused item color + width = Screen:scaleByDPI(216), + height = Screen:scaleByDPI(30), + bgcolor = 0, -- unfoused item color + fgcolor = 7, -- focused item color } function ToggleSwitch:init() - self.n_pos = #self.toggle - self.position = nil + self.n_pos = #self.toggle + self.position = nil - local label_font_face = "cfont" - local label_font_size = 16 + local label_font_face = "cfont" + local label_font_size = 16 - self.toggle_frame = FrameContainer:new{background = 0, color = 7, radius = 7, bordersize = 1, padding = 2,} - self.toggle_content = HorizontalGroup:new{} + self.toggle_frame = FrameContainer:new{background = 0, color = 7, radius = 7, bordersize = 1, padding = 2,} + self.toggle_content = HorizontalGroup:new{} - for i=1,#self.toggle do - local label = ToggleLabel:new{ - align = "center", - text = self.toggle[i], - face = Font:getFace(label_font_face, label_font_size), - } - local content = CenterContainer:new{ - dimen = Geom:new{w = self.width/self.n_pos, h = self.height}, - label, - } - local button = FrameContainer:new{ - background = 0, - color = 7, - margin = 0, - radius = 5, - bordersize = 1, - padding = 0, - content, - } - table.insert(self.toggle_content, button) - end + for i=1,#self.toggle do + local label = ToggleLabel:new{ + align = "center", + text = self.toggle[i], + face = Font:getFace(label_font_face, label_font_size), + } + local content = CenterContainer:new{ + dimen = Geom:new{w = self.width/self.n_pos, h = self.height}, + label, + } + local button = FrameContainer:new{ + background = 0, + color = 7, + margin = 0, + radius = 5, + bordersize = 1, + padding = 0, + content, + } + table.insert(self.toggle_content, button) + end - self.toggle_frame[1] = self.toggle_content - self[1] = self.toggle_frame - self.dimen = Geom:new(self.toggle_frame:getSize()) - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = _("Toggle switch"), - }, - } - end + self.toggle_frame[1] = self.toggle_content + self[1] = self.toggle_frame + self.dimen = Geom:new(self.toggle_frame:getSize()) + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = _("Toggle switch"), + }, + } + end end function ToggleSwitch:update() - local pos = self.position - for i=1,#self.toggle_content do - if pos == i then - self.toggle_content[i].color = self.fgcolor - self.toggle_content[i].background = self.fgcolor - self.toggle_content[i][1][1].bgcolor = self.fgcolor/15 - self.toggle_content[i][1][1].fgcolor = 0.0 - else - self.toggle_content[i].color = self.bgcolor - self.toggle_content[i].background = self.bgcolor - self.toggle_content[i][1][1].bgcolor = 0.0 - self.toggle_content[i][1][1].fgcolor = 1.0 - end - end + local pos = self.position + for i=1,#self.toggle_content do + if pos == i then + self.toggle_content[i].color = self.fgcolor + self.toggle_content[i].background = self.fgcolor + self.toggle_content[i][1][1].bgcolor = self.fgcolor/15 + self.toggle_content[i][1][1].fgcolor = 0.0 + else + self.toggle_content[i].color = self.bgcolor + self.toggle_content[i].background = self.bgcolor + self.toggle_content[i][1][1].bgcolor = 0.0 + self.toggle_content[i][1][1].fgcolor = 1.0 + end + end end function ToggleSwitch:setPosition(position) - self.position = position - self:update() + self.position = position + self:update() end function ToggleSwitch:togglePosition(position) - if self.n_pos == 2 and self.alternate ~= false then - self.position = (self.position+1)%self.n_pos - self.position = self.position == 0 and self.n_pos or self.position - elseif self.n_pos == 1 then - self.position = self.position == 1 and 0 or 1 - else - self.position = position - end - self:update() + if self.n_pos == 2 and self.alternate ~= false then + self.position = (self.position+1)%self.n_pos + self.position = self.position == 0 and self.n_pos or self.position + elseif self.n_pos == 1 then + self.position = self.position == 1 and 0 or 1 + else + self.position = position + end + self:update() end function ToggleSwitch:onTapSelect(arg, gev) - local position = math.ceil( - (gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos - ) - --DEBUG("toggle position:", position) - self:togglePosition(position) - --[[ - if self.values then - self.values = self.values or {} - self.config:onConfigChoice(self.name, self.values[self.position]) - end - if self.event then - self.args = self.args or {} - self.config:onConfigEvent(self.event, self.args[self.position]) - end - if self.events then - self.config:onConfigEvents(self.events, self.position) - end - --]] - self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.position) - UIManager:setDirty(self.config, "partial") - return true + local position = math.ceil( + (gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos + ) + --DEBUG("toggle position:", position) + self:togglePosition(position) + --[[ + if self.values then + self.values = self.values or {} + self.config:onConfigChoice(self.name, self.values[self.position]) + end + if self.event then + self.args = self.args or {} + self.config:onConfigEvent(self.event, self.args[self.position]) + end + if self.events then + self.config:onConfigEvents(self.events, self.position) + end + --]] + self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.position) + UIManager:setDirty(self.config, "partial") + return true end return ToggleSwitch diff --git a/frontend/ui/widget/touchmenu.lua b/frontend/ui/widget/touchmenu.lua index 21ab3f873..3d0c3bdd8 100644 --- a/frontend/ui/widget/touchmenu.lua +++ b/frontend/ui/widget/touchmenu.lua @@ -22,49 +22,49 @@ local _ = require("gettext") TouchMenuItem widget --]] local TouchMenuItem = InputContainer:new{ - menu = nil, - vertical_align = "center", - item = nil, - dimen = nil, - face = Font:getFace("cfont", 22), - show_parent = nil, + menu = nil, + vertical_align = "center", + item = nil, + dimen = nil, + face = Font:getFace("cfont", 22), + show_parent = nil, } function TouchMenuItem:init() - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - doc = _("Select Menu Item"), - }, - } - - self.item_frame = FrameContainer:new{ - width = self.dimen.w, - bordersize = 0, - color = 15, - HorizontalGroup:new { - align = "center", - HorizontalSpan:new{ width = 10 }, - TextWidget:new{ - text = self.item.text or self.item.text_func(), - face = self.face, - }, - }, - } - self[1] = self.item_frame + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = _("Select Menu Item"), + }, + } + + self.item_frame = FrameContainer:new{ + width = self.dimen.w, + bordersize = 0, + color = 15, + HorizontalGroup:new { + align = "center", + HorizontalSpan:new{ width = 10 }, + TextWidget:new{ + text = self.item.text or self.item.text_func(), + face = self.face, + }, + }, + } + self[1] = self.item_frame end function TouchMenuItem:onTapSelect(arg, ges) - self.item_frame.invert = true - UIManager:setDirty(self.show_parent, "partial") - UIManager:scheduleIn(0.5, function() - self.item_frame.invert = false - UIManager:setDirty(self.show_parent, "partial") - end) - self.menu:onMenuSelect(self.item) + self.item_frame.invert = true + UIManager:setDirty(self.show_parent, "partial") + UIManager:scheduleIn(0.5, function() + self.item_frame.invert = false + UIManager:setDirty(self.show_parent, "partial") + end) + self.menu:onMenuSelect(self.item) end @@ -72,97 +72,97 @@ end TouchMenuBar widget --]] local TouchMenuBar = InputContainer:new{ - height = Screen:scaleByDPI(70), - width = Screen:getWidth(), - icons = {}, - -- touch menu that holds the bar, used for trigger repaint on icons - show_parent = nil, - menu = nil, + height = Screen:scaleByDPI(70), + width = Screen:getWidth(), + icons = {}, + -- touch menu that holds the bar, used for trigger repaint on icons + show_parent = nil, + menu = nil, } function TouchMenuBar:init() - self.show_parent = self.show_parent or self - - self.dimen = Geom:new{ - w = self.width, - h = self.height, - } - - self.bar_icon_group = HorizontalGroup:new{} - - local icon_sep = LineWidget:new{ - dimen = Geom:new{ - w = Screen:scaleByDPI(2), - h = self.height, - } - } - - local icon_span = HorizontalSpan:new{ width = Screen:scaleByDPI(20) } - - -- build up image widget for menu icon bar - self.icon_widgets = {} - -- the start_seg for first icon_widget should be 0 - -- we asign negative here to offset it in the loop - start_seg = -icon_sep:getSize().w - end_seg = start_seg - for k, v in ipairs(self.icons) do - local ib = IconButton:new{ - show_parent = self.show_parent, - icon_file = v, - callback = nil, - } - - table.insert(self.icon_widgets, HorizontalGroup:new{ - icon_span, - ib, - icon_span, - }) - - -- we have to use local variable here for closure callback - local _start_seg = end_seg + icon_sep:getSize().w - local _end_seg = _start_seg + self.icon_widgets[k]:getSize().w - - if k == 1 then - self.bar_sep = LineWidget:new{ - dimen = Geom:new{ - w = self.width, - h = Screen:scaleByDPI(2), - }, - empty_segments = { - { - s = _start_seg, e = _end_seg - } - }, - } - end - - ib.callback = function() - self.bar_sep.empty_segments = { - { - s = _start_seg, e = _end_seg - } - } - self.menu:switchMenuTab(k) - end - - table.insert(self.bar_icon_group, self.icon_widgets[k]) - table.insert(self.bar_icon_group, icon_sep) - - start_seg = _start_seg - end_seg = _end_seg - end - - self[1] = FrameContainer:new{ - bordersize = 0, - padding = 0, - VerticalGroup:new{ - align = "left", - -- bar icons - self.bar_icon_group, - -- separate line - self.bar_sep - }, - } + self.show_parent = self.show_parent or self + + self.dimen = Geom:new{ + w = self.width, + h = self.height, + } + + self.bar_icon_group = HorizontalGroup:new{} + + local icon_sep = LineWidget:new{ + dimen = Geom:new{ + w = Screen:scaleByDPI(2), + h = self.height, + } + } + + local icon_span = HorizontalSpan:new{ width = Screen:scaleByDPI(20) } + + -- build up image widget for menu icon bar + self.icon_widgets = {} + -- the start_seg for first icon_widget should be 0 + -- we asign negative here to offset it in the loop + start_seg = -icon_sep:getSize().w + end_seg = start_seg + for k, v in ipairs(self.icons) do + local ib = IconButton:new{ + show_parent = self.show_parent, + icon_file = v, + callback = nil, + } + + table.insert(self.icon_widgets, HorizontalGroup:new{ + icon_span, + ib, + icon_span, + }) + + -- we have to use local variable here for closure callback + local _start_seg = end_seg + icon_sep:getSize().w + local _end_seg = _start_seg + self.icon_widgets[k]:getSize().w + + if k == 1 then + self.bar_sep = LineWidget:new{ + dimen = Geom:new{ + w = self.width, + h = Screen:scaleByDPI(2), + }, + empty_segments = { + { + s = _start_seg, e = _end_seg + } + }, + } + end + + ib.callback = function() + self.bar_sep.empty_segments = { + { + s = _start_seg, e = _end_seg + } + } + self.menu:switchMenuTab(k) + end + + table.insert(self.bar_icon_group, self.icon_widgets[k]) + table.insert(self.bar_icon_group, icon_sep) + + start_seg = _start_seg + end_seg = _end_seg + end + + self[1] = FrameContainer:new{ + bordersize = 0, + padding = 0, + VerticalGroup:new{ + align = "left", + -- bar icons + self.bar_icon_group, + -- separate line + self.bar_sep + }, + } end @@ -170,258 +170,258 @@ end TouchMenu widget --]] local TouchMenu = InputContainer:new{ - tab_item_table = {}, - -- for returnning in multi-level menus - item_table_stack = nil, - item_table = nil, - item_height = Screen:scaleByDPI(50), - bordersize = Screen:scaleByDPI(2), - padding = Screen:scaleByDPI(5), - footer_height = Screen:scaleByDPI(50), - width = nil, - height = nil, - page = 1, - max_per_page = 10, - -- for UIManager:setDirty - show_parent = nil, - cur_tab = -1, - close_callback = nil, + tab_item_table = {}, + -- for returnning in multi-level menus + item_table_stack = nil, + item_table = nil, + item_height = Screen:scaleByDPI(50), + bordersize = Screen:scaleByDPI(2), + padding = Screen:scaleByDPI(5), + footer_height = Screen:scaleByDPI(50), + width = nil, + height = nil, + page = 1, + max_per_page = 10, + -- for UIManager:setDirty + show_parent = nil, + cur_tab = -1, + close_callback = nil, } function TouchMenu:init() - self.show_parent = self.show_parent or self - if not self.close_callback then - self.close_callback = function() - UIManager:close(self.show_parent) - end - end - - self.ges_events.TapCloseAllMenus = { - GestureRange:new{ - ges = "tap", - range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - } - } - self.ges_events.Swipe = { - GestureRange:new{ - ges = "swipe", - range = self.dimen, - } - } - - local icons = {} - for _,v in ipairs(self.tab_item_table) do - table.insert(icons, v.icon) - end - self.bar = TouchMenuBar:new{ - width = self.width - self.padding * 2 - self.bordersize * 2, - icons = icons, - show_parent = self.show_parent, - menu = self, - } - - self.item_group = VerticalGroup:new{ - align = "left", - } - - self.footer_page = TextWidget:new{ - face = Font:getFace("ffont", 20), - text = "", - } - self.time_info = TextWidget:new{ - face = Font:getFace("ffont", 20), - text = "", - } - local footer_width = self.width - self.padding*2 - self.bordersize*2 - self.footer = HorizontalGroup:new{ - LeftContainer:new{ - dimen = Geom:new{ w = footer_width*0.33, h = self.footer_height}, - IconButton:new{ - invert = true, - icon_file = "resources/icons/appbar.chevron.up.png", - show_parent = self.show_parent, - callback = function() - self:backToUpperMenu() - end, - }, - }, - CenterContainer:new{ - dimen = Geom:new{ w = footer_width*0.33, h = self.footer_height}, - self.footer_page, - }, - RightContainer:new{ - dimen = Geom:new{ w = footer_width*0.33, h = self.footer_height}, - self.time_info, - } - } - - self[1] = FrameContainer:new{ - padding = self.padding, - bordersize = self.bordersize, - background = 0, - -- menubar and footer will be inserted in - -- item_group in updateItems - self.item_group, - } - - self:switchMenuTab(1) - self:updateItems() + self.show_parent = self.show_parent or self + if not self.close_callback then + self.close_callback = function() + UIManager:close(self.show_parent) + end + end + + self.ges_events.TapCloseAllMenus = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + self.ges_events.Swipe = { + GestureRange:new{ + ges = "swipe", + range = self.dimen, + } + } + + local icons = {} + for _,v in ipairs(self.tab_item_table) do + table.insert(icons, v.icon) + end + self.bar = TouchMenuBar:new{ + width = self.width - self.padding * 2 - self.bordersize * 2, + icons = icons, + show_parent = self.show_parent, + menu = self, + } + + self.item_group = VerticalGroup:new{ + align = "left", + } + + self.footer_page = TextWidget:new{ + face = Font:getFace("ffont", 20), + text = "", + } + self.time_info = TextWidget:new{ + face = Font:getFace("ffont", 20), + text = "", + } + local footer_width = self.width - self.padding*2 - self.bordersize*2 + self.footer = HorizontalGroup:new{ + LeftContainer:new{ + dimen = Geom:new{ w = footer_width*0.33, h = self.footer_height}, + IconButton:new{ + invert = true, + icon_file = "resources/icons/appbar.chevron.up.png", + show_parent = self.show_parent, + callback = function() + self:backToUpperMenu() + end, + }, + }, + CenterContainer:new{ + dimen = Geom:new{ w = footer_width*0.33, h = self.footer_height}, + self.footer_page, + }, + RightContainer:new{ + dimen = Geom:new{ w = footer_width*0.33, h = self.footer_height}, + self.time_info, + } + } + + self[1] = FrameContainer:new{ + padding = self.padding, + bordersize = self.bordersize, + background = 0, + -- menubar and footer will be inserted in + -- item_group in updateItems + self.item_group, + } + + self:switchMenuTab(1) + self:updateItems() end function TouchMenu:_recalculateDimen() - self.dimen.w = self.width - - -- if height not given, dynamically calculate it - if not self.height then - self.dimen.h = (#self.item_table + 2) * self.item_height - + self.bar:getSize().h - else - self.dimen.h = self.height - end - -- make sure self.dimen.h does not overflow screen height - if self.dimen.h > Screen:getHeight() then - self.dimen.h = Screen:getHeight() - self.bar:getSize().h - end - - self.perpage = math.floor(self.dimen.h / self.item_height) - 2 - if self.perpage > self.max_per_page then - self.perpage = self.max_per_page - end - - self.page_num = math.ceil(#self.item_table / self.perpage) + self.dimen.w = self.width + + -- if height not given, dynamically calculate it + if not self.height then + self.dimen.h = (#self.item_table + 2) * self.item_height + + self.bar:getSize().h + else + self.dimen.h = self.height + end + -- make sure self.dimen.h does not overflow screen height + if self.dimen.h > Screen:getHeight() then + self.dimen.h = Screen:getHeight() - self.bar:getSize().h + end + + self.perpage = math.floor(self.dimen.h / self.item_height) - 2 + if self.perpage > self.max_per_page then + self.perpage = self.max_per_page + end + + self.page_num = math.ceil(#self.item_table / self.perpage) end function TouchMenu:updateItems() - self:_recalculateDimen() - self.item_group:clear() - table.insert(self.item_group, self.bar) - - local item_width = self.dimen.w - self.padding*2 - self.bordersize*2 - - for c = 1, self.perpage do - -- calculate index in item_table - local i = (self.page - 1) * self.perpage + c - if i <= #self.item_table then - local item_tmp = TouchMenuItem:new{ - item = self.item_table[i], - menu = self, - dimen = Geom:new{ - w = item_width, - h = self.item_height, - }, - show_parent = self.show_parent, - } - table.insert(self.item_group, item_tmp) - -- insert split line - if c ~= self.perpage then - table.insert(self.item_group, HorizontalGroup:new{ - -- pad with 10 pixel to align with the up arrow in footer - HorizontalSpan:new{width = 10}, - LineWidget:new{ - style = "dashed", - dimen = Geom:new{ - w = item_width - 20, - h = 1, - } - } - }) - end - else - -- item not enough to fill the whole page, break out of loop - --table.insert(self.item_group, - --VerticalSpan:new{ - --width = self.item_height - --}) - --break - end -- if i <= self.items - end -- for c=1, self.perpage - - table.insert(self.item_group, VerticalSpan:new{width = Screen:scaleByDPI(2)}) - table.insert(self.item_group, self.footer) - self.footer_page.text = _("Page ")..self.page.."/"..self.page_num - self.time_info.text = os.date("%H:%M") - -- FIXME: this is a dirty hack to clear previous menus - -- refert to issue #664 - UIManager.repaint_all = true + self:_recalculateDimen() + self.item_group:clear() + table.insert(self.item_group, self.bar) + + local item_width = self.dimen.w - self.padding*2 - self.bordersize*2 + + for c = 1, self.perpage do + -- calculate index in item_table + local i = (self.page - 1) * self.perpage + c + if i <= #self.item_table then + local item_tmp = TouchMenuItem:new{ + item = self.item_table[i], + menu = self, + dimen = Geom:new{ + w = item_width, + h = self.item_height, + }, + show_parent = self.show_parent, + } + table.insert(self.item_group, item_tmp) + -- insert split line + if c ~= self.perpage then + table.insert(self.item_group, HorizontalGroup:new{ + -- pad with 10 pixel to align with the up arrow in footer + HorizontalSpan:new{width = 10}, + LineWidget:new{ + style = "dashed", + dimen = Geom:new{ + w = item_width - 20, + h = 1, + } + } + }) + end + else + -- item not enough to fill the whole page, break out of loop + --table.insert(self.item_group, + --VerticalSpan:new{ + --width = self.item_height + --}) + --break + end -- if i <= self.items + end -- for c=1, self.perpage + + table.insert(self.item_group, VerticalSpan:new{width = Screen:scaleByDPI(2)}) + table.insert(self.item_group, self.footer) + self.footer_page.text = _("Page ")..self.page.."/"..self.page_num + self.time_info.text = os.date("%H:%M") + -- FIXME: this is a dirty hack to clear previous menus + -- refert to issue #664 + UIManager.repaint_all = true end function TouchMenu:switchMenuTab(tab_num) - if self.tab_item_table[tab_num].callback then - self.tab_item_table[tab_num].callback() - end - if self.cur_tab ~= tab_num then - -- it's like getting a new menu everytime we switch tab! - self.page = 1 - -- clear item table stack - self.item_table_stack = {} - self.cur_tab = tab_num - self.item_table = self.tab_item_table[tab_num] - self:updateItems() - end + if self.tab_item_table[tab_num].callback then + self.tab_item_table[tab_num].callback() + end + if self.cur_tab ~= tab_num then + -- it's like getting a new menu everytime we switch tab! + self.page = 1 + -- clear item table stack + self.item_table_stack = {} + self.cur_tab = tab_num + self.item_table = self.tab_item_table[tab_num] + self:updateItems() + end end function TouchMenu:backToUpperMenu() - if #self.item_table_stack ~= 0 then - self.item_table = table.remove(self.item_table_stack) - self.page = 1 - self:updateItems() - end + if #self.item_table_stack ~= 0 then + self.item_table = table.remove(self.item_table_stack) + self.page = 1 + self:updateItems() + end end function TouchMenu:closeMenu() - self.close_callback() + self.close_callback() end function TouchMenu:onNextPage() - if self.page < self.page_num then - self.page = self.page + 1 - self:updateItems() - end - return true + if self.page < self.page_num then + self.page = self.page + 1 + self:updateItems() + end + return true end function TouchMenu:onPrevPage() - if self.page > 1 then - self.page = self.page - 1 - self:updateItems() - end - return true + if self.page > 1 then + self.page = self.page - 1 + self:updateItems() + end + return true end function TouchMenu:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" or ges_ev.direction == "north" then - self:onNextPage() - elseif ges_ev.direction == "east" or ges_ev.direction == "south" then - self:onPrevPage() - end + if ges_ev.direction == "west" or ges_ev.direction == "north" then + self:onNextPage() + elseif ges_ev.direction == "east" or ges_ev.direction == "south" then + self:onPrevPage() + end end function TouchMenu:onMenuSelect(item) - if item.sub_item_table == nil then - if item.callback then - -- put stuff in scheduler so we can See - -- the effect of inverted menu item - UIManager:scheduleIn(0.1, function() - self:closeMenu() - item.callback() - end) - end - else - table.insert(self.item_table_stack, self.item_table) - self.item_table = item.sub_item_table - self:updateItems() - end - return true + if item.sub_item_table == nil then + if item.callback then + -- put stuff in scheduler so we can See + -- the effect of inverted menu item + UIManager:scheduleIn(0.1, function() + self:closeMenu() + item.callback() + end) + end + else + table.insert(self.item_table_stack, self.item_table) + self.item_table = item.sub_item_table + self:updateItems() + end + return true end function TouchMenu:onTapCloseAllMenus(arg, ges_ev) - if ges_ev.pos:notIntersectWith(self.dimen) then - self:closeMenu() - end + if ges_ev.pos:notIntersectWith(self.dimen) then + self:closeMenu() + end end return TouchMenu diff --git a/frontend/ui/widget/verticalgroup.lua b/frontend/ui/widget/verticalgroup.lua index fe94c1f52..510c3adea 100644 --- a/frontend/ui/widget/verticalgroup.lua +++ b/frontend/ui/widget/verticalgroup.lua @@ -4,61 +4,61 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer") A Layout widget that puts objects under each other --]] local VerticalGroup = WidgetContainer:new{ - align = "center", - _size = nil, - _offsets = {} + align = "center", + _size = nil, + _offsets = {} } function VerticalGroup:getSize() - if not self._size then - self._size = { w = 0, h = 0 } - self._offsets = { } - for i, widget in ipairs(self) do - local w_size = widget:getSize() - self._offsets[i] = { - x = w_size.w, - y = self._size.h, - } - self._size.h = self._size.h + w_size.h - if w_size.w > self._size.w then - self._size.w = w_size.w - end - end - end - return self._size + if not self._size then + self._size = { w = 0, h = 0 } + self._offsets = { } + for i, widget in ipairs(self) do + local w_size = widget:getSize() + self._offsets[i] = { + x = w_size.w, + y = self._size.h, + } + self._size.h = self._size.h + w_size.h + if w_size.w > self._size.w then + self._size.w = w_size.w + end + end + end + return self._size end function VerticalGroup:paintTo(bb, x, y) - local size = self:getSize() + local size = self:getSize() - for i, widget in ipairs(self) do - if self.align == "center" then - widget:paintTo(bb, - x + math.floor((size.w - self._offsets[i].x) / 2), - y + self._offsets[i].y) - elseif self.align == "left" then - widget:paintTo(bb, x, y + self._offsets[i].y) - elseif self.align == "right" then - widget:paintTo(bb, - x + size.w - self._offsets[i].x, - y + self._offsets[i].y) - end - end + for i, widget in ipairs(self) do + if self.align == "center" then + widget:paintTo(bb, + x + math.floor((size.w - self._offsets[i].x) / 2), + y + self._offsets[i].y) + elseif self.align == "left" then + widget:paintTo(bb, x, y + self._offsets[i].y) + elseif self.align == "right" then + widget:paintTo(bb, + x + size.w - self._offsets[i].x, + y + self._offsets[i].y) + end + end end function VerticalGroup:clear() - self:free() - WidgetContainer.clear(self) + self:free() + WidgetContainer.clear(self) end function VerticalGroup:resetLayout() - self._size = nil - self._offsets = {} + self._size = nil + self._offsets = {} end function VerticalGroup:free() - self:resetLayout() - WidgetContainer.free(self) + self:resetLayout() + WidgetContainer.free(self) end return VerticalGroup diff --git a/frontend/ui/widget/verticalscrollbar.lua b/frontend/ui/widget/verticalscrollbar.lua index fd16f6f73..06b7b3a32 100644 --- a/frontend/ui/widget/verticalscrollbar.lua +++ b/frontend/ui/widget/verticalscrollbar.lua @@ -2,37 +2,37 @@ local Widget = require("ui/widget/widget") local Geom = require("ui/geometry") local VerticalScrollBar = Widget:new{ - enable = true, - low = 0, - high = 1, - - width = 6, - height = 50, - bordersize = 1, - bordercolor = 15, - radius = 0, - rectcolor = 15, + enable = true, + low = 0, + high = 1, + + width = 6, + height = 50, + bordersize = 1, + bordercolor = 15, + radius = 0, + rectcolor = 15, } function VerticalScrollBar:getSize() - return Geom:new{ - w = self.width, - h = self.height - } + return Geom:new{ + w = self.width, + h = self.height + } end function VerticalScrollBar:set(low, high) - self.low = low > 0 and low or 0 - self.high = high < 1 and high or 1 + self.low = low > 0 and low or 0 + self.high = high < 1 and high or 1 end function VerticalScrollBar:paintTo(bb, x, y) - if not self.enable then return end - bb:paintBorder(x, y, self.width, self.height, - self.bordersize, self.bordercolor, self.radius) - bb:paintRect(x + self.bordersize, y + self.bordersize + self.low*self.height, - self.width - 2*self.bordersize, - self.height * (self.high - self.low), self.rectcolor) + if not self.enable then return end + bb:paintBorder(x, y, self.width, self.height, + self.bordersize, self.bordercolor, self.radius) + bb:paintRect(x + self.bordersize, y + self.bordersize + self.low*self.height, + self.width - 2*self.bordersize, + self.height * (self.high - self.low), self.rectcolor) end return VerticalScrollBar diff --git a/frontend/ui/widget/verticalspan.lua b/frontend/ui/widget/verticalspan.lua index c3c897f71..db2f16302 100644 --- a/frontend/ui/widget/verticalspan.lua +++ b/frontend/ui/widget/verticalspan.lua @@ -4,11 +4,11 @@ local Widget = require("ui/widget/widget") Dummy Widget that reserves vertical space --]] local VerticalSpan = Widget:new{ - width = 0, + width = 0, } function VerticalSpan:getSize() - return {w = 0, h = self.width} + return {w = 0, h = self.width} end return VerticalSpan diff --git a/frontend/ui/widget/virtualkeyboard.lua b/frontend/ui/widget/virtualkeyboard.lua index 8d16130f2..1836d2e9e 100644 --- a/frontend/ui/widget/virtualkeyboard.lua +++ b/frontend/ui/widget/virtualkeyboard.lua @@ -17,281 +17,281 @@ local UIManager = require("ui/uimanager") local DEBUG = require("dbg") local VirtualKey = InputContainer:new{ - key = nil, - icon = nil, - label = nil, - - keyboard = nil, - callback = nil, - - width = nil, - height = nil, - bordersize = 2, - face = Font:getFace("infont", 22), + key = nil, + icon = nil, + label = nil, + + keyboard = nil, + callback = nil, + + width = nil, + height = nil, + bordersize = 2, + face = Font:getFace("infont", 22), } function VirtualKey:init() - if self.label == "Sym" or self.label == "ABC" then - self.callback = function () self.keyboard:setLayout(self.key or self.label) end - elseif self.label == "Shift" then - self.callback = function () self.keyboard:setLayout(self.key or self.label) end - elseif self.label == "IM" then - self.callback = function () self.keyboard:setLayout(self.key or self.label) end - elseif self.label == "Backspace" then - self.callback = function () self.keyboard:delChar() end - else - self.callback = function () self.keyboard:addChar(self.key) end - end - - local label_widget = nil - if self.icon then - label_widget = ImageWidget:new{ - file = self.icon, - } - else - label_widget = TextWidget:new{ - text = self.label, - face = self.face, - } - end - self[1] = FrameContainer:new{ - margin = 0, - bordersize = self.bordersize, - background = 0, - radius = 5, - padding = 0, - CenterContainer:new{ - dimen = Geom:new{ - w = self.width - 2*self.bordersize, - h = self.height - 2*self.bordersize, - }, - label_widget, - }, - } - self.dimen = Geom:new{ - w = self.width, - h = self.height, - } - --self.dimen = self[1]:getSize() - if Device:isTouchDevice() then - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, - }, - }, - } - end + if self.label == "Sym" or self.label == "ABC" then + self.callback = function () self.keyboard:setLayout(self.key or self.label) end + elseif self.label == "Shift" then + self.callback = function () self.keyboard:setLayout(self.key or self.label) end + elseif self.label == "IM" then + self.callback = function () self.keyboard:setLayout(self.key or self.label) end + elseif self.label == "Backspace" then + self.callback = function () self.keyboard:delChar() end + else + self.callback = function () self.keyboard:addChar(self.key) end + end + + local label_widget = nil + if self.icon then + label_widget = ImageWidget:new{ + file = self.icon, + } + else + label_widget = TextWidget:new{ + text = self.label, + face = self.face, + } + end + self[1] = FrameContainer:new{ + margin = 0, + bordersize = self.bordersize, + background = 0, + radius = 5, + padding = 0, + CenterContainer:new{ + dimen = Geom:new{ + w = self.width - 2*self.bordersize, + h = self.height - 2*self.bordersize, + }, + label_widget, + }, + } + self.dimen = Geom:new{ + w = self.width, + h = self.height, + } + --self.dimen = self[1]:getSize() + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + }, + } + end end function VirtualKey:onTapSelect() - self[1].invert = true - if self.callback then - self.callback() - end - UIManager:scheduleIn(0.02, function() self:invert(false) end) - return true + self[1].invert = true + if self.callback then + self.callback() + end + UIManager:scheduleIn(0.02, function() self:invert(false) end) + return true end function VirtualKey:invert(invert) - self[1].invert = invert - UIManager.update_region_func = function() - DEBUG("update key region", self[1].dimen) - return self[1].dimen - end - UIManager:setDirty(self.keyboard, "partial") + self[1].invert = invert + UIManager.update_region_func = function() + DEBUG("update key region", self[1].dimen) + return self[1].dimen + end + UIManager:setDirty(self.keyboard, "partial") end local VirtualKeyboard = InputContainer:new{ - is_always_active = true, - disable_double_tap = true, - inputbox = nil, - KEYS = {}, -- table to store layouts - min_layout = 2, - max_layout = 9, - layout = 2, - shiftmode = false, - symbolmode = false, - utf8mode = false, - - width = 600, - height = 256, - bordersize = 2, - padding = 2, - key_padding = Screen:scaleByDPI(6), + is_always_active = true, + disable_double_tap = true, + inputbox = nil, + KEYS = {}, -- table to store layouts + min_layout = 2, + max_layout = 9, + layout = 2, + shiftmode = false, + symbolmode = false, + utf8mode = false, + + width = 600, + height = 256, + bordersize = 2, + padding = 2, + key_padding = Screen:scaleByDPI(6), } function VirtualKeyboard:init() - self.KEYS = { - -- first row - { - { "Q", "q", "1", "!", "Я", "я", "1", "!", }, - { "W", "w", "2", "?", "Ж", "ж", "2", "?", }, - { "E", "e", "3", "|", "Е", "е", "3", "«", }, - { "R", "r", "4", "#", "Р", "р", "4", "»", }, - { "T", "t", "5", "@", "Т", "т", "5", ":", }, - { "Y", "y", "6", "‰", "Ы", "ы", "6", ";", }, - { "U", "u", "7", "'", "У", "у", "7", "~", }, - { "I", "i", "8", "`", "И", "и", "8", "(", }, - { "O", "o", "9", ":", "О", "о", "9", ")", }, - { "P", "p", "0", ";", "П", "п", "0", "=", }, - }, - -- second raw - { - { "A", "a", "+", "…", "А", "а", "Ш", "ш", }, - { "S", "s", "-", "_", "С", "с", "Ѕ", "ѕ", }, - { "D", "d", "*", "=", "Д", "д", "Э", "э", }, - { "F", "f", "/", "\\", "Ф", "ф", "Ю", "ю", }, - { "G", "g", "%", "„", "Г", "г", "Ґ", "ґ", }, - { "H", "h", "^", "“", "Ч", "ч", "Ј", "ј", }, - { "J", "j", "<", "”", "Й", "й", "І", "і", }, - { "K", "k", "=", "\"", "К", "к", "Ќ", "ќ", }, - { "L", "l", ">", "~", "Л", "л", "Љ", "љ", }, - }, - -- third raw - { - { label = "Shift", - icon = "resources/icons/appbar.arrow.shift.png", - width = 1.5 - }, - { "Z", "z", "(", "$", "З", "з", "Щ", "щ", }, - { "X", "x", ")", "€", "Х", "х", "№", "@", }, - { "C", "c", "&", "¥", "Ц", "ц", "Џ", "џ", }, - { "V", "v", ":", "£", "В", "в", "Ў", "ў", }, - { "B", "b", "π", "‚", "Б", "б", "Ћ", "ћ", }, - { "N", "n", "е", "‘", "Н", "н", "Њ", "њ", }, - { "M", "m", "~", "’", "М", "м", "Ї", "ї", }, - { label = "Backspace", - icon = "resources/icons/appbar.clear.reflect.horizontal.png", - width = 1.5 - }, - }, - -- fourth raw - { - { "Sym", "Sym", "ABC", "ABC", "Sym", "Sym", "ABC", "ABC", - width = 1.5}, - { label = "IM", - icon = "resources/icons/appbar.globe.wire.png", - }, - { label = "space", - " ", " ", " ", " ", " ", " ", " ", " ", - width = 5.0}, - { ",", ".", ".", ",", ",", ".", "Є", "є", }, - { label = "Enter", - "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", - icon = "resources/icons/appbar.arrow.enter.png", - width = 1.5, - }, - - } - } - self:initLayout(self.layout) + self.KEYS = { + -- first row + { + { "Q", "q", "1", "!", "Я", "я", "1", "!", }, + { "W", "w", "2", "?", "Ж", "ж", "2", "?", }, + { "E", "e", "3", "|", "Е", "е", "3", "«", }, + { "R", "r", "4", "#", "Р", "р", "4", "»", }, + { "T", "t", "5", "@", "Т", "т", "5", ":", }, + { "Y", "y", "6", "‰", "Ы", "ы", "6", ";", }, + { "U", "u", "7", "'", "У", "у", "7", "~", }, + { "I", "i", "8", "`", "И", "и", "8", "(", }, + { "O", "o", "9", ":", "О", "о", "9", ")", }, + { "P", "p", "0", ";", "П", "п", "0", "=", }, + }, + -- second raw + { + { "A", "a", "+", "…", "А", "а", "Ш", "ш", }, + { "S", "s", "-", "_", "С", "с", "Ѕ", "ѕ", }, + { "D", "d", "*", "=", "Д", "д", "Э", "э", }, + { "F", "f", "/", "\\", "Ф", "ф", "Ю", "ю", }, + { "G", "g", "%", "„", "Г", "г", "Ґ", "ґ", }, + { "H", "h", "^", "“", "Ч", "ч", "Ј", "ј", }, + { "J", "j", "<", "”", "Й", "й", "І", "і", }, + { "K", "k", "=", "\"", "К", "к", "Ќ", "ќ", }, + { "L", "l", ">", "~", "Л", "л", "Љ", "љ", }, + }, + -- third raw + { + { label = "Shift", + icon = "resources/icons/appbar.arrow.shift.png", + width = 1.5 + }, + { "Z", "z", "(", "$", "З", "з", "Щ", "щ", }, + { "X", "x", ")", "€", "Х", "х", "№", "@", }, + { "C", "c", "&", "¥", "Ц", "ц", "Џ", "џ", }, + { "V", "v", ":", "£", "В", "в", "Ў", "ў", }, + { "B", "b", "π", "‚", "Б", "б", "Ћ", "ћ", }, + { "N", "n", "е", "‘", "Н", "н", "Њ", "њ", }, + { "M", "m", "~", "’", "М", "м", "Ї", "ї", }, + { label = "Backspace", + icon = "resources/icons/appbar.clear.reflect.horizontal.png", + width = 1.5 + }, + }, + -- fourth raw + { + { "Sym", "Sym", "ABC", "ABC", "Sym", "Sym", "ABC", "ABC", + width = 1.5}, + { label = "IM", + icon = "resources/icons/appbar.globe.wire.png", + }, + { label = "space", + " ", " ", " ", " ", " ", " ", " ", " ", + width = 5.0}, + { ",", ".", ".", ",", ",", ".", "Є", "є", }, + { label = "Enter", + "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", + icon = "resources/icons/appbar.arrow.enter.png", + width = 1.5, + }, + + } + } + self:initLayout(self.layout) end function VirtualKeyboard:initLayout(layout) - local function VKLayout(b1, b2, b3) - local function boolnum(bool) - return bool and 1 or 0 - end - return 2 - boolnum(b1) + 2 * boolnum(b2) + 4 * boolnum(b3) - end - - if layout then - -- to be sure layout is selected properly - layout = math.max(layout, self.min_layout) - layout = math.min(layout, self.max_layout) - self.layout = layout - -- fill the layout modes - layout = layout % 4 - self.shiftmode = (layout == 1 or layout == 3) - self.symbolmode = (layout == 3 or layout == 4) - self.utf8mode = (self.layout > 5) - else -- or, without input parameter, restore layout from current layout modes - self.layout = VKLayout(self.shiftmode, self.symbolmode, self.utf8mode) - end - self:addKeys() + local function VKLayout(b1, b2, b3) + local function boolnum(bool) + return bool and 1 or 0 + end + return 2 - boolnum(b1) + 2 * boolnum(b2) + 4 * boolnum(b3) + end + + if layout then + -- to be sure layout is selected properly + layout = math.max(layout, self.min_layout) + layout = math.min(layout, self.max_layout) + self.layout = layout + -- fill the layout modes + layout = layout % 4 + self.shiftmode = (layout == 1 or layout == 3) + self.symbolmode = (layout == 3 or layout == 4) + self.utf8mode = (self.layout > 5) + else -- or, without input parameter, restore layout from current layout modes + self.layout = VKLayout(self.shiftmode, self.symbolmode, self.utf8mode) + end + self:addKeys() end function VirtualKeyboard:addKeys() - local base_key_width = math.floor((self.width - 11*self.key_padding - 2*self.padding)/10) - local base_key_height = math.floor((self.height - 5*self.key_padding - 2*self.padding)/4) - local h_key_padding = HorizontalSpan:new{width = self.key_padding} - local v_key_padding = VerticalSpan:new{width = self.key_padding} - local vertical_group = VerticalGroup:new{} - for i = 1, #self.KEYS do - local horizontal_group = HorizontalGroup:new{} - for j = 1, #self.KEYS[i] do - local width_factor = self.KEYS[i][j].width or 1.0 - local key_width = math.floor((base_key_width + self.key_padding) * width_factor) - - self.key_padding - local key_height = base_key_height - local label = self.KEYS[i][j].label or self.KEYS[i][j][self.layout] - local key = VirtualKey:new{ - key = self.KEYS[i][j][self.layout], - icon = self.KEYS[i][j].icon, - label = label, - keyboard = self, - width = key_width, - height = key_height, - } - table.insert(horizontal_group, key) - if j ~= #self.KEYS[i] then - table.insert(horizontal_group, h_key_padding) - end - end - table.insert(vertical_group, horizontal_group) - if i ~= #self.KEYS then - table.insert(vertical_group, v_key_padding) - end - end - - local size = vertical_group:getSize() - local keyboard_frame = FrameContainer:new{ - margin = 0, - bordersize = self.bordersize, - background = 0, - radius = 0, - padding = self.padding, - CenterContainer:new{ - dimen = Geom:new{ - w = self.width - 2*self.bordersize -2*self.padding, - h = self.height - 2*self.bordersize -2*self.padding, - }, - vertical_group, - } - } - self[1] = BottomContainer:new{ - dimen = Screen:getSize(), - keyboard_frame, - } - self.dimen = keyboard_frame:getSize() + local base_key_width = math.floor((self.width - 11*self.key_padding - 2*self.padding)/10) + local base_key_height = math.floor((self.height - 5*self.key_padding - 2*self.padding)/4) + local h_key_padding = HorizontalSpan:new{width = self.key_padding} + local v_key_padding = VerticalSpan:new{width = self.key_padding} + local vertical_group = VerticalGroup:new{} + for i = 1, #self.KEYS do + local horizontal_group = HorizontalGroup:new{} + for j = 1, #self.KEYS[i] do + local width_factor = self.KEYS[i][j].width or 1.0 + local key_width = math.floor((base_key_width + self.key_padding) * width_factor) + - self.key_padding + local key_height = base_key_height + local label = self.KEYS[i][j].label or self.KEYS[i][j][self.layout] + local key = VirtualKey:new{ + key = self.KEYS[i][j][self.layout], + icon = self.KEYS[i][j].icon, + label = label, + keyboard = self, + width = key_width, + height = key_height, + } + table.insert(horizontal_group, key) + if j ~= #self.KEYS[i] then + table.insert(horizontal_group, h_key_padding) + end + end + table.insert(vertical_group, horizontal_group) + if i ~= #self.KEYS then + table.insert(vertical_group, v_key_padding) + end + end + + local size = vertical_group:getSize() + local keyboard_frame = FrameContainer:new{ + margin = 0, + bordersize = self.bordersize, + background = 0, + radius = 0, + padding = self.padding, + CenterContainer:new{ + dimen = Geom:new{ + w = self.width - 2*self.bordersize -2*self.padding, + h = self.height - 2*self.bordersize -2*self.padding, + }, + vertical_group, + } + } + self[1] = BottomContainer:new{ + dimen = Screen:getSize(), + keyboard_frame, + } + self.dimen = keyboard_frame:getSize() end function VirtualKeyboard:setLayout(key) - if key == "Shift" then - self.shiftmode = not self.shiftmode - elseif key == "Sym" or key == "ABC" then - self.symbolmode = not self.symbolmode - elseif key == "IM" then - self.utf8mode = not self.utf8mode - end - self:initLayout() - UIManager:setDirty(self, "partial") + if key == "Shift" then + self.shiftmode = not self.shiftmode + elseif key == "Sym" or key == "ABC" then + self.symbolmode = not self.symbolmode + elseif key == "IM" then + self.utf8mode = not self.utf8mode + end + self:initLayout() + UIManager:setDirty(self, "partial") end function VirtualKeyboard:addChar(key) - DEBUG("add char", key) - self.inputbox:addChar(key) - UIManager:setDirty(self, "partial") - UIManager:setDirty(self.inputbox, "partial") + DEBUG("add char", key) + self.inputbox:addChar(key) + UIManager:setDirty(self, "partial") + UIManager:setDirty(self.inputbox, "partial") end function VirtualKeyboard:delChar() - DEBUG("delete char") - self.inputbox:delChar() - UIManager:setDirty(self, "partial") - UIManager:setDirty(self.inputbox, "partial") + DEBUG("delete char") + self.inputbox:delChar() + UIManager:setDirty(self, "partial") + UIManager:setDirty(self.inputbox, "partial") end return VirtualKeyboard diff --git a/frontend/ui/widget/widget.lua b/frontend/ui/widget/widget.lua index 96e14ddbf..6b7c19c0d 100644 --- a/frontend/ui/widget/widget.lua +++ b/frontend/ui/widget/widget.lua @@ -19,10 +19,10 @@ It only setup the metabale (or prototype chain) and will not initiatie a real instance, i.e. call self:init() --]] function Widget:extend(o) - local o = o or {} - setmetatable(o, self) - self.__index = self - return o + local o = o or {} + setmetatable(o, self) + self.__index = self + return o end --[[ @@ -30,18 +30,18 @@ Use this method to initiatie a instance of a class, don't use it for class definition. --]] function Widget:new(o) - o = self:extend(o) - -- Both o._init and o.init are called on object create. But o._init is used - -- for base widget initialization (basic component used to build other - -- widgets). While o.init is for higher level widgets, for example Menu - -- Widget - if o._init then o:_init() end - if o.init then o:init() end - return o + o = self:extend(o) + -- Both o._init and o.init are called on object create. But o._init is used + -- for base widget initialization (basic component used to build other + -- widgets). While o.init is for higher level widgets, for example Menu + -- Widget + if o._init then o:_init() end + if o.init then o:init() end + return o end function Widget:getSize() - return self.dimen + return self.dimen end function Widget:paintTo(bb, x, y) diff --git a/reader.lua b/reader.lua index 84377f7c0..97d288320 100755 --- a/reader.lua +++ b/reader.lua @@ -13,7 +13,7 @@ local _ = require("gettext") G_reader_settings = DocSettings:open(".reader") local lang_locale = G_reader_settings:readSetting("language") if lang_locale then - _.changeLang(lang_locale) + _.changeLang(lang_locale) end local UIManager = require("ui/uimanager") @@ -29,147 +29,147 @@ local Screen = require("ui/screen") Profiler = nil function exitReader() - if Profiler ~= nil then - Profiler:stop() - Profiler:dump("./profile.html") - end - - G_reader_settings:close() - - input.closeAll() - - if not util.isEmulated() then - if Device:isKindle3() or (Device:getModel() == "KindleDXG") then - -- send double menu key press events to trigger screen refresh - os.execute("echo 'send 139' > /proc/keypad;echo 'send 139' > /proc/keypad") - end - if Device:isTouchDevice() and Device.survive_screen_saver then - -- hack the swipe to unlock screen - local dev = Device:getTouchInputDev() - if dev then - local width, height = Screen:getWidth(), Screen:getHeight() - input.fakeTapInput(dev, - math.min(width, height)/2, - math.max(width, height)-30 - ) - end - end - end - - os.exit(0) + if Profiler ~= nil then + Profiler:stop() + Profiler:dump("./profile.html") + end + + G_reader_settings:close() + + input.closeAll() + + if not util.isEmulated() then + if Device:isKindle3() or (Device:getModel() == "KindleDXG") then + -- send double menu key press events to trigger screen refresh + os.execute("echo 'send 139' > /proc/keypad;echo 'send 139' > /proc/keypad") + end + if Device:isTouchDevice() and Device.survive_screen_saver then + -- hack the swipe to unlock screen + local dev = Device:getTouchInputDev() + if dev then + local width, height = Screen:getWidth(), Screen:getHeight() + input.fakeTapInput(dev, + math.min(width, height)/2, + math.max(width, height)-30 + ) + end + end + end + + os.exit(0) end function showReaderUI(file, pass) - DEBUG("opening file", file) - if lfs.attributes(file, "mode") ~= "file" then - UIManager:show(InfoMessage:new{ - text = _("File does not exist") - }) - return - end - UIManager:show(InfoMessage:new{ - text = _("opening file") .. file, - timeout = 1, - }) - UIManager:scheduleIn(0.1, function() doShowReaderUI(file, pass) end) + DEBUG("opening file", file) + if lfs.attributes(file, "mode") ~= "file" then + UIManager:show(InfoMessage:new{ + text = _("File does not exist") + }) + return + end + UIManager:show(InfoMessage:new{ + text = _("opening file") .. file, + timeout = 1, + }) + UIManager:scheduleIn(0.1, function() doShowReaderUI(file, pass) end) end function doShowReaderUI(file, pass) - local document = DocumentRegistry:openDocument(file) - if not document then - UIManager:show(InfoMessage:new{ - text = _("No reader engine for this file") - }) - return - end - - G_reader_settings:saveSetting("lastfile", file) - local reader = ReaderUI:new{ - dialog = readerwindow, - dimen = Screen:getSize(), - document = document, - password = pass - } - UIManager:show(reader) + local document = DocumentRegistry:openDocument(file) + if not document then + UIManager:show(InfoMessage:new{ + text = _("No reader engine for this file") + }) + return + end + + G_reader_settings:saveSetting("lastfile", file) + local reader = ReaderUI:new{ + dialog = readerwindow, + dimen = Screen:getSize(), + document = document, + password = pass + } + UIManager:show(reader) end function showHomePage(path) - UIManager:show(FileManager:new{ - dimen = Screen:getSize(), - root_path = path, - onExit = function() - exitReader() - UIManager:quit() - end - }) + UIManager:show(FileManager:new{ + dimen = Screen:getSize(), + root_path = path, + onExit = function() + exitReader() + UIManager:quit() + end + }) end -- option parsing: local longopts = { - debug = "d", - profile = "p", - help = "h", + debug = "d", + profile = "p", + help = "h", } function showusage() - print(_("usage: ./reader.lua [OPTION] ... path")) - print(_("Read all the books on your E-Ink reader")) - print("") - print(_("-d start in debug mode")) - print(_("-p [rows] enable Lua code profiling")) - print(_("-h show this usage help")) - print("") - print(_("If you give the name of a directory instead of a file path, a file")) - print(_("chooser will show up and let you select a file")) - print("") - print(_("If you don't pass any path, the last viewed document will be opened")) - print("") - print(_("This software is licensed under the GPLv3.")) - print(_("See http://github.com/koreader/kindlepdfviewer for more info.")) - return + print(_("usage: ./reader.lua [OPTION] ... path")) + print(_("Read all the books on your E-Ink reader")) + print("") + print(_("-d start in debug mode")) + print(_("-p [rows] enable Lua code profiling")) + print(_("-h show this usage help")) + print("") + print(_("If you give the name of a directory instead of a file path, a file")) + print(_("chooser will show up and let you select a file")) + print("") + print(_("If you don't pass any path, the last viewed document will be opened")) + print("") + print(_("This software is licensed under the GPLv3.")) + print(_("See http://github.com/koreader/kindlepdfviewer for more info.")) + return end local argidx = 1 while argidx <= #ARGV do - local arg = ARGV[argidx] - argidx = argidx + 1 - if arg == "--" then break end - -- parse longopts - if arg:sub(1,2) == "--" then - local opt = longopts[arg:sub(3)] - if opt ~= nil then arg = "-"..opt end - end - -- code for each option - if arg == "-h" then - return showusage() - elseif arg == "-d" then - DEBUG:turnOn() - elseif arg == "-p" then - local lulip = require("ffi/lulip") - Profiler = lulip:new() - pcall(function() - -- set maxrows only if the optional arg is numeric - Profiler:maxrows(ARGV[argidx] + 0) - argidx = argidx + 1 - end) - Profiler:start() - else - -- not a recognized option, should be a filename - argidx = argidx - 1 - break - end + local arg = ARGV[argidx] + argidx = argidx + 1 + if arg == "--" then break end + -- parse longopts + if arg:sub(1,2) == "--" then + local opt = longopts[arg:sub(3)] + if opt ~= nil then arg = "-"..opt end + end + -- code for each option + if arg == "-h" then + return showusage() + elseif arg == "-d" then + DEBUG:turnOn() + elseif arg == "-p" then + local lulip = require("ffi/lulip") + Profiler = lulip:new() + pcall(function() + -- set maxrows only if the optional arg is numeric + Profiler:maxrows(ARGV[argidx] + 0) + argidx = argidx + 1 + end) + Profiler:start() + else + -- not a recognized option, should be a filename + argidx = argidx - 1 + break + end end if Device:hasNoKeyboard() then - -- remove menu item shortcut for K4 - Menu.is_enable_shortcut = false + -- remove menu item shortcut for K4 + Menu.is_enable_shortcut = false end -- read some global reader setting here: -- font local fontmap = G_reader_settings:readSetting("fontmap") if fontmap ~= nil then - Font.fontmap = fontmap + Font.fontmap = fontmap end -- last file local last_file = G_reader_settings:readSetting("lastfile") @@ -179,26 +179,26 @@ local last_file = G_reader_settings:readSetting("lastfile") --87712cf0e43fed624f8a9f610be42b1fe174b9fe do - local powerd = Device:getPowerDevice() - if powerd and powerd.restore_settings then - local intensity = G_reader_settings:readSetting("frontlight_intensity") - intensity = intensity or powerd.flIntensity - powerd:setIntensity(intensity) - end + local powerd = Device:getPowerDevice() + if powerd and powerd.restore_settings then + local intensity = G_reader_settings:readSetting("frontlight_intensity") + intensity = intensity or powerd.flIntensity + powerd:setIntensity(intensity) + end end if ARGV[argidx] and ARGV[argidx] ~= "" then - if lfs.attributes(ARGV[argidx], "mode") == "directory" then - showHomePage(ARGV[argidx]) - else - showReaderUI(ARGV[argidx]) - end - UIManager:run() + if lfs.attributes(ARGV[argidx], "mode") == "directory" then + showHomePage(ARGV[argidx]) + else + showReaderUI(ARGV[argidx]) + end + UIManager:run() elseif last_file then - showReaderUI(last_file) - UIManager:run() + showReaderUI(last_file) + UIManager:run() else - return showusage() + return showusage() end exitReader() diff --git a/wtest.lua b/wtest.lua index 3e57f8421..306e20582 100755 --- a/wtest.lua +++ b/wtest.lua @@ -29,63 +29,63 @@ local Screen = require("ui/screen") TestGrid = Widget:new{} function TestGrid:paintTo(bb) - v_line = math.floor(bb:getWidth() / 50) - h_line = math.floor(bb:getHeight() / 50) - for i=1,h_line do - y_num = i*50 - RenderText:renderUtf8Text(bb, 0, y_num+10, Font:getFace("ffont", 12), y_num, true) - bb:paintRect(0, y_num, bb:getWidth(), 1, 10) - end - for i=1,v_line do - x_num = i*50 - RenderText:renderUtf8Text(bb, x_num, 10, Font:getFace("ffont", 12), x_num, true) - bb:paintRect(x_num, 0, 1, bb:getHeight(), 10) - end + v_line = math.floor(bb:getWidth() / 50) + h_line = math.floor(bb:getHeight() / 50) + for i=1,h_line do + y_num = i*50 + RenderText:renderUtf8Text(bb, 0, y_num+10, Font:getFace("ffont", 12), y_num, true) + bb:paintRect(0, y_num, bb:getWidth(), 1, 10) + end + for i=1,v_line do + x_num = i*50 + RenderText:renderUtf8Text(bb, x_num, 10, Font:getFace("ffont", 12), x_num, true) + bb:paintRect(x_num, 0, 1, bb:getHeight(), 10) + end end ----------------------------------------------------- -- we create a widget that paints a background: ----------------------------------------------------- Background = InputContainer:new{ - is_always_active = true, -- receive events when other dialogs are active - key_events = { - OpenDialog = { { "Press" } }, - OpenConfirmBox = { { "Del" } }, - QuitApplication = { { {"Home","Back"} } } - }, - -- contains a gray rectangular desktop - FrameContainer:new{ - background = 3, - bordersize = 0, - dimen = Screen:getSize(), - Widget:new{ - dimen = { - w = Screen:getWidth(), - h = Screen:getHeight(), - } - }, - } + is_always_active = true, -- receive events when other dialogs are active + key_events = { + OpenDialog = { { "Press" } }, + OpenConfirmBox = { { "Del" } }, + QuitApplication = { { {"Home","Back"} } } + }, + -- contains a gray rectangular desktop + FrameContainer:new{ + background = 3, + bordersize = 0, + dimen = Screen:getSize(), + Widget:new{ + dimen = { + w = Screen:getWidth(), + h = Screen:getHeight(), + } + }, + } } function Background:onOpenDialog() - UIManager:show(InfoMessage:new{ - text = "Example message.", - timeout = 10 - }) + UIManager:show(InfoMessage:new{ + text = "Example message.", + timeout = 10 + }) end function Background:onOpenConfirmBox() - UIManager:show(ConfirmBox:new{ - text = "Please confirm delete" - }) + UIManager:show(ConfirmBox:new{ + text = "Please confirm delete" + }) end function Background:onInputError() - UIManager:quit() + UIManager:quit() end function Background:onQuitApplication() - UIManager:quit() + UIManager:quit() end @@ -94,79 +94,79 @@ end -- example widget: a clock ----------------------------------------------------- Clock = FrameContainer:new{ - background = 0, - bordersize = 1, - margin = 0, - padding = 1 + background = 0, + bordersize = 1, + margin = 0, + padding = 1 } function Clock:schedFunc() - self[1]:free() - self[1] = self:getTextWidget() - UIManager:setDirty(self) - -- reschedule - -- TODO: wait until next real second shift - UIManager:scheduleIn(1, function() self:schedFunc() end) + self[1]:free() + self[1] = self:getTextWidget() + UIManager:setDirty(self) + -- reschedule + -- TODO: wait until next real second shift + UIManager:scheduleIn(1, function() self:schedFunc() end) end function Clock:onShow() - self[1] = self:getTextWidget() - self:schedFunc() + self[1] = self:getTextWidget() + self:schedFunc() end function Clock:getTextWidget() - return CenterContainer:new{ - dimen = { w = 300, h = 25 }, - TextWidget:new{ - text = os.date("%H:%M:%S"), - face = Font:getFace("cfont", 12) - } - } + return CenterContainer:new{ + dimen = { w = 300, h = 25 }, + TextWidget:new{ + text = os.date("%H:%M:%S"), + face = Font:getFace("cfont", 12) + } + } end ----------------------------------------------------- -- a confirmbox box widget ----------------------------------------------------- Quiz = ConfirmBox:new{ - text = "Tell me the truth, isn't it COOL?!", - width = 300, - ok_text = "Yes, of course.", - cancel_text = "No, it's ugly.", - cancel_callback = function() - UIManager:show(InfoMessage:new{ - text="You liar!", - }) - end, + text = "Tell me the truth, isn't it COOL?!", + width = 300, + ok_text = "Yes, of course.", + cancel_text = "No, it's ugly.", + cancel_callback = function() + UIManager:show(InfoMessage:new{ + text="You liar!", + }) + end, } ----------------------------------------------------- -- a menu widget ----------------------------------------------------- menu_items = { - {text = "item1"}, - {text = "item2"}, - {text = "This is a very very log item whose length should exceed the width of the menu."}, - {text = "item3"}, - {text = "item4"}, - {text = "item5"}, - {text = "item6"}, - {text = "item7"}, - {text = "item8"}, - {text = "item9"}, - {text = "item10"}, - {text = "item11"}, - {text = "item12"}, - {text = "item13"}, - {text = "item14"}, - {text = "item15"}, - {text = "item16"}, - {text = "item17"}, + {text = "item1"}, + {text = "item2"}, + {text = "This is a very very log item whose length should exceed the width of the menu."}, + {text = "item3"}, + {text = "item4"}, + {text = "item5"}, + {text = "item6"}, + {text = "item7"}, + {text = "item8"}, + {text = "item9"}, + {text = "item10"}, + {text = "item11"}, + {text = "item12"}, + {text = "item13"}, + {text = "item14"}, + {text = "item15"}, + {text = "item16"}, + {text = "item17"}, } M = Menu:new{ - title = "Test Menu", - item_table = menu_items, - width = 500, - height = 600, + title = "Test Menu", + item_table = menu_items, + width = 500, + height = 600, } @@ -174,98 +174,98 @@ M = Menu:new{ -- a reader view widget ----------------------------------------------------- readerwindow = CenterContainer:new{ - dimen = Screen:getSize(), - FrameContainer:new{ - background = 0 - } + dimen = Screen:getSize(), + FrameContainer:new{ + background = 0 + } } reader = ReaderUI:new{ - dialog = readerwindow, - dimen = Geom:new{ w = Screen:getWidth() - 100, h = Screen:getHeight() - 100 }, - document = DocumentRegistry:openDocument("test/2col.pdf") - --document = DocumentRegistry:openDocument("test/djvu3spec.djvu") - --document = DocumentRegistry:openDocument("./README.TXT") + dialog = readerwindow, + dimen = Geom:new{ w = Screen:getWidth() - 100, h = Screen:getHeight() - 100 }, + document = DocumentRegistry:openDocument("test/2col.pdf") + --document = DocumentRegistry:openDocument("test/djvu3spec.djvu") + --document = DocumentRegistry:openDocument("./README.TXT") } readerwindow[1][1] = reader touch_menu = TouchMenu:new{ - title = "Document menu", - width = Screen:getWidth(), - tab_item_table = { - { - icon = "resources/icons/appbar.pokeball.png", - { - text = "item1", - callback = function() - end, - }, - { - text = "item2", - callback = function() - end, - }, - { - text = "item3", - callback = function() - end, - }, - { - text = "item4", - callback = function() - end, - }, - { - text = "item5", - callback = function() - end, - }, - { - text = "item6", - callback = function() - end, - }, - { - text = "item7", - callback = function() - end, - }, - { - text = "item8", - callback = function() - end, - }, - { - text = "item9", - callback = function() - end, - }, - }, - { - icon = "resources/icons/appbar.page.corner.bookmark.png", - { - text = "item10", - callback = function() - end, - }, - { - text = "item11", - callback = function() - end, - }, - }, - { - icon = "resources/icons/appbar.home.png", - callback = function() - DEBUG("hello world!") - end - } - }, + title = "Document menu", + width = Screen:getWidth(), + tab_item_table = { + { + icon = "resources/icons/appbar.pokeball.png", + { + text = "item1", + callback = function() + end, + }, + { + text = "item2", + callback = function() + end, + }, + { + text = "item3", + callback = function() + end, + }, + { + text = "item4", + callback = function() + end, + }, + { + text = "item5", + callback = function() + end, + }, + { + text = "item6", + callback = function() + end, + }, + { + text = "item7", + callback = function() + end, + }, + { + text = "item8", + callback = function() + end, + }, + { + text = "item9", + callback = function() + end, + }, + }, + { + icon = "resources/icons/appbar.page.corner.bookmark.png", + { + text = "item10", + callback = function() + end, + }, + { + text = "item11", + callback = function() + end, + }, + }, + { + icon = "resources/icons/appbar.home.png", + callback = function() + DEBUG("hello world!") + end + } + }, } inputtext = InputText:new{ - width = 400, - height = 300, + width = 400, + height = 300, } -----------------------------------------------------------------------