mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement adding files to the book
This commit is contained in:
parent
1f0f3038c1
commit
b1652a2316
602
imgsrc/document-new.svg
Normal file
602
imgsrc/document-new.svg
Normal file
@ -0,0 +1,602 @@
|
|||||||
|
<?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:ns="http://ns.adobe.com/SaveForWeb/1.0/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://web.resource.org/cc/"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
viewBox="0 0 128 128"
|
||||||
|
overflow="visible"
|
||||||
|
enable-background="new 0 0 128 128"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.45+devel"
|
||||||
|
sodipodi:docname="document-new.svgz"
|
||||||
|
sodipodi:docbase="/Users/david/Desktop/sandbox"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
|
||||||
|
inkscape:export-filename="/Users/david/Desktop/sandbox/document-new2.png"
|
||||||
|
inkscape:export-xdpi="22.5"
|
||||||
|
inkscape:export-ydpi="22.5"><defs
|
||||||
|
id="defs105"><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter5943"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.04"
|
||||||
|
id="feGaussianBlur5945" /></filter><linearGradient
|
||||||
|
id="linearGradient6740"><stop
|
||||||
|
style="stop-color:#004d00;stop-opacity:0;"
|
||||||
|
offset="0"
|
||||||
|
id="stop6742" /><stop
|
||||||
|
id="stop6748"
|
||||||
|
offset="0.5"
|
||||||
|
style="stop-color:#004d00;stop-opacity:1;" /><stop
|
||||||
|
style="stop-color:#004d00;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop6744" /></linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient6740"
|
||||||
|
id="linearGradient6930"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(20.000035,-56.000003)"
|
||||||
|
x1="102"
|
||||||
|
y1="118"
|
||||||
|
x2="102"
|
||||||
|
y2="65.932846" /><linearGradient
|
||||||
|
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
|
||||||
|
y2="-383.9975"
|
||||||
|
x2="-23.516129"
|
||||||
|
y1="-383.9971"
|
||||||
|
x1="-84.002403"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient3711"><stop
|
||||||
|
id="stop3713"
|
||||||
|
style="stop-color:white;stop-opacity:1;"
|
||||||
|
offset="0" /><stop
|
||||||
|
id="stop3715"
|
||||||
|
style="stop-color:white;stop-opacity:0;"
|
||||||
|
offset="1" /></linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3711"
|
||||||
|
id="linearGradient8927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0,0.7388168,-0.7388168,0,-1.5226652,63.255682)"
|
||||||
|
x1="-80.00296"
|
||||||
|
y1="-131.93112"
|
||||||
|
x2="-45.096584"
|
||||||
|
y2="-131.93112" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter7317"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="2.8805897"
|
||||||
|
id="feGaussianBlur7319" /></filter><clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath5163"><circle
|
||||||
|
sodipodi:ry="36"
|
||||||
|
sodipodi:rx="36"
|
||||||
|
sodipodi:cy="92"
|
||||||
|
sodipodi:cx="343.99899"
|
||||||
|
style="fill:url(#linearGradient5167);fill-opacity:1"
|
||||||
|
r="36"
|
||||||
|
rx="8.0010004"
|
||||||
|
cx="343.99899"
|
||||||
|
cy="92"
|
||||||
|
ry="8.0010004"
|
||||||
|
id="circle5165" /></clipPath><linearGradient
|
||||||
|
id="linearGradient4296"><stop
|
||||||
|
id="stop4298"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#00ff00;stop-opacity:1" /><stop
|
||||||
|
id="stop4300"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#006500;stop-opacity:1" /></linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4296"
|
||||||
|
id="linearGradient4272"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="328.12448"
|
||||||
|
y1="120.81158"
|
||||||
|
x2="336.98077"
|
||||||
|
y2="87.759453" /><linearGradient
|
||||||
|
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
|
||||||
|
y2="-383.9971"
|
||||||
|
x2="-12.0029"
|
||||||
|
y1="-383.9971"
|
||||||
|
x1="-84.002403"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient4770"><stop
|
||||||
|
id="stop4772"
|
||||||
|
style="stop-color:#e5ff00;stop-opacity:1"
|
||||||
|
offset="0" /><stop
|
||||||
|
id="stop4774"
|
||||||
|
style="stop-color:#bff500;stop-opacity:0;"
|
||||||
|
offset="1" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4770"
|
||||||
|
id="radialGradient8920"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="343.99899"
|
||||||
|
cy="92"
|
||||||
|
fx="343.99899"
|
||||||
|
fy="92"
|
||||||
|
r="36" /><linearGradient
|
||||||
|
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
|
||||||
|
y2="-383.9971"
|
||||||
|
x2="-12.0029"
|
||||||
|
y1="-383.9971"
|
||||||
|
x1="-84.002403"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient5958"><stop
|
||||||
|
id="stop5960"
|
||||||
|
style="stop-color:#008c00;stop-opacity:1"
|
||||||
|
offset="0" /><stop
|
||||||
|
id="stop5962"
|
||||||
|
style="stop-color:#00bf00;stop-opacity:1"
|
||||||
|
offset="1" /></linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient5958"
|
||||||
|
id="linearGradient8916"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
|
||||||
|
x1="-86.120354"
|
||||||
|
y1="-381.09921"
|
||||||
|
x2="-56.357521"
|
||||||
|
y2="-373.1243" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter4292"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="2.1604423"
|
||||||
|
id="feGaussianBlur4294" /></filter><linearGradient
|
||||||
|
y2="0"
|
||||||
|
x2="28"
|
||||||
|
y1="57.5"
|
||||||
|
x1="28"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient18668">
|
||||||
|
<stop
|
||||||
|
id="stop18670"
|
||||||
|
style="stop-color:#fffccf;stop-opacity:1;"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop18672"
|
||||||
|
style="stop-color:white;stop-opacity:0;"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
id="XMLID_2_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="28"
|
||||||
|
y1="57.5"
|
||||||
|
x2="28"
|
||||||
|
y2="0">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFEA00"
|
||||||
|
id="stop12" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#FFCC00"
|
||||||
|
id="stop14" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_9_"
|
||||||
|
id="linearGradient2291"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="94.3438"
|
||||||
|
y1="102.3447"
|
||||||
|
x2="86.5356"
|
||||||
|
y2="94.5366" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_10_"
|
||||||
|
id="linearGradient2293"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="86.5865"
|
||||||
|
y2="94.5865" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_11_"
|
||||||
|
id="linearGradient2295"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="87.293"
|
||||||
|
y2="95.293" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_12_"
|
||||||
|
id="linearGradient2297"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="96"
|
||||||
|
y1="104"
|
||||||
|
x2="88.0002"
|
||||||
|
y2="96.0002" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_2_"
|
||||||
|
id="linearGradient2299"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="28"
|
||||||
|
y1="57.5"
|
||||||
|
x2="28"
|
||||||
|
y2="0" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_8_"
|
||||||
|
id="radialGradient2272"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.9787234,0,0,0.9818182,1.3617045,1.1636364)"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.55859" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_7_"
|
||||||
|
id="radialGradient2275"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(2.4e-6,0)"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.5585" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient18668"
|
||||||
|
id="radialGradient3239"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1,0,0,1.2916104,-4,-19.57938)"
|
||||||
|
cx="30.010139"
|
||||||
|
cy="34.750149"
|
||||||
|
fx="30.010139"
|
||||||
|
fy="34.750149"
|
||||||
|
r="20.78125" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3241"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.0394514"
|
||||||
|
id="feGaussianBlur3243" /></filter></defs><sodipodi:namedview
|
||||||
|
inkscape:window-height="670"
|
||||||
|
inkscape:window-width="1022"
|
||||||
|
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"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="63.856972"
|
||||||
|
inkscape:cy="57.431611"
|
||||||
|
inkscape:window-x="2"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:current-layer="Layer_1"
|
||||||
|
showgrid="true"
|
||||||
|
gridspacingx="4px"
|
||||||
|
gridspacingy="4px"
|
||||||
|
gridempspacing="2"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3">
|
||||||
|
<ns:sfw>
|
||||||
|
<ns:slices>
|
||||||
|
<ns:slice
|
||||||
|
y="0"
|
||||||
|
x="0"
|
||||||
|
height="128"
|
||||||
|
width="128"
|
||||||
|
sliceID="1316743234" />
|
||||||
|
</ns:slices>
|
||||||
|
<ns:sliceSourceBounds
|
||||||
|
y="0"
|
||||||
|
x="0"
|
||||||
|
height="128"
|
||||||
|
width="128"
|
||||||
|
bottomLeftOrigin="true" />
|
||||||
|
<ns:optimizationSettings>
|
||||||
|
<ns:targetSettings
|
||||||
|
targetSettingsID="0"
|
||||||
|
fileFormat="PNG24Format">
|
||||||
|
<ns:PNG24Format
|
||||||
|
transparency="true"
|
||||||
|
filtered="false"
|
||||||
|
matteColor="#FFFFFF"
|
||||||
|
noMatteColor="false"
|
||||||
|
interlaced="false">
|
||||||
|
</ns:PNG24Format>
|
||||||
|
</ns:targetSettings>
|
||||||
|
<ns:targetSettings
|
||||||
|
targetSettingsID="1696735251"
|
||||||
|
fileFormat="PNG24Format">
|
||||||
|
<ns:PNG24Format
|
||||||
|
transparency="true"
|
||||||
|
filtered="false"
|
||||||
|
matteColor="#FFFFFF"
|
||||||
|
noMatteColor="false"
|
||||||
|
interlaced="false">
|
||||||
|
</ns:PNG24Format>
|
||||||
|
</ns:targetSettings>
|
||||||
|
</ns:optimizationSettings>
|
||||||
|
</ns:sfw>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
id="XMLID_7_"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.5585"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#00537D"
|
||||||
|
id="stop16" />
|
||||||
|
<stop
|
||||||
|
offset="0.0151"
|
||||||
|
style="stop-color:#186389"
|
||||||
|
id="stop18" />
|
||||||
|
<stop
|
||||||
|
offset="0.0558"
|
||||||
|
style="stop-color:#558CA8"
|
||||||
|
id="stop20" />
|
||||||
|
<stop
|
||||||
|
offset="0.0964"
|
||||||
|
style="stop-color:#89AFC3"
|
||||||
|
id="stop22" />
|
||||||
|
<stop
|
||||||
|
offset="0.1357"
|
||||||
|
style="stop-color:#B3CCD8"
|
||||||
|
id="stop24" />
|
||||||
|
<stop
|
||||||
|
offset="0.1737"
|
||||||
|
style="stop-color:#D4E2E9"
|
||||||
|
id="stop26" />
|
||||||
|
<stop
|
||||||
|
offset="0.2099"
|
||||||
|
style="stop-color:#ECF2F5"
|
||||||
|
id="stop28" />
|
||||||
|
<stop
|
||||||
|
offset="0.2435"
|
||||||
|
style="stop-color:#FAFCFD"
|
||||||
|
id="stop30" />
|
||||||
|
<stop
|
||||||
|
offset="0.2722"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop32" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
id="XMLID_8_"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.55859"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#535557"
|
||||||
|
id="stop37" />
|
||||||
|
|
||||||
|
<stop
|
||||||
|
offset="0.11366145"
|
||||||
|
style="stop-color:#898A8C"
|
||||||
|
id="stop41" />
|
||||||
|
|
||||||
|
|
||||||
|
<stop
|
||||||
|
offset="0.20296688"
|
||||||
|
style="stop-color:#ECECEC"
|
||||||
|
id="stop47" />
|
||||||
|
<stop
|
||||||
|
offset="0.2363"
|
||||||
|
style="stop-color:#FAFAFA"
|
||||||
|
id="stop49" />
|
||||||
|
<stop
|
||||||
|
offset="0.2722"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop51" />
|
||||||
|
<stop
|
||||||
|
offset="0.5313"
|
||||||
|
style="stop-color:#FAFAFA"
|
||||||
|
id="stop53" />
|
||||||
|
<stop
|
||||||
|
offset="0.8449"
|
||||||
|
style="stop-color:#EBECEC"
|
||||||
|
id="stop55" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#E1E2E3"
|
||||||
|
id="stop57" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_9_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="94.3438"
|
||||||
|
y1="102.3447"
|
||||||
|
x2="86.5356"
|
||||||
|
y2="94.5366">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop62" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#555753"
|
||||||
|
id="stop64" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_10_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="86.5865"
|
||||||
|
y2="94.5865">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop69" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#555753"
|
||||||
|
id="stop71" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_11_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="87.293"
|
||||||
|
y2="95.293">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop76" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#393B38"
|
||||||
|
id="stop78" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_12_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="96"
|
||||||
|
y1="104"
|
||||||
|
x2="88.0002"
|
||||||
|
y2="96.0002">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#888A85"
|
||||||
|
id="stop83" />
|
||||||
|
<stop
|
||||||
|
offset="0.0072"
|
||||||
|
style="stop-color:#8C8E89"
|
||||||
|
id="stop85" />
|
||||||
|
<stop
|
||||||
|
offset="0.0673"
|
||||||
|
style="stop-color:#ABACA9"
|
||||||
|
id="stop87" />
|
||||||
|
<stop
|
||||||
|
offset="0.1347"
|
||||||
|
style="stop-color:#C5C6C4"
|
||||||
|
id="stop89" />
|
||||||
|
<stop
|
||||||
|
offset="0.2115"
|
||||||
|
style="stop-color:#DBDBDA"
|
||||||
|
id="stop91" />
|
||||||
|
<stop
|
||||||
|
offset="0.3012"
|
||||||
|
style="stop-color:#EBEBEB"
|
||||||
|
id="stop93" />
|
||||||
|
<stop
|
||||||
|
offset="0.4122"
|
||||||
|
style="stop-color:#F7F7F6"
|
||||||
|
id="stop95" />
|
||||||
|
<stop
|
||||||
|
offset="0.5679"
|
||||||
|
style="stop-color:#FDFDFD"
|
||||||
|
id="stop97" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop99" />
|
||||||
|
</linearGradient>
|
||||||
|
<path
|
||||||
|
id="path6594"
|
||||||
|
d="M 23,9 L 23.040816,121 L 84.172,121 C 84.702,121 85.211,120.789 85.586,120.414 L 118.414,87.586 C 118.789,87.211 119,86.702 119,86.172 L 119,9 L 23,9 z "
|
||||||
|
style="opacity:0.5;fill:#000000;fill-opacity:1;filter:url(#filter3241)"
|
||||||
|
transform="matrix(1.0416667,0,0,1.0357143,-9.9583333,-3.3215342)"
|
||||||
|
sodipodi:nodetypes="cccccccc" /><path
|
||||||
|
style="fill:url(#radialGradient2275)"
|
||||||
|
d="M 16.000002,8 L 16.000002,120 L 77.172002,120 C 77.702002,120 78.211002,119.789 78.586002,119.414 L 111.414,86.586 C 111.789,86.211 112,85.702 112,85.172 L 112,8 L 16.000002,8 z "
|
||||||
|
id="path34"
|
||||||
|
sodipodi:nodetypes="cccccccc" /><path
|
||||||
|
style="fill:url(#radialGradient2272);fill-opacity:1"
|
||||||
|
d="M 18.978725,10 C 18.439449,10 18.000002,10.440836 18.000002,10.981818 L 18.000002,117.01818 C 18.000002,117.56015 18.439449,118 18.978725,118 L 76.891747,118 C 77.149151,118 77.401662,117.89593 77.583704,117.71233 L 109.71323,85.4812 C 109.89626,85.2976 110,85.045273 110,84.787055 L 110,10.981818 C 110,10.440836 109.56153,10 109.02128,10 L 18.978725,10 z "
|
||||||
|
id="path59" /><g
|
||||||
|
id="g1973"
|
||||||
|
transform="translate(2.4e-6,0)"><path
|
||||||
|
style="opacity:0.1;fill:url(#linearGradient2291)"
|
||||||
|
id="path66"
|
||||||
|
d="M 111.414,86.586 C 111.664,86.336 93.035,93 88,93 C 86.346,93 85,94.346 85,96 C 85,101.035 78.336,119.664 78.586,119.414 L 111.414,86.586 z " /><path
|
||||||
|
style="opacity:0.1;fill:url(#linearGradient2293)"
|
||||||
|
id="path73"
|
||||||
|
d="M 111.414,86.586 C 111.789,86.211 97.444,94 88,94 C 86.897,94 86,94.897 86,96 C 86,105.444 78.211,119.789 78.586,119.414 L 111.414,86.586 z " /><path
|
||||||
|
style="opacity:0.1;fill:url(#linearGradient2295)"
|
||||||
|
id="path80"
|
||||||
|
d="M 111.414,86.586 C 111.653,86.347 97.807,95 88,95 C 87.447,95 87,95.447 87,96 C 87,105.807 78.347,119.653 78.586,119.414 L 111.414,86.586 z " /><path
|
||||||
|
style="fill:url(#linearGradient2297)"
|
||||||
|
id="path101"
|
||||||
|
d="M 78.586,119.414 C 78.586,119.414 90.5,109.5 96,104 C 101.5,98.5 111.414,86.586 111.414,86.586 C 111.414,86.586 98.25,96 88,96 C 88,106.25 78.586,119.414 78.586,119.414 z " /></g>
|
||||||
|
|
||||||
|
<g
|
||||||
|
id="g3640"
|
||||||
|
transform="translate(9.545815e-6,1.710865e-5)"><circle
|
||||||
|
sodipodi:ry="36"
|
||||||
|
sodipodi:rx="36"
|
||||||
|
sodipodi:cy="92"
|
||||||
|
sodipodi:cx="343.99899"
|
||||||
|
style="opacity:0.5;fill:#000000;fill-opacity:1;filter:url(#filter4292)"
|
||||||
|
r="36"
|
||||||
|
rx="8.0010004"
|
||||||
|
cx="343.99899"
|
||||||
|
cy="92"
|
||||||
|
ry="8.0010004"
|
||||||
|
id="circle4274"
|
||||||
|
transform="matrix(-0.858425,0.2300143,-0.2300143,-0.858425,412.45864,35.85043)" /><circle
|
||||||
|
transform="matrix(-0.858425,0.2300143,-0.2300143,-0.858425,412.45864,31.85043)"
|
||||||
|
id="circle3581_2_"
|
||||||
|
ry="8.0010004"
|
||||||
|
cy="92"
|
||||||
|
cx="343.99899"
|
||||||
|
rx="8.0010004"
|
||||||
|
r="36"
|
||||||
|
style="fill:url(#linearGradient8916);fill-opacity:1"
|
||||||
|
sodipodi:cx="343.99899"
|
||||||
|
sodipodi:cy="92"
|
||||||
|
sodipodi:rx="36"
|
||||||
|
sodipodi:ry="36" /><circle
|
||||||
|
sodipodi:ry="36"
|
||||||
|
sodipodi:rx="36"
|
||||||
|
sodipodi:cy="92"
|
||||||
|
sodipodi:cx="343.99899"
|
||||||
|
style="fill:url(#radialGradient8920);fill-opacity:1"
|
||||||
|
r="36"
|
||||||
|
rx="8.0010004"
|
||||||
|
cx="343.99899"
|
||||||
|
cy="92"
|
||||||
|
ry="8.0010004"
|
||||||
|
id="circle4383"
|
||||||
|
transform="matrix(-0.6438188,0.1725107,-0.1725107,-0.6438188,333.34401,31.887831)" /><circle
|
||||||
|
sodipodi:ry="36"
|
||||||
|
sodipodi:rx="36"
|
||||||
|
sodipodi:cy="92"
|
||||||
|
sodipodi:cx="343.99899"
|
||||||
|
style="opacity:0.8;fill:none;fill-opacity:1;stroke:url(#linearGradient4272);stroke-width:6.75138187;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter7317)"
|
||||||
|
r="36"
|
||||||
|
rx="8.0010004"
|
||||||
|
cx="343.99899"
|
||||||
|
cy="92"
|
||||||
|
ry="8.0010004"
|
||||||
|
id="circle4776"
|
||||||
|
transform="matrix(-0.858425,-0.2300143,-0.2300143,0.858425,412.45864,32.149572)"
|
||||||
|
clip-path="url(#clipPath5163)" /><path
|
||||||
|
id="circle16776"
|
||||||
|
d="M 96.000027,4.1481901 C 84.654311,4.1481901 75.173932,12.159796 72.888913,22.826405 C 77.443574,27.0723 86.085806,29.937514 96.000027,29.937514 C 105.91426,29.937514 114.55648,27.0723 119.11114,22.826405 C 116.82613,12.159796 107.34574,4.1481901 96.000027,4.1481901 z"
|
||||||
|
style="opacity:0.8;fill:url(#linearGradient8927);fill-opacity:1" /><g
|
||||||
|
transform="translate(-26.000031,-3.999996)"
|
||||||
|
id="g6850"><path
|
||||||
|
id="path4123"
|
||||||
|
d="M 118.00003,15.999997 L 118.00003,31.999997 L 102.00003,31.999997 L 102.00003,39.999997 L 118.00003,39.999997 L 118.00003,55.999997 L 126.00003,55.999997 L 126.00003,39.999997 L 142.00003,39.999997 L 142.00003,31.999997 L 126.00003,31.999997 L 126.00003,15.999997 L 118.00003,15.999997 z"
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient6930);stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter5943)"
|
||||||
|
sodipodi:nodetypes="ccccccccccccc" /><path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
|
||||||
|
d="M 118.00003,15.999997 L 118.00003,31.999997 L 102.00003,31.999997 L 102.00003,39.999997 L 118.00003,39.999997 L 118.00003,55.999997 L 126.00003,55.999997 L 126.00003,39.999997 L 142.00003,39.999997 L 142.00003,31.999997 L 126.00003,31.999997 L 126.00003,15.999997 L 118.00003,15.999997 z"
|
||||||
|
id="rect3232"
|
||||||
|
sodipodi:nodetypes="ccccccccccccc" /></g></g></svg>
|
After Width: | Height: | Size: 18 KiB |
BIN
resources/images/document-new.png
Normal file
BIN
resources/images/document-new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
@ -158,6 +158,46 @@ class Container(object): # {{{
|
|||||||
for name, path in self.name_path_map.iteritems()}
|
for name, path in self.name_path_map.iteritems()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def add_file(self, name, data, media_type=None):
|
||||||
|
''' Add a file to this container. Entries for the file are
|
||||||
|
automatically created in the OPF manifest and spine
|
||||||
|
(if the file is a text document) '''
|
||||||
|
if self.has_name(name):
|
||||||
|
raise ValueError('A file with the name %s already exists' % name)
|
||||||
|
if '..' in name:
|
||||||
|
raise ValueError('Names are not allowed to have .. in them')
|
||||||
|
href = self.name_to_href(name, self.opf_name)
|
||||||
|
all_hrefs = {x.get('href') for x in self.opf_xpath('//opf:manifest/opf:item[@href]')}
|
||||||
|
if href in all_hrefs:
|
||||||
|
raise ValueError('An item with the href %s already exists in the manifest' % href)
|
||||||
|
path = self.name_to_abspath(name)
|
||||||
|
base = os.path.dirname(path)
|
||||||
|
if not os.path.exists(base):
|
||||||
|
os.makedirs(base)
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
|
mt = media_type or guess_type(name)
|
||||||
|
self.name_path_map[name] = path
|
||||||
|
self.mime_map[name] = mt
|
||||||
|
if name in self.names_that_need_not_be_manifested:
|
||||||
|
return
|
||||||
|
all_ids = {x.get('id') for x in self.opf_xpath('//*[@id]')}
|
||||||
|
c = 0
|
||||||
|
item_id = 'id'
|
||||||
|
while item_id in all_ids:
|
||||||
|
c += 1
|
||||||
|
item_id = 'id' + '%d'%c
|
||||||
|
manifest = self.opf_xpath('//opf:manifest')[0]
|
||||||
|
item = manifest.makeelement(OPF('item'),
|
||||||
|
id=item_id, href=href)
|
||||||
|
item.set('media-type', mt)
|
||||||
|
self.insert_into_xml(manifest, item)
|
||||||
|
self.dirty(self.opf_name)
|
||||||
|
if mt in OEB_DOCS:
|
||||||
|
spine = self.opf_xpath('//opf:spine')[0]
|
||||||
|
si = manifest.makeelement(OPF('itemref'), idref=item_id)
|
||||||
|
self.insert_into_xml(spine, si)
|
||||||
|
|
||||||
def rename(self, current_name, new_name):
|
def rename(self, current_name, new_name):
|
||||||
''' Renames a file from current_name to new_name. It automatically
|
''' Renames a file from current_name to new_name. It automatically
|
||||||
rebases all links inside the file if the directory the file is in
|
rebases all links inside the file if the directory the file is in
|
||||||
|
@ -158,3 +158,20 @@ class ContainerTests(BaseTest):
|
|||||||
|
|
||||||
# self.run_external_tools(c, gvim=True)
|
# self.run_external_tools(c, gvim=True)
|
||||||
|
|
||||||
|
def test_file_add(self):
|
||||||
|
' Test adding of files '
|
||||||
|
book = get_simple_book()
|
||||||
|
c = get_container(book)
|
||||||
|
name = 'folder/added file.html'
|
||||||
|
c.add_file(name, b'xxx')
|
||||||
|
self.assertEqual('xxx', c.raw_data(name))
|
||||||
|
self.assertIn(name, set(c.manifest_id_map.itervalues()))
|
||||||
|
self.assertIn(name, {x[0] for x in c.spine_names})
|
||||||
|
|
||||||
|
name = 'added.css'
|
||||||
|
c.add_file(name, b'xxx')
|
||||||
|
self.assertEqual('xxx', c.raw_data(name))
|
||||||
|
self.assertIn(name, set(c.manifest_id_map.itervalues()))
|
||||||
|
self.assertNotIn(name, {x[0] for x in c.spine_names})
|
||||||
|
|
||||||
|
self.check_links(c)
|
||||||
|
@ -24,6 +24,7 @@ from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialo
|
|||||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||||
from calibre.gui2.tweak_book import set_current_container, current_container, tprefs, actions, editors
|
from calibre.gui2.tweak_book import set_current_container, current_container, tprefs, actions, editors
|
||||||
from calibre.gui2.tweak_book.undo import GlobalUndoHistory
|
from calibre.gui2.tweak_book.undo import GlobalUndoHistory
|
||||||
|
from calibre.gui2.tweak_book.file_list import NewFileDialog
|
||||||
from calibre.gui2.tweak_book.save import SaveManager
|
from calibre.gui2.tweak_book.save import SaveManager
|
||||||
from calibre.gui2.tweak_book.preview import parse_worker
|
from calibre.gui2.tweak_book.preview import parse_worker
|
||||||
from calibre.gui2.tweak_book.toc import TOCEditor
|
from calibre.gui2.tweak_book.toc import TOCEditor
|
||||||
@ -144,15 +145,52 @@ class Boss(QObject):
|
|||||||
if not editors:
|
if not editors:
|
||||||
self.gui.preview.clear()
|
self.gui.preview.clear()
|
||||||
|
|
||||||
|
def check_opf_dirtied(self):
|
||||||
|
c = current_container()
|
||||||
|
if c.opf_name in editors and editors[c.opf_name].is_modified:
|
||||||
|
return question_dialog(self.gui, _('Unsaved changes'), _(
|
||||||
|
'You have unsaved changes in %s. If you proceed,'
|
||||||
|
' you will lose them. Proceed anyway?') % c.opf_name)
|
||||||
|
return True
|
||||||
|
|
||||||
def reorder_spine(self, items):
|
def reorder_spine(self, items):
|
||||||
# TODO: If content.opf is dirty in an editor, abort, calling
|
if not self.check_opf_dirtied():
|
||||||
# file_list.build(current_container) to undo drag and drop
|
return
|
||||||
self.add_savepoint(_('Re-order text'))
|
self.add_savepoint(_('Re-order text'))
|
||||||
c = current_container()
|
c = current_container()
|
||||||
c.set_spine(items)
|
c.set_spine(items)
|
||||||
self.gui.action_save.setEnabled(True)
|
self.gui.action_save.setEnabled(True)
|
||||||
self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items
|
self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items
|
||||||
# TODO: If content.opf is open in an editor, reload it
|
if c.opf_name in editors:
|
||||||
|
editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
|
||||||
|
|
||||||
|
def add_file(self):
|
||||||
|
if not self.check_opf_dirtied():
|
||||||
|
return
|
||||||
|
d = NewFileDialog(self.gui)
|
||||||
|
if d.exec_() != d.Accepted:
|
||||||
|
return
|
||||||
|
self.add_savepoint(_('Add file %s') % self.gui.elided_text(d.file_name))
|
||||||
|
c = current_container()
|
||||||
|
data = d.file_data
|
||||||
|
if d.using_template:
|
||||||
|
data = data.replace(b'%CURSOR%', b'')
|
||||||
|
try:
|
||||||
|
c.add_file(d.file_name, data)
|
||||||
|
except:
|
||||||
|
self.rewind_savepoint()
|
||||||
|
raise
|
||||||
|
self.gui.file_list.build(c)
|
||||||
|
self.gui.file_list.select_name(d.file_name)
|
||||||
|
if c.opf_name in editors:
|
||||||
|
editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
|
||||||
|
mt = c.mime_map[d.file_name]
|
||||||
|
syntax = syntax_from_mime(mt)
|
||||||
|
if syntax:
|
||||||
|
if d.using_template:
|
||||||
|
self.edit_file(d.file_name, syntax, use_template=d.file_data.decode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.edit_file(d.file_name, syntax)
|
||||||
|
|
||||||
def edit_toc(self):
|
def edit_toc(self):
|
||||||
if not self.check_dirtied():
|
if not self.check_dirtied():
|
||||||
@ -454,21 +492,27 @@ class Boss(QObject):
|
|||||||
_('Saving of the book failed. Click "Show Details"'
|
_('Saving of the book failed. Click "Show Details"'
|
||||||
' for more information.'), det_msg=tb, show=True)
|
' for more information.'), det_msg=tb, show=True)
|
||||||
|
|
||||||
def init_editor(self, name, editor, data=None):
|
def init_editor(self, name, editor, data=None, use_template=False):
|
||||||
editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed)
|
editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed)
|
||||||
editor.data_changed.connect(self.editor_data_changed)
|
editor.data_changed.connect(self.editor_data_changed)
|
||||||
editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed)
|
editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed)
|
||||||
if data is not None:
|
if data is not None:
|
||||||
editor.data = data
|
if use_template:
|
||||||
|
editor.init_from_template(data)
|
||||||
|
else:
|
||||||
|
editor.data = data
|
||||||
editor.modification_state_changed.connect(self.editor_modification_state_changed)
|
editor.modification_state_changed.connect(self.editor_modification_state_changed)
|
||||||
self.gui.central.add_editor(name, editor)
|
self.gui.central.add_editor(name, editor)
|
||||||
|
|
||||||
def edit_file(self, name, syntax):
|
def edit_file(self, name, syntax, use_template=None):
|
||||||
editor = editors.get(name, None)
|
editor = editors.get(name, None)
|
||||||
if editor is None:
|
if editor is None:
|
||||||
editor = editors[name] = editor_from_syntax(syntax, self.gui.editor_tabs)
|
editor = editors[name] = editor_from_syntax(syntax, self.gui.editor_tabs)
|
||||||
data = current_container().raw_data(name)
|
if use_template is None:
|
||||||
self.init_editor(name, editor, data)
|
data = current_container().raw_data(name)
|
||||||
|
else:
|
||||||
|
data = use_template
|
||||||
|
self.init_editor(name, editor, data, use_template=bool(use_template))
|
||||||
self.show_editor(name)
|
self.show_editor(name)
|
||||||
|
|
||||||
def show_editor(self, name):
|
def show_editor(self, name):
|
||||||
|
@ -115,11 +115,14 @@ class TextEdit(QPlainTextEdit):
|
|||||||
self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg')
|
self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg')
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def load_text(self, text, syntax='html'):
|
def load_text(self, text, syntax='html', process_template=False):
|
||||||
self.highlighter = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)(self)
|
self.highlighter = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)(self)
|
||||||
self.highlighter.apply_theme(self.theme)
|
self.highlighter.apply_theme(self.theme)
|
||||||
self.highlighter.setDocument(self.document())
|
self.highlighter.setDocument(self.document())
|
||||||
self.setPlainText(text)
|
self.setPlainText(text)
|
||||||
|
if process_template and QPlainTextEdit.find(self, '%CURSOR%'):
|
||||||
|
c = self.textCursor()
|
||||||
|
c.insertText('')
|
||||||
|
|
||||||
def replace_text(self, text):
|
def replace_text(self, text):
|
||||||
c = self.textCursor()
|
c = self.textCursor()
|
||||||
|
@ -48,6 +48,9 @@ class Editor(QMainWindow):
|
|||||||
self.editor.load_text(val, syntax=self.syntax)
|
self.editor.load_text(val, syntax=self.syntax)
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
|
def init_from_template(self, template):
|
||||||
|
self.editor.load_text(template, syntax=self.syntax, process_template=True)
|
||||||
|
|
||||||
def get_raw_data(self):
|
def get_raw_data(self):
|
||||||
return unicode(self.editor.toPlainText())
|
return unicode(self.editor.toPlainText())
|
||||||
|
|
||||||
|
@ -6,19 +6,22 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import os
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QWidget, QTreeWidget, QGridLayout, QSize, Qt, QTreeWidgetItem, QIcon,
|
QWidget, QTreeWidget, QGridLayout, QSize, Qt, QTreeWidgetItem, QIcon,
|
||||||
QStyledItemDelegate, QStyle, QPixmap, QPainter, pyqtSignal)
|
QStyledItemDelegate, QStyle, QPixmap, QPainter, pyqtSignal,
|
||||||
|
QDialogButtonBox, QDialog, QLabel, QLineEdit, QVBoxLayout)
|
||||||
|
|
||||||
from calibre import human_readable
|
from calibre import human_readable, sanitize_file_name_unicode
|
||||||
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
||||||
from calibre.ebooks.oeb.polish.container import guess_type
|
from calibre.ebooks.oeb.polish.container import guess_type
|
||||||
from calibre.ebooks.oeb.polish.cover import get_cover_page_name, get_raster_cover_name
|
from calibre.ebooks.oeb.polish.cover import get_cover_page_name, get_raster_cover_name
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog, choose_files
|
||||||
from calibre.gui2.tweak_book import current_container
|
from calibre.gui2.tweak_book import current_container
|
||||||
from calibre.gui2.tweak_book.editor import syntax_from_mime
|
from calibre.gui2.tweak_book.editor import syntax_from_mime
|
||||||
|
from calibre.gui2.tweak_book.templates import template_for
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
TOP_ICON_SIZE = 24
|
TOP_ICON_SIZE = 24
|
||||||
@ -128,6 +131,14 @@ class FileList(QTreeWidget):
|
|||||||
if name in state['selected']:
|
if name in state['selected']:
|
||||||
c.setSelected(True)
|
c.setSelected(True)
|
||||||
|
|
||||||
|
def select_name(self, name):
|
||||||
|
for parent in self.categories.itervalues():
|
||||||
|
for c in (parent.child(i) for i in xrange(parent.childCount())):
|
||||||
|
q = unicode(c.data(0, NAME_ROLE).toString())
|
||||||
|
c.setSelected(q == name)
|
||||||
|
if q == name:
|
||||||
|
self.scrollToItem(c)
|
||||||
|
|
||||||
def build(self, container, preserve_state=True):
|
def build(self, container, preserve_state=True):
|
||||||
if preserve_state:
|
if preserve_state:
|
||||||
state = self.get_state()
|
state = self.get_state()
|
||||||
@ -360,6 +371,86 @@ class FileList(QTreeWidget):
|
|||||||
ans['selected'][name] = syntax_from_mime(mime)
|
ans['selected'][name] = syntax_from_mime(mime)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
class NewFileDialog(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, initial_choice='html', parent=None):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self.l = l = QVBoxLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
self.la = la = QLabel(_(
|
||||||
|
'Choose a name for the new file'))
|
||||||
|
self.setWindowTitle(_('Choose file'))
|
||||||
|
l.addWidget(la)
|
||||||
|
self.name = n = QLineEdit(self)
|
||||||
|
n.textChanged.connect(self.update_ok)
|
||||||
|
l.addWidget(n)
|
||||||
|
self.err_label = la = QLabel('')
|
||||||
|
la.setWordWrap(True)
|
||||||
|
l.addWidget(la)
|
||||||
|
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
|
l.addWidget(bb)
|
||||||
|
bb.accepted.connect(self.accept)
|
||||||
|
bb.rejected.connect(self.reject)
|
||||||
|
self.imp_button = b = bb.addButton(_('Import resource file (image/font/etc.)'), bb.ActionRole)
|
||||||
|
b.setIcon(QIcon(I('view-image.png')))
|
||||||
|
b.clicked.connect(self.import_file)
|
||||||
|
|
||||||
|
self.ok_button = bb.button(bb.Ok)
|
||||||
|
|
||||||
|
self.file_data = ''
|
||||||
|
self.using_template = False
|
||||||
|
|
||||||
|
def show_error(self, msg):
|
||||||
|
self.err_label.setText('<p style="color:red">' + msg)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def import_file(self):
|
||||||
|
path = choose_files(self, 'tweak-book-new-resource-file', _('Choose file'), select_only_single_file=True)
|
||||||
|
if path:
|
||||||
|
path = path[0]
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
self.file_data = f.read()
|
||||||
|
name = os.path.basename(path)
|
||||||
|
self.name.setText(name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name_is_ok(self):
|
||||||
|
name = unicode(self.name.text())
|
||||||
|
if not name or not name.strip():
|
||||||
|
return self.show_error('')
|
||||||
|
ext = name.rpartition('.')[-1]
|
||||||
|
if not ext or ext == name:
|
||||||
|
return self.show_error(_('The file name must have an extension'))
|
||||||
|
norm = name.replace('\\', '/')
|
||||||
|
parts = name.split('/')
|
||||||
|
for x in parts:
|
||||||
|
if sanitize_file_name_unicode(x) != x:
|
||||||
|
return self.show_error(_('The file name contains invalid characters'))
|
||||||
|
if current_container().has_name(norm):
|
||||||
|
return self.show_error(_('This file name already exists in the book'))
|
||||||
|
self.show_error('')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_ok(self, *args):
|
||||||
|
self.ok_button.setEnabled(self.name_is_ok)
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
if not self.name_is_ok:
|
||||||
|
return error_dialog(self, _('No name specified'), _(
|
||||||
|
'You must specify a name for the new file, with an extension, for example, chapter1.html'), show=True)
|
||||||
|
name = unicode(self.name.text())
|
||||||
|
name, ext = name.rpartition('.')[0::2]
|
||||||
|
name = (name + '.' + ext.lower()).replace('\\', '/')
|
||||||
|
mt = guess_type(name)
|
||||||
|
if mt in OEB_DOCS:
|
||||||
|
self.file_data = template_for('html').encode('utf-8')
|
||||||
|
self.using_template = True
|
||||||
|
elif mt in OEB_STYLES:
|
||||||
|
self.file_data = template_for('css').encode('utf-8')
|
||||||
|
self.using_template = True
|
||||||
|
self.file_name = name
|
||||||
|
QDialog.accept(self)
|
||||||
|
|
||||||
class FileListWidget(QWidget):
|
class FileListWidget(QWidget):
|
||||||
|
|
||||||
delete_requested = pyqtSignal(object, object)
|
delete_requested = pyqtSignal(object, object)
|
||||||
@ -375,7 +466,7 @@ class FileListWidget(QWidget):
|
|||||||
self.layout().setContentsMargins(0, 0, 0, 0)
|
self.layout().setContentsMargins(0, 0, 0, 0)
|
||||||
for x in ('delete_requested', 'reorder_spine', 'rename_requested', 'edit_file'):
|
for x in ('delete_requested', 'reorder_spine', 'rename_requested', 'edit_file'):
|
||||||
getattr(self.file_list, x).connect(getattr(self, x))
|
getattr(self.file_list, x).connect(getattr(self, x))
|
||||||
for x in ('delete_done',):
|
for x in ('delete_done', 'select_name'):
|
||||||
setattr(self, x, getattr(self.file_list, x))
|
setattr(self, x, getattr(self.file_list, x))
|
||||||
|
|
||||||
def build(self, container, preserve_state=True):
|
def build(self, container, preserve_state=True):
|
||||||
|
44
src/calibre/gui2/tweak_book/templates.py
Normal file
44
src/calibre/gui2/tweak_book/templates.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from calibre import prepare_string_for_xml
|
||||||
|
from calibre.gui2.tweak_book import current_container
|
||||||
|
|
||||||
|
DEFAULT_TEMPLATES = {
|
||||||
|
'html':
|
||||||
|
'''\
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
%CURSOR%
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''',
|
||||||
|
|
||||||
|
|
||||||
|
'css':
|
||||||
|
'''\
|
||||||
|
@charset utf-8;
|
||||||
|
/* Styles for {TITLE} */
|
||||||
|
%CURSOR%
|
||||||
|
''',
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def template_for(syntax):
|
||||||
|
mi = current_container().mi
|
||||||
|
data = {
|
||||||
|
'TITLE':mi.title,
|
||||||
|
'AUTHOR': ' & '.join(mi.authors),
|
||||||
|
}
|
||||||
|
template = DEFAULT_TEMPLATES.get(syntax, '')
|
||||||
|
return template.format(**{k:prepare_string_for_xml(v, True) for k, v in data.iteritems()})
|
||||||
|
|
@ -9,7 +9,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QDockWidget, Qt, QLabel, QIcon, QAction, QApplication, QWidget,
|
QDockWidget, Qt, QLabel, QIcon, QAction, QApplication, QWidget, QFontMetrics,
|
||||||
QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap, pyqtSignal)
|
QVBoxLayout, QStackedWidget, QTabWidget, QImage, QPixmap, pyqtSignal)
|
||||||
|
|
||||||
from calibre.constants import __appname__, get_version
|
from calibre.constants import __appname__, get_version
|
||||||
@ -22,6 +22,10 @@ from calibre.gui2.tweak_book.keyboard import KeyboardManager
|
|||||||
from calibre.gui2.tweak_book.preview import Preview
|
from calibre.gui2.tweak_book.preview import Preview
|
||||||
from calibre.gui2.tweak_book.search import SearchPanel
|
from calibre.gui2.tweak_book.search import SearchPanel
|
||||||
|
|
||||||
|
def elided_text(font, text, width=200, mode=Qt.ElideMiddle):
|
||||||
|
fm = QFontMetrics(font)
|
||||||
|
return unicode(fm.elidedText(text, mode, int(width)))
|
||||||
|
|
||||||
class Central(QStackedWidget):
|
class Central(QStackedWidget):
|
||||||
|
|
||||||
' The central widget, hosts the editors '
|
' The central widget, hosts the editors '
|
||||||
@ -146,6 +150,9 @@ class Main(MainWindow):
|
|||||||
self.keyboard.finalize()
|
self.keyboard.finalize()
|
||||||
self.keyboard.set_mode('other')
|
self.keyboard.set_mode('other')
|
||||||
|
|
||||||
|
def elided_text(self, text, width=200, mode=Qt.ElideMiddle):
|
||||||
|
return elided_text(self.font(), text, width=width, mode=mode)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def editor_tabs(self):
|
def editor_tabs(self):
|
||||||
return self.central.editor_tabs
|
return self.central.editor_tabs
|
||||||
@ -165,6 +172,7 @@ class Main(MainWindow):
|
|||||||
self.addAction(ac)
|
self.addAction(ac)
|
||||||
return ac
|
return ac
|
||||||
|
|
||||||
|
self.action_new_file = reg('document-new.png', _('&New file'), self.boss.add_file, 'new-file', (), _('Create a new file in the current book'))
|
||||||
self.action_open_book = reg('document_open.png', _('Open &book'), self.boss.open_book, 'open-book', 'Ctrl+O', _('Open a new book'))
|
self.action_open_book = reg('document_open.png', _('Open &book'), self.boss.open_book, 'open-book', 'Ctrl+O', _('Open a new book'))
|
||||||
self.action_global_undo = reg('back.png', _('&Revert to before'), self.boss.do_global_undo, 'global-undo', 'Ctrl+Left',
|
self.action_global_undo = reg('back.png', _('&Revert to before'), self.boss.do_global_undo, 'global-undo', 'Ctrl+Left',
|
||||||
_('Revert book to before the last action (Undo)'))
|
_('Revert book to before the last action (Undo)'))
|
||||||
@ -245,6 +253,7 @@ class Main(MainWindow):
|
|||||||
b = self.menuBar()
|
b = self.menuBar()
|
||||||
|
|
||||||
f = b.addMenu(_('&File'))
|
f = b.addMenu(_('&File'))
|
||||||
|
f.addAction(self.action_new_file)
|
||||||
f.addAction(self.action_open_book)
|
f.addAction(self.action_open_book)
|
||||||
f.addAction(self.action_save)
|
f.addAction(self.action_save)
|
||||||
f.addAction(self.action_quit)
|
f.addAction(self.action_quit)
|
||||||
@ -302,7 +311,7 @@ class Main(MainWindow):
|
|||||||
return b
|
return b
|
||||||
|
|
||||||
a = create(_('Book tool bar'), 'global').addAction
|
a = create(_('Book tool bar'), 'global').addAction
|
||||||
for x in ('open_book', 'global_undo', 'global_redo', 'save', 'create_checkpoint', 'toc'):
|
for x in ('new_file', 'open_book', 'global_undo', 'global_redo', 'save', 'create_checkpoint', 'toc'):
|
||||||
a(getattr(self, 'action_' + x))
|
a(getattr(self, 'action_' + x))
|
||||||
|
|
||||||
a = create(_('Polish book tool bar'), 'polish').addAction
|
a = create(_('Polish book tool bar'), 'polish').addAction
|
||||||
|
Loading…
x
Reference in New Issue
Block a user