Merge branch 'koreader:master' into first-open-autocrop-default

reviewable/pr11438/r2
hugleo 3 months ago committed by GitHub
commit 343f800f57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1 +1 @@
Subproject commit f7482e5dea67bfb8cd90abffddffc210911bb954
Subproject commit 8f5f38d732bba170abdae5df015f9f4b475fac6e

@ -77,6 +77,7 @@ local function initDataDir()
"data/dict",
"data/tessdata",
-- "docsettings", -- created when needed
-- "hashdocsettings", -- created when needed
-- "history", -- legacy/obsolete sidecar files
"ota",
-- "patches", -- must be created manually by the interested user

@ -185,18 +185,18 @@ function FileManager:setupLayout()
return true
end
function file_chooser:onFileHold(file)
function file_chooser:onFileHold(item)
if file_manager.select_mode then
file_manager:tapPlus()
else
self:showFileDialog(file)
self:showFileDialog(item)
end
end
function file_chooser:showFileDialog(file) -- luacheck: ignore
local is_file = isFile(file)
local is_folder = lfs.attributes(file, "mode") == "directory"
local is_not_parent_folder = BaseUtil.basename(file) ~= ".."
function file_chooser:showFileDialog(item) -- luacheck: ignore
local file = item.path
local is_file = item.is_file
local is_not_parent_folder = not item.is_go_up
local function close_dialog_callback()
UIManager:close(self.file_dialog)
@ -269,14 +269,26 @@ function FileManager:setupLayout()
}
if is_file then
self.bookinfo = nil
self.book_props = nil -- in 'self' to provide access to it in CoverBrowser
local has_provider = DocumentRegistry:hasProvider(file)
if has_provider or DocSettings:hasSidecarFile(file) then
self.bookinfo = file_manager.coverbrowser and file_manager.coverbrowser:getBookInfo(file)
table.insert(buttons, filemanagerutil.genStatusButtonsRow(file, close_dialog_refresh_callback))
local has_sidecar = DocSettings:hasSidecarFile(file)
if has_provider or has_sidecar then
self.book_props = file_manager.coverbrowser and file_manager.coverbrowser:getBookInfo(file)
local doc_settings_or_file
if has_sidecar then
doc_settings_or_file = DocSettings:open(file)
if not self.book_props then
local props = doc_settings_or_file:readSetting("doc_props")
self.book_props = FileManagerBookInfo.extendProps(props, file)
self.book_props.has_cover = true -- to enable "Book cover" button, we do not know if cover exists
end
else
doc_settings_or_file = file
end
table.insert(buttons, filemanagerutil.genStatusButtonsRow(doc_settings_or_file, close_dialog_refresh_callback))
table.insert(buttons, {}) -- separator
table.insert(buttons, {
filemanagerutil.genResetSettingsButton(file, close_dialog_refresh_callback),
filemanagerutil.genResetSettingsButton(doc_settings_or_file, close_dialog_refresh_callback),
filemanagerutil.genAddRemoveFavoritesButton(file, close_dialog_callback),
})
end
@ -288,12 +300,12 @@ function FileManager:setupLayout()
file_manager:showOpenWithDialog(file)
end,
},
filemanagerutil.genBookInformationButton(file, self.bookinfo, close_dialog_callback),
filemanagerutil.genBookInformationButton(file, self.book_props, close_dialog_callback),
})
if has_provider then
table.insert(buttons, {
filemanagerutil.genBookCoverButton(file, self.bookinfo, close_dialog_callback),
filemanagerutil.genBookDescriptionButton(file, self.bookinfo, close_dialog_callback),
filemanagerutil.genBookCoverButton(file, self.book_props, close_dialog_callback),
filemanagerutil.genBookDescriptionButton(file, self.book_props, close_dialog_callback),
})
end
if Device:canExecuteScript(file) then
@ -312,9 +324,7 @@ function FileManager:setupLayout()
},
})
end
end
if is_folder then
else -- folder
local folder = BaseUtil.realpath(file)
table.insert(buttons, {
{
@ -1414,13 +1424,14 @@ function FileManager:showOpenWithDialog(file)
end
end
local t = {}
for extension, provider_key in BaseUtil.orderedPairs(associated_providers) do
for extension, provider_key in pairs(associated_providers) do
local provider = DocumentRegistry:getProviderFromKey(provider_key)
if provider then
local space = string.rep(" ", max_len - #extension)
table.insert(t, T("%1%2: %3", extension, space, provider.provider_name))
end
end
table.sort(t)
UIManager:show(InfoMessage:new{
text = table.concat(t, "\n"),
monospace_font = true,
@ -1479,9 +1490,8 @@ function FileManager:showOpenWithDialog(file)
end
function FileManager:openFile(file, provider, doc_caller_callback, aux_caller_callback)
if not provider then -- check associated
local provider_key = DocumentRegistry:getAssociatedProviderKey(file)
provider = provider_key and DocumentRegistry:getProviderFromKey(provider_key)
if provider == nil then
provider = DocumentRegistry:getProvider(file, true) -- include auxiliary
end
if provider and provider.order then -- auxiliary
if aux_caller_callback then

@ -1,7 +1,9 @@
local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog")
local Device = require("device")
local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry")
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
local Menu = require("ui/widget/menu")
local ReadCollection = require("readcollection")
local UIManager = require("ui/uimanager")
@ -49,7 +51,7 @@ end
function FileManagerCollection:onMenuHold(item)
local file = item.file
self.collfile_dialog = nil
self.bookinfo = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file)
self.book_props = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file)
local function close_dialog_callback()
UIManager:close(self.collfile_dialog)
@ -66,11 +68,29 @@ function FileManagerCollection:onMenuHold(item)
local is_currently_opened = file == (self.ui.document and self.ui.document.file)
local buttons = {}
local doc_settings_or_file = is_currently_opened and self.ui.doc_settings or file
local doc_settings_or_file
if is_currently_opened then
doc_settings_or_file = self.ui.doc_settings
if not self.book_props then
self.book_props = self.ui.doc_props
self.book_props.has_cover = true
end
else
if DocSettings:hasSidecarFile(file) then
doc_settings_or_file = DocSettings:open(file)
if not self.book_props then
local props = doc_settings_or_file:readSetting("doc_props")
self.book_props = FileManagerBookInfo.extendProps(props, file)
self.book_props.has_cover = true
end
else
doc_settings_or_file = file
end
end
table.insert(buttons, filemanagerutil.genStatusButtonsRow(doc_settings_or_file, close_dialog_update_callback))
table.insert(buttons, {}) -- separator
table.insert(buttons, {
filemanagerutil.genResetSettingsButton(file, close_dialog_update_callback, is_currently_opened),
filemanagerutil.genResetSettingsButton(doc_settings_or_file, close_dialog_update_callback, is_currently_opened),
{
text = _("Remove from favorites"),
callback = function()
@ -82,11 +102,11 @@ function FileManagerCollection:onMenuHold(item)
})
table.insert(buttons, {
filemanagerutil.genShowFolderButton(file, close_dialog_menu_callback),
filemanagerutil.genBookInformationButton(file, self.bookinfo, close_dialog_callback),
filemanagerutil.genBookInformationButton(file, self.book_props, close_dialog_callback),
})
table.insert(buttons, {
filemanagerutil.genBookCoverButton(file, self.bookinfo, close_dialog_callback),
filemanagerutil.genBookDescriptionButton(file, self.bookinfo, close_dialog_callback),
filemanagerutil.genBookCoverButton(file, self.book_props, close_dialog_callback),
filemanagerutil.genBookDescriptionButton(file, self.book_props, close_dialog_callback),
})
if Device:canExecuteScript(file) then

@ -2,6 +2,8 @@ local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog")
local CheckButton = require("ui/widget/checkbutton")
local ConfirmBox = require("ui/widget/confirmbox")
local DocSettings = require("docsettings")
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
local UIManager = require("ui/uimanager")
@ -62,32 +64,25 @@ function FileManagerHistory:fetchStatuses(count)
end
function FileManagerHistory:updateItemTable()
-- try to stay on current page
local select_number = nil
if self.hist_menu.page and self.hist_menu.perpage and self.hist_menu.page > 0 then
select_number = (self.hist_menu.page - 1) * self.hist_menu.perpage + 1
end
self.count = { all = #require("readhistory").hist,
reading = 0, abandoned = 0, complete = 0, deleted = 0, new = 0, }
local item_table = {}
for _, v in ipairs(require("readhistory").hist) do
if self:isItemMatch(v) then
if self.is_frozen and v.status == "complete" then
v.mandatory_dim = true
end
v.mandatory_dim = (self.is_frozen and v.status == "complete") and true or nil
table.insert(item_table, v)
end
if self.statuses_fetched then
self.count[v.status] = self.count[v.status] + 1
end
end
local subtitle
local subtitle = ""
if self.search_string then
subtitle = T(_("Search results (%1)"), #item_table)
elseif self.filter ~= "all" then
subtitle = T(_("Status: %1 (%2)"), filter_text[self.filter]:lower(), #item_table)
end
self.hist_menu:switchItemTable(nil, item_table, select_number, nil, subtitle or "")
self.hist_menu:switchItemTable(nil, item_table, -1, nil, subtitle)
end
function FileManagerHistory:isItemMatch(item)
@ -126,7 +121,7 @@ end
function FileManagerHistory:onMenuHold(item)
local file = item.file
self.histfile_dialog = nil
self.bookinfo = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file)
self.book_props = self.ui.coverbrowser and self.ui.coverbrowser:getBookInfo(file)
local function close_dialog_callback()
UIManager:close(self.histfile_dialog)
@ -137,7 +132,7 @@ function FileManagerHistory:onMenuHold(item)
end
local function close_dialog_update_callback()
UIManager:close(self.histfile_dialog)
if self._manager.filter ~= "all" then
if self._manager.filter ~= "all" or self._manager.is_frozen then
self._manager:fetchStatuses(false)
else
self._manager.statuses_fetched = false
@ -148,13 +143,31 @@ function FileManagerHistory:onMenuHold(item)
local is_currently_opened = file == (self.ui.document and self.ui.document.file)
local buttons = {}
local doc_settings_or_file
if is_currently_opened then
doc_settings_or_file = self.ui.doc_settings
if not self.book_props then
self.book_props = self.ui.doc_props
self.book_props.has_cover = true
end
else
if DocSettings:hasSidecarFile(file) then
doc_settings_or_file = DocSettings:open(file)
if not self.book_props then
local props = doc_settings_or_file:readSetting("doc_props")
self.book_props = FileManagerBookInfo.extendProps(props, file)
self.book_props.has_cover = true
end
else
doc_settings_or_file = file
end
end
if not item.dim then
local doc_settings_or_file = is_currently_opened and self.ui.doc_settings or file
table.insert(buttons, filemanagerutil.genStatusButtonsRow(doc_settings_or_file, close_dialog_update_callback))
table.insert(buttons, {}) -- separator
end
table.insert(buttons, {
filemanagerutil.genResetSettingsButton(file, close_dialog_update_callback, is_currently_opened),
filemanagerutil.genResetSettingsButton(doc_settings_or_file, close_dialog_update_callback, is_currently_opened),
filemanagerutil.genAddRemoveFavoritesButton(file, close_dialog_callback, item.dim),
})
table.insert(buttons, {
@ -182,11 +195,11 @@ function FileManagerHistory:onMenuHold(item)
})
table.insert(buttons, {
filemanagerutil.genShowFolderButton(file, close_dialog_menu_callback, item.dim),
filemanagerutil.genBookInformationButton(file, self.bookinfo, close_dialog_callback, item.dim),
filemanagerutil.genBookInformationButton(file, self.book_props, close_dialog_callback, item.dim),
})
table.insert(buttons, {
filemanagerutil.genBookCoverButton(file, self.bookinfo, close_dialog_callback, item.dim),
filemanagerutil.genBookDescriptionButton(file, self.bookinfo, close_dialog_callback, item.dim),
filemanagerutil.genBookCoverButton(file, self.book_props, close_dialog_callback, item.dim),
filemanagerutil.genBookDescriptionButton(file, self.book_props, close_dialog_callback, item.dim),
})
self.histfile_dialog = ButtonDialog:new{

@ -108,9 +108,14 @@ function filemanagerutil.getStatus(file)
end
-- Set a document status ("reading", "complete", or "abandoned")
function filemanagerutil.setStatus(file, status)
function filemanagerutil.setStatus(doc_settings_or_file, status)
-- In case the book doesn't have a sidecar file, this'll create it
local doc_settings = DocSettings:open(file)
local doc_settings
if type(doc_settings_or_file) == "table" then
doc_settings = doc_settings_or_file
else
doc_settings = DocSettings:open(doc_settings_or_file)
end
local summary = doc_settings:readSetting("summary", {})
summary.status = status
summary.modified = os.date("%Y-%m-%d", os.time())
@ -131,9 +136,9 @@ end
-- Generate all book status file dialog buttons in a row
function filemanagerutil.genStatusButtonsRow(doc_settings_or_file, caller_callback)
local file, summary, status
if type(doc_settings_or_file) == "table" then -- currently opened file
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", {})
status = summary.status
else
file = doc_settings_or_file
@ -146,7 +151,7 @@ function filemanagerutil.genStatusButtonsRow(doc_settings_or_file, caller_callba
enabled = status ~= to_status,
callback = function()
summary.status = to_status
filemanagerutil.setStatus(file, to_status)
filemanagerutil.setStatus(doc_settings_or_file, to_status)
UIManager:broadcastEvent(Event:new("DocSettingsItemsChanged", file, { summary = summary })) -- for CoverBrowser
caller_callback()
end,
@ -160,9 +165,16 @@ function filemanagerutil.genStatusButtonsRow(doc_settings_or_file, caller_callba
end
-- Generate "Reset" file dialog button
function filemanagerutil.genResetSettingsButton(file, caller_callback, button_disabled)
file = ffiutil.realpath(file) or file
local has_sidecar_file = DocSettings:hasSidecarFile(file)
function filemanagerutil.genResetSettingsButton(doc_settings_or_file, caller_callback, button_disabled)
local doc_settings, file, has_sidecar_file
if type(doc_settings_or_file) == "table" then
doc_settings = doc_settings_or_file
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
has_sidecar_file = DocSettings:hasSidecarFile(file)
end
local custom_cover_file = DocSettings:findCustomCoverFile(file)
local has_custom_cover_file = custom_cover_file and true or false
local custom_metadata_file = DocSettings:findCustomMetadataFile(file)
@ -185,7 +197,7 @@ function filemanagerutil.genResetSettingsButton(file, caller_callback, button_di
custom_cover_file = check_button_cover.checked and custom_cover_file,
custom_metadata_file = check_button_metadata.checked and custom_metadata_file,
}
DocSettings:open(file):purge(nil, data_to_purge)
(doc_settings or DocSettings:open(file)):purge(nil, data_to_purge)
if data_to_purge.custom_cover_file or data_to_purge.custom_metadata_file then
UIManager:broadcastEvent(Event:new("InvalidateMetadataCache", file))
end
@ -260,23 +272,23 @@ function filemanagerutil.genShowFolderButton(file, caller_callback, button_disab
}
end
function filemanagerutil.genBookInformationButton(file, bookinfo, caller_callback, button_disabled)
function filemanagerutil.genBookInformationButton(file, book_props, caller_callback, button_disabled)
return {
text = _("Book information"),
enabled = not button_disabled,
callback = function()
caller_callback()
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
FileManagerBookInfo:show(file, bookinfo and FileManagerBookInfo.extendProps(bookinfo))
FileManagerBookInfo:show(file, book_props and FileManagerBookInfo.extendProps(book_props))
end,
}
end
function filemanagerutil.genBookCoverButton(file, bookinfo, caller_callback, button_disabled)
local has_cover = bookinfo and bookinfo.has_cover
function filemanagerutil.genBookCoverButton(file, book_props, caller_callback, button_disabled)
local has_cover = book_props and book_props.has_cover
return {
text = _("Book cover"),
enabled = (not button_disabled and (not bookinfo or has_cover)) and true or false,
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")
@ -285,12 +297,12 @@ function filemanagerutil.genBookCoverButton(file, bookinfo, caller_callback, but
}
end
function filemanagerutil.genBookDescriptionButton(file, bookinfo, caller_callback, button_disabled)
local description = bookinfo and bookinfo.description
function filemanagerutil.genBookDescriptionButton(file, book_props, caller_callback, button_disabled)
local description = book_props and book_props.description
return {
text = _("Book description"),
-- enabled for deleted books if description is kept in CoverBrowser bookinfo cache
enabled = (not (button_disabled or bookinfo) or description) and true or false,
enabled = (not (button_disabled or book_props) or description) and true or false,
callback = function()
caller_callback()
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")

@ -1058,7 +1058,9 @@ function ReaderLink:onGoToPageLink(ges, internal_links_only, max_distance)
for _, link in ipairs(links) do
-- link.uri may be an empty string with some invalid links: ignore them
if link.section or (link.uri and link.uri ~= "") then
if link.segments then
-- Note: we may get segments empty in some conditions (in which
-- case we'll fallback to the 'else' branch and using x/y)
if link.segments and #link.segments > 0 then
-- With segments, each is a horizontal segment, with start_x < end_x,
-- and we should compute the distance from gesture position to
-- each segment.

@ -1029,8 +1029,15 @@ function ReaderPaging:onGotoPageRel(diff)
goto_end(y)
goto_end(x)
elseif new_page > 0 then
-- Be sure that the new and old view areas are reset so that no value is carried over to next page.
-- Without this, we would have panned_y = new_va.y - old_va.y > 0, and panned_y will be added to the next page's y direction.
-- This occurs when the current page has a y > 0 position (for example, a cropped page) and can fit the whole page height,
-- while the next page needs scrolling in the height.
self:_gotoPage(new_page)
new_va = self.visible_area:copy()
old_va = self.visible_area
goto_end(y, -y_diff)
goto_end(x, -x_diff)
else
goto_end(x)
end

@ -13,6 +13,7 @@ local Utf8Proc = require("ffi/utf8proc")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local _ = require("gettext")
local C_ = _.pgettext
local Screen = Device.screen
local T = require("ffi/util").template
@ -196,8 +197,8 @@ function ReaderSearch:onShowFulltextSearchInput()
end,
},
{
-- @translators Search all entries in entire document
text = _("All"),
-- @translators Find all results in entire document, button displayed on the search bar, should be short.
text = C_("Search text", "All"),
callback = function()
self:searchCallback()
end,

@ -695,7 +695,7 @@ function ReaderView:recalculate()
-- start from right of page_area
self.visible_area.x = self.page_area.x + self.page_area.w - self.visible_area.w
end
if self.ui.zooming.zoom_bottom_to_top then
if self.document.configurable.zoom_direction >= 2 and self.document.configurable.zoom_direction <= 5 then -- zoom_bottom_to_top
-- starts from bottom of page_area
self.visible_area.y = self.page_area.y + self.page_area.h - self.visible_area.h
else

@ -533,7 +533,7 @@ function ReaderZooming:getZoom(pageno)
local ubbox_dimen = self.ui.document:getUsedBBoxDimensions(pageno, 1)
-- if bbox is larger than the native page dimension render the full page
-- See discussion in koreader/koreader#970.
if (ubbox_dimen.w <= page_size.w and ubbox_dimen.h <= page_size.h) or (self.ui.document.configurable.trim_page == 1) then
if ubbox_dimen.w <= page_size.w and ubbox_dimen.h <= page_size.h then
page_size = ubbox_dimen
self.view:onBBoxUpdate(ubbox_dimen)
else

@ -470,21 +470,22 @@ function ReaderUI:init()
-- And have an extended and customized copy in memory for quick access.
self.doc_props = FileManagerBookInfo.extendProps(props, self.document.file)
-- Set "reading" status if there is no status.
local md5 = self.doc_settings:readSetting("partial_md5_checksum")
if md5 == nil then
md5 = util.partialMD5(self.document.file)
self.doc_settings:saveSetting("partial_md5_checksum", md5)
end
local summary = self.doc_settings:readSetting("summary", {})
if summary.status == nil then
summary.status = "reading"
summary.modified = os.date("%Y-%m-%d", os.time())
end
local md5 = self.doc_settings:readSetting("partial_md5_checksum")
if md5 == nil then
md5 = util.partialMD5(self.document.file)
self.doc_settings:saveSetting("partial_md5_checksum", md5)
if summary.status ~= "complete" or not G_reader_settings:isTrue("history_freeze_finished_books") then
require("readhistory"):addItem(self.document.file) -- (will update "lastfile")
end
require("readhistory"):addItem(self.document.file) -- (will update "lastfile")
-- After initialisation notify that document is loaded and rendered
-- CREngine only reports correct page count after rendering is done
-- Need the same event for PDF document

@ -560,7 +560,6 @@ local KindleOasis = Kindle:extend{
model = "KindleOasis",
isTouchDevice = yes,
hasFrontlight = yes,
hasLightSensor = yes,
hasKeys = yes,
hasGSensor = yes,
display_dpi = 300,

@ -35,6 +35,8 @@ local function isFile(file)
return lfs.attributes(file, "mode") == "file"
end
local is_history_location_enabled = isDir(HISTORY_DIR)
local doc_hash_cache = {}
local is_hash_location_enabled
@ -164,7 +166,7 @@ function DocSettings:findSidecarFile(doc_path, no_legacy)
return sidecar_file, location
end
end
if not no_legacy then
if is_history_location_enabled and not no_legacy then
sidecar_file = self:getHistoryPath(doc_path)
if isFile(sidecar_file) then
return sidecar_file, "hist" -- for isSidecarFileNotInPreferredLocation() used in moveBookMetadata
@ -238,7 +240,7 @@ function DocSettings:open(doc_path)
new.hash_sidecar_dir = new:getSidecarDir(doc_path, "hash")
hash_sidecar_file = new.hash_sidecar_dir .. "/" .. new.sidecar_filename
end
local history_file = new:getHistoryPath(doc_path)
local history_file = is_history_location_enabled and new:getHistoryPath(doc_path)
-- Candidates list, in order of priority:
local candidates_list = {
@ -257,9 +259,9 @@ function DocSettings:open(doc_path)
-- Backup file of new sidecar file in hashdocsettings folder
hash_sidecar_file and (hash_sidecar_file .. ".old") or "",
-- Legacy history folder
history_file,
history_file or "",
-- Backup file in legacy history folder
history_file .. ".old",
history_file and (history_file .. ".old") or "",
-- Legacy kpdfview setting
doc_path .. ".kpdfview.lua",
}

@ -86,18 +86,19 @@ end
--- Returns the preferred registered document handler or fallback provider.
-- @string file
-- @bool include_aux include auxiliary (non-document) providers
-- @treturn table provider
function DocumentRegistry:getProvider(file)
function DocumentRegistry:getProvider(file, include_aux)
local providers = self:getProviders(file)
if providers then
if providers or include_aux then
-- associated provider
local provider_key = DocumentRegistry:getAssociatedProviderKey(file)
local provider = provider_key and self.known_providers[provider_key]
if provider and not provider.order then -- excluding auxiliary
if provider and (not provider.order or include_aux) then -- excluding auxiliary by default
return provider
end
-- highest weighted provider
return providers[1].provider
return providers and providers[1].provider
end
return self:getFallbackProvider()
end

@ -3,7 +3,6 @@ local DocSettings = require("docsettings")
local datetime = require("datetime")
local dump = require("dump")
local ffiutil = require("ffi/util")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
local util = require("util")
local joinPath = ffiutil.joinPath
local lfs = require("libs/libkoreader-lfs")
@ -323,36 +322,32 @@ end
--- Adds new item (last opened document) to the top of the history list.
-- If item time (ts) is passed, add item to the history list at this time position.
function ReadHistory:addItem(file, ts, no_flush)
if file ~= nil and lfs.attributes(file, "mode") == "file" then
local index = self:getIndexByFile(realpath(file))
if index then -- book is in the history already
if ts and self.hist[index].time == ts then
return -- legacy item already added
end
if not ts and G_reader_settings:isTrue("history_freeze_finished_books")
and filemanagerutil.getStatus(file) == "complete" then
return -- book marked as finished, do not update timestamps of item and file
end
end
local now = ts or os.time()
local mtime = lfs.attributes(file, "modification")
lfs.touch(file, now, mtime) -- update book access time for sorting by last read date
if index == 1 and not ts then -- last book, update access time only
self.hist[1].time = now
self.hist[1].mandatory = getMandatory(now)
else -- old or new book
if index then -- old book
table.remove(self.hist, index)
end
index = ts and self:getIndexByTime(ts, file:gsub(".*/", "")) or 1
table.insert(self.hist, index, buildEntry(now, file))
end
if not no_flush then
self:_reduce()
self:_flush()
file = realpath(file)
if not file or (ts and lfs.attributes(file, "mode") ~= "file") then
return -- bad legacy item
end
local index = self:getIndexByFile(file)
if ts and index and self.hist[index].time == ts then
return -- legacy item already added
end
local now = ts or os.time()
local mtime = lfs.attributes(file, "modification")
lfs.touch(file, now, mtime) -- update book access time for sorting by last read date
if index == 1 and not ts then -- last book, update access time only
self.hist[1].time = now
self.hist[1].mandatory = getMandatory(now)
else -- old or new book
if index then -- old book
table.remove(self.hist, index)
end
return true -- used while adding legacy items
index = ts and self:getIndexByTime(ts, file:gsub(".*/", "")) or 1
table.insert(self.hist, index, buildEntry(now, file))
end
if not no_flush then
self:_reduce()
self:_flush()
end
return true -- used while adding legacy items
end
--- Updates last book access time on closing the document.

@ -965,6 +965,8 @@ This tweak can be duplicated as a user style tweak when books contain footnotes
.footnote, .footnotes, .fn,
.note, .note1, .note2, .note3,
.ntb, .ntb-txt, .ntb-txt-j,
.fnote, .fnote1,
.duokan-footnote-item, /* Common chinese books */
.przypis, .przypis1, /* Polish footnotes */
.voetnoten /* Dutch footnotes */
{
@ -1114,6 +1116,21 @@ If the footnote text uses variable or absolute font sizes, line height or vertic
font-size: inherit !important;
line-height: inherit !important;
vertical-align: inherit !important;
}
]],
},
{
id = "inpage_footnote_combine_non_linear",
title = _("Combine footnotes in a non-linear flow"),
description = _([[
This will mark each section of consecutive footnotes (at their original location in the book) as being non-linear.
The menu checkbox "Hide non-linear fragments" will then be available after the document is reopened, allowing to hide these sections from the regular flow: they will be skipped when turning pages and not considered in the various book & chapter progress and time to read features.]]),
priority = 6,
css = [[
*, autoBoxing {
-cr-hint: late;
-cr-only-if: inpage-footnote;
-cr-hint: non-linear-combining;
}
]],
},

@ -0,0 +1,68 @@
local socket = require("socket")
local logger = require("logger")
-- Reference:
-- https://lunarmodules.github.io/luasocket/tcp.html
-- Drop-in alternative to streammessagequeueserver.lua, using
-- LuaSocket instead of ZeroMQ.
-- This SimpleTCPServer is still tied to HTTP, expecting lines of headers,
-- a blank like marking the end of the input request.
local SimpleTCPServer = {
host = nil,
port = nil,
}
function SimpleTCPServer:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
if o.init then o:init() end
return o
end
function SimpleTCPServer:start()
self.server = socket.bind(self.host, self.port)
self.server:settimeout(0.01) -- set timeout (10ms)
logger.dbg("SimpleTCPServer: Server listening on port " .. self.port)
end
function SimpleTCPServer:stop()
self.server:close()
end
function SimpleTCPServer:waitEvent()
local client = self.server:accept() -- wait for a client to connect
if client then
-- We expect to get all headers in 100ms. We will block during this timeframe.
client:settimeout(0.1, "t")
local lines = {}
while true do
local data = client:receive("*l") -- read a line from input
if not data then -- timeout
client:close()
break
end
if data == "" then -- proper empty line after request headers
table.insert(lines, data) -- keep it in content
data = table.concat(lines, "\r\n")
logger.dbg("SimpleTCPServer: Received data: ", data)
-- Give us more time to process the request and send the response
client:settimeout(0.5, "t")
self.receiveCallback(data, client)
-- This should call SimpleTCPServer:send() to send
-- the response and close this connection.
else
table.insert(lines, data)
end
end
end
end
function SimpleTCPServer:send(data, client)
client:send(data) -- send the response back to the client
client:close() -- close the connection to the client
end
return SimpleTCPServer

@ -0,0 +1,86 @@
local ffi = require("ffi")
local logger = require("logger")
local MessageQueue = require("ui/message/messagequeue")
local _ = require("ffi/zeromq_h")
local czmq = ffi.load("libs/libczmq.so.1")
local C = ffi.C
local StreamMessageQueueServer = MessageQueue:extend{
host = nil,
port = nil,
}
function StreamMessageQueueServer:start()
self.context = czmq.zctx_new()
self.socket = czmq.zsocket_new(self.context, C.ZMQ_STREAM)
self.poller = czmq.zpoller_new(self.socket, nil)
local endpoint = string.format("tcp://%s:%d", self.host, self.port)
logger.dbg("StreamMessageQueueServer: Binding to endpoint", endpoint)
local rc = czmq.zsocket_bind(self.socket, endpoint)
-- If success, rc is port number
if rc == -1 then
logger.err("StreamMessageQueueServer: Cannot bind to ", endpoint)
end
end
function StreamMessageQueueServer:stop()
if self.poller ~= nil then
czmq.zpoller_destroy(ffi.new('zpoller_t *[1]', self.poller))
end
if self.socket ~= nil then
czmq.zsocket_destroy(self.context, self.socket)
end
if self.context ~= nil then
czmq.zctx_destroy(ffi.new('zctx_t *[1]', self.context))
end
end
function StreamMessageQueueServer:handleZframe(frame)
local size = czmq.zframe_size(frame)
local data = nil
if size > 0 then
local frame_data = czmq.zframe_data(frame)
if frame_data ~= nil then
data = ffi.string(frame_data, size)
end
end
czmq.zframe_destroy(ffi.new('zframe_t *[1]', frame))
return data
end
function StreamMessageQueueServer:waitEvent()
local request, id
while czmq.zpoller_wait(self.poller, 0) ~= nil do
-- See about ZMQ_STREAM and these 2 frames at http://hintjens.com/blog:42
local id_frame = czmq.zframe_recv(self.socket)
if id_frame ~= nil then
id = id_frame
end
local frame = czmq.zframe_recv(self.socket)
if frame ~= nil then
local data = self:handleZframe(frame)
if data then
logger.dbg("StreamMessageQueueServer: Received data: ", data)
request = data
end
end
end
if self.receiveCallback and request ~= nil then
self.receiveCallback(request, id)
end
end
function StreamMessageQueueServer:send(data, id_frame)
czmq.zframe_send(ffi.new('zframe_t *[1]', id_frame), self.socket, C.ZFRAME_MORE + C.ZFRAME_REUSE)
czmq.zmq_send(self.socket, ffi.cast("unsigned char*", data), #data, C.ZFRAME_MORE)
-- Note: We can't use czmq.zstr_send(self.socket, data), which would stop on the first
-- null byte in data (Lua strings can have null bytes inside).
-- Close connection
czmq.zframe_send(ffi.new('zframe_t *[1]', id_frame), self.socket, C.ZFRAME_MORE)
czmq.zmq_send(self.socket, nil, 0, 0)
end
return StreamMessageQueueServer

@ -1439,11 +1439,13 @@ end
-- Process all pending events on all registered ZMQs.
function UIManager:processZMQs()
if self._zeromqs[1] then
self.event_hook:execute("InputEvent")
end
local sent_InputEvent = false
for _, zeromq in ipairs(self._zeromqs) do
for input_event in zeromq.waitEvent, zeromq do
if not sent_InputEvent then
self.event_hook:execute("InputEvent")
sent_InputEvent = true
end
self:handleInputEvent(input_event)
end
end

@ -206,6 +206,42 @@ local FileChooser = Menu:extend{
return item.opened and string.format("%d %%", 100 * item.percent_finished) or ""
end,
},
percent_natural = {
-- sort 90% > 50% > 0% > unopened > 100%
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.percent_finished == b.percent_finished then
return natsort(a.text, b.text)
elseif a.percent_finished == 1 then
return false
elseif b.percent_finished == 1 then
return true
else
return a.percent_finished > b.percent_finished
end
end
return sortfunc, 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 numbers
item.percent_finished = math.floor((percent_finished or -1) * 100) / 100
end,
mandatory_func = function(item)
return item.opened and string.format("%d %%", 100 * item.percent_finished) or ""
end,
},
},
}
@ -232,6 +268,9 @@ end
function FileChooser:init()
self.path_items = {}
if lfs.attributes(self.path, "mode") ~= "directory" then
self.path = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir()
end
self.item_table = self:genItemTableFromPath(self.path)
Menu.init(self) -- call parent's init()
end
@ -540,7 +579,7 @@ function FileChooser:onMenuSelect(item)
end
function FileChooser:onMenuHold(item)
self:onFileHold(item.path)
self:onFileHold(item)
return true
end
@ -549,7 +588,7 @@ function FileChooser:onFileSelect(file)
return true
end
function FileChooser:onFileHold(file)
function FileChooser:onFileHold(item)
return true
end

@ -242,12 +242,13 @@ function CoverMenu:updateItems(select_number)
-- Replace it with ours
-- This causes luacheck warning: "shadowing upvalue argument 'self' on line 34".
-- Ignoring it (as done in filemanager.lua for the same showFileDialog)
self.showFileDialog = function(self, file) -- luacheck: ignore
self.showFileDialog = function(self, item) -- luacheck: ignore
local file = item.path
-- Call original function: it will create a ButtonDialog
-- and store it as self.file_dialog, and UIManager:show() it.
self.showFileDialog_orig(self, file)
self.showFileDialog_orig(self, item)
local bookinfo = self.bookinfo -- getBookInfo(file) called by FileManager
local bookinfo = self.book_props -- getBookInfo(file) called by FileManager
if not bookinfo or bookinfo._is_directory then
-- If no bookinfo (yet) about this file, or it's a directory, let the original dialog be
return true
@ -326,7 +327,7 @@ function CoverMenu:onHistoryMenuHold(item)
self.onMenuHold_orig(self, item)
local file = item.file
local bookinfo = self.bookinfo -- getBookInfo(file) called by FileManagerHistory
local bookinfo = self.book_props -- getBookInfo(file) called by FileManagerHistory
if not bookinfo then
-- If no bookinfo (yet) about this file, let the original dialog be
return true
@ -397,7 +398,7 @@ function CoverMenu:onCollectionsMenuHold(item)
self.onMenuHold_orig(self, item)
local file = item.file
local bookinfo = self.bookinfo -- getBookInfo(file) called by FileManagerCollection
local bookinfo = self.book_props -- getBookInfo(file) called by FileManagerCollection
if not bookinfo then
-- If no bookinfo (yet) about this file, let the original dialog be
return true

@ -27,7 +27,6 @@ local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
@ -115,7 +114,7 @@ local ListMenuItem = InputContainer:extend{
}
function ListMenuItem:init()
-- filepath may be provided as 'file' (history) or 'path' (filechooser)
-- filepath may be provided as 'file' (history, collection) or 'path' (filechooser)
-- store it as attribute so we can use it elsewhere
self.filepath = self.entry.file or self.entry.path
@ -224,9 +223,8 @@ function ListMenuItem:update()
self.menu.cover_specs = false
end
local file_mode = lfs.attributes(self.filepath, "mode")
if file_mode == "directory" then
self.is_directory = true
self.is_directory = not (self.entry.is_file or self.entry.file)
if self.is_directory then
-- nb items on the right, directory name on the left
local wright = TextWidget:new{
text = self.mandatory or "",
@ -261,13 +259,9 @@ function ListMenuItem:update()
},
},
}
else
local is_file_selected = self.menu.filemanager and self.menu.filemanager.selected_files
and self.menu.filemanager.selected_files[self.filepath]
if file_mode ~= "file" or is_file_selected then
self.file_deleted = true -- dim file
end
-- File
else -- file
self.file_deleted = self.entry.dim -- entry with deleted file from History or selected file from FM
local fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil
local bookinfo = BookInfoManager:getBookInfo(self.filepath, self.do_cover_image)
@ -437,7 +431,7 @@ function ListMenuItem:update()
local wfileinfo = TextWidget:new{
text = fileinfo_str,
face = Font:getFace("cfont", fontsize_info),
fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil,
fgcolor = fgcolor,
}
table.insert(wright_items, wfileinfo)
end
@ -446,7 +440,7 @@ function ListMenuItem:update()
local wpageinfo = TextWidget:new{
text = pages_str,
face = Font:getFace("cfont", fontsize_info),
fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil,
fgcolor = fgcolor,
}
table.insert(wright_items, wpageinfo)
end
@ -584,7 +578,7 @@ function ListMenuItem:update()
height_overflow_show_ellipsis = true,
alignment = "left",
bold = true,
fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil,
fgcolor = fgcolor,
}
end
local build_authors = function(height)
@ -601,7 +595,7 @@ function ListMenuItem:update()
height_adjust = true,
height_overflow_show_ellipsis = true,
alignment = "left",
fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil,
fgcolor = fgcolor,
}
end
while true do
@ -727,7 +721,7 @@ function ListMenuItem:update()
local wfileinfo = TextWidget:new{
text = fileinfo_str,
face = Font:getFace("cfont", fontsize_info),
fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil,
fgcolor = fgcolor,
}
local wpageinfo = TextWidget:new{ -- Empty but needed for similar positionning
text = "",
@ -762,7 +756,7 @@ function ListMenuItem:update()
face = Font:getFace("cfont", fontsize_no_bookinfo),
width = dimen.w - 2 * Screen:scaleBySize(10) - wright_width - wright_right_padding,
alignment = "left",
fgcolor = self.file_deleted and Blitbuffer.COLOR_DARK_GRAY or nil,
fgcolor = fgcolor,
}
-- reduce font size for next loop, in case text widget is too large to fit into ListMenuItem
fontsize_no_bookinfo = fontsize_no_bookinfo - fontsize_dec_step

@ -25,7 +25,6 @@ local UnderlineContainer = require("ui/widget/container/underlinecontainer")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
@ -462,9 +461,8 @@ function MosaicMenuItem:update()
self.menu.cover_specs = false
end
local file_mode = lfs.attributes(self.filepath, "mode")
if file_mode == "directory" then
self.is_directory = true
self.is_directory = not (self.entry.is_file or self.entry.file)
if self.is_directory then
-- Directory : rounded corners
local margin = Screen:scaleBySize(5) -- make directories less wide
local padding = Screen:scaleBySize(5)
@ -528,13 +526,8 @@ function MosaicMenuItem:update()
BottomContainer:new{ dimen = dimen_in, nbitems},
},
}
else
local is_file_selected = self.menu.filemanager and self.menu.filemanager.selected_files
and self.menu.filemanager.selected_files[self.filepath]
if file_mode ~= "file" or is_file_selected then
self.file_deleted = true -- dim file
end
-- File : various appearances
else -- file
self.file_deleted = self.entry.dim -- entry with deleted file from History or selected file from FM
if self.do_hint_opened and DocSettings:hasSidecarFile(self.filepath) then
self.been_opened = true

@ -0,0 +1,6 @@
local _ = require("gettext")
return {
name = "httpinspector",
fullname = _("HTTP KOReader Inspector"),
description = _([[Allow browsing KOReader internal objects over HTTP. This is aimed at developers, and may pose some security risks. Only enable this on networks you can trust.]]),
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save