Profiles: more auto-exec triggers 2 (#12691)

This commit is contained in:
hius07
2024-10-31 20:26:21 +02:00
committed by GitHub
parent 8b710d183c
commit c2d1099976
6 changed files with 291 additions and 137 deletions

View File

@@ -33,16 +33,16 @@ local ReadHistory = require("readhistory")
local Screenshoter = require("ui/widget/screenshoter")
local TitleBar = require("ui/widget/titlebar")
local UIManager = require("ui/uimanager")
local ffiUtil = require("ffi/util")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local BaseUtil = require("ffi/util")
local util = require("util")
local _ = require("gettext")
local C_ = _.pgettext
local N_ = _.ngettext
local Screen = Device.screen
local T = BaseUtil.template
local T = ffiUtil.template
local FileManager = InputContainer:extend{
title = _("KOReader"),
@@ -106,14 +106,15 @@ function FileManager:onSetDimensions(dimen)
end
function FileManager:updateTitleBarPath(path)
path = path or self.file_chooser.path
local text = BD.directory(filemanagerutil.abbreviate(path))
if FileManagerShortcuts:hasFolderShortcut(path) then
if self.folder_shortcuts:hasFolderShortcut(path) then
text = "" .. text
end
self.title_bar:setSubTitle(text)
end
FileManager.onPathChanged = FileManager.updateTitleBarPath
function FileManager:setupLayout()
self.show_parent = self.show_parent or self
self.title_bar = TitleBar:new{
@@ -135,7 +136,6 @@ function FileManager:setupLayout()
right_icon_tap_callback = function() self:onShowPlusMenu() end,
right_icon_hold_callback = false, -- propagate long-press to dispatcher
}
self:updateTitleBarPath(self.root_path)
local file_chooser = FileChooser:new{
path = self.root_path,
@@ -158,11 +158,6 @@ function FileManager:setupLayout()
local file_manager = self
function file_chooser:onPathChanged(path)
file_manager:updateTitleBarPath(path)
return true
end
function file_chooser:onFileSelect(item)
if file_manager.selected_files then -- toggle selection
item.dim = not item.dim and true or nil
@@ -262,7 +257,7 @@ function FileManager:setupLayout()
self.book_props = nil -- in 'self' to provide access to it in CoverBrowser
local has_provider = DocumentRegistry:hasProvider(file)
local has_sidecar = DocSettings:hasSidecarFile(file)
local doc_settings_or_file
local doc_settings_or_file = file
if has_provider or has_sidecar then
self.book_props = file_manager.coverbrowser and file_manager.coverbrowser:getBookInfo(file)
if has_sidecar then
@@ -272,8 +267,6 @@ function FileManager:setupLayout()
self.book_props = FileManagerBookInfo.extendProps(props, file)
self.book_props.has_cover = true -- to enable "Book cover" button, we do not know if cover exists
end
else
doc_settings_or_file = file
end
table.insert(buttons, filemanagerutil.genStatusButtonsRow(doc_settings_or_file, close_dialog_refresh_callback))
table.insert(buttons, {}) -- separator
@@ -315,7 +308,7 @@ function FileManager:setupLayout()
})
end
else -- folder
local folder = BaseUtil.realpath(file)
local folder = ffiUtil.realpath(file)
table.insert(buttons, {
{
text = _("Set as HOME folder"),
@@ -431,6 +424,7 @@ function FileManager:init()
self:initGesListener()
self:handleEvent(Event:new("SetDimensions", self.dimen))
self:handleEvent(Event:new("PathChanged", self.file_chooser.path))
if FileManager.instance == nil then
logger.dbg("Spinning up new FileManager instance", tostring(self))
@@ -726,7 +720,7 @@ function FileManager:reinit(path, focused_file)
UIManager:flushSettings()
self.dimen = Screen:getSize()
-- backup the root path and path items
self.root_path = BaseUtil.realpath(path or self.file_chooser.path)
self.root_path = ffiUtil.realpath(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
@@ -826,7 +820,7 @@ function FileManager:openRandomFile(dir)
local random_file = filemanagerutil.getRandomFile(dir, match_func)
if random_file then
UIManager:show(MultiConfirmBox:new{
text = T(_("Do you want to open %1?"), BD.filename(BaseUtil.basename(random_file))),
text = T(_("Do you want to open %1?"), BD.filename(ffiUtil.basename(random_file))),
choice1_text = _("Open"),
choice1_callback = function()
local ReaderUI = require("apps/reader/readerui")
@@ -856,11 +850,11 @@ function FileManager:cutFile(file)
end
function FileManager:pasteFileFromClipboard(file)
local orig_file = BaseUtil.realpath(self.clipboard)
local orig_name = BaseUtil.basename(orig_file)
local dest_path = BaseUtil.realpath(file or self.file_chooser.path)
local orig_file = ffiUtil.realpath(self.clipboard)
local orig_name = ffiUtil.basename(orig_file)
local dest_path = ffiUtil.realpath(file or self.file_chooser.path)
dest_path = isFile(dest_path) and dest_path:match("(.*/)") or dest_path
local dest_file = BaseUtil.joinPath(dest_path, orig_name)
local dest_file = ffiUtil.joinPath(dest_path, orig_name)
if orig_file == dest_file or orig_file == dest_path then -- do not paste to itself
self.clipboard = nil
return
@@ -951,12 +945,12 @@ function FileManager:showCopyMoveSelectedFilesDialog(close_callback)
end
function FileManager:pasteSelectedFiles(overwrite)
local dest_path = BaseUtil.realpath(self.file_chooser.path)
local dest_path = ffiUtil.realpath(self.file_chooser.path)
local ok_files = {}
for orig_file in pairs(self.selected_files) do
local orig_name = BaseUtil.basename(orig_file)
local dest_file = BaseUtil.joinPath(dest_path, orig_name)
if BaseUtil.realpath(orig_file) == dest_file then -- do not paste to itself
local orig_name = ffiUtil.basename(orig_file)
local dest_file = ffiUtil.joinPath(dest_path, orig_name)
if ffiUtil.realpath(orig_file) == dest_file then -- do not paste to itself
self.selected_files[orig_file] = nil
else
local ok
@@ -1046,7 +1040,7 @@ function FileManager:createFolder()
end
function FileManager:showDeleteFileDialog(filepath, post_delete_callback, pre_delete_callback)
local file = BaseUtil.realpath(filepath)
local file = ffiUtil.realpath(filepath)
if file == nil then
UIManager:show(InfoMessage:new{
text = T(_("File not found:\n%1"), BD.filepath(filepath)),
@@ -1083,7 +1077,7 @@ function FileManager:deleteFile(file, is_file)
return true
end
else
local ok = BaseUtil.purgeDir(file)
local ok = ffiUtil.purgeDir(file)
if ok then
ReadHistory:folderDeleted(file) -- will delete sdr
ReadCollection:removeItemsByPath(file)
@@ -1099,7 +1093,7 @@ end
function FileManager:deleteSelectedFiles()
local ok_files = {}
for orig_file in pairs(self.selected_files) do
local file_abs_path = BaseUtil.realpath(orig_file)
local file_abs_path = ffiUtil.realpath(orig_file)
local ok = file_abs_path and os.remove(file_abs_path)
if ok then
DocSettings.updateLocation(file_abs_path) -- delete sdr
@@ -1129,7 +1123,7 @@ function FileManager:showRenameFileDialog(file, is_file)
local dialog
dialog = InputDialog:new{
title = is_file and _("Rename file") or _("Rename folder"),
input = BaseUtil.basename(file),
input = ffiUtil.basename(file),
buttons = {{
{
text = _("Cancel"),
@@ -1155,8 +1149,8 @@ function FileManager:showRenameFileDialog(file, is_file)
end
function FileManager:renameFile(file, basename, is_file)
if BaseUtil.basename(file) == basename then return end
local dest = BaseUtil.joinPath(BaseUtil.dirname(file), basename)
if ffiUtil.basename(file) == basename then return end
local dest = ffiUtil.joinPath(ffiUtil.dirname(file), basename)
local function doRenameFile()
if self:moveFile(file, dest) then
@@ -1220,7 +1214,7 @@ function FileManager:showFiles(path, focused_file, selected_files)
FileManager.instance:onClose()
end
path = BaseUtil.realpath(path or G_reader_settings:readSetting("lastdir") or filemanagerutil.getDefaultDir())
path = ffiUtil.realpath(path or G_reader_settings:readSetting("lastdir") or filemanagerutil.getDefaultDir())
G_reader_settings:saveSetting("lastdir", path)
self:setRotationMode()
local file_manager = FileManager:new{
@@ -1236,19 +1230,19 @@ end
--- A shortcut to execute mv.
-- @treturn boolean result of mv command
function FileManager:moveFile(from, to)
return BaseUtil.execute(self.mv_bin, from, to) == 0
return ffiUtil.execute(self.mv_bin, from, to) == 0
end
--- A shortcut to execute cp.
-- @treturn boolean result of cp command
function FileManager:copyFileFromTo(from, to)
return BaseUtil.execute(self.cp_bin, from, to) == 0
return ffiUtil.execute(self.cp_bin, from, to) == 0
end
--- A shortcut to execute cp recursively.
-- @treturn boolean result of cp command
function FileManager:copyRecursive(from, to)
return BaseUtil.execute(self.cp_bin, "-r", from, to ) == 0
return ffiUtil.execute(self.cp_bin, "-r", from, to ) == 0
end
function FileManager:onHome()
@@ -1341,9 +1335,9 @@ function FileManager:showSelectedFilesList()
local a_path, a_name = util.splitFilePathName(a.text)
local b_path, b_name = util.splitFilePathName(b.text)
if a_path == b_path then
return BaseUtil.strcoll(a_name, b_name)
return ffiUtil.strcoll(a_name, b_name)
end
return BaseUtil.strcoll(a_path, b_path)
return ffiUtil.strcoll(a_path, b_path)
end
table.sort(selected_files, sorting)

View File

@@ -3,7 +3,6 @@ local ButtonDialog = require("ui/widget/buttondialog")
local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device")
local DocSettings = require("docsettings")
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
@@ -70,12 +69,17 @@ function FileManagerCollection:onShowColl(collection_name)
title_bar_fm_style = true,
title_bar_left_icon = "appbar.menu",
onLeftButtonTap = function() self:showCollDialog() end,
onReturn = function()
self.coll_menu.close_callback()
self:onShowCollList()
end,
onMenuChoice = self.onMenuChoice,
onMenuHold = self.onMenuHold,
_manager = self,
_recreate_func = function() self:onShowColl(collection_name) end,
collection_name = collection_name,
}
table.insert(self.coll_menu.paths, true) -- enable onReturn button
self.coll_menu.close_callback = function()
self:refreshFileManager()
UIManager:close(self.coll_menu)
@@ -146,7 +150,7 @@ function FileManagerCollection:onMenuHold(item)
doc_settings_or_file = DocSettings:open(file)
if not self.book_props then
local props = doc_settings_or_file:readSetting("doc_props")
self.book_props = FileManagerBookInfo.extendProps(props, file)
self.book_props = self.ui.bookinfo.extendProps(props, file)
self.book_props.has_cover = true
end
else
@@ -285,14 +289,18 @@ end
-- collection list
function FileManagerCollection:onShowCollList(file_or_files, caller_callback, no_dialog)
self.selected_colections = nil
if file_or_files then -- select mode
if type(file_or_files) == "string" then -- checkmark collections containing the file
self.selected_colections = ReadCollection:getCollectionsWithFile(file_or_files)
else -- do not checkmark any
self.selected_colections = {}
function FileManagerCollection:onShowCollList(file_or_selected_collections, caller_callback, no_dialog)
local title_bar_left_icon
if file_or_selected_collections ~= nil then -- select mode
title_bar_left_icon = "check"
if type(file_or_selected_collections) == "string" then -- checkmark collections containing the file
self.selected_collections = ReadCollection:getCollectionsWithFile(file_or_selected_collections)
else
self.selected_collections = util.tableDeepCopy(file_or_selected_collections)
end
else
title_bar_left_icon = "appbar.menu"
self.selected_collections = nil
end
self.coll_list = Menu:new{
subtitle = "",
@@ -300,15 +308,15 @@ function FileManagerCollection:onShowCollList(file_or_files, caller_callback, no
is_borderless = true,
is_popout = false,
title_bar_fm_style = true,
title_bar_left_icon = file_or_files and "check" or "appbar.menu",
title_bar_left_icon = title_bar_left_icon,
onLeftButtonTap = function() self:showCollListDialog(caller_callback, no_dialog) end,
onMenuChoice = self.onCollListChoice,
onMenuHold = self.onCollListHold,
_manager = self,
_recreate_func = function() self:onShowCollList(file_or_files, caller_callback, no_dialog) end,
_recreate_func = function() self:onShowCollList(file_or_selected_collections, caller_callback, no_dialog) end,
}
self.coll_list.close_callback = function(force_close)
if force_close or self.selected_colections == nil then
if force_close or self.selected_collections == nil then
self:refreshFileManager()
UIManager:close(self.coll_list)
self.coll_list = nil
@@ -325,8 +333,8 @@ function FileManagerCollection:updateCollListItemTable(do_init, item_number)
item_table = {}
for name, coll in pairs(ReadCollection.coll) do
local mandatory
if self.selected_colections then
mandatory = self.selected_colections[name] and self.checkmark or " "
if self.selected_collections then
mandatory = self.selected_collections[name] and self.checkmark or " "
self.coll_list.items_mandatory_font_size = self.coll_list.font_size
else
mandatory = util.tableSize(coll)
@@ -346,12 +354,12 @@ function FileManagerCollection:updateCollListItemTable(do_init, item_number)
end
local title = T(_("Collections (%1)"), #item_table)
local subtitle
if self.selected_colections then
local selected_nb = util.tableSize(self.selected_colections)
subtitle = self.selected_colections and T(_("Selected: %1"), selected_nb)
if self.selected_collections then
local selected_nb = util.tableSize(self.selected_collections)
subtitle = self.selected_collections and T(_("Selected: %1"), selected_nb)
if do_init and selected_nb > 0 then -- show first collection containing the long-pressed book
for i, item in ipairs(item_table) do
if self.selected_colections[item.name] then
if self.selected_collections[item.name] then
item_number = i
break
end
@@ -362,13 +370,13 @@ function FileManagerCollection:updateCollListItemTable(do_init, item_number)
end
function FileManagerCollection:onCollListChoice(item)
if self._manager.selected_colections then
if self._manager.selected_collections then
if item.mandatory == self._manager.checkmark then
self.item_table[item.idx].mandatory = " "
self._manager.selected_colections[item.name] = nil
self._manager.selected_collections[item.name] = nil
else
self.item_table[item.idx].mandatory = self._manager.checkmark
self._manager.selected_colections[item.name] = true
self._manager.selected_collections[item.name] = true
end
self._manager:updateCollListItemTable()
else
@@ -378,7 +386,7 @@ end
function FileManagerCollection:onCollListHold(item)
if item.name == ReadCollection.default_collection_name -- Favorites non-editable
or self._manager.selected_colections then -- select mode
or self._manager.selected_collections then -- select mode
return
end
@@ -412,7 +420,7 @@ end
function FileManagerCollection:showCollListDialog(caller_callback, no_dialog)
if no_dialog then
caller_callback()
caller_callback(self.selected_collections)
self.coll_list.close_callback(true)
return
end
@@ -427,7 +435,7 @@ function FileManagerCollection:showCollListDialog(caller_callback, no_dialog)
end,
},
}
if self.selected_colections then -- select mode
if self.selected_collections then -- select mode
buttons = {
new_collection_button,
{}, -- separator
@@ -436,8 +444,8 @@ function FileManagerCollection:showCollListDialog(caller_callback, no_dialog)
text = _("Deselect all"),
callback = function()
UIManager:close(button_dialog)
for name in pairs(self.selected_colections) do
self.selected_colections[name] = nil
for name in pairs(self.selected_collections) do
self.selected_collections[name] = nil
end
self:updateCollListItemTable(true)
end,
@@ -447,7 +455,7 @@ function FileManagerCollection:showCollListDialog(caller_callback, no_dialog)
callback = function()
UIManager:close(button_dialog)
for name in pairs(ReadCollection.coll) do
self.selected_colections[name] = true
self.selected_collections[name] = true
end
self:updateCollListItemTable(true)
end,
@@ -458,7 +466,7 @@ function FileManagerCollection:showCollListDialog(caller_callback, no_dialog)
text = _("Apply selection"),
callback = function()
UIManager:close(button_dialog)
caller_callback()
caller_callback(self.selected_collections)
self.coll_list.close_callback(true)
end,
},
@@ -523,8 +531,8 @@ function FileManagerCollection:addCollection()
local editCallback = function(name)
ReadCollection:addCollection(name)
local mandatory
if self.selected_colections then
self.selected_colections[name] = true
if self.selected_collections then
self.selected_collections[name] = true
mandatory = self.checkmark
else
mandatory = 0
@@ -579,6 +587,7 @@ end
-- external
function FileManagerCollection:genAddToCollectionButton(file_or_files, caller_pre_callback, caller_post_callback, button_disabled)
local is_single_file = type(file_or_files) == "string"
return {
text = _("Collections…"),
enabled = not button_disabled,
@@ -586,17 +595,18 @@ function FileManagerCollection:genAddToCollectionButton(file_or_files, caller_pr
if caller_pre_callback then
caller_pre_callback()
end
local caller_callback = function()
if type(file_or_files) == "string" then
ReadCollection:addRemoveItemMultiple(file_or_files, self.selected_colections)
local caller_callback = function(selected_collections)
if is_single_file then
ReadCollection:addRemoveItemMultiple(file_or_files, selected_collections)
else -- selected files
ReadCollection:addItemsMultiple(file_or_files, self.selected_colections)
ReadCollection:addItemsMultiple(file_or_files, selected_collections)
end
if caller_post_callback then
caller_post_callback()
end
end
self:onShowCollList(file_or_files, caller_callback)
-- if selected files, do not checkmark any collection on start
self:onShowCollList(is_single_file and file_or_files or {}, caller_callback)
end,
}
end

View File

@@ -3,7 +3,6 @@ local ButtonDialog = require("ui/widget/buttondialog")
local CheckButton = require("ui/widget/checkbutton")
local ConfirmBox = require("ui/widget/confirmbox")
local DocSettings = require("docsettings")
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
local ReadCollection = require("readcollection")
@@ -85,7 +84,7 @@ function FileManagerHistory:updateItemTable()
local subtitle = ""
if self.search_string then
subtitle = T(_("Search results (%1)"), #item_table)
elseif self.selected_colections then
elseif self.selected_collections then
subtitle = T(_("Filtered by collections (%1)"), #item_table)
elseif self.filter ~= "all" then
subtitle = T(_("Status: %1 (%2)"), filter_text[self.filter]:lower(), #item_table)
@@ -109,8 +108,8 @@ function FileManagerHistory:isItemMatch(item)
end
end
end
if self.selected_colections then
for name in pairs(self.selected_colections) do
if self.selected_collections then
for name in pairs(self.selected_collections) do
if not ReadCollection:isFileInCollection(item.file, name) then
return false
end
@@ -173,7 +172,7 @@ function FileManagerHistory:onMenuHold(item)
doc_settings_or_file = DocSettings:open(file)
if not self.book_props then
local props = doc_settings_or_file:readSetting("doc_props")
self.book_props = FileManagerBookInfo.extendProps(props, file)
self.book_props = self.ui.bookinfo.extendProps(props, file)
self.book_props.has_cover = true
end
else
@@ -203,7 +202,7 @@ function FileManagerHistory:onMenuHold(item)
UIManager:close(self.histfile_dialog)
-- The item's idx field is tied to the current *view*, so we can only pass it as-is when there's no filtering *at all* involved.
local index = item.idx
if self._manager.search_string or self._manager.selected_colections or self._manager.filter ~= "all" then
if self._manager.search_string or self._manager.selected_collections or self._manager.filter ~= "all" then
index = nil
end
require("readhistory"):removeItem(item, index)
@@ -252,7 +251,7 @@ function FileManagerHistory:onShowHist(search_info)
self.case_sensitive = search_info.case_sensitive
else
self.search_string = nil
self.selected_colections = nil
self.selected_collections = nil
end
self.filter = G_reader_settings:readSetting("history_filter", "all")
self.is_frozen = G_reader_settings:isTrue("history_freeze_finished_books")
@@ -291,7 +290,7 @@ function FileManagerHistory:showHistDialog()
self.filter = filter
if filter == "all" then -- reset all filters
self.search_string = nil
self.selected_colections = nil
self.selected_collections = nil
end
self:updateItemTable()
end,
@@ -312,11 +311,11 @@ function FileManagerHistory:showHistDialog()
text = _("Filter by collections"),
callback = function()
UIManager:close(hist_dialog)
local caller_callback = function()
self.selected_colections = self.ui.collections.selected_colections
local caller_callback = function(selected_collections)
self.selected_collections = selected_collections
self:updateItemTable()
end
self.ui.collections:onShowCollList({}, caller_callback, true) -- do not select any, no dialog to apply
self.ui.collections:onShowCollList(self.selected_collections or {}, caller_callback, true) -- no dialog to apply
end,
},
})

View File

@@ -3,6 +3,7 @@ local datetime = require("datetime")
local Device = require("device")
local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry")
local Event = require("ui/event")
local FileManagerShortcuts = require("apps/filemanager/filemanagershortcuts")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
local Menu = require("ui/widget/menu")
@@ -505,11 +506,11 @@ function FileChooser:refreshPath()
if self.focused_path then
itemmatch = {path = self.focused_path}
-- We use focused_path only once, but remember it
-- for CoverBrower to re-apply it on startup if needed
-- for CoverBrowser to re-apply it on startup if needed
self.prev_focused_path = self.focused_path
self.focused_path = nil
end
local subtitle = BD.directory(filemanagerutil.abbreviate(self.path))
local subtitle = self.filemanager == nil and BD.directory(filemanagerutil.abbreviate(self.path))
self:switchItemTable(nil, self:genItemTableFromPath(self.path), self.path_items[self.path], itemmatch, subtitle)
end
@@ -535,7 +536,9 @@ function FileChooser:changeToPath(path, focused_path)
end
self:refreshPath()
self:onPathChanged(path)
if self.filemanager then
self.filemanager:handleEvent(Event:new("PathChanged", path))
end
end
function FileChooser:goHome()
@@ -609,10 +612,6 @@ function FileChooser:onFileHold(item)
return true
end
function FileChooser:onPathChanged(path)
return true
end
-- Used in ReaderStatus:onOpenNextDocumentInFolder().
function FileChooser:getNextFile(curr_file)
local show_finished = FileChooser.show_finished

View File

@@ -2,13 +2,13 @@
This module contains miscellaneous helper functions for the KOReader frontend.
]]
local BaseUtil = require("ffi/util")
local Utf8Proc = require("ffi/utf8proc")
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local md5 = require("ffi/sha2").md5
local _ = require("gettext")
local C_ = _.pgettext
local T = BaseUtil.template
local T = ffiUtil.template
local lshift = bit.lshift
local rshift = bit.rshift
@@ -489,7 +489,7 @@ function util.splitToChars(text)
local hi_surrogate
local hi_surrogate_uchar
for uchar in text:gmatch(util.UTF8_CHAR_PATTERN) do
charcode = BaseUtil.utf8charcode(uchar)
charcode = ffiUtil.utf8charcode(uchar)
-- (not sure why we need this prevcharcode check; we could get
-- charcode=nil with invalid UTF-8, but should we then really
-- ignore the following charcode ?)
@@ -530,7 +530,7 @@ function util.isCJKChar(c)
if #c < 3 then
return false
end
local code = BaseUtil.utf8charcode(c)
local code = ffiUtil.utf8charcode(c)
-- The weird bracketing is intentional -- we use the lowest possible
-- codepoint as a shortcut so if the codepoint is below U+1100 we
-- immediately return false.
@@ -894,7 +894,7 @@ function util.removePath(path)
return nil, "Encountered a component that isn't a directory" .. " (removing `" .. component .. "` for `" .. path .. "`)"
end
local parent = BaseUtil.dirname(component)
local parent = ffiUtil.dirname(component)
component = parent
until parent == "." or parent == "/"
return true, nil
@@ -988,7 +988,7 @@ function util.getSafeFilename(str, path, limit, limit_ext)
limit_ext = limit_ext or 10
-- Always assume the worst on Android (#7837)
if path and not BaseUtil.isAndroid() then
if path and not ffiUtil.isAndroid() then
local file_system = util.getFilesystemType(path)
if file_system ~= "vfat" and file_system ~= "fuse.fsp" then
replaceFunc = replaceSlashChar
@@ -1140,11 +1140,11 @@ function util.writeToFile(data, filepath, force_flush, lua_dofile_ready, directo
end
file:write(data)
if force_flush then
BaseUtil.fsyncOpenedFile(file)
ffiUtil.fsyncOpenedFile(file)
end
file:close()
if directory_updated then
BaseUtil.fsyncDirectory(filepath)
ffiUtil.fsyncDirectory(filepath)
end
return true
end
@@ -1512,6 +1512,7 @@ end
-- @treturn table Text char list
-- @treturn table Search string char list
function util.stringSearch(txt, str, case_sensitive, start_pos)
start_pos = start_pos or 1
if not case_sensitive then
str = Utf8Proc.lowercase(util.fixUtf8(str, "?"))
end

View File

@@ -161,10 +161,11 @@ function Profiles:getSubMenuItems()
},
self:genAutoExecMenuItem(_("on KOReader start"), "Start", k),
self:genAutoExecMenuItem(_("on wake-up"), "Resume", k),
self:genAutoExecMenuItem(_("on rotation"), "SetRotationMode", k, true),
self:genAutoExecMenuItem(_("on rotation"), "SetRotationMode", k),
self:genAutoExecMenuItem(_("on showing folder"), "PathChanged", k, true),
-- separator
self:genAutoExecMenuItem(_("on book opening"), "ReaderReadyAll", k),
self:genAutoExecMenuItem(_("on book closing"), "CloseDocument", k),
self:genAutoExecMenuItem(_("on book closing"), "CloseDocumentAll", k),
}
end,
hold_callback = function(touchmenu_instance)
@@ -464,8 +465,10 @@ end
function Profiles:genAutoExecMenuItem(text, event, profile_name, separator)
if event == "SetRotationMode" then
return self:genAutoExecSetRotationModeMenuItem(text, event, profile_name, separator)
elseif event == "ReaderReadyAll" then
return self:genAutoExecReaderReadyAllMenuItem(text, event, profile_name, separator)
elseif event == "PathChanged" then
return self:genAutoExecPathChangedMenuItem(text, event, profile_name, separator)
elseif event == "ReaderReadyAll" or event == "CloseDocumentAll" then
return self:genAutoExecDocConditionalMenuItem(text, event, profile_name, separator)
end
return {
text = text,
@@ -477,8 +480,9 @@ function Profiles:genAutoExecMenuItem(text, event, profile_name, separator)
util.tableRemoveValue(self.autoexec, event, profile_name)
else
util.tableSetValue(self.autoexec, true, event, profile_name)
if event == "ReaderReady" then -- "always" is checked, clear all conditional triggers
util.tableRemoveValue(self.autoexec, "ReaderReadyAll", profile_name)
if event == "ReaderReady" or event == "CloseDocument" then
-- "always" is checked, clear all conditional triggers
util.tableRemoveValue(self.autoexec, event .. "All", profile_name)
end
end
end,
@@ -520,8 +524,84 @@ function Profiles:genAutoExecSetRotationModeMenuItem(text, event, profile_name,
}
end
function Profiles:genAutoExecReaderReadyAllMenuItem(text, event, profile_name, separator)
local event_always = "ReaderReady"
function Profiles:genAutoExecPathChangedMenuItem(text, event, profile_name, separator)
return {
text = text,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name) and true
end,
sub_item_table_func = function()
local conditions = {
{ _("if folder path contains"), "has" },
{ _("if folder path does not contain"), "has_not" },
}
local sub_item_table = {}
for i, mode in ipairs(conditions) do
sub_item_table[i] = {
text_func = function()
local txt = conditions[i][1]
local value = util.tableGetValue(self.autoexec, event, profile_name, conditions[i][2])
return value and txt .. ": " .. value or txt
end,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name, conditions[i][2])
end,
callback = function(touchmenu_instance)
local condition = conditions[i][2]
local dialog
local buttons = {{
{
text = _("Current folder"),
callback = function()
local curr_path = self.ui.file_chooser and self.ui.file_chooser.path or self.ui:getLastDirFile()
dialog:addTextToInput(curr_path)
end,
},
}}
table.insert(buttons, {
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(dialog)
end,
},
{
text = _("Save"),
callback = function()
local txt = dialog:getInputText()
if txt == "" then
util.tableRemoveValue(self.autoexec, event, profile_name, condition)
else
util.tableSetValue(self.autoexec, txt, event, profile_name, condition)
end
UIManager:close(dialog)
touchmenu_instance:updateItems()
end,
},
})
dialog = InputDialog:new{
title = _("Enter text contained in folder path"),
input = util.tableGetValue(self.autoexec, event, profile_name, condition),
buttons = buttons,
}
UIManager:show(dialog)
dialog:onShowKeyboard()
end,
}
end
return sub_item_table
end,
hold_callback = function(touchmenu_instance)
util.tableRemoveValue(self.autoexec, event, profile_name)
touchmenu_instance:updateItems()
end,
separator = separator,
}
end
function Profiles:genAutoExecDocConditionalMenuItem(text, event, profile_name, separator)
local event_always = event:gsub("All", "")
return {
text = text,
checked_func = function()
@@ -532,6 +612,7 @@ function Profiles:genAutoExecReaderReadyAllMenuItem(text, event, profile_name, s
{ _("if device orientation is"), "orientation" },
{ _("if book metadata contains"), "doc_props" },
{ _("if book file path contains"), "filepath" },
{ _("if book is in collections"), "collections" },
}
local sub_item_table = {
self:genAutoExecMenuItem(_("always"), event_always, profile_name, true),
@@ -706,6 +787,42 @@ function Profiles:genAutoExecReaderReadyAllMenuItem(text, event, profile_name, s
touchmenu_instance:updateItems()
end,
},
{
text_func = function() -- collections
local txt = conditions[4][1]
local collections = util.tableGetValue(self.autoexec, event, profile_name, conditions[4][2])
if collections then
local collections_nb = util.tableSize(collections)
return txt .. ": " ..
(collections_nb == 1 and self.ui.collections:getCollectionTitle(next(collections))
or "(" .. collections_nb .. ")")
end
return txt
end,
enabled_func = function()
return not util.tableGetValue(self.autoexec, event_always, profile_name)
end,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name, conditions[4][2]) and true
end,
callback = function(touchmenu_instance)
local condition = conditions[4][2]
local collections = util.tableGetValue(self.autoexec, event, profile_name, condition)
local caller_callback = function(selected_collections)
if next(selected_collections) == nil then
util.tableRemoveValue(self.autoexec, event, profile_name, condition)
else
util.tableSetValue(self.autoexec, selected_collections, event, profile_name, condition)
end
touchmenu_instance:updateItems()
end
self.ui.collections:onShowCollList(collections or {}, caller_callback, true)
end,
hold_callback = function(touchmenu_instance)
util.tableRemoveValue(self.autoexec, event, profile_name, conditions[4][2])
touchmenu_instance:updateItems()
end,
},
}
return sub_item_table
end,
@@ -729,16 +846,39 @@ function Profiles:onResume() -- global
self:executeAutoExecEvent("Resume")
end
function Profiles:onSetRotationMode(rotation) -- global
function Profiles:onSetRotationMode(mode) -- global
local event = "SetRotationMode"
if self.autoexec[event] then
for profile_name, modes in pairs(self.autoexec[event]) do
if modes[rotation] then
if self.ui.config then -- close bottom menu to let Dispatcher execute profile
self.ui.config:onCloseConfigMenu()
end
self:executeAutoExec(profile_name)
if self.autoexec[event] == nil then return end
for profile_name, modes in pairs(self.autoexec[event]) do
if modes[mode] then
if self.ui.config then -- close bottom menu to let Dispatcher execute profile
self.ui.config:onCloseConfigMenu()
end
self:executeAutoExec(profile_name)
end
end
end
function Profiles:onPathChanged(path) -- global
local event = "PathChanged"
if self.autoexec[event] == nil then return end
local function is_match(txt, pattern)
for str in util.gsplit(pattern, ",") do -- comma separated patterns are allowed
if util.stringSearch(txt, str) ~= 0 then
return true
end
end
end
for profile_name, conditions in pairs(self.autoexec[event]) do
local do_execute
if conditions.has then
do_execute = is_match(path, conditions.has)
end
if do_execute == nil and conditions.has_not then
do_execute = not is_match(path, conditions.has_not)
end
if do_execute then
self:executeAutoExec(profile_name)
end
end
end
@@ -746,21 +886,21 @@ end
function Profiles:onReaderReady() -- global
if not self.ui.reloading then
self:executeAutoExecEvent("ReaderReady")
self:executeAutoExecReaderReadyAll()
self:executeAutoExecDocConditional("ReaderReadyAll")
end
end
function Profiles:onCloseDocument() -- global
if not self.ui.reloading then
self:executeAutoExecEvent("CloseDocument")
self:executeAutoExecDocConditional("CloseDocumentAll")
end
end
function Profiles:executeAutoExecEvent(event)
if self.autoexec[event] then
for profile_name in pairs(self.autoexec[event]) do
self:executeAutoExec(profile_name)
end
if self.autoexec[event] == nil then return end
for profile_name in pairs(self.autoexec[event]) do
self:executeAutoExec(profile_name)
end
end
@@ -786,25 +926,24 @@ function Profiles:executeAutoExec(profile_name)
end
end
function Profiles:executeAutoExecReaderReadyAll()
local event = "ReaderReadyAll"
local function is_match(text, pattern)
text = text:lower()
for str in util.gsplit(pattern, ",") do -- comma separated patterns are allowed
if text:find(str:lower()) then
function Profiles:executeAutoExecDocConditional(event)
if self.autoexec[event] == nil then return end
local function is_match(txt, pattern)
for str in util.gsplit(pattern, ",") do
if util.stringSearch(txt, str) ~= 0 then
return true
end
end
end
if self.autoexec[event] then
for profile_name, conditions in pairs(self.autoexec[event]) do
if self.data[profile_name] then
local do_execute
for condition, trigger in pairs(conditions) do
if condition == "orientation" then
local mode = Screen:getRotationMode()
do_execute = trigger[mode]
elseif condition == "doc_props" then
for profile_name, conditions in pairs(self.autoexec[event]) do
if self.data[profile_name] then
local do_execute
for condition, trigger in pairs(conditions) do
if condition == "orientation" then
local mode = Screen:getRotationMode()
do_execute = trigger[mode]
elseif condition == "doc_props" then
if self.ui.document then
for prop_name, pattern in pairs(trigger) do
local prop = self.ui.doc_props[prop_name == "title" and "display_title" or prop_name]
do_execute = is_match(prop, pattern)
@@ -812,17 +951,29 @@ function Profiles:executeAutoExecReaderReadyAll()
break -- any prop match is enough
end
end
elseif condition == "filepath" then
end
elseif condition == "filepath" then
if self.ui.document then
do_execute = is_match(self.ui.document.file, trigger)
end
if do_execute then
break -- execute profile only once
elseif condition == "collections" then
if self.ui.document then
local ReadCollection = require("readcollection")
for collection_name in pairs(trigger) do
if ReadCollection:isFileInCollection(self.ui.document.file, collection_name) then
do_execute = true
break -- any collection is enough
end
end
end
end
if do_execute then
self:executeAutoExec(profile_name)
break -- execute profile only once
end
end
if do_execute then
self:executeAutoExec(profile_name)
end
end
end
end