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}
-- koptreader config defaults
DKOPTREADER_CONFIG_FONT_SIZE = 1.0 -- range from 0.1 to 3.0
DKOPTREADER_CONFIG_TEXT_WRAP = 0 -- 1 = on, 0 = off
DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual
DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable
DKOPTREADER_CONFIG_DEFECT_SIZE = 1.0 -- range from 0.0 to 3.0
DKOPTREADER_CONFIG_PAGE_MARGIN = 0.10 -- range from 0.0 to 1.0
DKOPTREADER_CONFIG_LINE_SPACING = 1.2 -- range from 0.5 to 2.0
DKOPTREADER_CONFIG_RENDER_QUALITY = 1.0 -- range from 0.5 to 2.0
DKOPTREADER_CONFIG_AUTO_STRAIGHTEN = 0 -- range from 0 to 10
DKOPTREADER_CONFIG_JUSTIFICATION = 3 -- -1 = auto, 0 = left, 1 = center, 2 = right, 3 = full
DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4
DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0
DKOPTREADER_CONFIG_FONT_SIZE = 1.0 -- range from 0.1 to 3.0
DKOPTREADER_CONFIG_TEXT_WRAP = 0 -- 1 = on, 0 = off
DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual
DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable
DKOPTREADER_CONFIG_DEFECT_SIZE = 1.0 -- range from 0.0 to 3.0
DKOPTREADER_CONFIG_PAGE_MARGIN = 0.10 -- range from 0.0 to 1.0
DKOPTREADER_CONFIG_LINE_SPACING = 1.2 -- range from 0.5 to 2.0
DKOPTREADER_CONFIG_RENDER_QUALITY = 1.0 -- range from 0.5 to 2.0
DKOPTREADER_CONFIG_AUTO_STRAIGHTEN = 0 -- range from 0 to 10
DKOPTREADER_CONFIG_JUSTIFICATION = 3 -- -1 = auto, 0 = left, 1 = center, 2 = right, 3 = full
DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4
DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0
-- word spacing for reflow
DKOPTREADER_CONFIG_WORD_SAPCINGS = {0.05, -1, 0.375} -- range from 0.05 to 0.5
DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING = -1 -- range from 0.05 to 0.5
DKOPTREADER_CONFIG_DEFAULT_WORD_SAPCING = -1 -- range from 0.05 to 0.5
-- document languages for OCR
DKOPTREADER_CONFIG_DOC_LANGS_TEXT = {"English", "Chinese"}
DKOPTREADER_CONFIG_DOC_LANGS_CODE = {"eng", "chi_sim"} -- language code, make sure you have corresponding training data
DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE = "eng" -- that have filenames starting with the language codes
DKOPTREADER_CONFIG_DOC_LANGS_CODE = {"eng", "chi_sim"} -- language code, make sure you have corresponding training data
DKOPTREADER_CONFIG_DOC_DEFAULT_LANG_CODE = "eng" -- that have filenames starting with the language codes
-- crereader font sizes
-- feel free to add more entries in this list
@ -170,5 +170,5 @@ DDICT_FONT_SIZE = 20
--DPICVIEWER_PAGE_MODE_ENABLE = false
--DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off
--DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees
--DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off
--DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees

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

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

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

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

@ -3,14 +3,14 @@ Inheritable abstraction for cache items
--]]
local CacheItem = {
size = 64, -- some reasonable default for simple Lua values / small tables
size = 64, -- some reasonable default for simple Lua values / small tables
}
function CacheItem:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
function CacheItem:onFree()

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save