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

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
  6. __docformat__ = 'restructuredtext en'
  7. import os
  8. import math
  9. import re
  10. import glob
  11. import sys
  12. from base64 import b64encode
  13. from functools import partial
  14. from PyQt4.Qt import QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer, QPainter, QPalette, QBrush, QFontDatabase, QDialog, QColor, QPoint, QImage, QRegion, QVariant, QIcon, QFont, pyqtSignature, QAction, QByteArray, QMenu
  15. from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
  16. from calibre.utils.config import Config, StringConfig
  17. from calibre.utils.localization import get_language
  18. from calibre.gui2.viewer.config_ui import Ui_Dialog
  19. from calibre.gui2.shortcuts import Shortcuts, ShortcutConfig
  20. from calibre.constants import iswindows
  21. from calibre import prints, guess_type
  22. from calibre.gui2.viewer.keys import SHORTCUTS
  23. bookmarks = referencing = hyphenation = jquery = jquery_scrollTo = hyphenator = images = None
  24.  
  25. def load_builtin_fonts():
  26.     base = P('fonts/liberation/*.ttf')
  27.     for f in glob.glob(base):
  28.         QFontDatabase.addApplicationFont(f)
  29.     
  30.     return ('Liberation Serif', 'Liberation Sans', 'Liberation Mono')
  31.  
  32.  
  33. def config(defaults = None):
  34.     desc = _('Options to customize the ebook viewer')
  35.     if defaults is None:
  36.         c = Config('viewer', desc)
  37.     else:
  38.         c = StringConfig(defaults, desc)
  39.     c.add_opt('remember_window_size', default = False, help = _('Remember last used window size'))
  40.     c.add_opt('user_css', default = '', help = _('Set the user CSS stylesheet. This can be used to customize the look of all books.'))
  41.     c.add_opt('max_view_width', default = 6000, help = _('Maximum width of the viewer window, in pixels.'))
  42.     c.add_opt('fit_images', default = True, help = _('Resize images larger than the viewer window to fit inside it'))
  43.     c.add_opt('hyphenate', default = False, help = _('Hyphenate text'))
  44.     c.add_opt('hyphenate_default_lang', default = 'en', help = _('Default language for hyphenation rules'))
  45.     fonts = c.add_group('FONTS', _('Font options'))
  46.     None(fonts, serif_family = 'default' if iswindows else 'Liberation Serif', help = _('The serif font family'))
  47.     None(fonts, sans_family = 'default' if iswindows else 'Liberation Sans', help = _('The sans-serif font family'))
  48.     None(fonts, mono_family = 'default' if iswindows else 'Liberation Mono', help = _('The monospaced font family'))
  49.     fonts('default_font_size', default = 20, help = _('The standard font size in px'))
  50.     fonts('mono_font_size', default = 16, help = _('The monospaced font size in px'))
  51.     fonts('standard_font', default = 'serif', help = _('The standard font type'))
  52.     return c
  53.  
  54.  
  55. class ConfigDialog(QDialog, Ui_Dialog):
  56.     
  57.     def __init__(self, shortcuts, parent = None):
  58.         QDialog.__init__(self, parent)
  59.         self.setupUi(self)
  60.         opts = config().parse()
  61.         self.opt_remember_window_size.setChecked(opts.remember_window_size)
  62.         self.serif_family.setCurrentFont(QFont(opts.serif_family))
  63.         self.sans_family.setCurrentFont(QFont(opts.sans_family))
  64.         self.mono_family.setCurrentFont(QFont(opts.mono_family))
  65.         self.default_font_size.setValue(opts.default_font_size)
  66.         self.mono_font_size.setValue(opts.mono_font_size)
  67.         self.standard_font.setCurrentIndex({
  68.             'serif': 0,
  69.             'sans': 1,
  70.             'mono': 2 }[opts.standard_font])
  71.         self.css.setPlainText(opts.user_css)
  72.         self.css.setToolTip(_('Set the user CSS stylesheet. This can be used to customize the look of all books.'))
  73.         self.max_view_width.setValue(opts.max_view_width)
  74.         pats = [ os.path.basename(x).split('.')[0] for x in glob.glob(P('viewer/hyphenate/patterns/*.js', allow_user_override = False)) ]
  75.         names = list(map(get_language, pats))
  76.         pmap = { }
  77.         for i in range(len(pats)):
  78.             pmap[names[i]] = pats[i]
  79.         
  80.         for x in sorted(names):
  81.             self.hyphenate_default_lang.addItem(x, QVariant(pmap[x]))
  82.         
  83.         
  84.         try:
  85.             idx = pats.index(opts.hyphenate_default_lang)
  86.         except ValueError:
  87.             []
  88.             []
  89.             []
  90.             idx = pats.index('en')
  91.         except:
  92.             []
  93.  
  94.         idx = self.hyphenate_default_lang.findText(names[idx])
  95.         self.hyphenate_default_lang.setCurrentIndex(idx)
  96.         self.hyphenate.setChecked(opts.hyphenate)
  97.         self.hyphenate_default_lang.setEnabled(opts.hyphenate)
  98.         self.shortcuts = shortcuts
  99.         self.shortcut_config = ShortcutConfig(shortcuts, parent = self)
  100.         p = self.tabs.widget(1)
  101.         p.layout().addWidget(self.shortcut_config)
  102.         self.opt_fit_images.setChecked(opts.fit_images)
  103.  
  104.     
  105.     def accept(self, *args):
  106.         c = config()
  107.         c.set('serif_family', unicode(self.serif_family.currentFont().family()))
  108.         c.set('sans_family', unicode(self.sans_family.currentFont().family()))
  109.         c.set('mono_family', unicode(self.mono_family.currentFont().family()))
  110.         c.set('default_font_size', self.default_font_size.value())
  111.         c.set('mono_font_size', self.mono_font_size.value())
  112.         c.set('standard_font', {
  113.             0: 'serif',
  114.             1: 'sans',
  115.             2: 'mono' }[self.standard_font.currentIndex()])
  116.         c.set('user_css', unicode(self.css.toPlainText()))
  117.         c.set('remember_window_size', self.opt_remember_window_size.isChecked())
  118.         c.set('fit_images', self.opt_fit_images.isChecked())
  119.         c.set('max_view_width', int(self.max_view_width.value()))
  120.         c.set('hyphenate', self.hyphenate.isChecked())
  121.         idx = self.hyphenate_default_lang.currentIndex()
  122.         c.set('hyphenate_default_lang', str(self.hyphenate_default_lang.itemData(idx).toString()))
  123.         return QDialog.accept(self, *args)
  124.  
  125.  
  126.  
  127. class Document(QWebPage):
  128.     
  129.     def set_font_settings(self):
  130.         opts = config().parse()
  131.         settings = self.settings()
  132.         settings.setFontSize(QWebSettings.DefaultFontSize, opts.default_font_size)
  133.         settings.setFontSize(QWebSettings.DefaultFixedFontSize, opts.mono_font_size)
  134.         settings.setFontSize(QWebSettings.MinimumLogicalFontSize, 8)
  135.         settings.setFontSize(QWebSettings.MinimumFontSize, 8)
  136.         settings.setFontFamily(QWebSettings.StandardFont, {
  137.             'serif': opts.serif_family,
  138.             'sans': opts.sans_family,
  139.             'mono': opts.mono_family }[opts.standard_font])
  140.         settings.setFontFamily(QWebSettings.SerifFont, opts.serif_family)
  141.         settings.setFontFamily(QWebSettings.SansSerifFont, opts.sans_family)
  142.         settings.setFontFamily(QWebSettings.FixedFont, opts.mono_family)
  143.  
  144.     
  145.     def do_config(self, parent = None):
  146.         d = ConfigDialog(self.shortcuts, parent)
  147.         if d.exec_() == QDialog.Accepted:
  148.             self.set_font_settings()
  149.             self.set_user_stylesheet()
  150.             self.misc_config()
  151.             self.triggerAction(QWebPage.Reload)
  152.         
  153.  
  154.     
  155.     def __init__(self, shortcuts, parent = None):
  156.         QWebPage.__init__(self, parent)
  157.         self.setObjectName('py_bridge')
  158.         self.debug_javascript = False
  159.         self.current_language = None
  160.         self.loaded_javascript = False
  161.         self.setLinkDelegationPolicy(self.DelegateAllLinks)
  162.         self.scroll_marks = []
  163.         self.shortcuts = shortcuts
  164.         pal = self.palette()
  165.         pal.setBrush(QPalette.Background, QColor(238, 238, 238))
  166.         self.setPalette(pal)
  167.         settings = self.settings()
  168.         load_builtin_fonts()
  169.         self.set_font_settings()
  170.         settings.setAttribute(QWebSettings.JavaEnabled, False)
  171.         settings.setAttribute(QWebSettings.PluginsEnabled, False)
  172.         settings.setAttribute(QWebSettings.JavascriptCanOpenWindows, False)
  173.         settings.setAttribute(QWebSettings.JavascriptCanAccessClipboard, False)
  174.         settings.setAttribute(QWebSettings.LinksIncludedInFocusChain, True)
  175.         self.set_user_stylesheet()
  176.         self.misc_config()
  177.         self.mainFrame().javaScriptWindowObjectCleared.connect(self.add_window_objects)
  178.  
  179.     
  180.     def set_user_stylesheet(self):
  181.         raw = config().parse().user_css
  182.         raw = '::selection {background:#ffff00; color:#000;}\nbody {background-color: white;}\n' + raw
  183.         data = 'data:text/css;charset=utf-8;base64,'
  184.         data += b64encode(raw.encode('utf-8'))
  185.         self.settings().setUserStyleSheetUrl(QUrl(data))
  186.  
  187.     
  188.     def misc_config(self):
  189.         opts = config().parse()
  190.         self.hyphenate = opts.hyphenate
  191.         self.hyphenate_default_lang = opts.hyphenate_default_lang
  192.         self.do_fit_images = opts.fit_images
  193.  
  194.     
  195.     def fit_images(self):
  196.         if self.do_fit_images:
  197.             self.javascript('setup_image_scaling_handlers()')
  198.         
  199.  
  200.     
  201.     def add_window_objects(self):
  202.         self.mainFrame().addToJavaScriptWindowObject('py_bridge', self)
  203.         self.loaded_javascript = False
  204.  
  205.     
  206.     def load_javascript_libraries(self):
  207.         global jquery, jquery_scrollTo, bookmarks, referencing, images, hyphenation, hyphenator
  208.         if self.loaded_javascript:
  209.             return None
  210.         self.loaded_javascript = True
  211.         if jquery is None:
  212.             jquery = P('content_server/jquery.js', data = True)
  213.         
  214.         self.javascript(jquery)
  215.         if jquery_scrollTo is None:
  216.             jquery_scrollTo = P('viewer/jquery_scrollTo.js', data = True)
  217.         
  218.         self.javascript(jquery_scrollTo)
  219.         if bookmarks is None:
  220.             bookmarks = P('viewer/bookmarks.js', data = True)
  221.         
  222.         self.javascript(bookmarks)
  223.         if referencing is None:
  224.             referencing = P('viewer/referencing.js', data = True)
  225.         
  226.         self.javascript(referencing)
  227.         if images is None:
  228.             images = P('viewer/images.js', data = True)
  229.         
  230.         self.javascript(images)
  231.         if hyphenation is None:
  232.             hyphenation = P('viewer/hyphenation.js', data = True)
  233.         
  234.         self.javascript(hyphenation)
  235.         default_lang = self.hyphenate_default_lang
  236.         lang = self.current_language
  237.         if not lang:
  238.             lang = default_lang
  239.         
  240.         lang = lang.lower()[:2]
  241.         if hyphenator is None:
  242.             hyphenator = P('viewer/hyphenate/Hyphenator.js', data = True).decode('utf-8')
  243.         
  244.         self.javascript(hyphenator)
  245.         p = P('viewer/hyphenate/patterns/%s.js' % lang)
  246.         if not os.path.exists(p):
  247.             lang = default_lang
  248.             p = P('viewer/hyphenate/patterns/%s.js' % lang)
  249.         
  250.         self.javascript(open(p, 'rb').read().decode('utf-8'))
  251.         self.loaded_lang = lang
  252.  
  253.     
  254.     def animated_scroll_done(self):
  255.         self.emit(SIGNAL('animated_scroll_done()'))
  256.  
  257.     animated_scroll_done = pyqtSignature('')(animated_scroll_done)
  258.     
  259.     def init_hyphenate(self):
  260.         if self.hyphenate:
  261.             self.javascript('do_hyphenation("%s")' % self.loaded_lang)
  262.         
  263.  
  264.     init_hyphenate = pyqtSignature('')(init_hyphenate)
  265.     
  266.     def debug(self, msg):
  267.         prints(msg)
  268.  
  269.     debug = pyqtSignature('QString')(debug)
  270.     
  271.     def reference_mode(self, enable):
  272.         None(self.javascript if enable else 'leave' + '_reference_mode()')
  273.  
  274.     
  275.     def set_reference_prefix(self, prefix):
  276.         self.javascript('reference_prefix = "%s"' % prefix)
  277.  
  278.     
  279.     def goto(self, ref):
  280.         self.javascript('goto_reference("%s")' % ref)
  281.  
  282.     
  283.     def goto_bookmark(self, bm):
  284.         bm = bm.strip()
  285.         if bm.startswith('>'):
  286.             bm = bm[1:].strip()
  287.         
  288.         self.javascript('scroll_to_bookmark("%s")' % bm)
  289.  
  290.     
  291.     def javascript(self, string, typ = None):
  292.         ans = self.mainFrame().evaluateJavaScript(string)
  293.         if typ == 'int':
  294.             ans = ans.toInt()
  295.             if ans[1]:
  296.                 return ans[0]
  297.             return 0
  298.         if typ == 'string':
  299.             return unicode(ans.toString())
  300.         return ans
  301.  
  302.     
  303.     def javaScriptConsoleMessage(self, msg, lineno, msgid):
  304.         if self.debug_javascript:
  305.             prints('JS:', msgid, lineno)
  306.             prints(msg)
  307.             prints(' ')
  308.         else:
  309.             return QWebPage.javaScriptConsoleMessage(self, msg, lineno, msgid)
  310.         return self.debug_javascript
  311.  
  312.     
  313.     def javaScriptAlert(self, frame, msg):
  314.         if self.debug_javascript:
  315.             prints(msg)
  316.         else:
  317.             return QWebPage.javaScriptAlert(self, frame, msg)
  318.         return self.debug_javascript
  319.  
  320.     
  321.     def scroll_by(self, dx = 0, dy = 0):
  322.         self.mainFrame().scroll(dx, dy)
  323.  
  324.     
  325.     def scroll_to(self, x = 0, y = 0):
  326.         self.mainFrame().setScrollPosition(QPoint(x, y))
  327.  
  328.     
  329.     def jump_to_anchor(self, anchor):
  330.         self.javascript('document.location.hash = "%s"' % anchor)
  331.  
  332.     
  333.     def quantize(self):
  334.         if self.height > self.window_height:
  335.             r = self.height % self.window_height
  336.             if r > 0:
  337.                 self.javascript('document.body.style.paddingBottom = "%dpx"' % r)
  338.             
  339.         
  340.  
  341.     
  342.     def element_ypos(self, elem):
  343.         (ans, ok) = elem.evaluateJavaScript('$(this).offset().top').toInt()
  344.         if not ok:
  345.             raise ValueError('No ypos found')
  346.         ok
  347.         return ans
  348.  
  349.     
  350.     def elem_outer_xml(self, elem):
  351.         return unicode(elem.toOuterXml())
  352.  
  353.     
  354.     def find_bookmark_element(self):
  355.         mf = self.mainFrame()
  356.         doc_pos = self.ypos
  357.         min_delta = sys.maxint
  358.         min_elem = None
  359.         for y in range(10, -500, -10):
  360.             for x in range(-50, 500, 10):
  361.                 pos = QPoint(x, y)
  362.                 result = mf.hitTestContent(pos)
  363.                 if result.isNull():
  364.                     continue
  365.                 
  366.                 elem = result.enclosingBlockElement()
  367.                 if elem.isNull():
  368.                     continue
  369.                 
  370.                 
  371.                 try:
  372.                     ypos = self.element_ypos(elem)
  373.                 except:
  374.                     continue
  375.  
  376.                 delta = abs(ypos - doc_pos)
  377.                 if delta < 25:
  378.                     return elem
  379.                 if delta < min_delta:
  380.                     min_elem = elem
  381.                     min_delta = delta
  382.                     continue
  383.                 delta < 25
  384.             
  385.         
  386.         return min_elem
  387.  
  388.     
  389.     def bookmark(self):
  390.         elem = self.find_bookmark_element()
  391.         if elem is None or self.element_ypos(elem) < 100:
  392.             bm = 'body|%f' % float(self.ypos) / (self.height * 0.7)
  393.         else:
  394.             bm = unicode(elem.evaluateJavaScript('calculate_bookmark(%d, this)' % self.ypos).toString())
  395.             if not bm:
  396.                 bm = 'body|%f' % float(self.ypos) / (self.height * 0.7)
  397.             
  398.         return bm
  399.  
  400.     
  401.     def at_bottom(self):
  402.         return self.height - self.ypos <= self.window_height
  403.  
  404.     at_bottom = property(at_bottom)
  405.     
  406.     def at_top(self):
  407.         return self.ypos <= 0
  408.  
  409.     at_top = property(at_top)
  410.     
  411.     def test(self):
  412.         pass
  413.  
  414.     
  415.     def ypos(self):
  416.         return self.mainFrame().scrollPosition().y()
  417.  
  418.     ypos = property(ypos)
  419.     
  420.     def window_height(self):
  421.         return self.javascript('window.innerHeight', 'int')
  422.  
  423.     window_height = property(window_height)
  424.     
  425.     def window_width(self):
  426.         return self.javascript('window.innerWidth', 'int')
  427.  
  428.     window_width = property(window_width)
  429.     
  430.     def xpos(self):
  431.         return self.mainFrame().scrollPosition().x()
  432.  
  433.     xpos = property(xpos)
  434.     
  435.     def scroll_fraction(self):
  436.         
  437.         try:
  438.             return float(self.ypos) / (self.height - self.window_height)
  439.         except ZeroDivisionError:
  440.             return 0
  441.  
  442.  
  443.     scroll_fraction = property(scroll_fraction)
  444.     
  445.     def hscroll_fraction(self):
  446.         
  447.         try:
  448.             return float(self.xpos) / self.width
  449.         except ZeroDivisionError:
  450.             return 0
  451.  
  452.  
  453.     hscroll_fraction = property(hscroll_fraction)
  454.     
  455.     def height(self):
  456.         j = self.javascript('document.body.offsetHeight', 'int')
  457.         q = self.mainFrame().contentsSize().height()
  458.         if q == j:
  459.             return j
  460.         if min(j, q) <= 0:
  461.             return max(j, q)
  462.         window_height = self.window_height
  463.         if j == window_height:
  464.             if q < 1.2 * j:
  465.                 return j
  466.             return q
  467.         return j
  468.  
  469.     height = property(height)
  470.     
  471.     def width(self):
  472.         return self.mainFrame().contentsSize().width()
  473.  
  474.     width = property(width)
  475.     
  476.     def set_bottom_padding(self, amount):
  477.         s = None if amount == 0 else QSize(self.width, self.height + amount)
  478.         self.setPreferredContentsSize(s)
  479.  
  480.  
  481.  
  482. class EntityDeclarationProcessor(object):
  483.     
  484.     def __init__(self, html):
  485.         self.declared_entities = { }
  486.         for match in re.finditer('<!\\s*ENTITY\\s+([^>]+)>', html):
  487.             tokens = match.group(1).split()
  488.             if len(tokens) > 1:
  489.                 self.declared_entities[tokens[0].strip()] = tokens[1].strip().replace('"', '')
  490.                 continue
  491.         
  492.         self.processed_html = html
  493.         for key, val in self.declared_entities.iteritems():
  494.             self.processed_html = self.processed_html.replace('&%s;' % key, val)
  495.         
  496.  
  497.  
  498.  
  499. class DocumentView(QWebView):
  500.     DISABLED_BRUSH = QBrush(Qt.lightGray, Qt.Dense5Pattern)
  501.     
  502.     def __init__(self, *args):
  503.         QWebView.__init__(self, *args)
  504.         self.debug_javascript = False
  505.         self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer')
  506.         self.self_closing_pat = re.compile('<([a-z1-6]+)\\s+([^>]+)/>', re.IGNORECASE)
  507.         self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
  508.         self._size_hint = QSize(510, 680)
  509.         self.initial_pos = 0
  510.         self.to_bottom = False
  511.         self.document = Document(self.shortcuts, parent = self)
  512.         self.setPage(self.document)
  513.         self.manager = None
  514.         self._reference_mode = False
  515.         self._ignore_scrollbar_signals = False
  516.         self.loading_url = None
  517.         self.loadFinished.connect(self.load_finished)
  518.         self.connect(self.document, SIGNAL('linkClicked(QUrl)'), self.link_clicked)
  519.         self.connect(self.document, SIGNAL('linkHovered(QString,QString,QString)'), self.link_hovered)
  520.         self.connect(self.document, SIGNAL('selectionChanged()'), self.selection_changed)
  521.         self.connect(self.document, SIGNAL('animated_scroll_done()'), self.animated_scroll_done, Qt.QueuedConnection)
  522.         copy_action = self.pageAction(self.document.Copy)
  523.         copy_action.setIcon(QIcon(I('convert.png')))
  524.         d = self.document
  525.         self.unimplemented_actions = list(map(self.pageAction, [
  526.             d.DownloadImageToDisk,
  527.             d.OpenLinkInNewWindow,
  528.             d.DownloadLinkToDisk,
  529.             d.OpenImageInNewWindow,
  530.             d.OpenLink]))
  531.         self.dictionary_action = QAction(QIcon(I('dictionary.png')), _('&Lookup in dictionary'), self)
  532.         self.dictionary_action.setShortcut(Qt.CTRL + Qt.Key_L)
  533.         self.dictionary_action.triggered.connect(self.lookup)
  534.         self.goto_location_action = QAction(_('Go to...'), self)
  535.         self.goto_location_menu = m = QMenu(self)
  536.         self.goto_location_actions = a = {
  537.             'Next Page': self.next_page,
  538.             'Previous Page': self.previous_page,
  539.             'Section Top': partial(self.scroll_to, 0),
  540.             'Document Top': self.goto_document_start,
  541.             'Section Bottom': partial(self.scroll_to, 1),
  542.             'Document Bottom': self.goto_document_end,
  543.             'Next Section': self.goto_next_section,
  544.             'Previous Section': self.goto_previous_section }
  545.         for name, key in [
  546.             (_('Next Section'), 'Next Section'),
  547.             (_('Previous Section'), 'Previous Section'),
  548.             (None, None),
  549.             (_('Document Start'), 'Document Top'),
  550.             (_('Document End'), 'Document Bottom'),
  551.             (None, None),
  552.             (_('Section Start'), 'Section Top'),
  553.             (_('Section End'), 'Section Bottom'),
  554.             (None, None),
  555.             (_('Next Page'), 'Next Page'),
  556.             (_('Previous Page'), 'Previous Page')]:
  557.             if key is None:
  558.                 m.addSeparator()
  559.                 continue
  560.             m.addAction(name, a[key], self.shortcuts.get_sequences(key)[0])
  561.         
  562.         self.goto_location_action.setMenu(self.goto_location_menu)
  563.  
  564.     
  565.     def goto_next_section(self, *args):
  566.         if self.manager is not None:
  567.             self.manager.goto_next_section()
  568.         
  569.  
  570.     
  571.     def goto_previous_section(self, *args):
  572.         if self.manager is not None:
  573.             self.manager.goto_previous_section()
  574.         
  575.  
  576.     
  577.     def goto_document_start(self, *args):
  578.         if self.manager is not None:
  579.             self.manager.goto_start()
  580.         
  581.  
  582.     
  583.     def goto_document_end(self, *args):
  584.         if self.manager is not None:
  585.             self.manager.goto_end()
  586.         
  587.  
  588.     
  589.     def copy_action(self):
  590.         return self.pageAction(self.document.Copy)
  591.  
  592.     copy_action = property(copy_action)
  593.     
  594.     def animated_scroll_done(self):
  595.         if self.manager is not None:
  596.             self.manager.scrolled(self.document.scroll_fraction)
  597.         
  598.  
  599.     
  600.     def reference_mode(self, enable):
  601.         self._reference_mode = enable
  602.         self.document.reference_mode(enable)
  603.  
  604.     
  605.     def goto(self, ref):
  606.         self.document.goto(ref)
  607.  
  608.     
  609.     def goto_bookmark(self, bm):
  610.         self.document.goto_bookmark(bm)
  611.  
  612.     
  613.     def config(self, parent = None):
  614.         self.document.do_config(parent)
  615.         if self.manager is not None:
  616.             self.manager.set_max_width()
  617.         
  618.         self.setFocus(Qt.OtherFocusReason)
  619.  
  620.     
  621.     def bookmark(self):
  622.         return self.document.bookmark()
  623.  
  624.     
  625.     def selection_changed(self):
  626.         if self.manager is not None:
  627.             self.manager.selection_changed(unicode(self.document.selectedText()))
  628.         
  629.  
  630.     
  631.     def contextMenuEvent(self, ev):
  632.         menu = self.document.createStandardContextMenu()
  633.         for action in self.unimplemented_actions:
  634.             menu.removeAction(action)
  635.         
  636.         text = unicode(self.selectedText())
  637.         if text:
  638.             menu.insertAction(list(menu.actions())[0], self.dictionary_action)
  639.         
  640.         menu.addSeparator()
  641.         menu.addAction(self.goto_location_action)
  642.         menu.exec_(ev.globalPos())
  643.  
  644.     
  645.     def lookup(self, *args):
  646.         if self.manager is not None:
  647.             t = unicode(self.selectedText()).strip()
  648.             if t:
  649.                 self.manager.lookup(t.split()[0])
  650.             
  651.         
  652.  
  653.     
  654.     def set_manager(self, manager):
  655.         self.manager = manager
  656.         self.scrollbar = manager.horizontal_scrollbar
  657.         self.connect(self.scrollbar, SIGNAL('valueChanged(int)'), self.scroll_horizontally)
  658.  
  659.     
  660.     def scroll_horizontally(self, amount):
  661.         self.document.scroll_to(y = self.document.ypos, x = amount)
  662.  
  663.     
  664.     def link_hovered(self, link, text, context):
  665.         link = unicode(link)
  666.         text = unicode(text)
  667.         if link:
  668.             self.setCursor(Qt.PointingHandCursor)
  669.         else:
  670.             self.unsetCursor()
  671.  
  672.     
  673.     def link_clicked(self, url):
  674.         if self.manager is not None:
  675.             self.manager.link_clicked(url)
  676.         
  677.  
  678.     
  679.     def sizeHint(self):
  680.         return self._size_hint
  681.  
  682.     
  683.     def scroll_fraction(self):
  684.         return self.document.scroll_fraction
  685.  
  686.     scroll_fraction = property(scroll_fraction)
  687.     
  688.     def hscroll_fraction(self):
  689.         return self.document.hscroll_fraction
  690.  
  691.     hscroll_fraction = property(hscroll_fraction)
  692.     
  693.     def content_size(self):
  694.         return (self.document.width, self.document.height)
  695.  
  696.     content_size = property(content_size)
  697.     
  698.     def current_language(self):
  699.         
  700.         def fget(self):
  701.             return self.document.current_language
  702.  
  703.         
  704.         def fset(self, val):
  705.             self.document.current_language = val
  706.  
  707.         return property(fget = fget, fset = fset)
  708.  
  709.     current_language = dynamic_property(current_language)
  710.     
  711.     def search(self, text, backwards = False):
  712.         if backwards:
  713.             return self.findText(text, self.document.FindBackwards)
  714.         return self.findText(text)
  715.  
  716.     
  717.     def path(self):
  718.         return os.path.abspath(unicode(self.url().toLocalFile()))
  719.  
  720.     
  721.     def self_closing_sub(self, match):
  722.         tag = match.group(1)
  723.         if tag.lower().strip() == 'br':
  724.             return match.group()
  725.         return '<%s %s></%s>' % (match.group(1), match.group(2), match.group(1))
  726.  
  727.     
  728.     def load_path(self, path, pos = 0):
  729.         self.initial_pos = pos
  730.         mt = getattr(path, 'mime_type', None)
  731.         if mt is None:
  732.             mt = guess_type(path)[0]
  733.         
  734.         html = open(path, 'rb').read().decode(path.encoding, 'replace')
  735.         html = EntityDeclarationProcessor(html).processed_html
  736.         has_svg = re.search('<[:a-zA-Z]*svg', html) is not None
  737.         if 'xhtml' in mt:
  738.             html = self.self_closing_pat.sub(self.self_closing_sub, html)
  739.         
  740.         if self.manager is not None:
  741.             self.manager.load_started()
  742.         
  743.         self.loading_url = QUrl.fromLocalFile(path)
  744.         if has_svg:
  745.             prints('Rendering as XHTML...')
  746.             self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path))
  747.         else:
  748.             self.setHtml(html, self.loading_url)
  749.         self.turn_off_internal_scrollbars()
  750.  
  751.     
  752.     def initialize_scrollbar(self):
  753.         if getattr(self, 'scrollbar', None) is not None:
  754.             delta = self.document.width - self.size().width()
  755.             if delta > 0:
  756.                 self._ignore_scrollbar_signals = True
  757.                 self.scrollbar.blockSignals(True)
  758.                 self.scrollbar.setRange(0, delta)
  759.                 self.scrollbar.setValue(0)
  760.                 self.scrollbar.setSingleStep(1)
  761.                 self.scrollbar.setPageStep(int(delta / 10))
  762.             
  763.             self.scrollbar.setVisible(delta > 0)
  764.             self.scrollbar.blockSignals(False)
  765.             self._ignore_scrollbar_signals = False
  766.         
  767.  
  768.     
  769.     def load_finished(self, ok):
  770.         if self.loading_url is None:
  771.             return None
  772.         self.loading_url = None
  773.         self.document.load_javascript_libraries()
  774.         self.document.set_bottom_padding(0)
  775.         self.document.fit_images()
  776.         self._size_hint = self.document.mainFrame().contentsSize()
  777.         scrolled = False
  778.         if self.to_bottom:
  779.             self.to_bottom = False
  780.             self.initial_pos = 1
  781.         
  782.         if self.initial_pos > 0:
  783.             scrolled = True
  784.         
  785.         self.scroll_to(self.initial_pos, notify = False)
  786.         self.initial_pos = 0
  787.         self.update()
  788.         self.initialize_scrollbar()
  789.         self.document.reference_mode(self._reference_mode)
  790.         if self.manager is not None:
  791.             spine_index = self.manager.load_finished(bool(ok))
  792.             if spine_index > -1:
  793.                 self.document.set_reference_prefix('%d.' % (spine_index + 1))
  794.             
  795.             if scrolled:
  796.                 self.manager.scrolled(self.document.scroll_fraction)
  797.             
  798.         
  799.         self.turn_off_internal_scrollbars()
  800.  
  801.     
  802.     def turn_off_internal_scrollbars(self):
  803.         self.document.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
  804.         self.document.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
  805.  
  806.     
  807.     def test_line(cls, img, y):
  808.         start = img.pixel(0, y)
  809.         for i in range(1, img.width()):
  810.             if img.pixel(i, y) != start:
  811.                 return False
  812.         
  813.         return True
  814.  
  815.     test_line = classmethod(test_line)
  816.     
  817.     def find_next_blank_line(self, overlap):
  818.         img = QImage(self.width(), overlap, QImage.Format_ARGB32)
  819.         painter = QPainter(img)
  820.         self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
  821.         painter.end()
  822.         for i in range(overlap - 1, -1, -1):
  823.             if self.test_line(img, i):
  824.                 self.scroll_by(y = i, notify = False)
  825.                 return None
  826.         
  827.         self.scroll_by(y = overlap)
  828.  
  829.     
  830.     def previous_page(self):
  831.         delta_y = self.document.window_height - 25
  832.         if self.document.at_top:
  833.             if self.manager is not None:
  834.                 self.to_bottom = True
  835.                 self.manager.previous_document()
  836.             
  837.         else:
  838.             opos = self.document.ypos
  839.             upper_limit = opos - delta_y
  840.             if upper_limit < 0:
  841.                 upper_limit = 0
  842.             
  843.             if upper_limit < opos:
  844.                 self.document.scroll_to(self.document.xpos, upper_limit)
  845.             
  846.             if self.manager is not None:
  847.                 self.manager.scrolled(self.scroll_fraction)
  848.             
  849.  
  850.     
  851.     def next_page(self):
  852.         window_height = self.document.window_height
  853.         document_height = self.document.height
  854.         ddelta = document_height - window_height
  855.         delta_y = window_height - 25
  856.         if self.document.at_bottom or ddelta <= 0:
  857.             if self.manager is not None:
  858.                 self.manager.next_document()
  859.             
  860.         elif ddelta < 25:
  861.             self.scroll_by(y = ddelta)
  862.             return None
  863.         oopos = self.document.ypos
  864.         self.document.set_bottom_padding(0)
  865.         opos = self.document.ypos
  866.         if opos < oopos:
  867.             if self.manager is not None:
  868.                 self.manager.next_document()
  869.             
  870.             return None
  871.         lower_limit = opos + delta_y
  872.         max_y = self.document.height - window_height
  873.         max_y = self.document.height - window_height
  874.         lower_limit = min(max_y, lower_limit)
  875.         if lower_limit > opos:
  876.             self.document.scroll_to(self.document.xpos, lower_limit)
  877.         
  878.         actually_scrolled = self.document.ypos - opos
  879.         self.find_next_blank_line(window_height - actually_scrolled)
  880.         if self.manager is not None:
  881.             self.manager.scrolled(self.scroll_fraction)
  882.         
  883.  
  884.     
  885.     def scroll_by(self, x = 0, y = 0, notify = True):
  886.         old_pos = self.document.ypos
  887.         self.document.scroll_by(x, y)
  888.         if notify and self.manager is not None and self.document.ypos != old_pos:
  889.             self.manager.scrolled(self.scroll_fraction)
  890.         
  891.  
  892.     
  893.     def scroll_to(self, pos, notify = True):
  894.         if self._ignore_scrollbar_signals:
  895.             return None
  896.         old_pos = self.document.ypos
  897.         if isinstance(pos, basestring):
  898.             self.document.jump_to_anchor(pos)
  899.         elif pos >= 1:
  900.             self.document.scroll_to(0, self.document.height)
  901.         else:
  902.             y = int(math.ceil(pos * (self.document.height - self.document.window_height)))
  903.             self.document.scroll_to(0, y)
  904.         if notify and self.manager is not None and self.document.ypos != old_pos:
  905.             self.manager.scrolled(self.scroll_fraction)
  906.         
  907.  
  908.     
  909.     def multiplier(self):
  910.         return self.document.mainFrame().textSizeMultiplier()
  911.  
  912.     
  913.     def magnify_fonts(self):
  914.         self.document.mainFrame().setTextSizeMultiplier(self.multiplier() + 0.2)
  915.         return self.document.scroll_fraction
  916.  
  917.     
  918.     def shrink_fonts(self):
  919.         self.document.mainFrame().setTextSizeMultiplier(max(self.multiplier() - 0.2, 0))
  920.         return self.document.scroll_fraction
  921.  
  922.     
  923.     def changeEvent(self, event):
  924.         if event.type() == event.EnabledChange:
  925.             self.update()
  926.         
  927.         return QWebView.changeEvent(self, event)
  928.  
  929.     
  930.     def paintEvent(self, event):
  931.         self.turn_off_internal_scrollbars()
  932.         painter = QPainter(self)
  933.         self.document.mainFrame().render(painter, event.region())
  934.         if not self.isEnabled():
  935.             painter.fillRect(event.region().boundingRect(), self.DISABLED_BRUSH)
  936.         
  937.         painter.end()
  938.  
  939.     
  940.     def wheelEvent(self, event):
  941.         if event.delta() < -14:
  942.             if self.document.at_bottom:
  943.                 self.scroll_by(y = 15)
  944.                 if self.manager is not None:
  945.                     self.manager.next_document()
  946.                     event.accept()
  947.                     return None
  948.             
  949.         elif event.delta() > 14:
  950.             if self.document.at_top:
  951.                 if self.manager is not None:
  952.                     self.manager.previous_document()
  953.                     event.accept()
  954.                     return None
  955.             
  956.         
  957.         ret = QWebView.wheelEvent(self, event)
  958.         scroll_amount = (event.delta() / 120) * 0.2 * -1
  959.         if event.orientation() == Qt.Vertical:
  960.             self.scroll_by(0, self.document.viewportSize().height() * scroll_amount)
  961.         else:
  962.             self.scroll_by(self.document.viewportSize().width() * scroll_amount, 0)
  963.         if self.manager is not None:
  964.             self.manager.scrolled(self.scroll_fraction)
  965.         
  966.         return ret
  967.  
  968.     
  969.     def keyPressEvent(self, event):
  970.         key = self.shortcuts.get_match(event)
  971.         func = self.goto_location_actions.get(key, None)
  972.         if func is not None:
  973.             func()
  974.         elif key == 'Down':
  975.             self.scroll_by(y = 15)
  976.         elif key == 'Up':
  977.             self.scroll_by(y = -15)
  978.         elif key == 'Left':
  979.             self.scroll_by(x = -15)
  980.         elif key == 'Right':
  981.             self.scroll_by(x = 15)
  982.         else:
  983.             return QWebView.keyPressEvent(self, event)
  984.         return func is not None
  985.  
  986.     
  987.     def resizeEvent(self, event):
  988.         ret = QWebView.resizeEvent(self, event)
  989.         QTimer.singleShot(10, self.initialize_scrollbar)
  990.         if self.manager is not None:
  991.             self.manager.viewport_resized(self.scroll_fraction)
  992.         
  993.         return ret
  994.  
  995.     
  996.     def mouseReleaseEvent(self, ev):
  997.         opos = self.document.ypos
  998.         ret = QWebView.mouseReleaseEvent(self, ev)
  999.         if self.manager is not None and opos != self.document.ypos:
  1000.             self.manager.internal_link_clicked(opos)
  1001.             self.manager.scrolled(self.scroll_fraction)
  1002.         
  1003.         return ret
  1004.  
  1005.  
  1006.