Merge from trunk

This commit is contained in:
Charles Haley 2011-08-11 13:13:24 +01:00
commit 7613deb908
25 changed files with 2265 additions and 497 deletions

View File

@ -251,8 +251,8 @@
- title: Techcrunch and Pecat
author: Darko Miletic
- title: Vio Mundo, IDG Now and Tojolaco
author: Diniz Bortoletto
- title: "Vio Mundo, IDG Now! and Tojolaco"
author: Diniz Bortolotto
- title: Geek and Poke, Automatiseringgids IT
author: DrMerry

912
imgsrc/keyboard-prefs.svg Normal file
View 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

View 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'})
]

View File

@ -11,7 +11,7 @@
<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 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.corner.js"></script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1029,7 +1029,7 @@ class TemplateFunctions(PreferencesPlugin):
category = 'Advanced'
gui_category = _('Advanced')
category_order = 5
name_order = 4
name_order = 5
config_widget = 'calibre.gui2.preferences.template_functions'
description = _('Create your own template functions')
@ -1092,6 +1092,17 @@ class Tweaks(PreferencesPlugin):
config_widget = 'calibre.gui2.preferences.tweaks'
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):
name = 'Misc'
icon = I('exec.png')
@ -1106,7 +1117,7 @@ class Misc(PreferencesPlugin):
plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard,
Email, Server, Plugins, Tweaks, Misc, TemplateFunctions,
MetadataSources]
MetadataSources, Keyboard]
#}}}

View File

@ -17,7 +17,7 @@ class FOLDER_DEVICE_FOR_CONFIG(USBMS):
description = _('Use an arbitrary folder as a device.')
author = 'John Schember/Charles Haley'
supported_platforms = ['windows', 'osx', 'linux']
FORMATS = BOOK_EXTENSIONS
FORMATS = list(BOOK_EXTENSIONS)
VENDOR_ID = [0xffff]
PRODUCT_ID = [0xffff]

View File

@ -169,7 +169,9 @@ def _config(): # {{{
c.add_opt('scheduler_search_history', default=[],
help='Search history for the recipe scheduler')
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,
help=_(
'Maximum number of simultaneous conversion/news download jobs. '
@ -425,6 +427,8 @@ class FileIconProvider(QFileIconProvider):
'snb' : 'snb',
'djv' : 'djvu',
'djvu' : 'djvu',
'xps' : 'xps',
'oxps' : 'xps',
}
def __init__(self):

View File

@ -8,9 +8,13 @@ __docformat__ = 'restructuredtext en'
from functools import partial
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.keyboard import NameConflict
class InterfaceAction(QObject):
@ -108,7 +112,10 @@ class InterfaceAction(QObject):
@property
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'):
if spec is None:
@ -129,7 +136,6 @@ class InterfaceAction(QObject):
a.setToolTip(text)
a.setStatusTip(text)
a.setWhatsThis(text)
keys = ()
shortcut_action = action
desc = tooltip if tooltip else None
if attr == 'qaction':
@ -138,9 +144,22 @@ class InterfaceAction(QObject):
keys = ((shortcut,) if isinstance(shortcut, basestring) else
tuple(shortcut))
self.gui.keyboard.register_shortcut(self.unique_name + ' - ' + attr,
unicode(shortcut_action.text()), default_keys=keys,
action=shortcut_action, description=desc)
if spec[0] and not (attr=='qaction' and self.popup_type ==
QToolButton.InstantPopup):
try:
self.gui.keyboard.register_shortcut(self.unique_name + ' - ' + attr,
unicode(spec[0]), default_keys=keys,
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:
setattr(self, attr, action)
if attr == 'qaction' and self.action_add_menu:
@ -166,10 +185,11 @@ class InterfaceAction(QObject):
ac.setToolTip(description)
ac.setStatusTip(description)
ac.setWhatsThis(description)
if shortcut is not False:
self.gui.keyboard.register_shortcut(unique_name,
unicode(text), default_keys=keys,
action=ac, description=description)
action=ac, description=description, group=self.action_spec[0])
if triggered is not None:
ac.triggered.connect(triggered)
return ac

View File

