From d61083a513b7e1188e4c50accccab88a126fab8d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 5 Aug 2014 15:34:45 +0530 Subject: [PATCH] E-book viewer: Make the Table of Contents panel a dockable window so it can be moved around and even made into a separate floating window Also refactor the viewer's ui code to not use Qt Designer. --- src/calibre/gui2/viewer/main.py | 235 ++--------------------- src/calibre/gui2/viewer/main.ui | 319 -------------------------------- src/calibre/gui2/viewer/toc.py | 16 +- src/calibre/gui2/viewer/ui.py | 277 +++++++++++++++++++++++++++ 4 files changed, 297 insertions(+), 550 deletions(-) delete mode 100644 src/calibre/gui2/viewer/main.ui create mode 100644 src/calibre/gui2/viewer/ui.py diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 10b9d3d24e..b8f87bc3a8 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -1,30 +1,25 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import traceback, os, sys, functools, textwrap +import traceback, os, sys, functools from functools import partial from threading import Thread from PyQt4.Qt import ( - QApplication, Qt, QIcon, QTimer, QByteArray, QSize, QTime, QDoubleSpinBox, - QLabel, QPropertyAnimation, pyqtSignal, QUrl, QRegExpValidator, QRegExp, - QLineEdit, QToolButton, QMenu, QInputDialog, QAction, QModelIndex, QPalette, - QPainter, QBrush, QColor) -from PyQt4.QtWebKit import QWebView + QApplication, Qt, QIcon, QTimer, QByteArray, QSize, QTime, QLabel, + QPropertyAnimation, QUrl, QInputDialog, QAction, QModelIndex) -from calibre.gui2.viewer.main_ui import Ui_EbookViewer +from calibre.gui2.viewer.ui import Main as MainWindow from calibre.gui2.viewer.printing import Printing from calibre.gui2.viewer.bookmarkmanager import BookmarkManager from calibre.gui2.viewer.toc import TOC from calibre.gui2.widgets import ProgressIndicator -from calibre.gui2.main_window import MainWindow -from calibre.gui2 import (Application, ORG_NAME, APP_UID, choose_files, rating_font, - info_dialog, error_dialog, open_url, available_height, setup_gui_option_parser, detach_gui) +from calibre.gui2 import (Application, ORG_NAME, APP_UID, choose_files, + info_dialog, error_dialog, open_url, setup_gui_option_parser, detach_gui) from calibre.ebooks.oeb.iterator.book import EbookIterator from calibre.ebooks import DRMError from calibre.constants import islinux, filesystem_encoding from calibre.utils.config import Config, StringConfig, JSONConfig -from calibre.gui2.search_box import SearchBox2 from calibre.customize.ui import available_input_formats from calibre import as_unicode, force_unicode, isbytestring from calibre.ptempfile import reset_base_dir @@ -47,145 +42,15 @@ class Worker(Thread): self.exception = err self.traceback = traceback.format_exc() -class History(list): - - def __init__(self, action_back, action_forward): - self.action_back = action_back - self.action_forward = action_forward - super(History, self).__init__(self) - self.insert_pos = 0 - self.back_pos = None - self.forward_pos = None - self.set_actions() - - def set_actions(self): - self.action_back.setDisabled(self.back_pos is None) - self.action_forward.setDisabled(self.forward_pos is None) - - def back(self, from_pos): - # Back clicked - if self.back_pos is None: - return None - item = self[self.back_pos] - self.forward_pos = self.back_pos+1 - if self.forward_pos >= len(self): - self.append(from_pos) - self.forward_pos = len(self) - 1 - self.insert_pos = self.forward_pos - self.back_pos = None if self.back_pos == 0 else self.back_pos - 1 - self.set_actions() - return item - - def forward(self, from_pos): - if self.forward_pos is None: - return None - item = self[self.forward_pos] - self.back_pos = self.forward_pos - 1 - if self.back_pos < 0: - self.back_pos = None - self.insert_pos = self.back_pos or 0 - self.forward_pos = None if self.forward_pos > len(self) - 2 else self.forward_pos + 1 - self.set_actions() - return item - - def add(self, item): - self[self.insert_pos:] = [] - while self.insert_pos > 0 and self[self.insert_pos-1] == item: - self.insert_pos -= 1 - self[self.insert_pos:] = [] - self.insert(self.insert_pos, item) - # The next back must go to item - self.back_pos = self.insert_pos - self.insert_pos += 1 - # There can be no forward - self.forward_pos = None - self.set_actions() - -class Metadata(QWebView): - - def __init__(self, parent): - QWebView.__init__(self, parent.centralWidget()) - s = self.settings() - s.setAttribute(s.JavascriptEnabled, False) - self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) - self.setAttribute(Qt.WA_OpaquePaintEvent, False) - palette = self.palette() - palette.setBrush(QPalette.Base, Qt.transparent) - self.page().setPalette(palette) - self.css = P('templates/book_details.css', data=True).decode('utf-8') - - self.view = parent.splitter - self.setGeometry(self.view.geometry()) - self.setVisible(False) - - def show_opf(self, opf, ext=''): - from calibre.gui2.book_details import render_html - from calibre.ebooks.metadata.book.render import mi_to_html - - def render_data(mi, use_roman_numbers=True, all_fields=False): - return mi_to_html(mi, use_roman_numbers=use_roman_numbers, rating_font=rating_font()) - - mi = opf.to_book_metadata() - html = render_html(mi, self.css, True, self, render_data_func=render_data) - self.setHtml(html) - - def setVisible(self, x): - if x: - self.setGeometry(self.view.geometry()) - QWebView.setVisible(self, x) - - def paintEvent(self, ev): - p = QPainter(self) - p.fillRect(ev.region().boundingRect(), QBrush(QColor(200, 200, 200, 220), Qt.SolidPattern)) - p.end() - QWebView.paintEvent(self, ev) - - -class DoubleSpinBox(QDoubleSpinBox): - - value_changed = pyqtSignal(object, object) - - def __init__(self, *args, **kwargs): - QDoubleSpinBox.__init__(self, *args, **kwargs) - self.tt = _('Position in book') - self.setToolTip(self.tt) - - def set_value(self, val): - self.blockSignals(True) - self.setValue(val) - self.setToolTip(self.tt + - ' [{0:.0%}]'.format(float(val)/self.maximum())) - self.blockSignals(False) - self.value_changed.emit(self.value(), self.maximum()) - -class Reference(QLineEdit): - - goto = pyqtSignal(object) - - def __init__(self, *args): - QLineEdit.__init__(self, *args) - self.setValidator(QRegExpValidator(QRegExp(r'\d+\.\d+'), self)) - self.setToolTip(textwrap.fill('

