mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
File search: improve (#12549)
This commit is contained in:
@@ -36,11 +36,23 @@ function FileSearcher:registerKeyEvents()
|
||||
end
|
||||
|
||||
function FileSearcher:onShowFileSearch(search_string)
|
||||
local search_dialog
|
||||
local check_button_case, check_button_subfolders, check_button_metadata
|
||||
local search_dialog, check_button_case, check_button_subfolders, check_button_metadata
|
||||
local function _doSearch()
|
||||
local search_str = search_dialog:getInputText()
|
||||
if search_str == "" then return end
|
||||
FileSearcher.search_string = search_str
|
||||
UIManager:close(search_dialog)
|
||||
self.case_sensitive = check_button_case.checked
|
||||
self.include_subfolders = check_button_subfolders.checked
|
||||
self.include_metadata = check_button_metadata and check_button_metadata.checked
|
||||
local Trapper = require("ui/trapper")
|
||||
Trapper:wrap(function()
|
||||
self:doSearch()
|
||||
end)
|
||||
end
|
||||
search_dialog = InputDialog:new{
|
||||
title = _("Enter text to search for in filename"),
|
||||
input = search_string or self.search_string,
|
||||
input = search_string or FileSearcher.search_string,
|
||||
buttons = {
|
||||
{
|
||||
{
|
||||
@@ -54,22 +66,16 @@ function FileSearcher:onShowFileSearch(search_string)
|
||||
text = _("Home folder"),
|
||||
enabled = G_reader_settings:has("home_dir"),
|
||||
callback = function()
|
||||
self.search_string = search_dialog:getInputText()
|
||||
if self.search_string == "" then return end
|
||||
UIManager:close(search_dialog)
|
||||
self.path = G_reader_settings:readSetting("home_dir")
|
||||
self:doSearch()
|
||||
_doSearch()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = self.ui.file_chooser and _("Current folder") or _("Book folder"),
|
||||
is_enter_default = true,
|
||||
callback = function()
|
||||
self.search_string = search_dialog:getInputText()
|
||||
if self.search_string == "" then return end
|
||||
UIManager:close(search_dialog)
|
||||
self.path = self.ui.file_chooser and self.ui.file_chooser.path or self.ui:getLastDirFile()
|
||||
self:doSearch()
|
||||
_doSearch()
|
||||
end,
|
||||
},
|
||||
},
|
||||
@@ -79,18 +85,12 @@ function FileSearcher:onShowFileSearch(search_string)
|
||||
text = _("Case sensitive"),
|
||||
checked = self.case_sensitive,
|
||||
parent = search_dialog,
|
||||
callback = function()
|
||||
self.case_sensitive = check_button_case.checked
|
||||
end,
|
||||
}
|
||||
search_dialog:addWidget(check_button_case)
|
||||
check_button_subfolders = CheckButton:new{
|
||||
text = _("Include subfolders"),
|
||||
checked = self.include_subfolders,
|
||||
parent = search_dialog,
|
||||
callback = function()
|
||||
self.include_subfolders = check_button_subfolders.checked
|
||||
end,
|
||||
}
|
||||
search_dialog:addWidget(check_button_subfolders)
|
||||
if self.ui.coverbrowser then
|
||||
@@ -98,9 +98,6 @@ function FileSearcher:onShowFileSearch(search_string)
|
||||
text = _("Also search in book metadata"),
|
||||
checked = self.include_metadata,
|
||||
parent = search_dialog,
|
||||
callback = function()
|
||||
self.include_metadata = check_button_metadata.checked
|
||||
end,
|
||||
}
|
||||
search_dialog:addWidget(check_button_metadata)
|
||||
end
|
||||
@@ -110,25 +107,50 @@ function FileSearcher:onShowFileSearch(search_string)
|
||||
end
|
||||
|
||||
function FileSearcher:doSearch()
|
||||
local dirs, files = self:getList()
|
||||
-- If we have a FileChooser instance, use it, to be able to make use of its natsort cache
|
||||
local results = (self.ui.file_chooser or FileChooser):genItemTable(dirs, files)
|
||||
if #results > 0 then
|
||||
self:showSearchResults(results)
|
||||
local search_hash = self.path .. (FileSearcher.search_string or "") ..
|
||||
tostring(self.case_sensitive) .. tostring(self.include_subfolders) .. tostring(self.include_metadata)
|
||||
local not_cached = FileSearcher.search_hash ~= search_hash
|
||||
if not_cached then
|
||||
local Trapper = require("ui/trapper")
|
||||
local info = InfoMessage:new{ text = _("Searching… (tap to cancel)") }
|
||||
UIManager:show(info)
|
||||
UIManager:forceRePaint()
|
||||
local completed, dirs, files, no_metadata_count = Trapper:dismissableRunInSubprocess(function()
|
||||
return self:getList()
|
||||
end, info)
|
||||
if not completed then return end
|
||||
UIManager:close(info)
|
||||
FileSearcher.search_hash = search_hash
|
||||
self.no_metadata_count = no_metadata_count
|
||||
-- Cannot do this in getList() within Trapper (cannot serialize function)
|
||||
local collate = FileChooser:getCollate()
|
||||
for i, v in ipairs(dirs) do
|
||||
local f, fullpath, attributes = unpack(v)
|
||||
dirs[i] = FileChooser:getListItem(nil, f, fullpath, attributes, collate)
|
||||
end
|
||||
for i, v in ipairs(files) do
|
||||
local f, fullpath, attributes = unpack(v)
|
||||
files[i] = FileChooser:getListItem(nil, f, fullpath, attributes, collate)
|
||||
end
|
||||
-- If we have a FileChooser instance, use it, to be able to make use of its natsort cache
|
||||
FileSearcher.search_results = (self.ui.file_chooser or FileChooser):genItemTable(dirs, files)
|
||||
end
|
||||
if #FileSearcher.search_results > 0 then
|
||||
self:onShowSearchResults(not_cached)
|
||||
else
|
||||
self:showSearchResultsMessage(true)
|
||||
end
|
||||
end
|
||||
|
||||
function FileSearcher:getList()
|
||||
self.no_metadata_count = 0
|
||||
self.no_metadata_count = 0 -- will be updated in doSearch() with result from subprocess
|
||||
local sys_folders = { -- do not search in sys_folders
|
||||
["/dev"] = true,
|
||||
["/proc"] = true,
|
||||
["/sys"] = true,
|
||||
["/mnt/base-us"] = true, -- Kindle
|
||||
}
|
||||
local collate = FileChooser:getCollate()
|
||||
local search_string = self.search_string
|
||||
local search_string = FileSearcher.search_string
|
||||
if search_string ~= "*" then -- one * to show all files
|
||||
if not self.case_sensitive then
|
||||
search_string = Utf8Proc.lowercase(util.fixUtf8(search_string, "?"))
|
||||
@@ -164,14 +186,14 @@ function FileSearcher:getList()
|
||||
table.insert(new_dirs, fullpath)
|
||||
end
|
||||
if self:isFileMatch(f, fullpath, search_string) then
|
||||
table.insert(dirs, FileChooser:getListItem(nil, f, fullpath, attributes, collate))
|
||||
table.insert(dirs, { f, fullpath, attributes })
|
||||
end
|
||||
-- Always ignore macOS resource forks, too.
|
||||
elseif attributes.mode == "file" and not util.stringStartsWith(f, "._")
|
||||
and (FileChooser.show_unsupported or DocumentRegistry:hasProvider(fullpath))
|
||||
and FileChooser:show_file(f) then
|
||||
if self:isFileMatch(f, fullpath, search_string, true) then
|
||||
table.insert(files, FileChooser:getListItem(nil, f, fullpath, attributes, collate))
|
||||
table.insert(files, { f, fullpath, attributes })
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -179,7 +201,7 @@ function FileSearcher:getList()
|
||||
end
|
||||
scan_dirs = new_dirs
|
||||
end
|
||||
return dirs, files
|
||||
return dirs, files, self.no_metadata_count
|
||||
end
|
||||
|
||||
function FileSearcher:isFileMatch(filename, fullpath, search_string, is_file)
|
||||
@@ -206,9 +228,16 @@ function FileSearcher:isFileMatch(filename, fullpath, search_string, is_file)
|
||||
end
|
||||
|
||||
function FileSearcher:showSearchResultsMessage(no_results)
|
||||
local text = no_results and T(_("No results for '%1'."), self.search_string)
|
||||
local text = no_results and T(_("No results for '%1'."), FileSearcher.search_string)
|
||||
if self.no_metadata_count == 0 then
|
||||
UIManager:show(InfoMessage:new{ text = text })
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = text,
|
||||
icon = "notice-info",
|
||||
ok_text = _("File search"),
|
||||
ok_callback = function()
|
||||
self:onShowFileSearch()
|
||||
end,
|
||||
})
|
||||
else
|
||||
local txt = T(N_("1 book has been skipped.", "%1 books have been skipped.",
|
||||
self.no_metadata_count), self.no_metadata_count) .. "\n" ..
|
||||
@@ -222,14 +251,19 @@ function FileSearcher:showSearchResultsMessage(no_results)
|
||||
self.search_menu.close_callback()
|
||||
end
|
||||
self.ui.coverbrowser:extractBooksInDirectory(self.path)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function FileSearcher:showSearchResults(results)
|
||||
function FileSearcher:onShowSearchResults(not_cached)
|
||||
if not not_cached and FileSearcher.search_results == nil then
|
||||
self:onShowFileSearch()
|
||||
return
|
||||
end
|
||||
|
||||
self.search_menu = Menu:new{
|
||||
subtitle = T(_("Query: %1"), self.search_string),
|
||||
subtitle = T(_("Query: %1"), FileSearcher.search_string),
|
||||
covers_fullscreen = true, -- hint for UIManager:_repaint()
|
||||
is_borderless = true,
|
||||
is_popout = false,
|
||||
@@ -249,9 +283,9 @@ function FileSearcher:showSearchResults(results)
|
||||
self.ui.file_chooser:refreshPath()
|
||||
end
|
||||
end
|
||||
self:updateMenu(results)
|
||||
self:updateMenu(FileSearcher.search_results)
|
||||
UIManager:show(self.search_menu)
|
||||
if self.no_metadata_count ~= 0 then
|
||||
if not_cached and self.no_metadata_count ~= 0 then
|
||||
self:showSearchResultsMessage()
|
||||
end
|
||||
end
|
||||
@@ -262,6 +296,7 @@ function FileSearcher:updateMenu(item_table)
|
||||
end
|
||||
|
||||
function FileSearcher:onMenuSelect(item)
|
||||
if lfs.attributes(item.path) == nil then return end
|
||||
if self._manager.selected_files then
|
||||
if item.is_file then
|
||||
item.dim = not item.dim and true or nil
|
||||
@@ -310,6 +345,7 @@ function FileSearcher:showFileDialog(item)
|
||||
callback = function()
|
||||
local function post_delete_callback()
|
||||
UIManager:close(dialog)
|
||||
table.remove(FileSearcher.search_results, item.idx)
|
||||
table.remove(self.search_menu.item_table, item.idx)
|
||||
self:updateMenu()
|
||||
end
|
||||
@@ -349,7 +385,7 @@ function FileSearcher:showFileDialog(item)
|
||||
end
|
||||
|
||||
function FileSearcher:onMenuHold(item)
|
||||
if self._manager.selected_files then return true end
|
||||
if self._manager.selected_files or lfs.attributes(item.path) == nil then return true end
|
||||
if item.is_file then
|
||||
if DocumentRegistry:hasProvider(item.path, nil, true) then
|
||||
self.close_callback()
|
||||
|
||||
@@ -794,7 +794,7 @@ To:
|
||||
end,
|
||||
}
|
||||
|
||||
self.menu_items.find_file = {
|
||||
self.menu_items.file_search = {
|
||||
-- @translators Search for files by name.
|
||||
text = _("File search"),
|
||||
help_text = _([[Search a book by filename in the current or home folder and its subfolders.
|
||||
@@ -807,7 +807,13 @@ The sorting order is the same as in filemanager.
|
||||
Tap a book in the search results to open it.]]),
|
||||
callback = function()
|
||||
self.ui.filesearcher:onShowFileSearch()
|
||||
end
|
||||
end,
|
||||
}
|
||||
self.menu_items.file_search_results = {
|
||||
text = _("Last file search results"),
|
||||
callback = function()
|
||||
self.ui.filesearcher:onShowSearchResults()
|
||||
end,
|
||||
}
|
||||
|
||||
-- main menu tab
|
||||
|
||||
@@ -130,13 +130,14 @@ local settingsList = {
|
||||
set_reverse_sorting = {category="string", event="SetReverseSorting", title=_("Reverse sorting"), args={true, false}, toggle={_("on"), _("off")}, filemanager=true},
|
||||
set_mixed_sorting = {category="string", event="SetMixedSorting", title=_("Folders and files mixed"), args={true, false}, toggle={_("on"), _("off")}, filemanager=true, separator=true},
|
||||
----
|
||||
folder_up = {category="none", event="FolderUp", title=_("Folder up"), filemanager=true},
|
||||
show_plus_menu = {category="none", event="ShowPlusMenu", title=_("Show plus menu"), filemanager=true},
|
||||
toggle_select_mode = {category="none", event="ToggleSelectMode", title=_("Toggle select mode"), filemanager=true},
|
||||
refresh_content = {category="none", event="RefreshContent", title=_("Refresh content"), filemanager=true},
|
||||
folder_shortcuts = {category="none", event="ShowFolderShortcutsDialog", title=_("Folder shortcuts"), filemanager=true},
|
||||
file_search = {category="none", event="ShowFileSearch", title=_("File search"), filemanager=true},
|
||||
file_search_results = {category="none", event="ShowSearchResults", title=_("Last file search results"), filemanager=true},
|
||||
----
|
||||
folder_up = {category="none", event="FolderUp", title=_("Folder up"), filemanager=true},
|
||||
-- go_to
|
||||
-- back
|
||||
|
||||
@@ -362,13 +363,14 @@ local dispatcher_menu_order = {
|
||||
"set_reverse_sorting",
|
||||
"set_mixed_sorting",
|
||||
----
|
||||
"folder_up",
|
||||
"show_plus_menu",
|
||||
"toggle_select_mode",
|
||||
"refresh_content",
|
||||
"folder_shortcuts",
|
||||
"file_search",
|
||||
"file_search_results",
|
||||
----
|
||||
"folder_up",
|
||||
-- "go_to"
|
||||
-- "back"
|
||||
|
||||
|
||||
@@ -155,8 +155,9 @@ local order = {
|
||||
"wikipedia_lookup",
|
||||
"wikipedia_history",
|
||||
"----------------------------",
|
||||
"file_search",
|
||||
"file_search_results",
|
||||
"find_book_in_calibre_catalog",
|
||||
"find_file",
|
||||
"----------------------------",
|
||||
"opds",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user