2021-08-18 14:17:27 +00:00
|
|
|
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
|
2021-07-15 10:53:28 +00:00
|
|
|
local CheckButton = require("ui/widget/checkbutton")
|
2014-11-16 11:57:56 +00:00
|
|
|
local CenterContainer = require("ui/widget/container/centercontainer")
|
|
|
|
local DocumentRegistry = require("document/documentregistry")
|
2021-07-18 18:21:39 +00:00
|
|
|
local FileChooser = require("ui/widget/filechooser")
|
2017-04-29 08:38:09 +00:00
|
|
|
local Font = require("ui/font")
|
2021-07-15 10:53:28 +00:00
|
|
|
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
|
|
|
local HorizontalSpan = require("ui/widget/horizontalspan")
|
2014-11-16 11:57:56 +00:00
|
|
|
local InfoMessage = require("ui/widget/infomessage")
|
2017-04-29 08:38:09 +00:00
|
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
2018-07-24 19:11:25 +00:00
|
|
|
local InputDialog = require("ui/widget/inputdialog")
|
2014-11-16 11:57:56 +00:00
|
|
|
local Menu = require("ui/widget/menu")
|
[RFC] Pagination UI shenanigans (#7335)
* Menu/KeyValuePage/ReaderGoTo: Unify the dialogs. (Generally, "Enter page number" as title, and "Go to page" as OK button).
* Allow *tapping* on pagination buttons, too. Added spacers around the text to accommodate for that.
* Disable input handlers when <= 1 pages, while still printing the label in black.
* Always display both the label and the chevrons, even on single page content. (Menu being an exception, because it can handle showing no content at all, in which case we hide the chevrons).
* KVP: Tweak the pagination buttons layout in order to have consistent centering, regardless of whether the return arrow is enabled or not. (Also, match Menu's layout, more or less).
* Menu: Minor layout tweaks to follow the KVP tweaks above. Fixes, among possibly other things, buttons in (non-FM) "List" menus overlapping the final entry (e.g., OPDS), and popout menus with a border being misaligned (e.g., Calibre, Find a file).
* CalendarView: Minor layout tweaks to follow the KVP tweaks. Ensures the pagination buttons are laid out in the same way as everywhere else (they used to be a wee bit higher).
2021-02-25 04:15:23 +00:00
|
|
|
local Size = require("ui/size")
|
2017-04-29 08:38:09 +00:00
|
|
|
local UIManager = require("ui/uimanager")
|
2021-07-15 10:53:28 +00:00
|
|
|
local Utf8Proc = require("ffi/utf8proc")
|
|
|
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
2017-04-29 08:38:09 +00:00
|
|
|
local lfs = require("libs/libkoreader-lfs")
|
2020-02-05 02:14:21 +00:00
|
|
|
local BaseUtil = require("ffi/util")
|
|
|
|
local util = require("util")
|
2014-11-16 11:57:56 +00:00
|
|
|
local _ = require("gettext")
|
2017-04-29 08:38:09 +00:00
|
|
|
local Screen = require("device").screen
|
2014-11-16 11:57:56 +00:00
|
|
|
|
|
|
|
local FileSearcher = InputContainer:new{
|
|
|
|
search_dialog = nil,
|
2015-04-27 00:49:27 +00:00
|
|
|
|
2014-11-16 11:57:56 +00:00
|
|
|
--filesearcher
|
|
|
|
-- state buffer
|
|
|
|
dirs = {},
|
|
|
|
files = {},
|
|
|
|
results = {},
|
|
|
|
items = 0,
|
|
|
|
commands = nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
function FileSearcher:readDir()
|
2021-03-06 18:31:39 +00:00
|
|
|
local FileManager = require("apps/filemanager/filemanager")
|
|
|
|
local ReaderUI = require("apps/reader/readerui")
|
|
|
|
local show_unsupported = G_reader_settings:isTrue("show_unsupported")
|
2014-11-16 11:57:56 +00:00
|
|
|
self.dirs = {self.path}
|
|
|
|
self.files = {}
|
|
|
|
while #self.dirs ~= 0 do
|
2015-04-27 00:49:27 +00:00
|
|
|
local new_dirs = {}
|
2014-11-16 11:57:56 +00:00
|
|
|
-- handle each dir
|
|
|
|
for __, d in pairs(self.dirs) do
|
|
|
|
-- handle files in d
|
|
|
|
for f in lfs.dir(d) do
|
|
|
|
local fullpath = d.."/"..f
|
2019-09-09 16:39:36 +00:00
|
|
|
local attributes = lfs.attributes(fullpath) or {}
|
2020-02-05 02:14:21 +00:00
|
|
|
-- Don't traverse hidden folders if we're not showing them
|
2021-07-18 18:21:39 +00:00
|
|
|
if attributes.mode == "directory" and f ~= "." and f ~= ".."
|
|
|
|
and (G_reader_settings:isTrue("show_hidden") or not util.stringStartsWith(f, "."))
|
|
|
|
and FileChooser:show_dir(f)
|
|
|
|
then
|
2016-01-07 06:44:07 +00:00
|
|
|
table.insert(new_dirs, fullpath)
|
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
|
|
|
table.insert(self.files, {
|
2021-08-18 14:17:27 +00:00
|
|
|
dir = d,
|
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
|
|
|
name = f,
|
|
|
|
text = f.."/",
|
|
|
|
attr = attributes,
|
|
|
|
callback = function()
|
|
|
|
FileManager:showFiles(fullpath)
|
|
|
|
end,
|
|
|
|
})
|
2020-02-05 02:14:21 +00:00
|
|
|
-- Always ignore macOS resource forks, too.
|
2021-07-18 18:21:39 +00:00
|
|
|
elseif attributes.mode == "file" and not util.stringStartsWith(f, "._")
|
|
|
|
and (show_unsupported or DocumentRegistry:hasProvider(fullpath))
|
|
|
|
and FileChooser:show_file(f)
|
|
|
|
then
|
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
|
|
|
table.insert(self.files, {
|
2021-08-18 14:17:27 +00:00
|
|
|
dir = d,
|
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
|
|
|
name = f,
|
|
|
|
text = f,
|
|
|
|
attr = attributes,
|
|
|
|
callback = function()
|
|
|
|
ReaderUI:showReader(fullpath)
|
|
|
|
end,
|
|
|
|
})
|
2014-11-16 11:57:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self.dirs = new_dirs
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function FileSearcher:setSearchResults()
|
|
|
|
local keywords = self.search_value
|
|
|
|
self.results = {}
|
2021-03-06 18:31:39 +00:00
|
|
|
if keywords == "*" then -- one * to show all files
|
2014-11-16 11:57:56 +00:00
|
|
|
self.results = self.files
|
|
|
|
else
|
2021-07-15 10:53:28 +00:00
|
|
|
if not self.case_sensitive then
|
2021-07-18 18:21:39 +00:00
|
|
|
keywords = Utf8Proc.lowercase(util.fixUtf8(keywords, "?"))
|
2021-07-15 10:53:28 +00:00
|
|
|
end
|
|
|
|
-- replace '.' with '%.'
|
|
|
|
keywords = keywords:gsub("%.","%%%.")
|
|
|
|
-- replace '*' with '.*'
|
|
|
|
keywords = keywords:gsub("%*","%.%*")
|
|
|
|
-- replace '?' with '.'
|
|
|
|
keywords = keywords:gsub("%?","%.")
|
2014-11-16 11:57:56 +00:00
|
|
|
for __,f in pairs(self.files) do
|
2021-07-15 10:53:28 +00:00
|
|
|
if self.case_sensitive then
|
2021-07-18 18:21:39 +00:00
|
|
|
if string.find(f.name, keywords) then
|
2021-07-15 10:53:28 +00:00
|
|
|
table.insert(self.results, f)
|
|
|
|
end
|
|
|
|
else
|
2021-07-18 18:21:39 +00:00
|
|
|
if string.find(Utf8Proc.lowercase(util.fixUtf8(f.name, "?")), keywords) then
|
2021-07-15 10:53:28 +00:00
|
|
|
table.insert(self.results, f)
|
|
|
|
end
|
2014-11-16 11:57:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self.keywords = keywords
|
|
|
|
self.items = #self.results
|
|
|
|
end
|
|
|
|
|
|
|
|
function FileSearcher:close()
|
2021-04-04 15:27:17 +00:00
|
|
|
UIManager:close(self.search_dialog)
|
|
|
|
self:readDir() --- @todo this probably doesn't need to be repeated once it's been done
|
|
|
|
self:setSearchResults() --- @todo doesn't have to be repeated if the search term is the same
|
|
|
|
if #self.results > 0 then
|
|
|
|
self:showSearchResults()
|
|
|
|
else
|
|
|
|
UIManager:show(
|
|
|
|
InfoMessage:new{
|
|
|
|
text = BaseUtil.template(_("No results for '%1'."),
|
|
|
|
self.search_value)
|
|
|
|
}
|
|
|
|
)
|
2014-11-16 11:57:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-08-14 15:53:39 +00:00
|
|
|
function FileSearcher:onShowFileSearch(search_string)
|
2014-11-16 11:57:56 +00:00
|
|
|
self.search_dialog = InputDialog:new{
|
2021-04-02 15:59:29 +00:00
|
|
|
title = _("Enter filename to search for"),
|
2021-08-14 15:53:39 +00:00
|
|
|
input = search_string or self.search_value,
|
2020-06-12 23:56:36 +00:00
|
|
|
width = math.floor(Screen:getWidth() * 0.9),
|
2014-11-16 11:57:56 +00:00
|
|
|
buttons = {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
text = _("Cancel"),
|
|
|
|
callback = function()
|
|
|
|
UIManager:close(self.search_dialog)
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
2021-04-04 15:27:17 +00:00
|
|
|
text = _("Home folder"),
|
|
|
|
enabled = G_reader_settings:has("home_dir"),
|
2014-11-16 11:57:56 +00:00
|
|
|
callback = function()
|
2018-07-24 19:11:25 +00:00
|
|
|
self.search_value = self.search_dialog:getInputText()
|
2021-04-04 15:27:17 +00:00
|
|
|
if self.search_value == "" then return end
|
|
|
|
self.path = G_reader_settings:readSetting("home_dir")
|
2018-07-24 19:11:25 +00:00
|
|
|
self:close()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
2021-04-04 15:27:17 +00:00
|
|
|
text = _("Current folder"),
|
|
|
|
is_enter_default = true,
|
2018-07-24 19:11:25 +00:00
|
|
|
callback = function()
|
2014-11-16 11:57:56 +00:00
|
|
|
self.search_value = self.search_dialog:getInputText()
|
2021-04-04 15:27:17 +00:00
|
|
|
if self.search_value == "" then return end
|
|
|
|
self.path = self.ui.file_chooser and self.ui.file_chooser.path or self.ui:getLastDirFile()
|
2014-11-16 11:57:56 +00:00
|
|
|
self:close()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2021-07-15 10:53:28 +00:00
|
|
|
-- checkbox
|
|
|
|
self.check_button_case = CheckButton:new{
|
|
|
|
text = _("Case sensitive"),
|
|
|
|
checked = self.case_sensitive,
|
|
|
|
parent = self.search_dialog,
|
2021-08-13 22:49:19 +00:00
|
|
|
max_width = self.search_dialog._input_widget.width,
|
2021-07-15 10:53:28 +00:00
|
|
|
callback = function()
|
2021-08-18 14:17:27 +00:00
|
|
|
if self.check_button_case.checked then
|
2021-07-15 10:53:28 +00:00
|
|
|
self.check_button_case:unCheck()
|
2021-08-18 14:17:27 +00:00
|
|
|
else
|
|
|
|
self.check_button_case:check()
|
2021-07-15 10:53:28 +00:00
|
|
|
end
|
2021-08-18 14:17:27 +00:00
|
|
|
self.case_sensitive = self.check_button_case.checked
|
2021-07-15 10:53:28 +00:00
|
|
|
end,
|
|
|
|
}
|
|
|
|
|
|
|
|
local checkbox_shift = math.floor((self.search_dialog.width - self.search_dialog._input_widget.width) / 2 + 0.5)
|
|
|
|
local check_buttons = HorizontalGroup:new{
|
|
|
|
HorizontalSpan:new{width = checkbox_shift},
|
|
|
|
VerticalGroup:new{
|
|
|
|
align = "left",
|
|
|
|
self.check_button_case,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
-- insert check buttons before the regular buttons
|
|
|
|
local nb_elements = #self.search_dialog.dialog_frame[1]
|
|
|
|
table.insert(self.search_dialog.dialog_frame[1], nb_elements-1, check_buttons)
|
|
|
|
|
2014-11-16 11:57:56 +00:00
|
|
|
UIManager:show(self.search_dialog)
|
2018-03-30 10:46:36 +00:00
|
|
|
self.search_dialog:onShowKeyboard()
|
2014-11-16 11:57:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function FileSearcher:showSearchResults()
|
|
|
|
local menu_container = CenterContainer:new{
|
|
|
|
dimen = Screen:getSize(),
|
|
|
|
}
|
|
|
|
self.search_menu = Menu:new{
|
[RFC] Pagination UI shenanigans (#7335)
* Menu/KeyValuePage/ReaderGoTo: Unify the dialogs. (Generally, "Enter page number" as title, and "Go to page" as OK button).
* Allow *tapping* on pagination buttons, too. Added spacers around the text to accommodate for that.
* Disable input handlers when <= 1 pages, while still printing the label in black.
* Always display both the label and the chevrons, even on single page content. (Menu being an exception, because it can handle showing no content at all, in which case we hide the chevrons).
* KVP: Tweak the pagination buttons layout in order to have consistent centering, regardless of whether the return arrow is enabled or not. (Also, match Menu's layout, more or less).
* Menu: Minor layout tweaks to follow the KVP tweaks above. Fixes, among possibly other things, buttons in (non-FM) "List" menus overlapping the final entry (e.g., OPDS), and popout menus with a border being misaligned (e.g., Calibre, Find a file).
* CalendarView: Minor layout tweaks to follow the KVP tweaks. Ensures the pagination buttons are laid out in the same way as everywhere else (they used to be a wee bit higher).
2021-02-25 04:15:23 +00:00
|
|
|
width = Screen:getWidth() - (Size.margin.fullscreen_popout * 2),
|
|
|
|
height = Screen:getHeight() - (Size.margin.fullscreen_popout * 2),
|
2014-11-16 11:57:56 +00:00
|
|
|
show_parent = menu_container,
|
|
|
|
onMenuHold = self.onMenuHold,
|
2017-04-29 08:38:09 +00:00
|
|
|
cface = Font:getFace("smallinfofont"),
|
2014-11-16 11:57:56 +00:00
|
|
|
_manager = self,
|
|
|
|
}
|
|
|
|
table.insert(menu_container, self.search_menu)
|
|
|
|
self.search_menu.close_callback = function()
|
|
|
|
UIManager:close(menu_container)
|
|
|
|
end
|
2021-07-18 18:21:39 +00:00
|
|
|
|
|
|
|
local collate = G_reader_settings:readSetting("collate") or "strcoll"
|
|
|
|
local reverse_collate = G_reader_settings:isTrue("reverse_collate")
|
|
|
|
local sorting = FileChooser:getSortingFunction(collate, reverse_collate)
|
|
|
|
|
|
|
|
table.sort(self.results, sorting)
|
2021-04-02 15:59:29 +00:00
|
|
|
self.search_menu:switchItemTable(_("Search results"), self.results)
|
2014-11-16 11:57:56 +00:00
|
|
|
UIManager:show(menu_container)
|
|
|
|
end
|
|
|
|
|
2021-08-18 14:17:27 +00:00
|
|
|
function FileSearcher:onMenuHold(item)
|
|
|
|
local FileManager = require("apps/filemanager/filemanager")
|
|
|
|
local ReaderUI = require("apps/reader/readerui")
|
|
|
|
local fullpath = item.dir .. "/" .. item.name
|
|
|
|
local is_file = item.attr.mode == "file"
|
|
|
|
local buttons = {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
text = _("Cancel"),
|
|
|
|
callback = function()
|
|
|
|
UIManager:close(self.results_dialog)
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text = _("Show folder"),
|
|
|
|
callback = function()
|
|
|
|
UIManager:close(self.results_dialog)
|
|
|
|
self.close_callback()
|
|
|
|
if is_file then
|
|
|
|
FileManager:showFiles(item.dir, fullpath)
|
|
|
|
else
|
|
|
|
FileManager:showFiles(fullpath)
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if is_file then
|
|
|
|
table.insert(buttons[1], {
|
|
|
|
text = _("Open"),
|
|
|
|
callback = function()
|
|
|
|
UIManager:close(self.results_dialog)
|
|
|
|
self.close_callback()
|
|
|
|
ReaderUI:showReader(fullpath)
|
|
|
|
end,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
self.results_dialog = ButtonDialogTitle:new{
|
|
|
|
title = is_file and fullpath or fullpath .. "/",
|
|
|
|
use_info_style = true,
|
|
|
|
buttons = buttons,
|
|
|
|
}
|
|
|
|
UIManager:show(self.results_dialog)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2014-11-16 11:57:56 +00:00
|
|
|
return FileSearcher
|