'+_( - 'Go to a reference. To get reference numbers, use the reference ' - 'mode, by clicking the reference mode button in the toolbar.'))) - if hasattr(self, 'setPlaceholderText'): - self.setPlaceholderText(_('Go to...')) - self.editingFinished.connect(self.editing_finished) - - def editing_finished(self): - text = unicode(self.text()) - self.setText('') - self.goto.emit(text) - class RecentAction(QAction): def __init__(self, path, parent): self.path = path QAction.__init__(self, os.path.basename(path), parent) -class EbookViewer(MainWindow, Ui_EbookViewer): +class EbookViewer(MainWindow): - STATE_VERSION = 1 + STATE_VERSION = 2 FLOW_MODE_TT = _('Switch to paged mode - where the text is broken up ' 'into pages like a paper book') PAGED_MODE_TT = _('Switch to flow mode - where the text is not broken up ' @@ -193,9 +58,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None, start_in_fullscreen=False): - MainWindow.__init__(self, None) - self.setupUi(self) - self.view.initialize_view(debug_javascript) + MainWindow.__init__(self, debug_javascript) self.view.magnification_changed.connect(self.magnification_changed) self.show_toc_on_open = False self.current_book_has_toc = False @@ -212,31 +75,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.selected_text = None self.was_maximized = False self.read_settings() - self.history = History(self.action_back, self.action_forward) - self.metadata = Metadata(self) - self.pos = DoubleSpinBox() - self.pos.setDecimals(1) - self.pos.setSuffix('/'+_('Unknown')+' ') - self.pos.setMinimum(1.) self.pos.value_changed.connect(self.update_pos_label) - self.splitter.setCollapsible(0, False) - self.splitter.setCollapsible(1, False) self.pos.setMinimumWidth(150) - self.tool_bar2.insertWidget(self.action_find_next, self.pos) - self.reference = Reference() - self.tool_bar2.insertSeparator(self.action_find_next) - self.tool_bar2.insertWidget(self.action_find_next, self.reference) - self.tool_bar2.insertSeparator(self.action_find_next) self.setFocusPolicy(Qt.StrongFocus) - self.search = SearchBox2(self) - self.search.setMinimumContentsLength(20) - self.search.initialize('viewer_search_history') - self.search.setToolTip(_('Search for text in book')) - self.search.setMinimumWidth(200) - self.tool_bar2.insertWidget(self.action_find_next, self.search) self.view.set_manager(self) self.pi = ProgressIndicator(self) - self.toc.setVisible(False) self.action_quit = QAction(_('&Quit'), self) self.addAction(self.action_quit) self.view_resized_timer = QTimer(self) @@ -244,11 +87,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.view_resized_timer.setSingleShot(True) self.resize_in_progress = False self.action_quit.triggered.connect(self.quit) - self.action_copy.setDisabled(True) - self.action_metadata.setCheckable(True) - self.action_table_of_contents.setCheckable(True) - self.toc.setMinimumWidth(80) - self.action_reference_mode.setCheckable(True) self.action_reference_mode.triggered[bool].connect(self.view.reference_mode) self.action_metadata.triggered[bool].connect(self.metadata.setVisible) self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible) @@ -261,8 +99,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.action_find_next.triggered.connect(self.find_next) self.action_find_previous.triggered.connect(self.find_previous) self.action_full_screen.triggered[bool].connect(self.toggle_fullscreen) - self.action_full_screen.setToolTip(_('Toggle full screen [%s]') % - _(' or ').join([x for x in self.view.shortcuts.get_shortcuts('Fullscreen')])) self.action_back.triggered[bool].connect(self.back) self.action_forward.triggered[bool].connect(self.forward) self.action_preferences.triggered.connect(self.do_config) @@ -274,24 +110,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.toc.pressed[QModelIndex].connect(self.toc_clicked) self.reference.goto.connect(self.goto) - self.bookmarks_menu = QMenu() - self.action_bookmark.setMenu(self.bookmarks_menu) self.set_bookmarks([]) - - self.themes_menu = QMenu() - self.action_load_theme.setMenu(self.themes_menu) - self.tool_bar.widgetForAction(self.action_load_theme).setPopupMode(QToolButton.InstantPopup) self.load_theme_menu() if pathtoebook is not None: f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at) QTimer.singleShot(50, f) self.view.setMinimumSize(100, 100) - self.toc.setCursor(Qt.PointingHandCursor) - self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) - self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) - self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.InstantPopup) - self.action_full_screen.setCheckable(True) self.full_screen_label = QLabel('''

