2
0
mirror of https://github.com/koreader/koreader synced 2024-11-10 01:10:34 +00:00
koreader/frontend/ui/widget/filechooser.lua

656 lines
23 KiB
Lua
Raw Normal View History

local BD = require("ui/bidi")
2023-07-03 05:24:28 +00:00
local datetime = require("datetime")
local Device = require("device")
local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry")
local FileManagerShortcuts = require("apps/filemanager/filemanagershortcuts")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
2017-04-29 08:38:09 +00:00
local Menu = require("ui/widget/menu")
local ReadCollection = require("readcollection")
2017-04-29 08:38:09 +00:00
local UIManager = require("ui/uimanager")
local ffi = require("ffi")
2018-10-14 06:49:00 +00:00
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local sort = require("sort")
2018-10-14 06:49:00 +00:00
local util = require("util")
local _ = require("gettext")
local Screen = Device.screen
local T = ffiUtil.template
2017-04-29 08:38:09 +00:00
2013-10-18 20:38:07 +00:00
local FileChooser = Menu:extend{
2014-03-13 13:52:43 +00:00
no_title = true,
path = lfs.currentdir(),
show_path = true,
2014-03-13 13:52:43 +00:00
parent = nil,
show_finished = G_reader_settings:readSetting("show_finished", true), -- books marked as finished
show_hidden = G_reader_settings:readSetting("show_hidden", false), -- folders/files starting with "."
show_unsupported = G_reader_settings:readSetting("show_unsupported", false), -- set to true to ignore file_filter
file_filter = nil, -- function defined in the caller, returns true for files to be shown
-- NOTE: Input is *always* a relative entry name
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
exclude_dirs = { -- const
-- KOReader / Kindle
"%.sdr$",
-- Kobo
"^%.adobe%-digital%-editions$",
"^certificates$",
"^custom%-dict$",
"^dict$",
"^iink$",
"^kepub$",
"^markups$",
"^webstorage$",
"^%.kobo%-images$",
-- macOS
"^%.fseventsd$",
"^%.Trashes$",
"^%.Spotlight%-V100$",
-- *nix
"^%.Trash$",
"^%.Trash%-%d+$",
-- Windows
"^RECYCLED$",
"^RECYCLER$",
"^%$Recycle%.Bin$",
"^System Volume Information$",
-- Plato
"^%.thumbnail%-previews$",
"^%.reading%-states$",
},
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
exclude_files = { -- const
-- Kobo
"^BookReader%.sqlite",
"^KoboReader%.sqlite",
"^device%.salt%.conf$",
-- macOS
"^%.DS_Store$",
-- *nix
"^%.directory$",
-- Windows
"^Thumbs%.db$",
-- Calibre
"^driveinfo%.calibre$",
"^metadata%.calibre$",
-- Plato
"^%.fat32%-epoch$",
"^%.metadata%.json$",
},
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
path_items = nil, -- hash, store last browsed location (item index) for each path
goto_letter = true,
collates = {
strcoll = {
text = _("name"),
menu_order = 10,
can_collate_mixed = true,
init_sort_func = function(cache)
return function(a, b)
return ffiUtil.strcoll(a.text, b.text)
end, cache
end,
},
natural = {
text = _("name (natural sorting)"),
menu_order = 20,
can_collate_mixed = true,
init_sort_func = function(cache)
local natsort
natsort, cache = sort.natsort_cmp(cache)
return function(a, b)
return natsort(a.text, b.text)
end, cache
end
},
access = {
text = _("last read date"),
menu_order = 30,
can_collate_mixed = true,
init_sort_func = function(cache)
return function(a, b)
return a.attr.access > b.attr.access
end, cache
end,
mandatory_func = function(item)
return datetime.secondsToDateTime(item.attr.access)
end,
},
date = {
text = _("date modified"),
menu_order = 40,
can_collate_mixed = true,
init_sort_func = function(cache)
return function(a, b)
return a.attr.modification > b.attr.modification
end, cache
end,
mandatory_func = function(item)
return datetime.secondsToDateTime(item.attr.modification)
end,
},
size = {
text = _("size"),
menu_order = 50,
can_collate_mixed = false,
init_sort_func = function(cache)
return function(a, b)
return a.attr.size < b.attr.size
end, cache
end,
},
type = {
text = _("type"),
menu_order = 60,
can_collate_mixed = false,
init_sort_func = function(cache)
return function(a, b)
if (a.suffix or b.suffix) and a.suffix ~= b.suffix then
return ffiUtil.strcoll(a.suffix, b.suffix)
end
return ffiUtil.strcoll(a.text, b.text)
end, cache
end,
item_func = function(item)
item.suffix = util.getFileNameSuffix(item.text)
end,
},
percent_unopened_first = {
text = _("percent - unopened first"),
menu_order = 70,
can_collate_mixed = false,
init_sort_func = function(cache)
return function(a, b)
if a.opened == b.opened then
if a.opened then
return a.percent_finished < b.percent_finished
end
return ffiUtil.strcoll(a.text, b.text)
end
return b.opened
end, cache
end,
item_func = function(item)
local percent_finished
item.opened = DocSettings:hasSidecarFile(item.path)
if item.opened then
local doc_settings = DocSettings:open(item.path)
percent_finished = doc_settings:readSetting("percent_finished")
end
-- smooth 2 decimal points (0.00) instead of 16 decimal points
item.percent_finished = util.round_decimal(percent_finished or 0, 2)
end,
mandatory_func = function(item)
return item.opened and string.format("%d %%", 100 * item.percent_finished) or ""
end,
},
percent_unopened_last = {
text = _("percent - unopened last"),
menu_order = 80,
can_collate_mixed = false,
init_sort_func = function(cache)
return function(a, b)
if a.opened == b.opened then
if a.opened then
return a.percent_finished < b.percent_finished
end
return ffiUtil.strcoll(a.text, b.text)
end
return a.opened
end, cache
end,
item_func = function(item)
local percent_finished
item.opened = DocSettings:hasSidecarFile(item.path)
if item.opened then
local doc_settings = DocSettings:open(item.path)
percent_finished = doc_settings:readSetting("percent_finished")
end
-- smooth 2 decimal points (0.00) instead of 16 decimal points
item.percent_finished = util.round_decimal(percent_finished or 0, 2)
end,
mandatory_func = function(item)
return item.opened and string.format("%d %%", 100 * item.percent_finished) or ""
end,
},
percent_natural = {
-- sort 90% > 50% > 0% or on hold > unopened > 100% or finished
text = _("percent unopened finished last"),
menu_order = 90,
can_collate_mixed = false,
init_sort_func = function(cache)
local natsort
natsort, cache = sort.natsort_cmp(cache)
local sortfunc = function(a, b)
if a.sort_percent == b.sort_percent then
return natsort(a.text, b.text)
elseif a.sort_percent == 1 then
return false
elseif b.sort_percent == 1 then
return true
else
return a.sort_percent > b.sort_percent
end
end
return sortfunc, cache
end,
item_func = function(item)
local percent_finished
local sort_percent
item.opened = DocSettings:hasSidecarFile(item.path)
if item.opened then
local doc_settings = DocSettings:open(item.path)
local summary = doc_settings:readSetting("summary")
-- books marked as "finished" or "on hold" should be considered the same as 100% and 0% respectively
if summary and summary.status == "complete" then
sort_percent = 1.0
elseif summary and summary.status == "abandoned" then
sort_percent = 0
end
percent_finished = doc_settings:readSetting("percent_finished")
end
-- smooth 2 decimal points (0.00) instead of 16 decimal points
item.sort_percent = sort_percent or util.round_decimal(percent_finished or -1, 2)
item.percent_finished = percent_finished or 0
end,
mandatory_func = function(item)
return item.opened and string.format("%d %%", 100 * item.percent_finished) or ""
end,
},
},
2012-05-27 21:43:00 +00:00
}
-- Cache of content we knew of for directories that are not readable
-- (i.e. /storage/emulated/ on Android that we can meet when coming
-- from readable /storage/emulated/0/ - so we know it contains "0/")
local unreadable_dir_content = {}
function FileChooser:show_dir(dirname)
for _, pattern in ipairs(self.exclude_dirs) do
if dirname:match(pattern) then return false end
end
return true
end
function FileChooser:show_file(filename, fullpath)
for _, pattern in ipairs(self.exclude_files) do
if filename:match(pattern) then return false end
end
2024-01-10 19:27:09 +00:00
if not self.show_unsupported and self.file_filter ~= nil and not self.file_filter(filename) then return false end
2023-12-27 06:45:52 +00:00
if not FileChooser.show_finished and fullpath ~= nil and filemanagerutil.getStatus(fullpath) == "complete" then return false end
return true
end
function FileChooser:init()
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
self.path_items = {}
if lfs.attributes(self.path, "mode") ~= "directory" then
self.path = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir()
end
Menu.init(self) -- call parent's init()
2024-05-07 06:34:30 +00:00
self:refreshPath()
end
function FileChooser:getList(path, collate)
local dirs, files = {}, {}
-- lfs.dir directory without permission will give error
local ok, iter, dir_obj = pcall(lfs.dir, path)
if ok then
unreadable_dir_content[path] = nil
for f in iter, dir_obj do
2023-12-27 06:45:52 +00:00
if FileChooser.show_hidden or not util.stringStartsWith(f, ".") then
local fullpath = path.."/"..f
local attributes = lfs.attributes(fullpath) or {}
local item = true
if attributes.mode == "directory" and f ~= "." and f ~= ".."
and self:show_dir(f) then
if collate then -- when collate == nil count only to display in folder mandatory
item = self:getListItem(path, f, fullpath, attributes, collate)
end
table.insert(dirs, item)
-- Always ignore macOS resource forks.
elseif attributes.mode == "file" and not util.stringStartsWith(f, "._")
and self:show_file(f, fullpath) then
if collate then -- when collate == nil count only to display in folder mandatory
item = self:getListItem(path, f, fullpath, attributes, collate)
end
table.insert(files, item)
end
end
end
else -- error, probably "permission denied"
if unreadable_dir_content[path] then
-- Add this dummy item that will be replaced with a message by genItemTable()
2024-01-10 19:27:09 +00:00
table.insert(dirs, self:getListItem(path, "./.", path, {}))
-- If we knew about some content (if we had come up from them
-- to this directory), have them shown
for k, v in pairs(unreadable_dir_content[path]) do
if v.attr and v.attr.mode == "directory" then
table.insert(dirs, v)
else
table.insert(files, v)
end
end
end
end
return dirs, files
end
function FileChooser:getListItem(dirpath, f, fullpath, attributes, collate)
local item = {
text = f,
path = fullpath,
attr = attributes,
}
if attributes.mode == "file" then
-- set to false to show all files in regular font
-- set to "opened" to show opened files in bold
-- otherwise, show new files in bold
local show_file_in_bold = G_reader_settings:readSetting("show_file_in_bold")
item.bidi_wrap_func = BD.filename
item.is_file = true
if show_file_in_bold ~= false then
item.opened = DocSettings:hasSidecarFile(fullpath)
item.bold = item.opened
if show_file_in_bold ~= "opened" then
item.bold = not item.bold
end
end
item.dim = self.filemanager and self.filemanager.selected_files
and self.filemanager.selected_files[item.path]
if collate.item_func ~= nil then
collate.item_func(item)
end
item.mandatory = self:getMenuItemMandatory(item, collate)
else -- folder
if item.text == "./." then -- added as content of an unreadable directory
item.text = _("Current folder not readable. Some content may not be shown.")
else
item.text = item.text.."/"
item.bidi_wrap_func = BD.directory
if collate.can_collate_mixed and collate.item_func ~= nil then
collate.item_func(item)
end
if dirpath then -- file browser or PathChooser
item.mandatory = self:getMenuItemMandatory(item)
end
end
end
return item
2012-05-27 21:43:00 +00:00
end
function FileChooser:getCollate()
local collate_id = G_reader_settings:readSetting("collate", "strcoll")
local collate = self.collates[collate_id]
if collate ~= nil then
return collate, collate_id
else
G_reader_settings:saveSetting("collate", "strcoll")
return self.collates.strcoll, "strcoll"
end
end
function FileChooser:getSortingFunction(collate, reverse_collate)
local sorting
-- Only keep the cache if we're an *instance* of FileChooser
if self ~= FileChooser then
sorting, self.sort_cache = collate.init_sort_func(self.sort_cache)
else
sorting = collate.init_sort_func()
end
if reverse_collate then
local sorting_unreversed = sorting
sorting = function(a, b) return sorting_unreversed(b, a) end
end
return sorting
end
function FileChooser:clearSortingCache()
self.sort_cache = nil
end
function FileChooser:genItemTableFromPath(path)
local collate = self:getCollate()
local dirs, files = self:getList(path, collate)
return self:genItemTable(dirs, files, path)
end
function FileChooser:genItemTable(dirs, files, path)
local collate = self:getCollate()
local collate_mixed = G_reader_settings:isTrue("collate_mixed")
local reverse_collate = G_reader_settings:isTrue("reverse_collate")
local sorting = self:getSortingFunction(collate, reverse_collate)
local item_table = {}
if collate.can_collate_mixed and collate_mixed then
table.move(dirs, 1, #dirs, 1, item_table)
table.move(files, 1, #files, #item_table + 1, item_table)
table.sort(item_table, sorting)
else
table.sort(files, sorting)
if not collate.can_collate_mixed then -- keep folders sorted by name not reversed
sorting = self:getSortingFunction(self.collates.strcoll)
end
table.sort(dirs, sorting)
table.move(dirs, 1, #dirs, 1, item_table)
table.move(files, 1, #files, #item_table + 1, item_table)
end
if path then -- file browser or PathChooser
if path ~= "/" and not (G_reader_settings:isTrue("lock_home_folder") and
path == G_reader_settings:readSetting("home_dir")) then
table.insert(item_table, 1, {
text = BD.mirroredUILayout() and BD.ltr("../ ⬆") or "⬆ ../",
path = path.."/..",
is_go_up = true,
})
end
if self.show_current_dir_for_hold then
table.insert(item_table, 1, {
text = _("Long-press to choose current folder"),
path = path.."/.",
})
end
end
2016-12-25 07:43:58 +00:00
-- lfs.dir iterated node string may be encoded with some weird codepage on
-- Windows we need to encode them to utf-8
if ffi.os == "Windows" then
for _, v in ipairs(item_table) do
if v.text then
2018-10-14 06:49:00 +00:00
v.text = ffiUtil.multiByteToUTF8(v.text) or ""
end
end
2014-03-13 13:52:43 +00:00
end
2014-03-13 13:52:43 +00:00
return item_table
end
2012-05-27 21:43:00 +00:00
function FileChooser:getMenuItemMandatory(item, collate)
local text
if collate then -- file
if collate.mandatory_func ~= nil then
text = collate.mandatory_func(item)
else
text = util.getFriendlySize(item.attr.size or 0)
end
if ReadCollection:isFileInCollections(item.path) then
text = "" .. text
end
else -- folder, count number of folders and files inside it
local sub_dirs, dir_files = self:getList(item.path)
text = T("%1 \u{F016}", #dir_files)
if #sub_dirs > 0 then
text = T("%1 \u{F114} ", #sub_dirs) .. text
end
if FileManagerShortcuts:hasFolderShortcut(item.path) then
text = "" .. text
end
end
return text
end
2024-05-07 06:34:30 +00:00
function FileChooser:updateItems(select_number, no_recalculate_dimen)
Menu.updateItems(self, select_number, no_recalculate_dimen) -- call parent's updateItems()
self:mergeTitleBarIntoLayout()
self.path_items[self.path] = (self.page - 1) * self.perpage + (select_number or 1)
2014-11-23 10:06:20 +00:00
end
function FileChooser:refreshPath()
2018-10-14 06:49:00 +00:00
local _, folder_name = util.splitFilePathName(self.path)
Screen:setWindowTitle(folder_name)
2024-05-07 06:34:30 +00:00
local itemmatch
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
self.prev_focused_path = self.focused_path
self.focused_path = nil
end
2024-05-07 06:34:30 +00:00
local subtitle = BD.directory(filemanagerutil.abbreviate(self.path))
self:switchItemTable(nil, self:genItemTableFromPath(self.path), self.path_items[self.path], itemmatch, subtitle)
end
function FileChooser:changeToPath(path, focused_path)
2018-10-14 06:49:00 +00:00
path = ffiUtil.realpath(path)
2014-03-13 13:52:43 +00:00
self.path = path
if focused_path then
self.focused_path = focused_path
-- We know focused_path is a child of path. In case path is
-- not a readable directory, we can have focused_path shown,
-- to allow the user to go back in it
if not unreadable_dir_content[path] then
unreadable_dir_content[path] = {}
end
if not unreadable_dir_content[path][focused_path] then
unreadable_dir_content[path][focused_path] = {
text = focused_path:sub(#path > 1 and #path+2 or 2),
path = focused_path,
2020-08-30 03:27:17 +00:00
attr = lfs.attributes(focused_path),
}
end
end
self:refreshPath()
2016-07-05 00:38:04 +00:00
self:onPathChanged(path)
end
function FileChooser:goHome()
local home_dir = G_reader_settings:readSetting("home_dir")
if not home_dir or lfs.attributes(home_dir, "mode") ~= "directory" then
-- Try some sane defaults, depending on platform
home_dir = Device.home_dir
end
if home_dir then
-- Jump to the first page if we're already home
if self.path and home_dir == self.path then
self:onGotoPage(1)
-- Also pick up new content, if any.
self:refreshPath()
else
self:changeToPath(home_dir)
end
return true
end
end
function FileChooser:onFolderUp()
if not (G_reader_settings:isTrue("lock_home_folder") and
self.path == G_reader_settings:readSetting("home_dir")) then
self:changeToPath(string.format("%s/..", self.path), self.path)
end
end
function FileChooser:changePageToPath(path)
if not path then return end
for num, item in ipairs(self.item_table) do
if not item.is_file and item.path == path then
local page = math.floor((num-1) / self.perpage) + 1
if page ~= self.page then
self:onGotoPage(page)
end
break
end
end
end
function FileChooser:toggleShowFilesMode(mode)
-- modes: "show_finished", "show_hidden", "show_unsupported"
FileChooser[mode] = not FileChooser[mode]
G_reader_settings:saveSetting(mode, FileChooser[mode])
self:refreshPath()
end
function FileChooser:onMenuSelect(item)
-- parent directory of dir without permission get nil mode
-- we need to change to parent path in this case
if item.is_file then
self:onFileSelect(item)
else
self:changeToPath(item.path, item.is_go_up and self.path)
2014-03-13 13:52:43 +00:00
end
return true
2012-05-27 22:14:08 +00:00
end
function FileChooser:onMenuHold(item)
self:onFileHold(item)
2014-03-13 13:52:43 +00:00
return true
end
function FileChooser:onFileSelect(item)
2014-03-13 13:52:43 +00:00
UIManager:close(self)
return true
end
2013-10-18 20:38:07 +00:00
function FileChooser:onFileHold(item)
2014-03-13 13:52:43 +00:00
return true
end
2016-07-05 00:38:04 +00:00
function FileChooser:onPathChanged(path)
return true
end
-- Used in ReaderStatus:onOpenNextDocumentInFolder().
function FileChooser:getNextFile(curr_file)
2023-12-27 06:45:52 +00:00
local show_finished = FileChooser.show_finished
FileChooser.show_finished = true
local curr_path = curr_file:match(".*/"):gsub("/$", "")
local item_table = self:genItemTableFromPath(curr_path)
FileChooser.show_finished = show_finished
local is_curr_file_found
2023-12-27 06:45:52 +00:00
for i, item in ipairs(item_table) do
if not is_curr_file_found and item.path == curr_file then
is_curr_file_found = true
end
if is_curr_file_found then
2023-12-27 06:45:52 +00:00
local next_file = item_table[i+1]
if next_file and next_file.is_file and DocumentRegistry:hasProvider(next_file.path)
and filemanagerutil.getStatus(next_file.path) ~= "complete" then
return next_file.path
end
end
end
end
-- Used in file manager select mode to select all files in a folder,
-- that are visible in all file browser pages, without subfolders.
function FileChooser:selectAllFilesInFolder(do_select)
for _, item in ipairs(self.item_table) do
if item.is_file then
if do_select then
self.filemanager.selected_files[item.path] = true
item.dim = true
else
item.dim = nil
end
end
end
2024-05-07 06:34:30 +00:00
self:updateItems(1, true)
end
2013-10-18 20:38:07 +00:00
return FileChooser