hius07 2 weeks ago committed by GitHub
commit f483e0d635
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -73,11 +73,18 @@ end
-- Purge doc settings except kept
function filemanagerutil.resetDocumentSettings(file)
local settings_to_keep = {
annotations = true,
annotations_paging = true,
annotations_rolling = true,
bookmarks = true,
bookmarks_paging = true,
bookmarks_rolling = true,
bookmarks_sorted_20220106 = true,
bookmarks_version = true,
cre_dom_version = true,
highlight = true,
highlight_paging = true,
highlight_rolling = true,
highlights_imported = true,
last_page = true,
last_xpointer = true,

@ -0,0 +1,425 @@
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local _ = require("gettext")
local T = require("ffi/util").template
local ReaderAnnotation = WidgetContainer:extend{
annotations = nil, -- array sorted by annotation position order, ascending
}
-- build, read, save
function ReaderAnnotation:buildAnnotation(bm, highlights, init)
-- bm: associated single bookmark ; highlights: tables with all highlights
local note = bm.text
if note == "" then
note = nil
end
local chapter = bm.chapter
local hl, pageno = self:getHighlightByDatetime(highlights, bm.datetime)
if init then
if note and self.ui.bookmark:isBookmarkAutoText(bm) then
note = nil
end
if chapter == nil then
chapter = self.ui.toc:getTocTitleByPage(bm.page)
end
pageno = self.ui.paging and bm.page or self.document:getPageFromXPointer(bm.page)
end
if not hl then -- page bookmark or orphaned bookmark
hl = {}
if bm.highlighted then -- orphaned bookmark
hl.drawer = self.view.highlight.saved_drawer
hl.color = self.view.highlight.saved_color
if self.ui.paging then
if bm.pos0.page == bm.pos1.page then
hl.pboxes = self.document:getPageBoxesFromPositions(bm.page, bm.pos0, bm.pos1)
else -- multi-page highlight, restore the first box only
hl.pboxes = self.document:getPageBoxesFromPositions(bm.page, bm.pos0, bm.pos0)
end
end
end
end
if self.ui.paging then
-- old single-page reflow highlights do not have page in position
if not bm.pos0.page then
bm.pos0.page = bm.page
bm.pos1.page = bm.page
end
end
return { -- annotation
datetime = bm.datetime, -- creation time, not changeable
drawer = hl.drawer, -- highlight drawer
color = hl.color, -- highlight color
text = bm.notes, -- highlighted text, editable
text_edited = hl.edited, -- true if highlighted text has been edited
note = note, -- user's note, editable
chapter = chapter, -- book chapter title
pageno = pageno, -- book page number
page = bm.page, -- highlight location, xPointer or number (pdf)
pos0 = bm.pos0, -- highlight start position, xPointer (== page) or table (pdf)
pos1 = bm.pos1, -- highlight end position, xPointer or table (pdf)
pboxes = hl.pboxes, -- pdf pboxes, used only and changeable by addMarkupAnnotation
ext = hl.ext, -- pdf multi-page highlight
}
end
function ReaderAnnotation:getHighlightByDatetime(highlights, datetime)
for pageno, page_highlights in pairs(highlights) do
for _, highlight in ipairs(page_highlights) do
if highlight.datetime == datetime then
return highlight, pageno
end
end
end
end
function ReaderAnnotation:getAnnotationsFromBookmarksHighlights(bookmarks, highlights, init)
local annotations = {}
for i = #bookmarks, 1, -1 do
table.insert(annotations, self:buildAnnotation(bookmarks[i], highlights, init))
end
if init then
self:sortItems(annotations)
end
return annotations
end
function ReaderAnnotation:onReadSettings(config)
local annotations = config:readSetting("annotations")
if annotations then
-- KOHighlights may set this key when it has merged annotations from different sources:
-- we want to make sure they are updated and sorted
local needs_update = config:isTrue("annotations_externally_modified")
local needs_sort -- if incompatible annotations were built of old highlights/bookmarks
-- Annotation formats in crengine and mupdf are incompatible.
local has_annotations = #annotations > 0
local annotations_type = has_annotations and type(annotations[1].page)
if self.ui.rolling and annotations_type ~= "string" then -- incompatible format loaded, or empty
if has_annotations then -- backup incompatible format if not empty
config:saveSetting("annotations_paging", annotations)
end
-- load compatible format
annotations = config:readSetting("annotations_rolling") or {}
config:delSetting("annotations_rolling")
needs_sort = true
elseif self.ui.paging and annotations_type ~= "number" then
if has_annotations then
config:saveSetting("annotations_rolling", annotations)
end
annotations = config:readSetting("annotations_paging") or {}
config:delSetting("annotations_paging")
needs_sort = true
end
self.annotations = annotations
if needs_update or needs_sort then
if self.ui.rolling then
self.ui:registerPostInitCallback(function()
self:updatedAnnotations(needs_update, needs_sort)
end)
else
self:updatedAnnotations(needs_update, needs_sort)
end
config:delSetting("annotations_externally_modified")
end
else -- first run
if self.ui.rolling then
self.ui:registerPostInitCallback(function()
self:migrateToAnnotations(config)
end)
else
self:migrateToAnnotations(config)
end
end
end
function ReaderAnnotation:migrateToAnnotations(config)
local bookmarks = config:readSetting("bookmarks") or {}
local highlights = config:readSetting("highlight") or {}
if config:hasNot("highlights_imported") then
-- before 2014, saved highlights were not added to bookmarks when they were created.
for page, hls in pairs(highlights) do
for _, hl in ipairs(hls) do
local hl_page = self.ui.paging and page or hl.pos0
-- highlights saved by some old versions don't have pos0 field
-- we just ignore those highlights
if hl_page then
local item = {
datetime = hl.datetime,
highlighted = true,
notes = hl.text,
page = hl_page,
pos0 = hl.pos0,
pos1 = hl.pos1,
}
if self.ui.paging then
item.pos0.page = page
item.pos1.page = page
end
table.insert(bookmarks, item)
end
end
end
end
-- Bookmarks/highlights formats in crengine and mupdf are incompatible.
local has_bookmarks = #bookmarks > 0
local bookmarks_type = has_bookmarks and type(bookmarks[1].page)
if self.ui.rolling then
if bookmarks_type == "string" then -- compatible format loaded, check for incompatible old backup
if config:has("bookmarks_paging") then -- save incompatible old backup
local bookmarks_paging = config:readSetting("bookmarks_paging")
local highlights_paging = config:readSetting("highlight_paging")
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks_paging, highlights_paging)
config:saveSetting("annotations_paging", annotations)
config:delSetting("bookmarks_paging")
config:delSetting("highlight_paging")
end
else -- incompatible format loaded, or empty
if has_bookmarks then -- save incompatible format if not empty
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights)
config:saveSetting("annotations_paging", annotations)
end
-- load compatible format
bookmarks = config:readSetting("bookmarks_rolling") or {}
highlights = config:readSetting("highlight_rolling") or {}
config:delSetting("bookmarks_rolling")
config:delSetting("highlight_rolling")
end
else -- self.ui.paging
if bookmarks_type == "number" then
if config:has("bookmarks_rolling") then
local bookmarks_rolling = config:readSetting("bookmarks_rolling")
local highlights_rolling = config:readSetting("highlight_rolling")
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks_rolling, highlights_rolling)
config:saveSetting("annotations_rolling", annotations)
config:delSetting("bookmarks_rolling")
config:delSetting("highlight_rolling")
end
else
if has_bookmarks then
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights)
config:saveSetting("annotations_rolling", annotations)
end
bookmarks = config:readSetting("bookmarks_paging") or {}
highlights = config:readSetting("highlight_paging") or {}
config:delSetting("bookmarks_paging")
config:delSetting("highlight_paging")
end
end
self.annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights, true)
end
function ReaderAnnotation:onDocumentRerendered()
self.needs_update = true
end
function ReaderAnnotation:onCloseDocument()
self:updatePageNumbers()
end
function ReaderAnnotation:onSaveSettings()
self:updatePageNumbers()
self.ui.doc_settings:saveSetting("annotations", self.annotations)
end
-- items handling
function ReaderAnnotation:updatePageNumbers()
if self.needs_update and self.ui.rolling then -- triggered by ReaderRolling on document layout change
for _, item in ipairs(self.annotations) do
item.pageno = self.document:getPageFromXPointer(item.page)
end
end
self.needs_update = nil
end
function ReaderAnnotation:sortItems(items)
if #items > 1 then
local sort_func = self.ui.rolling and function(a, b) return self:isItemInPositionOrderRolling(a, b) end
or function(a, b) return self:isItemInPositionOrderPaging(a, b) end
table.sort(items, sort_func)
end
end
function ReaderAnnotation:updatedAnnotations(needs_update, needs_sort)
if needs_update then
self.needs_update = true
self:updatePageNumbers()
needs_sort = true
end
if needs_sort then
self:sortItems(self.annotations)
end
end
function ReaderAnnotation:updateItemByXPointer(item)
-- called by ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade()
local chapter = self.ui.toc:getTocTitleByPage(item.page)
if chapter == "" then
chapter = nil
end
if not item.drawer then -- page bookmark
item.text = chapter and T(_("in %1"), chapter) or nil
end
item.chapter = chapter
item.pageno = self.document:getPageFromXPointer(item.page)
end
function ReaderAnnotation:isItemInPositionOrderRolling(a, b)
local a_page = self.document:getPageFromXPointer(a.page)
local b_page = self.document:getPageFromXPointer(b.page)
if a_page == b_page then -- both items in the same page
if a.drawer and b.drawer then -- both items are highlights, compare positions
local compare_xp = self.document:compareXPointers(a.page, b.page)
if compare_xp then
if compare_xp == 0 then -- both highlights with the same start, compare ends
compare_xp = self.document:compareXPointers(a.pos1, b.pos1)
if compare_xp then
return compare_xp > 0
end
logger.warn("Invalid xpointer in highlight:", a.pos1, b.pos1)
return true
end
return compare_xp > 0
end
-- if compare_xp is nil, some xpointer is invalid and "a" will be sorted first to page 1
logger.warn("Invalid xpointer in highlight:", a.page, b.page)
return true
end
return not a.drawer -- have page bookmarks before highlights
end
return a_page < b_page
end
function ReaderAnnotation:isItemInPositionOrderPaging(a, b)
if a.page == b.page then -- both items in the same page
if a.drawer and b.drawer then -- both items are highlights, compare positions
local is_reflow = self.document.configurable.text_wrap -- save reflow mode
self.document.configurable.text_wrap = 0 -- native positions
-- sort start and end positions of each highlight
local a_start, a_end, b_start, b_end, result
if self.document:comparePositions(a.pos0, a.pos1) > 0 then
a_start, a_end = a.pos0, a.pos1
else
a_start, a_end = a.pos1, a.pos0
end
if self.document:comparePositions(b.pos0, b.pos1) > 0 then
b_start, b_end = b.pos0, b.pos1
else
b_start, b_end = b.pos1, b.pos0
end
-- compare start positions
local compare_pos = self.document:comparePositions(a_start, b_start)
if compare_pos == 0 then -- both highlights with the same start, compare ends
result = self.document:comparePositions(a_end, b_end) > 0
else
result = compare_pos > 0
end
self.document.configurable.text_wrap = is_reflow -- restore reflow mode
return result
end
return not a.drawer -- have page bookmarks before highlights
end
return a.page < b.page
end
function ReaderAnnotation:getItemIndex(item, no_binary)
local doesMatch
if item.datetime then
doesMatch = function(a, b)
return a.datetime == b.datetime
end
else
if self.ui.rolling then
doesMatch = function(a, b)
if a.text ~= b.text or a.pos0 ~= b.pos0 or a.pos1 ~= b.pos1 then
return false
end
return true
end
else
doesMatch = function(a, b)
if a.text ~= b.text or a.pos0.page ~= b.pos0.page
or a.pos0.x ~= b.pos0.x or a.pos1.x ~= b.pos1.x
or a.pos0.y ~= b.pos0.y or a.pos1.y ~= b.pos1.y then
return false
end
return true
end
end
end
if not no_binary then
local isInOrder = self.ui.rolling and self.isItemInPositionOrderRolling or self.isItemInPositionOrderPaging
local _start, _end, _middle = 1, #self.annotations
while _start <= _end do
_middle = bit.rshift(_start + _end, 1)
local v = self.annotations[_middle]
if doesMatch(item, v) then
return _middle
elseif isInOrder(self, item, v) then
_end = _middle - 1
else
_start = _middle + 1
end
end
end
for i, v in ipairs(self.annotations) do
if doesMatch(item, v) then
return i
end
end
end
function ReaderAnnotation:getInsertionIndex(item)
local isInOrder = self.ui.rolling and self.isItemInPositionOrderRolling or self.isItemInPositionOrderPaging
local _start, _end, _middle, direction = 1, #self.annotations, 1, 0
while _start <= _end do
_middle = bit.rshift(_start + _end, 1)
if isInOrder(self, item, self.annotations[_middle]) then
_end, direction = _middle - 1, 0
else
_start, direction = _middle + 1, 1
end
end
return _middle + direction
end
function ReaderAnnotation:addItem(item)
item.datetime = os.date("%Y-%m-%d %H:%M:%S")
item.pageno = self.ui.paging and item.page or self.document:getPageFromXPointer(item.page)
local index = self:getInsertionIndex(item)
table.insert(self.annotations, index, item)
return index
end
-- info
function ReaderAnnotation:hasAnnotations()
return #self.annotations > 0
end
function ReaderAnnotation:getNumberOfAnnotations()
return #self.annotations
end
function ReaderAnnotation:getNumberOfHighlightsAndNotes() -- for Statistics plugin
local highlights = 0
local notes = 0
for _, item in ipairs(self.annotations) do
if item.drawer then
if item.note then
notes = notes + 1
else
highlights = highlights + 1
end
end
end
return highlights, notes
end
return ReaderAnnotation

