home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1328 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  33.4 KB  |  782 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.iterator = None
  199.         self.current_page = None
  200.         self.pending_search = None
  201.         self.pending_anchor = None
  202.         self.pending_reference = None
  203.         self.pending_bookmark = None
  204.         self.selected_text = None
  205.         self.read_settings()
  206.         self.dictionary_box.hide()
  207.         (self.close_dictionary_view.clicked.connect,)((lambda x: self.dictionary_box.hide()))
  208.         self.history = History(self.action_back, self.action_forward)
  209.         self.metadata = Metadata(self)
  210.         self.pos = DoubleSpinBox()
  211.         self.pos.setDecimals(1)
  212.         self.pos.setToolTip(_('Position in book'))
  213.         self.pos.setSuffix('/' + _('Unknown') + '     ')
  214.         self.pos.setMinimum(1)
  215.         self.pos.setMinimumWidth(150)
  216.         self.tool_bar2.insertWidget(self.action_find_next, self.pos)
  217.         self.reference = HelpfulLineEdit()
  218.         self.reference.setValidator(QRegExpValidator(QRegExp('\\d+\\.\\d+'), self.reference))
  219.         self.reference.setToolTip(_('Go to a reference. To get reference numbers, use the reference mode.'))
  220.         self.tool_bar2.insertSeparator(self.action_find_next)
  221.         self.tool_bar2.insertWidget(self.action_find_next, self.reference)
  222.         self.tool_bar2.insertSeparator(self.action_find_next)
  223.         self.setFocusPolicy(Qt.StrongFocus)
  224.         self.search = SearchBox2(self)
  225.         self.search.setMinimumContentsLength(20)
  226.         self.search.initialize('viewer_search_history')
  227.         self.search.setToolTip(_('Search for text in book'))
  228.         self.search.setMinimumWidth(200)
  229.         self.tool_bar2.insertWidget(self.action_find_next, self.search)
  230.         self.view.set_manager(self)
  231.         self.view.document.debug_javascript = debug_javascript
  232.         self.pi = ProgressIndicator(self)
  233.         self.toc.setVisible(False)
  234.         self.action_quit = QAction(self)
  235.         self.addAction(self.action_quit)
  236.         self.action_quit.setShortcut(Qt.CTRL + Qt.Key_Q)
  237.         self.connect(self.action_quit, SIGNAL('triggered(bool)'), (lambda x: QApplication.instance().quit()))
  238.         self.action_copy.setDisabled(True)
  239.         self.action_metadata.setCheckable(True)
  240.         self.action_metadata.setShortcut(Qt.CTRL + Qt.Key_I)
  241.         self.action_table_of_contents.setCheckable(True)
  242.         self.action_reference_mode.setCheckable(True)
  243.         self.connect(self.action_reference_mode, (SIGNAL('triggered(bool)'),), (lambda x: self.view.reference_mode(x)))
  244.         self.connect(self.action_metadata, (SIGNAL('triggered(bool)'),), (lambda x: self.metadata.setVisible(x)))
  245.         self.connect(self.action_table_of_contents, (SIGNAL('triggered(bool)'),), (lambda x: self.toc.setVisible(x)))
  246.         self.connect(self.action_copy, SIGNAL('triggered(bool)'), self.copy)
  247.         self.connect(self.action_font_size_larger, SIGNAL('triggered(bool)'), self.font_size_larger)
  248.         self.connect(self.action_font_size_smaller, SIGNAL('triggered(bool)'), self.font_size_smaller)
  249.         self.connect(self.action_open_ebook, SIGNAL('triggered(bool)'), self.open_ebook)
  250.         self.connect(self.action_next_page, (SIGNAL('triggered(bool)'),), (lambda x: self.view.next_page()))
  251.         self.connect(self.action_previous_page, (SIGNAL('triggered(bool)'),), (lambda x: self.view.previous_page()))
  252.         self.connect(self.action_find_next, (SIGNAL('triggered(bool)'),), (lambda x: self.find(self.search.smart_text, repeat = True)))
  253.         self.connect(self.action_find_previous, (SIGNAL('triggered(bool)'),), (lambda x: self.find(self.search.smart_text, repeat = True, backwards = True)))
  254.         self.connect(self.action_full_screen, SIGNAL('triggered(bool)'), self.toggle_fullscreen)
  255.         self.action_full_screen.setShortcuts([
  256.             Qt.Key_F11,
  257.             Qt.CTRL + Qt.SHIFT + Qt.Key_F])
  258.         self.connect(self.action_back, SIGNAL('triggered(bool)'), self.back)
  259.         self.connect(self.action_bookmark, SIGNAL('triggered(bool)'), self.bookmark)
  260.         self.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward)
  261.         self.connect(self.action_preferences, (SIGNAL('triggered(bool)'),), (lambda x: self.view.config(self)))
  262.         self.pos.editingFinished.connect(self.goto_page_num)
  263.         self.connect(self.vertical_scrollbar, (SIGNAL('valueChanged(int)'),), (lambda x: self.goto_page(x / 100)))
  264.         self.search.search.connect(self.find)
  265.         self.connect(self.toc, SIGNAL('clicked(QModelIndex)'), self.toc_clicked)
  266.         self.connect(self.reference, SIGNAL('goto(PyQt_PyObject)'), self.goto)
  267.         self.bookmarks_menu = QMenu()
  268.         self.action_bookmark.setMenu(self.bookmarks_menu)
  269.         self.set_bookmarks([])
  270.         if pathtoebook is not None:
  271.             f = functools.partial(self.load_ebook, pathtoebook)
  272.             QTimer.singleShot(50, f)
  273.         
  274.         self.view.setMinimumSize(100, 100)
  275.         self.splitter.setSizes([
  276.             1,
  277.             300])
  278.         self.toc.setCursor(Qt.PointingHandCursor)
  279.         self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
  280.         self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu)
  281.         self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup)
  282.         self.action_full_screen.setCheckable(True)
  283.         self.print_menu = QMenu()
  284.         self.print_menu.addAction(QIcon(I('print-preview.svg')), _('Print Preview'))
  285.         self.action_print.setMenu(self.print_menu)
  286.         self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup)
  287.         self.connect(self.action_print, SIGNAL('triggered(bool)'), partial(self.print_book, preview = False))
  288.         self.connect(self.print_menu.actions()[0], SIGNAL('triggered(bool)'), partial(self.print_book, preview = True))
  289.         self.set_max_width()
  290.         ca = self.view.copy_action
  291.         ca.setShortcut(QKeySequence.Copy)
  292.         self.addAction(ca)
  293.         self.restore_state()
  294.  
  295.     
  296.     def closeEvent(self, e):
  297.         self.save_state()
  298.         return MainWindow.closeEvent(self, e)
  299.  
  300.     
  301.     def save_state(self):
  302.         state = str(self.saveState(self.STATE_VERSION))
  303.         dynamic['viewer_toolbar_state'] = state
  304.  
  305.     
  306.     def restore_state(self):
  307.         state = dynamic.get('viewer_toolbar_state', None)
  308.         if state is not None:
  309.             
  310.             try:
  311.                 state = QByteArray(state)
  312.                 self.restoreState(state, self.STATE_VERSION)
  313.  
  314.         
  315.  
  316.     
  317.     def lookup(self, word):
  318.         self.dictionary_view.setHtml('<html><body><p>' + _('Connecting to dict.org to lookup: <b>%s</b>…') % word + '</p></body></html>')
  319.         self.dictionary_box.show()
  320.         self._lookup = Lookup(word, parent = self)
  321.         self._lookup.finished.connect(self.looked_up)
  322.         self._lookup.start()
  323.  
  324.     
  325.     def looked_up(self, *args):
  326.         html = self._lookup.html_result
  327.         self._lookup = None
  328.         self.dictionary_view.setHtml(html)
  329.  
  330.     
  331.     def set_max_width(self):
  332.         config = config
  333.         import calibre.gui2.viewer.documentview
  334.         c = config().parse()
  335.         self.frame.setMaximumWidth(c.max_view_width)
  336.  
  337.     
  338.     def print_book(self, preview):
  339.         Printing(self.iterator.spine, preview)
  340.  
  341.     
  342.     def toggle_fullscreen(self, x):
  343.         if self.isFullScreen():
  344.             self.showNormal()
  345.         else:
  346.             self.showFullScreen()
  347.  
  348.     
  349.     def goto(self, ref):
  350.         if ref:
  351.             tokens = ref.split('.')
  352.             if len(tokens) > 1:
  353.                 spine_index = int(tokens[0]) - 1
  354.                 if spine_index == self.current_index:
  355.                     self.view.goto(ref)
  356.                 else:
  357.                     self.pending_reference = ref
  358.                     self.load_path(self.iterator.spine[spine_index])
  359.             
  360.         
  361.  
  362.     
  363.     def goto_bookmark(self, bm):
  364.         m = bm[1].split('#')
  365.         if len(m) > 1:
  366.             spine_index = int(m[0])
  367.             m = m[1]
  368.             if spine_index > -1 and self.current_index == spine_index:
  369.                 self.view.goto_bookmark(m)
  370.             else:
  371.                 self.pending_bookmark = bm
  372.                 if spine_index < 0 or spine_index >= len(self.iterator.spine):
  373.                     spine_index = 0
  374.                 
  375.                 self.load_path(self.iterator.spine[spine_index])
  376.         
  377.  
  378.     
  379.     def toc_clicked(self, index):
  380.         item = self.toc_model.itemFromIndex(index)
  381.         url = QUrl.fromLocalFile(item.abspath)
  382.         if item.fragment:
  383.             url.setFragment(item.fragment)
  384.         
  385.         self.link_clicked(url)
  386.  
  387.     
  388.     def selection_changed(self, selected_text):
  389.         self.selected_text = selected_text.strip()
  390.         self.action_copy.setEnabled(bool(self.selected_text))
  391.  
  392.     
  393.     def copy(self, x):
  394.         if self.selected_text:
  395.             QApplication.clipboard().setText(self.selected_text)
  396.         
  397.  
  398.     
  399.     def back(self, x):
  400.         pos = self.history.back(self.pos.value())
  401.         if pos is not None:
  402.             self.goto_page(pos)
  403.         
  404.  
  405.     
  406.     def goto_page_num(self):
  407.         num = self.pos.value()
  408.         self.goto_page(num)
  409.  
  410.     
  411.     def forward(self, x):
  412.         pos = self.history.forward()
  413.         if pos is not None:
  414.             self.goto_page(pos)
  415.         
  416.  
  417.     
  418.     def goto_start(self):
  419.         self.goto_page(1)
  420.  
  421.     
  422.     def goto_end(self):
  423.         self.goto_page(self.pos.maximum())
  424.  
  425.     
  426.     def goto_page(self, new_page):
  427.         if self.current_page is not None:
  428.             for page in self.iterator.spine:
  429.                 if new_page >= page.start_page and new_page <= page.max_page:
  430.                     
  431.                     try:
  432.                         frac = float(new_page - page.start_page) / (page.pages - 1)
  433.                     except ZeroDivisionError:
  434.                         frac = 0
  435.  
  436.                     if page == self.current_page:
  437.                         self.view.scroll_to(frac)
  438.                     else:
  439.                         self.load_path(page, pos = frac)
  440.                 page == self.current_page
  441.             
  442.         
  443.  
  444.     
  445.     def open_ebook(self, checked):
  446.         files = choose_files(self, 'ebook viewer open dialog', _('Choose ebook'), [
  447.             (_('Ebooks'), available_input_formats())], all_files = False, select_only_single_file = True)
  448.         if files:
  449.             self.load_ebook(files[0])
  450.         
  451.  
  452.     
  453.     def font_size_larger(self, checked):
  454.         frac = self.view.magnify_fonts()
  455.         self.action_font_size_larger.setEnabled(self.view.multiplier() < 3)
  456.         self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
  457.         self.set_page_number(frac)
  458.  
  459.     
  460.     def font_size_smaller(self, checked):
  461.         frac = self.view.shrink_fonts()
  462.         self.action_font_size_larger.setEnabled(self.view.multiplier() < 3)
  463.         self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
  464.         self.set_page_number(frac)
  465.  
  466.     
  467.     def bookmark(self, *args):
  468.         (title, ok) = QInputDialog.getText(self, _('Add bookmark'), _('Enter title for bookmark:'))
  469.         title = unicode(title).strip()
  470.         if ok and title:
  471.             pos = self.view.bookmark()
  472.             bookmark = '%d#%s' % (self.current_index, pos)
  473.             self.iterator.add_bookmark((title, bookmark))
  474.             self.set_bookmarks(self.iterator.bookmarks)
  475.         
  476.  
  477.     
  478.     def find(self, text, repeat = False, backwards = False):
  479.         if not text:
  480.             self.view.search('')
  481.             return self.search.search_done(False)
  482.         if self.view.search(text):
  483.             self.scrolled(self.view.scroll_fraction)
  484.             return self.search.search_done(True)
  485.         index = self.iterator.search(text, self.current_index, backwards = backwards)
  486.         if index is None:
  487.             return self.search.search_done(True)
  488.         self.pending_search = text
  489.         self.load_path(self.iterator.spine[index])
  490.  
  491.     
  492.     def do_search(self, text):
  493.         self.pending_search = None
  494.         if self.view.search(text):
  495.             self.scrolled(self.view.scroll_fraction)
  496.         
  497.  
  498.     
  499.     def keyPressEvent(self, event):
  500.         if event.key() == Qt.Key_Slash:
  501.             self.search.setFocus(Qt.OtherFocusReason)
  502.         else:
  503.             return MainWindow.keyPressEvent(self, event)
  504.         return event.key() == Qt.Key_Slash
  505.  
  506.     
  507.     def internal_link_clicked(self, frac):
  508.         self.history.add(self.pos.value())
  509.  
  510.     
  511.     def link_clicked(self, url):
  512.         path = os.path.abspath(unicode(url.toLocalFile()))
  513.         frag = None
  514.         if path in self.iterator.spine:
  515.             self.history.add(self.pos.value())
  516.             path = self.iterator.spine[self.iterator.spine.index(path)]
  517.             if url.hasFragment():
  518.                 frag = unicode(url.fragment())
  519.             
  520.             if path != self.current_page:
  521.                 self.pending_anchor = frag
  522.                 self.load_path(path)
  523.             elif frag:
  524.                 self.view.scroll_to(frag)
  525.             
  526.         else:
  527.             open_url(url)
  528.  
  529.     
  530.     def load_started(self):
  531.         self.open_progress_indicator(_('Loading flow...'))
  532.  
  533.     
  534.     def load_finished(self, ok):
  535.         self.close_progress_indicator()
  536.         path = self.view.path()
  537.         
  538.         try:
  539.             index = self.iterator.spine.index(path)
  540.         except (ValueError, AttributeError):
  541.             return -1
  542.  
  543.         self.current_page = self.iterator.spine[index]
  544.         self.current_index = index
  545.         self.set_page_number(self.view.scroll_fraction)
  546.         if self.pending_search is not None:
  547.             self.do_search(self.pending_search)
  548.             self.pending_search = None
  549.         
  550.         if self.pending_anchor is not None:
  551.             self.view.scroll_to(self.pending_anchor)
  552.             self.pending_anchor = None
  553.         
  554.         if self.pending_reference is not None:
  555.             self.view.goto(self.pending_reference)
  556.             self.pending_reference = None
  557.         
  558.         if self.pending_bookmark is not None:
  559.             self.goto_bookmark(self.pending_bookmark)
  560.             self.pending_bookmark = None
  561.         
  562.         return self.current_index
  563.  
  564.     
  565.     def goto_next_section(self):
  566.         nindex = (self.current_index + 1) % len(self.iterator.spine)
  567.         self.load_path(self.iterator.spine[nindex])
  568.  
  569.     
  570.     def goto_previous_section(self):
  571.         pindex = ((self.current_index - 1) + len(self.iterator.spine)) % len(self.iterator.spine)
  572.         self.load_path(self.iterator.spine[pindex])
  573.  
  574.     
  575.     def load_path(self, path, pos = 0):
  576.         self.open_progress_indicator(_('Laying out %s') % self.current_title)
  577.         self.view.load_path(path, pos = pos)
  578.  
  579.     
  580.     def viewport_resized(self, frac):
  581.         new_page = self.pos.value()
  582.         if self.current_page is not None:
  583.             
  584.             try:
  585.                 frac = float(new_page - self.current_page.start_page) / (self.current_page.pages - 1)
  586.             except ZeroDivisionError:
  587.                 frac = 0
  588.  
  589.             self.view.scroll_to(frac, notify = False)
  590.         else:
  591.             self.set_page_number(frac)
  592.  
  593.     
  594.     def close_progress_indicator(self):
  595.         self.pi.stop()
  596.         for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'):
  597.             getattr(self, o).setEnabled(True)
  598.         
  599.         self.unsetCursor()
  600.         self.view.setFocus(Qt.PopupFocusReason)
  601.  
  602.     
  603.     def open_progress_indicator(self, msg = ''):
  604.         self.pi.start(msg)
  605.         for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'):
  606.             getattr(self, o).setEnabled(False)
  607.         
  608.         self.setCursor(Qt.BusyCursor)
  609.  
  610.     
  611.     def set_bookmarks(self, bookmarks):
  612.         self.bookmarks_menu.clear()
  613.         self.bookmarks_menu.addAction(_('Manage Bookmarks'), self.manage_bookmarks)
  614.         self.bookmarks_menu.addSeparator()
  615.         current_page = None
  616.         for bm in bookmarks:
  617.             if bm[0] == 'calibre_current_page_bookmark':
  618.                 current_page = bm
  619.                 continue
  620.             self.bookmarks_menu.addAction(bm[0], partial(self.goto_bookmark, bm))
  621.         
  622.         return current_page
  623.  
  624.     
  625.     def manage_bookmarks(self):
  626.         bmm = BookmarkManager(self, self.iterator.bookmarks)
  627.         if bmm.exec_() != BookmarkManager.Accepted:
  628.             return None
  629.         bookmarks = bmm.get_bookmarks()
  630.         if bookmarks != self.iterator.bookmarks:
  631.             self.iterator.set_bookmarks(bookmarks)
  632.             self.iterator.save_bookmarks()
  633.             self.set_bookmarks(bookmarks)
  634.         
  635.  
  636.     
  637.     def save_current_position(self):
  638.         
  639.         try:
  640.             pos = self.view.bookmark()
  641.             bookmark = '%d#%s' % (self.current_index, pos)
  642.             self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark))
  643.         except:
  644.             traceback.print_exc()
  645.  
  646.  
  647.     
  648.     def load_ebook(self, pathtoebook):
  649.         if self.iterator is not None:
  650.             self.save_current_position()
  651.             self.iterator.__exit__()
  652.         
  653.         self.iterator = EbookIterator(pathtoebook)
  654.         self.open_progress_indicator(_('Loading ebook...'))
  655.         worker = Worker(target = self.iterator.__enter__)
  656.         worker.start()
  657.         while worker.isAlive():
  658.             worker.join(0.1)
  659.             QApplication.processEvents()
  660.         if worker.exception is not None:
  661.             if isinstance(worker.exception, DRMError):
  662.                 error_dialog(self, _('DRM Error'), _('<p>This book is protected by <a href="%s">DRM</a>') % 'http://wiki.mobileread.com/wiki/DRM').exec_()
  663.             else:
  664.                 r = getattr(worker.exception, 'reason', worker.exception)
  665.                 error_dialog(self, _('Could not open ebook'), unicode(r), det_msg = worker.traceback, show = True)
  666.             self.close_progress_indicator()
  667.         else:
  668.             self.metadata.show_opf(self.iterator.opf, os.path.splitext(pathtoebook)[1][1:])
  669.             self.view.current_language = self.iterator.language
  670.             title = self.iterator.opf.title
  671.             if not title:
  672.                 title = os.path.splitext(os.path.basename(pathtoebook))[0]
  673.             
  674.             self.action_table_of_contents.setDisabled(not (self.iterator.toc))
  675.             if self.iterator.toc:
  676.                 self.toc_model = TOC(self.iterator.toc)
  677.                 self.toc.setModel(self.toc_model)
  678.             
  679.             self.current_title = title
  680.             self.setWindowTitle(unicode(self.windowTitle()) + ' - ' + title)
  681.             self.pos.setMaximum(sum(self.iterator.pages))
  682.             self.pos.setSuffix(' / %d' % sum(self.iterator.pages))
  683.             self.vertical_scrollbar.setMinimum(100)
  684.             self.vertical_scrollbar.setMaximum(100 * sum(self.iterator.pages))
  685.             self.vertical_scrollbar.setSingleStep(10)
  686.             self.vertical_scrollbar.setPageStep(100)
  687.             self.set_vscrollbar_value(1)
  688.             self.current_index = -1
  689.             QApplication.instance().alert(self, 5000)
  690.             previous = self.set_bookmarks(self.iterator.bookmarks)
  691.             if previous is not None:
  692.                 self.goto_bookmark(previous)
  693.             else:
  694.                 self.next_document()
  695.  
  696.     
  697.     def set_vscrollbar_value(self, pagenum):
  698.         self.vertical_scrollbar.blockSignals(True)
  699.         self.vertical_scrollbar.setValue(int(pagenum * 100))
  700.         self.vertical_scrollbar.blockSignals(False)
  701.  
  702.     
  703.     def set_page_number(self, frac):
  704.         if getattr(self, 'current_page', None) is not None:
  705.             page = self.current_page.start_page + frac * float(self.current_page.pages - 1)
  706.             self.pos.set_value(page)
  707.             self.set_vscrollbar_value(page)
  708.         
  709.  
  710.     
  711.     def scrolled(self, frac):
  712.         self.set_page_number(frac)
  713.  
  714.     
  715.     def next_document(self):
  716.         if self.current_index < len(self.iterator.spine) - 1:
  717.             self.load_path(self.iterator.spine[self.current_index + 1])
  718.         
  719.  
  720.     
  721.     def previous_document(self):
  722.         if self.current_index > 0:
  723.             self.load_path(self.iterator.spine[self.current_index - 1], pos = 1)
  724.         
  725.  
  726.     
  727.     def __enter__(self):
  728.         return self
  729.  
  730.     
  731.     def __exit__(self, *args):
  732.         self.write_settings()
  733.         if self.iterator is not None:
  734.             self.save_current_position()
  735.             self.iterator.__exit__(*args)
  736.         
  737.  
  738.     
  739.     def write_settings(self):
  740.         dynamic.set('viewer_window_geometry', self.saveGeometry())
  741.  
  742.     
  743.     def read_settings(self):
  744.         c = config().parse()
  745.         wg = dynamic['viewer_window_geometry']
  746.         if wg is not None and c.remember_window_size:
  747.             self.restoreGeometry(wg)
  748.         
  749.  
  750.  
  751.  
  752. def config(defaults = None):
  753.     desc = _('Options to control the ebook viewer')
  754.     if defaults is None:
  755.         c = Config('viewer', desc)
  756.     else:
  757.         c = StringConfig(defaults, desc)
  758.     c.add_opt('raise_window', [
  759.         '--raise-window'], default = False, help = _('If specified, viewer window will try to come to the front when started.'))
  760.     c.add_opt('remember_window_size', default = False, help = _('Remember last used window size'))
  761.     c.add_opt('debug_javascript', [
  762.         '--debug-javascript'], default = False, help = _('Print javascript alert and console messages to the console'))
  763.     return c
  764.  
  765.  
  766. def option_parser():
  767.     c = config()
  768.     return c.option_parser(usage = _('%prog [options] file\n\nView an ebook.\n'))
  769.  
  770.  
  771. def main(args = sys.argv):
  772.     parser = option_parser()
  773.     (opts, args) = parser.parse_args(args)
  774.     if False:
  775.         pass
  776.     pid = None if islinux or isfreebsd else -1
  777.     return 0
  778.  
  779. if __name__ == '__main__':
  780.     sys.exit(main())
  781.  
  782.