Implement reference mode and bookmarks in ebook-viewer

This commit is contained in:
Kovid Goyal 2008-10-21 17:58:40 -07:00
parent 0c538a70eb
commit 8db5608d74
11 changed files with 1156 additions and 28 deletions

View File

@ -145,6 +145,7 @@ if __name__ == '__main__':
fb2_xsl = 'ebooks/lrf/fb2/fb2.xsl',
metadata_sqlite = 'library/metadata_sqlite.sql',
jquery = 'gui2/viewer/jquery.js',
jquery_scrollTo = 'gui2/viewer/jquery_scrollTo.js',
)
DEST = os.path.join('src', APPNAME, 'resources.py')
@ -291,7 +292,7 @@ if __name__ == '__main__':
if form.endswith('viewer%smain.ui'%os.sep):
print 'Promoting WebView'
dat = dat.replace('self.view = QtWebKit.QWebView(self.widget)', 'self.view = DocumentView(self.widget)')
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
open(compiled_form, 'wb').write(dat)

View File

@ -6,6 +6,7 @@ Iterate over the HTML files in an ebook. Useful for writing viewers.
'''
import re, os, math, copy
from cStringIO import StringIO
from PyQt4.Qt import QFontDatabase
@ -16,6 +17,8 @@ from calibre.ebooks.metadata.opf2 import OPF
from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.chardet import xml_to_unicode
from calibre.ebooks.html import create_dir
from calibre.utils.zipfile import safe_replace, ZipFile
from calibre.utils.config import DynamicConfig
def character_count(html):
'''
@ -62,7 +65,8 @@ class EbookIterator(object):
CHARACTERS_PER_PAGE = 1000
def __init__(self, pathtoebook):
self.pathtoebook = pathtoebook
self.pathtoebook = os.path.abspath(pathtoebook)
self.config = DynamicConfig(name='iterator')
ext = os.path.splitext(pathtoebook)[1].replace('.', '').lower()
ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext)
map = dict(MAP)
@ -80,6 +84,9 @@ class EbookIterator(object):
return i
def find_embedded_fonts(self):
'''
This will become unnecessary once Qt WebKit supports the @font-face rule.
'''
for item in self.opf.manifest:
if item.mime_type and 'css' in item.mime_type.lower():
css = open(item.path, 'rb').read().decode('utf-8')
@ -125,9 +132,60 @@ class EbookIterator(object):
s.max_page = s.start_page + s.pages - 1
self.toc = self.opf.toc
self.find_embedded_fonts()
self.find_embedded_fonts()
self.read_bookmarks()
return self
def parse_bookmarks(self, raw):
for line in raw.splitlines():
if line.count('^') > 0:
tokens = line.rpartition('^')
title, ref = tokens[0], tokens[2]
self.bookmarks.append((title, ref))
def serialize_bookmarks(self, bookmarks):
dat = []
for title, bm in bookmarks:
dat.append(u'%s^%s'%(title, bm))
return (u'\n'.join(dat) +'\n').encode('utf-8')
def read_bookmarks(self):
self.bookmarks = []
bmfile = os.path.join(self.base, 'META-INF', 'calibre_bookmarks.txt')
raw = ''
if os.path.exists(bmfile):
raw = open(bmfile, 'rb').read().decode('utf-8')
else:
saved = self.config['bookmarks_'+self.pathtoebook]
if saved:
raw = saved
self.parse_bookmarks(raw)
def save_bookmarks(self, bookmarks=None):
if bookmarks is None:
bookmarks = self.bookmarks
dat = self.serialize_bookmarks(bookmarks)
if os.path.splitext(self.pathtoebook)[1].lower() == '.epub':
zf = open(self.pathtoebook, 'r+b')
zipf = ZipFile(zf, mode='a')
for name in zipf.namelist():
if name == 'META-INF/calibre_bookmarks.txt':
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat))
return
zipf.writestr('META-INF/calibre_bookmarks.txt', dat)
else:
self.config['bookmarks_'+self.pathtoebook] = dat
def add_bookmark(self, bm):
dups = []
for x in self.bookmarks:
if x[0] == bm[0]:
dups.append(x)
for x in dups:
self.bookmarks.remove(x)
self.bookmarks.append(bm)
self.save_bookmarks()
def __exit__(self, *args):
self._tdir.__exit__(*args)

View File