File diff suppressed because it is too large Load Diff

@ -219,7 +219,7 @@ local footerTextGeneratorMap = {
bookmark_count = function(footer)
local symbol_type = footer.settings.item_prefix
local prefix = symbol_prefix[symbol_type].bookmark_count
local bookmark_count = footer.ui.bookmark:getNumberOfBookmarks()
local bookmark_count = footer.ui.annotation:getNumberOfAnnotations()
if footer.settings.all_at_once and footer.settings.hide_empty_generators and bookmark_count == 0 then
return ""
end

@ -7,6 +7,7 @@ local Geom = require("ui/geometry")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local Notification = require("ui/widget/notification")
local RadioButtonWidget = require("ui/widget/radiobuttonwidget")
local TextViewer = require("ui/widget/textviewer")
local Translator = require("ui/translator")
local UIManager = require("ui/uimanager")
@ -73,11 +74,11 @@ function ReaderHighlight:init()
["02_highlight"] = function(this)
return {
text = _("Highlight"),
enabled = this.hold_pos ~= nil,
callback = function()
this:saveHighlight(true)
this:onClose()
end,
enabled = this.hold_pos ~= nil,
}
end,
["03_copy"] = function(this)
@ -96,11 +97,11 @@ function ReaderHighlight:init()
["04_add_note"] = function(this)
return {
text = _("Add note"),
enabled = this.hold_pos ~= nil,
callback = function()
this:addNote()
this:onClose()
end,
enabled = this.hold_pos ~= nil,
}
end,
-- then information lookup functions, putting on the left those that
@ -127,11 +128,11 @@ function ReaderHighlight:init()
end,
}
end,
["07_translate"] = function(this, page, index)
["07_translate"] = function(this, index)
return {
text = _("Translate"),
callback = function()
this:translate(this.selected_text, page, index)
this:translate(index)
-- We don't call this:onClose(), so one can still see
-- the highlighted text when moving the translated
-- text window, and also if NetworkMgr:promptWifiOn()
@ -429,7 +430,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
self.view.highlight.lighten_factor = spin.value
UIManager:setDirty(self.dialog, "ui")
if touchmenu_instance then touchmenu_instance:updateItems() end
end
end,
}
UIManager:show(spin_widget)
end,
@ -455,7 +456,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
},
})
end
UIManager:show(require("ui/widget/radiobuttonwidget"):new{
UIManager:show(RadioButtonWidget:new{
title_text = _("Note marker"),
width_factor = 0.5,
keep_shown_on_apply = true,
@ -475,7 +476,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
})
end,
})
if self.document.info.has_pages then
if self.ui.paging then
menu_items.panel_zoom_options = {
text = _("Panel zoom (manga/comic)"),
sub_item_table = self:genPanelZoomMenu(),
@ -563,7 +564,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
callback = function(spin)
G_reader_settings:saveSetting("highlight_long_hold_threshold_s", spin.value)
if touchmenu_instance then touchmenu_instance:updateItems() end
end
end,
}
UIManager:show(items)
end,
@ -671,6 +672,7 @@ function ReaderHighlight:clear(clear_id)
end
self.is_word_selection = false
self.selected_text_start_xpointer = nil
self.gest_pos = nil
if self.hold_pos then
self.hold_pos = nil
self.selected_text = nil
@ -693,8 +695,8 @@ function ReaderHighlight:onTapSelectModeIcon()
cancel_text = _("Close"),
ok_callback = function()
self.select_mode = false
self:deleteHighlight(self.highlight_page, self.highlight_idx)
end
self:deleteHighlight(self.highlight_idx)
end,
})
return true
end
@ -706,7 +708,7 @@ function ReaderHighlight:onTap(_, ges)
-- ReaderHighlight:clear can only return true if self.hold_pos was set anyway.
local cleared = self.hold_pos and self:clear()
-- We only care about potential taps on existing highlights, not on taps that closed a highlight menu.
if not cleared and ges then
if not cleared and ges and self.ui.annotation:hasAnnotations() then
if self.ui.paging then
return self:onTapPageSavedHighlight(ges)
else
@ -720,31 +722,24 @@ function ReaderHighlight:onTapPageSavedHighlight(ges)
local pos = self.view:screenToPageTransform(ges.pos)
local highlights_tapped = {}
for _, page in ipairs(pages) do
local items = self.view:getPageSavedHighlights(page)
if items then
for i, item in ipairs(items) do
local boxes = self.ui.document:getPageBoxesFromPositions(page, item.pos0, item.pos1)
if boxes then
for _, box in ipairs(boxes) do
if inside_box(pos, box) then
logger.dbg("Tap on highlight")
local hl_page, hl_i
if item.parent then -- multi-page highlight
hl_page, hl_i = unpack(item.parent)
else
hl_page, hl_i = page, i
end
if self.select_mode then
if hl_page == self.highlight_page and hl_i == self.highlight_idx then
-- tap on the first fragment: abort select mode, clear highlight
self.select_mode = false
self:deleteHighlight(hl_page, hl_i)
return true
end
else
table.insert(highlights_tapped, {hl_page, hl_i})
break
local items = self:getPageSavedHighlights(page)
for i, item in ipairs(items) do
local boxes = self.ui.document:getPageBoxesFromPositions(page, item.pos0, item.pos1)
if boxes then
for __, box in ipairs(boxes) do
if inside_box(pos, box) then
logger.dbg("Tap on highlight")
local hl_i = item.parent or i -- parent exists in multi-page highlight only
if self.select_mode then
if hl_i == self.highlight_idx then
-- tap on the first fragment: abort select mode, clear highlight
self.select_mode = false
self:deleteHighlight(hl_i)
return true
end
else
table.insert(highlights_tapped, hl_i)
break
end
end
end
@ -761,7 +756,6 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
-- showing menu...). We might want to cache these boxes per page (and
-- clear that cache when page layout change or highlights are added
-- or removed).
local cur_view_top, cur_view_bottom
local pos = self.view:screenToPageTransform(ges.pos)
-- NOTE: By now, pos.page is set, but if a highlight spans across multiple pages,
-- it's stored under the hash of its *starting* point,
@ -770,46 +764,39 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
-- because pos.page isn't super accurate in continuous mode
-- (it's the page number for what's it the topleft corner of the screen,
-- i.e., often a bit earlier)...
-- Even in page mode, it's safer to use pos and ui.dimen.h
-- than pages' xpointers pos, even if ui.dimen.h is a bit
-- larger than pages' heights
local cur_view_top = self.document:getCurrentPos()
local cur_view_bottom
if self.view.view_mode == "page" and self.document:getVisiblePageCount() > 1 then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
else
cur_view_bottom = cur_view_top + self.ui.dimen.h
end
local highlights_tapped = {}
for page, items in pairs(self.view.highlight.saved) do
if items then
for i = 1, #items do
local item = items[i]
local pos0, pos1 = item.pos0, item.pos1
-- document:getScreenBoxesFromPositions() is expensive, so we
-- first check this item is on current page
if not cur_view_top then
-- Even in page mode, it's safer to use pos and ui.dimen.h
-- than pages' xpointers pos, even if ui.dimen.h is a bit
-- larger than pages' heights
cur_view_top = self.ui.document:getCurrentPos()
if self.view.view_mode == "page" and self.ui.document:getVisiblePageCount() > 1 then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
else
cur_view_bottom = cur_view_top + self.ui.dimen.h
end
end
local spos0 = self.ui.document:getPosFromXPointer(pos0)
local spos1 = self.ui.document:getPosFromXPointer(pos1)
local start_pos = math.min(spos0, spos1)
local end_pos = math.max(spos0, spos1)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true
if boxes then
for index, box in pairs(boxes) do
if inside_box(pos, box) then
logger.dbg("Tap on highlight")
if self.select_mode then
if page == self.highlight_page and i == self.highlight_idx then
-- tap on the first fragment: abort select mode, clear highlight
self.select_mode = false
self:deleteHighlight(page, i)
return true
end
else
table.insert(highlights_tapped, {page, i})
break
for hl_i, item in ipairs(self.ui.annotation.annotations) do
if item.drawer then
-- document:getScreenBoxesFromPositions() is expensive, so we
-- first check this item is on current page
local start_pos = self.document:getPosFromXPointer(item.pos0)
local end_pos = self.document:getPosFromXPointer(item.pos1)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
local boxes = self.ui.document:getScreenBoxesFromPositions(item.pos0, item.pos1, true) -- get_segments=true
if boxes then
for _, box in ipairs(boxes) do
if inside_box(pos, box) then
logger.dbg("Tap on highlight")
if self.select_mode then
if hl_i == self.highlight_idx then
-- tap on the first fragment: abort select mode, clear highlight
self.select_mode = false
self:deleteHighlight(hl_i)
return true
end
else
table.insert(highlights_tapped, hl_i)
break
end
end
end
@ -822,10 +809,9 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
end
end
function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_char)
if self.ui.paging then return end
local highlight = self.view.highlight.saved[page][index]
local highlight_time = highlight.datetime
function ReaderHighlight:updateHighlight(index, side, direction, move_by_char)
local highlight = self.ui.annotation.annotations[index]
local highlight_before = util.tableDeepCopy(highlight)
local highlight_beginning = highlight.pos0
local highlight_end = highlight.pos1
if side == 0 then -- we move pos0
@ -846,7 +832,10 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c
if updated_highlight_beginning then
local order = self.ui.document:compareXPointers(updated_highlight_beginning, highlight_end)
if order and order > 0 then -- only if beginning did not go past end
self.view.highlight.saved[page][index].pos0 = updated_highlight_beginning
highlight.pos0 = updated_highlight_beginning
highlight.page = updated_highlight_beginning
highlight.chapter = self.ui.toc:getTocTitleByPage(updated_highlight_beginning)
highlight.pageno = self.document:getPageFromXPointer(updated_highlight_beginning)
end
end
else -- we move pos1
@ -867,23 +856,16 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c
if updated_highlight_end then
local order = self.ui.document:compareXPointers(highlight_beginning, updated_highlight_end)
if order and order > 0 then -- only if end did not go back past beginning
self.view.highlight.saved[page][index].pos1 = updated_highlight_end
highlight.pos1 = updated_highlight_end
end
end
end
local new_beginning = self.view.highlight.saved[page][index].pos0
local new_end = self.view.highlight.saved[page][index].pos1
local new_beginning = highlight.pos0
local new_end = highlight.pos1
local new_text = self.ui.document:getTextFromXPointers(new_beginning, new_end)
local new_chapter = self.ui.toc:getTocTitleByPage(new_beginning)
self.view.highlight.saved[page][index].text = cleanupSelectedText(new_text)
self.view.highlight.saved[page][index].chapter = new_chapter
local new_highlight = self.view.highlight.saved[page][index]
self.ui.bookmark:updateBookmark({
page = highlight_beginning,
datetime = highlight_time,
updated_highlight = new_highlight
})
highlight.text = cleanupSelectedText(new_text)
self.ui:handleEvent(Event:new("AnnotationsModified", { highlight, highlight_before }))
if side == 0 then
-- Ensure we show the page with the new beginning of highlight
if not self.ui.document:isXPointerInCurrentPage(new_beginning) then
@ -909,20 +891,15 @@ end
function ReaderHighlight:showChooseHighlightDialog(highlights)
if #highlights == 1 then
local page, index = unpack(highlights[1])
local item = self.view.highlight.saved[page][index]
local bookmark_note = self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
self:showHighlightNoteOrDialog(page, index, bookmark_note)
self:showHighlightNoteOrDialog(highlights[1])
else -- overlapped highlights
local dialog
local buttons = {}
for i, v in ipairs(highlights) do
local page, index = unpack(v)
local item = self.view.highlight.saved[page][index]
local bookmark_note = self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
for i, index in ipairs(highlights) do
local item = self.ui.annotation.annotations[index]
buttons[i] = {{
text = (bookmark_note and self.ui.bookmark.display_prefix["note"]
or self.ui.bookmark.display_prefix["highlight"]) .. item.text,
text = (item.note and self.ui.bookmark.display_prefix["note"]
or self.ui.bookmark.display_prefix["highlight"]) .. item.text,
align = "left",
avoid_text_truncation = false,
font_face = "smallinfofont",
@ -930,7 +907,7 @@ function ReaderHighlight:showChooseHighlightDialog(highlights)
font_bold = false,
callback = function()
UIManager:close(dialog)
self:showHighlightNoteOrDialog(page, index, bookmark_note)
self:showHighlightNoteOrDialog(index)
end,
}}
end
@ -942,7 +919,8 @@ function ReaderHighlight:showChooseHighlightDialog(highlights)
return true
end
function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note)
function ReaderHighlight:showHighlightNoteOrDialog(index)
local bookmark_note = self.ui.annotation.annotations[index].note
if bookmark_note then
local textviewer
textviewer = TextViewer:new{
@ -957,14 +935,14 @@ function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note)
text = _("Delete highlight"),
callback = function()
UIManager:close(textviewer)
self:deleteHighlight(page, index)
self:deleteHighlight(index)
end,
},
{
text = _("Edit highlight"),
callback = function()
UIManager:close(textviewer)
self:onShowHighlightDialog(page, index, false)
self:onShowHighlightDialog(index)
end,
},
},
@ -972,17 +950,18 @@ function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note)
}
UIManager:show(textviewer)
else
self:onShowHighlightDialog(page, index, true)
self:onShowHighlightDialog(index)
end
end
function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
function ReaderHighlight:onShowHighlightDialog(index)
local item = self.ui.annotation.annotations[index]
local buttons = {
{
{
text = _("Delete"),
callback = function()
self:deleteHighlight(page, index)
self:deleteHighlight(index)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end,
@ -990,15 +969,15 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
{
text = C_("Highlight", "Style"),
callback = function()
self:editHighlightStyle(page, index)
self:editHighlightStyle(index)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end,
},
{
text = is_auto_text and _("Add note") or _("Edit note"),
text = item.note and _("Edit note") or _("Add note"),
callback = function()
self:editHighlight(page, index)
self:editHighlight(index)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end,
@ -1006,8 +985,8 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
{
text = "",
callback = function()
self.selected_text = self.view.highlight.saved[page][index]
self:onShowHighlightMenu(page, index)
self.selected_text = util.tableDeepCopy(item)
self:onShowHighlightMenu(index)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end,
@ -1016,7 +995,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
}
if self.ui.rolling then
local enabled = not self.view.highlight.saved[page][index].edited
local enabled = not item.text_edited
local start_prev = "◁▒▒"
local start_next = "▷▒▒"
local end_prev = "▒▒◁"
@ -1031,10 +1010,10 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
text = start_prev,
enabled = enabled,
callback = function()
self:updateHighlight(page, index, 0, -1, false)
self:updateHighlight(index, 0, -1, false)
end,
hold_callback = function()
self:updateHighlight(page, index, 0, -1, true)
self:updateHighlight(index, 0, -1, true)
return true
end
},
@ -1042,10 +1021,10 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
text = start_next,
enabled = enabled,
callback = function()
self:updateHighlight(page, index, 0, 1, false)
self:updateHighlight(index, 0, 1, false)
end,
hold_callback = function()
self:updateHighlight(page, index, 0, 1, true)
self:updateHighlight(index, 0, 1, true)
return true
end
},
@ -1053,26 +1032,26 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
text = end_prev,
enabled = enabled,
callback = function()
self:updateHighlight(page, index, 1, -1, false)
self:updateHighlight(index, 1, -1, false)
end,
hold_callback = function()
self:updateHighlight(page, index, 1, -1, true)
self:updateHighlight(index, 1, -1, true)
end
},
{
text = end_next,
enabled = enabled,
callback = function()
self:updateHighlight(page, index, 1, 1, false)
self:updateHighlight(index, 1, 1, false)
end,
hold_callback = function()
self:updateHighlight(page, index, 1, 1, true)
self:updateHighlight(index, 1, 1, true)
end
}
})
end
self.edit_highlight_dialog = ButtonDialog:new{
buttons = buttons
buttons = buttons,
}
UIManager:show(self.edit_highlight_dialog)
return true
@ -1090,7 +1069,7 @@ function ReaderHighlight:removeFromHighlightDialog(idx)
return button
end
function ReaderHighlight:onShowHighlightMenu(page, index)
function ReaderHighlight:onShowHighlightMenu(index)
if not self.selected_text then
return
end
@ -1099,7 +1078,7 @@ function ReaderHighlight:onShowHighlightMenu(page, index)
local columns = 2
for idx, fn_button in ffiUtil.orderedPairs(self._highlight_buttons) do
local button = fn_button(self, page, index)
local button = fn_button(self, index)
if not button.show_in_highlight_dialog_func or button.show_in_highlight_dialog_func() then
if #highlight_buttons[#highlight_buttons] >= columns then
table.insert(highlight_buttons, {})
@ -1198,12 +1177,12 @@ function ReaderHighlight:_resetHoldTimer(clear)
end
function ReaderHighlight:onTogglePanelZoomSetting(arg, ges)
if not self.document.info.has_pages then return end
if self.ui.rolling then return end
self.panel_zoom_enabled = not self.panel_zoom_enabled
end
function ReaderHighlight:onToggleFallbackTextSelection(arg, ges)
if not self.document.info.has_pages then return end
if self.ui.rolling then return end
self.panel_zoom_fallback_to_text_selection = not self.panel_zoom_fallback_to_text_selection
end
@ -1229,7 +1208,7 @@ function ReaderHighlight:onPanelZoom(arg, ges)
end
function ReaderHighlight:onHold(arg, ges)
if self.document.info.has_pages and self.panel_zoom_enabled then
if self.ui.paging and self.panel_zoom_enabled then
local res = self:onPanelZoom(arg, ges)
if res or not self.panel_zoom_fallback_to_text_selection then
return res
@ -1548,20 +1527,20 @@ function ReaderHighlight:viewSelectionHTML(debug_view, no_css_files_buttons)
end
end
function ReaderHighlight:translate(selected_text, page, index)
function ReaderHighlight:translate(index)
if self.ui.rolling then
-- Extend the selected text to include any punctuation at start or end,
-- which may give a better translation with the added context.
local extended_text = self.ui.document:extendXPointersToSentenceSegment(selected_text.pos0, selected_text.pos1)
local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1)
if extended_text then
selected_text = extended_text
self.selected_text = extended_text
end
end
if #selected_text.text > 0 then
self:onTranslateText(selected_text.text, page, index)
if #self.selected_text.text > 0 then
self:onTranslateText(self.selected_text.text, index)
-- or we will do OCR
elseif self.hold_pos then
local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text)
local text = self.ui.document:getOCRText(self.hold_pos.page, self.selected_text)
logger.dbg("OCRed text:", text)
if text and text ~= "" then
self:onTranslateText(text)
@ -1572,14 +1551,9 @@ function ReaderHighlight:translate(selected_text, page, index)
end
end
end
dbg:guard(ReaderHighlight, "translate",
function(self, selected_text)
assert(selected_text ~= nil,
"translate must not be called with nil selected_text!")
end)
function ReaderHighlight:onTranslateText(text, page, index)
Translator:showTranslation(text, true, nil, nil, true, page, index)
function ReaderHighlight:onTranslateText(text, index)
Translator:showTranslation(text, true, nil, nil, true, index)
end
function ReaderHighlight:onTranslateCurrentPage()
@ -1661,7 +1635,7 @@ function ReaderHighlight:onHoldRelease()
self:addNote()
self:onClose()
elseif default_highlight_action == "translate" then
self:translate(self.selected_text)
self:translate()
elseif default_highlight_action == "wikipedia" then
self:lookupWikipedia()
self:onClose()
@ -1751,137 +1725,45 @@ function ReaderHighlight:highlightFromHoldPos()
end
end
function ReaderHighlight:onHighlight()
self:saveHighlight()
end
function ReaderHighlight:onUnhighlight(bookmark_item)
local page
local sel_text
local sel_pos0
local datetime
local idx
if bookmark_item then -- called from Bookmarks menu onHold
page = bookmark_item.page
sel_text = bookmark_item.notes
sel_pos0 = bookmark_item.pos0
datetime = bookmark_item.datetime
else -- called from DictQuickLookup Unhighlight button
--- @fixme: is this self.hold_pos access safe?
page = self.hold_pos.page
sel_text = cleanupSelectedText(self.selected_text.text)
sel_pos0 = self.selected_text.pos0
end
if self.ui.paging then -- We can safely use page
-- As we may have changed spaces and hyphens handling in the extracted
-- text over the years, check text identities with them removed
local sel_text_cleaned = sel_text:gsub("[ -]", ""):gsub("\u{00AD}", "")
for index = 1, #self.view.highlight.saved[page] do
local highlight = self.view.highlight.saved[page][index]
-- pos0 are tables and can't be compared directly, except when from
-- DictQuickLookup where these are the same object.
-- If bookmark_item provided, just check datetime
if ( (datetime == nil and highlight.pos0 == sel_pos0) or
(datetime ~= nil and highlight.datetime == datetime) ) then
if highlight.text:gsub("[ -]", ""):gsub("\u{00AD}", "") == sel_text_cleaned then
idx = index
break
end
end
end
else -- page is a xpointer
-- The original page could be found in bookmark_item.text, but
-- no more if it has been renamed: we need to loop through all
-- highlights on all page slots
for p, highlights in pairs(self.view.highlight.saved) do
for index = 1, #highlights do
local highlight = highlights[index]
-- pos0 are strings and can be compared directly
if highlight.text == sel_text and (
(datetime == nil and highlight.pos0 == sel_pos0) or
(datetime ~= nil and highlight.datetime == datetime)) then
page = p -- this is the original page slot
idx = index
break
end
end
if idx then
break
end
end
end
if idx then
logger.dbg("found highlight to delete on page", page, idx)
self:deleteHighlight(page, idx, bookmark_item)
return true
end
end
function ReaderHighlight:getHighlightByDatetime(datetime)
for page, highlights in pairs(self.view.highlight.saved) do
for _, highlight in ipairs(highlights) do
if highlight.datetime == datetime then
return highlight
end
end
end
end
function ReaderHighlight:getHighlightBookmarkItem()
function ReaderHighlight:saveHighlight(extend_to_sentence)
logger.dbg("save highlight")
if self.hold_pos and not self.selected_text then
self:highlightFromHoldPos()
end
if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then
return {
page = self.ui.paging and self.selected_text.pos0.page or self.selected_text.pos0,
pos0 = self.selected_text.pos0,
pos1 = self.selected_text.pos1,
notes = cleanupSelectedText(self.selected_text.text),
highlighted = true,
}
end
end
function ReaderHighlight:saveHighlight(extend_to_sentence)
self.ui:handleEvent(Event:new("AddHighlight"))
logger.dbg("save highlight")
if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then
if extend_to_sentence and self.ui.rolling then
local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1)
if extended_text then
self.selected_text = extended_text
local pg_or_xp
if self.ui.rolling then
if extend_to_sentence then
local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1)
if extended_text then
self.selected_text = extended_text
end
end
pg_or_xp = self.selected_text.pos0
else
pg_or_xp = self.selected_text.pos0.page
end
local page = self.ui.paging and self.selected_text.pos0.page or self.ui.document:getPageFromXPointer(self.selected_text.pos0)
if not self.view.highlight.saved[page] then
self.view.highlight.saved[page] = {}
end
local datetime = os.date("%Y-%m-%d %H:%M:%S")
local pg_or_xp = self.ui.paging and page or self.selected_text.pos0
local chapter_name = self.ui.toc:getTocTitleByPage(pg_or_xp)
local hl_item = {
datetime = datetime,
text = cleanupSelectedText(self.selected_text.text),
local item = {
page = self.ui.paging and self.selected_text.pos0.page or self.selected_text.pos0,
pos0 = self.selected_text.pos0,
pos1 = self.selected_text.pos1,
pboxes = self.selected_text.pboxes,
ext = self.selected_text.ext,
text = cleanupSelectedText(self.selected_text.text),
drawer = self.view.highlight.saved_drawer,
chapter = chapter_name,
chapter = self.ui.toc:getTocTitleByPage(pg_or_xp),
}
table.insert(self.view.highlight.saved[page], hl_item)
local bookmark_item = self:getHighlightBookmarkItem()
if bookmark_item then
bookmark_item.datetime = datetime
bookmark_item.chapter = chapter_name
self.ui.bookmark:addBookmark(bookmark_item)
if self.ui.paging then
item.pboxes = self.selected_text.pboxes
item.ext = self.selected_text.ext
self:writePdfAnnotation("save", item)
end
self:writePdfAnnotation("save", page, hl_item)
return page, #self.view.highlight.saved[page]
local index = self.ui.annotation:addItem(item)
self.view.footer:onUpdateFooter(self.view.footer_visible)
self.ui:handleEvent(Event:new("AnnotationsModified", { item, nb_highlights_added = 1 }))
return index
end
end
function ReaderHighlight:writePdfAnnotation(action, page, item, content)
function ReaderHighlight:writePdfAnnotation(action, item, content)
if self.ui.rolling or G_reader_settings:readSetting("save_document") == "disable" then
return
end
@ -1897,24 +1779,10 @@ function ReaderHighlight:writePdfAnnotation(action, page, item, content)
end
local can_write
if item.pos0.page == item.pos1.page then -- single-page highlight
local item_
if item.pboxes then
item_ = item
else -- called from bookmarks to write bookmark note to annotation
for _, hl in ipairs(self.view.highlight.saved[page]) do
if hl.datetime == item.datetime then
item_ = {pboxes = hl.pboxes}
break
end
end
end
can_write = doAction(action, page, item_, content)
can_write = doAction(action, item.pos0.page, item, content)
else -- multi-page highlight
local is_reflow = self.ui.document.configurable.text_wrap
for hl_page = item.pos0.page, item.pos1.page do
self.ui.document.configurable.text_wrap = 0
local hl_part = self:getSavedExtendedHighlightPage(item, hl_page)
self.ui.document.configurable.text_wrap = is_reflow
can_write = doAction(action, hl_page, hl_part, content)
if can_write == false then break end
if action == "save" then -- update pboxes from quadpoints
@ -1934,14 +1802,6 @@ If you wish your highlights to be saved in the document, just move it to a writa
end
end
function ReaderHighlight:addNote(text)
local page, index = self:saveHighlight(true)
if text then self:clear() end
self:editHighlight(page, index, true, text)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end
function ReaderHighlight:lookupWikipedia()
if self.selected_text then
self.ui:handleEvent(Event:new("LookupWikipedia", cleanupSelectedText(self.selected_text.text)))
@ -1970,59 +1830,48 @@ function ReaderHighlight:onHighlightDictLookup()
end
end
function ReaderHighlight:deleteHighlight(page, i, bookmark_item)
logger.dbg("delete highlight", page, i)
-- The per-page table is a pure array
local removed = table.remove(self.view.highlight.saved[page], i)
-- But the main, outer table is a hash, so clear the table for this page if there are no longer any highlights on it
if #self.view.highlight.saved[page] == 0 then
self.view.highlight.saved[page] = nil
end
if bookmark_item then
self.ui.bookmark:removeBookmark(bookmark_item)
else
self.ui.bookmark:removeBookmark({
page = self.ui.paging and page or removed.pos0,
datetime = removed.datetime,
})
end
self:writePdfAnnotation("delete", page, removed)
function ReaderHighlight:deleteHighlight(index)
logger.dbg("delete highlight", index)
local item = self.ui.annotation.annotations[index]
self:writePdfAnnotation("delete", item)
self.ui.bookmark:removeItemByIndex(index)
UIManager:setDirty(self.dialog, "ui")
end
function ReaderHighlight:editHighlight(page, i, is_new_note, text)
local item = self.view.highlight.saved[page][i]
self.ui.bookmark:setBookmarkNote({
page = self.ui.paging and page or item.pos0,
datetime = item.datetime,
}, true, is_new_note, text)
function ReaderHighlight:addNote(text)
local index = self:saveHighlight(true)
if text then -- called from Translator to save translation to note
self:clear()
end
self:editHighlight(index, true, text)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end
function ReaderHighlight:editHighlight(index, is_new_note, text)
self.ui.bookmark:setBookmarkNote(index, is_new_note, text)
end
function ReaderHighlight:editHighlightStyle(page, i)
local item = self.view.highlight.saved[page][i]
function ReaderHighlight:editHighlightStyle(index)
local item = self.ui.annotation.annotations[index]
local apply_drawer = function(drawer)
self:writePdfAnnotation("delete", page, item)
self:writePdfAnnotation("delete", item)
item.drawer = drawer
if self.ui.paging then
self:writePdfAnnotation("save", page, item)
local bm_note = self.ui.bookmark:getBookmarkNote(item)
if bm_note then
self:writePdfAnnotation("content", page, item, bm_note)
self:writePdfAnnotation("save", item)
if item.note then
self:writePdfAnnotation("content", item, item.note)
end
end
UIManager:setDirty(self.dialog, "ui")
self.ui:handleEvent(Event:new("BookmarkUpdated",
self.ui.bookmark:getBookmarkForHighlight({
page = self.ui.paging and page or item.pos0,
datetime = item.datetime,
})))
self.ui:handleEvent(Event:new("AnnotationsModified", { item }))
end
self:showHighlightStyleDialog(apply_drawer, item.drawer)
end
function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer)
local default_drawer, keep_shown_on_apply
if item_drawer then -- called from editHighlightStyle
if item_drawer then -- called from ReaderHighlight:editHighlightStyle()
default_drawer = self.view.highlight.saved_drawer or
G_reader_settings:readSetting("highlight_drawing_style", "lighten")
keep_shown_on_apply = true
@ -2037,7 +1886,6 @@ function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer)
},
})
end
local RadioButtonWidget = require("ui/widget/radiobuttonwidget")
UIManager:show(RadioButtonWidget:new{
title_text = _("Highlight style"),
width_factor = 0.5,
@ -2051,14 +1899,14 @@ function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer)
end
function ReaderHighlight:startSelection()
self.highlight_page, self.highlight_idx = self:saveHighlight()
self.highlight_idx = self:saveHighlight()
self.select_mode = true
end
function ReaderHighlight:extendSelection()
-- item1 - starting fragment (saved), item2 - ending fragment (currently selected)
-- new extended highlight includes item1, item2 and the text between them
local item1 = self.view.highlight.saved[self.highlight_page][self.highlight_idx]
local item1 = self.ui.annotation.annotations[self.highlight_idx]
local item2_pos0, item2_pos1 = self.selected_text.pos0, self.selected_text.pos1
-- getting starting and ending positions, text and pboxes of extended highlight
local new_pos0, new_pos1, new_text, new_pboxes, ext
@ -2106,7 +1954,7 @@ function ReaderHighlight:extendSelection()
-- true to draw
new_text = self.ui.document:getTextFromXPointers(new_pos0, new_pos1, true)
end
self:deleteHighlight(self.highlight_page, self.highlight_idx) -- starting fragment
self:deleteHighlight(self.highlight_idx) -- starting fragment
self.selected_text = {
text = new_text,
pos0 = new_pos0,
@ -2126,7 +1974,10 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page)
local page_boxes = self.ui.document:getTextBoxes(page)
if page == pos0.page then
-- first page (from the start of highlight to the end of the page)
item.pos0 = pos0
item.pos0 = {
x = pos0.x,
y = pos0.y,
}
item.pos1 = {
x = page_boxes[#page_boxes][#page_boxes[#page_boxes]].x1,
y = page_boxes[#page_boxes][#page_boxes[#page_boxes]].y1,
@ -2147,7 +1998,10 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page)
x = page_boxes[1][1].x0,
y = page_boxes[1][1].y0,
}
item.pos1 = pos1
item.pos1 = {
x = pos1.x,
y = pos1.y,
}
end
item.pos0.page = page
item.pos1.page = page
@ -2159,44 +2013,53 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page)
return item
end
-- Returns one page of saved multi-page highlight
-- Returns the list of highlights in page.
-- The list includes full single-page highlights and parts of multi-page highlights.
-- (For pdf documents only)
function ReaderHighlight:getSavedExtendedHighlightPage(hl_or_bm, page, index)
local highlight
if hl_or_bm.ext then
highlight = hl_or_bm
else -- called from bookmark, need to find the corresponding highlight
for _, hl in ipairs(self.view.highlight.saved[hl_or_bm.page]) do
if hl.datetime == hl_or_bm.datetime then
highlight = hl
break
function ReaderHighlight:getPageSavedHighlights(page)
local highlights = {}
for index, highlight in ipairs(self.ui.annotation.annotations) do
if highlight.drawer and highlight.pos0.page <= page and page <= highlight.pos1.page then
if highlight.ext then -- multi-page highlight
local item = self:getSavedExtendedHighlightPage(highlight, page, index)
table.insert(highlights, item)
else
table.insert(highlights, highlight)
end
end
end
local item = {}
item.datetime = highlight.datetime
item.drawer = highlight.drawer
item.pos0 = highlight.ext[page].pos0
item.pos0.zoom = highlight.pos0.zoom
return highlights
end
-- Returns one page of saved multi-page highlight
-- (For pdf documents only)
function ReaderHighlight:getSavedExtendedHighlightPage(highlight, page, index)
local item = {
datetime = highlight.datetime,
drawer = highlight.drawer,
text = highlight.text,
page = highlight.page,
pos0 = highlight.ext[page].pos0,
pos1 = highlight.ext[page].pos1,
pboxes = highlight.ext[page].pboxes,
parent = index,
}
item.pos0.zoom = highlight.pos0.zoom
item.pos0.rotation = highlight.pos0.rotation
item.pos1 = highlight.ext[page].pos1
item.pos1.zoom = highlight.pos0.zoom
item.pos1.zoom = highlight.pos0.zoom
item.pos1.rotation = highlight.pos0.rotation
item.pboxes = highlight.ext[page].pboxes
item.parent = {highlight.pos0.page, index}
return item
end
function ReaderHighlight:onReadSettings(config)
self.view.highlight.saved_drawer = config:readSetting("highlight_drawer")
or G_reader_settings:readSetting("highlight_drawing_style") or self.view.highlight.saved_drawer
self.view.highlight.disabled = G_reader_settings:has("default_highlight_action")
and G_reader_settings:readSetting("default_highlight_action") == "nothing"
self.view.highlight.disabled = G_reader_settings:readSetting("default_highlight_action") == "nothing"
self.allow_corner_scroll = G_reader_settings:nilOrTrue("highlight_corner_scroll")
-- panel zoom settings isn't supported in EPUB
if self.document.info.has_pages then
if self.ui.paging then
local ext = util.getFileNameSuffix(self.ui.document.file)
G_reader_settings:initializeExtSettings("panel_zoom_enabled", {cbz = true, cbt = true})
G_reader_settings:initializeExtSettings("panel_zoom_fallback_to_text_selection", {pdf = true})
@ -2231,6 +2094,8 @@ function ReaderHighlight:onClose()
self:clear()
end
-- dpad/keys support
function ReaderHighlight:onHighlightPress()
if self._current_indicator_pos then
if not self._start_indicator_highlight then

@ -3,7 +3,7 @@ ReaderLink is an abstraction for document-specific link interfaces.
]]
local BD = require("ui/bidi")
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
local ButtonDialog = require("ui/widget/buttondialog")
local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device")
local DocumentRegistry = require("document/documentregistry")
@ -118,7 +118,7 @@ function ReaderLink:init()
end)
if G_reader_settings:isTrue("opening_page_location_stack") then
-- Add location at book opening to stack
self.ui:registerPostReadyCallback(function()
self.ui:registerPostReaderReadyCallback(function()
self:addCurrentLocationToStack()
end)
end
@ -858,7 +858,7 @@ end
function ReaderLink:onGoToExternalLink(link_url)
local buttons, title = self:getButtonsForExternalLinkDialog(link_url)
self.external_link_dialog = ButtonDialogTitle:new{
self.external_link_dialog = ButtonDialog:new{
title = title,
buttons = buttons,
}

@ -100,7 +100,7 @@ function ReaderRolling:init()
self.valid_cache_rendering_hash = self.ui.document:getDocumentRenderingHash(false)
end
end)
table.insert(self.ui.postReaderCallback, function()
table.insert(self.ui.postReaderReadyCallback, function()
self:updatePos()
-- Disable crengine internal history, with required redraw
self.ui.document:enableInternalHistory(false)
@ -234,7 +234,7 @@ function ReaderRolling:onReadSettings(config)
-- And check if we can migrate to a newest DOM version after
-- the book is loaded (unless the user told us not to).
if config:nilOrFalse("cre_keep_old_dom_version") then
self.ui:registerPostReadyCallback(function()
self.ui:registerPostReaderReadyCallback(function()
self:checkXPointersAndProposeDOMVersionUpgrade()
end)
end
@ -1029,9 +1029,9 @@ function ReaderRolling:onUpdatePos(force)
if self.batched_update_count > 0 then
return
end
if self.ui.postReaderCallback ~= nil then -- ReaderUI:init() not yet done
if self.ui.postReaderReadyCallback ~= nil then -- ReaderUI:init() not yet done
-- Don't schedule any updatePos as long as ReaderUI:init() is
-- not finished (one will be called in the ui.postReaderCallback
-- not finished (one will be called in the ui.postReaderReadyCallback
-- we have set above) to avoid multiple refreshes.
return true
end
@ -1129,7 +1129,7 @@ function ReaderRolling:onRedrawCurrentView()
end
function ReaderRolling:onSetDimensions(dimen)
if self.ui.postReaderCallback ~= nil then
if self.ui.postReaderReadyCallback ~= nil then
-- ReaderUI:init() not yet done: just set document dimensions
self.ui.document:setViewDimen(Screen:getSize())
-- (what's done in the following else is done elsewhere by
@ -1445,25 +1445,12 @@ function ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade()
local applyFuncToXPointersSlots = function(func)
-- Last position
func(self, "xpointer", "last position in book")
-- Bookmarks
if self.ui.bookmark and self.ui.bookmark.bookmarks and #self.ui.bookmark.bookmarks > 0 then
-- Annotations
if self.ui.annotation and self.ui.annotation.annotations and #self.ui.annotation.annotations > 0 then
local slots = { "page", "pos0", "pos1" }
for _, bookmark in ipairs(self.ui.bookmark.bookmarks) do
for _, item in ipairs(self.ui.annotation.annotations) do
for _, slot in ipairs(slots) do
func(bookmark, slot, bookmark.notes or "bookmark")
end
end
end
-- Highlights
if self.view.highlight and self.view.highlight.saved then
local slots = { "pos0", "pos1" }
for page, items in pairs(self.view.highlight.saved) do
if items and #items > 0 then
for _, highlight in ipairs(items) do
for _, slot in ipairs(slots) do
func(highlight, slot, highlight.text or "highlight")
end
end
func(item, slot, item.text or "annotation")
end
end
end
@ -1509,6 +1496,9 @@ function ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade()
local new_xp = normalized_xpointers[xp]
if new_xp then
obj[slot] = new_xp
if slot == "page" then
self.ui.annotation:updateItemByXPointer(obj)
end
else
-- Let lost/not-found XPointer be. There is a small chance that
-- it will be found (it it was made before the boxing code moved

@ -186,11 +186,11 @@ function ReaderThumbnail:removeFromCache(hash_subs, remove_only_non_matching)
return nb_removed, size_removed
end
function ReaderThumbnail:resetCachedPagesForBookmarks(...)
function ReaderThumbnail:resetCachedPagesForBookmarks(annotations)
-- Multiple bookmarks may be provided
local start_page, end_page
for i = 1, select("#", ...) do
local bm = select(i, ...)
for i = 1, #annotations do
local bm = annotations[i]
if self.ui.rolling then
-- Look at all properties that may be xpointers
for _, k in ipairs({"page", "pos0", "pos1"}) do
@ -537,9 +537,6 @@ end
ReaderThumbnail.onDocumentRerendered = ReaderThumbnail.resetCache
ReaderThumbnail.onDocumentPartiallyRerendered = ReaderThumbnail.resetCache
-- Emitted When adding/removing/updating bookmarks and highlights
ReaderThumbnail.onBookmarkAdded = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onBookmarkRemoved = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onBookmarkUpdated = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onBookmarkEdited = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onAnnotationsModified = ReaderThumbnail.resetCachedPagesForBookmarks
return ReaderThumbnail

@ -95,7 +95,6 @@ function ReaderView:init()
temp_drawer = "invert",
temp = {},
saved_drawer = "lighten",
saved = {},
indicator = nil, -- geom: non-touch highlight position indicator: {x = 50, y=50}
}
self.page_states = {}
@ -522,6 +521,7 @@ function ReaderView:drawTempHighlight(bb, x, y)
end
function ReaderView:drawSavedHighlight(bb, x, y)
if #self.ui.annotation.annotations == 0 then return end
if self.ui.paging then
self:drawPageSavedHighlight(bb, x, y)
else
@ -529,45 +529,18 @@ function ReaderView:drawSavedHighlight(bb, x, y)
end
end
-- Returns the list of highlights in page.
-- The list includes full single-page highlights and parts of multi-page highlights.
function ReaderView:getPageSavedHighlights(page)
local highlights = {}
local is_reflow = self.document.configurable.text_wrap
self.document.configurable.text_wrap = 0
for page_num, page_highlights in pairs(self.highlight.saved) do
for i, highlight in ipairs(page_highlights) do
-- old single-page reflow highlights do not have page in position
local pos0_page = highlight.pos0.page or page_num
local pos1_page = highlight.pos1.page or page_num
if pos0_page <= page and page <= pos1_page then
if pos0_page == pos1_page then -- single-page highlight
table.insert(highlights, highlight)
else -- multi-page highlight
local item = self.ui.highlight:getSavedExtendedHighlightPage(highlight, page, i)
table.insert(highlights, item)
end
end
end
end
self.document.configurable.text_wrap = is_reflow
return highlights
end
function ReaderView:drawPageSavedHighlight(bb, x, y)
local pages = self:getCurrentPageList()
for _, page in ipairs(pages) do
local items = self:getPageSavedHighlights(page)
local items = self.ui.highlight:getPageSavedHighlights(page)
for _, item in ipairs(items) do
local boxes = self.document:getPageBoxesFromPositions(page, item.pos0, item.pos1)
if boxes then
local drawer = item.drawer or self.highlight.saved_drawer
local draw_note_mark = self.highlight.note_mark and
self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
local draw_note_mark = item.note and self.highlight.note_mark
for _, box in ipairs(boxes) do
local rect = self:pageToScreenTransform(page, box)
if rect then
self:drawHighlightRect(bb, x, y, rect, drawer, draw_note_mark)
self:drawHighlightRect(bb, x, y, rect, item.drawer, draw_note_mark)
if draw_note_mark and self.highlight.note_mark == "sidemark" then
draw_note_mark = false -- side mark in the first line only
end
@ -583,48 +556,38 @@ function ReaderView:drawXPointerSavedHighlight(bb, x, y)
-- showing menu...). We might want to cache these boxes per page (and
-- clear that cache when page layout change or highlights are added
-- or removed).
local cur_view_top, cur_view_bottom
for _, items in pairs(self.highlight.saved) do
if items then
for j = 1, #items do
local item = items[j]
local pos0, pos1 = item.pos0, item.pos1
-- document:getScreenBoxesFromPositions() is expensive, so we
-- first check this item is on current page
if not cur_view_top then
-- Even in page mode, it's safer to use pos and ui.dimen.h
-- than pages' xpointers pos, even if ui.dimen.h is a bit
-- larger than pages' heights
cur_view_top = self.document:getCurrentPos()
if self.view_mode == "page" and self.document:getVisiblePageCount() > 1 then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
else
cur_view_bottom = cur_view_top + self.ui.dimen.h
end
end
local spos0 = self.document:getPosFromXPointer(pos0)
local spos1 = self.document:getPosFromXPointer(pos1)
local start_pos = math.min(spos0, spos1)
local end_pos = math.max(spos0, spos1)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
local boxes = self.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true
if boxes then
local drawer = item.drawer or self.highlight.saved_drawer
local draw_note_mark = self.highlight.note_mark and
self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
for _, box in ipairs(boxes) do
if box.h ~= 0 then
self:drawHighlightRect(bb, x, y, box, drawer, draw_note_mark)
if draw_note_mark and self.highlight.note_mark == "sidemark" then
draw_note_mark = false -- side mark in the first line only
end
-- Even in page mode, it's safer to use pos and ui.dimen.h
-- than pages' xpointers pos, even if ui.dimen.h is a bit
-- larger than pages' heights
local cur_view_top = self.document:getCurrentPos()
local cur_view_bottom
if self.view_mode == "page" and self.document:getVisiblePageCount() > 1 then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
else
cur_view_bottom = cur_view_top + self.ui.dimen.h
end
for _, item in ipairs(self.ui.annotation.annotations) do
if item.drawer then
-- document:getScreenBoxesFromPositions() is expensive, so we
-- first check if this item is on current page
local start_pos = self.document:getPosFromXPointer(item.pos0)
local end_pos = self.document:getPosFromXPointer(item.pos1)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
local boxes = self.document:getScreenBoxesFromPositions(item.pos0, item.pos1, true) -- get_segments=true
if boxes then
local draw_note_mark = item.note and self.highlight.note_mark
for _, box in ipairs(boxes) do
if box.h ~= 0 then
self:drawHighlightRect(bb, x, y, box, item.drawer, draw_note_mark)
if draw_note_mark and self.highlight.note_mark == "sidemark" then
draw_note_mark = false -- side mark in the first line only
end
end -- end for each box
end -- end if boxes
end
end
end
end -- end for each highlight
end
end
end -- end for all saved highlight
end
end
function ReaderView:drawHighlightRect(bb, _x, _y, rect, drawer, draw_note_mark)
@ -914,40 +877,6 @@ function ReaderView:onReadSettings(config)
self:resetLayout()
local page_scroll = config:readSetting("kopt_page_scroll") or self.document.configurable.page_scroll
self.page_scroll = page_scroll == 1 and true or false
self.highlight.saved = config:readSetting("highlight", {})
-- Highlight formats in crengine and mupdf are incompatible.
-- Backup highlights when the document is opened with incompatible engine.
local page, page_highlights
while true do -- remove empty tables for pages without highlights and get the first page with highlights
page, page_highlights = next(self.highlight.saved)
if not page or #page_highlights > 0 then
break -- we're done (there is none, or there is some usable)
else
self.highlight.saved[page] = nil -- clean it up while we're at it, and find another one
end
end
if page_highlights then
local highlight_type = type(page_highlights[1].pos0)
if self.ui.rolling and highlight_type == "table" then
config:saveSetting("highlight_paging", self.highlight.saved)
self.highlight.saved = config:readSetting("highlight_rolling", {})
config:saveSetting("highlight", self.highlight.saved)
config:delSetting("highlight_rolling")
elseif self.ui.paging and highlight_type == "string" then
config:saveSetting("highlight_rolling", self.highlight.saved)
self.highlight.saved = config:readSetting("highlight_paging", {})
config:saveSetting("highlight", self.highlight.saved)
config:delSetting("highlight_paging")
end
else
if self.ui.rolling and config:has("highlight_rolling") then
self.highlight.saved = config:readSetting("highlight_rolling")
config:delSetting("highlight_rolling")
elseif self.ui.paging and config:has("highlight_paging") then
self.highlight.saved = config:readSetting("highlight_paging")
config:delSetting("highlight_paging")
end
end
self.inverse_reading_order = config:isTrue("inverse_reading_order") or G_reader_settings:isTrue("inverse_reading_order")
self.page_overlap_enable = config:isTrue("show_overlap_enable") or G_reader_settings:isTrue("page_overlap_enable") or G_defaults:readSetting("DSHOWOVERLAP")
self.page_overlap_style = config:readSetting("page_overlap_style") or G_reader_settings:readSetting("page_overlap_style") or "dim"
@ -1107,7 +1036,6 @@ function ReaderView:onSaveSettings()
if G_reader_settings:nilOrFalse("lock_rotation") then
self.document.configurable.rotation_mode = Screen:getRotationMode() -- will be saved by ReaderConfig
end
self.ui.doc_settings:saveSetting("highlight", self.highlight.saved)
self.ui.doc_settings:saveSetting("inverse_reading_order", self.inverse_reading_order)
self.ui.doc_settings:saveSetting("show_overlap_enable", self.page_overlap_enable)
self.ui.doc_settings:saveSetting("page_overlap_style", self.page_overlap_style)

@ -24,6 +24,7 @@ local LanguageSupport = require("languagesupport")
local Notification = require("ui/widget/notification")
local PluginLoader = require("pluginloader")
local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindicator")
local ReaderAnnotation = require("apps/reader/modules/readerannotation")
local ReaderBack = require("apps/reader/modules/readerback")
local ReaderBookmark = require("apps/reader/modules/readerbookmark")
local ReaderConfig = require("apps/reader/modules/readerconfig")
@ -83,7 +84,7 @@ local ReaderUI = InputContainer:extend{
password = nil,
postInitCallback = nil,
postReaderCallback = nil,
postReaderReadyCallback = nil,
}
function ReaderUI:registerModule(name, ui_module, always_active)
@ -102,8 +103,8 @@ function ReaderUI:registerPostInitCallback(callback)
table.insert(self.postInitCallback, callback)
end
function ReaderUI:registerPostReadyCallback(callback)
table.insert(self.postReaderCallback, callback)
function ReaderUI:registerPostReaderReadyCallback(callback)
table.insert(self.postReaderReadyCallback, callback)
end
function ReaderUI:init()
@ -116,7 +117,7 @@ function ReaderUI:init()
Device:setIgnoreInput(true) -- Avoid ANRs on Android with unprocessed events.
self.postInitCallback = {}
self.postReaderCallback = {}
self.postReaderReadyCallback = {}
-- if we are not the top level dialog ourselves, it must be given in the table
if not self.dialog then
self.dialog = self
@ -182,6 +183,12 @@ function ReaderUI:init()
view = self.view,
ui = self
})
self:registerModule("annotation", ReaderAnnotation:new{
dialog = self.dialog,
view = self.view,
ui = self,
document = self.document,
})
-- reader goto controller
-- "goto" being a dirty keyword in Lua?
self:registerModule("gotopage", ReaderGoto:new{
@ -491,10 +498,10 @@ function ReaderUI:init()
-- Need the same event for PDF document
self:handleEvent(Event:new("ReaderReady", self.doc_settings))
for _,v in ipairs(self.postReaderCallback) do
for _,v in ipairs(self.postReaderReadyCallback) do
v()
end
self.postReaderCallback = nil
self.postReaderReadyCallback = nil
Device:setIgnoreInput(false) -- Allow processing of events (on Android).
Input:inhibitInputUntil(0.2)
@ -584,6 +591,7 @@ end
function ReaderUI:showReader(file, provider, seamless)
logger.dbg("show reader ui")
file = ffiUtil.realpath(file)
if lfs.attributes(file, "mode") ~= "file" then
UIManager:show(InfoMessage:new{
text = T(_("File '%1' does not exist."), BD.filepath(file))

@ -503,14 +503,14 @@ Show translated text in TextViewer, with alternate translations
@string source_lang[opt="auto"] (`"en"`, `"fr"`, ``) or `"auto"` to auto-detect source language
@string target_lang[opt] (`"en"`, `"fr"`, ``)
--]]
function Translator:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index)
function Translator:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
if Device:hasClipboard() then
Device.input.setClipboardText(text)
end
local NetworkMgr = require("ui/network/manager")
if NetworkMgr:willRerunWhenOnline(function()
self:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index)
self:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
end) then
return
end
@ -519,11 +519,11 @@ function Translator:showTranslation(text, detailed_view, source_lang, target_lan
-- translation service query.
local Trapper = require("ui/trapper")
Trapper:wrap(function()
self:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index)
self:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
end)
end
function Translator:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index)
function Translator:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
if not target_lang then
target_lang = self:getTargetLanguage()
end
@ -632,8 +632,8 @@ function Translator:_showTranslation(text, detailed_view, source_lang, target_la
UIManager:close(textviewer)
UIManager:close(ui.highlight.highlight_dialog)
ui.highlight.highlight_dialog = nil
if page then
ui.highlight:editHighlight(page, index, false, text_main)
if index then
ui.highlight:editHighlight(index, false, text_main)
else
ui.highlight:addNote(text_main)
end
@ -645,8 +645,8 @@ function Translator:_showTranslation(text, detailed_view, source_lang, target_la
UIManager:close(textviewer)
UIManager:close(ui.highlight.highlight_dialog)
ui.highlight.highlight_dialog = nil
if page then
ui.highlight:editHighlight(page, index, false, text_all)
if index then
ui.highlight:editHighlight(index, false, text_all)
else
ui.highlight:addNote(text_all)
end

@ -65,11 +65,6 @@ local DictQuickLookup = InputContainer:extend{
rotated_update_wiki_languages_on_close = nil,
}
local highlight_strings = {
highlight =_("Highlight"),
unhighlight = _("Unhighlight"),
}
function DictQuickLookup.getWikiSaveEpubDefaultDir()
local dir = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir()
if dir:sub(-1) ~= "/" then
@ -438,19 +433,13 @@ function DictQuickLookup:init()
},
{
id = "highlight",
text = self:getHighlightText(),
text = _("Highlight"),
enabled = not self:isDocless() and self.highlight ~= nil,
callback = function()
if self:getHighlightText() == highlight_strings.highlight then
self.ui:handleEvent(Event:new("Highlight"))
else
self.ui:handleEvent(Event:new("Unhighlight"))
end
self.save_highlight = not self.save_highlight
-- Just update, repaint and refresh *this* button
local this = self.button_table:getButtonById("highlight")
if not this then return end
this:enableDisable(self.highlight ~= nil)
this:setText(self:getHighlightText(), this.width)
this:setText(self.save_highlight and _("Unhighlight") or _("Highlight"), this.width)
this:refresh()
end,
},
@ -953,22 +942,6 @@ function DictQuickLookup:onShow()
return true
end
function DictQuickLookup:getHighlightedItem()
if self:isDocless() then return end
return self.ui.highlight:getHighlightBookmarkItem()
end
function DictQuickLookup:getHighlightText()
local item = self:getHighlightedItem()
if not item then
return highlight_strings.highlight, false
elseif self.ui.bookmark:isBookmarkAdded(item) then
return highlight_strings.unhighlight, false
else
return highlight_strings.highlight, true
end
end
function DictQuickLookup:isPrevDictAvaiable()
return self.dict_index > 1
end
@ -1150,13 +1123,19 @@ function DictQuickLookup:onClose(no_clear)
self.ui:handleEvent(Event:new("UpdateWikiLanguages", self.wiki_languages))
end
end
if self.highlight and not no_clear then
-- delay unhighlight of selection, so we can see where we stopped when
-- back from our journey into dictionary or wikipedia
local clear_id = self.highlight:getClearId()
UIManager:scheduleIn(0.5, function()
self.highlight:clear(clear_id)
end)
if self.save_highlight then
self.highlight:saveHighlight()
self.highlight:clear()
else
if self.highlight and not no_clear then
-- delay unhighlight of selection, so we can see where we stopped when
-- back from our journey into dictionary or wikipedia
local clear_id = self.highlight:getClearId()
UIManager:scheduleIn(0.5, function()
self.highlight:clear(clear_id)
end)
end
end
return true
end

@ -57,8 +57,14 @@ function CoverMenu:updateCache(file, status, do_create, pages)
local percent_finished = doc_settings:readSetting("percent_finished")
local summary = doc_settings:readSetting("summary")
status = summary and summary.status
local highlight = doc_settings:readSetting("highlight")
local has_highlight = highlight and next(highlight) and true
local has_highlight
local annotations = doc_settings:readSetting("annotations")
if annotations then
has_highlight = #annotations > 0
else
local highlight = doc_settings:readSetting("highlight")
has_highlight = highlight and next(highlight) and true
end
self.cover_info_cache[file] = table.pack(pages, percent_finished, status, has_highlight) -- may be a sparse array
else
if self.cover_info_cache and self.cover_info_cache[file] then

@ -235,6 +235,23 @@ function MyClipping:getImage(image)
end
end
function MyClipping:parseAnnotations(annotations, book)
for _, item in ipairs(annotations) do
if item.drawer then
local clipping = {
sort = "highlight",
page = item.pageno,
time = self:getTime(item.datetime),
text = self:getText(item.text),
note = self:getText(item.note),
chapter = item.chapter,
drawer = item.drawer,
}
table.insert(book, { clipping })
end
end
end
function MyClipping:parseHighlight(highlights, bookmarks, book)
--DEBUG("book", book.file)
@ -249,13 +266,14 @@ function MyClipping:parseHighlight(highlights, bookmarks, book)
local orphan_highlights = {}
for page, items in pairs(highlights) do
for _, item in ipairs(items) do
local clipping = {}
clipping.page = page
clipping.sort = "highlight"
clipping.time = self:getTime(item.datetime or "")
clipping.text = self:getText(item.text)
clipping.chapter = item.chapter
clipping.drawer = item.drawer
local clipping = {
sort = "highlight",
page = page,
time = self:getTime(item.datetime or ""),
text = self:getText(item.text),
chapter = item.chapter,
drawer = item.drawer,
}
local bookmark_found = false
for _, bookmark in pairs(bookmarks) do
if bookmark.datetime == item.datetime then
@ -316,9 +334,13 @@ end
function MyClipping:getClippingsFromBook(clippings, doc_path)
local doc_settings = DocSettings:open(doc_path)
local highlights = doc_settings:readSetting("highlight")
if not highlights then return end
local bookmarks = doc_settings:readSetting("bookmarks")
local highlights, bookmarks
local annotations = doc_settings:readSetting("annotations")
if annotations == nil then
highlights = doc_settings:readSetting("highlight")
if highlights == nil then return end
bookmarks = doc_settings:readSetting("bookmarks")
end
local props = doc_settings:readSetting("doc_props")
props = FileManagerBookInfo.extendProps(props, doc_path)
local title, author = self:getTitleAuthor(doc_path, props)
@ -326,8 +348,13 @@ function MyClipping:getClippingsFromBook(clippings, doc_path)
file = doc_path,
title = title,
author = author,
number_of_pages = doc_settings:readSetting("doc_pages"),
}
self:parseHighlight(highlights, bookmarks, clippings[title])
if annotations then
self:parseAnnotations(annotations, clippings[title])
else
self:parseHighlight(highlights, bookmarks, clippings[title])
end
end
function MyClipping:parseHistory()
@ -361,7 +388,7 @@ function MyClipping:parseCurrentDoc(view)
output_filename = util.getSafeFilename(title),
number_of_pages = view.document.info.number_of_pages,
}
self:parseHighlight(view.highlight.saved, view.ui.bookmark.bookmarks, clippings[title])
self:parseAnnotations(view.ui.annotation.annotations, clippings[title])
return clippings
end

@ -156,6 +156,7 @@ end
--- Parse and export highlights from the currently opened document.
function Exporter:exportCurrentNotes()
self.ui.annotation:updatePageNumbers()
local clippings = self:getDocumentClippings()
self:exportClippings(clippings)
end

@ -187,7 +187,7 @@ function ReaderStatistics:initData()
self.data.pages = self.document:getPageCount()
-- Update these numbers to what's actually stored in the settings
self.data.highlights, self.data.notes = self.ui.bookmark:getNumberOfHighlightsAndNotes()
self.data.highlights, self.data.notes = self.ui.annotation:getNumberOfHighlightsAndNotes()
self.id_curr_book = self:getIdBookDB()
self.book_read_pages, self.book_read_time = self:getPageTimeTotalStats(self.id_curr_book)
if self.book_read_pages > 0 then
@ -2727,27 +2727,14 @@ function ReaderStatistics:onCloseDocument()
self:insertDB()
end
function ReaderStatistics:onAddHighlight()
function ReaderStatistics:onAnnotationsModified(annotations)
if self.settings.is_enabled then
self.data.highlights = self.data.highlights + 1
end
end
function ReaderStatistics:onDelHighlight()
if self.settings.is_enabled then
self.data.highlights = self.data.highlights - 1
end
end
function ReaderStatistics:onAddNote()
if self.settings.is_enabled then
self.data.notes = self.data.notes + 1
end
end
function ReaderStatistics:onDelNote()
if self.settings.is_enabled then
self.data.notes = self.data.notes - 1
if annotations.nb_highlights_added then
self.data.highlights = self.data.highlights + annotations.nb_highlights_added
end
if annotations.nb_notes_added then
self.data.notes = self.data.notes + annotations.nb_notes_added
end
end
end

@ -20,7 +20,7 @@ describe("ReaderBookmark module", function()
readerui.highlight:onHoldPan(nil, { pos = pos1 })
readerui.highlight:onHoldRelease()
assert.truthy(readerui.highlight.highlight_dialog)
readerui.highlight:onHighlight()
readerui.highlight:saveHighlight()
UIManager:nextTick(function()
UIManager:close(readerui.highlight.highlight_dialog)
UIManager:close(readerui)
@ -67,17 +67,6 @@ describe("ReaderBookmark module", function()
UIManager:show(readerui)
readerui.rolling:onGotoPage(10)
end)
it("should compare bookmarks properly", function()
assert.truthy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'bar', page = 1, pos0 = 0, pos1 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo0', page = 1, pos0 = 0, pos1 = 0, },
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }))
end)
it("should show dogear after toggling non-bookmarked page", function()
assert.falsy(readerui.view.dogear_visible)
toggler_dogear(readerui)
@ -90,7 +79,7 @@ describe("ReaderBookmark module", function()
Screen:shot("screenshots/reader_bookmark_nodogear_epub.png")
assert.falsy(readerui.view.dogear_visible)
end)
it("should sort bookmarks with descending page numbers", function()
it("should sort bookmarks with ascending page numbers", function()
local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45}
for _, page in ipairs(pages) do
readerui.rolling:onGotoPage(page)
@ -99,11 +88,11 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_10marks_epub.png")
assert.are.same(10, #readerui.bookmark.bookmarks)
assert.are.same(10, #readerui.annotation.annotations)
assert.are.same(15, readerui.document:getPageFromXPointer(readerui.annotation.annotations[4].page))
end)
it("should keep descending page numbers after removing bookmarks", function()
local pages = {1, 30, 10, 40, 20}
readerui.bookmark.bookmarks = {}
for _, page in ipairs(pages) do
readerui.rolling:onGotoPage(page)
toggler_dogear(readerui)
@ -111,7 +100,7 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_5marks_epub.png")
assert.are.same(5, #readerui.bookmark.bookmarks)
assert.are.same(5, #readerui.annotation.annotations)
end)
it("should add bookmark by highlighting", function()
highlight_text(readerui,
@ -120,18 +109,18 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_6marks_epub.png")
assert.are.same(6, #readerui.bookmark.bookmarks)
assert.are.same(6, #readerui.annotation.annotations)
end)
it("should get previous bookmark for certain page", function()
local xpointer = readerui.document:getXPointer()
local bm_xpointer = readerui.bookmark:getPreviousBookmarkedPage(xpointer)
assert.are.same(6, #readerui.bookmark.bookmarks)
assert.are.same(1, readerui.document:getPageFromXPointer(bm_xpointer))
assert.are.same(6, #readerui.annotation.annotations)
assert.are.same(5, readerui.document:getPageFromXPointer(bm_xpointer))
end)
it("should get next bookmark for certain page", function()
local xpointer = readerui.document:getXPointer()
local bm_xpointer = readerui.bookmark:getNextBookmarkedPage(xpointer)
assert.are.same(20, readerui.document:getPageFromXPointer(bm_xpointer))
assert.are.same(15, readerui.document:getPageFromXPointer(bm_xpointer))
end)
end)
@ -154,34 +143,6 @@ describe("ReaderBookmark module", function()
UIManager:show(readerui)
readerui.paging:onGotoPage(10)
end)
it("should does bookmark comparison properly", function()
assert.truthy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'foo', page = 1, pos1 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'foo', page = 1, pos0 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 2 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 1, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 1, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 1 , x = 1, y = 3},
pos1 = { page = 1, x = 20, y = 2 }, }))
end)
it("should show dogear after toggling non-bookmarked page", function()
toggler_dogear(readerui)
Screen:shot("screenshots/reader_bookmark_dogear_pdf.png")
@ -192,7 +153,7 @@ describe("ReaderBookmark module", function()
Screen:shot("screenshots/reader_bookmark_nodogear_pdf.png")
assert.truthy(not readerui.view.dogear_visible)
end)
it("should sort bookmarks with descending page numbers", function()
it("should sort bookmarks with ascending page numbers", function()
local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45}
for _, page in ipairs(pages) do
readerui.paging:onGotoPage(page)
@ -201,7 +162,8 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_10marks_pdf.png")
assert.are.same(10, #readerui.bookmark.bookmarks)
assert.are.same(10, #readerui.annotation.annotations)
assert.are.same(15, readerui.annotation.annotations[4].page)
end)
it("should keep descending page numbers after removing bookmarks", function()
local pages = {1, 30, 10, 40, 20}
@ -212,14 +174,14 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_5marks_pdf.png")
assert.are.same(5, #readerui.bookmark.bookmarks)
assert.are.same(5, #readerui.annotation.annotations)
end)
it("should add bookmark by highlighting", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_6marks_pdf.png")
assert.are.same(6, #readerui.bookmark.bookmarks)
assert.are.same(6, #readerui.annotation.annotations)
end)
it("should get previous bookmark for certain page", function()
assert.are.same(5, readerui.bookmark:getPreviousBookmarkedPage(10))
@ -227,31 +189,5 @@ describe("ReaderBookmark module", function()
it("should get next bookmark for certain page", function()
assert.are.same(15, readerui.bookmark:getNextBookmarkedPage(10))
end)
it("should search/add bookmarks properly", function()
-- clear bookmarks created by previous tests
readerui.bookmark.bookmarks = {}
local p1 = { x = 0, y = 0, page = 100 }
local bm1 = { notes = 'foo', page = 10,
pos0 = { x = 0, y = 0, page = 100 }, pos1 = p1, }
assert.falsy(readerui.bookmark:isBookmarkAdded(bm1))
readerui.bookmark:addBookmark(bm1)
assert.are.same(readerui.bookmark.bookmarks, {bm1})
local bm2 = { notes = 'foo', page = 1,
pos0 = { x = 0, y = 0, page = 1 }, pos1 = p1, }
assert.falsy(readerui.bookmark:isBookmarkAdded(bm2))
readerui.bookmark:addBookmark(bm2)
assert.are.same({bm1, bm2}, readerui.bookmark.bookmarks)
local bm3 = { notes = 'foo', page = 5,
pos0 = { x = 0, y = 0, page = 5 }, pos1 = p1, }
assert.falsy(readerui.bookmark:isBookmarkAdded(bm3))
readerui.bookmark:addBookmark(bm3)
assert.are.same({bm1, bm3, bm2}, readerui.bookmark.bookmarks)
assert.truthy(readerui.bookmark:isBookmarkAdded(bm1))
assert.truthy(readerui.bookmark:isBookmarkAdded(bm2))
assert.truthy(readerui.bookmark:isBookmarkAdded(bm3))
end)
end)
end)

@ -17,7 +17,7 @@ describe("Readerhighlight module", function()
readerui.highlight:onHold(nil, { pos = pos0 })
readerui.highlight:onHoldRelease()
readerui.highlight:onHighlight()
readerui.highlight:saveHighlight()
assert.spy(s).was_called()
assert.spy(s).was_called_with(match.is_ref(readerui.languagesupport),
@ -50,7 +50,7 @@ describe("Readerhighlight module", function()
assert.truthy(readerui.highlight.highlight_dialog)
assert.truthy(UIManager._window_stack[next_slot].widget
== readerui.highlight.highlight_dialog)
readerui.highlight:onHighlight()
readerui.highlight:saveHighlight()
UIManager:scheduleIn(1, function()
UIManager:close(readerui.highlight.highlight_dialog)
UIManager:close(readerui)
@ -64,7 +64,7 @@ describe("Readerhighlight module", function()
readerui.highlight:onHold(nil, { pos = pos0 })
readerui.highlight:onHoldPan(nil, { pos = pos1 })
readerui.highlight:onHoldRelease()
readerui.highlight:onHighlight()
readerui.highlight:saveHighlight()
readerui.highlight:clear()
UIManager:close(readerui.highlight.highlight_dialog)
readerui.highlight:onTap(nil, { pos = pos2 })
@ -106,12 +106,13 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
end)
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 400, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_epub.png")
assert.spy(selection_spy).was_called()
assert.truthy(readerui.view.highlight.saved[page])
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should highlight text", function()
highlight_text(readerui,
@ -119,7 +120,7 @@ describe("Readerhighlight module", function()
Geom:new{ x = 400, y = 170 })
Screen:shot("screenshots/reader_highlight_text_epub.png")
assert.spy(selection_spy).was_called()
assert.truthy(readerui.view.highlight.saved[page])
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should response on tap gesture", function()
tap_highlight_text(readerui,
@ -154,6 +155,7 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
end)
it("should response on tap gesture #nocov", function()
tap_highlight_text(readerui,
@ -165,10 +167,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 })
Screen:shot("screenshots/reader_highlight_text_pdf.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
end)
describe("for scanned page without text layer", function()
@ -179,6 +183,7 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
end)
it("should respond to tap gesture #nocov", function()
tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })
@ -187,10 +192,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
Screen:shot("screenshots/reader_highlight_text_pdf_scanned.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
end)
describe("for reflowed page", function()
@ -202,6 +209,7 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
readerui.document.configurable.text_wrap = 0
UIManager:close(readerui) -- close to flush settings
-- We haven't torn it down yet
@ -214,10 +222,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
Screen:shot("screenshots/reader_highlight_text_pdf_reflowed.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
end)
end)
@ -247,6 +257,7 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
end)
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
@ -255,6 +266,7 @@ describe("Readerhighlight module", function()
it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 })
Screen:shot("screenshots/reader_highlight_text_pdf_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should response on tap gesture", function()
tap_highlight_text(readerui,
@ -262,6 +274,7 @@ describe("Readerhighlight module", function()
Geom:new{ x = 260, y = 150 },
Geom:new{ x = 280, y = 110 })
Screen:shot("screenshots/reader_tap_highlight_text_pdf_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
end)
describe("for scanned page without text layer", function()
@ -273,14 +286,17 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
end)
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should highlight text", function()
highlight_text(readerui, Geom:new{x = 192, y = 186}, Geom:new{x = 280, y = 186})
Screen:shot("screenshots/reader_highlight_text_pdf_scanned_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should response on tap gesture", function()
tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })
@ -296,6 +312,7 @@ describe("Readerhighlight module", function()
end)
after_each(function()
readerui.highlight:clear()
readerui.annotation.annotations = {}
readerui.document.configurable.text_wrap = 0
UIManager:close(readerui) -- close to flush settings
-- We haven't torn it down yet
@ -304,10 +321,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
Screen:shot("screenshots/reader_highlight_text_pdf_reflowed_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end)
it("should response on tap gesture", function()
tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })

Loading…
Cancel
Save