mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Content server viewer: Add user interface for bookmarking
This commit is contained in:
parent
7edad3048e
commit
3f30103802
@ -2,6 +2,7 @@
|
|||||||
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
from __python__ import bound_methods, hash_literals
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
|
from gettext import gettext as _
|
||||||
from read_book.cfi import create_cfi_cmp, cfi_sort_key
|
from read_book.cfi import create_cfi_cmp, cfi_sort_key
|
||||||
from read_book.globals import ui_operations
|
from read_book.globals import ui_operations
|
||||||
|
|
||||||
@ -85,6 +86,7 @@ def merge_annotation_maps(a, b):
|
|||||||
b_items = b[field] or v'[]'
|
b_items = b[field] or v'[]'
|
||||||
if not a_items.length:
|
if not a_items.length:
|
||||||
ans[field] = b_items
|
ans[field] = b_items
|
||||||
|
updated = True
|
||||||
continue
|
continue
|
||||||
if not b_items.length:
|
if not b_items.length:
|
||||||
ans[field] = a_items
|
ans[field] = a_items
|
||||||
@ -102,6 +104,39 @@ class AnnotationsManager: # {{{
|
|||||||
def __init__(self, view):
|
def __init__(self, view):
|
||||||
self.view = view
|
self.view = view
|
||||||
self.set_highlights()
|
self.set_highlights()
|
||||||
|
self.set_bookmarks()
|
||||||
|
|
||||||
|
def set_bookmarks(self, bookmarks):
|
||||||
|
bookmarks = bookmarks or v'[]'
|
||||||
|
self.bookmarks = list(bookmarks)
|
||||||
|
|
||||||
|
def all_bookmarks(self):
|
||||||
|
return self.bookmarks
|
||||||
|
|
||||||
|
def add_bookmark(self, title, cfi):
|
||||||
|
if not title or not cfi:
|
||||||
|
return
|
||||||
|
self.bookmarks = [b for b in self.bookmarks if b.title is not title]
|
||||||
|
self.bookmarks.push({
|
||||||
|
'type': 'bookmark',
|
||||||
|
'timestamp': Date().toISOString(),
|
||||||
|
'pos_type': 'epubcfi',
|
||||||
|
'pos': cfi,
|
||||||
|
'title': title,
|
||||||
|
})
|
||||||
|
sort_annot_list(self.bookmarks, bookmark_get_cfi)
|
||||||
|
if ui_operations.bookmarks_changed:
|
||||||
|
ui_operations.bookmarks_changed(self.bookmarks.as_array())
|
||||||
|
|
||||||
|
def default_bookmark_title(self):
|
||||||
|
all_titles = {bm.title:True for bm in self.bookmarks if not bm.removed}
|
||||||
|
base_default_title = _('Bookmark')
|
||||||
|
c = 0
|
||||||
|
while True:
|
||||||
|
c += 1
|
||||||
|
default_title = f'{base_default_title} #{c}'
|
||||||
|
if not all_titles[default_title]:
|
||||||
|
return default_title
|
||||||
|
|
||||||
def set_highlights(self, highlights):
|
def set_highlights(self, highlights):
|
||||||
highlights = highlights or v'[]'
|
highlights = highlights or v'[]'
|
||||||
|
51
src/pyj/read_book/bookmarks.pyj
Normal file
51
src/pyj/read_book/bookmarks.pyj
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
|
from elementmaker import E
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from book_list.item_list import build_list, create_item
|
||||||
|
from dom import ensure_id, set_css
|
||||||
|
from widgets import create_button
|
||||||
|
|
||||||
|
|
||||||
|
def goto_cfi(cfi, view):
|
||||||
|
view.goto_cfi(cfi)
|
||||||
|
|
||||||
|
|
||||||
|
def create_bookmarks_list(annotations_manager, onclick):
|
||||||
|
bookmarks = sorted(annotations_manager.all_bookmarks(), key=def(x): return x.title.toLowerCase();)
|
||||||
|
items = []
|
||||||
|
for bookmark in bookmarks:
|
||||||
|
if not bookmark.removed:
|
||||||
|
items.push(create_item(bookmark.title, data=bookmark.pos, action=onclick.bind(None, goto_cfi.bind(None, bookmark.pos))))
|
||||||
|
c = E.div(style='margin-top: 1ex')
|
||||||
|
build_list(c, items)
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
def create_new_bookmark(annotations_manager, data):
|
||||||
|
title = window.prompt(_('Enter title for bookmark:'), data.selected_text or annotations_manager.default_bookmark_title())
|
||||||
|
if not title:
|
||||||
|
return False
|
||||||
|
cfi = data.cfi
|
||||||
|
if data.selection_bounds?.start:
|
||||||
|
cfi = data.selection_bounds.start
|
||||||
|
annotations_manager.add_bookmark(title, cfi)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def new_bookmark(container_id, annotations_manager, data, onclick, ev):
|
||||||
|
if create_new_bookmark(annotations_manager, data):
|
||||||
|
onclick(def(): pass;)
|
||||||
|
|
||||||
|
|
||||||
|
def create_bookmarks_panel(annotations_manager, data, book, container, onclick):
|
||||||
|
set_css(container, display='flex', flex_direction='column')
|
||||||
|
container.appendChild(E.div(style='margin: 1rem'))
|
||||||
|
container = container.lastChild
|
||||||
|
container_id = ensure_id(container)
|
||||||
|
button = create_button(_('New bookmark'), 'plus', new_bookmark.bind(None, container_id, annotations_manager, data, onclick))
|
||||||
|
container.appendChild(E.div(button))
|
||||||
|
container.appendChild(E.div(create_bookmarks_list(annotations_manager, onclick)))
|
@ -18,6 +18,7 @@ from dom import (
|
|||||||
add_extra_css, build_rule, clear, ensure_id, set_css, svgicon, unique_id
|
add_extra_css, build_rule, clear, ensure_id, set_css, svgicon, unique_id
|
||||||
)
|
)
|
||||||
from modals import error_dialog
|
from modals import error_dialog
|
||||||
|
from read_book.bookmarks import create_bookmarks_panel
|
||||||
from read_book.globals import runtime, ui_operations
|
from read_book.globals import runtime, ui_operations
|
||||||
from read_book.goto import create_goto_panel, create_location_overlay
|
from read_book.goto import create_goto_panel, create_location_overlay
|
||||||
from read_book.open_book import create_open_book
|
from read_book.open_book import create_open_book
|
||||||
@ -271,7 +272,6 @@ class MainOverlay: # {{{
|
|||||||
bookmarks_action = ac(_('Bookmarks'), None, self.overlay.show_bookmarks, 'bookmark')
|
bookmarks_action = ac(_('Bookmarks'), None, self.overlay.show_bookmarks, 'bookmark')
|
||||||
toc_actions = E.ul(ac(_('Table of Contents'), None, self.overlay.show_toc, 'toc'))
|
toc_actions = E.ul(ac(_('Table of Contents'), None, self.overlay.show_toc, 'toc'))
|
||||||
|
|
||||||
if runtime.is_standalone_viewer:
|
|
||||||
toc_actions.appendChild(bookmarks_action)
|
toc_actions.appendChild(bookmarks_action)
|
||||||
toc_actions.appendChild(ac(_('Reference mode'), _('Toggle the Reference mode'), self.overlay.toggle_reference_mode, 'reference-mode'))
|
toc_actions.appendChild(ac(_('Reference mode'), _('Toggle the Reference mode'), self.overlay.toggle_reference_mode, 'reference-mode'))
|
||||||
|
|
||||||
@ -665,6 +665,10 @@ class Overlay:
|
|||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
ui_operations.toggle_bookmarks()
|
ui_operations.toggle_bookmarks()
|
||||||
return
|
return
|
||||||
|
def do_it(action, data):
|
||||||
|
self.panels.push(TOCOverlay(self, create_bookmarks_panel.bind(None, self.view.annotations_manager, data), _('Bookmarks')))
|
||||||
|
self.show_current_panel()
|
||||||
|
self.view.get_current_cfi('show-bookmarks', do_it)
|
||||||
|
|
||||||
def show_goto(self):
|
def show_goto(self):
|
||||||
self.hide_current_panel()
|
self.hide_current_panel()
|
||||||
|
@ -262,6 +262,18 @@ def shortcuts_definition():
|
|||||||
_('Toggle the Reference mode')
|
_('Toggle the Reference mode')
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'toggle_bookmarks': desc(
|
||||||
|
v"['Ctrl+b']",
|
||||||
|
'ui',
|
||||||
|
_('Show/hide bookmarks'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'new_bookmark': desc(
|
||||||
|
v"['Ctrl+Alt+b']",
|
||||||
|
'ui',
|
||||||
|
_('Create a new bookmark'),
|
||||||
|
),
|
||||||
|
|
||||||
'metadata': desc(
|
'metadata': desc(
|
||||||
v"['Ctrl+n', 'Ctrl+e']",
|
v"['Ctrl+n', 'Ctrl+e']",
|
||||||
'ui',
|
'ui',
|
||||||
@ -346,18 +358,6 @@ def shortcuts_group_desc():
|
|||||||
def add_standalone_viewer_shortcuts():
|
def add_standalone_viewer_shortcuts():
|
||||||
ismacos = 'macos' in window.navigator.userAgent
|
ismacos = 'macos' in window.navigator.userAgent
|
||||||
sc = shortcuts_definition()
|
sc = shortcuts_definition()
|
||||||
sc['toggle_bookmarks'] = desc(
|
|
||||||
v"['Ctrl+b']",
|
|
||||||
'ui',
|
|
||||||
_('Show/hide bookmarks'),
|
|
||||||
)
|
|
||||||
|
|
||||||
sc['new_bookmark'] = desc(
|
|
||||||
v"['Ctrl+Alt+b']",
|
|
||||||
'ui',
|
|
||||||
_('Create a new bookmark'),
|
|
||||||
)
|
|
||||||
|
|
||||||
sc['toggle_inspector'] = desc(
|
sc['toggle_inspector'] = desc(
|
||||||
v"['Ctrl+i']",
|
v"['Ctrl+i']",
|
||||||
'ui',
|
'ui',
|
||||||
|
@ -76,6 +76,7 @@ class ReadUI:
|
|||||||
ui_operations.toggle_toc = self.toggle_toc.bind(self)
|
ui_operations.toggle_toc = self.toggle_toc.bind(self)
|
||||||
ui_operations.toggle_full_screen = self.toggle_full_screen.bind(self)
|
ui_operations.toggle_full_screen = self.toggle_full_screen.bind(self)
|
||||||
ui_operations.highlights_changed = self.highlights_changed.bind(self)
|
ui_operations.highlights_changed = self.highlights_changed.bind(self)
|
||||||
|
ui_operations.bookmarks_changed = self.bookmarks_changed.bind(self)
|
||||||
ui_operations.annotations_synced = self.annotations_synced.bind(self)
|
ui_operations.annotations_synced = self.annotations_synced.bind(self)
|
||||||
ui_operations.wait_for_messages_from = self.wait_for_messages_from.bind(self)
|
ui_operations.wait_for_messages_from = self.wait_for_messages_from.bind(self)
|
||||||
ui_operations.stop_waiting_for_messages_from = self.stop_waiting_for_messages_from.bind(self)
|
ui_operations.stop_waiting_for_messages_from = self.stop_waiting_for_messages_from.bind(self)
|
||||||
@ -209,14 +210,19 @@ class ReadUI:
|
|||||||
def update_color_scheme(self):
|
def update_color_scheme(self):
|
||||||
self.view.update_color_scheme()
|
self.view.update_color_scheme()
|
||||||
|
|
||||||
def highlights_changed(self, highlights):
|
def annots_changed(self, amap):
|
||||||
amap = {'highlight': highlights}
|
|
||||||
library_id = self.base_url_data.library_id
|
library_id = self.base_url_data.library_id
|
||||||
book_id = self.base_url_data.book_id
|
book_id = self.base_url_data.book_id
|
||||||
fmt = self.base_url_data.fmt
|
fmt = self.base_url_data.fmt
|
||||||
self.db.update_annotations_data_from_key(library_id, book_id, fmt, {'annotations_map': amap})
|
self.db.update_annotations_data_from_key(library_id, book_id, fmt, {'annotations_map': amap})
|
||||||
ajax_send(f'book-update-annotations/{library_id}/{book_id}/{fmt}', amap, def (): pass;)
|
ajax_send(f'book-update-annotations/{library_id}/{book_id}/{fmt}', amap, def (): pass;)
|
||||||
|
|
||||||
|
def highlights_changed(self, highlights):
|
||||||
|
self.annots_changed({'highlight': highlights})
|
||||||
|
|
||||||
|
def bookmarks_changed(self, bookmarks):
|
||||||
|
self.annots_changed({'bookmark': bookmarks})
|
||||||
|
|
||||||
def annotations_synced(self, amap):
|
def annotations_synced(self, amap):
|
||||||
library_id = self.base_url_data.library_id
|
library_id = self.base_url_data.library_id
|
||||||
book_id = self.base_url_data.book_id
|
book_id = self.base_url_data.book_id
|
||||||
|
@ -14,6 +14,7 @@ from dom import add_extra_css, build_rule, clear, set_css, svgicon, unique_id
|
|||||||
from iframe_comm import IframeWrapper
|
from iframe_comm import IframeWrapper
|
||||||
from modals import error_dialog, warning_dialog
|
from modals import error_dialog, warning_dialog
|
||||||
from read_book.annotations import AnnotationsManager
|
from read_book.annotations import AnnotationsManager
|
||||||
|
from read_book.bookmarks import create_new_bookmark
|
||||||
from read_book.content_popup import ContentPopupOverlay
|
from read_book.content_popup import ContentPopupOverlay
|
||||||
from read_book.globals import (
|
from read_book.globals import (
|
||||||
current_book, rtl_page_progression, runtime, set_current_spine_item,
|
current_book, rtl_page_progression, runtime, set_current_spine_item,
|
||||||
@ -447,7 +448,10 @@ class View:
|
|||||||
elif data.name is 'toggle_toc':
|
elif data.name is 'toggle_toc':
|
||||||
ui_operations.toggle_toc()
|
ui_operations.toggle_toc()
|
||||||
elif data.name is 'toggle_bookmarks':
|
elif data.name is 'toggle_bookmarks':
|
||||||
|
if ui_operations.toggle_bookmarks:
|
||||||
ui_operations.toggle_bookmarks()
|
ui_operations.toggle_bookmarks()
|
||||||
|
else:
|
||||||
|
self.overlay.show_bookmarks()
|
||||||
elif data.name is 'toggle_highlights':
|
elif data.name is 'toggle_highlights':
|
||||||
ui_operations.toggle_highlights()
|
ui_operations.toggle_highlights()
|
||||||
elif data.name is 'new_bookmark':
|
elif data.name is 'new_bookmark':
|
||||||
@ -545,7 +549,12 @@ class View:
|
|||||||
self.selection_bar.update_position()
|
self.selection_bar.update_position()
|
||||||
|
|
||||||
def new_bookmark(self):
|
def new_bookmark(self):
|
||||||
|
if ui_operations.new_bookmark:
|
||||||
self.get_current_cfi('new-bookmark', ui_operations.new_bookmark)
|
self.get_current_cfi('new-bookmark', ui_operations.new_bookmark)
|
||||||
|
else:
|
||||||
|
self.get_current_cfi('new-bookmark', def (req_id, data):
|
||||||
|
create_new_bookmark(self.annotations_manager, data)
|
||||||
|
)
|
||||||
|
|
||||||
def update_selection_position(self, data):
|
def update_selection_position(self, data):
|
||||||
sel = self.currently_showing.selection
|
sel = self.currently_showing.selection
|
||||||
@ -833,6 +842,7 @@ class View:
|
|||||||
else:
|
else:
|
||||||
if unkey and book.annotations_map[unkey]:
|
if unkey and book.annotations_map[unkey]:
|
||||||
hl = book.annotations_map[unkey].highlight
|
hl = book.annotations_map[unkey].highlight
|
||||||
|
self.annotations_manager.set_bookmarks(book.annotations_map[unkey].bookmark or v'[]')
|
||||||
self.annotations_manager.set_highlights(hl or v'[]')
|
self.annotations_manager.set_highlights(hl or v'[]')
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
add_book_to_recently_viewed(book)
|
add_book_to_recently_viewed(book)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user