mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
refactor: use new KeyValuePage widget for displaying statistics
This commit is contained in:
@@ -71,7 +71,7 @@ script:
|
||||
- make all
|
||||
- travis_retry make testfront
|
||||
- luajit $(which luacheck) --no-color -q frontend | tee ./luacheck.out
|
||||
- test $(grep Total ./luacheck.out | awk '{print $2}') -le 238
|
||||
- test $(grep Total ./luacheck.out | awk '{print $2}') -le 220
|
||||
|
||||
after_success:
|
||||
- make coverage
|
||||
|
||||
@@ -22,6 +22,10 @@ function DataStorage:getDataDir()
|
||||
return data_dir
|
||||
end
|
||||
|
||||
function DataStorage:getHistoryDir()
|
||||
return self:getDataDir() .. "/history"
|
||||
end
|
||||
|
||||
local function initDataDir()
|
||||
local data_dir = DataStorage:getDataDir()
|
||||
local sub_data_dirs = {"cache", "clipboard", "data", "history", "ota", "screenshots"}
|
||||
|
||||
@@ -6,10 +6,11 @@ local DataStorage = require("datastorage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local DocSettings = require("docsettings")
|
||||
local Menu = require("ui/widget/menu")
|
||||
local joinPath = require("ffi/util").joinPath
|
||||
local Screen = require("device").screen
|
||||
local _ = require("gettext")
|
||||
|
||||
local history_dir = DataStorage:getDataDir() .. "/history/"
|
||||
local history_dir = DataStorage:getHistoryDir()
|
||||
|
||||
local FileManagerHistory = InputContainer:extend{
|
||||
hist_menu_title = _("History"),
|
||||
@@ -30,7 +31,7 @@ function FileManagerHistory:onMenuHold(item)
|
||||
{
|
||||
text = _("Remove this item from history"),
|
||||
callback = function()
|
||||
os.remove(history_dir..item.histfile)
|
||||
os.remove(joinPath(history_dir, item.histfile))
|
||||
self._manager:updateItemTable()
|
||||
UIManager:close(self.histfile_dialog)
|
||||
end,
|
||||
@@ -82,7 +83,7 @@ function FileManagerHistory:updateItemTable()
|
||||
self.hist = {}
|
||||
|
||||
for f in lfs.dir(history_dir) do
|
||||
local path = history_dir..f
|
||||
local path = joinPath(history_dir, f)
|
||||
if lfs.attributes(path, "mode") == "file" then
|
||||
local name = DocSettings:getNameFromHistory(f)
|
||||
table.insert(self.hist, {
|
||||
|
||||
@@ -12,7 +12,6 @@ local BBoxWidget = require("ui/widget/bboxwidget")
|
||||
local HorizontalSpan = require("ui/widget/horizontalspan")
|
||||
local Button = require("ui/widget/button")
|
||||
local Math = require("optmath")
|
||||
local DEBUG = require("dbg")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
|
||||
local PageCropDialog = VerticalGroup:new{
|
||||
|
||||
@@ -2,7 +2,6 @@ local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local RightContainer = require("ui/widget/container/rightcontainer")
|
||||
local ImageWidget = require("ui/widget/imagewidget")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local Device = require("device")
|
||||
local Geom = require("ui/geometry")
|
||||
local Screen = require("device").screen
|
||||
|
||||
@@ -8,7 +8,6 @@ local Screen = require("device").screen
|
||||
local Input = require("device").input
|
||||
local Event = require("ui/event")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local DEBUG = require("dbg")
|
||||
local T = require("ffi/util").template
|
||||
local _ = require("gettext")
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ local Screen = require("device").screen
|
||||
local Geom = require("ui/geometry")
|
||||
local Event = require("ui/event")
|
||||
local Font = require("ui/font")
|
||||
local DEBUG = require("dbg")
|
||||
local _ = require("gettext")
|
||||
local util = require("util")
|
||||
|
||||
@@ -54,7 +53,7 @@ function ReaderFooter:init()
|
||||
book_time_to_read = true,
|
||||
chapter_time_to_read = true,
|
||||
}
|
||||
local text_default = ""
|
||||
local text_default
|
||||
if self.settings.all_at_once then
|
||||
local info = {}
|
||||
if self.settings.battery then
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local JSON = require("json")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local T = require("ffi/util").template
|
||||
local _ = require("gettext")
|
||||
@@ -11,32 +12,35 @@ local ReaderHyphenation = InputContainer:new{
|
||||
}
|
||||
|
||||
function ReaderHyphenation:init()
|
||||
local lang_data_file = assert(io.open("./data/hyph/languages.json"), "r")
|
||||
lang_data = json.decode(lang_data_file:read("*all"))
|
||||
|
||||
self.lang_table = {}
|
||||
self.hyph_table = {}
|
||||
self.hyph_alg = cre.getSelectedHyphDict()
|
||||
for k,v in ipairs(lang_data) do
|
||||
table.insert(self.hyph_table, {
|
||||
text = v.name,
|
||||
callback = function()
|
||||
self.hyph_alg = v.filename
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T( _("Changed hyphenation to %1."), v.name),
|
||||
})
|
||||
self.ui.document:setHyphDictionary(v.filename)
|
||||
self.ui.toc:onUpdateToc()
|
||||
end,
|
||||
checked_func = function()
|
||||
return v.filename == self.hyph_alg
|
||||
end
|
||||
})
|
||||
|
||||
self.lang_table[v.language] = v.filename
|
||||
if v.aliases then
|
||||
for i,alias in ipairs(v.aliases) do
|
||||
self.lang_table[alias] = v.filename
|
||||
local lang_data_file = assert(io.open("./data/hyph/languages.json"), "r")
|
||||
local ok, lang_data = pcall(JSON.decode, lang_data_file:read("*all"))
|
||||
|
||||
if ok and lang_data then
|
||||
for k,v in ipairs(lang_data) do
|
||||
table.insert(self.hyph_table, {
|
||||
text = v.name,
|
||||
callback = function()
|
||||
self.hyph_alg = v.filename
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("Changed hyphenation to %1."), v.name),
|
||||
})
|
||||
self.ui.document:setHyphDictionary(v.filename)
|
||||
self.ui.toc:onUpdateToc()
|
||||
end,
|
||||
checked_func = function()
|
||||
return v.filename == self.hyph_alg
|
||||
end
|
||||
})
|
||||
|
||||
self.lang_table[v.language] = v.filename
|
||||
if v.aliases then
|
||||
for i,alias in ipairs(v.aliases) do
|
||||
self.lang_table[alias] = v.filename
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,6 @@ local Geom = require("ui/geometry")
|
||||
local Screen = require("device").screen
|
||||
local Device = require("device")
|
||||
local Event = require("ui/event")
|
||||
local DEBUG = require("dbg")
|
||||
local _ = require("gettext")
|
||||
|
||||
local ReaderLink = InputContainer:new{
|
||||
|
||||
@@ -318,17 +318,9 @@ function ReaderPaging:onSwipe(arg, ges)
|
||||
elseif self.page_flipping_mode and self.original_page then
|
||||
self:gotoPage(self.original_page)
|
||||
elseif ges.direction == "west" then
|
||||
if DCHANGE_WEST_SWIPE_TO_EAST then
|
||||
self:onPagingRel(-1)
|
||||
else
|
||||
self:onPagingRel(1)
|
||||
end
|
||||
self:onPagingRel(1)
|
||||
elseif ges.direction == "east" then
|
||||
if DCHANGE_EAST_SWIPE_TO_WEST then
|
||||
self:onPagingRel(1)
|
||||
else
|
||||
self:onPagingRel(-1)
|
||||
end
|
||||
self:onPagingRel(-1)
|
||||
else
|
||||
-- trigger full refresh
|
||||
UIManager:setDirty(nil, "full")
|
||||
|
||||
@@ -14,8 +14,7 @@ local ReaderPanning = InputContainer:new{
|
||||
}
|
||||
|
||||
function ReaderPanning:init()
|
||||
if Device:isTouchDevice() then
|
||||
else
|
||||
if Device:hasKeyboard() then
|
||||
self.key_events = {
|
||||
-- these will all generate the same event, just with different arguments
|
||||
MoveUp = {
|
||||
|
||||
@@ -261,17 +261,9 @@ end
|
||||
|
||||
function ReaderRolling:onSwipe(arg, ges)
|
||||
if ges.direction == "west" or ges.direction == "north" then
|
||||
if DCHANGE_WEST_SWIPE_TO_EAST then
|
||||
self:onGotoViewRel(-1)
|
||||
else
|
||||
self:onGotoViewRel(1)
|
||||
end
|
||||
self:onGotoViewRel(1)
|
||||
elseif ges.direction == "east" or ges.direction == "south" then
|
||||
if DCHANGE_EAST_SWIPE_TO_WEST then
|
||||
self:onGotoViewRel(1)
|
||||
else
|
||||
self:onGotoViewRel(-1)
|
||||
end
|
||||
self:onGotoViewRel(-1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -424,6 +424,12 @@ function GestureDetector:handleSwipe(tev)
|
||||
y = self.first_tevs[slot].y,
|
||||
w = 0, h = 0,
|
||||
}
|
||||
-- TODO: dirty hack for some weird devices, replace it with better solution
|
||||
if swipe_direction == "west" and DCHANGE_WEST_SWIPE_TO_EAST then
|
||||
swipe_direction = "east"
|
||||
elseif swipe_direction == "east" and DCHANGE_EAST_SWIPE_TO_WEST then
|
||||
swipe_direction = "west"
|
||||
end
|
||||
DEBUG("swipe", swipe_direction, swipe_distance, "detected in slot", slot)
|
||||
self:clearState(slot)
|
||||
return {
|
||||
|
||||
@@ -4,14 +4,14 @@ local dump = require("dump")
|
||||
|
||||
local DocSettings = {}
|
||||
|
||||
local history_dir = DataStorage:getDataDir() .. "/history/"
|
||||
local history_dir = DataStorage:getHistoryDir()
|
||||
|
||||
function DocSettings:getSidecarDir(doc_path)
|
||||
return doc_path:match("(.*)%.")..".sdr"
|
||||
end
|
||||
|
||||
function DocSettings:getHistoryPath(fullpath)
|
||||
return history_dir .. "[" .. fullpath:gsub("(.*/)([^/]+)","%1] %2"):gsub("/","#") .. ".lua"
|
||||
return history_dir .. "/[" .. fullpath:gsub("(.*/)([^/]+)","%1] %2"):gsub("/","#") .. ".lua"
|
||||
end
|
||||
|
||||
function DocSettings:getPathFromHistory(hist_name)
|
||||
|
||||
@@ -31,9 +31,10 @@ function PdfDocument:init()
|
||||
else
|
||||
self:_readMetadata()
|
||||
end
|
||||
if not (self.info.number_of_pages > 0) then
|
||||
-- TODO: handle this
|
||||
-- if not (self.info.number_of_pages > 0) then
|
||||
--error("No page found in PDF file")
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
function PdfDocument:unlock(password)
|
||||
|
||||
@@ -10,6 +10,7 @@ local PicDocument = Document:new{
|
||||
|
||||
function PicDocument:init()
|
||||
if not pic then pic = require("ffi/pic") end
|
||||
local ok
|
||||
ok, self._document = pcall(pic.openDocument, self.file)
|
||||
if not ok then
|
||||
error("Failed to open jpeg image")
|
||||
|
||||
@@ -11,8 +11,8 @@ local GestureRange = {
|
||||
scale = nil,
|
||||
}
|
||||
|
||||
function GestureRange:new(o)
|
||||
local o = o or {}
|
||||
function GestureRange:new(from_o)
|
||||
local o = from_o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
@@ -29,7 +29,7 @@ function GestureRange:match(gs)
|
||||
-- e.g. range = function() return self.dimen end
|
||||
-- for inputcontainer given that the x and y field of `self.dimen` is only
|
||||
-- filled when the inputcontainer is painted into blitbuffer
|
||||
local range = nil
|
||||
local range
|
||||
if type(self.range) == "function" then
|
||||
range = self.range()
|
||||
else
|
||||
|
||||
@@ -18,7 +18,7 @@ local GlyphCache = Cache:new{
|
||||
}
|
||||
|
||||
-- iterator over UTF8 encoded characters in a string
|
||||
local function utf8Chars(input)
|
||||
local function utf8Chars(input_text)
|
||||
local function read_next_glyph(input, pos)
|
||||
if string.len(input) < pos then return nil end
|
||||
local value = string.byte(input, pos)
|
||||
@@ -60,7 +60,7 @@ local function utf8Chars(input)
|
||||
return pos+bytes_left+1, glyph, string.sub(input, pos, pos+bytes_left)
|
||||
end
|
||||
end
|
||||
return read_next_glyph, input, 1
|
||||
return read_next_glyph, input_text, 1
|
||||
end
|
||||
|
||||
function RenderText:getGlyph(face, charcode, bold)
|
||||
@@ -81,7 +81,7 @@ function RenderText:getGlyph(face, charcode, bold)
|
||||
rendered_glyph = fb_face.ftface:renderGlyph(charcode, bold)
|
||||
--DEBUG("fallback to font", font)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,7 +9,6 @@ local UIManager = require("ui/uimanager")
|
||||
local Geom = require("ui/geometry")
|
||||
local Device = require("device")
|
||||
local Font = require("ui/font")
|
||||
local DEBUG = require("dbg")
|
||||
local _ = require("gettext")
|
||||
|
||||
--[[
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
--[[--
|
||||
Button widget that shows an "×" and handles closing window when tapped
|
||||
|
||||
Example:
|
||||
|
||||
local parent_widget = HorizontalGroup:new{}
|
||||
table.insert(parent_widget, CloseButton:new{
|
||||
window = parent_widget,
|
||||
})
|
||||
UIManager:show(parent_widget)
|
||||
|
||||
]]
|
||||
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local FrameContainer = require("ui/widget/container/framecontainer")
|
||||
local TextWidget = require("ui/widget/textwidget")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local Font = require("ui/font")
|
||||
|
||||
--[[
|
||||
a button widget that shows an "×" and handles closing window when tapped
|
||||
--]]
|
||||
local CloseButton = InputContainer:new{
|
||||
align = "right",
|
||||
overlap_align = "right",
|
||||
window = nil,
|
||||
}
|
||||
|
||||
@@ -23,7 +33,7 @@ function CloseButton:init()
|
||||
text_widget
|
||||
}
|
||||
|
||||
self.dimen = text_widget:getSize():copy()
|
||||
self.dimen = text_widget:getSize()
|
||||
|
||||
self.ges_events.Close = {
|
||||
GestureRange:new{
|
||||
|
||||
@@ -8,11 +8,15 @@ local WidgetContainer = Widget:new()
|
||||
|
||||
function WidgetContainer:init()
|
||||
if self.dimen then
|
||||
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
|
||||
if self.initDimen then
|
||||
self:initDimen()
|
||||
else
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
@@ -91,9 +91,7 @@ function DictQuickLookup:init()
|
||||
},
|
||||
}
|
||||
table.insert(self.dict_bar,
|
||||
CloseButton:new{
|
||||
window = self,
|
||||
})
|
||||
CloseButton:new{ window = self, })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ function HorizontalGroup:paintTo(bb, x, y)
|
||||
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)
|
||||
else
|
||||
print("[!] invalid alignment for HorizontalGroup", self.align)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
273
frontend/ui/widget/keyvaluepage.lua
Normal file
273
frontend/ui/widget/keyvaluepage.lua
Normal file
@@ -0,0 +1,273 @@
|
||||
--[[--
|
||||
Widget that presents a multi-page to show key value pairs.
|
||||
|
||||
Example:
|
||||
|
||||
local Foo = KeyValuePage:new{
|
||||
title = "Statistics",
|
||||
kv_pairs = {
|
||||
{"Current period", "00:00:00"},
|
||||
-- single or more "-" will generate a solid line
|
||||
"----------------------------",
|
||||
{"Page to read", "5"},
|
||||
{"Time to read", "00:01:00"},
|
||||
{"Press me", "will invoke the callback",
|
||||
callback = function() print("hello") end },
|
||||
},
|
||||
}
|
||||
UIManager:show(Foo)
|
||||
|
||||
]]
|
||||
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local FrameContainer = require("ui/widget/container/framecontainer")
|
||||
local VerticalGroup = require("ui/widget/verticalgroup")
|
||||
local VerticalSpan = require("ui/widget/verticalspan")
|
||||
local OverlapGroup = require("ui/widget/overlapgroup")
|
||||
local LeftContainer = require("ui/widget/container/leftcontainer")
|
||||
local RightContainer = require("ui/widget/container/rightcontainer")
|
||||
local LineWidget = require("ui/widget/linewidget")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
local CloseButton = require("ui/widget/closebutton")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local TextWidget = require("ui/widget/textwidget")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local Geom = require("ui/geometry")
|
||||
local Font = require("ui/font")
|
||||
local Device = require("device")
|
||||
local Screen = Device.screen
|
||||
|
||||
|
||||
local KeyValueTitle = VerticalGroup:new{
|
||||
kv_page = nil,
|
||||
title = "",
|
||||
align = "left",
|
||||
}
|
||||
|
||||
function KeyValueTitle:init()
|
||||
self.close_button = CloseButton:new{ window = self }
|
||||
table.insert(self, OverlapGroup:new{
|
||||
dimen = { w = self.width },
|
||||
TextWidget:new{
|
||||
text = self.title,
|
||||
face = Font:getFace("tfont", 26),
|
||||
},
|
||||
self.close_button,
|
||||
})
|
||||
self.page_cnt = FrameContainer:new{
|
||||
padding = 4,
|
||||
margin = 0,
|
||||
bordersize = 0,
|
||||
background = Blitbuffer.COLOR_WHITE,
|
||||
overlap_offset = {0, -18},
|
||||
TextWidget:new{
|
||||
text = "", -- page count
|
||||
fgcolor = Blitbuffer.COLOR_GREY,
|
||||
face = Font:getFace("ffont", 16),
|
||||
},
|
||||
}
|
||||
self.title_bottom = OverlapGroup:new{
|
||||
dimen = { w = self.width, h = Screen:scaleBySize(2) },
|
||||
LineWidget:new{
|
||||
dimen = Geom:new{ w = self.width, h = Screen:scaleBySize(2) },
|
||||
background = Blitbuffer.COLOR_GREY,
|
||||
style = "solid",
|
||||
},
|
||||
self.page_cnt,
|
||||
}
|
||||
table.insert(self, self.title_bottom)
|
||||
table.insert(self, VerticalSpan:new{ width = Screen:scaleBySize(5) })
|
||||
end
|
||||
|
||||
function KeyValueTitle:setPageCount(curr, total)
|
||||
if total == 1 then
|
||||
-- remove page count if there is only one page
|
||||
table.remove(self.title_bottom, 2)
|
||||
return
|
||||
end
|
||||
self.page_cnt[1]:setText(curr .. "/" .. total)
|
||||
self.page_cnt.overlap_offset[1] = (self.width - self.page_cnt:getSize().w
|
||||
- self.close_button:getSize().w)
|
||||
self.title_bottom[2] = self.page_cnt
|
||||
end
|
||||
|
||||
function KeyValueTitle:onClose()
|
||||
self.kv_page:onClose()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local KeyValueItem = InputContainer:new{
|
||||
key = nil,
|
||||
value = nil,
|
||||
cface = Font:getFace("cfont", 24),
|
||||
width = nil,
|
||||
height = nil,
|
||||
}
|
||||
|
||||
function KeyValueItem:init()
|
||||
self.dimen = Geom:new{w = self.width, h = self.height}
|
||||
|
||||
if self.callback and Device:isTouchDevice() then
|
||||
self.ges_events.Tap = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = self.dimen,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
self[1] = OverlapGroup:new{
|
||||
dimen = self.dimen:copy(),
|
||||
LeftContainer:new{
|
||||
dimen = self.dimen:copy(),
|
||||
TextWidget:new{
|
||||
text = self.key,
|
||||
face = self.cface,
|
||||
}
|
||||
},
|
||||
RightContainer:new{
|
||||
dimen = self.dimen:copy(),
|
||||
TextWidget:new{
|
||||
text = self.value,
|
||||
face = self.cface,
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
function KeyValueItem:onTap()
|
||||
self.callback()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local KeyValuePage = InputContainer:new{
|
||||
title = "",
|
||||
width = nil,
|
||||
height = nil,
|
||||
-- index for the first item to show
|
||||
show_page = 1,
|
||||
}
|
||||
|
||||
function KeyValuePage:init()
|
||||
self.dimen = Geom:new{
|
||||
w = self.width or Screen:getWidth(),
|
||||
h = self.height or Screen:getHeight(),
|
||||
}
|
||||
|
||||
if Device:isTouchDevice() then
|
||||
self.ges_events.Swipe = {
|
||||
GestureRange:new{
|
||||
ges = "swipe",
|
||||
range = self.dimen,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local padding = Screen:scaleBySize(10)
|
||||
self.item_width = self.dimen.w - 2 * padding
|
||||
self.item_height = Screen:scaleBySize(30)
|
||||
-- setup title bar
|
||||
self.title_bar = KeyValueTitle:new{
|
||||
title = self.title,
|
||||
width = self.item_width,
|
||||
height = self.item_height,
|
||||
kv_page = self,
|
||||
}
|
||||
-- setup main content
|
||||
self.item_padding = self.item_height / 4
|
||||
local line_height = self.item_height + 2 * self.item_padding
|
||||
local content_height = self.dimen.h - self.title_bar:getSize().h
|
||||
self.items_per_page = math.floor(content_height / line_height)
|
||||
self.pages = math.ceil(#self.kv_pairs / self.items_per_page)
|
||||
self.main_content = VerticalGroup:new{}
|
||||
self:_populateItems()
|
||||
-- assemble page
|
||||
self[1] = FrameContainer:new{
|
||||
height = self.dimen.h,
|
||||
padding = padding,
|
||||
bordersize = 0,
|
||||
background = Blitbuffer.COLOR_WHITE,
|
||||
VerticalGroup:new{
|
||||
self.title_bar,
|
||||
self.main_content,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
function KeyValuePage:nextPage()
|
||||
local new_page = math.min(self.show_page+1, self.pages)
|
||||
if new_page > self.show_page then
|
||||
self.show_page = new_page
|
||||
self:_populateItems()
|
||||
end
|
||||
end
|
||||
|
||||
function KeyValuePage:prevPage()
|
||||
local new_page = math.max(self.show_page-1, 1)
|
||||
if new_page < self.show_page then
|
||||
self.show_page = new_page
|
||||
self:_populateItems()
|
||||
end
|
||||
end
|
||||
|
||||
-- make sure self.item_padding and self.item_height are set before calling this
|
||||
function KeyValuePage:_populateItems()
|
||||
self.main_content:clear()
|
||||
local idx_offset = (self.show_page - 1) * self.items_per_page
|
||||
for idx = 1, self.items_per_page do
|
||||
local entry = self.kv_pairs[idx_offset + idx]
|
||||
if entry == nil then break end
|
||||
|
||||
table.insert(self.main_content,
|
||||
VerticalSpan:new{ width = self.item_padding })
|
||||
if type(entry) == "table" then
|
||||
table.insert(
|
||||
self.main_content,
|
||||
KeyValueItem:new{
|
||||
height = self.item_height,
|
||||
width = self.item_width,
|
||||
key = entry[1],
|
||||
value = entry[2],
|
||||
callback = entry.callback,
|
||||
}
|
||||
)
|
||||
elseif type(entry) == "string" then
|
||||
local c = string.sub(entry, 1, 1)
|
||||
if c == "-" then
|
||||
table.insert(self.main_content, LineWidget:new{
|
||||
background = Blitbuffer.COLOR_LIGHT_GREY,
|
||||
dimen = Geom:new{
|
||||
w = self.item_width,
|
||||
h = Screen:scaleBySize(2)
|
||||
},
|
||||
style = "solid",
|
||||
})
|
||||
end
|
||||
end
|
||||
table.insert(self.main_content,
|
||||
VerticalSpan:new{ width = self.item_padding })
|
||||
end
|
||||
self.title_bar:setPageCount(self.show_page, self.pages)
|
||||
UIManager:setDirty(self, function()
|
||||
return "ui", self.dimen
|
||||
end)
|
||||
end
|
||||
|
||||
function KeyValuePage:onSwipe(arg, ges_ev)
|
||||
if ges_ev.direction == "west" then
|
||||
self:nextPage()
|
||||
return true
|
||||
elseif ges_ev.direction == "east" then
|
||||
self:prevPage()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function KeyValuePage:onClose()
|
||||
UIManager:close(self)
|
||||
return true
|
||||
end
|
||||
|
||||
return KeyValuePage
|
||||
@@ -19,13 +19,13 @@ function LineWidget:paintTo(bb, x, y)
|
||||
else
|
||||
if self.empty_segments then
|
||||
bb:paintRect(x, y,
|
||||
self.empty_segments[1].s,
|
||||
self.dimen.h,
|
||||
self.background)
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -8,7 +8,6 @@ local BottomContainer = require("ui/widget/container/bottomcontainer")
|
||||
local UnderlineContainer = require("ui/widget/container/underlinecontainer")
|
||||
local FocusManager = require("ui/widget/focusmanager")
|
||||
local TextWidget = require("ui/widget/textwidget")
|
||||
local LineWidget = require("ui/widget/linewidget")
|
||||
local OverlapGroup = require("ui/widget/overlapgroup")
|
||||
local VerticalSpan = require("ui/widget/verticalspan")
|
||||
local HorizontalSpan = require("ui/widget/horizontalspan")
|
||||
@@ -82,7 +81,7 @@ NOTICE:
|
||||
@menu entry must be provided in order to close the menu
|
||||
--]]
|
||||
local MenuCloseButton = InputContainer:new{
|
||||
align = "right",
|
||||
overlap_align = "right",
|
||||
menu = nil,
|
||||
dimen = Geom:new{},
|
||||
}
|
||||
@@ -94,7 +93,10 @@ function MenuCloseButton:init()
|
||||
}
|
||||
|
||||
local text_size = self[1]:getSize()
|
||||
self.dimen.w, self.dimen.h = text_size.w*2, text_size.h*2
|
||||
self.dimen = Geom:new{
|
||||
w = text_size.w*2,
|
||||
h = text_size.h*2,
|
||||
}
|
||||
|
||||
self.ges_events.Close = {
|
||||
GestureRange:new{
|
||||
@@ -110,45 +112,6 @@ function MenuCloseButton:onClose()
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
Widget that displays a solid line in menu
|
||||
--]]
|
||||
local SeparatorMenuItem = InputContainer:new{
|
||||
style = "solid",
|
||||
dimen = nil,
|
||||
_line_container = nil,
|
||||
}
|
||||
|
||||
function SeparatorMenuItem:init()
|
||||
self._line_container = CenterContainer:new{
|
||||
vertical_align = "center",
|
||||
dimen = Geom:new {
|
||||
w = self.dimen.w,
|
||||
h = self.dimen.h
|
||||
},
|
||||
HorizontalGroup:new {
|
||||
align = "center",
|
||||
OverlapGroup:new {
|
||||
dimen = Geom:new { w = self.dimen.w, h = Screen:scaleBySize(2) },
|
||||
LineWidget:new {
|
||||
style = self.style,
|
||||
dimen = Geom:new { w = self.dimen.w - 30, h = Screen:scaleBySize(2) },
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
self[1] = FrameContainer:new {
|
||||
bordersize = 0,
|
||||
padding = 0,
|
||||
HorizontalGroup:new {
|
||||
align = "center",
|
||||
HorizontalSpan:new { width = 15 },
|
||||
self._line_container
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
--[[
|
||||
Widget that displays an item for menu
|
||||
--]]
|
||||
@@ -430,7 +393,7 @@ function Menu:init()
|
||||
-- start to set up widget layout --
|
||||
-----------------------------------
|
||||
self.menu_title = TextWidget:new{
|
||||
align = "center",
|
||||
overlap_align = "center",
|
||||
text = self.title,
|
||||
face = self.tface,
|
||||
}
|
||||
@@ -578,9 +541,7 @@ function Menu:init()
|
||||
if Device:isTouchDevice() then
|
||||
if self.has_close_button then
|
||||
table.insert(self.title_bar,
|
||||
MenuCloseButton:new{
|
||||
menu = self,
|
||||
})
|
||||
MenuCloseButton:new{ menu = self })
|
||||
end
|
||||
-- watch for outer region if it's a self contained widget
|
||||
if self.is_popout then
|
||||
@@ -681,27 +642,20 @@ function Menu:updateItems(select_number)
|
||||
item_shortcut = "Ent"
|
||||
end
|
||||
end
|
||||
local item_tmp
|
||||
if self.item_table[i].text == "-" then
|
||||
item_tmp = SeparatorMenuItem:new{
|
||||
dimen = self.item_dimen:new{ h = Screen:scaleBySize(10) },
|
||||
}
|
||||
else
|
||||
item_tmp = MenuItem:new{
|
||||
show_parent = self.show_parent,
|
||||
state = self.item_table[i].state,
|
||||
state_size = self.state_size or {},
|
||||
text = self.item_table[i].text,
|
||||
mandatory = self.item_table[i].mandatory,
|
||||
bold = self.item_table.current == i,
|
||||
face = self.cface,
|
||||
dimen = self.item_dimen:new(),
|
||||
shortcut = item_shortcut,
|
||||
shortcut_style = shortcut_style,
|
||||
table = self.item_table[i],
|
||||
menu = self,
|
||||
}
|
||||
end
|
||||
local item_tmp = MenuItem:new{
|
||||
show_parent = self.show_parent,
|
||||
state = self.item_table[i].state,
|
||||
state_size = self.state_size or {},
|
||||
text = self.item_table[i].text,
|
||||
mandatory = self.item_table[i].mandatory,
|
||||
bold = self.item_table.current == i,
|
||||
face = self.cface,
|
||||
dimen = self.item_dimen:new(),
|
||||
shortcut = item_shortcut,
|
||||
shortcut_style = shortcut_style,
|
||||
table = self.item_table[i],
|
||||
menu = self,
|
||||
}
|
||||
table.insert(self.item_group, item_tmp)
|
||||
-- this is for focus manager
|
||||
table.insert(self.layout, {item_tmp})
|
||||
@@ -923,17 +877,9 @@ end
|
||||
|
||||
function Menu:onSwipe(arg, ges_ev)
|
||||
if ges_ev.direction == "west" then
|
||||
if DCHANGE_WEST_SWIPE_TO_EAST then
|
||||
self:onPrevPage()
|
||||
else
|
||||
self:onNextPage()
|
||||
end
|
||||
self:onNextPage()
|
||||
elseif ges_ev.direction == "east" then
|
||||
if DCHANGE_WEST_SWIPE_TO_EAST then
|
||||
self:onNextPage()
|
||||
else
|
||||
self:onPrevPage()
|
||||
end
|
||||
self:onPrevPage()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -22,14 +22,22 @@ function OverlapGroup:getSize()
|
||||
end
|
||||
end
|
||||
|
||||
return self._size
|
||||
end
|
||||
|
||||
function OverlapGroup:initDimen()
|
||||
self:getSize() -- populate self._size
|
||||
-- sync self._size with self.dimen, self.dimen has higher priority
|
||||
if self.dimen.w then
|
||||
self._size.w = self.dimen.w
|
||||
else
|
||||
self.dimen.w = self._size.w
|
||||
end
|
||||
if self.dimen.h then
|
||||
self._size.h = self.dimen.h
|
||||
else
|
||||
self.dimen.h = self._size.h
|
||||
end
|
||||
|
||||
return self._size
|
||||
end
|
||||
|
||||
function OverlapGroup:paintTo(bb, x, y)
|
||||
@@ -37,10 +45,12 @@ function OverlapGroup:paintTo(bb, x, y)
|
||||
|
||||
for i, wget in ipairs(self) do
|
||||
local wget_size = wget:getSize()
|
||||
if wget.align == "right" then
|
||||
if wget.overlap_align == "right" then
|
||||
wget:paintTo(bb, x+size.w-wget_size.w, y)
|
||||
elseif wget.align == "center" then
|
||||
elseif wget.overlap_align == "center" then
|
||||
wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y)
|
||||
elseif wget.overlap_offset then
|
||||
wget:paintTo(bb, x+wget.overlap_offset[1], y+wget.overlap_offset[2])
|
||||
else
|
||||
-- default to left
|
||||
wget:paintTo(bb, x, y)
|
||||
|
||||
@@ -25,23 +25,33 @@ local TextWidget = Widget:new{
|
||||
--self._length = RenderText:renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, true, self.bold)
|
||||
--end
|
||||
|
||||
function TextWidget:updateSize()
|
||||
local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold)
|
||||
if not tsize then
|
||||
self._length = 0
|
||||
else
|
||||
self._length = tsize.x
|
||||
end
|
||||
self._height = self.face.size * 1.5
|
||||
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
|
||||
self:updateSize()
|
||||
return Geom:new{
|
||||
w = self._length,
|
||||
h = self._height,
|
||||
}
|
||||
end
|
||||
|
||||
function TextWidget:setText(text)
|
||||
self.text = text
|
||||
self:updateSize()
|
||||
end
|
||||
|
||||
function TextWidget:paintTo(bb, x, y)
|
||||
--if not self._bb then
|
||||
--self:_render()
|
||||
|
||||
@@ -94,16 +94,13 @@ function TouchMenuItem:onTapSelect(arg, ges)
|
||||
end
|
||||
if enabled == false then return end
|
||||
|
||||
UIManager:scheduleIn(0.0, function()
|
||||
self.item_frame.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self.dimen
|
||||
end)
|
||||
self.item_frame.invert = true
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self.dimen
|
||||
end)
|
||||
-- yield to main UI loop to invert item
|
||||
UIManager:scheduleIn(0.1, function()
|
||||
self.menu:onMenuSelect(self.item)
|
||||
end)
|
||||
UIManager:scheduleIn(0.5, function()
|
||||
self.item_frame.invert = false
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self.dimen
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local MultiInputDialog = require("ui/widget/multiinputdialog")
|
||||
local CenterContainer = require("ui/widget/container/centercontainer")
|
||||
local KeyValuePage = require("ui/widget/keyvaluepage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local Screen = require("device").screen
|
||||
local Menu = require("ui/widget/menu")
|
||||
local Font = require("ui/font")
|
||||
local TimeVal = require("ui/timeval")
|
||||
local DataStorage = require("datastorage")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local DEBUG = require("dbg")
|
||||
local T = require("ffi/util").template
|
||||
local joinPath = require("ffi/util").joinPath
|
||||
local _ = require("gettext")
|
||||
local util = require("util")
|
||||
local tableutil = require("tableutil")
|
||||
|
||||
local statistics_dir = DataStorage:getDataDir() .. "/statistics/"
|
||||
local history_dir = DataStorage:getDataDir() .. "/history/"
|
||||
local history_dir = DataStorage:getHistoryDir()
|
||||
|
||||
local ReaderStatistics = InputContainer:new {
|
||||
last_time = nil,
|
||||
@@ -51,37 +52,37 @@ function ReaderStatistics:init()
|
||||
self.last_time = TimeVal:now()
|
||||
end
|
||||
|
||||
function ReaderStatistics:getBookProperties()
|
||||
local props = self.view.document:getProps()
|
||||
if props.title == "No document" or props.title == "" then
|
||||
-- FIXME: sometimes crengine returns "No document", try one more time
|
||||
props = self.view.document:getProps()
|
||||
end
|
||||
return props
|
||||
end
|
||||
|
||||
function ReaderStatistics:initData(config)
|
||||
--first execution
|
||||
-- first execution
|
||||
if self.is_enabled then
|
||||
local book_properties = self:getBookProperties()
|
||||
self:savePropertiesInToData(book_properties)
|
||||
if not self.data then
|
||||
--first time merge data
|
||||
self:inplaceMigration();
|
||||
self.data = { performance_in_pages= {} }
|
||||
self:inplaceMigration(); -- first time merge data
|
||||
end
|
||||
|
||||
local book_properties = self:getBookProperties()
|
||||
self.data.title = book_properties.title
|
||||
self.data.authors = book_properties.authors
|
||||
self.data.language = book_properties.language
|
||||
self.data.series = book_properties.series
|
||||
|
||||
self.data.pages = self.view.document:getPageCount()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderStatistics:addToMainMenu(tab_item_table)
|
||||
table.insert(tab_item_table.plugins, {
|
||||
text = _("Statistics"),
|
||||
sub_item_table = {
|
||||
self:getStatisticEnabledMenuTable(),
|
||||
self:getStatisticSettingsMenuTable(),
|
||||
self:getStatisticForCurrentBookMenuTable(),
|
||||
self:getStatisticTotalStatisticMenuTable(),
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
function ReaderStatistics:getStatisticEnabledMenuTable()
|
||||
function ReaderStatistics:getStatisticEnabledMenuItem()
|
||||
return {
|
||||
text_func = function()
|
||||
return _("Enabled")
|
||||
end,
|
||||
text = _("Enabled"),
|
||||
checked_func = function() return self.is_enabled end,
|
||||
callback = function()
|
||||
-- if was enabled, have to save data to file
|
||||
@@ -99,18 +100,6 @@ function ReaderStatistics:getStatisticEnabledMenuTable()
|
||||
}
|
||||
end
|
||||
|
||||
function ReaderStatistics:getStatisticSettingsMenuTable()
|
||||
return {
|
||||
text_func = function()
|
||||
return _("Settings")
|
||||
end,
|
||||
checked_func = function() return false end,
|
||||
callback = function()
|
||||
self:updateSettings()
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
function ReaderStatistics:updateSettings()
|
||||
self.settings_dialog = MultiInputDialog:new {
|
||||
title = _("Statistics settings"),
|
||||
@@ -138,9 +127,9 @@ function ReaderStatistics:updateSettings()
|
||||
{
|
||||
text = _("Apply"),
|
||||
callback = function()
|
||||
self:saveSettings(MultiInputDialog:getFields())
|
||||
self.settings_dialog:onClose()
|
||||
UIManager:close(self.settings_dialog)
|
||||
self:saveSettings(MultiInputDialog:getFields())
|
||||
end
|
||||
},
|
||||
},
|
||||
@@ -153,101 +142,74 @@ function ReaderStatistics:updateSettings()
|
||||
UIManager:show(self.settings_dialog)
|
||||
end
|
||||
|
||||
function ReaderStatistics:getStatisticForCurrentBookMenuTable()
|
||||
self.status_menu = {}
|
||||
|
||||
local book_status = Menu:new {
|
||||
title = _("Status"),
|
||||
item_table = self:updateCurrentStat(),
|
||||
is_borderless = true,
|
||||
is_popout = false,
|
||||
is_enable_shortcut = false,
|
||||
width = Screen:getWidth(),
|
||||
height = Screen:getHeight(),
|
||||
cface = Font:getFace("cfont", 20),
|
||||
}
|
||||
|
||||
self.status_menu = CenterContainer:new {
|
||||
dimen = Screen:getSize(),
|
||||
book_status,
|
||||
}
|
||||
|
||||
book_status.close_callback = function()
|
||||
UIManager:close(self.status_menu)
|
||||
end
|
||||
|
||||
book_status.show_parent = self.status_menu
|
||||
|
||||
return {
|
||||
text = _("Current"),
|
||||
enabled_func = function() return true end,
|
||||
checked_func = function() return false end,
|
||||
callback = function()
|
||||
book_status:swithItemTable(nil, self:updateCurrentStat())
|
||||
UIManager:show(self.status_menu)
|
||||
return true
|
||||
end
|
||||
}
|
||||
function ReaderStatistics:addToMainMenu(tab_item_table)
|
||||
table.insert(tab_item_table.plugins, {
|
||||
text = _("Statistics"),
|
||||
sub_item_table = {
|
||||
self:getStatisticEnabledMenuItem(),
|
||||
{
|
||||
text = _("Settings"),
|
||||
callback = function() self:updateSettings() end,
|
||||
},
|
||||
{
|
||||
text = _("Current book"),
|
||||
callback = function()
|
||||
UIManager:show(KeyValuePage:new{
|
||||
title = _("Statistics"),
|
||||
kv_pairs = self:getCurrentStat(),
|
||||
})
|
||||
end
|
||||
},
|
||||
{
|
||||
text = _("All books"),
|
||||
callback = function()
|
||||
total_msg, kv_pairs = self:getTotalStat()
|
||||
UIManager:show(KeyValuePage:new{
|
||||
title = total_msg,
|
||||
kv_pairs = kv_pairs,
|
||||
})
|
||||
end
|
||||
},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
function ReaderStatistics:getStatisticTotalStatisticMenuTable()
|
||||
self.total_status = Menu:new {
|
||||
title = _("Total"),
|
||||
item_table = self:updateTotalStat(),
|
||||
is_borderless = true,
|
||||
is_popout = false,
|
||||
is_enable_shortcut = false,
|
||||
width = Screen:getWidth(),
|
||||
height = Screen:getHeight(),
|
||||
cface = Font:getFace("cfont", 20),
|
||||
}
|
||||
|
||||
self.total_menu = CenterContainer:new {
|
||||
dimen = Screen:getSize(),
|
||||
self.total_status,
|
||||
}
|
||||
|
||||
self.total_status.close_callback = function()
|
||||
UIManager:close(self.total_menu)
|
||||
end
|
||||
|
||||
self.total_status.show_parent = self.total_menu
|
||||
|
||||
return {
|
||||
text = _("Total"),
|
||||
callback = function()
|
||||
self.total_status:swithItemTable(nil, self:updateTotalStat())
|
||||
UIManager:show(self.total_menu)
|
||||
return true
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
function ReaderStatistics:updateCurrentStat()
|
||||
local stats = {}
|
||||
function ReaderStatistics:getCurrentStat()
|
||||
local dates = {}
|
||||
|
||||
for k, v in pairs(self.data.performance_in_pages) do
|
||||
dates[os.date("%Y-%m-%d", k)] = ""
|
||||
end
|
||||
local total_days = util.tableSize(dates)
|
||||
|
||||
local read_pages = util.tableSize(self.data.performance_in_pages)
|
||||
local current_page = self.view.state.page --get current page from the view
|
||||
local average_time_per_page = self.data.total_time_in_sec / read_pages
|
||||
local current_page = self.view.state.page -- get current page from the view
|
||||
local avg_time_per_page = self.data.total_time_in_sec / read_pages
|
||||
|
||||
table.insert(stats, { text = _("Current period"), mandatory = util.secondsToClock(self.current_period, false) })
|
||||
table.insert(stats, { text = _("Time to read"), mandatory = util.secondsToClock((self.data.pages - current_page) * average_time_per_page, false) })
|
||||
table.insert(stats, { text = _("Total time"), mandatory = util.secondsToClock(self.data.total_time_in_sec, false) })
|
||||
table.insert(stats, { text = _("Total highlights"), mandatory = self.data.highlights })
|
||||
table.insert(stats, { text = _("Total notes"), mandatory = self.data.notes })
|
||||
table.insert(stats, { text = _("Total days"), mandatory = util.tableSize(dates) })
|
||||
table.insert(stats, { text = _("Average time per page"), mandatory = util.secondsToClock(average_time_per_page, false) })
|
||||
table.insert(stats, { text = _("Read pages/Total pages"), mandatory = read_pages .. "/" .. self.data.pages })
|
||||
return stats
|
||||
return {
|
||||
{ _("Current period"), util.secondsToClock(self.current_period, false) },
|
||||
{ _("Time to read"), util.secondsToClock((self.data.pages - current_page) * avg_time_per_page, false) },
|
||||
{ _("Total time"), util.secondsToClock(self.data.total_time_in_sec, false) },
|
||||
{ _("Total highlights"), self.data.highlights },
|
||||
{ _("Total notes"), self.data.notes },
|
||||
{ _("Total days"), total_days },
|
||||
{ _("Average time per page"), util.secondsToClock(avg_time_per_page, false) },
|
||||
{ _("Read pages/Total pages"), read_pages .. "/" .. self.data.pages },
|
||||
}
|
||||
end
|
||||
|
||||
function generateReadBooksTable(title, dates)
|
||||
local result = {}
|
||||
for k, v in tableutil.spairs(dates, function(t, a, b) return t[b].date < t[a].date end) do
|
||||
table.insert(result, {
|
||||
k,
|
||||
T(_("Pages (%1) Time: %2"), v.count, util.secondsToClock(v.read, false))
|
||||
})
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- For backward compatibility
|
||||
function ReaderStatistics:getDatesForBookOldFormat(book)
|
||||
function getDatesForBookOldFormat(book)
|
||||
local dates = {}
|
||||
|
||||
for k, v in pairs(book.details) do
|
||||
@@ -267,11 +229,10 @@ function ReaderStatistics:getDatesForBookOldFormat(book)
|
||||
end
|
||||
end
|
||||
|
||||
return self:generateReadBooksTable(book.title, dates)
|
||||
return generateReadBooksTable(book.title, dates)
|
||||
end
|
||||
|
||||
|
||||
function ReaderStatistics:getDatesForBook(book)
|
||||
function getDatesForBook(book)
|
||||
local dates = {}
|
||||
|
||||
for k, v in pairs(book.performance_in_pages) do
|
||||
@@ -283,107 +244,93 @@ function ReaderStatistics:getDatesForBook(book)
|
||||
count = 1
|
||||
}
|
||||
else
|
||||
dates[date_text] = {
|
||||
read = dates[date_text].read + v,
|
||||
count = dates[date_text].count + 1,
|
||||
date = dates[date_text].date
|
||||
}
|
||||
-- TODO: test this
|
||||
local entry = dates[date_text]
|
||||
entry.read = entry.read + v
|
||||
entry.count = entry.count + 1
|
||||
end
|
||||
end
|
||||
|
||||
return self:generateReadBooksTable(book.title, dates)
|
||||
return generateReadBooksTable(book.title, dates)
|
||||
end
|
||||
|
||||
function ReaderStatistics:getTotalStat()
|
||||
local total_stats = {
|
||||
{
|
||||
self.data.title,
|
||||
util.secondsToClock(self.data.total_time_in_sec, false),
|
||||
callback = function()
|
||||
UIManager:show(KeyValuePage:new{
|
||||
title = self.data.title,
|
||||
kv_pairs = getDatesForBook(self.data),
|
||||
})
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
function ReaderStatistics:generateReadBooksTable(title, dates)
|
||||
local result = {}
|
||||
table.insert(result, { text = title })
|
||||
for k, v in tableutil.spairs(dates, function(t, a, b) return t[b].date < t[a].date end) do
|
||||
table.insert(result, { text = k, mandatory = T(_("Pages (%1) Time: %2"), v.count, util.secondsToClock(v.read, false)) })
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
function ReaderStatistics:updateTotalStat()
|
||||
local total_stats = {}
|
||||
local total_books_time = 0
|
||||
|
||||
local proceded_titles = self:getStatisticsFromHistory(total_stats, total_books_time)
|
||||
self:getOldStatisticsFromDirectory(proceded_titles, total_stats, total_books_time)
|
||||
|
||||
-- find stats for all other books in history
|
||||
local proceded_titles, total_books_time = self:getStatisticsFromHistory(total_stats)
|
||||
total_books_time = total_books_time + self:getOldStatisticsFromDirectory(proceded_titles, total_stats)
|
||||
total_books_time = total_books_time + tonumber(self.data.total_time_in_sec)
|
||||
|
||||
table.insert(total_stats, 1, { text = _("Total hours read"), mandatory = util.secondsToClock(total_books_time, false) })
|
||||
table.insert(total_stats, 2, { text = "-" })
|
||||
table.insert(total_stats, 3, {
|
||||
text = self.data.title,
|
||||
mandatory = util.secondsToClock(self.data.total_time_in_sec, false),
|
||||
callback = function()
|
||||
self.total_status:swithItemTable(nil, self:getDatesForBook(self.data))
|
||||
UIManager:show(self.total_menu)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
return total_stats
|
||||
return T(_("Total hours read %1"),
|
||||
util.secondsToClock(total_books_time, false)),
|
||||
total_stats
|
||||
end
|
||||
|
||||
function ReaderStatistics:getStatisticsFromHistory(total_stats, total_books_time)
|
||||
function ReaderStatistics:getStatisticsFromHistory(total_stats)
|
||||
local titles = {}
|
||||
local total_books_time = 0
|
||||
for curr_file in lfs.dir(history_dir) do
|
||||
local path = history_dir .. curr_file
|
||||
local path = joinPath(history_dir, curr_file)
|
||||
if lfs.attributes(path, "mode") == "file" then
|
||||
local book_result = self:importFromFile(history_dir, curr_file)
|
||||
local book_stats = book_result.stats
|
||||
if book_stats and book_stats.title ~= self.data.title then
|
||||
titles[book_stats.title] = true
|
||||
table.insert(total_stats, {
|
||||
text = book_stats.title,
|
||||
mandatory = util.secondsToClock(book_stats.total_time_in_sec, false),
|
||||
book_stats.title,
|
||||
util.secondsToClock(book_stats.total_time_in_sec, false),
|
||||
callback = function()
|
||||
self.total_status:swithItemTable(nil, self:getDatesForBook(book_stats))
|
||||
UIManager:show(self.total_menu)
|
||||
return true
|
||||
UIManager:show(KeyValuePage:new{
|
||||
title = book_stats.title,
|
||||
kv_pairs = getDatesForBook(book_stats),
|
||||
})
|
||||
end,
|
||||
})
|
||||
total_books_time = total_books_time + tonumber(book_stats.total_time_in_sec)
|
||||
end
|
||||
end
|
||||
end
|
||||
return titles
|
||||
return titles, total_books_time
|
||||
end
|
||||
|
||||
-- For backward compatibility
|
||||
function ReaderStatistics:getOldStatisticsFromDirectory(exlude_titles, total_stats, total_books_time)
|
||||
function ReaderStatistics:getOldStatisticsFromDirectory(exlude_titles, total_stats)
|
||||
if lfs.attributes(statistics_dir, "mode") ~= "directory" then
|
||||
return
|
||||
return 0
|
||||
end
|
||||
local total_books_time = 0
|
||||
for curr_file in lfs.dir(statistics_dir) do
|
||||
local path = statistics_dir .. curr_file
|
||||
if lfs.attributes(path, "mode") == "file" then
|
||||
local book_result = self:importFromFile(statistics_dir, curr_file)
|
||||
if book_result and book_result.title ~= self.data.title and not exlude_titles[book_result.title] then
|
||||
table.insert(total_stats, {
|
||||
text = book_result.title,
|
||||
mandatory = util.secondsToClock(book_result.total_time, false),
|
||||
book_result.title,
|
||||
util.secondsToClock(book_result.total_time, false),
|
||||
callback = function()
|
||||
self.total_status:swithItemTable(nil, self:getDatesForBookOldFormat(book_result))
|
||||
UIManager:show(self.total_menu)
|
||||
return true
|
||||
UIManager:show(KeyValuePage:new{
|
||||
title = book_result.title,
|
||||
kv_pairs = getDatesForBookOldFormat(book_result),
|
||||
})
|
||||
end,
|
||||
})
|
||||
total_books_time = total_books_time + tonumber(book_result.total_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderStatistics:getBookProperties()
|
||||
local props = self.view.document:getProps()
|
||||
if props.title == "No document" or props.title == "" then --sometime crengine returns "No document" try to get one more time
|
||||
props = self.view.document:getProps()
|
||||
end
|
||||
return props
|
||||
return total_books_time
|
||||
end
|
||||
|
||||
function ReaderStatistics:onPageUpdate(pageno)
|
||||
@@ -411,14 +358,6 @@ function ReaderStatistics:onPageUpdate(pageno)
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderStatistics:savePropertiesInToData(item)
|
||||
self.data.title = item.title
|
||||
self.data.authors = item.authors
|
||||
self.data.language = item.language
|
||||
self.data.series = item.series
|
||||
end
|
||||
|
||||
|
||||
-- For backward compatibility
|
||||
function ReaderStatistics:inplaceMigration()
|
||||
local oldData = self:importFromFile(statistics_dir, self.data.title .. ".stat")
|
||||
@@ -431,12 +370,12 @@ end
|
||||
|
||||
-- For backward compatibility
|
||||
function ReaderStatistics:importFromFile(base_path, item)
|
||||
item = string.gsub(item, "^%s*(.-)%s*$", "%1") --trim
|
||||
if lfs.attributes(base_path .. item, "mode") == "directory" then
|
||||
item = string.gsub(item, "^%s*(.-)%s*$", "%1") -- trim
|
||||
local statistic_file = joinPath(base_path, item)
|
||||
if lfs.attributes(statistic_file, "mode") == "directory" then
|
||||
return
|
||||
end
|
||||
local statisticFile = base_path .. item
|
||||
local ok, stored = pcall(dofile, statisticFile)
|
||||
local ok, stored = pcall(dofile, statistic_file)
|
||||
if ok then
|
||||
return stored
|
||||
else
|
||||
|
||||
@@ -64,8 +64,7 @@ function TestGrid:paintTo(bb)
|
||||
end
|
||||
|
||||
function TestVisible:paintTo(bb)
|
||||
--Draw three lines at the borders to assess what the maximum visible coordinates are
|
||||
|
||||
-- Draw three lines at the borders to assess what the maximum visible coordinates are
|
||||
v_line = math.floor(bb:getWidth() / 50)
|
||||
h_line = math.floor(bb:getHeight() / 50)
|
||||
-- Paint white background for higher contrast
|
||||
@@ -165,8 +164,6 @@ function Background:onQuitApplication()
|
||||
UIManager:quit()
|
||||
end
|
||||
|
||||
|
||||
|
||||
-----------------------------------------------------
|
||||
-- example widget: a clock
|
||||
-----------------------------------------------------
|
||||
@@ -185,7 +182,6 @@ function Clock:schedFunc()
|
||||
self[1][1]:free()
|
||||
self[1][1] = self:getTextWidget()
|
||||
UIManager:setDirty(self)
|
||||
-- reschedule
|
||||
-- TODO: wait until next real second shift
|
||||
UIManager:scheduleIn(1, function() self:schedFunc() end)
|
||||
end
|
||||
@@ -250,7 +246,6 @@ M = Menu:new{
|
||||
height = 600,
|
||||
}
|
||||
|
||||
|
||||
-----------------------------------------------------
|
||||
-- a reader view widget
|
||||
-----------------------------------------------------
|
||||
@@ -277,72 +272,62 @@ touch_menu = TouchMenu:new{
|
||||
icon = "resources/icons/appbar.pokeball.png",
|
||||
{
|
||||
text = "item1",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item2",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item3",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item4",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item5",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item6",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item7",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item8",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item9",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
},
|
||||
{
|
||||
icon = "resources/icons/appbar.page.corner.bookmark.png",
|
||||
{
|
||||
text = "item10",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
{
|
||||
text = "item11",
|
||||
callback = function()
|
||||
end,
|
||||
callback = function() end,
|
||||
},
|
||||
},
|
||||
{
|
||||
icon = "resources/icons/appbar.home.png",
|
||||
callback = function()
|
||||
DEBUG("hello world!")
|
||||
end
|
||||
callback = function() DEBUG("hello world!") end
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
-----------------------------------------------------
|
||||
-- input box widget
|
||||
-----------------------------------------------------
|
||||
local TestInputText = InputText:new{
|
||||
width = 400,
|
||||
enter_callback = function() print("Entered") end,
|
||||
@@ -353,11 +338,43 @@ local TestInputText = InputText:new{
|
||||
},
|
||||
}
|
||||
|
||||
-----------------------------------------------------
|
||||
-- key value page
|
||||
-----------------------------------------------------
|
||||
local KeyValuePage = require("ui/widget/keyvaluepage")
|
||||
local kvp = KeyValuePage:new{
|
||||
title = 'Statistics',
|
||||
kv_pairs = {
|
||||
{"1 Current period", "00:00:00"},
|
||||
{"2 Time to read", "00:00:00"},
|
||||
{"3 Time to read", "00:00:00"},
|
||||
{"4 Time to read", "00:00:00"},
|
||||
{"5 Time to read", "00:00:00"},
|
||||
{"6 Time to read", "00:00:00"},
|
||||
{"7 Time to read", "00:00:00"},
|
||||
{"8 Time to read", "00:00:00"},
|
||||
{"9 Time to read", "00:00:00"},
|
||||
{"10 Time to read", "00:00:00"},
|
||||
{"11 Time to read", "00:00:00"},
|
||||
"----------------------------",
|
||||
{"12 Time to read", "00:00:00"},
|
||||
{"13 Time to read", "00:00:00"},
|
||||
{"14 Time to read", "00:00:00"},
|
||||
{"15 Time to read", "00:00:00"},
|
||||
{"16 Time to read", "00:00:00"},
|
||||
{"17 Time to read", "00:00:00"},
|
||||
{"18 Time to read", "00:00:00"},
|
||||
{"19 Time to read", "00:00:00"},
|
||||
{"20 Time to read", "00:00:00"},
|
||||
{"21 Time to read", "00:00:00"},
|
||||
},
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- you may want to uncomment following show calls to see the changes
|
||||
-----------------------------------------------------------------------
|
||||
--UIManager:show(Background:new())
|
||||
-- UIManager:show(TestGrid)
|
||||
--UIManager:show(TestGrid)
|
||||
UIManager:show(TestVisible)
|
||||
UIManager:show(Clock:new())
|
||||
--UIManager:show(M)
|
||||
@@ -365,7 +382,9 @@ UIManager:show(Clock:new())
|
||||
--UIManager:show(readerwindow)
|
||||
--UIManager:show(touch_menu)
|
||||
--UIManager:show(keyboard)
|
||||
UIManager:show(TestInputText)
|
||||
TestInputText:onShowKeyboard()
|
||||
--UIManager:show(TestInputText)
|
||||
--TestInputText:onShowKeyboard()
|
||||
|
||||
UIManager:show(kvp)
|
||||
|
||||
UIManager:run()
|
||||
|
||||
Reference in New Issue
Block a user