Merge pull request #531 from chrox/retab

cleanup: expand tab to 4 spaces
pull/536/merge
Qingping Hou 10 years ago
commit 3e7d518207

@ -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} DTAP_ZONE_FLIPPING = {x = 0, y = 0, w = 1/8, h = 1/8}
-- koptreader config defaults -- koptreader config defaults
DKOPTREADER_CONFIG_FONT_SIZE = 1.0 -- range from 0.1 to 3.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_TEXT_WRAP = 0 -- 1 = on, 0 = off
DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual
DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable
DKOPTREADER_CONFIG_DEFECT_SIZE = 1.0 -- range from 0.0 to 3.0 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_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_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_RENDER_QUALITY = 1.0 -- range from 0.5 to 2.0
DKOPTREADER_CONFIG_AUTO_STRAIGHTEN = 0 -- range from 0 to 10 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_JUSTIFICATION = 3 -- -1 = auto, 0 = left, 1 = center, 2 = right, 3 = full
DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4 DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4
DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0 DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0
-- word spacing for reflow -- word spacing for reflow
DKOPTREADER_CONFIG_WORD_SAPCINGS = {0.05, -1, 0.375} -- range from 0.05 to 0.5 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 -- document languages for OCR
DKOPTREADER_CONFIG_DOC_LANGS_TEXT = {"English", "Chinese"} 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_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_DEFAULT_LANG_CODE = "eng" -- that have filenames starting with the language codes
-- crereader font sizes -- crereader font sizes
-- feel free to add more entries in this list -- feel free to add more entries in this list
@ -170,5 +170,5 @@ DDICT_FONT_SIZE = 20
--DPICVIEWER_PAGE_MODE_ENABLE = false --DPICVIEWER_PAGE_MODE_ENABLE = false
--DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off --DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off
--DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees --DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees

@ -17,175 +17,175 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local FileManager = InputContainer:extend{ local FileManager = InputContainer:extend{
title = _("FileManager"), title = _("FileManager"),
width = Screen:getWidth(), width = Screen:getWidth(),
height = Screen:getHeight(), height = Screen:getHeight(),
root_path = lfs.currentdir(), root_path = lfs.currentdir(),
-- our own size -- our own size
dimen = Geom:new{ w = 400, h = 600 }, dimen = Geom:new{ w = 400, h = 600 },
onExit = function() end, onExit = function() end,
} }
function FileManager:init() function FileManager:init()
local exclude_dirs = {"%.sdr$"} local exclude_dirs = {"%.sdr$"}
self.show_parent = self.show_parent or self self.show_parent = self.show_parent or self
self.banner = VerticalGroup:new{ self.banner = VerticalGroup:new{
TextWidget:new{ TextWidget:new{
face = Font:getFace("tfont", 24), face = Font:getFace("tfont", 24),
text = self.title, text = self.title,
}, },
VerticalSpan:new{ width = Screen:scaleByDPI(10) } VerticalSpan:new{ width = Screen:scaleByDPI(10) }
} }
local g_show_hidden = G_reader_settings:readSetting("show_hidden") local g_show_hidden = G_reader_settings:readSetting("show_hidden")
local show_hidden = g_show_hidden == nil and DSHOWHIDDENFILES or g_show_hidden local show_hidden = g_show_hidden == nil and DSHOWHIDDENFILES or g_show_hidden
local file_chooser = FileChooser:new{ local file_chooser = FileChooser:new{
-- remeber to adjust the height when new item is added to the group -- remeber to adjust the height when new item is added to the group
path = self.root_path, path = self.root_path,
show_parent = self.show_parent, show_parent = self.show_parent,
show_hidden = show_hidden, show_hidden = show_hidden,
height = Screen:getHeight() - self.banner:getSize().h, height = Screen:getHeight() - self.banner:getSize().h,
is_popout = false, is_popout = false,
is_borderless = true, is_borderless = true,
has_close_button = true, has_close_button = true,
dir_filter = function(dirname) dir_filter = function(dirname)
for _, pattern in ipairs(exclude_dirs) do for _, pattern in ipairs(exclude_dirs) do
if dirname:match(pattern) then return end if dirname:match(pattern) then return end
end end
return true return true
end, end,
file_filter = function(filename) file_filter = function(filename)
if DocumentRegistry:getProvider(filename) then if DocumentRegistry:getProvider(filename) then
return true return true
end end
end end
} }
self.file_chooser = file_chooser self.file_chooser = file_chooser
function file_chooser:onFileSelect(file) function file_chooser:onFileSelect(file)
showReaderUI(file) showReaderUI(file)
return true return true
end end
local copyFile = function(file) self:copyFile(file) end local copyFile = function(file) self:copyFile(file) end
local pasteHere = function(file) self:pasteHere(file) end local pasteHere = function(file) self:pasteHere(file) end
local cutFile = function(file) self:cutFile(file) end local cutFile = function(file) self:cutFile(file) end
local deleteFile = function(file) self:deleteFile(file) end local deleteFile = function(file) self:deleteFile(file) end
local fileManager = self local fileManager = self
function file_chooser:onFileHold(file) function file_chooser:onFileHold(file)
--DEBUG("hold file", file) --DEBUG("hold file", file)
self.file_dialog = ButtonDialog:new{ self.file_dialog = ButtonDialog:new{
buttons = { buttons = {
{ {
{ {
text = _("Copy"), text = _("Copy"),
callback = function() callback = function()
copyFile(file) copyFile(file)
UIManager:close(self.file_dialog) UIManager:close(self.file_dialog)
end, end,
}, },
{ {
text = _("Paste"), text = _("Paste"),
enabled = fileManager.clipboard and true or false, enabled = fileManager.clipboard and true or false,
callback = function() callback = function()
pasteHere(file) pasteHere(file)
self:refreshPath() self:refreshPath()
UIManager:close(self.file_dialog) UIManager:close(self.file_dialog)
end, end,
}, },
}, },
{ {
{ {
text = _("Cut"), text = _("Cut"),
callback = function() callback = function()
cutFile(file) cutFile(file)
UIManager:close(self.file_dialog) UIManager:close(self.file_dialog)
end, end,
}, },
{ {
text = _("Delete"), text = _("Delete"),
callback = function() callback = function()
local path = util.realpath(file) local path = util.realpath(file)
deleteFile(file) deleteFile(file)
self:refreshPath() self:refreshPath()
UIManager:close(self.file_dialog) UIManager:close(self.file_dialog)
end, end,
}, },
}, },
}, },
} }
UIManager:show(self.file_dialog) UIManager:show(self.file_dialog)
return true return true
end end
self.layout = VerticalGroup:new{ self.layout = VerticalGroup:new{
self.banner, self.banner,
file_chooser, file_chooser,
} }
local fm_ui = FrameContainer:new{ local fm_ui = FrameContainer:new{
padding = 0, padding = 0,
bordersize = 0, bordersize = 0,
background = 0, background = 0,
self.layout, self.layout,
} }
self[1] = fm_ui self[1] = fm_ui
self.menu = FileManagerMenu:new{ self.menu = FileManagerMenu:new{
ui = self ui = self
} }
table.insert(self, self.menu) table.insert(self, self.menu)
table.insert(self, FileManagerHistory:new{ table.insert(self, FileManagerHistory:new{
ui = self, ui = self,
menu = self.menu menu = self.menu
}) })
self:handleEvent(Event:new("SetDimensions", self.dimen)) self:handleEvent(Event:new("SetDimensions", self.dimen))
end end
function FileManager:toggleHiddenFiles() function FileManager:toggleHiddenFiles()
self.file_chooser:toggleHiddenFiles() self.file_chooser:toggleHiddenFiles()
G_reader_settings:saveSetting("show_hidden", self.file_chooser.show_hidden) G_reader_settings:saveSetting("show_hidden", self.file_chooser.show_hidden)
end end
function FileManager:onClose() function FileManager:onClose()
UIManager:close(self) UIManager:close(self)
if self.onExit then if self.onExit then
self:onExit() self:onExit()
end end
return true return true
end end
function FileManager:copyFile(file) function FileManager:copyFile(file)
self.cutfile = false self.cutfile = false
self.clipboard = file self.clipboard = file
end end
function FileManager:cutFile(file) function FileManager:cutFile(file)
self.cutfile = true self.cutfile = true
self.clipboard = file self.clipboard = file
end end
function FileManager:pasteHere(file) function FileManager:pasteHere(file)
if self.clipboard then if self.clipboard then
file = util.realpath(file) file = util.realpath(file)
local orig = util.realpath(self.clipboard) local orig = util.realpath(self.clipboard)
local dest = lfs.attributes(file, "mode") == "directory" and local dest = lfs.attributes(file, "mode") == "directory" and
file or file:match("(.*/)") file or file:match("(.*/)")
if self.cutfile then if self.cutfile then
util.execute("/bin/mv", orig, dest) util.execute("/bin/mv", orig, dest)
else else
util.execute("/bin/cp", "-r", orig, dest) util.execute("/bin/cp", "-r", orig, dest)
end end
end end
end end
function FileManager:deleteFile(file) function FileManager:deleteFile(file)
util.execute("/bin/rm", "-r", util.realpath(file)) util.execute("/bin/rm", "-r", util.realpath(file))
end end
return FileManager return FileManager

@ -11,103 +11,103 @@ local _ = require("gettext")
local history_dir = "./history/" local history_dir = "./history/"
local FileManagerHistory = InputContainer:extend{ local FileManagerHistory = InputContainer:extend{
hist_menu_title = _("History"), hist_menu_title = _("History"),
} }
function FileManagerHistory:init() function FileManagerHistory:init()
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function FileManagerHistory:onSetDimensions(dimen) function FileManagerHistory:onSetDimensions(dimen)
self.dimen = dimen self.dimen = dimen
end end
function FileManagerHistory:onMenuHold(item) function FileManagerHistory:onMenuHold(item)
self.histfile_dialog = ButtonDialog:new{ self.histfile_dialog = ButtonDialog:new{
buttons = { buttons = {
{ {
{ {
text = _("Delete"), text = _("Delete"),
callback = function() callback = function()
os.remove(history_dir..item.histfile) os.remove(history_dir..item.histfile)
self._manager:updateItemTable() self._manager:updateItemTable()
UIManager:close(self.histfile_dialog) UIManager:close(self.histfile_dialog)
end, end,
}, },
}, },
}, },
} }
UIManager:show(self.histfile_dialog) UIManager:show(self.histfile_dialog)
return true return true
end end
function FileManagerHistory:onShowHist() function FileManagerHistory:onShowHist()
local menu_container = CenterContainer:new{ local menu_container = CenterContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
} }
self.hist_menu = Menu:new{ self.hist_menu = Menu:new{
ui = self.ui, ui = self.ui,
width = Screen:getWidth()-50, width = Screen:getWidth()-50,
height = Screen:getHeight()-50, height = Screen:getHeight()-50,
show_parent = menu_container, show_parent = menu_container,
onMenuHold = self.onMenuHold, onMenuHold = self.onMenuHold,
_manager = self, _manager = self,
} }
self:updateItemTable() self:updateItemTable()
table.insert(menu_container, self.hist_menu) table.insert(menu_container, self.hist_menu)
self.hist_menu.close_callback = function() self.hist_menu.close_callback = function()
UIManager:close(menu_container) UIManager:close(menu_container)
end end
UIManager:show(menu_container) UIManager:show(menu_container)
return true return true
end end
function FileManagerHistory:addToMainMenu(tab_item_table) function FileManagerHistory:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.main, { table.insert(tab_item_table.main, {
text = self.hist_menu_title, text = self.hist_menu_title,
callback = function() callback = function()
self:onShowHist() self:onShowHist()
end, end,
}) })
end end
function FileManagerHistory:updateItemTable() function FileManagerHistory:updateItemTable()
function readHistDir(re) function readHistDir(re)
local sorted_files = {} local sorted_files = {}
for f in lfs.dir(history_dir) do for f in lfs.dir(history_dir) do
local path = history_dir..f local path = history_dir..f
if lfs.attributes(path, "mode") == "file" then if lfs.attributes(path, "mode") == "file" then
table.insert(sorted_files, {file = f, date = lfs.attributes(path, "modification")}) table.insert(sorted_files, {file = f, date = lfs.attributes(path, "modification")})
end end
end end
table.sort(sorted_files, function(v1,v2) return v1.date > v2.date end) table.sort(sorted_files, function(v1,v2) return v1.date > v2.date end)
for _, v in pairs(sorted_files) do for _, v in pairs(sorted_files) do
table.insert(re, { table.insert(re, {
dir = DocSettings:getPathFromHistory(v.file), dir = DocSettings:getPathFromHistory(v.file),
name = DocSettings:getNameFromHistory(v.file), name = DocSettings:getNameFromHistory(v.file),
histfile = v.file, histfile = v.file,
}) })
end end
end end
self.hist = {} self.hist = {}
local last_files = {} local last_files = {}
readHistDir(last_files) readHistDir(last_files)
for _,v in pairs(last_files) do for _,v in pairs(last_files) do
table.insert(self.hist, { table.insert(self.hist, {
text = v.name, text = v.name,
histfile = v.histfile, histfile = v.histfile,
callback = function() callback = function()
showReaderUI(v.dir .. "/" .. v.name) showReaderUI(v.dir .. "/" .. v.name)
end end
}) })
end end
self.hist_menu:swithItemTable(self.hist_menu_title, self.hist) self.hist_menu:swithItemTable(self.hist_menu_title, self.hist)
end end
return FileManagerHistory return FileManagerHistory

@ -12,143 +12,143 @@ local Language = require("ui/language")
local _ = require("gettext") local _ = require("gettext")
local FileManagerMenu = InputContainer:extend{ local FileManagerMenu = InputContainer:extend{
tab_item_table = nil, tab_item_table = nil,
registered_widgets = {}, registered_widgets = {},
} }
function FileManagerMenu:init() function FileManagerMenu:init()
self.tab_item_table = { self.tab_item_table = {
main = { main = {
icon = "resources/icons/appbar.pokeball.png", icon = "resources/icons/appbar.pokeball.png",
}, },
home = { home = {
icon = "resources/icons/appbar.home.png", icon = "resources/icons/appbar.home.png",
callback = function() callback = function()
UIManager:close(self.menu_container) UIManager:close(self.menu_container)
self.ui:onClose() self.ui:onClose()
end, end,
}, },
} }
self.registered_widgets = {} self.registered_widgets = {}
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ShowMenu = { { "Menu" }, doc = _("show menu") }, ShowMenu = { { "Menu" }, doc = _("show menu") },
} }
end end
end end
function FileManagerMenu:initGesListener() function FileManagerMenu:initGesListener()
self.ges_events = { self.ges_events = {
TapShowMenu = { TapShowMenu = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, x = 0,
y = 0, y = 0,
w = Screen:getWidth()*3/4, w = Screen:getWidth()*3/4,
h = Screen:getHeight()/4, h = Screen:getHeight()/4,
} }
} }
}, },
} }
end end
function FileManagerMenu:setUpdateItemTable() function FileManagerMenu:setUpdateItemTable()
for _, widget in pairs(self.registered_widgets) do for _, widget in pairs(self.registered_widgets) do
widget:addToMainMenu(self.tab_item_table) widget:addToMainMenu(self.tab_item_table)
end end
table.insert(self.tab_item_table.main, { table.insert(self.tab_item_table.main, {
text = _("Toggle hidden files"), text = _("Toggle hidden files"),
callback = function() callback = function()
self.ui:toggleHiddenFiles() self.ui:toggleHiddenFiles()
end end
}) })
if Device:hasFrontlight() then if Device:hasFrontlight() then
ReaderFrontLight:addToMainMenu(self.tab_item_table) ReaderFrontLight:addToMainMenu(self.tab_item_table)
end end
table.insert(self.tab_item_table.main, { table.insert(self.tab_item_table.main, {
text = _("Help"), text = _("Help"),
callback = function() callback = function()
UIManager:show(InfoMessage:new{ 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"), text = _("Please report bugs to https://github.com/koreader/ koreader/issues, Click at the bottom of the page for more options"),
}) })
end end
}) })
table.insert(self.tab_item_table.main, { table.insert(self.tab_item_table.main, {
text = _("Version"), text = _("Version"),
callback = function() callback = function()
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = io.open("git-rev", "r"):read(), text = io.open("git-rev", "r"):read(),
}) })
end end
}) })
table.insert(self.tab_item_table.main, Language:getLangMenuTable()) table.insert(self.tab_item_table.main, Language:getLangMenuTable())
end end
function FileManagerMenu:onShowMenu() function FileManagerMenu:onShowMenu()
if #self.tab_item_table.main == 0 then if #self.tab_item_table.main == 0 then
self:setUpdateItemTable() self:setUpdateItemTable()
end end
local menu_container = CenterContainer:new{ local menu_container = CenterContainer:new{
ignore = "height", ignore = "height",
dimen = Screen:getSize(), dimen = Screen:getSize(),
} }
local main_menu = nil local main_menu = nil
if Device:isTouchDevice() then if Device:isTouchDevice() then
main_menu = TouchMenu:new{ main_menu = TouchMenu:new{
width = Screen:getWidth(), width = Screen:getWidth(),
tab_item_table = { tab_item_table = {
self.tab_item_table.main, self.tab_item_table.main,
self.tab_item_table.home, self.tab_item_table.home,
}, },
show_parent = menu_container, show_parent = menu_container,
} }
else else
main_menu = Menu:new{ main_menu = Menu:new{
title = _("File manager menu"), title = _("File manager menu"),
item_table = {}, item_table = {},
width = Screen:getWidth() - 100, width = Screen:getWidth() - 100,
} }
for _,item_table in pairs(self.tab_item_table) do for _,item_table in pairs(self.tab_item_table) do
for k,v in ipairs(item_table) do for k,v in ipairs(item_table) do
table.insert(main_menu.item_table, v) table.insert(main_menu.item_table, v)
end end
end end
end end
main_menu.close_callback = function () main_menu.close_callback = function ()
UIManager:close(menu_container) UIManager:close(menu_container)
end end
menu_container[1] = main_menu menu_container[1] = main_menu
-- maintain a reference to menu_container -- maintain a reference to menu_container
self.menu_container = menu_container self.menu_container = menu_container
UIManager:show(menu_container) UIManager:show(menu_container)
return true return true
end end
function FileManagerMenu:onTapShowMenu() function FileManagerMenu:onTapShowMenu()
self:onShowMenu() self:onShowMenu()
return true return true
end end
function FileManagerMenu:onSetDimensions(dimen) function FileManagerMenu:onSetDimensions(dimen)
-- update listening according to new screen dimen -- update listening according to new screen dimen
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function FileManagerMenu:registerToMainMenu(widget) function FileManagerMenu:registerToMainMenu(widget)
table.insert(self.registered_widgets, widget) table.insert(self.registered_widgets, widget)
end end
return FileManagerMenu return FileManagerMenu

@ -2,98 +2,98 @@
A global LRU cache A global LRU cache
]]-- ]]--
local function calcFreeMem() local function calcFreeMem()
local meminfo = io.open("/proc/meminfo", "r") local meminfo = io.open("/proc/meminfo", "r")
local freemem = 0 local freemem = 0
if meminfo then if meminfo then
for line in meminfo:lines() do for line in meminfo:lines() do
local free, buffer, cached, n local free, buffer, cached, n
free, n = line:gsub("^MemFree:%s-(%d+) kB", "%1") free, n = line:gsub("^MemFree:%s-(%d+) kB", "%1")
if n ~= 0 then freemem = freemem + tonumber(free)*1024 end if n ~= 0 then freemem = freemem + tonumber(free)*1024 end
buffer, n = line:gsub("^Buffers:%s-(%d+) kB", "%1") buffer, n = line:gsub("^Buffers:%s-(%d+) kB", "%1")
if n ~= 0 then freemem = freemem + tonumber(buffer)*1024 end if n ~= 0 then freemem = freemem + tonumber(buffer)*1024 end
cached, n = line:gsub("^Cached:%s-(%d+) kB", "%1") cached, n = line:gsub("^Cached:%s-(%d+) kB", "%1")
if n ~= 0 then freemem = freemem + tonumber(cached)*1024 end if n ~= 0 then freemem = freemem + tonumber(cached)*1024 end
end end
meminfo:close() meminfo:close()
end end
return freemem return freemem
end end
local function calcCacheMemSize() local function calcCacheMemSize()
local min = DGLOBAL_CACHE_SIZE_MINIMUM local min = DGLOBAL_CACHE_SIZE_MINIMUM
local max = DGLOBAL_CACHE_SIZE_MAXIMUM local max = DGLOBAL_CACHE_SIZE_MAXIMUM
local calc = calcFreeMem()*(DGLOBAL_CACHE_FREE_PROPORTION or 0) local calc = calcFreeMem()*(DGLOBAL_CACHE_FREE_PROPORTION or 0)
return math.min(max, math.max(min, calc)) return math.min(max, math.max(min, calc))
end end
local Cache = { local Cache = {
-- cache configuration: -- cache configuration:
max_memsize = calcCacheMemSize(), max_memsize = calcCacheMemSize(),
-- cache state: -- cache state:
current_memsize = 0, current_memsize = 0,
-- associative cache -- associative cache
cache = {}, cache = {},
-- this will hold the LRU order of the cache -- this will hold the LRU order of the cache
cache_order = {} cache_order = {}
} }
function Cache:new(o) function Cache:new(o)
o = o or {} o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function Cache:insert(key, object) function Cache:insert(key, object)
-- guarantee that we have enough memory in cache -- guarantee that we have enough memory in cache
if(object.size > self.max_memsize) then if(object.size > self.max_memsize) then
-- we're not allowed to claim this much at all -- we're not allowed to claim this much at all
error("too much memory claimed") error("too much memory claimed")
end end
-- delete objects that least recently used -- delete objects that least recently used
-- (they are at the end of the cache_order array) -- (they are at the end of the cache_order array)
while self.current_memsize + object.size > self.max_memsize do while self.current_memsize + object.size > self.max_memsize do
local removed_key = table.remove(self.cache_order) local removed_key = table.remove(self.cache_order)
self.current_memsize = self.current_memsize - self.cache[removed_key].size self.current_memsize = self.current_memsize - self.cache[removed_key].size
self.cache[removed_key]:onFree() self.cache[removed_key]:onFree()
self.cache[removed_key] = nil self.cache[removed_key] = nil
end end
-- insert new object in front of the LRU order -- insert new object in front of the LRU order
table.insert(self.cache_order, 1, key) table.insert(self.cache_order, 1, key)
self.cache[key] = object self.cache[key] = object
self.current_memsize = self.current_memsize + object.size self.current_memsize = self.current_memsize + object.size
end end
function Cache:check(key) function Cache:check(key)
if self.cache[key] then if self.cache[key] then
if self.cache_order[1] ~= key then if self.cache_order[1] ~= key then
-- put key in front of the LRU list -- put key in front of the LRU list
for k, v in ipairs(self.cache_order) do for k, v in ipairs(self.cache_order) do
if v == key then if v == key then
table.remove(self.cache_order, k) table.remove(self.cache_order, k)
end end
end end
table.insert(self.cache_order, 1, key) table.insert(self.cache_order, 1, key)
end end
return self.cache[key] return self.cache[key]
end end
end end
function Cache:willAccept(size) function Cache:willAccept(size)
-- we only allow single objects to fill 75% of the cache -- we only allow single objects to fill 75% of the cache
if size*4 < self.max_memsize*3 then if size*4 < self.max_memsize*3 then
return true return true
end end
end end
-- blank the cache -- blank the cache
function Cache:clear() function Cache:clear()
for k, _ in pairs(self.cache) do for k, _ in pairs(self.cache) do
self.cache[k]:onFree() self.cache[k]:onFree()
end end
self.cache = {} self.cache = {}
self.cache_order = {} self.cache_order = {}
self.current_memsize = 0 self.current_memsize = 0
end end
return Cache return Cache

@ -3,14 +3,14 @@ Inheritable abstraction for cache items
--]] --]]
local CacheItem = { 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) function CacheItem:new(o)
o = o or {} o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function CacheItem:onFree() function CacheItem:onFree()

@ -1,45 +1,45 @@
local DocSettings = require("docsettings") -- for dump method local DocSettings = require("docsettings") -- for dump method
local Dbg = { local Dbg = {
is_on = false, is_on = false,
ev_log = nil, ev_log = nil,
} }
local Dbg_mt = {} local Dbg_mt = {}
local function LvDEBUG(lv, ...) local function LvDEBUG(lv, ...)
local line = "" local line = ""
for i,v in ipairs({...}) do for i,v in ipairs({...}) do
if type(v) == "table" then if type(v) == "table" then
line = line .. " " .. DocSettings:dump(v, lv) line = line .. " " .. DocSettings:dump(v, lv)
else else
line = line .. " " .. tostring(v) line = line .. " " .. tostring(v)
end end
end end
print("#"..line) print("#"..line)
end end
function Dbg_mt.__call(dbg, ...) function Dbg_mt.__call(dbg, ...)
if dbg.is_on then LvDEBUG(math.huge, ...) end if dbg.is_on then LvDEBUG(math.huge, ...) end
end end
function Dbg:turnOn() function Dbg:turnOn()
self.is_on = true self.is_on = true
-- create or clear ev log file -- create or clear ev log file
os.execute("echo > ev.log") os.execute("echo > ev.log")
self.ev_log = io.open("ev.log", "w") self.ev_log = io.open("ev.log", "w")
end end
function Dbg:logEv(ev) function Dbg:logEv(ev)
local log = ev.type.."|"..ev.code.."|" local log = ev.type.."|"..ev.code.."|"
..ev.value.."|"..ev.time.sec.."|"..ev.time.usec.."\n" ..ev.value.."|"..ev.time.sec.."|"..ev.time.usec.."\n"
self.ev_log:write(log) self.ev_log:write(log)
self.ev_log:flush() self.ev_log:flush()
end end
function Dbg:traceback() function Dbg:traceback()
LvDEBUG(math.huge, debug.traceback()) LvDEBUG(math.huge, debug.traceback())
end end
setmetatable(Dbg, Dbg_mt) setmetatable(Dbg, Dbg_mt)

@ -2,133 +2,133 @@ local DocSettings = {}
-- lfs -- lfs
function DocSettings:getHistoryPath(fullpath) function DocSettings:getHistoryPath(fullpath)
local i = #fullpath - 1 local i = #fullpath - 1
-- search for last slash -- search for last slash
while i > 0 do while i > 0 do
if fullpath:sub(i,i) == "/" then if fullpath:sub(i,i) == "/" then
break break
end end
i = i - 1 i = i - 1
end end
-- construct path to configuration file in history dir -- construct path to configuration file in history dir
local filename = fullpath:sub(i+1, -1) local filename = fullpath:sub(i+1, -1)
local basename = fullpath:sub(1, i) local basename = fullpath:sub(1, i)
return "./history/["..basename:gsub("/","#").."] "..filename..".lua" return "./history/["..basename:gsub("/","#").."] "..filename..".lua"
end end
function DocSettings:getPathFromHistory(hist_name) function DocSettings:getPathFromHistory(hist_name)
-- 1. select everything included in brackets -- 1. select everything included in brackets
local s = string.match(hist_name,"%b[]") local s = string.match(hist_name,"%b[]")
-- 2. crop the bracket-sign from both sides -- 2. crop the bracket-sign from both sides
-- 3. and finally replace decorative signs '#' to dir-char '/' -- 3. and finally replace decorative signs '#' to dir-char '/'
return string.gsub(string.sub(s,2,-3),"#","/") return string.gsub(string.sub(s,2,-3),"#","/")
end end
function DocSettings:getNameFromHistory(hist_name) function DocSettings:getNameFromHistory(hist_name)
-- at first, search for path length -- at first, search for path length
local s = string.len(string.match(hist_name,"%b[]")) local s = string.len(string.match(hist_name,"%b[]"))
-- and return the rest of string without 4 last characters (".lua") -- and return the rest of string without 4 last characters (".lua")
return string.sub(hist_name, s+2, -5) return string.sub(hist_name, s+2, -5)
end end
function DocSettings:open(docfile) function DocSettings:open(docfile)
local conf_path = nil local conf_path = nil
if docfile == ".reader" then if docfile == ".reader" then
-- we handle reader setting as special case -- we handle reader setting as special case
conf_path = "settings.reader.lua" conf_path = "settings.reader.lua"
else else
if lfs.attributes("./history","mode") ~= "directory" then if lfs.attributes("./history","mode") ~= "directory" then
lfs.mkdir("history") lfs.mkdir("history")
end end
conf_path = self:getHistoryPath(docfile) conf_path = self:getHistoryPath(docfile)
end end
-- construct settings obj -- construct settings obj
local new = { file = conf_path, data = {} } local new = { file = conf_path, data = {} }
local ok, stored = pcall(dofile, new.file) local ok, stored = pcall(dofile, new.file)
if not ok then if not ok then
-- try legacy conf path, for backward compatibility. this also -- try legacy conf path, for backward compatibility. this also
-- takes care of reader legacy setting -- takes care of reader legacy setting
ok, stored = pcall(dofile, docfile..".kpdfview.lua") ok, stored = pcall(dofile, docfile..".kpdfview.lua")
end end
if ok then if ok then
new.data = stored new.data = stored
end end
return setmetatable(new, { __index = DocSettings}) return setmetatable(new, { __index = DocSettings})
end end
function DocSettings:readSetting(key) function DocSettings:readSetting(key)
return self.data[key] return self.data[key]
end end
function DocSettings:saveSetting(key, value) function DocSettings:saveSetting(key, value)
self.data[key] = value self.data[key] = value
end end
function DocSettings:delSetting(key) function DocSettings:delSetting(key)
self.data[key] = nil self.data[key] = nil
end end
function DocSettings:dump(data, max_lv) function DocSettings:dump(data, max_lv)
local out = {} local out = {}
self:_serialize(data, out, 0, max_lv) self:_serialize(data, out, 0, max_lv)
return table.concat(out) return table.concat(out)
end end
-- simple serialization function, won't do uservalues, functions, loops -- simple serialization function, won't do uservalues, functions, loops
function DocSettings:_serialize(what, outt, indent, max_lv) function DocSettings:_serialize(what, outt, indent, max_lv)
if not max_lv then if not max_lv then
max_lv = math.huge max_lv = math.huge
end end
if indent > max_lv then if indent > max_lv then
return return
end end
if type(what) == "table" then if type(what) == "table" then
local didrun = false local didrun = false
table.insert(outt, "{") table.insert(outt, "{")
for k, v in pairs(what) do for k, v in pairs(what) do
if didrun then if didrun then
table.insert(outt, ",") table.insert(outt, ",")
end end
table.insert(outt, "\n") table.insert(outt, "\n")
table.insert(outt, string.rep("\t", indent+1)) table.insert(outt, string.rep("\t", indent+1))
table.insert(outt, "[") table.insert(outt, "[")
self:_serialize(k, outt, indent+1, max_lv) self:_serialize(k, outt, indent+1, max_lv)
table.insert(outt, "] = ") table.insert(outt, "] = ")
self:_serialize(v, outt, indent+1, max_lv) self:_serialize(v, outt, indent+1, max_lv)
didrun = true didrun = true
end end
if didrun then if didrun then
table.insert(outt, "\n") table.insert(outt, "\n")
table.insert(outt, string.rep("\t", indent)) table.insert(outt, string.rep("\t", indent))
end end
table.insert(outt, "}") table.insert(outt, "}")
elseif type(what) == "string" then elseif type(what) == "string" then
table.insert(outt, string.format("%q", what)) table.insert(outt, string.format("%q", what))
elseif type(what) == "number" or type(what) == "boolean" then elseif type(what) == "number" or type(what) == "boolean" then
table.insert(outt, tostring(what)) table.insert(outt, tostring(what))
end end
end end
function DocSettings:flush() function DocSettings:flush()
-- write a serialized version of the data table -- write a serialized version of the data table
if not self.file then if not self.file then
return return
end end
local f_out = io.open(self.file, "w") local f_out = io.open(self.file, "w")
if f_out ~= nil then if f_out ~= nil then
os.setlocale('C', 'numeric') os.setlocale('C', 'numeric')
local out = {"-- we can read Lua syntax here!\nreturn "} local out = {"-- we can read Lua syntax here!\nreturn "}
self:_serialize(self.data, out, 0) self:_serialize(self.data, out, 0)
table.insert(out, "\n") table.insert(out, "\n")
f_out:write(table.concat(out)) f_out:write(table.concat(out))
f_out:close() f_out:close()
end end
end end
function DocSettings:close() function DocSettings:close()
self:flush() self:flush()
end end
return DocSettings return DocSettings

@ -10,140 +10,140 @@ local DEBUG = require("dbg")
-- TBD: DrawContext -- TBD: DrawContext
local CreDocument = Document:new{ local CreDocument = Document:new{
-- this is defined in kpvcrlib/crengine/crengine/include/lvdocview.h -- this is defined in kpvcrlib/crengine/crengine/include/lvdocview.h
SCROLL_VIEW_MODE = 0, SCROLL_VIEW_MODE = 0,
PAGE_VIEW_MODE = 1, PAGE_VIEW_MODE = 1,
_document = false, _document = false,
engine_initilized = false, engine_initilized = false,
line_space_percent = 100, line_space_percent = 100,
default_font = G_reader_settings:readSetting("cre_font") or "FreeSerif", default_font = G_reader_settings:readSetting("cre_font") or "FreeSerif",
header_font = G_reader_settings:readSetting("header_font") or "FreeSans", header_font = G_reader_settings:readSetting("header_font") or "FreeSans",
fallback_font = G_reader_settings:readSetting("fallback_font") or "Droid Sans Fallback", fallback_font = G_reader_settings:readSetting("fallback_font") or "Droid Sans Fallback",
default_css = "./data/cr3.css", default_css = "./data/cr3.css",
options = CreOptions, options = CreOptions,
} }
-- NuPogodi, 20.05.12: inspect the zipfile content -- NuPogodi, 20.05.12: inspect the zipfile content
function CreDocument.zipContentExt(self, fname) function CreDocument.zipContentExt(self, fname)
local outfile = "./data/zip_content" local outfile = "./data/zip_content"
local s = "" local s = ""
os.execute("unzip ".."-l \""..fname.."\" > "..outfile) os.execute("unzip ".."-l \""..fname.."\" > "..outfile)
local i = 1 local i = 1
if io.open(outfile,"r") then if io.open(outfile,"r") then
for lines in io.lines(outfile) do for lines in io.lines(outfile) do
if i == 4 then s = lines break else i = i + 1 end if i == 4 then s = lines break else i = i + 1 end
end end
end end
-- return the extention -- return the extention
return string.lower(string.match(s, ".+%.([^.]+)")) return string.lower(string.match(s, ".+%.([^.]+)"))
end end
function CreDocument:engineInit() function CreDocument:engineInit()
if not engine_initilized then if not engine_initilized then
-- initialize cache -- initialize cache
cre.initCache(1024*1024*64) cre.initCache(1024*1024*64)
-- we need to initialize the CRE font list -- we need to initialize the CRE font list
local fonts = Font:getFontList() local fonts = Font:getFontList()
for _k, _v in ipairs(fonts) do for _k, _v in ipairs(fonts) do
if _v ~= "Dingbats.cff" and _v ~= "StandardSymL.cff" then if _v ~= "Dingbats.cff" and _v ~= "StandardSymL.cff" then
local ok, err = pcall(cre.registerFont, Font.fontdir..'/'.._v) local ok, err = pcall(cre.registerFont, Font.fontdir..'/'.._v)
if not ok then if not ok then
DEBUG(err) DEBUG(err)
end end
end end
end end
engine_initilized = true engine_initilized = true
end end
end end
function CreDocument:init() function CreDocument:init()
require "libs/libkoreader-cre" require "libs/libkoreader-cre"
self:engineInit() self:engineInit()
self.configurable:loadDefaults(self.options) self.configurable:loadDefaults(self.options)
local ok local ok
local file_type = string.lower(string.match(self.file, ".+%.([^.]+)")) local file_type = string.lower(string.match(self.file, ".+%.([^.]+)"))
if file_type == "zip" then if file_type == "zip" then
-- NuPogodi, 20.05.12: read the content of zip-file -- NuPogodi, 20.05.12: read the content of zip-file
-- and return extention of the 1st file -- and return extention of the 1st file
file_type = self:zipContentExt(self.file) file_type = self:zipContentExt(self.file)
end end
-- these two format use the same css file -- these two format use the same css file
if file_type == "html" then if file_type == "html" then
file_type = "htm" file_type = "htm"
end end
-- if native css-file doesn't exist, one needs to use default cr3.css -- if native css-file doesn't exist, one needs to use default cr3.css
if not io.open("./data/"..file_type..".css") then if not io.open("./data/"..file_type..".css") then
file_type = "cr3" file_type = "cr3"
end end
self.default_css = "./data/"..file_type..".css" self.default_css = "./data/"..file_type..".css"
-- @TODO check the default view_mode to a global user configurable -- @TODO check the default view_mode to a global user configurable
-- variable 22.12 2012 (houqp) -- variable 22.12 2012 (houqp)
ok, self._document = pcall(cre.newDocView, ok, self._document = pcall(cre.newDocView,
Screen:getWidth(), Screen:getHeight(), self.PAGE_VIEW_MODE Screen:getWidth(), Screen:getHeight(), self.PAGE_VIEW_MODE
) )
if not ok then if not ok then
self.error_message = self.doc -- will contain error message self.error_message = self.doc -- will contain error message
return return
end end
-- adjust font sizes according to screen dpi -- adjust font sizes according to screen dpi
self._document:adjustFontSizes(Screen:getDPI()) self._document:adjustFontSizes(Screen:getDPI())
-- set fallback font face -- set fallback font face
self._document:setStringProperty("crengine.font.fallback.face", self.fallback_font) self._document:setStringProperty("crengine.font.fallback.face", self.fallback_font)
self.is_open = true self.is_open = true
self.info.has_pages = false self.info.has_pages = false
self:_readMetadata() self:_readMetadata()
self.info.configurable = true self.info.configurable = true
end end
function CreDocument:loadDocument() function CreDocument:loadDocument()
self._document:loadDocument(self.file) self._document:loadDocument(self.file)
if not self.info.has_pages then if not self.info.has_pages then
self.info.doc_height = self._document:getFullHeight() self.info.doc_height = self._document:getFullHeight()
end end
if Device:getModel() ~= "KindleDXG" then if Device:getModel() ~= "KindleDXG" then
self:setVisiblePageCount(1) self:setVisiblePageCount(1)
end end
end end
function CreDocument:close() function CreDocument:close()
self._document:saveDefaults() self._document:saveDefaults()
Document.close(self) Document.close(self)
end end
function CreDocument:getPageCount() function CreDocument:getPageCount()
return self._document:getPages() return self._document:getPages()
end end
function CreDocument:getWordFromPosition(pos) function CreDocument:getWordFromPosition(pos)
local word_box = self._document:getWordFromPosition(pos.x, pos.y) local word_box = self._document:getWordFromPosition(pos.x, pos.y)
local text_range = self._document:getTextFromPositions(pos.x, pos.y, pos.x, pos.y) local text_range = self._document:getTextFromPositions(pos.x, pos.y, pos.x, pos.y)
if word_box.word then if word_box.word then
return { return {
word = text_range.text == "" and word_box.word or text_range.text, word = text_range.text == "" and word_box.word or text_range.text,
page = self._document:getCurrentPage(), page = self._document:getCurrentPage(),
sbox = Geom:new{ sbox = Geom:new{
x = word_box.x0, y = word_box.y0, x = word_box.x0, y = word_box.y0,
w = word_box.x1 - word_box.x0, w = word_box.x1 - word_box.x0,
h = word_box.y1 - word_box.y0, h = word_box.y1 - word_box.y0,
} }
} }
end end
end end
function CreDocument:getTextFromPositions(pos0, pos1) function CreDocument:getTextFromPositions(pos0, pos1)
local text_range = self._document:getTextFromPositions(pos0.x, pos0.y, pos1.x, pos1.y) local text_range = self._document:getTextFromPositions(pos0.x, pos0.y, pos1.x, pos1.y)
DEBUG("CreDocument: get text range", text_range) DEBUG("CreDocument: get text range", text_range)
local line_boxes = self:getScreenBoxesFromPositions(text_range.pos0, text_range.pos1) local line_boxes = self:getScreenBoxesFromPositions(text_range.pos0, text_range.pos1)
return { return {
text = text_range.text, text = text_range.text,
pos0 = text_range.pos0, pos0 = text_range.pos0,
pos1 = text_range.pos1, pos1 = text_range.pos1,
@ -152,38 +152,38 @@ function CreDocument:getTextFromPositions(pos0, pos1)
end end
function CreDocument:getScreenBoxesFromPositions(pos0, pos1) function CreDocument:getScreenBoxesFromPositions(pos0, pos1)
local line_boxes = {} local line_boxes = {}
if pos0 and pos1 then if pos0 and pos1 then
local word_boxes = self._document:getWordBoxesFromPositions(pos0, pos1) local word_boxes = self._document:getWordBoxesFromPositions(pos0, pos1)
--DEBUG("word boxes", word_boxes) --DEBUG("word boxes", word_boxes)
for i = 1, #word_boxes do for i = 1, #word_boxes do
local line_box = word_boxes[i] local line_box = word_boxes[i]
table.insert(line_boxes, Geom:new{ table.insert(line_boxes, Geom:new{
x = line_box.x0, y = line_box.y0, x = line_box.x0, y = line_box.y0,
w = line_box.x1 - line_box.x0, w = line_box.x1 - line_box.x0,
h = line_box.y1 - line_box.y0, h = line_box.y1 - line_box.y0,
}) })
end end
--DEBUG("line boxes", line_boxes) --DEBUG("line boxes", line_boxes)
end end
return line_boxes return line_boxes
end end
function CreDocument:drawCurrentView(target, x, y, rect, pos) function CreDocument:drawCurrentView(target, x, y, rect, pos)
tile_bb = Blitbuffer.new(rect.w, rect.h) tile_bb = Blitbuffer.new(rect.w, rect.h)
self._document:drawCurrentPage(tile_bb) self._document:drawCurrentPage(tile_bb)
target:blitFrom(tile_bb, x, y, 0, 0, rect.w, rect.h) target:blitFrom(tile_bb, x, y, 0, 0, rect.w, rect.h)
tile_bb:free() tile_bb:free()
end end
function CreDocument:drawCurrentViewByPos(target, x, y, rect, pos) function CreDocument:drawCurrentViewByPos(target, x, y, rect, pos)
self._document:gotoPos(pos) self._document:gotoPos(pos)
self:drawCurrentView(target, x, y, rect) self:drawCurrentView(target, x, y, rect)
end end
function CreDocument:drawCurrentViewByPage(target, x, y, rect, page) function CreDocument:drawCurrentViewByPage(target, x, y, rect, page)
self._document:gotoPage(page) self._document:gotoPage(page)
self:drawCurrentView(target, x, y, rect) self:drawCurrentView(target, x, y, rect)
end end
function CreDocument:hintPage(pageno, zoom, rotation) function CreDocument:hintPage(pageno, zoom, rotation)
@ -196,182 +196,182 @@ function CreDocument:renderPage(pageno, rect, zoom, rotation)
end end
function CreDocument:gotoXPointer(xpointer) function CreDocument:gotoXPointer(xpointer)
DEBUG("CreDocument: goto xpointer", xpointer) DEBUG("CreDocument: goto xpointer", xpointer)
self._document:gotoXPointer(xpointer) self._document:gotoXPointer(xpointer)
end end
function CreDocument:getXPointer() function CreDocument:getXPointer()
return self._document:getXPointer() return self._document:getXPointer()
end end
function CreDocument:getPosFromXPointer(xp) function CreDocument:getPosFromXPointer(xp)
return self._document:getPosFromXPointer(xp) return self._document:getPosFromXPointer(xp)
end end
function CreDocument:getPageFromXPointer(xp) function CreDocument:getPageFromXPointer(xp)
return self._document:getPageFromXPointer(xp) return self._document:getPageFromXPointer(xp)
end end
function CreDocument:getFontFace() function CreDocument:getFontFace()
return self._document:getFontFace() return self._document:getFontFace()
end end
function CreDocument:getCurrentPos() function CreDocument:getCurrentPos()
return self._document:getCurrentPos() return self._document:getCurrentPos()
end end
function CreDocument:getPageLinks() function CreDocument:getPageLinks()
return self._document:getPageLinks() return self._document:getPageLinks()
end end
function CreDocument:getLinkFromPosition(pos) function CreDocument:getLinkFromPosition(pos)
return self._document:getLinkFromPosition(pos.x, pos.y) return self._document:getLinkFromPosition(pos.x, pos.y)
end end
function Document:gotoPos(pos) function Document:gotoPos(pos)
DEBUG("CreDocument: goto position", pos) DEBUG("CreDocument: goto position", pos)
self._document:gotoPos(pos) self._document:gotoPos(pos)
end end
function CreDocument:gotoPage(page) function CreDocument:gotoPage(page)
DEBUG("CreDocument: goto page", page) DEBUG("CreDocument: goto page", page)
self._document:gotoPage(page) self._document:gotoPage(page)
end end
function CreDocument:gotoLink(link) function CreDocument:gotoLink(link)
DEBUG("CreDocument: goto link", link) DEBUG("CreDocument: goto link", link)
self._document:gotoLink(link) self._document:gotoLink(link)
end end
function CreDocument:goBack() function CreDocument:goBack()
DEBUG("CreDocument: go back") DEBUG("CreDocument: go back")
self._document:goBack() self._document:goBack()
end end
function CreDocument:goForward(link) function CreDocument:goForward(link)
DEBUG("CreDocument: go forward") DEBUG("CreDocument: go forward")
self._document:goForward() self._document:goForward()
end end
function CreDocument:getCurrentPage() function CreDocument:getCurrentPage()
return self._document:getCurrentPage() return self._document:getCurrentPage()
end end
function CreDocument:setFontFace(new_font_face) function CreDocument:setFontFace(new_font_face)
if new_font_face then if new_font_face then
DEBUG("CreDocument: set font face", new_font_face) DEBUG("CreDocument: set font face", new_font_face)
self._document:setFontFace(new_font_face) self._document:setFontFace(new_font_face)
end end
end end
function CreDocument:clearSelection() function CreDocument:clearSelection()
self._document:clearSelection() self._document:clearSelection()
end end
function CreDocument:getFontSize() function CreDocument:getFontSize()
return self._document:getFontSize() return self._document:getFontSize()
end end
function CreDocument:setFontSize(new_font_size) function CreDocument:setFontSize(new_font_size)
if new_font_size then if new_font_size then
DEBUG("CreDocument: set font size", new_font_size) DEBUG("CreDocument: set font size", new_font_size)
self._document:setFontSize(new_font_size) self._document:setFontSize(new_font_size)
end end
end end
function CreDocument:setViewMode(new_mode) function CreDocument:setViewMode(new_mode)
if new_mode then if new_mode then
DEBUG("CreDocument: set view mode", new_mode) DEBUG("CreDocument: set view mode", new_mode)
if new_mode == "scroll" then if new_mode == "scroll" then
self._document:setViewMode(self.SCROLL_VIEW_MODE) self._document:setViewMode(self.SCROLL_VIEW_MODE)
else else
self._document:setViewMode(self.PAGE_VIEW_MODE) self._document:setViewMode(self.PAGE_VIEW_MODE)
end end
end end
end end
function CreDocument:setHeaderFont(new_font) function CreDocument:setHeaderFont(new_font)
if new_font then if new_font then
DEBUG("CreDocument: set header font", new_font) DEBUG("CreDocument: set header font", new_font)
self._document:setHeaderFont(new_font) self._document:setHeaderFont(new_font)
end end
end end
function CreDocument:zoomFont(delta) function CreDocument:zoomFont(delta)
DEBUG("CreDocument: zoom font", delta) DEBUG("CreDocument: zoom font", delta)
self._document:zoomFont(delta) self._document:zoomFont(delta)
end end
function CreDocument:setInterlineSpacePercent(percent) function CreDocument:setInterlineSpacePercent(percent)
DEBUG("CreDocument: set interline space", percent) DEBUG("CreDocument: set interline space", percent)
self._document:setDefaultInterlineSpace(percent) self._document:setDefaultInterlineSpace(percent)
end end
function CreDocument:toggleFontBolder() function CreDocument:toggleFontBolder()
DEBUG("CreDocument: toggle font bolder") DEBUG("CreDocument: toggle font bolder")
self._document:toggleFontBolder() self._document:toggleFontBolder()
end end
function CreDocument:setGammaIndex(index) function CreDocument:setGammaIndex(index)
DEBUG("CreDocument: set gamma index", index) DEBUG("CreDocument: set gamma index", index)
cre.setGammaIndex(index) cre.setGammaIndex(index)
end end
function CreDocument:setStyleSheet(new_css) function CreDocument:setStyleSheet(new_css)
DEBUG("CreDocument: set style sheet", new_css) DEBUG("CreDocument: set style sheet", new_css)
self._document:setStyleSheet(new_css) self._document:setStyleSheet(new_css)
end end
function CreDocument:setEmbeddedStyleSheet(toggle) function CreDocument:setEmbeddedStyleSheet(toggle)
DEBUG("CreDocument: set embedded style sheet", toggle) DEBUG("CreDocument: set embedded style sheet", toggle)
self._document:setEmbeddedStyleSheet(toggle) self._document:setEmbeddedStyleSheet(toggle)
end end
function CreDocument:setPageMargins(left, top, right, bottom) function CreDocument:setPageMargins(left, top, right, bottom)
DEBUG("CreDocument: set page margins", left, top, right, bottom) DEBUG("CreDocument: set page margins", left, top, right, bottom)
self._document:setPageMargins(left, top, right, bottom) self._document:setPageMargins(left, top, right, bottom)
end end
function CreDocument:setFloatingPunctuation(enabled) function CreDocument:setFloatingPunctuation(enabled)
DEBUG("CreDocument: set floating punctuation", enabled) DEBUG("CreDocument: set floating punctuation", enabled)
self._document:setIntProperty("crengine.style.floating.punctuation.enabled", enabled) self._document:setIntProperty("crengine.style.floating.punctuation.enabled", enabled)
end end
function CreDocument:setVisiblePageCount(new_count) function CreDocument:setVisiblePageCount(new_count)
DEBUG("CreDocument: set visible page count", new_count) DEBUG("CreDocument: set visible page count", new_count)
self._document:setVisiblePageCount(new_count) self._document:setVisiblePageCount(new_count)
end end
function CreDocument:setBatteryState(state) function CreDocument:setBatteryState(state)
DEBUG("CreDocument: set battery state", state) DEBUG("CreDocument: set battery state", state)
self._document:setBatteryState(state) self._document:setBatteryState(state)
end end
function CreDocument:isXPointerInCurrentPage(xp) function CreDocument:isXPointerInCurrentPage(xp)
DEBUG("CreDocument: check in page", xp) DEBUG("CreDocument: check in page", xp)
return self._document:isXPointerInCurrentPage(xp) return self._document:isXPointerInCurrentPage(xp)
end end
function CreDocument:setStatusLineProp(prop) function CreDocument:setStatusLineProp(prop)
DEBUG("CreDocument: set status line property", prop) DEBUG("CreDocument: set status line property", prop)
self._document:setStringProperty("window.status.line", prop) self._document:setStringProperty("window.status.line", prop)
end end
function CreDocument:register(registry) function CreDocument:register(registry)
registry:addProvider("txt", "application/txt", self) registry:addProvider("txt", "application/txt", self)
registry:addProvider("epub", "application/epub", self) registry:addProvider("epub", "application/epub", self)
registry:addProvider("fb2", "application/fb2", self) registry:addProvider("fb2", "application/fb2", self)
registry:addProvider("html", "application/html", self) registry:addProvider("html", "application/html", self)
registry:addProvider("htm", "application/htm", self) registry:addProvider("htm", "application/htm", self)
registry:addProvider("zip", "application/zip", self) registry:addProvider("zip", "application/zip", self)
registry:addProvider("rtf", "application/rtf", self) registry:addProvider("rtf", "application/rtf", self)
registry:addProvider("mobi", "application/mobi", self) registry:addProvider("mobi", "application/mobi", self)
registry:addProvider("prc", "application/prc", self) registry:addProvider("prc", "application/prc", self)
registry:addProvider("azw", "application/azw", self) registry:addProvider("azw", "application/azw", self)
registry:addProvider("chm", "application/chm", self) registry:addProvider("chm", "application/chm", self)
registry:addProvider("pdb", "application/pdb", self) registry:addProvider("pdb", "application/pdb", self)
registry:addProvider("doc", "application/doc", self) registry:addProvider("doc", "application/doc", self)
registry:addProvider("tcr", "application/tcr", self) registry:addProvider("tcr", "application/tcr", self)
end end
return CreDocument return CreDocument

@ -7,113 +7,113 @@ local Configurable = require("ui/reader/configurable")
local DrawContext = require("ffi/drawcontext") local DrawContext = require("ffi/drawcontext")
local DjvuDocument = Document:new{ local DjvuDocument = Document:new{
_document = false, _document = false,
-- libdjvulibre manages its own additional cache, default value is hard written in c module. -- libdjvulibre manages its own additional cache, default value is hard written in c module.
djvulibre_cache_size = nil, djvulibre_cache_size = nil,
dc_null = DrawContext.new(), dc_null = DrawContext.new(),
options = KoptOptions, options = KoptOptions,
koptinterface = nil, koptinterface = nil,
} }
-- check DjVu magic string to validate -- check DjVu magic string to validate
local function validDjvuFile(filename) local function validDjvuFile(filename)
f = io.open(filename, "r") f = io.open(filename, "r")
if not f then return false end if not f then return false end
local magic = f:read(8) local magic = f:read(8)
f:close() f:close()
if not magic or magic ~= "AT&TFORM" then return false end if not magic or magic ~= "AT&TFORM" then return false end
return true return true
end end
function DjvuDocument:init() function DjvuDocument:init()
local djvu = require("libs/libkoreader-djvu") local djvu = require("libs/libkoreader-djvu")
self.koptinterface = require("document/koptinterface") self.koptinterface = require("document/koptinterface")
self.configurable:loadDefaults(self.options) self.configurable:loadDefaults(self.options)
if not validDjvuFile(self.file) then if not validDjvuFile(self.file) then
self.error_message = "Not a valid DjVu file" self.error_message = "Not a valid DjVu file"
return return
end end
local ok local ok
ok, self._document = pcall(djvu.openDocument, self.file, self.djvulibre_cache_size) ok, self._document = pcall(djvu.openDocument, self.file, self.djvulibre_cache_size)
if not ok then if not ok then
self.error_message = self.doc -- will contain error message self.error_message = self.doc -- will contain error message
return return
end end
self.is_open = true self.is_open = true
self.info.has_pages = true self.info.has_pages = true
self.info.configurable = true self.info.configurable = true
self:_readMetadata() self:_readMetadata()
end end
function DjvuDocument:invertTextYAxel(pageno, text_table) function DjvuDocument:invertTextYAxel(pageno, text_table)
local _, height = self.doc:getOriginalPageSize(pageno) local _, height = self.doc:getOriginalPageSize(pageno)
for _,text in pairs(text_table) do for _,text in pairs(text_table) do
for _,line in ipairs(text) do for _,line in ipairs(text) do
line.y0, line.y1 = (height - line.y1), (height - line.y0) line.y0, line.y1 = (height - line.y1), (height - line.y0)
end end
end end
return text_table return text_table
end end
function DjvuDocument:getPageTextBoxes(pageno) function DjvuDocument:getPageTextBoxes(pageno)
return self._document:getPageText(pageno) return self._document:getPageText(pageno)
end end
function DjvuDocument:getWordFromPosition(spos) function DjvuDocument:getWordFromPosition(spos)
return self.koptinterface:getWordFromPosition(self, spos) return self.koptinterface:getWordFromPosition(self, spos)
end end
function DjvuDocument:getTextFromPositions(spos0, spos1) function DjvuDocument:getTextFromPositions(spos0, spos1)
return self.koptinterface:getTextFromPositions(self, spos0, spos1) return self.koptinterface:getTextFromPositions(self, spos0, spos1)
end end
function DjvuDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1) function DjvuDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1)
return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1) return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1)
end end
function DjvuDocument:getOCRWord(pageno, wbox) function DjvuDocument:getOCRWord(pageno, wbox)
return self.koptinterface:getOCRWord(self, pageno, wbox) return self.koptinterface:getOCRWord(self, pageno, wbox)
end end
function DjvuDocument:getOCRText(pageno, tboxes) function DjvuDocument:getOCRText(pageno, tboxes)
return self.koptinterface:getOCRText(self, pageno, tboxes) return self.koptinterface:getOCRText(self, pageno, tboxes)
end end
function DjvuDocument:getPageRegions(pageno) function DjvuDocument:getPageRegions(pageno)
return self.koptinterface:getPageRegions(self, pageno) return self.koptinterface:getPageRegions(self, pageno)
end end
function DjvuDocument:getUsedBBox(pageno) function DjvuDocument:getUsedBBox(pageno)
-- djvu does not support usedbbox, so fake it. -- djvu does not support usedbbox, so fake it.
local used = {} local used = {}
local native_dim = self:getNativePageDimensions(pageno) local native_dim = self:getNativePageDimensions(pageno)
used.x0, used.y0, used.x1, used.y1 = 0, 0, native_dim.w, native_dim.h used.x0, used.y0, used.x1, used.y1 = 0, 0, native_dim.w, native_dim.h
return used return used
end end
function DjvuDocument:getPageBBox(pageno) function DjvuDocument:getPageBBox(pageno)
return self.koptinterface:getPageBBox(self, pageno) return self.koptinterface:getPageBBox(self, pageno)
end end
function DjvuDocument:getPageDimensions(pageno, zoom, rotation) function DjvuDocument:getPageDimensions(pageno, zoom, rotation)
return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation) return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation)
end end
function DjvuDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) 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 end
function DjvuDocument:hintPage(pageno, zoom, rotation, gamma, render_mode) 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 end
function DjvuDocument:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) 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 end
function DjvuDocument:register(registry) function DjvuDocument:register(registry)
registry:addProvider("djvu", "application/djvu", self) registry:addProvider("djvu", "application/djvu", self)
end end
return DjvuDocument return DjvuDocument

@ -11,55 +11,55 @@ local DEBUG = require("dbg")
This is an abstract interface to a document This is an abstract interface to a document
]]-- ]]--
local Document = { local Document = {
-- file name -- file name
file = nil, file = nil,
info = { info = {
-- whether the document is pageable -- whether the document is pageable
has_pages = false, has_pages = false,
-- whether words can be provided -- whether words can be provided
has_words = false, has_words = false,
-- whether hyperlinks can be provided -- whether hyperlinks can be provided
has_hyperlinks = false, has_hyperlinks = false,
-- whether (native to format) annotations can be provided -- whether (native to format) annotations can be provided
has_annotations = false, has_annotations = false,
-- whether pages can be rotated -- whether pages can be rotated
is_rotatable = false, is_rotatable = false,
number_of_pages = 0, number_of_pages = 0,
-- if not pageable, length of the document in pixels -- if not pageable, length of the document in pixels
doc_height = 0, doc_height = 0,
-- other metadata -- other metadata
title = "", title = "",
author = "", author = "",
date = "" date = ""
}, },
links = {}, links = {},
GAMMA_NO_GAMMA = 1.0, GAMMA_NO_GAMMA = 1.0,
-- override bbox from orignal page's getUsedBBox -- override bbox from orignal page's getUsedBBox
bbox = {}, bbox = {},
-- flag to show whether the document was opened successfully -- flag to show whether the document was opened successfully
is_open = false, is_open = false,
error_message = nil, error_message = nil,
-- flag to show that the document needs to be unlocked by a password -- flag to show that the document needs to be unlocked by a password
is_locked = false, is_locked = false,
configurable = Configurable, configurable = Configurable,
} }
function Document:new(o) function Document:new(o)
local o = o or {} local o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
if o.init then o:init() end if o.init then o:init() end
return o return o
end end
-- override this method to open a document -- override this method to open a document
@ -68,195 +68,195 @@ end
-- this might be overridden by a document implementation -- this might be overridden by a document implementation
function Document:unlock(password) function Document:unlock(password)
-- return true instead when the password provided unlocked the document -- return true instead when the password provided unlocked the document
return false return false
end end
-- this might be overridden by a document implementation -- this might be overridden by a document implementation
function Document:close() function Document:close()
if self.is_open then if self.is_open then
self.is_open = false self.is_open = false
self._document:close() self._document:close()
end end
end end
-- this might be overridden by a document implementation -- this might be overridden by a document implementation
function Document:getNativePageDimensions(pageno) function Document:getNativePageDimensions(pageno)
local hash = "pgdim|"..self.file.."|"..pageno local hash = "pgdim|"..self.file.."|"..pageno
local cached = Cache:check(hash) local cached = Cache:check(hash)
if cached then if cached then
return cached[1] return cached[1]
end end
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
local page_size_w, page_size_h = page:getSize(self.dc_null) 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 } local page_size = Geom:new{ w = page_size_w, h = page_size_h }
Cache:insert(hash, CacheItem:new{ page_size }) Cache:insert(hash, CacheItem:new{ page_size })
page:close() page:close()
return page_size return page_size
end end
function Document:_readMetadata() function Document:_readMetadata()
self.info.number_of_pages = self._document:getPages() self.info.number_of_pages = self._document:getPages()
return true return true
end end
function Document:getPageCount() function Document:getPageCount()
return self.info.number_of_pages return self.info.number_of_pages
end end
-- calculates page dimensions -- calculates page dimensions
function Document:getPageDimensions(pageno, zoom, rotation) function Document:getPageDimensions(pageno, zoom, rotation)
local native_dimen = self:getNativePageDimensions(pageno):copy() local native_dimen = self:getNativePageDimensions(pageno):copy()
if rotation == 90 or rotation == 270 then if rotation == 90 or rotation == 270 then
-- switch orientation -- switch orientation
native_dimen.w, native_dimen.h = native_dimen.h, native_dimen.w native_dimen.w, native_dimen.h = native_dimen.h, native_dimen.w
end end
native_dimen:scaleBy(zoom) native_dimen:scaleBy(zoom)
--DEBUG("dimen for pageno", pageno, "zoom", zoom, "rotation", rotation, "is", native_dimen) --DEBUG("dimen for pageno", pageno, "zoom", zoom, "rotation", rotation, "is", native_dimen)
return native_dimen return native_dimen
end end
function Document:getPageBBox(pageno) function Document:getPageBBox(pageno)
local bbox = self.bbox[pageno] -- exact local bbox = self.bbox[pageno] -- exact
if bbox ~= nil then if bbox ~= nil then
--DEBUG("bbox from", pageno) --DEBUG("bbox from", pageno)
return bbox return bbox
else else
local oddEven = Math.oddEven(pageno) local oddEven = Math.oddEven(pageno)
bbox = self.bbox[oddEven] -- odd/even bbox = self.bbox[oddEven] -- odd/even
end end
if bbox ~= nil then -- last used up to this page if bbox ~= nil then -- last used up to this page
--DEBUG("bbox from", oddEven) --DEBUG("bbox from", oddEven)
return bbox return bbox
else else
for i = 0,pageno do for i = 0,pageno do
bbox = self.bbox[ pageno - i ] bbox = self.bbox[ pageno - i ]
if bbox ~= nil then if bbox ~= nil then
--DEBUG("bbox from", pageno - i) --DEBUG("bbox from", pageno - i)
return bbox return bbox
end end
end end
end end
if bbox == nil then -- fallback bbox if bbox == nil then -- fallback bbox
bbox = self:getUsedBBox(pageno) bbox = self:getUsedBBox(pageno)
--DEBUG("bbox from ORIGINAL page") --DEBUG("bbox from ORIGINAL page")
end end
--DEBUG("final bbox", bbox) --DEBUG("final bbox", bbox)
return bbox return bbox
end end
--[[ --[[
This method returns pagesize if bbox is corrupted This method returns pagesize if bbox is corrupted
--]] --]]
function Document:getUsedBBoxDimensions(pageno, zoom, rotation) function Document:getUsedBBoxDimensions(pageno, zoom, rotation)
local bbox = self:getPageBBox(pageno) local bbox = self:getPageBBox(pageno)
-- clipping page bbox -- clipping page bbox
if bbox.x0 < 0 then bbox.x0 = 0 end if bbox.x0 < 0 then bbox.x0 = 0 end
if bbox.y0 < 0 then bbox.y0 = 0 end if bbox.y0 < 0 then bbox.y0 = 0 end
if bbox.x1 < 0 then bbox.x1 = 0 end if bbox.x1 < 0 then bbox.x1 = 0 end
if bbox.y1 < 0 then bbox.y1 = 0 end if bbox.y1 < 0 then bbox.y1 = 0 end
local ubbox_dimen = nil local ubbox_dimen = nil
if (bbox.x0 > bbox.x1) or (bbox.y0 > bbox.y1) then if (bbox.x0 > bbox.x1) or (bbox.y0 > bbox.y1) then
-- if document's bbox info is corrupted, we use the page size -- if document's bbox info is corrupted, we use the page size
ubbox_dimen = self:getPageDimensions(pageno, zoom, rotation) ubbox_dimen = self:getPageDimensions(pageno, zoom, rotation)
else else
ubbox_dimen = Geom:new{ ubbox_dimen = Geom:new{
x = bbox.x0, x = bbox.x0,
y = bbox.y0, y = bbox.y0,
w = bbox.x1 - bbox.x0, w = bbox.x1 - bbox.x0,
h = bbox.y1 - bbox.y0, h = bbox.y1 - bbox.y0,
} }
if zoom ~= 1 then if zoom ~= 1 then
ubbox_dimen:transformByScale(zoom) ubbox_dimen:transformByScale(zoom)
end end
end end
return ubbox_dimen return ubbox_dimen
end end
function Document:getToc() function Document:getToc()
return self._document:getToc() return self._document:getToc()
end end
function Document:getPageLinks(pageno) function Document:getPageLinks(pageno)
return nil return nil
end end
function Document:getLinkFromPosition(pageno, pos) function Document:getLinkFromPosition(pageno, pos)
return nil return nil
end end
function Document:getTextBoxes(pageno) function Document:getTextBoxes(pageno)
return nil return nil
end end
function Document:getOCRWord(pageno, rect) function Document:getOCRWord(pageno, rect)
return nil return nil
end end
function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
local hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode local hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode
local page_size = self:getPageDimensions(pageno, zoom, rotation) local page_size = self:getPageDimensions(pageno, zoom, rotation)
-- this will be the size we actually render -- this will be the size we actually render
local size = page_size local size = page_size
-- we prefer to render the full page, if it fits into cache -- we prefer to render the full page, if it fits into cache
if not Cache:willAccept(size.w * size.h / 2) then if not Cache:willAccept(size.w * size.h / 2) then
-- whole page won't fit into cache -- whole page won't fit into cache
DEBUG("rendering only part of the page") DEBUG("rendering only part of the page")
-- TODO: figure out how to better segment the page -- TODO: figure out how to better segment the page
if not rect then if not rect then
DEBUG("aborting, since we do not have a specification for that part") DEBUG("aborting, since we do not have a specification for that part")
-- required part not given, so abort -- required part not given, so abort
return return
end end
-- only render required part -- only render required part
hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode.."|"..tostring(rect) hash = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode.."|"..tostring(rect)
size = rect size = rect
end end
-- prepare cache item with contained blitbuffer -- prepare cache item with contained blitbuffer
local tile = TileCacheItem:new{ local tile = TileCacheItem:new{
size = size.w * size.h / 2 + 64, -- estimation size = size.w * size.h / 2 + 64, -- estimation
excerpt = size, excerpt = size,
pageno = pageno, pageno = pageno,
bb = Blitbuffer.new(size.w, size.h) bb = Blitbuffer.new(size.w, size.h)
} }
-- create a draw context -- create a draw context
local dc = DrawContext.new() local dc = DrawContext.new()
dc:setRotate(rotation) dc:setRotate(rotation)
-- correction of rotation -- correction of rotation
if rotation == 90 then if rotation == 90 then
dc:setOffset(page_size.w, 0) dc:setOffset(page_size.w, 0)
elseif rotation == 180 then elseif rotation == 180 then
dc:setOffset(page_size.w, page_size.h) dc:setOffset(page_size.w, page_size.h)
elseif rotation == 270 then elseif rotation == 270 then
dc:setOffset(0, page_size.h) dc:setOffset(0, page_size.h)
end end
dc:setZoom(zoom) dc:setZoom(zoom)
if gamma ~= self.GAMMA_NO_GAMMA then if gamma ~= self.GAMMA_NO_GAMMA then
--DEBUG("gamma correction: ", gamma) --DEBUG("gamma correction: ", gamma)
dc:setGamma(gamma) dc:setGamma(gamma)
end end
-- render -- render
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
page:draw(dc, tile.bb, size.x, size.y, render_mode) page:draw(dc, tile.bb, size.x, size.y, render_mode)
page:close() page:close()
Cache:insert(hash, tile) Cache:insert(hash, tile)
return tile return tile
end end
-- a hint for the cache engine to paint a full page to the cache -- a hint for the cache engine to paint a full page to the cache
-- TODO: this should trigger a background operation -- TODO: this should trigger a background operation
function Document:hintPage(pageno, zoom, rotation, gamma, render_mode) function Document:hintPage(pageno, zoom, rotation, gamma, render_mode)
local hash_full_page = "renderpg|"..self.file.."|"..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 if not Cache:check(hash_full_page) then
DEBUG("hinting page", pageno) DEBUG("hinting page", pageno)
self:renderPage(pageno, nil, zoom, rotation, gamma, render_mode) self:renderPage(pageno, nil, zoom, rotation, gamma, render_mode)
end end
end end
--[[ --[[
@ -268,59 +268,59 @@ Draw page content to blitbuffer.
@rect: visible_area inside document page @rect: visible_area inside document page
--]] --]]
function Document:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) 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_full_page = "renderpg|"..self.file.."|"..pageno.."|"..zoom.."|"..rotation.."|"..gamma.."|"..render_mode
local hash_excerpt = hash_full_page.."|"..tostring(rect) local hash_excerpt = hash_full_page.."|"..tostring(rect)
local tile = Cache:check(hash_full_page) local tile = Cache:check(hash_full_page)
if not tile then if not tile then
tile = Cache:check(hash_excerpt) tile = Cache:check(hash_excerpt)
if not tile then if not tile then
DEBUG("rendering") DEBUG("rendering")
tile = self:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) tile = self:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
end end
end end
DEBUG("now painting", tile, rect) DEBUG("now painting", tile, rect)
target:blitFrom(tile.bb, target:blitFrom(tile.bb,
x, y, x, y,
rect.x - tile.excerpt.x, rect.x - tile.excerpt.x,
rect.y - tile.excerpt.y, rect.y - tile.excerpt.y,
rect.w, rect.h) rect.w, rect.h)
end end
function Document:getPageText(pageno) function Document:getPageText(pageno)
-- is this worth caching? not done yet. -- is this worth caching? not done yet.
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
local text = page:getPageText() local text = page:getPageText()
page:close() page:close()
return text return text
end end
function Document:saveHighlight(pageno, item) function Document:saveHighlight(pageno, item)
return nil return nil
end end
--[[ --[[
helper functions helper functions
--]] --]]
function Document:logMemoryUsage(pageno) function Document:logMemoryUsage(pageno)
local status_file = io.open("/proc/self/status", "r") local status_file = io.open("/proc/self/status", "r")
local log_file = io.open("mem_usage_log.txt", "a+") local log_file = io.open("mem_usage_log.txt", "a+")
local data = -1 local data = -1
if status_file then if status_file then
for line in status_file:lines() do for line in status_file:lines() do
local s, n local s, n
s, n = line:gsub("VmData:%s-(%d+) kB", "%1") s, n = line:gsub("VmData:%s-(%d+) kB", "%1")
if n ~= 0 then data = tonumber(s) end if n ~= 0 then data = tonumber(s) end
if data ~= -1 then break end if data ~= -1 then break end
end end
status_file:close() status_file:close()
end end
if log_file then if log_file then
if log_file:seek("end") == 0 then -- write the header only once if log_file:seek("end") == 0 then -- write the header only once
log_file:write("PAGE\tMEM\n") log_file:write("PAGE\tMEM\n")
end end
log_file:write(string.format("%s\t%s\n", pageno, data)) log_file:write(string.format("%s\t%s\n", pageno, data))
log_file:close() log_file:close()
end end
end end
return Document return Document

@ -2,21 +2,21 @@
This is a registry for document providers This is a registry for document providers
]]-- ]]--
local DocumentRegistry = { local DocumentRegistry = {
providers = { } providers = { }
} }
function DocumentRegistry:addProvider(extension, mimetype, provider) 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 end
function DocumentRegistry:getProvider(file) function DocumentRegistry:getProvider(file)
-- TODO: some implementation based on mime types? -- TODO: some implementation based on mime types?
local extension = string.lower(string.match(file, ".+%.([^.]+)") or "") local extension = string.lower(string.match(file, ".+%.([^.]+)") or "")
for _, provider in ipairs(self.providers) do for _, provider in ipairs(self.providers) do
if extension == provider.extension then if extension == provider.extension then
return provider.provider return provider.provider
end end
end end
end end
function DocumentRegistry:openDocument(file) function DocumentRegistry:openDocument(file)

File diff suppressed because it is too large Load Diff

@ -8,211 +8,211 @@ local ffi = require("ffi")
ffi.cdef[[ ffi.cdef[[
typedef struct fz_point_s fz_point; typedef struct fz_point_s fz_point;
struct fz_point_s { struct fz_point_s {
float x, y; float x, y;
}; };
typedef enum { typedef enum {
FZ_ANNOT_TEXT, FZ_ANNOT_TEXT,
FZ_ANNOT_LINK, FZ_ANNOT_LINK,
FZ_ANNOT_FREETEXT, FZ_ANNOT_FREETEXT,
FZ_ANNOT_LINE, FZ_ANNOT_LINE,
FZ_ANNOT_SQUARE, FZ_ANNOT_SQUARE,
FZ_ANNOT_CIRCLE, FZ_ANNOT_CIRCLE,
FZ_ANNOT_POLYGON, FZ_ANNOT_POLYGON,
FZ_ANNOT_POLYLINE, FZ_ANNOT_POLYLINE,
FZ_ANNOT_HIGHLIGHT, FZ_ANNOT_HIGHLIGHT,
FZ_ANNOT_UNDERLINE, FZ_ANNOT_UNDERLINE,
FZ_ANNOT_SQUIGGLY, FZ_ANNOT_SQUIGGLY,
FZ_ANNOT_STRIKEOUT, FZ_ANNOT_STRIKEOUT,
FZ_ANNOT_STAMP, FZ_ANNOT_STAMP,
FZ_ANNOT_CARET, FZ_ANNOT_CARET,
FZ_ANNOT_INK, FZ_ANNOT_INK,
FZ_ANNOT_POPUP, FZ_ANNOT_POPUP,
FZ_ANNOT_FILEATTACHMENT, FZ_ANNOT_FILEATTACHMENT,
FZ_ANNOT_SOUND, FZ_ANNOT_SOUND,
FZ_ANNOT_MOVIE, FZ_ANNOT_MOVIE,
FZ_ANNOT_WIDGET, FZ_ANNOT_WIDGET,
FZ_ANNOT_SCREEN, FZ_ANNOT_SCREEN,
FZ_ANNOT_PRINTERMARK, FZ_ANNOT_PRINTERMARK,
FZ_ANNOT_TRAPNET, FZ_ANNOT_TRAPNET,
FZ_ANNOT_WATERMARK, FZ_ANNOT_WATERMARK,
FZ_ANNOT_3D FZ_ANNOT_3D
} fz_annot_type; } fz_annot_type;
]] ]]
local PdfDocument = Document:new{ local PdfDocument = Document:new{
_document = false, _document = false,
-- muPDF manages its own additional cache -- muPDF manages its own additional cache
mupdf_cache_size = 5 * 1024 * 1024, mupdf_cache_size = 5 * 1024 * 1024,
dc_null = DrawContext.new(), dc_null = DrawContext.new(),
options = KoptOptions, options = KoptOptions,
koptinterface = nil, koptinterface = nil,
annot_revision = 0, annot_revision = 0,
} }
function PdfDocument:init() function PdfDocument:init()
local pdf = require("libs/libkoreader-pdf") local pdf = require("libs/libkoreader-pdf")
self.koptinterface = require("document/koptinterface") self.koptinterface = require("document/koptinterface")
self.configurable:loadDefaults(self.options) self.configurable:loadDefaults(self.options)
local ok local ok
ok, self._document = pcall(pdf.openDocument, self.file, self.mupdf_cache_size) ok, self._document = pcall(pdf.openDocument, self.file, self.mupdf_cache_size)
if not ok then if not ok then
self.error_message = self.doc -- will contain error message self.error_message = self.doc -- will contain error message
return return
end end
self.is_open = true self.is_open = true
self.info.has_pages = true self.info.has_pages = true
self.info.configurable = true self.info.configurable = true
if self._document:needsPassword() then if self._document:needsPassword() then
self.is_locked = true self.is_locked = true
else else
self:_readMetadata() self:_readMetadata()
end end
end end
function PdfDocument:unlock(password) function PdfDocument:unlock(password)
if not self._document:authenticatePassword(password) then if not self._document:authenticatePassword(password) then
self._document:close() self._document:close()
return false, "wrong password" return false, "wrong password"
end end
self.is_locked = false self.is_locked = false
return self:_readMetadata() return self:_readMetadata()
end end
function PdfDocument:getPageTextBoxes(pageno) function PdfDocument:getPageTextBoxes(pageno)
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
local text = page:getPageText() local text = page:getPageText()
page:close() page:close()
return text return text
end end
function PdfDocument:getWordFromPosition(spos) function PdfDocument:getWordFromPosition(spos)
return self.koptinterface:getWordFromPosition(self, spos) return self.koptinterface:getWordFromPosition(self, spos)
end end
function PdfDocument:getTextFromPositions(spos0, spos1) function PdfDocument:getTextFromPositions(spos0, spos1)
return self.koptinterface:getTextFromPositions(self, spos0, spos1) return self.koptinterface:getTextFromPositions(self, spos0, spos1)
end end
function PdfDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1) function PdfDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1)
return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1) return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1)
end end
function PdfDocument:getOCRWord(pageno, wbox) function PdfDocument:getOCRWord(pageno, wbox)
return self.koptinterface:getOCRWord(self, pageno, wbox) return self.koptinterface:getOCRWord(self, pageno, wbox)
end end
function PdfDocument:getOCRText(pageno, tboxes) function PdfDocument:getOCRText(pageno, tboxes)
return self.koptinterface:getOCRText(self, pageno, tboxes) return self.koptinterface:getOCRText(self, pageno, tboxes)
end end
function PdfDocument:getPageRegions(pageno) function PdfDocument:getPageRegions(pageno)
return self.koptinterface:getPageRegions(self, pageno) return self.koptinterface:getPageRegions(self, pageno)
end end
function PdfDocument:getUsedBBox(pageno) function PdfDocument:getUsedBBox(pageno)
local hash = "pgubbox|"..self.file.."|"..pageno local hash = "pgubbox|"..self.file.."|"..pageno
local cached = Cache:check(hash) local cached = Cache:check(hash)
if cached then if cached then
return cached.ubbox return cached.ubbox
end end
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
local used = {} local used = {}
used.x0, used.y0, used.x1, used.y1 = page:getUsedBBox() used.x0, used.y0, used.x1, used.y1 = page:getUsedBBox()
local pwidth, pheight = page:getSize(self.dc_null) local pwidth, pheight = page:getSize(self.dc_null)
-- clamp to page BBox -- clamp to page BBox
if used.x0 < 0 then used.x0 = 0 end if used.x0 < 0 then used.x0 = 0 end
if used.x1 > pwidth then used.x1 = pwidth end if used.x1 > pwidth then used.x1 = pwidth end
if used.y0 < 0 then used.y0 = 0 end if used.y0 < 0 then used.y0 = 0 end
if used.y1 > pheight then used.y1 = pheight end if used.y1 > pheight then used.y1 = pheight end
--@TODO give size for cacheitem? 02.12 2012 (houqp) --@TODO give size for cacheitem? 02.12 2012 (houqp)
Cache:insert(hash, CacheItem:new{ Cache:insert(hash, CacheItem:new{
ubbox = used, ubbox = used,
}) })
page:close() page:close()
return used return used
end end
function PdfDocument:getPageLinks(pageno) function PdfDocument:getPageLinks(pageno)
local hash = "pglinks|"..self.file.."|"..pageno local hash = "pglinks|"..self.file.."|"..pageno
local cached = Cache:check(hash) local cached = Cache:check(hash)
if cached then if cached then
return cached.links return cached.links
end end
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
local links = page:getPageLinks() local links = page:getPageLinks()
Cache:insert(hash, CacheItem:new{ Cache:insert(hash, CacheItem:new{
links = links, links = links,
}) })
page:close() page:close()
return links return links
end end
function PdfDocument:saveHighlight(pageno, item) function PdfDocument:saveHighlight(pageno, item)
self.annot_revision = self.annot_revision + 1 self.annot_revision = self.annot_revision + 1
local n = #item.pboxes local n = #item.pboxes
local quadpoints = ffi.new("fz_point[?]", 4*n) local quadpoints = ffi.new("fz_point[?]", 4*n)
for i=1, n do for i=1, n do
quadpoints[4*i-4].x = item.pboxes[i].x + item.pboxes[i].w 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-4].y = item.pboxes[i].y + item.pboxes[i].h
quadpoints[4*i-3].x = item.pboxes[i].x 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-3].y = item.pboxes[i].y + item.pboxes[i].h
quadpoints[4*i-2].x = item.pboxes[i].x quadpoints[4*i-2].x = item.pboxes[i].x
quadpoints[4*i-2].y = item.pboxes[i].y 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].x = item.pboxes[i].x + item.pboxes[i].w
quadpoints[4*i-1].y = item.pboxes[i].y quadpoints[4*i-1].y = item.pboxes[i].y
end end
local page = self._document:openPage(pageno) local page = self._document:openPage(pageno)
local annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT local annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT
if item.drawer == "lighten" then if item.drawer == "lighten" then
annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT
elseif item.drawer == "underscore" then elseif item.drawer == "underscore" then
annot_type = ffi.C.FZ_ANNOT_UNDERLINE annot_type = ffi.C.FZ_ANNOT_UNDERLINE
elseif item.drawer == "strikeout" then elseif item.drawer == "strikeout" then
annot_type = ffi.C.FZ_ANNOT_STRIKEOUT annot_type = ffi.C.FZ_ANNOT_STRIKEOUT
end end
page:addMarkupAnnotation(quadpoints, 4*n, annot_type) page:addMarkupAnnotation(quadpoints, 4*n, annot_type)
page:close() page:close()
end end
function PdfDocument:writeDocument() function PdfDocument:writeDocument()
self._document:writeDocument(self.file) self._document:writeDocument(self.file)
end end
function PdfDocument:close() function PdfDocument:close()
if self.annot_revision ~= 0 then if self.annot_revision ~= 0 then
self:writeDocument() self:writeDocument()
end end
Document.close(self) Document.close(self)
end end
function PdfDocument:getLinkFromPosition(pageno, pos) function PdfDocument:getLinkFromPosition(pageno, pos)
return self.koptinterface:getLinkFromPosition(self, pageno, pos) return self.koptinterface:getLinkFromPosition(self, pageno, pos)
end end
function PdfDocument:getPageBBox(pageno) function PdfDocument:getPageBBox(pageno)
return self.koptinterface:getPageBBox(self, pageno) return self.koptinterface:getPageBBox(self, pageno)
end end
function PdfDocument:getPageDimensions(pageno, zoom, rotation) function PdfDocument:getPageDimensions(pageno, zoom, rotation)
return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation) return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation)
end end
function PdfDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode) 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 end
function PdfDocument:hintPage(pageno, zoom, rotation, gamma, render_mode) 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 end
function PdfDocument:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode) 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 end
function PdfDocument:register(registry) function PdfDocument:register(registry)
registry:addProvider("pdf", "application/pdf", self) registry:addProvider("pdf", "application/pdf", self)
registry:addProvider("cbz", "application/cbz", self) registry:addProvider("cbz", "application/cbz", self)
registry:addProvider("xps", "application/xps", self) registry:addProvider("xps", "application/xps", self)
end end
return PdfDocument return PdfDocument

@ -2,31 +2,31 @@ local Document = require("document/document")
local DrawContext = require("ffi/drawcontext") local DrawContext = require("ffi/drawcontext")
local PicDocument = Document:new{ local PicDocument = Document:new{
_document = false, _document = false,
dc_null = DrawContext.new() dc_null = DrawContext.new()
} }
function PicDocument:init() function PicDocument:init()
require "libs/libkoreader-pic" require "libs/libkoreader-pic"
ok, self._document = pcall(pic.openDocument, self.file) ok, self._document = pcall(pic.openDocument, self.file)
if not ok then if not ok then
self.error_message = "failed to open jpeg image" self.error_message = "failed to open jpeg image"
return return
end end
self.info.has_pages = true self.info.has_pages = true
self.info.configurable = false self.info.configurable = false
self:readMetadata() self:readMetadata()
end end
function PicDocument:readMetadata() function PicDocument:readMetadata()
self.info.number_of_pages = 1 self.info.number_of_pages = 1
end end
function PicDocument:register(registry) function PicDocument:register(registry)
registry:addProvider("jpeg", "application/jpeg", self) registry:addProvider("jpeg", "application/jpeg", self)
registry:addProvider("jpg", "application/jpeg", self) registry:addProvider("jpg", "application/jpeg", self)
end end
return PicDocument return PicDocument

@ -4,10 +4,10 @@ local DEBUG = require("dbg")
local TileCacheItem = CacheItem:new{} local TileCacheItem = CacheItem:new{}
function TileCacheItem:onFree() function TileCacheItem:onFree()
if self.bb.free then if self.bb.free then
DEBUG("free blitbuffer", self.bb) DEBUG("free blitbuffer", self.bb)
self.bb:free() self.bb:free()
end end
end end
return TileCacheItem return TileCacheItem

@ -4,11 +4,11 @@ local GetText = {}
local GetText_mt = {} local GetText_mt = {}
function GetText_mt.__call(gettext, string) function GetText_mt.__call(gettext, string)
return lua_gettext.translate(string) return lua_gettext.translate(string)
end end
function GetText.changeLang(new_lang) function GetText.changeLang(new_lang)
lua_gettext.change_lang(new_lang) lua_gettext.change_lang(new_lang)
end end
setmetatable(GetText, GetText_mt) setmetatable(GetText, GetText_mt)

@ -5,42 +5,42 @@ Simple math helper function
local Math = {} local Math = {}
function Math.roundAwayFromZero(num) function Math.roundAwayFromZero(num)
if num > 0 then if num > 0 then
return math.ceil(num) return math.ceil(num)
else else
return math.floor(num) return math.floor(num)
end end
end end
function Math.round(num) function Math.round(num)
return math.floor(num + 0.5) return math.floor(num + 0.5)
end end
function Math.oddEven(number) function Math.oddEven(number)
if number % 2 == 1 then if number % 2 == 1 then
return "odd" return "odd"
else else
return "even" return "even"
end end
end end
local function tmin_max(tab, func, op) local function tmin_max(tab, func, op)
if #tab == 0 then return nil, nil end if #tab == 0 then return nil, nil end
local index, value = 1, tab[1] local index, value = 1, tab[1]
for i = 2, #tab do for i = 2, #tab do
if func then if func then
if func(value, tab[i]) then if func(value, tab[i]) then
index, value = i, tab[i] index, value = i, tab[i]
end end
elseif op == "min" then elseif op == "min" then
if value > tab[i] then if value > tab[i] then
index, value = i, tab[i] index, value = i, tab[i]
end end
elseif op == "max" then elseif op == "max" then
if value < tab[i] then if value < tab[i] then
index, value = i, tab[i] index, value = i, tab[i]
end end
end end
end end
return index, value return index, value
end end
@ -50,7 +50,7 @@ Return the minimum element of a table.
The optional argument func specifies a one-argument ordering function. The optional argument func specifies a one-argument ordering function.
]]-- ]]--
function Math.tmin(tab, func) function Math.tmin(tab, func)
return tmin_max(tab, func, "min") return tmin_max(tab, func, "min")
end end
--[[ --[[
@ -58,7 +58,7 @@ Return the maximum element of a table.
The optional argument func specifies a one-argument ordering function. The optional argument func specifies a one-argument ordering function.
]]-- ]]--
function Math.tmax(tab, func) function Math.tmax(tab, func)
return tmin_max(tab, func, "max") return tmin_max(tab, func, "max")
end end
return Math return Math

@ -5,144 +5,144 @@ local _ = require("gettext")
-- add multiply operator to Aa dict -- add multiply operator to Aa dict
local Aa = setmetatable({"Aa"}, { local Aa = setmetatable({"Aa"}, {
__mul = function(t, mul) __mul = function(t, mul)
local new = {} local new = {}
for i = 1, mul do for i = 1, mul do
for _, v in ipairs(t) do table.insert(new, v) end for _, v in ipairs(t) do table.insert(new, v) end
end end
return new return new
end end
}) })
local CreOptions = { local CreOptions = {
prefix = 'copt', prefix = 'copt',
{ {
icon = "resources/icons/appbar.transform.rotate.right.large.png", icon = "resources/icons/appbar.transform.rotate.right.large.png",
options = { options = {
{ {
name = "screen_mode", name = "screen_mode",
name_text = S.SCREEN_MODE, name_text = S.SCREEN_MODE,
toggle = {S.PORTRAIT, S.LANDSCAPE}, toggle = {S.PORTRAIT, S.LANDSCAPE},
alternate = false, alternate = false,
args = {"portrait", "landscape"}, args = {"portrait", "landscape"},
default_arg = "portrait", default_arg = "portrait",
current_func = function() return Screen:getScreenMode() end, current_func = function() return Screen:getScreenMode() end,
event = "ChangeScreenMode", event = "ChangeScreenMode",
} }
} }
}, },
{ {
icon = "resources/icons/appbar.column.two.large.png", icon = "resources/icons/appbar.column.two.large.png",
options = { options = {
{ {
name = "line_spacing", name = "line_spacing",
name_text = S.LINE_SPACING, name_text = S.LINE_SPACING,
toggle = {S.DECREASE, S.INCREASE}, toggle = {S.DECREASE, S.INCREASE},
alternate = false, alternate = false,
args = {"decrease", "increase"}, args = {"decrease", "increase"},
default_arg = "decrease", default_arg = "decrease",
event = "ChangeLineSpace", event = "ChangeLineSpace",
}, },
{ {
name = "page_margins", name = "page_margins",
name_text = S.PAGE_MARGIN, name_text = S.PAGE_MARGIN,
toggle = {S.SMALL, S.MEDIUM, S.LARGE}, toggle = {S.SMALL, S.MEDIUM, S.LARGE},
values = { values = {
DCREREADER_CONFIG_MARGIN_SIZES_SMALL, DCREREADER_CONFIG_MARGIN_SIZES_SMALL,
DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM,
DCREREADER_CONFIG_MARGIN_SIZES_LARGE, DCREREADER_CONFIG_MARGIN_SIZES_LARGE,
}, },
default_value = DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, default_value = DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM,
args = { args = {
DCREREADER_CONFIG_MARGIN_SIZES_SMALL, DCREREADER_CONFIG_MARGIN_SIZES_SMALL,
DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM, DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM,
DCREREADER_CONFIG_MARGIN_SIZES_LARGE, DCREREADER_CONFIG_MARGIN_SIZES_LARGE,
}, },
event = "SetPageMargins", event = "SetPageMargins",
}, },
} }
}, },
{ {
icon = "resources/icons/appbar.text.size.large.png", icon = "resources/icons/appbar.text.size.large.png",
options = { options = {
{ {
name = "font_size", name = "font_size",
item_text = Aa * #DCREREADER_CONFIG_FONT_SIZES, item_text = Aa * #DCREREADER_CONFIG_FONT_SIZES,
item_align_center = 1.0, item_align_center = 1.0,
spacing = 15, spacing = 15,
item_font_size = DCREREADER_CONFIG_FONT_SIZES, item_font_size = DCREREADER_CONFIG_FONT_SIZES,
values = DCREREADER_CONFIG_FONT_SIZES, values = DCREREADER_CONFIG_FONT_SIZES,
default_value = DCREREADER_CONFIG_DEFAULT_FONT_SIZE, default_value = DCREREADER_CONFIG_DEFAULT_FONT_SIZE,
args = DCREREADER_CONFIG_FONT_SIZES, args = DCREREADER_CONFIG_FONT_SIZES,
event = "SetFontSize", event = "SetFontSize",
}, },
{ {
name = "font_fine_tune", name = "font_fine_tune",
name_text = S.FONTSIZE_FINE_TUNING, name_text = S.FONTSIZE_FINE_TUNING,
toggle = {S.DECREASE, S.INCREASE}, toggle = {S.DECREASE, S.INCREASE},
event = "ChangeSize", event = "ChangeSize",
args = {"decrease", "increase"}, args = {"decrease", "increase"},
alternate = false, alternate = false,
height = 60, height = 60,
} }
} }
}, },
{ {
icon = "resources/icons/appbar.grade.b.large.png", icon = "resources/icons/appbar.grade.b.large.png",
options = { options = {
{ {
name = "font_weight", name = "font_weight",
name_text = S.FONT_WEIGHT, name_text = S.FONT_WEIGHT,
toggle = {S.TOGGLE_BOLD}, toggle = {S.TOGGLE_BOLD},
default_arg = nil, default_arg = nil,
event = "ToggleFontBolder", event = "ToggleFontBolder",
}, },
{ {
name = "font_gamma", name = "font_gamma",
name_text = S.CONTRAST, name_text = S.CONTRAST,
toggle = {S.DECREASE, S.INCREASE}, toggle = {S.DECREASE, S.INCREASE},
alternate = false, alternate = false,
args = {"decrease", "increase"}, args = {"decrease", "increase"},
default_arg = "increase", default_arg = "increase",
event = "ChangeFontGamma", event = "ChangeFontGamma",
} }
} }
}, },
{ {
icon = "resources/icons/appbar.settings.large.png", icon = "resources/icons/appbar.settings.large.png",
options = { options = {
{ {
name = "view_mode", name = "view_mode",
name_text = S.VIEW_MODE, name_text = S.VIEW_MODE,
toggle = {S.VIEW_SCROLL, S.VIEW_PAGE}, toggle = {S.VIEW_SCROLL, S.VIEW_PAGE},
values = {1, 0}, values = {1, 0},
default_value = 0, default_value = 0,
args = {"scroll", "page"}, args = {"scroll", "page"},
default_arg = "page", default_arg = "page",
event = "SetViewMode", event = "SetViewMode",
}, },
{ {
name = "status_line", name = "status_line",
name_text = S.PROGRESS_BAR, name_text = S.PROGRESS_BAR,
toggle = {S.FULL, S.MINI}, toggle = {S.FULL, S.MINI},
values = {0, 1}, values = {0, 1},
default_value = DCREREADER_PROGRESS_BAR, default_value = DCREREADER_PROGRESS_BAR,
args = {0, 1}, args = {0, 1},
default_arg = DCREREADER_PROGRESS_BAR, default_arg = DCREREADER_PROGRESS_BAR,
event = "SetStatusLine", event = "SetStatusLine",
}, },
{ {
name = "embedded_css", name = "embedded_css",
name_text = S.EMBEDDED_STYLE, name_text = S.EMBEDDED_STYLE,
toggle = {S.ON, S.OFF}, toggle = {S.ON, S.OFF},
values = {1, 0}, values = {1, 0},
default_value = 1, default_value = 1,
args = {true, false}, args = {true, false},
default_arg = nil, default_arg = nil,
event = "ToggleEmbeddedStyleSheet", event = "ToggleEmbeddedStyleSheet",
}, },
}, },
}, },
} }
return CreOptions return CreOptions

@ -4,222 +4,222 @@ local S = require("ui/data/strings")
local _ = require("gettext") local _ = require("gettext")
local KoptOptions = { local KoptOptions = {
prefix = 'kopt', prefix = 'kopt',
{ {
icon = "resources/icons/appbar.transform.rotate.right.large.png", icon = "resources/icons/appbar.transform.rotate.right.large.png",
options = { options = {
{ {
name = "screen_mode", name = "screen_mode",
name_text = S.SCREEN_MODE, name_text = S.SCREEN_MODE,
toggle = {S.PORTRAIT, S.LANDSCAPE}, toggle = {S.PORTRAIT, S.LANDSCAPE},
alternate = false, alternate = false,
args = {"portrait", "landscape"}, args = {"portrait", "landscape"},
default_arg = "portrait", default_arg = "portrait",
current_func = function() return Screen:getScreenMode() end, current_func = function() return Screen:getScreenMode() end,
event = "SetScreenMode", event = "SetScreenMode",
} }
} }
}, },
{ {
icon = "resources/icons/appbar.crop.large.png", icon = "resources/icons/appbar.crop.large.png",
options = { options = {
{ {
name = "trim_page", name = "trim_page",
name_text = S.PAGE_CROP, name_text = S.PAGE_CROP,
width = 225, width = 225,
toggle = {S.MANUAL, S.AUTO, S.SEMIAUTO}, toggle = {S.MANUAL, S.AUTO, S.SEMIAUTO},
alternate = false, alternate = false,
values = {0, 1, 2}, values = {0, 1, 2},
default_value = DKOPTREADER_CONFIG_TRIM_PAGE, default_value = DKOPTREADER_CONFIG_TRIM_PAGE,
event = "PageCrop", event = "PageCrop",
args = {"manual", "auto", "semi-auto"}, args = {"manual", "auto", "semi-auto"},
} }
} }
}, },
{ {
icon = "resources/icons/appbar.column.two.large.png", icon = "resources/icons/appbar.column.two.large.png",
options = { options = {
{ {
name = "page_scroll", name = "page_scroll",
name_text = S.SCROLL_MODE, name_text = S.SCROLL_MODE,
toggle = {S.ON, S.OFF}, toggle = {S.ON, S.OFF},
values = {1, 0}, values = {1, 0},
default_value = DSCROLL_MODE, default_value = DSCROLL_MODE,
event = "ToggleScrollMode", event = "ToggleScrollMode",
args = {true, false}, args = {true, false},
}, },
{ {
name = "full_screen", name = "full_screen",
name_text = S.PROGRESS_BAR, name_text = S.PROGRESS_BAR,
toggle = {S.OFF, S.ON}, toggle = {S.OFF, S.ON},
values = {1, 0}, values = {1, 0},
default_value = DFULL_SCREEN, default_value = DFULL_SCREEN,
event = "SetFullScreen", event = "SetFullScreen",
args = {true, false}, args = {true, false},
}, },
{ {
name = "page_margin", name = "page_margin",
name_text = S.PAGE_MARGIN, name_text = S.PAGE_MARGIN,
toggle = {S.SMALL, S.MEDIUM, S.LARGE}, toggle = {S.SMALL, S.MEDIUM, S.LARGE},
values = {0.05, 0.10, 0.15}, values = {0.05, 0.10, 0.15},
default_value = DKOPTREADER_CONFIG_PAGE_MARGIN, default_value = DKOPTREADER_CONFIG_PAGE_MARGIN,
event = "MarginUpdate", event = "MarginUpdate",
}, },
{ {
name = "line_spacing", name = "line_spacing",
name_text = S.LINE_SPACING, name_text = S.LINE_SPACING,
toggle = {S.SMALL, S.MEDIUM, S.LARGE}, toggle = {S.SMALL, S.MEDIUM, S.LARGE},
values = {1.0, 1.2, 1.4}, values = {1.0, 1.2, 1.4},
default_value = DKOPTREADER_CONFIG_LINE_SPACING, default_value = DKOPTREADER_CONFIG_LINE_SPACING,
}, },
} }
}, },
{ {
icon = "resources/icons/appbar.text.size.large.png", icon = "resources/icons/appbar.text.size.large.png",
options = { options = {
{ {
name = "font_size", name = "font_size",
item_text = {"Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa"}, item_text = {"Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa"},
item_align_center = 1.0, item_align_center = 1.0,
spacing = 15, spacing = 15,
height = 60, height = 60,
item_font_size = {22,24,28,32,34,36,38,42,46,50}, 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}, 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, default_value = DKOPTREADER_CONFIG_FONT_SIZE,
event = "FontSizeUpdate", event = "FontSizeUpdate",
}, },
{ {
name = "font_fine_tune", name = "font_fine_tune",
name_text = S.FONTSIZE_FINE_TUNING, name_text = S.FONTSIZE_FINE_TUNING,
toggle = {S.DECREASE, S.INCREASE}, toggle = {S.DECREASE, S.INCREASE},
values = {-0.05, 0.05}, values = {-0.05, 0.05},
default_value = 0.05, default_value = 0.05,
event = "FineTuningFontSize", event = "FineTuningFontSize",
args = {-0.05, 0.05}, args = {-0.05, 0.05},
alternate = false, alternate = false,
height = 60, height = 60,
} }
} }
}, },
{ {
icon = "resources/icons/appbar.grade.b.large.png", icon = "resources/icons/appbar.grade.b.large.png",
options = { options = {
{ {
name = "contrast", name = "contrast",
name_text = S.CONTRAST, name_text = S.CONTRAST,
name_align_right = 0.2, name_align_right = 0.2,
item_text = {S.LIGHTEST , S.LIGHTER, S.DEFAULT, S.DARKER, S.DARKEST}, item_text = {S.LIGHTEST , S.LIGHTER, S.DEFAULT, S.DARKER, S.DARKEST},
item_font_size = 18, item_font_size = 18,
item_align_center = 0.8, item_align_center = 0.8,
values = {2.0, 1.5, 1.0, 0.5, 0.2}, values = {2.0, 1.5, 1.0, 0.5, 0.2},
default_value = DKOPTREADER_CONFIG_CONTRAST, default_value = DKOPTREADER_CONFIG_CONTRAST,
event = "GammaUpdate", event = "GammaUpdate",
args = {0.5, 0.8, 1.0, 2.0, 4.0}, args = {0.5, 0.8, 1.0, 2.0, 4.0},
} }
} }
}, },
{ {
icon = "resources/icons/appbar.settings.large.png", icon = "resources/icons/appbar.settings.large.png",
options = { options = {
{ {
name = "text_wrap", name = "text_wrap",
name_text = _("Reflow"), name_text = _("Reflow"),
toggle = {S.ON, S.OFF}, toggle = {S.ON, S.OFF},
values = {1, 0}, values = {1, 0},
default_value = DKOPTREADER_CONFIG_TEXT_WRAP, default_value = DKOPTREADER_CONFIG_TEXT_WRAP,
events = { events = {
{ {
event = "RedrawCurrentPage", event = "RedrawCurrentPage",
}, },
{ {
event = "RestoreZoomMode", event = "RestoreZoomMode",
}, },
{ {
event = "InitScrollPageStates", event = "InitScrollPageStates",
}, },
} }
}, },
{ {
name="doc_language", name="doc_language",
name_text = S.DOC_LANG, name_text = S.DOC_LANG,
toggle = DKOPTREADER_CONFIG_DOC_LANGS_TEXT, toggle = DKOPTREADER_CONFIG_DOC_LANGS_TEXT,
values = DKOPTREADER_CONFIG_DOC_LANGS_CODE, values = DKOPTREADER_CONFIG_DOC_LANGS_CODE,
default_value = DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE, default_value = DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE,
event = "DocLangUpdate", event = "DocLangUpdate",
args = DKOPTREADER_CONFIG_DOC_LANGS_CODE, args = DKOPTREADER_CONFIG_DOC_LANGS_CODE,
}, },
{ {
name = "word_spacing", name = "word_spacing",
name_text = S.WORD_GAP, name_text = S.WORD_GAP,
toggle = {S.SMALL, S.AUTO, S.LARGE}, toggle = {S.SMALL, S.AUTO, S.LARGE},
values = DKOPTREADER_CONFIG_WORD_SAPCINGS, values = DKOPTREADER_CONFIG_WORD_SAPCINGS,
default_value = DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING, default_value = DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING,
}, },
{ {
name = "writing_direction", name = "writing_direction",
name_text = S.WRITING_DIR, name_text = S.WRITING_DIR,
toggle = {S.LTR, S.RTL, S.TBRTL}, toggle = {S.LTR, S.RTL, S.TBRTL},
values = {0, 1, 2}, values = {0, 1, 2},
default_value = 0, default_value = 0,
}, },
{ {
name = "quality", name = "quality",
name_text = S.RENDER_QUALITY, name_text = S.RENDER_QUALITY,
toggle = {S.LOW, S.DEFAULT, S.HIGH}, toggle = {S.LOW, S.DEFAULT, S.HIGH},
values={0.5, 1.0, 1.5}, values={0.5, 1.0, 1.5},
default_value = DKOPTREADER_CONFIG_RENDER_QUALITY, default_value = DKOPTREADER_CONFIG_RENDER_QUALITY,
}, },
{ {
name = "max_columns", name = "max_columns",
name_text = S.COLUMNS, name_text = S.COLUMNS,
item_icons = { item_icons = {
"resources/icons/appbar.column.one.png", "resources/icons/appbar.column.one.png",
"resources/icons/appbar.column.two.png", "resources/icons/appbar.column.two.png",
"resources/icons/appbar.column.three.png", "resources/icons/appbar.column.three.png",
}, },
values = {1,2,3}, values = {1,2,3},
default_value = DKOPTREADER_CONFIG_MAX_COLUMNS, default_value = DKOPTREADER_CONFIG_MAX_COLUMNS,
}, },
{ {
name = "justification", name = "justification",
name_text = S.TEXT_ALIGN, name_text = S.TEXT_ALIGN,
item_icons = { item_icons = {
"resources/icons/appbar.align.auto.png", "resources/icons/appbar.align.auto.png",
"resources/icons/appbar.align.left.png", "resources/icons/appbar.align.left.png",
"resources/icons/appbar.align.center.png", "resources/icons/appbar.align.center.png",
"resources/icons/appbar.align.right.png", "resources/icons/appbar.align.right.png",
"resources/icons/appbar.align.justify.png", "resources/icons/appbar.align.justify.png",
}, },
values = {-1,0,1,2,3}, values = {-1,0,1,2,3},
default_value = DKOPTREADER_CONFIG_JUSTIFICATION, default_value = DKOPTREADER_CONFIG_JUSTIFICATION,
}, },
{ {
name = "defect_size", name = "defect_size",
name_text = S.DEFECT_SIZE, name_text = S.DEFECT_SIZE,
toggle = {S.SMALL, S.MEDIUM, S.LARGE}, toggle = {S.SMALL, S.MEDIUM, S.LARGE},
values = {1.0, 3.0, 5.0}, values = {1.0, 3.0, 5.0},
default_value = DKOPTREADER_CONFIG_DEFECT_SIZE, default_value = DKOPTREADER_CONFIG_DEFECT_SIZE,
event = "DefectSizeUpdate", event = "DefectSizeUpdate",
show = false, show = false,
}, },
{ {
name = "auto_straighten", name = "auto_straighten",
name_text = S.AUTO_STRAIGHTEN, name_text = S.AUTO_STRAIGHTEN,
toggle = {S.ZERO_DEG, S.FIVE_DEG, S.TEN_DEG}, toggle = {S.ZERO_DEG, S.FIVE_DEG, S.TEN_DEG},
values = {0, 5, 10}, values = {0, 5, 10},
default_value = DKOPTREADER_CONFIG_AUTO_STRAIGHTEN, default_value = DKOPTREADER_CONFIG_AUTO_STRAIGHTEN,
show = false, show = false,
}, },
{ {
name = "detect_indent", name = "detect_indent",
name_text = S.INDENTATION, name_text = S.INDENTATION,
toggle = {S.ON, S.OFF}, toggle = {S.ON, S.OFF},
values = {1, 0}, values = {1, 0},
default_value = DKOPTREADER_CONFIG_DETECT_INDENT, default_value = DKOPTREADER_CONFIG_DETECT_INDENT,
show = false, show = false,
}, },
} }
}, },
} }
return KoptOptions return KoptOptions

@ -6,229 +6,229 @@ local Screen = require("ui/device/screen")
-- lfs -- lfs
local Device = { local Device = {
screen_saver_mode = false, screen_saver_mode = false,
charging_mode = false, charging_mode = false,
survive_screen_saver = false, survive_screen_saver = false,
touch_dev = nil, touch_dev = nil,
model = nil, model = nil,
firmware_rev = nil, firmware_rev = nil,
powerd = nil, powerd = nil,
has_no_keyboard = nil, has_no_keyboard = nil,
is_touch_device = nil, is_touch_device = nil,
has_front_light = nil, has_front_light = nil,
screen = Screen screen = Screen
} }
Screen.device = Device Screen.device = Device
function Set (list) function Set (list)
local set = {} local set = {}
for _, l in ipairs(list) do set[l] = true end for _, l in ipairs(list) do set[l] = true end
return set return set
end end
function Device:getModel() function Device:getModel()
if self.model then return self.model end if self.model then return self.model end
if util.isEmulated() then if util.isEmulated() then
self.model = "Emulator" self.model = "Emulator"
return self.model return self.model
end end
self.model = nil self.model = nil
local kindle_sn = io.open("/proc/usid", "r") local kindle_sn = io.open("/proc/usid", "r")
if kindle_sn then if kindle_sn then
local kindle_devcode = string.sub(kindle_sn:read(),3,4) local kindle_devcode = string.sub(kindle_sn:read(),3,4)
kindle_sn:close() kindle_sn:close()
-- NOTE: Update me when new models come out :) -- NOTE: Update me when new models come out :)
local k2_set = Set { "02", "03" } local k2_set = Set { "02", "03" }
local dx_set = Set { "04", "05" } local dx_set = Set { "04", "05" }
local dxg_set = Set { "09" } local dxg_set = Set { "09" }
local k3_set = Set { "08", "06", "0A" } local k3_set = Set { "08", "06", "0A" }
local k4_set = Set { "0E", "23" } local k4_set = Set { "0E", "23" }
local touch_set = Set { "0F", "11", "10", "12" } local touch_set = Set { "0F", "11", "10", "12" }
local pw_set = Set { "24", "1B", "1D", "1F", "1C", "20" } local pw_set = Set { "24", "1B", "1D", "1F", "1C", "20" }
local pw2_set = Set { "D4", "5A", "D5", "D7", "D8", "F2" } local pw2_set = Set { "D4", "5A", "D5", "D7", "D8", "F2" }
if k2_set[kindle_devcode] then if k2_set[kindle_devcode] then
self.model = "Kindle2" self.model = "Kindle2"
elseif dx_set[kindle_devcode] then elseif dx_set[kindle_devcode] then
self.model = "Kindle2" self.model = "Kindle2"
elseif dxg_set[kindle_devcode] then elseif dxg_set[kindle_devcode] then
self.model = "Kindle2" self.model = "Kindle2"
elseif k3_set[kindle_devcode] then elseif k3_set[kindle_devcode] then
self.model = "Kindle3" self.model = "Kindle3"
elseif k4_set[kindle_devcode] then elseif k4_set[kindle_devcode] then
self.model = "Kindle4" self.model = "Kindle4"
elseif touch_set[kindle_devcode] then elseif touch_set[kindle_devcode] then
self.model = "KindleTouch" self.model = "KindleTouch"
elseif pw_set[kindle_devcode] then elseif pw_set[kindle_devcode] then
self.model = "KindlePaperWhite" self.model = "KindlePaperWhite"
elseif pw2_set[kindle_devcode] then elseif pw2_set[kindle_devcode] then
self.model = "KindlePaperWhite2" self.model = "KindlePaperWhite2"
end end
else else
local kg_test_fd = lfs.attributes("/bin/kobo_config.sh") local kg_test_fd = lfs.attributes("/bin/kobo_config.sh")
if kg_test_fd then if kg_test_fd then
local std_out = io.popen("/bin/kobo_config.sh", "r") local std_out = io.popen("/bin/kobo_config.sh", "r")
local codename = std_out:read() local codename = std_out:read()
self.model = "Kobo_" .. codename self.model = "Kobo_" .. codename
local version_file = io.open("/mnt/onboard/.kobo/version", "r") local version_file = io.open("/mnt/onboard/.kobo/version", "r")
self.firmware_rev = string.sub(version_file:read(),24,28) self.firmware_rev = string.sub(version_file:read(),24,28)
version_file:close() version_file:close()
end end
end end
return self.model return self.model
end end
function Device:getFirmVer() function Device:getFirmVer()
if not self.model then self:getModel() end if not self.model then self:getModel() end
return self.firmware_rev return self.firmware_rev
end end
function Device:isKindle4() function Device:isKindle4()
return (self:getModel() == "Kindle4") return (self:getModel() == "Kindle4")
end end
function Device:isKindle3() function Device:isKindle3()
return (self:getModel() == "Kindle3") return (self:getModel() == "Kindle3")
end end
function Device:isKindle2() function Device:isKindle2()
return (self:getModel() == "Kindle2") return (self:getModel() == "Kindle2")
end end
function Device:isKobo() function Device:isKobo()
return string.find(self:getModel(),"Kobo_") == 1 return string.find(self:getModel(),"Kobo_") == 1
end end
function Device:hasNoKeyboard() function Device:hasNoKeyboard()
if self.has_no_keyboard ~= nil then return self.has_no_keyboard end if self.has_no_keyboard ~= nil then return self.has_no_keyboard end
local model = self:getModel() local model = self:getModel()
self.has_no_keyboard = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") self.has_no_keyboard = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2")
or (model == "KindleTouch") or self:isKobo() or (model == "KindleTouch") or self:isKobo()
return self.has_no_keyboard return self.has_no_keyboard
end end
function Device:hasKeyboard() function Device:hasKeyboard()
return not self:hasNoKeyboard() return not self:hasNoKeyboard()
end end
function Device:isTouchDevice() function Device:isTouchDevice()
if self.is_touch_device ~= nil then return self.is_touch_device end if self.is_touch_device ~= nil then return self.is_touch_device end
local model = self:getModel() local model = self:getModel()
self.is_touch_device = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") self.is_touch_device = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2")
or (model == "KindleTouch") or self:isKobo() or util.isEmulated() or (model == "KindleTouch") or self:isKobo() or util.isEmulated()
return self.is_touch_device return self.is_touch_device
end end
function Device:hasFrontlight() function Device:hasFrontlight()
if self.has_front_light ~= nil then return self.has_front_light end if self.has_front_light ~= nil then return self.has_front_light end
local model = self:getModel() local model = self:getModel()
self.has_front_light = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2") self.has_front_light = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2")
or (model == "Kobo_dragon") or (model == "Kobo_kraken") or (model == "Kobo_phoenix") or (model == "Kobo_dragon") or (model == "Kobo_kraken") or (model == "Kobo_phoenix")
or util.isEmulated() or util.isEmulated()
return self.has_front_light return self.has_front_light
end end
function Device:setTouchInputDev(dev) function Device:setTouchInputDev(dev)
self.touch_dev = dev self.touch_dev = dev
end end
function Device:getTouchInputDev() function Device:getTouchInputDev()
return self.touch_dev return self.touch_dev
end end
function Device:intoScreenSaver() function Device:intoScreenSaver()
--os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt") --os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt")
if self.charging_mode == false and self.screen_saver_mode == false then if self.charging_mode == false and self.screen_saver_mode == false then
self.screen:saveCurrentBB() self.screen:saveCurrentBB()
--UIManager:show(InfoMessage:new{ --UIManager:show(InfoMessage:new{
--text = "Going into screensaver... ", --text = "Going into screensaver... ",
--timeout = 2, --timeout = 2,
--}) --})
--util.sleep(1) --util.sleep(1)
--os.execute("killall -cont cvm") --os.execute("killall -cont cvm")
self.screen_saver_mode = true self.screen_saver_mode = true
end end
end end
function Device:outofScreenSaver() function Device:outofScreenSaver()
--os.execute("echo 'screensaver out' >> /mnt/us/event_test.txt") --os.execute("echo 'screensaver out' >> /mnt/us/event_test.txt")
if self.screen_saver_mode == true and self.charging_mode == false then if self.screen_saver_mode == true and self.charging_mode == false then
-- wait for native system update screen before we recover saved -- wait for native system update screen before we recover saved
-- Blitbuffer. -- Blitbuffer.
util.usleep(1500000) util.usleep(1500000)
--os.execute("killall -stop cvm") --os.execute("killall -stop cvm")
self.screen:restoreFromSavedBB() self.screen:restoreFromSavedBB()
self.screen:refresh(0) self.screen:refresh(0)
self.survive_screen_saver = true self.survive_screen_saver = true
end end
self.screen_saver_mode = false self.screen_saver_mode = false
end end
function Device:prepareSuspend() -- currently only used for kobo devices function Device:prepareSuspend() -- currently only used for kobo devices
local powerd = self:getPowerDevice() local powerd = self:getPowerDevice()
if powerd ~= nil then if powerd ~= nil then
powerd.fl:sleep() powerd.fl:sleep()
end end
self.screen:refresh(0) self.screen:refresh(0)
self.screen_saver_mode = true self.screen_saver_mode = true
end end
function Device:Suspend() -- currently only used for kobo devices function Device:Suspend() -- currently only used for kobo devices
os.execute("./kobo_suspend.sh") os.execute("./kobo_suspend.sh")
end end
function Device:Resume() -- currently only used for kobo devices function Device:Resume() -- currently only used for kobo devices
os.execute("echo 0 > /sys/power/state-extended") os.execute("echo 0 > /sys/power/state-extended")
self.screen:refresh(0) self.screen:refresh(0)
local powerd = self:getPowerDevice() local powerd = self:getPowerDevice()
if powerd ~= nil then if powerd ~= nil then
powerd.fl:restore() powerd.fl:restore()
end end
self.screen_saver_mode = false self.screen_saver_mode = false
end end
function Device:usbPlugIn() function Device:usbPlugIn()
--os.execute("echo 'usb in' >> /mnt/us/event_test.txt") --os.execute("echo 'usb in' >> /mnt/us/event_test.txt")
if self.charging_mode == false and self.screen_saver_mode == false then if self.charging_mode == false and self.screen_saver_mode == false then
self.screen:saveCurrentBB() self.screen:saveCurrentBB()
--UIManager:show(InfoMessage:new{ --UIManager:show(InfoMessage:new{
--text = "Going into USB mode... ", --text = "Going into USB mode... ",
--timeout = 2, --timeout = 2,
--}) --})
--util.sleep(1) --util.sleep(1)
--os.execute("killall -cont cvm") --os.execute("killall -cont cvm")
end end
self.charging_mode = true self.charging_mode = true
end end
function Device:usbPlugOut() function Device:usbPlugOut()
--os.execute("echo 'usb out' >> /mnt/us/event_test.txt") --os.execute("echo 'usb out' >> /mnt/us/event_test.txt")
if self.charging_mode == true and self.screen_saver_mode == false then if self.charging_mode == true and self.screen_saver_mode == false then
--util.usleep(1500000) --util.usleep(1500000)
--os.execute("killall -stop cvm") --os.execute("killall -stop cvm")
self.screen:restoreFromSavedBB() self.screen:restoreFromSavedBB()
self.screen:refresh(0) self.screen:refresh(0)
end end
--@TODO signal filemanager for file changes 13.06 2012 (houqp) --@TODO signal filemanager for file changes 13.06 2012 (houqp)
self.charging_mode = false self.charging_mode = false
end end
function Device:getPowerDevice() function Device:getPowerDevice()
if self.powerd ~= nil then if self.powerd ~= nil then
return self.powerd return self.powerd
else else
local model = self:getModel() local model = self:getModel()
if model == "KindleTouch" or model == "KindlePaperWhite" or model == "KindlePaperWhite2" then if model == "KindleTouch" or model == "KindlePaperWhite" or model == "KindlePaperWhite2" then
self.powerd = KindlePowerD:new{model = model} self.powerd = KindlePowerD:new{model = model}
elseif self:isKobo() then elseif self:isKobo() then
self.powerd = KoboPowerD:new() self.powerd = KoboPowerD:new()
else -- emulated FrontLight else -- emulated FrontLight
self.powerd = BasePowerD:new() self.powerd = BasePowerD:new()
end end
end end
return self.powerd return self.powerd
end end
return Device return Device

@ -1,20 +1,20 @@
local BasePowerD = { local BasePowerD = {
fl_min = 0, -- min frontlight intensity fl_min = 0, -- min frontlight intensity
fl_max = 10, -- max frontlight intensity fl_max = 10, -- max frontlight intensity
flIntensity = nil, -- frontlight intensity flIntensity = nil, -- frontlight intensity
battCapacity = nil, -- battery capacity battCapacity = nil, -- battery capacity
model = nil, -- device model model = nil, -- device model
capacity_pulled_count = 0, capacity_pulled_count = 0,
capacity_cached_count = 10, capacity_cached_count = 10,
} }
function BasePowerD:new(o) function BasePowerD:new(o)
local o = o or {} local o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
if o.init then o:init() end if o.init then o:init() end
return o return o
end end
function BasePowerD:init() end function BasePowerD:init() end
@ -26,39 +26,39 @@ function BasePowerD:suspendHW() end
function BasePowerD:wakeUpHW() end function BasePowerD:wakeUpHW() end
function BasePowerD:read_int_file(file) function BasePowerD:read_int_file(file)
local f = io.open(file, "r") local f = io.open(file, "r")
local sysint = tonumber(f:read("*all"):match("%d+")) local sysint = tonumber(f:read("*all"):match("%d+"))
f:close() f:close()
return sysint return sysint
end end
function BasePowerD:setIntensity(intensity) function BasePowerD:setIntensity(intensity)
intensity = intensity < self.fl_min and self.fl_min or intensity intensity = intensity < self.fl_min and self.fl_min or intensity
intensity = intensity > self.fl_max and self.fl_max or intensity intensity = intensity > self.fl_max and self.fl_max or intensity
self.flIntensity = intensity self.flIntensity = intensity
self:setIntensityHW() self:setIntensityHW()
end end
function BasePowerD:getCapacity() function BasePowerD:getCapacity()
if capacity_pulled_count == capacity_cached_count then if capacity_pulled_count == capacity_cached_count then
capacity_pulled_count = 0 capacity_pulled_count = 0
return self:getCapacityHW() return self:getCapacityHW()
else else
capacity_pulled_count = capacity_pulled_count + 1 capacity_pulled_count = capacity_pulled_count + 1
return self.battCapacity or self:getCapacityHW() return self.battCapacity or self:getCapacityHW()
end end
end end
function BasePowerD:isCharging() function BasePowerD:isCharging()
return self:isChargingHW() return self:isChargingHW()
end end
function BasePowerD:suspend() function BasePowerD:suspend()
return self:suspendHW() return self:suspendHW()
end end
function BasePowerD:wakeUp() function BasePowerD:wakeUp()
return self:wakeUpHW() return self:wakeUpHW()
end end
return BasePowerD return BasePowerD

@ -2,85 +2,85 @@ local BasePowerD = require("ui/device/basepowerd")
-- liblipclua, see require below -- liblipclua, see require below
local KindlePowerD = BasePowerD:new{ local KindlePowerD = BasePowerD:new{
fl_min = 0, fl_max = 24, fl_min = 0, fl_max = 24,
kpw1_frontlight = "/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity", kpw1_frontlight = "/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity",
kpw2_frontlight = "/sys/class/backlight/max77696-bl/brightness", kpw2_frontlight = "/sys/class/backlight/max77696-bl/brightness",
kt_kpw_capacity = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity", kt_kpw_capacity = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity",
kpw_charging = "/sys/devices/platform/aplite_charger.0/charging", kpw_charging = "/sys/devices/platform/aplite_charger.0/charging",
kt_charging = "/sys/devices/platform/fsl-usb2-udc/charging", kt_charging = "/sys/devices/platform/fsl-usb2-udc/charging",
flIntensity = nil, flIntensity = nil,
battCapacity = nil, battCapacity = nil,
is_charging = nil, is_charging = nil,
lipc_handle = nil, lipc_handle = nil,
} }
function KindlePowerD:new(o) function KindlePowerD:new(o)
local o = o or {} local o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
if o.init then o:init(o.model) end if o.init then o:init(o.model) end
return o return o
end end
function KindlePowerD:init(model) function KindlePowerD:init(model)
local lipc = require("liblipclua") local lipc = require("liblipclua")
if lipc then if lipc then
self.lipc_handle = lipc.init("com.github.koreader") self.lipc_handle = lipc.init("com.github.koreader")
end end
if model == "KindleTouch" then if model == "KindleTouch" then
self.batt_capacity_file = self.kt_kpw_capacity self.batt_capacity_file = self.kt_kpw_capacity
self.is_charging_file = self.kt_charging self.is_charging_file = self.kt_charging
elseif model == "KindlePaperWhite" then elseif model == "KindlePaperWhite" then
self.fl_intensity_file = self.kpw1_frontlight self.fl_intensity_file = self.kpw1_frontlight
self.batt_capacity_file = self.kt_kpw_capacity self.batt_capacity_file = self.kt_kpw_capacity
self.is_charging_file = self.kpw_charging self.is_charging_file = self.kpw_charging
elseif model == "KindlePaperWhite2" then elseif model == "KindlePaperWhite2" then
self.fl_intensity_file = self.kpw2_frontlight self.fl_intensity_file = self.kpw2_frontlight
self.batt_capacity_file = self.kt_kpw_capacity self.batt_capacity_file = self.kt_kpw_capacity
self.is_charging_file = self.kpw_charging self.is_charging_file = self.kpw_charging
end end
if self.lipc_handle then if self.lipc_handle then
self.flIntensity = self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity") self.flIntensity = self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity")
else else
self.flIntensity = self:read_int_file(self.fl_intensity_file) self.flIntensity = self:read_int_file(self.fl_intensity_file)
end end
end end
function KindlePowerD:toggleFrontlight() function KindlePowerD:toggleFrontlight()
local sysint = self:read_int_file(self.fl_intensity_file) local sysint = self:read_int_file(self.fl_intensity_file)
if sysint == 0 then if sysint == 0 then
self:setIntensity(self.flIntensity) self:setIntensity(self.flIntensity)
else else
os.execute("echo -n 0 > " .. self.fl_intensity_file) os.execute("echo -n 0 > " .. self.fl_intensity_file)
end end
end end
function KindlePowerD:setIntensityHW() function KindlePowerD:setIntensityHW()
if self.lipc_handle ~= nil then if self.lipc_handle ~= nil then
self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", self.flIntensity) self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", self.flIntensity)
else else
os.execute("echo -n ".. self.flIntensity .." > " .. self.fl_intensity_file) os.execute("echo -n ".. self.flIntensity .." > " .. self.fl_intensity_file)
end end
end end
function KindlePowerD:getCapacityHW() function KindlePowerD:getCapacityHW()
if self.lipc_handle ~= nil then if self.lipc_handle ~= nil then
self.battCapacity = self.lipc_handle:get_int_property("com.lab126.powerd", "battLevel") self.battCapacity = self.lipc_handle:get_int_property("com.lab126.powerd", "battLevel")
else else
self.battCapacity = self:read_int_file(self.batt_capacity_file) self.battCapacity = self:read_int_file(self.batt_capacity_file)
end end
return self.battCapacity return self.battCapacity
end end
function KindlePowerD:isChargingHW() function KindlePowerD:isChargingHW()
if self.lipc_handle ~= nil then if self.lipc_handle ~= nil then
self.is_charging = self.lipc_handle:get_int_property("com.lab126.powerd", "isCharging") self.is_charging = self.lipc_handle:get_int_property("com.lab126.powerd", "isCharging")
else else
self.is_charging = self:read_int_file(self.is_charging_file) self.is_charging = self:read_int_file(self.is_charging_file)
end end
return self.is_charging == 1 return self.is_charging == 1
end end
return KindlePowerD return KindlePowerD

@ -1,41 +1,41 @@
local BasePowerD = require("ui/device/basepowerd") local BasePowerD = require("ui/device/basepowerd")
local KoboPowerD = BasePowerD:new{ local KoboPowerD = BasePowerD:new{
fl_min = 1, fl_max = 100, fl_min = 1, fl_max = 100,
flIntensity = 20, flIntensity = 20,
restore_settings = true, restore_settings = true,
fl = nil, fl = nil,
batt_capacity_file = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/capacity", 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", is_charging_file = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892_bat/charge_now",
battCapacity = nil, battCapacity = nil,
is_charging = nil, is_charging = nil,
} }
function KoboPowerD:init() function KoboPowerD:init()
self.fl = kobolight.open() self.fl = kobolight.open()
end end
function KoboPowerD:toggleFrontlight() function KoboPowerD:toggleFrontlight()
if self.fl ~= nil then if self.fl ~= nil then
self.fl:toggle() self.fl:toggle()
end end
end end
function KoboPowerD:setIntensityHW() function KoboPowerD:setIntensityHW()
if self.fl ~= nil then if self.fl ~= nil then
self.fl:setBrightness(self.flIntensity) self.fl:setBrightness(self.flIntensity)
end end
end end
function KoboPowerD:getCapacityHW() function KoboPowerD:getCapacityHW()
self.battCapacity = self:read_int_file(self.batt_capacity_file) self.battCapacity = self:read_int_file(self.batt_capacity_file)
return self.battCapacity return self.battCapacity
end end
function KoboPowerD:isChargingHW() function KoboPowerD:isChargingHW()
self.is_charging = self:read_int_file(self.is_charging_file) self.is_charging = self:read_int_file(self.is_charging_file)
return self.is_charging == 1 return self.is_charging == 1
end end
return KoboPowerD return KoboPowerD

@ -29,131 +29,131 @@ Codes for rotation modes:
local Screen = { local Screen = {
cur_rotation_mode = 0, cur_rotation_mode = 0,
native_rotation_mode = nil, native_rotation_mode = nil,
blitbuffer_rotation_mode = 0, blitbuffer_rotation_mode = 0,
bb = nil, bb = nil,
saved_bb = nil, saved_bb = nil,
fb = einkfb.open("/dev/fb0"), fb = einkfb.open("/dev/fb0"),
-- will be set upon loading by Device class: -- will be set upon loading by Device class:
device = nil, device = nil,
} }
function Screen:init() function Screen:init()
self.bb = self.fb.bb self.bb = self.fb.bb
self.blitbuffer_rotation_mode = self.bb:getRotation() self.blitbuffer_rotation_mode = self.bb:getRotation()
-- asking the framebuffer for orientation is error prone, -- asking the framebuffer for orientation is error prone,
-- so we do this simple heuristic (for now) -- so we do this simple heuristic (for now)
if self:getWidth() > self:getHeight() then if self:getWidth() > self:getHeight() then
self.native_rotation_mode = 1 self.native_rotation_mode = 1
else else
self.native_rotation_mode = 0 self.native_rotation_mode = 0
end end
self.cur_rotation_mode = self.native_rotation_mode self.cur_rotation_mode = self.native_rotation_mode
end end
function Screen:refresh(refresh_type, waveform_mode, x, y, w, h) 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 end
function Screen:getSize() 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 end
function Screen:getWidth() function Screen:getWidth()
return self.bb:getWidth() return self.bb:getWidth()
end end
function Screen:getHeight() function Screen:getHeight()
return self.bb:getHeight() return self.bb:getHeight()
end end
function Screen:getDPI() function Screen:getDPI()
if(self.device:getModel() == "KindlePaperWhite") if(self.device:getModel() == "KindlePaperWhite")
or (self.device:getModel() == "Kobo_kraken") or (self.device:getModel() == "Kobo_kraken")
or (self.device:getModel() == "Kobo_phoenix") then or (self.device:getModel() == "Kobo_phoenix") then
return 212 return 212
elseif self.device:getModel() == "Kobo_dragon" then elseif self.device:getModel() == "Kobo_dragon" then
return 265 return 265
elseif self.device:getModel() == "Kobo_pixie" then elseif self.device:getModel() == "Kobo_pixie" then
return 200 return 200
else else
return 167 return 167
end end
end end
function Screen:scaleByDPI(px) function Screen:scaleByDPI(px)
return math.floor(px * self:getDPI()/167) return math.floor(px * self:getDPI()/167)
end end
function Screen:rescaleByDPI(px) function Screen:rescaleByDPI(px)
return math.ceil(px * 167/self:getDPI()) return math.ceil(px * 167/self:getDPI())
end end
function Screen:getRotationMode() function Screen:getRotationMode()
return self.cur_rotation_mode return self.cur_rotation_mode
end end
function Screen:getScreenMode() function Screen:getScreenMode()
if self:getWidth() > self:getHeight() then if self:getWidth() > self:getHeight() then
return "landscape" return "landscape"
else else
return "portrait" return "portrait"
end end
end end
function Screen:setRotationMode(mode) function Screen:setRotationMode(mode)
self.fb.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode)) self.fb.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode))
self.cur_rotation_mode = mode self.cur_rotation_mode = mode
end end
function Screen:setScreenMode(mode) function Screen:setScreenMode(mode)
if mode == "portrait" then if mode == "portrait" then
if self.cur_rotation_mode ~= 0 then if self.cur_rotation_mode ~= 0 then
self:setRotationMode(0) self:setRotationMode(0)
end end
elseif mode == "landscape" then elseif mode == "landscape" then
if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then
self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3) self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3)
elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then
self:setRotationMode((self.cur_rotation_mode + 2) % 4) self:setRotationMode((self.cur_rotation_mode + 2) % 4)
end end
end end
end end
function Screen:saveCurrentBB() function Screen:saveCurrentBB()
local width, height = self:getWidth(), self:getHeight() local width, height = self:getWidth(), self:getHeight()
if not self.saved_bb then if not self.saved_bb then
self.saved_bb = Blitbuffer.new(width, height) self.saved_bb = Blitbuffer.new(width, height)
end end
if self.saved_bb:getWidth() ~= width then if self.saved_bb:getWidth() ~= width then
self.saved_bb:free() self.saved_bb:free()
self.saved_bb = Blitbuffer.new(width, height) self.saved_bb = Blitbuffer.new(width, height)
end end
self.saved_bb:blitFullFrom(self.bb) self.saved_bb:blitFullFrom(self.bb)
end end
function Screen:restoreFromSavedBB() function Screen:restoreFromSavedBB()
self:restoreFromBB(self.saved_bb) self:restoreFromBB(self.saved_bb)
-- free data -- free data
self.saved_bb = nil self.saved_bb = nil
end end
function Screen:getCurrentScreenBB() function Screen:getCurrentScreenBB()
local bb = Blitbuffer.new(self:getWidth(), self:getHeight()) local bb = Blitbuffer.new(self:getWidth(), self:getHeight())
bb:blitFullFrom(self.bb) bb:blitFullFrom(self.bb)
return bb return bb
end end
function Screen:restoreFromBB(bb) function Screen:restoreFromBB(bb)
if bb then if bb then
self.bb:blitFullFrom(bb) self.bb:blitFullFrom(bb)
else else
DEBUG("Got nil bb in restoreFromSavedBB!") DEBUG("Got nil bb in restoreFromSavedBB!")
end end
end end
return Screen return Screen

@ -10,13 +10,13 @@ below.
local Event = {} local Event = {}
function Event:new(name, ...) function Event:new(name, ...)
local o = { local o = {
handler = "on"..name, handler = "on"..name,
args = {...} args = {...}
} }
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
return Event return Event

@ -2,102 +2,102 @@ local Screen = require("ui/screen")
local DEBUG = require("dbg") local DEBUG = require("dbg")
local Font = { local Font = {
fontmap = { fontmap = {
-- default font for menu contents -- default font for menu contents
cfont = "droid/DroidSans.ttf", cfont = "droid/DroidSans.ttf",
-- default font for title -- default font for title
--tfont = "NimbusSanL-BoldItal.cff", --tfont = "NimbusSanL-BoldItal.cff",
tfont = "droid/DroidSans.ttf", tfont = "droid/DroidSans.ttf",
-- default font for footer -- default font for footer
ffont = "droid/DroidSans.ttf", ffont = "droid/DroidSans.ttf",
-- default font for reading position info -- default font for reading position info
rifont = "droid/DroidSans.ttf", rifont = "droid/DroidSans.ttf",
-- default font for pagination display -- default font for pagination display
pgfont = "droid/DroidSans.ttf", pgfont = "droid/DroidSans.ttf",
-- selectmenu: font for item shortcut -- selectmenu: font for item shortcut
scfont = "droid/DroidSansMono.ttf", scfont = "droid/DroidSansMono.ttf",
-- help page: font for displaying keys -- help page: font for displaying keys
hpkfont = "droid/DroidSansMono.ttf", hpkfont = "droid/DroidSansMono.ttf",
-- font for displaying help messages -- font for displaying help messages
hfont = "droid/DroidSans.ttf", hfont = "droid/DroidSans.ttf",
-- font for displaying input content -- font for displaying input content
-- we have to use mono here for better distance controlling -- we have to use mono here for better distance controlling
infont = "droid/DroidSansMono.ttf", infont = "droid/DroidSansMono.ttf",
-- font for info messages -- font for info messages
infofont = "droid/DroidSans.ttf", infofont = "droid/DroidSans.ttf",
}, },
fallbacks = { fallbacks = {
[1] = "droid/DroidSansFallback.ttf", [1] = "droid/DroidSansFallback.ttf",
[2] = "droid/DroidSans.ttf", [2] = "droid/DroidSans.ttf",
[3] = "freefont/FreeSans.ttf", [3] = "freefont/FreeSans.ttf",
}, },
fontdir = os.getenv("FONTDIR") or "./fonts", fontdir = os.getenv("FONTDIR") or "./fonts",
-- face table -- face table
faces = {}, faces = {},
} }
function Font:getFace(font, size) function Font:getFace(font, size)
if not font then if not font then
-- default to content font -- default to content font
font = self.cfont font = self.cfont
end end
local size = Screen:scaleByDPI(size) local size = Screen:scaleByDPI(size)
local face = self.faces[font..size] local face = self.faces[font..size]
-- build face if not found -- build face if not found
if not face then if not face then
local realname = self.fontmap[font] local realname = self.fontmap[font]
if not realname then if not realname then
realname = font realname = font
end end
realname = self.fontdir.."/"..realname realname = self.fontdir.."/"..realname
ok, face = pcall(freetype.newFace, realname, size) ok, face = pcall(freetype.newFace, realname, size)
if not ok then if not ok then
DEBUG("#! Font "..font.." ("..realname..") not supported: "..face) DEBUG("#! Font "..font.." ("..realname..") not supported: "..face)
return nil return nil
end end
self.faces[font..size] = face self.faces[font..size] = face
--DEBUG("getFace, found: "..realname.." size:"..size) --DEBUG("getFace, found: "..realname.." size:"..size)
end end
return { size = size, ftface = face, hash = font..size } return { size = size, ftface = face, hash = font..size }
end end
function Font:_readList(target, dir, effective_dir) function Font:_readList(target, dir, effective_dir)
for f in lfs.dir(dir) do for f in lfs.dir(dir) do
if lfs.attributes(dir.."/"..f, "mode") == "directory" and f ~= "." and f ~= ".." then if lfs.attributes(dir.."/"..f, "mode") == "directory" and f ~= "." and f ~= ".." then
self:_readList(target, dir.."/"..f, effective_dir..f.."/") self:_readList(target, dir.."/"..f, effective_dir..f.."/")
else else
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "") local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
if file_type == "ttf" or file_type == "cff" or file_type == "otf" then if file_type == "ttf" or file_type == "cff" or file_type == "otf" then
table.insert(target, effective_dir..f) table.insert(target, effective_dir..f)
end end
end end
end end
end end
function Font:getFontList() function Font:getFontList()
fontlist = {} fontlist = {}
self:_readList(fontlist, self.fontdir, "") self:_readList(fontlist, self.fontdir, "")
table.sort(fontlist) table.sort(fontlist)
return fontlist return fontlist
end end
function Font:update() function Font:update()
for _k, _v in ipairs(self.faces) do for _k, _v in ipairs(self.faces) do
_v:done() _v:done()
end end
self.faces = {} self.faces = {}
clearGlyphCache() clearGlyphCache()
end end
return Font return Font

@ -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 or define your own types using this as a metatable
]]-- ]]--
local Geom = { local Geom = {
x = 0, x = 0,
y = 0, y = 0,
w = 0, w = 0,
h = 0 h = 0
} }
function Geom:new(o) function Geom:new(o)
local o = o or {} local o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function Geom:copy() function Geom:copy()
local n = Geom:new() local n = Geom:new()
n.x = self.x n.x = self.x
n.y = self.y n.y = self.y
n.w = self.w n.w = self.w
n.h = self.h n.h = self.h
return n return n
end end
function Geom:__tostring() function Geom:__tostring()
return self.w.."x"..self.h.."+"..self.x.."+"..self.y return self.w.."x"..self.h.."+"..self.x.."+"..self.y
end end
--[[ --[[
offset rectangle or point by relative values offset rectangle or point by relative values
]]-- ]]--
function Geom:offsetBy(dx, dy) function Geom:offsetBy(dx, dy)
self.x = self.x + dx self.x = self.x + dx
self.y = self.y + dy self.y = self.y + dy
return self return self
end end
--[[ --[[
offset rectangle or point to certain coordinates offset rectangle or point to certain coordinates
]]-- ]]--
function Geom:offsetTo(x, y) function Geom:offsetTo(x, y)
self.x = x self.x = x
self.y = y self.y = y
return self return self
end 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 if a single factor is given, it is applied to both width and height
]]-- ]]--
function Geom:scaleBy(zx, zy) function Geom:scaleBy(zx, zy)
self.w = self.w * zx self.w = self.w * zx
self.h = self.h * (zy or zx) self.h = self.h * (zy or zx)
return self return self
end end
--[[ --[[
this method also takes care of x and y this method also takes care of x and y
]]-- ]]--
function Geom:transformByScale(zx, zy) function Geom:transformByScale(zx, zy)
self.x = self.x * zx self.x = self.x * zx
self.y = self.y * (zx or zy) self.y = self.y * (zx or zy)
self:scaleBy(zx, zy) self:scaleBy(zx, zy)
end end
--[[ --[[
return size of geom return size of geom
]]-- ]]--
function Geom:sizeof() function Geom:sizeof()
if not self.w or not self.h then if not self.w or not self.h then
return 0 return 0
else else
return self.w * self.h return self.w * self.h
end end
end end
--[[ --[[
@ -93,9 +93,9 @@ enlarges or shrinks dimensions or rectangles
note that for rectangles the offset stays the same note that for rectangles the offset stays the same
]]-- ]]--
function Geom:changeSizeBy(dw, dh) function Geom:changeSizeBy(dw, dh)
self.w = self.w + dw self.w = self.w + dw
self.h = self.h + dh self.h = self.h + dh
return self return self
end end
--[[ --[[
@ -104,25 +104,25 @@ return the outer rectangle that contains both us and a given rectangle
works for rectangles, dimensions and points works for rectangles, dimensions and points
]]-- ]]--
function Geom:combine(rect_b) function Geom:combine(rect_b)
local combined = self:copy() local combined = self:copy()
if not rect_b or rect_b:sizeof() == 0 then return combined end if not rect_b or rect_b:sizeof() == 0 then return combined end
if combined.x > rect_b.x then if combined.x > rect_b.x then
combined.x = rect_b.x combined.x = rect_b.x
end end
if combined.y > rect_b.y then if combined.y > rect_b.y then
combined.y = rect_b.y combined.y = rect_b.y
end end
if self.x + self.w > rect_b.x + rect_b.w then if self.x + self.w > rect_b.x + rect_b.w then
combined.w = self.x + self.w - combined.x combined.w = self.x + self.w - combined.x
else else
combined.w = rect_b.x + rect_b.w - combined.x combined.w = rect_b.x + rect_b.w - combined.x
end end
if self.y + self.h > rect_b.y + rect_b.h then if self.y + self.h > rect_b.y + rect_b.h then
combined.h = self.y + self.h - combined.y combined.h = self.y + self.h - combined.y
else else
combined.h = rect_b.y + rect_b.h - combined.y combined.h = rect_b.y + rect_b.h - combined.y
end end
return combined return combined
end 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. TODO: what happens if there is no rectangle shared? currently behaviour is undefined.
]]-- ]]--
function Geom:intersect(rect_b) function Geom:intersect(rect_b)
-- make a copy of self -- make a copy of self
local intersected = self:copy() local intersected = self:copy()
if self.x < rect_b.x then if self.x < rect_b.x then
intersected.x = rect_b.x intersected.x = rect_b.x
end end
if self.y < rect_b.y then if self.y < rect_b.y then
intersected.y = rect_b.y intersected.y = rect_b.y
end end
if self.x + self.w < rect_b.x + rect_b.w then if self.x + self.w < rect_b.x + rect_b.w then
intersected.w = self.x + self.w - intersected.x intersected.w = self.x + self.w - intersected.x
else else
intersected.w = rect_b.x + rect_b.w - intersected.x intersected.w = rect_b.x + rect_b.w - intersected.x
end end
if self.y + self.h < rect_b.y + rect_b.h then if self.y + self.h < rect_b.y + rect_b.h then
intersected.h = self.y + self.h - intersected.y intersected.h = self.y + self.h - intersected.y
else else
intersected.h = rect_b.y + rect_b.h - intersected.y intersected.h = rect_b.y + rect_b.h - intersected.y
end end
return intersected return intersected
end end
--[[ --[[
return true if self does not share any area with rect_b return true if self does not share any area with rect_b
]]-- ]]--
function Geom:notIntersectWith(rect_b) function Geom:notIntersectWith(rect_b)
if (self.x >= (rect_b.x + rect_b.w)) if (self.x >= (rect_b.x + rect_b.w))
or (self.y >= (rect_b.y + rect_b.h)) or (self.y >= (rect_b.y + rect_b.h))
or (rect_b.x >= (self.x + self.w)) or (rect_b.x >= (self.x + self.w))
or (rect_b.y >= (self.y + self.h)) then or (rect_b.y >= (self.y + self.h)) then
return true return true
end end
return false return false
end end
--[[ --[[
set size of dimension or rectangle to size of given dimension/rectangle set size of dimension or rectangle to size of given dimension/rectangle
]]-- ]]--
function Geom:setSizeTo(rect_b) function Geom:setSizeTo(rect_b)
self.w = rect_b.w self.w = rect_b.w
self.h = rect_b.h self.h = rect_b.h
return self return self
end end
--[[ --[[
@ -181,14 +181,14 @@ works for dimensions, too
for points, it is basically an equality check for points, it is basically an equality check
]]-- ]]--
function Geom:contains(rect_b) function Geom:contains(rect_b)
if self.x <= rect_b.x if self.x <= rect_b.x
and self.y <= rect_b.y and self.y <= rect_b.y
and self.x + self.w >= rect_b.x + rect_b.w and self.x + self.w >= rect_b.x + rect_b.w
and self.y + self.h >= rect_b.y + rect_b.h and self.y + self.h >= rect_b.y + rect_b.h
then then
return true return true
end end
return false return false
end end
--[[ --[[
@ -197,48 +197,48 @@ check for equality
works for rectangles, points, dimensions works for rectangles, points, dimensions
]]-- ]]--
function Geom:__eq(rect_b) function Geom:__eq(rect_b)
if self.x == rect_b.x if self.x == rect_b.x
and self.y == rect_b.y and self.y == rect_b.y
and self:equalSize(rect_b) and self:equalSize(rect_b)
then then
return true return true
end end
return false return false
end end
--[[ --[[
check size of dimension/rectangle for equality check size of dimension/rectangle for equality
]]-- ]]--
function Geom:equalSize(rect_b) function Geom:equalSize(rect_b)
if self.w == rect_b.w if self.w == rect_b.w
and self.h == rect_b.h and self.h == rect_b.h
then then
return true return true
end end
return false return false
end end
--[[ --[[
check if our size is smaller than the size of the given dimension/rectangle check if our size is smaller than the size of the given dimension/rectangle
]]-- ]]--
function Geom:__lt(rect_b) function Geom:__lt(rect_b)
DEBUG("lt:",self,rect_b) DEBUG("lt:",self,rect_b)
if self.w < rect_b.w and self.h < rect_b.h then if self.w < rect_b.w and self.h < rect_b.h then
DEBUG("lt+") DEBUG("lt+")
return true return true
end end
DEBUG("lt-") DEBUG("lt-")
return false return false
end end
--[[ --[[
check if our size is smaller or equal the size of the given dimension/rectangle check if our size is smaller or equal the size of the given dimension/rectangle
]]-- ]]--
function Geom:__le(rect_b) function Geom:__le(rect_b)
if self.w <= rect_b.w and self.h <= rect_b.h then if self.w <= rect_b.w and self.h <= rect_b.h then
return true return true
end end
return false return false
end 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 rectangle into the given rectangle
]]-- ]]--
function Geom:offsetWithin(rect_b, dx, dy) function Geom:offsetWithin(rect_b, dx, dy)
-- check size constraints and shrink us when we're too big -- check size constraints and shrink us when we're too big
if self.w > rect_b.w then if self.w > rect_b.w then
self.w = rect_b.w self.w = rect_b.w
end end
if self.h > rect_b.h then if self.h > rect_b.h then
self.h = rect_b.h self.h = rect_b.h
end end
-- offset -- offset
self.x = self.x + dx self.x = self.x + dx
self.y = self.y + dy self.y = self.y + dy
-- check offsets -- check offsets
if self.x < rect_b.x then if self.x < rect_b.x then
self.x = rect_b.x self.x = rect_b.x
end end
if self.y < rect_b.y then if self.y < rect_b.y then
self.y = rect_b.y self.y = rect_b.y
end end
if self.x + self.w > rect_b.x + rect_b.w then if self.x + self.w > rect_b.x + rect_b.w then
self.x = rect_b.x + rect_b.w - self.w self.x = rect_b.x + rect_b.w - self.w
end end
if self.y + self.h > rect_b.y + rect_b.h then if self.y + self.h > rect_b.y + rect_b.h then
self.y = rect_b.y + rect_b.h - self.h self.y = rect_b.y + rect_b.h - self.h
end end
end end
--[[ --[[
center the current rectangle at position x and y of a given rectangle center the current rectangle at position x and y of a given rectangle
]]-- ]]--
function Geom:centerWithin(rect_b, x, y) function Geom:centerWithin(rect_b, x, y)
-- check size constraints and shrink us when we're too big -- check size constraints and shrink us when we're too big
if self.w > rect_b.w then if self.w > rect_b.w then
self.w = rect_b.w self.w = rect_b.w
end end
if self.h > rect_b.h then if self.h > rect_b.h then
self.h = rect_b.h self.h = rect_b.h
end end
-- place to center -- place to center
self.x = x - self.w/2 self.x = x - self.w/2
self.y = y - self.h/2 self.y = y - self.h/2
-- check boundary -- check boundary
if self.x < rect_b.x then if self.x < rect_b.x then
self.x = rect_b.x self.x = rect_b.x
end end
if self.y < rect_b.y then if self.y < rect_b.y then
self.y = rect_b.y self.y = rect_b.y
end end
if self.x + self.w > rect_b.x + rect_b.w then if self.x + self.w > rect_b.x + rect_b.w then
self.x = rect_b.x + rect_b.w - self.w self.x = rect_b.x + rect_b.w - self.w
end end
if self.y + self.h > rect_b.y + rect_b.h then if self.y + self.h > rect_b.y + rect_b.h then
self.y = rect_b.y + rect_b.h - self.h self.y = rect_b.y + rect_b.h - self.h
end end
end end
function Geom:shrinkInside(rect_b, dx, dy) function Geom:shrinkInside(rect_b, dx, dy)
self:offsetBy(dx, dy) self:offsetBy(dx, dy)
return self:intersect(rect_b) return self:intersect(rect_b)
end end
--[[ --[[
return the Euclidean distance between two geoms return the Euclidean distance between two geoms
]]-- ]]--
function Geom:distance(geom) 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 end
--[[ --[[
return the midpoint of two geoms return the midpoint of two geoms
]]-- ]]--
function Geom:midpoint(geom) function Geom:midpoint(geom)
return Geom:new{ return Geom:new{
x = (self.x + geom.x) / 2, x = (self.x + geom.x) / 2,
y = (self.y + geom.y) / 2, y = (self.y + geom.y) / 2,
w = 0, h = 0, w = 0, h = 0,
} }
end end
--[[ --[[
return center point in this geom return center point in this geom
]]-- ]]--
function Geom:center() function Geom:center()
return Geom:new{ return Geom:new{
x = self.x + self.w / 2, x = self.x + self.w / 2,
y = self.y + self.h / 2, y = self.y + self.h / 2,
w = 0, h = 0, w = 0, h = 0,
} }
end end
return Geom return Geom

File diff suppressed because it is too large Load Diff

@ -1,50 +1,50 @@
local TimeVal = require("ui/timeval") local TimeVal = require("ui/timeval")
local GestureRange = { local GestureRange = {
ges = nil, ges = nil,
-- spatial range limits the gesture emitting position -- spatial range limits the gesture emitting position
range = nil, range = nil,
-- temproal range limits the gesture emitting rate -- temproal range limits the gesture emitting rate
rate = nil, rate = nil,
-- span limits of this gesture -- span limits of this gesture
scale = nil, scale = nil,
} }
function GestureRange:new(o) function GestureRange:new(o)
local o = o or {} local o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function GestureRange:match(gs) function GestureRange:match(gs)
if gs.ges ~= self.ges then if gs.ges ~= self.ges then
return false return false
end end
if self.range then if self.range then
if not self.range:contains(gs.pos) then if not self.range:contains(gs.pos) then
return false return false
end end
end end
if self.rate then if self.rate then
local last_time = self.last_time or TimeVal:new{} local last_time = self.last_time or TimeVal:new{}
if gs.time - last_time > TimeVal:new{usec = 1000000 / self.rate} then if gs.time - last_time > TimeVal:new{usec = 1000000 / self.rate} then
self.last_time = gs.time self.last_time = gs.time
else else
return false return false
end end
end end
if self.scale then if self.scale then
if self.scale[1] > gs.span or self.scale[2] < gs.span then if self.scale[1] > gs.span or self.scale[2] < gs.span then
return false return false
end end
end end
if self.direction then if self.direction then
if self.direction ~= gs.direction then if self.direction ~= gs.direction then
return false return false
end end
end end
return true return true
end end
return GestureRange return GestureRange

File diff suppressed because it is too large Load Diff

@ -7,47 +7,47 @@ local _ = require("gettext")
Language = {} Language = {}
function Language:changeLanguage(lang_locale) function Language:changeLanguage(lang_locale)
_.changeLang(lang_locale) _.changeLang(lang_locale)
G_reader_settings:saveSetting("language", lang_locale) G_reader_settings:saveSetting("language", lang_locale)
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = _("Please restart reader for new language setting to take effect."), text = _("Please restart reader for new language setting to take effect."),
timeout = 3, timeout = 3,
}) })
end end
function Language:genLanguageSubItem(lang, lang_locale) function Language:genLanguageSubItem(lang, lang_locale)
return { return {
text = lang, text = lang,
callback = function() callback = function()
self:changeLanguage(lang_locale) self:changeLanguage(lang_locale)
end end
} }
end end
function Language:getLangMenuTable() function Language:getLangMenuTable()
-- cache menu table -- cache menu table
if not self.LangMenuTable then if not self.LangMenuTable then
self.LangMenuTable = { self.LangMenuTable = {
text = _("Language"), text = _("Language"),
-- NOTE: language with no translation are commented out for now -- NOTE: language with no translation are commented out for now
sub_item_table = { sub_item_table = {
self:genLanguageSubItem("English", "C"), self:genLanguageSubItem("English", "C"),
self:genLanguageSubItem("čeština", "cs_CZ"), self:genLanguageSubItem("čeština", "cs_CZ"),
self:genLanguageSubItem("Deutsch", "de"), self:genLanguageSubItem("Deutsch", "de"),
self:genLanguageSubItem("français", "fr"), self:genLanguageSubItem("français", "fr"),
--self:genLanguageSubItem("magyar", "hu"), --self:genLanguageSubItem("magyar", "hu"),
self:genLanguageSubItem("Italiano", "it_IT"), self:genLanguageSubItem("Italiano", "it_IT"),
self:genLanguageSubItem("Polski", "pl"), self:genLanguageSubItem("Polski", "pl"),
self:genLanguageSubItem("Português do Brasil", "pt_BR"), self:genLanguageSubItem("Português do Brasil", "pt_BR"),
self:genLanguageSubItem("Русский язык", "ru"), self:genLanguageSubItem("Русский язык", "ru"),
--self:genLanguageSubItem("svenska", "sv"), --self:genLanguageSubItem("svenska", "sv"),
self:genLanguageSubItem("Türkçe", "tr"), self:genLanguageSubItem("Türkçe", "tr"),
--self:genLanguageSubItem("Tiếng Việt", "vi"), --self:genLanguageSubItem("Tiếng Việt", "vi"),
self:genLanguageSubItem("简体中文", "zh_CN"), self:genLanguageSubItem("简体中文", "zh_CN"),
} }
} }
end end
return self.LangMenuTable return self.LangMenuTable
end end
return Language return Language

@ -1,49 +1,66 @@
local Configurable = {} 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) function Configurable:hash(sep)
local hash = "" local hash = ""
local excluded = {multi_threads = true,} local excluded = {multi_threads = true,}
for key,value in pairs(self) do for key,value in pairs(self) do
if type(value) == "number" or type(value) == "string" if type(value) == "number" or type(value) == "string" then
and not excluded[key] then hash = hash..sep..value
hash = hash..sep..value end
end end
end return hash
return hash
end end
function Configurable:loadDefaults(config_options) function Configurable:loadDefaults(config_options)
for i=1,#config_options do -- reset configurable before loading new options
local options = config_options[i].options self:reset()
for j=1,#config_options[i].options do for i=1,#config_options do
local key = config_options[i].options[j].name local options = config_options[i].options
self[key] = config_options[i].options[j].default_value for j=1,#config_options[i].options do
if not self[key] then local key = config_options[i].options[j].name
self[key] = config_options[i].options[j].default_arg self[key] = config_options[i].options[j].default_value
end if not self[key] then
end self[key] = config_options[i].options[j].default_arg
end end
end
end
end end
function Configurable:loadSettings(settings, prefix) function Configurable:loadSettings(settings, prefix)
for key,value in pairs(self) do for key,value in pairs(self) do
if type(value) == "number" or type(value) == "string" if type(value) == "number" or type(value) == "string"
or type(value) == "table" then or type(value) == "table" then
local saved_value = settings:readSetting(prefix..key) local saved_value = settings:readSetting(prefix..key)
self[key] = (saved_value == nil) and self[key] or saved_value self[key] = (saved_value == nil) and self[key] or saved_value
--Debug("Configurable:loadSettings", "key", key, "saved value", saved_value,"Configurable.key", self[key]) --Debug("Configurable:loadSettings", "key", key, "saved value",
end --saved_value,"Configurable.key", self[key])
end end
--Debug("loaded config:", dump(Configurable)) end
--Debug("loaded config:", dump(Configurable))
end end
function Configurable:saveSettings(settings, prefix) function Configurable:saveSettings(settings, prefix)
for key,value in pairs(self) do for key,value in pairs(self) do
if type(value) == "number" or type(value) == "string" if type(value) == "number" or type(value) == "string"
or type(value) == "table" then or type(value) == "table" then
settings:saveSetting(prefix..key, value) settings:saveSetting(prefix..key, value)
end end
end end
end end
return Configurable return Configurable

@ -5,43 +5,43 @@ local Device = require("ui/device")
local ReaderActivityIndicator = EventListener:new{} local ReaderActivityIndicator = EventListener:new{}
function ReaderActivityIndicator:init() function ReaderActivityIndicator:init()
local dev_mod = Device:getModel() local dev_mod = Device:getModel()
if dev_mod == "KindlePaperWhite" or dev_mod == "KindlePaperWhite2" or dev_mod == "KindleTouch" then if dev_mod == "KindlePaperWhite" or dev_mod == "KindlePaperWhite2" or dev_mod == "KindleTouch" then
require "liblipclua" require "liblipclua"
self.lipc_handle = lipc.init("com.github.koreader.activityindicator") self.lipc_handle = lipc.init("com.github.koreader.activityindicator")
end end
end end
function ReaderActivityIndicator:onStartActivityIndicator() function ReaderActivityIndicator:onStartActivityIndicator()
if self.lipc_handle then if self.lipc_handle then
-- check if activity indicator is needed -- check if activity indicator is needed
if self.document.configurable.text_wrap == 1 then if self.document.configurable.text_wrap == 1 then
-- start indicator depends on pillow being enabled -- start indicator depends on pillow being enabled
self.lipc_handle:set_string_property( self.lipc_handle:set_string_property(
"com.lab126.pillow", "activityIndicator", "com.lab126.pillow", "activityIndicator",
'{"activityIndicator":{ \ '{"activityIndicator":{ \
"action":"start","timeout":10000, \ "action":"start","timeout":10000, \
"clientId":"com.github.koreader.activityindicator", \ "clientId":"com.github.koreader.activityindicator", \
"priority":true}}') "priority":true}}')
self.indicator_started = true self.indicator_started = true
end end
end end
return true return true
end end
function ReaderActivityIndicator:onStopActivityIndicator() function ReaderActivityIndicator:onStopActivityIndicator()
if self.lipc_handle and self.indicator_started then if self.lipc_handle and self.indicator_started then
-- stop indicator depends on pillow being enabled -- stop indicator depends on pillow being enabled
self.lipc_handle:set_string_property( self.lipc_handle:set_string_property(
"com.lab126.pillow", "activityIndicator", "com.lab126.pillow", "activityIndicator",
'{"activityIndicator":{ \ '{"activityIndicator":{ \
"action":"stop","timeout":10000, \ "action":"stop","timeout":10000, \
"clientId":"com.github.koreader.activityindicator", \ "clientId":"com.github.koreader.activityindicator", \
"priority":true}}') "priority":true}}')
self.indicator_started = false self.indicator_started = false
util.usleep(1000000) util.usleep(1000000)
end end
return true return true
end end
return ReaderActivityIndicator return ReaderActivityIndicator

@ -11,218 +11,218 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ReaderBookmark = InputContainer:new{ local ReaderBookmark = InputContainer:new{
bm_menu_title = _("Bookmarks"), bm_menu_title = _("Bookmarks"),
bookmarks = nil, bookmarks = nil,
} }
function ReaderBookmark:init() function ReaderBookmark:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ShowBookmark = { ShowBookmark = {
{ "B" }, { "B" },
doc = _("show bookmarks") }, doc = _("show bookmarks") },
} }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
ShowBookmark = { ShowBookmark = {
GestureRange:new{ GestureRange:new{
ges = "two_finger_swipe", ges = "two_finger_swipe",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
}, },
direction = "west" direction = "west"
} }
}, },
} }
end end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderBookmark:onReadSettings(config) function ReaderBookmark:onReadSettings(config)
self.bookmarks = config:readSetting("bookmarks") or {} self.bookmarks = config:readSetting("bookmarks") or {}
end end
function ReaderBookmark:onSaveSettings() function ReaderBookmark:onSaveSettings()
self.ui.doc_settings:saveSetting("bookmarks", self.bookmarks) self.ui.doc_settings:saveSetting("bookmarks", self.bookmarks)
end end
function ReaderBookmark:onToggleBookmark() function ReaderBookmark:onToggleBookmark()
local pn_or_xp = nil local pn_or_xp = nil
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
pn_or_xp = self.view.state.page pn_or_xp = self.view.state.page
else else
pn_or_xp = self.ui.document:getXPointer() pn_or_xp = self.ui.document:getXPointer()
end end
self:toggleBookmark(pn_or_xp) self:toggleBookmark(pn_or_xp)
self.view.dogear_visible = not self.view.dogear_visible self.view.dogear_visible = not self.view.dogear_visible
UIManager:setDirty(self.view.dialog, "partial") UIManager:setDirty(self.view.dialog, "partial")
return true return true
end end
function ReaderBookmark:setDogearVisibility(pn_or_xp) function ReaderBookmark:setDogearVisibility(pn_or_xp)
if self:isBookmarked(pn_or_xp) then if self:isBookmarked(pn_or_xp) then
self.ui:handleEvent(Event:new("SetDogearVisibility", true)) self.ui:handleEvent(Event:new("SetDogearVisibility", true))
else else
self.ui:handleEvent(Event:new("SetDogearVisibility", false)) self.ui:handleEvent(Event:new("SetDogearVisibility", false))
end end
end end
function ReaderBookmark:onPageUpdate(pageno) function ReaderBookmark:onPageUpdate(pageno)
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
self:setDogearVisibility(pageno) self:setDogearVisibility(pageno)
else else
-- FIXME: this is a dirty hack to prevent crash in isXPointerInCurrentPage -- FIXME: this is a dirty hack to prevent crash in isXPointerInCurrentPage
if pageno ~= 1 then if pageno ~= 1 then
self:setDogearVisibility("dummy") self:setDogearVisibility("dummy")
end end
end end
end end
function ReaderBookmark:onPosUpdate(pos) function ReaderBookmark:onPosUpdate(pos)
self:setDogearVisibility("dummy") self:setDogearVisibility("dummy")
end end
function ReaderBookmark:onShowBookmark() function ReaderBookmark:onShowBookmark()
-- build up item_table -- build up item_table
for k, v in ipairs(self.bookmarks) do for k, v in ipairs(self.bookmarks) do
local page = v.page local page = v.page
-- for CREngine, bookmark page is xpointer -- for CREngine, bookmark page is xpointer
if type(page) == "string" then if type(page) == "string" then
page = self.ui.document:getPageFromXPointer(v.page) page = self.ui.document:getPageFromXPointer(v.page)
end end
v.text = "Page "..page.." "..v.notes.." @ "..v.datetime v.text = "Page "..page.." "..v.notes.." @ "..v.datetime
end end
local menu_container = CenterContainer:new{ local menu_container = CenterContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
} }
local bm_menu = Menu:new{ local bm_menu = Menu:new{
title = "Bookmarks", title = "Bookmarks",
item_table = self.bookmarks, item_table = self.bookmarks,
width = Screen:getWidth()-50, width = Screen:getWidth()-50,
height = Screen:getHeight()-50, height = Screen:getHeight()-50,
show_parent = menu_container, show_parent = menu_container,
} }
table.insert(menu_container, bm_menu) table.insert(menu_container, bm_menu)
-- buid up menu widget method as closure -- buid up menu widget method as closure
local doc = self.ui.document local doc = self.ui.document
local view = self.view local view = self.view
local sendEv = function(ev) local sendEv = function(ev)
self.ui:handleEvent(ev) self.ui:handleEvent(ev)
end end
function bm_menu:onMenuChoice(item) function bm_menu:onMenuChoice(item)
if doc.info.has_pages then if doc.info.has_pages then
sendEv(Event:new("PageUpdate", item.page)) sendEv(Event:new("PageUpdate", item.page))
elseif view.view_mode == "page" then elseif view.view_mode == "page" then
sendEv(Event:new("PageUpdate", doc:getPageFromXPointer(item.page))) sendEv(Event:new("PageUpdate", doc:getPageFromXPointer(item.page)))
else else
sendEv(Event:new("PosUpdate", doc:getPosFromXPointer(item.page))) sendEv(Event:new("PosUpdate", doc:getPosFromXPointer(item.page)))
end end
end end
bm_menu.close_callback = function() bm_menu.close_callback = function()
UIManager:close(menu_container) UIManager:close(menu_container)
end end
UIManager:show(menu_container) UIManager:show(menu_container)
return true return true
end end
function ReaderBookmark:addToMainMenu(tab_item_table) function ReaderBookmark:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.navi, { table.insert(tab_item_table.navi, {
text = self.bm_menu_title, text = self.bm_menu_title,
callback = function() callback = function()
self:onShowBookmark() self:onShowBookmark()
end, end,
}) })
end end
function ReaderBookmark:isBookmarked(pn_or_xp) function ReaderBookmark:isBookmarked(pn_or_xp)
for k,v in ipairs(self.bookmarks) do for k,v in ipairs(self.bookmarks) do
if (type(pn_or_xp) == "number" and v.page == pn_or_xp) or 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 (type(pn_or_xp) == "string" and self.ui.document:isXPointerInCurrentPage(v.page)) then
return true return true
end end
end end
return false return false
end end
function ReaderBookmark:addBookmark(pn_or_xp) function ReaderBookmark:addBookmark(pn_or_xp)
-- build notes from TOC -- build notes from TOC
local notes = self.ui.toc:getTocTitleByPage(pn_or_xp) local notes = self.ui.toc:getTocTitleByPage(pn_or_xp)
if notes ~= "" then if notes ~= "" then
notes = "in "..notes notes = "in "..notes
end end
mark_item = { mark_item = {
page = pn_or_xp, page = pn_or_xp,
datetime = os.date("%Y-%m-%d %H:%M:%S"), datetime = os.date("%Y-%m-%d %H:%M:%S"),
notes = notes, notes = notes,
} }
table.insert(self.bookmarks, mark_item) table.insert(self.bookmarks, mark_item)
table.sort(self.bookmarks, function(a,b) table.sort(self.bookmarks, function(a,b)
return self:isBookmarkInSequence(a, b) return self:isBookmarkInSequence(a, b)
end) end)
return true return true
end end
function ReaderBookmark:isBookmarkInSequence(a, b) function ReaderBookmark:isBookmarkInSequence(a, b)
return a.page < b.page return a.page < b.page
end end
function ReaderBookmark:toggleBookmark(pn_or_xp) function ReaderBookmark:toggleBookmark(pn_or_xp)
for k,v in ipairs(self.bookmarks) do for k,v in ipairs(self.bookmarks) do
if (type(pn_or_xp) == "number" and v.page == pn_or_xp) or 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 (type(pn_or_xp) == "string" and self.ui.document:isXPointerInCurrentPage(v.page)) then
table.remove(self.bookmarks, k) table.remove(self.bookmarks, k)
return return
end end
end end
self:addBookmark(pn_or_xp) self:addBookmark(pn_or_xp)
end end
function ReaderBookmark:getPreviousBookmarkedPage(pn_or_xp) function ReaderBookmark:getPreviousBookmarkedPage(pn_or_xp)
for i = #self.bookmarks, 1, -1 do for i = #self.bookmarks, 1, -1 do
if pn_or_xp > self.bookmarks[i].page then if pn_or_xp > self.bookmarks[i].page then
return self.bookmarks[i].page return self.bookmarks[i].page
end end
end end
end end
function ReaderBookmark:getNextBookmarkedPage(pn_or_xp) function ReaderBookmark:getNextBookmarkedPage(pn_or_xp)
for i = 1, #self.bookmarks do for i = 1, #self.bookmarks do
if pn_or_xp < self.bookmarks[i].page then if pn_or_xp < self.bookmarks[i].page then
return self.bookmarks[i].page return self.bookmarks[i].page
end end
end end
end end
function ReaderBookmark:onGotoPreviousBookmark(pn_or_xp) function ReaderBookmark:onGotoPreviousBookmark(pn_or_xp)
self:GotoBookmark(self:getPreviousBookmarkedPage(pn_or_xp)) self:GotoBookmark(self:getPreviousBookmarkedPage(pn_or_xp))
return true return true
end end
function ReaderBookmark:onGotoNextBookmark(pn_or_xp) function ReaderBookmark:onGotoNextBookmark(pn_or_xp)
self:GotoBookmark(self:getNextBookmarkedPage(pn_or_xp)) self:GotoBookmark(self:getNextBookmarkedPage(pn_or_xp))
return true return true
end end
function ReaderBookmark:GotoBookmark(pn_or_xp) function ReaderBookmark:GotoBookmark(pn_or_xp)
if type(pn_or_xp) == "string" then if type(pn_or_xp) == "string" then
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(pn_or_xp))) self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(pn_or_xp)))
else else
self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(pn_or_xp))) self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(pn_or_xp)))
end end
elseif type(pn_or_xp) == "number" then elseif type(pn_or_xp) == "number" then
self.ui:handleEvent(Event:new("PageUpdate", pn_or_xp)) self.ui:handleEvent(Event:new("PageUpdate", pn_or_xp))
end end
end end
return ReaderBookmark return ReaderBookmark

@ -9,87 +9,87 @@ local UIManager = require("ui/uimanager")
local _ = require("gettext") local _ = require("gettext")
local ReaderConfig = InputContainer:new{ local ReaderConfig = InputContainer:new{
last_panel_index = 1, last_panel_index = 1,
} }
function ReaderConfig:init() function ReaderConfig:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ShowConfigMenu = { { "AA" }, doc = _("show config dialog") }, ShowConfigMenu = { { "AA" }, doc = _("show config dialog") },
} }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function ReaderConfig:initGesListener() function ReaderConfig:initGesListener()
self.ges_events = { self.ges_events = {
TapShowConfigMenu = { TapShowConfigMenu = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_CONFIG.x, x = Screen:getWidth()*DTAP_ZONE_CONFIG.x,
y = Screen:getHeight()*DTAP_ZONE_CONFIG.y, y = Screen:getHeight()*DTAP_ZONE_CONFIG.y,
w = Screen:getWidth()*DTAP_ZONE_CONFIG.w, w = Screen:getWidth()*DTAP_ZONE_CONFIG.w,
h = Screen:getHeight()*DTAP_ZONE_CONFIG.h, h = Screen:getHeight()*DTAP_ZONE_CONFIG.h,
} }
} }
} }
} }
end end
function ReaderConfig:onShowConfigMenu() function ReaderConfig:onShowConfigMenu()
self.config_dialog = ConfigDialog:new{ self.config_dialog = ConfigDialog:new{
dimen = self.dimen:copy(), dimen = self.dimen:copy(),
ui = self.ui, ui = self.ui,
configurable = self.configurable, configurable = self.configurable,
config_options = self.options, config_options = self.options,
is_always_active = true, is_always_active = true,
close_callback = function() self:onCloseCallback() end, close_callback = function() self:onCloseCallback() end,
} }
self.ui:handleEvent(Event:new("DisableHinting")) self.ui:handleEvent(Event:new("DisableHinting"))
-- show last used panel when opening config dialog -- show last used panel when opening config dialog
self.config_dialog:onShowConfigPanel(self.last_panel_index) self.config_dialog:onShowConfigPanel(self.last_panel_index)
UIManager:show(self.config_dialog) UIManager:show(self.config_dialog)
return true return true
end end
function ReaderConfig:onTapShowConfigMenu() function ReaderConfig:onTapShowConfigMenu()
self:onShowConfigMenu() self:onShowConfigMenu()
return true return true
end end
function ReaderConfig:onSetDimensions(dimen) function ReaderConfig:onSetDimensions(dimen)
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
-- since we cannot redraw config_dialog with new size, we close -- since we cannot redraw config_dialog with new size, we close
-- the old one on screen size change -- the old one on screen size change
if self.config_dialog then if self.config_dialog then
self.config_dialog:closeDialog() self.config_dialog:closeDialog()
end end
end end
function ReaderConfig:onCloseCallback() function ReaderConfig:onCloseCallback()
self.last_panel_index = self.config_dialog.panel_index self.last_panel_index = self.config_dialog.panel_index
self.ui:handleEvent(Event:new("RestoreHinting")) self.ui:handleEvent(Event:new("RestoreHinting"))
end end
-- event handler for readercropping -- event handler for readercropping
function ReaderConfig:onCloseConfig() function ReaderConfig:onCloseConfig()
self.config_dialog:closeDialog() self.config_dialog:closeDialog()
end end
function ReaderConfig:onReadSettings(config) function ReaderConfig:onReadSettings(config)
self.configurable:loadSettings(config, self.options.prefix.."_") self.configurable:loadSettings(config, self.options.prefix.."_")
self.last_panel_index = config:readSetting("config_panel_index") or 1 self.last_panel_index = config:readSetting("config_panel_index") or 1
end end
function ReaderConfig:onSaveSettings() function ReaderConfig:onSaveSettings()
self.configurable:saveSettings(self.ui.doc_settings, self.options.prefix.."_") self.configurable:saveSettings(self.ui.doc_settings, self.options.prefix.."_")
self.ui.doc_settings:saveSetting("config_panel_index", self.last_panel_index) self.ui.doc_settings:saveSetting("config_panel_index", self.last_panel_index)
end end
return ReaderConfig return ReaderConfig

@ -4,29 +4,29 @@ local Event = require("ui/event")
local ReaderCoptListener = EventListener:new{} local ReaderCoptListener = EventListener:new{}
function ReaderCoptListener:onReadSettings(config) function ReaderCoptListener:onReadSettings(config)
local embedded_css = config:readSetting("copt_embedded_css") local embedded_css = config:readSetting("copt_embedded_css")
local toggle_embedded_css = embedded_css == 0 and false or true local toggle_embedded_css = embedded_css == 0 and false or true
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self.ui:handleEvent(Event:new("ToggleEmbeddedStyleSheet", toggle_embedded_css)) self.ui:handleEvent(Event:new("ToggleEmbeddedStyleSheet", toggle_embedded_css))
end) end)
local view_mode = config:readSetting("copt_view_mode") local view_mode = config:readSetting("copt_view_mode")
if view_mode == 0 then if view_mode == 0 then
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self.ui:handleEvent(Event:new("SetViewMode", "page")) self.ui:handleEvent(Event:new("SetViewMode", "page"))
end) end)
elseif view_mode == 1 then elseif view_mode == 1 then
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self.ui:handleEvent(Event:new("SetViewMode", "scroll")) self.ui:handleEvent(Event:new("SetViewMode", "scroll"))
end) end)
end end
local status_line = config:readSetting("copt_status_line") or DCREREADER_PROGRESS_BAR local status_line = config:readSetting("copt_status_line") or DCREREADER_PROGRESS_BAR
self.document:setStatusLineProp(status_line) self.document:setStatusLineProp(status_line)
end end
function ReaderCoptListener:onSetFontSize(font_size) function ReaderCoptListener:onSetFontSize(font_size)
self.document.configurable.font_size = font_size self.document.configurable.font_size = font_size
end end
return ReaderCoptListener return ReaderCoptListener

@ -15,149 +15,149 @@ local Math = require("optmath")
local DEBUG = require("dbg") local DEBUG = require("dbg")
local PageCropDialog = VerticalGroup:new{ local PageCropDialog = VerticalGroup:new{
ok_text = "OK", ok_text = "OK",
cancel_text = "Cancel", cancel_text = "Cancel",
ok_callback = function() end, ok_callback = function() end,
cancel_callback = function() end, cancel_callback = function() end,
button_width = math.floor(Screen:scaleByDPI(70)), button_width = math.floor(Screen:scaleByDPI(70)),
} }
function PageCropDialog:init() function PageCropDialog:init()
local horizontal_group = HorizontalGroup:new{} local horizontal_group = HorizontalGroup:new{}
local ok_button = Button:new{ local ok_button = Button:new{
text = self.ok_text, text = self.ok_text,
callback = self.ok_callback, callback = self.ok_callback,
width = self.button_width, width = self.button_width,
bordersize = 2, bordersize = 2,
radius = 7, radius = 7,
text_font_face = "cfont", text_font_face = "cfont",
text_font_size = 20, text_font_size = 20,
} }
local cancel_button = Button:new{ local cancel_button = Button:new{
text = self.cancel_text, text = self.cancel_text,
callback = self.cancel_callback, callback = self.cancel_callback,
width = self.button_width, width = self.button_width,
bordersize = 2, bordersize = 2,
radius = 7, radius = 7,
text_font_face = "cfont", text_font_face = "cfont",
text_font_size = 20, text_font_size = 20,
} }
local ok_container = RightContainer:new{ local ok_container = RightContainer:new{
dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12}, dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12},
ok_button, ok_button,
} }
local cancel_container = LeftContainer:new{ local cancel_container = LeftContainer:new{
dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12}, dimen = Geom:new{ w = Screen:getWidth()*0.33, h = Screen:getHeight()/12},
cancel_button, cancel_button,
} }
table.insert(horizontal_group, ok_container) table.insert(horizontal_group, ok_container)
table.insert(horizontal_group, HorizontalSpan:new{ width = Screen:getWidth()*0.34}) table.insert(horizontal_group, HorizontalSpan:new{ width = Screen:getWidth()*0.34})
table.insert(horizontal_group, cancel_container) table.insert(horizontal_group, cancel_container)
self[2] = FrameContainer:new{ self[2] = FrameContainer:new{
horizontal_group, horizontal_group,
background = 0, background = 0,
bordersize = 0, bordersize = 0,
padding = 0, padding = 0,
} }
end end
local ReaderCropping = InputContainer:new{} local ReaderCropping = InputContainer:new{}
function ReaderCropping:onPageCrop(mode) function ReaderCropping:onPageCrop(mode)
if mode == "auto" then return end if mode == "auto" then return end
self.ui:handleEvent(Event:new("CloseConfig")) self.ui:handleEvent(Event:new("CloseConfig"))
-- backup original view dimen -- backup original view dimen
self.orig_view_dimen = Geom:new{w = self.view.dimen.w, h = self.view.dimen.h} self.orig_view_dimen = Geom:new{w = self.view.dimen.w, h = self.view.dimen.h}
-- backup original view bgcolor -- backup original view bgcolor
self.orig_view_bgcolor = self.view.outer_page_color self.orig_view_bgcolor = self.view.outer_page_color
self.view.outer_page_color = 7 -- gray bgcolor self.view.outer_page_color = 7 -- gray bgcolor
-- backup original zoom mode as cropping use "page" zoom mode -- backup original zoom mode as cropping use "page" zoom mode
self.orig_zoom_mode = self.view.zoom_mode self.orig_zoom_mode = self.view.zoom_mode
-- backup original page scroll -- backup original page scroll
self.orig_page_scroll = self.view.page_scroll self.orig_page_scroll = self.view.page_scroll
self.view.page_scroll = false self.view.page_scroll = false
-- backup and disable original hinting state -- backup and disable original hinting state
self.ui:handleEvent(Event:new("DisableHinting")) self.ui:handleEvent(Event:new("DisableHinting"))
-- backup original reflow mode as cropping use non-reflow mode -- backup original reflow mode as cropping use non-reflow mode
self.orig_reflow_mode = self.document.configurable.text_wrap self.orig_reflow_mode = self.document.configurable.text_wrap
if self.orig_reflow_mode == 1 then if self.orig_reflow_mode == 1 then
self.document.configurable.text_wrap = 0 self.document.configurable.text_wrap = 0
-- if we are in reflow mode, then we are already in page -- if we are in reflow mode, then we are already in page
-- mode, just force readerview to recalculate visible_area -- mode, just force readerview to recalculate visible_area
self.view:recalculate() self.view:recalculate()
else else
self.ui:handleEvent(Event:new("SetZoomMode", "page", "cropping")) self.ui:handleEvent(Event:new("SetZoomMode", "page", "cropping"))
end end
self.ui:handleEvent(Event:new("SetDimensions", self.ui:handleEvent(Event:new("SetDimensions",
Geom:new{w = Screen:getWidth(), h = Screen:getHeight()*11/12}) Geom:new{w = Screen:getWidth(), h = Screen:getHeight()*11/12})
) )
self.bbox_widget = BBoxWidget:new{ self.bbox_widget = BBoxWidget:new{
crop = self, crop = self,
ui = self.ui, ui = self.ui,
view = self.view, view = self.view,
document = self.document, document = self.document,
} }
self.crop_dialog = PageCropDialog:new{ self.crop_dialog = PageCropDialog:new{
self.bbox_widget, self.bbox_widget,
ok_callback = function() self:onConfirmPageCrop() end, ok_callback = function() self:onConfirmPageCrop() end,
cancel_callback = function() self:onCancelPageCrop() end, cancel_callback = function() self:onCancelPageCrop() end,
} }
UIManager:show(self.crop_dialog) UIManager:show(self.crop_dialog)
return true return true
end end
function ReaderCropping:onConfirmPageCrop() function ReaderCropping:onConfirmPageCrop()
--DEBUG("new bbox", new_bbox) --DEBUG("new bbox", new_bbox)
UIManager:close(self.crop_dialog) UIManager:close(self.crop_dialog)
local new_bbox = self.bbox_widget:getModifiedPageBBox() local new_bbox = self.bbox_widget:getModifiedPageBBox()
self.ui:handleEvent(Event:new("BBoxUpdate", new_bbox)) self.ui:handleEvent(Event:new("BBoxUpdate", new_bbox))
local pageno = self.view.state.page local pageno = self.view.state.page
self.document.bbox[pageno] = new_bbox self.document.bbox[pageno] = new_bbox
self.document.bbox[Math.oddEven(pageno)] = new_bbox self.document.bbox[Math.oddEven(pageno)] = new_bbox
self:exitPageCrop(true) self:exitPageCrop(true)
return true return true
end end
function ReaderCropping:onCancelPageCrop() function ReaderCropping:onCancelPageCrop()
UIManager:close(self.crop_dialog) UIManager:close(self.crop_dialog)
self:exitPageCrop(false) self:exitPageCrop(false)
return true return true
end end
function ReaderCropping:exitPageCrop(confirmed) function ReaderCropping:exitPageCrop(confirmed)
-- restore hinting state -- restore hinting state
self.ui:handleEvent(Event:new("RestoreHinting")) self.ui:handleEvent(Event:new("RestoreHinting"))
-- restore page scroll -- restore page scroll
self.view.page_scroll = self.orig_page_scroll self.view.page_scroll = self.orig_page_scroll
-- restore view bgcolor -- restore view bgcolor
self.view.outer_page_color = self.orig_view_bgcolor self.view.outer_page_color = self.orig_view_bgcolor
-- restore reflow mode -- restore reflow mode
self.document.configurable.text_wrap = self.orig_reflow_mode self.document.configurable.text_wrap = self.orig_reflow_mode
-- restore view dimens -- restore view dimens
self.ui:handleEvent(Event:new("RestoreDimensions", self.orig_view_dimen)) self.ui:handleEvent(Event:new("RestoreDimensions", self.orig_view_dimen))
self.view:recalculate() self.view:recalculate()
-- Exiting should have the same look and feel with entering. -- Exiting should have the same look and feel with entering.
if self.orig_reflow_mode == 1 then if self.orig_reflow_mode == 1 then
self.ui:handleEvent(Event:new("RestoreZoomMode")) self.ui:handleEvent(Event:new("RestoreZoomMode"))
else else
if confirmed then if confirmed then
-- if original zoom mode is not "content", set zoom mode to "contentwidth" -- if original zoom mode is not "content", set zoom mode to "contentwidth"
self.ui:handleEvent(Event:new("SetZoomMode", self.ui:handleEvent(Event:new("SetZoomMode",
self.orig_zoom_mode:find("content") and self.orig_zoom_mode or "contentwidth")) self.orig_zoom_mode:find("content") and self.orig_zoom_mode or "contentwidth"))
self.ui:handleEvent(Event:new("InitScrollPageStates")) self.ui:handleEvent(Event:new("InitScrollPageStates"))
else else
self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode))
end end
end end
UIManager.repaint_all = true UIManager.repaint_all = true
end end
function ReaderCropping:onReadSettings(config) function ReaderCropping:onReadSettings(config)
self.document.bbox = config:readSetting("bbox") self.document.bbox = config:readSetting("bbox")
end end
function ReaderCropping:onSaveSettings() function ReaderCropping:onSaveSettings()
self.ui.doc_settings:saveSetting("bbox", self.document.bbox) self.ui.doc_settings:saveSetting("bbox", self.document.bbox)
end end
return ReaderCropping return ReaderCropping

@ -9,69 +9,69 @@ local DEBUG = require("dbg")
local ReaderDictionary = EventListener:new{} local ReaderDictionary = EventListener:new{}
function ReaderDictionary:onLookupWord(highlight, word, box) function ReaderDictionary:onLookupWord(highlight, word, box)
self.highlight = highlight self.highlight = highlight
self:stardictLookup(word, box) self:stardictLookup(word, box)
end end
function ReaderDictionary:stardictLookup(word, box) function ReaderDictionary:stardictLookup(word, box)
DEBUG("lookup word:", word, box) DEBUG("lookup word:", word, box)
if word then if word then
-- strip punctuation characters around selected word -- strip punctuation characters around selected word
word = string.gsub(word, "^%p+", '') word = string.gsub(word, "^%p+", '')
word = string.gsub(word, "%p+$", '') word = string.gsub(word, "%p+$", '')
DEBUG("stripped word:", word) DEBUG("stripped word:", word)
-- escape quotes and other funny characters in 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 std_out = io.popen("./sdcv --utf8-input --utf8-output -nj "..("%q"):format(word), "r")
local results_str = nil local results_str = nil
if std_out then results_str = std_out:read("*all") end if std_out then results_str = std_out:read("*all") end
if results_str then if results_str then
--DEBUG("result str:", word, results_str) --DEBUG("result str:", word, results_str)
local ok, results = pcall(JSON.decode, JSON, results_str) local ok, results = pcall(JSON.decode, JSON, results_str)
--DEBUG("lookup result table:", word, results) --DEBUG("lookup result table:", word, results)
self:showDict(results, box) self:showDict(results, box)
end end
end end
end end
function ReaderDictionary:showDict(results, box) function ReaderDictionary:showDict(results, box)
if results and results[1] and box then if results and results[1] and box then
DEBUG("showing quick lookup dictionary window") DEBUG("showing quick lookup dictionary window")
local align = nil local align = nil
local region = Geom:new{x = 0, w = Screen:getWidth()} local region = Geom:new{x = 0, w = Screen:getWidth()}
if box.y + box.h/2 < Screen:getHeight()/2 then if box.y + box.h/2 < Screen:getHeight()/2 then
region.y = box.y + box.h region.y = box.y + box.h
region.h = Screen:getHeight() - box.y - box.h region.h = Screen:getHeight() - box.y - box.h
align = "top" align = "top"
else else
region.y = 0 region.y = 0
region.h = box.y region.h = box.y
align = "bottom" align = "bottom"
end end
UIManager:show(DictQuickLookup:new{ UIManager:show(DictQuickLookup:new{
ui = self.ui, ui = self.ui,
highlight = self.highlight, highlight = self.highlight,
dialog = self.dialog, dialog = self.dialog,
results = results, results = results,
dictionary = self.default_dictionary, dictionary = self.default_dictionary,
width = Screen:getWidth() - Screen:scaleByDPI(80), width = Screen:getWidth() - Screen:scaleByDPI(80),
height = math.min(region.h*0.7, Screen:getHeight()*0.5), height = math.min(region.h*0.7, Screen:getHeight()*0.5),
region = region, region = region,
align = align, align = align,
}) })
end end
end end
function ReaderDictionary:onUpdateDefaultDict(dict) function ReaderDictionary:onUpdateDefaultDict(dict)
DEBUG("make default dictionary:", dict) DEBUG("make default dictionary:", dict)
self.default_dictionary = dict self.default_dictionary = dict
end end
function ReaderDictionary:onReadSettings(config) function ReaderDictionary:onReadSettings(config)
self.default_dictionary = config:readSetting("default_dictionary") self.default_dictionary = config:readSetting("default_dictionary")
end end
function ReaderDictionary:onSaveSettings() function ReaderDictionary:onSaveSettings()
self.ui.doc_settings:saveSetting("default_dictionary", self.default_dictionary) self.ui.doc_settings:saveSetting("default_dictionary", self.default_dictionary)
end end
return ReaderDictionary return ReaderDictionary

@ -11,54 +11,54 @@ local Event = require("ui/event")
local ReaderDogear = InputContainer:new{} local ReaderDogear = InputContainer:new{}
function ReaderDogear:init() function ReaderDogear:init()
local widget = ImageWidget:new{ local widget = ImageWidget:new{
file = "resources/icons/dogear.png", file = "resources/icons/dogear.png",
} }
self[1] = RightContainer:new{ self[1] = RightContainer:new{
dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h},
widget, widget,
} }
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
Tap = { Tap = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x, x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x,
y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y, y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y,
w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w, w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w,
h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h
} }
} }
}, },
Hold = { Hold = {
GestureRange:new{ GestureRange:new{
ges = "hold", ges = "hold",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x, x = Screen:getWidth()*DTAP_ZONE_BOOKMARK.x,
y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y, y = Screen:getHeight()*DTAP_ZONE_BOOKMARK.y,
w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w, w = Screen:getWidth()*DTAP_ZONE_BOOKMARK.w,
h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h h = Screen:getHeight()*DTAP_ZONE_BOOKMARK.h
} }
} }
} }
} }
end end
end end
function ReaderDogear:onTap() function ReaderDogear:onTap()
self.ui:handleEvent(Event:new("ToggleBookmark")) self.ui:handleEvent(Event:new("ToggleBookmark"))
return true return true
end end
function ReaderDogear:onHold() function ReaderDogear:onHold()
self.ui:handleEvent(Event:new("ToggleBookmarkFlipping")) self.ui:handleEvent(Event:new("ToggleBookmarkFlipping"))
return true return true
end end
function ReaderDogear:onSetDogearVisibility(visible) function ReaderDogear:onSetDogearVisibility(visible)
self.view.dogear_visible = visible self.view.dogear_visible = visible
return true return true
end end
return ReaderDogear return ReaderDogear

@ -8,37 +8,37 @@ local Screen = require("ui/screen")
local Event = require("ui/event") local Event = require("ui/event")
local ReaderFlipping = InputContainer:new{ local ReaderFlipping = InputContainer:new{
orig_reflow_mode = 0, orig_reflow_mode = 0,
} }
function ReaderFlipping:init() function ReaderFlipping:init()
local widget = ImageWidget:new{ local widget = ImageWidget:new{
file = "resources/icons/appbar.book.open.png", file = "resources/icons/appbar.book.open.png",
} }
self[1] = LeftContainer:new{ self[1] = LeftContainer:new{
dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h},
widget, widget,
} }
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
Tap = { Tap = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_FLIPPING.x, x = Screen:getWidth()*DTAP_ZONE_FLIPPING.x,
y = Screen:getHeight()*DTAP_ZONE_FLIPPING.y, y = Screen:getHeight()*DTAP_ZONE_FLIPPING.y,
w = Screen:getWidth()*DTAP_ZONE_FLIPPING.w, w = Screen:getWidth()*DTAP_ZONE_FLIPPING.w,
h = Screen:getHeight()*DTAP_ZONE_FLIPPING.h h = Screen:getHeight()*DTAP_ZONE_FLIPPING.h
} }
} }
} }
} }
end end
end end
function ReaderFlipping:onTap() function ReaderFlipping:onTap()
self.ui:handleEvent(Event:new("TogglePageFlipping")) self.ui:handleEvent(Event:new("TogglePageFlipping"))
return true return true
end end
return ReaderFlipping return ReaderFlipping

@ -12,220 +12,220 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ReaderFont = InputContainer:new{ local ReaderFont = InputContainer:new{
font_face = nil, font_face = nil,
font_size = nil, font_size = nil,
line_space_percent = nil, line_space_percent = nil,
font_menu_title = _("Change font"), font_menu_title = _("Change font"),
face_table = nil, face_table = nil,
-- default gamma from crengine's lvfntman.cpp -- default gamma from crengine's lvfntman.cpp
gamma_index = nil, gamma_index = nil,
} }
function ReaderFont:init() function ReaderFont:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
-- add shortcut for keyboard -- add shortcut for keyboard
self.key_events = { self.key_events = {
ShowFontMenu = { {"F"}, doc = _("show font menu") }, ShowFontMenu = { {"F"}, doc = _("show font menu") },
IncreaseSize = { IncreaseSize = {
{ "Shift", Input.group.PgFwd }, { "Shift", Input.group.PgFwd },
doc = _("increase font size"), doc = _("increase font size"),
event = "ChangeSize", args = "increase" }, event = "ChangeSize", args = "increase" },
DecreaseSize = { DecreaseSize = {
{ "Shift", Input.group.PgBack }, { "Shift", Input.group.PgBack },
doc = _("decrease font size"), doc = _("decrease font size"),
event = "ChangeSize", args = "decrease" }, event = "ChangeSize", args = "decrease" },
IncreaseLineSpace = { IncreaseLineSpace = {
{ "Alt", Input.group.PgFwd }, { "Alt", Input.group.PgFwd },
doc = _("increase line space"), doc = _("increase line space"),
event = "ChangeLineSpace", args = "increase" }, event = "ChangeLineSpace", args = "increase" },
DecreaseLineSpace = { DecreaseLineSpace = {
{ "Alt", Input.group.PgBack }, { "Alt", Input.group.PgBack },
doc = _("decrease line space"), doc = _("decrease line space"),
event = "ChangeLineSpace", args = "decrease" }, event = "ChangeLineSpace", args = "decrease" },
} }
end end
-- build face_table for menu -- build face_table for menu
self.face_table = {} self.face_table = {}
local face_list = cre.getFontFaces() local face_list = cre.getFontFaces()
for k,v in ipairs(face_list) do for k,v in ipairs(face_list) do
table.insert(self.face_table, { table.insert(self.face_table, {
text = v, text = v,
callback = function() callback = function()
self:setFont(v) self:setFont(v)
end end
}) })
face_list[k] = {text = v} face_list[k] = {text = v}
end end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderFont:onSetDimensions(dimen) function ReaderFont:onSetDimensions(dimen)
self.dimen = dimen self.dimen = dimen
end end
function ReaderFont:onReadSettings(config) function ReaderFont:onReadSettings(config)
self.font_face = config:readSetting("font_face") self.font_face = config:readSetting("font_face")
if not self.font_face then if not self.font_face then
self.font_face = self.ui.document.default_font self.font_face = self.ui.document.default_font
end end
self.ui.document:setFontFace(self.font_face) self.ui.document:setFontFace(self.font_face)
self.header_font_face = config:readSetting("header_font_face") self.header_font_face = config:readSetting("header_font_face")
if not self.header_font_face then if not self.header_font_face then
self.header_font_face = self.ui.document.header_font self.header_font_face = self.ui.document.header_font
end end
self.ui.document:setHeaderFont(self.header_font_face) self.ui.document:setHeaderFont(self.header_font_face)
self.font_size = config:readSetting("font_size") self.font_size = config:readSetting("font_size")
if not self.font_size then if not self.font_size then
--@TODO change this! 12.01 2013 (houqp) --@TODO change this! 12.01 2013 (houqp)
self.font_size = DCREREADER_CONFIG_DEFAULT_FONT_SIZE self.font_size = DCREREADER_CONFIG_DEFAULT_FONT_SIZE
end end
self.ui.document:setFontSize(Screen:scaleByDPI(self.font_size)) self.ui.document:setFontSize(Screen:scaleByDPI(self.font_size))
self.line_space_percent = config:readSetting("line_space_percent") self.line_space_percent = config:readSetting("line_space_percent")
if not self.line_space_percent then if not self.line_space_percent then
self.line_space_percent = 100 self.line_space_percent = 100
else else
self.ui.document:setInterlineSpacePercent(self.line_space_percent) self.ui.document:setInterlineSpacePercent(self.line_space_percent)
end end
self.gamma_index = config:readSetting("gamma_index") self.gamma_index = config:readSetting("gamma_index")
if not self.gamma_index then if not self.gamma_index then
self.gamma_index = 15 self.gamma_index = 15
end end
self.ui.document:setGammaIndex(self.gamma_index) self.ui.document:setGammaIndex(self.gamma_index)
-- Dirty hack: we have to add folloing call in order to set -- Dirty hack: we have to add folloing call in order to set
-- m_is_rendered(member of LVDocView) to true. Otherwise position inside -- m_is_rendered(member of LVDocView) to true. Otherwise position inside
-- document will be reset to 0 on first view render. -- 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. -- So far, I don't know why this call will alter the value of m_is_rendered.
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
end) end)
end end
function ReaderFont:onShowFontMenu() function ReaderFont:onShowFontMenu()
-- build menu widget -- build menu widget
local main_menu = Menu:new{ local main_menu = Menu:new{
title = self.font_menu_title, title = self.font_menu_title,
item_table = self.face_table, item_table = self.face_table,
width = Screen:getWidth() - 100, width = Screen:getWidth() - 100,
} }
-- build container -- build container
local menu_container = CenterContainer:new{ local menu_container = CenterContainer:new{
main_menu, main_menu,
dimen = Screen:getSize(), dimen = Screen:getSize(),
} }
main_menu.close_callback = function () main_menu.close_callback = function ()
UIManager:close(menu_container) UIManager:close(menu_container)
end end
-- show menu -- show menu
UIManager:show(menu_container) UIManager:show(menu_container)
return true return true
end 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) function ReaderFont:onChangeSize(direction)
local delta = direction == "decrease" and -1 or 1 local delta = direction == "decrease" and -1 or 1
self.font_size = self.font_size + delta self.font_size = self.font_size + delta
self.ui:handleEvent(Event:new("SetFontSize", self.font_size)) self.ui:handleEvent(Event:new("SetFontSize", self.font_size))
return true return true
end end
function ReaderFont:onSetFontSize(new_size) function ReaderFont:onSetFontSize(new_size)
if new_size > 72 then new_size = 72 end if new_size > 72 then new_size = 72 end
if new_size < 12 then new_size = 12 end if new_size < 12 then new_size = 12 end
self.font_size = new_size self.font_size = new_size
UIManager:show(Notification:new{ UIManager:show(Notification:new{
text = _("Set font size to ")..self.font_size, text = _("Set font size to ")..self.font_size,
timeout = 1, timeout = 1,
}) })
self.ui.document:setFontSize(Screen:scaleByDPI(new_size)) self.ui.document:setFontSize(Screen:scaleByDPI(new_size))
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
return true return true
end end
function ReaderFont:onChangeLineSpace(direction) function ReaderFont:onChangeLineSpace(direction)
local msg = "" local msg = ""
if direction == "decrease" then if direction == "decrease" then
self.line_space_percent = self.line_space_percent - 10 self.line_space_percent = self.line_space_percent - 10
-- NuPogodi, 15.05.12: reduce lowest space_percent to 80 -- NuPogodi, 15.05.12: reduce lowest space_percent to 80
self.line_space_percent = math.max(self.line_space_percent, 80) self.line_space_percent = math.max(self.line_space_percent, 80)
msg = _("Decrease line space to ") msg = _("Decrease line space to ")
else else
self.line_space_percent = self.line_space_percent + 10 self.line_space_percent = self.line_space_percent + 10
self.line_space_percent = math.min(self.line_space_percent, 200) self.line_space_percent = math.min(self.line_space_percent, 200)
msg = _("Increase line space to ") msg = _("Increase line space to ")
end end
UIManager:show(Notification:new{ UIManager:show(Notification:new{
text = msg..self.line_space_percent.."%", text = msg..self.line_space_percent.."%",
timeout = 1, timeout = 1,
}) })
self.ui.document:setInterlineSpacePercent(self.line_space_percent) self.ui.document:setInterlineSpacePercent(self.line_space_percent)
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
return true return true
end end
function ReaderFont:onToggleFontBolder() function ReaderFont:onToggleFontBolder()
self.ui.document:toggleFontBolder() self.ui.document:toggleFontBolder()
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
return true return true
end end
function ReaderFont:onChangeFontGamma(direction) function ReaderFont:onChangeFontGamma(direction)
local msg = "" local msg = ""
if direction == "increase" then if direction == "increase" then
cre.setGammaIndex(self.gamma_index+2) cre.setGammaIndex(self.gamma_index+2)
msg = _("Increase gamma to ") msg = _("Increase gamma to ")
elseif direction == "decrease" then elseif direction == "decrease" then
cre.setGammaIndex(self.gamma_index-2) cre.setGammaIndex(self.gamma_index-2)
msg = _("Decrease gamma to ") msg = _("Decrease gamma to ")
end end
self.gamma_index = cre.getGammaIndex() self.gamma_index = cre.getGammaIndex()
UIManager:show(Notification:new{ UIManager:show(Notification:new{
text = msg..self.gamma_index, text = msg..self.gamma_index,
timeout = 1 timeout = 1
}) })
self.ui:handleEvent(Event:new("RedrawCurrentView")) self.ui:handleEvent(Event:new("RedrawCurrentView"))
return true return true
end end
function ReaderFont:onSaveSettings() function ReaderFont:onSaveSettings()
self.ui.doc_settings:saveSetting("font_face", self.font_face) 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("header_font_face", self.header_font_face)
self.ui.doc_settings:saveSetting("font_size", self.font_size) 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("line_space_percent", self.line_space_percent)
self.ui.doc_settings:saveSetting("gamma_index", self.gamma_index) self.ui.doc_settings:saveSetting("gamma_index", self.gamma_index)
end end
function ReaderFont:setFont(face) function ReaderFont:setFont(face)
if face and self.font_face ~= face then if face and self.font_face ~= face then
self.font_face = face self.font_face = face
UIManager:show(Notification:new{ UIManager:show(Notification:new{
text = _("Redrawing with font ")..face, text = _("Redrawing with font ")..face,
timeout = 1, timeout = 1,
}) })
self.ui.document:setFontFace(face) self.ui.document:setFontFace(face)
-- signal readerrolling to update pos in new height -- signal readerrolling to update pos in new height
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
UIManager:close(msg) UIManager:close(msg)
end end
end end
function ReaderFont:addToMainMenu(tab_item_table) function ReaderFont:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.typeset, { table.insert(tab_item_table.typeset, {
text = self.font_menu_title, text = self.font_menu_title,
sub_item_table = self.face_table, sub_item_table = self.face_table,
}) })
end end
return ReaderFont return ReaderFont

@ -16,136 +16,136 @@ local Font = require("ui/font")
local DEBUG = require("dbg") local DEBUG = require("dbg")
local ReaderFooter = InputContainer:new{ local ReaderFooter = InputContainer:new{
visible = true, visible = true,
pageno = nil, pageno = nil,
pages = nil, pages = nil,
progress_percentage = 0.0, progress_percentage = 0.0,
progress_text = "0 / 0", progress_text = "0 / 0",
show_time = false, show_time = false,
bar_width = 0.85, bar_width = 0.85,
text_width = 0.15, text_width = 0.15,
text_font_face = "ffont", text_font_face = "ffont",
text_font_size = 14, text_font_size = 14,
height = 19, height = 19,
} }
function ReaderFooter:init() function ReaderFooter:init()
self.progress_bar = ProgressWidget:new{ self.progress_bar = ProgressWidget:new{
width = math.floor(Screen:getWidth()*(self.bar_width-0.02)), width = math.floor(Screen:getWidth()*(self.bar_width-0.02)),
height = 7, height = 7,
percentage = self.progress_percentage, percentage = self.progress_percentage,
} }
self.progress_text = TextWidget:new{ self.progress_text = TextWidget:new{
text = self.progress_text, text = self.progress_text,
face = Font:getFace(self.text_font_face, self.text_font_size), face = Font:getFace(self.text_font_face, self.text_font_size),
} }
local _, text_height = self.progress_text:getSize() local _, text_height = self.progress_text:getSize()
local horizontal_group = HorizontalGroup:new{} local horizontal_group = HorizontalGroup:new{}
local bar_container = RightContainer:new{ local bar_container = RightContainer:new{
dimen = Geom:new{w = Screen:getWidth()*self.bar_width, h = self.height}, dimen = Geom:new{w = Screen:getWidth()*self.bar_width, h = self.height},
self.progress_bar, self.progress_bar,
} }
local text_container = CenterContainer:new{ local text_container = CenterContainer:new{
dimen = Geom:new{w = Screen:getWidth()*self.text_width, h = self.height}, dimen = Geom:new{w = Screen:getWidth()*self.text_width, h = self.height},
self.progress_text, self.progress_text,
} }
table.insert(horizontal_group, bar_container) table.insert(horizontal_group, bar_container)
table.insert(horizontal_group, text_container) table.insert(horizontal_group, text_container)
self[1] = BottomContainer:new{ self[1] = BottomContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
FrameContainer:new{ FrameContainer:new{
horizontal_group, horizontal_group,
background = 0, background = 0,
bordersize = 0, bordersize = 0,
padding = 0, padding = 0,
} }
} }
self.dimen = self[1]:getSize() self.dimen = self[1]:getSize()
self.pageno = self.view.state.page self.pageno = self.view.state.page
self.pages = self.view.document.info.number_of_pages self.pages = self.view.document.info.number_of_pages
self:updateFooterPage() self:updateFooterPage()
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapFooter = { TapFooter = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self[1]:contentRange(), range = self[1]:contentRange(),
}, },
}, },
HoldFooter = { HoldFooter = {
GestureRange:new{ GestureRange:new{
ges = "hold", ges = "hold",
range = self[1]:contentRange(), range = self[1]:contentRange(),
}, },
}, },
} }
end end
end end
function ReaderFooter:updateFooterPage() function ReaderFooter:updateFooterPage()
if type(self.pageno) ~= "number" then return end if type(self.pageno) ~= "number" then return end
self.progress_bar.percentage = self.pageno / self.pages self.progress_bar.percentage = self.pageno / self.pages
if self.show_time then if self.show_time then
self.progress_text.text = os.date("%H:%M") self.progress_text.text = os.date("%H:%M")
else else
self.progress_text.text = string.format("%d / %d", self.pageno, self.pages) self.progress_text.text = string.format("%d / %d", self.pageno, self.pages)
end end
end end
function ReaderFooter:updateFooterPos() function ReaderFooter:updateFooterPos()
if type(self.position) ~= "number" then return end if type(self.position) ~= "number" then return end
self.progress_bar.percentage = self.position / self.doc_height self.progress_bar.percentage = self.position / self.doc_height
if self.show_time then if self.show_time then
self.progress_text.text = os.date("%H:%M") self.progress_text.text = os.date("%H:%M")
else else
self.progress_text.text = string.format("%1.f", self.progress_bar.percentage*100).."%" self.progress_text.text = string.format("%1.f", self.progress_bar.percentage*100).."%"
end end
end end
function ReaderFooter:onPageUpdate(pageno) function ReaderFooter:onPageUpdate(pageno)
self.pageno = pageno self.pageno = pageno
self.pages = self.view.document.info.number_of_pages self.pages = self.view.document.info.number_of_pages
self:updateFooterPage() self:updateFooterPage()
end end
function ReaderFooter:onPosUpdate(pos) function ReaderFooter:onPosUpdate(pos)
self.position = pos self.position = pos
self.doc_height = self.view.document.info.doc_height self.doc_height = self.view.document.info.doc_height
self:updateFooterPos() self:updateFooterPos()
end end
function ReaderFooter:onTapFooter(arg, ges) function ReaderFooter:onTapFooter(arg, ges)
if self.view.flipping_visible then if self.view.flipping_visible then
local pos = ges.pos local pos = ges.pos
local dimen = self.progress_bar.dimen local dimen = self.progress_bar.dimen
local percentage = (pos.x - dimen.x)/dimen.w local percentage = (pos.x - dimen.x)/dimen.w
self.ui:handleEvent(Event:new("GotoPercentage", percentage)) self.ui:handleEvent(Event:new("GotoPercentage", percentage))
else else
self.show_time = not self.show_time self.show_time = not self.show_time
end end
if self.pageno then if self.pageno then
self:updateFooterPage() self:updateFooterPage()
else else
self:updateFooterPos() self:updateFooterPos()
end end
UIManager:setDirty(self.view.dialog, "partial") UIManager:setDirty(self.view.dialog, "partial")
-- consume this tap when footer is visible -- consume this tap when footer is visible
if self.view.footer_visible then if self.view.footer_visible then
return true return true
end end
end end
function ReaderFooter:onHoldFooter(arg, ges) function ReaderFooter:onHoldFooter(arg, ges)
self.ui:handleEvent(Event:new("ShowGotoDialog")) self.ui:handleEvent(Event:new("ShowGotoDialog"))
return true return true
end end
function ReaderFooter:onSetStatusLine(status_line) function ReaderFooter:onSetStatusLine(status_line)
self.view.footer_visible = status_line == 1 and true or false self.view.footer_visible = status_line == 1 and true or false
self.ui.document:setStatusLineProp(status_line) self.ui.document:setStatusLineProp(status_line)
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
end end
return ReaderFooter return ReaderFooter

@ -7,86 +7,86 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ReaderGoto = InputContainer:new{ local ReaderGoto = InputContainer:new{
goto_menu_title = _("Go To"), goto_menu_title = _("Go To"),
goto_dialog_title = _("Go to Page or Location"), goto_dialog_title = _("Go to Page or Location"),
} }
function ReaderGoto:init() function ReaderGoto:init()
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderGoto:addToMainMenu(tab_item_table) function ReaderGoto:addToMainMenu(tab_item_table)
-- insert goto command to main reader menu -- insert goto command to main reader menu
table.insert(tab_item_table.navi, { table.insert(tab_item_table.navi, {
text = self.goto_menu_title, text = self.goto_menu_title,
callback = function() callback = function()
self:onShowGotoDialog() self:onShowGotoDialog()
end, end,
}) })
end end
function ReaderGoto:onShowGotoDialog() function ReaderGoto:onShowGotoDialog()
DEBUG("show goto dialog") DEBUG("show goto dialog")
self.goto_dialog = InputDialog:new{ self.goto_dialog = InputDialog:new{
title = self.goto_dialog_title, title = self.goto_dialog_title,
input_hint = "(1 - "..self.document:getPageCount()..")", input_hint = "(1 - "..self.document:getPageCount()..")",
buttons = { buttons = {
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
enabled = true, enabled = true,
callback = function() callback = function()
self:close() self:close()
end, end,
}, },
{ {
text = _("Page"), text = _("Page"),
enabled = self.document.info.has_pages, enabled = self.document.info.has_pages,
callback = function() callback = function()
self:gotoPage() self:gotoPage()
end, end,
}, },
{ {
text = _("Location"), text = _("Location"),
enabled = not self.document.info.has_pages, enabled = not self.document.info.has_pages,
callback = function() callback = function()
self:gotoLocation() self:gotoLocation()
end, end,
}, },
}, },
}, },
input_type = "number", input_type = "number",
enter_callback = self.document.info.has_pages enter_callback = self.document.info.has_pages
and function() self:gotoPage() end and function() self:gotoPage() end
or function() self:gotoLocation() end, or function() self:gotoLocation() end,
width = Screen:getWidth() * 0.8, width = Screen:getWidth() * 0.8,
height = Screen:getHeight() * 0.2, height = Screen:getHeight() * 0.2,
} }
self.goto_dialog:onShowKeyboard() self.goto_dialog:onShowKeyboard()
UIManager:show(self.goto_dialog) UIManager:show(self.goto_dialog)
end end
function ReaderGoto:close() function ReaderGoto:close()
self.goto_dialog:onClose() self.goto_dialog:onClose()
UIManager:close(self.goto_dialog) UIManager:close(self.goto_dialog)
end end
function ReaderGoto:gotoPage() function ReaderGoto:gotoPage()
local number = tonumber(self.goto_dialog:getInputText()) local number = tonumber(self.goto_dialog:getInputText())
if number then if number then
self.ui:handleEvent(Event:new("GotoPage", number)) self.ui:handleEvent(Event:new("GotoPage", number))
end end
self:close() self:close()
return true return true
end end
function ReaderGoto:gotoLocation() function ReaderGoto:gotoLocation()
local number = tonumber(self.goto_dialog:getInputText()) local number = tonumber(self.goto_dialog:getInputText())
if number then if number then
self.ui:handleEvent(Event:new("GotoPage", number)) self.ui:handleEvent(Event:new("GotoPage", number))
end end
self:close() self:close()
return true return true
end end
return ReaderGoto return ReaderGoto

@ -12,405 +12,405 @@ local _ = require("gettext")
local ReaderHighlight = InputContainer:new{} local ReaderHighlight = InputContainer:new{}
function ReaderHighlight:init() function ReaderHighlight:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ShowToc = { ShowToc = {
{ "." }, { "." },
doc = _("highlight text") }, doc = _("highlight text") },
} }
end end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderHighlight:initGesListener() function ReaderHighlight:initGesListener()
self.ges_events = { self.ges_events = {
Tap = { Tap = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight() h = Screen:getHeight()
} }
} }
}, },
Hold = { Hold = {
GestureRange:new{ GestureRange:new{
ges = "hold", ges = "hold",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight() h = Screen:getHeight()
} }
} }
}, },
HoldRelease = { HoldRelease = {
GestureRange:new{ GestureRange:new{
ges = "hold_release", ges = "hold_release",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight() h = Screen:getHeight()
} }
} }
}, },
HoldPan = { HoldPan = {
GestureRange:new{ GestureRange:new{
ges = "hold_pan", ges = "hold_pan",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight() h = Screen:getHeight()
}, },
rate = 2.0, rate = 2.0,
} }
}, },
} }
end end
function ReaderHighlight:addToMainMenu(tab_item_table) function ReaderHighlight:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.typeset, { table.insert(tab_item_table.typeset, {
text_func = function() text_func = function()
return _("Set highlight drawer ").."( "..self.view.highlight.saved_drawer.." )" return _("Set highlight drawer ").."( "..self.view.highlight.saved_drawer.." )"
end, end,
sub_item_table = self:genHighlightDrawerMenu(), sub_item_table = self:genHighlightDrawerMenu(),
}) })
end end
function ReaderHighlight:genHighlightDrawerMenu() function ReaderHighlight:genHighlightDrawerMenu()
return { return {
{ {
text = _("Lighten"), text = _("Lighten"),
callback = function() callback = function()
self.view.highlight.saved_drawer = "lighten" self.view.highlight.saved_drawer = "lighten"
end end
}, },
{ {
text = _("Underscore"), text = _("Underscore"),
callback = function() callback = function()
self.view.highlight.saved_drawer = "underscore" self.view.highlight.saved_drawer = "underscore"
end end
}, },
{ {
text = _("Invert"), text = _("Invert"),
callback = function() callback = function()
self.view.highlight.saved_drawer = "invert" self.view.highlight.saved_drawer = "invert"
end end
}, },
} }
end end
function ReaderHighlight:onSetDimensions(dimen) function ReaderHighlight:onSetDimensions(dimen)
-- update listening according to new screen dimen -- update listening according to new screen dimen
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function ReaderHighlight:onTap(arg, ges) function ReaderHighlight:onTap(arg, ges)
if self.hold_pos then if self.hold_pos then
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
self.view.highlight.temp[self.hold_pos.page] = nil self.view.highlight.temp[self.hold_pos.page] = nil
else else
self.ui.document:clearSelection() self.ui.document:clearSelection()
end end
self.hold_pos = nil self.hold_pos = nil
UIManager:setDirty(self.dialog, "partial") UIManager:setDirty(self.dialog, "partial")
return true return true
end end
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
return self:onTapPageSavedHighlight(ges) return self:onTapPageSavedHighlight(ges)
else else
return self:onTapXPointerSavedHighlight(ges) return self:onTapXPointerSavedHighlight(ges)
end end
end end
local function inside_box(pos, box) local function inside_box(pos, box)
if pos then if pos then
local x, y = pos.x, pos.y local x, y = pos.x, pos.y
if box.x <= x and box.y <= y if box.x <= x and box.y <= y
and box.x + box.w >= x and box.x + box.w >= x
and box.y + box.h >= y then and box.y + box.h >= y then
return true return true
end end
end end
end end
function ReaderHighlight:onTapPageSavedHighlight(ges) function ReaderHighlight:onTapPageSavedHighlight(ges)
local pages = self.view:getCurrentPageList() local pages = self.view:getCurrentPageList()
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
for key, page in pairs(pages) do for key, page in pairs(pages) do
local items = self.view.highlight.saved[page] local items = self.view.highlight.saved[page]
if not items then items = {} end if not items then items = {} end
for i = 1, #items do for i = 1, #items do
local pos0, pos1 = items[i].pos0, items[i].pos1 local pos0, pos1 = items[i].pos0, items[i].pos1
local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1) local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1)
if boxes then if boxes then
for index, box in pairs(boxes) do for index, box in pairs(boxes) do
if inside_box(pos, box) then if inside_box(pos, box) then
DEBUG("Tap on hightlight") DEBUG("Tap on hightlight")
return self:onShowHighlightDialog(page, i) return self:onShowHighlightDialog(page, i)
end end
end end
end end
end end
end end
end end
function ReaderHighlight:onTapXPointerSavedHighlight(ges) function ReaderHighlight:onTapXPointerSavedHighlight(ges)
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
for page, _ in pairs(self.view.highlight.saved) do for page, _ in pairs(self.view.highlight.saved) do
local items = self.view.highlight.saved[page] local items = self.view.highlight.saved[page]
if not items then items = {} end if not items then items = {} end
for i = 1, #items do for i = 1, #items do
local pos0, pos1 = items[i].pos0, items[i].pos1 local pos0, pos1 = items[i].pos0, items[i].pos1
local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1) local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1)
if boxes then if boxes then
for index, box in pairs(boxes) do for index, box in pairs(boxes) do
if inside_box(pos, box) then if inside_box(pos, box) then
DEBUG("Tap on hightlight") DEBUG("Tap on hightlight")
return self:onShowHighlightDialog(page, i) return self:onShowHighlightDialog(page, i)
end end
end end
end end
end end
end end
end end
function ReaderHighlight:onShowHighlightDialog(page, index) function ReaderHighlight:onShowHighlightDialog(page, index)
self.edit_highlight_dialog = ButtonDialog:new{ self.edit_highlight_dialog = ButtonDialog:new{
buttons = { buttons = {
{ {
{ {
text = _("Delete"), text = _("Delete"),
callback = function() callback = function()
self:deleteHighlight(page, index) self:deleteHighlight(page, index)
UIManager:close(self.edit_highlight_dialog) UIManager:close(self.edit_highlight_dialog)
end, end,
}, },
{ {
text = _("Edit"), text = _("Edit"),
enabled = false, enabled = false,
callback = function() callback = function()
self:editHighlight() self:editHighlight()
UIManager:close(self.edit_highlight_dialog) UIManager:close(self.edit_highlight_dialog)
end, end,
}, },
}, },
}, },
} }
UIManager:show(self.edit_highlight_dialog) UIManager:show(self.edit_highlight_dialog)
return true return true
end end
function ReaderHighlight:onHold(arg, ges) function ReaderHighlight:onHold(arg, ges)
self.hold_pos = self.view:screenToPageTransform(ges.pos) self.hold_pos = self.view:screenToPageTransform(ges.pos)
DEBUG("hold position in page", self.hold_pos) DEBUG("hold position in page", self.hold_pos)
if not self.hold_pos then if not self.hold_pos then
DEBUG("not inside page area") DEBUG("not inside page area")
return true return true
end end
local ok, word = pcall(self.ui.document.getWordFromPosition, self.ui.document, self.hold_pos) local ok, word = pcall(self.ui.document.getWordFromPosition, self.ui.document, self.hold_pos)
if ok and word then if ok and word then
DEBUG("selected word:", word) DEBUG("selected word:", word)
self.selected_word = word self.selected_word = word
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
local boxes = {} local boxes = {}
table.insert(boxes, self.selected_word.sbox) table.insert(boxes, self.selected_word.sbox)
self.view.highlight.temp[self.hold_pos.page] = boxes self.view.highlight.temp[self.hold_pos.page] = boxes
end end
UIManager:setDirty(self.dialog, "partial") UIManager:setDirty(self.dialog, "partial")
end end
return true return true
end end
function ReaderHighlight:onHoldPan(arg, ges) function ReaderHighlight:onHoldPan(arg, ges)
if self.hold_pos == nil then if self.hold_pos == nil then
DEBUG("no previous hold position") DEBUG("no previous hold position")
return true return true
end end
self.holdpan_pos = self.view:screenToPageTransform(ges.pos) self.holdpan_pos = self.view:screenToPageTransform(ges.pos)
DEBUG("holdpan position in page", self.holdpan_pos) DEBUG("holdpan position in page", self.holdpan_pos)
self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.holdpan_pos) self.selected_text = self.ui.document:getTextFromPositions(self.hold_pos, self.holdpan_pos)
DEBUG("selected text:", self.selected_text) DEBUG("selected text:", self.selected_text)
if self.selected_text then if self.selected_text then
self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes self.view.highlight.temp[self.hold_pos.page] = self.selected_text.sboxes
-- remove selected word if hold moves out of word box -- remove selected word if hold moves out of word box
if not self.selected_text.sboxes or #self.selected_text.sboxes == 0 then if not self.selected_text.sboxes or #self.selected_text.sboxes == 0 then
self.selected_word = nil self.selected_word = nil
elseif self.selected_word and not self.selected_word.sbox:contains(self.selected_text.sboxes[1]) or 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_text.sboxes > 1 then
self.selected_word = nil self.selected_word = nil
end end
end end
UIManager:setDirty(self.dialog, "partial") UIManager:setDirty(self.dialog, "partial")
end end
function ReaderHighlight:lookup(selected_word) function ReaderHighlight:lookup(selected_word)
-- if we extracted text directly -- if we extracted text directly
if selected_word.word then if selected_word.word then
local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox) 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)) self.ui:handleEvent(Event:new("LookupWord", self, selected_word.word, word_box))
-- or we will do OCR -- or we will do OCR
elseif selected_word.sbox and self.hold_pos then elseif selected_word.sbox and self.hold_pos then
local word = self.ui.document:getOCRWord(self.hold_pos.page, selected_word) local word = self.ui.document:getOCRWord(self.hold_pos.page, selected_word)
DEBUG("OCRed word:", word) DEBUG("OCRed word:", word)
local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox) local word_box = self.view:pageToScreenTransform(self.hold_pos.page, selected_word.sbox)
self.ui:handleEvent(Event:new("LookupWord", self, word, word_box)) self.ui:handleEvent(Event:new("LookupWord", self, word, word_box))
end end
end end
function ReaderHighlight:translate(selected_text) function ReaderHighlight:translate(selected_text)
if selected_text.text ~= "" then if selected_text.text ~= "" then
self.ui:handleEvent(Event:new("TranslateText", self, selected_text.text)) self.ui:handleEvent(Event:new("TranslateText", self, selected_text.text))
-- or we will do OCR -- or we will do OCR
else else
local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text) local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text)
DEBUG("OCRed text:", text) DEBUG("OCRed text:", text)
self.ui:handleEvent(Event:new("TranslateText", self, text)) self.ui:handleEvent(Event:new("TranslateText", self, text))
end end
end end
function ReaderHighlight:onHoldRelease(arg, ges) function ReaderHighlight:onHoldRelease(arg, ges)
if self.selected_word then if self.selected_word then
self:lookup(self.selected_word) self:lookup(self.selected_word)
self.selected_word = nil self.selected_word = nil
elseif self.selected_text then elseif self.selected_text then
DEBUG("show highlight dialog") DEBUG("show highlight dialog")
self.highlight_dialog = ButtonDialog:new{ self.highlight_dialog = ButtonDialog:new{
buttons = { buttons = {
{ {
{ {
text = _("Highlight"), text = _("Highlight"),
callback = function() callback = function()
self:saveHighlight() self:saveHighlight()
UIManager:close(self.highlight_dialog) UIManager:close(self.highlight_dialog)
self:handleEvent(Event:new("Tap")) self:handleEvent(Event:new("Tap"))
end, end,
}, },
{ {
text = _("Add Note"), text = _("Add Note"),
enabled = false, enabled = false,
callback = function() callback = function()
self:addNote() self:addNote()
UIManager:close(self.highlight_dialog) UIManager:close(self.highlight_dialog)
self:handleEvent(Event:new("Tap")) self:handleEvent(Event:new("Tap"))
end, end,
}, },
}, },
{ {
{ {
text = _("Translate"), text = _("Translate"),
callback = function() callback = function()
self:translate(self.selected_text) self:translate(self.selected_text)
UIManager:close(self.highlight_dialog) UIManager:close(self.highlight_dialog)
self:handleEvent(Event:new("Tap")) self:handleEvent(Event:new("Tap"))
end, end,
}, },
{ {
text = _("Share"), text = _("Share"),
enabled = false, enabled = false,
callback = function() callback = function()
self:shareHighlight() self:shareHighlight()
UIManager:close(self.highlight_dialog) UIManager:close(self.highlight_dialog)
self:handleEvent(Event:new("Tap")) self:handleEvent(Event:new("Tap"))
end, end,
}, },
}, },
{ {
{ {
text = _("More"), text = _("More"),
enabled = false, enabled = false,
callback = function() callback = function()
self:moreAction() self:moreAction()
UIManager:close(self.highlight_dialog) UIManager:close(self.highlight_dialog)
self:handleEvent(Event:new("Tap")) self:handleEvent(Event:new("Tap"))
end, end,
}, },
}, },
}, },
tap_close_callback = function() self:handleEvent(Event:new("Tap")) end, tap_close_callback = function() self:handleEvent(Event:new("Tap")) end,
} }
UIManager:show(self.highlight_dialog) UIManager:show(self.highlight_dialog)
end end
return true return true
end end
function ReaderHighlight:saveHighlight() function ReaderHighlight:saveHighlight()
DEBUG("save highlight") DEBUG("save highlight")
local page = self.hold_pos.page local page = self.hold_pos.page
if self.hold_pos and self.selected_text then if self.hold_pos and self.selected_text then
if not self.view.highlight.saved[page] then if not self.view.highlight.saved[page] then
self.view.highlight.saved[page] = {} self.view.highlight.saved[page] = {}
end end
local hl_item = {} local hl_item = {}
hl_item["text"] = self.selected_text.text hl_item["text"] = self.selected_text.text
hl_item["pos0"] = self.selected_text.pos0 hl_item["pos0"] = self.selected_text.pos0
hl_item["pos1"] = self.selected_text.pos1 hl_item["pos1"] = self.selected_text.pos1
hl_item["pboxes"] = self.selected_text.pboxes hl_item["pboxes"] = self.selected_text.pboxes
hl_item["datetime"] = os.date("%Y-%m-%d %H:%M:%S") hl_item["datetime"] = os.date("%Y-%m-%d %H:%M:%S")
hl_item["drawer"] = self.view.highlight.saved_drawer hl_item["drawer"] = self.view.highlight.saved_drawer
table.insert(self.view.highlight.saved[page], hl_item) table.insert(self.view.highlight.saved[page], hl_item)
if self.selected_text.text ~= "" then if self.selected_text.text ~= "" then
self:exportToClippings(page, hl_item) self:exportToClippings(page, hl_item)
end end
if self.selected_text.pboxes then if self.selected_text.pboxes then
self:exportToDocument(page, hl_item) self:exportToDocument(page, hl_item)
end end
end end
--DEBUG("saved hightlights", self.view.highlight.saved[page]) --DEBUG("saved hightlights", self.view.highlight.saved[page])
end end
function ReaderHighlight:exportToClippings(page, item) function ReaderHighlight:exportToClippings(page, item)
DEBUG("export highlight to clippings", item) DEBUG("export highlight to clippings", item)
local clippings = io.open("/mnt/us/documents/My Clippings.txt", "a+") local clippings = io.open("/mnt/us/documents/My Clippings.txt", "a+")
if clippings and item.text then if clippings and item.text then
local current_locale = os.setlocale() local current_locale = os.setlocale()
os.setlocale("C") os.setlocale("C")
clippings:write(self.document.file:gsub("(.*/)(.*)", "%2").."\n") clippings:write(self.document.file:gsub("(.*/)(.*)", "%2").."\n")
clippings:write("- Koreader Highlight Page "..page.." ") clippings:write("- Koreader Highlight Page "..page.." ")
clippings:write("| Added on "..os.date("%A, %b %d, %Y %I:%M:%S %p\n\n")) clippings:write("| Added on "..os.date("%A, %b %d, %Y %I:%M:%S %p\n\n"))
clippings:write(item["text"].."\n") clippings:write(item["text"].."\n")
clippings:write("==========\n") clippings:write("==========\n")
clippings:close() clippings:close()
os.setlocale(current_locale) os.setlocale(current_locale)
end end
end end
function ReaderHighlight:exportToDocument(page, item) function ReaderHighlight:exportToDocument(page, item)
DEBUG("export highlight to document", item) DEBUG("export highlight to document", item)
self.ui.document:saveHighlight(page, item) self.ui.document:saveHighlight(page, item)
end end
function ReaderHighlight:addNote() function ReaderHighlight:addNote()
DEBUG("add Note") DEBUG("add Note")
end end
function ReaderHighlight:shareHighlight() function ReaderHighlight:shareHighlight()
DEBUG("share highlight") DEBUG("share highlight")
end end
function ReaderHighlight:moreAction() function ReaderHighlight:moreAction()
DEBUG("more action") DEBUG("more action")
end end
function ReaderHighlight:deleteHighlight(page, i) function ReaderHighlight:deleteHighlight(page, i)
DEBUG("delete highlight") DEBUG("delete highlight")
table.remove(self.view.highlight.saved[page], i) table.remove(self.view.highlight.saved[page], i)
end end
function ReaderHighlight:editHighlight() function ReaderHighlight:editHighlight()
DEBUG("edit highlight") DEBUG("edit highlight")
end end
function ReaderHighlight:onReadSettings(config) 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 end
function ReaderHighlight:onSaveSettings() 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 end
return ReaderHighlight return ReaderHighlight

@ -1,37 +1,37 @@
local EventListener = require("ui/widget/eventlistener") local EventListener = require("ui/widget/eventlistener")
local ReaderHinting = EventListener:new{ local ReaderHinting = EventListener:new{
hinting_states = {} hinting_states = {}
} }
function ReaderHinting:onHintPage() function ReaderHinting:onHintPage()
if not self.view.hinting then return true end if not self.view.hinting then return true end
for i=1, DHINTCOUNT do for i=1, DHINTCOUNT do
if self.view.state.page + i <= self.ui.document.info.number_of_pages then if self.view.state.page + i <= self.ui.document.info.number_of_pages then
self.ui.document:hintPage( self.ui.document:hintPage(
self.view.state.page + i, self.view.state.page + i,
self.zoom:getZoom(self.view.state.page + i), self.zoom:getZoom(self.view.state.page + i),
self.view.state.rotation, self.view.state.rotation,
self.view.state.gamma, self.view.state.gamma,
self.view.render_mode) self.view.render_mode)
end end
end end
return true return true
end end
function ReaderHinting:onSetHinting(hinting) function ReaderHinting:onSetHinting(hinting)
self.view.hinting = hinting self.view.hinting = hinting
end end
function ReaderHinting:onDisableHinting() function ReaderHinting:onDisableHinting()
table.insert(self.hinting_states, self.view.hinting) table.insert(self.hinting_states, self.view.hinting)
self.view.hinting = false self.view.hinting = false
return true return true
end end
function ReaderHinting:onRestoreHinting() function ReaderHinting:onRestoreHinting()
self.view.hinting = table.remove(self.hinting_states) self.view.hinting = table.remove(self.hinting_states)
return true return true
end end
return ReaderHinting return ReaderHinting

@ -4,52 +4,52 @@ local InfoMessage = require("ui/widget/infomessage")
local _ = require("gettext") local _ = require("gettext")
local ReaderHyphenation = InputContainer:new{ local ReaderHyphenation = InputContainer:new{
hyph_menu_title = _("Hyphenation"), hyph_menu_title = _("Hyphenation"),
hyph_table = nil, hyph_table = nil,
cur_hyph_idx = nil, cur_hyph_idx = nil,
} }
function ReaderHyphenation:_changeSel(k) function ReaderHyphenation:_changeSel(k)
if self.cur_hyph_idx then if self.cur_hyph_idx then
self.hyph_table[self.cur_hyph_idx].selected = false self.hyph_table[self.cur_hyph_idx].selected = false
end end
self.hyph_table[k].selected = true self.hyph_table[k].selected = true
self.cur_hyph_idx = k self.cur_hyph_idx = k
end end
function ReaderHyphenation:init() function ReaderHyphenation:init()
self.hyph_table = {} self.hyph_table = {}
self.hyph_alg = cre.getSelectedHyphDict() self.hyph_alg = cre.getSelectedHyphDict()
for k,v in ipairs(cre.getHyphDictList()) do for k,v in ipairs(cre.getHyphDictList()) do
if v == self.hyph_alg then if v == self.hyph_alg then
self.cur_hyph_idx = k self.cur_hyph_idx = k
end end
table.insert(self.hyph_table, { table.insert(self.hyph_table, {
text = v, text = v,
callback = function() callback = function()
self.hyph_alg = v self.hyph_alg = v
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = _("Change Hyphenation to ")..v, text = _("Change Hyphenation to ")..v,
}) })
self:_changeSel(k) self:_changeSel(k)
cre.setHyphDictionary(v) cre.setHyphDictionary(v)
end end
}) })
end end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderHyphenation:onReadSettings(config) function ReaderHyphenation:onReadSettings(config)
local hyph_alg = config:readSetting("hyph_alg") local hyph_alg = config:readSetting("hyph_alg")
if hyph_alg then if hyph_alg then
cre.setHyphDictionary(hyph_alg) cre.setHyphDictionary(hyph_alg)
end end
self.hyph_alg = cre.getSelectedHyphDict() self.hyph_alg = cre.getSelectedHyphDict()
for k,v in ipairs(self.hyph_table) do for k,v in ipairs(self.hyph_table) do
if v.text == self.hyph_alg then if v.text == self.hyph_alg then
self:_changeSel(k) self:_changeSel(k)
end end
end end
end end
function ReaderHyphenation:onSaveSettings() function ReaderHyphenation:onSaveSettings()
@ -57,11 +57,11 @@ function ReaderHyphenation:onSaveSettings()
end end
function ReaderHyphenation:addToMainMenu(tab_item_table) function ReaderHyphenation:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.typeset, { table.insert(tab_item_table.typeset, {
text = self.hyph_menu_title, text = self.hyph_menu_title,
sub_item_table = self.hyph_table, sub_item_table = self.hyph_table,
}) })
end end
return ReaderHyphenation return ReaderHyphenation

@ -4,56 +4,56 @@ local Event = require("ui/event")
local ReaderKoptListener = EventListener:new{} local ReaderKoptListener = EventListener:new{}
function ReaderKoptListener:setZoomMode(zoom_mode) function ReaderKoptListener:setZoomMode(zoom_mode)
if self.document.configurable.text_wrap == 1 then if self.document.configurable.text_wrap == 1 then
-- in reflow mode only "page" zoom mode is valid so override any other zoom mode -- in reflow mode only "page" zoom mode is valid so override any other zoom mode
self.ui:handleEvent(Event:new("SetZoomMode", "page", "koptlistener")) self.ui:handleEvent(Event:new("SetZoomMode", "page", "koptlistener"))
else else
self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode, "koptlistener")) self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode, "koptlistener"))
end end
end end
function ReaderKoptListener:onReadSettings(config) function ReaderKoptListener:onReadSettings(config)
-- normal zoom mode is zoom mode used in non-reflow mode. -- normal zoom mode is zoom mode used in non-reflow mode.
self.normal_zoom_mode = config:readSetting("normal_zoom_mode") or "page" self.normal_zoom_mode = config:readSetting("normal_zoom_mode") or "page"
self:setZoomMode(self.normal_zoom_mode) self:setZoomMode(self.normal_zoom_mode)
end end
function ReaderKoptListener:onSaveSettings() 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 end
function ReaderKoptListener:onRestoreZoomMode() function ReaderKoptListener:onRestoreZoomMode()
-- "RestoreZoomMode" event is sent when reflow mode on/off is toggled -- "RestoreZoomMode" event is sent when reflow mode on/off is toggled
self:setZoomMode(self.normal_zoom_mode) self:setZoomMode(self.normal_zoom_mode)
return true return true
end end
function ReaderKoptListener:onSetZoomMode(zoom_mode, orig) function ReaderKoptListener:onSetZoomMode(zoom_mode, orig)
if orig == "koptlistener" then return end if orig == "koptlistener" then return end
-- capture zoom mode set outside of koptlistener which should always be normal zoom mode -- capture zoom mode set outside of koptlistener which should always be normal zoom mode
self.normal_zoom_mode = zoom_mode self.normal_zoom_mode = zoom_mode
self:setZoomMode(self.normal_zoom_mode) self:setZoomMode(self.normal_zoom_mode)
end end
function ReaderKoptListener:onFineTuningFontSize(delta) 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 end
function ReaderKoptListener:onZoomUpdate(zoom) function ReaderKoptListener:onZoomUpdate(zoom)
-- an exceptional case is reflow mode -- an exceptional case is reflow mode
if self.document.configurable.text_wrap == 1 then if self.document.configurable.text_wrap == 1 then
self.view.state.zoom = 1.0 self.view.state.zoom = 1.0
end end
end end
-- misc koptoption handler -- misc koptoption handler
function ReaderKoptListener:onDocLangUpdate(lang) function ReaderKoptListener:onDocLangUpdate(lang)
if lang == "chi_sim" or lang == "chi_tra" or if lang == "chi_sim" or lang == "chi_tra" or
lang == "jpn" or lang == "kor" then lang == "jpn" or lang == "kor" then
self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[1] self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[1]
else else
self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[3] self.document.configurable.word_spacing = DKOPTREADER_CONFIG_WORD_SAPCINGS[3]
end end
end end
return ReaderKoptListener return ReaderKoptListener

@ -7,100 +7,100 @@ local Event = require("ui/event")
local DEBUG = require("dbg") local DEBUG = require("dbg")
local ReaderLink = InputContainer:new{ local ReaderLink = InputContainer:new{
link_states = {} link_states = {}
} }
function ReaderLink:init() function ReaderLink:init()
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function ReaderLink:onReadSettings(config) function ReaderLink:onReadSettings(config)
-- called when loading new document -- called when loading new document
self.link_states = {} self.link_states = {}
end end
function ReaderLink:initGesListener() function ReaderLink:initGesListener()
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
Tap = { Tap = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight() h = Screen:getHeight()
} }
} }
}, },
Swipe = { Swipe = {
GestureRange:new{ GestureRange:new{
ges = "swipe", ges = "swipe",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
}, },
} }
end end
end end
function ReaderLink:onSetDimensions(dimen) function ReaderLink:onSetDimensions(dimen)
-- update listening according to new screen dimen -- update listening according to new screen dimen
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function ReaderLink:onTap(arg, ges) function ReaderLink:onTap(arg, ges)
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
if pos then if pos then
local link = self.ui.document:getLinkFromPosition(pos.page, pos) local link = self.ui.document:getLinkFromPosition(pos.page, pos)
if link then if link then
return self:onGotoLink(link) return self:onGotoLink(link)
end end
end end
else else
local link = self.ui.document:getLinkFromPosition(ges.pos) local link = self.ui.document:getLinkFromPosition(ges.pos)
if link ~= "" then if link ~= "" then
return self:onGotoLink(link) return self:onGotoLink(link)
end end
end end
end end
function ReaderLink:onGotoLink(link) function ReaderLink:onGotoLink(link)
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
table.insert(self.link_states, self.view.state.page) table.insert(self.link_states, self.view.state.page)
self.ui:handleEvent(Event:new("PageUpdate", link.page + 1)) self.ui:handleEvent(Event:new("PageUpdate", link.page + 1))
else else
table.insert(self.link_states, self.ui.document:getXPointer()) table.insert(self.link_states, self.ui.document:getXPointer())
self.document:gotoLink(link) self.document:gotoLink(link)
self.ui:handleEvent(Event:new("UpdateXPointer")) self.ui:handleEvent(Event:new("UpdateXPointer"))
end end
return true return true
end end
function ReaderLink:onSwipe(arg, ges) function ReaderLink:onSwipe(arg, ges)
if ges.direction == "east" then if ges.direction == "east" then
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
local last_page = table.remove(self.link_states) local last_page = table.remove(self.link_states)
if last_page then if last_page then
self.ui:handleEvent(Event:new("PageUpdate", last_page)) self.ui:handleEvent(Event:new("PageUpdate", last_page))
return true return true
end end
else else
local last_xp = table.remove(self.link_states) local last_xp = table.remove(self.link_states)
if last_xp then if last_xp then
self.ui.document:gotoXPointer(last_xp) self.ui.document:gotoXPointer(last_xp)
self.ui:handleEvent(Event:new("UpdateXPointer")) self.ui:handleEvent(Event:new("UpdateXPointer"))
return true return true
end end
end end
end end
end end
return ReaderLink return ReaderLink

@ -13,146 +13,146 @@ local Language = require("ui/language")
local _ = require("gettext") local _ = require("gettext")
local ReaderMenu = InputContainer:new{ local ReaderMenu = InputContainer:new{
tab_item_table = nil, tab_item_table = nil,
registered_widgets = {}, registered_widgets = {},
} }
function ReaderMenu:init() function ReaderMenu:init()
self.tab_item_table = { self.tab_item_table = {
main = { main = {
icon = "resources/icons/appbar.pokeball.png", icon = "resources/icons/appbar.pokeball.png",
}, },
navi = { navi = {
icon = "resources/icons/appbar.page.corner.bookmark.png", icon = "resources/icons/appbar.page.corner.bookmark.png",
}, },
typeset = { typeset = {
icon = "resources/icons/appbar.page.text.png", icon = "resources/icons/appbar.page.text.png",
}, },
home = { home = {
icon = "resources/icons/appbar.home.png", icon = "resources/icons/appbar.home.png",
callback = function() callback = function()
self.ui:handleEvent(Event:new("RestoreScreenMode", self.ui:handleEvent(Event:new("RestoreScreenMode",
G_reader_settings:readSetting("screen_mode") or "portrait")) G_reader_settings:readSetting("screen_mode") or "portrait"))
UIManager:close(self.menu_container) UIManager:close(self.menu_container)
self.ui:onClose() self.ui:onClose()
end, end,
}, },
} }
self.registered_widgets = {} self.registered_widgets = {}
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ShowMenu = { { "Menu" }, doc = _("show menu") }, ShowMenu = { { "Menu" }, doc = _("show menu") },
} }
end end
end end
function ReaderMenu:initGesListener() function ReaderMenu:initGesListener()
self.ges_events = { self.ges_events = {
TapShowMenu = { TapShowMenu = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_MENU.x, x = Screen:getWidth()*DTAP_ZONE_MENU.x,
y = Screen:getHeight()*DTAP_ZONE_MENU.y, y = Screen:getHeight()*DTAP_ZONE_MENU.y,
w = Screen:getWidth()*DTAP_ZONE_MENU.w, w = Screen:getWidth()*DTAP_ZONE_MENU.w,
h = Screen:getHeight()*DTAP_ZONE_MENU.h h = Screen:getHeight()*DTAP_ZONE_MENU.h
} }
} }
}, },
} }
end end
function ReaderMenu:setUpdateItemTable() function ReaderMenu:setUpdateItemTable()
for _, widget in pairs(self.registered_widgets) do for _, widget in pairs(self.registered_widgets) do
widget:addToMainMenu(self.tab_item_table) widget:addToMainMenu(self.tab_item_table)
end end
table.insert(self.tab_item_table.main, { table.insert(self.tab_item_table.main, {
text = _("Help"), text = _("Help"),
callback = function() callback = function()
UIManager:show(InfoMessage:new{ 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"), text = _("Please report bugs to https://github.com/koreader/ koreader/issues, Click at the bottom of the page for more options"),
}) })
end end
}) })
table.insert(self.tab_item_table.main, { table.insert(self.tab_item_table.main, {
text = _("Version"), text = _("Version"),
callback = function() callback = function()
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = io.open("git-rev", "r"):read(), text = io.open("git-rev", "r"):read(),
}) })
end end
}) })
table.insert(self.tab_item_table.main, Language:getLangMenuTable()) table.insert(self.tab_item_table.main, Language:getLangMenuTable())
end end
function ReaderMenu:onShowReaderMenu() function ReaderMenu:onShowReaderMenu()
if #self.tab_item_table.main == 0 then if #self.tab_item_table.main == 0 then
self:setUpdateItemTable() self:setUpdateItemTable()
end end
local menu_container = CenterContainer:new{ local menu_container = CenterContainer:new{
ignore = "height", ignore = "height",
dimen = Screen:getSize(), dimen = Screen:getSize(),
} }
local main_menu = nil local main_menu = nil
if Device:isTouchDevice() then if Device:isTouchDevice() then
main_menu = TouchMenu:new{ main_menu = TouchMenu:new{
width = Screen:getWidth(), width = Screen:getWidth(),
tab_item_table = { tab_item_table = {
self.tab_item_table.navi, self.tab_item_table.navi,
self.tab_item_table.typeset, self.tab_item_table.typeset,
self.tab_item_table.main, self.tab_item_table.main,
self.tab_item_table.home, self.tab_item_table.home,
}, },
show_parent = menu_container, show_parent = menu_container,
} }
else else
main_menu = Menu:new{ main_menu = Menu:new{
title = _("Document menu"), title = _("Document menu"),
item_table = {}, item_table = {},
width = Screen:getWidth() - 100, width = Screen:getWidth() - 100,
} }
for _,item_table in pairs(self.tab_item_table) do for _,item_table in pairs(self.tab_item_table) do
for k,v in ipairs(item_table) do for k,v in ipairs(item_table) do
table.insert(main_menu.item_table, v) table.insert(main_menu.item_table, v)
end end
end end
end end
main_menu.close_callback = function () main_menu.close_callback = function ()
UIManager:close(menu_container) UIManager:close(menu_container)
end end
menu_container[1] = main_menu menu_container[1] = main_menu
-- maintain a reference to menu_container -- maintain a reference to menu_container
self.menu_container = menu_container self.menu_container = menu_container
UIManager:show(menu_container) UIManager:show(menu_container)
return true return true
end end
function ReaderMenu:onTapShowMenu() function ReaderMenu:onTapShowMenu()
self.ui:handleEvent(Event:new("ShowConfigMenu")) self.ui:handleEvent(Event:new("ShowConfigMenu"))
self.ui:handleEvent(Event:new("ShowReaderMenu")) self.ui:handleEvent(Event:new("ShowReaderMenu"))
return true return true
end end
function ReaderMenu:onSetDimensions(dimen) function ReaderMenu:onSetDimensions(dimen)
-- update listening according to new screen dimen -- update listening according to new screen dimen
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function ReaderMenu:onSaveSettings() function ReaderMenu:onSaveSettings()
end end
function ReaderMenu:registerToMainMenu(widget) function ReaderMenu:registerToMainMenu(widget)
table.insert(self.registered_widgets, widget) table.insert(self.registered_widgets, widget)
end end
return ReaderMenu return ReaderMenu

File diff suppressed because it is too large Load Diff

@ -4,48 +4,48 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ReaderPanning = InputContainer:new{ local ReaderPanning = InputContainer:new{
-- defaults -- defaults
panning_steps = { panning_steps = {
normal = 50, normal = 50,
alt = 25, alt = 25,
shift = 10, shift = 10,
altshift = 5 altshift = 5
}, },
} }
function ReaderPanning:init() function ReaderPanning:init()
if Device:isTouchDevice() then if Device:isTouchDevice() then
else else
self.key_events = { self.key_events = {
-- these will all generate the same event, just with different arguments -- these will all generate the same event, just with different arguments
MoveUp = { MoveUp = {
{ "Up" }, doc = _("move visible area up"), { "Up" }, doc = _("move visible area up"),
event = "Panning", args = {0, -1} }, event = "Panning", args = {0, -1} },
MoveDown = { MoveDown = {
{ "Down" }, doc = _("move visible area down"), { "Down" }, doc = _("move visible area down"),
event = "Panning", args = {0, 1} }, event = "Panning", args = {0, 1} },
MoveLeft = { MoveLeft = {
{ "Left" }, doc = _("move visible area left"), { "Left" }, doc = _("move visible area left"),
event = "Panning", args = {-1, 0} }, event = "Panning", args = {-1, 0} },
MoveRight = { MoveRight = {
{ "Right" }, doc = _("move visible area right"), { "Right" }, doc = _("move visible area right"),
event = "Panning", args = {1, 0} }, event = "Panning", args = {1, 0} },
} }
end end
end end
function ReaderPanning:onSetDimensions(dimensions) function ReaderPanning:onSetDimensions(dimensions)
self.dimen = dimensions self.dimen = dimensions
end end
function ReaderPanning:onPanning(args, key) function ReaderPanning:onPanning(args, key)
local dx, dy = unpack(args) local dx, dy = unpack(args)
DEBUG("key =", key) DEBUG("key =", key)
-- for now, bounds checking/calculation is done in the view -- for now, bounds checking/calculation is done in the view
self.view:PanningUpdate( self.view:PanningUpdate(
dx * self.panning_steps.normal * self.dimen.w / 100, dx * self.panning_steps.normal * self.dimen.w / 100,
dy * self.panning_steps.normal * self.dimen.h / 100) dy * self.panning_steps.normal * self.dimen.h / 100)
return true return true
end end
return ReaderPanning return ReaderPanning

@ -11,363 +11,363 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ReaderRolling = InputContainer:new{ local ReaderRolling = InputContainer:new{
old_doc_height = nil, old_doc_height = nil,
old_page = nil, old_page = nil,
current_pos = 0, current_pos = 0,
-- only used for page view mode -- only used for page view mode
current_page= nil, current_page= nil,
doc_height = nil, doc_height = nil,
panning_steps = ReaderPanning.panning_steps, panning_steps = ReaderPanning.panning_steps,
show_overlap_enable = true, show_overlap_enable = true,
overlap = 20, overlap = 20,
} }
function ReaderRolling:init() function ReaderRolling:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
GotoNextView = { GotoNextView = {
{ Input.group.PgFwd }, { Input.group.PgFwd },
doc = _("go to next view"), doc = _("go to next view"),
event = "GotoViewRel", args = 1 event = "GotoViewRel", args = 1
}, },
GotoPrevView = { GotoPrevView = {
{ Input.group.PgBack }, { Input.group.PgBack },
doc = _("go to previous view"), doc = _("go to previous view"),
event = "GotoViewRel", args = -1 event = "GotoViewRel", args = -1
}, },
MoveUp = { MoveUp = {
{ "Up" }, { "Up" },
doc = _("move view up"), doc = _("move view up"),
event = "Panning", args = {0, -1} event = "Panning", args = {0, -1}
}, },
MoveDown = { MoveDown = {
{ "Down" }, { "Down" },
doc = _("move view down"), doc = _("move view down"),
event = "Panning", args = {0, 1} event = "Panning", args = {0, 1}
}, },
GotoFirst = { GotoFirst = {
{"1"}, doc = _("go to start"), event = "GotoPercent", args = 0}, {"1"}, doc = _("go to start"), event = "GotoPercent", args = 0},
Goto11 = { Goto11 = {
{"2"}, doc = _("go to 11%"), event = "GotoPercent", args = 11}, {"2"}, doc = _("go to 11%"), event = "GotoPercent", args = 11},
Goto22 = { Goto22 = {
{"3"}, doc = _("go to 22%"), event = "GotoPercent", args = 22}, {"3"}, doc = _("go to 22%"), event = "GotoPercent", args = 22},
Goto33 = { Goto33 = {
{"4"}, doc = _("go to 33%"), event = "GotoPercent", args = 33}, {"4"}, doc = _("go to 33%"), event = "GotoPercent", args = 33},
Goto44 = { Goto44 = {
{"5"}, doc = _("go to 44%"), event = "GotoPercent", args = 44}, {"5"}, doc = _("go to 44%"), event = "GotoPercent", args = 44},
Goto55 = { Goto55 = {
{"6"}, doc = _("go to 55%"), event = "GotoPercent", args = 55}, {"6"}, doc = _("go to 55%"), event = "GotoPercent", args = 55},
Goto66 = { Goto66 = {
{"7"}, doc = _("go to 66%"), event = "GotoPercent", args = 66}, {"7"}, doc = _("go to 66%"), event = "GotoPercent", args = 66},
Goto77 = { Goto77 = {
{"8"}, doc = _("go to 77%"), event = "GotoPercent", args = 77}, {"8"}, doc = _("go to 77%"), event = "GotoPercent", args = 77},
Goto88 = { Goto88 = {
{"9"}, doc = _("go to 88%"), event = "GotoPercent", args = 88}, {"9"}, doc = _("go to 88%"), event = "GotoPercent", args = 88},
GotoLast = { GotoLast = {
{"0"}, doc = _("go to end"), event = "GotoPercent", args = 100}, {"0"}, doc = _("go to end"), event = "GotoPercent", args = 100},
} }
end end
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self.doc_height = self.ui.document.info.doc_height self.doc_height = self.ui.document.info.doc_height
self.old_doc_height = self.doc_height self.old_doc_height = self.doc_height
self.old_page = self.ui.document.info.number_of_pages self.old_page = self.ui.document.info.number_of_pages
end) end)
end end
-- This method will be called in onSetDimensions handler -- This method will be called in onSetDimensions handler
function ReaderRolling:initGesListener() function ReaderRolling:initGesListener()
self.ges_events = { self.ges_events = {
TapForward = { TapForward = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_FORWARD.x, x = Screen:getWidth()*DTAP_ZONE_FORWARD.x,
y = Screen:getHeight()*DTAP_ZONE_FORWARD.y, y = Screen:getHeight()*DTAP_ZONE_FORWARD.y,
w = Screen:getWidth()*DTAP_ZONE_FORWARD.w, w = Screen:getWidth()*DTAP_ZONE_FORWARD.w,
h = Screen:getHeight()*DTAP_ZONE_FORWARD.h, h = Screen:getHeight()*DTAP_ZONE_FORWARD.h,
} }
} }
}, },
TapBackward = { TapBackward = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x, x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x,
y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y, y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y,
w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w, w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w,
h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h, h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h,
} }
} }
}, },
Swipe = { Swipe = {
GestureRange:new{ GestureRange:new{
ges = "swipe", ges = "swipe",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
}, },
Pan = { Pan = {
GestureRange:new{ GestureRange:new{
ges = "pan", ges = "pan",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
}, },
rate = 4.0, rate = 4.0,
} }
}, },
} }
end end
function ReaderRolling:onReadSettings(config) function ReaderRolling:onReadSettings(config)
local soe = config:readSetting("show_overlap_enable") local soe = config:readSetting("show_overlap_enable")
if not soe then if not soe then
self.show_overlap_enable = soe self.show_overlap_enable = soe
end end
local last_xp = config:readSetting("last_xpointer") local last_xp = config:readSetting("last_xpointer")
if last_xp then if last_xp then
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self:gotoXPointer(last_xp) self:gotoXPointer(last_xp)
-- we have to do a real jump in self.ui.document._document to -- we have to do a real jump in self.ui.document._document to
-- update status information in CREngine. -- update status information in CREngine.
self.ui.document:gotoXPointer(last_xp) self.ui.document:gotoXPointer(last_xp)
end) end)
end end
-- we read last_percent just for backward compatibility -- we read last_percent just for backward compatibility
if not last_xp then if not last_xp then
local last_per = config:readSetting("last_percent") local last_per = config:readSetting("last_percent")
if last_per then if last_per then
table.insert(self.ui.postInitCallback, function() table.insert(self.ui.postInitCallback, function()
self:gotoPercent(last_per) self:gotoPercent(last_per)
-- we have to do a real pos change in self.ui.document._document -- we have to do a real pos change in self.ui.document._document
-- to update status information in CREngine. -- to update status information in CREngine.
self.ui.document:gotoPos(self.current_pos) self.ui.document:gotoPos(self.current_pos)
end) end)
end end
end end
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage())) self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage()))
end end
end end
function ReaderRolling:onSaveSettings() function ReaderRolling:onSaveSettings()
-- remove last_percent config since its deprecated -- remove last_percent config since its deprecated
self.ui.doc_settings:saveSetting("last_percent", nil) self.ui.doc_settings:saveSetting("last_percent", nil)
self.ui.doc_settings:saveSetting("last_xpointer", self.ui.document:getXPointer()) self.ui.doc_settings:saveSetting("last_xpointer", self.ui.document:getXPointer())
self.ui.doc_settings:saveSetting("percent_finished", self:getLastPercent()) self.ui.doc_settings:saveSetting("percent_finished", self:getLastPercent())
end end
function ReaderRolling:getLastPercent() function ReaderRolling:getLastPercent()
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
return self.current_page / self.old_page return self.current_page / self.old_page
else else
-- FIXME: the calculated percent is not accurate in "scroll" mode. -- FIXME: the calculated percent is not accurate in "scroll" mode.
return self.ui.document:getPosFromXPointer( return self.ui.document:getPosFromXPointer(
self.ui.document:getXPointer()) / self.doc_height self.ui.document:getXPointer()) / self.doc_height
end end
end end
function ReaderRolling:onTapForward() function ReaderRolling:onTapForward()
self:onGotoViewRel(1) self:onGotoViewRel(1)
return true return true
end end
function ReaderRolling:onTapBackward() function ReaderRolling:onTapBackward()
self:onGotoViewRel(-1) self:onGotoViewRel(-1)
return true return true
end end
function ReaderRolling:onSwipe(arg, ges) function ReaderRolling:onSwipe(arg, ges)
if ges.direction == "west" or ges.direction == "north" then if ges.direction == "west" or ges.direction == "north" then
self:onGotoViewRel(1) self:onGotoViewRel(1)
elseif ges.direction == "east" or ges.direction == "south" then elseif ges.direction == "east" or ges.direction == "south" then
self:onGotoViewRel(-1) self:onGotoViewRel(-1)
end end
return true return true
end end
function ReaderRolling:onPan(arg, ges) function ReaderRolling:onPan(arg, ges)
if self.view.view_mode == "scroll" then if self.view.view_mode == "scroll" then
if ges.direction == "north" then if ges.direction == "north" then
self:gotoPos(self.current_pos + ges.distance) self:gotoPos(self.current_pos + ges.distance)
elseif ges.direction == "south" then elseif ges.direction == "south" then
self:gotoPos(self.current_pos - ges.distance) self:gotoPos(self.current_pos - ges.distance)
end end
end end
return true return true
end end
function ReaderRolling:onPosUpdate(new_pos) function ReaderRolling:onPosUpdate(new_pos)
self.current_pos = new_pos self.current_pos = new_pos
self:updateBatteryState() self:updateBatteryState()
end end
function ReaderRolling:onPageUpdate(new_page) function ReaderRolling:onPageUpdate(new_page)
self.current_page = new_page self.current_page = new_page
self:updateBatteryState() self:updateBatteryState()
end end
function ReaderRolling:onResume() function ReaderRolling:onResume()
self:updateBatteryState() self:updateBatteryState()
end end
function ReaderRolling:onNotCharging() function ReaderRolling:onNotCharging()
self:updateBatteryState() self:updateBatteryState()
end end
function ReaderRolling:onGotoPercent(percent) function ReaderRolling:onGotoPercent(percent)
DEBUG("goto document offset in percent:", percent) DEBUG("goto document offset in percent:", percent)
self:gotoPercent(percent) self:gotoPercent(percent)
return true return true
end end
function ReaderRolling:onGotoViewRel(diff) function ReaderRolling:onGotoViewRel(diff)
DEBUG("goto relative screen:", diff, ", in mode: ", self.view.view_mode) DEBUG("goto relative screen:", diff, ", in mode: ", self.view.view_mode)
if self.view.view_mode == "scroll" then if self.view.view_mode == "scroll" then
local pan_diff = diff * self.ui.dimen.h local pan_diff = diff * self.ui.dimen.h
if self.show_overlap_enable then if self.show_overlap_enable then
if pan_diff > self.overlap then if pan_diff > self.overlap then
pan_diff = pan_diff - self.overlap pan_diff = pan_diff - self.overlap
elseif pan_diff < -self.overlap then elseif pan_diff < -self.overlap then
pan_diff = pan_diff + self.overlap pan_diff = pan_diff + self.overlap
end end
end end
self:gotoPos(self.current_pos + pan_diff) self:gotoPos(self.current_pos + pan_diff)
elseif self.view.view_mode == "page" then elseif self.view.view_mode == "page" then
self:gotoPage(self.current_page + diff) self:gotoPage(self.current_page + diff)
end end
return true return true
end end
function ReaderRolling:onPanning(args, key) function ReaderRolling:onPanning(args, key)
--@TODO disable panning in page view_mode? 22.12 2012 (houqp) --@TODO disable panning in page view_mode? 22.12 2012 (houqp)
local _, dy = unpack(args) local _, dy = unpack(args)
DEBUG("key =", key) DEBUG("key =", key)
self:gotoPos(self.current_pos + dy * self.panning_steps.normal) self:gotoPos(self.current_pos + dy * self.panning_steps.normal)
return true return true
end end
function ReaderRolling:onZoom() function ReaderRolling:onZoom()
--@TODO re-read doc_height info after font or lineheight changes 05.06 2012 (houqp) --@TODO re-read doc_height info after font or lineheight changes 05.06 2012 (houqp)
self:updatePos() self:updatePos()
end end
--[[ --[[
remember to signal this event when the document has been zoomed, remember to signal this event when the document has been zoomed,
font has been changed, or line height has been changed. font has been changed, or line height has been changed.
--]] --]]
function ReaderRolling:onUpdatePos() function ReaderRolling:onUpdatePos()
UIManager:scheduleIn(0.1, function () self:updatePos() end) UIManager:scheduleIn(0.1, function () self:updatePos() end)
return true return true
end end
function ReaderRolling:updatePos() function ReaderRolling:updatePos()
-- reread document height -- reread document height
self.ui.document:_readMetadata() self.ui.document:_readMetadata()
-- update self.current_pos if the height of document has been changed. -- update self.current_pos if the height of document has been changed.
local new_height = self.ui.document.info.doc_height local new_height = self.ui.document.info.doc_height
local new_page = self.ui.document.info.number_of_pages local new_page = self.ui.document.info.number_of_pages
if self.old_doc_height ~= new_height or self.old_page ~= new_page then if self.old_doc_height ~= new_height or self.old_page ~= new_page then
self:gotoXPointer(self.ui.document:getXPointer()) self:gotoXPointer(self.ui.document:getXPointer())
self.old_doc_height = new_height self.old_doc_height = new_height
self.old_page = new_page self.old_page = new_page
self.ui:handleEvent(Event:new("UpdateToc")) self.ui:handleEvent(Event:new("UpdateToc"))
end end
UIManager.repaint_all = true UIManager.repaint_all = true
end end
function ReaderRolling:onUpdateXPointer() function ReaderRolling:onUpdateXPointer()
local xp = self.ui.document:getXPointer() local xp = self.ui.document:getXPointer()
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(xp))) self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getPageFromXPointer(xp)))
else else
self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(xp))) self.ui:handleEvent(Event:new("PosUpdate", self.ui.document:getPosFromXPointer(xp)))
end end
return true return true
end end
function ReaderRolling:onChangeViewMode() function ReaderRolling:onChangeViewMode()
self.ui.document:_readMetadata() self.ui.document:_readMetadata()
self.old_doc_height = self.ui.document.info.doc_height self.old_doc_height = self.ui.document.info.doc_height
self.old_page = self.ui.document.info.number_of_pages self.old_page = self.ui.document.info.number_of_pages
self.ui:handleEvent(Event:new("UpdateToc")) self.ui:handleEvent(Event:new("UpdateToc"))
self:gotoXPointer(self.ui.document:getXPointer()) self:gotoXPointer(self.ui.document:getXPointer())
if self.view.view_mode == "scroll" then if self.view.view_mode == "scroll" then
self.current_pos = self.ui.document:getCurrentPos() self.current_pos = self.ui.document:getCurrentPos()
else else
self.current_page = self.ui.document:getCurrentPage() self.current_page = self.ui.document:getCurrentPage()
end end
return true return true
end end
function ReaderRolling:onRedrawCurrentView() function ReaderRolling:onRedrawCurrentView()
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
self.ui:handleEvent(Event:new("PageUpdate", self.current_page)) self.ui:handleEvent(Event:new("PageUpdate", self.current_page))
else else
self.ui:handleEvent(Event:new("PosUpdate", self.current_pos)) self.ui:handleEvent(Event:new("PosUpdate", self.current_pos))
end end
return true return true
end end
function ReaderRolling:onSetDimensions() function ReaderRolling:onSetDimensions()
-- update listening according to new screen dimen -- update listening according to new screen dimen
if Device:isTouchDevice() then if Device:isTouchDevice() then
self:initGesListener() self:initGesListener()
end end
end end
function ReaderRolling:onChangeScreenMode(mode) function ReaderRolling:onChangeScreenMode(mode)
self.ui:handleEvent(Event:new("SetScreenMode", mode)) self.ui:handleEvent(Event:new("SetScreenMode", mode))
self:onChangeViewMode() self:onChangeViewMode()
end 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) function ReaderRolling:gotoPos(new_pos)
if new_pos == self.current_pos then return end if new_pos == self.current_pos then return end
if new_pos < 0 then new_pos = 0 end if new_pos < 0 then new_pos = 0 end
if new_pos > self.doc_height then new_pos = self.doc_height end if new_pos > self.doc_height then new_pos = self.doc_height end
-- adjust dim_area according to new_pos -- adjust dim_area according to new_pos
if self.view.view_mode ~= "page" and self.show_overlap_enable then if self.view.view_mode ~= "page" and self.show_overlap_enable then
local panned_step = new_pos - self.current_pos local panned_step = new_pos - self.current_pos
self.view.dim_area.x = 0 self.view.dim_area.x = 0
self.view.dim_area.h = self.ui.dimen.h - math.abs(panned_step) self.view.dim_area.h = self.ui.dimen.h - math.abs(panned_step)
self.view.dim_area.w = self.ui.dimen.w self.view.dim_area.w = self.ui.dimen.w
if panned_step < 0 then if panned_step < 0 then
self.view.dim_area.y = self.ui.dimen.h - self.view.dim_area.h self.view.dim_area.y = self.ui.dimen.h - self.view.dim_area.h
elseif panned_step > 0 then elseif panned_step > 0 then
self.view.dim_area.y = 0 self.view.dim_area.y = 0
end end
end end
self.ui:handleEvent(Event:new("PosUpdate", new_pos)) self.ui:handleEvent(Event:new("PosUpdate", new_pos))
end end
function ReaderRolling:gotoPage(new_page) function ReaderRolling:gotoPage(new_page)
self.ui.document:gotoPage(new_page) self.ui.document:gotoPage(new_page)
self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage())) self.ui:handleEvent(Event:new("PageUpdate", self.ui.document:getCurrentPage()))
end end
function ReaderRolling:gotoXPointer(xpointer) function ReaderRolling:gotoXPointer(xpointer)
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
self:gotoPage(self.ui.document:getPageFromXPointer(xpointer)) self:gotoPage(self.ui.document:getPageFromXPointer(xpointer))
else else
self:gotoPos(self.ui.document:getPosFromXPointer(xpointer)) self:gotoPos(self.ui.document:getPosFromXPointer(xpointer))
end end
end end
function ReaderRolling:gotoPercent(new_percent) function ReaderRolling:gotoPercent(new_percent)
self:gotoPos(new_percent * self.doc_height / 10000) self:gotoPos(new_percent * self.doc_height / 10000)
end end
function ReaderRolling:onGotoPage(number) function ReaderRolling:onGotoPage(number)
self:gotoPage(number) self:gotoPage(number)
return true return true
end 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 since we can check link on the fly when tapping on the screen
--]] --]]
function ReaderRolling:updatePageLink() function ReaderRolling:updatePageLink()
DEBUG("update page link") DEBUG("update page link")
local links = self.ui.document:getPageLinks() local links = self.ui.document:getPageLinks()
self.view.links = links self.view.links = links
end end
function ReaderRolling:updateBatteryState() function ReaderRolling:updateBatteryState()
DEBUG("update battery state") DEBUG("update battery state")
if self.view.view_mode == "page" then if self.view.view_mode == "page" then
local powerd = Device:getPowerDevice() local powerd = Device:getPowerDevice()
local state = powerd:isCharging() and -1 or powerd:getCapacity() local state = powerd:isCharging() and -1 or powerd:getCapacity()
if state then if state then
self.ui.document:setBatteryState(state) self.ui.document:setBatteryState(state)
end end
end end
end end
return ReaderRolling return ReaderRolling

@ -7,72 +7,72 @@ local GestureRange = require("ui/gesturerange")
local _ = require("gettext") local _ = require("gettext")
local ReaderRotation = InputContainer:new{ local ReaderRotation = InputContainer:new{
ROTATE_ANGLE_THRESHOLD = 15, ROTATE_ANGLE_THRESHOLD = 15,
current_rotation = 0 current_rotation = 0
} }
function ReaderRotation:init() function ReaderRotation:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
-- these will all generate the same event, just with different arguments -- these will all generate the same event, just with different arguments
RotateLeft = { RotateLeft = {
{"J"}, {"J"},
doc = _("rotate left by 90 degrees"), doc = _("rotate left by 90 degrees"),
event = "Rotate", args = -90 }, event = "Rotate", args = -90 },
RotateRight = { RotateRight = {
{"K"}, {"K"},
doc = _("rotate right by 90 degrees"), doc = _("rotate right by 90 degrees"),
event = "Rotate", args = 90 }, event = "Rotate", args = 90 },
} }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
RotateGes = { RotateGes = {
GestureRange:new{ GestureRange:new{
ges = "rotate", ges = "rotate",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
}, },
TwoFingerPanRelease = { TwoFingerPanRelease = {
GestureRange:new{ GestureRange:new{
ges = "two_finger_pan_release", ges = "two_finger_pan_release",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
} }
} }
end end
end end
-- TODO: reset rotation on new document, maybe on new page? -- TODO: reset rotation on new document, maybe on new page?
function ReaderRotation:onRotate(rotate_by) function ReaderRotation:onRotate(rotate_by)
self.current_rotation = (self.current_rotation + rotate_by) % 360 self.current_rotation = (self.current_rotation + rotate_by) % 360
self.ui:handleEvent(Event:new("RotationUpdate", self.current_rotation)) self.ui:handleEvent(Event:new("RotationUpdate", self.current_rotation))
return true return true
end end
function ReaderRotation:onRotateGes(arg, ges) function ReaderRotation:onRotateGes(arg, ges)
self.rotate_angle = ges.angle self.rotate_angle = ges.angle
return true return true
end end
function ReaderRotation:onTwoFingerPanRelease(arg, ges) function ReaderRotation:onTwoFingerPanRelease(arg, ges)
if self.rotate_angle and self.rotate_angle > self.ROTATE_ANGLE_THRESHOLD then if self.rotate_angle and self.rotate_angle > self.ROTATE_ANGLE_THRESHOLD then
if Screen:getScreenMode() == "portrait" then if Screen:getScreenMode() == "portrait" then
self.ui:handleEvent(Event:new("SetScreenMode", "landscape")) self.ui:handleEvent(Event:new("SetScreenMode", "landscape"))
else else
self.ui:handleEvent(Event:new("SetScreenMode", "portrait")) self.ui:handleEvent(Event:new("SetScreenMode", "portrait"))
end end
self.rotate_angle = nil self.rotate_angle = nil
end end
end end
return ReaderRotation return ReaderRotation

@ -10,36 +10,36 @@ local DEBUG = require("dbg")
local ReaderScreenshot = InputContainer:new{} local ReaderScreenshot = InputContainer:new{}
function ReaderScreenshot:init() function ReaderScreenshot:init()
local diagonal = math.sqrt( local diagonal = math.sqrt(
math.pow(Screen:getWidth(), 2) + math.pow(Screen:getWidth(), 2) +
math.pow(Screen:getHeight(), 2) math.pow(Screen:getHeight(), 2)
) )
self.ges_events = { self.ges_events = {
Screenshot = { Screenshot = {
GestureRange:new{ GestureRange:new{
ges = "two_finger_tap", ges = "two_finger_tap",
scale = {diagonal - Screen:scaleByDPI(100), diagonal}, scale = {diagonal - Screen:scaleByDPI(100), diagonal},
rate = 1.0, rate = 1.0,
} }
}, },
} }
end end
function ReaderScreenshot:onScreenshot() function ReaderScreenshot:onScreenshot()
if Device:getModel() ~= 'Kobo_phoenix' then if Device:getModel() ~= 'Kobo_phoenix' then
os.execute("screenshot") os.execute("screenshot")
else Screen.bb:invert() else Screen.bb:invert()
local screenshot_name = os.date("screenshots/Screenshot_%Y-%B-%d_%Hh%M.pam") local screenshot_name = os.date("screenshots/Screenshot_%Y-%B-%d_%Hh%M.pam")
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = _("Writing screen to ")..screenshot_name, text = _("Writing screen to ")..screenshot_name,
timeout = 2, timeout = 2,
}) })
Screen.bb:writePAM(screenshot_name) Screen.bb:writePAM(screenshot_name)
DEBUG(screenshot_name) DEBUG(screenshot_name)
Screen.bb:invert() Screen.bb:invert()
end end
UIManager.full_refresh = true UIManager.full_refresh = true
return true return true
end end
return ReaderScreenshot return ReaderScreenshot

@ -10,135 +10,135 @@ local Event = require("ui/event")
local _ = require("gettext") local _ = require("gettext")
local ReaderToc = InputContainer:new{ local ReaderToc = InputContainer:new{
toc = nil, toc = nil,
toc_menu_title = _("Table of contents"), toc_menu_title = _("Table of contents"),
} }
function ReaderToc:init() function ReaderToc:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ShowToc = { ShowToc = {
{ "T" }, { "T" },
doc = _("show Table of Content menu") }, doc = _("show Table of Content menu") },
} }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
ShowToc = { ShowToc = {
GestureRange:new{ GestureRange:new{
ges = "two_finger_swipe", ges = "two_finger_swipe",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
}, },
direction = "east" direction = "east"
} }
}, },
} }
end end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderToc:cleanUpTocTitle(title) function ReaderToc:cleanUpTocTitle(title)
return (title:gsub("\13", "")) return (title:gsub("\13", ""))
end end
function ReaderToc:onSetDimensions(dimen) function ReaderToc:onSetDimensions(dimen)
self.dimen = dimen self.dimen = dimen
end end
function ReaderToc:onUpdateToc() function ReaderToc:onUpdateToc()
self.toc = nil self.toc = nil
return true return true
end end
function ReaderToc:fillToc() function ReaderToc:fillToc()
self.toc = self.ui.document:getToc() self.toc = self.ui.document:getToc()
end end
-- _getTocTitleByPage wrapper, so specific reader -- _getTocTitleByPage wrapper, so specific reader
-- can tranform pageno according its need -- can tranform pageno according its need
function ReaderToc:getTocTitleByPage(pn_or_xp) function ReaderToc:getTocTitleByPage(pn_or_xp)
local page = pn_or_xp local page = pn_or_xp
if type(pn_or_xp) == "string" then if type(pn_or_xp) == "string" then
page = self.ui.document:getPageFromXPointer(pn_or_xp) page = self.ui.document:getPageFromXPointer(pn_or_xp)
end end
return self:_getTocTitleByPage(page) return self:_getTocTitleByPage(page)
end end
function ReaderToc:_getTocTitleByPage(pageno) function ReaderToc:_getTocTitleByPage(pageno)
if not self.toc then if not self.toc then
-- build toc when needed. -- build toc when needed.
self:fillToc() self:fillToc()
end end
-- no table of content -- no table of content
if #self.toc == 0 then if #self.toc == 0 then
return "" return ""
end end
local pre_entry = self.toc[1] local pre_entry = self.toc[1]
for _k,_v in ipairs(self.toc) do for _k,_v in ipairs(self.toc) do
if _v.page > pageno then if _v.page > pageno then
break break
end end
pre_entry = _v pre_entry = _v
end end
return self:cleanUpTocTitle(pre_entry.title) return self:cleanUpTocTitle(pre_entry.title)
end end
function ReaderToc:getTocTitleOfCurrentPage() function ReaderToc:getTocTitleOfCurrentPage()
return self:getTocTitleByPage(self.pageno) return self:getTocTitleByPage(self.pageno)
end end
function ReaderToc:onShowToc() function ReaderToc:onShowToc()
if not self.toc then if not self.toc then
self:fillToc() self:fillToc()
end end
-- build menu items -- build menu items
if #self.toc > 0 and not self.toc[1].text then if #self.toc > 0 and not self.toc[1].text then
for _,v in ipairs(self.toc) do for _,v in ipairs(self.toc) do
v.text = (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title) v.text = (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title)
v.mandatory = v.page v.mandatory = v.page
end end
end end
local menu_container = CenterContainer:new{ local menu_container = CenterContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
} }
local toc_menu = Menu:new{ local toc_menu = Menu:new{
title = _("Table of Contents"), title = _("Table of Contents"),
item_table = self.toc, item_table = self.toc,
ui = self.ui, ui = self.ui,
width = Screen:getWidth()-50, width = Screen:getWidth()-50,
height = Screen:getHeight()-50, height = Screen:getHeight()-50,
show_parent = menu_container, show_parent = menu_container,
} }
table.insert(menu_container, toc_menu) table.insert(menu_container, toc_menu)
function toc_menu:onMenuChoice(item) function toc_menu:onMenuChoice(item)
self.ui:handleEvent(Event:new("PageUpdate", item.page)) self.ui:handleEvent(Event:new("PageUpdate", item.page))
end end
toc_menu.close_callback = function() toc_menu.close_callback = function()
UIManager:close(menu_container) UIManager:close(menu_container)
end end
UIManager:show(menu_container) UIManager:show(menu_container)
return true return true
end end
function ReaderToc:addToMainMenu(tab_item_table) function ReaderToc:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.navi, { table.insert(tab_item_table.navi, {
text = self.toc_menu_title, text = self.toc_menu_title,
callback = function() callback = function()
self:onShowToc() self:onShowToc()
end, end,
}) })
end end
return ReaderToc return ReaderToc

@ -6,121 +6,121 @@ local _ = require("gettext")
-- lfs -- lfs
local ReaderTypeset = InputContainer:new{ local ReaderTypeset = InputContainer:new{
css_menu_title = _("Set render style"), css_menu_title = _("Set render style"),
css = nil, css = nil,
internal_css = true, internal_css = true,
} }
function ReaderTypeset:init() function ReaderTypeset:init()
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderTypeset:onReadSettings(config) function ReaderTypeset:onReadSettings(config)
self.css = config:readSetting("css") self.css = config:readSetting("css")
if self.css and self.css ~= "" then if self.css and self.css ~= "" then
self.ui.document:setStyleSheet(self.css) self.ui.document:setStyleSheet(self.css)
else else
self.ui.document:setStyleSheet(self.ui.document.default_css) self.ui.document:setStyleSheet(self.ui.document.default_css)
self.css = self.ui.document.default_css self.css = self.ui.document.default_css
end end
-- default to enable embedded css -- default to enable embedded css
self.embedded_css = config:readSetting("embedded_css") or true self.embedded_css = config:readSetting("embedded_css") or true
self.ui.document:setEmbeddedStyleSheet(self.embedded_css and 1 or 0) self.ui.document:setEmbeddedStyleSheet(self.embedded_css and 1 or 0)
-- set page margins -- set page margins
self:onSetPageMargins(config:readSetting("copt_page_margins") or DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM) self:onSetPageMargins(config:readSetting("copt_page_margins") or DCREREADER_CONFIG_MARGIN_SIZES_MEDIUM)
end end
function ReaderTypeset:onSaveSettings() function ReaderTypeset:onSaveSettings()
self.ui.doc_settings:saveSetting("css", self.css) self.ui.doc_settings:saveSetting("css", self.css)
self.ui.doc_settings:saveSetting("embedded_css", self.embedded_css) self.ui.doc_settings:saveSetting("embedded_css", self.embedded_css)
end end
function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle) function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)
self:toggleEmbeddedStyleSheet(toggle) self:toggleEmbeddedStyleSheet(toggle)
return true return true
end end
function ReaderTypeset:genStyleSheetMenu() function ReaderTypeset:genStyleSheetMenu()
local file_list = { local file_list = {
{ {
text = _("clear all external styles"), text = _("clear all external styles"),
callback = function() callback = function()
self:setStyleSheet(nil) self:setStyleSheet(nil)
end end
}, },
{ {
text = _("Auto"), text = _("Auto"),
callback = function() callback = function()
self:setStyleSheet(self.ui.document.default_css) self:setStyleSheet(self.ui.document.default_css)
end end
}, },
} }
for f in lfs.dir("./data") do for f in lfs.dir("./data") do
if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then if lfs.attributes("./data/"..f, "mode") == "file" and string.match(f, "%.css$") then
table.insert(file_list, { table.insert(file_list, {
text = f, text = f,
callback = function() callback = function()
self:setStyleSheet("./data/"..f) self:setStyleSheet("./data/"..f)
end end
}) })
end end
end end
return file_list return file_list
end end
function ReaderTypeset:setStyleSheet(new_css) function ReaderTypeset:setStyleSheet(new_css)
if new_css ~= self.css then if new_css ~= self.css then
--DEBUG("setting css to ", new_css) --DEBUG("setting css to ", new_css)
self.css = new_css self.css = new_css
if new_css == nil then if new_css == nil then
new_css = "" new_css = ""
end end
self.ui.document:setStyleSheet(new_css) self.ui.document:setStyleSheet(new_css)
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
end end
end end
function ReaderTypeset:setEmbededStyleSheetOnly() function ReaderTypeset:setEmbededStyleSheetOnly()
if self.css ~= nil then if self.css ~= nil then
-- clear applied css -- clear applied css
self.ui.document:setStyleSheet("") self.ui.document:setStyleSheet("")
self.ui.document:setEmbeddedStyleSheet(1) self.ui.document:setEmbeddedStyleSheet(1)
self.css = nil self.css = nil
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
end end
end end
function ReaderTypeset:toggleEmbeddedStyleSheet(toggle) function ReaderTypeset:toggleEmbeddedStyleSheet(toggle)
if not toggle then if not toggle then
self.embedded_css = false self.embedded_css = false
self:setStyleSheet(self.ui.document.default_css) self:setStyleSheet(self.ui.document.default_css)
self.ui.document:setEmbeddedStyleSheet(0) self.ui.document:setEmbeddedStyleSheet(0)
else else
self.embedded_css = true self.embedded_css = true
--self:setStyleSheet(self.ui.document.default_css) --self:setStyleSheet(self.ui.document.default_css)
self.ui.document:setEmbeddedStyleSheet(1) self.ui.document:setEmbeddedStyleSheet(1)
end end
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
end end
function ReaderTypeset:addToMainMenu(tab_item_table) function ReaderTypeset:addToMainMenu(tab_item_table)
-- insert table to main reader menu -- insert table to main reader menu
table.insert(tab_item_table.typeset, { table.insert(tab_item_table.typeset, {
text = self.css_menu_title, text = self.css_menu_title,
sub_item_table = self:genStyleSheetMenu(), sub_item_table = self:genStyleSheetMenu(),
}) })
end end
function ReaderTypeset:onSetPageMargins(margins) function ReaderTypeset:onSetPageMargins(margins)
local left = Screen:scaleByDPI(margins[1]) local left = Screen:scaleByDPI(margins[1])
local top = Screen:scaleByDPI(margins[2]) local top = Screen:scaleByDPI(margins[2])
local right = Screen:scaleByDPI(margins[3]) local right = Screen:scaleByDPI(margins[3])
local bottom = Screen:scaleByDPI(margins[4]) local bottom = Screen:scaleByDPI(margins[4])
self.ui.document:setPageMargins(left, top, right, bottom) self.ui.document:setPageMargins(left, top, right, bottom)
self.ui:handleEvent(Event:new("UpdatePos")) self.ui:handleEvent(Event:new("UpdatePos"))
return true return true
end end
return ReaderTypeset return ReaderTypeset

File diff suppressed because it is too large Load Diff

@ -9,315 +9,315 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ReaderZooming = InputContainer:new{ local ReaderZooming = InputContainer:new{
zoom = 1.0, zoom = 1.0,
-- default to nil so we can trigger ZoomModeUpdate events on start up -- default to nil so we can trigger ZoomModeUpdate events on start up
zoom_mode = nil, zoom_mode = nil,
DEFAULT_ZOOM_MODE = "page", DEFAULT_ZOOM_MODE = "page",
current_page = 1, current_page = 1,
rotation = 0 rotation = 0
} }
function ReaderZooming:init() function ReaderZooming:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
ZoomIn = { ZoomIn = {
{ "Shift", Input.group.PgFwd }, { "Shift", Input.group.PgFwd },
doc = _("zoom in"), doc = _("zoom in"),
event = "Zoom", args = "in" event = "Zoom", args = "in"
}, },
ZoomOut = { ZoomOut = {
{ "Shift", Input.group.PgBack }, { "Shift", Input.group.PgBack },
doc = _("zoom out"), doc = _("zoom out"),
event = "Zoom", args = "out" event = "Zoom", args = "out"
}, },
ZoomToFitPage = { ZoomToFitPage = {
{ "A" }, { "A" },
doc = _("zoom to fit page"), doc = _("zoom to fit page"),
event = "SetZoomMode", args = "page" event = "SetZoomMode", args = "page"
}, },
ZoomToFitContent = { ZoomToFitContent = {
{ "Shift", "A" }, { "Shift", "A" },
doc = _("zoom to fit content"), doc = _("zoom to fit content"),
event = "SetZoomMode", args = "content" event = "SetZoomMode", args = "content"
}, },
ZoomToFitPageWidth = { ZoomToFitPageWidth = {
{ "S" }, { "S" },
doc = _("zoom to fit page width"), doc = _("zoom to fit page width"),
event = "SetZoomMode", args = "pagewidth" event = "SetZoomMode", args = "pagewidth"
}, },
ZoomToFitContentWidth = { ZoomToFitContentWidth = {
{ "Shift", "S" }, { "Shift", "S" },
doc = _("zoom to fit content width"), doc = _("zoom to fit content width"),
event = "SetZoomMode", args = "contentwidth" event = "SetZoomMode", args = "contentwidth"
}, },
ZoomToFitPageHeight = { ZoomToFitPageHeight = {
{ "D" }, { "D" },
doc = _("zoom to fit page height"), doc = _("zoom to fit page height"),
event = "SetZoomMode", args = "pageheight" event = "SetZoomMode", args = "pageheight"
}, },
ZoomToFitContentHeight = { ZoomToFitContentHeight = {
{ "Shift", "D" }, { "Shift", "D" },
doc = _("zoom to fit content height"), doc = _("zoom to fit content height"),
event = "SetZoomMode", args = "contentheight" event = "SetZoomMode", args = "contentheight"
}, },
} }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
Spread = { Spread = {
GestureRange:new{ GestureRange:new{
ges = "spread", ges = "spread",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
}, },
Pinch = { Pinch = {
GestureRange:new{ GestureRange:new{
ges = "pinch", ges = "pinch",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
}, },
ToggleFreeZoom = { ToggleFreeZoom = {
GestureRange:new{ GestureRange:new{
ges = "double_tap", ges = "double_tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
}, },
} }
end end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function ReaderZooming:onReadSettings(config) function ReaderZooming:onReadSettings(config)
-- @TODO config file from old code base uses globalzoom_mode -- @TODO config file from old code base uses globalzoom_mode
-- instead of zoom_mode, we need to handle this imcompatibility -- instead of zoom_mode, we need to handle this imcompatibility
-- 04.12 2012 (houqp) -- 04.12 2012 (houqp)
local zoom_mode = config:readSetting("zoom_mode") local zoom_mode = config:readSetting("zoom_mode")
if not zoom_mode then if not zoom_mode then
zoom_mode = self.DEFAULT_ZOOM_MODE zoom_mode = self.DEFAULT_ZOOM_MODE
end end
self:setZoomMode(zoom_mode) self:setZoomMode(zoom_mode)
end end
function ReaderZooming:onSaveSettings() function ReaderZooming:onSaveSettings()
self.ui.doc_settings:saveSetting("zoom_mode", self.zoom_mode) self.ui.doc_settings:saveSetting("zoom_mode", self.zoom_mode)
end end
function ReaderZooming:onSpread(arg, ges) function ReaderZooming:onSpread(arg, ges)
if ges.direction == "horizontal" then if ges.direction == "horizontal" then
self:genSetZoomModeCallBack("contentwidth")() self:genSetZoomModeCallBack("contentwidth")()
elseif ges.direction == "vertical" then elseif ges.direction == "vertical" then
self:genSetZoomModeCallBack("contentheight")() self:genSetZoomModeCallBack("contentheight")()
elseif ges.direction == "diagonal" then elseif ges.direction == "diagonal" then
self:genSetZoomModeCallBack("content")() self:genSetZoomModeCallBack("content")()
end end
return true return true
end end
function ReaderZooming:onPinch(arg, ges) function ReaderZooming:onPinch(arg, ges)
if ges.direction == "diagonal" then if ges.direction == "diagonal" then
self:genSetZoomModeCallBack("page")() self:genSetZoomModeCallBack("page")()
elseif ges.direction == "horizontal" then elseif ges.direction == "horizontal" then
self:genSetZoomModeCallBack("pagewidth")() self:genSetZoomModeCallBack("pagewidth")()
elseif ges.direction == "vertical" then elseif ges.direction == "vertical" then
self:genSetZoomModeCallBack("pageheight")() self:genSetZoomModeCallBack("pageheight")()
end end
return true return true
end end
function ReaderZooming:onToggleFreeZoom(arg, ges) function ReaderZooming:onToggleFreeZoom(arg, ges)
if self.zoom_mode ~= "free" then if self.zoom_mode ~= "free" then
self.orig_zoom = self.zoom self.orig_zoom = self.zoom
self.orig_zoom_mode = self.zoom_mode self.orig_zoom_mode = self.zoom_mode
local xpos, ypos local xpos, ypos
self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos) self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos)
DEBUG("zoom center", self.zoom, xpos, ypos) DEBUG("zoom center", self.zoom, xpos, ypos)
self.ui:handleEvent(Event:new("SetZoomMode", "free")) self.ui:handleEvent(Event:new("SetZoomMode", "free"))
if xpos == nil or ypos == nil then if xpos == nil or ypos == nil then
xpos = ges.pos.x * self.zoom / self.orig_zoom xpos = ges.pos.x * self.zoom / self.orig_zoom
ypos = ges.pos.y * self.zoom / self.orig_zoom ypos = ges.pos.y * self.zoom / self.orig_zoom
end end
self.view:SetZoomCenter(xpos, ypos) self.view:SetZoomCenter(xpos, ypos)
else else
self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode or "page")) self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode or "page"))
end end
end end
function ReaderZooming:onSetDimensions(dimensions) function ReaderZooming:onSetDimensions(dimensions)
-- we were resized -- we were resized
self.dimen = dimensions self.dimen = dimensions
self:setZoom() self:setZoom()
end end
function ReaderZooming:onRestoreDimensions(dimensions) function ReaderZooming:onRestoreDimensions(dimensions)
-- we were resized -- we were resized
self.dimen = dimensions self.dimen = dimensions
self:setZoom() self:setZoom()
end end
function ReaderZooming:onRotationUpdate(rotation) function ReaderZooming:onRotationUpdate(rotation)
self.rotation = rotation self.rotation = rotation
self:setZoom() self:setZoom()
end end
function ReaderZooming:onZoom(direction) function ReaderZooming:onZoom(direction)
DEBUG("zoom", direction) DEBUG("zoom", direction)
if direction == "in" then if direction == "in" then
self.zoom = self.zoom * 1.333333 self.zoom = self.zoom * 1.333333
elseif direction == "out" then elseif direction == "out" then
self.zoom = self.zoom * 0.75 self.zoom = self.zoom * 0.75
end end
DEBUG("zoom is now at", self.zoom) DEBUG("zoom is now at", self.zoom)
self:onSetZoomMode("free") self:onSetZoomMode("free")
self.view:onZoomUpdate(self.zoom) self.view:onZoomUpdate(self.zoom)
return true return true
end end
function ReaderZooming:onSetZoomMode(new_mode) function ReaderZooming:onSetZoomMode(new_mode)
self.view.zoom_mode = new_mode self.view.zoom_mode = new_mode
if self.zoom_mode ~= new_mode then if self.zoom_mode ~= new_mode then
DEBUG("setting zoom mode to", new_mode) DEBUG("setting zoom mode to", new_mode)
self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode)) self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode))
self.zoom_mode = new_mode self.zoom_mode = new_mode
self:setZoom() self:setZoom()
end end
end end
function ReaderZooming:onPageUpdate(new_page_no) function ReaderZooming:onPageUpdate(new_page_no)
self.current_page = new_page_no self.current_page = new_page_no
self:setZoom() self:setZoom()
end end
function ReaderZooming:onReZoom() function ReaderZooming:onReZoom()
self:setZoom() self:setZoom()
self.ui:handleEvent(Event:new("InitScrollPageStates")) self.ui:handleEvent(Event:new("InitScrollPageStates"))
return true return true
end end
function ReaderZooming:getZoom(pageno) function ReaderZooming:getZoom(pageno)
-- check if we're in bbox mode and work on bbox if that's the case -- check if we're in bbox mode and work on bbox if that's the case
local zoom = nil local zoom = nil
local page_size = {} local page_size = {}
if self.zoom_mode == "content" if self.zoom_mode == "content"
or self.zoom_mode == "contentwidth" or self.zoom_mode == "contentwidth"
or self.zoom_mode == "contentheight" then or self.zoom_mode == "contentheight" then
local ubbox_dimen = self.ui.document:getUsedBBoxDimensions(pageno, 1) local ubbox_dimen = self.ui.document:getUsedBBoxDimensions(pageno, 1)
--self.view:handleEvent(Event:new("BBoxUpdate", page_size)) --self.view:handleEvent(Event:new("BBoxUpdate", page_size))
self.view:onBBoxUpdate(ubbox_dimen) self.view:onBBoxUpdate(ubbox_dimen)
page_size = ubbox_dimen page_size = ubbox_dimen
else else
-- otherwise, operate on full page -- otherwise, operate on full page
self.view:onBBoxUpdate(nil) self.view:onBBoxUpdate(nil)
page_size = self.ui.document:getNativePageDimensions(pageno) page_size = self.ui.document:getNativePageDimensions(pageno)
--page_size = self.ui.document:getPageDimensions(pageno, 1, 0) --page_size = self.ui.document:getPageDimensions(pageno, 1, 0)
end end
-- calculate zoom value: -- calculate zoom value:
local zoom_w = self.dimen.w / page_size.w local zoom_w = self.dimen.w / page_size.w
local zoom_h = self.dimen.h / page_size.h local zoom_h = self.dimen.h / page_size.h
if self.rotation % 180 ~= 0 then if self.rotation % 180 ~= 0 then
-- rotated by 90 or 270 degrees -- rotated by 90 or 270 degrees
zoom_w = self.dimen.w / page_size.h zoom_w = self.dimen.w / page_size.h
zoom_h = self.dimen.h / page_size.w zoom_h = self.dimen.h / page_size.w
end end
if self.zoom_mode == "content" or self.zoom_mode == "page" then if self.zoom_mode == "content" or self.zoom_mode == "page" then
if zoom_w < zoom_h then if zoom_w < zoom_h then
zoom = zoom_w zoom = zoom_w
else else
zoom = zoom_h zoom = zoom_h
end end
elseif self.zoom_mode == "contentwidth" or self.zoom_mode == "pagewidth" then elseif self.zoom_mode == "contentwidth" or self.zoom_mode == "pagewidth" then
zoom = zoom_w zoom = zoom_w
elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then
zoom = zoom_h zoom = zoom_h
elseif self.zoom_mode == "free" then elseif self.zoom_mode == "free" then
zoom = self.zoom zoom = self.zoom
end end
return zoom return zoom
end end
function ReaderZooming:getRegionalZoomCenter(pageno, pos) function ReaderZooming:getRegionalZoomCenter(pageno, pos)
local p_pos = self.view:getSinglePagePosition(pos) local p_pos = self.view:getSinglePagePosition(pos)
local page_size = self.ui.document:getNativePageDimensions(pageno) local page_size = self.ui.document:getNativePageDimensions(pageno)
local pos_x = p_pos.x / page_size.w / p_pos.zoom 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 pos_y = p_pos.y / page_size.h / p_pos.zoom
local regions = self.ui.document:getPageRegions(pageno) local regions = self.ui.document:getPageRegions(pageno)
DEBUG("get page regions", regions) DEBUG("get page regions", regions)
local margin = self.ui.document.configurable.page_margin * Screen:getDPI() local margin = self.ui.document.configurable.page_margin * Screen:getDPI()
for i = 1, #regions do for i = 1, #regions do
if regions[i].x0 <= pos_x and pos_x <= regions[i].x1 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 and regions[i].y0 <= pos_y and pos_y <= regions[i].y1 then
local zoom = 1/(regions[i].x1 - regions[i].x0) local zoom = 1/(regions[i].x1 - regions[i].x0)
zoom = zoom/(1 + 3*margin/zoom/page_size.w) zoom = zoom/(1 + 3*margin/zoom/page_size.w)
local xpos = (regions[i].x0 + regions[i].x1)/2 * 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 local ypos = p_pos.y / p_pos.zoom * zoom
return zoom, xpos, ypos return zoom, xpos, ypos
end end
end end
return 2 return 2
end end
function ReaderZooming:setZoom() function ReaderZooming:setZoom()
if not self.dimen then if not self.dimen then
self.dimen = self.ui.dimen self.dimen = self.ui.dimen
end end
self.zoom = self:getZoom(self.current_page) self.zoom = self:getZoom(self.current_page)
self.ui:handleEvent(Event:new("ZoomUpdate", self.zoom)) self.ui:handleEvent(Event:new("ZoomUpdate", self.zoom))
end end
function ReaderZooming:genSetZoomModeCallBack(mode) function ReaderZooming:genSetZoomModeCallBack(mode)
return function() return function()
self:setZoomMode(mode) self:setZoomMode(mode)
end end
end end
function ReaderZooming:setZoomMode(mode) function ReaderZooming:setZoomMode(mode)
self.ui:handleEvent(Event:new("SetZoomMode", mode)) self.ui:handleEvent(Event:new("SetZoomMode", mode))
self.ui:handleEvent(Event:new("InitScrollPageStates")) self.ui:handleEvent(Event:new("InitScrollPageStates"))
end end
function ReaderZooming:addToMainMenu(tab_item_table) function ReaderZooming:addToMainMenu(tab_item_table)
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
table.insert(tab_item_table.typeset, { table.insert(tab_item_table.typeset, {
text = _("Switch zoom mode"), text = _("Switch zoom mode"),
sub_item_table = { sub_item_table = {
{ {
text = _("Zoom to fit content width"), text = _("Zoom to fit content width"),
callback = self:genSetZoomModeCallBack("contentwidth") callback = self:genSetZoomModeCallBack("contentwidth")
}, },
{ {
text = _("Zoom to fit content height"), text = _("Zoom to fit content height"),
callback = self:genSetZoomModeCallBack("contentheight") callback = self:genSetZoomModeCallBack("contentheight")
}, },
{ {
text = _("Zoom to fit page width"), text = _("Zoom to fit page width"),
callback = self:genSetZoomModeCallBack("pagewidth") callback = self:genSetZoomModeCallBack("pagewidth")
}, },
{ {
text = _("Zoom to fit page height"), text = _("Zoom to fit page height"),
callback = self:genSetZoomModeCallBack("pageheight") callback = self:genSetZoomModeCallBack("pageheight")
}, },
{ {
text = _("Zoom to fit content"), text = _("Zoom to fit content"),
callback = self:genSetZoomModeCallBack("content") callback = self:genSetZoomModeCallBack("content")
}, },
{ {
text = _("Zoom to fit page"), text = _("Zoom to fit page"),
callback = self:genSetZoomModeCallBack("page") callback = self:genSetZoomModeCallBack("page")
}, },
} }
}) })
end end
end end
return ReaderZooming return ReaderZooming

@ -39,256 +39,256 @@ it works using data gathered from a document interface
]]-- ]]--
local ReaderUI = InputContainer:new{ local ReaderUI = InputContainer:new{
key_events = { key_events = {
Close = { { "Home" }, Close = { { "Home" },
doc = _("close document"), event = "Close" }, doc = _("close document"), event = "Close" },
}, },
active_widgets = {}, active_widgets = {},
-- our own size -- our own size
dimen = Geom:new{ w = 400, h = 600 }, dimen = Geom:new{ w = 400, h = 600 },
-- if we have a parent container, it must be referenced for now -- if we have a parent container, it must be referenced for now
dialog = nil, dialog = nil,
-- the document interface -- the document interface
document = nil, document = nil,
-- initial page or percent inside document on opening -- initial page or percent inside document on opening
start_pos = nil, start_pos = nil,
-- password for document unlock -- password for document unlock
password = nil, password = nil,
postInitCallback = nil, postInitCallback = nil,
} }
function ReaderUI:init() function ReaderUI:init()
self.postInitCallback = {} self.postInitCallback = {}
-- if we are not the top level dialog ourselves, it must be given in the table -- if we are not the top level dialog ourselves, it must be given in the table
if not self.dialog then if not self.dialog then
self.dialog = self self.dialog = self
end end
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events.Back = { self.key_events.Back = {
{ "Back" }, doc = _("close document"), { "Back" }, doc = _("close document"),
event = "Close" } event = "Close" }
end 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!) -- a view container (so it must be child #1!)
self[1] = ReaderView:new{ self[1] = ReaderView:new{
dialog = self.dialog, dialog = self.dialog,
dimen = self.dimen, dimen = self.dimen,
ui = self, ui = self,
document = self.document, document = self.document,
} }
-- reader menu controller -- reader menu controller
-- hold reference to menu widget -- hold reference to menu widget
self.menu = ReaderMenu:new{ self.menu = ReaderMenu:new{
view = self[1], view = self[1],
ui = self ui = self
} }
-- link -- link
table.insert(self, ReaderLink:new{ table.insert(self, ReaderLink:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
-- text highlight -- text highlight
table.insert(self, ReaderHighlight:new{ table.insert(self, ReaderHighlight:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
-- menu widget should be registered after link widget and highlight widget -- menu widget should be registered after link widget and highlight widget
-- so that taps on link and highlight areas won't popup reader menu -- so that taps on link and highlight areas won't popup reader menu
table.insert(self, self.menu) table.insert(self, self.menu)
-- rotation controller -- rotation controller
table.insert(self, ReaderRotation:new{ table.insert(self, ReaderRotation:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
-- Table of content controller -- Table of content controller
-- hold reference to bm widget -- hold reference to bm widget
self.toc = ReaderToc:new{ self.toc = ReaderToc:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
} }
table.insert(self, self.toc) table.insert(self, self.toc)
-- bookmark controller -- bookmark controller
table.insert(self, ReaderBookmark:new{ table.insert(self, ReaderBookmark:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
-- reader goto controller -- reader goto controller
table.insert(self, ReaderGoto:new{ table.insert(self, ReaderGoto:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
-- dictionary -- dictionary
table.insert(self, ReaderDictionary:new{ table.insert(self, ReaderDictionary:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
-- screenshot controller -- screenshot controller
table.insert(self.active_widgets, ReaderScreenshot:new{ table.insert(self.active_widgets, ReaderScreenshot:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
-- frontlight controller -- frontlight controller
if Device:hasFrontlight() then if Device:hasFrontlight() then
table.insert(self, ReaderFrontLight:new{ table.insert(self, ReaderFrontLight:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
end end
-- configuable controller -- configuable controller
if self.document.info.configurable then if self.document.info.configurable then
-- config panel controller -- config panel controller
table.insert(self, ReaderConfig:new{ table.insert(self, ReaderConfig:new{
configurable = self.document.configurable, configurable = self.document.configurable,
options = self.document.options, options = self.document.options,
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
if not self.document.info.has_pages then if not self.document.info.has_pages then
-- cre option controller -- cre option controller
table.insert(self, ReaderCoptListener:new{ table.insert(self, ReaderCoptListener:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
end end
end end
-- for page specific controller -- for page specific controller
if self.document.info.has_pages then if self.document.info.has_pages then
-- cropping controller -- cropping controller
table.insert(self, ReaderCropping:new{ table.insert(self, ReaderCropping:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
-- paging controller -- paging controller
table.insert(self, ReaderPaging:new{ table.insert(self, ReaderPaging:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
-- zooming controller -- zooming controller
local zoom = ReaderZooming:new{ local zoom = ReaderZooming:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
} }
table.insert(self, zoom) table.insert(self, zoom)
-- panning controller -- panning controller
table.insert(self, ReaderPanning:new{ table.insert(self, ReaderPanning:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
-- hinting controller -- hinting controller
table.insert(self, ReaderHinting:new{ table.insert(self, ReaderHinting:new{
dialog = self.dialog, dialog = self.dialog,
zoom = zoom, zoom = zoom,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
else else
-- make sure we load document first before calling any callback -- make sure we load document first before calling any callback
table.insert(self.postInitCallback, function() table.insert(self.postInitCallback, function()
self.document:loadDocument() self.document:loadDocument()
end) end)
-- typeset controller -- typeset controller
table.insert(self, ReaderTypeset:new{ table.insert(self, ReaderTypeset:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
-- font menu -- font menu
self.font = ReaderFont:new{ self.font = ReaderFont:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
} }
table.insert(self, self.font) -- hold reference to font menu table.insert(self, self.font) -- hold reference to font menu
-- hyphenation menu -- hyphenation menu
self.hyphenation = ReaderHyphenation:new{ self.hyphenation = ReaderHyphenation:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
} }
table.insert(self, self.hyphenation) -- hold reference to hyphenation menu table.insert(self, self.hyphenation) -- hold reference to hyphenation menu
-- rolling controller -- rolling controller
table.insert(self, ReaderRolling:new{ table.insert(self, ReaderRolling:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self ui = self
}) })
end end
-- configuable controller -- configuable controller
if self.document.info.configurable then if self.document.info.configurable then
if self.document.info.has_pages then if self.document.info.has_pages then
-- kopt option controller -- kopt option controller
table.insert(self, ReaderKoptListener:new{ table.insert(self, ReaderKoptListener:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
end end
-- activity indicator -- activity indicator
table.insert(self, ReaderActivityIndicator:new{ table.insert(self, ReaderActivityIndicator:new{
dialog = self.dialog, dialog = self.dialog,
view = self[1], view = self[1],
ui = self, ui = self,
document = self.document, document = self.document,
}) })
end end
--DEBUG(self.doc_settings) --DEBUG(self.doc_settings)
-- we only read settings after all the widgets are initialized -- we only read settings after all the widgets are initialized
self:handleEvent(Event:new("ReadSettings", self.doc_settings)) self:handleEvent(Event:new("ReadSettings", self.doc_settings))
for _,v in ipairs(self.postInitCallback) do for _,v in ipairs(self.postInitCallback) do
v() v()
end end
end end
function ReaderUI:onSetDimensions(dimen) function ReaderUI:onSetDimensions(dimen)
self.dimen = dimen self.dimen = dimen
end end
function ReaderUI:saveSettings() function ReaderUI:saveSettings()
self:handleEvent(Event:new("SaveSettings")) self:handleEvent(Event:new("SaveSettings"))
self.doc_settings:flush() self.doc_settings:flush()
end end
function ReaderUI:onClose() function ReaderUI:onClose()
DEBUG("closing reader") DEBUG("closing reader")
self:saveSettings() self:saveSettings()
if self.document ~= nil then if self.document ~= nil then
self.document:close() self.document:close()
self.document = nil self.document = nil
self.start_pos = nil self.start_pos = nil
end end
UIManager:close(self.dialog) UIManager:close(self.dialog)
return true return true
end end
return ReaderUI return ReaderUI

@ -10,170 +10,170 @@ TODO: all these functions should probably be methods on Face objects
local RenderText = {} local RenderText = {}
local GlyphCache = Cache:new{ local GlyphCache = Cache:new{
max_memsize = 512*1024, max_memsize = 512*1024,
current_memsize = 0, current_memsize = 0,
cache = {}, cache = {},
-- this will hold the LRU order of the cache -- this will hold the LRU order of the cache
cache_order = {} cache_order = {}
} }
-- iterator over UTF8 encoded characters in a string -- iterator over UTF8 encoded characters in a string
local function utf8Chars(input) local function utf8Chars(input)
local function read_next_glyph(input, pos) local function read_next_glyph(input, pos)
if string.len(input) < pos then return nil end if string.len(input) < pos then return nil end
local value = string.byte(input, pos) local value = string.byte(input, pos)
if bit.band(value, 0x80) == 0 then if bit.band(value, 0x80) == 0 then
-- TODO: check valid ranges -- TODO: check valid ranges
return pos+1, value, string.sub(input, pos, pos) return pos+1, value, string.sub(input, pos, pos)
elseif bit.band(value, 0xC0) == 0x80 -- invalid, continuation elseif bit.band(value, 0xC0) == 0x80 -- invalid, continuation
or bit.band(value, 0xF8) == 0xF8 -- 5-or-more byte sequence, illegal due to RFC3629 or bit.band(value, 0xF8) == 0xF8 -- 5-or-more byte sequence, illegal due to RFC3629
then then
return pos+1, 0xFFFD, "\xFF\xFD" return pos+1, 0xFFFD, "\xFF\xFD"
else else
local glyph, bytes_left local glyph, bytes_left
if bit.band(value, 0xE0) == 0xC0 then if bit.band(value, 0xE0) == 0xC0 then
glyph = bit.band(value, 0x1F) glyph = bit.band(value, 0x1F)
bytes_left = 1 bytes_left = 1
elseif bit.band(value, 0xF0) == 0xE0 then elseif bit.band(value, 0xF0) == 0xE0 then
glyph = bit.band(value, 0x0F) glyph = bit.band(value, 0x0F)
bytes_left = 2 bytes_left = 2
elseif bit.band(value, 0xF8) == 0xF0 then elseif bit.band(value, 0xF8) == 0xF0 then
glyph = bit.band(value, 0x07) glyph = bit.band(value, 0x07)
bytes_left = 3 bytes_left = 3
else else
return pos+1, 0xFFFD, "\xFF\xFD" return pos+1, 0xFFFD, "\xFF\xFD"
end end
if string.len(input) < (pos + bytes_left - 1) then if string.len(input) < (pos + bytes_left - 1) then
return pos+1, 0xFFFD, "\xFF\xFD" return pos+1, 0xFFFD, "\xFF\xFD"
end end
for i = pos+1, pos + bytes_left do for i = pos+1, pos + bytes_left do
value = string.byte(input, i) value = string.byte(input, i)
if bit.band(value, 0xC0) == 0x80 then if bit.band(value, 0xC0) == 0x80 then
glyph = bit.bor(bit.lshift(glyph, 6), bit.band(value, 0x3F)) glyph = bit.bor(bit.lshift(glyph, 6), bit.band(value, 0x3F))
else else
return i+1, 0xFFFD, "\xFF\xFD" return i+1, 0xFFFD, "\xFF\xFD"
end end
end end
-- TODO: check for valid ranges here! -- TODO: check for valid ranges here!
return pos+bytes_left+1, glyph, string.sub(input, pos, pos+bytes_left) return pos+bytes_left+1, glyph, string.sub(input, pos, pos+bytes_left)
end end
end end
return read_next_glyph, input, 1 return read_next_glyph, input, 1
end end
function RenderText:getGlyph(face, charcode, bold, bgcolor, fgcolor) function RenderText:getGlyph(face, charcode, bold, bgcolor, fgcolor)
if bgcolor == nil then bgcolor = 0.0 end if bgcolor == nil then bgcolor = 0.0 end
if fgcolor == nil then fgcolor = 1.0 end if fgcolor == nil then fgcolor = 1.0 end
local hash = "glyph|"..face.hash.."|"..charcode.."|"..(bold and 1 or 0).."|"..bgcolor.."|"..fgcolor local hash = "glyph|"..face.hash.."|"..charcode.."|"..(bold and 1 or 0).."|"..bgcolor.."|"..fgcolor
local glyph = GlyphCache:check(hash) local glyph = GlyphCache:check(hash)
if glyph then if glyph then
-- cache hit -- cache hit
return glyph[1] return glyph[1]
end end
local rendered_glyph = face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold) local rendered_glyph = face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold)
if face.ftface:checkGlyph(charcode) == 0 then if face.ftface:checkGlyph(charcode) == 0 then
for index, font in pairs(Font.fallbacks) do for index, font in pairs(Font.fallbacks) do
-- rescale face size by DPI since it will be scaled in getFace again -- rescale face size by DPI since it will be scaled in getFace again
local fb_face = Font:getFace(font, Screen:rescaleByDPI(face.size)) local fb_face = Font:getFace(font, Screen:rescaleByDPI(face.size))
if fb_face.ftface:checkGlyph(charcode) ~= 0 then if fb_face.ftface:checkGlyph(charcode) ~= 0 then
rendered_glyph = fb_face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold) rendered_glyph = fb_face.ftface:renderGlyph(charcode, bgcolor, fgcolor, bold)
--DEBUG("fallback to font", font) --DEBUG("fallback to font", font)
break break
end end
end end
end end
if not rendered_glyph then if not rendered_glyph then
DEBUG("error rendering glyph (charcode=", charcode, ") for face", face) DEBUG("error rendering glyph (charcode=", charcode, ") for face", face)
return return
end end
glyph = CacheItem:new{rendered_glyph} glyph = CacheItem:new{rendered_glyph}
glyph.size = glyph[1].bb:getWidth() * glyph[1].bb:getHeight() / 2 + 32 glyph.size = glyph[1].bb:getWidth() * glyph[1].bb:getHeight() / 2 + 32
GlyphCache:insert(hash, glyph) GlyphCache:insert(hash, glyph)
return rendered_glyph return rendered_glyph
end end
function RenderText:getSubTextByWidth(text, face, width, kerning, bold) function RenderText:getSubTextByWidth(text, face, width, kerning, bold)
local pen_x = 0 local pen_x = 0
local prevcharcode = 0 local prevcharcode = 0
local char_list = {} local char_list = {}
for _, charcode, uchar in utf8Chars(text) do for _, charcode, uchar in utf8Chars(text) do
if pen_x < width then if pen_x < width then
local glyph = self:getGlyph(face, charcode, bold) local glyph = self:getGlyph(face, charcode, bold)
if kerning and prevcharcode then if kerning and prevcharcode then
local kern = face.ftface:getKerning(prevcharcode, charcode) local kern = face.ftface:getKerning(prevcharcode, charcode)
pen_x = pen_x + kern pen_x = pen_x + kern
end end
pen_x = pen_x + glyph.ax pen_x = pen_x + glyph.ax
if pen_x <= width then if pen_x <= width then
prevcharcode = charcode prevcharcode = charcode
table.insert(char_list, uchar) table.insert(char_list, uchar)
else else
break break
end end
end end
end end
return table.concat(char_list) return table.concat(char_list)
end end
function RenderText:sizeUtf8Text(x, width, face, text, kerning, bold) function RenderText:sizeUtf8Text(x, width, face, text, kerning, bold)
if not text then if not text then
DEBUG("sizeUtf8Text called without text"); DEBUG("sizeUtf8Text called without text");
return return
end end
-- may still need more adaptive pen placement when kerning, -- may still need more adaptive pen placement when kerning,
-- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html
local pen_x = 0 local pen_x = 0
local pen_y_top = 0 local pen_y_top = 0
local pen_y_bottom = 0 local pen_y_bottom = 0
local prevcharcode = 0 local prevcharcode = 0
for _, charcode, uchar in utf8Chars(text) do for _, charcode, uchar in utf8Chars(text) do
if pen_x < (width - x) then if pen_x < (width - x) then
local glyph = self:getGlyph(face, charcode, bold) local glyph = self:getGlyph(face, charcode, bold)
if kerning and (prevcharcode ~= 0) then if kerning and (prevcharcode ~= 0) then
pen_x = pen_x + (face.ftface):getKerning(prevcharcode, charcode) pen_x = pen_x + (face.ftface):getKerning(prevcharcode, charcode)
end end
pen_x = pen_x + glyph.ax pen_x = pen_x + glyph.ax
pen_y_top = math.max(pen_y_top, glyph.t) pen_y_top = math.max(pen_y_top, glyph.t)
pen_y_bottom = math.max(pen_y_bottom, glyph.bb:getHeight() - 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) --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 prevcharcode = charcode
end -- if pen_x < (width - x) end -- if pen_x < (width - x)
end end
return { x = pen_x, y_top = pen_y_top, y_bottom = pen_y_bottom} return { x = pen_x, y_top = pen_y_top, y_bottom = pen_y_bottom}
end end
function RenderText:renderUtf8Text(buffer, x, y, face, text, kerning, bold, bgcolor, fgcolor, width) function RenderText:renderUtf8Text(buffer, x, y, face, text, kerning, bold, bgcolor, fgcolor, width)
if not text then if not text then
DEBUG("renderUtf8Text called without text"); DEBUG("renderUtf8Text called without text");
return 0 return 0
end end
-- may still need more adaptive pen placement when kerning, -- may still need more adaptive pen placement when kerning,
-- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html
local pen_x = 0 local pen_x = 0
local prevcharcode = 0 local prevcharcode = 0
local text_width = buffer:getWidth() - x local text_width = buffer:getWidth() - x
if width and width < text_width then if width and width < text_width then
text_width = width text_width = width
end end
for _, charcode, uchar in utf8Chars(text) do for _, charcode, uchar in utf8Chars(text) do
if pen_x < text_width then if pen_x < text_width then
local glyph = self:getGlyph(face, charcode, bold, bgcolor, fgcolor) local glyph = self:getGlyph(face, charcode, bold, bgcolor, fgcolor)
if kerning and (prevcharcode ~= 0) then if kerning and (prevcharcode ~= 0) then
pen_x = pen_x + face.ftface:getKerning(prevcharcode, charcode) pen_x = pen_x + face.ftface:getKerning(prevcharcode, charcode)
end end
buffer:addblitFrom( buffer:addblitFrom(
glyph.bb, glyph.bb,
x + pen_x + glyph.l, y - glyph.t, x + pen_x + glyph.l, y - glyph.t,
0, 0, 0, 0,
glyph.bb:getWidth(), glyph.bb:getHeight(), 1) glyph.bb:getWidth(), glyph.bb:getHeight(), 1)
pen_x = pen_x + glyph.ax pen_x = pen_x + glyph.ax
prevcharcode = charcode prevcharcode = charcode
end -- if pen_x < text_width end -- if pen_x < text_width
end end
return pen_x return pen_x
end end
return RenderText return RenderText

@ -1,104 +1,104 @@
local TimeVal = { local TimeVal = {
sec = 0, sec = 0,
usec = 0, usec = 0,
} }
function TimeVal:new(o) function TimeVal:new(o)
local o = o or {} local o = o or {}
if o.sec == nil then if o.sec == nil then
o.sec = 0 o.sec = 0
end end
if o.usec == nil then if o.usec == nil then
o.usec = 0 o.usec = 0
elseif o.usec > 1000000 then elseif o.usec > 1000000 then
o.sec = o.sec + maht.floor(o.usec/1000000) o.sec = o.sec + maht.floor(o.usec/1000000)
o.usec = o.usec % 1000000 o.usec = o.usec % 1000000
end end
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function TimeVal:__lt(time_b) function TimeVal:__lt(time_b)
if self.sec < time_b.sec then if self.sec < time_b.sec then
return true return true
elseif self.sec > time_b.sec then elseif self.sec > time_b.sec then
return false return false
else else
-- self.sec == time_b.sec -- self.sec == time_b.sec
if self.usec < time_b.usec then if self.usec < time_b.usec then
return true return true
else else
return false return false
end end
end end
end end
function TimeVal:__le(time_b) function TimeVal:__le(time_b)
if self.sec < time_b.sec then if self.sec < time_b.sec then
return true return true
elseif self.sec > time_b.sec then elseif self.sec > time_b.sec then
return false return false
else else
-- self.sec == time_b.sec -- self.sec == time_b.sec
if self.usec > time_b.usec then if self.usec > time_b.usec then
return false return false
else else
return true return true
end end
end end
end end
function TimeVal:__eq(time_b) function TimeVal:__eq(time_b)
if self.sec == time_b.sec and self.usec == time_b.usec then if self.sec == time_b.sec and self.usec == time_b.usec then
return true return true
else else
return false return false
end end
end end
function TimeVal:__sub(time_b) function TimeVal:__sub(time_b)
local diff = TimeVal:new{} local diff = TimeVal:new{}
diff.sec = self.sec - time_b.sec diff.sec = self.sec - time_b.sec
diff.usec = self.usec - time_b.usec diff.usec = self.usec - time_b.usec
if diff.sec < 0 and diff.usec > 0 then if diff.sec < 0 and diff.usec > 0 then
diff.sec = diff.sec + 1 diff.sec = diff.sec + 1
diff.usec = diff.usec - 1000000 diff.usec = diff.usec - 1000000
elseif diff.sec > 0 and diff.usec < 0 then elseif diff.sec > 0 and diff.usec < 0 then
diff.sec = diff.sec - 1 diff.sec = diff.sec - 1
diff.usec = diff.usec + 1000000 diff.usec = diff.usec + 1000000
end end
return diff return diff
end end
function TimeVal:__add(time_b) function TimeVal:__add(time_b)
local sum = TimeVal:new{} local sum = TimeVal:new{}
sum.sec = self.sec + time_b.sec sum.sec = self.sec + time_b.sec
sum.usec = self.usec + time_b.usec sum.usec = self.usec + time_b.usec
if sum.usec > 1000000 then if sum.usec > 1000000 then
sum.usec = sum.usec - 1000000 sum.usec = sum.usec - 1000000
sum.sec = sum.sec + 1 sum.sec = sum.sec + 1
end end
if sum.sec < 0 and sum.usec > 0 then if sum.sec < 0 and sum.usec > 0 then
sum.sec = sum.sec + 1 sum.sec = sum.sec + 1
sum.usec = sum.usec - 1000000 sum.usec = sum.usec - 1000000
elseif sum.sec > 0 and sum.usec < 0 then elseif sum.sec > 0 and sum.usec < 0 then
sum.sec = sum.sec - 1 sum.sec = sum.sec - 1
sum.usec = sum.usec + 1000000 sum.usec = sum.usec + 1000000
end end
return sum return sum
end end
function TimeVal:now() function TimeVal:now()
local sec, usec = util.gettime() local sec, usec = util.gettime()
return TimeVal:new{sec = sec, usec = usec} return TimeVal:new{sec = sec, usec = usec}
end end
return TimeVal return TimeVal

@ -10,304 +10,304 @@ Screen:init()
-- initialize the input handling -- initialize the input handling
Input:init() Input:init()
local WAVEFORM_MODE_INIT = 0x0 -- Screen goes to white (clears) local WAVEFORM_MODE_INIT = 0x0 -- Screen goes to white (clears)
local WAVEFORM_MODE_DU = 0x1 -- Grey->white/grey->black local WAVEFORM_MODE_DU = 0x1 -- Grey->white/grey->black
local WAVEFORM_MODE_GC16 = 0x2 -- High fidelity (flashing) local WAVEFORM_MODE_GC16 = 0x2 -- High fidelity (flashing)
local WAVEFORM_MODE_GC4 = WAVEFORM_MODE_GC16 -- For compatibility local WAVEFORM_MODE_GC4 = WAVEFORM_MODE_GC16 -- For compatibility
local WAVEFORM_MODE_GC16_FAST = 0x3 -- Medium fidelity local WAVEFORM_MODE_GC16_FAST = 0x3 -- Medium fidelity
local WAVEFORM_MODE_A2 = 0x4 -- Faster but even lower 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 = 0x5 -- High fidelity from white transition
local WAVEFORM_MODE_GL16_FAST = 0x6 -- Medium fidelity from white transition local WAVEFORM_MODE_GL16_FAST = 0x6 -- Medium fidelity from white transition
local WAVEFORM_MODE_AUTO = 0x101 local WAVEFORM_MODE_AUTO = 0x101
-- there is only one instance of this -- there is only one instance of this
local UIManager = { local UIManager = {
default_refresh_type = 0, -- 0 for partial refresh, 1 for full refresh default_refresh_type = 0, -- 0 for partial refresh, 1 for full refresh
default_waveform_mode = WAVEFORM_MODE_GC16, -- high fidelity waveform default_waveform_mode = WAVEFORM_MODE_GC16, -- high fidelity waveform
fast_waveform_mode = WAVEFORM_MODE_A2, fast_waveform_mode = WAVEFORM_MODE_A2,
-- force to repaint all the widget is stack, will be reset to false -- force to repaint all the widget is stack, will be reset to false
-- after each ui loop -- after each ui loop
repaint_all = false, repaint_all = false,
-- force to do full refresh, will be reset to false -- force to do full refresh, will be reset to false
-- after each ui loop -- after each ui loop
full_refresh = false, full_refresh = false,
-- force to do patial refresh, will be reset to false -- force to do patial refresh, will be reset to false
-- after each ui loop -- after each ui loop
patial_refresh = false, patial_refresh = false,
-- trigger a full refresh when counter reaches FULL_REFRESH_COUNT -- trigger a full refresh when counter reaches FULL_REFRESH_COUNT
FULL_REFRESH_COUNT = DRCOUNTMAX, FULL_REFRESH_COUNT = DRCOUNTMAX,
refresh_count = 0, refresh_count = 0,
_running = true, _running = true,
_window_stack = {}, _window_stack = {},
_execution_stack = {}, _execution_stack = {},
_dirty = {} _dirty = {}
} }
-- register & show a widget -- register & show a widget
function UIManager:show(widget, x, y) function UIManager:show(widget, x, y)
-- put widget on top of stack -- put widget on top of stack
table.insert(self._window_stack, {x = x or 0, y = y or 0, widget = widget}) table.insert(self._window_stack, {x = x or 0, y = y or 0, widget = widget})
-- and schedule it to be painted -- and schedule it to be painted
self:setDirty(widget) self:setDirty(widget)
-- tell the widget that it is shown now -- tell the widget that it is shown now
widget:handleEvent(Event:new("Show")) widget:handleEvent(Event:new("Show"))
-- check if this widget disables double tap gesture -- check if this widget disables double tap gesture
if widget.disable_double_tap then if widget.disable_double_tap then
Input.disable_double_tap = true Input.disable_double_tap = true
end end
end end
-- unregister a widget -- unregister a widget
function UIManager:close(widget) function UIManager:close(widget)
Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP
local dirty = false local dirty = false
for i = #self._window_stack, 1, -1 do for i = #self._window_stack, 1, -1 do
if self._window_stack[i].widget == widget then if self._window_stack[i].widget == widget then
table.remove(self._window_stack, i) table.remove(self._window_stack, i)
dirty = true dirty = true
elseif self._window_stack[i].widget.disable_double_tap then elseif self._window_stack[i].widget.disable_double_tap then
Input.disable_double_tap = true Input.disable_double_tap = true
end end
end end
if dirty then if dirty then
-- schedule remaining widgets to be painted -- schedule remaining widgets to be painted
for i = 1, #self._window_stack do for i = 1, #self._window_stack do
self:setDirty(self._window_stack[i].widget) self:setDirty(self._window_stack[i].widget)
end end
end end
end end
-- schedule an execution task -- schedule an execution task
function UIManager:schedule(time, action) function UIManager:schedule(time, action)
table.insert(self._execution_stack, { time = time, action = action }) table.insert(self._execution_stack, { time = time, action = action })
end end
-- schedule task in a certain amount of seconds (fractions allowed) from now -- schedule task in a certain amount of seconds (fractions allowed) from now
function UIManager:scheduleIn(seconds, action) function UIManager:scheduleIn(seconds, action)
local when = { util.gettime() } local when = { util.gettime() }
local s = math.floor(seconds) local s = math.floor(seconds)
local usecs = (seconds - s) * 1000000 local usecs = (seconds - s) * 1000000
when[1] = when[1] + s when[1] = when[1] + s
when[2] = when[2] + usecs when[2] = when[2] + usecs
if when[2] > 1000000 then if when[2] > 1000000 then
when[1] = when[1] + 1 when[1] = when[1] + 1
when[2] = when[2] - 1000000 when[2] = when[2] - 1000000
end end
self:schedule(when, action) self:schedule(when, action)
end end
-- register a widget to be repainted -- register a widget to be repainted
function UIManager:setDirty(widget, refresh_type) function UIManager:setDirty(widget, refresh_type)
-- "auto": request full refresh -- "auto": request full refresh
-- "full": force full refresh -- "full": force full refresh
-- "partial": partial refresh -- "partial": partial refresh
if not refresh_type then if not refresh_type then
refresh_type = "auto" refresh_type = "auto"
end end
self._dirty[widget] = refresh_type self._dirty[widget] = refresh_type
end end
-- signal to quit -- signal to quit
function UIManager:quit() function UIManager:quit()
self._running = false self._running = false
end end
-- transmit an event to registered widgets -- transmit an event to registered widgets
function UIManager:sendEvent(event) function UIManager:sendEvent(event)
-- top level widget has first access to the event -- top level widget has first access to the event
if self._window_stack[#self._window_stack].widget:handleEvent(event) then if self._window_stack[#self._window_stack].widget:handleEvent(event) then
return return
end end
-- if the event is not consumed, active widgets can access it -- if the event is not consumed, active widgets can access it
for _, widget in ipairs(self._window_stack) do for _, widget in ipairs(self._window_stack) do
if widget.widget.is_always_active then if widget.widget.is_always_active then
if widget.widget:handleEvent(event) then return end if widget.widget:handleEvent(event) then return end
end end
if widget.widget.active_widgets then if widget.widget.active_widgets then
for _, active_widget in ipairs(widget.widget.active_widgets) do for _, active_widget in ipairs(widget.widget.active_widgets) do
if active_widget:handleEvent(event) then return end if active_widget:handleEvent(event) then return end
end end
end end
end end
end end
function UIManager:checkTasks() function UIManager:checkTasks()
local now = { util.gettime() } local now = { util.gettime() }
-- check if we have timed events in our queue and search next one -- check if we have timed events in our queue and search next one
local wait_until = nil local wait_until = nil
local all_tasks_checked local all_tasks_checked
repeat repeat
all_tasks_checked = true all_tasks_checked = true
for i = #self._execution_stack, 1, -1 do for i = #self._execution_stack, 1, -1 do
local task = self._execution_stack[i] local task = self._execution_stack[i]
if not task.time if not task.time
or task.time[1] < now[1] or task.time[1] < now[1]
or task.time[1] == now[1] and task.time[2] < now[2] then or task.time[1] == now[1] and task.time[2] < now[2] then
-- task is pending to be executed right now. do it. -- task is pending to be executed right now. do it.
task.action() task.action()
-- and remove from table -- and remove from table
table.remove(self._execution_stack, i) table.remove(self._execution_stack, i)
-- start loop again, since new tasks might be on the -- start loop again, since new tasks might be on the
-- queue now -- queue now
all_tasks_checked = false all_tasks_checked = false
elseif not wait_until elseif not wait_until
or wait_until[1] > task.time[1] or wait_until[1] > task.time[1]
or wait_until[1] == task.time[1] and wait_until[2] > task.time[2] then 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 -- task is to be run in the future _and_ is scheduled
-- earlier than the tasks we looked at already -- earlier than the tasks we looked at already
-- so adjust to the currently examined task instead. -- so adjust to the currently examined task instead.
wait_until = task.time wait_until = task.time
end end
end end
until all_tasks_checked until all_tasks_checked
return wait_until return wait_until
end end
-- this is the main loop of the UI controller -- this is the main loop of the UI controller
-- it is intended to manage input events and delegate -- it is intended to manage input events and delegate
-- them to dialogs -- them to dialogs
function UIManager:run() function UIManager:run()
self._running = true self._running = true
while self._running do while self._running do
local now = { util.gettime() } local now = { util.gettime() }
local wait_until = self:checkTasks() local wait_until = self:checkTasks()
--DEBUG("---------------------------------------------------") --DEBUG("---------------------------------------------------")
--DEBUG("exec stack", self._execution_stack) --DEBUG("exec stack", self._execution_stack)
--DEBUG("window stack", self._window_stack) --DEBUG("window stack", self._window_stack)
--DEBUG("dirty stack", self._dirty) --DEBUG("dirty stack", self._dirty)
--DEBUG("---------------------------------------------------") --DEBUG("---------------------------------------------------")
-- stop when we have no window to show -- stop when we have no window to show
if #self._window_stack == 0 then if #self._window_stack == 0 then
DEBUG("no dialog left to show, would loop endlessly") DEBUG("no dialog left to show, would loop endlessly")
return nil return nil
end end
-- repaint dirty widgets -- repaint dirty widgets
local dirty = false local dirty = false
local request_full_refresh = false local request_full_refresh = false
local force_full_refresh = false local force_full_refresh = false
local force_patial_refresh = false local force_patial_refresh = false
local force_fast_refresh = false local force_fast_refresh = false
for _, widget in ipairs(self._window_stack) do for _, widget in ipairs(self._window_stack) do
if self.repaint_all or self._dirty[widget.widget] then if self.repaint_all or self._dirty[widget.widget] then
widget.widget:paintTo(Screen.bb, widget.x, widget.y) widget.widget:paintTo(Screen.bb, widget.x, widget.y)
if self._dirty[widget.widget] == "auto" then if self._dirty[widget.widget] == "auto" then
request_full_refresh = true request_full_refresh = true
end end
if self._dirty[widget.widget] == "full" then if self._dirty[widget.widget] == "full" then
force_full_refresh = true force_full_refresh = true
end end
if self._dirty[widget.widget] == "partial" then if self._dirty[widget.widget] == "partial" then
force_patial_refresh = true force_patial_refresh = true
end end
if self._dirty[widget.widget] == "fast" then if self._dirty[widget.widget] == "fast" then
force_fast_refresh = true force_fast_refresh = true
end end
-- and remove from list after painting -- and remove from list after painting
self._dirty[widget.widget] = nil self._dirty[widget.widget] = nil
-- trigger repaint -- trigger repaint
dirty = true dirty = true
end end
end end
if self.full_refresh then if self.full_refresh then
dirty = true dirty = true
force_full_refresh = true force_full_refresh = true
end end
if self.patial_refresh then if self.patial_refresh then
dirty = true dirty = true
force_patial_refresh = true force_patial_refresh = true
end end
self.repaint_all = false self.repaint_all = false
self.full_refresh = false self.full_refresh = false
self.patial_refresh = false self.patial_refresh = false
local refresh_type = self.default_refresh_type local refresh_type = self.default_refresh_type
local waveform_mode = self.default_waveform_mode local waveform_mode = self.default_waveform_mode
if dirty then if dirty then
if force_patial_refresh or force_fast_refresh then if force_patial_refresh or force_fast_refresh then
refresh_type = 0 refresh_type = 0
elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then
refresh_type = 1 refresh_type = 1
end end
if force_fast_refresh then if force_fast_refresh then
waveform_mode = self.fast_waveform_mode waveform_mode = self.fast_waveform_mode
end end
if self.update_region_func then if self.update_region_func then
local update_region = self.update_region_func() local update_region = self.update_region_func()
-- in some rare cases update region has 1 pixel offset -- in some rare cases update region has 1 pixel offset
Screen:refresh(refresh_type, waveform_mode, Screen:refresh(refresh_type, waveform_mode,
update_region.x-1, update_region.y-1, update_region.x-1, update_region.y-1,
update_region.w+2, update_region.h+2) update_region.w+2, update_region.h+2)
else else
Screen:refresh(refresh_type, waveform_mode) Screen:refresh(refresh_type, waveform_mode)
end end
if self.refresh_type == 1 then if self.refresh_type == 1 then
self.refresh_count = 0 self.refresh_count = 0
elseif not force_patial_refresh and not force_full_refresh then elseif not force_patial_refresh and not force_full_refresh then
self.refresh_count = (self.refresh_count + 1)%self.FULL_REFRESH_COUNT self.refresh_count = (self.refresh_count + 1)%self.FULL_REFRESH_COUNT
end end
self.update_region_func = nil self.update_region_func = nil
end end
self:checkTasks() self:checkTasks()
-- wait for next event -- wait for next event
-- note that we will skip that if in the meantime we have tasks that are ready to run -- note that we will skip that if in the meantime we have tasks that are ready to run
local input_event = nil local input_event = nil
if not wait_until then if not wait_until then
-- no pending task, wait endlessly -- no pending task, wait endlessly
input_event = Input:waitEvent() input_event = Input:waitEvent()
elseif wait_until[1] > now[1] elseif wait_until[1] > now[1]
or wait_until[1] == now[1] and wait_until[2] > now[2] then 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] } local wait_for = { s = wait_until[1] - now[1], us = wait_until[2] - now[2] }
if wait_for.us < 0 then if wait_for.us < 0 then
wait_for.s = wait_for.s - 1 wait_for.s = wait_for.s - 1
wait_for.us = 1000000 + wait_for.us wait_for.us = 1000000 + wait_for.us
end end
-- wait until next task is pending -- wait until next task is pending
input_event = Input:waitEvent(wait_for.us, wait_for.s) input_event = Input:waitEvent(wait_for.us, wait_for.s)
end end
-- delegate input_event to handler -- delegate input_event to handler
if input_event then if input_event then
--DEBUG("in ui.lua:", input_event) --DEBUG("in ui.lua:", input_event)
if input_event == "IntoSS" then if input_event == "IntoSS" then
Device:intoScreenSaver() Device:intoScreenSaver()
elseif input_event == "OutOfSS" then elseif input_event == "OutOfSS" then
Device:outofScreenSaver() Device:outofScreenSaver()
elseif input_event == "Charging" then elseif input_event == "Charging" then
Device:usbPlugIn() Device:usbPlugIn()
elseif input_event == "NotCharging" then elseif input_event == "NotCharging" then
Device:usbPlugOut() Device:usbPlugOut()
self:sendEvent(Event:new("NotCharging")) self:sendEvent(Event:new("NotCharging"))
elseif input_event == "Light" then elseif input_event == "Light" then
Device:getPowerDevice():toggleFrontlight() Device:getPowerDevice():toggleFrontlight()
elseif (input_event == "Power" and not Device.screen_saver_mode) elseif (input_event == "Power" and not Device.screen_saver_mode)
or input_event == "Suspend" then or input_event == "Suspend" then
local InfoMessage = require("ui/widget/infomessage") local InfoMessage = require("ui/widget/infomessage")
self:show(InfoMessage:new{ self:show(InfoMessage:new{
text = _("Standby"), text = _("Standby"),
timeout = 1, timeout = 1,
}) })
Device:prepareSuspend() Device:prepareSuspend()
self:scheduleIn(0.5, function() Device:Suspend() end) self:scheduleIn(0.5, function() Device:Suspend() end)
elseif (input_event == "Power" and Device.screen_saver_mode) elseif (input_event == "Power" and Device.screen_saver_mode)
or input_event == "Resume" then or input_event == "Resume" then
Device:Resume() Device:Resume()
self:sendEvent(Event:new("Resume")) self:sendEvent(Event:new("Resume"))
else else
self:sendEvent(input_event) self:sendEvent(input_event)
end end
end end
end end
end end
return UIManager return UIManager

@ -11,212 +11,212 @@ local DEBUG = require("dbg")
BBoxWidget shows a bbox for page cropping BBoxWidget shows a bbox for page cropping
]] ]]
local BBoxWidget = InputContainer:new{ local BBoxWidget = InputContainer:new{
page_bbox = nil, page_bbox = nil,
screen_bbox = nil, screen_bbox = nil,
linesize = 2, linesize = 2,
fine_factor = 10, fine_factor = 10,
} }
function BBoxWidget:init() function BBoxWidget:init()
self.page_bbox = self.document:getPageBBox(self.view.state.page) self.page_bbox = self.document:getPageBBox(self.view.state.page)
--DEBUG("used page bbox on page", self.view.state.page, self.page_bbox) --DEBUG("used page bbox on page", self.view.state.page, self.page_bbox)
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapAdjust = { TapAdjust = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.view.dimen, range = self.view.dimen,
} }
}, },
SwipeAdjust = { SwipeAdjust = {
GestureRange:new{ GestureRange:new{
ges = "swipe", ges = "swipe",
range = self.view.dimen, range = self.view.dimen,
} }
}, },
HoldAdjust = { HoldAdjust = {
GestureRange:new{ GestureRange:new{
ges = "hold", ges = "hold",
range = self.view.dimen, range = self.view.dimen,
} }
}, },
ConfirmAdjust = { ConfirmAdjust = {
GestureRange:new{ GestureRange:new{
ges = "double_tap", ges = "double_tap",
range = self.view.dimen, range = self.view.dimen,
} }
} }
} }
end end
end end
function BBoxWidget:getSize() function BBoxWidget:getSize()
return self.view.dimen return self.view.dimen
end end
function BBoxWidget:paintTo(bb, x, y) function BBoxWidget:paintTo(bb, x, y)
-- As getScreenBBox uses view states, screen_bbox initialization is postponed. -- As getScreenBBox uses view states, screen_bbox initialization is postponed.
self.screen_bbox = self.screen_bbox or self:getScreenBBox(self.page_bbox) self.screen_bbox = self.screen_bbox or self:getScreenBBox(self.page_bbox)
local bbox = self.screen_bbox local bbox = self.screen_bbox
-- top edge -- top edge
bb:invertRect(bbox.x0 + self.linesize, bbox.y0, bbox.x1 - bbox.x0, self.linesize) bb:invertRect(bbox.x0 + self.linesize, bbox.y0, bbox.x1 - bbox.x0, self.linesize)
-- bottom edge -- bottom edge
bb:invertRect(bbox.x0 + self.linesize, bbox.y1, bbox.x1 - bbox.x0 - self.linesize, self.linesize) bb:invertRect(bbox.x0 + self.linesize, bbox.y1, bbox.x1 - bbox.x0 - self.linesize, self.linesize)
-- left edge -- left edge
bb:invertRect(bbox.x0, bbox.y0, self.linesize, bbox.y1 - bbox.y0 + self.linesize) bb:invertRect(bbox.x0, bbox.y0, self.linesize, bbox.y1 - bbox.y0 + self.linesize)
-- right edge -- right edge
bb:invertRect(bbox.x1, bbox.y0 + self.linesize, self.linesize, bbox.y1 - bbox.y0) bb:invertRect(bbox.x1, bbox.y0 + self.linesize, self.linesize, bbox.y1 - bbox.y0)
end end
-- transform page bbox to screen bbox -- transform page bbox to screen bbox
function BBoxWidget:getScreenBBox(page_bbox) function BBoxWidget:getScreenBBox(page_bbox)
local bbox = {} local bbox = {}
local scale = self.view.state.zoom local scale = self.view.state.zoom
local screen_offset = self.view.state.offset local screen_offset = self.view.state.offset
--DEBUG("screen offset in page_to_screen", screen_offset) --DEBUG("screen offset in page_to_screen", screen_offset)
bbox.x0 = Math.round(page_bbox.x0 * scale + screen_offset.x) bbox.x0 = Math.round(page_bbox.x0 * scale + screen_offset.x)
bbox.y0 = Math.round(page_bbox.y0 * scale + screen_offset.y) bbox.y0 = Math.round(page_bbox.y0 * scale + screen_offset.y)
bbox.x1 = Math.round(page_bbox.x1 * scale + screen_offset.x) bbox.x1 = Math.round(page_bbox.x1 * scale + screen_offset.x)
bbox.y1 = Math.round(page_bbox.y1 * scale + screen_offset.y) bbox.y1 = Math.round(page_bbox.y1 * scale + screen_offset.y)
return bbox return bbox
end end
-- transform screen bbox to page bbox -- transform screen bbox to page bbox
function BBoxWidget:getPageBBox(screen_bbox) function BBoxWidget:getPageBBox(screen_bbox)
local bbox = {} local bbox = {}
local scale = self.view.state.zoom local scale = self.view.state.zoom
local screen_offset = self.view.state.offset local screen_offset = self.view.state.offset
--DEBUG("screen offset in screen_to_page", screen_offset) --DEBUG("screen offset in screen_to_page", screen_offset)
bbox.x0 = Math.round((screen_bbox.x0 - screen_offset.x) / scale) bbox.x0 = Math.round((screen_bbox.x0 - screen_offset.x) / scale)
bbox.y0 = Math.round((screen_bbox.y0 - screen_offset.y) / scale) bbox.y0 = Math.round((screen_bbox.y0 - screen_offset.y) / scale)
bbox.x1 = Math.round((screen_bbox.x1 - screen_offset.x) / scale) bbox.x1 = Math.round((screen_bbox.x1 - screen_offset.x) / scale)
bbox.y1 = Math.round((screen_bbox.y1 - screen_offset.y) / scale) bbox.y1 = Math.round((screen_bbox.y1 - screen_offset.y) / scale)
return bbox return bbox
end end
function BBoxWidget:inPageArea(ges) function BBoxWidget:inPageArea(ges)
local offset = self.view.state.offset local offset = self.view.state.offset
local page_area = self.view.page_area 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} 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) return not ges.pos:notIntersectWith(page_dimen)
end end
function BBoxWidget:adjustScreenBBox(ges, relative) function BBoxWidget:adjustScreenBBox(ges, relative)
--DEBUG("adjusting crop bbox with pos", ges.pos) --DEBUG("adjusting crop bbox with pos", ges.pos)
if not self:inPageArea(ges) then return end if not self:inPageArea(ges) then return end
local bbox = self.screen_bbox local bbox = self.screen_bbox
local upper_left = Geom:new{ x = bbox.x0, y = bbox.y0} local upper_left = Geom:new{ x = bbox.x0, y = bbox.y0}
local upper_right = Geom:new{ x = bbox.x1, 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_left = Geom:new{ x = bbox.x0, y = bbox.y1}
local bottom_right = Geom:new{ x = bbox.x1, 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 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 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 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 left_center = Geom:new{ x = bbox.x0, y = (bbox.y0 + bbox.y1) / 2}
local anchors = { local anchors = {
upper_left, upper_center, upper_right, upper_left, upper_center, upper_right,
left_center, right_center, left_center, right_center,
bottom_left, bottom_center, bottom_right, bottom_left, bottom_center, bottom_right,
} }
local _, nearest = Math.tmin(anchors, function(a,b) local _, nearest = Math.tmin(anchors, function(a,b)
return a:distance(ges.pos) > b:distance(ges.pos) return a:distance(ges.pos) > b:distance(ges.pos)
end) end)
--DEBUG("nearest anchor", nearest) --DEBUG("nearest anchor", nearest)
if nearest == upper_left then if nearest == upper_left then
upper_left.x = ges.pos.x upper_left.x = ges.pos.x
upper_left.y = ges.pos.y upper_left.y = ges.pos.y
elseif nearest == bottom_right then elseif nearest == bottom_right then
bottom_right.x = ges.pos.x bottom_right.x = ges.pos.x
bottom_right.y = ges.pos.y bottom_right.y = ges.pos.y
elseif nearest == upper_right then elseif nearest == upper_right then
bottom_right.x = ges.pos.x bottom_right.x = ges.pos.x
upper_left.y = ges.pos.y upper_left.y = ges.pos.y
elseif nearest == bottom_left then elseif nearest == bottom_left then
upper_left.x = ges.pos.x upper_left.x = ges.pos.x
bottom_right.y = ges.pos.y bottom_right.y = ges.pos.y
elseif nearest == upper_center then elseif nearest == upper_center then
if relative then if relative then
local delta = 0 local delta = 0
if ges.direction == "north" then if ges.direction == "north" then
delta = -ges.distance / self.fine_factor delta = -ges.distance / self.fine_factor
elseif ges.direction == "south" then elseif ges.direction == "south" then
delta = ges.distance / self.fine_factor delta = ges.distance / self.fine_factor
end end
upper_left.y = upper_left.y + delta upper_left.y = upper_left.y + delta
else else
upper_left.y = ges.pos.y upper_left.y = ges.pos.y
end end
elseif nearest == right_center then elseif nearest == right_center then
if relative then if relative then
local delta = 0 local delta = 0
if ges.direction == "west" then if ges.direction == "west" then
delta = -ges.distance / self.fine_factor delta = -ges.distance / self.fine_factor
elseif ges.direction == "east" then elseif ges.direction == "east" then
delta = ges.distance / self.fine_factor delta = ges.distance / self.fine_factor
end end
bottom_right.x = bottom_right.x + delta bottom_right.x = bottom_right.x + delta
else else
bottom_right.x = ges.pos.x bottom_right.x = ges.pos.x
end end
elseif nearest == bottom_center then elseif nearest == bottom_center then
if relative then if relative then
local delta = 0 local delta = 0
if ges.direction == "north" then if ges.direction == "north" then
delta = -ges.distance / self.fine_factor delta = -ges.distance / self.fine_factor
elseif ges.direction == "south" then elseif ges.direction == "south" then
delta = ges.distance / self.fine_factor delta = ges.distance / self.fine_factor
end end
bottom_right.y = bottom_right.y + delta bottom_right.y = bottom_right.y + delta
else else
bottom_right.y = ges.pos.y bottom_right.y = ges.pos.y
end end
elseif nearest == left_center then elseif nearest == left_center then
if relative then if relative then
local delta = 0 local delta = 0
if ges.direction == "west" then if ges.direction == "west" then
delta = -ges.distance / self.fine_factor delta = -ges.distance / self.fine_factor
elseif ges.direction == "east" then elseif ges.direction == "east" then
delta = ges.distance / self.fine_factor delta = ges.distance / self.fine_factor
end end
upper_left.x = upper_left.x + delta upper_left.x = upper_left.x + delta
else else
upper_left.x = ges.pos.x upper_left.x = ges.pos.x
end end
end end
self.screen_bbox = { self.screen_bbox = {
x0 = Math.round(upper_left.x), x0 = Math.round(upper_left.x),
y0 = Math.round(upper_left.y), y0 = Math.round(upper_left.y),
x1 = Math.round(bottom_right.x), x1 = Math.round(bottom_right.x),
y1 = Math.round(bottom_right.y) y1 = Math.round(bottom_right.y)
} }
UIManager.repaint_all = true UIManager.repaint_all = true
end end
function BBoxWidget:getModifiedPageBBox() function BBoxWidget:getModifiedPageBBox()
return self:getPageBBox(self.screen_bbox) return self:getPageBBox(self.screen_bbox)
end end
function BBoxWidget:onTapAdjust(arg, ges) function BBoxWidget:onTapAdjust(arg, ges)
self:adjustScreenBBox(ges) self:adjustScreenBBox(ges)
return true return true
end end
function BBoxWidget:onSwipeAdjust(arg, ges) function BBoxWidget:onSwipeAdjust(arg, ges)
self:adjustScreenBBox(ges, true) self:adjustScreenBBox(ges, true)
return true return true
end end
function BBoxWidget:onHoldAdjust(arg, ges) function BBoxWidget:onHoldAdjust(arg, ges)
self:adjustScreenBBox(ges) self:adjustScreenBBox(ges)
return true return true
end end
function BBoxWidget:onConfirmAdjust(arg, ges) function BBoxWidget:onConfirmAdjust(arg, ges)
if self:inPageArea(ges) then if self:inPageArea(ges) then
self.ui:handleEvent(Event:new("ConfirmPageCrop")) self.ui:handleEvent(Event:new("ConfirmPageCrop"))
end end
return true return true
end end
return BBoxWidget return BBoxWidget

@ -13,135 +13,135 @@ local _ = require("gettext")
a button widget that shows text or a icon and handles callback when tapped a button widget that shows text or a icon and handles callback when tapped
--]] --]]
local Button = InputContainer:new{ local Button = InputContainer:new{
text = nil, -- mandatory text = nil, -- mandatory
icon = nil, icon = nil,
preselect = false, preselect = false,
callback = nil, callback = nil,
enabled = true, enabled = true,
margin = 0, margin = 0,
bordersize = 3, bordersize = 3,
background = 0, background = 0,
radius = 15, radius = 15,
padding = 2, padding = 2,
width = nil, width = nil,
text_font_face = "cfont", text_font_face = "cfont",
text_font_size = 20, text_font_size = 20,
} }
function Button:init() function Button:init()
if self.text then if self.text then
self.label_widget = TextWidget:new{ self.label_widget = TextWidget:new{
text = self.text, text = self.text,
bgcolor = 0.0, bgcolor = 0.0,
fgcolor = self.enabled and 1.0 or 0.5, fgcolor = self.enabled and 1.0 or 0.5,
bold = true, bold = true,
face = Font:getFace(self.text_font_face, self.text_font_size) face = Font:getFace(self.text_font_face, self.text_font_size)
} }
else else
self.label_widget = ImageWidget:new{ self.label_widget = ImageWidget:new{
file = self.icon, file = self.icon,
dim = self.enabled, dim = self.enabled,
} }
end end
local widget_size = self.label_widget:getSize() local widget_size = self.label_widget:getSize()
if self.width == nil then if self.width == nil then
self.width = widget_size.w self.width = widget_size.w
end end
-- set FrameContainer content -- set FrameContainer content
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
margin = self.margin, margin = self.margin,
bordersize = self.bordersize, bordersize = self.bordersize,
background = self.background, background = self.background,
radius = self.radius, radius = self.radius,
padding = self.padding, padding = self.padding,
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = self.width, w = self.width,
h = widget_size.h h = widget_size.h
}, },
self.label_widget, self.label_widget,
} }
} }
if self.preselect then if self.preselect then
self[1].color = 15 self[1].color = 15
else else
self[1].color = 5 self[1].color = 5
end end
self.dimen = self[1]:getSize() self.dimen = self[1]:getSize()
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapSelect = { TapSelect = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
}, },
doc = _("Tap Button"), doc = _("Tap Button"),
}, },
} }
end end
end end
function Button:onFocus() function Button:onFocus()
self[1].color = 15 self[1].color = 15
return true return true
end end
function Button:onUnfocus() function Button:onUnfocus()
self[1].color = 5 self[1].color = 5
return true return true
end end
function Button:enable() function Button:enable()
self.enabled = true self.enabled = true
if self.text then if self.text then
self.label_widget.fgcolor = self.enabled and 1.0 or 0.5 self.label_widget.fgcolor = self.enabled and 1.0 or 0.5
else else
self.label_widget.dim = not self.enabled self.label_widget.dim = not self.enabled
end end
end end
function Button:disable() function Button:disable()
self.enabled = false self.enabled = false
if self.text then if self.text then
self.label_widget.fgcolor = self.enabled and 1.0 or 0.5 self.label_widget.fgcolor = self.enabled and 1.0 or 0.5
else else
self.label_widget.dim = not self.enabled self.label_widget.dim = not self.enabled
end end
end end
function Button:enableDisable(enable) function Button:enableDisable(enable)
if enable then if enable then
self:enable() self:enable()
else else
self:disable() self:disable()
end end
end end
function Button:hide() function Button:hide()
if self.icon then if self.icon then
self.label_widget.hide = true self.label_widget.hide = true
end end
end end
function Button:show() function Button:show()
if self.icon then if self.icon then
self.label_widget.hide = false self.label_widget.hide = false
end end
end end
function Button:showHide(show) function Button:showHide(show)
if show then if show then
self:show() self:show()
else else
self:hide() self:hide()
end end
end end
function Button:onTapSelect() function Button:onTapSelect()
if self.enabled then if self.enabled then
self.callback() self.callback()
end end
return true return true
end end
return Button return Button

@ -11,49 +11,49 @@ local UIManager = require("ui/uimanager")
local _ = require("gettext") local _ = require("gettext")
local ButtonDialog = InputContainer:new{ local ButtonDialog = InputContainer:new{
buttons = nil, buttons = nil,
tap_close_callback = nil, tap_close_callback = nil,
} }
function ButtonDialog:init() function ButtonDialog:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
AnyKeyPressed = { { Input.group.Any }, AnyKeyPressed = { { Input.group.Any },
seqtext = "any key", doc = _("close dialog") } seqtext = "any key", doc = _("close dialog") }
} }
else else
self.ges_events.TapClose = { self.ges_events.TapClose = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
} }
end end
self[1] = CenterContainer:new{ self[1] = CenterContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
FrameContainer:new{ FrameContainer:new{
ButtonTable:new{ ButtonTable:new{
width = Screen:getWidth()*0.9, width = Screen:getWidth()*0.9,
buttons = self.buttons, buttons = self.buttons,
}, },
background = 0, background = 0,
bordersize = 2, bordersize = 2,
radius = 7, radius = 7,
padding = 2, padding = 2,
} }
} }
end end
function ButtonDialog:onTapClose() function ButtonDialog:onTapClose()
UIManager:close(self) UIManager:close(self)
if self.tap_close_callback then if self.tap_close_callback then
self.tap_close_callback() self.tap_close_callback()
end end
return true return true
end end
return ButtonDialog return ButtonDialog

@ -7,72 +7,72 @@ local Screen = require("ui/screen")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local ButtonTable = VerticalGroup:new{ local ButtonTable = VerticalGroup:new{
width = Screen:getWidth(), width = Screen:getWidth(),
buttons = { buttons = {
{ {
{text="OK", enabled=true, callback=nil}, {text="OK", enabled=true, callback=nil},
{text="Cancel", enabled=false, callback=nil}, {text="Cancel", enabled=false, callback=nil},
}, },
}, },
sep_width = Screen:scaleByDPI(1), sep_width = Screen:scaleByDPI(1),
padding = Screen:scaleByDPI(2), padding = Screen:scaleByDPI(2),
zero_sep = false, zero_sep = false,
button_font_face = "cfont", button_font_face = "cfont",
button_font_size = 20, button_font_size = 20,
} }
function ButtonTable:init() function ButtonTable:init()
--local vertical_group = VerticalGroup:new{} --local vertical_group = VerticalGroup:new{}
if self.zero_sep then if self.zero_sep then
self:addHorizontalSep() self:addHorizontalSep()
end end
for i = 1, #self.buttons do for i = 1, #self.buttons do
local horizontal_group = HorizontalGroup:new{} local horizontal_group = HorizontalGroup:new{}
local line = self.buttons[i] local line = self.buttons[i]
local sizer_space = self.sep_width * (#line - 1) + 2 local sizer_space = self.sep_width * (#line - 1) + 2
for j = 1, #line do for j = 1, #line do
local button = Button:new{ local button = Button:new{
text = line[j].text, text = line[j].text,
enabled = line[j].enabled, enabled = line[j].enabled,
callback = line[j].callback, callback = line[j].callback,
width = (self.width - sizer_space)/#line, width = (self.width - sizer_space)/#line,
bordersize = 0, bordersize = 0,
margin = 0, margin = 0,
padding = 0, padding = 0,
text_font_face = self.button_font_face, text_font_face = self.button_font_face,
text_font_size = self.button_font_size, text_font_size = self.button_font_size,
} }
local button_dim = button:getSize() local button_dim = button:getSize()
local vertical_sep = LineWidget:new{ local vertical_sep = LineWidget:new{
background = 8, background = 8,
dimen = Geom:new{ dimen = Geom:new{
w = self.sep_width, w = self.sep_width,
h = button_dim.h, h = button_dim.h,
} }
} }
table.insert(horizontal_group, button) table.insert(horizontal_group, button)
if j < #line then if j < #line then
table.insert(horizontal_group, vertical_sep) table.insert(horizontal_group, vertical_sep)
end end
end -- end for each button end -- end for each button
table.insert(self, horizontal_group) table.insert(self, horizontal_group)
if i < #self.buttons then if i < #self.buttons then
self:addHorizontalSep() self:addHorizontalSep()
end end
end -- end for each button line end -- end for each button line
end end
function ButtonTable:addHorizontalSep() function ButtonTable:addHorizontalSep()
table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) }) table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) })
table.insert(self, LineWidget:new{ table.insert(self, LineWidget:new{
background = 8, background = 8,
dimen = Geom:new{ dimen = Geom:new{
w = self.width, w = self.width,
h = self.sep_width, h = self.sep_width,
} }
}) })
table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) }) table.insert(self, VerticalSpan:new{ width = Screen:scaleByDPI(2) })
end end
return ButtonTable return ButtonTable

@ -11,35 +11,35 @@ local Font = require("ui/font")
a button widget that shows an "×" and handles closing window when tapped a button widget that shows an "×" and handles closing window when tapped
--]] --]]
local CloseButton = InputContainer:new{ local CloseButton = InputContainer:new{
align = "right", align = "right",
window = nil, window = nil,
} }
function CloseButton:init() function CloseButton:init()
local text_widget = TextWidget:new{ local text_widget = TextWidget:new{
text = "×", text = "×",
face = Font:getFace("cfont", 32), face = Font:getFace("cfont", 32),
} }
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
bordersize = 0, bordersize = 0,
padding = 0, padding = 0,
text_widget text_widget
} }
self.dimen = text_widget:getSize():copy() self.dimen = text_widget:getSize():copy()
self.ges_events.Close = { self.ges_events.Close = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
}, },
doc = "Tap on close button", doc = "Tap on close button",
} }
end end
function CloseButton:onClose() function CloseButton:onClose()
self.window:onClose() self.window:onClose()
return true return true
end end
return CloseButton return CloseButton

@ -26,363 +26,363 @@ local _ = require("gettext")
local MenuBarItem = InputContainer:new{} local MenuBarItem = InputContainer:new{}
function MenuBarItem:init() function MenuBarItem:init()
self.dimen = self[1]:getSize() self.dimen = self[1]:getSize()
-- we need this table per-instance, so we declare it here -- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapSelect = { TapSelect = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
}, },
doc = _("Select Menu Item"), doc = _("Select Menu Item"),
}, },
} }
else else
self.active_key_events = { self.active_key_events = {
Select = { {"Press"}, doc = _("chose selected item") }, Select = { {"Press"}, doc = _("chose selected item") },
} }
end end
end end
function MenuBarItem:onTapSelect() function MenuBarItem:onTapSelect()
UIManager:scheduleIn(0.0, function() self:invert(true) end) UIManager:scheduleIn(0.0, function() self:invert(true) end)
UIManager:scheduleIn(0.1, function() UIManager:scheduleIn(0.1, function()
UIManager:sendEvent(Event:new("ShowConfigPanel", self.index)) UIManager:sendEvent(Event:new("ShowConfigPanel", self.index))
end) end)
UIManager:scheduleIn(0.5, function() self:invert(false) end) UIManager:scheduleIn(0.5, function() self:invert(false) end)
return true return true
end end
function MenuBarItem:invert(invert) function MenuBarItem:invert(invert)
self[1].invert = invert self[1].invert = invert
UIManager.update_region_func = function() UIManager.update_region_func = function()
DEBUG("update icon region", self[1].dimen) DEBUG("update icon region", self[1].dimen)
return self[1].dimen return self[1].dimen
end end
UIManager:setDirty(self.config, "full") UIManager:setDirty(self.config, "full")
end end
local OptionTextItem = InputContainer:new{} local OptionTextItem = InputContainer:new{}
function OptionTextItem:init() function OptionTextItem:init()
local text_widget = self[1] local text_widget = self[1]
self[1] = UnderlineContainer:new{ self[1] = UnderlineContainer:new{
text_widget, text_widget,
padding = self.padding, padding = self.padding,
color = self.color, color = self.color,
} }
self.dimen = self[1]:getSize() self.dimen = self[1]:getSize()
-- we need this table per-instance, so we declare it here -- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapSelect = { TapSelect = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
}, },
doc = _("Select Option Item"), doc = _("Select Option Item"),
}, },
} }
else else
self.active_key_events = { self.active_key_events = {
Select = { {"Press"}, doc = _("chose selected item") }, Select = { {"Press"}, doc = _("chose selected item") },
} }
end end
end end
function OptionTextItem:onTapSelect() function OptionTextItem:onTapSelect()
for _, item in pairs(self.items) do for _, item in pairs(self.items) do
item[1].color = 0 item[1].color = 0
end end
self[1].color = 15 self[1].color = 15
self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item)
UIManager:setDirty(self.config, "partial") UIManager:setDirty(self.config, "partial")
return true return true
end end
local OptionIconItem = InputContainer:new{} local OptionIconItem = InputContainer:new{}
function OptionIconItem:init() function OptionIconItem:init()
self.dimen = self.icon:getSize() self.dimen = self.icon:getSize()
self[1] = UnderlineContainer:new{ self[1] = UnderlineContainer:new{
self.icon, self.icon,
padding = self.padding, padding = self.padding,
color = self.color, color = self.color,
} }
-- we need this table per-instance, so we declare it here -- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapSelect = { TapSelect = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
}, },
doc = _("Select Option Item"), doc = _("Select Option Item"),
}, },
} }
end end
end end
function OptionIconItem:onTapSelect() function OptionIconItem:onTapSelect()
for _, item in pairs(self.items) do for _, item in pairs(self.items) do
--item[1][1].invert = false --item[1][1].invert = false
item[1].color = 0 item[1].color = 0
end end
--self[1][1].invert = true --self[1][1].invert = true
self[1].color = 15 self[1].color = 15
self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item) self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.current_item)
UIManager:setDirty(self.config, "partial") UIManager:setDirty(self.config, "partial")
return true return true
end end
local ConfigOption = CenterContainer:new{} local ConfigOption = CenterContainer:new{}
function ConfigOption:init() function ConfigOption:init()
local default_name_font_size = 20 local default_name_font_size = 20
local default_item_font_size = 16 local default_item_font_size = 16
local default_items_spacing = 30 local default_items_spacing = 30
local default_option_height = 50 local default_option_height = 50
local default_option_padding = 15 local default_option_padding = 15
local vertical_group = VerticalGroup:new{} local vertical_group = VerticalGroup:new{}
table.insert(vertical_group, VerticalSpan:new{ table.insert(vertical_group, VerticalSpan:new{
width = Screen:scaleByDPI(default_option_padding), width = Screen:scaleByDPI(default_option_padding),
}) })
for c = 1, #self.options do for c = 1, #self.options do
if self.options[c].show ~= false then 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 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 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_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 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_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 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 option_height = Screen:scaleByDPI(self.options[c].height and self.options[c].height or default_option_height)
local items_spacing = HorizontalSpan:new{ local items_spacing = HorizontalSpan:new{
width = Screen:scaleByDPI(self.options[c].spacing and self.options[c].spacing or default_items_spacing) width = Screen:scaleByDPI(self.options[c].spacing and self.options[c].spacing or default_items_spacing)
} }
local horizontal_group = HorizontalGroup:new{} local horizontal_group = HorizontalGroup:new{}
if self.options[c].name_text then if self.options[c].name_text then
local option_name_container = RightContainer:new{ local option_name_container = RightContainer:new{
dimen = Geom:new{ w = Screen:getWidth()*name_align, h = option_height}, dimen = Geom:new{ w = Screen:getWidth()*name_align, h = option_height},
} }
local option_name = TextWidget:new{ local option_name = TextWidget:new{
text = self.options[c].name_text, text = self.options[c].name_text,
face = Font:getFace(name_font_face, name_font_size), face = Font:getFace(name_font_face, name_font_size),
} }
table.insert(option_name_container, option_name) table.insert(option_name_container, option_name)
table.insert(horizontal_group, option_name_container) table.insert(horizontal_group, option_name_container)
end end
if self.options[c].widget == "ProgressWidget" then if self.options[c].widget == "ProgressWidget" then
local widget_container = CenterContainer:new{ local widget_container = CenterContainer:new{
dimen = Geom:new{w = Screen:getWidth()*self.options[c].widget_align_center, h = option_height} dimen = Geom:new{w = Screen:getWidth()*self.options[c].widget_align_center, h = option_height}
} }
local widget = ProgressWidget:new{ local widget = ProgressWidget:new{
width = self.options[c].width, width = self.options[c].width,
height = self.options[c].height, height = self.options[c].height,
percentage = self.options[c].percentage, percentage = self.options[c].percentage,
} }
table.insert(widget_container, widget) table.insert(widget_container, widget)
table.insert(horizontal_group, widget_container) table.insert(horizontal_group, widget_container)
end end
local option_items_container = CenterContainer:new{ local option_items_container = CenterContainer:new{
dimen = Geom:new{w = Screen:getWidth()*item_align, h = option_height} dimen = Geom:new{w = Screen:getWidth()*item_align, h = option_height}
} }
local option_items_group = HorizontalGroup:new{} local option_items_group = HorizontalGroup:new{}
local option_items_fixed = false local option_items_fixed = false
local option_items = {} local option_items = {}
if type(self.options[c].item_font_size) == "table" then if type(self.options[c].item_font_size) == "table" then
option_items_group.align = "bottom" option_items_group.align = "bottom"
option_items_fixed = true option_items_fixed = true
end end
-- make current index according to configurable table -- make current index according to configurable table
local current_item = nil local current_item = nil
local function value_diff(val1, val2, name) local function value_diff(val1, val2, name)
if type(val1) ~= type(val2) then if type(val1) ~= type(val2) then
error("different data types in option", name) error("different data types in option", name)
end end
if type(val1) == "number" then if type(val1) == "number" then
return math.abs(val1 - val2) return math.abs(val1 - val2)
elseif type(val1) == "string" then elseif type(val1) == "string" then
return val1 == val2 and 0 or 1 return val1 == val2 and 0 or 1
end end
end end
if self.options[c].name then if self.options[c].name then
if self.options[c].values then if self.options[c].values then
-- check if current value is stored in configurable or calculated in runtime -- 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() local val = self.options[c].current_func and self.options[c].current_func()
or self.config.configurable[self.options[c].name] or self.config.configurable[self.options[c].name]
local min_diff = nil local min_diff = nil
if type(val) == "table" then if type(val) == "table" then
min_diff = value_diff(val[1], self.options[c].values[1][1]) min_diff = value_diff(val[1], self.options[c].values[1][1])
else else
min_diff = value_diff(val, self.options[c].values[1]) min_diff = value_diff(val, self.options[c].values[1])
end end
local diff = nil local diff = nil
for index, val_ in pairs(self.options[c].values) do for index, val_ in pairs(self.options[c].values) do
local diff = nil local diff = nil
if type(val) == "table" then if type(val) == "table" then
diff = value_diff(val[1], val_[1]) diff = value_diff(val[1], val_[1])
else else
diff = value_diff(val, val_) diff = value_diff(val, val_)
end end
if val == val_ then if val == val_ then
current_item = index current_item = index
break break
end end
if diff <= min_diff then if diff <= min_diff then
min_diff = diff min_diff = diff
current_item = index current_item = index
end end
end end
elseif self.options[c].args then elseif self.options[c].args then
-- check if current arg is stored in configurable or calculated in runtime -- 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() local arg = self.options[c].current_func and self.options[c].current_func()
or self.config.configurable[self.options[c].name] or self.config.configurable[self.options[c].name]
for idx, arg_ in pairs(self.options[c].args) do for idx, arg_ in pairs(self.options[c].args) do
if arg_ == arg then if arg_ == arg then
current_item = idx current_item = idx
break break
end end
end end
end end
end end
if self.options[c].item_text then if self.options[c].item_text then
for d = 1, #self.options[c].item_text do for d = 1, #self.options[c].item_text do
local option_item = nil local option_item = nil
if option_items_fixed then if option_items_fixed then
option_item = OptionTextItem:new{ option_item = OptionTextItem:new{
FixedTextWidget:new{ FixedTextWidget:new{
text = self.options[c].item_text[d], text = self.options[c].item_text[d],
face = Font:getFace(item_font_face, item_font_size[d]), face = Font:getFace(item_font_face, item_font_size[d]),
}, },
padding = 3, padding = 3,
color = d == current_item and 15 or 0, color = d == current_item and 15 or 0,
} }
else else
option_item = OptionTextItem:new{ option_item = OptionTextItem:new{
TextWidget:new{ TextWidget:new{
text = self.options[c].item_text[d], text = self.options[c].item_text[d],
face = Font:getFace(item_font_face, item_font_size), face = Font:getFace(item_font_face, item_font_size),
}, },
padding = -3, padding = -3,
color = d == current_item and 15 or 0, color = d == current_item and 15 or 0,
} }
end end
option_items[d] = option_item option_items[d] = option_item
option_item.items = option_items option_item.items = option_items
option_item.name = self.options[c].name option_item.name = self.options[c].name
option_item.values = self.options[c].values option_item.values = self.options[c].values
option_item.args = self.options[c].args option_item.args = self.options[c].args
option_item.event = self.options[c].event option_item.event = self.options[c].event
option_item.current_item = d option_item.current_item = d
option_item.config = self.config option_item.config = self.config
table.insert(option_items_group, option_item) table.insert(option_items_group, option_item)
if d ~= #self.options[c].item_text then if d ~= #self.options[c].item_text then
table.insert(option_items_group, items_spacing) table.insert(option_items_group, items_spacing)
end end
end end
end end
if self.options[c].item_icons then if self.options[c].item_icons then
for d = 1, #self.options[c].item_icons do for d = 1, #self.options[c].item_icons do
local option_item = OptionIconItem:new{ local option_item = OptionIconItem:new{
icon = ImageWidget:new{ icon = ImageWidget:new{
file = self.options[c].item_icons[d] file = self.options[c].item_icons[d]
}, },
padding = -2, padding = -2,
color = d == current_item and 15 or 0, color = d == current_item and 15 or 0,
} }
option_items[d] = option_item option_items[d] = option_item
option_item.items = option_items option_item.items = option_items
option_item.name = self.options[c].name option_item.name = self.options[c].name
option_item.values = self.options[c].values option_item.values = self.options[c].values
option_item.args = self.options[c].args option_item.args = self.options[c].args
option_item.event = self.options[c].event option_item.event = self.options[c].event
option_item.current_item = d option_item.current_item = d
option_item.config = self.config option_item.config = self.config
table.insert(option_items_group, option_item) table.insert(option_items_group, option_item)
if d ~= #self.options[c].item_icons then if d ~= #self.options[c].item_icons then
table.insert(option_items_group, items_spacing) table.insert(option_items_group, items_spacing)
end end
end end
end end
if self.options[c].toggle then if self.options[c].toggle then
local switch = ToggleSwitch:new{ local switch = ToggleSwitch:new{
width = Screen:scaleByDPI(self.options[c].width or 216), width = Screen:scaleByDPI(self.options[c].width or 216),
name = self.options[c].name, name = self.options[c].name,
toggle = self.options[c].toggle, toggle = self.options[c].toggle,
alternate = self.options[c].alternate, alternate = self.options[c].alternate,
values = self.options[c].values, values = self.options[c].values,
args = self.options[c].args, args = self.options[c].args,
event = self.options[c].event, event = self.options[c].event,
events = self.options[c].events, events = self.options[c].events,
config = self.config, config = self.config,
} }
local position = current_item local position = current_item
switch:setPosition(position) switch:setPosition(position)
table.insert(option_items_group, switch) table.insert(option_items_group, switch)
end end
table.insert(option_items_container, option_items_group) table.insert(option_items_container, option_items_group)
table.insert(horizontal_group, option_items_container) table.insert(horizontal_group, option_items_container)
table.insert(vertical_group, horizontal_group) table.insert(vertical_group, horizontal_group)
end -- if end -- if
end -- for end -- for
table.insert(vertical_group, VerticalSpan:new{ width = default_option_padding }) table.insert(vertical_group, VerticalSpan:new{ width = default_option_padding })
self[1] = vertical_group self[1] = vertical_group
self.dimen = vertical_group:getSize() self.dimen = vertical_group:getSize()
end end
local ConfigPanel = FrameContainer:new{ background = 0, bordersize = 0, } local ConfigPanel = FrameContainer:new{ background = 0, bordersize = 0, }
function ConfigPanel:init() function ConfigPanel:init()
local config_options = self.config_dialog.config_options local config_options = self.config_dialog.config_options
local default_option = config_options.default_options and config_options.default_options local default_option = config_options.default_options and config_options.default_options
or config_options[1].options or config_options[1].options
local panel = ConfigOption:new{ local panel = ConfigOption:new{
options = self.index and config_options[self.index].options or default_option, options = self.index and config_options[self.index].options or default_option,
config = self.config_dialog, config = self.config_dialog,
} }
self.dimen = panel:getSize() self.dimen = panel:getSize()
table.insert(self, panel) table.insert(self, panel)
end end
local MenuBar = FrameContainer:new{ background = 0, } local MenuBar = FrameContainer:new{ background = 0, }
function MenuBar:init() function MenuBar:init()
local config_options = self.config_dialog.config_options local config_options = self.config_dialog.config_options
local menu_items = {} local menu_items = {}
local icons_width = 0 local icons_width = 0
local icons_height = 0 local icons_height = 0
for c = 1, #config_options do for c = 1, #config_options do
local menu_icon = ImageWidget:new{ local menu_icon = ImageWidget:new{
file = config_options[c].icon file = config_options[c].icon
} }
local icon_dimen = menu_icon:getSize() local icon_dimen = menu_icon:getSize()
icons_width = icons_width + icon_dimen.w icons_width = icons_width + icon_dimen.w
icons_height = icon_dimen.h > icons_height and icon_dimen.h or icons_height icons_height = icon_dimen.h > icons_height and icon_dimen.h or icons_height
menu_items[c] = MenuBarItem:new{ menu_items[c] = MenuBarItem:new{
menu_icon, menu_icon,
index = c, index = c,
config = self.config_dialog, config = self.config_dialog,
} }
end end
local spacing = HorizontalSpan:new{ local spacing = HorizontalSpan:new{
width = (Screen:getWidth() - icons_width) / (#menu_items+1) width = (Screen:getWidth() - icons_width) / (#menu_items+1)
} }
local menu_bar = HorizontalGroup:new{} local menu_bar = HorizontalGroup:new{}
for c = 1, #menu_items do for c = 1, #menu_items do
table.insert(menu_bar, spacing) table.insert(menu_bar, spacing)
table.insert(menu_bar, menu_items[c]) table.insert(menu_bar, menu_items[c])
end end
table.insert(menu_bar, spacing) table.insert(menu_bar, spacing)
self.dimen = Geom:new{ w = Screen:getWidth(), h = icons_height} self.dimen = Geom:new{ w = Screen:getWidth(), h = icons_height}
table.insert(self, menu_bar) table.insert(self, menu_bar)
end end
--[[ --[[
@ -405,122 +405,122 @@ Widget that displays config menubar and config panel
--]] --]]
local ConfigDialog = InputContainer:new{ local ConfigDialog = InputContainer:new{
--is_borderless = false, --is_borderless = false,
panel_index = 1, panel_index = 1,
} }
function ConfigDialog:init() function ConfigDialog:init()
------------------------------------------ ------------------------------------------
-- start to set up widget layout --------- -- start to set up widget layout ---------
------------------------------------------ ------------------------------------------
self.config_menubar = MenuBar:new{ self.config_menubar = MenuBar:new{
config_dialog = self, config_dialog = self,
} }
self:update() self:update()
------------------------------------------ ------------------------------------------
-- start to set up input event callback -- -- start to set up input event callback --
------------------------------------------ ------------------------------------------
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events.TapCloseMenu = { self.ges_events.TapCloseMenu = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
} }
else else
-- set up keyboard events -- set up keyboard events
self.key_events.Close = { {"Back"}, doc = _("close config menu") } self.key_events.Close = { {"Back"}, doc = _("close config menu") }
-- we won't catch presses to "Right" -- we won't catch presses to "Right"
self.key_events.FocusRight = nil self.key_events.FocusRight = nil
end end
self.key_events.Select = { {"Press"}, doc = _("select current menu item") } self.key_events.Select = { {"Press"}, doc = _("select current menu item") }
end end
function ConfigDialog:updateConfigPanel(index) function ConfigDialog:updateConfigPanel(index)
end end
function ConfigDialog:update() function ConfigDialog:update()
self.config_panel = ConfigPanel:new{ self.config_panel = ConfigPanel:new{
index = self.panel_index, index = self.panel_index,
config_dialog = self, config_dialog = self,
} }
self.dialog_frame = FrameContainer:new{ self.dialog_frame = FrameContainer:new{
background = 0, background = 0,
VerticalGroup:new{ VerticalGroup:new{
self.config_panel, self.config_panel,
self.config_menubar, self.config_menubar,
}, },
} }
self[1] = BottomContainer:new{ self[1] = BottomContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
self.dialog_frame, self.dialog_frame,
} }
end end
function ConfigDialog:onShowConfigPanel(index) function ConfigDialog:onShowConfigPanel(index)
self.panel_index = index self.panel_index = index
self:update() self:update()
UIManager.repaint_all = true UIManager.repaint_all = true
return true return true
end end
function ConfigDialog:onConfigChoice(option_name, option_value) function ConfigDialog:onConfigChoice(option_name, option_value)
--DEBUG("config option value", option_name, option_value) --DEBUG("config option value", option_name, option_value)
self.configurable[option_name] = option_value self.configurable[option_name] = option_value
self.ui:handleEvent(Event:new("StartActivityIndicator")) self.ui:handleEvent(Event:new("StartActivityIndicator"))
return true return true
end end
function ConfigDialog:onConfigEvent(option_event, option_arg) function ConfigDialog:onConfigEvent(option_event, option_arg)
--DEBUG("config option event", option_event, option_arg) --DEBUG("config option event", option_event, option_arg)
self.ui:handleEvent(Event:new(option_event, option_arg)) self.ui:handleEvent(Event:new(option_event, option_arg))
return true return true
end end
function ConfigDialog:onConfigEvents(option_events, arg_index) function ConfigDialog:onConfigEvents(option_events, arg_index)
--DEBUG("config option events", option_events, arg_index) --DEBUG("config option events", option_events, arg_index)
for i=1, #option_events do for i=1, #option_events do
option_events[i].args = option_events[i].args or {} option_events[i].args = option_events[i].args or {}
self.ui:handleEvent(Event:new(option_events[i].event, option_events[i].args[arg_index])) self.ui:handleEvent(Event:new(option_events[i].event, option_events[i].args[arg_index]))
end end
return true return true
end end
function ConfigDialog:onConfigChoose(values, name, event, args, events, position) function ConfigDialog:onConfigChoose(values, name, event, args, events, position)
UIManager:scheduleIn(0.05, function() UIManager:scheduleIn(0.05, function()
if values then if values then
self:onConfigChoice(name, values[position]) self:onConfigChoice(name, values[position])
end end
if event then if event then
args = args or {} args = args or {}
self:onConfigEvent(event, args[position]) self:onConfigEvent(event, args[position])
end end
if events then if events then
self:onConfigEvents(events, position) self:onConfigEvents(events, position)
end end
UIManager.repaint_all = true UIManager.repaint_all = true
end) end)
end end
function ConfigDialog:closeDialog() function ConfigDialog:closeDialog()
UIManager:close(self) UIManager:close(self)
if self.close_callback then if self.close_callback then
self.close_callback() self.close_callback()
end end
return true return true
end end
function ConfigDialog:onTapCloseMenu(arg, ges_ev) function ConfigDialog:onTapCloseMenu(arg, ges_ev)
if ges_ev.pos:notIntersectWith(self.dialog_frame.dimen) then if ges_ev.pos:notIntersectWith(self.dialog_frame.dimen) then
self:closeDialog() self:closeDialog()
return true return true
end end
end end
return ConfigDialog return ConfigDialog

@ -20,86 +20,86 @@ local _ = require("gettext")
Widget that shows a message and OK/Cancel buttons Widget that shows a message and OK/Cancel buttons
]] ]]
local ConfirmBox = FocusManager:new{ local ConfirmBox = FocusManager:new{
text = _("no text"), text = _("no text"),
width = nil, width = nil,
ok_text = _("OK"), ok_text = _("OK"),
cancel_text = _("Cancel"), cancel_text = _("Cancel"),
ok_callback = function() end, ok_callback = function() end,
cancel_callback = function() end, cancel_callback = function() end,
} }
function ConfirmBox:init() function ConfirmBox:init()
-- calculate box width on the fly if not given -- calculate box width on the fly if not given
if not self.width then if not self.width then
self.width = Screen:getWidth() - 200 self.width = Screen:getWidth() - 200
end end
-- build bottons -- build bottons
self.key_events.Close = { {{"Home","Back"}}, doc = _("cancel") } self.key_events.Close = { {{"Home","Back"}}, doc = _("cancel") }
self.key_events.Select = { {{"Enter","Press"}}, doc = _("chose selected option") } self.key_events.Select = { {{"Enter","Press"}}, doc = _("chose selected option") }
local ok_button = Button:new{ local ok_button = Button:new{
text = self.ok_text, text = self.ok_text,
callback = function() callback = function()
self.ok_callback() self.ok_callback()
UIManager:close(self) UIManager:close(self)
end, end,
} }
local cancel_button = Button:new{ local cancel_button = Button:new{
text = self.cancel_text, text = self.cancel_text,
preselect = true, preselect = true,
callback = function() callback = function()
self.cancel_callback() self.cancel_callback()
UIManager:close(self) UIManager:close(self)
end, end,
} }
self.layout = { { ok_button, cancel_button } } self.layout = { { ok_button, cancel_button } }
self.selected.x = 2 -- Cancel is default self.selected.x = 2 -- Cancel is default
self[1] = CenterContainer:new{ self[1] = CenterContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
FrameContainer:new{ FrameContainer:new{
margin = 2, margin = 2,
background = 0, background = 0,
padding = 10, padding = 10,
HorizontalGroup:new{ HorizontalGroup:new{
ImageWidget:new{ ImageWidget:new{
file = "resources/info-i.png" file = "resources/info-i.png"
}, },
HorizontalSpan:new{ width = 10 }, HorizontalSpan:new{ width = 10 },
VerticalGroup:new{ VerticalGroup:new{
align = "left", align = "left",
TextBoxWidget:new{ TextBoxWidget:new{
text = self.text, text = self.text,
face = Font:getFace("cfont", 30), face = Font:getFace("cfont", 30),
width = self.width, width = self.width,
}, },
VerticalSpan:new{ width = 10 }, VerticalSpan:new{ width = 10 },
HorizontalGroup:new{ HorizontalGroup:new{
ok_button, ok_button,
HorizontalSpan:new{ width = 10 }, HorizontalSpan:new{ width = 10 },
cancel_button, cancel_button,
} }
} }
} }
} }
} }
end end
function ConfirmBox:onClose() function ConfirmBox:onClose()
UIManager:close(self) UIManager:close(self)
return true return true
end end
function ConfirmBox:onSelect() function ConfirmBox:onSelect()
DEBUG("selected:", self.selected.x) DEBUG("selected:", self.selected.x)
if self.selected.x == 1 then if self.selected.x == 1 then
self:ok_callback() self:ok_callback()
else else
self:cancel_callback() self:cancel_callback()
end end
UIManager:close(self) UIManager:close(self)
return true return true
end end
return ConfirmBox return ConfirmBox

@ -8,24 +8,24 @@ dimensions
local BottomContainer = WidgetContainer:new() local BottomContainer = WidgetContainer:new()
function BottomContainer:paintTo(bb, x, y) function BottomContainer:paintTo(bb, x, y)
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
-- throw error? paint to scrap buffer and blit partially? -- throw error? paint to scrap buffer and blit partially?
-- for now, we ignore this -- for now, we ignore this
end end
self[1]:paintTo(bb, self[1]:paintTo(bb,
x + math.floor((self.dimen.w - contentSize.w)/2), x + math.floor((self.dimen.w - contentSize.w)/2),
y + (self.dimen.h - contentSize.h)) y + (self.dimen.h - contentSize.h))
end end
function BottomContainer:contentRange() function BottomContainer:contentRange()
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
return Geom:new{ return Geom:new{
x = (self.dimen.x or 0) + math.floor((self.dimen.w - contentSize.w)/2), 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, y = (self.dimen.y or 0) + self.dimen.h - contentSize.h,
w = contentSize.w, w = contentSize.w,
h = contentSize.h h = contentSize.h
} }
end end
return BottomContainer return BottomContainer

@ -6,20 +6,20 @@ CenterContainer centers its content (1 widget) within its own dimensions
local CenterContainer = WidgetContainer:new() local CenterContainer = WidgetContainer:new()
function CenterContainer:paintTo(bb, x, y) function CenterContainer:paintTo(bb, x, y)
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
-- throw error? paint to scrap buffer and blit partially? -- throw error? paint to scrap buffer and blit partially?
-- for now, we ignore this -- for now, we ignore this
end end
local x_pos = x local x_pos = x
local y_pos = y local y_pos = y
if self.ignore ~= "height" then if self.ignore ~= "height" then
y_pos = y + math.floor((self.dimen.h - contentSize.h)/2) y_pos = y + math.floor((self.dimen.h - contentSize.h)/2)
end end
if self.ignore ~= "width" then if self.ignore ~= "width" then
x_pos = x + math.floor((self.dimen.w - contentSize.w)/2) x_pos = x + math.floor((self.dimen.w - contentSize.w)/2)
end end
self[1]:paintTo(bb, x_pos, y_pos) self[1]:paintTo(bb, x_pos, y_pos)
end end
return CenterContainer return CenterContainer

@ -6,56 +6,56 @@ A FrameContainer is some graphics content (1 widget) that is surrounded by a
frame frame
--]] --]]
local FrameContainer = WidgetContainer:new{ local FrameContainer = WidgetContainer:new{
background = nil, background = nil,
color = 15, color = 15,
margin = 0, margin = 0,
radius = 0, radius = 0,
bordersize = 2, bordersize = 2,
padding = 5, padding = 5,
width = nil, width = nil,
height = nil, height = nil,
invert = false, invert = false,
} }
function FrameContainer:getSize() function FrameContainer:getSize()
local content_size = self[1]:getSize() local content_size = self[1]:getSize()
return Geom:new{ return Geom:new{
w = content_size.w + ( self.margin + self.bordersize + self.padding ) * 2, w = content_size.w + ( self.margin + self.bordersize + self.padding ) * 2,
h = content_size.h + ( self.margin + self.bordersize + self.padding ) * 2 h = content_size.h + ( self.margin + self.bordersize + self.padding ) * 2
} }
end end
function FrameContainer:paintTo(bb, x, y) function FrameContainer:paintTo(bb, x, y)
local my_size = self:getSize() local my_size = self:getSize()
self.dimen = Geom:new{ self.dimen = Geom:new{
x = x, y = y, x = x, y = y,
w = my_size.w, w = my_size.w,
h = my_size.h h = my_size.h
} }
local container_width = self.width or my_size.w local container_width = self.width or my_size.w
local container_height = self.height or my_size.h local container_height = self.height or my_size.h
--@TODO get rid of margin here? 13.03 2013 (houqp) --@TODO get rid of margin here? 13.03 2013 (houqp)
if self.background then if self.background then
bb:paintRoundedRect(x, y, container_width, container_height, bb:paintRoundedRect(x, y, container_width, container_height,
self.background, self.radius) self.background, self.radius)
end end
if self.bordersize > 0 then if self.bordersize > 0 then
bb:paintBorder(x + self.margin, y + self.margin, bb:paintBorder(x + self.margin, y + self.margin,
container_width - self.margin * 2, container_width - self.margin * 2,
container_height - self.margin * 2, container_height - self.margin * 2,
self.bordersize, self.color, self.radius) self.bordersize, self.color, self.radius)
end end
if self[1] then if self[1] then
self[1]:paintTo(bb, self[1]:paintTo(bb,
x + self.margin + self.bordersize + self.padding, x + self.margin + self.bordersize + self.padding,
y + self.margin + self.bordersize + self.padding) y + self.margin + self.bordersize + self.padding)
end end
if self.invert then if self.invert then
bb:invertRect(x + self.bordersize, y + self.bordersize, bb:invertRect(x + self.bordersize, y + self.bordersize,
container_width - 2*self.bordersize, container_width - 2*self.bordersize,
container_height - 2*self.bordersize) container_height - 2*self.bordersize)
end end
end end
return FrameContainer return FrameContainer

@ -8,60 +8,60 @@ an InputContainer is an WidgetContainer that handles input events
an example for a key_event is this: an example for a key_event is this:
PanBy20 = { PanBy20 = {
{ "Shift", Input.group.Cursor }, { "Shift", Input.group.Cursor },
seqtext = "Shift+Cursor", seqtext = "Shift+Cursor",
doc = "pan by 20px", doc = "pan by 20px",
event = "Pan", args = 20, is_inactive = true, event = "Pan", args = 20, is_inactive = true,
}, },
PanNormal = { PanNormal = {
{ Input.group.Cursor }, { Input.group.Cursor },
seqtext = "Cursor", seqtext = "Cursor",
doc = "pan by 10 px", event = "Pan", args = 10, doc = "pan by 10 px", event = "Pan", args = 10,
}, },
Quit = { {"Home"} }, Quit = { {"Home"} },
it is suggested to reference configurable sequences from another table it is suggested to reference configurable sequences from another table
and store that table as configuration setting and store that table as configuration setting
--]] --]]
local InputContainer = WidgetContainer:new{ local InputContainer = WidgetContainer:new{
vertical_align = "top", vertical_align = "top",
} }
function InputContainer:_init() function InputContainer:_init()
-- we need to do deep copy here -- we need to do deep copy here
local new_key_events = {} local new_key_events = {}
if self.key_events then if self.key_events then
for k,v in pairs(self.key_events) do for k,v in pairs(self.key_events) do
new_key_events[k] = v new_key_events[k] = v
end end
end end
self.key_events = new_key_events self.key_events = new_key_events
local new_ges_events = {} local new_ges_events = {}
if self.ges_events then if self.ges_events then
for k,v in pairs(self.ges_events) do for k,v in pairs(self.ges_events) do
new_ges_events[k] = v new_ges_events[k] = v
end end
end end
self.ges_events = new_ges_events self.ges_events = new_ges_events
if not self.dimen then if not self.dimen then
self.dimen = Geom:new{} self.dimen = Geom:new{}
end end
end end
function InputContainer:paintTo(bb, x, y) function InputContainer:paintTo(bb, x, y)
self.dimen.x = x self.dimen.x = x
self.dimen.y = y self.dimen.y = y
if self[1] then if self[1] then
if self.vertical_align == "center" then if self.vertical_align == "center" then
local content_size = self[1]:getSize() local content_size = self[1]:getSize()
self[1]:paintTo(bb, x, y + math.floor((self.dimen.h - content_size.h)/2)) self[1]:paintTo(bb, x, y + math.floor((self.dimen.h - content_size.h)/2))
else else
self[1]:paintTo(bb, x, y) self[1]:paintTo(bb, x, y)
end end
end 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 if this is the case, we retransmit another event within ourselves
--]] --]]
function InputContainer:onKeyPress(key) function InputContainer:onKeyPress(key)
for name, seq in pairs(self.key_events) do for name, seq in pairs(self.key_events) do
if not seq.is_inactive then if not seq.is_inactive then
for _, oneseq in ipairs(seq) do for _, oneseq in ipairs(seq) do
if key:match(oneseq) then if key:match(oneseq) then
local eventname = seq.event or name local eventname = seq.event or name
return self:handleEvent(Event:new(eventname, seq.args, key)) return self:handleEvent(Event:new(eventname, seq.args, key))
end end
end end
end end
end end
end end
function InputContainer:onGesture(ev) function InputContainer:onGesture(ev)
for name, gsseq in pairs(self.ges_events) do for name, gsseq in pairs(self.ges_events) do
for _, gs_range in ipairs(gsseq) do for _, gs_range in ipairs(gsseq) do
--DEBUG("gs_range", gs_range) --DEBUG("gs_range", gs_range)
if gs_range:match(ev) then if gs_range:match(ev) then
local eventname = gsseq.event or name local eventname = gsseq.event or name
return self:handleEvent(Event:new(eventname, gsseq.args, ev)) return self:handleEvent(Event:new(eventname, gsseq.args, ev))
end end
end end
end end
end end
return InputContainer return InputContainer

@ -6,12 +6,12 @@ LeftContainer aligns its content (1 widget) at the left of its own dimensions
local LeftContainer = WidgetContainer:new() local LeftContainer = WidgetContainer:new()
function LeftContainer:paintTo(bb, x, y) function LeftContainer:paintTo(bb, x, y)
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
-- throw error? paint to scrap buffer and blit partially? -- throw error? paint to scrap buffer and blit partially?
-- for now, we ignore this -- for now, we ignore this
end end
self[1]:paintTo(bb, x , y + math.floor((self.dimen.h - contentSize.h)/2)) self[1]:paintTo(bb, x , y + math.floor((self.dimen.h - contentSize.h)/2))
end end
return LeftContainer return LeftContainer

@ -6,14 +6,14 @@ RightContainer aligns its content (1 widget) at the right of its own dimensions
local RightContainer = WidgetContainer:new() local RightContainer = WidgetContainer:new()
function RightContainer:paintTo(bb, x, y) function RightContainer:paintTo(bb, x, y)
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
-- throw error? paint to scrap buffer and blit partially? -- throw error? paint to scrap buffer and blit partially?
-- for now, we ignore this -- for now, we ignore this
end end
self[1]:paintTo(bb, self[1]:paintTo(bb,
x + (self.dimen.w - contentSize.w), x + (self.dimen.w - contentSize.w),
y + math.floor((self.dimen.h - contentSize.h)/2)) y + math.floor((self.dimen.h - contentSize.h)/2))
end end
return RightContainer return RightContainer

@ -7,36 +7,36 @@ a line under its child node
--]] --]]
local UnderlineContainer = WidgetContainer:new{ local UnderlineContainer = WidgetContainer:new{
linesize = 2, linesize = 2,
padding = 1, padding = 1,
color = 0, color = 0,
vertical_align = "top", vertical_align = "top",
} }
function UnderlineContainer:getSize() function UnderlineContainer:getSize()
return self:getContentSize() return self:getContentSize()
end end
function UnderlineContainer:getContentSize() function UnderlineContainer:getContentSize()
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
return Geom:new{ return Geom:new{
w = contentSize.w, w = contentSize.w,
h = contentSize.h + self.linesize + self.padding h = contentSize.h + self.linesize + self.padding
} }
end end
function UnderlineContainer:paintTo(bb, x, y) function UnderlineContainer:paintTo(bb, x, y)
local container_size = self:getSize() local container_size = self:getSize()
local content_size = self:getContentSize() local content_size = self:getContentSize()
local p_y = y local p_y = y
if self.vertical_align == "center" then if self.vertical_align == "center" then
p_y = math.floor((container_size.h - content_size.h) / 2) + y p_y = math.floor((container_size.h - content_size.h) / 2) + y
elseif self.vertical_align == "bottom" then elseif self.vertical_align == "bottom" then
p_y = (container_size.h - content_size.h) + y p_y = (container_size.h - content_size.h) + y
end end
self[1]:paintTo(bb, x, p_y) self[1]:paintTo(bb, x, p_y)
bb:paintRect(x, y + container_size.h - self.linesize, bb:paintRect(x, y + container_size.h - self.linesize,
container_size.w, self.linesize, self.color) container_size.w, self.linesize, self.color)
end end
return UnderlineContainer return UnderlineContainer

@ -7,83 +7,83 @@ WidgetContainer is a container for another Widget
local WidgetContainer = Widget:new() local WidgetContainer = Widget:new()
function WidgetContainer:init() function WidgetContainer:init()
if not self.dimen then if not self.dimen then
self.dimen = Geom:new{} self.dimen = Geom:new{}
end end
if not self.dimen.w then if not self.dimen.w then
self.dimen.w = self[1].getSize().w self.dimen.w = self[1].getSize().w
end end
if not self.dimen.h then if not self.dimen.h then
self.dimen.h = self[1].getSize().h self.dimen.h = self[1].getSize().h
end end
end end
function WidgetContainer:getSize() function WidgetContainer:getSize()
if self.dimen then if self.dimen then
-- fixed size -- fixed size
return self.dimen return self.dimen
elseif self[1] then elseif self[1] then
-- return size of first child widget -- return size of first child widget
return self[1]:getSize() return self[1]:getSize()
else else
return Geom:new{ w = 0, h = 0 } return Geom:new{ w = 0, h = 0 }
end end
end end
--[[ --[[
delete all child widgets delete all child widgets
--]] --]]
function WidgetContainer:clear() function WidgetContainer:clear()
while table.remove(self) do end while table.remove(self) do end
end end
function WidgetContainer:paintTo(bb, x, y) function WidgetContainer:paintTo(bb, x, y)
-- default to pass request to first child widget -- default to pass request to first child widget
if self[1] then if self[1] then
x = x + (self.dimen.x or 0) x = x + (self.dimen.x or 0)
y = y + (self.dimen.y or 0) y = y + (self.dimen.y or 0)
if self.align == "top" then if self.align == "top" then
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
self[1]:paintTo(bb, self[1]:paintTo(bb,
x + math.floor((self.dimen.w - contentSize.w)/2), y) x + math.floor((self.dimen.w - contentSize.w)/2), y)
elseif self.align == "bottom" then elseif self.align == "bottom" then
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
self[1]:paintTo(bb, self[1]:paintTo(bb,
x + math.floor((self.dimen.w - contentSize.w)/2), x + math.floor((self.dimen.w - contentSize.w)/2),
y + (self.dimen.h - contentSize.h)) y + (self.dimen.h - contentSize.h))
else else
return self[1]:paintTo(bb, x, y) return self[1]:paintTo(bb, x, y)
end end
end end
end end
function WidgetContainer:propagateEvent(event) function WidgetContainer:propagateEvent(event)
-- propagate to children -- propagate to children
for _, widget in ipairs(self) do for _, widget in ipairs(self) do
if widget:handleEvent(event) then if widget:handleEvent(event) then
-- stop propagating when an event handler returns true -- stop propagating when an event handler returns true
return true return true
end end
end end
return false return false
end end
--[[ --[[
Containers will pass events to children or react on them themselves Containers will pass events to children or react on them themselves
--]] --]]
function WidgetContainer:handleEvent(event) function WidgetContainer:handleEvent(event)
if not self:propagateEvent(event) then if not self:propagateEvent(event) then
-- call our own standard event handler -- call our own standard event handler
return Widget.handleEvent(self, event) return Widget.handleEvent(self, event)
else else
return true return true
end end
end end
function WidgetContainer:free() function WidgetContainer:free()
for _, widget in ipairs(self) do for _, widget in ipairs(self) do
if widget.free then widget:free() end if widget.free then widget:free() end
end end
end end
return WidgetContainer return WidgetContainer

@ -24,280 +24,280 @@ local _ = require("gettext")
Display quick lookup word definition Display quick lookup word definition
]] ]]
local DictQuickLookup = InputContainer:new{ local DictQuickLookup = InputContainer:new{
results = nil, results = nil,
lookupword = nil, lookupword = nil,
dictionary = nil, dictionary = nil,
definition = nil, definition = nil,
dict_index = 1, dict_index = 1,
title_face = Font:getFace("tfont", 22), title_face = Font:getFace("tfont", 22),
word_face = Font:getFace("tfont", 22), word_face = Font:getFace("tfont", 22),
content_face = Font:getFace("cfont", DDICT_FONT_SIZE), content_face = Font:getFace("cfont", DDICT_FONT_SIZE),
width = nil, width = nil,
height = nil, height = nil,
title_padding = Screen:scaleByDPI(5), title_padding = Screen:scaleByDPI(5),
title_margin = Screen:scaleByDPI(2), title_margin = Screen:scaleByDPI(2),
word_padding = Screen:scaleByDPI(2), word_padding = Screen:scaleByDPI(2),
word_margin = Screen:scaleByDPI(2), word_margin = Screen:scaleByDPI(2),
definition_padding = Screen:scaleByDPI(2), definition_padding = Screen:scaleByDPI(2),
definition_margin = Screen:scaleByDPI(2), definition_margin = Screen:scaleByDPI(2),
button_padding = Screen:scaleByDPI(14), button_padding = Screen:scaleByDPI(14),
} }
function DictQuickLookup:init() function DictQuickLookup:init()
self:changeToDefaultDict() self:changeToDefaultDict()
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapCloseDict = { TapCloseDict = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
}, },
}, },
Swipe = { Swipe = {
GestureRange:new{ GestureRange:new{
ges = "swipe", ges = "swipe",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
}, },
}, },
} }
table.insert(self.dict_bar, table.insert(self.dict_bar,
CloseButton:new{ CloseButton:new{
window = self, window = self,
}) })
end end
end end
function DictQuickLookup:update() function DictQuickLookup:update()
-- dictionary title -- dictionary title
self.dict_title = FrameContainer:new{ self.dict_title = FrameContainer:new{
padding = self.title_padding, padding = self.title_padding,
margin = self.title_margin, margin = self.title_margin,
bordersize = 0, bordersize = 0,
TextWidget:new{ TextWidget:new{
text = self.dictionary, text = self.dictionary,
face = self.title_face, face = self.title_face,
bold = true, bold = true,
width = self.width - self.button_padding, width = self.width - self.button_padding,
} }
} }
-- lookup word -- lookup word
local lookup_word = FrameContainer:new{ local lookup_word = FrameContainer:new{
padding = self.word_padding, padding = self.word_padding,
margin = self.word_margin, margin = self.word_margin,
bordersize = 0, bordersize = 0,
TextBoxWidget:new{ TextBoxWidget:new{
text = self.lookupword, text = self.lookupword,
face = self.word_face, face = self.word_face,
bold = true, bold = true,
width = self.width, width = self.width,
}, },
} }
-- word definition -- word definition
local definition = FrameContainer:new{ local definition = FrameContainer:new{
padding = self.definition_padding, padding = self.definition_padding,
margin = self.definition_margin, margin = self.definition_margin,
bordersize = 0, bordersize = 0,
ScrollTextWidget:new{ ScrollTextWidget:new{
text = self.definition, text = self.definition,
face = self.content_face, face = self.content_face,
width = self.width, width = self.width,
height = self.height*0.7, height = self.height*0.7,
dialog = self, dialog = self,
}, },
} }
local button_table = ButtonTable:new{ local button_table = ButtonTable:new{
width = math.max(self.width, definition:getSize().w), width = math.max(self.width, definition:getSize().w),
button_font_face = "cfont", button_font_face = "cfont",
button_font_size = 20, button_font_size = 20,
buttons = { buttons = {
{ {
{ {
text = _("<<"), text = _("<<"),
enabled = self:isPrevDictAvaiable(), enabled = self:isPrevDictAvaiable(),
callback = function() callback = function()
self:changeToPrevDict() self:changeToPrevDict()
end, end,
}, },
{ {
text = _(">>"), text = _(">>"),
enabled = self:isNextDictAvaiable(), enabled = self:isNextDictAvaiable(),
callback = function() callback = function()
self:changeToNextDict() self:changeToNextDict()
end, end,
}, },
}, },
{ {
{ {
text = _("Highlight"), text = _("Highlight"),
enabled = false, enabled = false,
callback = function() callback = function()
self.ui:handleEvent(Event:new("Highlight")) self.ui:handleEvent(Event:new("Highlight"))
end, end,
}, },
{ {
text = _("Add Note"), text = _("Add Note"),
enabled = false, enabled = false,
callback = function() callback = function()
self.ui:handleEvent(Event:new("AddNote")) self.ui:handleEvent(Event:new("AddNote"))
end, end,
}, },
}, },
}, },
zero_sep = true, zero_sep = true,
} }
local title_bar = LineWidget:new{ local title_bar = LineWidget:new{
--background = 8, --background = 8,
dimen = Geom:new{ dimen = Geom:new{
w = button_table:getSize().w + self.button_padding, w = button_table:getSize().w + self.button_padding,
h = Screen:scaleByDPI(2), h = Screen:scaleByDPI(2),
} }
} }
self.dict_bar = OverlapGroup:new{ self.dict_bar = OverlapGroup:new{
dimen = {w = button_table:getSize().w, h = self.dict_title:getSize().h}, dimen = {w = button_table:getSize().w, h = self.dict_title:getSize().h},
self.dict_title, self.dict_title,
} }
self.dict_frame = FrameContainer:new{ self.dict_frame = FrameContainer:new{
radius = 8, radius = 8,
bordersize = 3, bordersize = 3,
padding = 0, padding = 0,
margin = 0, margin = 0,
background = 0, background = 0,
VerticalGroup:new{ VerticalGroup:new{
align = "left", align = "left",
self.dict_bar, self.dict_bar,
title_bar, title_bar,
-- word -- word
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = title_bar:getSize().w, w = title_bar:getSize().w,
h = lookup_word:getSize().h, h = lookup_word:getSize().h,
}, },
lookup_word, lookup_word,
}, },
-- definition -- definition
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = title_bar:getSize().w, w = title_bar:getSize().w,
h = definition:getSize().h, h = definition:getSize().h,
}, },
definition, definition,
}, },
-- buttons -- buttons
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = title_bar:getSize().w, w = title_bar:getSize().w,
h = button_table:getSize().h, h = button_table:getSize().h,
}, },
button_table, button_table,
} }
} }
} }
self[1] = WidgetContainer:new{ self[1] = WidgetContainer:new{
align = self.align, align = self.align,
dimen = self.region:copy(), dimen = self.region:copy(),
FrameContainer:new{ FrameContainer:new{
bordersize = 0, bordersize = 0,
padding = Screen:scaleByDPI(5), padding = Screen:scaleByDPI(5),
self.dict_frame, self.dict_frame,
} }
} }
UIManager.repaint_all = true UIManager.repaint_all = true
UIManager.full_refresh = true UIManager.full_refresh = true
end end
function DictQuickLookup:isPrevDictAvaiable() function DictQuickLookup:isPrevDictAvaiable()
return self.dict_index > 1 return self.dict_index > 1
end end
function DictQuickLookup:isNextDictAvaiable() function DictQuickLookup:isNextDictAvaiable()
return self.dict_index < #self.results return self.dict_index < #self.results
end end
function DictQuickLookup:changeToPrevDict() function DictQuickLookup:changeToPrevDict()
self:changeDictionary(self.dict_index - 1) self:changeDictionary(self.dict_index - 1)
end end
function DictQuickLookup:changeToNextDict() function DictQuickLookup:changeToNextDict()
self:changeDictionary(self.dict_index + 1) self:changeDictionary(self.dict_index + 1)
end end
function DictQuickLookup:changeDictionary(index) function DictQuickLookup:changeDictionary(index)
self.dict_index = index self.dict_index = index
self.dictionary = self.results[index].dict self.dictionary = self.results[index].dict
self.lookupword = self.results[index].word self.lookupword = self.results[index].word
self.definition = self.results[index].definition self.definition = self.results[index].definition
local orig_dimen = self.dict_frame and self.dict_frame.dimen or Geom:new{} local orig_dimen = self.dict_frame and self.dict_frame.dimen or Geom:new{}
self:update() self:update()
UIManager.update_region_func = function() UIManager.update_region_func = function()
local update_region = self.dict_frame.dimen:combine(orig_dimen) local update_region = self.dict_frame.dimen:combine(orig_dimen)
DEBUG("update region", update_region) DEBUG("update region", update_region)
return update_region return update_region
end end
end end
function DictQuickLookup:changeToDefaultDict() function DictQuickLookup:changeToDefaultDict()
if self.dictionary then if self.dictionary then
-- dictionaries that have definition of the first word(accurate word) -- dictionaries that have definition of the first word(accurate word)
-- excluding Fuzzy queries. -- excluding Fuzzy queries.
local n_accurate_dicts = nil local n_accurate_dicts = nil
local default_word = self.results[1].word local default_word = self.results[1].word
for i=1, #self.results do for i=1, #self.results do
if self.results[i].word == default_word then if self.results[i].word == default_word then
n_accurate_dicts = i n_accurate_dicts = i
else else
break break
end end
end end
-- change to dictionary specified by self.dictionary -- change to dictionary specified by self.dictionary
for i=1, n_accurate_dicts do for i=1, n_accurate_dicts do
if self.results[i].dict == self.dictionary then if self.results[i].dict == self.dictionary then
self:changeDictionary(i) self:changeDictionary(i)
break break
end end
-- cannot find definition in default dictionary -- cannot find definition in default dictionary
if i == n_accurate_dicts then if i == n_accurate_dicts then
self:changeDictionary(1) self:changeDictionary(1)
end end
end end
else else
self:changeDictionary(1) self:changeDictionary(1)
end end
end end
function DictQuickLookup:onAnyKeyPressed() function DictQuickLookup:onAnyKeyPressed()
-- triggered by our defined key events -- triggered by our defined key events
UIManager:close(self) UIManager:close(self)
return true return true
end end
function DictQuickLookup:onTapCloseDict(arg, ges_ev) function DictQuickLookup:onTapCloseDict(arg, ges_ev)
if ges_ev.pos:notIntersectWith(self.dict_frame.dimen) then if ges_ev.pos:notIntersectWith(self.dict_frame.dimen) then
self:onClose() self:onClose()
return true return true
elseif not ges_ev.pos:notIntersectWith(self.dict_title.dimen) then elseif not ges_ev.pos:notIntersectWith(self.dict_title.dimen) then
self.ui:handleEvent(Event:new("UpdateDefaultDict", self.dictionary)) self.ui:handleEvent(Event:new("UpdateDefaultDict", self.dictionary))
return true return true
end end
return true return true
end end
function DictQuickLookup:onClose() function DictQuickLookup:onClose()
UIManager:close(self) UIManager:close(self)
self.highlight:handleEvent(Event:new("Tap")) self.highlight:handleEvent(Event:new("Tap"))
return true return true
end end
return DictQuickLookup return DictQuickLookup

@ -8,17 +8,17 @@ will call a method "onEventName" for an event with name
local EventListener = {} local EventListener = {}
function EventListener:new(o) function EventListener:new(o)
local o = o or {} local o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
if o.init then o:init() end if o.init then o:init() end
return o return o
end end
function EventListener:handleEvent(event) function EventListener:handleEvent(event)
if self[event.handler] then if self[event.handler] then
return self[event.handler](self, unpack(event.args)) return self[event.handler](self, unpack(event.args))
end end
end end
return EventListener return EventListener

@ -5,97 +5,97 @@ local DEBUG = require("dbg")
-- lfs -- lfs
local FileChooser = Menu:extend{ local FileChooser = Menu:extend{
height = Screen:getHeight(), height = Screen:getHeight(),
width = Screen:getWidth(), width = Screen:getWidth(),
no_title = true, no_title = true,
path = lfs.currentdir(), path = lfs.currentdir(),
parent = nil, parent = nil,
show_hidden = nil, show_hidden = nil,
show_filesize = DSHOWFILESIZE, show_filesize = DSHOWFILESIZE,
filter = function(filename) return true end, filter = function(filename) return true end,
} }
function FileChooser:init() function FileChooser:init()
self.item_table = self:genItemTableFromPath(self.path) self.item_table = self:genItemTableFromPath(self.path)
Menu.init(self) -- call parent's init() Menu.init(self) -- call parent's init()
end end
function FileChooser:genItemTableFromPath(path) function FileChooser:genItemTableFromPath(path)
local dirs = {} local dirs = {}
local files = {} local files = {}
for f in lfs.dir(self.path) do for f in lfs.dir(self.path) do
if self.show_hidden or not string.match(f, "^%.[^.]") then if self.show_hidden or not string.match(f, "^%.[^.]") then
local filename = self.path.."/"..f local filename = self.path.."/"..f
local filemode = lfs.attributes(filename, "mode") local filemode = lfs.attributes(filename, "mode")
if filemode == "directory" and f ~= "." and f~=".." then if filemode == "directory" and f ~= "." and f~=".." then
if self.dir_filter(filename) then if self.dir_filter(filename) then
table.insert(dirs, f) table.insert(dirs, f)
end end
elseif filemode == "file" then elseif filemode == "file" then
if self.file_filter(filename) then if self.file_filter(filename) then
table.insert(files, f) table.insert(files, f)
end end
end end
end end
end end
table.sort(dirs) table.sort(dirs)
if path ~= "/" then table.insert(dirs, 1, "..") end if path ~= "/" then table.insert(dirs, 1, "..") end
table.sort(files) table.sort(files)
local item_table = {} local item_table = {}
for _, dir in ipairs(dirs) do for _, dir in ipairs(dirs) do
table.insert(item_table, { text = dir.."/", path = self.path.."/"..dir }) table.insert(item_table, { text = dir.."/", path = self.path.."/"..dir })
end end
for _, file in ipairs(files) do for _, file in ipairs(files) do
local full_path = self.path.."/"..file local full_path = self.path.."/"..file
if self.show_filesize then if self.show_filesize then
local sstr = string.format("%4.1fM",lfs.attributes(full_path, "size")/1024/1024) local sstr = string.format("%4.1fM",lfs.attributes(full_path, "size")/1024/1024)
table.insert(item_table, { text = file, mandatory = sstr, path = full_path }) table.insert(item_table, { text = file, mandatory = sstr, path = full_path })
else else
table.insert(item_table, { text = file, path = full_path }) table.insert(item_table, { text = file, path = full_path })
end end
end end
return item_table return item_table
end end
function FileChooser:changeToPath(path) function FileChooser:changeToPath(path)
path = util.realpath(path) path = util.realpath(path)
self.path = path self.path = path
self:refreshPath() self:refreshPath()
end end
function FileChooser:refreshPath() function FileChooser:refreshPath()
self:swithItemTable(nil, self:genItemTableFromPath(self.path)) self:swithItemTable(nil, self:genItemTableFromPath(self.path))
end end
function FileChooser:toggleHiddenFiles() function FileChooser:toggleHiddenFiles()
self.show_hidden = not self.show_hidden self.show_hidden = not self.show_hidden
self:swithItemTable(nil, self:genItemTableFromPath(self.path)) self:swithItemTable(nil, self:genItemTableFromPath(self.path))
end end
function FileChooser:onMenuSelect(item) function FileChooser:onMenuSelect(item)
if lfs.attributes(item.path, "mode") == "directory" then if lfs.attributes(item.path, "mode") == "directory" then
self:changeToPath(item.path) self:changeToPath(item.path)
else else
self:onFileSelect(item.path) self:onFileSelect(item.path)
end end
return true return true
end end
function FileChooser:onMenuHold(item) function FileChooser:onMenuHold(item)
self:onFileHold(item.path) self:onFileHold(item.path)
return true return true
end end
function FileChooser:onFileSelect(file) function FileChooser:onFileSelect(file)
UIManager:close(self) UIManager:close(self)
return true return true
end end
function FileChooser:onFileHold(file) function FileChooser:onFileHold(file)
return true return true
end end
return FileChooser return FileChooser

@ -9,21 +9,21 @@ FixedTextWidget
local FixedTextWidget = TextWidget:new{} local FixedTextWidget = TextWidget:new{}
function FixedTextWidget:getSize() function FixedTextWidget:getSize()
local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold) local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold)
if not tsize then if not tsize then
return Geom:new{} return Geom:new{}
end end
self._length = tsize.x self._length = tsize.x
self._height = self.face.size self._height = self.face.size
return Geom:new{ return Geom:new{
w = self._length, w = self._length,
h = self._height, h = self._height,
} }
end end
function FixedTextWidget:paintTo(bb, x, y) function FixedTextWidget:paintTo(bb, x, y)
RenderText:renderUtf8Text(bb, x, y+self._height, self.face, self.text, true, self.bold, RenderText:renderUtf8Text(bb, x, y+self._height, self.face, self.text, true, self.bold,
self.bgcolor, self.fgcolor) self.bgcolor, self.fgcolor)
end end
return FixedTextWidget return FixedTextWidget

@ -8,10 +8,10 @@ Wrapper Widget that manages focus for a whole dialog
supports a 2D model of active elements supports a 2D model of active elements
e.g.: e.g.:
layout = { layout = {
{ textinput, textinput }, { textinput, textinput },
{ okbutton, cancelbutton } { okbutton, cancelbutton }
} }
this is a dialog with 2 rows. in the top row, there is the this is a dialog with 2 rows. in the top row, there is the
single (!) widget <textinput>. when the focus is in this single (!) widget <textinput>. 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. it rather defines an abstract layout.
]] ]]
local FocusManager = InputContainer:new{ local FocusManager = InputContainer:new{
selected = nil, -- defaults to x=1, y=1 selected = nil, -- defaults to x=1, y=1
layout = nil, -- mandatory layout = nil, -- mandatory
movement_allowed = { x = true, y = true } movement_allowed = { x = true, y = true }
} }
function FocusManager:init() function FocusManager:init()
self.selected = { x = 1, y = 1 } self.selected = { x = 1, y = 1 }
self.key_events = { self.key_events = {
-- these will all generate the same event, just with different arguments -- these will all generate the same event, just with different arguments
FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} }, FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} },
FocusDown = { {"Down"}, doc = "move focus down", 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} }, FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} },
FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} }, FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} },
} }
end end
function FocusManager:onFocusMove(args) function FocusManager:onFocusMove(args)
local dx, dy = unpack(args) local dx, dy = unpack(args)
if (dx ~= 0 and not self.movement_allowed.x) if (dx ~= 0 and not self.movement_allowed.x)
or (dy ~= 0 and not self.movement_allowed.y) then or (dy ~= 0 and not self.movement_allowed.y) then
return true return true
end end
if not self.layout or not self.layout[self.selected.y] or not self.layout[self.selected.y][self.selected.x] then if not self.layout or not self.layout[self.selected.y] or not self.layout[self.selected.y][self.selected.x] then
return true return true
end end
local current_item = self.layout[self.selected.y][self.selected.x] local current_item = self.layout[self.selected.y][self.selected.x]
while true do while true do
if self.selected.x + dx > #self.layout[self.selected.y] if self.selected.x + dx > #self.layout[self.selected.y]
or self.selected.x + dx < 1 then or self.selected.x + dx < 1 then
break -- abort when we run into horizontal borders break -- abort when we run into horizontal borders
end end
-- move cyclic in vertical direction -- move cyclic in vertical direction
if self.selected.y + dy > #self.layout then if self.selected.y + dy > #self.layout then
if not self:onWrapLast() then if not self:onWrapLast() then
break break
end end
elseif self.selected.y + dy < 1 then elseif self.selected.y + dy < 1 then
if not self:onWrapFirst() then if not self:onWrapFirst() then
break break
end end
else else
self.selected.y = self.selected.y + dy self.selected.y = self.selected.y + dy
end end
self.selected.x = self.selected.x + dx self.selected.x = self.selected.x + dx
if self.layout[self.selected.y][self.selected.x] ~= current_item if self.layout[self.selected.y][self.selected.x] ~= current_item
or not self.layout[self.selected.y][self.selected.x].is_inactive then or not self.layout[self.selected.y][self.selected.x].is_inactive then
-- we found a different object to focus -- we found a different object to focus
current_item:handleEvent(Event:new("Unfocus")) current_item:handleEvent(Event:new("Unfocus"))
self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus")) self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus"))
-- trigger a repaint (we need to be the registered widget!) -- trigger a repaint (we need to be the registered widget!)
UIManager:setDirty(self, "partial") UIManager:setDirty(self, "partial")
break break
end end
end end
return true return true
end end
function FocusManager:onWrapFirst() function FocusManager:onWrapFirst()
self.selected.y = #self.layout self.selected.y = #self.layout
return true return true
end end
function FocusManager:onWrapLast() function FocusManager:onWrapLast()
self.selected.y = 1 self.selected.y = 1
return true return true
end end
return FocusManager return FocusManager

@ -4,58 +4,58 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer")
A Layout widget that puts objects besides each others A Layout widget that puts objects besides each others
--]] --]]
local HorizontalGroup = WidgetContainer:new{ local HorizontalGroup = WidgetContainer:new{
align = "center", align = "center",
_size = nil, _size = nil,
} }
function HorizontalGroup:getSize() function HorizontalGroup:getSize()
if not self._size then if not self._size then
self._size = { w = 0, h = 0 } self._size = { w = 0, h = 0 }
self._offsets = { } self._offsets = { }
for i, widget in ipairs(self) do for i, widget in ipairs(self) do
local w_size = widget:getSize() local w_size = widget:getSize()
self._offsets[i] = { self._offsets[i] = {
x = self._size.w, x = self._size.w,
y = w_size.h y = w_size.h
} }
self._size.w = self._size.w + w_size.w self._size.w = self._size.w + w_size.w
if w_size.h > self._size.h then if w_size.h > self._size.h then
self._size.h = w_size.h self._size.h = w_size.h
end end
end end
end end
return self._size return self._size
end end
function HorizontalGroup:paintTo(bb, x, y) function HorizontalGroup:paintTo(bb, x, y)
local size = self:getSize() local size = self:getSize()
for i, widget in ipairs(self) do for i, widget in ipairs(self) do
if self.align == "center" then if self.align == "center" then
widget:paintTo(bb, widget:paintTo(bb,
x + self._offsets[i].x, x + self._offsets[i].x,
y + math.floor((size.h - self._offsets[i].y) / 2)) y + math.floor((size.h - self._offsets[i].y) / 2))
elseif self.align == "top" then elseif self.align == "top" then
widget:paintTo(bb, x + self._offsets[i].x, y) widget:paintTo(bb, x + self._offsets[i].x, y)
elseif self.align == "bottom" then elseif self.align == "bottom" then
widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y) widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y)
end end
end end
end end
function HorizontalGroup:clear() function HorizontalGroup:clear()
self:free() self:free()
WidgetContainer.clear(self) WidgetContainer.clear(self)
end end
function HorizontalGroup:resetLayout() function HorizontalGroup:resetLayout()
self._size = nil self._size = nil
self._offsets = {} self._offsets = {}
end end
function HorizontalGroup:free() function HorizontalGroup:free()
self:resetLayout() self:resetLayout()
WidgetContainer.free(self) WidgetContainer.free(self)
end end
return HorizontalGroup return HorizontalGroup

@ -4,11 +4,11 @@ local Widget = require("ui/widget/widget")
Dummy Widget that reserves horizontal space Dummy Widget that reserves horizontal space
--]] --]]
local HorizontalSpan = Widget:new{ local HorizontalSpan = Widget:new{
width = 0, width = 0,
} }
function HorizontalSpan:getSize() function HorizontalSpan:getSize()
return {w = self.width, h = 0} return {w = self.width, h = 0}
end end
return HorizontalSpan return HorizontalSpan

@ -7,51 +7,51 @@ local UIManager = require("ui/uimanager")
Button with a big icon image! Designed for touch device Button with a big icon image! Designed for touch device
--]] --]]
local IconButton = InputContainer:new{ local IconButton = InputContainer:new{
icon_file = "resources/info-confirm.png", icon_file = "resources/info-confirm.png",
dimen = nil, dimen = nil,
-- show_parent is used for UIManager:setDirty, so we can trigger repaint -- show_parent is used for UIManager:setDirty, so we can trigger repaint
show_parent = nil, show_parent = nil,
callback = function() end, callback = function() end,
} }
function IconButton:init() function IconButton:init()
self.image = ImageWidget:new{ self.image = ImageWidget:new{
file = self.icon_file file = self.icon_file
} }
self.show_parent = self.show_parent or self self.show_parent = self.show_parent or self
self.dimen = self.image:getSize() self.dimen = self.image:getSize()
self:initGesListener() self:initGesListener()
self[1] = self.image self[1] = self.image
end end
function IconButton:initGesListener() function IconButton:initGesListener()
self.ges_events = { self.ges_events = {
TapClickButton = { TapClickButton = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
} }
}, },
} }
end end
function IconButton:onTapClickButton() function IconButton:onTapClickButton()
self.image.invert = true self.image.invert = true
UIManager:setDirty(self.show_parent, "partial") UIManager:setDirty(self.show_parent, "partial")
-- make sure button reacts before doing callback -- make sure button reacts before doing callback
UIManager:scheduleIn(0.1, function() UIManager:scheduleIn(0.1, function()
self.callback() self.callback()
self.image.invert = false self.image.invert = false
UIManager:setDirty(self.show_parent, "partial") UIManager:setDirty(self.show_parent, "partial")
end) end)
return true return true
end end
function IconButton:onSetDimensions(new_dimen) function IconButton:onSetDimensions(new_dimen)
self.dimen = new_dimen self.dimen = new_dimen
end end
return IconButton return IconButton

@ -6,51 +6,51 @@ local Geom = require("ui/geometry")
ImageWidget shows an image from a file ImageWidget shows an image from a file
--]] --]]
local ImageWidget = Widget:new{ local ImageWidget = Widget:new{
file = nil, file = nil,
invert = nil, invert = nil,
dim = nil, dim = nil,
hide = nil, hide = nil,
_bb = nil _bb = nil
} }
function ImageWidget:_render() function ImageWidget:_render()
local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "") local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "")
if itype == "jpeg" or itype == "jpg" then if itype == "jpeg" or itype == "jpg" then
self._bb = Image:fromJPEG(self.file) self._bb = Image:fromJPEG(self.file)
elseif itype == "png" then elseif itype == "png" then
self._bb = Image:fromPNG(self.file) self._bb = Image:fromPNG(self.file)
end end
end end
function ImageWidget:getSize() function ImageWidget:getSize()
if not self._bb then if not self._bb then
self:_render() self:_render()
end end
return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() } return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() }
end end
function ImageWidget:paintTo(bb, x, y) function ImageWidget:paintTo(bb, x, y)
local size = self:getSize() local size = self:getSize()
self.dimen = Geom:new{ self.dimen = Geom:new{
x = x, y = y, x = x, y = y,
w = size.w, w = size.w,
h = size.h h = size.h
} }
if self.hide then return end if self.hide then return end
bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h) bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h)
if self.invert then if self.invert then
bb:invertRect(x, y, size.w, size.h) bb:invertRect(x, y, size.w, size.h)
end end
if self.dim then if self.dim then
bb:dimRect(x, y, size.w, size.h) bb:dimRect(x, y, size.w, size.h)
end end
end end
function ImageWidget:free() function ImageWidget:free()
if self._bb then if self._bb then
self._bb:free() self._bb:free()
self._bb = nil self._bb = nil
end end
end end
return ImageWidget return ImageWidget

@ -20,68 +20,68 @@ Widget that displays an informational message
it vanishes on key press or after a given timeout it vanishes on key press or after a given timeout
]] ]]
local InfoMessage = InputContainer:new{ local InfoMessage = InputContainer:new{
face = Font:getFace("infofont", 25), face = Font:getFace("infofont", 25),
text = "", text = "",
timeout = nil, -- in seconds timeout = nil, -- in seconds
} }
function InfoMessage:init() function InfoMessage:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
AnyKeyPressed = { { Input.group.Any }, AnyKeyPressed = { { Input.group.Any },
seqtext = "any key", doc = _("close dialog") } seqtext = "any key", doc = _("close dialog") }
} }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events.TapClose = { self.ges_events.TapClose = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = Geom:new{ range = Geom:new{
x = 0, y = 0, x = 0, y = 0,
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
} }
} }
} }
end end
-- we construct the actual content here because self.text is only available now -- we construct the actual content here because self.text is only available now
self[1] = CenterContainer:new{ self[1] = CenterContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
FrameContainer:new{ FrameContainer:new{
margin = 2, margin = 2,
background = 0, background = 0,
HorizontalGroup:new{ HorizontalGroup:new{
align = "center", align = "center",
ImageWidget:new{ ImageWidget:new{
file = "resources/info-i.png" file = "resources/info-i.png"
}, },
HorizontalSpan:new{ width = 10 }, HorizontalSpan:new{ width = 10 },
TextBoxWidget:new{ TextBoxWidget:new{
text = self.text, text = self.text,
face = self.face face = self.face
} }
} }
} }
} }
end end
function InfoMessage:onShow() function InfoMessage:onShow()
-- triggered by the UIManager after we got successfully shown (not yet painted) -- triggered by the UIManager after we got successfully shown (not yet painted)
if self.timeout then if self.timeout then
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end) UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
end end
return true return true
end end
function InfoMessage:onAnyKeyPressed() function InfoMessage:onAnyKeyPressed()
-- triggered by our defined key events -- triggered by our defined key events
UIManager:close(self) UIManager:close(self)
return true return true
end end
function InfoMessage:onTapClose() function InfoMessage:onTapClose()
UIManager:close(self) UIManager:close(self)
return true return true
end end
return InfoMessage return InfoMessage

@ -12,112 +12,112 @@ local UIManager = require("ui/uimanager")
local Screen = require("ui/screen") local Screen = require("ui/screen")
local InputDialog = InputContainer:new{ local InputDialog = InputContainer:new{
title = "", title = "",
input = "", input = "",
input_hint = "", input_hint = "",
buttons = nil, buttons = nil,
input_type = nil, input_type = nil,
enter_callback = nil, enter_callback = nil,
width = nil, width = nil,
height = nil, height = nil,
title_face = Font:getFace("tfont", 22), title_face = Font:getFace("tfont", 22),
input_face = Font:getFace("cfont", 20), input_face = Font:getFace("cfont", 20),
title_padding = Screen:scaleByDPI(5), title_padding = Screen:scaleByDPI(5),
title_margin = Screen:scaleByDPI(2), title_margin = Screen:scaleByDPI(2),
input_padding = Screen:scaleByDPI(10), input_padding = Screen:scaleByDPI(10),
input_margin = Screen:scaleByDPI(10), input_margin = Screen:scaleByDPI(10),
button_padding = Screen:scaleByDPI(14), button_padding = Screen:scaleByDPI(14),
} }
function InputDialog:init() function InputDialog:init()
self.title = FrameContainer:new{ self.title = FrameContainer:new{
padding = self.title_padding, padding = self.title_padding,
margin = self.title_margin, margin = self.title_margin,
bordersize = 0, bordersize = 0,
TextWidget:new{ TextWidget:new{
text = self.title, text = self.title,
face = self.title_face, face = self.title_face,
width = self.width, width = self.width,
} }
} }
self.input = InputText:new{ self.input = InputText:new{
text = self.input, text = self.input,
hint = self.input_hint, hint = self.input_hint,
face = self.input_face, face = self.input_face,
width = self.width * 0.9, width = self.width * 0.9,
input_type = self.input_type, input_type = self.input_type,
enter_callback = self.enter_callback, enter_callback = self.enter_callback,
scroll = false, scroll = false,
parent = self, parent = self,
} }
local button_table = ButtonTable:new{ local button_table = ButtonTable:new{
width = self.width, width = self.width,
button_font_face = "cfont", button_font_face = "cfont",
button_font_size = 20, button_font_size = 20,
buttons = self.buttons, buttons = self.buttons,
zero_sep = true, zero_sep = true,
} }
local title_bar = LineWidget:new{ local title_bar = LineWidget:new{
--background = 8, --background = 8,
dimen = Geom:new{ dimen = Geom:new{
w = button_table:getSize().w + self.button_padding, w = button_table:getSize().w + self.button_padding,
h = Screen:scaleByDPI(2), h = Screen:scaleByDPI(2),
} }
} }
self.dialog_frame = FrameContainer:new{ self.dialog_frame = FrameContainer:new{
radius = 8, radius = 8,
bordersize = 3, bordersize = 3,
padding = 0, padding = 0,
margin = 0, margin = 0,
background = 0, background = 0,
VerticalGroup:new{ VerticalGroup:new{
align = "left", align = "left",
self.title, self.title,
title_bar, title_bar,
-- input -- input
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = title_bar:getSize().w, w = title_bar:getSize().w,
h = self.input:getSize().h, h = self.input:getSize().h,
}, },
self.input, self.input,
}, },
-- buttons -- buttons
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = title_bar:getSize().w, w = title_bar:getSize().w,
h = button_table:getSize().h, h = button_table:getSize().h,
}, },
button_table, button_table,
} }
} }
} }
self[1] = CenterContainer:new{ self[1] = CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight() - self.input:getKeyboardDimen().h, h = Screen:getHeight() - self.input:getKeyboardDimen().h,
}, },
self.dialog_frame, self.dialog_frame,
} }
UIManager.repaint_all = true UIManager.repaint_all = true
UIManager.full_refresh = true UIManager.full_refresh = true
end end
function InputDialog:onShowKeyboard() function InputDialog:onShowKeyboard()
self.input:onShowKeyboard() self.input:onShowKeyboard()
end end
function InputDialog:getInputText() function InputDialog:getInputText()
return self.input:getText() return self.input:getText()
end end
function InputDialog:onClose() function InputDialog:onClose()
self.input:onCloseKeyboard() self.input:onCloseKeyboard()
end end
return InputDialog return InputDialog

@ -8,149 +8,149 @@ local Screen = require("ui/screen")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local InputText = InputContainer:new{ local InputText = InputContainer:new{
text = "", text = "",
hint = "demo hint", hint = "demo hint",
charlist = {}, -- table to store input string charlist = {}, -- table to store input string
charpos = 1, charpos = 1,
input_type = nil, input_type = nil,
width = nil, width = nil,
height = nil, height = nil,
face = Font:getFace("cfont", 22), face = Font:getFace("cfont", 22),
padding = 5, padding = 5,
margin = 5, margin = 5,
bordersize = 2, bordersize = 2,
parent = nil, -- parent dialog that will be set dirty parent = nil, -- parent dialog that will be set dirty
scroll = false, scroll = false,
} }
function InputText:init() function InputText:init()
self:StringToCharlist(self.text) self:StringToCharlist(self.text)
self:initTextBox() self:initTextBox()
self:initKeyboard() self:initKeyboard()
end end
function InputText:initTextBox() function InputText:initTextBox()
local bgcolor = nil local bgcolor = nil
local fgcolor = nil local fgcolor = nil
if self.text == "" then if self.text == "" then
self.text = self.hint self.text = self.hint
bgcolor = 0.0 bgcolor = 0.0
fgcolor = 0.5 fgcolor = 0.5
else else
bgcolor = 0.0 bgcolor = 0.0
fgcolor = 1.0 fgcolor = 1.0
end end
local text_widget = nil local text_widget = nil
if self.scroll then if self.scroll then
text_widget = ScrollTextWidget:new{ text_widget = ScrollTextWidget:new{
text = self.text, text = self.text,
face = self.face, face = self.face,
bgcolor = bgcolor, bgcolor = bgcolor,
fgcolor = fgcolor, fgcolor = fgcolor,
width = self.width, width = self.width,
height = self.height, height = self.height,
} }
else else
text_widget = TextBoxWidget:new{ text_widget = TextBoxWidget:new{
text = self.text, text = self.text,
face = self.face, face = self.face,
bgcolor = bgcolor, bgcolor = bgcolor,
fgcolor = fgcolor, fgcolor = fgcolor,
width = self.width, width = self.width,
height = self.height, height = self.height,
} }
end end
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
bordersize = self.bordersize, bordersize = self.bordersize,
padding = self.padding, padding = self.padding,
margin = self.margin, margin = self.margin,
text_widget, text_widget,
} }
self.dimen = self[1]:getSize() self.dimen = self[1]:getSize()
end end
function InputText:initKeyboard() function InputText:initKeyboard()
local keyboard_layout = 2 local keyboard_layout = 2
if self.input_type == "number" then if self.input_type == "number" then
keyboard_layout = 3 keyboard_layout = 3
end end
self.keyboard = VirtualKeyboard:new{ self.keyboard = VirtualKeyboard:new{
layout = keyboard_layout, layout = keyboard_layout,
inputbox = self, inputbox = self,
width = Screen:getWidth(), width = Screen:getWidth(),
height = math.max(Screen:getWidth(), Screen:getHeight())*0.33, height = math.max(Screen:getWidth(), Screen:getHeight())*0.33,
} }
end end
function InputText:onShowKeyboard() function InputText:onShowKeyboard()
UIManager:show(self.keyboard) UIManager:show(self.keyboard)
end end
function InputText:onCloseKeyboard() function InputText:onCloseKeyboard()
UIManager:close(self.keyboard) UIManager:close(self.keyboard)
end end
function InputText:getKeyboardDimen() function InputText:getKeyboardDimen()
return self.keyboard.dimen return self.keyboard.dimen
end end
function InputText:addChar(char) function InputText:addChar(char)
if self.enter_callback and char == '\n' then if self.enter_callback and char == '\n' then
UIManager:scheduleIn(0.3, function() self.enter_callback() end) UIManager:scheduleIn(0.3, function() self.enter_callback() end)
return return
end end
table.insert(self.charlist, self.charpos, char) table.insert(self.charlist, self.charpos, char)
self.charpos = self.charpos + 1 self.charpos = self.charpos + 1
self.text = self:CharlistToString() self.text = self:CharlistToString()
self:initTextBox() self:initTextBox()
UIManager:setDirty(self.parent, "partial") UIManager:setDirty(self.parent, "partial")
end end
function InputText:delChar() function InputText:delChar()
if self.charpos == 1 then return end if self.charpos == 1 then return end
self.charpos = self.charpos - 1 self.charpos = self.charpos - 1
table.remove(self.charlist, self.charpos) table.remove(self.charlist, self.charpos)
self.text = self:CharlistToString() self.text = self:CharlistToString()
self:initTextBox() self:initTextBox()
UIManager:setDirty(self.parent, "partial") UIManager:setDirty(self.parent, "partial")
end end
function InputText:getText() function InputText:getText()
return self.text return self.text
end end
function InputText:setText(text) function InputText:setText(text)
self:StringToCharlist(text) self:StringToCharlist(text)
self:initTextBox() self:initTextBox()
UIManager:setDirty(self.parent, "partial") UIManager:setDirty(self.parent, "partial")
end end
function InputText:StringToCharlist(text) function InputText:StringToCharlist(text)
if text == nil then return end if text == nil then return end
-- clear -- clear
self.charlist = {} self.charlist = {}
self.charpos = 1 self.charpos = 1
local prevcharcode, charcode = 0 local prevcharcode, charcode = 0
for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do
charcode = util.utf8charcode(uchar) charcode = util.utf8charcode(uchar)
if prevcharcode then -- utf8 if prevcharcode then -- utf8
self.charlist[#self.charlist+1] = uchar self.charlist[#self.charlist+1] = uchar
end end
prevcharcode = charcode prevcharcode = charcode
end end
self.text = self:CharlistToString() self.text = self:CharlistToString()
self.charpos = #self.charlist+1 self.charpos = #self.charlist+1
end end
function InputText:CharlistToString() function InputText:CharlistToString()
local s, i = "" local s, i = ""
for i=1, #self.charlist do for i=1, #self.charlist do
s = s .. self.charlist[i] s = s .. self.charlist[i]
end end
return s return s
end end
return InputText return InputText

@ -1,33 +1,33 @@
local Widget = require("ui/widget/widget") local Widget = require("ui/widget/widget")
local LineWidget = Widget:new{ local LineWidget = Widget:new{
style = "solid", style = "solid",
background = 15, background = 15,
dimen = nil, dimen = nil,
--@TODO replay dirty hack here 13.03 2013 (houqp) --@TODO replay dirty hack here 13.03 2013 (houqp)
empty_segments = nil, empty_segments = nil,
} }
function LineWidget:paintTo(bb, x, y) function LineWidget:paintTo(bb, x, y)
if self.style == "dashed" then if self.style == "dashed" then
for i = 0, self.dimen.w - 20, 20 do for i = 0, self.dimen.w - 20, 20 do
bb:paintRect(x + i, y, bb:paintRect(x + i, y,
16, self.dimen.h, self.background) 16, self.dimen.h, self.background)
end end
else else
if self.empty_segments then if self.empty_segments then
bb:paintRect(x, y, bb:paintRect(x, y,
self.empty_segments[1].s, self.empty_segments[1].s,
self.dimen.h, self.dimen.h,
self.background) self.background)
bb:paintRect(x + self.empty_segments[1].e, y, bb:paintRect(x + self.empty_segments[1].e, y,
self.dimen.w - x - self.empty_segments[1].e, self.dimen.w - x - self.empty_segments[1].e,
self.dimen.h, self.dimen.h,
self.background) self.background)
else else
bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.background) bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.background)
end end
end end
end end
return LineWidget return LineWidget

File diff suppressed because it is too large Load Diff

@ -14,50 +14,50 @@ local Screen = require("ui/screen")
Widget that displays a tiny notification on top of screen Widget that displays a tiny notification on top of screen
--]] --]]
local Notification = InputContainer:new{ local Notification = InputContainer:new{
face = Font:getFace("infofont", 20), face = Font:getFace("infofont", 20),
text = "Null Message", text = "Null Message",
timeout = nil, timeout = nil,
} }
function Notification:init() function Notification:init()
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events = { self.key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" } AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
} }
end end
-- we construct the actual content here because self.text is only available now -- we construct the actual content here because self.text is only available now
self[1] = CenterContainer:new{ self[1] = CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight()/10, h = Screen:getHeight()/10,
}, },
ignore = "height", ignore = "height",
FrameContainer:new{ FrameContainer:new{
background = 0, background = 0,
radius = 0, radius = 0,
HorizontalGroup:new{ HorizontalGroup:new{
align = "center", align = "center",
TextBoxWidget:new{ TextBoxWidget:new{
text = self.text, text = self.text,
face = self.face, face = self.face,
} }
} }
} }
} }
end end
function Notification:onShow() function Notification:onShow()
-- triggered by the UIManager after we got successfully shown (not yet painted) -- triggered by the UIManager after we got successfully shown (not yet painted)
if self.timeout then if self.timeout then
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end) UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
end end
return true return true
end end
function Notification:onAnyKeyPressed() function Notification:onAnyKeyPressed()
-- triggered by our defined key events -- triggered by our defined key events
UIManager:close(self) UIManager:close(self)
return true return true
end end
return Notification return Notification

@ -4,48 +4,48 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer")
A Layout widget that puts objects above each other A Layout widget that puts objects above each other
--]] --]]
local OverlapGroup = WidgetContainer:new{ local OverlapGroup = WidgetContainer:new{
_size = nil, _size = nil,
} }
function OverlapGroup:getSize() function OverlapGroup:getSize()
if not self._size then if not self._size then
self._size = {w = 0, h = 0} self._size = {w = 0, h = 0}
self._offsets = { x = math.huge, y = math.huge } self._offsets = { x = math.huge, y = math.huge }
for i, widget in ipairs(self) do for i, widget in ipairs(self) do
local w_size = widget:getSize() local w_size = widget:getSize()
if self._size.h < w_size.h then if self._size.h < w_size.h then
self._size.h = w_size.h self._size.h = w_size.h
end end
if self._size.w < w_size.w then if self._size.w < w_size.w then
self._size.w = w_size.w self._size.w = w_size.w
end end
end end
end end
if self.dimen.w then if self.dimen.w then
self._size.w = self.dimen.w self._size.w = self.dimen.w
end end
if self.dimen.h then if self.dimen.h then
self._size.h = self.dimen.h self._size.h = self.dimen.h
end end
return self._size return self._size
end end
function OverlapGroup:paintTo(bb, x, y) function OverlapGroup:paintTo(bb, x, y)
local size = self:getSize() local size = self:getSize()
for i, wget in ipairs(self) do for i, wget in ipairs(self) do
local wget_size = wget:getSize() local wget_size = wget:getSize()
if wget.align == "right" then if wget.align == "right" then
wget:paintTo(bb, x+size.w-wget_size.w, y) wget:paintTo(bb, x+size.w-wget_size.w, y)
elseif wget.align == "center" then elseif wget.align == "center" then
wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y) wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y)
else else
-- default to left -- default to left
wget:paintTo(bb, x, y) wget:paintTo(bb, x, y)
end end
end end
end end
return OverlapGroup return OverlapGroup

@ -5,39 +5,39 @@ local Geom = require("ui/geometry")
ProgressWidget shows a progress bar ProgressWidget shows a progress bar
--]] --]]
local ProgressWidget = Widget:new{ local ProgressWidget = Widget:new{
width = nil, width = nil,
height = nil, height = nil,
margin_h = 3, margin_h = 3,
margin_v = 1, margin_v = 1,
radius = 2, radius = 2,
bordersize = 1, bordersize = 1,
bordercolor = 15, bordercolor = 15,
bgcolor = 0, bgcolor = 0,
rectcolor = 10, rectcolor = 10,
percentage = nil, percentage = nil,
} }
function ProgressWidget:getSize() function ProgressWidget:getSize()
return { w = self.width, h = self.height } return { w = self.width, h = self.height }
end end
function ProgressWidget:paintTo(bb, x, y) function ProgressWidget:paintTo(bb, x, y)
local my_size = self:getSize() local my_size = self:getSize()
self.dimen = Geom:new{ self.dimen = Geom:new{
x = x, y = y, x = x, y = y,
w = my_size.w, w = my_size.w,
h = my_size.h h = my_size.h
} }
bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius) bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius)
bb:paintBorder(x, y, my_size.w, my_size.h, bb:paintBorder(x, y, my_size.w, my_size.h,
self.bordersize, self.bordercolor, self.radius) self.bordersize, self.bordercolor, self.radius)
bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize, bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize,
(my_size.w-2*self.margin_h)*self.percentage, (my_size.w-2*self.margin_h)*self.percentage,
(my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor) (my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor)
end end
function ProgressWidget:setPercentage(percentage) function ProgressWidget:setPercentage(percentage)
self.percentage = percentage self.percentage = percentage
end end
return ProgressWidget return ProgressWidget

@ -4,12 +4,12 @@ local Widget = require("ui/widget/widget")
Dummy Widget that reserves vertical and horizontal space Dummy Widget that reserves vertical and horizontal space
]] ]]
local RectSpan = Widget:new{ local RectSpan = Widget:new{
width = 0, width = 0,
hright = 0, hright = 0,
} }
function RectSpan:getSize() function RectSpan:getSize()
return {w = self.width, h = self.height} return {w = self.width, h = self.height}
end end
return RectSpan return RectSpan

@ -13,73 +13,73 @@ local Device = require("ui/device")
Text widget with vertical scroll bar Text widget with vertical scroll bar
--]] --]]
local ScrollTextWidget = InputContainer:new{ local ScrollTextWidget = InputContainer:new{
text = nil, text = nil,
face = nil, face = nil,
bgcolor = 0.0, -- [0.0, 1.0] bgcolor = 0.0, -- [0.0, 1.0]
fgcolor = 1.0, -- [0.0, 1.0] fgcolor = 1.0, -- [0.0, 1.0]
width = 400, width = 400,
height = 20, height = 20,
scroll_bar_width = Screen:scaleByDPI(6), scroll_bar_width = Screen:scaleByDPI(6),
text_scroll_span = Screen:scaleByDPI(6), text_scroll_span = Screen:scaleByDPI(6),
dialog = nil, dialog = nil,
} }
function ScrollTextWidget:init() function ScrollTextWidget:init()
self.text_widget = TextBoxWidget:new{ self.text_widget = TextBoxWidget:new{
text = self.text, text = self.text,
face = self.face, face = self.face,
bgcolor = self.bgcolor, bgcolor = self.bgcolor,
fgcolor = self.fgcolor, fgcolor = self.fgcolor,
width = self.width - self.scroll_bar_width - self.text_scroll_span, width = self.width - self.scroll_bar_width - self.text_scroll_span,
height = self.height height = self.height
} }
local visible_line_count = self.text_widget:getVisLineCount() local visible_line_count = self.text_widget:getVisLineCount()
local total_line_count = self.text_widget:getAllLineCount() local total_line_count = self.text_widget:getAllLineCount()
self.v_scroll_bar = VerticalScrollBar:new{ self.v_scroll_bar = VerticalScrollBar:new{
enable = visible_line_count < total_line_count, enable = visible_line_count < total_line_count,
low = 0, low = 0,
high = visible_line_count/total_line_count, high = visible_line_count/total_line_count,
width = Screen:scaleByDPI(6), width = Screen:scaleByDPI(6),
height = self.height, height = self.height,
} }
local horizontal_group = HorizontalGroup:new{} local horizontal_group = HorizontalGroup:new{}
table.insert(horizontal_group, self.text_widget) table.insert(horizontal_group, self.text_widget)
table.insert(horizontal_group, HorizontalSpan:new{width = Screen:scaleByDPI(6)}) table.insert(horizontal_group, HorizontalSpan:new{width = Screen:scaleByDPI(6)})
table.insert(horizontal_group, self.v_scroll_bar) table.insert(horizontal_group, self.v_scroll_bar)
self[1] = horizontal_group self[1] = horizontal_group
self.dimen = Geom:new(self[1]:getSize()) self.dimen = Geom:new(self[1]:getSize())
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
Swipe = { Swipe = {
GestureRange:new{ GestureRange:new{
ges = "swipe", ges = "swipe",
range = self.dimen, range = self.dimen,
}, },
}, },
} }
end end
end end
function ScrollTextWidget:updateScrollBar(text) function ScrollTextWidget:updateScrollBar(text)
local virtual_line_num = text:getVirtualLineNum() local virtual_line_num = text:getVirtualLineNum()
local visible_line_count = text:getVisLineCount() local visible_line_count = text:getVisLineCount()
local all_line_count = text:getAllLineCount() local all_line_count = text:getAllLineCount()
self.v_scroll_bar:set( self.v_scroll_bar:set(
(virtual_line_num - 1) / all_line_count, (virtual_line_num - 1) / all_line_count,
(virtual_line_num - 1 + visible_line_count) / all_line_count (virtual_line_num - 1 + visible_line_count) / all_line_count
) )
end end
function ScrollTextWidget:onSwipe(arg, ges) function ScrollTextWidget:onSwipe(arg, ges)
if ges.direction == "north" then if ges.direction == "north" then
self.text_widget:scrollDown() self.text_widget:scrollDown()
self:updateScrollBar(self.text_widget) self:updateScrollBar(self.text_widget)
elseif ges.direction == "south" then elseif ges.direction == "south" then
self.text_widget:scrollUp() self.text_widget:scrollUp()
self:updateScrollBar(self.text_widget) self:updateScrollBar(self.text_widget)
end end
UIManager:setDirty(self.dialog, "partial") UIManager:setDirty(self.dialog, "partial")
return true return true
end end
return ScrollTextWidget return ScrollTextWidget

@ -9,59 +9,59 @@ local Geom = require("ui/geometry")
A TextWidget that handles long text wrapping A TextWidget that handles long text wrapping
--]] --]]
local TextBoxWidget = Widget:new{ local TextBoxWidget = Widget:new{
text = nil, text = nil,
face = nil, face = nil,
bold = nil, bold = nil,
bgcolor = 0.0, -- [0.0, 1.0] bgcolor = 0.0, -- [0.0, 1.0]
fgcolor = 1.0, -- [0.0, 1.0] fgcolor = 1.0, -- [0.0, 1.0]
width = 400, -- in pixels width = 400, -- in pixels
height = nil, height = nil,
first_line = 1, first_line = 1,
virtual_line = 1, -- used by scroll bar virtual_line = 1, -- used by scroll bar
line_height = 0.3, -- in em line_height = 0.3, -- in em
v_list = nil, v_list = nil,
_bb = nil, _bb = nil,
_length = 0, _length = 0,
} }
function TextBoxWidget:init() function TextBoxWidget:init()
local v_list = nil local v_list = nil
if self.height then if self.height then
v_list = self:_getCurrentVerticalList() v_list = self:_getCurrentVerticalList()
else else
v_list = self:_getVerticalList() v_list = self:_getVerticalList()
end end
self:_render(v_list) self:_render(v_list)
end end
function TextBoxWidget:_wrapGreedyAlg(h_list) function TextBoxWidget:_wrapGreedyAlg(h_list)
local cur_line_width = 0 local cur_line_width = 0
local cur_line = {} local cur_line = {}
local v_list = {} local v_list = {}
for k,w in ipairs(h_list) do for k,w in ipairs(h_list) do
cur_line_width = cur_line_width + w.width cur_line_width = cur_line_width + w.width
if w.word == "\n" then if w.word == "\n" then
if cur_line_width > 0 then if cur_line_width > 0 then
-- hard line break -- hard line break
table.insert(v_list, cur_line) table.insert(v_list, cur_line)
cur_line = {} cur_line = {}
cur_line_width = 0 cur_line_width = 0
end end
elseif cur_line_width > self.width then elseif cur_line_width > self.width then
-- wrap to next line -- wrap to next line
table.insert(v_list, cur_line) table.insert(v_list, cur_line)
cur_line = {} cur_line = {}
cur_line_width = w.width cur_line_width = w.width
table.insert(cur_line, w) table.insert(cur_line, w)
else else
table.insert(cur_line, w) table.insert(cur_line, w)
end end
end end
-- handle last line -- handle last line
table.insert(v_list, cur_line) table.insert(v_list, cur_line)
return v_list return v_list
end end
--[[ --[[
@ -77,199 +77,199 @@ License: MIT/X11
Source: http://snippets.luacode.org/snippets/String_splitting_130 Source: http://snippets.luacode.org/snippets/String_splitting_130
--]] --]]
function string:gsplit(pattern, capture) function string:gsplit(pattern, capture)
pattern = pattern and tostring(pattern) or '%s+' pattern = pattern and tostring(pattern) or '%s+'
if (''):find(pattern) then if (''):find(pattern) then
error('pattern matches empty string!', 2) error('pattern matches empty string!', 2)
end end
return coroutine.wrap(function() return coroutine.wrap(function()
local index = 1 local index = 1
repeat repeat
local first, last = self:find(pattern, index) local first, last = self:find(pattern, index)
if first and last then if first and last then
if index < first then if index < first then
coroutine.yield(self:sub(index, first - 1)) coroutine.yield(self:sub(index, first - 1))
end end
if capture then if capture then
coroutine.yield(self:sub(first, last)) coroutine.yield(self:sub(first, last))
end end
index = last + 1 index = last + 1
else else
if index <= #self then if index <= #self then
coroutine.yield(self:sub(index)) coroutine.yield(self:sub(index))
end end
break break
end end
until index > #self until index > #self
end) end)
end end
function TextBoxWidget:_getVerticalList(alg) function TextBoxWidget:_getVerticalList(alg)
if self.vertical_list then if self.vertical_list then
return self.vertical_list return self.vertical_list
end end
-- build horizontal list -- build horizontal list
local h_list = {} local h_list = {}
local line_count = 0 local line_count = 0
for line in self.text:gsplit("\n", true) do for line in self.text:gsplit("\n", true) do
for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do
for word in words:gsplit("%s+", true) do for word in words:gsplit("%s+", true) do
for w in word:gsplit("%p+", true) do for w in word:gsplit("%p+", true) do
local word_box = {} local word_box = {}
word_box.word = w word_box.word = w
word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, w, true, self.bold).x word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, w, true, self.bold).x
table.insert(h_list, word_box) table.insert(h_list, word_box)
end end
end end
end end
if line:sub(-1) == "\n" then table.insert(h_list, {word = '\n', width = 0}) end if line:sub(-1) == "\n" then table.insert(h_list, {word = '\n', width = 0}) end
end end
-- @TODO check alg here 25.04 2012 (houqp) -- @TODO check alg here 25.04 2012 (houqp)
-- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp) -- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp)
self.vertical_list = self:_wrapGreedyAlg(h_list) self.vertical_list = self:_wrapGreedyAlg(h_list)
return self.vertical_list return self.vertical_list
end end
function TextBoxWidget:_getCurrentVerticalList() function TextBoxWidget:_getCurrentVerticalList()
local line_height = (1 + self.line_height) * self.face.size local line_height = (1 + self.line_height) * self.face.size
local v_list = self:_getVerticalList() local v_list = self:_getVerticalList()
local current_v_list = {} local current_v_list = {}
local height = 0 local height = 0
for i = self.first_line, #v_list do for i = self.first_line, #v_list do
if height < self.height - line_height then if height < self.height - line_height then
table.insert(current_v_list, v_list[i]) table.insert(current_v_list, v_list[i])
height = height + line_height height = height + line_height
else else
break break
end end
end end
return current_v_list return current_v_list
end end
function TextBoxWidget:_getPreviousVerticalList() function TextBoxWidget:_getPreviousVerticalList()
local line_height = (1 + self.line_height) * self.face.size local line_height = (1 + self.line_height) * self.face.size
local v_list = self:_getVerticalList() local v_list = self:_getVerticalList()
local previous_v_list = {} local previous_v_list = {}
local height = 0 local height = 0
if self.first_line == 1 then if self.first_line == 1 then
return self:_getCurrentVerticalList() return self:_getCurrentVerticalList()
end end
self.virtual_line = self.first_line self.virtual_line = self.first_line
for i = self.first_line - 1, 1, -1 do for i = self.first_line - 1, 1, -1 do
if height < self.height - line_height then if height < self.height - line_height then
table.insert(previous_v_list, 1, v_list[i]) table.insert(previous_v_list, 1, v_list[i])
height = height + line_height height = height + line_height
self.virtual_line = self.virtual_line - 1 self.virtual_line = self.virtual_line - 1
else else
break break
end end
end end
for i = self.first_line, #v_list do for i = self.first_line, #v_list do
if height < self.height - line_height then if height < self.height - line_height then
table.insert(previous_v_list, v_list[i]) table.insert(previous_v_list, v_list[i])
height = height + line_height height = height + line_height
else else
break break
end end
end end
if self.first_line > #previous_v_list then if self.first_line > #previous_v_list then
self.first_line = self.first_line - #previous_v_list self.first_line = self.first_line - #previous_v_list
else else
self.first_line = 1 self.first_line = 1
end end
return previous_v_list return previous_v_list
end end
function TextBoxWidget:_getNextVerticalList() function TextBoxWidget:_getNextVerticalList()
local line_height = (1 + self.line_height) * self.face.size local line_height = (1 + self.line_height) * self.face.size
local v_list = self:_getVerticalList() local v_list = self:_getVerticalList()
local current_v_list = self:_getCurrentVerticalList() local current_v_list = self:_getCurrentVerticalList()
local next_v_list = {} local next_v_list = {}
local height = 0 local height = 0
if self.first_line + #current_v_list > #v_list then if self.first_line + #current_v_list > #v_list then
return current_v_list return current_v_list
end end
self.virtual_line = self.first_line self.virtual_line = self.first_line
for i = self.first_line + #current_v_list, #v_list do for i = self.first_line + #current_v_list, #v_list do
if height < self.height - line_height then if height < self.height - line_height then
table.insert(next_v_list, v_list[i]) table.insert(next_v_list, v_list[i])
height = height + line_height height = height + line_height
self.virtual_line = self.virtual_line + 1 self.virtual_line = self.virtual_line + 1
else else
break break
end end
end end
self.first_line = self.first_line + #current_v_list self.first_line = self.first_line + #current_v_list
return next_v_list return next_v_list
end end
function TextBoxWidget:_render(v_list) function TextBoxWidget:_render(v_list)
local font_height = self.face.size local font_height = self.face.size
local line_height_px = self.line_height * font_height local line_height_px = self.line_height * font_height
local space_w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x local space_w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x
local h = (font_height + line_height_px) * #v_list local h = (font_height + line_height_px) * #v_list
self._bb = Blitbuffer.new(self.width, h) self._bb = Blitbuffer.new(self.width, h)
local y = font_height local y = font_height
local pen_x = 0 local pen_x = 0
for _,l in ipairs(v_list) do for _,l in ipairs(v_list) do
pen_x = 0 pen_x = 0
for _,w in ipairs(l) do for _,w in ipairs(l) do
--@TODO Don't use kerning for monospaced fonts. (houqp) --@TODO Don't use kerning for monospaced fonts. (houqp)
-- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree
RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, w.word, true, self.bold, self.bgcolor, self.fgcolor) RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, w.word, true, self.bold, self.bgcolor, self.fgcolor)
pen_x = pen_x + w.width pen_x = pen_x + w.width
end end
y = y + line_height_px + font_height y = y + line_height_px + font_height
end end
-- -- if text is shorter than one line, shrink to text's width -- -- if text is shorter than one line, shrink to text's width
-- if #v_list == 1 then -- if #v_list == 1 then
-- self.width = pen_x -- self.width = pen_x
-- end -- end
end end
function TextBoxWidget:getVirtualLineNum() function TextBoxWidget:getVirtualLineNum()
return self.virtual_line return self.virtual_line
end end
function TextBoxWidget:getAllLineCount() function TextBoxWidget:getAllLineCount()
local v_list = self:_getVerticalList() local v_list = self:_getVerticalList()
return #v_list return #v_list
end end
function TextBoxWidget:getVisLineCount() function TextBoxWidget:getVisLineCount()
local line_height = (1 + self.line_height) * self.face.size local line_height = (1 + self.line_height) * self.face.size
return math.floor(self.height / line_height) return math.floor(self.height / line_height)
end end
function TextBoxWidget:scrollDown() function TextBoxWidget:scrollDown()
local next_v_list = self:_getNextVerticalList() local next_v_list = self:_getNextVerticalList()
self:free() self:free()
self:_render(next_v_list) self:_render(next_v_list)
end end
function TextBoxWidget:scrollUp() function TextBoxWidget:scrollUp()
local previous_v_list = self:_getPreviousVerticalList() local previous_v_list = self:_getPreviousVerticalList()
self:free() self:free()
self:_render(previous_v_list) self:_render(previous_v_list)
end end
function TextBoxWidget:getSize() function TextBoxWidget:getSize()
if self.width and self.height then if self.width and self.height then
return Geom:new{ w = self.width, h = self.height} return Geom:new{ w = self.width, h = self.height}
else else
return Geom:new{ w = self.width, h = self._bb:getHeight()} return Geom:new{ w = self.width, h = self._bb:getHeight()}
end end
end end
function TextBoxWidget:paintTo(bb, x, y) 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 end
function TextBoxWidget:free() function TextBoxWidget:free()
if self._bb then if self._bb then
self._bb:free() self._bb:free()
self._bb = nil self._bb = nil
end end
end end
return TextBoxWidget return TextBoxWidget

@ -7,55 +7,55 @@ local Geom = require("ui/geometry")
A TextWidget puts a string on a single line A TextWidget puts a string on a single line
--]] --]]
local TextWidget = Widget:new{ local TextWidget = Widget:new{
text = nil, text = nil,
face = nil, face = nil,
bold = nil, bold = nil,
bgcolor = 0.0, -- [0.0, 1.0] bgcolor = 0.0, -- [0.0, 1.0]
fgcolor = 1.0, -- [0.0, 1.0] fgcolor = 1.0, -- [0.0, 1.0]
_bb = nil, _bb = nil,
_length = 0, _length = 0,
_height = 0, _height = 0,
_maxlength = 1200, _maxlength = 1200,
} }
--function TextWidget:_render() --function TextWidget:_render()
--local h = self.face.size * 1.3 --local h = self.face.size * 1.3
--self._bb = Blitbuffer.new(self._maxlength, h) --self._bb = Blitbuffer.new(self._maxlength, h)
--self._length = RenderText:renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, true, self.bold) --self._length = RenderText:renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, true, self.bold)
--end --end
function TextWidget:getSize() function TextWidget:getSize()
--if not self._bb then --if not self._bb then
--self:_render() --self:_render()
--end --end
--return { w = self._length, h = self._bb:getHeight() } --return { w = self._length, h = self._bb:getHeight() }
local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold) local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold)
if not tsize then if not tsize then
return Geom:new{} return Geom:new{}
end end
self._length = tsize.x self._length = tsize.x
self._height = self.face.size * 1.5 self._height = self.face.size * 1.5
return Geom:new{ return Geom:new{
w = self._length, w = self._length,
h = self._height, h = self._height,
} }
end end
function TextWidget:paintTo(bb, x, y) function TextWidget:paintTo(bb, x, y)
--if not self._bb then --if not self._bb then
--self:_render() --self:_render()
--end --end
--bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight()) --bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight())
--@TODO Don't use kerning for monospaced fonts. (houqp) --@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, RenderText:renderUtf8Text(bb, x, y+self._height*0.7, self.face, self.text, true, self.bold,
self.bgcolor, self.fgcolor, self.width) self.bgcolor, self.fgcolor, self.width)
end end
function TextWidget:free() function TextWidget:free()
if self._bb then if self._bb then
self._bb:free() self._bb:free()
self._bb = nil self._bb = nil
end end
end end
return TextWidget return TextWidget

@ -14,126 +14,126 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local ToggleLabel = TextWidget:new{ local ToggleLabel = TextWidget:new{
bold = true, bold = true,
bgcolor = 0, bgcolor = 0,
fgcolor = 1, fgcolor = 1,
} }
function ToggleLabel:paintTo(bb, x, y) 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 end
local ToggleSwitch = InputContainer:new{ local ToggleSwitch = InputContainer:new{
width = Screen:scaleByDPI(216), width = Screen:scaleByDPI(216),
height = Screen:scaleByDPI(30), height = Screen:scaleByDPI(30),
bgcolor = 0, -- unfoused item color bgcolor = 0, -- unfoused item color
fgcolor = 7, -- focused item color fgcolor = 7, -- focused item color
} }
function ToggleSwitch:init() function ToggleSwitch:init()
self.n_pos = #self.toggle self.n_pos = #self.toggle
self.position = nil self.position = nil
local label_font_face = "cfont" local label_font_face = "cfont"
local label_font_size = 16 local label_font_size = 16
self.toggle_frame = FrameContainer:new{background = 0, color = 7, radius = 7, bordersize = 1, padding = 2,} self.toggle_frame = FrameContainer:new{background = 0, color = 7, radius = 7, bordersize = 1, padding = 2,}
self.toggle_content = HorizontalGroup:new{} self.toggle_content = HorizontalGroup:new{}
for i=1,#self.toggle do for i=1,#self.toggle do
local label = ToggleLabel:new{ local label = ToggleLabel:new{
align = "center", align = "center",
text = self.toggle[i], text = self.toggle[i],
face = Font:getFace(label_font_face, label_font_size), face = Font:getFace(label_font_face, label_font_size),
} }
local content = CenterContainer:new{ local content = CenterContainer:new{
dimen = Geom:new{w = self.width/self.n_pos, h = self.height}, dimen = Geom:new{w = self.width/self.n_pos, h = self.height},
label, label,
} }
local button = FrameContainer:new{ local button = FrameContainer:new{
background = 0, background = 0,
color = 7, color = 7,
margin = 0, margin = 0,
radius = 5, radius = 5,
bordersize = 1, bordersize = 1,
padding = 0, padding = 0,
content, content,
} }
table.insert(self.toggle_content, button) table.insert(self.toggle_content, button)
end end
self.toggle_frame[1] = self.toggle_content self.toggle_frame[1] = self.toggle_content
self[1] = self.toggle_frame self[1] = self.toggle_frame
self.dimen = Geom:new(self.toggle_frame:getSize()) self.dimen = Geom:new(self.toggle_frame:getSize())
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
TapSelect = { TapSelect = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
range = self.dimen, range = self.dimen,
}, },
doc = _("Toggle switch"), doc = _("Toggle switch"),
}, },
} }
end end
end end
function ToggleSwitch:update() function ToggleSwitch:update()
local pos = self.position local pos = self.position
for i=1,#self.toggle_content do for i=1,#self.toggle_content do
if pos == i then if pos == i then
self.toggle_content[i].color = self.fgcolor self.toggle_content[i].color = self.fgcolor
self.toggle_content[i].background = 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].bgcolor = self.fgcolor/15
self.toggle_content[i][1][1].fgcolor = 0.0 self.toggle_content[i][1][1].fgcolor = 0.0
else else
self.toggle_content[i].color = self.bgcolor self.toggle_content[i].color = self.bgcolor
self.toggle_content[i].background = self.bgcolor self.toggle_content[i].background = self.bgcolor
self.toggle_content[i][1][1].bgcolor = 0.0 self.toggle_content[i][1][1].bgcolor = 0.0
self.toggle_content[i][1][1].fgcolor = 1.0 self.toggle_content[i][1][1].fgcolor = 1.0
end end
end end
end end
function ToggleSwitch:setPosition(position) function ToggleSwitch:setPosition(position)
self.position = position self.position = position
self:update() self:update()
end end
function ToggleSwitch:togglePosition(position) function ToggleSwitch:togglePosition(position)
if self.n_pos == 2 and self.alternate ~= false then if self.n_pos == 2 and self.alternate ~= false then
self.position = (self.position+1)%self.n_pos self.position = (self.position+1)%self.n_pos
self.position = self.position == 0 and self.n_pos or self.position self.position = self.position == 0 and self.n_pos or self.position
elseif self.n_pos == 1 then elseif self.n_pos == 1 then
self.position = self.position == 1 and 0 or 1 self.position = self.position == 1 and 0 or 1
else else
self.position = position self.position = position
end end
self:update() self:update()
end end
function ToggleSwitch:onTapSelect(arg, gev) function ToggleSwitch:onTapSelect(arg, gev)
local position = math.ceil( local position = math.ceil(
(gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos (gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos
) )
--DEBUG("toggle position:", position) --DEBUG("toggle position:", position)
self:togglePosition(position) self:togglePosition(position)
--[[ --[[
if self.values then if self.values then
self.values = self.values or {} self.values = self.values or {}
self.config:onConfigChoice(self.name, self.values[self.position]) self.config:onConfigChoice(self.name, self.values[self.position])
end end
if self.event then if self.event then
self.args = self.args or {} self.args = self.args or {}
self.config:onConfigEvent(self.event, self.args[self.position]) self.config:onConfigEvent(self.event, self.args[self.position])
end end
if self.events then if self.events then
self.config:onConfigEvents(self.events, self.position) self.config:onConfigEvents(self.events, self.position)
end end
--]] --]]
self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.position) self.config:onConfigChoose(self.values, self.name, self.event, self.args, self.events, self.position)
UIManager:setDirty(self.config, "partial") UIManager:setDirty(self.config, "partial")
return true return true
end end
return ToggleSwitch return ToggleSwitch

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save