@ -6,31 +6,51 @@ if not (Device.isAndroid() or Device.isEmulator() or Device.isRemarkable() or De
return { disabled = true }
return { disabled = true }
end
end
local A , android = pcall ( require , " android " ) -- luacheck: ignore
local Blitbuffer = require ( " ffi/blitbuffer " )
local Blitbuffer = require ( " ffi/blitbuffer " )
local ConfirmBox = require ( " ui/widget/confirmbox " )
local DataStorage = require ( " datastorage " )
local InfoMessage = require ( " ui/widget/infomessage " )
local InfoMessage = require ( " ui/widget/infomessage " )
local InputDialog = require ( " ui/widget/inputdialog " )
local PathChooser = require ( " ui/widget/pathchooser " )
local UIManager = require ( " ui/uimanager " )
local UIManager = require ( " ui/uimanager " )
local WidgetContainer = require ( " ui/widget/container/widgetcontainer " )
local RenderImage = require ( " ui/renderimage " )
local RenderImage = require ( " ui/renderimage " )
local Screen = require ( " device " ) . screen
local T = require ( " ffi/util " ) . template
local WidgetContainer = require ( " ui/widget/container/widgetcontainer " )
local ffiutil = require ( " ffi/util " )
local ffiutil = require ( " ffi/util " )
local lfs = require ( " libs/libkoreader-lfs " )
local lfs = require ( " libs/libkoreader-lfs " )
local logger = require ( " logger " )
local logger = require ( " logger " )
local md5 = require ( " ffi/sha2 " ) . md5
local util = require ( " util " )
local util = require ( " util " )
local _ = require ( " gettext " )
local _ = require ( " gettext " )
local T = require ( " ffi/util " ) . template
local function pathOk ( filename )
-- todo: please check the default paths directly on the depending Device:getDefaultCoverPath()
local path , name = util.splitFilePathName ( filename )
local function isPathAllowed ( path )
-- don't allow a path that interferes with frontent cache-framework; quick and dirty check
if not Device : isValidPath ( path ) then -- isValidPath expects a trailing slash
if not Device : isValidPath ( path ) then -- isValidPath expects a trailing slash
return false , T ( _ ( " Path \" %1 \" isn't in a writable location. " ) , path )
return false
elseif not util.pathExists ( path : gsub ( " /$ " , " " ) ) then -- pathExists expects no trailing slash
elseif not util.pathExists ( path : gsub ( " /$ " , " " ) ) then -- pathExists expects no trailing slash
return false , T ( _ ( " The path \" %1 \" doesn't exist. " ) , path )
return false
elseif name == " " then
elseif Device.isAndroid ( ) then
return false , _ ( " Please enter a filename at the end of the path. " )
return path ~= " /sdcard/koreader/cache/ "
elseif lfs.attributes ( filename , " mode " ) == " directory " then
and ffiutil.realpath ( path ) ~= ffiutil.realpath ( android.getExternalStoragePath ( ) .. " /koreader/cache/ " )
return false , T ( _ ( " The path \" %1 \" must point to a file, but it points to a folder. " ) , filename )
else
return path ~= " ./cache/ " and ffiutil.realpath ( path ) ~= ffiutil.realpath ( " ./cache/ " )
end
end
end
local function isFileOk ( filename )
local path , name = util.splitFilePathName ( filename )
return true
if not isPathAllowed ( path ) then
return false
end
return name ~= " " and lfs.attributes ( filename , " mode " ) ~= " directory "
end
end
local function getExtension ( filename )
local function getExtension ( filename )
@ -43,53 +63,60 @@ local CoverImage = WidgetContainer:new{
is_doc_only = true ,
is_doc_only = true ,
}
}
local default_cache_path = DataStorage : getDataDir ( ) .. " /cache/cover_image.cache/ "
local default_fallback_path = DataStorage : getDataDir ( ) .. " / "
function CoverImage : init ( )
function CoverImage : init ( )
self.cover_image_path = G_reader_settings : readSetting ( " cover_image_path " ) or " cover.jpg "
self.cover_image_path = G_reader_settings : readSetting ( " cover_image_path " ) or Device : getDefaultCoverPath ( )
self.cover_image_format = G_reader_settings : readSetting ( " cover_image_format " ) or " auto "
self.cover_image_format = G_reader_settings : readSetting ( " cover_image_format " ) or " auto "
self.cover_image_extension = getExtension ( self.cover_image_path )
self.cover_image_quality = G_reader_settings : readSetting ( " cover_image_quality " ) or 75
self.cover_image_quality = G_reader_settings : readSetting ( " cover_image_quality " ) or 75
self.cover_image_stretch_limit = G_reader_settings : readSetting ( " cover_image_stretch_limit " ) or 5
self.cover_image_stretch_limit = G_reader_settings : readSetting ( " cover_image_stretch_limit " ) or 8
self.cover_image_background = G_reader_settings : readSetting ( " cover_image_background " ) or " black "
self.cover_image_background = G_reader_settings : readSetting ( " cover_image_background " ) or " black "
self.cover_image_fallback_path = G_reader_settings : readSetting ( " cover_image_fallback_path " ) or " cover_fallback.png "
self.cover_image_fallback_path = G_reader_settings : readSetting ( " cover_image_fallback_path " ) or default_fallback_path
self.enabled = G_reader_settings : isTrue ( " cover_image_enabled " )
self.cover_image_cache_path = G_reader_settings : readSetting ( " cover_image_cache_path " ) or default_cache_path
self.cover_image_cache_maxfiles = G_reader_settings : readSetting ( " cover_image_cache_maxfiles " ) or 36
self.cover_image_cache_maxsize = G_reader_settings : readSetting ( " cover_image_cache_maxsize " ) or 5 -- MiB
self.cover_image_cache_prefix = " cover_ "
self.cover = G_reader_settings : isTrue ( " cover_image_enabled " )
self.fallback = G_reader_settings : isTrue ( " cover_image_fallback " )
self.fallback = G_reader_settings : isTrue ( " cover_image_fallback " )
self.ui . menu : registerToMainMenu ( self )
end
lfs.mkdir ( self.cover_image_cache_path )
function CoverImage : _enabled ( )
return self.enabled
end
function CoverImage : _fallback ( )
self.ui . menu : registerToMainMenu ( self )
return self.fallback
end
end
function CoverImage : cleanUpImage ( )
function CoverImage : cleanUpImage ( )
if self.cover_image_fallback_path == " " or not self .fallback then
if self.cover_image_fallback_path == " " or not self : fallbackEnabled ( ) then
os.remove ( self.cover_image_path )
os.remove ( self.cover_image_path )
elseif lfs.attributes ( self.cover_image_fallback_path , " mode " ) ~= " file " then
elseif lfs.attributes ( self.cover_image_fallback_path , " mode " ) ~= " file " then
UIManager : show ( InfoMessage : new {
UIManager : show ( InfoMessage : new {
text = T ( _ ( " \" %1 \" \n is not a valid image file! \n A valid fallback image is required in Cover-Image. " ) , self.cover_image_fallback_path ) ,
text = T ( _ ( " \" %1 \" \n is not a valid image file! \n A valid fallback image is required in Cover-Image. " ) , self.cover_image_fallback_path ) ,
show_icon = true ,
show_icon = true ,
timeout = 10 ,
timeout = 10 ,
} )
} )
os.remove ( self.cover_image_path )
os.remove ( self.cover_image_path )
elseif path Ok( self.cover_image_path ) then
elseif isFile Ok( self.cover_image_path ) then
ffiutil.copyFile ( self.cover_image_fallback_path , self.cover_image_path )
ffiutil.copyFile ( self.cover_image_fallback_path , self.cover_image_path )
end
end
end
end
function CoverImage : createCoverImage ( doc_settings )
function CoverImage : createCoverImage ( doc_settings )
if self .enabled and doc_settings : nilOrFalse ( " exclude_cover_image " ) then
if self : coverEnabled ( ) and doc_settings : nilOrFalse ( " exclude_cover_image " ) then
local cover_image = self.ui . document : getCoverPageImage ( )
local cover_image = self.ui . document : getCoverPageImage ( )
if cover_image then
if cover_image then
local cache_file = self : getCacheFile ( )
if lfs.attributes ( cache_file , " mode " ) == " file " then
ffiutil.copyFile ( cache_file , self.cover_image_path )
lfs.touch ( cache_file ) -- update date
return
end
local s_w , s_h = Device.screen : getWidth ( ) , Device.screen : getHeight ( )
local s_w , s_h = Device.screen : getWidth ( ) , Device.screen : getHeight ( )
local i_w , i_h = cover_image : getWidth ( ) , cover_image : getHeight ( )
local i_w , i_h = cover_image : getWidth ( ) , cover_image : getHeight ( )
local scale_factor = math.min ( s_w / i_w , s_h / i_h )
local scale_factor = math.min ( s_w / i_w , s_h / i_h )
if self.cover_image_background == " none " or scale_factor == 1 then
if self.cover_image_background == " none " or scale_factor == 1 then
local act_format = self.cover_image_format == " auto " and self.cover_image_extension or self.cover_image_format
local act_format = self.cover_image_format == " auto " and getExtension( self.cover_image_path ) or self.cover_image_format
if not cover_image : writeToFile ( self.cover_image_path , act_format , self.cover_image_quality ) then
if not cover_image : writeToFile ( self.cover_image_path , act_format , self.cover_image_quality ) then
UIManager : show ( InfoMessage : new {
UIManager : show ( InfoMessage : new {
text = _ ( " Error writing file " ) .. " \n " .. self.cover_image_path ,
text = _ ( " Error writing file " ) .. " \n " .. self.cover_image_path ,
@ -97,6 +124,8 @@ function CoverImage:createCoverImage(doc_settings)
} )
} )
end
end
cover_image : free ( )
cover_image : free ( )
ffiutil.copyFile ( self.cover_image_path , cache_file )
self : cleanCache ( )
return
return
end
end
@ -132,7 +161,7 @@ function CoverImage:createCoverImage(doc_settings)
cover_image : free ( )
cover_image : free ( )
local act_format = self.cover_image_format == " auto " and self.cover_image_extension or self.cover_image_format
local act_format = self.cover_image_format == " auto " and getExtension( self.cover_image_path ) or self.cover_image_format
if not image : writeToFile ( self.cover_image_path , act_format , self.cover_image_quality ) then
if not image : writeToFile ( self.cover_image_path , act_format , self.cover_image_quality ) then
UIManager : show ( InfoMessage : new {
UIManager : show ( InfoMessage : new {
text = _ ( " Error writing file " ) .. " \n " .. self.cover_image_path ,
text = _ ( " Error writing file " ) .. " \n " .. self.cover_image_path ,
@ -142,13 +171,16 @@ function CoverImage:createCoverImage(doc_settings)
image : free ( )
image : free ( )
logger.dbg ( " CoverImage: image written to " .. self.cover_image_path )
logger.dbg ( " CoverImage: image written to " .. self.cover_image_path )
ffiutil.copyFile ( self.cover_image_path , cache_file )
self : cleanCache ( )
end
end
end
end
end
end
function CoverImage : onCloseDocument ( )
function CoverImage : onCloseDocument ( )
logger.dbg ( " CoverImage: onCloseDocument " )
logger.dbg ( " CoverImage: onCloseDocument " )
if self .fallback then
if self : fallbackEnabled ( ) then
self : cleanUpImage ( )
self : cleanUpImage ( )
end
end
end
end
@ -158,105 +190,515 @@ function CoverImage:onReaderReady(doc_settings)
self : createCoverImage ( doc_settings )
self : createCoverImage ( doc_settings )
end
end
function CoverImage : fallbackEnabled ( )
return self.fallback and isFileOk ( self.cover_image_fallback_path )
end
function CoverImage : coverEnabled ( )
return self.cover and isFileOk ( self.cover_image_path )
end
---------------------------
-- cache handling functions
---------------------------
function CoverImage : getCacheFile ( )
local dummy , document_name = util.splitFilePathName ( self.ui . document.file )
-- use document_name here. Title may contain characters not allowed on every filesystem (esp. vfat on /sdcard)
local key = document_name .. " _ " .. self.cover_image_quality .. " _ " .. self.cover_image_stretch_limit .. " _ "
.. self.cover_image_background .. " _ " .. self.cover_image_format
return self.cover_image_cache_path .. self.cover_image_cache_prefix .. md5 ( key ) .. " . " .. getExtension ( self.cover_image_path )
end
function CoverImage : emptyCache ( )
for entry in lfs.dir ( self.cover_image_cache_path ) do
if entry ~= " . " and entry ~= " .. " then
local file = self.cover_image_cache_path .. entry
if entry : sub ( 1 , self.cover_image_cache_prefix : len ( ) ) == self.cover_image_cache_prefix
and lfs.attributes ( file , " mode " ) == " file " then
os.remove ( file )
end
end
end
end
function CoverImage : getCacheFiles ( cache_path , cache_prefix )
local cache_count = 0
local cache_size_KiB = 0
local files = { }
for entry in lfs.dir ( self.cover_image_cache_path ) do
if entry ~= " . " and entry ~= " .. " then
local file = cache_path .. entry
if entry : sub ( 1 , self.cover_image_cache_prefix : len ( ) ) == cache_prefix
and lfs.attributes ( file , " mode " ) == " file " then
cache_count = cache_count + 1
files [ cache_count ] = {
name = file ,
size = math.floor ( ( lfs.attributes ( file ) . size + 1023 ) / 1024 ) , -- round up to KiB
mod = lfs.attributes ( file ) . modification ,
}
cache_size_KiB = cache_size_KiB + files [ cache_count ] . size -- size in KiB
end
end
end
logger.dbg ( " CoverImage: start - cache size: " .. cache_size_KiB .. " KiB, cached files: " .. cache_count )
return cache_count , cache_size_KiB , files
end
function CoverImage : cleanCache ( )
if not self : isCacheEnabled ( ) then
self : emptyCache ( )
return
end
local cache_count , cache_size_KiB , files = self : getCacheFiles ( self.cover_image_cache_path , self.cover_image_cache_prefix )
-- delete the oldest files first
table.sort ( files , function ( a , b ) return a.mod < b.mod end )
local index = 1
while ( cache_count > self.cover_image_cache_maxfiles and self.cover_image_cache_maxfiles ~= 0 )
or ( cache_size_KiB > self.cover_image_cache_maxsize * 1024 and self.cover_image_cache_maxsize ~= 0 )
and index <= # files do
os.remove ( files [ index ] . name )
cache_count = cache_count - 1
cache_size_KiB = cache_size_KiB - files [ index ] . size
index = index + 1
end
logger.dbg ( " CoverImage: clean - cache size: " .. cache_size_KiB .. " KiB, cached files: " .. cache_count )
end
function CoverImage : isCacheEnabled ( path )
if not path then
path = self.cover_image_cache_path
end
return self.cover_image_cache_maxfiles >= 0 and self.cover_image_cache_maxsize >= 0
and lfs.attributes ( path , " mode " ) == " directory " and isPathAllowed ( path )
end
-- callback for choosePathFile()
function CoverImage : migrateCache ( old_path , new_path )
if old_path == new_path or not self : isCacheEnabled ( new_path ) then
return
end
for entry in lfs.dir ( old_path ) do
if entry ~= " . " and entry ~= " .. " then
local old_file = old_path .. entry
if lfs.attributes ( old_file , " mode " ) == " file " and entry : sub ( 1 , self.cover_image_cache_prefix : len ( ) ) == self.cover_image_cache_prefix then
local old_access_time = lfs.attributes ( old_file , " access " )
local new_file = new_path .. entry
os.rename ( old_file , new_file )
lfs.touch ( new_file , old_access_time ) -- restore original time
end
end
end
end
-- callback for choosePathFile()
function CoverImage : migrateCover ( old_file , new_file )
if old_file ~= new_file then
os.rename ( old_file , new_file )
end
end
--[[--
chooses a path or ( an existing ) file
@ touchmenu_instance for updating of the menu
@ string key is the G_reader_setting key which is used and changed
@ boolean folder_only just selects a path , no file handling
@ boolean new_file allows to enter a new filename , or use just an existing file
@ function migrate ( a , b ) callback to a function to mangle old folder / file with new folder / file .
Can be used for migrating the contents of the old path to the new one
] ]
function CoverImage : choosePathFile ( touchmenu_instance , key , folder_only , new_file , migrate )
local old_path , dummy = util.splitFilePathName ( self [ key ] )
UIManager : show ( PathChooser : new {
select_directory = folder_only or new_file ,
select_file = not folder_only ,
height = Screen : getHeight ( ) ,
path = old_path ,
onConfirm = function ( dir_path )
local mode = lfs.attributes ( dir_path , " mode " )
if folder_only then -- just select a folder
if not dir_path : find ( " /$ " ) then
dir_path = dir_path .. " / "
end
if migrate then
migrate ( self , self [ key ] , dir_path )
end
self [ key ] = dir_path
G_reader_settings : saveSetting ( key , dir_path )
if touchmenu_instance then
touchmenu_instance : updateItems ( )
end
elseif new_file and mode == " directory " then -- new filename should be entered or a file could be selected
local file_input
file_input = InputDialog : new {
title = _ ( " Append filename " ) ,
input = dir_path .. " / " ,
buttons = { {
{
text = _ ( " Cancel " ) ,
callback = function ( )
UIManager : close ( file_input )
end ,
} ,
{
text = _ ( " Save " ) ,
callback = function ( )
local file = file_input : getInputText ( )
if migrate and self [ key ] and self [ key ] ~= " " then
migrate ( self , self [ key ] , file )
end
self [ key ] = file
G_reader_settings : saveSetting ( key , file )
if touchmenu_instance then
touchmenu_instance : updateItems ( )
end
UIManager : close ( file_input )
end ,
} ,
} } ,
}
UIManager : show ( file_input )
file_input : onShowKeyboard ( )
elseif mode == " file " then -- just select an existing file
if migrate then
migrate ( self , self [ key ] , dir_path )
end
self [ key ] = dir_path
G_reader_settings : saveSetting ( key , dir_path )
if touchmenu_instance then
touchmenu_instance : updateItems ( )
end
end
end ,
} )
end
--[[--
Update a specific G_reader_setting ' s value via a Spinner
@ touchmenu_instance used for updating the menu
@ string setting is the G_reader_setting key which is used and changed
@ string title shown in the spinner
@ int min minimum value of the spinner
@ int max maximum value of the spinner
@ int default default value of the spinner
@ function callback to call , when spinner changed the value
] ]
function CoverImage : sizeSpinner ( touchmenu_instance , setting , title , min , max , default , callback )
local SpinWidget = require ( " ui/widget/spinwidget " )
local old_val = self [ setting ]
UIManager : show ( SpinWidget : new {
width = math.floor ( Device.screen : getWidth ( ) * 0.6 ) ,
value = old_val ,
value_min = min ,
value_max = max ,
default_value = default ,
title_text = title ,
ok_text = _ ( " Set " ) ,
callback = function ( spin )
if self : coverEnabled ( ) and spin.value ~= old_val then
self [ setting ] = spin.value
G_reader_settings : saveSetting ( setting , self [ setting ] )
if callback then
callback ( self )
end
end
if touchmenu_instance then touchmenu_instance : updateItems ( ) end
end
} )
end
-------------- menus and longer texts -----------
local about_text = _ ( [ [
local about_text = _ ( [ [
This plugin saves the current book cover to a file . That file can be used as a screensaver on certain Android devices , such as Tolinos .
This plugin saves a book cover to a file . That file can then be used as a screensaver on certain devices .
If enabled , the cover image of the current file is stored in the set path on book opening . Books can be excluded if desired .
If enabled , the cover image of the actual file is stored in the selected screensaver file . Books can be excluded if desired .
If disabled, the cover file will be delet ed.
If fallback is activated , the fallback file will be copied to the screensaver file on book closing .
If fallback is enabl ed, the fallback file will be copied to the screensaver file on book closing .
If the filename is empty or the file doesn ' t exist, the cover file will be deleted and the system screensaver will be used instead.
If the filename is empty or the file doesn ' t exist, the cover file will be deleted .
If the fallback image isn ' t activated, the screensaver image will stay in place after closing a book.]])
If fallback is disabled , the screensaver image will stay in place after closing a book . ] ] )
local set_image_text = _ ( [ [
You can either choose an existing file :
- Select a file
or specify a new file :
- First select a directory
- Then add the name of the new file
or delete the path :
- First select a directory
- Clear the name of the file ] ] )
-- menu entry: Cache settings
function CoverImage : menu_entry_cache ( )
return {
text = _ ( " Cache settings " ) ,
checked_func = function ( )
return self : isCacheEnabled ( )
end ,
sub_item_table = {
{
text_func = function ( )
local number
if self.cover_image_cache_maxfiles > 0 then
number = self.cover_image_cache_maxfiles
elseif self.cover_image_cache_maxfiles == 0 then
number = _ ( " unlimited " )
else
number = _ ( " off " )
end
return T ( _ ( " Maximum number of cached covers (%1) " ) , number )
end ,
help_text = _ ( " If set to zero the number of cache files is unlimited. \n If set to -1 the cache is disabled. " ) ,
checked_func = function ( )
return self.cover_image_cache_maxfiles >= 0
end ,
callback = function ( touchmenu_instance )
self : sizeSpinner ( touchmenu_instance , " cover_image_cache_maxfiles " , _ ( " Number of covers " ) , - 1 , 100 , 36 , self.cleanCache )
end ,
} ,
{
text_func = function ( )
local number
if self.cover_image_cache_maxsize > 0 then
number = self.cover_image_cache_maxsize
elseif self.cover_image_cache_maxsize == 0 then
number = _ ( " unlimited " )
else
number = _ ( " off " )
end
return T ( _ ( " Maximum size of cached covers (%1MiB) " ) , number )
end ,
help_text = _ ( " If set to zero the cache size is unlimited. \n If set to -1 the cache is disabled. " ) ,
checked_func = function ( )
return self.cover_image_cache_maxsize >= 0
end ,
callback = function ( touchmenu_instance )
self : sizeSpinner ( touchmenu_instance , " cover_image_cache_maxsize " , _ ( " Cache size " ) , - 1 , 100 , 5 , self.cleanCache )
end ,
} ,
self : menu_entry_set_path ( " cover_image_cache_path " , _ ( " Cover cache folder " ) , _ ( " Current cache path: \n %1 " ) ,
( " Select a cache folder. The contents of the old folder will be migrated. " ) , default_cache_path , true , false , self.migrateCache ) ,
{
text = _ ( " Clear cached covers " ) ,
help_text_func = function ( )
local cache_count , cache_size_KiB
= self : getCacheFiles ( self.cover_image_cache_path , self.cover_image_cache_prefix )
return T ( _ ( " The cache contains %1 files and uses %2 MiB. " ) , cache_count , math.floor ( ( cache_size_KiB + 1023 ) / 1024 ) )
end ,
callback = function ( )
UIManager : show ( ConfirmBox : new {
text = _ ( " Cear the cover image cache? " ) ,
ok_text = _ ( " Clear " ) ,
ok_callback = function ( )
self : emptyCache ( )
end ,
} )
end ,
keep_menu_open = true ,
} ,
} ,
}
end
--[[--
Menu entry for setting an specific G_reader_setting key for a path / file
@ string key is the G_reader_setting key which is used and changed
@ string title shown in the menu
@ string help shown in the menu
@ string info shown in the menu ( if containing % 1 , the value of the key is shown )
@ string the default value
@ bool folder_only sets if only folders can be selected
@ bool new_file sets if a new filename can be entered
@ function migrate a callback for example moving the folder contents
] ]
function CoverImage : menu_entry_set_path ( key , title , help , info , default , folder_only , new_file , migrate )
return {
text = title ,
help_text_func = function ( )
local text = self [ key ]
text = text ~= " " and text or _ ( " not set " )
return T ( help , text )
end ,
checked_func = function ( )
return isFileOk ( self [ key ] ) or ( isPathAllowed ( self [ key ] ) and folder_only )
end ,
callback = function ( touchmenu_instance )
UIManager : show ( ConfirmBox : new {
text = info ,
ok_callback = function ( )
self : choosePathFile ( touchmenu_instance , key , folder_only , new_file , migrate )
end ,
other_buttons = { {
{
text = _ ( " Default " ) ,
callback = function ( )
if migrate then
migrate ( self , self [ key ] , default )
end
self [ key ] = default
G_reader_settings : saveSetting ( key , default )
if touchmenu_instance then
touchmenu_instance : updateItems ( )
end
end
}
} } ,
} )
end ,
}
end
function CoverImage : menu_entry_format ( title , format )
return {
text = title ,
checked_func = function ( )
return self.cover_image_format == format
end ,
callback = function ( )
local old_cover_image_format = self.cover_image_format
self.cover_image_format = format
G_reader_settings : saveSetting ( " cover_image_format " , format )
if self : coverEnabld ( ) and old_cover_image_format ~= format then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
}
end
function CoverImage : menu_entry_background ( color )
return {
text = _ ( " Fit to screen, " .. color .. " background " ) ,
checked_func = function ( )
return self.cover_image_background == color
end ,
callback = function ( )
local old_background = self.cover_image_background
self.cover_image_background = color
G_reader_settings : saveSetting ( " cover_image_background " , self.cover_image_background )
if self : coverEnabled ( ) and old_background ~= self.cover_image_background then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
}
end
-- menu entry: scale, background, format
function CoverImage : menu_entry_sbf ( )
return {
text = _ ( " Size, background and format " ) ,
enabled_func = function ( )
return self : coverEnabled ( )
end ,
sub_item_table = {
{
text_func = function ( )
return T ( _ ( " Aspect ratio stretch threshold (%1) " ) ,
self.cover_image_stretch_limit ~= 0 and self.cover_image_stretch_limit .. " % " or " off " )
end ,
keep_menu_open = true ,
help_text_func = function ( )
return T ( _ ( " If the image and the screen have a similar aspect ratio (±%1%), stretch the image instead of keeping its aspect ratio. " ) , self.cover_image_stretch_limit )
end ,
callback = function ( touchmenu_instance )
local function createCover ( )
self : createCoverImage ( self.ui . doc_settings )
end
self : sizeSpinner ( touchmenu_instance , " cover_image_stretch_limit " , _ ( " Set strech threshold " ) , 0 , 20 , 8 , createCover )
end ,
} ,
self : menu_entry_background ( " black " ) ,
self : menu_entry_background ( " white " ) ,
self : menu_entry_background ( " gray " ) ,
{
text = _ ( " Original image " ) ,
checked_func = function ( )
return self.cover_image_background == " none "
end ,
callback = function ( )
local old_background = self.cover_image_background
self.cover_image_background = " none "
G_reader_settings : saveSetting ( " cover_image_background " , self.cover_image_background )
if self : coverEnabled ( ) and old_background ~= self.cover_image_background then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
separator = true ,
} ,
-- menu entries: File format
{
text = _ ( " File format derived from filename " ) ,
help_text = _ ( " If the file format is not supported, then JPG will be used. " ) ,
checked_func = function ( )
return self.cover_image_format == " auto "
end ,
callback = function ( )
local old_cover_image_format = self.cover_image_format
self.cover_image_format = " auto "
G_reader_settings : saveSetting ( " cover_image_format " , self.cover_image_format )
if self : coverEnabled ( ) and old_cover_image_format ~= self.cover_image_format then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
self : menu_entry_format ( _ ( " JPG file format " ) , " jpg " ) ,
self : menu_entry_format ( _ ( " PNG file format " ) , " png " ) ,
self : menu_entry_format ( _ ( " BMP file format " ) , " bmp " ) ,
} ,
}
end
-- CoverImage main menu
function CoverImage : addToMainMenu ( menu_items )
function CoverImage : addToMainMenu ( menu_items )
menu_items.coverimage = {
menu_items.coverimage = {
sorting_hint = " screen " ,
sorting_hint = " screen " ,
text = _ ( " Cover image " ) ,
text = _ ( " Cover image " ) ,
checked_func = function ( )
checked_func = function ( )
return self.enabled or self.fallback
return self : coverEnabled ( ) or self : fallbackEnabled ( )
end ,
end ,
sub_item_table = {
sub_item_table = {
-- menu entry: about cover image
-- menu entry: about cover image
{
{
text = _ ( " About cover image " ) ,
text = _ ( " About cover image " ) ,
keep_menu_open = true ,
callback = function ( )
callback = function ( )
UIManager : show ( InfoMessage : new {
UIManager : show ( InfoMessage : new {
text = about_text ,
text = about_text ,
} )
} )
end ,
end ,
keep_menu_open = true ,
separator = true ,
separator = true ,
} ,
} ,
-- menu entry: filename dialog
-- menu entry: filename dialog
{
self : menu_entry_set_path ( " cover_image_path " , _ ( " Set image path " ) , _ ( " Current Cover image path: \n %1 " ) , set_image_text ,
text = _ ( " Set image path " ) ,
Device : getDefaultCoverPath ( ) , false , true , self.migrateCover ) ,
checked_func = function ( )
return self.cover_image_path ~= " " and pathOk ( self.cover_image_path )
end ,
help_text = _ ( " The cover of the current book will be stored in this file. " ) ,
keep_menu_open = true ,
callback = function ( menu )
local InputDialog = require ( " ui/widget/inputdialog " )
local sample_input
sample_input = InputDialog : new {
title = _ ( " Screensaver image filename " ) ,
input = self.cover_image_path ,
input_type = " string " ,
description = _ ( " You can enter the filename of the cover image here. " ) ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
callback = function ( )
UIManager : close ( sample_input )
end ,
} ,
{
text = _ ( " Save " ) ,
is_enter_default = true ,
callback = function ( )
local new_cover_image_path = sample_input : getInputText ( )
if new_cover_image_path ~= self.cover_image_path then
self : cleanUpImage ( ) -- with old filename
self.cover_image_path = new_cover_image_path -- update filename
G_reader_settings : saveSetting ( " cover_image_path " , self.cover_image_path )
local is_path_ok , is_path_ok_message = pathOk ( self.cover_image_path )
if self.cover_image_path ~= " " and is_path_ok then
self : createCoverImage ( self.ui . doc_settings ) -- with new filename
else
self.enabled = false
UIManager : show ( InfoMessage : new {
text = is_path_ok_message ,
show_icon = true ,
} )
end
end
self.cover_image_extension = getExtension ( self.cover_image_path )
UIManager : close ( sample_input )
menu : updateItems ( )
end ,
} ,
}
} ,
}
UIManager : show ( sample_input )
sample_input : onShowKeyboard ( )
end ,
} ,
-- menu entry: enable
-- menu entry: enable
{
{
text = _ ( " Save cover image " ) ,
text = _ ( " Save cover image " ) ,
checked_func = function ( )
checked_func = function ( )
return self : _enabled ( ) and pathOk ( self.cover_image_path )
return self : coverEnabled ( )
end ,
end ,
enabled_func = function ( )
enabled_func = function ( )
return self.cover_image_path ~= " " and pathOk ( self.cover_image_path )
return self.cover_image_path ~= " " and isFileOk ( self.cover_image_path )
end ,
end ,
callback = function ( )
callback = function ( )
if self.cover_image_path ~= " " then
if self.cover_image_path ~= " " then
self.enabled = not self.enabled
self.cover = not self.cover
G_reader_settings : saveSetting ( " cover_image_enabled " , self.enabled )
self.cover = self.cover and self : coverEnabled ( )
if self.enabled then
G_reader_settings : saveSetting ( " cover_image_enabled " , self.cover )
if self : coverEnabled ( ) then
self : createCoverImage ( self.ui . doc_settings )
self : createCoverImage ( self.ui . doc_settings )
else
else
self : cleanUpImage ( )
self : cleanUpImage ( )
@ -264,164 +706,8 @@ function CoverImage:addToMainMenu(menu_items)
end
end
end ,
end ,
} ,
} ,
-- menu entry: scale book cover
-- menu entry: scale, background, format
{
self : menu_entry_sbf ( ) ,
text = _ ( " Size, background and format " ) ,
enabled_func = function ( )
return self.enabled
end ,
sub_item_table = {
{
text_func = function ( )
return T ( _ ( " Aspect ratio stretch threshold (%1%) " ) , self.cover_image_stretch_limit )
end ,
help_text_func = function ( )
return T ( _ ( " If the image and the screen have a similar aspect ratio (±%1%), stretch the image instead of keeping its aspect ratio. " ) , self.cover_image_stretch_limit )
end ,
keep_menu_open = true ,
callback = function ( touchmenu_instance )
local old_stretch_limit = self.cover_image_stretch_limit
local SpinWidget = require ( " ui/widget/spinwidget " )
local size_spinner = SpinWidget : new {
width = math.floor ( Device.screen : getWidth ( ) * 0.6 ) ,
value = old_stretch_limit ,
value_min = 0 ,
value_max = 25 ,
default_value = 5 ,
title_text = _ ( " Set stretch threshold " ) ,
ok_text = _ ( " Set " ) ,
callback = function ( spin )
if self.enabled and spin.value ~= old_stretch_limit then
self.cover_image_stretch_limit = spin.value
G_reader_settings : saveSetting ( " cover_image_stretch_limit " , self.cover_image_stretch_limit )
self : createCoverImage ( self.ui . doc_settings )
end
if touchmenu_instance then touchmenu_instance : updateItems ( ) end
end
}
UIManager : show ( size_spinner )
if self.enabled and old_stretch_limit ~= self.cover_image_stretch_limit then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " Fit to screen, black background " ) ,
checked_func = function ( )
return self.cover_image_background == " black "
end ,
callback = function ( )
local old_background = self.cover_image_background
self.cover_image_background = " black "
G_reader_settings : saveSetting ( " cover_image_background " , self.cover_image_background )
if self.enabled and old_background ~= self.cover_image_background then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " Fit to screen, white background " ) ,
checked_func = function ( )
return self.cover_image_background == " white "
end ,
callback = function ( )
local old_background = self.cover_image_background
self.cover_image_background = " white "
G_reader_settings : saveSetting ( " cover_image_background " , self.cover_image_background )
if self.enabled and old_background ~= self.cover_image_background then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " Fit to screen, gray background " ) ,
checked_func = function ( )
return self.cover_image_background == " gray "
end ,
callback = function ( )
local old_background = self.cover_image_background
self.cover_image_background = " gray "
G_reader_settings : saveSetting ( " cover_image_background " , self.cover_image_background )
if self.enabled and old_background ~= self.cover_image_background then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " Original image " ) ,
checked_func = function ( )
return self.cover_image_background == " none "
end ,
callback = function ( )
local old_background = self.cover_image_background
self.cover_image_background = " none "
G_reader_settings : saveSetting ( " cover_image_background " , self.cover_image_background )
if self.enabled and old_background ~= self.cover_image_background then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
separator = true ,
} ,
-- menu entries: File format
{
text = _ ( " File format derived from filename " ) ,
help_text = _ ( " If the file format is not supported, then JPG will be used. " ) ,
checked_func = function ( )
return self.cover_image_format == " auto "
end ,
callback = function ( )
local old_cover_image_format = self.cover_image_format
self.cover_image_format = " auto "
G_reader_settings : saveSetting ( " cover_image_format " , self.cover_image_format )
if self.enabled and old_cover_image_format ~= self.cover_image_format then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " JPG file format " ) ,
checked_func = function ( )
return self.cover_image_format == " jpg "
end ,
callback = function ( )
local old_cover_image_format = self.cover_image_format
self.cover_image_format = " jpg "
G_reader_settings : saveSetting ( " cover_image_format " , self.cover_image_format )
if self.enabled and old_cover_image_format ~= self.cover_image_format then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " PNG file format " ) ,
checked_func = function ( )
return self.cover_image_format == " png "
end ,
callback = function ( )
local old_cover_image_format = self.cover_image_format
self.cover_image_format = " png "
G_reader_settings : saveSetting ( " cover_image_format " , self.cover_image_format )
if self.enabled and old_cover_image_format ~= self.cover_image_format then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
{
text = _ ( " BMP file format " ) ,
checked_func = function ( )
return self.cover_image_format == " bmp "
end ,
callback = function ( )
local old_cover_image_format = self.cover_image_format
self.cover_image_format = " bmp "
G_reader_settings : saveSetting ( " cover_image_format " , self.cover_image_format )
if self.enabled and old_cover_image_format ~= self.cover_image_format then
self : createCoverImage ( self.ui . doc_settings )
end
end ,
} ,
}
} ,
-- menu entry: exclude this cover
-- menu entry: exclude this cover
{
{
text = _ ( " Exclude this book cover " ) ,
text = _ ( " Exclude this book cover " ) ,
@ -441,66 +727,29 @@ function CoverImage:addToMainMenu(menu_items)
separator = true ,
separator = true ,
} ,
} ,
-- menu entry: set fallback image
-- menu entry: set fallback image
{
self : menu_entry_set_path ( " cover_image_fallback_path " , _ ( " Set fallback path " ) ,
text = _ ( " Set fallback image path " ) ,
_ ( " The fallback image used on document close is: \n %1 " ) , _ ( " You can select a fallback image. " ) , default_fallback_path , false , false ) ,
checked_func = function ( )
return lfs.attributes ( self.cover_image_fallback_path , " mode " ) == " file "
end ,
keep_menu_open = true ,
callback = function ( menu )
local InputDialog = require ( " ui/widget/inputdialog " )
local sample_input
sample_input = InputDialog : new {
title = _ ( " Fallback image filename " ) ,
input = self.cover_image_fallback_path ,
input_type = " string " ,
description = _ ( " Leave this empty to remove the cover when the document is closed. " ) ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
callback = function ( )
UIManager : close ( sample_input )
end ,
} ,
{
text = _ ( " Save " ) ,
is_enter_default = true ,
callback = function ( )
self.cover_image_fallback_path = sample_input : getInputText ( )
G_reader_settings : saveSetting ( " cover_image_fallback_path " , self.cover_image_fallback_path )
if lfs.attributes ( self.cover_image_fallback_path , " mode " ) ~= " file "
and self.cover_image_fallback_path ~= " " then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " \" %1 \" \n is not a valid image file! \n A valid fallback image is required in Cover-Image " ) ,
self.cover_image_fallback_path ) ,
show_icon = true ,
timeout = 10 ,
} )
end
UIManager : close ( sample_input )
menu : updateItems ( )
end ,
} ,
}
} ,
}
UIManager : show ( sample_input )
sample_input : onShowKeyboard ( )
end ,
} ,
-- menu entry: fallback
-- menu entry: fallback
{
{
text = _ ( " Turn on fallback image " ) ,
text = _ ( " Turn on fallback image " ) ,
checked_func = function ( )
checked_func = function ( )
return self : _fallback ( )
return self : fallbackEnabled ( )
end ,
enabled_func = function ( )
return lfs.attributes ( self.cover_image_fallback_path , " mode " ) == " file "
end ,
end ,
callback = function ( )
callback = function ( )
self.fallback = not self.fallback
self.fallback = not self.fallback
self.fallback = self.fallback and self : fallbackEnabled ( )
G_reader_settings : saveSetting ( " cover_image_fallback " , self.fallback )
G_reader_settings : saveSetting ( " cover_image_fallback " , self.fallback )
if not self : coverEnabled ( ) then
self : cleanUpImage ( )
end
end ,
end ,
separator = true ,
separator = true ,
} ,
} ,
-- menu entry: Cache settings
self : menu_entry_cache ( ) ,
} ,
} ,
}
}
end
end