mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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.
This commit is contained in:
parent
e01cb3f319
commit
d61083a513
@ -1,30 +1,25 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import traceback, os, sys, functools, textwrap
|
import traceback, os, sys, functools
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QApplication, Qt, QIcon, QTimer, QByteArray, QSize, QTime, QDoubleSpinBox,
|
QApplication, Qt, QIcon, QTimer, QByteArray, QSize, QTime, QLabel,
|
||||||
QLabel, QPropertyAnimation, pyqtSignal, QUrl, QRegExpValidator, QRegExp,
|
QPropertyAnimation, QUrl, QInputDialog, QAction, QModelIndex)
|
||||||
QLineEdit, QToolButton, QMenu, QInputDialog, QAction, QModelIndex, QPalette,
|
|
||||||
QPainter, QBrush, QColor)
|
|
||||||
from PyQt4.QtWebKit import QWebView
|
|
||||||
|
|
||||||
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.printing import Printing
|
||||||
from calibre.gui2.viewer.bookmarkmanager import BookmarkManager
|
from calibre.gui2.viewer.bookmarkmanager import BookmarkManager
|
||||||
from calibre.gui2.viewer.toc import TOC
|
from calibre.gui2.viewer.toc import TOC
|
||||||
from calibre.gui2.widgets import ProgressIndicator
|
from calibre.gui2.widgets import ProgressIndicator
|
||||||
from calibre.gui2.main_window import MainWindow
|
from calibre.gui2 import (Application, ORG_NAME, APP_UID, choose_files,
|
||||||
from calibre.gui2 import (Application, ORG_NAME, APP_UID, choose_files, rating_font,
|
info_dialog, error_dialog, open_url, setup_gui_option_parser, detach_gui)
|
||||||
info_dialog, error_dialog, open_url, available_height, setup_gui_option_parser, detach_gui)
|
|
||||||
from calibre.ebooks.oeb.iterator.book import EbookIterator
|
from calibre.ebooks.oeb.iterator.book import EbookIterator
|
||||||
from calibre.ebooks import DRMError
|
from calibre.ebooks import DRMError
|
||||||
from calibre.constants import islinux, filesystem_encoding
|
from calibre.constants import islinux, filesystem_encoding
|
||||||
from calibre.utils.config import Config, StringConfig, JSONConfig
|
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.customize.ui import available_input_formats
|
||||||
from calibre import as_unicode, force_unicode, isbytestring
|
from calibre import as_unicode, force_unicode, isbytestring
|
||||||
from calibre.ptempfile import reset_base_dir
|
from calibre.ptempfile import reset_base_dir
|
||||||
@ -47,145 +42,15 @@ class Worker(Thread):
|
|||||||
self.exception = err
|
self.exception = err
|
||||||
self.traceback = traceback.format_exc()
|
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('<p>'+_(
|
|
||||||
'Go to a reference. To get reference numbers, use the <i>reference '
|
|
||||||
'mode</i>, 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):
|
class RecentAction(QAction):
|
||||||
|
|
||||||
def __init__(self, path, parent):
|
def __init__(self, path, parent):
|
||||||
self.path = path
|
self.path = path
|
||||||
QAction.__init__(self, os.path.basename(path), parent)
|
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 '
|
FLOW_MODE_TT = _('Switch to paged mode - where the text is broken up '
|
||||||
'into pages like a paper book')
|
'into pages like a paper book')
|
||||||
PAGED_MODE_TT = _('Switch to flow mode - where the text is not broken up '
|
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,
|
def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None,
|
||||||
start_in_fullscreen=False):
|
start_in_fullscreen=False):
|
||||||
MainWindow.__init__(self, None)
|
MainWindow.__init__(self, debug_javascript)
|
||||||
self.setupUi(self)
|
|
||||||
self.view.initialize_view(debug_javascript)
|
|
||||||
self.view.magnification_changed.connect(self.magnification_changed)
|
self.view.magnification_changed.connect(self.magnification_changed)
|
||||||
self.show_toc_on_open = False
|
self.show_toc_on_open = False
|
||||||
self.current_book_has_toc = False
|
self.current_book_has_toc = False
|
||||||
@ -212,31 +75,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.selected_text = None
|
self.selected_text = None
|
||||||
self.was_maximized = False
|
self.was_maximized = False
|
||||||
self.read_settings()
|
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.pos.value_changed.connect(self.update_pos_label)
|
||||||
self.splitter.setCollapsible(0, False)
|
|
||||||
self.splitter.setCollapsible(1, False)
|
|
||||||
self.pos.setMinimumWidth(150)
|
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.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.view.set_manager(self)
|
||||||
self.pi = ProgressIndicator(self)
|
self.pi = ProgressIndicator(self)
|
||||||
self.toc.setVisible(False)
|
|
||||||
self.action_quit = QAction(_('&Quit'), self)
|
self.action_quit = QAction(_('&Quit'), self)
|
||||||
self.addAction(self.action_quit)
|
self.addAction(self.action_quit)
|
||||||
self.view_resized_timer = QTimer(self)
|
self.view_resized_timer = QTimer(self)
|
||||||
@ -244,11 +87,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.view_resized_timer.setSingleShot(True)
|
self.view_resized_timer.setSingleShot(True)
|
||||||
self.resize_in_progress = False
|
self.resize_in_progress = False
|
||||||
self.action_quit.triggered.connect(self.quit)
|
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_reference_mode.triggered[bool].connect(self.view.reference_mode)
|
||||||
self.action_metadata.triggered[bool].connect(self.metadata.setVisible)
|
self.action_metadata.triggered[bool].connect(self.metadata.setVisible)
|
||||||
self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible)
|
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_next.triggered.connect(self.find_next)
|
||||||
self.action_find_previous.triggered.connect(self.find_previous)
|
self.action_find_previous.triggered.connect(self.find_previous)
|
||||||
self.action_full_screen.triggered[bool].connect(self.toggle_fullscreen)
|
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_back.triggered[bool].connect(self.back)
|
||||||
self.action_forward.triggered[bool].connect(self.forward)
|
self.action_forward.triggered[bool].connect(self.forward)
|
||||||
self.action_preferences.triggered.connect(self.do_config)
|
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.toc.pressed[QModelIndex].connect(self.toc_clicked)
|
||||||
self.reference.goto.connect(self.goto)
|
self.reference.goto.connect(self.goto)
|
||||||
|
|
||||||
self.bookmarks_menu = QMenu()
|
|
||||||
self.action_bookmark.setMenu(self.bookmarks_menu)
|
|
||||||
self.set_bookmarks([])
|
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()
|
self.load_theme_menu()
|
||||||
|
|
||||||
if pathtoebook is not None:
|
if pathtoebook is not None:
|
||||||
f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at)
|
f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at)
|
||||||
QTimer.singleShot(50, f)
|
QTimer.singleShot(50, f)
|
||||||
self.view.setMinimumSize(100, 100)
|
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('''
|
self.full_screen_label = QLabel('''
|
||||||
<center>
|
<center>
|
||||||
<h1>%s</h1>
|
<h1>%s</h1>
|
||||||
@ -338,28 +163,19 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
font-size: larger;
|
font-size: larger;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}'''
|
}'''
|
||||||
self.original_frame_style = self.frame.frameStyle()
|
|
||||||
self.pos_label = QLabel('2000/4000', self)
|
self.pos_label = QLabel('2000/4000', self)
|
||||||
self.pos_label.setVisible(False)
|
self.pos_label.setVisible(False)
|
||||||
self.pos_label.setFocusPolicy(Qt.NoFocus)
|
self.pos_label.setFocusPolicy(Qt.NoFocus)
|
||||||
self.clock_timer = QTimer(self)
|
self.clock_timer = QTimer(self)
|
||||||
self.clock_timer.timeout.connect(self.update_clock)
|
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.action_print.triggered.connect(self.print_book)
|
||||||
self.print_menu.actions()[0].triggered.connect(self.print_preview)
|
self.print_menu.actions()[0].triggered.connect(self.print_preview)
|
||||||
self.open_history_menu = QMenu()
|
|
||||||
self.clear_recent_history_action = QAction(
|
self.clear_recent_history_action = QAction(
|
||||||
_('Clear list of recently opened books'), self)
|
_('Clear list of recently opened books'), self)
|
||||||
self.clear_recent_history_action.triggered.connect(self.clear_recent_history)
|
self.clear_recent_history_action.triggered.connect(self.clear_recent_history)
|
||||||
self.build_recent_menu()
|
self.build_recent_menu()
|
||||||
self.action_open_ebook.setMenu(self.open_history_menu)
|
|
||||||
self.open_history_menu.triggered[QAction].connect(self.open_recent)
|
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'):
|
for x in ('tool_bar', 'tool_bar2'):
|
||||||
x = getattr(self, x)
|
x = getattr(self, x)
|
||||||
@ -400,7 +216,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.load_path(self.view.last_loaded_path)
|
self.load_path(self.view.last_loaded_path)
|
||||||
|
|
||||||
def set_toc_visible(self, yes):
|
def set_toc_visible(self, yes):
|
||||||
self.toc.setVisible(yes)
|
self.toc_dock.setVisible(yes)
|
||||||
|
|
||||||
def clear_recent_history(self, *args):
|
def clear_recent_history(self, *args):
|
||||||
vprefs.set('viewer_open_history', [])
|
vprefs.set('viewer_open_history', [])
|
||||||
@ -445,19 +261,16 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
state = bytearray(self.saveState(self.STATE_VERSION))
|
state = bytearray(self.saveState(self.STATE_VERSION))
|
||||||
vprefs['viewer_toolbar_state'] = state
|
vprefs['main_window_state'] = state
|
||||||
if not self.isFullScreen():
|
if not self.isFullScreen():
|
||||||
vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry()))
|
vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry()))
|
||||||
if self.current_book_has_toc:
|
if self.current_book_has_toc:
|
||||||
vprefs.set('viewer_toc_isvisible', bool(self.toc.isVisible()))
|
vprefs.set('viewer_toc_isvisible', bool(self.toc_dock.isVisible()))
|
||||||
if self.toc.isVisible():
|
|
||||||
vprefs.set('viewer_splitter_state',
|
|
||||||
bytearray(self.splitter.saveState()))
|
|
||||||
vprefs['multiplier'] = self.view.multiplier
|
vprefs['multiplier'] = self.view.multiplier
|
||||||
vprefs['in_paged_mode'] = not self.action_toggle_paged_mode.isChecked()
|
vprefs['in_paged_mode'] = not self.action_toggle_paged_mode.isChecked()
|
||||||
|
|
||||||
def restore_state(self):
|
def restore_state(self):
|
||||||
state = vprefs.get('viewer_toolbar_state', None)
|
state = vprefs.get('main_window_state', None)
|
||||||
if state is not None:
|
if state is not None:
|
||||||
try:
|
try:
|
||||||
state = QByteArray(state)
|
state = QByteArray(state)
|
||||||
@ -471,6 +284,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
# specific location, ensure they are visible.
|
# specific location, ensure they are visible.
|
||||||
self.tool_bar.setVisible(True)
|
self.tool_bar.setVisible(True)
|
||||||
self.tool_bar2.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',
|
self.action_toggle_paged_mode.setChecked(not vprefs.get('in_paged_mode',
|
||||||
True))
|
True))
|
||||||
self.toggle_paged_mode(self.action_toggle_paged_mode.isChecked(),
|
self.toggle_paged_mode(self.action_toggle_paged_mode.isChecked(),
|
||||||
@ -516,13 +330,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.was_maximized = self.isMaximized()
|
self.was_maximized = self.isMaximized()
|
||||||
if not self.view.document.fullscreen_scrollbar:
|
if not self.view.document.fullscreen_scrollbar:
|
||||||
self.vertical_scrollbar.setVisible(False)
|
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.centralwidget.layout().setContentsMargins(0, 0, 0, 0)
|
||||||
self.frame.setFrameStyle(self.frame.NoFrame|self.frame.Plain)
|
|
||||||
|
|
||||||
super(EbookViewer, self).showFullScreen()
|
super(EbookViewer, self).showFullScreen()
|
||||||
|
|
||||||
@ -587,17 +395,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.view.document.page_position.save()
|
self.view.document.page_position.save()
|
||||||
self.clock_label.setVisible(False)
|
self.clock_label.setVisible(False)
|
||||||
self.pos_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.clock_timer.stop()
|
||||||
self.vertical_scrollbar.setVisible(True)
|
self.vertical_scrollbar.setVisible(True)
|
||||||
self.window_mode_changed = 'normal'
|
self.window_mode_changed = 'normal'
|
||||||
self.settings_changed()
|
self.settings_changed()
|
||||||
self.full_screen_label.setVisible(False)
|
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:
|
if self.was_maximized:
|
||||||
super(EbookViewer, self).showMaximized()
|
super(EbookViewer, self).showMaximized()
|
||||||
else:
|
else:
|
||||||
@ -1154,20 +956,15 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
|
|
||||||
def read_settings(self):
|
def read_settings(self):
|
||||||
c = config().parse()
|
c = config().parse()
|
||||||
self.splitter.setSizes([1, 300])
|
|
||||||
if c.remember_window_size:
|
if c.remember_window_size:
|
||||||
wg = vprefs.get('viewer_window_geometry', None)
|
wg = vprefs.get('viewer_window_geometry', None)
|
||||||
if wg is not None:
|
if wg is not None:
|
||||||
self.restoreGeometry(wg)
|
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)
|
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:
|
if self.height() > av:
|
||||||
self.resize(self.width(), av)
|
self.resize(self.width(), av)
|
||||||
self.splitter.setCollapsible(0, False)
|
|
||||||
self.splitter.setCollapsible(1, False)
|
|
||||||
|
|
||||||
def config(defaults=None):
|
def config(defaults=None):
|
||||||
desc = _('Options to control the ebook viewer')
|
desc = _('Options to control the ebook viewer')
|
||||||
|
@ -1,319 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>EbookViewer</class>
|
|
||||||
<widget class="QMainWindow" name="EbookViewer">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>653</width>
|
|
||||||
<height>746</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>E-book Viewer</string>
|
|
||||||
</property>
|
|
||||||
<property name="windowIcon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/viewer.png</normaloff>:/images/viewer.png</iconset>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralwidget">
|
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QSplitter" name="splitter">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="childrenCollapsible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<widget class="TOCView" name="toc">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>150</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QFrame" name="frame">
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::StyledPanel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QScrollBar" name="vertical_scrollbar">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QScrollBar" name="horizontal_scrollbar">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="DocumentView" name="view" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QToolBar" name="tool_bar">
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>toolBar</string>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>32</width>
|
|
||||||
<height>32</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<attribute name="toolBarArea">
|
|
||||||
<enum>LeftToolBarArea</enum>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="toolBarBreak">
|
|
||||||
<bool>false</bool>
|
|
||||||
</attribute>
|
|
||||||
<addaction name="action_back"/>
|
|
||||||
<addaction name="action_forward"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_open_ebook"/>
|
|
||||||
<addaction name="action_copy"/>
|
|
||||||
<addaction name="action_font_size_larger"/>
|
|
||||||
<addaction name="action_font_size_smaller"/>
|
|
||||||
<addaction name="action_table_of_contents"/>
|
|
||||||
<addaction name="action_full_screen"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_previous_page"/>
|
|
||||||
<addaction name="action_next_page"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_bookmark"/>
|
|
||||||
<addaction name="action_reference_mode"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_preferences"/>
|
|
||||||
<addaction name="action_metadata"/>
|
|
||||||
<addaction name="action_load_theme"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="action_print"/>
|
|
||||||
</widget>
|
|
||||||
<widget class="QToolBar" name="tool_bar2">
|
|
||||||
<attribute name="toolBarArea">
|
|
||||||
<enum>TopToolBarArea</enum>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="toolBarBreak">
|
|
||||||
<bool>false</bool>
|
|
||||||
</attribute>
|
|
||||||
<addaction name="action_find_next"/>
|
|
||||||
<addaction name="action_find_previous"/>
|
|
||||||
<addaction name="action_toggle_paged_mode"/>
|
|
||||||
</widget>
|
|
||||||
<action name="action_back">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/back.png</normaloff>:/images/back.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Back</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_forward">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/forward.png</normaloff>:/images/forward.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Forward</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_next_page">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/next.png</normaloff>:/images/next.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Next page</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_previous_page">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/previous.png</normaloff>:/images/previous.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Previous page</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_font_size_larger">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/font_size_larger.png</normaloff>:/images/font_size_larger.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Increase font size</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_font_size_smaller">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/font_size_smaller.png</normaloff>:/images/font_size_smaller.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Decrease font size</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_table_of_contents">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/highlight_only_on.png</normaloff>:/images/highlight_only_on.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Table of Contents</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_metadata">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/dialog_information.png</normaloff>:/images/dialog_information.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Metadata</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_open_ebook">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/document_open.png</normaloff>:/images/document_open.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Open ebook</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_find_next">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/arrow-down.png</normaloff>:/images/arrow-down.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Find next</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Find next occurrence</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_copy">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/edit-copy.png</normaloff>:/images/edit-copy.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Copy to clipboard</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_preferences">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/config.png</normaloff>:/images/config.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Preferences</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_reference_mode">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/lookfeel.png</normaloff>:/images/lookfeel.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Reference Mode</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_bookmark">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/bookmarks.png</normaloff>:/images/bookmarks.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Bookmark</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_full_screen">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/page.png</normaloff>:/images/page.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Toggle full screen</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_print">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/print.png</normaloff>:/images/print.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Print</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_find_previous">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/arrow-up.png</normaloff>:/images/arrow-up.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Find previous</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Find previous occurrence</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_toggle_paged_mode">
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/scroll.png</normaloff>:/images/scroll.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Toggle Paged mode</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_load_theme">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Load theme</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Load a theme</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>DocumentView</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>calibre/gui2/viewer/documentview.h</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>TOCView</class>
|
|
||||||
<extends>QTreeView</extends>
|
|
||||||
<header>calibre/gui2/viewer/toc.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources>
|
|
||||||
<include location="../../../../resources/images.qrc"/>
|
|
||||||
</resources>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -17,12 +17,16 @@ class TOCView(QTreeView):
|
|||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
QTreeView.__init__(self, *args)
|
QTreeView.__init__(self, *args)
|
||||||
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
self.setMinimumWidth(80)
|
||||||
|
self.header().close()
|
||||||
self.setStyleSheet('''
|
self.setStyleSheet('''
|
||||||
QTreeView {
|
QTreeView {
|
||||||
background-color: palette(window);
|
background-color: palette(window);
|
||||||
color: palette(window-text);
|
color: palette(window-text);
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTreeView::item {
|
QTreeView::item {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
padding-top:0.5ex;
|
padding-top:0.5ex;
|
||||||
@ -34,17 +38,6 @@ class TOCView(QTreeView):
|
|||||||
border: 1px solid #bfcde4;
|
border: 1px solid #bfcde4;
|
||||||
border-radius: 6px;
|
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):
|
class TOCItem(QStandardItem):
|
||||||
@ -216,7 +209,6 @@ class TOC(QStandardItemModel):
|
|||||||
self.all_items = depth_first = []
|
self.all_items = depth_first = []
|
||||||
for t in toc:
|
for t in toc:
|
||||||
self.appendRow(TOCItem(spine, t, 0, depth_first))
|
self.appendRow(TOCItem(spine, t, 0, depth_first))
|
||||||
self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents')))
|
|
||||||
|
|
||||||
for x in depth_first:
|
for x in depth_first:
|
||||||
possible_enders = [t for t in depth_first if t.depth <= x.depth
|
possible_enders = [t for t in depth_first if t.depth <= x.depth
|
||||||
|
277
src/calibre/gui2/viewer/ui.py
Normal file
277
src/calibre/gui2/viewer/ui.py
Normal file
@ -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 <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
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('<p>'+_(
|
||||||
|
'Go to a reference. To get reference numbers, use the <i>reference '
|
||||||
|
'mode</i>, 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)
|
Loading…
x
Reference in New Issue
Block a user