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
|
import json
|
||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QAction, QGridLayout, QIcon, QItemSelectionModel, QLabel, QListWidget,
|
QAction, QGridLayout, QIcon, QInputDialog, QItemSelectionModel, QLabel,
|
||||||
QListWidgetItem, QPushButton, Qt, QWidget, pyqtSignal
|
QListWidget, QListWidgetItem, QPushButton, Qt, QWidget, pyqtSignal
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre.gui2 import choose_files, choose_save_file
|
from calibre.gui2 import choose_files, choose_save_file
|
||||||
from calibre.gui2.viewer.annotations import serialize_annotation
|
from calibre.gui2.viewer.annotations import serialize_annotation
|
||||||
from calibre.srv.render_book import parse_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 calibre.utils.icu import sort_key
|
||||||
from polyglot.builtins import range, unicode_type
|
from polyglot.builtins import range, unicode_type
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ class BookmarkManager(QWidget):
|
|||||||
|
|
||||||
def item_activated(self, item):
|
def item_activated(self, item):
|
||||||
bm = self.item_to_bm(item)
|
bm = self.item_to_bm(item)
|
||||||
self.activated.emit(bm)
|
self.activated.emit(bm['pos'])
|
||||||
|
|
||||||
def set_bookmarks(self, bookmarks=()):
|
def set_bookmarks(self, bookmarks=()):
|
||||||
self.bookmarks_list.clear()
|
self.bookmarks_list.clear()
|
||||||
@ -146,12 +146,20 @@ class BookmarkManager(QWidget):
|
|||||||
for i in range(self.bookmarks_list.count()):
|
for i in range(self.bookmarks_list.count()):
|
||||||
yield self.item_to_bm(self.bookmarks_list.item(i))
|
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):
|
def item_changed(self, item):
|
||||||
self.bookmarks_list.blockSignals(True)
|
self.bookmarks_list.blockSignals(True)
|
||||||
title = unicode_type(item.data(Qt.DisplayRole))
|
title = unicode_type(item.data(Qt.DisplayRole)) or _('Unknown')
|
||||||
if not title:
|
title = self.uniqify_bookmark_title(title)
|
||||||
title = _('Unknown')
|
item.setData(Qt.DisplayRole, title)
|
||||||
item.setData(Qt.DisplayRole, title)
|
|
||||||
bm = self.item_to_bm(item)
|
bm = self.item_to_bm(item)
|
||||||
bm['title'] = title
|
bm['title'] = title
|
||||||
item.setData(Qt.UserRole, self.bm_to_item(bm))
|
item.setData(Qt.UserRole, self.bm_to_item(bm))
|
||||||
@ -231,7 +239,7 @@ class BookmarkManager(QWidget):
|
|||||||
for bm in imported:
|
for bm in imported:
|
||||||
if bm['title'] == 'calibre_current_page_bookmark':
|
if bm['title'] == 'calibre_current_page_bookmark':
|
||||||
continue
|
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']}
|
q = {'pos_type': 'epubcfi', 'pos': epubcfi, 'timestamp': EPOCH, 'title': bm['title']}
|
||||||
if q not in bookmarks:
|
if q not in bookmarks:
|
||||||
bookmarks.append(q)
|
bookmarks.append(q)
|
||||||
@ -254,3 +262,22 @@ class BookmarkManager(QWidget):
|
|||||||
import_old_bookmarks(imported)
|
import_old_bookmarks(imported)
|
||||||
else:
|
else:
|
||||||
import_current_bookmarks(imported)
|
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_dock = create_dock(_('Bookmarks'), 'bookmarks-dock', Qt.RightDockWidgetArea)
|
||||||
self.bookmarks_widget = w = BookmarkManager(self)
|
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.bookmarks_dock.setWidget(w)
|
||||||
|
|
||||||
self.inspector_dock = create_dock(_('Inspector'), 'inspector', Qt.RightDockWidgetArea)
|
self.inspector_dock = create_dock(_('Inspector'), 'inspector', Qt.RightDockWidgetArea)
|
||||||
@ -144,6 +149,12 @@ class EbookViewer(MainWindow):
|
|||||||
def toc_searched(self, index):
|
def toc_searched(self, index):
|
||||||
item = self.toc_model.itemFromIndex(index)
|
item = self.toc_model.itemFromIndex(index)
|
||||||
self.web_view.goto_toc_node(item.node_id)
|
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 {{{
|
# Load book {{{
|
||||||
@ -194,6 +205,7 @@ class EbookViewer(MainWindow):
|
|||||||
toc = manifest.get('toc')
|
toc = manifest.get('toc')
|
||||||
self.toc_model = TOC(toc)
|
self.toc_model = TOC(toc)
|
||||||
self.toc.setModel(self.toc_model)
|
self.toc.setModel(self.toc_model)
|
||||||
|
self.bookmarks_widget.set_bookmarks(self.current_book_data['annotations_map']['bookmark'])
|
||||||
|
|
||||||
def load_book_annotations(self):
|
def load_book_annotations(self):
|
||||||
amap = self.current_book_data['annotations_map']
|
amap = self.current_book_data['annotations_map']
|
||||||
|
@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QApplication, QBuffer, QByteArray, QHBoxLayout, QSize, Qt, QTimer, QUrl, QWidget,
|
QApplication, QBuffer, QByteArray, QHBoxLayout, QSize, Qt, QTimer, QUrl, QWidget,
|
||||||
@ -177,12 +178,15 @@ class ViewerBridge(Bridge):
|
|||||||
toggle_bookmarks = from_js()
|
toggle_bookmarks = from_js()
|
||||||
update_current_toc_nodes = from_js(object, object)
|
update_current_toc_nodes = from_js(object, object)
|
||||||
toggle_full_screen = from_js()
|
toggle_full_screen = from_js()
|
||||||
|
report_cfi = from_js(object, object)
|
||||||
|
|
||||||
create_view = to_js()
|
create_view = to_js()
|
||||||
show_preparing_message = to_js()
|
show_preparing_message = to_js()
|
||||||
start_book_load = to_js()
|
start_book_load = to_js()
|
||||||
goto_toc_node = to_js()
|
goto_toc_node = to_js()
|
||||||
|
goto_cfi = to_js()
|
||||||
full_screen_state_changed = to_js()
|
full_screen_state_changed = to_js()
|
||||||
|
get_current_cfi = to_js()
|
||||||
|
|
||||||
|
|
||||||
class WebPage(QWebEnginePage):
|
class WebPage(QWebEnginePage):
|
||||||
@ -264,6 +268,8 @@ class WebView(RestartingWebEngineView):
|
|||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self._host_widget = None
|
self._host_widget = None
|
||||||
|
self.callback_id_counter = count()
|
||||||
|
self.callback_map = {}
|
||||||
self.current_cfi = None
|
self.current_cfi = None
|
||||||
RestartingWebEngineView.__init__(self, parent)
|
RestartingWebEngineView.__init__(self, parent)
|
||||||
self.dead_renderer_error_shown = False
|
self.dead_renderer_error_shown = False
|
||||||
@ -278,6 +284,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
self.bridge.toggle_bookmarks.connect(self.toggle_bookmarks)
|
self.bridge.toggle_bookmarks.connect(self.toggle_bookmarks)
|
||||||
self.bridge.update_current_toc_nodes.connect(self.update_current_toc_nodes)
|
self.bridge.update_current_toc_nodes.connect(self.update_current_toc_nodes)
|
||||||
self.bridge.toggle_full_screen.connect(self.toggle_full_screen)
|
self.bridge.toggle_full_screen.connect(self.toggle_full_screen)
|
||||||
|
self.bridge.report_cfi.connect(self.call_callback)
|
||||||
self.pending_bridge_ready_actions = {}
|
self.pending_bridge_ready_actions = {}
|
||||||
self.setPage(self._page)
|
self.setPage(self._page)
|
||||||
self.setAcceptDrops(False)
|
self.setAcceptDrops(False)
|
||||||
@ -354,6 +361,9 @@ class WebView(RestartingWebEngineView):
|
|||||||
def goto_toc_node(self, node_id):
|
def goto_toc_node(self, node_id):
|
||||||
self.execute_when_ready('goto_toc_node', 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):
|
def notify_full_screen_state_change(self, in_fullscreen_mode):
|
||||||
self.execute_when_ready('full_screen_state_changed', 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 = vprefs['session_data']
|
||||||
sd[key] = val
|
sd[key] = val
|
||||||
vprefs['session_data'] = sd
|
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,
|
'gesture_from_margin': self.gesture_from_margin,
|
||||||
'find': self.find,
|
'find': self.find,
|
||||||
'window_size': self.received_window_size,
|
'window_size': self.received_window_size,
|
||||||
|
'get_current_cfi': self.get_current_cfi,
|
||||||
}
|
}
|
||||||
self.comm = IframeClient(handlers)
|
self.comm = IframeClient(handlers)
|
||||||
self.last_window_ypos = 0
|
self.last_window_ypos = 0
|
||||||
@ -261,6 +262,20 @@ class IframeBoss:
|
|||||||
return 0
|
return 0
|
||||||
return self.calculate_progress_frac(current_name, index)
|
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):
|
def update_cfi(self):
|
||||||
cfi = at_current()
|
cfi = at_current()
|
||||||
if cfi:
|
if cfi:
|
||||||
|
@ -157,6 +157,7 @@ class View:
|
|||||||
'goto_doc_boundary': def(data): self.goto_doc_boundary(data.start);,
|
'goto_doc_boundary': def(data): self.goto_doc_boundary(data.start);,
|
||||||
'scroll_to_anchor': self.on_scroll_to_anchor,
|
'scroll_to_anchor': self.on_scroll_to_anchor,
|
||||||
'update_cfi': self.on_update_cfi,
|
'update_cfi': self.on_update_cfi,
|
||||||
|
'report_cfi': self.on_report_cfi,
|
||||||
'update_toc_position': self.on_update_toc_position,
|
'update_toc_position': self.on_update_toc_position,
|
||||||
'content_loaded': self.on_content_loaded,
|
'content_loaded': self.on_content_loaded,
|
||||||
'show_chrome': self.show_chrome,
|
'show_chrome': self.show_chrome,
|
||||||
@ -503,6 +504,13 @@ class View:
|
|||||||
if toc_node:
|
if toc_node:
|
||||||
self.goto_named_destination(toc_node.dest, toc_node.frag)
|
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):
|
def on_update_cfi(self, data):
|
||||||
overlay_shown = not self.processing_spine_item_display and self.overlay.is_visible
|
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:
|
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)
|
view.goto_toc_node(node_id)
|
||||||
|
|
||||||
|
|
||||||
|
@from_python
|
||||||
|
def goto_cfi(cfi):
|
||||||
|
view.goto_bookpos(cfi)
|
||||||
|
|
||||||
|
|
||||||
@from_python
|
@from_python
|
||||||
def full_screen_state_changed(viewer_in_full_screen):
|
def full_screen_state_changed(viewer_in_full_screen):
|
||||||
runtime.viewer_in_full_screen = 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):
|
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||||
if not error_object:
|
if not error_object:
|
||||||
# cross domain error
|
# 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)
|
to_python.update_current_toc_nodes(current_node_id, top_level_node_id)
|
||||||
ui_operations.toggle_full_screen = def():
|
ui_operations.toggle_full_screen = def():
|
||||||
to_python.toggle_full_screen()
|
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'))
|
document.body.appendChild(E.div(id='view'))
|
||||||
window.onerror = onerror
|
window.onerror = onerror
|
||||||
create_modal_container()
|
create_modal_container()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user