@ -8,13 +8,14 @@ __docformat__ = 'restructuredtext en'
import os
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.constants import filesystem_encoding, iswindows
from calibre.utils.config import prefs
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.gui2.actions import InterfaceAction
@ -76,16 +77,73 @@ class LibraryUsageStats(object): # {{{
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):
name = 'Choose Library'
action_spec = (_('%d books'), 'lt.png',
action_spec = (_('Choose Library'), 'lt.png',
_('Choose calibre library to work with'), None)
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
action_add_menu = True
action_menu_clone_qaction = _('Switch/create library...')
def genesis(self):
self.base_text = _('%d books')
self.count_changed(0)
self.qaction.triggered.connect(self.choose_library,
type=Qt.QueuedConnection)
@ -338,14 +396,14 @@ class ChooseLibraryAction(InterfaceAction):
loc = location.replace('/', os.sep)
exists = self.gui.library_view.model().db.exists_at(loc)
if not exists:
warning_dialog(self.gui, _('No library found'),
_('No existing calibre library was found at %s.'
' It will be removed from the list of known'
' libraries.')%loc, show=True)
self.stats.remove(location)
d = MovedDialog(self.stats, location, self.gui)
ret = d.exec_()
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
return
if ret == d.Accepted:
loc = d.newloc.replace('/', os.sep)
else:
return
prefs['library_path'] = loc
#from calibre.utils.mem import memory
@ -376,7 +434,7 @@ class ChooseLibraryAction(InterfaceAction):
self.switch_requested(self.qs_locations[idx])
def count_changed(self, new_count):
text = self.action_spec[0]%new_count
text = self.base_text%new_count
a = self.qaction
a.setText(text)
tooltip = self.action_spec[2] + '\n\n' + text

View File

@ -12,7 +12,7 @@ from threading import Thread
from PyQt4.Qt import QToolButton
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.utils.config import prefs, tweaks
@ -65,14 +65,37 @@ class Worker(Thread): # {{{
as_path=True)
if p:
paths.append(p)
added = False
automerged = False
if prefs['add_formats_to_existing']:
identical_book_list = newdb.find_identical_books(mi)
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:
self.add_formats(identical_book, paths, newdb, replace=False)
if not added:
ib_fmts = newdb.formats(identical_book, index_is_id=True)
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,
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
preserve_uuid=self.delete_after)

View File

@ -63,11 +63,15 @@ class ShareConnMenu(QMenu): # {{{
if hasattr(parent, 'keyboard'):
r = parent.keyboard.register_shortcut
prefix = 'Share/Connect Menu '
gr = ConnectShareAction.action_spec[0]
for attr in ('folder', 'bambook', 'itunes'):
if not (iswindows or isosx) and attr == 'itunes':
continue
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):
text = _('Start Content Server')

View File

@ -11,7 +11,7 @@ from calibre.gui2.actions import InterfaceAction
class RestartAction(InterfaceAction):
name = 'Restart'
action_spec = (_('&Restart'), None, None, _('Ctrl+R'))
action_spec = (_('Restart'), None, None, _('Ctrl+R'))
def genesis(self):
self.qaction.triggered.connect(self.restart)

View File

@ -193,6 +193,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.recipe_model = recipe_model
self.recipe_model.do_refresh()
self.count_label.setText(
# NOTE: Number of news sources
_('%s news sources') %
self.recipe_model.showing_count)

View File

