2017-06-17 03:44:29 +00:00
--[[
ReaderUI is an abstraction for a reader interface .
It works using data gathered from a document interface .
] ] --
2020-01-04 00:18:51 +00:00
local BD = require ( " ui/bidi " )
2017-06-17 03:44:29 +00:00
local Cache = require ( " cache " )
2014-08-24 07:16:01 +00:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2014-10-30 18:42:18 +00:00
local Device = require ( " device " )
2020-07-12 18:47:49 +00:00
local DeviceListener = require ( " device/devicelistener " )
2017-06-17 03:44:29 +00:00
local DocSettings = require ( " docsettings " )
local DocumentRegistry = require ( " document/documentregistry " )
2013-10-18 20:38:07 +00:00
local Event = require ( " ui/event " )
2017-07-01 10:11:44 +00:00
local FileManagerBookInfo = require ( " apps/filemanager/filemanagerbookinfo " )
2019-11-05 23:17:28 +00:00
local FileManagerCollection = require ( " apps/filemanager/filemanagercollection " )
2017-06-17 03:44:29 +00:00
local FileManagerHistory = require ( " apps/filemanager/filemanagerhistory " )
2019-03-03 11:43:09 +00:00
local FileManagerFileSearcher = require ( " apps/filemanager/filemanagerfilesearcher " )
2019-03-08 20:02:25 +00:00
local FileManagerShortcuts = require ( " apps/filemanager/filemanagershortcuts " )
2017-06-17 03:44:29 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
local InputContainer = require ( " ui/widget/container/inputcontainer " )
local InputDialog = require ( " ui/widget/inputdialog " )
local PluginLoader = require ( " pluginloader " )
local ReaderActivityIndicator = require ( " apps/reader/modules/readeractivityindicator " )
2018-03-31 19:19:31 +00:00
local ReaderBack = require ( " apps/reader/modules/readerback " )
2014-04-02 20:59:17 +00:00
local ReaderBookmark = require ( " apps/reader/modules/readerbookmark " )
local ReaderConfig = require ( " apps/reader/modules/readerconfig " )
local ReaderCoptListener = require ( " apps/reader/modules/readercoptlistener " )
2017-06-17 03:44:29 +00:00
local ReaderCropping = require ( " apps/reader/modules/readercropping " )
2018-08-11 20:47:33 +00:00
local ReaderDeviceStatus = require ( " apps/reader/modules/readerdevicestatus " )
2017-06-17 03:44:29 +00:00
local ReaderDictionary = require ( " apps/reader/modules/readerdictionary " )
local ReaderFont = require ( " apps/reader/modules/readerfont " )
local ReaderGoto = require ( " apps/reader/modules/readergoto " )
2014-04-02 20:59:17 +00:00
local ReaderHinting = require ( " apps/reader/modules/readerhinting " )
local ReaderHighlight = require ( " apps/reader/modules/readerhighlight " )
2017-06-17 03:44:29 +00:00
local ReaderKoptListener = require ( " apps/reader/modules/readerkoptlistener " )
2014-04-02 20:59:17 +00:00
local ReaderLink = require ( " apps/reader/modules/readerlink " )
2017-06-17 03:44:29 +00:00
local ReaderMenu = require ( " apps/reader/modules/readermenu " )
2020-03-26 13:04:59 +00:00
local ReaderPageMap = require ( " apps/reader/modules/readerpagemap " )
2017-06-17 03:44:29 +00:00
local ReaderPanning = require ( " apps/reader/modules/readerpanning " )
local ReaderRotation = require ( " apps/reader/modules/readerrotation " )
local ReaderPaging = require ( " apps/reader/modules/readerpaging " )
local ReaderRolling = require ( " apps/reader/modules/readerrolling " )
local ReaderSearch = require ( " apps/reader/modules/readersearch " )
2016-02-02 17:38:14 +00:00
local ReaderStatus = require ( " apps/reader/modules/readerstatus " )
2018-05-12 21:24:43 +00:00
local ReaderStyleTweak = require ( " apps/reader/modules/readerstyletweak " )
2017-06-17 03:44:29 +00:00
local ReaderToc = require ( " apps/reader/modules/readertoc " )
local ReaderTypeset = require ( " apps/reader/modules/readertypeset " )
2020-04-21 19:30:16 +00:00
local ReaderTypography = require ( " apps/reader/modules/readertypography " )
2017-06-17 03:44:29 +00:00
local ReaderView = require ( " apps/reader/modules/readerview " )
local ReaderWikipedia = require ( " apps/reader/modules/readerwikipedia " )
local ReaderZooming = require ( " apps/reader/modules/readerzooming " )
local Screenshoter = require ( " ui/widget/screenshoter " )
2019-05-01 00:09:01 +00:00
local SettingsMigration = require ( " ui/data/settings_migration " )
2017-06-17 03:44:29 +00:00
local UIManager = require ( " ui/uimanager " )
2020-08-25 13:45:05 +00:00
local ffiUtil = require ( " ffi/util " )
2017-06-17 03:44:29 +00:00
local lfs = require ( " libs/libkoreader-lfs " )
local logger = require ( " logger " )
2018-03-21 19:10:35 +00:00
local util = require ( " util " )
2017-06-17 03:44:29 +00:00
local _ = require ( " gettext " )
local Screen = require ( " device " ) . screen
2020-09-15 18:39:32 +00:00
local T = ffiUtil.template
2012-05-18 22:50:26 +00:00
2013-10-18 20:38:07 +00:00
local ReaderUI = InputContainer : new {
2016-08-12 06:05:18 +00:00
name = " ReaderUI " ,
2014-03-13 13:52:43 +00:00
active_widgets = { } ,
2012-05-18 22:50:26 +00:00
2014-03-13 13:52:43 +00:00
-- if we have a parent container, it must be referenced for now
dialog = nil ,
2012-05-18 22:50:26 +00:00
2014-03-13 13:52:43 +00:00
-- the document interface
document = nil ,
2012-06-11 16:35:13 +00:00
2014-03-13 13:52:43 +00:00
-- password for document unlock
password = nil ,
2013-01-13 02:22:33 +00:00
2014-03-13 13:52:43 +00:00
postInitCallback = nil ,
2017-03-24 19:02:17 +00:00
postReaderCallback = nil ,
2012-05-18 22:50:26 +00:00
}
2016-02-04 18:24:39 +00:00
function ReaderUI : registerModule ( name , ui_module , always_active )
if name then self [ name ] = ui_module end
2016-08-12 06:05:18 +00:00
ui_module.name = " reader " .. name
2020-11-08 21:43:37 +00:00
table.insert ( self , ui_module )
if always_active then
-- to get events even when hidden
table.insert ( self.active_widgets , ui_module )
end
2014-10-09 09:41:23 +00:00
end
function ReaderUI : registerPostInitCallback ( callback )
table.insert ( self.postInitCallback , callback )
end
2017-03-24 19:02:17 +00:00
function ReaderUI : registerPostReadyCallback ( callback )
table.insert ( self.postReaderCallback , callback )
end
2012-05-18 22:50:26 +00:00
function ReaderUI : init ( )
2017-03-23 18:40:42 +00:00
-- cap screen refresh on pan to 2 refreshes per second
2018-10-10 19:34:47 +00:00
local pan_rate = Screen.low_pan_rate and 2.0 or 30.0
2017-03-23 18:40:42 +00:00
2014-03-13 13:52:43 +00:00
self.postInitCallback = { }
2017-03-24 19:02:17 +00:00
self.postReaderCallback = { }
2014-03-13 13:52:43 +00:00
-- if we are not the top level dialog ourselves, it must be given in the table
if not self.dialog then
self.dialog = self
end
2012-06-26 09:05:09 +00:00
2018-03-14 21:16:38 +00:00
self.doc_settings = DocSettings : open ( self.document . file )
2019-05-01 00:09:01 +00:00
-- Handle local settings migration
SettingsMigration : migrateSettings ( self.doc_settings )
2018-03-14 21:16:38 +00:00
2014-06-10 07:57:10 +00:00
if Device : hasKeys ( ) then
2018-03-14 21:16:38 +00:00
self.key_events . Home = { { " Home " } , doc = " open file browser " }
2014-03-13 13:52:43 +00:00
end
2013-01-01 02:22:45 +00:00
2014-03-13 13:52:43 +00:00
-- a view container (so it must be child #1!)
2016-12-04 23:42:22 +00:00
-- all paintable widgets need to be a child of reader view
2014-10-09 09:41:23 +00:00
self : registerModule ( " view " , ReaderView : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
dimen = self.dimen ,
ui = self ,
document = self.document ,
2014-10-09 09:41:23 +00:00
} )
-- goto link controller
self : registerModule ( " link " , ReaderLink : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- text highlight
2014-10-09 09:41:23 +00:00
self : registerModule ( " highlight " , ReaderHighlight : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- menu widget should be registered after link widget and highlight widget
-- so that taps on link and highlight areas won't popup reader menu
2014-10-09 09:41:23 +00:00
-- reader menu controller
self : registerModule ( " menu " , ReaderMenu : new {
view = self.view ,
ui = self
} )
2014-03-13 13:52:43 +00:00
-- rotation controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " rotation " , ReaderRotation : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- Table of content controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " toc " , ReaderToc : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2014-03-13 13:52:43 +00:00
-- bookmark controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " bookmark " , ReaderBookmark : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- reader goto controller
2014-10-09 09:41:23 +00:00
-- "goto" being a dirty keyword in Lua?
self : registerModule ( " gotopage " , ReaderGoto : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- dictionary
2014-10-09 09:41:23 +00:00
self : registerModule ( " dictionary " , ReaderDictionary : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
2014-08-20 06:41:45 +00:00
-- wikipedia
2014-10-09 09:41:23 +00:00
self : registerModule ( " wikipedia " , ReaderWikipedia : new {
2014-08-20 06:41:45 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-08-20 06:41:45 +00:00
ui = self ,
document = self.document ,
} )
2014-03-13 13:52:43 +00:00
-- screenshot controller
2016-01-03 08:47:01 +00:00
self : registerModule ( " screenshot " , Screenshoter : new {
prefix = ' Reader ' ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} , true )
2018-08-11 20:47:33 +00:00
-- device status controller
self : registerModule ( " battery " , ReaderDeviceStatus : new {
ui = self ,
} )
2018-03-21 19:10:35 +00:00
-- configurable controller
2014-03-13 13:52:43 +00:00
if self.document . info.configurable then
-- config panel controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " config " , ReaderConfig : new {
2014-03-13 13:52:43 +00:00
configurable = self.document . configurable ,
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2018-10-26 15:27:43 +00:00
ui = self ,
document = self.document ,
2014-03-13 13:52:43 +00:00
} )
2014-10-09 09:41:23 +00:00
if self.document . info.has_pages then
-- kopt option controller
self : registerModule ( " koptlistener " , ReaderKoptListener : new {
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
else
2014-03-13 13:52:43 +00:00
-- cre option controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " crelistener " , ReaderCoptListener : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
end
2020-12-18 17:26:58 +00:00
-- activity indicator for when some settings take time to take effect (Kindle under KPV)
if not ReaderActivityIndicator : isStub ( ) then
self : registerModule ( " activityindicator " , ReaderActivityIndicator : new {
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
end
2014-03-13 13:52:43 +00:00
end
-- for page specific controller
if self.document . info.has_pages then
-- cropping controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " cropping " , ReaderCropping : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- paging controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " paging " , ReaderPaging : new {
2017-03-23 18:40:42 +00:00
pan_rate = pan_rate ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- zooming controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " zooming " , ReaderZooming : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2019-08-30 11:47:51 +00:00
document = self.document ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2014-03-13 13:52:43 +00:00
-- panning controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " panning " , ReaderPanning : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- hinting controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " hinting " , ReaderHinting : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
zoom = self.zooming ,
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
else
2018-11-20 20:07:59 +00:00
-- load crengine default settings (from cr3.ini, some of these
-- will be overriden by our settings by some reader modules below)
if self.document . setupDefaultView then
self.document : setupDefaultView ( )
end
2014-08-28 05:22:31 +00:00
-- make sure we render document first before calling any callback
2014-10-09 09:41:23 +00:00
self : registerPostInitCallback ( function ( )
2020-08-25 13:45:05 +00:00
local start_ts = ffiUtil.getTimestamp ( )
2018-01-03 08:43:49 +00:00
if not self.document : loadDocument ( ) then
self : dealWithLoadDocumentFailure ( )
end
2020-08-25 13:45:05 +00:00
logger.dbg ( string.format ( " loading took %.3f seconds " , ffiUtil.getDuration ( start_ts ) ) )
2016-01-16 18:33:16 +00:00
2016-02-07 22:03:53 +00:00
-- used to read additional settings after the document has been
-- loaded (but not rendered yet)
2016-01-16 18:33:16 +00:00
self : handleEvent ( Event : new ( " PreRenderDocument " , self.doc_settings ) )
2020-08-25 13:45:05 +00:00
start_ts = ffiUtil.getTimestamp ( )
2014-08-28 05:22:31 +00:00
self.document : render ( )
2020-08-25 13:45:05 +00:00
logger.dbg ( string.format ( " rendering took %.3f seconds " , ffiUtil.getDuration ( start_ts ) ) )
-- Uncomment to output the built DOM (for debugging)
-- logger.dbg(self.document:getHTMLFromXPointer(".0", 0x6830))
2014-03-13 13:52:43 +00:00
end )
2018-05-12 21:24:43 +00:00
-- styletweak controller (must be before typeset controller)
self : registerModule ( " styletweak " , ReaderStyleTweak : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2014-03-13 13:52:43 +00:00
-- typeset controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " typeset " , ReaderTypeset : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- font menu
2014-10-09 09:41:23 +00:00
self : registerModule ( " font " , ReaderFont : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2020-04-21 19:30:16 +00:00
-- typography menu (replaces previous hyphenation menu / ReaderHyphenation)
self : registerModule ( " typography " , ReaderTypography : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2014-03-13 13:52:43 +00:00
-- rolling controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " rolling " , ReaderRolling : new {
2017-03-23 18:40:42 +00:00
pan_rate = pan_rate ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
2020-03-26 13:04:59 +00:00
-- pagemap controller
self : registerModule ( " pagemap " , ReaderPageMap : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2014-03-13 13:52:43 +00:00
end
2020-06-28 13:41:24 +00:00
self.disable_double_tap = G_reader_settings : readSetting ( " disable_double_tap " ) ~= false
2018-03-31 19:19:31 +00:00
-- back location stack
self : registerModule ( " back " , ReaderBack : new {
ui = self ,
view = self.view ,
} )
2014-11-05 04:28:11 +00:00
-- fulltext search
self : registerModule ( " search " , ReaderSearch : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2016-03-07 05:52:53 +00:00
-- book status
2016-02-02 17:38:14 +00:00
self : registerModule ( " status " , ReaderStatus : new {
ui = self ,
document = self.document ,
2016-02-12 14:55:02 +00:00
view = self.view ,
2016-02-02 17:38:14 +00:00
} )
2019-03-03 11:43:09 +00:00
-- file searcher
self : registerModule ( " filesearcher " , FileManagerFileSearcher : new {
dialog = self.dialog ,
ui = self ,
} )
2019-03-08 20:02:25 +00:00
-- folder shortcuts
self : registerModule ( " folder_shortcuts " , FileManagerShortcuts : new {
dialog = self.dialog ,
ui = self ,
} )
2016-03-07 05:52:53 +00:00
-- history view
self : registerModule ( " history " , FileManagerHistory : new {
dialog = self.dialog ,
ui = self ,
} )
2019-11-05 23:17:28 +00:00
-- collections/favorites view
self : registerModule ( " collections " , FileManagerCollection : new {
dialog = self.dialog ,
ui = self ,
} )
2017-07-01 10:11:44 +00:00
-- book info
self : registerModule ( " bookinfo " , FileManagerBookInfo : new {
dialog = self.dialog ,
document = self.document ,
ui = self ,
} )
2020-07-12 18:47:49 +00:00
-- event listener to change device settings
self : registerModule ( " devicelistener " , DeviceListener : new {
document = self.document ,
view = self.view ,
ui = self ,
} )
2014-04-23 13:57:48 +00:00
-- koreader plugins
2017-04-21 06:37:04 +00:00
for _ , plugin_module in ipairs ( PluginLoader : loadPlugins ( ) ) do
local ok , plugin_or_err = PluginLoader : createPluginInstance (
plugin_module ,
{
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
if ok then
self : registerModule ( plugin_module.name , plugin_or_err )
logger.info ( " RD loaded plugin " , plugin_module.name ,
" at " , plugin_module.path )
end
2014-04-23 13:57:48 +00:00
end
2020-07-12 18:47:49 +00:00
if Device : hasWifiToggle ( ) then
local NetworkListener = require ( " ui/network/networklistener " )
self : registerModule ( " networklistener " , NetworkListener : new {
document = self.document ,
view = self.view ,
ui = self ,
} )
end
2019-10-08 15:14:38 +00:00
-- Allow others to change settings based on external factors
-- Must be called after plugins are loaded & before setting are read.
self : handleEvent ( Event : new ( " DocSettingsLoad " , self.doc_settings , self.document ) )
2014-03-13 13:52:43 +00:00
-- we only read settings after all the widgets are initialized
self : handleEvent ( Event : new ( " ReadSettings " , self.doc_settings ) )
2013-01-13 02:22:33 +00:00
2014-03-13 13:52:43 +00:00
for _ , v in ipairs ( self.postInitCallback ) do
v ( )
end
2017-03-24 19:02:17 +00:00
self.postInitCallback = nil
2016-02-12 14:55:02 +00:00
2017-07-01 09:41:27 +00:00
-- Now that document is loaded, store book metadata in settings
-- (so that filemanager can use it from sideCar file to display
-- Book information).
2017-08-20 10:43:52 +00:00
self.doc_settings : saveSetting ( " doc_props " , self.document : getProps ( ) )
2017-07-01 09:41:27 +00:00
2016-06-03 08:31:46 +00:00
-- After initialisation notify that document is loaded and rendered
2016-02-12 14:55:02 +00:00
-- CREngine only reports correct page count after rendering is done
-- Need the same event for PDF document
self : handleEvent ( Event : new ( " ReaderReady " , self.doc_settings ) )
2017-03-24 19:02:17 +00:00
for _ , v in ipairs ( self.postReaderCallback ) do
v ( )
end
self.postReaderCallback = nil
2020-01-24 19:05:21 +00:00
-- print("Ordered registered gestures:")
-- for _, tzone in ipairs(self._ordered_touch_zones) do
-- print(" "..tzone.def.id)
-- end
2012-05-18 22:50:26 +00:00
end
2020-05-19 16:21:58 +00:00
function ReaderUI : setLastDirForFileBrowser ( dir )
2020-06-08 19:54:24 +00:00
if dir and # dir > 1 and dir : sub ( - 1 ) == " / " then
dir = dir : sub ( 1 , - 2 )
end
2020-05-19 16:21:58 +00:00
self.last_dir_for_file_browser = dir
end
function ReaderUI : getLastDirFile ( to_file_browser )
if to_file_browser and self.last_dir_for_file_browser then
local dir = self.last_dir_for_file_browser
self.last_dir_for_file_browser = nil
return dir
end
2017-06-17 03:44:29 +00:00
local QuickStart = require ( " ui/quickstart " )
2017-10-13 16:43:02 +00:00
local last_dir
2017-06-17 03:44:29 +00:00
local last_file = G_reader_settings : readSetting ( " lastfile " )
-- ignore quickstart guide as last_file so we can go back to home dir
if last_file and last_file ~= QuickStart.quickstart_filename then
2017-10-13 16:43:02 +00:00
last_dir = last_file : match ( " (.*)/ " )
2017-06-17 03:44:29 +00:00
end
2019-03-03 11:43:09 +00:00
return last_dir , last_file
end
2019-07-20 15:36:41 +00:00
function ReaderUI : showFileManager ( file )
2019-03-03 11:43:09 +00:00
local FileManager = require ( " apps/filemanager/filemanager " )
2019-07-20 15:36:41 +00:00
local last_dir , last_file
if file then
last_dir , last_file = util.splitFilePathName ( file )
last_dir = last_dir : match ( " (.*)/ " )
else
2020-05-19 16:21:58 +00:00
last_dir , last_file = self : getLastDirFile ( true )
2019-07-20 15:36:41 +00:00
end
2017-06-17 03:44:29 +00:00
if FileManager.instance then
2017-10-13 16:43:02 +00:00
FileManager.instance : reinit ( last_dir , last_file )
2017-06-17 03:44:29 +00:00
else
2017-10-13 16:43:02 +00:00
FileManager : showFiles ( last_dir , last_file )
2017-06-17 03:44:29 +00:00
end
end
2018-02-02 20:21:52 +00:00
function ReaderUI : showReader ( file , provider )
2016-12-29 08:10:38 +00:00
logger.dbg ( " show reader ui " )
2018-02-02 20:21:52 +00:00
2014-09-03 04:09:25 +00:00
if lfs.attributes ( file , " mode " ) ~= " file " then
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " File '%1' does not exist. " ) , BD.filepath ( file ) )
2014-09-03 04:09:25 +00:00
} )
return
end
2018-02-02 20:21:52 +00:00
2019-10-23 21:15:30 +00:00
if not DocumentRegistry : hasProvider ( file ) and provider == nil then
2019-07-20 15:36:41 +00:00
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " File '%1' is not supported. " ) , BD.filepath ( file ) )
2019-07-20 15:36:41 +00:00
} )
self : showFileManager ( file )
return
end
2018-02-02 20:21:52 +00:00
-- prevent crash due to incompatible bookmarks
2019-08-23 17:53:53 +00:00
--- @todo Split bookmarks from metadata and do per-engine in conversion.
2018-02-02 20:21:52 +00:00
provider = provider or DocumentRegistry : getProvider ( file )
if provider.provider then
local doc_settings = DocSettings : open ( file )
local bookmarks = doc_settings : readSetting ( " bookmarks " ) or { }
if # bookmarks >= 1 and
( ( provider.provider == " crengine " and type ( bookmarks [ 1 ] . page ) == " number " ) or
( provider.provider == " mupdf " and type ( bookmarks [ 1 ] . page ) == " string " ) ) then
UIManager : show ( ConfirmBox : new {
text = T ( _ ( " The document '%1' with bookmarks or highlights was previously opened with a different engine. To prevent issues, bookmarks need to be deleted before continuing. " ) ,
2020-01-04 00:18:51 +00:00
BD.filepath ( file ) ) ,
2018-02-02 20:21:52 +00:00
ok_text = _ ( " Delete " ) ,
ok_callback = function ( )
doc_settings : delSetting ( " bookmarks " )
doc_settings : close ( )
self : showReaderCoroutine ( file , provider )
end ,
2019-03-07 17:04:39 +00:00
cancel_callback = function ( ) self : showFileManager ( ) end ,
2018-02-02 20:21:52 +00:00
} )
else
self : showReaderCoroutine ( file , provider )
end
end
end
function ReaderUI : showReaderCoroutine ( file , provider )
2014-09-03 04:09:25 +00:00
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " Opening file '%1'. " ) , BD.filepath ( file ) ) ,
2016-04-23 15:09:31 +00:00
timeout = 0.0 ,
2014-09-03 04:09:25 +00:00
} )
2016-04-24 04:09:50 +00:00
-- doShowReader might block for a long time, so force repaint here
UIManager : forceRePaint ( )
2016-03-05 20:37:45 +00:00
UIManager : nextTick ( function ( )
2016-12-29 08:10:38 +00:00
logger.dbg ( " creating coroutine for showing reader " )
2015-10-06 13:18:07 +00:00
local co = coroutine.create ( function ( )
2018-02-02 20:21:52 +00:00
self : doShowReader ( file , provider )
2015-10-06 13:18:07 +00:00
end )
2015-12-23 08:01:43 +00:00
local ok , err = coroutine.resume ( co )
2016-01-03 08:56:55 +00:00
if err ~= nil or ok == false then
2016-03-18 04:52:57 +00:00
io.stderr : write ( ' [!] doShowReader coroutine crashed: \n ' )
io.stderr : write ( debug.traceback ( co , err , 1 ) )
2019-04-16 15:54:02 +00:00
UIManager : show ( InfoMessage : new {
text = _ ( " No reader engine for this file or invalid file. " )
} )
self : showFileManager ( )
2015-12-23 08:01:43 +00:00
end
2015-10-06 13:18:07 +00:00
end )
2014-09-03 04:09:25 +00:00
end
2016-05-03 05:35:18 +00:00
local _running_instance = nil
2018-02-02 20:21:52 +00:00
function ReaderUI : doShowReader ( file , provider )
2016-12-29 08:10:38 +00:00
logger.info ( " opening file " , file )
2014-10-28 10:09:22 +00:00
-- keep only one instance running
2016-05-03 05:35:18 +00:00
if _running_instance then
_running_instance : onClose ( )
2014-10-28 10:09:22 +00:00
end
2018-02-02 20:21:52 +00:00
local document = DocumentRegistry : openDocument ( file , provider )
2014-09-03 04:09:25 +00:00
if not document then
UIManager : show ( InfoMessage : new {
2017-06-17 03:44:29 +00:00
text = _ ( " No reader engine for this file or invalid file. " )
2014-09-03 04:09:25 +00:00
} )
2017-06-17 03:44:29 +00:00
self : showFileManager ( )
2014-09-03 04:09:25 +00:00
return
end
2015-10-06 13:18:07 +00:00
if document.is_locked then
2016-12-29 08:10:38 +00:00
logger.info ( " document is locked " )
2015-10-06 13:18:07 +00:00
self._coroutine = coroutine.running ( ) or self._coroutine
self : unlockDocumentWithPassword ( document )
if coroutine.running ( ) then
local unlock_success = coroutine.yield ( )
if not unlock_success then
2017-06-17 03:44:29 +00:00
self : showFileManager ( )
2015-10-06 13:18:07 +00:00
return
end
end
end
2020-05-06 19:11:34 +00:00
require ( " readhistory " ) : addItem ( file ) -- (will update "lastfile")
2014-09-03 04:09:25 +00:00
local reader = ReaderUI : new {
dimen = Screen : getSize ( ) ,
2018-03-17 22:02:32 +00:00
covers_fullscreen = true , -- hint for UIManager:_repaint()
2014-09-03 04:09:25 +00:00
document = document ,
}
2018-03-21 19:10:35 +00:00
local title = reader.document : getProps ( ) . title
if title ~= " " then
Screen : setWindowTitle ( title )
else
local _ , filename = util.splitFilePathName ( file )
Screen : setWindowTitle ( filename )
end
2020-09-01 15:52:16 +00:00
Device : notifyBookState ( title , document )
2018-03-21 19:10:35 +00:00
2014-09-03 04:09:25 +00:00
UIManager : show ( reader )
2016-05-03 05:35:18 +00:00
_running_instance = reader
2018-05-20 14:35:53 +00:00
local FileManager = require ( " apps/filemanager/filemanager " )
if FileManager.instance then
FileManager.instance : onClose ( )
end
2014-09-03 04:09:25 +00:00
end
2016-02-17 08:06:55 +00:00
function ReaderUI : _getRunningInstance ( )
2016-05-03 05:35:18 +00:00
return _running_instance
2016-02-17 08:06:55 +00:00
end
2015-10-06 13:18:07 +00:00
function ReaderUI : unlockDocumentWithPassword ( document , try_again )
2016-12-29 08:10:38 +00:00
logger.dbg ( " show input password dialog " )
2015-10-06 13:18:07 +00:00
self.password_dialog = InputDialog : new {
title = try_again and _ ( " Password is incorrect, try again? " )
or _ ( " Input document password " ) ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
enabled = true ,
callback = function ( )
self : closeDialog ( )
coroutine.resume ( self._coroutine )
end ,
} ,
{
text = _ ( " OK " ) ,
enabled = true ,
callback = function ( )
local success = self : onVerifyPassword ( document )
self : closeDialog ( )
if success then
coroutine.resume ( self._coroutine , success )
else
self : unlockDocumentWithPassword ( document , true )
end
end ,
} ,
} ,
} ,
text_type = " password " ,
}
UIManager : show ( self.password_dialog )
2018-03-30 10:46:36 +00:00
self.password_dialog : onShowKeyboard ( )
2015-10-06 13:18:07 +00:00
end
function ReaderUI : onVerifyPassword ( document )
local password = self.password_dialog : getInputText ( )
return document : unlock ( password )
end
function ReaderUI : closeDialog ( )
self.password_dialog : onClose ( )
UIManager : close ( self.password_dialog )
end
2016-12-04 06:57:57 +00:00
function ReaderUI : onScreenResize ( dimen )
2014-03-13 13:52:43 +00:00
self.dimen = dimen
2016-12-04 06:57:57 +00:00
self : updateTouchZonesOnScreenResize ( dimen )
2012-06-12 16:14:23 +00:00
end
2013-12-27 15:18:16 +00:00
function ReaderUI : saveSettings ( )
2014-03-13 13:52:43 +00:00
self : handleEvent ( Event : new ( " SaveSettings " ) )
self.doc_settings : flush ( )
2014-07-03 12:43:17 +00:00
G_reader_settings : flush ( )
2013-12-27 15:18:16 +00:00
end
2014-06-08 09:56:52 +00:00
function ReaderUI : onFlushSettings ( )
self : saveSettings ( )
end
2014-08-24 07:16:01 +00:00
function ReaderUI : closeDocument ( )
self.document : close ( )
self.document = nil
end
2015-03-09 12:20:32 +00:00
function ReaderUI : notifyCloseDocument ( )
self : handleEvent ( Event : new ( " CloseDocument " ) )
2014-08-24 07:16:01 +00:00
if self.document : isEdited ( ) then
2016-07-14 17:38:00 +00:00
local setting = G_reader_settings : readSetting ( " save_document " )
if setting == " always " then
self : closeDocument ( )
elseif setting == " disable " then
self.document : discardChange ( )
self : closeDocument ( )
else
UIManager : show ( ConfirmBox : new {
text = _ ( " Do you want to save this document? " ) ,
2017-08-21 07:19:24 +00:00
ok_text = _ ( " Save " ) ,
cancel_text = _ ( " Don't save " ) ,
2016-07-14 17:38:00 +00:00
ok_callback = function ( )
self : closeDocument ( )
end ,
cancel_callback = function ( )
self.document : discardChange ( )
self : closeDocument ( )
end ,
} )
end
2014-08-24 07:16:01 +00:00
else
self : closeDocument ( )
end
end
2019-11-04 15:44:13 +00:00
function ReaderUI : onClose ( full_refresh )
2016-12-29 08:10:38 +00:00
logger.dbg ( " closing reader " )
2020-09-01 15:52:16 +00:00
Device : notifyBookState ( nil , nil )
2019-11-04 15:44:13 +00:00
if full_refresh == nil then
full_refresh = true
end
2017-08-25 16:55:44 +00:00
-- if self.dialog is us, we'll have our onFlushSettings() called
-- by UIManager:close() below, so avoid double save
if self.dialog ~= self then
self : saveSettings ( )
end
2014-03-13 13:52:43 +00:00
if self.document ~= nil then
2016-12-29 08:10:38 +00:00
logger.dbg ( " closing document " )
2015-03-09 12:20:32 +00:00
self : notifyCloseDocument ( )
2014-03-13 13:52:43 +00:00
end
2019-11-04 15:44:13 +00:00
UIManager : close ( self.dialog , full_refresh and " full " )
2014-05-15 11:41:10 +00:00
-- serialize last used items for later launch
Cache : serialize ( )
2016-05-03 05:35:18 +00:00
if _running_instance == self then
_running_instance = nil
2016-02-17 06:36:40 +00:00
end
2012-05-18 22:50:26 +00:00
end
2018-01-03 08:43:49 +00:00
function ReaderUI : dealWithLoadDocumentFailure ( )
-- Sadly, we had to delay loadDocument() to about now, so we only
-- know now this document is not valid or recognized.
-- We can't do much more than crash properly here (still better than
-- going on and segfaulting when calling other methods on unitiliazed
-- _document)
-- We must still remove it from lastfile and history (as it has
-- already been added there) so that koreader don't crash again
-- at next launch...
2020-05-06 19:11:34 +00:00
require ( " readhistory " ) : removeItemByPath ( self.document . file ) -- (will update "lastfile")
2018-01-03 08:43:49 +00:00
-- As we are in a coroutine, we can pause and show an InfoMessage before exiting
local _coroutine = coroutine.running ( )
if coroutine then
logger.warn ( " crengine failed recognizing or parsing this file: unsupported or invalid document " )
UIManager : show ( InfoMessage : new {
text = _ ( " Failed recognizing or parsing this file: unsupported or invalid document. \n KOReader will exit now. " ) ,
dismiss_callback = function ( )
coroutine.resume ( _coroutine , false )
end ,
} )
coroutine.yield ( ) -- pause till InfoMessage is dismissed
end
-- We have to error and exit the coroutine anyway to avoid any segfault
error ( " crengine failed recognizing or parsing this file: unsupported or invalid document " )
end
2018-03-14 21:16:38 +00:00
function ReaderUI : onHome ( )
2018-04-09 07:22:16 +00:00
self : onClose ( )
self : showFileManager ( )
return true
2018-03-14 21:16:38 +00:00
end
2018-05-20 23:19:36 +00:00
function ReaderUI : reloadDocument ( after_close_callback )
local file = self.document . file
local provider = getmetatable ( self.document ) . __index
self : handleEvent ( Event : new ( " CloseReaderMenu " ) )
self : handleEvent ( Event : new ( " CloseConfigMenu " ) )
2018-07-11 16:05:30 +00:00
self.highlight : onClose ( ) -- close highlight dialog if any
2019-11-04 15:44:13 +00:00
self : onClose ( false )
2018-05-20 23:19:36 +00:00
if after_close_callback then
-- allow caller to do stuff between close an re-open
after_close_callback ( file , provider )
end
self : showReader ( file , provider )
end
2018-07-07 11:03:33 +00:00
function ReaderUI : switchDocument ( new_file )
2019-02-22 15:29:19 +00:00
if not new_file then return end
2018-07-07 11:03:33 +00:00
self : handleEvent ( Event : new ( " CloseReaderMenu " ) )
self : handleEvent ( Event : new ( " CloseConfigMenu " ) )
2018-07-11 16:05:30 +00:00
self.highlight : onClose ( ) -- close highlight dialog if any
2019-11-04 15:44:13 +00:00
self : onClose ( false )
2018-07-07 11:03:33 +00:00
self : showReader ( new_file )
end
2020-07-12 18:47:49 +00:00
function ReaderUI : onOpenLastDoc ( )
self : switchDocument ( self.menu : getPreviousFile ( ) )
end
2020-03-16 15:52:09 +00:00
function ReaderUI : getCurrentPage ( )
if self.document . info.has_pages then
return self.paging . current_page
else
return self.document : getCurrentPage ( )
end
end
2013-10-18 20:38:07 +00:00
return ReaderUI