resolve conflict on frontend/ui/widget/scrolltextwidget.lua

pull/2028/head
union2find 8 years ago
commit 9ea56827dc

@ -5,7 +5,7 @@ source "${CI_DIR}/common.sh"
set +e
make coverage
travis_retry make coverage
pushd koreader-*/koreader
luajit $(which luacov-coveralls) -v
popd

@ -18,6 +18,10 @@ trim_trailing_whitespace = false
indent_style = tab
indent_size = 8
[Makefile.def]
indent_style = tab
indent_size = 4
[*.{js,css,scss,sass,html,handlebars,tpl}]
indent_style = space
indent_size = 2

7
.gitignore vendored

@ -8,13 +8,15 @@ lua-*
.vimrc
*.o
tags
test/*.sdr
test/*
*.tar
*.log
spec/unit/data
doc/html
git-rev
emu
luacov.stats.out
trace-out.txt
koreader-*.zip
koreader-*.apk
@ -37,5 +39,6 @@ koreader-kobo-arm-linux-gnueabihf*
koreader-emulator-i686-w64-mingw32
koreader-emulator-x86_64-linux-gnu
koreader-emulator-x86_64-pc-linux-gnu
koreader-emulator-x86_64-apple-darwin*
koreader-pocketbook-arm-obreey-linux-gnueabi
koreader-ubuntu-touch-arm-linux-gnueabihf

@ -63,7 +63,7 @@ ifneq ($(or $(EMULATE_READER),$(WIN32)),)
cd $(INSTALL_DIR)/koreader/spec/front/unit && test -e data || \
ln -sf ../../test ./data
else
cp -rfL $(KOR_BASE)/$(OUTPUT_DIR)/* $(INSTALL_DIR)/koreader/
$(RCP) -fL $(KOR_BASE)/$(OUTPUT_DIR)/* $(INSTALL_DIR)/koreader/
endif
for f in $(INSTALL_FILES); do \
ln -sf ../../$$f $(INSTALL_DIR)/koreader/; \
@ -77,9 +77,9 @@ ifdef WIN32
cd $(INSTALL_DIR)/koreader && cp ../../$(WIN32_DIR)/*.dll .
endif
@echo "[*] Install plugins"
cp -r plugins/* $(INSTALL_DIR)/koreader/plugins/
$(RCP) plugins/* $(INSTALL_DIR)/koreader/plugins/
@echo "[*] Installresources"
cp -rpL resources/fonts/* $(INSTALL_DIR)/koreader/fonts/
$(RCP) -pL resources/fonts/* $(INSTALL_DIR)/koreader/fonts/
install -d $(INSTALL_DIR)/koreader/{screenshots,data/{dict,tessdata},fonts/host,ota}
ifeq ($(or $(EMULATE_READER),$(WIN32)),)
@echo "[*] Clean up, remove unused files for releases"
@ -107,7 +107,10 @@ test:
$(MAKE) testfront
coverage: $(INSTALL_DIR)/koreader/.luacov
cd $(INSTALL_DIR)/koreader && ./luajit $(shell which busted) -o verbose_print --coverage --exclude-tags=nocov
cd $(INSTALL_DIR)/koreader && \
./luajit $(shell which busted) -o verbose_print \
--no-auto-insulate \
--coverage --exclude-tags=nocov
# coverage report summary
cd $(INSTALL_DIR)/koreader && tail -n \
+$$(($$(grep -nm1 Summary luacov.report.out|cut -d: -f1)-1)) \

@ -8,8 +8,9 @@ KOReader
KOReader is a document viewer application, originally created for Kindle
e-ink readers. It currently runs on Kindle, Kobo, PocketBook, Ubuntu Touch
and Android (2.3+) devices. Developers can also run Koreader emulator
for development purpose on desktop PC with Linux and Windows operating system.
and Android (2.3+) devices. Developers can also run KOReader emulator
for development purpose on desktop PC with Linux and Windows and
Mac OSX (experimental for now).
Main features for users
-----------------------
@ -91,6 +92,16 @@ sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
sudo apt-get install gcc-mingw-w64-i686 g++-mingw-w64-i686
```
Packages pkg-config-arm-linux-gnueabihf and pkg-config-arm-linux-gnueabi may
block you to build for Kobo or Kindle, remove them if you got ld error,
`/usr/lib/gcc-cross/arm-linux-gnueabihf/4.8/../../../../arm-linux-gnueabihf/bin/
ld: cannot find -lglib-2.0`
Mac OSX users may need to install these tools:
```
brew install nasm binutils libtool autoconf automake sdl2
```
A recent version of Android SDK/NDK and `ant` are needed in order to build
Koreader for Android devices.
```
@ -155,10 +166,10 @@ Then, run this command to build installable package for Android:
./kodev release android
```
For emulating KOReader on Linux and Windows
For emulating KOReader on Linux, Windows and Mac OSX
-------------
To build an emulator on current Linux machine just run:
To build an emulator on current Linux or OSX machine:
```
./kodev build
```

@ -1 +1 @@
Subproject commit b5c82144e253844849ab66e55bdff363d4991c66
Subproject commit edfd1239c685e930fd0918bab6eb80267555aead

@ -229,10 +229,10 @@ function FileManager:init()
self:handleEvent(Event:new("SetDimensions", self.dimen))
end
function FileManager:resetDimen(dimen)
self.dimen = dimen
function FileManager:reinit(path)
self.dimen = Screen:getSize()
-- backup the root path and path items
self.root_path = self.file_chooser.path
self.root_path = path or self.file_chooser.path
local path_items_backup = {}
for k, v in pairs(self.file_chooser.path_items) do
path_items_backup[k] = v

@ -1,17 +1,11 @@
local InputContainer = require("ui/widget/container/inputcontainer")
local CenterContainer = require("ui/widget/container/centercontainer")
local ButtonDialog = require("ui/widget/buttondialog")
local lfs = require("libs/libkoreader-lfs")
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:getHistoryDir()
local FileManagerHistory = InputContainer:extend{
hist_menu_title = _("History"),
}
@ -31,27 +25,8 @@ function FileManagerHistory:addToMainMenu(tab_item_table)
end
function FileManagerHistory:updateItemTable()
local ReaderUI = require("apps/reader/readerui")
self.hist = {}
for f in lfs.dir(history_dir) do
local path = joinPath(history_dir, f)
if lfs.attributes(path, "mode") == "file" then
local name = DocSettings:getNameFromHistory(f)
table.insert(self.hist, {
date = lfs.attributes(path, "modification"),
text = name,
histfile = f,
callback = function()
ReaderUI:showReader(
DocSettings:getPathFromHistory(f).. "/" .. name)
end
})
end
end
table.sort(self.hist, function(v1, v2) return v1.date > v2.date end)
self.hist_menu:swithItemTable(self.hist_menu_title, self.hist)
self.hist_menu:swithItemTable(self.hist_menu_title,
require("readhistory").hist)
end
function FileManagerHistory:onSetDimensions(dimen)
@ -65,7 +40,7 @@ function FileManagerHistory:onMenuHold(item)
{
text = _("Remove this item from history"),
callback = function()
os.remove(joinPath(history_dir, item.histfile))
require("readhistory"):removeItem(item)
self._manager:updateItemTable()
UIManager:close(self.histfile_dialog)
end,

@ -168,14 +168,20 @@ end
function ReaderCropping:setCropZoomMode(confirmed)
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: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))
self:setZoomMode(self.orig_zoom_mode)
end
end
function ReaderCropping:setZoomMode(mode)
self.ui:handleEvent(Event:new("SetZoomMode", mode))
end
function ReaderCropping:onReadSettings(config)
self.document.bbox = config:readSetting("bbox")
end

@ -18,9 +18,23 @@ local _ = require("gettext")
local util = require("util")
local MODE = {
off = 0,
page_progress = 1,
time = 2,
pages_left = 3,
battery = 4,
percentage = 5,
book_time_to_read = 6,
chapter_time_to_read = 7,
}
local MODE_INDEX = {}
for k,v in pairs(MODE) do
MODE_INDEX[v] = k
end
local ReaderFooter = InputContainer:new{
mode = 1,
visible = true,
mode = MODE.page_progress,
pageno = nil,
pages = nil,
toc_level = 0,
@ -67,7 +81,6 @@ function ReaderFooter:init()
face = Font:getFace(self.text_font_face, self.text_font_size),
}
self.text_width = self.progress_text:getSize().w + self.text_left_margin
self:applyFooterMode()
self.progress_bar = ProgressWidget:new{
width = nil, -- width will be updated in self:resetLayout()
height = self.bar_height,
@ -101,7 +114,8 @@ function ReaderFooter:init()
}
}
self.mode = G_reader_settings:readSetting("reader_footer_mode") or self.mode
self:applyFooterMode(
G_reader_settings:readSetting("reader_footer_mode") or self.mode)
self:resetLayout()
if self.settings.auto_refresh_time then
@ -295,7 +309,7 @@ function ReaderFooter:updateFooterText()
if #ticks_candidates > 0 then
self.progress_bar.ticks = ticks_candidates[1]
self.progress_bar.last = self.pages
self.progress_bar.last = self.pages or self.view.document:getPageCount()
else
-- we still set ticks here so self.progress_bar.ticks will not be
-- initialized again if ticks_candidates is empty
@ -328,21 +342,23 @@ function ReaderFooter:updateFooterText()
end
self.progress_text:setText(table.concat(info, " | "))
else
local info = ""
if self.mode == 1 then
local info
if self.mode == MODE.page_progress then
info = self:getProgressInfo()
elseif self.mode == 2 then
elseif self.mode == MODE.time then
info = self:getTimeInfo()
elseif self.mode == 3 then
elseif self.mode == MODE.pages_left then
info = self:getNextChapterInfo()
elseif self.mode == 4 then
elseif self.mode == MODE.battery then
info = self:getBatteryInfo()
elseif self.mode == 5 then
elseif self.mode == MODE.percentage then
info = self:getProgressPercentage()
elseif self.mode == 6 then
elseif self.mode == MODE.book_time_to_read then
info = self:getBookTimeToRead()
elseif self.mode == 7 then
elseif self.mode == MODE.chapter_time_to_read then
info = self:getChapterTimeToRead()
else
info = ""
end
self.progress_text:setText(info)
end
@ -384,16 +400,12 @@ function ReaderFooter:applyFooterMode(mode)
-- 6 for from statistics book time to read
-- 7 for from statistics chapter time to read
if mode ~= nil then self.mode = mode end
if self.mode == 0 then
self.view.footer_visible = false
else
self.view.footer_visible = true
end
self.view.footer_visible = (self.mode ~= MODE.off)
end
function ReaderFooter:onEnterFlippingMode()
self.orig_mode = self.mode
self:applyFooterMode(1)
self:applyFooterMode(MODE.page_progress)
end
function ReaderFooter:onExitFlippingMode()
@ -410,30 +422,24 @@ function ReaderFooter:onTapFooter(arg, ges)
self.ui:handleEvent(Event:new("GotoPercentage", percentage))
end
else
self.mode = (self.mode + 1) % 8
if self.settings.all_at_once and (self.mode > 1) then
self.mode = 0
end
if (self.mode == 1) and not self.settings.page_progress then
self.mode = 2
end
if (self.mode == 2) and not self.settings.time then
self.mode = 3
end
if (self.mode == 3) and not self.settings.pages_left then
self.mode = 4
end
if (self.mode == 4) and not self.settings.battery then
self.mode = 5
end
if (self.mode == 5) and not self.settings.percentage then
self.mode = 6
end
if (self.mode == 6) and not self.settings.book_time_to_read then
self.mode = 7
end
if (self.mode == 7) and not self.settings.chapter_time_to_read then
self.mode = 0
if self.settings.all_at_once then
if self.mode >= 1 then
self.mode = MODE.off
else
self.mode = MODE.page_progress
end
else
self.mode = (self.mode + 1) % 8
for i, m in ipairs(MODE_INDEX) do
if self.mode == MODE.off then break end
if self.mode == i then
if self.settings[m] then
break
else
self.mode = (self.mode + 1) % 8
end
end
end
end
self:applyFooterMode()
G_reader_settings:saveSetting("reader_footer_mode", self.mode)
@ -443,13 +449,13 @@ function ReaderFooter:onTapFooter(arg, ges)
end
function ReaderFooter:onHoldFooter(arg, ges)
if self.mode == 0 then return end
if self.mode == MODE.off then return end
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.view.footer_visible = (status_line == 1)
self.ui.document:setStatusLineProp(status_line)
self.ui:handleEvent(Event:new("UpdatePos"))
end

@ -66,7 +66,7 @@ end
function ReaderHighlight:addToMainMenu(tab_item_table)
-- insert table to main reader menu
table.insert(tab_item_table.typeset, {
text = _("Highlight"),
text = _("Highlight options"),
sub_item_table = self:genHighlightDrawerMenu(),
})
end

@ -38,14 +38,14 @@ function ReaderMenu:init()
self:onTapCloseMenu()
self.ui:onClose()
local FileManager = require("apps/filemanager/filemanager")
local lastdir = nil
local last_file = G_reader_settings:readSetting("lastfile")
if last_file then
lastdir = last_file:match("(.*)/")
end
if FileManager.instance then
FileManager.instance:resetDimen(Screen:getSize())
FileManager.instance:reinit(lastdir)
else
local lastdir = nil
local last_file = G_reader_settings:readSetting("lastfile")
if last_file then
lastdir = last_file:match("(.*)/")
end
FileManager:showFiles(lastdir)
end
end,

@ -357,11 +357,6 @@ end
function ReaderRolling:onGotoViewRel(diff)
DEBUG("goto relative screen:", diff, ", in mode: ", self.view.view_mode)
local prev_xp
-- save xpointer to check whether we reach the end of the book
if diff > 0 then
prev_xp = self.xpointer
end
if self.view.view_mode == "scroll" then
local pan_diff = diff * self.ui.dimen.h
if self.show_overlap_enable then
@ -371,15 +366,20 @@ function ReaderRolling:onGotoViewRel(diff)
pan_diff = pan_diff + self.overlap
end
end
local old_pos = self.current_pos
self:_gotoPos(self.current_pos + pan_diff)
if diff > 0 and old_pos == self.current_pos then
self.ui:handleEvent(Event:new("EndOfBook"))
end
elseif self.view.view_mode == "page" then
local page_count = self.ui.document:getVisiblePageCount()
local old_page = self.current_page
self:_gotoPage(self.current_page + diff*page_count)
if diff > 0 and old_page == self.current_page then
self.ui:handleEvent(Event:new("EndOfBook"))
end
end
self.xpointer = self.ui.document:getXPointer()
if self.xpointer == prev_xp then
self.ui:handleEvent(Event:new("EndOfBook"))
end
return true
end

@ -94,7 +94,6 @@ function ReaderView:addWidgets()
self.footer = ReaderFooter:new{
view = self,
ui = self.ui,
visible = self.footer_visible,
}
self.flipping = ReaderFlipping:new{
view = self,
@ -644,7 +643,9 @@ function ReaderView:onReadSettings(config)
self.state.gamma = config:readSetting("gamma") or DGLOBALGAMMA
local full_screen = config:readSetting("kopt_full_screen") or self.document.configurable.full_screen
local status_line = config:readSetting("copt_status_line") or self.document.configurable.status_line
self.footer_visible = (full_screen == 0 or status_line == 1) and true or false
if full_screen == 0 or status_line == 0 then
self.footer_visible = false
end
self:resetLayout()
local page_scroll = config:readSetting("kopt_page_scroll") or self.document.configurable.page_scroll
self.page_scroll = page_scroll == 1 and true or false

@ -12,7 +12,7 @@ local Device = require("device")
local Screen = require("device").screen
local Event = require("ui/event")
local Cache = require("cache")
local DEBUG = require("dbg")
local dbg = require("dbg")
local T = require("ffi/util").template
local _ = require("gettext")
@ -304,7 +304,7 @@ function ReaderUI:init()
})
-- koreader plugins
for _,plugin_module in ipairs(PluginLoader:loadPlugins()) do
DEBUG("Loaded plugin", plugin_module.name, "at", plugin_module.path)
dbg("Loaded plugin", plugin_module.name, "at", plugin_module.path)
self:registerModule(plugin_module.name, plugin_module:new{
dialog = self.dialog,
view = self.view,
@ -313,7 +313,7 @@ function ReaderUI:init()
})
end
--DEBUG(self.doc_settings)
--dbg(self.doc_settings)
-- we only read settings after all the widgets are initialized
self:handleEvent(Event:new("ReadSettings", self.doc_settings))
@ -328,19 +328,22 @@ function ReaderUI:init()
end
function ReaderUI:showReader(file)
DEBUG("show reader ui")
dbg("show reader ui")
require("readhistory"):addItem(file)
if lfs.attributes(file, "mode") ~= "file" then
UIManager:show(InfoMessage:new{
text = T( _("File '%1' does not exist."), file)
text = T(_("File '%1' does not exist."), file)
})
return
end
UIManager:show(InfoMessage:new{
text = T( _("Opening file '%1'."), file),
timeout = 0.1,
text = T(_("Opening file '%1'."), file),
timeout = 0.0,
})
-- doShowReader might block for a long time, so force repaint here
UIManager:forceRePaint()
UIManager:nextTick(function()
DEBUG("creating coroutine for showing reader")
dbg("creating coroutine for showing reader")
local co = coroutine.create(function()
self:doShowReader(file)
end)
@ -353,12 +356,12 @@ function ReaderUI:showReader(file)
end)
end
local running_instance = nil
local _running_instance = nil
function ReaderUI:doShowReader(file)
DEBUG("opening file", file)
dbg("opening file", file)
-- keep only one instance running
if running_instance then
running_instance:onClose()
if _running_instance then
_running_instance:onClose()
end
local document = DocumentRegistry:openDocument(file)
if not document then
@ -368,7 +371,7 @@ function ReaderUI:doShowReader(file)
return
end
if document.is_locked then
DEBUG("document is locked")
dbg("document is locked")
self._coroutine = coroutine.running() or self._coroutine
self:unlockDocumentWithPassword(document)
if coroutine.running() then
@ -385,15 +388,15 @@ function ReaderUI:doShowReader(file)
document = document,
}
UIManager:show(reader)
running_instance = reader
_running_instance = reader
end
function ReaderUI:_getRunningInstance()
return running_instance
return _running_instance
end
function ReaderUI:unlockDocumentWithPassword(document, try_again)
DEBUG("show input password dialog")
dbg("show input password dialog")
self.password_dialog = InputDialog:new{
title = try_again and _("Password is incorrect, try again?")
or _("Input document password"),
@ -481,17 +484,17 @@ function ReaderUI:notifyCloseDocument()
end
function ReaderUI:onClose()
DEBUG("closing reader")
dbg("closing reader")
self:saveSettings()
if self.document ~= nil then
DEBUG("closing document")
dbg("closing document")
self:notifyCloseDocument()
end
UIManager:close(self.dialog, "full")
-- serialize last used items for later launch
Cache:serialize()
if running_instance == self then
running_instance = nil
if _running_instance == self then
_running_instance = nil
end
return true
end

@ -153,6 +153,9 @@ function Device:suspend() end
-- Hardware specific method to resume the device
function Device:resume() end
-- Hardware specific method to power off the device
function Device:powerOff() end
function Device:usbPlugIn()
if self.charging_mode == false and self.screen_saver_mode == false then
self.screen:saveCurrentBB()

@ -258,20 +258,26 @@ function Input:handleKeyBoardEv(ev)
end
end
if ev.value == EVENT_VALUE_KEY_RELEASE then
if keycode == "Light" then
return keycode
elseif keycode == "Power" then
-- Kobo generates Power keycode only, we need to decide whether it's
-- power-on or power-off ourselves.
if self.device.screen_saver_mode then
if keycode == "Power" then
-- Kobo generates Power keycode only, we need to decide whether it's
-- power-on or power-off ourselves.
if self.device.screen_saver_mode then
if ev.value == EVENT_VALUE_KEY_RELEASE then
return "Resume"
else
return "Suspend"
end
else
if ev.value == EVENT_VALUE_KEY_PRESS then
return "PowerPress"
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
return "PowerRelease"
end
end
end
if ev.value == EVENT_VALUE_KEY_RELEASE and keycode == "Light" then
return keycode
end
-- handle modifier keys
if self.modifiers[keycode] ~= nil then
if ev.value == EVENT_VALUE_KEY_PRESS then

@ -86,13 +86,16 @@ function Kobo:init()
self.input = require("device/input"):new{
device = self,
event_map = {
[59] = "Power_SleepCover",
[90] = "Light",
[102] = "Home",
[116] = "Power",
}
}
if not G_reader_settings:readSetting("ignore_power_sleepcover") then
self.input.event_map[59] = "Power_SleepCover"
end
Generic.init(self)
self.input.open("/dev/input/event0") -- Light button and sleep slider
@ -186,6 +189,10 @@ function Kobo:resume()
end
end
function Kobo:powerOff()
os.execute("poweroff")
end
-------------- device probe ------------
local codename = Kobo:getCodeName()

@ -36,38 +36,37 @@ function DocSettings:purgeDocSettings(doc_path)
end
function DocSettings:open(docfile)
local history_path
local sidecar_path
-- TODO(zijiehe): Remove history_path, use only sidecar.
local new = { data = {} }
local ok, stored
if docfile == ".reader" then
-- we handle reader setting as special case
history_path = DataStorage:getDataDir() .. "/settings.reader.lua"
new.history_file = DataStorage:getDataDir() .. "/settings.reader.lua"
ok, stored = pcall(dofile, new.history_file)
else
history_path = self:getHistoryPath(docfile)
new.history_file = self:getHistoryPath(docfile)
local sidecar = self:getSidecarDir(docfile)
if lfs.attributes(sidecar, "mode") ~= "directory" then
lfs.mkdir(sidecar)
end
sidecar_path = sidecar.."/"..docfile:match(".*%/(.*)")..".lua"
end
-- construct settings obj
local new = {
history_file = history_path,
sidecar_file = sidecar_path,
data = {}
}
local ok, stored = pcall(dofile, new.sidecar_file or "")
if not ok then
ok, stored = pcall(dofile, new.history_file or "")
new.sidecar_file = sidecar.."/"..docfile:match(".*%/(.*)")..".lua"
ok, stored = pcall(dofile, new.sidecar_file or "")
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")
ok, stored = pcall(dofile, new.history_file or "")
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
end
end
if ok and stored then
new.data = stored
end
return setmetatable(new, { __index = DocSettings})
end

@ -0,0 +1,121 @@
local lfs = require("libs/libkoreader-lfs")
local DataStorage = require("datastorage")
local DocSettings = require("docsettings")
local joinPath = require("ffi/util").joinPath
local dump = require("dump")
local history_file = joinPath(DataStorage:getDataDir(), "history.lua")
local ReadHistory = {
hist = {},
}
local function buildEntry(input_time, input_file)
return {
time = input_time,
text = input_file:gsub(".*/", ""),
file = input_file,
callback = function()
local ReaderUI = require("apps/reader/readerui")
ReaderUI:showReader(input_file)
end
}
end
function ReadHistory:_sort()
for i = #self.hist, 1, -1 do
if lfs.attributes(self.hist[i].file, "mode") ~= "file" then
table.remove(self.hist, i)
end
end
table.sort(self.hist, function(l, r) return l.file < r.file end)
-- TODO(zijiehe): Use binary insert instead of a loop to deduplicate.
for i = #self.hist, 2, -1 do
if self.hist[i].file == self.hist[i - 1].file then
if self.hist[i].time < self.hist[i - 1].time then
table.remove(self.hist, i)
else
table.remove(self.hist,i - 1)
end
end
end
table.sort(self.hist, function(v1, v2) return v1.time > v2.time end)
-- TODO(zijiehe): Use binary search to find an item when deleting it.
for i = 1, #self.hist, 1 do
self.hist[i].index = i
end
end
-- Reduces total count in hist list to a reasonable number by removing last
-- several items.
function ReadHistory:_reduce()
while #self.hist > 500 do
table.remove(self.hist, #self.hist)
end
end
-- Flushes current history table into file.
function ReadHistory:_flush()
local content = {}
for k, v in pairs(self.hist) do
content[k] = {
time = v.time,
file = v.file
}
end
local f = io.open(history_file, "w")
f:write("return " .. dump(content) .. "\n")
f:close()
end
-- Reads history table from file
function ReadHistory:_read()
local ok, data = pcall(dofile, history_file)
if ok then
for k, v in pairs(data) do
table.insert(self.hist, buildEntry(v.time, v.file))
end
end
end
-- Reads history from legacy history folder
function ReadHistory:_readLegacyHistory()
local history_dir = DataStorage:getHistoryDir()
for f in lfs.dir(history_dir) do
local path = joinPath(history_dir, f)
if lfs.attributes(path, "mode") == "file" then
local file = joinPath(DocSettings:getPathFromHistory(f),
DocSettings:getNameFromHistory(f))
table.insert(self.hist,
buildEntry(lfs.attributes(path, "modification"), file))
end
end
end
function ReadHistory:_init()
self:_read()
self:_readLegacyHistory()
self:_sort()
self:_reduce()
end
function ReadHistory:removeItem(item)
table.remove(self.hist, item.index)
os.remove(DocSettings:getHistoryPath(item.file))
self:_flush()
end
function ReadHistory:addItem(file)
if file ~= nil and lfs.attributes(file, "mode") == "file" then
table.insert(self.hist, 1, buildEntry(os.time(), file))
-- TODO(zijiehe): We do not need to sort if we can use binary insert and
-- binary search.
self:_sort()
self:_reduce()
self:_flush()
end
end
ReadHistory:_init()
return ReadHistory

@ -34,6 +34,16 @@ local CreOptions = {
{
icon = "resources/icons/appbar.column.two.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 = "line_spacing",
name_text = S.LINE_SPACING,
@ -91,7 +101,6 @@ local CreOptions = {
event = "ChangeSize",
args = {"decrease", "increase"},
alternate = false,
height = 60,
}
}
},
@ -129,16 +138,6 @@ local CreOptions = {
{
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,

@ -144,7 +144,6 @@ local KoptOptions = {
event = "FineTuningFontSize",
args = {-0.05, 0.05},
alternate = false,
height = 60,
enabled_func = function(configurable)
return enable_if_equals(configurable, "text_wrap", 1)
end,

@ -27,6 +27,7 @@ local UIManager = {
_zeromqs = {},
_refresh_stack = {},
_refresh_func_stack = {},
_power_ev_handled = false,
}
function UIManager:init()
@ -46,15 +47,45 @@ function UIManager:init()
-- suspend. So let's unschedule it when suspending, and restart it after
-- resume.
self:_initAutoSuspend()
self.event_handlers["Suspend"] = function(input_event)
self.event_handlers["Suspend"] = function()
self:_stopAutoSuspend()
Device:onPowerEvent(input_event)
Device:onPowerEvent("Suspend")
end
self.event_handlers["Resume"] = function(input_event)
Device:onPowerEvent(input_event)
self.event_handlers["Resume"] = function()
Device:onPowerEvent("Resume")
self:sendEvent(Event:new("Resume"))
self:_startAutoSuspend()
end
self.event_handlers["PowerPress"] = function()
self._power_ev_handled = false
local showPowerOffDialog = function()
if self._power_ev_handled then return end
self._power_ev_handled = true
local ConfirmBox = require("ui/widget/confirmbox")
UIManager:show(ConfirmBox:new{
text = _("Power off?"),
ok_callback = function()
local InfoMessage = require("ui/widget/infomessage")
UIManager:show(InfoMessage:new{
text = _("Powered off."),
})
-- The message can fail to render if this is executed directly
UIManager:scheduleIn(0.1, function()
self:broadcastEvent(Event:new("Close"))
Device:powerOff()
end)
end,
})
end
UIManager:scheduleIn(3, showPowerOffDialog)
end
self.event_handlers["PowerRelease"] = function()
if not self._power_ev_handled then
self._power_ev_handled = true
self.event_handlers["Suspend"]()
end
end
self.event_handlers["Light"] = function()
Device:getPowerDevice():toggleFrontlight()
end
@ -198,8 +229,8 @@ function UIManager:schedule(time, action)
break
end
else
-- for fairness, it's better to make p+1 is strictly less than p
-- might want to revisit here in the future
-- for fairness, it's better to make p+1 is strictly less than
-- p might want to revisit here in the future
break
end
until e < s
@ -347,7 +378,7 @@ function UIManager:quit()
end
end
-- transmit an event to registered widgets
-- transmit an event to an active widget
function UIManager:sendEvent(event)
if #self._window_stack == 0 then return end
-- top level widget has first access to the event
@ -368,6 +399,20 @@ function UIManager:sendEvent(event)
end
end
-- transmit an event to all registered widgets
function UIManager:broadcastEvent(event)
-- the widget's event handler might close widgets in which case
-- a simple iterator like ipairs would skip over some entries
local i = 1
while (i <= #self._window_stack) do
local prev_widget = self._window_stack[i].widget
self._window_stack[i].widget:handleEvent(event)
if (self._window_stack[i].widget == prev_widget) then
i = i + 1
end
end
end
function UIManager:_checkTasks()
local now = { util.gettime() }
local now_us = now[1] * MILLION + now[2]
@ -521,6 +566,10 @@ function UIManager:_repaint()
self.refresh_counted = false
end
function UIManager:forceRePaint()
self:_repaint()
end
function UIManager:setInputTimeout(timeout)
self.INPUT_TIMEOUT = timeout or 200*1000
end

@ -57,7 +57,7 @@ function BookStatusWidget:init()
self.stats = {
total_time_in_sec = 0,
performance_in_pages = {},
pages = self.document:getPageCount(),
total_pages = self.document:getPageCount(),
}
self:getStatisticsSettings()
if self.settings then
@ -123,8 +123,8 @@ function BookStatusWidget:getStatHours(stats)
end
function BookStatusWidget:getReadPages(stats)
if stats and stats.performance_in_pages and stats.pages then
return util.tableSize(stats.performance_in_pages) .. "/" .. stats.pages
if stats and stats.performance_in_pages and stats.total_pages then
return util.tableSize(stats.performance_in_pages) .. "/" .. stats.total_pages
end
return "none"
end
@ -253,12 +253,14 @@ function BookStatusWidget:genBookInfoGroup()
}
)
-- progress bar
local total_pages = self.document:getPageCount()
local total_pages = self.stats.total_pages
local read_percentage = self.view.state.page / total_pages
local progress_bar = ProgressWidget:new{
width = width * 0.7,
height = Screen:scaleBySize(10),
percentage = read_percentage,
ticks = nil,
last = nil,
}
table.insert(book_meta_info_group,
CenterContainer:new{

@ -353,9 +353,14 @@ function ConfigOption:init()
if self.options[c].toggle then
local max_toggle_width = Screen:getWidth() / 2
local toggle_width = Screen:scaleBySize(self.options[c].width or 216)
local toggle_width = Screen:scaleBySize(self.options[c].width
or 216)
local row_count = self.options[c].row_count or 1
local toggle_height = Screen:scaleBySize(self.options[c].height
or 30 * row_count)
local switch = ToggleSwitch:new{
width = math.min(max_toggle_width, toggle_width),
height = toggle_height,
font_face = item_font_face,
font_size = item_font_size,
name = self.options[c].name,
@ -368,6 +373,7 @@ function ConfigOption:init()
events = self.options[c].events,
config = self.config,
enabled = enabled,
row_count = row_count,
}
local position = current_item
switch:setPosition(position)

@ -87,7 +87,7 @@ function ImageWidget:_render()
error("cannot render image")
end
local native_w, native_h = self._bb:getWidth(), self._bb:getHeight()
local w, h
local w, h = self.width, self.height
if self.autoscale then
local dpi_scale = Screen:getDPI() / 167
-- rounding off to power of 2 to avoid alias with pow(2, floor(log(x)/log(2))

@ -14,7 +14,7 @@ Configurable attributes:
* rectcolor -- infill color
* ticks (list) -- default to nil, use this if you want to insert markers
* tick_width
* last -- maximum tick
* last -- maximum tick, used with ticks
Example:
@ -68,7 +68,7 @@ function ProgressWidget:paintTo(bb, x, y)
bb:paintRect(x+self.margin_h, math.ceil(y+self.margin_v+self.bordersize),
math.ceil((my_size.w-2*self.margin_h)*self.percentage),
my_size.h-2*(self.margin_v+self.bordersize), self.rectcolor)
if self.ticks then
if self.ticks and self.last then
for i=1, #self.ticks do
bb:paintRect(
x + (my_size.w-2*self.margin_h)*(self.ticks[i]/self.last),

@ -4,10 +4,11 @@ local VerticalScrollBar = require("ui/widget/verticalscrollbar")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local UIManager = require("ui/uimanager")
local Screen = require("device").screen
local Device = require("device")
local Screen = Device.screen
local Input = Device.input
local HorizontalGroup = require("ui/widget/horizontalgroup")
local HorizontalSpan = require("ui/widget/horizontalspan")
local Device = require("device")
local Blitbuffer = require("ffi/blitbuffer")
--[[
@ -64,13 +65,20 @@ function ScrollTextWidget:init()
},
}
end
if Device:hasKeyboard() or Device:hasKeys() then
self.key_events = {
ScrollDown = {{Input.group.PgFwd}, doc = "scroll down"},
ScrollUp = {{Input.group.PgBack}, doc = "scroll up"},
}
end
end
function ScrollTextWidget:onScrollText(arg, ges)
if ges.direction == "north" then
function ScrollTextWidget:scrollText(direction)
if direction == 0 then return end
if direction > 0 then
low, high = self.text_widget:scrollDown()
self.v_scroll_bar:set(low, high)
elseif ges.direction == "south" then
else
low, high = self.text_widget:scrollUp()
self.v_scroll_bar:set(low, high)
end
@ -79,4 +87,23 @@ function ScrollTextWidget:onScrollText(arg, ges)
end)
end
function ScrollTextWidget:onScrollText(arg, ges)
if ges.direction == "north" then
self:scrollText(1)
elseif ges.direction == "south" then
self:scrollText(-1)
end
return true
end
function ScrollTextWidget:onScrollDown()
self:scrollText(1)
return true
end
function ScrollTextWidget:onScrollUp()
self:scrollText(-1)
return true
end
return ScrollTextWidget

@ -3,6 +3,7 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local FrameContainer = require("ui/widget/container/framecontainer")
local CenterContainer = require("ui/widget/container/centercontainer")
local HorizontalGroup = require("ui/widget/horizontalgroup")
local VerticalGroup = require("ui/widget/verticalgroup")
local Font = require("ui/font")
local Geom = require("ui/geometry")
local RenderText = require("ui/rendertext")
@ -31,10 +32,12 @@ local ToggleSwitch = InputContainer:new{
font_face = "cfont",
font_size = 16,
enabled = true,
row_count = 1,
}
function ToggleSwitch:init()
self.n_pos = #self.toggle
-- Item count per row
self.n_pos = math.ceil(#self.toggle / self.row_count)
self.position = nil
self.toggle_frame = FrameContainer:new{
@ -45,16 +48,24 @@ function ToggleSwitch:init()
padding = 2,
dim = not self.enabled,
}
self.toggle_content = HorizontalGroup:new{}
for i=1,#self.toggle do
self.toggle_content = VerticalGroup:new{}
for i = 1, self.row_count do
table.insert(self.toggle_content, HorizontalGroup:new{})
end
local center_dimen = Geom:new{
w = self.width / self.n_pos,
h = self.height / self.row_count,
}
for i = 1, #self.toggle do
local label = ToggleLabel:new{
align = "center",
text = self.toggle[i],
face = Font:getFace(self.font_face, self.font_size),
}
local content = CenterContainer:new{
dimen = Geom:new{w = self.width/self.n_pos, h = self.height},
dimen = center_dimen,
label,
}
local button = FrameContainer:new{
@ -66,7 +77,7 @@ function ToggleSwitch:init()
padding = 0,
content,
}
table.insert(self.toggle_content, button)
table.insert(self.toggle_content[math.ceil(i / self.n_pos)], button)
end
self.toggle_frame[1] = self.toggle_content
@ -94,15 +105,19 @@ 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].fgcolor = Blitbuffer.COLOR_WHITE
else
self.toggle_content[i].color = self.bgcolor
self.toggle_content[i].background = self.bgcolor
self.toggle_content[i][1][1].fgcolor = Blitbuffer.COLOR_BLACK
for i = 1, #self.toggle_content do
local row = self.toggle_content[i]
for j = 1, #row do
local cell = row[j]
if pos == (i - 1) * self.n_pos + j then
cell.color = self.fgcolor
cell.background = self.fgcolor
cell[1][1].fgcolor = Blitbuffer.COLOR_WHITE
else
cell.color = self.bgcolor
cell.background = self.bgcolor
cell[1][1].fgcolor = Blitbuffer.COLOR_BLACK
end
end
end
end
@ -124,11 +139,15 @@ function ToggleSwitch:togglePosition(position)
self:update()
end
function ToggleSwitch:calculatePosition(gev)
local x = (gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos
local y = (gev.pos.y - self.dimen.y) / self.dimen.h * self.row_count
return math.ceil(x) + math.floor(y) * self.n_pos
end
function ToggleSwitch:onTapSelect(arg, gev)
if not self.enabled then return true end
local position = math.ceil(
(gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos
)
local position = self:calculatePosition(gev)
self:togglePosition(position)
--[[
if self.values then
@ -152,9 +171,7 @@ function ToggleSwitch:onTapSelect(arg, gev)
end
function ToggleSwitch:onHoldSelect(arg, gev)
local position = math.ceil(
(gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos
)
local position = self:calculatePosition(gev)
self.config:onMakeDefault(self.name, self.name_text,
self.values or self.args, self.toggle, position)
return true

67
kodev

@ -3,16 +3,17 @@
CURDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
function assert_ret_zero {
if [ $1 -ne 0 ]; then
if [ ! -z $2 ]; then
echo $2
if [ "$1" -ne 0 ]; then
if [ ! -z "$2" ]; then
echo "$2"
fi
exit 1
fi
}
function setup_env {
files=`ls -d ./koreader-emulator-*/koreader`
files=$(ls -d ./koreader-emulator-*/koreader)
assert_ret_zero $? "Emulator not found, please build it first."
export EMU_DIR=${files[0]}
}
@ -43,8 +44,8 @@ TARGET:
${SUPPORTED_TARGETS}"
while [[ $1 == '-'* ]]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | awk -F= '{print $2}'`
PARAM=$(echo "$1" | awk -F= '{print $1}')
VALUE=$(echo "$1" | awk -F= '{print $2}')
case $PARAM in
-v | --verbose)
export VERBOSE=1
@ -76,7 +77,7 @@ ${SUPPORTED_TARGETS}"
assert_ret_zero $?
;;
android)
if [ ! -d ${CURDIR}/base/toolchain/android-toolchain ]; then
if [ ! -d "${CURDIR}/base/toolchain/android-toolchain" ]; then
make android-toolchain
assert_ret_zero $?
fi
@ -84,7 +85,7 @@ ${SUPPORTED_TARGETS}"
assert_ret_zero $?
;;
pocketbook)
if [ ! -d ${CURDIR}/base/toolchain/pocketbook-toolchain ]; then
if [ ! -d "${CURDIR}/base/toolchain/pocketbook-toolchain" ]; then
make pocketbook-toolchain
assert_ret_zero $?
fi
@ -130,7 +131,7 @@ ${SUPPORTED_TARGETS}"
;;
android)
make TARGET=android clean
rm -f *.apk
rm -f ./*.apk
;;
pocketbook)
make TARGET=pocketbook clean
@ -202,7 +203,7 @@ ${SUPPORTED_RELEASE_TARGETS}"
function kodev-wbuilder {
kodev-build
echo "[*] Running wbuilder.lua..."
pushd ${EMU_DIR}
pushd "${EMU_DIR}"
EMULATE_READER_W=540 EMULATE_READER_H=720 ./luajit ./utils/wbuilder.lua
popd
}
@ -221,8 +222,8 @@ OPTIONS:
screen_width=540
screen_height=720
while [[ $1 == '-'* ]]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | awk -F= '{print $2}'`
PARAM=$(echo "$1" | awk -F= '{print $1}')
VALUE=$(echo "$1" | awk -F= '{print $2}')
case $PARAM in
--disable-touch)
export DISABLE_TOUCH=1
@ -256,18 +257,18 @@ OPTIONS:
setup_env
fi
if [ ! -d ${EMU_DIR} ]; then
if [ ! -d "${EMU_DIR}" ]; then
echo "Failed to find emulator directory! Please try build command first."
exit 1
fi
echo "[*] Running KOReader with arguments: $@..."
pushd ${EMU_DIR}
echo "[*] Running KOReader with arguments: $*..."
pushd "${EMU_DIR}"
if [ $# -lt 1 ]; then
args=${CURDIR}/test
else
args="$@"
args="$*"
[[ $args != /* ]] && args="${CURDIR}/${args}"
fi
@ -287,8 +288,8 @@ OPTIONS:
--tags=TAGS only run tests with given tags
"
while [[ $1 == '-'* ]]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | awk -F= '{print $2}'`
PARAM=$(echo "$1" | awk -F= '{print $1}')
VALUE=$(echo "$1" | awk -F= '{print $2}')
case $PARAM in
--tags)
opts="--tags=${VALUE}"
@ -312,19 +313,19 @@ OPTIONS:
fi
setup_env
make ${EMU_DIR}/.busted
pushd ${EMU_DIR}
make "${EMU_DIR}/.busted"
pushd "${EMU_DIR}"
test_path=./spec/$1/unit
test_path="./spec/$1/unit"
if [ ! -z $2 ]; then
if [ ! -z "$2" ]; then
test_path="${test_path}/$2"
fi
busted --lua=./luajit ${opts} \
busted --lua="./luajit ${opts}" \
--no-auto-insulate \
--lazy \
-o ./spec/$1/unit/verbose_print \
--exclude-tags=notest ${test_path}
-o "./spec/$1/unit/verbose_print" \
--exclude-tags=notest "${test_path}"
popd
}
@ -387,38 +388,38 @@ case $1 in
activate)
echo "adding ${CURDIR} to \$PATH..."
export PATH="${PATH}:${CURDIR}"
eval $(luarocks path bin)
exec ${SHELL}
eval "$(luarocks path bin)"
exec "${SHELL}"
;;
fetch-thirdparty)
kodev-fetch-thirdparty
;;
clean)
shift 1
kodev-clean $@
kodev-clean "$@"
;;
build)
shift 1
kodev-build $@
kodev-build "$@"
;;
release)
shift 1
kodev-release $@
kodev-release "$@"
;;
wbuilder)
kodev-wbuilder
;;
run)
shift 1
kodev-run $@
kodev-run "$@"
;;
test)
shift 1
kodev-test $@
kodev-test "$@"
;;
log)
shift 1
kodev-log $@
kodev-log "$@"
;;
--help | -h)
echo "${HELP_MSG}"

@ -35,22 +35,47 @@ if pkill -0 nickel ; then
FROM_NICKEL="true"
fi
if [ "${FROM_NICKEL}" == "true" ] ; then
# Siphon a few things from nickel's env...
eval "$(xargs -n 1 -0 < /proc/$(pidof nickel)/environ | grep -e DBUS_SESSION_BUS_ADDRESS -e WIFI_MODULE -e PLATFORM -e WIFI_MODULE_PATH -e INTERFACE -e PRODUCT 2>/dev/null)"
export DBUS_SESSION_BUS_ADDRESS WIFI_MODULE PLATFORM WIFI_MODULE_PATH INTERFACE PRODUCT
if [ "${FROM_NICKEL}" = "true" ] ; then
# Detect if we were started from KFMon
FROM_KFMON="false"
if pkill -0 kfmon ; then
# That's a start, now check if KFMon truly is our parent...
if [ "$(pidof kfmon)" -eq "${PPID}" ] ; then
FROM_KFMON="true"
fi
fi
if [ "${FROM_KFMON}" = "true" ] ; then
# Siphon nickel's full environment, since KFMon inherits such a minimal one, and that apparently confuses the hell out of Nickel for some reason if we decide to restart it without a reboot...
for env in $(xargs -n 1 -0 < /proc/$(pidof nickel)/environ) ; do
export ${env}
done
else
# Siphon a few things from nickel's env...
eval "$(xargs -n 1 -0 < /proc/$(pidof nickel)/environ | grep -e DBUS_SESSION_BUS_ADDRESS -e WIFI_MODULE -e PLATFORM -e WIFI_MODULE_PATH -e INTERFACE -e PRODUCT 2>/dev/null)"
export DBUS_SESSION_BUS_ADDRESS WIFI_MODULE PLATFORM WIFI_MODULE_PATH INTERFACE PRODUCT
fi
# flush disks, might help avoid trashing nickel's DB...
sync
# Double the fun!
sleep 1
sync
# stop kobo software because it's running
killall nickel hindenburg fmon 2>/dev/null
killall nickel hindenburg sickel fickel fmon 2>/dev/null
# NOTE: Not particularly critical, we should be safe leaving it up, but since we reboot on exit anyway...
# Keep KFMon up for now to make sure it's not doing anything overly stupid we might have overlooked ;).
#if [ "${FROM_KFMON}" == "true" ] ; then
# killall kfmon 2>/dev/null
#fi
fi
# fallback for old fmon (and advboot) users (-> if no args were passed to the sript, start the FM)
if [ "$#" -eq 0 ] ; then
args="/mnt/onboard"
else
args="$@"
args="$*"
fi
# check whether PLATFORM & PRODUCT have a value assigned by rcS
@ -68,7 +93,7 @@ if [ ! -n "${PLATFORM}" ] ; then
PLATFORM="${CPU}-ntx"
fi
if [ "${PLATFORM}" == "freescale" ] ; then
if [ "${PLATFORM}" = "freescale" ] ; then
if [ ! -s "/lib/firmware/imx/epdc_E60_V220.fw" ] ; then
mkdir -p "/lib/firmware/imx"
dd if="/dev/mmcblk0" bs=512K skip=10 count=1 | zcat > "/lib/firmware/imx/epdc_E60_V220.fw"
@ -88,9 +113,15 @@ fi
./reader.lua "${args}" > crash.log 2>&1
if [ "${FROM_NICKEL}" == "true" ] ; then
# start kobo software because it was running before koreader
./nickel.sh &
if [ "${FROM_NICKEL}" = "true" ] ; then
if [ "${FROM_KFMON}" != "true" ] ; then
# start kobo software because it was running before koreader
./nickel.sh &
else
# If we were called from KFMon, just reboot, because there's always a (hopefully slim to nonexistent, now) chance Nickel will get its panties in a serious twist on restore for one reason or another...
# And at best, we'd still restart with broken suspend behavior anyway...
reboot
fi
else
# if we were called from advboot then we must reboot to go to the menu
# NOTE: This is actually achieved by checking if KSM or a KSM-related script is running:

@ -94,5 +94,107 @@ describe("device module", function()
os.getenv:revert()
mock_input.open:revert()
end)
it("should flush book settings before suspend", function()
local sample_pdf = "spec/front/unit/data/tall.pdf"
local ReaderUI = require("apps/reader/readerui")
local Device = require("device")
local NickelConf = require("device/kobo/nickel_conf")
stub(NickelConf.frontLightLevel, "get")
stub(NickelConf.frontLightState, "get")
NickelConf.frontLightLevel.get.returns(1)
NickelConf.frontLightState.get.returns(0)
local UIManager = require("ui/uimanager")
stub(Device, "suspend")
stub(Device.powerd, "beforeSuspend")
stub(Device, "isKobo")
Device.isKobo.returns(true)
local saved_noop = UIManager._resetAutoSuspendTimer
UIManager:init()
ReaderUI:doShowReader(sample_pdf)
local readerui = ReaderUI._getRunningInstance()
stub(readerui, "onFlushSettings")
UIManager.event_handlers["PowerPress"]()
UIManager.event_handlers["PowerRelease"]()
assert.stub(readerui.onFlushSettings).was_called()
Device.suspend:revert()
Device.powerd.beforeSuspend:revert()
Device.isKobo:revert()
NickelConf.frontLightLevel.get:revert()
NickelConf.frontLightState.get:revert()
UIManager._startAutoSuspend = nil
UIManager._stopAutoSuspend = nil
UIManager._resetAutoSuspendTimer = saved_noop
readerui:onClose()
end)
end)
describe("kindle", function()
it("should initialize voyager without error", function()
package.loaded['ffi/framebuffer_mxcfb'] = mock_fb
stub(io, "open")
io.open.returns({
read = function()
return "XX13XX"
end,
close = function() end
})
mock_input = require('device/input')
stub(mock_input, "open")
local kindle_dev = require("device/kindle/device")
assert.is.same(kindle_dev.model, "KindleVoyage")
kindle_dev:init()
assert.is.same(kindle_dev.input.event_map[104], "LPgBack")
assert.is.same(kindle_dev.input.event_map[109], "LPgFwd")
assert.is.same(kindle_dev.powerd.fl_min, 0)
assert.is.same(kindle_dev.powerd.fl_max, 24)
io.open:revert()
package.loaded['ffi/framebuffer_mxcfb'] = nil
mock_input.open:revert()
end)
it("should toggle frontlight", function()
package.loaded['ffi/framebuffer_mxcfb'] = mock_fb
stub(io, "open")
io.open.returns({
read = function()
return "12"
end,
close = function() end
})
mock_input = require('device/input')
stub(mock_input, "open")
stub(os, "execute")
local kindle_dev = require("device/kindle/device")
kindle_dev:init()
assert.is.same(kindle_dev.powerd.fl_intensity, 12)
kindle_dev.powerd:setIntensity(5)
assert.stub(os.execute).was_called_with(
"echo -n 5 > /sys/class/backlight/max77696-bl/brightness")
assert.is.same(kindle_dev.powerd.fl_intensity, 5)
kindle_dev.powerd:toggleFrontlight()
assert.stub(os.execute).was_called_with(
"echo -n 0 > /sys/class/backlight/max77696-bl/brightness")
assert.is.same(kindle_dev.powerd.fl_intensity, 5)
kindle_dev.powerd:toggleFrontlight()
assert.stub(os.execute).was_called_with(
"echo -n 5 > /sys/class/backlight/max77696-bl/brightness")
io.open:revert()
package.loaded['ffi/framebuffer_mxcfb'] = nil
mock_input.open:revert()
os.execute:revert()
end)
end)
end)

@ -29,6 +29,67 @@ describe("Readerfooter module", function()
})
end)
it("should setup footer as visible", function()
G_reader_settings:saveSetting("reader_footer_mode", 1)
local sample_pdf = "spec/front/unit/data/2col.pdf"
purgeDir(DocSettings:getSidecarDir(sample_pdf))
os.remove(DocSettings:getHistoryPath(sample_pdf))
local readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_pdf),
}
assert.is.same(true, readerui.view.footer_visible)
G_reader_settings:delSetting("reader_footer_mode")
end)
it("should setup footer as invisible in full screen mode", function()
G_reader_settings:saveSetting("reader_footer_mode", 1)
local sample_pdf = "spec/front/unit/data/2col.pdf"
purgeDir(DocSettings:getSidecarDir(sample_pdf))
os.remove(DocSettings:getHistoryPath(sample_pdf))
local cfg = DocSettings:open(sample_pdf)
cfg:saveSetting("kopt_full_screen", 0)
cfg:flush()
local readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_pdf),
}
assert.is.same(false, readerui.view.footer_visible)
G_reader_settings:delSetting("reader_footer_mode")
end)
it("should setup footer as visible in mini progress bar mode", function()
G_reader_settings:saveSetting("reader_footer_mode", 1)
local sample_pdf = "spec/front/unit/data/2col.pdf"
purgeDir(DocSettings:getSidecarDir(sample_pdf))
os.remove(DocSettings:getHistoryPath(sample_pdf))
local cfg = DocSettings:open(sample_pdf)
cfg:saveSetting("kopt_full_screen", 0)
cfg:flush()
local readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_pdf),
}
assert.is.same(false, readerui.view.footer_visible)
G_reader_settings:delSetting("reader_footer_mode")
end)
it("should setup footer as invisible", function()
G_reader_settings:saveSetting("reader_footer_mode", 1)
local sample_epub = "spec/front/unit/data/juliet.epub"
purgeDir(DocSettings:getSidecarDir(sample_epub))
os.remove(DocSettings:getHistoryPath(sample_epub))
local cfg = DocSettings:open(sample_epub)
cfg:saveSetting("copt_status_line", 1)
cfg:flush()
local readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_epub),
}
assert.is.same(true, readerui.view.footer_visible)
G_reader_settings:delSetting("reader_footer_mode")
end)
it("should setup footer for epub without error", function()
local sample_epub = "spec/front/unit/data/juliet.epub"
purgeDir(DocSettings:getSidecarDir(sample_epub))
@ -100,6 +161,41 @@ describe("Readerfooter module", function()
assert.are.same('TC: na', footer.progress_text.text)
end)
it("should rotate through different modes", function()
local sample_pdf = "spec/front/unit/data/2col.pdf"
local readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_pdf),
}
local footer = readerui.view.footer
footer.settings.all_at_once = false
footer.mode = 0
footer:onTapFooter()
assert.is.same(1, footer.mode)
footer:onTapFooter()
assert.is.same(2, footer.mode)
footer:onTapFooter()
assert.is.same(3, footer.mode)
footer:onTapFooter()
assert.is.same(4, footer.mode)
footer:onTapFooter()
assert.is.same(5, footer.mode)
footer:onTapFooter()
assert.is.same(6, footer.mode)
footer:onTapFooter()
assert.is.same(7, footer.mode)
footer:onTapFooter()
assert.is.same(0, footer.mode)
footer.settings.all_at_once = true
footer.mode = 5
footer:onTapFooter()
assert.is.same(0, footer.mode)
footer:onTapFooter()
assert.is.same(1, footer.mode)
footer:onTapFooter()
assert.is.same(0, footer.mode)
end)
it("should pick up screen resize in resetLayout", function()
local sample_pdf = "spec/front/unit/data/2col.pdf"
purgeDir(DocSettings:getSidecarDir(sample_pdf))

@ -88,6 +88,8 @@ describe("ReaderLink module", function()
zoom = 0.9501187648456056456,
},
}
-- disable footer
G_reader_settings:saveSetting("reader_footer_mode", 0)
local readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_pdf),
}

@ -20,12 +20,14 @@ describe("Readerrolling module", function()
it("should goto portrait screen mode", function()
readerui:handleEvent(Event:new("ChangeScreenMode", "portrait"))
end)
it("should goto certain page", function()
for i = 1, 10, 5 do
rolling:onGotoPage(i)
assert.are.same(i, rolling.current_page)
end
end)
it("should goto relative page", function()
for i = 20, 40, 5 do
rolling:onGotoPage(i)
@ -35,6 +37,7 @@ describe("Readerrolling module", function()
assert.are.same(i, rolling.current_page)
end
end)
it("should goto next chapter", function()
local toc = readerui.toc
for i = 30, 50, 5 do
@ -43,6 +46,7 @@ describe("Readerrolling module", function()
assert.are.same(toc:getNextChapter(i, 0), rolling.current_page)
end
end)
it("should goto previous chapter", function()
local toc = readerui.toc
for i = 60, 80, 5 do
@ -51,18 +55,58 @@ describe("Readerrolling module", function()
assert.are.same(toc:getPreviousChapter(i, 0), rolling.current_page)
end
end)
it("should emit EndOfBook event at the end", function()
rolling:onGotoPage(readerui.document:getPageCount())
it("should emit EndOfBook event at the end of sample epub", function()
local called = false
readerui.onEndOfBook = function()
called = true
end
-- check beginning of the book
rolling:onGotoPage(1)
assert.is.falsy(called)
rolling:onGotoViewRel(-1)
rolling:onGotoViewRel(-1)
assert.is.falsy(called)
-- check end of the book
rolling:onGotoPage(readerui.document:getPageCount())
assert.is.falsy(called)
rolling:onGotoViewRel(1)
assert.is.truthy(called)
rolling:onGotoViewRel(1)
assert.is.truthy(called)
readerui.onEndOfBook = nil
end)
it("should emit EndOfBook event at the end sample txt", function()
local sample_txt = "spec/front/unit/data/sample.txt"
local txt_readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_txt),
}
local called = false
txt_readerui.onEndOfBook = function()
called = true
end
local txt_rolling = txt_readerui.rolling
-- check beginning of the book
txt_rolling:onGotoPage(1)
assert.is.falsy(called)
txt_rolling:onGotoViewRel(-1)
txt_rolling:onGotoViewRel(-1)
assert.is.falsy(called)
-- not at the end of the book
txt_rolling:onGotoPage(3)
assert.is.falsy(called)
txt_rolling:onGotoViewRel(1)
assert.is.falsy(called)
-- at the end of the book
txt_rolling:onGotoPage(txt_readerui.document:getPageCount())
assert.is.falsy(called)
txt_rolling:onGotoViewRel(1)
assert.is.truthy(called)
readerui.onEndOfBook = nil
end)
end)
describe("test in landscape screen mode", function()
it("should go to landscape screen mode", function()
readerui:handleEvent(Event:new("ChangeScreenMode", "landscape"))
@ -110,6 +154,7 @@ describe("Readerrolling module", function()
readerui.onEndOfBook = nil
end)
end)
describe("switching screen mode should not change current page number", function()
it("for portrait-landscape-portrait switching", function()
for i = 80, 100, 10 do

@ -87,12 +87,13 @@ describe("Readersearch module", function()
for _, word in ipairs(words) do
--dbg("found word", word.start)
end
doc:gotoXPointer(words[1].start)
doc:gotoXPointer(words[#words].start)
words = search:searchNext("Verona", 0)
end
assert.are.equal(13, count)
end)
end)
describe("search API for PDF documents", function()
local doc, search, paging
setup(function()

@ -1,8 +1,11 @@
require("commonrequire")
local Menu = require("ui/widget/menu")
local DEBUG = require("dbg")
describe("Menu widget", function()
local Menu, dbg
setup(function()
require("commonrequire")
Menu = require("ui/widget/menu")
dbg = require("dbg")
end)
it("should convert item table from touch menu properly", function()
local cb1 = function() end
local cb2 = function() end

@ -0,0 +1,18 @@
describe("ProgressWidget widget", function()
local ProgressWidget, Screen
setup(function()
require("commonrequire")
ProgressWidget = require("ui/widget/progresswidget")
Screen = require("device").screen
end)
it("should not crash with nil self.last", function()
local progress = ProgressWidget:new{
width = 100,
height = 50,
percentage = 5/100,
ticks = {1},
}
progress:paintTo(Screen.bb, 0, 0)
end)
end)

@ -0,0 +1,94 @@
Lorem ipsum
Dolor sit amet, foo@bar.com
v0.1, 99 September 7999
Lorem ipsum dolor sit amet, pri id laudem vulputate disputando, ad mea
pericula consetetur. Nusquam detraxit ad sed, tritani mandamus aliquando et
has, porro graeco at pri. Sale denique ut sit, mel suas erroribus repudiare
ea. Vim probo dicit consequuntur te.
______________________________________________________________________
Table of Contents
1. Eos ex eius iusto delicata
2. Illum argumentum sed a
3. In eum magna iusto integre
______________________________________________________________________
1. Eos ex eius iusto delicata
Eos ex eius iusto delicata, ius ne facer invenire electram, cu mel assum
novum efficiendi. Duo enim eleifend te. Elitr nihil vivendo vix ex, ex homero
salutatus sed, ea nec posse commune consetetur. Ea iusto labore docendi his,
at per mollis mentitum. Ex esse recteque eos, ex iudicabit gloriatur mei.
Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue:
1. Maecenas nec odio et ante tincidunt tempus.
2. Donec sodales sagittis magna.
3. Phasellus viverra nulla ut metus varius laoreet.
Li Europan lingues es membres del sam familie. Lor separat existentie es un
myth.
It va esser tam simplic quam Occidental in fact, it va esser Occidental. A un
Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico
dit me que Occidental es.Li Europan lingues es membres del sam familie. Lor
separat existentie es un myth. Por scientie, musica, sport etc, litot Europa
usa li sam vocabular. Li lingues differe solmen in li grammatica, li
pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de
un nov lingua franca: On refusa continuar payar custosi traductores. At
solmen va esser necessi far uniform grammatica, pronunciation e plu sommun
paroles.
2. Illum argumentum sed a
Illum argumentum sed ad, vel accumsan noluisse eu. Nam ne minimum consulatu,
vim nullam quidam ut. Ea pro temporibus ullamcorper, at case aeque vix. Est
id consetetur intellegam. Eu cum oratio gubergren, aeque tritani feugiat vel
te.
· Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo.
· Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam
nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas
nulla pariatur?
cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod
maxime placeat facere.
To install the tar.gz source, use the commands:
______________________________________________________________________
./configure
make
make install
______________________________________________________________________
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed
quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur,
adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore
et dolore magnam aliquam quaerat voluptatem.
3. In eum magna iusto integre
In eum magna iusto integre, cu solet commodo constituto pro. Te nec tota
altera, diam periculis ius eu, eum te velit partiendo conclusionemque. Diam
mnesarchum at usu, agam nonumes at nec. Vix aliquip liberavisse ex, nam at
quis choro accusam. Eu his zril graecis, latine legendos inimicus eum at, qui
te adolescens adipiscing.
Loading…
Cancel
Save