@ -8,32 +8,97 @@ __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
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.constants import DEBUG
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):
pass
class Manager(QObject):
def finalize(shortcuts, custom_keys_map={}): # {{{
'''
Resolve conflicts and assign keys to every action in shorcuts, which must
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
of unique names to resolved keys. Also sets the set_to_defaul member
correctly for each shortcut.
'''
seen, keys_map = {}, {}
for unique_name, shortcut in shortcuts.iteritems():
custom_keys = custom_keys_map.get(unique_name, None)
if custom_keys is None:
candidates = shortcut['default_keys']
shortcut['set_to_default'] = True
else:
candidates = custom_keys
shortcut['set_to_default'] = False
keys = []
for x in candidates:
ks = QKeySequence(x, QKeySequence.PortableText)
x = unicode(ks.toString(QKeySequence.PortableText))
if x in seen:
if DEBUG:
prints('Key %r for shortcut %s is already used by'
' %s, ignoring'%(x, shortcut['name'], seen[x]['name']))
continue
seen[x] = shortcut
keys.append(ks)
keys = tuple(keys)
#print (111111, unique_name, candidates, keys)
keys_map[unique_name] = keys
ac = shortcut['action']
if ac is not None:
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.custom_keys_map = {}
self.shortcuts = OrderedDict()
self.keys_map = {}
for unique_name, keys in self.config.get(
'map', {}).iteritems():
self.custom_keys_map[unique_name] = tuple(keys)
self.groups = {}
def register_shortcut(self, unique_name, name, default_keys=(),
description=None, action=None):
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'%(
@ -41,31 +106,517 @@ class Manager(QObject):
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):
seen = {}
for unique_name, shortcut in self.shortcuts.iteritems():
custom_keys = self.custom_keys_map.get(unique_name, None)
if custom_keys is None:
candidates = shortcut['default_keys']
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:
candidates = custom_keys
keys = []
for x in candidates:
ks = QKeySequence(x, QKeySequence.PortableText)
x = unicode(ks.toString(QKeySequence.PortableText))
if x in seen:
if DEBUG:
prints('Key %r for shortcut %s is already used by'
' %s, ignoring'%(x, shortcut['name'], seen[x]['name']))
try:
sc = self.data[c].children[p].data
except:
continue
seen[x] = shortcut
keys.append(ks)
keys = tuple(keys)
#print (111111, unique_name, candidates, keys)
if query in lower(sc['name']):
ans.add((c, p))
return ans
self.keys_map[unique_name] = keys
ac = shortcut['action']
if ac is not None:
ac.setShortcuts(list(keys))
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_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.PositionAtCenter)
@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)
# }}}

View File

@ -62,7 +62,7 @@ class BooksModel(QAbstractTableModel): # {{{
'rating' : _('Rating'),
'publisher' : _("Publisher"),
'tags' : _("Tags"),
'series' : _("Series"),
'series' : ngettext("Series", 'Series', 1),
'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.
if col >= len(self.column_to_dc_map):
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())
elif role == Qt.BackgroundRole:
if self.id(index) in self.ids_to_highlight_set:

View File

@ -719,7 +719,7 @@ class BooksView(QTableView): # {{{
break
def set_current_row(self, row, select=True):
if row > -1:
if row > -1 and row < self.model().rowCount(QModelIndex()):
h = self.horizontalHeader()
logical_indices = list(range(h.count()))
logical_indices = [x for x in logical_indices if not

View File

@ -27,7 +27,7 @@
<item row="1" column="0">
<widget class="QCheckBox" name="opt_read_file_metadata">
<property name="text">
<string>Read &amp;metadata from &amp;file contents rather than file name</string>
<string>Read &amp;metadata from file contents rather than file name</string>
</property>
</widget>
</item>

View 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')

View File

@ -522,6 +522,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.card_a_view.reset()
self.card_b_view.reset()
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
# interface later
gc.collect()

View File

@ -979,6 +979,11 @@ class DocumentView(QWebView): # {{{
return ret
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)
func = self.goto_location_actions.get(key, None)
if func is not None:
@ -996,7 +1001,8 @@ class DocumentView(QWebView): # {{{
elif key == 'Right':
self.scroll_by(x=15)
else:
return QWebView.keyPressEvent(self, event)
handled = False
return handled
def resizeEvent(self, event):
ret = QWebView.resizeEvent(self, event)

View File

@ -755,6 +755,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if self.current_index > 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):
return self

View File

@ -296,8 +296,8 @@ def do_save_book_to_disk(id_, mi, cover, plugboards,
replace_whitespace=opts.replace_whitespace, safe_format=False)
except Exception, e:
raise ValueError(_('Failed to calculate path for '
'save to disk. Template: %s\n'
'Error: %s'%(opts.template, e)))
'save to disk. Template: %(templ)s\n'
'Error: %(err)s')%dict(templ=opts.template, err=e))
if opts.single_dir:
components = components[-1:]
if not components:

File diff suppressed because it is too large Load Diff