@ -429,7 +429,7 @@ class Parser(PreProcessor, LoggingInterface):
if not m:
return '<html xmlns="http://www.w3.org/1999/xhtml" %s>'%raw
else:
return match.group().sub(m.group('uri'), "http://www.w3.org/1999/xhtml")
return match.group().replace(m.group('uri'), "http://www.w3.org/1999/xhtml")
def save(self):
'''

View File

@ -809,8 +809,9 @@ class HTMLConverter(object, LoggingInterface):
def append_text(src):
fp, key, variant = self.font_properties(css)
src = src.replace(u'\xa0', ' ') #Sony's wonderful reading software doesn't handle the nbsp character
for x, y in [(u'\xa0', ' '), (u'\ufb00', 'ff'), (u'\ufb01', 'fi'), (u'\ufb02', 'fl'), (u'\ufb03', 'ffi'), (u'\ufb04', 'ffl')]:
src = src.replace(x, y)
valigner = lambda x: x
if 'vertical-align' in css:
valign = css['vertical-align']

View File

@ -0,0 +1,590 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.44.1"
version="1.0"
sodipodi:docbase="/Users/david/Progetti/oxygen-svn/theme/svg/actions"
sodipodi:docname="bookmark.svg">
<defs
id="defs4">
<linearGradient
id="linearGradient26907"
gradientUnits="userSpaceOnUse"
x1="-84.002403"
y1="-383.9971"
x2="-12.0029"
y2="-383.9971"
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)">
<stop
offset="0"
style="stop-color:#888a85;stop-opacity:1;"
id="stop26909" />
<stop
offset="1"
style="stop-color:#2e3436;stop-opacity:1;"
id="stop26911" />
</linearGradient>
<linearGradient
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
y2="-383.9975"
x2="-23.516129"
y1="-383.9971"
x1="-84.002403"
gradientUnits="userSpaceOnUse"
id="linearGradient3711">
<stop
id="stop3713"
style="stop-color:white;stop-opacity:1;"
offset="0" />
<stop
id="stop3715"
style="stop-color:white;stop-opacity:0;"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3081">
<stop
id="stop3083"
offset="0"
style="stop-color:#28691f;stop-opacity:1;" />
<stop
id="stop3085"
offset="1"
style="stop-color:#00bf00;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3290">
<stop
style="stop-color:yellow;stop-opacity:1;"
offset="0"
id="stop3292" />
<stop
style="stop-color:#ffb66d;stop-opacity:1;"
offset="1"
id="stop3294" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3765">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop3767" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3769" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3747">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop3749" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3751" />
</linearGradient>
<linearGradient
id="linearGradient3638">
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="0"
id="stop3640" />
<stop
id="stop3661"
offset="0.06868132"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop3659"
offset="0.5"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3642" />
</linearGradient>
<linearGradient
id="linearGradient1563">
<stop
id="stop1565"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop1567"
offset="1"
style="stop-color:white;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient3273">
<stop
id="stop3275"
offset="0"
style="stop-color:#ffffff;stop-opacity:0.55035973;" />
<stop
id="stop3277"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient3291"
inkscape:collect="always">
<stop
id="stop3293"
offset="0"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop3295"
offset="1"
style="stop-color:#000000;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient12948">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop12950" />
<stop
style="stop-color:#c0c0c0;stop-opacity:0;"
offset="1"
id="stop12952" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3273"
id="linearGradient3605"
x1="80.100487"
y1="44.807674"
x2="77.714729"
y2="101.4734"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.959962,0,0,0.959962,2.35549,3.275418)"
spreadMethod="reflect" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3638"
id="linearGradient3644"
x1="57.287113"
y1="1.1597457"
x2="144.2531"
y2="16.876789"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3638"
id="linearGradient3646"
x1="57.287113"
y1="1.1597457"
x2="144.2531"
y2="16.876789"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3638"
id="linearGradient3648"
x1="57.287113"
y1="1.1597457"
x2="144.2531"
y2="16.876789"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12948"
id="radialGradient3716"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,-7.045514e-15,1.946707e-15,0.941176,2.788953e-13,3.492906)"
cx="23.190451"
cy="59.379417"
fx="22.471308"
fy="59.354759"
r="2.1082227" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1563"
id="linearGradient3732"
x1="98.291809"
y1="-126.7503"
x2="44.242641"
y2="101.45739"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1563"
id="linearGradient3739"
gradientUnits="userSpaceOnUse"
x1="98.291809"
y1="-44.01474"
x2="44.242641"
y2="101.45739" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3291"
id="radialGradient3743"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
cx="63.912209"
cy="115.70919"
fx="63.975182"
fy="116.88514"
r="63.912209" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3747"
id="radialGradient3753"
cx="5.7531347"
cy="-45.41592"
fx="74.816956"
fy="-43.169445"
r="124.10334"
gradientTransform="matrix(1,-5.290907e-17,-3.962245e-18,9.492274e-2,9.333694e-14,-41.10492)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3747"
id="radialGradient3757"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,-1.087455e-16,-5.565153e-18,9.492274e-2,-1.420331e-15,-41.10492)"
cx="5.7531347"
cy="-45.41592"
fx="74.816956"
fy="-43.169445"
r="124.10334" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3747"
id="radialGradient3761"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,-1.302059e-16,-7.897474e-18,9.492274e-2,1.345372e-13,-41.10492)"
cx="5.7531347"
cy="-45.41592"
fx="74.816956"
fy="-43.169445"
r="124.10334" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3765"
id="radialGradient3771"
cx="23.662739"
cy="95.898506"
fx="24.26058"
fy="96.778763"
r="2.793914"
gradientTransform="matrix(1.484142,0.129521,-0.489782,5.61225,35.51325,-445.3727)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3290"
id="linearGradient3106"
x1="84.634949"
y1="116.10083"
x2="89.72541"
y2="-15.33666"
gradientUnits="userSpaceOnUse" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.111111,0,138.1081)"
r="64.796692"
fy="177.29686"
fx="80.738739"
cy="155.37218"
cx="80.738739"
id="radialGradient5079"
xlink:href="#linearGradient5073"
inkscape:collect="always" />
<linearGradient
id="linearGradient5073"
inkscape:collect="always">
<stop
id="stop5075"
offset="0"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop5077"
offset="1"
style="stop-color:#000000;stop-opacity:0;" />
</linearGradient>
<foreignObject
id="foreignObject7221"
height="1"
width="1"
y="0"
x="0"
requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/">
<i:pgfRef
xlink:href="#adobe_illustrator_pgf" />
</foreignObject>
<linearGradient
id="XMLID_1_"
gradientUnits="userSpaceOnUse"
x1="95.693398"
y1="141.1738"
x2="32.308601"
y2="77.789001">
<stop
offset="0"
style="stop-color:#75511A"
id="stop7227" />
<stop
offset="0.3988"
style="stop-color:#563A11"
id="stop7229" />
<stop
offset="0.7642"
style="stop-color:#402B0B"
id="stop7231" />
<stop
offset="1"
style="stop-color:#382509"
id="stop7233" />
</linearGradient>
<linearGradient
id="XMLID_3_"
gradientUnits="userSpaceOnUse"
x1="63.9995"
y1="92.865196"
x2="63.9995"
y2="120.8652"
gradientTransform="translate(175.0067,11.74752)">
<stop
offset="0"
style="stop-color:#888A85"
id="stop7261" />
<stop
offset="0.3226"
style="stop-color:#A6A7A3"
id="stop7263" />
<stop
offset="1"
style="stop-color:#EEEEEC"
id="stop7265" />
</linearGradient>
<linearGradient
id="XMLID_4_"
gradientUnits="userSpaceOnUse"
x1="64.000504"
y1="108.8652"
x2="64.000504"
y2="92.865196">
<stop
offset="0"
style="stop-color:#EEEEEC"
id="stop7270" />
<stop
offset="1"
style="stop-color:#FFFFFF"
id="stop7272" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3081"
id="linearGradient2149"
gradientUnits="userSpaceOnUse"
x1="62.112335"
y1="90.513916"
x2="67.887672"
y2="39.095695" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient26907"
id="linearGradient3226"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
x1="-70.002899"
y1="-383.9971"
x2="-11.91648"
y2="-383.9971" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3711"
id="radialGradient3228"
gradientUnits="userSpaceOnUse"
cx="343.99899"
cy="92"
fx="343.99899"
fy="92"
r="36" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3711"
id="linearGradient3230"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,1.022977,-1.022977,0,111.9686,137.8125)"
x1="-88.058083"
y1="-131.93112"
x2="-45.096584"
y2="-131.93112" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.8203125"
inkscape:cx="64"
inkscape:cy="64"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="1247"
inkscape:window-height="816"
inkscape:window-x="388"
inkscape:window-y="110"
showgrid="true"
gridspacingx="4px"
gridspacingy="4px"
gridempspacing="0"
inkscape:grid-points="true" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
transform="matrix(0.511285,0.187762,-0.187762,0.511285,41.72321,44.08266)"
d="M 153.09403,94.713757 C 144.53658,107.09689 92.616372,93.013297 78.414631,98.001518 C 64.21289,102.98974 32.50348,146.4474 18.082028,142.13539 C 3.6605746,137.82337 1.0106378,84.092245 -8.1220219,72.127031 C -17.254681,60.161818 -68.384124,43.433534 -68.739625,28.385431 C -69.095125,13.337327 -18.812666,-5.7867426 -10.255219,-18.169872 C -1.697772,-30.553002 -1.5880954,-84.349316 12.613645,-89.337536 C 26.815387,-94.325757 60.541592,-52.41396 74.963045,-48.101941 C 89.384498,-43.789923 140.58172,-60.30959 149.71438,-48.344376 C 158.84704,-36.379162 129.40853,8.6478227 129.76403,23.695927 C 130.11953,38.74403 161.65148,82.330628 153.09403,94.713757 z "
inkscape:randomized="0"
inkscape:rounded="0.20136392"
inkscape:flatsided="false"
sodipodi:arg2="1.2330172"
sodipodi:arg1="0.60469864"
sodipodi:r2="76.832565"
sodipodi:r1="121.72647"
sodipodi:cy="25.510532"
sodipodi:cx="52.952892"
sodipodi:sides="5"
id="path3574"
style="opacity:1;fill:#e3ad00;fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:14.80892919;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
sodipodi:type="star" />
<path
style="opacity:1;fill:url(#linearGradient3106);fill-opacity:1.0;fill-rule:nonzero;stroke:url(#linearGradient3605);stroke-width:6.803;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
d="M 64.817613,10.159328 C 64.581604,10.317484 63.312654,10.957094 61.843149,12.708869 C 60.101516,14.785047 58.138879,17.917081 56.177505,21.235666 C 54.216128,24.55425 52.251443,28.092247 50.341888,31.150546 C 48.432331,34.208845 46.806952,36.762169 44.279648,38.544213 C 41.752344,40.326257 38.764915,41.002069 35.242943,41.773631 C 31.720971,42.545192 27.75285,43.193621 23.968308,43.926576 C 20.183765,44.659533 16.5656,45.476237 14.025101,46.419462 C 11.841858,47.230044 10.829167,48.201295 10.625712,48.345781 C 10.626839,48.347738 10.624524,48.371367 10.625712,48.374108 C 10.696321,48.571093 10.870285,49.989399 12.127109,52.000123 C 13.563478,54.298089 15.950898,57.154462 18.50096,60.045339 C 21.051023,62.936217 23.774397,65.867627 26.092925,68.628793 C 28.411454,71.389955 30.363146,73.748045 31.27699,76.702337 C 32.190833,79.656627 31.914822,82.68926 31.560274,86.277278 C 31.205724,89.8653 30.616267,93.839413 30.143862,97.665227 C 30.113483,97.911252 30.08362,98.156728 30.054361,98.401429 C 29.628627,101.96194 29.330856,105.3582 29.435657,107.89172 C 29.533657,110.26089 30.173974,111.54076 30.228847,111.74436 C 30.438123,111.73798 31.837454,111.97838 34.138142,111.40442 C 36.7675,110.74847 40.20401,109.39531 43.741411,107.86339 C 47.278812,106.33148 50.937026,104.62602 54.279513,103.27421 C 57.621999,101.9224 60.450754,100.79418 63.542844,100.838 C 66.634933,100.8818 69.418339,102.08321 72.721189,103.52916 C 76.024038,104.97512 79.653393,106.79843 83.145977,108.42996 C 86.638561,110.06147 90.026215,111.49575 92.635935,112.22595 C 94.919441,112.86485 96.334003,112.66799 96.54523,112.67918 C 96.605258,112.47676 97.286649,111.22034 97.451733,108.85487 C 97.640395,106.1515 97.418963,102.46604 97.055137,98.628384 C 96.691312,94.790732 96.174767,90.780408 95.922008,87.183781 C 95.669251,83.587159 95.491404,80.56438 96.488573,77.637169 C 97.485753,74.709955 99.503438,72.399636 101.89927,69.705264 C 104.29508,67.010894 107.11524,64.16591 109.74619,61.348436 C 112.37711,58.530963 114.78913,55.729544 116.29001,53.47319 C 117.57984,51.534136 117.84976,50.137859 117.93304,49.903833 C 117.93436,49.901119 117.93183,49.877435 117.93304,49.875506 C 117.7318,49.725835 116.72138,48.73631 114.56198,47.864201 C 112.04922,46.849382 108.49434,45.927914 104.73209,45.088035 C 100.96983,44.248155 97.012813,43.466178 93.514108,42.595149 C 90.015409,41.724119 87.038194,40.992031 84.562389,39.139105 C 82.086586,37.286179 80.548923,34.65831 78.726774,31.54714 C 76.904626,28.435971 75.041014,24.863438 73.174441,21.49062 C 71.307869,18.117801 69.417536,14.946869 67.735421,12.822182 C 66.263569,10.963081 64.982527,10.293346 64.817613,10.159328 z "
id="path3580"
sodipodi:nodetypes="cssssssssssssssssscssssssscssssssssssssssssc" />
<path
sodipodi:nodetypes="ccc"
id="path2276"
d="M -106.3852,44.124126 L -106.3852,41.329417 L -106.3852,44.124126 z "
style="fill:#ffffff;fill-opacity:0.75688076;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
<path
sodipodi:type="arc"
style="opacity:0.38139535;fill:url(#radialGradient3743);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
id="path3289"
sodipodi:cx="63.912209"
sodipodi:cy="115.70919"
sodipodi:rx="63.912209"
sodipodi:ry="12.641975"
d="M 127.82442 115.70919 A 63.912209 12.641975 0 1 1 0,115.70919 A 63.912209 12.641975 0 1 1 127.82442 115.70919 z"
transform="matrix(-1.001374,0,0,0.410379,128,75.32738)" />
<path
style="fill:none;fill-opacity:1.0;fill-rule:evenodd;stroke:url(#linearGradient3648);stroke-width:0.50672567;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5813008"
d="M 55.266721,10.739701 C 56.520212,8.5685899 61.220699,1.3579337 65.008418,1.4134271 C 71.889436,1.5172832 83.511202,31.129589 88.946059,34.460427 C 95.635958,38.560436 119.92387,41.46414 124.34296,44.969282"
id="path3632"
sodipodi:nodetypes="csss" />
<path
sodipodi:nodetypes="csss"
id="path3634"
d="M 55.236135,11.274949 C 56.489626,9.1038383 61.236542,1.57297 65.023711,1.6581121 C 71.830955,1.8111507 83.271335,31.483209 88.869595,34.705112 C 95.670099,38.618929 119.98852,41.765855 124.40761,45.270997"
style="fill:none;fill-opacity:1.0;fill-rule:evenodd;stroke:url(#linearGradient3646);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.43089432" />
<path
style="fill:none;fill-opacity:1.0;fill-rule:evenodd;stroke:url(#linearGradient3644);stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.43089432"
d="M 55.205632,12.312054 C 56.459123,10.140944 61.420826,2.0361374 65.023711,2.1461616 C 71.282863,2.3391656 83.42385,32.459308 88.778086,35.193162 C 95.766183,38.761259 121.08663,42.680948 124.71264,46.094581"
id="path3636"
sodipodi:nodetypes="csss" />
<path
sodipodi:type="arc"
style="opacity:0.70232556;fill:url(#radialGradient3716);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.89999998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
id="path11160"
sodipodi:cx="23.190451"
sodipodi:cy="59.379417"
sodipodi:rx="2.1082227"
sodipodi:ry="1.9842097"
d="M 25.298673 59.379417 A 2.1082227 1.9842097 0 1 1 21.082228,59.379417 A 2.1082227 1.9842097 0 1 1 25.298673 59.379417 z"
transform="matrix(-1.742936,-1.063485,-0.470527,1.244278,191.1539,-3.699137)" />
<path
style="opacity:1;fill:url(#linearGradient3732);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:6.803;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
d="M 64.8125 10.15625 C 64.576492 10.314406 63.313255 10.966975 61.84375 12.71875 C 60.102119 14.794928 58.148874 17.931415 56.1875 21.25 C 54.226123 24.568584 52.253305 28.097951 50.34375 31.15625 C 48.434193 34.21455 46.808554 36.749206 44.28125 38.53125 C 41.753946 40.313295 38.771972 41.009688 35.25 41.78125 C 31.728028 42.55281 27.753292 43.204545 23.96875 43.9375 C 20.184208 44.670458 16.571749 45.463025 14.03125 46.40625 C 11.848007 47.216834 10.828455 48.199264 10.625 48.34375 C 10.62496 48.346283 10.625145 48.371753 10.625 48.375 C 10.695609 48.571986 10.868176 49.989276 12.125 52 C 13.561369 54.297967 15.949938 57.140373 18.5 60.03125 C 20.422509 62.210702 22.440722 64.427292 24.3125 66.5625 C 47.187815 68.967477 71.532076 77.450485 95.75 81.53125 C 95.830132 80.186335 96.067405 78.894893 96.5 77.625 C 97.497182 74.697786 99.510418 72.413122 101.90625 69.71875 C 104.30206 67.024383 107.11905 64.161224 109.75 61.34375 C 112.38092 58.526279 114.78037 55.725104 116.28125 53.46875 C 117.57108 51.529696 117.85422 50.140276 117.9375 49.90625 C 117.93747 49.903618 117.93766 49.878251 117.9375 49.875 C 117.73626 49.725328 116.7219 48.747109 114.5625 47.875 C 112.04974 46.860181 108.481 45.933629 104.71875 45.09375 C 100.95649 44.253869 96.998705 43.464779 93.5 42.59375 C 90.001302 41.722719 87.038305 40.977926 84.5625 39.125 C 82.0867 37.272072 80.540899 34.67367 78.71875 31.5625 C 76.8966 28.451331 75.054073 24.872818 73.1875 21.5 C 71.320931 18.127181 69.432115 14.937187 67.75 12.8125 C 66.278149 10.953399 64.977414 10.290268 64.8125 10.15625 z "
id="path3718" />
<path
style="opacity:0.41393443;fill:url(#linearGradient3739);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:6.803;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
d="M 64.8125 10.15625 C 64.576492 10.314406 63.313255 10.966975 61.84375 12.71875 C 60.102119 14.794928 58.148874 17.931415 56.1875 21.25 C 54.226123 24.568584 52.253305 28.097951 50.34375 31.15625 C 48.434193 34.21455 46.808554 36.749206 44.28125 38.53125 C 41.753946 40.313295 38.771972 41.009688 35.25 41.78125 C 31.728028 42.55281 27.753292 43.204545 23.96875 43.9375 C 20.184208 44.670458 16.571749 45.463025 14.03125 46.40625 C 11.848007 47.216834 10.828455 48.199264 10.625 48.34375 C 10.62496 48.346283 10.625145 48.371753 10.625 48.375 C 10.695609 48.571986 10.868176 49.989276 12.125 52 C 13.561369 54.297967 15.949938 57.140373 18.5 60.03125 C 20.422509 62.210702 22.440722 64.427292 24.3125 66.5625 C 25.752576 66.713901 27.204929 66.896788 28.65625 67.09375 C 42.328845 56.623879 60.733777 43.188439 78.53125 31.25 C 76.771363 28.21678 74.98888 24.755017 73.1875 21.5 C 71.320931 18.127181 69.432115 14.937187 67.75 12.8125 C 66.278149 10.953399 64.977414 10.290268 64.8125 10.15625 z M 99.34375 43.90625 C 86.79565 53.381359 75.792347 63.914843 66.25 75.09375 C 76.032927 77.504442 85.901575 79.871772 95.75 81.53125 C 95.830132 80.186335 96.067405 78.894893 96.5 77.625 C 97.497182 74.697786 99.510418 72.413122 101.90625 69.71875 C 104.30206 67.024383 107.11905 64.161224 109.75 61.34375 C 112.38092 58.526279 114.78037 55.725104 116.28125 53.46875 C 117.57108 51.529696 117.85422 50.140276 117.9375 49.90625 C 117.93747 49.903618 117.93766 49.878251 117.9375 49.875 C 117.73626 49.725328 116.7219 48.747109 114.5625 47.875 C 112.04974 46.860181 108.481 45.933629 104.71875 45.09375 C 102.94925 44.698729 101.13165 44.292609 99.34375 43.90625 z "
id="path3736" />
<path
id="path3741"
d="M 64.8125,10.15625 C 64.576492,10.314406 63.313255,10.966975 61.84375,12.71875 C 60.102119,14.794928 58.148874,17.931415 56.1875,21.25 C 54.226123,24.568584 52.253305,28.097951 50.34375,31.15625 C 48.434193,34.21455 46.808554,36.749206 44.28125,38.53125 C 41.753946,40.313295 38.771972,41.009688 35.25,41.78125 C 31.728028,42.55281 27.753292,43.204545 23.96875,43.9375 C 20.184208,44.670458 16.571749,45.463025 14.03125,46.40625 C 11.848007,47.216834 10.828455,48.199264 10.625,48.34375 C 10.62496,48.346283 10.625145,48.371753 10.625,48.375 C 10.695609,48.571986 10.868176,49.989276 12.125,52 C 13.561369,54.297967 60.733777,43.188439 78.53125,31.25 C 76.771363,28.21678 74.98888,24.755017 73.1875,21.5 C 71.320931,18.127181 69.432115,14.937187 67.75,12.8125 C 66.278149,10.953399 64.977414,10.290268 64.8125,10.15625 z M 99.34375,43.90625 C 68.470207,67.487324 85.901575,79.871772 95.75,81.53125 C 95.830132,80.186335 96.067405,78.894893 96.5,77.625 C 97.497182,74.697786 99.510418,72.413122 101.90625,69.71875 C 104.30206,67.024383 107.11905,64.161224 109.75,61.34375 C 112.38092,58.526279 114.78037,55.725104 116.28125,53.46875 C 117.57108,51.529696 117.85422,50.140276 117.9375,49.90625 C 117.93747,49.903618 117.93766,49.878251 117.9375,49.875 C 117.73626,49.725328 116.7219,48.747109 114.5625,47.875 C 112.04974,46.860181 108.481,45.933629 104.71875,45.09375 C 102.94925,44.698729 101.13165,44.292609 99.34375,43.90625 z "
style="opacity:0.34836066;fill:url(#linearGradient3739);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:6.803;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
sodipodi:nodetypes="csssssssssscsscccssssssssc" />
<path
sodipodi:type="arc"
style="opacity:0.35655739;fill:url(#radialGradient3753);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:0.43089432"
id="path3745"
sodipodi:cx="5.7531347"
sodipodi:cy="-45.41592"
sodipodi:rx="124.10334"
sodipodi:ry="11.780229"
d="M 129.85647 -45.41592 A 124.10334 11.780229 0 1 1 -118.35021,-45.41592 A 124.10334 11.780229 0 1 1 129.85647 -45.41592 z"
transform="matrix(0.126835,-5.623734e-2,-3.870485e-2,-9.211943e-2,44.81196,106.2565)" />
<path
transform="matrix(-0.126834,-5.702883e-2,3.870485e-2,-9.341592e-2,81.95911,106.3126)"
d="M 129.85647 -45.41592 A 124.10334 11.780229 0 1 1 -118.35021,-45.41592 A 124.10334 11.780229 0 1 1 129.85647 -45.41592 z"
sodipodi:ry="11.780229"
sodipodi:rx="124.10334"
sodipodi:cy="-45.41592"
sodipodi:cx="5.7531347"
id="path3755"
style="opacity:0.49590164;fill:url(#radialGradient3757);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:0.43089432"
sodipodi:type="arc" />
<path
transform="matrix(-6.548665e-3,-0.135343,-9.988208e-2,2.696531e-3,91.9485,98.93228)"
d="M 129.85647 -45.41592 A 124.10334 11.780229 0 1 1 -118.35021,-45.41592 A 124.10334 11.780229 0 1 1 129.85647 -45.41592 z"
sodipodi:ry="11.780229"
sodipodi:rx="124.10334"
sodipodi:cy="-45.41592"
sodipodi:cx="5.7531347"
id="path3759"
style="opacity:0.27459016;fill:url(#radialGradient3761);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:0.43089432"
sodipodi:type="arc" />
<path
style="opacity:0.17;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
d="M 126.314,45.945281 C 127.57715,54.014451 105.05512,70.888621 102.47025,78.476531 C 99.826558,86.237151 107.76438,114.43949 101.06401,119.16403 C 94.363618,123.88859 70.449248,106.96767 62.251508,106.85153 C 54.053768,106.73539 29.690348,122.98275 23.126508,118.07028 C 22.815738,117.8377 22.558998,117.52665 22.314008,117.19528 C 22.708398,118.50348 23.279848,119.53038 24.126508,120.16403 C 30.690348,125.0765 55.053768,108.82913 63.251508,108.94528 C 71.449248,109.06142 95.363618,126.01358 102.064,121.28903 C 108.76438,116.56449 100.82656,88.362141 103.47025,80.601531 C 106.11395,72.840911 129.61178,55.340191 127.189,47.507781 C 127.02007,46.961651 126.72727,46.430131 126.314,45.945281 z M 1.5015079,49.851531 C 4.5831579,57.838661 18.613888,70.110761 22.845258,77.320281 C 20.896748,71.159061 6.3960679,58.682221 1.5015079,49.851531 z "
id="path3308" />
<path
style="opacity:0.17;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
d="M 127.1265,47.382781 C 127.77543,55.569451 105.80307,72.111371 103.2515,79.601531 C 100.60781,87.362151 108.54563,115.56449 101.84525,120.28903 C 95.144868,125.01357 71.261748,108.06142 63.064008,107.94528 C 54.866278,107.82913 30.471598,124.10775 23.907758,119.19528 C 22.913828,118.45141 22.292008,117.15904 21.907758,115.50778 C 22.250148,117.65334 22.937778,119.30561 24.126508,120.19528 C 30.690348,125.10775 55.053778,108.82913 63.251508,108.94528 C 71.449248,109.06142 95.363638,126.01357 102.064,121.28903 C 108.76438,116.56449 100.82655,88.362151 103.47025,80.601531 C 106.11395,72.840911 129.61177,55.340181 127.189,47.507781 C 127.17552,47.464201 127.14164,47.425951 127.1265,47.382781 z M 0.93900787,47.757781 C 1.9815279,56.300511 21.645828,72.265141 23.876508,79.476531 C 23.903658,79.564291 23.914628,79.665031 23.939008,79.757781 C 23.870988,79.288551 23.775168,78.856101 23.657758,78.476531 C 21.511968,71.539581 3.2557579,56.495971 0.93900787,47.757781 z "
id="path3303" />
<path
style="fill:url(#radialGradient3771);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.55327869"
d="M 25.088207,80.21837 C 25.534474,90.747814 22.054583,105.95279 22.237274,111.46459 L 24.632057,111.69267 C 26.600057,101.69251 28.017156,91.508728 28.167214,80.902594 L 25.088207,80.21837 z "
id="path3763"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:0.62;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
d="M 0.89521787,47.070281 C 0.89114787,55.417331 21.603498,72.067851 23.895208,79.476531 C 24.122348,80.210811 24.248638,81.111361 24.301458,82.164031 C 24.258118,80.995191 24.141798,79.961201 23.895208,79.164031 C 21.629398,71.839061 1.3489179,55.506151 0.89521787,47.070281 z M 127.36395,48.789031 C 126.76815,57.193121 105.96742,73.013441 103.48895,80.289031 C 103.13757,81.320501 102.96318,82.732101 102.92645,84.382781 C 102.97549,82.859581 103.15849,81.571601 103.48895,80.601531 C 105.99802,73.236121 127.27213,57.108781 127.36395,48.789031 z M 63.270208,108.63278 C 55.072468,108.51664 30.709048,124.79525 24.145208,119.88278 C 22.709368,118.80818 21.987678,116.61813 21.738958,113.78903 C 21.963318,116.76637 22.658218,119.08239 24.145208,120.19528 C 30.709048,125.10775 55.072468,108.82913 63.270208,108.94528 C 71.467938,109.06142 95.382328,126.01357 102.0827,121.28903 C 103.80587,120.07399 104.52313,117.30046 104.73895,113.72653 C 104.49958,117.15646 103.75779,119.79539 102.0827,120.97653 C 95.382328,125.70107 71.467938,108.74892 63.270208,108.63278 z "
id="path3296" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -904,11 +904,7 @@ class Main(MainWindow, Ui_MainWindow):
#############################View book######################################
def view_format(self, row, format):
pt = PersistentTemporaryFile('_viewer.'+format.lower())
pt.write(self.library_view.model().db.format(row, format))
pt.close()
self.persistent_files.append(pt)
self._view_file(pt.name)
self._view_file(self.library_view.model().db.format(row, format, as_file=True).name)
def book_downloaded_for_viewing(self, job):
if job.exception:

View File

@ -13,7 +13,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
from calibre.utils.config import Config, StringConfig
from calibre.gui2.viewer.config_ui import Ui_Dialog
from calibre.gui2.viewer.js import bookmarks, referencing
def load_builtin_fonts():
from calibre.ebooks.lrf.fonts.liberation import LiberationMono_BoldItalic
@ -134,8 +134,23 @@ class Document(QWebPage):
self.load_javascript_libraries)
def load_javascript_libraries(self):
from calibre.resources import jquery
from calibre.resources import jquery, jquery_scrollTo
self.javascript(jquery)
self.javascript(jquery_scrollTo)
self.javascript(bookmarks)
self.javascript(referencing)
def reference_mode(self, enable):
self.javascript(('enter' if enable else 'leave')+'_reference_mode()')
def set_reference_prefix(self, prefix):
self.javascript('reference_prefix = "%s"'%prefix)
def goto(self, ref):
self.javascript('goto_reference("%s")'%ref)
def goto_bookmark(self, bm):
self.javascript('scroll_to_bookmark("%s")'%bm)
def javascript(self, string, typ=None):
ans = self.mainFrame().evaluateJavaScript(string)
@ -162,7 +177,10 @@ class Document(QWebPage):
r = self.height%self.window_height
if r > 0:
self.javascript('document.body.style.paddingBottom = "%dpx"'%r)
def bookmark(self):
return self.javascript('calculate_bookmark(%d)'%(self.ypos+25), 'string')
@apply
def at_bottom():
def fget(self):
@ -190,6 +208,12 @@ class Document(QWebPage):
def fget(self):
return self.javascript('window.innerHeight', 'int')
return property(fget=fget)
@apply
def window_width():
def fget(self):
return self.javascript('window.innerWidth', 'int')
return property(fget=fget)
@apply
def xpos():
@ -249,15 +273,29 @@ class DocumentView(QWebView):
self.document = Document(self)
self.setPage(self.document)
self.manager = None
self._reference_mode = False
self.connect(self.document, SIGNAL('loadStarted()'), self.load_started)
self.connect(self.document, SIGNAL('loadFinished(bool)'), self.load_finished)
self.connect(self.document, SIGNAL('linkClicked(QUrl)'), self.link_clicked)
self.connect(self.document, SIGNAL('linkHovered(QString,QString,QString)'), self.link_hovered)
self.connect(self.document, SIGNAL('selectionChanged()'), self.selection_changed)
def reference_mode(self, enable):
self._reference_mode = enable
self.document.reference_mode(enable)
def goto(self, ref):
self.document.goto(ref)
def goto_bookmark(self, bm):
self.document.goto_bookmark(bm)
def config(self, parent=None):
self.document.do_config(parent)
def bookmark(self):
return self.document.bookmark()
def selection_changed(self):
if self.manager is not None:
self.manager.selection_changed(unicode(self.document.selectedText()))
@ -344,8 +382,11 @@ class DocumentView(QWebView):
self.initial_pos = 0.0
self.update()
self.initialize_scrollbar()
self.document.reference_mode(self._reference_mode)
if self.manager is not None:
self.manager.load_finished(bool(ok))
spine_index = self.manager.load_finished(bool(ok))
if spine_index > -1:
self.document.set_reference_prefix('%d.'%(spine_index+1))
if scrolled:
self.manager.scrolled(self.document.scroll_fraction)

View File

@ -0,0 +1,194 @@
/**
* jQuery.ScrollTo
* Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Dual licensed under MIT and GPL.
* Date: 9/11/2008
*
* @projectDescription Easy element scrolling using jQuery.
* http://flesler.blogspot.com/2007/10/jqueryscrollto.html
* Tested with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.2/5 and Safari 3. on Windows.
*
* @author Ariel Flesler
* @version 1.4
*
* @id jQuery.scrollTo
* @id jQuery.fn.scrollTo
* @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
* The different options for target are:
* - A number position (will be applied to all axes).
* - A string position ('44', '100px', '+=90', etc ) will be applied to all axes
* - A jQuery/DOM element ( logically, child of the element to scroll )
* - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
* - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
* @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
* @param {Object,Function} settings Optional set of settings or the onAfter callback.
* @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
* @option {Number} duration The OVERALL length of the animation.
* @option {String} easing The easing method for the animation.
* @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
* @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
* @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
* @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
* @option {Function} onAfter Function to be called after the scrolling ends.
* @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
* @return {jQuery} Returns the same jQuery object, for chaining.
*
* @desc Scroll to a fixed position
* @example $('div').scrollTo( 340 );
*
* @desc Scroll relatively to the actual position
* @example $('div').scrollTo( '+=340px', { axis:'y' } );
*
* @dec Scroll using a selector (relative to the scrolled element)
* @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
*
* @ Scroll to a DOM element (same for jQuery object)
* @example var second_child = document.getElementById('container').firstChild.nextSibling;
* $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
* alert('scrolled!!');
* }});
*
* @desc Scroll on both axes, to different values
* @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
*/
;(function( $ ){
var $scrollTo = $.scrollTo = function( target, duration, settings ){
$(window).scrollTo( target, duration, settings );
};
$scrollTo.defaults = {
axis:'y',
duration:1
};
// Returns the element that needs to be animated to scroll the window.
// Kept for backwards compatibility (specially for localScroll & serialScroll)
$scrollTo.window = function( scope ){
return $(window).scrollable();
};
// Hack, hack, hack... stay away!
// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
$.fn.scrollable = function(){
return this.map(function(){
// Just store it, we might need it
var win = this.parentWindow || this.defaultView,
// If it's a document, get its iframe or the window if it's THE document
elem = this.nodeName == '#document' ? win.frameElement || win : this,
// Get the corresponding document
doc = elem.contentDocument || (elem.contentWindow || elem).document,
isWin = elem.setInterval;
return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body
: isWin ? doc.documentElement
: this;
});
};
$.fn.scrollTo = function( target, duration, settings ){
if( typeof duration == 'object' ){
settings = duration;
duration = 0;
}
if( typeof settings == 'function' )
settings = { onAfter:settings };
settings = $.extend( {}, $scrollTo.defaults, settings );
// Speed is still recognized for backwards compatibility
duration = duration || settings.speed || settings.duration;
// Make sure the settings are given right
settings.queue = settings.queue && settings.axis.length > 1;
if( settings.queue )
// Let's keep the overall duration
duration /= 2;
settings.offset = both( settings.offset );
settings.over = both( settings.over );
return this.scrollable().each(function(){
var elem = this,
$elem = $(elem),
targ = target, toff, attr = {},
win = $elem.is('html,body');
switch( typeof targ ){
// A number will pass the regex
case 'number':
case 'string':
if( /^([+-]=)?\d+(px)?$/.test(targ) ){
targ = both( targ );
// We are done
break;
}
// Relative selector, no break!
targ = $(targ,this);
case 'object':
// DOMElement / jQuery
if( targ.is || targ.style )
// Get the real position of the target
toff = (targ = $(targ)).offset();
}
$.each( settings.axis.split(''), function( i, axis ){
var Pos = axis == 'x' ? 'Left' : 'Top',
pos = Pos.toLowerCase(),
key = 'scroll' + Pos,
old = elem[key],
Dim = axis == 'x' ? 'Width' : 'Height',
dim = Dim.toLowerCase();
if( toff ){// jQuery / DOMElement
attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );
// If it's a dom element, reduce the margin
if( settings.margin ){
attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
}
attr[key] += settings.offset[pos] || 0;
if( settings.over[pos] )
// Scroll to a fraction of its width/height
attr[key] += targ[dim]() * settings.over[pos];
}else
attr[key] = targ[pos];
// Number or 'number'
if( /^\d+$/.test(attr[key]) )
// Check the limits
attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );
// Queueing axes
if( !i && settings.queue ){
// Don't waste time animating, if there's no need.
if( old != attr[key] )
// Intermediate animation
animate( settings.onAfterFirst );
// Don't animate this axis again in the next iteration.
delete attr[key];
}
});
animate( settings.onAfter );
function animate( callback ){
$elem.animate( attr, duration, settings.easing, callback && function(){
callback.call(this, target, settings);
});
};
function max( Dim ){
var attr ='scroll'+Dim,
doc = elem.ownerDocument;
return win
? Math.max( doc.documentElement[attr], doc.body[attr] )
: elem[attr];
};
}).end();
};
function both( val ){
return typeof val == 'object' ? val : { top:val, left:val };
};
})( jQuery );

View File

@ -0,0 +1,121 @@
bookmarks = '''
function find_enclosing_block(y) {
var elements = $("*", document.body);
var min = 0;
var temp, left, top, elem, width, height, ratio;
for (i = 0; i < elements.length; i++) {
elem = $(elements[i]);
temp = elem.offset();
left = temp.left; top = temp.top;
width = elem.width(); height = elem.height();
if (top > y+50) break;
for ( x = 40; x < window.innerWidth; x += 20) {
if (x >= left && x <= left+width && y >= top && y <= top+height) {
if (min == 0 || min.height() > height) { min = elem; break; }
}
}
if (min != 0 && min.height() < 200) break;
}
if (y <= 0) return document.body;
if (min == 0) { return find_enclosing_block(x, y-20); }
return min;
}
function selector_in_parent(elem) {
var num = elem.prevAll().length;
var sel = " > *:eq("+num+") ";
return sel;
}
function selector(elem) {
var obj = elem;
var sel = "";
while (obj[0] != document) {
sel = selector_in_parent(obj) + sel;
obj = obj.parent();
}
return sel;
}
function calculate_bookmark(y) {
var elem = find_enclosing_block(y);
var sel = selector(elem);
var ratio = (y - elem.offset().top)/elem.height();
if (ratio > 1) { ratio = 1; }
if (ratio < 0) { ratio = 0; }
return sel + "|" + ratio;
}
function scroll_to_bookmark(bookmark) {
bm = bookmark.split("|");
var ratio = 0.7 * parseFloat(bm[1]);
$.scrollTo($(bm[0]), 1000, {over:ratio});
}
'''
referencing = '''
var reference_old_bgcol = "transparent";
var reference_prefix = "1.";
function show_reference_panel(ref) {
panel = $("#calibre_reference_panel");
if (panel.length < 1) {
$(document.body).append('<div id="calibre_reference_panel" style="top:20px; left:20px; padding-left:30px; padding-right:30px; font:monospace normal;text-align:center; z-index:10000; background: beige; border:red ridge 2px; position:absolute;"><h5>Paragraph</h5><p style="text-indent:0pt">None</p></div>')
panel = $("#calibre_reference_panel");
}
$("> p", panel).text(ref);
panel.css({top:(window.pageYOffset+20)+"px"});
panel.fadeIn(500);
}
function toggle_reference(e) {
p = $(this);
if (e.type == "mouseenter") {
reference_old_bgcol = p.css("background-color");
p.css({backgroundColor:"beige"});
var i = 0;
var paras = $("p");
for (j = 0; j < paras.length; j++,i++) {
if (paras[j] == p[0]) break;
}
show_reference_panel(reference_prefix+(i+1) );
} else {
p.css({backgroundColor:reference_old_bgcol});
panel = $("#calibre_reference_panel").hide();
}
return false;
}
function enter_reference_mode() {
$("p").bind("mouseenter mouseleave", toggle_reference);
}
function leave_reference_mode() {
$("p").unbind("mouseenter mouseleave", toggle_reference);
}
function goto_reference(ref) {
var tokens = ref.split(".");
if (tokens.length != 2) {alert("Invalid reference: "+ref); return;}
var num = parseInt(tokens[1]);
if (isNaN(num)) {alert("Invalid reference: "+ref); return;}
num -= 1;
if (num < 0) {alert("Invalid reference: "+ref); return;}
var p = $("p");
if (num >= p.length) {alert("Reference not found: "+ref); return;}
$.scrollTo($(p[num]), 1000);
}
'''
test = '''
$(document.body).click(function(e) {
bm = calculate_bookmark(e.pageY);
scroll_to_bookmark(bm);
});
$(document).ready(enter_reference_mode);
'''

View File

@ -2,11 +2,14 @@ from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import traceback, os, sys, functools, collections
from functools import partial
from threading import Thread
from PyQt4.Qt import QMovie, QApplication, Qt, QIcon, QTimer, QWidget, SIGNAL, \
QDesktopServices, QDoubleSpinBox, QLabel, QTextBrowser, \
QPainter, QBrush, QColor, QStandardItemModel, QStandardItem, QUrl
QPainter, QBrush, QColor, QStandardItemModel, QPalette, \
QStandardItem, QUrl, QRegExpValidator, QRegExp, QLineEdit, \
QToolButton, QMenu, QInputDialog
from calibre.gui2.viewer.main_ui import Ui_EbookViewer
from calibre.gui2.main_window import MainWindow
@ -163,29 +166,63 @@ class DoubleSpinBox(QDoubleSpinBox):
self.blockSignals(True)
self.setValue(val)
self.blockSignals(False)
class HelpfulLineEdit(QLineEdit):
HELP_TEXT = _('Go to...')
def __init__(self, *args):
QLineEdit.__init__(self, *args)
self.default_palette = QApplication.palette(self)
self.gray = QPalette(self.default_palette)
self.gray.setBrush(QPalette.Text, QBrush(QColor('gray')))
self.connect(self, SIGNAL('editingFinished()'),
lambda : self.emit(SIGNAL('goto(PyQt_PyObject)'), unicode(self.text())))
self.clear_to_help_mode()
def focusInEvent(self, ev):
self.setPalette(QApplication.palette(self))
if self.in_help_mode():
self.setText('')
return QLineEdit.focusInEvent(self, ev)
def in_help_mode(self):
return unicode(self.text()) == self.HELP_TEXT
def clear_to_help_mode(self):
self.setPalette(self.gray)
self.setText(self.HELP_TEXT)
class EbookViewer(MainWindow, Ui_EbookViewer):
def __init__(self, pathtoebook=None):
MainWindow.__init__(self, None)
self.setupUi(self)
self.iterator = None
self.current_page = None
self.pending_search = None
self.pending_anchor = None
self.selected_text = None
self.iterator = None
self.current_page = None
self.pending_search = None
self.pending_anchor = None
self.pending_reference = None
self.pending_bookmark = None
self.selected_text = None
self.history = History(self.action_back, self.action_forward)
self.metadata = Metadata(self)
self.pos = DoubleSpinBox()
self.pos.setDecimals(1)
self.pos.setToolTip(_('Position in book'))
self.pos.setSuffix(_('/Unknown')+' ')
self.pos.setMinimum(1.)
self.tool_bar2.insertWidget(self.action_find_next, self.pos)
self.reference = HelpfulLineEdit()
self.reference.setValidator(QRegExpValidator(QRegExp(r'\d+\.\d+'), self.reference))
self.reference.setToolTip(_('Go to a reference. To get reference numbers, use the reference mode.'))
self.tool_bar2.insertSeparator(self.action_find_next)
self.tool_bar2.insertWidget(self.action_find_next, self.reference)
self.tool_bar2.insertSeparator(self.action_find_next)
self.setFocusPolicy(Qt.StrongFocus)
self.search = SearchBox(self, _('Search'))
self.search.setToolTip(_('Search for text in book'))
self.tool_bar2.insertWidget(self.action_find_next, self.search)
self.view.set_manager(self)
self.pi = ProgressIndicator(self)
@ -193,6 +230,9 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.action_copy.setDisabled(True)
self.action_metadata.setCheckable(True)
self.action_table_of_contents.setCheckable(True)
self.action_reference_mode.setCheckable(True)
self.connect(self.action_reference_mode, SIGNAL('triggered(bool)'),
lambda x: self.view.reference_mode(x))
self.connect(self.action_metadata, SIGNAL('triggered(bool)'), lambda x:self.metadata.setVisible(x))
self.connect(self.action_table_of_contents, SIGNAL('triggered(bool)'), lambda x:self.toc.setVisible(x))
self.connect(self.action_copy, SIGNAL('triggered(bool)'), self.copy)
@ -209,6 +249,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.connect(self.action_find_next, SIGNAL('triggered(bool)'),
lambda x:self.find(unicode(self.search.text()), True, repeat=True))
self.connect(self.action_back, SIGNAL('triggered(bool)'), self.back)
self.connect(self.action_bookmark, SIGNAL('triggered(bool)'), self.bookmark)
self.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward)
self.connect(self.action_preferences, SIGNAL('triggered(bool)'), lambda x: self.view.config(self))
self.connect(self.pos, SIGNAL('valueChanged(double)'), self.goto_page)
@ -216,13 +257,39 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
lambda x: self.goto_page(x/100.))
self.connect(self.search, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.find)
self.connect(self.toc, SIGNAL('clicked(QModelIndex)'), self.toc_clicked)
self.connect(self.reference, SIGNAL('goto(PyQt_PyObject)'), self.goto)
self.set_bookmarks([])
if pathtoebook is not None:
f = functools.partial(self.load_ebook, pathtoebook)
QTimer.singleShot(50, f)
self.view.setMinimumSize(100, 100)
self.splitter.setSizes([1, 300])
self.toc.setCursor(Qt.PointingHandCursor)
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu)
self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup)
def goto(self, ref):
if ref:
tokens = ref.split('.')
if len(tokens) > 1:
spine_index = int(tokens[0]) -1
if spine_index == self.current_index:
self.view.goto(ref)
else:
self.pending_reference = ref
self.load_path(self.iterator.spine[spine_index])
def goto_bookmark(self, bm):
m = bm[1].split('#')
if len(m) > 1:
spine_index, m = int(m[0]), m[1]
if self.current_index == spine_index:
self.view.goto_bookmark(m)
else:
self.pending_bookmark = bm
self.load_path(self.iterator.spine[spine_index])
def toc_clicked(self, index):
item = self.toc_model.itemFromIndex(index)
@ -263,8 +330,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
else:
self.load_path(page, pos=frac)
def open_ebook(self, checked):
files = choose_files(self, 'ebook viewer open dialog',
_('Choose ebook'),
@ -285,6 +350,16 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
self.set_page_number(frac)
def bookmark(self, *args):
title, ok = QInputDialog.getText(self, _('Add bookmark'), _('Enter title for bookmark:'))
title = unicode(title).strip()
if ok and title:
pos = self.view.bookmark()
bookmark = '%d#%s'%(self.current_index, pos)
self.iterator.add_bookmark((title, bookmark))
self.set_bookmarks(self.iterator.bookmarks)
def find(self, text, refinement, repeat=False):
if not text:
return
@ -308,7 +383,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if self.view.search(text):
self.scrolled(self.view.scroll_fraction)
def keyPressEvent(self, event):
if event.key() == Qt.Key_F3:
text = unicode(self.search.text())
@ -347,7 +421,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
index = self.iterator.spine.index(path)
except ValueError:
print path
return
return -1
self.current_page = self.iterator.spine[index]
self.current_index = index
self.set_page_number(self.view.scroll_fraction)
@ -357,6 +431,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if self.pending_anchor is not None:
self.view.scroll_to(self.pending_anchor)
self.pending_anchor = None
if self.pending_reference is not None:
self.view.goto(self.pending_reference)
self.pending_reference = None
if self.pending_bookmark is not None:
self.goto_bookmark(self.pending_bookmark)
self.pending_bookmark = None
return self.current_index
def load_path(self, path, pos=0.0):
self.open_progress_indicator(_('Laying out %s')%self.current_title)
@ -385,9 +466,27 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'):
getattr(self, o).setEnabled(False)
self.setCursor(Qt.BusyCursor)
def set_bookmarks(self, bookmarks):
menu = QMenu()
current_page = None
for bm in bookmarks:
if bm[0] == 'calibre_current_page_bookmark':
current_page = bm
else:
menu.addAction(bm[0], partial(self.goto_bookmark, bm))
self.action_bookmark.setMenu(menu)
self._menu = menu
return current_page
def save_current_position(self):
pos = self.view.bookmark()
bookmark = '%d#%s'%(self.current_index, pos)
self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark))
def load_ebook(self, pathtoebook):
if self.iterator is not None:
self.save_current_position()
self.iterator.__exit__()
self.iterator = EbookIterator(pathtoebook)
self.open_progress_indicator(_('Loading ebook...'))
@ -422,8 +521,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.vertical_scrollbar.setPageStep(100)
self.set_vscrollbar_value(1)
self.current_index = -1
self.next_document()
QApplication.instance().alert(self, 5000)
previous = self.set_bookmarks(self.iterator.bookmarks)
if previous is not None:
self.goto_bookmark(previous)
else:
self.next_document()
def set_vscrollbar_value(self, pagenum):
self.vertical_scrollbar.blockSignals(True)
@ -452,7 +555,9 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
def __exit__(self, *args):
if self.iterator is not None:
self.save_current_position()
self.iterator.__exit__(*args)
def config(defaults=None):
desc = _('Options to control the ebook viewer')

View File

@ -24,7 +24,7 @@
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeView" name="toc" />
<widget class="QWidget" name="" >
<widget class="QWidget" name="layoutWidget" >
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QWebView" name="view" >
@ -84,6 +84,9 @@
<addaction name="action_previous_page" />
<addaction name="action_next_page" />
<addaction name="separator" />
<addaction name="action_bookmark" />
<addaction name="action_reference_mode" />
<addaction name="separator" />
<addaction name="action_preferences" />
</widget>
<widget class="QToolBar" name="tool_bar2" >
@ -203,6 +206,24 @@
<string>Preferences</string>
</property>
</action>
<action name="action_reference_mode" >
<property name="icon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/lookfeel.svg</normaloff>:/images/lookfeel.svg</iconset>
</property>
<property name="text" >
<string>Reference Mode</string>
</property>
</action>
<action name="action_bookmark" >
<property name="icon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/bookmarks.svg</normaloff>:/images/bookmarks.svg</iconset>
</property>
<property name="text" >
<string>Bookmark</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>