%s

@@ -338,28 +163,19 @@ class EbookViewer(MainWindow, Ui_EbookViewer): font-size: larger; padding: 5px; }''' - self.original_frame_style = self.frame.frameStyle() self.pos_label = QLabel('2000/4000', self) self.pos_label.setVisible(False) self.pos_label.setFocusPolicy(Qt.NoFocus) self.clock_timer = QTimer(self) self.clock_timer.timeout.connect(self.update_clock) - self.print_menu = QMenu() - self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) - self.action_print.setMenu(self.print_menu) - self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup) self.action_print.triggered.connect(self.print_book) self.print_menu.actions()[0].triggered.connect(self.print_preview) - self.open_history_menu = QMenu() self.clear_recent_history_action = QAction( _('Clear list of recently opened books'), self) self.clear_recent_history_action.triggered.connect(self.clear_recent_history) self.build_recent_menu() - self.action_open_ebook.setMenu(self.open_history_menu) self.open_history_menu.triggered[QAction].connect(self.open_recent) - w = self.tool_bar.widgetForAction(self.action_open_ebook) - w.setPopupMode(QToolButton.MenuButtonPopup) for x in ('tool_bar', 'tool_bar2'): x = getattr(self, x) @@ -400,7 +216,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.load_path(self.view.last_loaded_path) def set_toc_visible(self, yes): - self.toc.setVisible(yes) + self.toc_dock.setVisible(yes) def clear_recent_history(self, *args): vprefs.set('viewer_open_history', []) @@ -445,19 +261,16 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def save_state(self): state = bytearray(self.saveState(self.STATE_VERSION)) - vprefs['viewer_toolbar_state'] = state + vprefs['main_window_state'] = state if not self.isFullScreen(): vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry())) if self.current_book_has_toc: - vprefs.set('viewer_toc_isvisible', bool(self.toc.isVisible())) - if self.toc.isVisible(): - vprefs.set('viewer_splitter_state', - bytearray(self.splitter.saveState())) + vprefs.set('viewer_toc_isvisible', bool(self.toc_dock.isVisible())) vprefs['multiplier'] = self.view.multiplier vprefs['in_paged_mode'] = not self.action_toggle_paged_mode.isChecked() def restore_state(self): - state = vprefs.get('viewer_toolbar_state', None) + state = vprefs.get('main_window_state', None) if state is not None: try: state = QByteArray(state) @@ -471,6 +284,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): # specific location, ensure they are visible. self.tool_bar.setVisible(True) self.tool_bar2.setVisible(True) + self.toc_dock.close() # This will be opened on book open, if the book has a toc and it was previously opened self.action_toggle_paged_mode.setChecked(not vprefs.get('in_paged_mode', True)) self.toggle_paged_mode(self.action_toggle_paged_mode.isChecked(), @@ -516,13 +330,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.was_maximized = self.isMaximized() if not self.view.document.fullscreen_scrollbar: self.vertical_scrollbar.setVisible(False) - self.frame.layout().setSpacing(0) - self._original_frame_margins = ( - self.centralwidget.layout().contentsMargins(), - self.frame.layout().contentsMargins()) - self.frame.layout().setContentsMargins(0, 0, 0, 0) self.centralwidget.layout().setContentsMargins(0, 0, 0, 0) - self.frame.setFrameStyle(self.frame.NoFrame|self.frame.Plain) super(EbookViewer, self).showFullScreen() @@ -587,17 +395,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.view.document.page_position.save() self.clock_label.setVisible(False) self.pos_label.setVisible(False) - self.frame.setFrameStyle(self.original_frame_style) - self.frame.layout().setSpacing(-1) self.clock_timer.stop() self.vertical_scrollbar.setVisible(True) self.window_mode_changed = 'normal' self.settings_changed() self.full_screen_label.setVisible(False) - if hasattr(self, '_original_frame_margins'): - om = self._original_frame_margins - self.centralwidget.layout().setContentsMargins(om[0]) - self.frame.layout().setContentsMargins(om[1]) if self.was_maximized: super(EbookViewer, self).showMaximized() else: @@ -1154,20 +956,15 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def read_settings(self): c = config().parse() - self.splitter.setSizes([1, 300]) if c.remember_window_size: wg = vprefs.get('viewer_window_geometry', None) if wg is not None: self.restoreGeometry(wg) - ss = vprefs.get('viewer_splitter_state', None) - if ss is not None: - self.splitter.restoreState(ss) self.show_toc_on_open = vprefs.get('viewer_toc_isvisible', False) - av = available_height() - 30 + desktop = QApplication.instance().desktop() + av = desktop.availableGeometry(self).height() - 30 if self.height() > av: self.resize(self.width(), av) - self.splitter.setCollapsible(0, False) - self.splitter.setCollapsible(1, False) def config(defaults=None): desc = _('Options to control the ebook viewer') diff --git a/src/calibre/gui2/viewer/main.ui b/src/calibre/gui2/viewer/main.ui deleted file mode 100644 index f99d155af3..0000000000 --- a/src/calibre/gui2/viewer/main.ui +++ /dev/null @@ -1,319 +0,0 @@ - - - EbookViewer - - - - 0 - 0 - 653 - 746 - - - - E-book Viewer - - - - :/images/viewer.png:/images/viewer.png - - - - - - - Qt::Horizontal - - - false - - - - - 150 - 0 - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Qt::Vertical - - - - - - - Qt::Horizontal - - - - - - - - - - - - - - - toolBar - - - - 32 - 32 - - - - LeftToolBarArea - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - TopToolBarArea - - - false - - - - - - - - - :/images/back.png:/images/back.png - - - Back - - - - - - :/images/forward.png:/images/forward.png - - - Forward - - - - - - :/images/next.png:/images/next.png - - - Next page - - - - - - :/images/previous.png:/images/previous.png - - - Previous page - - - - - - :/images/font_size_larger.png:/images/font_size_larger.png - - - Increase font size - - - - - - :/images/font_size_smaller.png:/images/font_size_smaller.png - - - Decrease font size - - - - - - :/images/highlight_only_on.png:/images/highlight_only_on.png - - - Table of Contents - - - - - - :/images/dialog_information.png:/images/dialog_information.png - - - Metadata - - - - - - :/images/document_open.png:/images/document_open.png - - - Open ebook - - - - - - :/images/arrow-down.png:/images/arrow-down.png - - - Find next - - - Find next occurrence - - - - - - :/images/edit-copy.png:/images/edit-copy.png - - - Copy to clipboard - - - - - - :/images/config.png:/images/config.png - - - Preferences - - - - - - :/images/lookfeel.png:/images/lookfeel.png - - - Reference Mode - - - - - - :/images/bookmarks.png:/images/bookmarks.png - - - Bookmark - - - - - - :/images/page.png:/images/page.png - - - Toggle full screen - - - - - - :/images/print.png:/images/print.png - - - Print - - - - - - :/images/arrow-up.png:/images/arrow-up.png - - - Find previous - - - Find previous occurrence - - - - - true - - - - :/images/scroll.png:/images/scroll.png - - - Toggle Paged mode - - - - - - :/images/wizard.png:/images/wizard.png - - - Load theme - - - Load a theme - - - - - - DocumentView - QWidget -
calibre/gui2/viewer/documentview.h
- 1 -
- - TOCView - QTreeView -
calibre/gui2/viewer/toc.h
-
-
- - - - -
diff --git a/src/calibre/gui2/viewer/toc.py b/src/calibre/gui2/viewer/toc.py index ba951f5c18..e2f770b3d9 100644 --- a/src/calibre/gui2/viewer/toc.py +++ b/src/calibre/gui2/viewer/toc.py @@ -17,12 +17,16 @@ class TOCView(QTreeView): def __init__(self, *args): QTreeView.__init__(self, *args) + self.setCursor(Qt.PointingHandCursor) + self.setMinimumWidth(80) + self.header().close() self.setStyleSheet(''' QTreeView { background-color: palette(window); color: palette(window-text); border: none; } + QTreeView::item { border: 1px solid transparent; padding-top:0.5ex; @@ -34,17 +38,6 @@ class TOCView(QTreeView): border: 1px solid #bfcde4; border-radius: 6px; } - QHeaderView::section { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 #616161, stop: 0.5 #505050, - stop: 0.6 #434343, stop:1 #656565); - color: white; - padding-left: 4px; - padding-top: 0.5ex; - padding-bottom: 0.5ex; - border: 1px solid #6c6c6c; - font-weight: bold; - } ''') class TOCItem(QStandardItem): @@ -216,7 +209,6 @@ class TOC(QStandardItemModel): self.all_items = depth_first = [] for t in toc: self.appendRow(TOCItem(spine, t, 0, depth_first)) - self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents'))) for x in depth_first: possible_enders = [t for t in depth_first if t.depth <= x.depth diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py new file mode 100644 index 0000000000..5aa8ecdebe --- /dev/null +++ b/src/calibre/gui2/viewer/ui.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Kovid Goyal ' + +import textwrap + +from PyQt4.Qt import ( + QIcon, QWidget, Qt, QGridLayout, QScrollBar, QToolBar, QAction, + QToolButton, QMenu, QDoubleSpinBox, pyqtSignal, QLineEdit, + QRegExpValidator, QRegExp, QPalette, QColor, QBrush, QPainter, + QDockWidget, QSize) +from PyQt4.QtWebKit import QWebView + +from calibre.gui2 import rating_font +from calibre.gui2.main_window import MainWindow +from calibre.gui2.search_box import SearchBox2 +from calibre.gui2.viewer.documentview import DocumentView +from calibre.gui2.viewer.toc import TOCView + +class DoubleSpinBox(QDoubleSpinBox): # {{{ + + value_changed = pyqtSignal(object, object) + + def __init__(self, *args, **kwargs): + QDoubleSpinBox.__init__(self, *args, **kwargs) + self.tt = _('Position in book') + self.setToolTip(self.tt) + + def set_value(self, val): + self.blockSignals(True) + self.setValue(val) + self.setToolTip(self.tt + + ' [{0:.0%}]'.format(float(val)/self.maximum())) + self.blockSignals(False) + self.value_changed.emit(self.value(), self.maximum()) +# }}} + +class Reference(QLineEdit): # {{{ + + goto = pyqtSignal(object) + + def __init__(self, *args): + QLineEdit.__init__(self, *args) + self.setValidator(QRegExpValidator(QRegExp(r'\d+\.\d+'), self)) + self.setToolTip(textwrap.fill('

'+_( + 'Go to a reference. To get reference numbers, use the reference ' + 'mode, by clicking the reference mode button in the toolbar.'))) + if hasattr(self, 'setPlaceholderText'): + self.setPlaceholderText(_('Go to...')) + self.editingFinished.connect(self.editing_finished) + + def editing_finished(self): + text = unicode(self.text()) + self.setText('') + self.goto.emit(text) +# }}} + +class Metadata(QWebView): # {{{ + + def __init__(self, parent): + QWebView.__init__(self, parent.centralWidget()) + s = self.settings() + s.setAttribute(s.JavascriptEnabled, False) + self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) + self.setAttribute(Qt.WA_OpaquePaintEvent, False) + palette = self.palette() + palette.setBrush(QPalette.Base, Qt.transparent) + self.page().setPalette(palette) + self.css = P('templates/book_details.css', data=True).decode('utf-8') + + self.view = parent.centralwidget + self.setGeometry(self.view.geometry()) + self.setVisible(False) + + def show_opf(self, opf, ext=''): + from calibre.gui2.book_details import render_html + from calibre.ebooks.metadata.book.render import mi_to_html + + def render_data(mi, use_roman_numbers=True, all_fields=False): + return mi_to_html(mi, use_roman_numbers=use_roman_numbers, rating_font=rating_font()) + + mi = opf.to_book_metadata() + html = render_html(mi, self.css, True, self, render_data_func=render_data) + self.setHtml(html) + + def setVisible(self, x): + if x: + self.setGeometry(self.view.geometry()) + QWebView.setVisible(self, x) + + def paintEvent(self, ev): + p = QPainter(self) + p.fillRect(ev.region().boundingRect(), QBrush(QColor(200, 200, 200, 220), Qt.SolidPattern)) + p.end() + QWebView.paintEvent(self, ev) +# }}} + +class History(list): # {{{ + + def __init__(self, action_back, action_forward): + self.action_back = action_back + self.action_forward = action_forward + super(History, self).__init__(self) + self.insert_pos = 0 + self.back_pos = None + self.forward_pos = None + self.set_actions() + + def set_actions(self): + self.action_back.setDisabled(self.back_pos is None) + self.action_forward.setDisabled(self.forward_pos is None) + + def back(self, from_pos): + # Back clicked + if self.back_pos is None: + return None + item = self[self.back_pos] + self.forward_pos = self.back_pos+1 + if self.forward_pos >= len(self): + self.append(from_pos) + self.forward_pos = len(self) - 1 + self.insert_pos = self.forward_pos + self.back_pos = None if self.back_pos == 0 else self.back_pos - 1 + self.set_actions() + return item + + def forward(self, from_pos): + if self.forward_pos is None: + return None + item = self[self.forward_pos] + self.back_pos = self.forward_pos - 1 + if self.back_pos < 0: + self.back_pos = None + self.insert_pos = self.back_pos or 0 + self.forward_pos = None if self.forward_pos > len(self) - 2 else self.forward_pos + 1 + self.set_actions() + return item + + def add(self, item): + self[self.insert_pos:] = [] + while self.insert_pos > 0 and self[self.insert_pos-1] == item: + self.insert_pos -= 1 + self[self.insert_pos:] = [] + self.insert(self.insert_pos, item) + # The next back must go to item + self.back_pos = self.insert_pos + self.insert_pos += 1 + # There can be no forward + self.forward_pos = None + self.set_actions() +# }}} + +class Main(MainWindow): + + def __init__(self, debug_javascript): + MainWindow.__init__(self, None) + self.setObjectName('EbookViewer') + self.setWindowIcon(QIcon(I('viewer.png'))) + self.setDockOptions(self.AnimatedDocks | self.AllowTabbedDocks) + + self.centralwidget = c = QWidget(self) + c.setObjectName('centralwidget') + self.setCentralWidget(c) + self.central_layout = cl = QGridLayout(c) + c.setLayout(cl), cl.setContentsMargins(0, 0, 0, 0) + + self.view = v = DocumentView(self) + self.view.initialize_view(debug_javascript) + v.setObjectName('view') + cl.addWidget(v) + + self.vertical_scrollbar = vs = QScrollBar(c) + vs.setOrientation(Qt.Vertical), vs.setObjectName("vertical_scrollbar") + cl.addWidget(vs, 0, 1, 2, 1) + + self.horizontal_scrollbar = hs = QScrollBar(c) + hs.setOrientation(Qt.Vertical), hs.setObjectName("horizontal_scrollbar") + cl.addWidget(hs, 1, 0, 1, 1) + + self.tool_bar = tb = QToolBar(self) + tb.setObjectName('tool_bar'), tb.setIconSize(QSize(32, 32)) + self.addToolBar(Qt.LeftToolBarArea, tb) + + self.tool_bar2 = tb2 = QToolBar(self) + tb2.setObjectName('tool_bar2') + self.addToolBar(Qt.TopToolBarArea, tb2) + self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) + self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) + + self.pos = DoubleSpinBox() + self.pos.setDecimals(1) + self.pos.setSuffix('/'+_('Unknown')+' ') + self.pos.setMinimum(1.) + self.tool_bar2.addWidget(self.pos) + self.tool_bar2.addSeparator() + self.reference = Reference() + self.tool_bar2.addWidget(self.reference) + self.tool_bar2.addSeparator() + self.search = SearchBox2(self) + self.search.setMinimumContentsLength(20) + self.search.initialize('viewer_search_history') + self.search.setToolTip(_('Search for text in book')) + self.search.setMinimumWidth(200) + self.tool_bar2.addWidget(self.search) + + self.toc_dock = d = QDockWidget(_('Table of Contents'), self) + self.toc = TOCView(self) + d.setObjectName('toc-dock') + d.setWidget(self.toc) + d.close() # starts out hidden + self.addDockWidget(Qt.LeftDockWidgetArea, d) + d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) + + self.create_actions() + + self.metadata = Metadata(self) + self.history = History(self.action_back, self.action_forward) + + self.resize(653, 746) + + def create_actions(self): + def a(name, text, icon, tb=None, sc_name=None, menu_name=None, popup_mode=QToolButton.MenuButtonPopup): + name = 'action_' + name + if isinstance(text, QDockWidget): + ac = text.toggleViewAction() + ac.setIcon(QIcon(I(icon))) + else: + ac = QAction(QIcon(I(icon)), text, self) + setattr(self, name, ac) + ac.setObjectName(name) + (tb or self.tool_bar).addAction(ac) + if sc_name: + ac.setToolTip(unicode(ac.text()) + (' [%s]' % _(' or ').join(self.view.shortcuts.get_shortcuts(sc_name)))) + if menu_name is not None: + menu_name += '_menu' + m = QMenu() + setattr(self, menu_name, m) + ac.setMenu(m) + w = (tb or self.tool_bar).widgetForAction(ac) + w.setPopupMode(popup_mode) + return ac + + a('back', _('Back'), 'back.png') + a('forward', _('Forward'), 'forward.png') + self.tool_bar.addSeparator() + + a('open_ebook', _('Open ebook'), 'document_open.png', menu_name='open_history') + a('copy', _('Copy to clipboard'), 'edit-copy.png').setDisabled(True) + a('font_size_larger', _('Increase font size'), 'font_size_larger.png') + a('font_size_smaller', _('Decrease font size'), 'font_size_smaller.png') + a('table_of_contents', self.toc_dock, 'highlight_only_on.png') + a('full_screen', _('Toggle full screen'), 'page.png', sc_name='Fullscreen').setCheckable(True) + self.tool_bar.addSeparator() + + a('previous_page', _('Previous page'), 'previous.png') + a('next_page', _('Next page'), 'next.png') + self.tool_bar.addSeparator() + + a('bookmark', _('Bookmark'), 'bookmarks.png', menu_name='bookmarks', popup_mode=QToolButton.InstantPopup) + a('reference_mode', _('Reference mode'), 'lookfeel.png').setCheckable(True) + self.tool_bar.addSeparator() + + a('preferences', _('Preferences'), 'config.png') + a('metadata', _('Show book metadata'), 'dialog_information.png').setCheckable(True) + a('load_theme', _('Load a theme'), 'wizard.png', menu_name='themes', popup_mode=QToolButton.InstantPopup) + self.tool_bar.addSeparator() + + a('print', _('Print'), 'print.png', menu_name='print') + self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) + + a('find_next', _('Find next occurrence'), 'arrow-down.png', tb=self.tool_bar2) + a('find_previous', _('Find previous occurrence'), 'arrow-up.png', tb=self.tool_bar2) + a('toggle_paged_mode', _('Toggle paged mode'), 'scroll.png', tb=self.tool_bar2)