mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
resolve conflict on frontend/ui/widget/scrolltextwidget.lua
This commit is contained in:
@@ -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
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
|
||||
|
||||
11
Makefile
11
Makefile
@@ -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)) \
|
||||
|
||||
19
README.md
19
README.md
@@ -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
|
||||
```
|
||||
|
||||
2
base
2
base
Submodule base updated: b5c82144e2...edfd1239c6
@@ -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
|
||||
|
||||
|
||||
121
frontend/readhistory.lua
Normal file
121
frontend/readhistory.lua
Normal file
@@ -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
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
|
||||
|
||||
18
spec/unit/widget_progresswidget_spec.lua
Normal file
18
spec/unit/widget_progresswidget_spec.lua
Normal file
@@ -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)
|
||||
94
test/sample.txt
Normal file
94
test/sample.txt
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user