mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Sync to trunk.
This commit is contained in:
commit
dae4b7cfa6
@ -19,6 +19,53 @@
|
|||||||
# new recipes:
|
# new recipes:
|
||||||
# - title:
|
# - title:
|
||||||
|
|
||||||
|
- version: 0.8.14
|
||||||
|
date: 2011-08-12
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Make the keyboard shortcuts used by the main calibre interface user customizable, via Preferences->Advanced->Keyboard"
|
||||||
|
type: major
|
||||||
|
|
||||||
|
- title: "When switching libraries, if the library no longer exists, give the user a chance to specify a new location for the library, in case it was moved, before forgetting it."
|
||||||
|
tickets: [822018]
|
||||||
|
|
||||||
|
- title: "Template language: Add strcat and strlen builtin functions."
|
||||||
|
tickets: [821935]
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "The various options to control how automerging works when adding books now also apply when copying a book from one library to another."
|
||||||
|
tickets: [822033]
|
||||||
|
|
||||||
|
- title: "Ebook viewer: Respond to key presses even when the book display area does not have keyboard focus"
|
||||||
|
|
||||||
|
- title: "Allow integer and float column values to go to -999999. -1000000 is the value of 'undefined'."
|
||||||
|
tickets: [821941]
|
||||||
|
|
||||||
|
- title: "Fix in calibre browser not working for the Open books store in Get Books."
|
||||||
|
tickets: [822359]
|
||||||
|
|
||||||
|
- title: "Fix regression in 0.8.13 that caused incorrect title/author for downloaded news if you turned off reading metadata from file contents in Preferences->Adding books"
|
||||||
|
|
||||||
|
- title: "Save to disk: When saving to a single directory, handle the case of the save to disk template containing path separators inside template expression correctly."
|
||||||
|
tickets: [821912]
|
||||||
|
|
||||||
|
- title: "Get Books: Always read metadata from the file contents, ignoring the setting in Preferences->Adding books"
|
||||||
|
|
||||||
|
- title: "Fix merge_metadata to not overwrite non-text fields ('bool', 'int', 'float', 'rating', 'datetime') that have a value of zero/false instead of None."
|
||||||
|
tickets: [821665]
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- The Independent
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Novinite"
|
||||||
|
author: Martin Tsanchev
|
||||||
|
|
||||||
|
- title: "Blog Escrevinhador"
|
||||||
|
author: Diniz Bortolotto
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- version: 0.8.13
|
- version: 0.8.13
|
||||||
date: 2011-08-05
|
date: 2011-08-05
|
||||||
|
|
||||||
@ -251,8 +298,8 @@
|
|||||||
- title: Techcrunch and Pecat
|
- title: Techcrunch and Pecat
|
||||||
author: Darko Miletic
|
author: Darko Miletic
|
||||||
|
|
||||||
- title: Vio Mundo, IDG Now and Tojolaco
|
- title: "Vio Mundo, IDG Now! and Tojolaco"
|
||||||
author: Diniz Bortoletto
|
author: Diniz Bortolotto
|
||||||
|
|
||||||
- title: Geek and Poke, Automatiseringgids IT
|
- title: Geek and Poke, Automatiseringgids IT
|
||||||
author: DrMerry
|
author: DrMerry
|
||||||
|
912
imgsrc/keyboard-prefs.svg
Normal file
912
imgsrc/keyboard-prefs.svg
Normal file
@ -0,0 +1,912 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
|
||||||
|
<svg
|
||||||
|
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"
|
||||||
|
version="1.0"
|
||||||
|
id="Livello_1"
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
viewBox="0 0 144 94"
|
||||||
|
overflow="visible"
|
||||||
|
enable-background="new 0 0 144 94"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.45+devel"
|
||||||
|
sodipodi:docname="preferences-desktop-keyboard.svgz"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
|
||||||
|
style="overflow:visible"><metadata
|
||||||
|
id="metadata224"><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><defs
|
||||||
|
id="defs222"><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_35_"
|
||||||
|
id="linearGradient2719"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
x1="72.000504"
|
||||||
|
y1="83.799797"
|
||||||
|
x2="72.000504"
|
||||||
|
y2="5.8003001" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#rect3941_1_"
|
||||||
|
id="linearGradient2721"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.474754,0,0,-0.465075,-255.92554,-542.49842)"
|
||||||
|
x1="780.77576"
|
||||||
|
y1="-1248.1824"
|
||||||
|
x2="780.81049"
|
||||||
|
y2="-1195.5962" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#rect3941_1_"
|
||||||
|
id="linearGradient2723"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.474754,0,0,-0.465075,-263.1733,-542.49842)"
|
||||||
|
x1="708.36438"
|
||||||
|
y1="-1248.1824"
|
||||||
|
x2="708.39648"
|
||||||
|
y2="-1195.5962" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#rect3941_1_"
|
||||||
|
id="linearGradient2725"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.474754,0,0,-0.465075,-270.42218,-542.49842)"
|
||||||
|
x1="635.95538"
|
||||||
|
y1="-1248.1824"
|
||||||
|
x2="635.9834"
|
||||||
|
y2="-1195.5962" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#rect3941_1_"
|
||||||
|
id="linearGradient2727"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.474754,0,0,-0.465075,-253.92268,-535.12325)"
|
||||||
|
x1="790.77502"
|
||||||
|
y1="-1324.245"
|
||||||
|
x2="790.81049"
|
||||||
|
y2="-1271.6509" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#rect3941_1_"
|
||||||
|
id="linearGradient2729"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.474754,0,0,-0.465075,-261.17157,-535.12325)"
|
||||||
|
x1="718.36609"
|
||||||
|
y1="-1324.245"
|
||||||
|
x2="718.39838"
|
||||||
|
y2="-1271.6509" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#rect3941_1_"
|
||||||
|
id="linearGradient2731"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.474754,0,0,-0.465075,-268.41933,-535.12325)"
|
||||||
|
x1="645.95471"
|
||||||
|
y1="-1324.245"
|
||||||
|
x2="645.9834"
|
||||||
|
y2="-1271.6509" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.36659993"
|
||||||
|
width="1.7331999"
|
||||||
|
y="-0.17839379"
|
||||||
|
height="1.3567876"
|
||||||
|
id="filter3416"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.51430916"
|
||||||
|
id="feGaussianBlur3418" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.36972603"
|
||||||
|
width="1.7394521"
|
||||||
|
y="-0.17766281"
|
||||||
|
height="1.3553256"
|
||||||
|
id="filter3424"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.51984026"
|
||||||
|
id="feGaussianBlur3426" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.22179123"
|
||||||
|
width="1.4435825"
|
||||||
|
y="-0.10660794"
|
||||||
|
height="1.2132159"
|
||||||
|
id="filter3444"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.31193415"
|
||||||
|
id="feGaussianBlur3446" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.21995996"
|
||||||
|
width="1.4399199"
|
||||||
|
y="-0.10703628"
|
||||||
|
height="1.2140726"
|
||||||
|
id="filter3448"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.30858549"
|
||||||
|
id="feGaussianBlur3450" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.22183562"
|
||||||
|
width="1.4436712"
|
||||||
|
y="-0.10659768"
|
||||||
|
height="1.2131954"
|
||||||
|
id="filter3452"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.31190415"
|
||||||
|
id="feGaussianBlur3454" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.21995996"
|
||||||
|
width="1.4399199"
|
||||||
|
y="-0.10703628"
|
||||||
|
height="1.2140726"
|
||||||
|
id="filter3456"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.30858549"
|
||||||
|
id="feGaussianBlur3458" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.22179123"
|
||||||
|
width="1.4435825"
|
||||||
|
y="-0.10660794"
|
||||||
|
height="1.2132159"
|
||||||
|
id="filter3460"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.31193415"
|
||||||
|
id="feGaussianBlur3462" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.21991603"
|
||||||
|
width="1.4398321"
|
||||||
|
y="-0.10704668"
|
||||||
|
height="1.2140934"
|
||||||
|
id="filter3464"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.30861549"
|
||||||
|
id="feGaussianBlur3466" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.22179123"
|
||||||
|
width="1.4435825"
|
||||||
|
y="-0.10660794"
|
||||||
|
height="1.2132159"
|
||||||
|
id="filter3468"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.31193415"
|
||||||
|
id="feGaussianBlur3470" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.21995996"
|
||||||
|
width="1.4399199"
|
||||||
|
y="-0.10703628"
|
||||||
|
height="1.2140726"
|
||||||
|
id="filter3472"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.30858549"
|
||||||
|
id="feGaussianBlur3474" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.22183562"
|
||||||
|
width="1.4436712"
|
||||||
|
y="-0.10659768"
|
||||||
|
height="1.2131954"
|
||||||
|
id="filter3476"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.31190415"
|
||||||
|
id="feGaussianBlur3478" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.21995996"
|
||||||
|
width="1.4399199"
|
||||||
|
y="-0.10703628"
|
||||||
|
height="1.2140726"
|
||||||
|
id="filter3484"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.30858549"
|
||||||
|
id="feGaussianBlur3486" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.02891983"
|
||||||
|
width="1.0578397"
|
||||||
|
y="-0.14107949"
|
||||||
|
height="1.282159"
|
||||||
|
id="filter3492"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.33591005"
|
||||||
|
id="feGaussianBlur3494" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.02891983"
|
||||||
|
width="1.0578397"
|
||||||
|
y="-0.14107949"
|
||||||
|
height="1.282159"
|
||||||
|
id="filter3496"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.33591005"
|
||||||
|
id="feGaussianBlur3498" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.028919654"
|
||||||
|
width="1.0578393"
|
||||||
|
y="-0.14108369"
|
||||||
|
height="1.2821674"
|
||||||
|
id="filter3500"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.33592005"
|
||||||
|
id="feGaussianBlur3502" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.02891983"
|
||||||
|
width="1.0578397"
|
||||||
|
y="-0.14107949"
|
||||||
|
height="1.282159"
|
||||||
|
id="filter3504"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.33591005"
|
||||||
|
id="feGaussianBlur3506" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.02891983"
|
||||||
|
width="1.0578397"
|
||||||
|
y="-0.14107949"
|
||||||
|
height="1.282159"
|
||||||
|
id="filter3508"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.33591005"
|
||||||
|
id="feGaussianBlur3510" /></filter><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
x="-0.02891983"
|
||||||
|
width="1.0578397"
|
||||||
|
y="-0.14107949"
|
||||||
|
height="1.282159"
|
||||||
|
id="filter3512"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.33591005"
|
||||||
|
id="feGaussianBlur3514" /></filter><linearGradient
|
||||||
|
id="XMLID_31_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="69.333504"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="69.333504"
|
||||||
|
y2="9.7958002"
|
||||||
|
xlink:href="#XMLID_32_">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop169" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#DDDDDD"
|
||||||
|
id="stop171" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
id="XMLID_32_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="106.334"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="106.334"
|
||||||
|
y2="9.7958002">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop186" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#DDDDDD"
|
||||||
|
id="stop188" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
id="XMLID_30_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="31.742201"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="31.742201"
|
||||||
|
y2="9.7958002">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop152" /><stop
|
||||||
|
id="stop3366"
|
||||||
|
style="stop-color:#eaeaea;stop-opacity:1;"
|
||||||
|
offset="0.68235296" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#c8c8c8;stop-opacity:1;"
|
||||||
|
id="stop154" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient2945"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="69.333504"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="69.333504"
|
||||||
|
y2="9.7958002" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient2947"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="106.334"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="106.334"
|
||||||
|
y2="9.7958002" /><linearGradient
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="XMLID_28_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="38.033699"
|
||||||
|
y1="55.649399"
|
||||||
|
x2="38.033699"
|
||||||
|
y2="47.795502"
|
||||||
|
spreadMethod="pad">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop118" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#DDDDDD"
|
||||||
|
id="stop120" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
id="XMLID_29_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="75.333"
|
||||||
|
y1="55.649399"
|
||||||
|
x2="75.333"
|
||||||
|
y2="47.795502">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop135" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#DDDDDD"
|
||||||
|
id="stop137" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
id="XMLID_33_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="112.334"
|
||||||
|
y1="55.649399"
|
||||||
|
x2="112.334"
|
||||||
|
y2="47.795502">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop203" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#DDDDDD"
|
||||||
|
id="stop205" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient2979"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
spreadMethod="pad"
|
||||||
|
x1="38.033699"
|
||||||
|
y1="55.649399"
|
||||||
|
x2="38.033699"
|
||||||
|
y2="47.795502" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient2981"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="75.333"
|
||||||
|
y1="55.649399"
|
||||||
|
x2="75.333"
|
||||||
|
y2="47.795502" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient2983"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="112.334"
|
||||||
|
y1="55.649399"
|
||||||
|
x2="112.334"
|
||||||
|
y2="47.795502" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3372"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.3179705"
|
||||||
|
id="feGaussianBlur3374" /></filter><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient3378"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="106.334"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="106.334"
|
||||||
|
y2="9.7958002" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_30_"
|
||||||
|
id="linearGradient3380"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="31.742201"
|
||||||
|
y1="17.6504"
|
||||||
|
x2="31.742201"
|
||||||
|
y2="9.7958002" /></defs><sodipodi:namedview
|
||||||
|
inkscape:window-height="696"
|
||||||
|
inkscape:window-width="998"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
guidetolerance="10.0"
|
||||||
|
gridtolerance="10.0"
|
||||||
|
objecttolerance="10.0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
id="base"
|
||||||
|
height="128px"
|
||||||
|
width="128px"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="65.761733"
|
||||||
|
inkscape:cy="68.182683"
|
||||||
|
inkscape:window-x="26"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:current-layer="g2620" />
|
||||||
|
<filter
|
||||||
|
id="AI_Sfocatura_2">
|
||||||
|
<feGaussianBlur
|
||||||
|
stdDeviation="2"
|
||||||
|
id="feGaussianBlur4" />
|
||||||
|
</filter>
|
||||||
|
<path
|
||||||
|
display="none"
|
||||||
|
d="M 89.758,1.8 C 88.983,1.8 88.229,1.945 87.501,2.17 C 86.774,1.945 86.022,1.8 85.248,1.8 L 52.752,1.8 C 51.978,1.8 51.226,1.945 50.499,2.17 C 49.772,1.944 49.02,1.8 48.245,1.8 L 15.755,1.8 C 11.479,1.8 8,5.275 8,9.546 L 8,42.054 C 8,45.72 10.57,48.783 14,49.582 C 14,53.16 14,80.057 14,80.057 C 14,84.327 17.478,87.8 21.752,87.8 L 54.248,87.8 C 55.022,87.8 55.774,87.655 56.5,87.431 C 57.227,87.656 57.979,87.8 58.754,87.8 L 91.241,87.8 C 92.017,87.8 92.77,87.655 93.498,87.431 C 94.225,87.655 94.977,87.8 95.751,87.8 L 128.241,87.8 C 132.518,87.8 135.999,84.326 135.999,80.057 L 135.999,47.546 C 135.999,43.881 133.43,40.818 129.999,40.019 C 129.999,36.442 129.999,9.546 129.999,9.546 C 129.999,5.275 126.521,1.8 122.247,1.8 L 89.758,1.8 L 89.758,1.8 z"
|
||||||
|
id="path6"
|
||||||
|
style="fill:#ff00bf;display:none" />
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_35_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="72.000504"
|
||||||
|
y1="83.799797"
|
||||||
|
x2="72.000504"
|
||||||
|
y2="5.8003001">
|
||||||
|
<stop
|
||||||
|
offset="0.0059"
|
||||||
|
style="stop-color:#888888"
|
||||||
|
id="stop13" />
|
||||||
|
<stop
|
||||||
|
offset="0.5"
|
||||||
|
style="stop-color:#555555"
|
||||||
|
id="stop15" />
|
||||||
|
<stop
|
||||||
|
offset="0.54"
|
||||||
|
style="stop-color:#888888"
|
||||||
|
id="stop17" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#555555"
|
||||||
|
id="stop19" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="rect3785_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="780.81049"
|
||||||
|
y1="-1240.9404"
|
||||||
|
x2="780.81049"
|
||||||
|
y2="-1195.5962"
|
||||||
|
gradientTransform="matrix(0.422,0,0,-0.4134,-223.4874,-472.1986)">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#BEBEBE"
|
||||||
|
id="stop24" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#EDEDED"
|
||||||
|
id="stop26" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="rect3791_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="708.39648"
|
||||||
|
y1="-1240.9404"
|
||||||
|
x2="708.39648"
|
||||||
|
y2="-1195.5962"
|
||||||
|
gradientTransform="matrix(0.422,0,0,-0.4134,-229.9298,-472.1986)">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#BEBEBE"
|
||||||
|
id="stop38" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#EDEDED"
|
||||||
|
id="stop40" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="rect3797_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="635.9834"
|
||||||
|
y1="-1240.9404"
|
||||||
|
x2="635.9834"
|
||||||
|
y2="-1195.5962"
|
||||||
|
gradientTransform="matrix(0.422,0,0,-0.4134,-236.3732,-472.1986)">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#BEBEBE"
|
||||||
|
id="stop52" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#EDEDED"
|
||||||
|
id="stop54" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="rect3929_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="790.81049"
|
||||||
|
y1="-1316.9951"
|
||||||
|
x2="790.81049"
|
||||||
|
y2="-1271.6509"
|
||||||
|
gradientTransform="matrix(0.422,0,0,-0.4134,-221.7071,-465.6429)">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#BEBEBE"
|
||||||
|
id="stop66" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#EDEDED"
|
||||||
|
id="stop68" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="rect3935_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="718.39838"
|
||||||
|
y1="-1316.9951"
|
||||||
|
x2="718.39838"
|
||||||
|
y2="-1271.6509"
|
||||||
|
gradientTransform="matrix(0.422,0,0,-0.4134,-228.1505,-465.6429)">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#BEBEBE"
|
||||||
|
id="stop80" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#EDEDED"
|
||||||
|
id="stop82" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="rect3941_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="645.9834"
|
||||||
|
y1="-1316.9951"
|
||||||
|
x2="645.9834"
|
||||||
|
y2="-1271.6509"
|
||||||
|
gradientTransform="matrix(0.422,0,0,-0.4134,-234.5929,-465.6429)">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#e4e4e4;stop-opacity:1;"
|
||||||
|
id="stop94" /><stop
|
||||||
|
id="stop3516"
|
||||||
|
style="stop-color:#bebebe;stop-opacity:1;"
|
||||||
|
offset="0.18012393" /><stop
|
||||||
|
offset="0.61417598"
|
||||||
|
style="stop-color:#cdcdcd;stop-opacity:1;"
|
||||||
|
id="stop3376" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#b3b3b3;stop-opacity:1;"
|
||||||
|
id="stop96" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
id="g8"
|
||||||
|
style="opacity:0.8;filter:url(#AI_Sfocatura_2)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-9.0001128,-12.4)">
|
||||||
|
<path
|
||||||
|
d="M 128.242,45.8 L 125.547,45.8 C 125.826,45.276 126,44.688 126,44.054 L 126,11.546 C 126,9.481 124.316,7.8 122.248,7.8 L 89.758,7.8 C 88.907,7.8 88.131,8.093 87.501,8.57 C 86.872,8.093 86.097,7.8 85.248,7.8 L 52.752,7.8 C 51.903,7.8 51.128,8.093 50.499,8.57 C 49.869,8.094 49.094,7.8 48.245,7.8 L 15.755,7.8 C 13.685,7.8 12,9.48 12,11.546 L 12,44.054 C 12,46.119 13.685,47.8 15.755,47.8 L 18.453,47.8 C 18.173,48.323 18,48.912 18,49.546 L 18,82.057 C 18,84.121 19.683,85.8 21.752,85.8 L 54.248,85.8 C 55.097,85.8 55.872,85.507 56.5,85.031 C 57.13,85.507 57.905,85.8 58.754,85.8 L 91.241,85.8 C 92.092,85.8 92.868,85.507 93.498,85.03 C 94.127,85.507 94.902,85.8 95.751,85.8 L 128.241,85.8 C 130.313,85.8 131.999,84.121 131.999,82.057 L 131.999,49.546 C 132,47.48 130.314,45.8 128.242,45.8 z"
|
||||||
|
id="path10"
|
||||||
|
style="opacity:0.8" />
|
||||||
|
</g><g
|
||||||
|
id="g2620"
|
||||||
|
transform="translate(-4.5,0)"><path
|
||||||
|
style="fill:url(#linearGradient2721);fill-opacity:1"
|
||||||
|
d="M 96.478481,-2.5 L 133.03004,-2.5 C 134.11905,-2.5 135.00105,-1.621375 135.00105,-0.53575 L 135.00105,36.03575 C 135.00105,37.121374 134.11905,38 133.03004,38 L 96.478481,38 C 95.382722,38 94.500715,37.121374 94.500715,36.03575 L 94.500715,-0.53575 C 94.500715,-1.621375 95.382722,-2.5 96.478481,-2.5 z"
|
||||||
|
id="rect3785" /><g
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
nodetypes="cccsssssscccc"
|
||||||
|
id="path3787">
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-240.4975,-567.9111)"
|
||||||
|
r="40.036301"
|
||||||
|
cy="-971.75677"
|
||||||
|
cx="527.62299"
|
||||||
|
id="XMLID_17_">
|
||||||
|
<stop
|
||||||
|
id="stop31"
|
||||||
|
style="stop-color:#E8E8E8"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop33"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
offset="1" />
|
||||||
|
</radialGradient>
|
||||||
|
<path
|
||||||
|
style="fill:url(#XMLID_17_)"
|
||||||
|
id="path35"
|
||||||
|
d="M 91.145,9.856 L 89.924,35.848 L 89.852,37.041 C 91.107,37.752 92.344,38.38 93.661,38.908 C 93.683,38.914 93.717,38.9 93.738,38.908 C 97.663,40.465 101.819,41.179 105.966,41.145 C 106.662,41.14 107.359,41.12 108.055,41.072 C 109.436,40.973 110.852,40.816 112.223,40.55 C 113.593,40.285 114.914,39.932 116.252,39.504 C 118.259,38.861 120.26,38.043 122.15,37.042 L 122.077,35.849 L 120.928,9.857 L 91.145,9.857 L 91.145,9.856 z" />
|
||||||
|
</g><path
|
||||||
|
style="fill:url(#linearGradient2723);fill-opacity:1"
|
||||||
|
d="M 54.846383,-2.5 L 91.404689,-2.5 C 92.492573,-2.5 93.375705,-1.621375 93.375705,-0.53575 L 93.375705,36.03575 C 93.375705,37.121374 92.492573,38 91.404689,38 L 54.846383,38 C 53.758499,38 52.875367,37.121374 52.875367,36.03575 L 52.875367,-0.53575 C 52.875367,-1.621375 53.758499,-2.5 54.846383,-2.5 z"
|
||||||
|
id="rect3791" /><g
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
nodetypes="cccsssssscccc"
|
||||||
|
id="path3793">
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-247.2058,-567.9111)"
|
||||||
|
r="40.036098"
|
||||||
|
cy="-971.12653"
|
||||||
|
cx="480.29791"
|
||||||
|
id="XMLID_19_">
|
||||||
|
<stop
|
||||||
|
id="stop45"
|
||||||
|
style="stop-color:#E8E8E8"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop47"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
offset="1" />
|
||||||
|
</radialGradient>
|
||||||
|
<path
|
||||||
|
style="fill:url(#XMLID_19_)"
|
||||||
|
id="path49"
|
||||||
|
d="M 54.145,9.856 L 52.921,35.848 L 52.849,37.041 C 54.109,37.752 55.348,38.38 56.663,38.908 C 56.682,38.914 56.715,38.9 56.734,38.908 C 60.662,40.465 64.821,41.179 68.962,41.145 C 69.658,41.14 70.355,41.12 71.048,41.072 C 72.433,40.973 73.848,40.816 75.218,40.55 C 76.589,40.285 77.909,39.932 79.253,39.504 C 81.254,38.861 83.255,38.043 85.147,37.042 L 85.08,35.849 L 83.927,9.856 L 54.145,9.856 z" />
|
||||||
|
</g><path
|
||||||
|
style="fill:url(#linearGradient2725);fill-opacity:1"
|
||||||
|
d="M 13.224411,-2.5 L 49.775966,-2.5 C 50.867225,-2.5 51.750357,-1.621375 51.750357,-0.53575 L 51.750357,36.03575 C 51.750357,37.121374 50.867225,38 49.775966,38 L 13.224411,38 C 12.133151,38 11.250019,37.121374 11.250019,36.03575 L 11.250019,-0.53575 C 11.250019,-1.621375 12.133151,-2.5 13.224411,-2.5 z"
|
||||||
|
id="rect3797" /><g
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
nodetypes="cccsssssscccc"
|
||||||
|
id="path3799">
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.6403,-8.5e-3,-7.7e-3,-0.6279,-253.8748,-567.9111)"
|
||||||
|
r="40.033199"
|
||||||
|
cy="-970.48578"
|
||||||
|
cx="432.98141"
|
||||||
|
id="XMLID_24_">
|
||||||
|
<stop
|
||||||
|
id="stop59"
|
||||||
|
style="stop-color:#E8E8E8"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop61"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
offset="1" />
|
||||||
|
</radialGradient>
|
||||||
|
<path
|
||||||
|
style="fill:url(#XMLID_24_)"
|
||||||
|
id="path63"
|
||||||
|
d="M 17.146,9.856 L 15.924,35.848 L 15.852,37.041 C 17.109,37.752 18.348,38.38 19.663,38.908 C 19.682,38.914 19.716,38.9 19.735,38.908 C 23.662,40.465 27.821,41.179 31.964,41.145 C 32.657,41.14 33.357,41.12 34.05,41.072 C 35.435,40.973 36.85,40.816 38.22,40.55 C 39.591,40.285 40.909,39.932 42.249,39.504 C 44.258,38.861 46.254,38.043 48.146,37.042 L 48.075,35.849 L 46.925,9.857 L 17.146,9.857 L 17.146,9.856 z" />
|
||||||
|
</g><path
|
||||||
|
style="fill:url(#linearGradient2727);fill-opacity:1"
|
||||||
|
d="M 103.22179,40.25 L 139.77334,40.25 C 140.8691,40.25 141.75111,41.128625 141.75111,42.21425 L 141.75111,78.789125 C 141.75111,79.87475 140.8691,80.75 139.77334,80.75 L 103.22179,80.75 C 102.13278,80.75 101.25077,79.873625 101.25077,78.789125 L 101.25077,42.21425 C 101.25077,41.128625 102.13278,40.25 103.22179,40.25 z"
|
||||||
|
id="rect3929" /><g
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
nodetypes="cccsssssscccc"
|
||||||
|
id="path3931">
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-238.6489,-561.7972)"
|
||||||
|
r="40.036499"
|
||||||
|
cy="-1022.5366"
|
||||||
|
cx="533.49512"
|
||||||
|
id="XMLID_25_">
|
||||||
|
<stop
|
||||||
|
id="stop73"
|
||||||
|
style="stop-color:#E8E8E8"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop75"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
offset="1" />
|
||||||
|
</radialGradient>
|
||||||
|
<path
|
||||||
|
style="fill:url(#XMLID_25_)"
|
||||||
|
id="path77"
|
||||||
|
d="M 97.145,47.856 L 95.918,73.85 L 95.851,75.04 C 97.113,75.751 98.35,76.383 99.665,76.908 C 99.682,76.914 99.716,76.902 99.731,76.908 C 103.661,78.47 107.824,79.181 111.963,79.148 C 112.654,79.142 113.357,79.125 114.047,79.075 C 115.434,78.979 116.849,78.821 118.221,78.55 C 119.591,78.29 120.912,77.936 122.25,77.506 C 124.262,76.863 126.252,76.045 128.147,75.04 L 128.075,73.85 L 126.926,47.857 L 97.145,47.857 L 97.145,47.856 z" />
|
||||||
|
</g><path
|
||||||
|
style="fill:url(#linearGradient2729);fill-opacity:1"
|
||||||
|
d="M 61.599815,40.25 L 98.148,40.25 C 99.24263,40.25 100.12576,41.128625 100.12576,42.21425 L 100.12576,78.789125 C 100.12576,79.87475 99.24263,80.75 98.148,80.75 L 61.599815,80.75 C 60.511931,80.75 59.625423,79.873625 59.625423,78.789125 L 59.625423,42.21425 C 59.625423,41.128625 60.511931,40.25 61.599815,40.25 z"
|
||||||
|
id="rect3935" /><g
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
nodetypes="cccsssssscccc"
|
||||||
|
id="path3937">
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.6403,-8.5e-3,-7.7e-3,-0.6279,-245.3333,-561.7972)"
|
||||||
|
r="40.038898"
|
||||||
|
cy="-1021.9087"
|
||||||
|
cx="486.17969"
|
||||||
|
id="XMLID_26_">
|
||||||
|
<stop
|
||||||
|
id="stop87"
|
||||||
|
style="stop-color:#E8E8E8"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop89"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
offset="1" />
|
||||||
|
</radialGradient>
|
||||||
|
<path
|
||||||
|
style="fill:url(#XMLID_26_)"
|
||||||
|
id="path91"
|
||||||
|
d="M 60.145,47.856 L 58.923,73.85 L 58.852,75.04 C 60.112,75.751 61.347,76.383 62.663,76.908 C 62.684,76.914 62.715,76.902 62.735,76.908 C 66.662,78.47 70.824,79.181 74.964,79.148 C 75.654,79.142 76.357,79.125 77.048,79.075 C 78.441,78.979 79.856,78.821 81.227,78.55 C 82.597,78.29 83.913,77.936 85.25,77.506 C 87.263,76.863 89.258,76.045 91.153,75.04 L 91.076,73.85 L 89.926,47.857 L 60.145,47.857 L 60.145,47.856 z" />
|
||||||
|
</g><path
|
||||||
|
style="fill:url(#linearGradient2731)"
|
||||||
|
d="M 19.971092,40.25 L 56.529397,40.25 C 57.617282,40.25 58.500414,41.128625 58.500414,42.21425 L 58.500414,78.789125 C 58.500414,79.87475 57.617282,80.75 56.529397,80.75 L 19.971092,80.75 C 18.883208,80.75 18.000075,79.873625 18.000075,78.789125 L 18.000075,42.21425 C 18.000075,41.128625 18.883208,40.25 19.971092,40.25 z"
|
||||||
|
id="rect3941" /><g
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||||
|
nodetypes="cccsssssscccc"
|
||||||
|
id="path3943"
|
||||||
|
style="filter:url(#filter3372)">
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-252.0475,-561.7972)"
|
||||||
|
r="40.039001"
|
||||||
|
cy="-1021.27"
|
||||||
|
cx="438.85059"
|
||||||
|
id="XMLID_27_">
|
||||||
|
<stop
|
||||||
|
id="stop101"
|
||||||
|
style="stop-color:#E8E8E8"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop103"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
offset="0.5858" />
|
||||||
|
</radialGradient>
|
||||||
|
<path
|
||||||
|
style="fill:url(#XMLID_27_)"
|
||||||
|
id="path105"
|
||||||
|
d="M 23.146,47.856 L 21.921,73.85 L 21.849,75.04 C 23.11,75.751 24.348,76.383 25.663,76.908 C 25.682,76.914 25.715,76.902 25.734,76.908 C 29.662,78.47 33.821,79.181 37.965,79.148 C 38.658,79.142 39.358,79.125 40.049,79.075 C 41.436,78.979 42.851,78.821 44.222,78.55 C 45.593,78.29 46.911,77.936 48.252,77.506 C 50.262,76.863 52.257,76.045 54.15,75.04 L 54.078,73.85 L 52.926,47.857 L 23.146,47.857 L 23.146,47.856 z" />
|
||||||
|
</g><g
|
||||||
|
style="filter:url(#filter3460)"
|
||||||
|
id="g109"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 22.213,81.018 L 23.666,76.231 C 23.666,76.231 23.743,74.036 21.849,75.041 L 21.921,76.231 L 20.291,81.018 C 20.605,81.49 21.142,81.8 21.752,81.8 L 23.515,81.8 C 22.971,81.8 22.493,81.489 22.213,81.018 z"
|
||||||
|
id="path111"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><g
|
||||||
|
style="filter:url(#filter3464)"
|
||||||
|
id="g113"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 54.079,76.23 L 54.151,75.04 C 52.89,74.329 52.333,76.23 52.333,76.23 L 53.779,81.033 C 53.498,81.496 53.023,81.8 52.485,81.8 L 54.249,81.8 C 54.853,81.8 55.386,81.496 55.701,81.033 L 54.079,76.23 z"
|
||||||
|
id="path115"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><path
|
||||||
|
d="M 24.1,48.856 C 23.693,57.45 52.262,55.434 51.971,48.856 C 42.68,48.856 33.39,48.856 24.1,48.856"
|
||||||
|
id="path122"
|
||||||
|
style="fill:url(#linearGradient2979);fill-opacity:1;filter:url(#filter3500)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-41.815049)" /><g
|
||||||
|
style="filter:url(#filter3468)"
|
||||||
|
id="g126"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 59.513,81.018 L 60.966,76.231 C 60.966,76.231 61.043,74.036 59.149,75.041 L 59.221,76.231 L 57.591,81.018 C 57.905,81.49 58.442,81.8 59.052,81.8 L 60.815,81.8 C 60.271,81.8 59.792,81.489 59.513,81.018 z"
|
||||||
|
id="path128"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><g
|
||||||
|
style="filter:url(#filter3472)"
|
||||||
|
id="g130"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 91.379,76.23 L 91.451,75.04 C 90.189,74.329 89.633,76.23 89.633,76.23 L 91.078,81.033 C 90.797,81.496 90.322,81.8 89.784,81.8 L 91.548,81.8 C 92.152,81.8 92.685,81.496 93,81.033 L 91.379,76.23 z"
|
||||||
|
id="path132"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><path
|
||||||
|
d="M 61.399,48.856 C 60.992,57.45 89.56,55.434 89.269,48.856 C 79.98,48.856 70.689,48.856 61.399,48.856"
|
||||||
|
id="path139"
|
||||||
|
style="fill:url(#linearGradient2981);filter:url(#filter3496)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-41.815049)" /><g
|
||||||
|
id="g143"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 15.921,43.018 L 17.374,38.231 C 17.374,38.231 17.451,36.036 15.557,37.041 L 15.629,38.231 L 14,43.018 C 14.314,43.49 14.851,43.8 15.461,43.8 L 17.224,43.8 C 16.68,43.8 16.201,43.489 15.921,43.018 z"
|
||||||
|
id="path145"
|
||||||
|
style="fill:#ffffff;filter:url(#filter3424)" />
|
||||||
|
</g><path
|
||||||
|
id="path149"
|
||||||
|
d="M 47.788,38.23 L 47.86,37.04 C 46.598,36.329 46.042,38.23 46.042,38.23 L 47.487,43.033 C 47.206,43.496 46.731,43.8 46.193,43.8 L 47.957,43.8 C 48.561,43.8 49.094,43.496 49.409,43.033 L 47.788,38.23 z"
|
||||||
|
style="fill:#ffffff;filter:url(#filter3416)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)" /><path
|
||||||
|
d="M 17.808,10.856 C 17.401,19.45 45.969,17.434 45.678,10.856 C 36.389,10.856 27.098,10.856 17.808,10.856"
|
||||||
|
id="path156"
|
||||||
|
style="fill:url(#linearGradient3380);fill-opacity:1;filter:url(#filter3504)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-17.235103)" /><g
|
||||||
|
style="filter:url(#filter3444)"
|
||||||
|
id="g160"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 53.513,43.018 L 54.966,38.231 C 54.966,38.231 55.043,36.036 53.149,37.041 L 53.221,38.231 L 51.591,43.018 C 51.905,43.49 52.442,43.8 53.052,43.8 L 54.815,43.8 C 54.271,43.8 53.792,43.489 53.513,43.018 z"
|
||||||
|
id="path162"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><g
|
||||||
|
style="filter:url(#filter3448)"
|
||||||
|
id="g164"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 85.379,38.23 L 85.451,37.04 C 84.189,36.329 83.633,38.23 83.633,38.23 L 85.078,43.033 C 84.797,43.496 84.322,43.8 83.784,43.8 L 85.548,43.8 C 86.152,43.8 86.685,43.496 87,43.033 L 85.379,38.23 z"
|
||||||
|
id="path166"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><path
|
||||||
|
d="M 55.399,10.856 C 54.992,19.45 83.56,17.434 83.269,10.856 C 73.98,10.856 64.689,10.856 55.399,10.856"
|
||||||
|
id="path173"
|
||||||
|
style="fill:url(#linearGradient2945);fill-opacity:1;filter:url(#filter3508)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-17.235103)" /><g
|
||||||
|
style="filter:url(#filter3452)"
|
||||||
|
id="g177"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 90.513,43.018 L 91.966,38.231 C 91.966,38.231 92.043,36.036 90.149,37.041 L 90.221,38.231 L 88.592,43.018 C 88.905,43.49 89.442,43.8 90.053,43.8 L 91.815,43.8 C 91.271,43.8 90.793,43.489 90.513,43.018 z"
|
||||||
|
id="path179"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><g
|
||||||
|
style="filter:url(#filter3456)"
|
||||||
|
id="g181"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 122.379,38.23 L 122.451,37.04 C 121.189,36.329 120.633,38.23 120.633,38.23 L 122.078,43.033 C 121.797,43.496 121.322,43.8 120.784,43.8 L 122.548,43.8 C 123.152,43.8 123.685,43.496 124,43.033 L 122.379,38.23 z"
|
||||||
|
id="path183"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><path
|
||||||
|
d="M 92.399,10.856 C 91.992,19.45 120.56,17.434 120.269,10.856 C 110.98,10.856 101.689,10.856 92.399,10.856"
|
||||||
|
id="path190"
|
||||||
|
style="fill:url(#linearGradient3378);fill-opacity:1;filter:url(#filter3512)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-17.235103)" /><g
|
||||||
|
style="filter:url(#filter3476)"
|
||||||
|
id="g194"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 96.513,81.018 L 97.966,76.231 C 97.966,76.231 98.043,74.036 96.149,75.041 L 96.221,76.231 L 94.592,81.018 C 94.905,81.49 95.442,81.8 96.053,81.8 L 97.815,81.8 C 97.271,81.8 96.793,81.489 96.513,81.018 z"
|
||||||
|
id="path196"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><g
|
||||||
|
style="filter:url(#filter3484)"
|
||||||
|
id="g198"
|
||||||
|
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||||
|
<path
|
||||||
|
d="M 128.379,76.23 L 128.451,75.04 C 127.189,74.329 126.633,76.23 126.633,76.23 L 128.078,81.033 C 127.797,81.496 127.322,81.8 126.784,81.8 L 128.548,81.8 C 129.152,81.8 129.685,81.496 130,81.033 L 128.379,76.23 z"
|
||||||
|
id="path200"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</g><path
|
||||||
|
d="M 98.399,48.856 C 97.992,57.45 126.56,55.434 126.269,48.856 C 116.98,48.856 107.689,48.856 98.399,48.856"
|
||||||
|
id="path207"
|
||||||
|
style="fill:url(#linearGradient2983);filter:url(#filter3492)"
|
||||||
|
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-41.815049)" /><path
|
||||||
|
style="fill:#323232"
|
||||||
|
id="path209"
|
||||||
|
d="M 21.99503,12.125 L 19.778139,23.842759 C 19.61869,24.635599 19.540614,25.268992 19.540614,25.848502 C 19.540614,27.088895 20.172912,27.749779 21.150501,27.749779 C 22.470079,27.749779 23.261827,26.799689 23.788558,23.974716 L 26.058233,12.125 L 28.723782,12.125 L 26.533281,23.632728 C 25.715142,27.960908 24.079966,30.125 20.832702,30.125 C 18.27382,30.125 16.875066,28.645985 16.875066,26.112415 C 16.875066,25.399849 16.981732,24.502542 17.166473,23.47328 L 19.330582,12.125 L 21.99503,12.125 L 21.99503,12.125 z" /><path
|
||||||
|
style="fill:#323232"
|
||||||
|
id="path211"
|
||||||
|
d="M 64.643632,12.125 L 61.198712,30.125 L 58.500414,30.125 L 61.918629,12.125 L 64.643632,12.125 L 64.643632,12.125 z" /><path
|
||||||
|
style="fill:#323232"
|
||||||
|
id="path213"
|
||||||
|
d="M 111.34148,17.759442 C 111.34148,21.280968 110.11546,26.238278 107.82102,28.532705 C 106.7515,29.602206 105.57766,30.125 104.19402,30.125 C 100.56813,30.125 100.12576,26.368704 100.12576,24.464472 C 100.12576,20.995117 101.35178,16.037807 103.77666,13.664038 C 104.79508,12.672793 105.99394,12.125 107.35038,12.125 C 110.95129,12.125 111.34148,15.62044 111.34148,17.759442 z M 105.60374,15.124819 C 103.82883,16.899712 102.83867,21.985274 102.83867,24.593812 C 102.83867,25.793739 102.94301,27.802314 104.58532,27.802314 C 105.05486,27.802314 105.4983,27.567546 105.91676,27.15018 C 107.76775,25.270946 108.60358,19.585419 108.60358,17.629015 C 108.60358,16.089978 108.47315,14.446599 106.98627,14.446599 C 106.48956,14.447686 106.0211,14.681367 105.60374,15.124819 z" /><path
|
||||||
|
style="fill:#323232"
|
||||||
|
id="path215"
|
||||||
|
d="M 30.011882,54.875 L 32.678531,54.875 L 30.223015,67.727587 C 29.431267,71.819347 27.794991,72.875 25.182225,72.875 C 24.522435,72.875 23.91543,72.768335 23.625122,72.663868 L 24.073779,70.288648 C 24.443261,70.394213 24.785251,70.420605 25.287791,70.420605 C 26.450121,70.420605 27.161593,69.681647 27.531075,67.728687 L 30.011882,54.875 z" /><path
|
||||||
|
style="fill:#323232"
|
||||||
|
id="path217"
|
||||||
|
d="M 68.668474,54.876125 L 71.338789,54.876125 L 69.683194,63.422176 L 69.763303,63.422176 C 70.350773,62.407464 70.804726,61.659781 71.285383,60.885397 L 75.156227,54.877238 L 78.146981,54.877238 L 72.512615,62.72901 L 74.782383,72.876125 L 71.978553,72.876125 L 70.428657,64.732844 L 69.148018,66.387312 L 67.895195,72.875012 L 65.25047,72.875012 L 68.668474,54.876125 z" /><path
|
||||||
|
style="fill:#323232"
|
||||||
|
id="path219"
|
||||||
|
d="M 110.29425,54.873875 L 113.0183,54.873875 L 110.02717,70.523727 L 114.67522,70.523727 L 114.22121,72.873875 L 106.87582,72.873875 L 110.29425,54.873875 z" /></g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 34 KiB |
@ -30,8 +30,14 @@ class CnetNews(BasicNewsRecipe):
|
|||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div', attrs={'id':'tweetmemeAndFacebook'})
|
dict(name='div', attrs={'id':'tweetmemeAndFacebook'})
|
||||||
,dict(name='ul', attrs={'class':'contentTools'})
|
,dict(name='ul', attrs={'class':'contentTools'})
|
||||||
|
,dict(name='aside', attrs={'id':'filed'})
|
||||||
|
,dict(name='div', attrs={'class':'postLinks'})
|
||||||
|
,dict(name='span', attrs={'class':'shareButton'})
|
||||||
|
,dict(name='span', attrs={'class':'printButton'})
|
||||||
|
,dict(name='span', attrs={'class':'emailButton'})
|
||||||
|
,dict(name='div', attrs={'class':'editorBio'})
|
||||||
]
|
]
|
||||||
keep_only_tags = dict(name='div', attrs={'class':'txtWrap'})
|
keep_only_tags = dict(name='div', attrs={'class':'post'})
|
||||||
|
|
||||||
feeds = [(u'News', u'http://news.cnet.com/2547-1_3-0-20.xml')]
|
feeds = [(u'News', u'http://news.cnet.com/2547-1_3-0-20.xml')]
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
from calibre.ebooks.BeautifulSoup import Tag, NavigableString
|
from calibre.ebooks.BeautifulSoup import Tag, NavigableString
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import time, re
|
import re
|
||||||
|
|
||||||
class Economist(BasicNewsRecipe):
|
class Economist(BasicNewsRecipe):
|
||||||
|
|
||||||
@ -31,41 +31,32 @@ class Economist(BasicNewsRecipe):
|
|||||||
{'class': lambda x: x and 'share-links-header' in x},
|
{'class': lambda x: x and 'share-links-header' in x},
|
||||||
]
|
]
|
||||||
keep_only_tags = [dict(id='ec-article-body')]
|
keep_only_tags = [dict(id='ec-article-body')]
|
||||||
needs_subscription = False
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
|
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
|
||||||
lambda x:'</html>')]
|
lambda x:'</html>')]
|
||||||
|
|
||||||
|
# economist.com has started throttling after about 60% of the total has
|
||||||
|
# downloaded with connection reset by peer (104) errors.
|
||||||
|
delay = 1
|
||||||
|
|
||||||
|
needs_subscription = False
|
||||||
'''
|
'''
|
||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser()
|
||||||
br.open('http://www.economist.com')
|
if self.username and self.password:
|
||||||
req = mechanize.Request(
|
br.open('http://www.economist.com/user/login')
|
||||||
'http://www.economist.com/members/members.cfm?act=exec_login',
|
br.select_form(nr=1)
|
||||||
headers = {
|
br['name'] = self.username
|
||||||
'Referer':'http://www.economist.com/',
|
br['pass'] = self.password
|
||||||
},
|
res = br.submit()
|
||||||
data=urllib.urlencode({
|
raw = res.read()
|
||||||
'logging_in' : 'Y',
|
if '>Log out<' not in raw:
|
||||||
'returnURL' : '/',
|
raise ValueError('Failed to login to economist.com. '
|
||||||
'email_address': self.username,
|
'Check your username and password.')
|
||||||
'fakepword' : 'Password',
|
|
||||||
'pword' : self.password,
|
|
||||||
'x' : '0',
|
|
||||||
'y' : '0',
|
|
||||||
}))
|
|
||||||
br.open(req).read()
|
|
||||||
return br
|
return br
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
try:
|
|
||||||
return self.economist_parse_index()
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
self.log.warn(
|
|
||||||
'Initial attempt to parse index failed, retrying in 30 seconds')
|
|
||||||
time.sleep(30)
|
|
||||||
return self.economist_parse_index()
|
return self.economist_parse_index()
|
||||||
|
|
||||||
def economist_parse_index(self):
|
def economist_parse_index(self):
|
||||||
|
@ -36,27 +36,10 @@ class Economist(BasicNewsRecipe):
|
|||||||
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
|
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
|
||||||
lambda x:'</html>')]
|
lambda x:'</html>')]
|
||||||
|
|
||||||
'''
|
# economist.com has started throttling after about 60% of the total has
|
||||||
def get_browser(self):
|
# downloaded with connection reset by peer (104) errors.
|
||||||
br = BasicNewsRecipe.get_browser()
|
delay = 1
|
||||||
br.open('http://www.economist.com')
|
|
||||||
req = mechanize.Request(
|
|
||||||
'http://www.economist.com/members/members.cfm?act=exec_login',
|
|
||||||
headers = {
|
|
||||||
'Referer':'http://www.economist.com/',
|
|
||||||
},
|
|
||||||
data=urllib.urlencode({
|
|
||||||
'logging_in' : 'Y',
|
|
||||||
'returnURL' : '/',
|
|
||||||
'email_address': self.username,
|
|
||||||
'fakepword' : 'Password',
|
|
||||||
'pword' : self.password,
|
|
||||||
'x' : '0',
|
|
||||||
'y' : '0',
|
|
||||||
}))
|
|
||||||
br.open(req).read()
|
|
||||||
return br
|
|
||||||
'''
|
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
try:
|
try:
|
||||||
|
@ -18,7 +18,7 @@ class ElMundo(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'iso8859_15'
|
encoding = 'iso8859_15'
|
||||||
language = 'es_ES'
|
language = 'es'
|
||||||
masthead_url = 'http://estaticos03.elmundo.es/elmundo/iconos/v4.x/v4.01/bg_h1.png'
|
masthead_url = 'http://estaticos03.elmundo.es/elmundo/iconos/v4.x/v4.01/bg_h1.png'
|
||||||
publication_type = 'newspaper'
|
publication_type = 'newspaper'
|
||||||
extra_css = """
|
extra_css = """
|
||||||
|
28
recipes/escrevinhador.recipe
Normal file
28
recipes/escrevinhador.recipe
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Escrevinhador(BasicNewsRecipe):
|
||||||
|
title = 'Blog Escrevinhador'
|
||||||
|
__author__ = 'Diniz Bortolotto'
|
||||||
|
description = 'Posts do Blog Escrevinhador'
|
||||||
|
publisher = 'Rodrigo Viana'
|
||||||
|
oldest_article = 5
|
||||||
|
max_articles_per_feed = 20
|
||||||
|
category = 'news, politics, Brazil'
|
||||||
|
language = 'pt_BR'
|
||||||
|
publication_type = 'news and politics portal'
|
||||||
|
use_embedded_content = False
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
feeds = [(u'Blog Escrevinhador', u'http://www.rodrigovianna.com.br/feed')]
|
||||||
|
|
||||||
|
reverse_article_order = True
|
||||||
|
|
||||||
|
remove_tags_after = [dict(name='div', attrs={'class':'text'})]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(id='header'),
|
||||||
|
dict(name='p', attrs={'class':'tags'}),
|
||||||
|
dict(name='div', attrs={'class':'sociable'})
|
||||||
|
]
|
||||||
|
|
BIN
recipes/icons/independent.png
Normal file
BIN
recipes/icons/independent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 343 B |
@ -1,70 +1,86 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.independent.co.uk
|
||||||
|
'''
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
|
||||||
class TheIndependent(BasicNewsRecipe):
|
class TheIndependent(BasicNewsRecipe):
|
||||||
title = u'The Independent'
|
title = 'The Independent'
|
||||||
language = 'en_GB'
|
__author__ = 'Darko Miletic'
|
||||||
__author__ = 'Krittika Goyal'
|
description = 'Independent News - Breaking news, comment and features from The Independent newspaper'
|
||||||
oldest_article = 1 #days
|
publisher = 'The Independent'
|
||||||
max_articles_per_feed = 30
|
category = 'news, politics, UK'
|
||||||
encoding = 'latin1'
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
|
encoding = 'cp1252'
|
||||||
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
use_embedded_content = False
|
||||||
|
language = 'en_GB'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://www.independent.co.uk/independent.co.uk/images/logo-london.png'
|
||||||
|
extra_css = """
|
||||||
|
h1{font-family: Georgia,serif }
|
||||||
|
body{font-family: Verdana,Arial,Helvetica,sans-serif}
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
.info,.caption,.credits{font-size: x-small}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
remove_tags =[
|
remove_tags =[
|
||||||
dict(name='iframe'),
|
dict(name=['meta','link','object','embed','iframe','base','style'])
|
||||||
dict(name='div', attrs={'class':'related-articles'}),
|
,dict(attrs={'class':['related-articles','share','googleCols','article-tools','paging','googleArt']})
|
||||||
dict(name='div', attrs={'id':['qrformdiv', 'inSection', 'alpha-inner']}),
|
,dict(attrs={'id':['newsVideoPlayer','yahoobook','google-intext']})
|
||||||
dict(name='ul', attrs={'class':'article-tools'}),
|
|
||||||
dict(name='ul', attrs={'class':'articleTools'}),
|
|
||||||
]
|
]
|
||||||
|
keep_only_tags =[dict(attrs={'id':'article'})]
|
||||||
|
remove_attributes=['lang','onclick','width','xmlns:fb']
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('UK',
|
(u'UK' , u'http://www.independent.co.uk/news/uk/rss' )
|
||||||
'http://www.independent.co.uk/news/uk/rss'),
|
,(u'World' , u'http://www.independent.co.uk/news/world/rss' )
|
||||||
('World',
|
,(u'Business' , u'http://www.independent.co.uk/news/business/rss' )
|
||||||
'http://www.independent.co.uk/news/world/rss'),
|
,(u'People' , u'http://www.independent.co.uk/news/people/rss' )
|
||||||
('Business',
|
,(u'Science' , u'http://www.independent.co.uk/news/science/rss' )
|
||||||
'http://www.independent.co.uk/news/business/rss'),
|
,(u'Media' , u'http://www.independent.co.uk/news/media/rss' )
|
||||||
('People',
|
,(u'Education' , u'http://www.independent.co.uk/news/education/rss' )
|
||||||
'http://www.independent.co.uk/news/people/rss'),
|
,(u'Leading Articles' , u'http://www.independent.co.uk/opinion/leading-articles/rss')
|
||||||
('Science',
|
,(u'Comentators' , u'http://www.independent.co.uk/opinion/commentators/rss' )
|
||||||
'http://www.independent.co.uk/news/science/rss'),
|
,(u'Columnists' , u'http://www.independent.co.uk/opinion/columnists/rss' )
|
||||||
('Media',
|
,(u'Letters' , u'http://www.independent.co.uk/opinion/letters/rss' )
|
||||||
'http://www.independent.co.uk/news/media/rss'),
|
,(u'Big Question' , u'http://www.independent.co.uk/extras/big-question/rss' )
|
||||||
('Education',
|
,(u'Sport' , u'http://www.independent.co.uk/sport/rss' )
|
||||||
'http://www.independent.co.uk/news/education/rss'),
|
,(u'Life&Style' , u'http://www.independent.co.uk/life-style/rss' )
|
||||||
('Obituaries',
|
,(u'Arts&Entertainment' , u'http://www.independent.co.uk/arts-entertainment/rss' )
|
||||||
'http://www.independent.co.uk/news/obituaries/rss'),
|
,(u'Travel' , u'http://www.independent.co.uk/travel/rss' )
|
||||||
|
,(u'Money' , u'http://www.independent.co.uk/money/rss' )
|
||||||
('Opinion',
|
|
||||||
'http://www.independent.co.uk/opinion/rss'),
|
|
||||||
|
|
||||||
('Environment',
|
|
||||||
'http://www.independent.co.uk/environment/rss'),
|
|
||||||
|
|
||||||
('Sport',
|
|
||||||
'http://www.independent.co.uk/sport/rss'),
|
|
||||||
|
|
||||||
('Life and Style',
|
|
||||||
'http://www.independent.co.uk/life-style/rss'),
|
|
||||||
|
|
||||||
('Arts and Entertainment',
|
|
||||||
'http://www.independent.co.uk/arts-entertainment/rss'),
|
|
||||||
|
|
||||||
('Travel',
|
|
||||||
'http://www.independent.co.uk/travel/rss'),
|
|
||||||
|
|
||||||
('Money',
|
|
||||||
'http://www.independent.co.uk/money/rss'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
return article.get('guid', None)
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
story = soup.find(name='div', attrs={'id':'mainColumn'})
|
for item in soup.body.findAll(style=True):
|
||||||
#td = heading.findParent(name='td')
|
del item['style']
|
||||||
#td.extract()
|
for item in soup.body.findAll(['author','preform']):
|
||||||
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
item.name='span'
|
||||||
body = soup.find(name='body')
|
for item in soup.body.findAll('img'):
|
||||||
body.insert(0, story)
|
if not item.has_key('alt'):
|
||||||
return soup
|
item['alt'] = 'image'
|
||||||
|
for item in soup.body.findAll('div', attrs={'class':['clear-o','body','photoCaption']}):
|
||||||
|
item.name = 'p'
|
||||||
|
for item in soup.body.findAll('div'):
|
||||||
|
if not item.attrs and not item.contents:
|
||||||
|
item.extract()
|
||||||
|
soup2 = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||||
|
soup2.body.replaceWith(soup.body)
|
||||||
|
return soup2
|
||||||
|
@ -1,55 +1,46 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2011, Oscar Megia Lopez'
|
||||||
'''
|
'''
|
||||||
juventudrebelde.cu
|
juventudrebelde.cu
|
||||||
'''
|
'''
|
||||||
|
import re
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
from calibre import strftime
|
class JuventudRebelde(BasicNewsRecipe):
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
title = u'Juventud Rebelde'
|
||||||
|
__author__ = 'Oscar Megia Lopez'
|
||||||
class Juventudrebelde(BasicNewsRecipe):
|
description = 'Periodico cubano'
|
||||||
title = 'Juventud Rebelde'
|
oldest_article = 30
|
||||||
__author__ = 'Darko Miletic'
|
|
||||||
description = 'Diario de la Juventud Cubana'
|
|
||||||
publisher = 'Juventud rebelde'
|
|
||||||
category = 'news, politics, Cuba'
|
|
||||||
oldest_article = 2
|
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
#delay = 1
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'cp1252'
|
encoding = 'utf8'
|
||||||
language = 'es_CU'
|
publisher = 'Juventud Rebelde'
|
||||||
|
category = 'Noticias'
|
||||||
|
language = 'es'
|
||||||
|
publication_type = 'Periodico'
|
||||||
|
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .title{font-weight: bold} .read{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||||
|
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
,'linearize_tables': True
|
||||||
|
}
|
||||||
|
|
||||||
cover_url = strftime('http://www.juventudrebelde.cu/UserFiles/File/impreso/iportada-%Y-%m-%d.jpg')
|
keep_only_tags = [
|
||||||
remove_javascript = True
|
dict(name='div', attrs={'class':['title']})
|
||||||
|
,dict(attrs={'class':['read']})
|
||||||
html2lrf_options = [
|
,dict(attrs={'class':['author']})
|
||||||
'--comment' , description
|
|
||||||
, '--category' , category
|
|
||||||
, '--publisher', publisher
|
|
||||||
, '--ignore-tables'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':['share']}),
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'noticia'})]
|
|
||||||
|
|
||||||
feeds = [
|
|
||||||
(u'Generales', u'http://www.juventudrebelde.cu/rss/generales.php' )
|
|
||||||
,(u'Cuba', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=cuba' )
|
|
||||||
,(u'Internacionales', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=internacionales' )
|
|
||||||
,(u'Opinion', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=opinion' )
|
|
||||||
,(u'Cultura', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=cultura' )
|
|
||||||
,(u'Deportes', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=deportes' )
|
|
||||||
,(u'Lectura', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=lectura' )
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
remove_attributes = ['width','height']
|
||||||
mtag = '<meta http-equiv="Content-Language" content="es-CU"/>'
|
|
||||||
soup.head.insert(0,mtag)
|
feeds = [(u'Generales', u'http://www.juventudrebelde.cu/get/rss/grupo/generales/'), (u'Internacionales', u'http://www.psychologytoday.com/blog/romance-redux/feed'), (u'Ciencia y Tecnica', u'http://www.juventudrebelde.cu/get/rss/noticias/ciencia-tecnica/'), (u'Opini\xf3n', u'http://www.juventudrebelde.cu/get/rss/noticias/opinion/'), (u'Cuba', u'http://www.juventudrebelde.cu/get/rss/noticias/cuba/'), (u'Cultura', u'http://www.juventudrebelde.cu/get/rss/noticias/cultura/'), (u'Deportes', u'http://www.juventudrebelde.cu/get/rss/noticias/deportes')]
|
||||||
for item in soup.findAll(style=True):
|
|
||||||
del item['style']
|
|
||||||
return soup
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class AdvancedUserRecipe1294946868(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
|
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
language = 'es_ES'
|
language = 'es'
|
||||||
timefmt = '[%a, %d %b, %Y]'
|
timefmt = '[%a, %d %b, %Y]'
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
|
@ -17,18 +17,15 @@ class Lanacion(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
language = 'es_AR'
|
language = 'es_AR'
|
||||||
delay = 14
|
|
||||||
publication_type = 'newspaper'
|
publication_type = 'newspaper'
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
masthead_url = 'http://www.lanacion.com.ar/_ui/desktop/imgs/layout/logos/ln341x47.gif'
|
masthead_url = 'http://www.lanacion.com.ar/_ui/desktop/imgs/layout/logos/ln-home.gif'
|
||||||
extra_css = """
|
extra_css = """
|
||||||
h1{font-family: Georgia,serif}
|
h1{font-family: TheSans,Arial,sans-serif}
|
||||||
h2{color: #626262; font-weight: normal; font-size: 1.1em}
|
|
||||||
body{font-family: Arial,sans-serif}
|
body{font-family: Arial,sans-serif}
|
||||||
img{margin-top: 0.5em; margin-bottom: 0.2em; display: block}
|
img{display: block}
|
||||||
.notaFecha{color: #808080; font-size: small}
|
.firma,.fecha{font-size: small}
|
||||||
.notaEpigrafe{font-size: x-small}
|
.epigrafe-columna{font-size: x-small}
|
||||||
.topNota h1{font-family: Arial,sans-serif}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -39,21 +36,13 @@ class Lanacion(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [
|
|
||||||
dict(name='div', attrs={'class':['topNota','itemHeader','nota','itemBody']})
|
|
||||||
,dict(name='div', attrs={'id':'content'})
|
|
||||||
]
|
|
||||||
|
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div' , attrs={'class':'notaComentario floatFix noprint' })
|
dict(name=['iframe','embed','object','meta','link'])
|
||||||
,dict(name='ul' , attrs={'class':['cajaHerramientas cajaTop noprint','herramientas noprint']})
|
,dict(attrs={'id':['herramientas','relacionadas','ampliar']})
|
||||||
,dict(name='div' , attrs={'class':['titulosMultimedia','herramientas noprint','cajaHerramientas noprint','cajaHerramientas floatFix'] })
|
|
||||||
,dict(attrs={'class':['izquierda','espacio17','espacio10','espacio20','floatFix ultimasNoticias','relacionadas','titulosMultimedia','derecha','techo color','encuesta','izquierda compartir','floatFix','videoCentro']})
|
|
||||||
,dict(name=['iframe','embed','object','form','base','hr','meta','link','input'])
|
|
||||||
]
|
]
|
||||||
|
|
||||||
remove_tags_after = dict(attrs={'class':['tags','nota-destacado']})
|
remove_tags_before = dict(attrs={'id':'encabezado'})
|
||||||
remove_attributes = ['height','width','visible','onclick','data-count','name']
|
remove_tags_after = dict(attrs={'id':'relacionadas'})
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Politica' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=30' )
|
(u'Politica' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=30' )
|
||||||
@ -92,6 +81,15 @@ class Lanacion(BasicNewsRecipe):
|
|||||||
return None
|
return None
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
def get_cover_url(self):
|
||||||
|
soup = self.index_to_soup('http://www.lanacion.com.ar/edicion-impresa')
|
||||||
|
atap = soup.find(attrs={'class':'tapa'})
|
||||||
|
if atap:
|
||||||
|
li = atap.find('img')
|
||||||
|
if li:
|
||||||
|
return li['src']
|
||||||
|
return None
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
|
@ -23,7 +23,7 @@ class LaTribuna(BasicNewsRecipe):
|
|||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
language = 'es_HN'
|
language = 'es_HN'
|
||||||
|
|
||||||
lang = 'es-HN'
|
lang = 'es_HN'
|
||||||
direction = 'ltr'
|
direction = 'ltr'
|
||||||
|
|
||||||
html2lrf_options = [
|
html2lrf_options = [
|
||||||
|
@ -19,7 +19,7 @@ class Marca(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
delay = 1
|
delay = 1
|
||||||
encoding = 'iso-8859-15'
|
encoding = 'iso-8859-15'
|
||||||
language = 'es_ES'
|
language = 'es'
|
||||||
publication_type = 'newsportal'
|
publication_type = 'newsportal'
|
||||||
masthead_url = 'http://estaticos.marca.com/deporte/img/v3.0/img_marca-com.png'
|
masthead_url = 'http://estaticos.marca.com/deporte/img/v3.0/img_marca-com.png'
|
||||||
extra_css = """
|
extra_css = """
|
||||||
|
@ -1,91 +1,135 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2010, matek09, matek09@gmail.com'
|
__copyright__ = '2010, matek09, matek09@gmail.com'
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class Newsweek(BasicNewsRecipe):
|
class Newsweek(BasicNewsRecipe):
|
||||||
FIND_LAST_FULL_ISSUE = True
|
|
||||||
EDITION = '0'
|
EDITION = '0'
|
||||||
EXCLUDE_LOCKED = True
|
DATE = None
|
||||||
LOCKED_ICO = 'http://www.newsweek.pl/bins/media/static/newsweek/img/ico_locked.gif'
|
YEAR = datetime.datetime.now().year
|
||||||
|
|
||||||
title = u'Newsweek Polska'
|
title = u'Newsweek Polska'
|
||||||
__author__ = 'matek09'
|
__author__ = 'matek09'
|
||||||
description = 'Weekly magazine'
|
description = 'Weekly magazine'
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
no_stylesheets = True
|
|
||||||
language = 'pl'
|
language = 'pl'
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
|
|
||||||
keep_only_tags =[]
|
temp_files = []
|
||||||
keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'article'}))
|
articles_are_obfuscated = True
|
||||||
|
|
||||||
remove_tags =[]
|
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'copy'}))
|
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'url'}))
|
|
||||||
|
|
||||||
extra_css = '''
|
def get_obfuscated_article(self, url):
|
||||||
.body {font-size: small}
|
br = self.get_browser()
|
||||||
.author {font-size: x-small}
|
br.open(url)
|
||||||
.lead {font-size: x-small}
|
source = br.response().read()
|
||||||
.title{font-size: x-large; font-weight: bold}
|
page = self.index_to_soup(source)
|
||||||
'''
|
|
||||||
|
|
||||||
def print_version(self, url):
|
main_section = page.find(id='mainSection')
|
||||||
return url.replace("http://www.newsweek.pl/artykuly/wydanie/" + str(self.EDITION), "http://www.newsweek.pl/artykuly") + '/print'
|
|
||||||
|
|
||||||
def is_locked(self, a):
|
title = main_section.find('h1')
|
||||||
if a.findNext('img')['src'] == 'http://www.newsweek.pl/bins/media/static/newsweek/img/ico_locked.gif':
|
info = main_section.find('ul', attrs={'class' : 'articleInfo'})
|
||||||
return True
|
authors = info.find('li').find('h4')
|
||||||
else:
|
article = main_section.find('div', attrs={'id' : 'article'})
|
||||||
return False
|
html = unicode(title) + unicode(authors) + unicode(article)
|
||||||
|
next = main_section.find('li', attrs={'class' : 'next'})
|
||||||
|
|
||||||
|
while next:
|
||||||
|
url = next.find('a')['href']
|
||||||
|
br.open(url)
|
||||||
|
source = br.response().read()
|
||||||
|
page = self.index_to_soup(source)
|
||||||
|
main_section = page.find(id='mainSection')
|
||||||
|
article = main_section.find('div', attrs={'id' : 'article'})
|
||||||
|
aside = article.find(id='articleAside')
|
||||||
|
if aside is not None:
|
||||||
|
aside.extract()
|
||||||
|
html = html + unicode(article)
|
||||||
|
next = main_section.find('li', attrs={'class' : 'next'})
|
||||||
|
|
||||||
|
|
||||||
|
self.temp_files.append(PersistentTemporaryFile('_temparse.html'))
|
||||||
|
self.temp_files[-1].write(html)
|
||||||
|
self.temp_files[-1].close()
|
||||||
|
return self.temp_files[-1].name
|
||||||
|
|
||||||
def is_full(self, issue_soup):
|
def is_full(self, issue_soup):
|
||||||
if len(issue_soup.findAll('img', attrs={'src' : 'http://www.newsweek.pl/bins/media/static/newsweek/img/ico_locked.gif'})) > 1:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def find_last_full_issue(self):
|
|
||||||
frame_url = 'http://www.newsweek.pl/Frames/IssueCover.aspx'
|
|
||||||
while True:
|
while True:
|
||||||
frame_soup = self.index_to_soup(frame_url)
|
main_section = issue_soup.find(id='mainSection')
|
||||||
self.EDITION = frame_soup.find('a', attrs={'target' : '_parent'})['href'].replace('/wydania/','')
|
next = main_section.find('li', attrs={'class' : 'next'})
|
||||||
|
if len(main_section.findAll(attrs={'class' : 'locked'})) > 1:
|
||||||
|
return False
|
||||||
|
elif next is None:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
issue_soup = self.index_to_soup(next.find('a')['href'])
|
||||||
|
|
||||||
|
def find_last_full_issue(self, archive_url):
|
||||||
|
archive_soup = self.index_to_soup(archive_url)
|
||||||
|
select = archive_soup.find('select', attrs={'id' : 'paper_issue_select'})
|
||||||
|
for option in select.findAll(lambda tag: tag.name == 'option' and tag.has_key('value')):
|
||||||
|
self.EDITION = option['value'].replace('http://www.newsweek.pl/wydania/','')
|
||||||
issue_soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + self.EDITION)
|
issue_soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + self.EDITION)
|
||||||
if self.is_full(issue_soup):
|
if self.is_full(issue_soup):
|
||||||
break
|
return
|
||||||
frame_url = 'http://www.newsweek.pl/Frames/' + frame_soup.find(lambda tag: tag.name == 'span' and not tag.attrs).a['href']
|
|
||||||
|
|
||||||
|
|
||||||
|
self.YEAR = self.YEAR - 1
|
||||||
|
self.find_last_full_issue(archive_url + ',' + str(self.YEAR))
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
if self.FIND_LAST_FULL_ISSUE:
|
archive_url = 'http://www.newsweek.pl/wydania/archiwum'
|
||||||
self.find_last_full_issue()
|
self.find_last_full_issue(archive_url)
|
||||||
soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + self.EDITION)
|
soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + self.EDITION)
|
||||||
img = soup.find('img', id="ctl00_C1_PaperIsssueView_IssueImage", src=True)
|
self.DATE = self.tag_to_string(soup.find('span', attrs={'class' : 'data'}))
|
||||||
|
main_section = soup.find(id='mainSection')
|
||||||
|
img = main_section.find(lambda tag: tag.name == 'img' and tag.has_key('alt') and tag.has_key('title'))
|
||||||
self.cover_url = img['src']
|
self.cover_url = img['src']
|
||||||
feeds = []
|
feeds = []
|
||||||
parent = soup.find(id='content-left-big')
|
articles = {}
|
||||||
for txt in parent.findAll(attrs={'class':'txt_normal_red strong'}):
|
sections = []
|
||||||
articles = list(self.find_articles(txt))
|
while True:
|
||||||
if len(articles) > 0:
|
news_list = main_section.find('ul', attrs={'class' : 'newsList'})
|
||||||
section = self.tag_to_string(txt).capitalize()
|
for h2 in news_list.findAll('h2'):
|
||||||
feeds.append((section, articles))
|
|
||||||
|
article = self.create_article(h2)
|
||||||
|
category_div = h2.findNext('div', attrs={'class' : 'kategorie'})
|
||||||
|
section = self.tag_to_string(category_div)
|
||||||
|
if articles.has_key(section):
|
||||||
|
articles[section].append(article)
|
||||||
|
else:
|
||||||
|
articles[section] = [article]
|
||||||
|
sections.append(section)
|
||||||
|
|
||||||
|
next = main_section.find('li', attrs={'class' : 'next'})
|
||||||
|
if next is None:
|
||||||
|
break
|
||||||
|
soup = self.index_to_soup(next.find('a')['href'])
|
||||||
|
main_section = soup.find(id='mainSection')
|
||||||
|
|
||||||
|
for section in sections:
|
||||||
|
feeds.append((section, articles[section]))
|
||||||
return feeds
|
return feeds
|
||||||
|
|
||||||
def find_articles(self, txt):
|
def create_article(self, h2):
|
||||||
for a in txt.findAllNext( attrs={'class':['strong','hr']}):
|
article = {}
|
||||||
if a.name in "div":
|
a = h2.find('a')
|
||||||
break
|
article['title'] = self.tag_to_string(a)
|
||||||
if (not self.FIND_LAST_FULL_ISSUE) & self.EXCLUDE_LOCKED & self.is_locked(a):
|
article['url'] = a['href']
|
||||||
continue
|
article['date'] = self.DATE
|
||||||
yield {
|
desc = h2.findNext('p')
|
||||||
'title' : self.tag_to_string(a),
|
|
||||||
'url' : 'http://www.newsweek.pl' + a['href'],
|
if desc is not None:
|
||||||
'date' : '',
|
article['description'] = self.tag_to_string(desc)
|
||||||
'description' : ''
|
else:
|
||||||
}
|
article['description'] = ''
|
||||||
|
return article
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
35
recipes/novinite.recipe
Normal file
35
recipes/novinite.recipe
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1308572538(BasicNewsRecipe):
|
||||||
|
title = u'Novinite.com'
|
||||||
|
__author__ = 'Martin Tsanchev'
|
||||||
|
description = 'Real time provider of the latest Bulgarian news in English'
|
||||||
|
category = 'Business, Politics, Society, Sports, Crime, Lifestyle, World, People'
|
||||||
|
language = 'en_BG'
|
||||||
|
encoding = 'utf-8'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 10
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
||||||
|
remove_tags = [dict(name='a', attrs={'class':'twitter-share-button'})]
|
||||||
|
remove_tags_after = dict(id='textsize')
|
||||||
|
no_stylesheets = True
|
||||||
|
feeds = [(u'Business', u'http://www.novinite.com/services/news_rdf.php?category_id=1'),
|
||||||
|
(u'Finance', u'http://www.novinite.com/services/news_rdf.php?category_id=15'),
|
||||||
|
(u'Energy', u'http://www.novinite.com/services/news_rdf.php?category_id=16'),
|
||||||
|
(u'Industry', u'http://www.novinite.com/services/news_rdf.php?category_id=17'),
|
||||||
|
(u'Properties', u'http://www.novinite.com/services/news_rdf.php?category_id=18'),
|
||||||
|
(u'Politics', u'http://www.novinite.com/services/news_rdf.php?category_id=2'),
|
||||||
|
(u'Diplomacy', u'http://www.novinite.com/services/news_rdf.php?category_id=20'),
|
||||||
|
(u'Defense', u'http://www.novinite.com/services/news_rdf.php?category_id=21'),
|
||||||
|
(u'Bulgaria in EU', u'http://www.novinite.com/services/news_rdf.php?category_id=22'),
|
||||||
|
(u'Domestic', u'http://www.novinite.com/services/news_rdf.php?category_id=23'),
|
||||||
|
(u'Society', u'http://www.novinite.com/services/news_rdf.php?category_id=3'),
|
||||||
|
(u'Environment', u'http://www.novinite.com/services/news_rdf.php?category_id=24'),
|
||||||
|
(u'Education', u'http://www.novinite.com/services/news_rdf.php?category_id=25'),
|
||||||
|
(u'Culture', u'http://www.novinite.com/services/news_rdf.php?category_id=26'),
|
||||||
|
(u'Archaeology', u'http://www.novinite.com/services/news_rdf.php?category_id=34'),
|
||||||
|
(u'Health', u'http://www.novinite.com/services/news_rdf.php?category_id=62'),
|
||||||
|
(u'Sports', u'http://www.novinite.com/services/news_rdf.php?category_id=4'),
|
||||||
|
(u'Crime', u'http://www.novinite.com/services/news_rdf.php?category_id=5'),
|
||||||
|
(u'Lifestyle', u'http://www.novinite.com/services/news_rdf.php?category_id=6'),
|
||||||
|
(u'World', u'http://www.novinite.com/services/news_rdf.php?category_id=30')]
|
43
recipes/patente_de_corso.recipe
Normal file
43
recipes/patente_de_corso.recipe
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Oscar Megia Lopez'
|
||||||
|
'''
|
||||||
|
perezreverte.com
|
||||||
|
'''
|
||||||
|
import re
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class PerezReverte(BasicNewsRecipe):
|
||||||
|
title = u'Patente de Corso'
|
||||||
|
__author__ = 'Oscar Megia Lopez'
|
||||||
|
description = 'Arturo Perez Reverte'
|
||||||
|
oldest_article = 90
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
#delay = 1
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'utf8'
|
||||||
|
publisher = 'Arturo Perez Reverte'
|
||||||
|
category = 'Articulo'
|
||||||
|
language = 'es'
|
||||||
|
publication_type = 'Magazine'
|
||||||
|
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .contentheading{font-weight: bold} .txt_articulo{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||||
|
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
,'linearize_tables': True
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h2', attrs={'class':['titular']}),
|
||||||
|
dict(name='p', attrs={'class':['fecha']}),
|
||||||
|
dict(name='div', attrs={'class':['bloqueTexto']})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Patente de corso - Web oficial de Arturo Perez Reverte', 'http://www.perezreverte.com/rss/patentes-corso/')
|
||||||
|
]
|
@ -31,15 +31,19 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
|
|||||||
feeds.append(u'http://www.rp.pl/rss/8.html')
|
feeds.append(u'http://www.rp.pl/rss/8.html')
|
||||||
|
|
||||||
keep_only_tags =[]
|
keep_only_tags =[]
|
||||||
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'storyp'}))
|
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'story'}))
|
||||||
|
|
||||||
remove_tags =[]
|
remove_tags =[]
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'adk_0'}))
|
remove_tags.append(dict(name = 'div', attrs = {'id' : 'socialTools'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleToolBoxTop'}))
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'clr'}))
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'clr'}))
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'share_bottom'}))
|
remove_tags.append(dict(name = 'div', attrs = {'id' : 'recommendations'}))
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'copyright_law'}))
|
remove_tags.append(dict(name = 'div', attrs = {'id' : 'editorPicks'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id' : 'articleCopyrightText'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id' : 'articleCopyrightButton'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleToolBoxBottom'}))
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'more'}))
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'more'}))
|
||||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'editorPicks'}))
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'addRecommendation'}))
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
|
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
|
||||||
@ -62,3 +66,4 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
|
|||||||
forget, sep, index = rest.rpartition(',')
|
forget, sep, index = rest.rpartition(',')
|
||||||
|
|
||||||
return start + '/' + index + '?print=tak'
|
return start + '/' + index + '?print=tak'
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="{prefix}/static/browse/browse.css" />
|
<link rel="stylesheet" type="text/css" href="{prefix}/static/browse/browse.css" />
|
||||||
<link type="text/css" href="{prefix}/static/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css" rel="stylesheet" />
|
<link type="text/css" href="{prefix}/static/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css" rel="stylesheet" />
|
||||||
<link rel="stylesheet" type="text/css" href="{prefix}/static/jquery.multiselect.css" />
|
<link rel="stylesheet" type="text/css" href="{prefix}/static/jquery.multiselect.css" />
|
||||||
<link rel="apple-touch-icon" href="/static/calibre.png" />
|
<link rel="apple-touch-icon" href="{prefix}/static/calibre.png" />
|
||||||
|
|
||||||
<script type="text/javascript" src="{prefix}/static/jquery.js"></script>
|
<script type="text/javascript" src="{prefix}/static/jquery.js"></script>
|
||||||
<script type="text/javascript" src="{prefix}/static/jquery.corner.js"></script>
|
<script type="text/javascript" src="{prefix}/static/jquery.corner.js"></script>
|
||||||
|
@ -179,6 +179,9 @@ save_template_title_series_sorting = 'library_order'
|
|||||||
# changed. Changes to this tweak won't have an effect until the book is modified
|
# changed. Changes to this tweak won't have an effect until the book is modified
|
||||||
# in some way. If you enter an invalid pattern, it is silently ignored.
|
# in some way. If you enter an invalid pattern, it is silently ignored.
|
||||||
# To disable use the expression: '^$'
|
# To disable use the expression: '^$'
|
||||||
|
# This expression is designed for articles that are followed by spaces. If you
|
||||||
|
# also need to match articles that are followed by other characters, for example L'
|
||||||
|
# in French, use: r"^(A\s+|The\s+|An\s+|L')" instead.
|
||||||
# Default: '^(A|The|An)\s+'
|
# Default: '^(A|The|An)\s+'
|
||||||
title_sort_articles=r'^(A|The|An)\s+'
|
title_sort_articles=r'^(A|The|An)\s+'
|
||||||
|
|
||||||
|
BIN
resources/images/keyboard-prefs.png
Normal file
BIN
resources/images/keyboard-prefs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
BIN
resources/images/mimetypes/xps.png
Executable file
BIN
resources/images/mimetypes/xps.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -21,3 +21,5 @@ vipy.session.add_content_browser('.r', ',r', 'Recipe',
|
|||||||
vipy.session.glob_based_iterator(os.path.join(project_dir, 'recipes', '*.recipe')),
|
vipy.session.glob_based_iterator(os.path.join(project_dir, 'recipes', '*.recipe')),
|
||||||
vipy.session.regexp_based_matcher(r'title\s*=\s*(?P<title>.+)', 'title', recipe_title_callback))
|
vipy.session.regexp_based_matcher(r'title\s*=\s*(?P<title>.+)', 'title', recipe_title_callback))
|
||||||
EOFPY
|
EOFPY
|
||||||
|
|
||||||
|
nmap \log :enew<CR>:read ! bzr log -l 500 ../.. <CR>:e ../../Changelog.yaml<CR>:e constants.py<CR>
|
||||||
|
2169
setup/iso639.xml
Normal file
2169
setup/iso639.xml
Normal file
File diff suppressed because it is too large
Load Diff
@ -273,10 +273,9 @@ class GetTranslations(Translations):
|
|||||||
class ISO639(Command):
|
class ISO639(Command):
|
||||||
|
|
||||||
description = 'Compile translations for ISO 639 codes'
|
description = 'Compile translations for ISO 639 codes'
|
||||||
XML = '/usr/lib/python2.7/site-packages/pycountry/databases/iso639.xml'
|
|
||||||
|
|
||||||
def run(self, opts):
|
def run(self, opts):
|
||||||
src = self.XML
|
src = self.j(self.d(self.SRC), 'setup', 'iso639.xml')
|
||||||
if not os.path.exists(src):
|
if not os.path.exists(src):
|
||||||
raise Exception(src + ' does not exist')
|
raise Exception(src + ' does not exist')
|
||||||
dest = self.j(self.d(self.SRC), 'resources', 'localization',
|
dest = self.j(self.d(self.SRC), 'resources', 'localization',
|
||||||
@ -290,20 +289,27 @@ class ISO639(Command):
|
|||||||
by_2 = {}
|
by_2 = {}
|
||||||
by_3b = {}
|
by_3b = {}
|
||||||
by_3t = {}
|
by_3t = {}
|
||||||
|
m2to3 = {}
|
||||||
|
m3to2 = {}
|
||||||
codes2, codes3t, codes3b = set([]), set([]), set([])
|
codes2, codes3t, codes3b = set([]), set([]), set([])
|
||||||
for x in root.xpath('//iso_639_entry'):
|
for x in root.xpath('//iso_639_entry'):
|
||||||
name = x.get('name')
|
name = x.get('name')
|
||||||
two = x.get('iso_639_1_code', None)
|
two = x.get('iso_639_1_code', None)
|
||||||
|
threeb = x.get('iso_639_2B_code')
|
||||||
|
threet = x.get('iso_639_2T_code')
|
||||||
if two is not None:
|
if two is not None:
|
||||||
by_2[two] = name
|
by_2[two] = name
|
||||||
codes2.add(two)
|
codes2.add(two)
|
||||||
by_3b[x.get('iso_639_2B_code')] = name
|
m2to3[two] = threet
|
||||||
by_3t[x.get('iso_639_2T_code')] = name
|
m3to2[threeb] = m3to2[threet] = two
|
||||||
|
by_3b[threeb] = name
|
||||||
|
by_3t[threet] = name
|
||||||
codes3b.add(x.get('iso_639_2B_code'))
|
codes3b.add(x.get('iso_639_2B_code'))
|
||||||
codes3t.add(x.get('iso_639_2T_code'))
|
codes3t.add(x.get('iso_639_2T_code'))
|
||||||
|
|
||||||
from cPickle import dump
|
from cPickle import dump
|
||||||
x = {'by_2':by_2, 'by_3b':by_3b, 'by_3t':by_3t, 'codes2':codes2,
|
x = {'by_2':by_2, 'by_3b':by_3b, 'by_3t':by_3t, 'codes2':codes2,
|
||||||
'codes3b':codes3b, 'codes3t':codes3t}
|
'codes3b':codes3b, 'codes3t':codes3t, '2to3':m2to3,
|
||||||
|
'3to2':m3to2}
|
||||||
dump(x, open(dest, 'wb'), -1)
|
dump(x, open(dest, 'wb'), -1)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = u'calibre'
|
__appname__ = u'calibre'
|
||||||
numeric_version = (0, 8, 13)
|
numeric_version = (0, 8, 14)
|
||||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
|
@ -1029,7 +1029,7 @@ class TemplateFunctions(PreferencesPlugin):
|
|||||||
category = 'Advanced'
|
category = 'Advanced'
|
||||||
gui_category = _('Advanced')
|
gui_category = _('Advanced')
|
||||||
category_order = 5
|
category_order = 5
|
||||||
name_order = 4
|
name_order = 5
|
||||||
config_widget = 'calibre.gui2.preferences.template_functions'
|
config_widget = 'calibre.gui2.preferences.template_functions'
|
||||||
description = _('Create your own template functions')
|
description = _('Create your own template functions')
|
||||||
|
|
||||||
@ -1092,6 +1092,17 @@ class Tweaks(PreferencesPlugin):
|
|||||||
config_widget = 'calibre.gui2.preferences.tweaks'
|
config_widget = 'calibre.gui2.preferences.tweaks'
|
||||||
description = _('Fine tune how calibre behaves in various contexts')
|
description = _('Fine tune how calibre behaves in various contexts')
|
||||||
|
|
||||||
|
class Keyboard(PreferencesPlugin):
|
||||||
|
name = 'Keyboard'
|
||||||
|
icon = I('keyboard-prefs.png')
|
||||||
|
gui_name = _('Keyboard')
|
||||||
|
category = 'Advanced'
|
||||||
|
gui_category = _('Advanced')
|
||||||
|
category_order = 5
|
||||||
|
name_order = 4
|
||||||
|
config_widget = 'calibre.gui2.preferences.keyboard'
|
||||||
|
description = _('Customize the keyboard shortcuts used by calibre')
|
||||||
|
|
||||||
class Misc(PreferencesPlugin):
|
class Misc(PreferencesPlugin):
|
||||||
name = 'Misc'
|
name = 'Misc'
|
||||||
icon = I('exec.png')
|
icon = I('exec.png')
|
||||||
@ -1106,7 +1117,7 @@ class Misc(PreferencesPlugin):
|
|||||||
plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
|
plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
|
||||||
CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard,
|
CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard,
|
||||||
Email, Server, Plugins, Tweaks, Misc, TemplateFunctions,
|
Email, Server, Plugins, Tweaks, Misc, TemplateFunctions,
|
||||||
MetadataSources]
|
MetadataSources, Keyboard]
|
||||||
|
|
||||||
#}}}
|
#}}}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
# Google
|
# Google
|
||||||
0x18d1 : {
|
0x18d1 : {
|
||||||
0x0001 : [0x0223],
|
0x0001 : [0x0223, 0x9999],
|
||||||
0x4e11 : [0x0100, 0x226, 0x227],
|
0x4e11 : [0x0100, 0x226, 0x227],
|
||||||
0x4e12 : [0x0100, 0x226, 0x227],
|
0x4e12 : [0x0100, 0x226, 0x227],
|
||||||
0x4e21 : [0x0100, 0x226, 0x227],
|
0x4e21 : [0x0100, 0x226, 0x227],
|
||||||
@ -76,8 +76,11 @@ class ANDROID(USBMS):
|
|||||||
0x413c : { 0xb007 : [0x0100, 0x0224, 0x0226]},
|
0x413c : { 0xb007 : [0x0100, 0x0224, 0x0226]},
|
||||||
|
|
||||||
# LG
|
# LG
|
||||||
0x1004 : { 0x61cc : [0x100], 0x61ce : [0x100], 0x618e : [0x226,
|
0x1004 : {
|
||||||
0x9999] },
|
0x61cc : [0x100],
|
||||||
|
0x61ce : [0x100],
|
||||||
|
0x618e : [0x226, 0x9999, 0x100]
|
||||||
|
},
|
||||||
|
|
||||||
# Archos
|
# Archos
|
||||||
0x0e79 : {
|
0x0e79 : {
|
||||||
|
@ -6,6 +6,7 @@ Created on 15 May 2010
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS, BookList
|
from calibre.devices.usbms.driver import USBMS, BookList
|
||||||
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
|
||||||
# This class is added to the standard device plugin chain, so that it can
|
# This class is added to the standard device plugin chain, so that it can
|
||||||
# be configured. It has invalid vendor_id etc, so it will never match a
|
# be configured. It has invalid vendor_id etc, so it will never match a
|
||||||
@ -16,8 +17,8 @@ class FOLDER_DEVICE_FOR_CONFIG(USBMS):
|
|||||||
description = _('Use an arbitrary folder as a device.')
|
description = _('Use an arbitrary folder as a device.')
|
||||||
author = 'John Schember/Charles Haley'
|
author = 'John Schember/Charles Haley'
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
FORMATS = ['epub', 'fb2', 'mobi', 'azw', 'lrf', 'tcr', 'pmlz', 'lit',
|
FORMATS = list(BOOK_EXTENSIONS)
|
||||||
'rtf', 'rb', 'pdf', 'oeb', 'txt', 'pdb', 'prc']
|
|
||||||
VENDOR_ID = [0xffff]
|
VENDOR_ID = [0xffff]
|
||||||
PRODUCT_ID = [0xffff]
|
PRODUCT_ID = [0xffff]
|
||||||
BCD = [0xffff]
|
BCD = [0xffff]
|
||||||
|
@ -97,6 +97,7 @@ gprefs.defaults['book_display_fields'] = [
|
|||||||
('last_modified', False), ('size', False),
|
('last_modified', False), ('size', False),
|
||||||
]
|
]
|
||||||
gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}'
|
gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}'
|
||||||
|
gprefs.defaults['preserve_date_on_ctl'] = True
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -169,7 +170,9 @@ def _config(): # {{{
|
|||||||
c.add_opt('scheduler_search_history', default=[],
|
c.add_opt('scheduler_search_history', default=[],
|
||||||
help='Search history for the recipe scheduler')
|
help='Search history for the recipe scheduler')
|
||||||
c.add_opt('plugin_search_history', default=[],
|
c.add_opt('plugin_search_history', default=[],
|
||||||
help='Search history for the recipe scheduler')
|
help='Search history for the plugin preferences')
|
||||||
|
c.add_opt('shortcuts_search_history', default=[],
|
||||||
|
help='Search history for the keyboard preferences')
|
||||||
c.add_opt('worker_limit', default=6,
|
c.add_opt('worker_limit', default=6,
|
||||||
help=_(
|
help=_(
|
||||||
'Maximum number of simultaneous conversion/news download jobs. '
|
'Maximum number of simultaneous conversion/news download jobs. '
|
||||||
@ -425,6 +428,8 @@ class FileIconProvider(QFileIconProvider):
|
|||||||
'snb' : 'snb',
|
'snb' : 'snb',
|
||||||
'djv' : 'djvu',
|
'djv' : 'djvu',
|
||||||
'djvu' : 'djvu',
|
'djvu' : 'djvu',
|
||||||
|
'xps' : 'xps',
|
||||||
|
'oxps' : 'xps',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -8,9 +8,13 @@ __docformat__ = 'restructuredtext en'
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from PyQt4.Qt import QToolButton, QAction, QIcon, QObject, QMenu
|
from PyQt4.Qt import (QToolButton, QAction, QIcon, QObject, QMenu,
|
||||||
|
QKeySequence)
|
||||||
|
|
||||||
|
from calibre import prints
|
||||||
from calibre.gui2 import Dispatcher
|
from calibre.gui2 import Dispatcher
|
||||||
|
from calibre.gui2.keyboard import NameConflict
|
||||||
|
|
||||||
|
|
||||||
class InterfaceAction(QObject):
|
class InterfaceAction(QObject):
|
||||||
|
|
||||||
@ -108,7 +112,10 @@ class InterfaceAction(QObject):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_name(self):
|
def unique_name(self):
|
||||||
return u'%s(%s)'%(self.__class__.__name__, self.name)
|
bn = self.__class__.__name__
|
||||||
|
if getattr(self.interface_action_base_plugin, 'name'):
|
||||||
|
bn = self.interface_action_base_plugin.name
|
||||||
|
return u'Interface Action: %s (%s)'%(bn, self.name)
|
||||||
|
|
||||||
def create_action(self, spec=None, attr='qaction'):
|
def create_action(self, spec=None, attr='qaction'):
|
||||||
if spec is None:
|
if spec is None:
|
||||||
@ -129,7 +136,6 @@ class InterfaceAction(QObject):
|
|||||||
a.setToolTip(text)
|
a.setToolTip(text)
|
||||||
a.setStatusTip(text)
|
a.setStatusTip(text)
|
||||||
a.setWhatsThis(text)
|
a.setWhatsThis(text)
|
||||||
keys = ()
|
|
||||||
shortcut_action = action
|
shortcut_action = action
|
||||||
desc = tooltip if tooltip else None
|
desc = tooltip if tooltip else None
|
||||||
if attr == 'qaction':
|
if attr == 'qaction':
|
||||||
@ -138,9 +144,22 @@ class InterfaceAction(QObject):
|
|||||||
keys = ((shortcut,) if isinstance(shortcut, basestring) else
|
keys = ((shortcut,) if isinstance(shortcut, basestring) else
|
||||||
tuple(shortcut))
|
tuple(shortcut))
|
||||||
|
|
||||||
|
if spec[0] and not (attr=='qaction' and self.popup_type ==
|
||||||
|
QToolButton.InstantPopup):
|
||||||
|
try:
|
||||||
self.gui.keyboard.register_shortcut(self.unique_name + ' - ' + attr,
|
self.gui.keyboard.register_shortcut(self.unique_name + ' - ' + attr,
|
||||||
unicode(shortcut_action.text()), default_keys=keys,
|
unicode(spec[0]), default_keys=keys,
|
||||||
action=shortcut_action, description=desc)
|
action=shortcut_action, description=desc,
|
||||||
|
group=self.action_spec[0])
|
||||||
|
except NameConflict as e:
|
||||||
|
try:
|
||||||
|
prints(unicode(e))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
shortcut_action.setShortcuts([QKeySequence(key,
|
||||||
|
QKeySequence.PortableText) for key in keys])
|
||||||
|
|
||||||
|
|
||||||
if attr is not None:
|
if attr is not None:
|
||||||
setattr(self, attr, action)
|
setattr(self, attr, action)
|
||||||
if attr == 'qaction' and self.action_add_menu:
|
if attr == 'qaction' and self.action_add_menu:
|
||||||
@ -166,10 +185,11 @@ class InterfaceAction(QObject):
|
|||||||
ac.setToolTip(description)
|
ac.setToolTip(description)
|
||||||
ac.setStatusTip(description)
|
ac.setStatusTip(description)
|
||||||
ac.setWhatsThis(description)
|
ac.setWhatsThis(description)
|
||||||
|
|
||||||
if shortcut is not False:
|
if shortcut is not False:
|
||||||
self.gui.keyboard.register_shortcut(unique_name,
|
self.gui.keyboard.register_shortcut(unique_name,
|
||||||
unicode(text), default_keys=keys,
|
unicode(text), default_keys=keys,
|
||||||
action=ac, description=description)
|
action=ac, description=description, group=self.action_spec[0])
|
||||||
if triggered is not None:
|
if triggered is not None:
|
||||||
ac.triggered.connect(triggered)
|
ac.triggered.connect(triggered)
|
||||||
return ac
|
return ac
|
||||||
|
@ -8,13 +8,14 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import os
|
import os
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QMenu, Qt, QInputDialog, QToolButton
|
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
||||||
|
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize)
|
||||||
|
|
||||||
from calibre import isbytestring
|
from calibre import isbytestring
|
||||||
from calibre.constants import filesystem_encoding, iswindows
|
from calibre.constants import filesystem_encoding, iswindows
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
||||||
question_dialog, info_dialog, open_local_file)
|
question_dialog, info_dialog, open_local_file, choose_dir)
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
|
|
||||||
@ -76,16 +77,73 @@ class LibraryUsageStats(object): # {{{
|
|||||||
self.write_stats()
|
self.write_stats()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class MovedDialog(QDialog): # {{{
|
||||||
|
|
||||||
|
def __init__(self, stats, location, parent=None):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self.setWindowTitle(_('No library found'))
|
||||||
|
self._l = l = QGridLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
self.stats, self.location = stats, location
|
||||||
|
|
||||||
|
loc = self.oldloc = location.replace('/', os.sep)
|
||||||
|
self.header = QLabel(_('No existing calibre library was found at %s. '
|
||||||
|
'If the library was moved, select its new location below. '
|
||||||
|
'Otherwise calibre will forget this library.')%loc)
|
||||||
|
self.header.setWordWrap(True)
|
||||||
|
ncols = 2
|
||||||
|
l.addWidget(self.header, 0, 0, 1, ncols)
|
||||||
|
self.cl = QLabel('<br><b>'+_('New location of this library:'))
|
||||||
|
l.addWidget(self.cl, 1, 0, 1, ncols)
|
||||||
|
self.loc = QLineEdit(loc, self)
|
||||||
|
l.addWidget(self.loc, 2, 0, 1, 1)
|
||||||
|
self.cd = QToolButton(self)
|
||||||
|
self.cd.setIcon(QIcon(I('document_open.png')))
|
||||||
|
self.cd.clicked.connect(self.choose_dir)
|
||||||
|
l.addWidget(self.cd, 2, 1, 1, 1)
|
||||||
|
self.bb = QDialogButtonBox(self)
|
||||||
|
b = self.bb.addButton(_('Library moved'), self.bb.AcceptRole)
|
||||||
|
b.setIcon(QIcon(I('ok.png')))
|
||||||
|
b = self.bb.addButton(_('Forget library'), self.bb.RejectRole)
|
||||||
|
b.setIcon(QIcon(I('edit-clear.png')))
|
||||||
|
self.bb.accepted.connect(self.accept)
|
||||||
|
self.bb.rejected.connect(self.reject)
|
||||||
|
l.addWidget(self.bb, 3, 0, 1, ncols)
|
||||||
|
self.resize(self.sizeHint() + QSize(100, 50))
|
||||||
|
|
||||||
|
def choose_dir(self):
|
||||||
|
d = choose_dir(self, 'library moved choose new loc',
|
||||||
|
_('New library location'), default_dir=self.oldloc)
|
||||||
|
if d is not None:
|
||||||
|
self.loc.setText(d)
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
self.stats.remove(self.location)
|
||||||
|
QDialog.reject(self)
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
newloc = unicode(self.loc.text())
|
||||||
|
if not LibraryDatabase2.exists_at(newloc):
|
||||||
|
error_dialog(self, _('No library found'),
|
||||||
|
_('No existing calibre library found at %s')%newloc,
|
||||||
|
show=True)
|
||||||
|
return
|
||||||
|
self.stats.rename(self.location, newloc)
|
||||||
|
self.newloc = newloc
|
||||||
|
QDialog.accept(self)
|
||||||
|
# }}}
|
||||||
|
|
||||||
class ChooseLibraryAction(InterfaceAction):
|
class ChooseLibraryAction(InterfaceAction):
|
||||||
|
|
||||||
name = 'Choose Library'
|
name = 'Choose Library'
|
||||||
action_spec = (_('%d books'), 'lt.png',
|
action_spec = (_('Choose Library'), 'lt.png',
|
||||||
_('Choose calibre library to work with'), None)
|
_('Choose calibre library to work with'), None)
|
||||||
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
action_add_menu = True
|
action_add_menu = True
|
||||||
action_menu_clone_qaction = _('Switch/create library...')
|
action_menu_clone_qaction = _('Switch/create library...')
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
self.base_text = _('%d books')
|
||||||
self.count_changed(0)
|
self.count_changed(0)
|
||||||
self.qaction.triggered.connect(self.choose_library,
|
self.qaction.triggered.connect(self.choose_library,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
@ -338,13 +396,13 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
loc = location.replace('/', os.sep)
|
loc = location.replace('/', os.sep)
|
||||||
exists = self.gui.library_view.model().db.exists_at(loc)
|
exists = self.gui.library_view.model().db.exists_at(loc)
|
||||||
if not exists:
|
if not exists:
|
||||||
warning_dialog(self.gui, _('No library found'),
|
d = MovedDialog(self.stats, location, self.gui)
|
||||||
_('No existing calibre library was found at %s.'
|
ret = d.exec_()
|
||||||
' It will be removed from the list of known'
|
|
||||||
' libraries.')%loc, show=True)
|
|
||||||
self.stats.remove(location)
|
|
||||||
self.build_menus()
|
self.build_menus()
|
||||||
self.gui.iactions['Copy To Library'].build_menus()
|
self.gui.iactions['Copy To Library'].build_menus()
|
||||||
|
if ret == d.Accepted:
|
||||||
|
loc = d.newloc.replace('/', os.sep)
|
||||||
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
prefs['library_path'] = loc
|
prefs['library_path'] = loc
|
||||||
@ -376,7 +434,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
self.switch_requested(self.qs_locations[idx])
|
self.switch_requested(self.qs_locations[idx])
|
||||||
|
|
||||||
def count_changed(self, new_count):
|
def count_changed(self, new_count):
|
||||||
text = self.action_spec[0]%new_count
|
text = self.base_text%new_count
|
||||||
a = self.qaction
|
a = self.qaction
|
||||||
a.setText(text)
|
a.setText(text)
|
||||||
tooltip = self.action_spec[2] + '\n\n' + text
|
tooltip = self.action_spec[2] + '\n\n' + text
|
||||||
|
@ -12,9 +12,10 @@ from threading import Thread
|
|||||||
from PyQt4.Qt import QToolButton
|
from PyQt4.Qt import QToolButton
|
||||||
|
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
from calibre.gui2 import error_dialog, Dispatcher, warning_dialog
|
from calibre.gui2 import error_dialog, Dispatcher, warning_dialog, gprefs
|
||||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
|
from calibre.utils.date import now
|
||||||
|
|
||||||
class Worker(Thread): # {{{
|
class Worker(Thread): # {{{
|
||||||
|
|
||||||
@ -55,6 +56,8 @@ class Worker(Thread): # {{{
|
|||||||
for i, x in enumerate(self.ids):
|
for i, x in enumerate(self.ids):
|
||||||
mi = self.db.get_metadata(x, index_is_id=True, get_cover=True,
|
mi = self.db.get_metadata(x, index_is_id=True, get_cover=True,
|
||||||
cover_as_data=True)
|
cover_as_data=True)
|
||||||
|
if not gprefs['preserve_date_on_ctl']:
|
||||||
|
mi.timestamp = now()
|
||||||
self.progress(i, mi.title)
|
self.progress(i, mi.title)
|
||||||
fmts = self.db.formats(x, index_is_id=True)
|
fmts = self.db.formats(x, index_is_id=True)
|
||||||
if not fmts: fmts = []
|
if not fmts: fmts = []
|
||||||
@ -65,14 +68,37 @@ class Worker(Thread): # {{{
|
|||||||
as_path=True)
|
as_path=True)
|
||||||
if p:
|
if p:
|
||||||
paths.append(p)
|
paths.append(p)
|
||||||
added = False
|
automerged = False
|
||||||
if prefs['add_formats_to_existing']:
|
if prefs['add_formats_to_existing']:
|
||||||
identical_book_list = newdb.find_identical_books(mi)
|
identical_book_list = newdb.find_identical_books(mi)
|
||||||
if identical_book_list: # books with same author and nearly same title exist in newdb
|
if identical_book_list: # books with same author and nearly same title exist in newdb
|
||||||
added = True
|
automerged = True
|
||||||
|
seen_fmts = set()
|
||||||
for identical_book in identical_book_list:
|
for identical_book in identical_book_list:
|
||||||
self.add_formats(identical_book, paths, newdb, replace=False)
|
ib_fmts = newdb.formats(identical_book, index_is_id=True)
|
||||||
if not added:
|
if ib_fmts:
|
||||||
|
seen_fmts |= set(ib_fmts.split(','))
|
||||||
|
replace = gprefs['automerge'] == 'overwrite'
|
||||||
|
self.add_formats(identical_book, paths, newdb,
|
||||||
|
replace=replace)
|
||||||
|
|
||||||
|
if gprefs['automerge'] == 'new record':
|
||||||
|
incoming_fmts = \
|
||||||
|
set([os.path.splitext(path)[-1].replace('.',
|
||||||
|
'').upper() for path in paths])
|
||||||
|
|
||||||
|
if incoming_fmts.intersection(seen_fmts):
|
||||||
|
# There was at least one duplicate format
|
||||||
|
# so create a new record and put the
|
||||||
|
# incoming formats into it
|
||||||
|
# We should arguably put only the duplicate
|
||||||
|
# formats, but no real harm is done by having
|
||||||
|
# all formats
|
||||||
|
newdb.import_book(mi, paths, notify=False, import_hooks=False,
|
||||||
|
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
|
||||||
|
preserve_uuid=False)
|
||||||
|
|
||||||
|
if not automerged:
|
||||||
newdb.import_book(mi, paths, notify=False, import_hooks=False,
|
newdb.import_book(mi, paths, notify=False, import_hooks=False,
|
||||||
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
|
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
|
||||||
preserve_uuid=self.delete_after)
|
preserve_uuid=self.delete_after)
|
||||||
|
@ -63,11 +63,15 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
if hasattr(parent, 'keyboard'):
|
if hasattr(parent, 'keyboard'):
|
||||||
r = parent.keyboard.register_shortcut
|
r = parent.keyboard.register_shortcut
|
||||||
prefix = 'Share/Connect Menu '
|
prefix = 'Share/Connect Menu '
|
||||||
|
gr = ConnectShareAction.action_spec[0]
|
||||||
for attr in ('folder', 'bambook', 'itunes'):
|
for attr in ('folder', 'bambook', 'itunes'):
|
||||||
if not (iswindows or isosx) and attr == 'itunes':
|
if not (iswindows or isosx) and attr == 'itunes':
|
||||||
continue
|
continue
|
||||||
ac = getattr(self, 'connect_to_%s_action'%attr)
|
ac = getattr(self, 'connect_to_%s_action'%attr)
|
||||||
r(prefix + attr, unicode(ac.text()), action=ac)
|
r(prefix + attr, unicode(ac.text()), action=ac,
|
||||||
|
group=gr)
|
||||||
|
r(prefix+' content server', _('Start/stop content server'),
|
||||||
|
action=self.toggle_server_action, group=gr)
|
||||||
|
|
||||||
def server_state_changed(self, running):
|
def server_state_changed(self, running):
|
||||||
text = _('Start Content Server')
|
text = _('Start Content Server')
|
||||||
|
@ -11,7 +11,7 @@ from calibre.gui2.actions import InterfaceAction
|
|||||||
class RestartAction(InterfaceAction):
|
class RestartAction(InterfaceAction):
|
||||||
|
|
||||||
name = 'Restart'
|
name = 'Restart'
|
||||||
action_spec = (_('&Restart'), None, None, _('Ctrl+R'))
|
action_spec = (_('Restart'), None, None, _('Ctrl+R'))
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.qaction.triggered.connect(self.restart)
|
self.qaction.triggered.connect(self.restart)
|
||||||
|
@ -239,7 +239,7 @@ class DBAdder(QObject): # {{{
|
|||||||
|
|
||||||
class Adder(QObject): # {{{
|
class Adder(QObject): # {{{
|
||||||
|
|
||||||
ADD_TIMEOUT = 600 # seconds
|
ADD_TIMEOUT = 900 # seconds (15 minutes)
|
||||||
|
|
||||||
def __init__(self, parent, db, callback, spare_server=None):
|
def __init__(self, parent, db, callback, spare_server=None):
|
||||||
QObject.__init__(self, parent)
|
QObject.__init__(self, parent)
|
||||||
|
@ -87,7 +87,7 @@ class Int(Base):
|
|||||||
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
|
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
|
||||||
QSpinBox(parent)]
|
QSpinBox(parent)]
|
||||||
w = self.widgets[1]
|
w = self.widgets[1]
|
||||||
w.setRange(-100, 100000000)
|
w.setRange(-1000000, 100000000)
|
||||||
w.setSpecialValueText(_('Undefined'))
|
w.setSpecialValueText(_('Undefined'))
|
||||||
w.setSingleStep(1)
|
w.setSingleStep(1)
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ class Float(Int):
|
|||||||
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
|
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
|
||||||
QDoubleSpinBox(parent)]
|
QDoubleSpinBox(parent)]
|
||||||
w = self.widgets[1]
|
w = self.widgets[1]
|
||||||
w.setRange(-100., float(100000000))
|
w.setRange(-1000000., float(100000000))
|
||||||
w.setDecimals(2)
|
w.setDecimals(2)
|
||||||
w.setSpecialValueText(_('Undefined'))
|
w.setSpecialValueText(_('Undefined'))
|
||||||
w.setSingleStep(1)
|
w.setSingleStep(1)
|
||||||
@ -300,7 +300,6 @@ class Series(Base):
|
|||||||
w = QDoubleSpinBox(parent)
|
w = QDoubleSpinBox(parent)
|
||||||
w.setRange(-100., float(100000000))
|
w.setRange(-100., float(100000000))
|
||||||
w.setDecimals(2)
|
w.setDecimals(2)
|
||||||
w.setSpecialValueText(_('Undefined'))
|
|
||||||
w.setSingleStep(1)
|
w.setSingleStep(1)
|
||||||
self.idx_widget=w
|
self.idx_widget=w
|
||||||
self.widgets.append(w)
|
self.widgets.append(w)
|
||||||
@ -605,7 +604,7 @@ class BulkInt(BulkBase):
|
|||||||
|
|
||||||
def setup_ui(self, parent):
|
def setup_ui(self, parent):
|
||||||
self.make_widgets(parent, QSpinBox)
|
self.make_widgets(parent, QSpinBox)
|
||||||
self.main_widget.setRange(-100, 100000000)
|
self.main_widget.setRange(-1000000, 100000000)
|
||||||
self.main_widget.setSpecialValueText(_('Undefined'))
|
self.main_widget.setSpecialValueText(_('Undefined'))
|
||||||
self.main_widget.setSingleStep(1)
|
self.main_widget.setSingleStep(1)
|
||||||
|
|
||||||
@ -627,7 +626,7 @@ class BulkFloat(BulkInt):
|
|||||||
|
|
||||||
def setup_ui(self, parent):
|
def setup_ui(self, parent):
|
||||||
self.make_widgets(parent, QDoubleSpinBox)
|
self.make_widgets(parent, QDoubleSpinBox)
|
||||||
self.main_widget.setRange(-100., float(100000000))
|
self.main_widget.setRange(-1000000., float(100000000))
|
||||||
self.main_widget.setDecimals(2)
|
self.main_widget.setDecimals(2)
|
||||||
self.main_widget.setSpecialValueText(_('Undefined'))
|
self.main_widget.setSpecialValueText(_('Undefined'))
|
||||||
self.main_widget.setSingleStep(1)
|
self.main_widget.setSingleStep(1)
|
||||||
|
@ -193,6 +193,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
|||||||
self.recipe_model = recipe_model
|
self.recipe_model = recipe_model
|
||||||
self.recipe_model.do_refresh()
|
self.recipe_model.do_refresh()
|
||||||
self.count_label.setText(
|
self.count_label.setText(
|
||||||
|
# NOTE: Number of news sources
|
||||||
_('%s news sources') %
|
_('%s news sources') %
|
||||||
self.recipe_model.showing_count)
|
self.recipe_model.showing_count)
|
||||||
|
|
||||||
|
@ -8,48 +8,43 @@ __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (QObject, QKeySequence)
|
from PyQt4.Qt import (QObject, QKeySequence, QAbstractItemModel, QModelIndex,
|
||||||
|
Qt, QStyledItemDelegate, QTextDocument, QStyle, pyqtSignal, QFrame,
|
||||||
|
QApplication, QSize, QRectF, QWidget, QTreeView,
|
||||||
|
QGridLayout, QLabel, QRadioButton, QPushButton, QToolButton, QIcon)
|
||||||
|
|
||||||
from calibre.utils.config import JSONConfig
|
from calibre.utils.config import JSONConfig
|
||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
|
from calibre.utils.icu import sort_key, lower
|
||||||
|
from calibre.gui2 import NONE, error_dialog, info_dialog
|
||||||
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
from calibre.gui2.search_box import SearchBox2
|
||||||
|
|
||||||
|
ROOT = QModelIndex()
|
||||||
|
|
||||||
class NameConflict(ValueError):
|
class NameConflict(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Manager(QObject):
|
def finalize(shortcuts, custom_keys_map={}): # {{{
|
||||||
|
'''
|
||||||
def __init__(self, parent=None):
|
Resolve conflicts and assign keys to every action in shorcuts, which must
|
||||||
QObject.__init__(self, parent)
|
be a OrderedDict. User specified mappings of unique names to keys (as a
|
||||||
|
list of strings) should be passed in in custom_keys_map. Return a mapping
|
||||||
self.config = JSONConfig('shortcuts/main')
|
of unique names to resolved keys. Also sets the set_to_defaul member
|
||||||
self.custom_keys_map = {}
|
correctly for each shortcut.
|
||||||
self.shortcuts = OrderedDict()
|
'''
|
||||||
self.keys_map = {}
|
seen, keys_map = {}, {}
|
||||||
|
for unique_name, shortcut in shortcuts.iteritems():
|
||||||
for unique_name, keys in self.config.get(
|
custom_keys = custom_keys_map.get(unique_name, None)
|
||||||
'map', {}).iteritems():
|
|
||||||
self.custom_keys_map[unique_name] = tuple(keys)
|
|
||||||
|
|
||||||
def register_shortcut(self, unique_name, name, default_keys=(),
|
|
||||||
description=None, action=None):
|
|
||||||
if unique_name in self.shortcuts:
|
|
||||||
name = self.shortcuts[unique_name]['name']
|
|
||||||
raise NameConflict('Shortcut for %r already registered by %s'%(
|
|
||||||
unique_name, name))
|
|
||||||
shortcut = {'name':name, 'desc':description, 'action': action,
|
|
||||||
'default_keys':tuple(default_keys)}
|
|
||||||
self.shortcuts[unique_name] = shortcut
|
|
||||||
|
|
||||||
def finalize(self):
|
|
||||||
seen = {}
|
|
||||||
for unique_name, shortcut in self.shortcuts.iteritems():
|
|
||||||
custom_keys = self.custom_keys_map.get(unique_name, None)
|
|
||||||
if custom_keys is None:
|
if custom_keys is None:
|
||||||
candidates = shortcut['default_keys']
|
candidates = shortcut['default_keys']
|
||||||
|
shortcut['set_to_default'] = True
|
||||||
else:
|
else:
|
||||||
candidates = custom_keys
|
candidates = custom_keys
|
||||||
|
shortcut['set_to_default'] = False
|
||||||
keys = []
|
keys = []
|
||||||
for x in candidates:
|
for x in candidates:
|
||||||
ks = QKeySequence(x, QKeySequence.PortableText)
|
ks = QKeySequence(x, QKeySequence.PortableText)
|
||||||
@ -64,8 +59,566 @@ class Manager(QObject):
|
|||||||
keys = tuple(keys)
|
keys = tuple(keys)
|
||||||
#print (111111, unique_name, candidates, keys)
|
#print (111111, unique_name, candidates, keys)
|
||||||
|
|
||||||
self.keys_map[unique_name] = keys
|
keys_map[unique_name] = keys
|
||||||
ac = shortcut['action']
|
ac = shortcut['action']
|
||||||
if ac is not None:
|
if ac is not None:
|
||||||
ac.setShortcuts(list(keys))
|
ac.setShortcuts(list(keys))
|
||||||
|
|
||||||
|
return keys_map
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class Manager(QObject): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QObject.__init__(self, parent)
|
||||||
|
|
||||||
|
self.config = JSONConfig('shortcuts/main')
|
||||||
|
self.shortcuts = OrderedDict()
|
||||||
|
self.keys_map = {}
|
||||||
|
self.groups = {}
|
||||||
|
|
||||||
|
def register_shortcut(self, unique_name, name, default_keys=(),
|
||||||
|
description=None, action=None, group=None):
|
||||||
|
'''
|
||||||
|
Register a shortcut with calibre. calibre will manage the shortcut,
|
||||||
|
automatically resolving conflicts and allowing the user to customize
|
||||||
|
it.
|
||||||
|
|
||||||
|
:param unique_name: A string that uniquely identifies this shortcut
|
||||||
|
:param name: A user visible name describing the action performed by
|
||||||
|
this shortcut
|
||||||
|
:param default_keys: A tuple of keys that trigger this shortcut. Each
|
||||||
|
key must be a string. For example: ('Ctrl+A', 'Alt+B', 'C',
|
||||||
|
'Shift+Meta+D'). These keys will be assigned to the
|
||||||
|
shortcut unless there is a conflict.
|
||||||
|
:param action: A QAction object. The shortcut will cause this QAction
|
||||||
|
to be triggered. Connect to its triggered signal in your code to
|
||||||
|
respond to the shortcut.
|
||||||
|
:param group: A string describing what "group" this shortcut belongs
|
||||||
|
to. This is used to organize the list of shortcuts when the user is
|
||||||
|
customizing them.
|
||||||
|
'''
|
||||||
|
if unique_name in self.shortcuts:
|
||||||
|
name = self.shortcuts[unique_name]['name']
|
||||||
|
raise NameConflict('Shortcut for %r already registered by %s'%(
|
||||||
|
unique_name, name))
|
||||||
|
shortcut = {'name':name, 'desc':description, 'action': action,
|
||||||
|
'default_keys':tuple(default_keys)}
|
||||||
|
self.shortcuts[unique_name] = shortcut
|
||||||
|
group = group if group else _('Miscellaneous')
|
||||||
|
self.groups[group] = self.groups.get(group, []) + [unique_name]
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
custom_keys_map = {un:tuple(keys) for un, keys in self.config.get(
|
||||||
|
'map', {}).iteritems()}
|
||||||
|
self.keys_map = finalize(self.shortcuts, custom_keys_map=custom_keys_map)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Model {{{
|
||||||
|
|
||||||
|
class Node(object):
|
||||||
|
|
||||||
|
def __init__(self, group_map, shortcut_map, name=None, shortcut=None):
|
||||||
|
self.data = name if name is not None else shortcut
|
||||||
|
self.is_shortcut = shortcut is not None
|
||||||
|
self.children = []
|
||||||
|
if name is not None:
|
||||||
|
self.children = [Node(None, None, shortcut=shortcut_map[uname])
|
||||||
|
for uname in group_map[name]]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.children)
|
||||||
|
|
||||||
|
def __getitem__(self, row):
|
||||||
|
return self.children[row]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for child in self.children:
|
||||||
|
yield child
|
||||||
|
|
||||||
|
class ConfigModel(QAbstractItemModel, SearchQueryParser):
|
||||||
|
|
||||||
|
def __init__(self, keyboard, parent=None):
|
||||||
|
QAbstractItemModel.__init__(self, parent)
|
||||||
|
SearchQueryParser.__init__(self, ['all'])
|
||||||
|
|
||||||
|
self.keyboard = keyboard
|
||||||
|
groups = sorted(keyboard.groups, key=sort_key)
|
||||||
|
shortcut_map = {k:v.copy() for k, v in
|
||||||
|
self.keyboard.shortcuts.iteritems()}
|
||||||
|
for un, s in shortcut_map.iteritems():
|
||||||
|
s['keys'] = tuple(self.keyboard.keys_map[un])
|
||||||
|
s['unique_name'] = un
|
||||||
|
s['group'] = [g for g, names in self.keyboard.groups.iteritems() if un in
|
||||||
|
names][0]
|
||||||
|
|
||||||
|
group_map = {group:sorted(names, key=lambda x:
|
||||||
|
sort_key(shortcut_map[x]['name'])) for group, names in
|
||||||
|
self.keyboard.groups.iteritems()}
|
||||||
|
|
||||||
|
self.data = [Node(group_map, shortcut_map, group) for group in groups]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_shortcuts(self):
|
||||||
|
for group in self.data:
|
||||||
|
for sc in group:
|
||||||
|
yield sc
|
||||||
|
|
||||||
|
def rowCount(self, parent=ROOT):
|
||||||
|
ip = parent.internalPointer()
|
||||||
|
if ip is None:
|
||||||
|
return len(self.data)
|
||||||
|
return len(ip)
|
||||||
|
|
||||||
|
def columnCount(self, parent=ROOT):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def index(self, row, column, parent=ROOT):
|
||||||
|
ip = parent.internalPointer()
|
||||||
|
if ip is None:
|
||||||
|
ip = self.data
|
||||||
|
try:
|
||||||
|
return self.createIndex(row, column, ip[row])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return ROOT
|
||||||
|
|
||||||
|
def parent(self, index):
|
||||||
|
ip = index.internalPointer()
|
||||||
|
if ip is None or not ip.is_shortcut:
|
||||||
|
return ROOT
|
||||||
|
group = ip.data['group']
|
||||||
|
for i, g in enumerate(self.data):
|
||||||
|
if g.data == group:
|
||||||
|
return self.index(i, 0)
|
||||||
|
return ROOT
|
||||||
|
|
||||||
|
def data(self, index, role=Qt.DisplayRole):
|
||||||
|
ip = index.internalPointer()
|
||||||
|
if ip is not None and role == Qt.UserRole:
|
||||||
|
return ip
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
ans = QAbstractItemModel.flags(self, index)
|
||||||
|
ip = index.internalPointer()
|
||||||
|
if getattr(ip, 'is_shortcut', False):
|
||||||
|
ans |= Qt.ItemIsEditable
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def restore_defaults(self):
|
||||||
|
shortcut_map = {}
|
||||||
|
for node in self.all_shortcuts:
|
||||||
|
sc = node.data
|
||||||
|
shortcut_map[sc['unique_name']] = sc
|
||||||
|
shortcuts = OrderedDict([(un, shortcut_map[un]) for un in
|
||||||
|
self.keyboard.shortcuts])
|
||||||
|
keys_map = finalize(shortcuts)
|
||||||
|
for node in self.all_shortcuts:
|
||||||
|
s = node.data
|
||||||
|
s['keys'] = tuple(keys_map[s['unique_name']])
|
||||||
|
for r in xrange(self.rowCount()):
|
||||||
|
group = self.index(r, 0)
|
||||||
|
num = self.rowCount(group)
|
||||||
|
if num > 0:
|
||||||
|
self.dataChanged.emit(self.index(0, 0, group),
|
||||||
|
self.index(num-1, 0, group))
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
kmap = {}
|
||||||
|
for node in self.all_shortcuts:
|
||||||
|
sc = node.data
|
||||||
|
if sc['set_to_default']: continue
|
||||||
|
keys = [unicode(k.toString(k.PortableText)) for k in sc['keys']]
|
||||||
|
kmap[sc['unique_name']] = keys
|
||||||
|
self.keyboard.config['map'] = kmap
|
||||||
|
|
||||||
|
def universal_set(self):
|
||||||
|
ans = set()
|
||||||
|
for i, group in enumerate(self.data):
|
||||||
|
ans.add((i, -1))
|
||||||
|
for j, sc in enumerate(group.children):
|
||||||
|
ans.add((i, j))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def get_matches(self, location, query, candidates=None):
|
||||||
|
if candidates is None:
|
||||||
|
candidates = self.universal_set()
|
||||||
|
ans = set([])
|
||||||
|
if not query:
|
||||||
|
return ans
|
||||||
|
query = lower(query)
|
||||||
|
for c, p in candidates:
|
||||||
|
if p < 0:
|
||||||
|
if query in lower(self.data[c].data):
|
||||||
|
ans.add((c, p))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
sc = self.data[c].children[p].data
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
if query in lower(sc['name']):
|
||||||
|
ans.add((c, p))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def find(self, query):
|
||||||
|
query = query.strip()
|
||||||
|
if not query:
|
||||||
|
return ROOT
|
||||||
|
matches = self.parse(query)
|
||||||
|
if not matches:
|
||||||
|
return ROOT
|
||||||
|
matches = list(sorted(matches))
|
||||||
|
c, p = matches[0]
|
||||||
|
cat_idx = self.index(c, 0)
|
||||||
|
if p == -1:
|
||||||
|
return cat_idx
|
||||||
|
return self.index(p, 0, cat_idx)
|
||||||
|
|
||||||
|
def find_next(self, idx, query, backwards=False):
|
||||||
|
query = query.strip()
|
||||||
|
if not query:
|
||||||
|
return idx
|
||||||
|
matches = self.parse(query)
|
||||||
|
if not matches:
|
||||||
|
return idx
|
||||||
|
if idx.parent().isValid():
|
||||||
|
loc = (idx.parent().row(), idx.row())
|
||||||
|
else:
|
||||||
|
loc = (idx.row(), -1)
|
||||||
|
if loc not in matches:
|
||||||
|
return self.find(query)
|
||||||
|
if len(matches) == 1:
|
||||||
|
return ROOT
|
||||||
|
matches = list(sorted(matches))
|
||||||
|
i = matches.index(loc)
|
||||||
|
if backwards:
|
||||||
|
ans = i - 1 if i - 1 >= 0 else len(matches)-1
|
||||||
|
else:
|
||||||
|
ans = i + 1 if i + 1 < len(matches) else 0
|
||||||
|
|
||||||
|
ans = matches[ans]
|
||||||
|
|
||||||
|
return (self.index(ans[0], 0) if ans[1] < 0 else
|
||||||
|
self.index(ans[1], 0, self.index(ans[0], 0)))
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class Editor(QFrame): # {{{
|
||||||
|
|
||||||
|
editing_done = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QFrame.__init__(self, parent)
|
||||||
|
self.setFocusPolicy(Qt.StrongFocus)
|
||||||
|
self.setAutoFillBackground(True)
|
||||||
|
self.capture = 0
|
||||||
|
|
||||||
|
self.setFrameShape(self.StyledPanel)
|
||||||
|
self.setFrameShadow(self.Raised)
|
||||||
|
self._layout = l = QGridLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
|
||||||
|
self.header = QLabel('')
|
||||||
|
l.addWidget(self.header, 0, 0, 1, 2)
|
||||||
|
|
||||||
|
self.use_default = QRadioButton('')
|
||||||
|
self.use_custom = QRadioButton(_('Custom'))
|
||||||
|
l.addWidget(self.use_default, 1, 0, 1, 3)
|
||||||
|
l.addWidget(self.use_custom, 2, 0, 1, 3)
|
||||||
|
self.use_custom.toggled.connect(self.custom_toggled)
|
||||||
|
|
||||||
|
off = 2
|
||||||
|
for which in (1, 2):
|
||||||
|
text = _('&Shortcut:') if which == 1 else _('&Alternate shortcut:')
|
||||||
|
la = QLabel(text)
|
||||||
|
la.setStyleSheet('QLabel { margin-left: 1.5em }')
|
||||||
|
l.addWidget(la, off+which, 0, 1, 3)
|
||||||
|
setattr(self, 'label%d'%which, la)
|
||||||
|
button = QPushButton(_('None'), self)
|
||||||
|
button.clicked.connect(partial(self.capture_clicked, which=which))
|
||||||
|
button.keyPressEvent = partial(self.key_press_event, which=which)
|
||||||
|
setattr(self, 'button%d'%which, button)
|
||||||
|
clear = QToolButton(self)
|
||||||
|
clear.setIcon(QIcon(I('clear_left.png')))
|
||||||
|
clear.clicked.connect(partial(self.clear_clicked, which=which))
|
||||||
|
setattr(self, 'clear%d'%which, clear)
|
||||||
|
l.addWidget(button, off+which, 1, 1, 1)
|
||||||
|
l.addWidget(clear, off+which, 2, 1, 1)
|
||||||
|
la.setBuddy(button)
|
||||||
|
|
||||||
|
self.done_button = doneb = QPushButton(_('Done'), self)
|
||||||
|
l.addWidget(doneb, 0, 2, 1, 1)
|
||||||
|
doneb.clicked.connect(lambda : self.editing_done.emit(self))
|
||||||
|
l.setColumnStretch(0, 100)
|
||||||
|
|
||||||
|
self.custom_toggled(False)
|
||||||
|
|
||||||
|
def initialize(self, shortcut, all_shortcuts):
|
||||||
|
self.header.setText('<b>%s: %s</b>'%(_('Customize'), shortcut['name']))
|
||||||
|
self.all_shortcuts = all_shortcuts
|
||||||
|
self.shortcut = shortcut
|
||||||
|
|
||||||
|
self.default_keys = [QKeySequence(k, QKeySequence.PortableText) for k
|
||||||
|
in shortcut['default_keys']]
|
||||||
|
self.current_keys = list(shortcut['keys'])
|
||||||
|
default = ', '.join([unicode(k.toString(k.NativeText)) for k in
|
||||||
|
self.default_keys])
|
||||||
|
if not default: default = _('None')
|
||||||
|
current = ', '.join([unicode(k.toString(k.NativeText)) for k in
|
||||||
|
self.current_keys])
|
||||||
|
if not current: current = _('None')
|
||||||
|
|
||||||
|
self.use_default.setText(_('Default: %s [Currently not conflicting: %s]')%
|
||||||
|
(default, current))
|
||||||
|
|
||||||
|
if shortcut['set_to_default']:
|
||||||
|
self.use_default.setChecked(True)
|
||||||
|
else:
|
||||||
|
self.use_custom.setChecked(True)
|
||||||
|
for key, which in zip(self.current_keys, [1,2]):
|
||||||
|
button = getattr(self, 'button%d'%which)
|
||||||
|
button.setText(key.toString(key.NativeText))
|
||||||
|
|
||||||
|
def custom_toggled(self, checked):
|
||||||
|
for w in ('1', '2'):
|
||||||
|
for o in ('label', 'button', 'clear'):
|
||||||
|
getattr(self, o+w).setEnabled(checked)
|
||||||
|
|
||||||
|
def capture_clicked(self, which=1):
|
||||||
|
self.capture = which
|
||||||
|
button = getattr(self, 'button%d'%which)
|
||||||
|
button.setText(_('Press a key...'))
|
||||||
|
button.setFocus(Qt.OtherFocusReason)
|
||||||
|
button.setStyleSheet('QPushButton { font-weight: bold}')
|
||||||
|
|
||||||
|
def clear_clicked(self, which=0):
|
||||||
|
button = getattr(self, 'button%d'%which)
|
||||||
|
button.setText(_('None'))
|
||||||
|
|
||||||
|
def key_press_event(self, ev, which=0):
|
||||||
|
code = ev.key()
|
||||||
|
if self.capture == 0 or code in (0, Qt.Key_unknown,
|
||||||
|
Qt.Key_Shift, Qt.Key_Control, Qt.Key_Alt, Qt.Key_Meta,
|
||||||
|
Qt.Key_AltGr, Qt.Key_CapsLock, Qt.Key_NumLock, Qt.Key_ScrollLock):
|
||||||
|
return QWidget.keyPressEvent(self, ev)
|
||||||
|
button = getattr(self, 'button%d'%which)
|
||||||
|
button.setStyleSheet('QPushButton { font-weight: normal}')
|
||||||
|
sequence = QKeySequence(code|(int(ev.modifiers())&~Qt.KeypadModifier))
|
||||||
|
button.setText(sequence.toString(QKeySequence.NativeText))
|
||||||
|
self.capture = 0
|
||||||
|
dup_desc = self.dup_check(sequence)
|
||||||
|
if dup_desc is not None:
|
||||||
|
error_dialog(self, _('Already assigned'),
|
||||||
|
unicode(sequence.toString(QKeySequence.NativeText)) + ' ' +
|
||||||
|
_('already assigned to') + ' ' + dup_desc, show=True)
|
||||||
|
self.clear_clicked(which=which)
|
||||||
|
|
||||||
|
def dup_check(self, sequence):
|
||||||
|
for sc in self.all_shortcuts:
|
||||||
|
if sc is self.shortcut: continue
|
||||||
|
for k in sc['keys']:
|
||||||
|
if k == sequence:
|
||||||
|
return sc['name']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def custom_keys(self):
|
||||||
|
if self.use_default.isChecked():
|
||||||
|
return None
|
||||||
|
ans = []
|
||||||
|
for which in (1, 2):
|
||||||
|
button = getattr(self, 'button%d'%which)
|
||||||
|
t = unicode(button.text())
|
||||||
|
if t == _('None'):
|
||||||
|
continue
|
||||||
|
ks = QKeySequence(t, QKeySequence.NativeText)
|
||||||
|
if not ks.isEmpty():
|
||||||
|
ans.append(ks)
|
||||||
|
return tuple(ans)
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class Delegate(QStyledItemDelegate): # {{{
|
||||||
|
|
||||||
|
changed_signal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QStyledItemDelegate.__init__(self, parent)
|
||||||
|
self.editing_index = None
|
||||||
|
self.closeEditor.connect(self.editing_done)
|
||||||
|
|
||||||
|
def to_doc(self, index):
|
||||||
|
data = index.data(Qt.UserRole).toPyObject()
|
||||||
|
if data is None:
|
||||||
|
html = _('<b>This shortcut no longer exists</b>')
|
||||||
|
elif data.is_shortcut:
|
||||||
|
shortcut = data.data
|
||||||
|
# Shortcut
|
||||||
|
keys = [unicode(k.toString(k.NativeText)) for k in shortcut['keys']]
|
||||||
|
if not keys:
|
||||||
|
keys = _('None')
|
||||||
|
else:
|
||||||
|
keys = ', '.join(keys)
|
||||||
|
html = '<b>%s</b><br>%s: %s'%(shortcut['name'], _('Shortcuts'), keys)
|
||||||
|
else:
|
||||||
|
# Group
|
||||||
|
html = '<h3>%s</h3>'%data.data
|
||||||
|
doc = QTextDocument()
|
||||||
|
doc.setHtml(html)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def sizeHint(self, option, index):
|
||||||
|
if index == self.editing_index:
|
||||||
|
return QSize(200, 200)
|
||||||
|
ans = self.to_doc(index).size().toSize()
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def paint(self, painter, option, index):
|
||||||
|
painter.save()
|
||||||
|
painter.setClipRect(QRectF(option.rect))
|
||||||
|
if hasattr(QStyle, 'CE_ItemViewItem'):
|
||||||
|
QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
|
||||||
|
elif option.state & QStyle.State_Selected:
|
||||||
|
painter.fillRect(option.rect, option.palette.highlight())
|
||||||
|
painter.translate(option.rect.topLeft())
|
||||||
|
self.to_doc(index).drawContents(painter)
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
w = Editor(parent=parent)
|
||||||
|
w.editing_done.connect(self.editor_done)
|
||||||
|
self.editing_index = index
|
||||||
|
self.sizeHintChanged.emit(index)
|
||||||
|
return w
|
||||||
|
|
||||||
|
def editor_done(self, editor):
|
||||||
|
self.commitData.emit(editor)
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
all_shortcuts = [x.data for x in index.model().all_shortcuts]
|
||||||
|
shortcut = index.internalPointer().data
|
||||||
|
editor.initialize(shortcut, all_shortcuts)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
self.closeEditor.emit(editor, self.NoHint)
|
||||||
|
custom_keys = editor.custom_keys
|
||||||
|
sc = index.data(Qt.UserRole).toPyObject().data
|
||||||
|
if custom_keys is None:
|
||||||
|
candidates = []
|
||||||
|
for ckey in sc['default_keys']:
|
||||||
|
ckey = QKeySequence(ckey, QKeySequence.PortableText)
|
||||||
|
matched = False
|
||||||
|
for s in editor.all_shortcuts:
|
||||||
|
for k in s['keys']:
|
||||||
|
if k == ckey:
|
||||||
|
matched = True
|
||||||
|
break
|
||||||
|
if not matched:
|
||||||
|
candidates.append(ckey)
|
||||||
|
candidates = tuple(candidates)
|
||||||
|
sc['set_to_default'] = True
|
||||||
|
else:
|
||||||
|
sc['set_to_default'] = False
|
||||||
|
candidates = custom_keys
|
||||||
|
sc['keys'] = candidates
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
editor.setGeometry(option.rect)
|
||||||
|
|
||||||
|
def editing_done(self, *args):
|
||||||
|
idx = self.editing_index
|
||||||
|
self.editing_index = None
|
||||||
|
if idx is not None:
|
||||||
|
self.sizeHintChanged.emit(idx)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class ShortcutConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
changed_signal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self._layout = l = QGridLayout()
|
||||||
|
self.setLayout(self._layout)
|
||||||
|
self.header = QLabel(_('Double click on any entry to change the'
|
||||||
|
' keyboard shortcuts associated with it'))
|
||||||
|
l.addWidget(self.header, 0, 0, 1, 3)
|
||||||
|
self.view = QTreeView(self)
|
||||||
|
self.view.setAlternatingRowColors(True)
|
||||||
|
self.view.setHeaderHidden(True)
|
||||||
|
self.view.setAnimated(True)
|
||||||
|
l.addWidget(self.view, 1, 0, 1, 3)
|
||||||
|
self.delegate = Delegate()
|
||||||
|
self.view.setItemDelegate(self.delegate)
|
||||||
|
self.delegate.sizeHintChanged.connect(self.scrollTo)
|
||||||
|
self.delegate.changed_signal.connect(self.changed_signal)
|
||||||
|
self.search = SearchBox2(self)
|
||||||
|
self.search.initialize('shortcuts_search_history',
|
||||||
|
help_text=_('Search for a shortcut by name'))
|
||||||
|
self.search.search.connect(self.find)
|
||||||
|
l.addWidget(self.search, 2, 0, 1, 1)
|
||||||
|
self.nb = QPushButton(QIcon(I('arrow-down.png')), _('&Next'), self)
|
||||||
|
self.pb = QPushButton(QIcon(I('arrow-up.png')), _('&Previous'), self)
|
||||||
|
self.nb.clicked.connect(self.find_next)
|
||||||
|
self.pb.clicked.connect(self.find_previous)
|
||||||
|
l.addWidget(self.nb, 2, 1, 1, 1)
|
||||||
|
l.addWidget(self.pb, 2, 2, 1, 1)
|
||||||
|
l.setColumnStretch(0, 100)
|
||||||
|
|
||||||
|
def restore_defaults(self):
|
||||||
|
self._model.restore_defaults()
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
self._model.commit()
|
||||||
|
|
||||||
|
def initialize(self, keyboard):
|
||||||
|
self._model = ConfigModel(keyboard, parent=self)
|
||||||
|
self.view.setModel(self._model)
|
||||||
|
|
||||||
|
def scrollTo(self, index):
|
||||||
|
if index is not None:
|
||||||
|
self.view.scrollTo(index, self.view.PositionAtTop)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_editing(self):
|
||||||
|
return self.view.state() == self.view.EditingState
|
||||||
|
|
||||||
|
def find(self, query):
|
||||||
|
idx = self._model.find(query)
|
||||||
|
if not idx.isValid():
|
||||||
|
return info_dialog(self, _('No matches'),
|
||||||
|
_('Could not find any matching shortcuts'), show=True,
|
||||||
|
show_copy_button=False)
|
||||||
|
self.highlight_index(idx)
|
||||||
|
|
||||||
|
def highlight_index(self, idx):
|
||||||
|
self.view.scrollTo(idx)
|
||||||
|
self.view.selectionModel().select(idx,
|
||||||
|
self.view.selectionModel().ClearAndSelect)
|
||||||
|
self.view.setCurrentIndex(idx)
|
||||||
|
|
||||||
|
def find_next(self, *args):
|
||||||
|
idx = self.view.currentIndex()
|
||||||
|
if not idx.isValid():
|
||||||
|
idx = self._model.index(0, 0)
|
||||||
|
idx = self._model.find_next(idx,
|
||||||
|
unicode(self.search.currentText()))
|
||||||
|
self.highlight_index(idx)
|
||||||
|
|
||||||
|
def find_previous(self, *args):
|
||||||
|
idx = self.view.currentIndex()
|
||||||
|
if not idx.isValid():
|
||||||
|
idx = self._model.index(0, 0)
|
||||||
|
idx = self._model.find_next(idx,
|
||||||
|
unicode(self.search.currentText()), backwards=True)
|
||||||
|
self.highlight_index(idx)
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -300,13 +300,13 @@ class CcNumberDelegate(QStyledItemDelegate): # {{{
|
|||||||
col = m.column_map[index.column()]
|
col = m.column_map[index.column()]
|
||||||
if m.custom_columns[col]['datatype'] == 'int':
|
if m.custom_columns[col]['datatype'] == 'int':
|
||||||
editor = QSpinBox(parent)
|
editor = QSpinBox(parent)
|
||||||
editor.setRange(-100, 100000000)
|
editor.setRange(-1000000, 100000000)
|
||||||
editor.setSpecialValueText(_('Undefined'))
|
editor.setSpecialValueText(_('Undefined'))
|
||||||
editor.setSingleStep(1)
|
editor.setSingleStep(1)
|
||||||
else:
|
else:
|
||||||
editor = QDoubleSpinBox(parent)
|
editor = QDoubleSpinBox(parent)
|
||||||
editor.setSpecialValueText(_('Undefined'))
|
editor.setSpecialValueText(_('Undefined'))
|
||||||
editor.setRange(-100., 100000000)
|
editor.setRange(-1000000., 100000000)
|
||||||
editor.setDecimals(2)
|
editor.setDecimals(2)
|
||||||
return editor
|
return editor
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
'rating' : _('Rating'),
|
'rating' : _('Rating'),
|
||||||
'publisher' : _("Publisher"),
|
'publisher' : _("Publisher"),
|
||||||
'tags' : _("Tags"),
|
'tags' : _("Tags"),
|
||||||
'series' : _("Series"),
|
'series' : ngettext("Series", 'Series', 1),
|
||||||
'last_modified' : _('Modified'),
|
'last_modified' : _('Modified'),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -694,7 +694,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
# we will get asked to display columns we don't know about. Must test for this.
|
# we will get asked to display columns we don't know about. Must test for this.
|
||||||
if col >= len(self.column_to_dc_map):
|
if col >= len(self.column_to_dc_map):
|
||||||
return NONE
|
return NONE
|
||||||
if role in (Qt.DisplayRole, Qt.EditRole):
|
if role in (Qt.DisplayRole, Qt.EditRole, Qt.ToolTipRole):
|
||||||
return self.column_to_dc_map[col](index.row())
|
return self.column_to_dc_map[col](index.row())
|
||||||
elif role == Qt.BackgroundRole:
|
elif role == Qt.BackgroundRole:
|
||||||
if self.id(index) in self.ids_to_highlight_set:
|
if self.id(index) in self.ids_to_highlight_set:
|
||||||
|
@ -719,7 +719,7 @@ class BooksView(QTableView): # {{{
|
|||||||
break
|
break
|
||||||
|
|
||||||
def set_current_row(self, row, select=True):
|
def set_current_row(self, row, select=True):
|
||||||
if row > -1:
|
if row > -1 and row < self.model().rowCount(QModelIndex()):
|
||||||
h = self.horizontalHeader()
|
h = self.horizontalHeader()
|
||||||
logical_indices = list(range(h.count()))
|
logical_indices = list(range(h.count()))
|
||||||
logical_indices = [x for x in logical_indices if not
|
logical_indices = [x for x in logical_indices if not
|
||||||
|
@ -24,6 +24,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
r('read_file_metadata', prefs)
|
r('read_file_metadata', prefs)
|
||||||
r('swap_author_names', prefs)
|
r('swap_author_names', prefs)
|
||||||
r('add_formats_to_existing', prefs)
|
r('add_formats_to_existing', prefs)
|
||||||
|
r('preserve_date_on_ctl', gprefs)
|
||||||
choices = [
|
choices = [
|
||||||
(_('Ignore duplicate incoming formats'), 'ignore'),
|
(_('Ignore duplicate incoming formats'), 'ignore'),
|
||||||
(_('Overwrite existing duplicate formats'), 'overwrite'),
|
(_('Overwrite existing duplicate formats'), 'overwrite'),
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="opt_read_file_metadata">
|
<widget class="QCheckBox" name="opt_read_file_metadata">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Read &metadata from &file contents rather than file name</string>
|
<string>Read &metadata from file contents rather than file name</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -58,7 +58,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QCheckBox" name="opt_add_formats_to_existing">
|
<widget class="QCheckBox" name="opt_add_formats_to_existing">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
||||||
@ -72,7 +72,7 @@ Title match ignores leading indefinite articles ("the", "a",
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QComboBox" name="opt_automerge">
|
<widget class="QComboBox" name="opt_automerge">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
||||||
@ -88,7 +88,7 @@ Author matching is exact.</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_230">
|
<widget class="QLabel" name="label_230">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Tags to apply when adding a book:</string>
|
<string>&Tags to apply when adding a book:</string>
|
||||||
@ -98,14 +98,14 @@ Author matching is exact.</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QLineEdit" name="opt_new_book_tags">
|
<widget class="QLineEdit" name="opt_new_book_tags">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>A comma-separated list of tags that will be applied to books added to the library</string>
|
<string>A comma-separated list of tags that will be applied to books added to the library</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="5" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="metadata_box">
|
<widget class="QGroupBox" name="metadata_box">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&Configure metadata from file name</string>
|
<string>&Configure metadata from file name</string>
|
||||||
@ -127,6 +127,13 @@ Author matching is exact.</string>
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_preserve_date_on_ctl">
|
||||||
|
<property name="text">
|
||||||
|
<string>When &copying books from one library to another, preserve the date</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
44
src/calibre/gui2/preferences/keyboard.py
Normal file
44
src/calibre/gui2/preferences/keyboard.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import QVBoxLayout
|
||||||
|
|
||||||
|
from calibre.gui2.preferences import (ConfigWidgetBase, test_widget)
|
||||||
|
from calibre.gui2.keyboard import ShortcutConfig
|
||||||
|
|
||||||
|
class ConfigWidget(ConfigWidgetBase):
|
||||||
|
|
||||||
|
def genesis(self, gui):
|
||||||
|
self.gui = gui
|
||||||
|
self.conf_widget = ShortcutConfig(self)
|
||||||
|
self.conf_widget.changed_signal.connect(self.changed_signal)
|
||||||
|
self._layout = l = QVBoxLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
l.addWidget(self.conf_widget)
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
ConfigWidgetBase.initialize(self)
|
||||||
|
self.conf_widget.initialize(self.gui.keyboard)
|
||||||
|
|
||||||
|
def restore_defaults(self):
|
||||||
|
ConfigWidgetBase.restore_defaults(self)
|
||||||
|
self.conf_widget.restore_defaults()
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
self.conf_widget.commit()
|
||||||
|
return ConfigWidgetBase.commit(self)
|
||||||
|
|
||||||
|
def refresh_gui(self, gui):
|
||||||
|
gui.keyboard.finalize()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from PyQt4.Qt import QApplication
|
||||||
|
app = QApplication([])
|
||||||
|
test_widget('Advanced', 'Keyboard')
|
||||||
|
|
@ -12,6 +12,7 @@ from calibre.gui2 import error_dialog, question_dialog
|
|||||||
from calibre.gui2.preferences.save_template_ui import Ui_Form
|
from calibre.gui2.preferences.save_template_ui import Ui_Form
|
||||||
from calibre.library.save_to_disk import FORMAT_ARG_DESCS, preprocess_template
|
from calibre.library.save_to_disk import FORMAT_ARG_DESCS, preprocess_template
|
||||||
from calibre.utils.formatter import validation_formatter
|
from calibre.utils.formatter import validation_formatter
|
||||||
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
|
|
||||||
|
|
||||||
class SaveTemplate(QWidget, Ui_Form):
|
class SaveTemplate(QWidget, Ui_Form):
|
||||||
@ -40,6 +41,14 @@ class SaveTemplate(QWidget, Ui_Form):
|
|||||||
self.opt_template.editTextChanged.connect(self.changed)
|
self.opt_template.editTextChanged.connect(self.changed)
|
||||||
self.opt_template.currentIndexChanged.connect(self.changed)
|
self.opt_template.currentIndexChanged.connect(self.changed)
|
||||||
self.option_name = name
|
self.option_name = name
|
||||||
|
self.open_editor.clicked.connect(self.do_open_editor)
|
||||||
|
|
||||||
|
def do_open_editor(self):
|
||||||
|
t = TemplateDialog(self, self.opt_template.text())
|
||||||
|
t.setWindowTitle(_('Edit template'))
|
||||||
|
if t.exec_():
|
||||||
|
self.opt_template.set_value(t.rule[1])
|
||||||
|
|
||||||
|
|
||||||
def changed(self, *args):
|
def changed(self, *args):
|
||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<string>Save &template</string>
|
<string>Save &template</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string.</string>
|
<string>By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string.</string>
|
||||||
@ -30,18 +30,38 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Available variables:</string>
|
<string>Available variables:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QTextBrowser" name="template_variables"/>
|
<widget class="QTextBrowser" name="template_variables"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="HistoryBox" name="opt_template"/>
|
<widget class="HistoryBox" name="opt_template">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>10</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>40</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="open_editor">
|
||||||
|
<property name="text">
|
||||||
|
<string>Template Editor</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -83,7 +83,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.builtin_source_dict = {}
|
self.builtin_source_dict = {}
|
||||||
|
|
||||||
self.funcs = formatter_functions.get_functions()
|
self.funcs = formatter_functions.get_functions()
|
||||||
self.builtins = formatter_functions.get_builtins()
|
self.builtins = formatter_functions.get_builtins_and_aliases()
|
||||||
|
|
||||||
self.build_function_names_box()
|
self.build_function_names_box()
|
||||||
self.function_name.currentIndexChanged[str].connect(self.function_index_changed)
|
self.function_name.currentIndexChanged[str].connect(self.function_index_changed)
|
||||||
@ -151,7 +151,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
name = unicode(self.function_name.currentText())
|
name = unicode(self.function_name.currentText())
|
||||||
if name in self.funcs:
|
if name in self.funcs:
|
||||||
error_dialog(self.gui, _('Template functions'),
|
error_dialog(self.gui, _('Template functions'),
|
||||||
_('Name already used'), show=True)
|
_('Name %s already used')%(name,), show=True)
|
||||||
return
|
return
|
||||||
if self.argument_count.value() == 0:
|
if self.argument_count.value() == 0:
|
||||||
box = warning_dialog(self.gui, _('Template functions'),
|
box = warning_dialog(self.gui, _('Template functions'),
|
||||||
|
@ -522,6 +522,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.card_a_view.reset()
|
self.card_a_view.reset()
|
||||||
self.card_b_view.reset()
|
self.card_b_view.reset()
|
||||||
self.device_manager.set_current_library_uuid(db.library_id)
|
self.device_manager.set_current_library_uuid(db.library_id)
|
||||||
|
self.library_view.set_current_row(0)
|
||||||
# Run a garbage collection now so that it does not freeze the
|
# Run a garbage collection now so that it does not freeze the
|
||||||
# interface later
|
# interface later
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
@ -979,6 +979,11 @@ class DocumentView(QWebView): # {{{
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
def keyPressEvent(self, event):
|
||||||
|
if not self.handle_key_press(event):
|
||||||
|
return QWebView.keyPressEvent(self, event)
|
||||||
|
|
||||||
|
def handle_key_press(self, event):
|
||||||
|
handled = True
|
||||||
key = self.shortcuts.get_match(event)
|
key = self.shortcuts.get_match(event)
|
||||||
func = self.goto_location_actions.get(key, None)
|
func = self.goto_location_actions.get(key, None)
|
||||||
if func is not None:
|
if func is not None:
|
||||||
@ -996,7 +1001,8 @@ class DocumentView(QWebView): # {{{
|
|||||||
elif key == 'Right':
|
elif key == 'Right':
|
||||||
self.scroll_by(x=15)
|
self.scroll_by(x=15)
|
||||||
else:
|
else:
|
||||||
return QWebView.keyPressEvent(self, event)
|
handled = False
|
||||||
|
return handled
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
ret = QWebView.resizeEvent(self, event)
|
ret = QWebView.resizeEvent(self, event)
|
||||||
|
@ -755,6 +755,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
if self.current_index > 0:
|
if self.current_index > 0:
|
||||||
self.load_path(self.iterator.spine[self.current_index-1], pos=1.0)
|
self.load_path(self.iterator.spine[self.current_index-1], pos=1.0)
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
MainWindow.keyPressEvent(self, event)
|
||||||
|
if not event.isAccepted():
|
||||||
|
if not self.view.handle_key_press(event):
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from xml.sax.saxutils import escape
|
|||||||
from lxml import etree
|
from lxml import etree
|
||||||
from types import StringType, UnicodeType
|
from types import StringType, UnicodeType
|
||||||
|
|
||||||
from calibre import prints, prepare_string_for_xml, strftime
|
from calibre import (prints, prepare_string_for_xml, strftime, force_unicode)
|
||||||
from calibre.constants import preferred_encoding, DEBUG
|
from calibre.constants import preferred_encoding, DEBUG
|
||||||
from calibre.customize import CatalogPlugin
|
from calibre.customize import CatalogPlugin
|
||||||
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
||||||
@ -1083,15 +1083,11 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.__totalSteps += incremental_jobs
|
self.__totalSteps += incremental_jobs
|
||||||
|
|
||||||
# Load section list templates
|
# Load section list templates
|
||||||
templates = []
|
templates = {}
|
||||||
with open(P('catalog/section_list_templates.py'), 'r') as f:
|
execfile(P('catalog/section_list_templates.py'), templates)
|
||||||
for line in f:
|
for name, template in templates.iteritems():
|
||||||
t = re.match("(by_.+_template)",line)
|
if name.startswith('by_') and name.endswith('_template'):
|
||||||
if t:
|
setattr(self, name, force_unicode(template, 'utf-8'))
|
||||||
templates.append(t.group(1))
|
|
||||||
execfile(P('catalog/section_list_templates.py'), locals())
|
|
||||||
for t in templates:
|
|
||||||
setattr(self,t,eval(t))
|
|
||||||
|
|
||||||
# Accessors
|
# Accessors
|
||||||
if True:
|
if True:
|
||||||
|
@ -296,8 +296,8 @@ def do_save_book_to_disk(id_, mi, cover, plugboards,
|
|||||||
replace_whitespace=opts.replace_whitespace, safe_format=False)
|
replace_whitespace=opts.replace_whitespace, safe_format=False)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise ValueError(_('Failed to calculate path for '
|
raise ValueError(_('Failed to calculate path for '
|
||||||
'save to disk. Template: %s\n'
|
'save to disk. Template: %(templ)s\n'
|
||||||
'Error: %s'%(opts.template, e)))
|
'Error: %(err)s')%dict(templ=opts.template, err=e))
|
||||||
if opts.single_dir:
|
if opts.single_dir:
|
||||||
components = components[-1:]
|
components = components[-1:]
|
||||||
if not components:
|
if not components:
|
||||||
|
@ -284,11 +284,22 @@ The most likely cause of this is your antivirus program. Try temporarily disabli
|
|||||||
I cannot send emails using |app|?
|
I cannot send emails using |app|?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Because of the large amount of spam in email, sending email can be tricky as different servers use different strategies to block email spam.
|
Because of the large amount of spam in email, sending email can be tricky, as different mail servers use different strategies to block email.
|
||||||
The most common problem is if you are sending email directly (without a mail relay) in |app|. Many servers (for example, Amazon) block email
|
The most common problem is if you are sending email directly (without a mail relay) in |app|. Many servers (for example, Amazon) block email
|
||||||
that does not come from a well known relay. The easiest way around this is to setup a free GMail account and then goto Preferences->Email in |app| and
|
that does not come from a well known relay. The most robust way to setup email sending in |app| is to do the following:
|
||||||
click the "Use Gmail" button. |app| will then use Gmail to send the mail. Remember to update the email preferences in on your Amazon Kindle page to
|
|
||||||
allow email sent from your Gmail email address.
|
* Create a free GMail account at `Google <http://www.gmail.com>`_.
|
||||||
|
* Goto Preferences->Email in |app| and click the "Use Gmail" button and fill in the information asked for.
|
||||||
|
* |app| will then use GMail to send the mail.
|
||||||
|
* If you are sending to your Kindle, remember to update the email preferences on your Amazon Kindle page to allow email sent from your GMail email address.
|
||||||
|
|
||||||
|
Even after doing this, you may have problems. One common source of problems is that some poorly designed antivirus
|
||||||
|
programs block |app| from opening a connection to send email. Try adding an exclusion for |app| in your
|
||||||
|
antivirus program.
|
||||||
|
|
||||||
|
.. note:: Google can disable your account if you use it to send large amounts of email. So, when using GMail to send mail |app| automatically restricts
|
||||||
|
itself to sending one book every five minutes. If you don't mind risking your account being blocked you can reduce this wait interval by
|
||||||
|
going to Preferences->Tweaks in |app|.
|
||||||
|
|
||||||
Why is my device not detected in linux?
|
Why is my device not detected in linux?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -307,7 +318,7 @@ must return ``CONFIG_SCSI_MULTI_LUN=y``. If you don't see either, you have to re
|
|||||||
My device is getting mounted read-only in linux, so |app| cannot connect to it?
|
My device is getting mounted read-only in linux, so |app| cannot connect to it?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
linux kernels mount devices read-only when their filesystems have errors. You can repair the filesystem with::
|
Linux kernels mount devices read-only when their filesystems have errors. You can repair the filesystem with::
|
||||||
|
|
||||||
sudo fsck.vfat -y /dev/sdc
|
sudo fsck.vfat -y /dev/sdc
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user