Notebook file (#12699)

This commit is contained in:
hius07
2024-11-21 07:53:22 +02:00
committed by GitHub
parent 9aa327a9de
commit 24d8baf3ec
4 changed files with 263 additions and 90 deletions

View File

@@ -12,17 +12,18 @@ local DocumentRegistry = require("document/documentregistry")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local Notification = require("ui/widget/notification")
local TextViewer = require("ui/widget/textviewer")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local Utf8Proc = require("ffi/utf8proc")
local ffiUtil = require("ffi/util")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
local lfs = require("libs/libkoreader-lfs")
local util = require("util")
local _ = require("gettext")
local N_ = _.ngettext
local Screen = Device.screen
local T = require("ffi/util").template
local T = ffiUtil.template
local BookInfo = WidgetContainer:extend{
title = _("Book information"),
@@ -73,6 +74,15 @@ function BookInfo:show(doc_settings_or_file, book_props)
-- File section
local has_sidecar = type(doc_settings_or_file) == "table"
local file = has_sidecar and doc_settings_or_file:readSetting("doc_path") or doc_settings_or_file
self.is_current_doc = self.document and self.document.file == file
if not has_sidecar and self.is_current_doc then
doc_settings_or_file = self.ui.doc_settings
has_sidecar = true
end
if not has_sidecar and DocSettings:hasSidecarFile(file) then
doc_settings_or_file = DocSettings:open(file)
has_sidecar = true
end
local folder, filename = util.splitFilePathName(file)
local __, filetype = filemanagerutil.splitFileNameType(filename)
local attr = lfs.attributes(file)
@@ -172,7 +182,15 @@ function BookInfo:show(doc_settings_or_file, book_props)
table.insert(kv_pairs, { _("Rating:"), (""):rep(rating) .. (""):rep(self.rating_max - rating),
hold_callback = summary_hold_callback })
table.insert(kv_pairs, { _("Review:"), summary.note or _("N/A"),
hold_callback = summary_hold_callback })
hold_callback = summary_hold_callback, separator = true })
-- Notebook file
local notebook_file = self:getNotebookFile(doc_settings_or_file)
local notebook_file_callback = function()
self:showNotebookFileDialog(notebook_file, doc_settings_or_file, book_props)
end
table.insert(kv_pairs, { _("Notebook file:"), notebook_file:gsub(".*/", ""),
callback = notebook_file_callback })
local KeyValuePage = require("ui/widget/keyvaluepage")
self.kvp_widget = KeyValuePage:new{
@@ -296,10 +314,7 @@ function BookInfo:findInProps(book_props, search_string, case_sensitive)
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
if util.stringSearch(prop, search_string, case_sensitive) ~= 0 then
return true
end
end
@@ -469,7 +484,7 @@ function BookInfo:setCustomMetadata(file, book_props, prop_key, prop_value)
if prop_key == "title" then -- generate when resetting the customized title and original is empty
book_props.display_title = book_props.title or filemanagerutil.splitFileNameType(file)
end
if self.document and self.document.file == file then -- currently opened document
if self.is_current_doc then
self.ui.doc_props[prop_key] = prop_value
if prop_key == "title" then
self.ui.doc_props.display_title = book_props.display_title
@@ -666,6 +681,159 @@ function BookInfo:editSummary(doc_settings_or_file, book_props)
input_dialog:onShowKeyboard(true)
end
-- notebook file
function BookInfo:getNotebookFile(doc_settings_or_file)
local notebook_file
if type(doc_settings_or_file) == "table" then
notebook_file = doc_settings_or_file:readSetting("notebook_file")
end
if notebook_file == nil then
notebook_file = G_reader_settings:readSetting("notebook_file")
if notebook_file == nil then
if type(doc_settings_or_file) == "table" then
notebook_file = doc_settings_or_file:readSetting("doc_path") .. ".txt"
elseif type(doc_settings_or_file) == "string" then
notebook_file = doc_settings_or_file .. ".txt"
else
local home_folder = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir()
notebook_file = ffiUtil.realpath(home_folder) .. "/notebook.txt"
end
end
end
return notebook_file
end
function BookInfo:showNotebookFileDialog(notebook_file, doc_settings_or_file, book_props)
local has_sidecar = type(doc_settings_or_file) == "table"
local file = has_sidecar and doc_settings_or_file:readSetting("doc_path") or doc_settings_or_file
local function saveNotebookFile(new_notebook_file)
if not has_sidecar then
doc_settings_or_file = DocSettings:open(doc_settings_or_file)
end
doc_settings_or_file:saveSetting("notebook_file", new_notebook_file)
if not self.is_current_doc then
if new_notebook_file or doc_settings_or_file:readSetting("summary") then
doc_settings_or_file:flush()
else -- remove empty sidecar
doc_settings_or_file:purge(nil, { doc_settings = true }) -- keep custom
doc_settings_or_file = file -- to reopen bookinfo
end
self.summary_updated = true -- refresh FM
end
self.kvp_widget:onClose()
self:show(doc_settings_or_file, book_props)
end
local button_dialog
local local_notebook_file = file .. ".txt"
local default_notebook_file = G_reader_settings:readSetting("notebook_file")
local buttons = {
{
{
text = _("Use default"),
enabled = default_notebook_file ~= nil and notebook_file ~= default_notebook_file,
callback = function()
UIManager:close(button_dialog)
saveNotebookFile(default_notebook_file)
end,
},
{
text = _("Reset default"),
enabled = default_notebook_file ~= nil,
callback = function()
UIManager:close(button_dialog)
G_reader_settings:delSetting("notebook_file")
Notification:notify(_("Notebook file default location reset"), Notification.SOURCE_ALWAYS_SHOW)
end,
},
},
{
{
text = _("Use local"),
enabled = notebook_file ~= local_notebook_file,
callback = function()
UIManager:close(button_dialog)
saveNotebookFile(local_notebook_file)
end,
},
{
text = _("Set as default"),
enabled = notebook_file ~= default_notebook_file,
callback = function()
UIManager:close(button_dialog)
G_reader_settings:saveSetting("notebook_file", notebook_file)
Notification:notify(_("Notebook file default location saved"), Notification.SOURCE_ALWAYS_SHOW)
end,
},
},
{
{
text = _("Choose"),
callback = function()
UIManager:close(button_dialog)
local PathChooser = require("ui/widget/pathchooser")
local path_chooser = PathChooser:new{
path = notebook_file:match("(.*)/"),
select_directory = false,
onConfirm = saveNotebookFile,
}
UIManager:show(path_chooser)
end,
},
{
text = _("Create"),
enabled = self.ui.texteditor ~= nil,
callback = function()
UIManager:close(button_dialog)
self.ui.texteditor:newFile(notebook_file, saveNotebookFile)
end,
},
},
{}, -- separator
{
{
text = _("View"),
enabled = lfs.attributes(notebook_file, "mode") == "file",
callback = function()
UIManager:close(button_dialog)
TextViewer.openFile(notebook_file)
end,
},
{
text = _("Edit"),
enabled = self.ui.texteditor ~= nil,
callback = function()
UIManager:close(button_dialog)
self.ui.texteditor:openFile(notebook_file, saveNotebookFile)
end,
},
},
}
button_dialog = ButtonDialog:new{
title = notebook_file,
title_align = "center",
buttons = buttons,
}
UIManager:show(button_dialog)
end
function BookInfo:onShowNotebookFile()
local notebook_file = self:getNotebookFile(self.ui.doc_settings)
if self.ui.texteditor then
local function saveNotebookFile(new_notebook_file)
if self.ui.doc_settings ~= nil then
self.ui.doc_settings:saveSetting("notebook_file", new_notebook_file)
end
end
self.ui.texteditor:openFile(notebook_file, saveNotebookFile)
elseif lfs.attributes(notebook_file, "mode") == "file" then
TextViewer.openFile(notebook_file)
end
end
-- book metadata (sdr)
function BookInfo:moveBookMetadata()
-- called by filemanagermenu only
local file_chooser = self.ui.file_chooser

View File

@@ -7,11 +7,11 @@ local Device = require("device")
local DocSettings = require("docsettings")
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
local ffiutil = require("ffi/util")
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local util = require("util")
local _ = require("gettext")
local T = ffiutil.template
local T = ffiUtil.template
local filemanagerutil = {}
@@ -89,7 +89,7 @@ function filemanagerutil.resetDocumentSettings(file)
last_page = true,
last_xpointer = true,
}
local file_abs_path = ffiutil.realpath(file)
local file_abs_path = ffiUtil.realpath(file)
if file_abs_path then
local doc_settings = DocSettings:open(file_abs_path)
for k in pairs(doc_settings.data) do
@@ -141,7 +141,7 @@ function filemanagerutil.genStatusButtonsRow(doc_settings_or_file, caller_callba
local file, summary, status
if type(doc_settings_or_file) == "table" then
file = doc_settings_or_file:readSetting("doc_path")
summary = doc_settings_or_file:readSetting("summary", {})
summary = doc_settings_or_file:readSetting("summary") or {}
status = summary.status
else
file = doc_settings_or_file
@@ -175,7 +175,7 @@ function filemanagerutil.genResetSettingsButton(doc_settings_or_file, caller_cal
file = doc_settings_or_file:readSetting("doc_path")
has_sidecar_file = true
else
file = ffiutil.realpath(doc_settings_or_file) or doc_settings_or_file
file = ffiUtil.realpath(doc_settings_or_file) or doc_settings_or_file
has_sidecar_file = DocSettings:hasSidecarFile(file)
end
local custom_cover_file = DocSettings:findCustomCoverFile(file)
@@ -262,8 +262,8 @@ function filemanagerutil.genBookInformationButton(doc_settings_or_file, book_pro
enabled = not button_disabled,
callback = function()
caller_callback()
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
FileManagerBookInfo:show(doc_settings_or_file, book_props and FileManagerBookInfo.extendProps(book_props))
local ui = require("apps/reader/readerui").instance or require("apps/filemanager/filemanager").instance
ui.bookinfo:show(doc_settings_or_file, book_props and ui.bookinfo.extendProps(book_props))
end,
}
end
@@ -275,8 +275,8 @@ function filemanagerutil.genBookCoverButton(file, book_props, caller_callback, b
enabled = (not button_disabled and (not book_props or has_cover)) and true or false,
callback = function()
caller_callback()
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
FileManagerBookInfo:onShowBookCover(file)
local ui = require("apps/reader/readerui").instance or require("apps/filemanager/filemanager").instance
ui.bookinfo:onShowBookCover(file)
end,
}
end
@@ -289,8 +289,8 @@ function filemanagerutil.genBookDescriptionButton(file, book_props, caller_callb
enabled = (not (button_disabled or book_props) or description) and true or false,
callback = function()
caller_callback()
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
FileManagerBookInfo:onShowBookDescription(description, file)
local ui = require("apps/reader/readerui").instance or require("apps/filemanager/filemanager").instance
ui.bookinfo:onShowBookDescription(description, file)
end,
}
end
@@ -305,17 +305,17 @@ function filemanagerutil.genExecuteScriptButton(file, caller_callback)
caller_callback()
local script_is_running_msg = InfoMessage:new{
-- @translators %1 is the script's programming language (e.g., shell or python), %2 is the filename
text = T(_("Running %1 script %2…"), util.getScriptType(file), BD.filename(ffiutil.basename(file))),
text = T(_("Running %1 script %2…"), util.getScriptType(file), BD.filename(ffiUtil.basename(file))),
}
UIManager:show(script_is_running_msg)
UIManager:scheduleIn(0.5, function()
local rv
if Device:isAndroid() then
Device:setIgnoreInput(true)
rv = os.execute("sh " .. ffiutil.realpath(file)) -- run by sh, because sdcard has no execute permissions
rv = os.execute("sh " .. ffiUtil.realpath(file)) -- run by sh, because sdcard has no execute permissions
Device:setIgnoreInput(false)
else
rv = os.execute(ffiutil.realpath(file))
rv = os.execute(ffiUtil.realpath(file))
end
UIManager:close(script_is_running_msg)
if rv == 0 then

View File

@@ -56,7 +56,8 @@ local settingsList = {
history_search = {category="none", event="SearchHistory", title=_("History search"), general=true},
favorites = {category="none", event="ShowColl", title=_("Favorites"), general=true},
collections = {category="none", event="ShowCollList", title=_("Collections"), general=true},
filemanager = {category="none", event="Home", title=_("File browser"), general=true, separator=true},
filemanager = {category="none", event="Home", title=_("File browser"), general=true},
notebook_file = {category="none", event="ShowNotebookFile", title=_("Notebook file"), general=true, separator=true},
----
dictionary_lookup = {category="none", event="ShowDictionaryLookup", title=_("Dictionary lookup"), general=true},
wikipedia_lookup = {category="none", event="ShowWikipediaLookup", title=_("Wikipedia lookup"), general=true, separator=true},
@@ -185,7 +186,7 @@ local settingsList = {
book_status = {category="none", event="ShowBookStatus", title=_("Book status"), reader=true},
book_info = {category="none", event="ShowBookInfo", title=_("Book information"), reader=true},
book_description = {category="none", event="ShowBookDescription", title=_("Book description"), reader=true},
book_cover = {category="none", event="ShowBookCover", title=_("Book cover"), reader=true, separator=true},
book_cover = {category="none", event="ShowBookCover", title=_("Book cover"), reader=true},
----
translate_page = {category="none", event="TranslateCurrentPage", title=_("Translate current page"), reader=true, separator=true},
----
@@ -289,6 +290,7 @@ local dispatcher_menu_order = {
"favorites",
"collections",
"filemanager",
"notebook_file",
----
"dictionary_lookup",
"wikipedia_lookup",

View File

@@ -14,13 +14,13 @@ local PathChooser = require("ui/widget/pathchooser")
local Trapper = require("ui/trapper")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local ffiutil = require("ffi/util")
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
local Screen = require("device").screen
local T = ffiutil.template
local T = ffiUtil.template
local TextEditor = WidgetContainer:extend{
name = "texteditor",
@@ -60,8 +60,8 @@ function TextEditor:isFileTypeSupported(file)
return true
end
function TextEditor:openFile(file)
self:checkEditFile(file)
function TextEditor:openFile(file, caller_callback)
self:checkEditFile(file, nil, nil, caller_callback)
end
function TextEditor:loadSettings()
@@ -72,7 +72,7 @@ function TextEditor:loadSettings()
-- NOTE: addToHistory assigns a new object
self.history = self.settings:readSetting("history") or {}
self.last_view_pos = self.settings:readSetting("last_view_pos") or {}
self.last_path = self.settings:readSetting("last_path") or ffiutil.realpath(DataStorage:getDataDir())
self.last_path = self.settings:readSetting("last_path") or ffiUtil.realpath(DataStorage:getDataDir())
self.font_face = self.settings:readSetting("font_face") or self.normal_font
self.font_size = self.settings:readSetting("font_size") or self.default_font_size
-- The font settings could be saved in G_reader_setting if we want them
@@ -342,55 +342,54 @@ function TextEditor:addToHistory(file_path)
self.history = new_history
end
function TextEditor:newFile()
function TextEditor:newFile(new_path, caller_callback)
self:loadSettings()
UIManager:show(ConfirmBox:new{
text = _([[To start editing a new file, you will have to:
- First choose a folder
- Then enter a name for the new file
- And start editing it
Do you want to proceed?]]),
ok_text = _("Yes"),
cancel_text = _("No"),
ok_callback = function()
local path_chooser = PathChooser:new{
select_file = false,
path = self.last_path,
onConfirm = function(dir_path)
local file_input
file_input = InputDialog:new{
title = _("Enter filename"),
input = dir_path == "/" and "/" or dir_path .. "/",
buttons = {{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(file_input)
end,
},
{
text = _("Edit"),
callback = function()
local file_path = file_input:getInputText()
UIManager:close(file_input)
-- Remember last_path
self.last_path = file_path:match("(.*)/")
if self.last_path == "" then self.last_path = "/" end
self:checkEditFile(file_path, false, true)
end,
},
}},
}
UIManager:show(file_input)
file_input:onShowKeyboard()
end,
}
UIManager:show(path_chooser)
end,
})
new_path = new_path or (self.last_path == "/" and "/" or self.last_path .. "/")
local file_input
file_input = InputDialog:new{
title = _("Enter filename"),
input = new_path,
buttons = {
{
{
text = _("Choose folder"),
callback = function()
UIManager:close(file_input) -- need to close keyboard
local path_chooser = PathChooser:new{
select_file = false,
path = new_path:match("(.*)/"),
onConfirm = function(dir_path)
self:newFile(dir_path .. "/", caller_callback)
end,
}
UIManager:show(path_chooser)
end,
},
},
{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(file_input)
end,
},
{
text = _("Edit"),
callback = function()
local file_path = file_input:getInputText()
UIManager:close(file_input)
-- Remember last_path
self.last_path = file_path:match("(.*)/")
if self.last_path == "" then self.last_path = "/" end
self:checkEditFile(file_path, false, true, caller_callback)
end,
},
},
},
}
UIManager:show(file_input)
file_input:onShowKeyboard()
end
function TextEditor:chooseFile()
@@ -408,23 +407,23 @@ function TextEditor:chooseFile()
UIManager:show(path_chooser)
end
function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
function TextEditor:checkEditFile(file_path, from_history, possibly_new_file, caller_callback)
self:loadSettings()
local attr = lfs.attributes(file_path)
if not possibly_new_file and not attr then
UIManager:show(ConfirmBox:new{
text = T(_("This file does not exist anymore:\n\n%1\n\nDo you want to create it and start editing it?"), BD.filepath(file_path)),
text = T(_("This file does not exist:\n\n%1\n\nDo you want to create it and start editing it?"), BD.filepath(file_path)),
ok_text = _("Create"),
ok_callback = function()
-- go again thru there with possibly_new_file=true
self:checkEditFile(file_path, from_history, true)
self:checkEditFile(file_path, from_history, true, caller_callback)
end,
})
return
end
if attr then
-- File exists: get its real path with symlink and ../ resolved
file_path = ffiutil.realpath(file_path)
file_path = ffiUtil.realpath(file_path)
attr = lfs.attributes(file_path)
end
if attr then -- File exists
@@ -451,11 +450,11 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
BD.filepath(file_path), util.getFriendlySize(attr.size)),
ok_text = _("Open"),
ok_callback = function()
self:editFile(file_path, readonly)
self:editFile(file_path, readonly, caller_callback)
end,
})
else
self:editFile(file_path, readonly)
self:editFile(file_path, readonly, caller_callback)
end
else -- File does not exist
-- Try to create it just to check if writing to it later is possible
@@ -465,7 +464,7 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
-- without saving in case the user has changed his mind.
file:close()
os.remove(file_path)
self:editFile(file_path)
self:editFile(file_path, nil, caller_callback)
else
UIManager:show(InfoMessage:new{
text = T(_("This file can not be created:\n\n%1\n\nReason: %2"), BD.filepath(file_path), err)
@@ -475,12 +474,13 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
end
end
function TextEditor:saveFileContent(file_path, content)
function TextEditor:saveFileContent(file_path, content, caller_callback)
local ok, err = util.writeToFile(content, file_path)
if ok then
if self.ui.file_chooser then
self.ui.file_chooser:refreshPath()
end
self.caller_callback = caller_callback -- will be called in self.input.close_callback
logger.info("TextEditor: saved file", file_path)
return true
end
@@ -498,7 +498,7 @@ function TextEditor:deleteFile(file_path)
return false, err
end
function TextEditor:editFile(file_path, readonly)
function TextEditor:editFile(file_path, readonly, caller_callback)
self:addToHistory(file_path)
local directory, filename = util.splitFilePathName(file_path) -- luacheck: no unused
local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) -- luacheck: no unused
@@ -578,6 +578,9 @@ function TextEditor:editFile(file_path, readonly)
if self.input.rotation_mode_backup and self.input.rotation_mode_backup ~= Screen:getRotationMode() then
Screen:setRotationMode(self.input.rotation_mode_backup)
end
if self.caller_callback then
self.caller_callback(file_path)
end
self:execWhenDoneFunc()
end,
-- File saving callback
@@ -588,7 +591,7 @@ function TextEditor:editFile(file_path, readonly)
end
if content and #content > 0 then
if not is_lua then
local ok, err = self:saveFileContent(file_path, content)
local ok, err = self:saveFileContent(file_path, content, caller_callback)
if ok then
return true, _("File saved")
else
@@ -597,7 +600,7 @@ function TextEditor:editFile(file_path, readonly)
end
local parse_error = util.checkLuaSyntax(content)
if not parse_error then
local ok, err = self:saveFileContent(file_path, content)
local ok, err = self:saveFileContent(file_path, content, caller_callback)
if ok then
return true, _("Lua syntax OK, file saved")
else
@@ -615,7 +618,7 @@ Do you really want to save to this file?
%2]]), parse_error, BD.filepath(file_path)), _("Do not save"), _("Save anyway"))
-- we'll get the safer "Do not save" on tap outside
if save_anyway then
local ok, err = self:saveFileContent(file_path, content)
local ok, err = self:saveFileContent(file_path, content, caller_callback)
if ok then
return true, _("File saved")
else
@@ -639,7 +642,7 @@ Do you want to keep this file as empty, or do you prefer to delete it?
return false, T(_("Failed deleting file: %1"), err)
end
else
local ok, err = self:saveFileContent(file_path, content)
local ok, err = self:saveFileContent(file_path, content, caller_callback)
if ok then
return true, _("File saved")
else