History: search (#11084)

reviewable/pr11091/r1
hius07 6 months ago committed by GitHub
parent 51f3115b90
commit d0d3cf78f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -15,6 +15,7 @@ local InputDialog = require("ui/widget/inputdialog")
local TextViewer = require("ui/widget/textviewer") local TextViewer = require("ui/widget/textviewer")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer") local WidgetContainer = require("ui/widget/container/widgetcontainer")
local Utf8Proc = require("ffi/utf8proc")
local filemanagerutil = require("apps/filemanager/filemanagerutil") local filemanagerutil = require("apps/filemanager/filemanagerutil")
local lfs = require("libs/libkoreader-lfs") local lfs = require("libs/libkoreader-lfs")
local util = require("util") local util = require("util")
@ -266,6 +267,25 @@ function BookInfo.getDocProps(file, book_props, no_open_document)
return BookInfo.extendProps(book_props, file) return BookInfo.extendProps(book_props, file)
end end
function BookInfo:findInProps(book_props, search_string, case_sensitive)
for _, key in ipairs(self.props) do
local prop = book_props[key]
if prop then
if key == "series_index" then
prop = tostring(prop)
elseif key == "description" then
prop = util.htmlToPlainTextIfHtml(prop)
end
if not case_sensitive then
prop = Utf8Proc.lowercase(util.fixUtf8(prop, "?"))
end
if prop:find(search_string) then
return true
end
end
end
end
-- Shows book information for currently opened document. -- Shows book information for currently opened document.
function BookInfo:onShowBookInfo() function BookInfo:onShowBookInfo()
if self.document then if self.document then

@ -5,7 +5,6 @@ local ConfirmBox = require("ui/widget/confirmbox")
local DocSettings = require("docsettings") local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry") local DocumentRegistry = require("document/documentregistry")
local FileChooser = require("ui/widget/filechooser") local FileChooser = require("ui/widget/filechooser")
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
local InfoMessage = require("ui/widget/infomessage") local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog") local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu") local Menu = require("ui/widget/menu")
@ -34,7 +33,7 @@ function FileSearcher:onShowFileSearch(search_string)
local check_button_case, check_button_subfolders, check_button_metadata local check_button_case, check_button_subfolders, check_button_metadata
search_dialog = InputDialog:new{ search_dialog = InputDialog:new{
title = _("Enter text to search for in filename"), title = _("Enter text to search for in filename"),
input = search_string or self.search_value, input = search_string or self.search_string,
buttons = { buttons = {
{ {
{ {
@ -48,8 +47,8 @@ function FileSearcher:onShowFileSearch(search_string)
text = _("Home folder"), text = _("Home folder"),
enabled = G_reader_settings:has("home_dir"), enabled = G_reader_settings:has("home_dir"),
callback = function() callback = function()
self.search_value = search_dialog:getInputText() self.search_string = search_dialog:getInputText()
if self.search_value == "" then return end if self.search_string == "" then return end
UIManager:close(search_dialog) UIManager:close(search_dialog)
self.path = G_reader_settings:readSetting("home_dir") self.path = G_reader_settings:readSetting("home_dir")
self:doSearch() self:doSearch()
@ -59,8 +58,8 @@ function FileSearcher:onShowFileSearch(search_string)
text = self.ui.file_chooser and _("Current folder") or _("Book folder"), text = self.ui.file_chooser and _("Current folder") or _("Book folder"),
is_enter_default = true, is_enter_default = true,
callback = function() callback = function()
self.search_value = search_dialog:getInputText() self.search_string = search_dialog:getInputText()
if self.search_value == "" then return end if self.search_string == "" then return end
UIManager:close(search_dialog) UIManager:close(search_dialog)
self.path = self.ui.file_chooser and self.ui.file_chooser.path or self.ui:getLastDirFile() self.path = self.ui.file_chooser and self.ui.file_chooser.path or self.ui:getLastDirFile()
self:doSearch() self:doSearch()
@ -126,17 +125,17 @@ function FileSearcher:getList()
["/sys"] = true, ["/sys"] = true,
} }
local collate = G_reader_settings:readSetting("collate") local collate = G_reader_settings:readSetting("collate")
local keywords = self.search_value local search_string = self.search_string
if keywords ~= "*" then -- one * to show all files if search_string ~= "*" then -- one * to show all files
if not self.case_sensitive then if not self.case_sensitive then
keywords = Utf8Proc.lowercase(util.fixUtf8(keywords, "?")) search_string = Utf8Proc.lowercase(util.fixUtf8(search_string, "?"))
end end
-- replace '.' with '%.' -- replace '.' with '%.'
keywords = keywords:gsub("%.","%%%.") search_string = search_string:gsub("%.","%%%.")
-- replace '*' with '.*' -- replace '*' with '.*'
keywords = keywords:gsub("%*","%.%*") search_string = search_string:gsub("%*","%.%*")
-- replace '?' with '.' -- replace '?' with '.'
keywords = keywords:gsub("%?","%.") search_string = search_string:gsub("%?","%.")
end end
local dirs, files = {}, {} local dirs, files = {}, {}
@ -161,14 +160,14 @@ function FileSearcher:getList()
if self.include_subfolders and not sys_folders[fullpath] then if self.include_subfolders and not sys_folders[fullpath] then
table.insert(new_dirs, fullpath) table.insert(new_dirs, fullpath)
end end
if self:isFileMatch(f, fullpath, keywords) then if self:isFileMatch(f, fullpath, search_string) then
table.insert(dirs, FileChooser.getListItem(f, fullpath, attributes)) table.insert(dirs, FileChooser.getListItem(f, fullpath, attributes))
end end
-- Always ignore macOS resource forks, too. -- Always ignore macOS resource forks, too.
elseif attributes.mode == "file" and not util.stringStartsWith(f, "._") elseif attributes.mode == "file" and not util.stringStartsWith(f, "._")
and (FileChooser.show_unsupported or DocumentRegistry:hasProvider(fullpath)) and (FileChooser.show_unsupported or DocumentRegistry:hasProvider(fullpath))
and FileChooser:show_file(f) then and FileChooser:show_file(f) then
if self:isFileMatch(f, fullpath, keywords, true) then if self:isFileMatch(f, fullpath, search_string, true) then
table.insert(files, FileChooser.getListItem(f, fullpath, attributes, collate)) table.insert(files, FileChooser.getListItem(f, fullpath, attributes, collate))
end end
end end
@ -180,36 +179,22 @@ function FileSearcher:getList()
return dirs, files return dirs, files
end end
function FileSearcher:isFileMatch(filename, fullpath, keywords, is_file) function FileSearcher:isFileMatch(filename, fullpath, search_string, is_file)
if keywords == "*" then if search_string == "*" then
return true return true
end end
if not self.case_sensitive then if not self.case_sensitive then
filename = Utf8Proc.lowercase(util.fixUtf8(filename, "?")) filename = Utf8Proc.lowercase(util.fixUtf8(filename, "?"))
end end
if string.find(filename, keywords) then if string.find(filename, search_string) then
return true return true
end end
if self.include_metadata and is_file and DocumentRegistry:hasProvider(fullpath) then if self.include_metadata and is_file and DocumentRegistry:hasProvider(fullpath) then
local book_props = self.ui.coverbrowser:getBookInfo(fullpath) or local book_props = self.ui.coverbrowser:getBookInfo(fullpath) or
FileManagerBookInfo.getDocProps(fullpath, nil, true) -- do not open the document self.ui.bookinfo.getDocProps(fullpath, nil, true) -- do not open the document
if next(book_props) ~= nil then if next(book_props) ~= nil then
for _, key in ipairs(FileManagerBookInfo.props) do if self.ui.bookinfo:findInProps(book_props, search_string, self.case_sensitive) then
local prop = book_props[key] return true
if prop and prop ~= "" then
if key == "series_index" then
prop = tostring(prop)
end
if not self.case_sensitive then
prop = Utf8Proc.lowercase(util.fixUtf8(prop, "?"))
end
if key == "description" then
prop = util.htmlToPlainTextIfHtml(prop)
end
if string.find(prop, keywords) then
return true
end
end
end end
else else
self.no_metadata_count = self.no_metadata_count + 1 self.no_metadata_count = self.no_metadata_count + 1
@ -218,7 +203,7 @@ function FileSearcher:isFileMatch(filename, fullpath, keywords, is_file)
end end
function FileSearcher:showSearchResultsMessage(no_results) function FileSearcher:showSearchResultsMessage(no_results)
local text = no_results and T(_("No results for '%1'."), self.search_value) local text = no_results and T(_("No results for '%1'."), self.search_string)
if self.no_metadata_count == 0 then if self.no_metadata_count == 0 then
UIManager:show(InfoMessage:new{ text = text }) UIManager:show(InfoMessage:new{ text = text })
else else

@ -1,11 +1,15 @@
local BD = require("ui/bidi") local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog") local ButtonDialog = require("ui/widget/buttondialog")
local CheckButton = require("ui/widget/checkbutton")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu") local Menu = require("ui/widget/menu")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer") local WidgetContainer = require("ui/widget/container/widgetcontainer")
local Screen = require("device").screen local Screen = require("device").screen
local Utf8Proc = require("ffi/utf8proc")
local filemanagerutil = require("apps/filemanager/filemanagerutil") local filemanagerutil = require("apps/filemanager/filemanagerutil")
local util = require("util")
local _ = require("gettext") local _ = require("gettext")
local C_ = _.pgettext local C_ = _.pgettext
local T = require("ffi/util").template local T = require("ffi/util").template
@ -15,12 +19,12 @@ local FileManagerHistory = WidgetContainer:extend{
} }
local filter_text = { local filter_text = {
all = C_("Book status filter", "All"), all = C_("Book status filter", "All"),
reading = C_("Book status filter", "Reading"), reading = C_("Book status filter", "Reading"),
abandoned = C_("Book status filter", "On hold"), abandoned = C_("Book status filter", "On hold"),
complete = C_("Book status filter", "Finished"), complete = C_("Book status filter", "Finished"),
deleted = C_("Book status filter", "Deleted"), deleted = C_("Book status filter", "Deleted"),
new = C_("Book status filter", "New"), new = C_("Book status filter", "New"),
} }
function FileManagerHistory:init() function FileManagerHistory:init()
@ -64,7 +68,7 @@ function FileManagerHistory:updateItemTable()
reading = 0, abandoned = 0, complete = 0, deleted = 0, new = 0, } reading = 0, abandoned = 0, complete = 0, deleted = 0, new = 0, }
local item_table = {} local item_table = {}
for _, v in ipairs(require("readhistory").hist) do for _, v in ipairs(require("readhistory").hist) do
if self.filter == "all" or v.status == self.filter then if self:isItemMatch(v) then
if self.is_frozen and v.status == "complete" then if self.is_frozen and v.status == "complete" then
v.mandatory_dim = true v.mandatory_dim = true
end end
@ -75,18 +79,50 @@ function FileManagerHistory:updateItemTable()
end end
end end
local title = self.hist_menu_title local title = self.hist_menu_title
if self.filter ~= "all" then local filter_title
title = title .. " (" .. filter_text[self.filter] .. ": " .. self.count[self.filter] .. ")" if self.search_string then
filter_title = _("search results")
elseif self.filter ~= "all" then
filter_title = filter_text[self.filter]:lower()
end
if filter_title then
title = title .. T(" (%1: %2)", filter_title, #item_table)
end end
self.hist_menu:switchItemTable(title, item_table, select_number) self.hist_menu:switchItemTable(title, item_table, select_number)
end end
function FileManagerHistory:isItemMatch(item)
if self.search_string then
local filename = self.case_sensitive and item.text or Utf8Proc.lowercase(util.fixUtf8(item.text, "?"))
if not filename:find(self.search_string) then
local book_props
if self.ui.coverbrowser then
book_props = self.ui.coverbrowser:getBookInfo(item.file)
end
if not book_props then
book_props = self.ui.bookinfo.getDocProps(item.file, nil, true) -- do not open the document
end
if not self.ui.bookinfo:findInProps(book_props, self.search_string, self.case_sensitive) then
return false
end
end
end
return self.filter == "all" or item.status == self.filter
end
function FileManagerHistory:onSetDimensions(dimen) function FileManagerHistory:onSetDimensions(dimen)
self.dimen = dimen self.dimen = dimen
end end
function FileManagerHistory:onMenuChoice(item) function FileManagerHistory:onMenuChoice(item)
require("apps/reader/readerui"):showReader(item.file) if self.ui.document then
if self.ui.document.file ~= item.file then
self.ui:switchDocument(item.file)
end
else
local ReaderUI = require("apps/reader/readerui")
ReaderUI:showReader(item.file)
end
end end
function FileManagerHistory:onMenuHold(item) function FileManagerHistory:onMenuHold(item)
@ -153,7 +189,7 @@ function FileManagerHistory:onMenuHold(item)
}) })
self.histfile_dialog = ButtonDialog:new{ self.histfile_dialog = ButtonDialog:new{
title = BD.filename(item.text:match("([^/]+)$")), title = BD.filename(item.text),
title_align = "center", title_align = "center",
buttons = buttons, buttons = buttons,
} }
@ -178,7 +214,7 @@ function FileManagerHistory:MenuSetRotationModeHandler(rotation)
return true return true
end end
function FileManagerHistory:onShowHist() function FileManagerHistory:onShowHist(search_info)
self.hist_menu = Menu:new{ self.hist_menu = Menu:new{
ui = self.ui, ui = self.ui,
covers_fullscreen = true, -- hint for UIManager:_repaint() covers_fullscreen = true, -- hint for UIManager:_repaint()
@ -192,6 +228,12 @@ function FileManagerHistory:onShowHist()
_manager = self, _manager = self,
} }
if search_info then
self.search_string = search_info.search_string
self.case_sensitive = search_info.case_sensitive
else
self.search_string = nil
end
self.filter = G_reader_settings:readSetting("history_filter", "all") self.filter = G_reader_settings:readSetting("history_filter", "all")
self.is_frozen = G_reader_settings:isTrue("history_freeze_finished_books") self.is_frozen = G_reader_settings:isTrue("history_freeze_finished_books")
if self.filter ~= "all" or self.is_frozen then if self.filter ~= "all" or self.is_frozen then
@ -200,14 +242,14 @@ function FileManagerHistory:onShowHist()
self:updateItemTable() self:updateItemTable()
self.hist_menu.close_callback = function() self.hist_menu.close_callback = function()
if self.files_updated then -- refresh Filemanager list of files if self.files_updated then -- refresh Filemanager list of files
local FileManager = require("apps/filemanager/filemanager") if self.ui.file_chooser then
if FileManager.instance then self.ui.file_chooser:refreshPath()
FileManager.instance:onRefresh()
end end
self.files_updated = nil self.files_updated = nil
end end
self.statuses_fetched = nil self.statuses_fetched = nil
UIManager:close(self.hist_menu) UIManager:close(self.hist_menu)
self.hist_menu = nil
G_reader_settings:saveSetting("history_filter", self.filter) G_reader_settings:saveSetting("history_filter", self.filter)
end end
UIManager:show(self.hist_menu) UIManager:show(self.hist_menu)
@ -227,6 +269,9 @@ function FileManagerHistory:showHistDialog()
callback = function() callback = function()
UIManager:close(hist_dialog) UIManager:close(hist_dialog)
self.filter = filter self.filter = filter
if filter == "all" then -- reset all filters
self.search_string = nil
end
self:updateItemTable() self:updateItemTable()
end, end,
} }
@ -241,6 +286,15 @@ function FileManagerHistory:showHistDialog()
genFilterButton("abandoned"), genFilterButton("abandoned"),
genFilterButton("complete"), genFilterButton("complete"),
}) })
table.insert(buttons, {
{
text = _("Search in filename and book metadata"),
callback = function()
UIManager:close(hist_dialog)
self:onSearchHistory()
end,
},
})
if self.count.deleted > 0 then if self.count.deleted > 0 then
table.insert(buttons, {}) -- separator table.insert(buttons, {}) -- separator
table.insert(buttons, { table.insert(buttons, {
@ -269,6 +323,57 @@ function FileManagerHistory:showHistDialog()
UIManager:show(hist_dialog) UIManager:show(hist_dialog)
end end
function FileManagerHistory:onSearchHistory()
local search_dialog, check_button_case
search_dialog = InputDialog:new{
title = _("Enter text to search history for"),
input = self.search_string,
buttons = {
{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(search_dialog)
end,
},
{
text = _("Search"),
is_enter_default = true,
callback = function()
local search_string = search_dialog:getInputText()
if search_string ~= "" then
UIManager:close(search_dialog)
self.search_string = self.case_sensitive and search_string or search_string:lower()
if self.hist_menu then -- called from History
self:updateItemTable()
else -- called by Dispatcher
local search_info = {
search_string = self.search_string,
case_sensitive = self.case_sensitive,
}
self:onShowHist(search_info)
end
end
end,
},
},
},
}
check_button_case = CheckButton:new{
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)
UIManager:show(search_dialog)
search_dialog:onShowKeyboard()
return true
end
function FileManagerHistory:onBookMetadataChanged() function FileManagerHistory:onBookMetadataChanged()
if self.hist_menu then if self.hist_menu then
self.hist_menu:updateItems() self.hist_menu:updateItems()

@ -52,6 +52,7 @@ local settingsList = {
reading_progress = {category="none", event="ShowReaderProgress", title=_("Reading progress"), general=true}, reading_progress = {category="none", event="ShowReaderProgress", title=_("Reading progress"), general=true},
open_previous_document = {category="none", event="OpenLastDoc", title=_("Open previous document"), general=true}, open_previous_document = {category="none", event="OpenLastDoc", title=_("Open previous document"), general=true},
history = {category="none", event="ShowHist", title=_("History"), general=true}, history = {category="none", event="ShowHist", title=_("History"), general=true},
history_search = {category="none", event="SearchHistory", title=_("History search"), general=true},
favorites = {category="none", event="ShowColl", arg="favorites", title=_("Favorites"), general=true}, favorites = {category="none", event="ShowColl", arg="favorites", title=_("Favorites"), general=true},
filemanager = {category="none", event="Home", title=_("File browser"), general=true, separator=true}, filemanager = {category="none", event="Home", title=_("File browser"), general=true, separator=true},
---- ----
@ -266,6 +267,7 @@ local dispatcher_menu_order = {
"reading_progress", "reading_progress",
"open_previous_document", "open_previous_document",
"history", "history",
"history_search",
"favorites", "favorites",
"filemanager", "filemanager",
---- ----

Loading…
Cancel
Save