mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-05 08:40:13 -04:00
Wire up the bookmarks panel fully
This commit is contained in:
parent
13be0d6712
commit
eeca114876
@ -7,14 +7,14 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
import json
|
||||
|
||||
from PyQt5.Qt import (
|
||||
QAction, QGridLayout, QIcon, QItemSelectionModel, QLabel, QListWidget,
|
||||
QListWidgetItem, QPushButton, Qt, QWidget, pyqtSignal
|
||||
QAction, QGridLayout, QIcon, QInputDialog, QItemSelectionModel, QLabel,
|
||||
QListWidget, QListWidgetItem, QPushButton, Qt, QWidget, pyqtSignal
|
||||
)
|
||||
|
||||
from calibre.gui2 import choose_files, choose_save_file
|
||||
from calibre.gui2.viewer.annotations import serialize_annotation
|
||||
from calibre.srv.render_book import parse_annotation
|
||||
from calibre.utils.date import EPOCH
|
||||
from calibre.utils.date import EPOCH, utcnow
|
||||
from calibre.utils.icu import sort_key
|
||||
from polyglot.builtins import range, unicode_type
|
||||
|
||||
@ -122,7 +122,7 @@ class BookmarkManager(QWidget):
|
||||
|
||||
def item_activated(self, item):
|
||||
bm = self.item_to_bm(item)
|
||||
self.activated.emit(bm)
|
||||
self.activated.emit(bm['pos'])
|
||||
|
||||
def set_bookmarks(self, bookmarks=()):
|
||||
self.bookmarks_list.clear()
|
||||
@ -146,12 +146,20 @@ class BookmarkManager(QWidget):
|
||||
for i in range(self.bookmarks_list.count()):
|
||||
yield self.item_to_bm(self.bookmarks_list.item(i))
|
||||
|
||||
def uniqify_bookmark_title(self, base):
|
||||
all_titles = {bm['title'] for bm in self.get_bookmarks()}
|
||||
c = 0
|
||||
q = base
|
||||
while q in all_titles:
|
||||
c += 1
|
||||
q = '{} #{}'.format(base, c)
|
||||
return q
|
||||
|
||||
def item_changed(self, item):
|
||||
self.bookmarks_list.blockSignals(True)
|
||||
title = unicode_type(item.data(Qt.DisplayRole))
|
||||
if not title:
|
||||
title = _('Unknown')
|
||||
item.setData(Qt.DisplayRole, title)
|
||||
title = unicode_type(item.data(Qt.DisplayRole)) or _('Unknown')
|
||||
title = self.uniqify_bookmark_title(title)
|
||||
item.setData(Qt.DisplayRole, title)
|
||||
bm = self.item_to_bm(item)
|
||||
bm['title'] = title
|
||||
item.setData(Qt.UserRole, self.bm_to_item(bm))
|
||||
@ -231,7 +239,7 @@ class BookmarkManager(QWidget):
|
||||
for bm in imported:
|
||||
if bm['title'] == 'calibre_current_page_bookmark':
|
||||
continue
|
||||
epubcfi = 'epubcfi(/{}/{})'.format(bm['spine'], bm['pos'].lstrip('/'))
|
||||
epubcfi = 'epubcfi(/{}/{})'.format((bm['spine'] + 1) * 2, bm['pos'].lstrip('/'))
|
||||
q = {'pos_type': 'epubcfi', 'pos': epubcfi, 'timestamp': EPOCH, 'title': bm['title']}
|
||||
if q not in bookmarks:
|
||||
bookmarks.append(q)
|
||||
@ -254,3 +262,22 @@ class BookmarkManager(QWidget):
|
||||
import_old_bookmarks(imported)
|
||||
else:
|
||||
import_current_bookmarks(imported)
|
||||
|
||||
def create_new_bookmark(self, pos_data):
|
||||
title, ok = QInputDialog.getText(self, _('Add bookmark'),
|
||||
_('Enter title for bookmark:'))
|
||||
title = unicode_type(title).strip()
|
||||
if not ok or not title:
|
||||
return
|
||||
title = self.uniqify_bookmark_title(title)
|
||||
bm = {
|
||||
'title': title,
|
||||
'pos_type': 'epubcfi',
|
||||
'pos': pos_data['cfi'],
|
||||
'timestamp': utcnow()
|
||||
}
|
||||
bookmarks = self.get_bookmarks()
|
||||
bookmarks.append(bm)
|
||||
self.set_bookmarks(bookmarks)
|
||||
self.set_current_bookmark(bm)
|
||||
self.edited.emit(bookmarks)
|
||||
|
@ -73,6 +73,11 @@ class EbookViewer(MainWindow):
|
||||
|
||||
self.bookmarks_dock = create_dock(_('Bookmarks'), 'bookmarks-dock', Qt.RightDockWidgetArea)
|
||||
self.bookmarks_widget = w = BookmarkManager(self)
|
||||
connect_lambda(
|
||||
w.create_requested, self,
|
||||
lambda self: self.web_view.get_current_cfi(self.bookmarks_widget.create_new_bookmark))
|
||||
self.bookmarks_widget.edited.connect(self.bookmarks_edited)
|
||||
self.bookmarks_widget.activated.connect(self.bookmark_activated)
|
||||
self.bookmarks_dock.setWidget(w)
|
||||
|
||||
self.inspector_dock = create_dock(_('Inspector'), 'inspector', Qt.RightDockWidgetArea)
|
||||
@ -144,6 +149,12 @@ class EbookViewer(MainWindow):
|
||||
def toc_searched(self, index):
|
||||
item = self.toc_model.itemFromIndex(index)
|
||||
self.web_view.goto_toc_node(item.node_id)
|
||||
|
||||
def bookmarks_edited(self, bookmarks):
|
||||
self.current_book_data['annotations_map']['bookmark'] = bookmarks
|
||||
|
||||
def bookmark_activated(self, cfi):
|
||||
self.web_view.goto_cfi(cfi)
|
||||
# }}}
|
||||
|
||||
# Load book {{{
|
||||
@ -194,6 +205,7 @@ class EbookViewer(MainWindow):
|
||||
toc = manifest.get('toc')
|
||||
self.toc_model = TOC(toc)
|
||||
self.toc.setModel(self.toc_model)
|
||||
self.bookmarks_widget.set_bookmarks(self.current_book_data['annotations_map']['bookmark'])
|
||||
|
||||
def load_book_annotations(self):
|
||||
amap = self.current_book_data['annotations_map']
|
||||
|
@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
|
||||
import os
|
||||
import sys
|
||||
from itertools import count
|
||||
|
||||
from PyQt5.Qt import (
|
||||
QApplication, QBuffer, QByteArray, QHBoxLayout, QSize, Qt, QTimer, QUrl, QWidget,
|
||||
@ -177,12 +178,15 @@ class ViewerBridge(Bridge):
|
||||
toggle_bookmarks = from_js()
|
||||
update_current_toc_nodes = from_js(object, object)
|
||||
toggle_full_screen = from_js()
|
||||
report_cfi = from_js(object, object)
|
||||
|
||||
create_view = to_js()
|
||||
show_preparing_message = to_js()
|
||||
start_book_load = to_js()
|
||||
goto_toc_node = to_js()
|
||||
goto_cfi = to_js()
|
||||
full_screen_state_changed = to_js()
|
||||
get_current_cfi = to_js()
|
||||
|
||||
|
||||
class WebPage(QWebEnginePage):
|
||||
@ -264,6 +268,8 @@ class WebView(RestartingWebEngineView):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self._host_widget = None
|
||||
self.callback_id_counter = count()
|
||||
self.callback_map = {}
|
||||
self.current_cfi = None
|
||||
RestartingWebEngineView.__init__(self, parent)
|
||||
self.dead_renderer_error_shown = False
|
||||
@ -278,6 +284,7 @@ class WebView(RestartingWebEngineView):
|
||||
self.bridge.toggle_bookmarks.connect(self.toggle_bookmarks)
|
||||
self.bridge.update_current_toc_nodes.connect(self.update_current_toc_nodes)
|
||||
self.bridge.toggle_full_screen.connect(self.toggle_full_screen)
|
||||
self.bridge.report_cfi.connect(self.call_callback)
|
||||
self.pending_bridge_ready_actions = {}
|
||||
self.setPage(self._page)
|
||||
self.setAcceptDrops(False)
|
||||
@ -354,6 +361,9 @@ class WebView(RestartingWebEngineView):
|
||||
def goto_toc_node(self, node_id):
|
||||
self.execute_when_ready('goto_toc_node', node_id)
|
||||
|
||||
def goto_cfi(self, cfi):
|
||||
self.execute_when_ready('goto_cfi', cfi)
|
||||
|
||||
def notify_full_screen_state_change(self, in_fullscreen_mode):
|
||||
self.execute_when_ready('full_screen_state_changed', in_fullscreen_mode)
|
||||
|
||||
@ -364,3 +374,16 @@ class WebView(RestartingWebEngineView):
|
||||
sd = vprefs['session_data']
|
||||
sd[key] = val
|
||||
vprefs['session_data'] = sd
|
||||
|
||||
def do_callback(self, func_name, callback):
|
||||
cid = next(self.callback_id_counter)
|
||||
self.callback_map[cid] = callback
|
||||
self.execute_when_ready('get_current_cfi', cid)
|
||||
|
||||
def call_callback(self, request_id, data):
|
||||
callback = self.callback_map.pop(request_id, None)
|
||||
if callback is not None:
|
||||
callback(data)
|
||||
|
||||
def get_current_cfi(self, callback):
|
||||
self.do_callback('get_current_cfi', callback)
|
||||
|
@ -86,6 +86,7 @@ class IframeBoss:
|
||||
'gesture_from_margin': self.gesture_from_margin,
|
||||
'find': self.find,
|
||||
'window_size': self.received_window_size,
|
||||
'get_current_cfi': self.get_current_cfi,
|
||||
}
|
||||
self.comm = IframeClient(handlers)
|
||||
self.last_window_ypos = 0
|
||||
@ -261,6 +262,20 @@ class IframeBoss:
|
||||
return 0
|
||||
return self.calculate_progress_frac(current_name, index)
|
||||
|
||||
def get_current_cfi(self, data):
|
||||
cfi = at_current()
|
||||
if cfi:
|
||||
spine = self.book.manifest.spine
|
||||
current_name = current_spine_item().name
|
||||
index = spine.indexOf(current_name)
|
||||
if index > -1:
|
||||
cfi = 'epubcfi(/{}{})'.format(2*(index+1), cfi)
|
||||
self.send_message(
|
||||
'report_cfi', cfi=cfi, progress_frac=self.calculate_progress_frac(current_name, index),
|
||||
file_progress_frac=progress_frac(), request_id=data.request_id)
|
||||
return
|
||||
self.send_message('report_cfi', cfi=None, progress_frac=0, file_progress_frac=0)
|
||||
|
||||
def update_cfi(self):
|
||||
cfi = at_current()
|
||||
if cfi:
|
||||
|
@ -157,6 +157,7 @@ class View:
|
||||
'goto_doc_boundary': def(data): self.goto_doc_boundary(data.start);,
|
||||
'scroll_to_anchor': self.on_scroll_to_anchor,
|
||||
'update_cfi': self.on_update_cfi,
|
||||
'report_cfi': self.on_report_cfi,
|
||||
'update_toc_position': self.on_update_toc_position,
|
||||
'content_loaded': self.on_content_loaded,
|
||||
'show_chrome': self.show_chrome,
|
||||
@ -503,6 +504,13 @@ class View:
|
||||
if toc_node:
|
||||
self.goto_named_destination(toc_node.dest, toc_node.frag)
|
||||
|
||||
def get_current_cfi(self, request_id):
|
||||
self.iframe_wrapper.send_message('get_current_cfi', request_id=request_id)
|
||||
|
||||
def on_report_cfi(self, data):
|
||||
ui_operations.report_cfi(
|
||||
data.request_id, {'cfi': data.cfi, 'progress_frac': data.progress_frac, 'file_progress_frac': data.file_progress_frac})
|
||||
|
||||
def on_update_cfi(self, data):
|
||||
overlay_shown = not self.processing_spine_item_display and self.overlay.is_visible
|
||||
if overlay_shown or self.search_overlay.is_visible or self.content_popup_overlay.is_visible:
|
||||
|
@ -199,11 +199,21 @@ def goto_toc_node(node_id):
|
||||
view.goto_toc_node(node_id)
|
||||
|
||||
|
||||
@from_python
|
||||
def goto_cfi(cfi):
|
||||
view.goto_bookpos(cfi)
|
||||
|
||||
|
||||
@from_python
|
||||
def full_screen_state_changed(viewer_in_full_screen):
|
||||
runtime.viewer_in_full_screen = viewer_in_full_screen
|
||||
|
||||
|
||||
@from_python
|
||||
def get_current_cfi(request_id):
|
||||
view.get_current_cfi(request_id)
|
||||
|
||||
|
||||
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||
if not error_object:
|
||||
# cross domain error
|
||||
@ -243,6 +253,8 @@ if window is window.top:
|
||||
to_python.update_current_toc_nodes(current_node_id, top_level_node_id)
|
||||
ui_operations.toggle_full_screen = def():
|
||||
to_python.toggle_full_screen()
|
||||
ui_operations.report_cfi = def(request_id, data):
|
||||
to_python.report_cfi(request_id, data)
|
||||
document.body.appendChild(E.div(id='view'))
|
||||
window.onerror = onerror
|
||||
create_modal_container()
|
||||
|
Loading…
x
Reference in New Issue
Block a user