home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_1387 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  34.3 KB  |  839 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import with_statement
  5. __license__ = 'GPL v3'
  6. __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
  7. import traceback
  8. import os
  9. import sys
  10. import functools
  11. import collections
  12. import re
  13. from functools import partial
  14. from threading import Thread
  15. from PyQt4.Qt import QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray, QDoubleSpinBox, QLabel, QTextBrowser, QPainter, QBrush, QColor, QStandardItemModel, QPalette, QStandardItem, QUrl, QRegExpValidator, QRegExp, QLineEdit, QToolButton, QMenu, QInputDialog, QAction, QKeySequence
  16. from calibre.gui2.viewer.main_ui import Ui_EbookViewer
  17. from calibre.gui2.viewer.printing import Printing
  18. from calibre.gui2.viewer.bookmarkmanager import BookmarkManager
  19. from calibre.gui2.widgets import ProgressIndicator
  20. from calibre.gui2.main_window import MainWindow
  21. from calibre.gui2 import Application, ORG_NAME, APP_UID, choose_files, info_dialog, error_dialog, open_url
  22. from calibre.ebooks.oeb.iterator import EbookIterator
  23. from calibre.ebooks import DRMError
  24. from calibre.constants import islinux, isfreebsd
  25. from calibre.utils.config import Config, StringConfig, dynamic
  26. from calibre.gui2.search_box import SearchBox2
  27. from calibre.ebooks.metadata import MetaInformation
  28. from calibre.customize.ui import available_input_formats
  29. from calibre.gui2.viewer.dictionary import Lookup
  30.  
  31. class TOCItem(QStandardItem):
  32.     
  33.     def __init__(self, toc):
  34.         text = toc.text
  35.         if text:
  36.             text = re.sub('\\s', ' ', text)
  37.         
  38.         None(QStandardItem.__init__, self if text else '')
  39.         self.abspath = toc.abspath
  40.         self.fragment = toc.fragment
  41.         for t in toc:
  42.             self.appendRow(TOCItem(t))
  43.         
  44.         self.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
  45.  
  46.     
  47.     def type(cls):
  48.         return QStandardItem.UserType + 10
  49.  
  50.     type = classmethod(type)
  51.  
  52.  
  53. class TOC(QStandardItemModel):
  54.     
  55.     def __init__(self, toc):
  56.         QStandardItemModel.__init__(self)
  57.         for t in toc:
  58.             self.appendRow(TOCItem(t))
  59.         
  60.         self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents')))
  61.  
  62.  
  63.  
  64. class Worker(Thread):
  65.     
  66.     def run(self):
  67.         
  68.         try:
  69.             Thread.run(self)
  70.             self.exception = None
  71.             self.traceback = None
  72.         except Exception:
  73.             err = None
  74.             self.exception = err
  75.             self.traceback = traceback.format_exc()
  76.  
  77.  
  78.  
  79.  
  80. class History(collections.deque):
  81.     
  82.     def __init__(self, action_back, action_forward):
  83.         self.action_back = action_back
  84.         self.action_forward = action_forward
  85.         collections.deque.__init__(self)
  86.         self.pos = 0
  87.         self.set_actions()
  88.  
  89.     
  90.     def set_actions(self):
  91.         self.action_back.setDisabled(self.pos < 1)
  92.         self.action_forward.setDisabled(self.pos + 1 >= len(self))
  93.  
  94.     
  95.     def back(self, from_pos):
  96.         if self.pos - 1 < 0:
  97.             return None
  98.         if self.pos == len(self):
  99.             self.append([])
  100.         
  101.         self[self.pos] = from_pos
  102.         self.pos -= 1
  103.         self.set_actions()
  104.         return self[self.pos]
  105.  
  106.     
  107.     def forward(self):
  108.         if self.pos + 1 >= len(self):
  109.             return None
  110.         self.pos += 1
  111.         self.set_actions()
  112.         return self[self.pos]
  113.  
  114.     
  115.     def add(self, item):
  116.         while len(self) > self.pos + 1:
  117.             self.pop()
  118.         self.append(item)
  119.         self.pos += 1
  120.         self.set_actions()
  121.  
  122.  
  123.  
  124. class Metadata(QLabel):
  125.     
  126.     def __init__(self, parent):
  127.         QTextBrowser.__init__(self, parent.centralWidget())
  128.         self.view = parent.splitter
  129.         self.setGeometry(self.view.geometry())
  130.         self.setWordWrap(True)
  131.         self.setVisible(False)
  132.  
  133.     
  134.     def show_opf(self, opf, ext = ''):
  135.         mi = MetaInformation(opf)
  136.         html = '<h2 align="center">%s</h2>%s\n<b>%s:</b> %s' % (_('Metadata'), u''.join(mi.to_html()), _('Book format'), ext.upper())
  137.         self.setText(html)
  138.  
  139.     
  140.     def setVisible(self, x):
  141.         self.setGeometry(self.view.geometry())
  142.         QLabel.setVisible(self, x)
  143.  
  144.     
  145.     def paintEvent(self, ev):
  146.         p = QPainter(self)
  147.         p.fillRect(ev.region().boundingRect(), QBrush(QColor(200, 200, 200, 220), Qt.SolidPattern))
  148.         p.end()
  149.         QLabel.paintEvent(self, ev)
  150.  
  151.  
  152.  
  153. class DoubleSpinBox(QDoubleSpinBox):
  154.     
  155.     def set_value(self, val):
  156.         self.blockSignals(True)
  157.         self.setValue(val)
  158.         self.blockSignals(False)
  159.  
  160.  
  161.  
  162. class HelpfulLineEdit(QLineEdit):
  163.     HELP_TEXT = _('Go to...')
  164.     
  165.     def __init__(self, *args):
  166.         QLineEdit.__init__(self, *args)
  167.         self.default_palette = QApplication.palette(self)
  168.         self.gray = QPalette(self.default_palette)
  169.         self.gray.setBrush(QPalette.Text, QBrush(QColor('gray')))
  170.         self.connect(self, (SIGNAL('editingFinished()'),), (lambda : self.emit(SIGNAL('goto(PyQt_PyObject)'), unicode(self.text()))))
  171.         self.clear_to_help_mode()
  172.  
  173.     
  174.     def focusInEvent(self, ev):
  175.         self.setPalette(QApplication.palette(self))
  176.         if self.in_help_mode():
  177.             self.setText('')
  178.         
  179.         return QLineEdit.focusInEvent(self, ev)
  180.  
  181.     
  182.     def in_help_mode(self):
  183.         return unicode(self.text()) == self.HELP_TEXT
  184.  
  185.     
  186.     def clear_to_help_mode(self):
  187.         self.setPalette(self.gray)
  188.         self.setText(self.HELP_TEXT)
  189.  
  190.  
  191.  
  192. class EbookViewer(MainWindow, Ui_EbookViewer):
  193.     STATE_VERSION = 1
  194.     
  195.     def __init__(self, pathtoebook = None, debug_javascript = False):
  196.         MainWindow.__init__(self, None)
  197.         self.setupUi(self)
  198.         self.show_toc_on_open = False
  199.         self.current_book_has_toc = False
  200.         self.base_window_title = unicode(self.windowTitle())
  201.         self.iterator = None
  202.         self.current_page = None
  203.         self.pending_search = None
  204.         self.pending_anchor = None
  205.         self.pending_reference = None
  206.         self.pending_bookmark = None
  207.         self.existing_bookmarks = []
  208.         self.selected_text = None
  209.         self.read_settings()
  210.         self.dictionary_box.hide()
  211.         (self.close_dictionary_view.clicked.connect,)((lambda x: self.dictionary_box.hide()))
  212.         self.history = History(self.action_back, self.action_forward)
  213.         self.metadata = Metadata(self)
  214.         self.pos = DoubleSpinBox()
  215.         self.pos.setDecimals(1)
  216.         self.pos.setToolTip(_('Position in book'))
  217.         self.pos.setSuffix('/' + _('Unknown') + '     ')
  218.         self.pos.setMinimum(1)
  219.         self.pos.setMinimumWidth(150)
  220.         self.tool_bar2.insertWidget(self.action_find_next, self.pos)
  221.         self.reference = HelpfulLineEdit()
  222.         self.reference.setValidator(QRegExpValidator(QRegExp('\\d+\\.\\d+'), self.reference))
  223.         self.reference.setToolTip(_('Go to a reference. To get reference numbers, use the reference mode.'))
  224.         self.tool_bar2.insertSeparator(self.action_find_next)
  225.         self.tool_bar2.insertWidget(self.action_find_next, self.reference)
  226.         self.tool_bar2.insertSeparator(self.action_find_next)
  227.         self.setFocusPolicy(Qt.StrongFocus)
  228.         self.search = SearchBox2(self)
  229.         self.search.setMinimumContentsLength(20)
  230.         self.search.initialize('viewer_search_history')
  231.         self.search.setToolTip(_('Search for text in book'))
  232.         self.search.setMinimumWidth(200)
  233.         self.tool_bar2.insertWidget(self.action_find_next, self.search)
  234.         self.view.set_manager(self)
  235.         self.view.document.debug_javascript = debug_javascript
  236.         self.pi = ProgressIndicator(self)
  237.         self.toc.setVisible(False)
  238.         self.action_quit = QAction(self)
  239.         self.addAction(self.action_quit)
  240.         self.action_quit.setShortcut(Qt.CTRL + Qt.Key_Q)
  241.         self.connect(self.action_quit, SIGNAL('triggered(bool)'), (lambda x: QApplication.instance().quit()))
  242.         self.action_copy.setDisabled(True)
  243.         self.action_metadata.setCheckable(True)
  244.         self.action_metadata.setShortcut(Qt.CTRL + Qt.Key_I)
  245.         self.action_table_of_contents.setCheckable(True)
  246.         self.toc.setMinimumWidth(80)
  247.         self.action_reference_mode.setCheckable(True)
  248.         self.connect(self.action_reference_mode, (SIGNAL('triggered(bool)'),), (lambda x: self.view.reference_mode(x)))
  249.         self.connect(self.action_metadata, (SIGNAL('triggered(bool)'),), (lambda x: self.metadata.setVisible(x)))
  250.         self.connect(self.action_table_of_contents, (SIGNAL('toggled(bool)'),), (lambda x: self.toc.setVisible(x)))
  251.         self.connect(self.action_copy, SIGNAL('triggered(bool)'), self.copy)
  252.         self.connect(self.action_font_size_larger, SIGNAL('triggered(bool)'), self.font_size_larger)
  253.         self.connect(self.action_font_size_smaller, SIGNAL('triggered(bool)'), self.font_size_smaller)
  254.         self.connect(self.action_open_ebook, SIGNAL('triggered(bool)'), self.open_ebook)
  255.         self.connect(self.action_next_page, (SIGNAL('triggered(bool)'),), (lambda x: self.view.next_page()))
  256.         self.connect(self.action_previous_page, (SIGNAL('triggered(bool)'),), (lambda x: self.view.previous_page()))
  257.         self.connect(self.action_find_next, (SIGNAL('triggered(bool)'),), (lambda x: self.find(self.search.smart_text, repeat = True)))
  258.         self.connect(self.action_find_previous, (SIGNAL('triggered(bool)'),), (lambda x: self.find(self.search.smart_text, repeat = True, backwards = True)))
  259.         self.connect(self.action_full_screen, SIGNAL('triggered(bool)'), self.toggle_fullscreen)
  260.         self.action_full_screen.setShortcuts([
  261.             Qt.Key_F11,
  262.             Qt.CTRL + Qt.SHIFT + Qt.Key_F])
  263.         self.connect(self.action_back, SIGNAL('triggered(bool)'), self.back)
  264.         self.connect(self.action_bookmark, SIGNAL('triggered(bool)'), self.bookmark)
  265.         self.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward)
  266.         self.connect(self.action_preferences, (SIGNAL('triggered(bool)'),), (lambda x: self.view.config(self)))
  267.         self.pos.editingFinished.connect(self.goto_page_num)
  268.         self.connect(self.vertical_scrollbar, (SIGNAL('valueChanged(int)'),), (lambda x: self.goto_page(x / 100)))
  269.         self.search.search.connect(self.find)
  270.         self.connect(self.toc, SIGNAL('clicked(QModelIndex)'), self.toc_clicked)
  271.         self.connect(self.reference, SIGNAL('goto(PyQt_PyObject)'), self.goto)
  272.         self.bookmarks_menu = QMenu()
  273.         self.action_bookmark.setMenu(self.bookmarks_menu)
  274.         self.set_bookmarks([])
  275.         if pathtoebook is not None:
  276.             f = functools.partial(self.load_ebook, pathtoebook)
  277.             QTimer.singleShot(50, f)
  278.         
  279.         self.view.setMinimumSize(100, 100)
  280.         self.toc.setCursor(Qt.PointingHandCursor)
  281.         self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
  282.         self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu)
  283.         self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup)
  284.         self.action_full_screen.setCheckable(True)
  285.         self.print_menu = QMenu()
  286.         self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview'))
  287.         self.action_print.setMenu(self.print_menu)
  288.         self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup)
  289.         self.connect(self.action_print, SIGNAL('triggered(bool)'), partial(self.print_book, preview = False))
  290.         self.connect(self.print_menu.actions()[0], SIGNAL('triggered(bool)'), partial(self.print_book, preview = True))
  291.         self.set_max_width()
  292.         ca = self.view.copy_action
  293.         ca.setShortcut(QKeySequence.Copy)
  294.         self.addAction(ca)
  295.         self.restore_state()
  296.  
  297.     
  298.     def closeEvent(self, e):
  299.         self.save_state()
  300.         return MainWindow.closeEvent(self, e)
  301.  
  302.     
  303.     def save_state(self):
  304.         state = str(self.saveState(self.STATE_VERSION))
  305.         dynamic['viewer_toolbar_state'] = state
  306.         dynamic.set('viewer_window_geometry', self.saveGeometry())
  307.         if self.current_book_has_toc:
  308.             dynamic.set('viewer_toc_isvisible', bool(self.toc.isVisible()))
  309.         
  310.         if self.toc.isVisible():
  311.             dynamic.set('viewer_splitter_state', bytearray(self.splitter.saveState()))
  312.         
  313.  
  314.     
  315.     def restore_state(self):
  316.         state = dynamic.get('viewer_toolbar_state', None)
  317.         if state is not None:
  318.             
  319.             try:
  320.                 state = QByteArray(state)
  321.                 self.restoreState(state, self.STATE_VERSION)
  322.  
  323.         
  324.  
  325.     
  326.     def lookup(self, word):
  327.         self.dictionary_view.setHtml('<html><body><p>' + _('Connecting to dict.org to lookup: <b>%s</b>…') % word + '</p></body></html>')
  328.         self.dictionary_box.show()
  329.         self._lookup = Lookup(word, parent = self)
  330.         self._lookup.finished.connect(self.looked_up)
  331.         self._lookup.start()
  332.  
  333.     
  334.     def looked_up(self, *args):
  335.         html = self._lookup.html_result
  336.         self._lookup = None
  337.         self.dictionary_view.setHtml(html)
  338.  
  339.     
  340.     def set_max_width(self):
  341.         config = config
  342.         import calibre.gui2.viewer.documentview
  343.         c = config().parse()
  344.         self.frame.setMaximumWidth(c.max_view_width)
  345.  
  346.     
  347.     def print_book(self, preview):
  348.         Printing(self.iterator.spine, preview)
  349.  
  350.     
  351.     def toggle_fullscreen(self, x):
  352.         if self.isFullScreen():
  353.             self.showNormal()
  354.         else:
  355.             self.showFullScreen()
  356.  
  357.     
  358.     def goto(self, ref):
  359.         if ref:
  360.             tokens = ref.split('.')
  361.             if len(tokens) > 1:
  362.                 spine_index = int(tokens[0]) - 1
  363.                 if spine_index == self.current_index:
  364.                     self.view.goto(ref)
  365.                 else:
  366.                     self.pending_reference = ref
  367.                     self.load_path(self.iterator.spine[spine_index])
  368.             
  369.         
  370.  
  371.     
  372.     def goto_bookmark(self, bm):
  373.         m = bm[1].split('#')
  374.         if len(m) > 1:
  375.             spine_index = int(m[0])
  376.             m = m[1]
  377.             if spine_index > -1 and self.current_index == spine_index:
  378.                 self.view.goto_bookmark(m)
  379.             else:
  380.                 self.pending_bookmark = bm
  381.                 if spine_index < 0 or spine_index >= len(self.iterator.spine):
  382.                     spine_index = 0
  383.                     self.pending_bookmark = None
  384.                 
  385.                 self.load_path(self.iterator.spine[spine_index])
  386.         
  387.  
  388.     
  389.     def toc_clicked(self, index):
  390.         item = self.toc_model.itemFromIndex(index)
  391.         url = QUrl.fromLocalFile(item.abspath)
  392.         if item.fragment:
  393.             url.setFragment(item.fragment)
  394.         
  395.         self.link_clicked(url)
  396.  
  397.     
  398.     def selection_changed(self, selected_text):
  399.         self.selected_text = selected_text.strip()
  400.         self.action_copy.setEnabled(bool(self.selected_text))
  401.  
  402.     
  403.     def copy(self, x):
  404.         if self.selected_text:
  405.             QApplication.clipboard().setText(self.selected_text)
  406.         
  407.  
  408.     
  409.     def back(self, x):
  410.         pos = self.history.back(self.pos.value())
  411.         if pos is not None:
  412.             self.goto_page(pos)
  413.         
  414.  
  415.     
  416.     def goto_page_num(self):
  417.         num = self.pos.value()
  418.         self.goto_page(num)
  419.  
  420.     
  421.     def forward(self, x):
  422.         pos = self.history.forward()
  423.         if pos is not None:
  424.             self.goto_page(pos)
  425.         
  426.  
  427.     
  428.     def goto_start(self):
  429.         self.goto_page(1)
  430.  
  431.     
  432.     def goto_end(self):
  433.         self.goto_page(self.pos.maximum())
  434.  
  435.     
  436.     def goto_page(self, new_page):
  437.         if self.current_page is not None:
  438.             for page in self.iterator.spine:
  439.                 if new_page >= page.start_page and new_page <= page.max_page:
  440.                     
  441.                     try:
  442.                         frac = float(new_page - page.start_page) / (page.pages - 1)
  443.                     except ZeroDivisionError:
  444.                         frac = 0
  445.  
  446.                     if page == self.current_page:
  447.                         self.view.scroll_to(frac)
  448.                     else:
  449.                         self.load_path(page, pos = frac)
  450.                 page == self.current_page
  451.             
  452.         
  453.  
  454.     
  455.     def open_ebook(self, checked):
  456.         files = choose_files(self, 'ebook viewer open dialog', _('Choose ebook'), [
  457.             (_('Ebooks'), available_input_formats())], all_files = False, select_only_single_file = True)
  458.         if files:
  459.             self.load_ebook(files[0])
  460.         
  461.  
  462.     
  463.     def font_size_larger(self, checked):
  464.         frac = self.view.magnify_fonts()
  465.         self.action_font_size_larger.setEnabled(self.view.multiplier() < 3)
  466.         self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
  467.         self.set_page_number(frac)
  468.  
  469.     
  470.     def font_size_smaller(self, checked):
  471.         frac = self.view.shrink_fonts()
  472.         self.action_font_size_larger.setEnabled(self.view.multiplier() < 3)
  473.         self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
  474.         self.set_page_number(frac)
  475.  
  476.     
  477.     def find(self, text, repeat = False, backwards = False):
  478.         if not text:
  479.             self.view.search('')
  480.             return self.search.search_done(False)
  481.         if self.view.search(text):
  482.             self.scrolled(self.view.scroll_fraction)
  483.             return self.search.search_done(True)
  484.         index = self.iterator.search(text, self.current_index, backwards = backwards)
  485.         if index is None:
  486.             return self.search.search_done(True)
  487.         self.pending_search = text
  488.         self.load_path(self.iterator.spine[index])
  489.  
  490.     
  491.     def do_search(self, text):
  492.         self.pending_search = None
  493.         if self.view.search(text):
  494.             self.scrolled(self.view.scroll_fraction)
  495.         
  496.  
  497.     
  498.     def keyPressEvent(self, event):
  499.         if event.key() == Qt.Key_Slash:
  500.             self.search.setFocus(Qt.OtherFocusReason)
  501.         else:
  502.             return MainWindow.keyPressEvent(self, event)
  503.         return event.key() == Qt.Key_Slash
  504.  
  505.     
  506.     def internal_link_clicked(self, frac):
  507.         self.history.add(self.pos.value())
  508.  
  509.     
  510.     def link_clicked(self, url):
  511.         path = os.path.abspath(unicode(url.toLocalFile()))
  512.         frag = None
  513.         if path in self.iterator.spine:
  514.             self.history.add(self.pos.value())
  515.             path = self.iterator.spine[self.iterator.spine.index(path)]
  516.             if url.hasFragment():
  517.                 frag = unicode(url.fragment())
  518.             
  519.             if path != self.current_page:
  520.                 self.pending_anchor = frag
  521.                 self.load_path(path)
  522.             elif frag:
  523.                 self.view.scroll_to(frag)
  524.             
  525.         else:
  526.             open_url(url)
  527.  
  528.     
  529.     def load_started(self):
  530.         self.open_progress_indicator(_('Loading flow...'))
  531.  
  532.     
  533.     def load_finished(self, ok):
  534.         self.close_progress_indicator()
  535.         path = self.view.path()
  536.         
  537.         try:
  538.             index = self.iterator.spine.index(path)
  539.         except (ValueError, AttributeError):
  540.             return -1
  541.  
  542.         self.current_page = self.iterator.spine[index]
  543.         self.current_index = index
  544.         self.set_page_number(self.view.scroll_fraction)
  545.         if self.pending_search is not None:
  546.             self.do_search(self.pending_search)
  547.             self.pending_search = None
  548.         
  549.         if self.pending_anchor is not None:
  550.             self.view.scroll_to(self.pending_anchor)
  551.             self.pending_anchor = None
  552.         
  553.         if self.pending_reference is not None:
  554.             self.view.goto(self.pending_reference)
  555.             self.pending_reference = None
  556.         
  557.         if self.pending_bookmark is not None:
  558.             self.goto_bookmark(self.pending_bookmark)
  559.             self.pending_bookmark = None
  560.         
  561.         return self.current_index
  562.  
  563.     
  564.     def goto_next_section(self):
  565.         nindex = (self.current_index + 1) % len(self.iterator.spine)
  566.         self.load_path(self.iterator.spine[nindex])
  567.  
  568.     
  569.     def goto_previous_section(self):
  570.         pindex = ((self.current_index - 1) + len(self.iterator.spine)) % len(self.iterator.spine)
  571.         self.load_path(self.iterator.spine[pindex])
  572.  
  573.     
  574.     def load_path(self, path, pos = 0):
  575.         self.open_progress_indicator(_('Laying out %s') % self.current_title)
  576.         self.view.load_path(path, pos = pos)
  577.  
  578.     
  579.     def viewport_resized(self, frac):
  580.         new_page = self.pos.value()
  581.         if self.current_page is not None:
  582.             
  583.             try:
  584.                 frac = float(new_page - self.current_page.start_page) / (self.current_page.pages - 1)
  585.             except ZeroDivisionError:
  586.                 frac = 0
  587.  
  588.             self.view.scroll_to(frac, notify = False)
  589.         else:
  590.             self.set_page_number(frac)
  591.  
  592.     
  593.     def close_progress_indicator(self):
  594.         self.pi.stop()
  595.         for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'):
  596.             getattr(self, o).setEnabled(True)
  597.         
  598.         self.unsetCursor()
  599.         self.view.setFocus(Qt.PopupFocusReason)
  600.  
  601.     
  602.     def open_progress_indicator(self, msg = ''):
  603.         self.pi.start(msg)
  604.         for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'):
  605.             getattr(self, o).setEnabled(False)
  606.         
  607.         self.setCursor(Qt.BusyCursor)
  608.  
  609.     
  610.     def bookmark(self, *args):
  611.         num = 1
  612.         bm = None
  613.         while True:
  614.             bm = _('Bookmark #%d') % num
  615.             if bm not in self.existing_bookmarks:
  616.                 break
  617.             
  618.             num += 1
  619.         (title, ok) = QInputDialog.getText(self, _('Add bookmark'), _('Enter title for bookmark:'), text = bm)
  620.         title = unicode(title).strip()
  621.         if ok and title:
  622.             pos = self.view.bookmark()
  623.             bookmark = '%d#%s' % (self.current_index, pos)
  624.             self.iterator.add_bookmark((title, bookmark))
  625.             self.set_bookmarks(self.iterator.bookmarks)
  626.         
  627.  
  628.     
  629.     def set_bookmarks(self, bookmarks):
  630.         self.bookmarks_menu.clear()
  631.         self.bookmarks_menu.addAction(_('Manage Bookmarks'), self.manage_bookmarks)
  632.         self.bookmarks_menu.addSeparator()
  633.         current_page = None
  634.         self.existing_bookmarks = []
  635.         for bm in bookmarks:
  636.             if bm[0] == 'calibre_current_page_bookmark':
  637.                 current_page = bm
  638.                 continue
  639.             self.existing_bookmarks.append(bm[0])
  640.             self.bookmarks_menu.addAction(bm[0], partial(self.goto_bookmark, bm))
  641.         
  642.         return current_page
  643.  
  644.     
  645.     def manage_bookmarks(self):
  646.         bmm = BookmarkManager(self, self.iterator.bookmarks)
  647.         if bmm.exec_() != BookmarkManager.Accepted:
  648.             return None
  649.         bookmarks = bmm.get_bookmarks()
  650.         if bookmarks != self.iterator.bookmarks:
  651.             self.iterator.set_bookmarks(bookmarks)
  652.             self.iterator.save_bookmarks()
  653.             self.set_bookmarks(bookmarks)
  654.         
  655.  
  656.     
  657.     def save_current_position(self):
  658.         
  659.         try:
  660.             pos = self.view.bookmark()
  661.             bookmark = '%d#%s' % (self.current_index, pos)
  662.             self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark))
  663.         except:
  664.             traceback.print_exc()
  665.  
  666.  
  667.     
  668.     def load_ebook(self, pathtoebook):
  669.         if self.iterator is not None:
  670.             self.save_current_position()
  671.             self.iterator.__exit__()
  672.         
  673.         self.iterator = EbookIterator(pathtoebook)
  674.         self.open_progress_indicator(_('Loading ebook...'))
  675.         worker = Worker(target = self.iterator.__enter__)
  676.         worker.start()
  677.         while worker.isAlive():
  678.             worker.join(0.1)
  679.             QApplication.processEvents()
  680.         if worker.exception is not None:
  681.             if isinstance(worker.exception, DRMError):
  682.                 error_dialog(self, _('DRM Error'), _('<p>This book is protected by <a href="%s">DRM</a>') % 'http://wiki.mobileread.com/wiki/DRM').exec_()
  683.             else:
  684.                 r = getattr(worker.exception, 'reason', worker.exception)
  685.                 error_dialog(self, _('Could not open ebook'), unicode(r), det_msg = worker.traceback, show = True)
  686.             self.close_progress_indicator()
  687.         else:
  688.             self.metadata.show_opf(self.iterator.opf, os.path.splitext(pathtoebook)[1][1:])
  689.             self.view.current_language = self.iterator.language
  690.             title = self.iterator.opf.title
  691.             if not title:
  692.                 title = os.path.splitext(os.path.basename(pathtoebook))[0]
  693.             
  694.             if self.iterator.toc:
  695.                 self.toc_model = TOC(self.iterator.toc)
  696.                 self.toc.setModel(self.toc_model)
  697.                 if self.show_toc_on_open:
  698.                     self.action_table_of_contents.setChecked(True)
  699.                 
  700.             else:
  701.                 self.action_table_of_contents.setChecked(False)
  702.             self.action_table_of_contents.setDisabled(not (self.iterator.toc))
  703.             self.current_book_has_toc = bool(self.iterator.toc)
  704.             self.current_title = title
  705.             self.setWindowTitle(self.base_window_title + ' - ' + title)
  706.             self.pos.setMaximum(sum(self.iterator.pages))
  707.             self.pos.setSuffix(' / %d' % sum(self.iterator.pages))
  708.             self.vertical_scrollbar.setMinimum(100)
  709.             self.vertical_scrollbar.setMaximum(100 * sum(self.iterator.pages))
  710.             self.vertical_scrollbar.setSingleStep(10)
  711.             self.vertical_scrollbar.setPageStep(100)
  712.             self.set_vscrollbar_value(1)
  713.             self.current_index = -1
  714.             QApplication.instance().alert(self, 5000)
  715.             previous = self.set_bookmarks(self.iterator.bookmarks)
  716.             if previous is not None:
  717.                 self.goto_bookmark(previous)
  718.             else:
  719.                 self.next_document()
  720.  
  721.     
  722.     def set_vscrollbar_value(self, pagenum):
  723.         self.vertical_scrollbar.blockSignals(True)
  724.         self.vertical_scrollbar.setValue(int(pagenum * 100))
  725.         self.vertical_scrollbar.blockSignals(False)
  726.  
  727.     
  728.     def set_page_number(self, frac):
  729.         if getattr(self, 'current_page', None) is not None:
  730.             page = self.current_page.start_page + frac * float(self.current_page.pages - 1)
  731.             self.pos.set_value(page)
  732.             self.set_vscrollbar_value(page)
  733.         
  734.  
  735.     
  736.     def scrolled(self, frac):
  737.         self.set_page_number(frac)
  738.  
  739.     
  740.     def next_document(self):
  741.         if self.current_index < len(self.iterator.spine) - 1:
  742.             self.load_path(self.iterator.spine[self.current_index + 1])
  743.         
  744.  
  745.     
  746.     def previous_document(self):
  747.         if self.current_index > 0:
  748.             self.load_path(self.iterator.spine[self.current_index - 1], pos = 1)
  749.         
  750.  
  751.     
  752.     def __enter__(self):
  753.         return self
  754.  
  755.     
  756.     def __exit__(self, *args):
  757.         if self.iterator is not None:
  758.             self.save_current_position()
  759.             self.iterator.__exit__(*args)
  760.         
  761.  
  762.     
  763.     def read_settings(self):
  764.         c = config().parse()
  765.         self.splitter.setSizes([
  766.             1,
  767.             300])
  768.         if c.remember_window_size:
  769.             wg = dynamic.get('viewer_window_geometry', None)
  770.             if wg is not None:
  771.                 self.restoreGeometry(wg)
  772.             
  773.             ss = dynamic.get('viewer_splitter_state', None)
  774.             if ss is not None:
  775.                 self.splitter.restoreState(ss)
  776.             
  777.             self.show_toc_on_open = dynamic.get('viewer_toc_isvisible', False)
  778.         
  779.  
  780.  
  781.  
  782. def config(defaults = None):
  783.     desc = _('Options to control the ebook viewer')
  784.     if defaults is None:
  785.         c = Config('viewer', desc)
  786.     else:
  787.         c = StringConfig(defaults, desc)
  788.     c.add_opt('raise_window', [
  789.         '--raise-window'], default = False, help = _('If specified, viewer window will try to come to the front when started.'))
  790.     c.add_opt('full_screen', [
  791.         '--full-screen',
  792.         '--fullscreen',
  793.         '-f'], default = False, help = _('If specified, viewer window will try to open full screen when started.'))
  794.     c.add_opt('remember_window_size', default = False, help = _('Remember last used window size'))
  795.     c.add_opt('debug_javascript', [
  796.         '--debug-javascript'], default = False, help = _('Print javascript alert and console messages to the console'))
  797.     return c
  798.  
  799.  
  800. def option_parser():
  801.     c = config()
  802.     return c.option_parser(usage = _('%prog [options] file\n\nView an ebook.\n'))
  803.  
  804.  
  805. def main(args = sys.argv):
  806.     os.environ.pop('CALIBRE_WORKER_TEMP_DIR', None)
  807.     parser = option_parser()
  808.     (opts, args) = parser.parse_args(args)
  809.     if False:
  810.         pass
  811.     pid = None if islinux or isfreebsd else -1
  812.     if pid <= 0:
  813.         app = Application(args)
  814.         app.setWindowIcon(QIcon(I('viewer.png')))
  815.         QApplication.setOrganizationName(ORG_NAME)
  816.         QApplication.setApplicationName(APP_UID)
  817.         main = None(EbookViewer if len(args) > 1 else None, debug_javascript = opts.debug_javascript)
  818.         sys.excepthook = main.unhandled_exception
  819.         main.show()
  820.         if opts.raise_window:
  821.             main.raise_()
  822.         
  823.     
  824.     if opts.full_screen:
  825.         main.action_full_screen.trigger()
  826.     
  827.     main.__enter__()
  828.     
  829.     try:
  830.         return app.exec_()
  831.     finally:
  832.         pass
  833.  
  834.     return 0
  835.  
  836. if __name__ == '__main__':
  837.     sys.exit(main())
  838.  
  839.