Merge pull request #314 from vkbo/update_search_replace

Update Search Replace
This commit is contained in:
Veronica K. Berglyd Olsen
2020-06-17 21:51:27 +02:00
committed by GitHub
26 changed files with 1191 additions and 322 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ install:
- pip install --upgrade pip
- pip install -r requirements.txt
# - pip install pytest-faulthandler
- pip install PyVirtualDisplay==0.2.5
- pip install PyVirtualDisplay
- pip install pytest-xvfb
- pip install pytest-cov
- pip install pytest-qt
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
version="1.2"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg7731">
<metadata
id="metadata7737">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs7735" />
<path
d="m 20.121169,3.8786433 c -1.170024,-1.1715244 -3.072064,-1.1715244 -4.242088,0 L 12,7.7577241 8.1209195,3.8786433 c -1.1700247,-1.1715244 -3.0720643,-1.1715244 -4.2420887,0 -1.1715244,1.1715244 -1.1715244,3.070564 0,4.2420884 l 3.8775807,3.8790803 -3.8775807,3.879081 c -1.1715244,1.171525 -1.1715244,3.070564 0,4.242089 C 4.463843,20.707494 5.231859,21 5.999875,21 6.767891,21 7.5359065,20.707494 8.1209195,20.120982 L 12,16.241901 15.879081,20.120982 C 16.464093,20.707494 17.232109,21 18.000125,21 c 0.768015,0 1.536031,-0.292506 2.121044,-0.879018 1.171525,-1.171525 1.171525,-3.070564 0,-4.242089 l -3.87758,-3.879081 3.87758,-3.8790803 c 1.171525,-1.1715244 1.171525,-3.070564 0,-4.2420884 z"
id="path7729"
style="stroke-width:1.50003123;fill:#aeaeae;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg7731"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata7737">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs7735" />
<path
style="stroke-width:1.50003123;fill:#000000;fill-opacity:0.78039217"
id="path7729"
d="m 20.121169,3.8786433 c -1.170024,-1.1715244 -3.072064,-1.1715244 -4.242088,0 L 12,7.7577241 8.1209195,3.8786433 c -1.1700247,-1.1715244 -3.0720643,-1.1715244 -4.2420887,0 -1.1715244,1.1715244 -1.1715244,3.070564 0,4.2420884 l 3.8775807,3.8790803 -3.8775807,3.879081 c -1.1715244,1.171525 -1.1715244,3.070564 0,4.242089 C 4.463843,20.707494 5.231859,21 5.999875,21 6.767891,21 7.5359065,20.707494 8.1209195,20.120982 L 12,16.241901 15.879081,20.120982 C 16.464093,20.707494 17.232109,21 18.000125,21 c 0.768015,0 1.536031,-0.292506 2.121044,-0.879018 1.171525,-1.171525 1.171525,-3.070564 0,-4.242089 l -3.87758,-3.879081 3.87758,-3.8790803 c 1.171525,-1.1715244 1.171525,-3.070564 0,-4.2420884 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
width="24"
height="24"
viewBox="0 0 6.3499999 6.3500002"
version="1.1"
id="svg8">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="fill:#aeaeae;fill-opacity:1"
id="layer1"
transform="translate(0,-290.64999)">
<g
aria-label="Aa"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;fill:#aeaeae;fill-opacity:1;stroke:none;stroke-width:0.264583"
id="text817">
<g
id="g968"
style="fill:#aeaeae;fill-opacity:1">
<path
id="path961"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#aeaeae;fill-opacity:1;stroke-width:0.264583"
d="M 0.35983601,295.49539 H 1.0364638 l 0.2815167,-0.85936 H 2.631725 l 0.2815166,0.85936 H 3.6195028 L 2.4292305,292.0678 H 1.6143138 Z m 1.14582229,-1.42734 0.4741333,-1.45203 0.4691945,1.45203 z" />
<path
id="path963"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#aeaeae;fill-opacity:1;stroke-width:0.264583"
d="m 4.701114,295.54478 c 0.3062111,0 0.5679722,-0.10372 0.7507111,-0.29139 l 0.0889,0.242 H 5.990164 v -1.44215 c 0,-0.65194 -0.3704167,-1.00754 -1.0766778,-1.00754 -0.3457222,0 -0.7112,0.084 -0.9927167,0.23213 l 0.1481667,0.43956 c 0.2469444,-0.0988 0.48895,-0.14816 0.7112,-0.14816 0.3852334,0 0.5926667,0.14816 0.5926667,0.43462 v 0.0642 c -1.0421056,0.0148 -1.5014223,0.25189 -1.5014223,0.76059 0,0.43462 0.3259667,0.71614 0.8297334,0.71614 z m -0.1975556,-0.8001 c 0,-0.21731 0.2518834,-0.31609 0.8692445,-0.32597 v 0.45438 c -0.1185333,0.1136 -0.2815167,0.1778 -0.4642556,0.1778 -0.2420055,0 -0.4049889,-0.12347 -0.4049889,-0.30621 z" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8"
version="1.1"
viewBox="0 0 6.3499999 6.3500002"
height="24"
width="24">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-290.64999)"
id="layer1">
<g
id="text817"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444494px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335"
aria-label="Aa">
<g
style="fill:#000000;fill-opacity:0.78039217"
id="g968">
<path
d="M 0.35983601,295.49539 H 1.0364638 l 0.2815167,-0.85936 H 2.631725 l 0.2815166,0.85936 H 3.6195028 L 2.4292305,292.0678 H 1.6143138 Z m 1.14582229,-1.42734 0.4741333,-1.45203 0.4691945,1.45203 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458335;fill:#000000;fill-opacity:0.78039217"
id="path961" />
<path
d="m 4.701114,295.54478 c 0.3062111,0 0.5679722,-0.10372 0.7507111,-0.29139 l 0.0889,0.242 H 5.990164 v -1.44215 c 0,-0.65194 -0.3704167,-1.00754 -1.0766778,-1.00754 -0.3457222,0 -0.7112,0.084 -0.9927167,0.23213 l 0.1481667,0.43956 c 0.2469444,-0.0988 0.48895,-0.14816 0.7112,-0.14816 0.3852334,0 0.5926667,0.14816 0.5926667,0.43462 v 0.0642 c -1.0421056,0.0148 -1.5014223,0.25189 -1.5014223,0.76059 0,0.43462 0.3259667,0.71614 0.8297334,0.71614 z m -0.1975556,-0.8001 c 0,-0.21731 0.2518834,-0.31609 0.8692445,-0.32597 v 0.45438 c -0.1185333,0.1136 -0.2815167,0.1778 -0.4642556,0.1778 -0.2420055,0 -0.4049889,-0.12347 -0.4049889,-0.30621 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458335;fill:#000000;fill-opacity:0.78039217"
id="path963" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
version="1.2"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg2150">
<metadata
id="metadata2156">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2154" />
<path
d="m 17,6.4445834 h -2.317778 l 1.436667,-1.4366667 c 0.434444,-0.4344445 0.434444,-1.1366667 0,-1.5711111 -0.434445,-0.4344445 -1.136667,-0.4344445 -1.571111,0 l -4.118889,4.1188889 4.118889,4.1188895 c 0.216666,0.216666 0.501111,0.325555 0.785555,0.325555 0.284445,0 0.568889,-0.108889 0.785556,-0.325555 0.434444,-0.434445 0.434444,-1.136667 0,-1.571112 L 14.682222,8.6668056 H 17 c 1.532222,0 2.777778,1.4955554 2.777778,3.3333334 0,1.837778 -1.495556,3.333333 -3.333334,3.333333 -0.614444,0 -1.111111,0.497778 -1.111111,1.111112 0,0.613333 0.496667,1.111111 1.111111,1.111111 C 19.507778,17.555695 22,15.063472 22,12.000139 22,8.9368056 19.756667,6.4445834 17,6.4445834 Z M 7.8811111,12.325695 c -0.4344444,0.434444 -0.4344444,1.136666 0,1.571111 L 9.317778,15.333472 H 7 c -1.5322222,0 -2.7777778,-1.495555 -2.7777778,-3.333333 0,-1.837778 1.4955556,-3.3333334 3.3333334,-3.3333334 0.6144444,0 1.1111111,-0.4977778 1.1111111,-1.1111111 C 8.6666667,6.9423611 8.17,6.4445834 7.5555556,6.4445834 4.4922222,6.4445834 2,8.9368056 2,12.000139 c 0,3.063333 2.2433333,5.555556 5,5.555556 h 2.317778 l -1.4366669,1.436666 c -0.4344444,0.434445 -0.4344444,1.136667 0,1.571111 0.2166667,0.216667 0.5011111,0.325556 0.7855556,0.325556 0.2844444,0 0.5688893,-0.108889 0.7855553,-0.325556 l 4.118889,-4.118888 -4.118889,-4.118889 c -0.434444,-0.434445 -1.1366664,-0.434445 -1.5711109,0 z"
id="path2148"
style="stroke-width:1.11111116;fill:#aeaeae;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg2150"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata2156">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2154" />
<path
style="stroke-width:1.11111116;fill:#000000;fill-opacity:0.78039217"
id="path2148"
d="m 17,6.4445834 h -2.317778 l 1.436667,-1.4366667 c 0.434444,-0.4344445 0.434444,-1.1366667 0,-1.5711111 -0.434445,-0.4344445 -1.136667,-0.4344445 -1.571111,0 l -4.118889,4.1188889 4.118889,4.1188895 c 0.216666,0.216666 0.501111,0.325555 0.785555,0.325555 0.284445,0 0.568889,-0.108889 0.785556,-0.325555 0.434444,-0.434445 0.434444,-1.136667 0,-1.571112 L 14.682222,8.6668056 H 17 c 1.532222,0 2.777778,1.4955554 2.777778,3.3333334 0,1.837778 -1.495556,3.333333 -3.333334,3.333333 -0.614444,0 -1.111111,0.497778 -1.111111,1.111112 0,0.613333 0.496667,1.111111 1.111111,1.111111 C 19.507778,17.555695 22,15.063472 22,12.000139 22,8.9368056 19.756667,6.4445834 17,6.4445834 Z M 7.8811111,12.325695 c -0.4344444,0.434444 -0.4344444,1.136666 0,1.571111 L 9.317778,15.333472 H 7 c -1.5322222,0 -2.7777778,-1.495555 -2.7777778,-3.333333 0,-1.837778 1.4955556,-3.3333334 3.3333334,-3.3333334 0.6144444,0 1.1111111,-0.4977778 1.1111111,-1.1111111 C 8.6666667,6.9423611 8.17,6.4445834 7.5555556,6.4445834 4.4922222,6.4445834 2,8.9368056 2,12.000139 c 0,3.063333 2.2433333,5.555556 5,5.555556 h 2.317778 l -1.4366669,1.436666 c -0.4344444,0.434445 -0.4344444,1.136667 0,1.571111 0.2166667,0.216667 0.5011111,0.325556 0.7855556,0.325556 0.2844444,0 0.5688893,-0.108889 0.7855553,-0.325556 l 4.118889,-4.118888 -4.118889,-4.118889 c -0.434444,-0.434445 -1.1366664,-0.434445 -1.5711109,0 z" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8"
version="1.1"
viewBox="0 0 6.3499999 6.3500002"
height="24"
width="24">
<g
style="font-size:5.64444px;line-height:1.25;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans, Normal';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
id="text1549"
aria-label="AB">
<path
id="path1551"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.251875;fill:#aeaeae;fill-opacity:1"
d="M 0.52916667,4.8827979 H 1.1423606 L 1.397485,4.023431 H 2.588066 L 2.8431905,4.8827979 H 3.4832396 L 2.4045554,1.4552083 H 1.6660372 Z M 1.5675681,3.4554587 1.9972514,2.003425 2.4224589,3.4554587 Z" />
<path
id="path1553"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.25188;fill:#aeaeae;fill-opacity:1"
d="m 3.4395834,4.8827979 h 1.1592928 c 0.7519737,0 1.2219572,-0.4099279 1.2219572,-1.0618613 0,-0.4296835 -0.2058975,-0.7408335 -0.5639802,-0.8840614 0.1969454,-0.1432278 0.3177984,-0.3951112 0.3177984,-0.6667501 0,-0.5284612 -0.389415,-0.8149168 -1.1055804,-0.8149168 H 3.4395834 Z M 4.0125157,1.9688528 h 0.4117952 c 0.3625587,0 0.5639802,0.1679223 0.5639802,0.4642557 0,0.1778 -0.076092,0.3160889 -0.2506578,0.4247445 H 4.0125157 Z m 0,2.4003005 V 3.3566809 h 0.6714051 c 0.3357025,0 0.5416002,0.2173112 0.5416002,0.5482168 0,0.3160889 -0.1745654,0.4642556 -0.5505523,0.4642556 z" />
</g>
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8"
version="1.1"
viewBox="0 0 6.3499999 6.3500002"
height="24"
width="24">
<g
style="font-size:5.64444px;line-height:1.25;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans, Normal';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
id="text1549"
aria-label="AB">
<path
id="path1551"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.251875;fill:#000000;fill-opacity:0.78039217"
d="M 0.52916667,4.8827979 H 1.1423606 L 1.397485,4.023431 H 2.588066 L 2.8431905,4.8827979 H 3.4832396 L 2.4045554,1.4552083 H 1.6660372 Z M 1.5675681,3.4554587 1.9972514,2.003425 2.4224589,3.4554587 Z" />
<path
id="path1553"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.25188;fill:#000000;fill-opacity:0.78039217"
d="m 3.4395834,4.8827979 h 1.1592928 c 0.7519737,0 1.2219572,-0.4099279 1.2219572,-1.0618613 0,-0.4296835 -0.2058975,-0.7408335 -0.5639802,-0.8840614 0.1969454,-0.1432278 0.3177984,-0.3951112 0.3177984,-0.6667501 0,-0.5284612 -0.389415,-0.8149168 -1.1055804,-0.8149168 H 3.4395834 Z M 4.0125157,1.9688528 h 0.4117952 c 0.3625587,0 0.5639802,0.1679223 0.5639802,0.4642557 0,0.1778 -0.076092,0.3160889 -0.2506578,0.4247445 H 4.0125157 Z m 0,2.4003005 V 3.3566809 h 0.6714051 c 0.3357025,0 0.5416002,0.2173112 0.5416002,0.5482168 0,0.3160889 -0.1745654,0.4642556 -0.5505523,0.4642556 z" />
</g>
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8301"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata8307">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8305" />
<path
style="stroke-width:1.18357;fill:#aeaeae;fill-opacity:1"
id="path8299"
d="m 19.591431,11.061428 c -0.92437,-0.92437 -2.422771,-0.92437 -3.347141,0 l -1.877146,1.877144 V 4.367144 C 14.367144,3.059297 13.306664,2 12,2 10.692153,2 9.6328557,3.059297 9.6328557,4.367144 v 8.571428 l -1.877145,-1.877144 c -0.9243698,-0.92437 -2.4227719,-0.92437 -3.3471417,0 -0.9243697,0.924369 -0.9243697,2.422771 0,3.347141 L 12,22 19.591431,14.408569 c 0.92437,-0.92437 0.92437,-2.421588 0,-3.347141 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8301"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata8307">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8305" />
<path
style="stroke-width:1.18357;fill:#000000;fill-opacity:0.77999997"
id="path8299"
d="m 19.591431,11.061428 c -0.92437,-0.92437 -2.422771,-0.92437 -3.347141,0 l -1.877146,1.877144 V 4.367144 C 14.367144,3.059297 13.306664,2 12,2 10.692153,2 9.6328557,3.059297 9.6328557,4.367144 v 8.571428 l -1.877145,-1.877144 c -0.9243698,-0.92437 -2.4227719,-0.92437 -3.3471417,0 -0.9243697,0.924369 -0.9243697,2.422771 0,3.347141 L 12,22 19.591431,14.408569 c 0.92437,-0.92437 0.92437,-2.421588 0,-3.347141 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
width="24"
height="24"
viewBox="0 0 6.3499999 6.3500002"
version="1.1"
id="svg8">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="fill:#aeaeae;fill-opacity:1"
id="layer1"
transform="translate(0,-290.64999)">
<g
aria-label=".*"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;fill:#aeaeae;fill-opacity:1;stroke:none;stroke-width:0.264583"
id="text817">
<g
id="g1606"
style="fill:#aeaeae;fill-opacity:1">
<path
id="path1599"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#aeaeae;fill-opacity:1;stroke-width:0.264583"
d="m 1.9550981,295.54478 c 0.2518833,0 0.4198055,-0.18274 0.4198055,-0.4198 0,-0.25189 -0.1679222,-0.43463 -0.4198055,-0.43463 -0.2568223,0 -0.4247445,0.18274 -0.4247445,0.43463 0,0.23706 0.1679222,0.4198 0.4247445,0.4198 z" />
<path
id="path1601"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#aeaeae;fill-opacity:1;stroke-width:0.264583"
d="m 3.5108408,294.12732 h 0.5482167 l -0.1234722,-0.77541 0.6124222,0.49883 0.2568223,-0.43956 -0.7655278,-0.32103 0.7803444,-0.29139 -0.2518833,-0.43956 -0.6371167,0.48401 0.1284111,-0.8001 H 3.5108408 l 0.1382889,0.79516 -0.6223,-0.50377 -0.2518833,0.43956 0.7704667,0.31609 -0.7754056,0.30621 0.2469444,0.4445 0.6321778,-0.50376 z" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8"
version="1.1"
viewBox="0 0 6.3499999 6.3500002"
height="24"
width="24">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-290.64999)"
id="layer1">
<g
id="text817"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444494px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335"
aria-label=".*">
<g
style="fill:#000000;fill-opacity:0.78039217"
id="g1606">
<path
d="m 1.9550981,295.54478 c 0.2518833,0 0.4198055,-0.18274 0.4198055,-0.4198 0,-0.25189 -0.1679222,-0.43463 -0.4198055,-0.43463 -0.2568223,0 -0.4247445,0.18274 -0.4247445,0.43463 0,0.23706 0.1679222,0.4198 0.4247445,0.4198 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458335;fill:#000000;fill-opacity:0.78039217"
id="path1599" />
<path
d="m 3.5108408,294.12732 h 0.5482167 l -0.1234722,-0.77541 0.6124222,0.49883 0.2568223,-0.43956 -0.7655278,-0.32103 0.7803444,-0.29139 -0.2518833,-0.43956 -0.6371167,0.48401 0.1284111,-0.8001 H 3.5108408 l 0.1382889,0.79516 -0.6223,-0.50377 -0.2518833,0.43956 0.7704667,0.31609 -0.7754056,0.30621 0.2469444,0.4445 0.6321778,-0.50376 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:Cantarell;-inkscape-font-specification:'Cantarell, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458335;fill:#000000;fill-opacity:0.78039217"
id="path1601" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8"
version="1.1"
viewBox="0 0 6.3499999 6.3500002"
height="24"
width="24">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-290.64999)"
id="layer1">
<g
id="text817"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
aria-label=".*">
<g
style="fill:#000000;fill-opacity:0.780392"
id="g1606">
<path
id="rect1629"
transform="matrix(0.26458334,0,0,0.26458334,0,290.64999)"
d="m 2,11.5 v 4 1 2 h 20 v -3 -4 h -3 v 4 H 5 v -4 z"
style="fill:#aeaeae;fill-opacity:1;stroke:none;stroke-width:1.76363;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
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"
id="svg8"
version="1.1"
viewBox="0 0 6.3499999 6.3500002"
height="24"
width="24">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-290.64999)"
id="layer1">
<g
id="text817"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:Roboto;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
aria-label=".*">
<g
style="fill:#000000;fill-opacity:0.780392"
id="g1606">
<path
id="rect1629"
transform="matrix(0.26458334,0,0,0.26458334,0,290.64999)"
d="m 2,11.5 v 4 1 2 h 20 v -3 -4 h -3 v 4 H 5 v -4 z"
style="fill:#000000;fill-opacity:0.780392;stroke:none;stroke-width:1.76363;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+21
View File
@@ -172,3 +172,24 @@ def splitVersionNumber(vString):
vInt = vMajor*10000 + vMinor*100 + vPatch
return [vMajor, vMinor, vPatch, vInt]
def transferCase(theSource, theTarget):
"""Transfers the case of the source word to the target word. This
will consider all upper or lower, and first char capitalisation.
"""
theResult = theTarget
if not isinstance(theSource, str) or not isinstance(theTarget, str):
return theResult
if len(theTarget) < 1 or len(theSource) < 1:
return theResult
if theSource[0] == theSource[0].upper():
theResult = theTarget[0].upper() + theTarget[1:]
if theSource == theSource.upper():
theResult = theTarget.upper()
elif theSource == theSource.lower():
theResult = theTarget.lower()
return theResult
+34 -3
View File
@@ -133,6 +133,13 @@ class Config:
self.spellTool = None
self.spellLanguage = None
self.searchCase = False
self.searchWord = False
self.searchRegEx = False
self.searchLoop = False
self.searchNextFile = False
self.searchMatchCap = False
## Backup
self.backupPath = ""
self.backupOnClose = False
@@ -482,6 +489,24 @@ class Config:
self.viewSynopsis = self._parseLine(
cnfParse, cnfSec, "viewsynopsis", self.CNF_BOOL, self.viewSynopsis
)
self.searchCase = self._parseLine(
cnfParse, cnfSec, "searchcase", self.CNF_BOOL, self.searchCase
)
self.searchWord = self._parseLine(
cnfParse, cnfSec, "searchword", self.CNF_BOOL, self.searchWord
)
self.searchRegEx = self._parseLine(
cnfParse, cnfSec, "searchregex", self.CNF_BOOL, self.searchRegEx
)
self.searchLoop = self._parseLine(
cnfParse, cnfSec, "searchloop", self.CNF_BOOL, self.searchLoop
)
self.searchNextFile = self._parseLine(
cnfParse, cnfSec, "searchnextfile", self.CNF_BOOL, self.searchNextFile
)
self.searchMatchCap = self._parseLine(
cnfParse, cnfSec, "searchmatchcap", self.CNF_BOOL, self.searchMatchCap
)
## Path
cnfSec = "Path"
@@ -571,9 +596,15 @@ class Config:
## State
cnfSec = "State"
cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec,"showrefpanel",str(self.showRefPanel))
cnfParse.set(cnfSec,"viewcomments",str(self.viewComments))
cnfParse.set(cnfSec,"viewsynopsis",str(self.viewSynopsis))
cnfParse.set(cnfSec,"showrefpanel", str(self.showRefPanel))
cnfParse.set(cnfSec,"viewcomments", str(self.viewComments))
cnfParse.set(cnfSec,"viewsynopsis", str(self.viewSynopsis))
cnfParse.set(cnfSec,"searchcase", str(self.searchCase))
cnfParse.set(cnfSec,"searchword", str(self.searchWord))
cnfParse.set(cnfSec,"searchregex", str(self.searchRegEx))
cnfParse.set(cnfSec,"searchloop", str(self.searchLoop))
cnfParse.set(cnfSec,"searchnextfile", str(self.searchNextFile))
cnfParse.set(cnfSec,"searchmatchcap", str(self.searchMatchCap))
## Path
cnfSec = "Path"
-2
View File
@@ -2,7 +2,6 @@
from nw.gui.about import GuiAbout
from nw.gui.build import GuiBuildNovel
from nw.gui.docbars import GuiSearchBar
from nw.gui.doceditor import GuiDocEditor
from nw.gui.docmerge import GuiDocMerge
from nw.gui.docsplit import GuiDocSplit
@@ -23,7 +22,6 @@ from nw.gui.theme import GuiIcons, GuiTheme
__all__ = [
"GuiAbout",
"GuiBuildNovel",
"GuiSearchBar",
"GuiDocEditor",
"GuiDocMerge",
"GuiDocSplit",
+1 -1
View File
@@ -459,7 +459,7 @@ class GuiBuildNovel(QDialog):
makeHtml.setStyles(not noStyling)
# Make sure the tree order is correct
self.theParent.treeView.saveTreeOrder()
self.theParent.treeView.flushTreeOrder()
self.buildProgress.setMaximum(len(self.theProject.projTree))
self.buildProgress.setValue(0)
-168
View File
@@ -1,168 +0,0 @@
# -*- coding: utf-8 -*-
"""novelWriter GUI Main Window SearchBar
novelWriter GUI Main Window SearchBar
=========================================
Class holding the main window search bar
File History:
Created: 2019-09-29 [0.2.1] GuiSearchBar
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
import nw
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QPalette, QColor, QIcon
from PyQt5.QtWidgets import (
qApp, QWidget, QFrame, QGridLayout, QLabel, QLineEdit, QPushButton,
QHBoxLayout, QToolButton, QScrollArea
)
from nw.constants import nwDocAction, nwUnicode
logger = logging.getLogger(__name__)
class GuiSearchBar(QWidget):
def __init__(self, theParent):
QWidget.__init__(self, theParent)
logger.debug("Initialising GuiSearchBar ...")
self.mainConf = nw.CONFIG
self.theParent = theParent
self.theTheme = theParent.theTheme
self.repVisible = False
self.setContentsMargins(0, 0, 0, 0)
self.mainBox = QGridLayout(self)
self.setLayout(self.mainBox)
self.searchBox = QLineEdit()
self.replaceBox = QLineEdit()
self.searchLabel = QLabel("Search")
self.replaceLabel = QLabel("Replace")
self.closeButton = QPushButton(self.theTheme.getIcon("close"),"")
self.searchButton = QPushButton(self.theTheme.getIcon("search"),"")
self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"),"")
self.closeButton.clicked.connect(self._doClose)
self.searchButton.clicked.connect(self._doSearch)
self.replaceButton.clicked.connect(self._doReplace)
self.searchBox.returnPressed.connect(self._doSearch)
self.replaceBox.returnPressed.connect(self._doSearch)
self.mainBox.addWidget(QLabel(""), 0, 0)
self.mainBox.addWidget(self.searchLabel, 0, 1)
self.mainBox.addWidget(self.searchBox, 0, 2)
self.mainBox.addWidget(self.searchButton, 0, 3)
self.mainBox.addWidget(self.closeButton, 0, 4)
self.mainBox.addWidget(self.replaceLabel, 1, 1)
self.mainBox.addWidget(self.replaceBox, 1, 2)
self.mainBox.addWidget(self.replaceButton, 1, 3)
self.mainBox.setColumnStretch(0, 1)
self.mainBox.setColumnStretch(1, 0)
self.mainBox.setColumnStretch(2, 0)
self.mainBox.setColumnStretch(3, 0)
self.mainBox.setColumnStretch(4, 0)
self.mainBox.setContentsMargins(0, 0, 0, 0)
boxWidth = 16*self.theTheme.textNWidth
self.searchBox.setMinimumWidth(boxWidth)
self.replaceBox.setMinimumWidth(boxWidth)
self._replaceVisible(False)
logger.debug("GuiSearchBar initialisation complete")
return
##
# Get and Set Functions
##
def setSearchText(self, theText):
"""Open the search bar and set the search text to the text
provided, if any.
"""
if not self.isVisible():
self.setVisible(True)
self.searchBox.setText(theText)
self.searchBox.setFocus()
logger.verbose("Setting search text to '%s'" % theText)
return True
def setReplaceText(self, theText):
"""Set the replace text.
"""
self._replaceVisible(True)
self.replaceBox.setFocus()
self.replaceBox.setText(theText)
return True
def getSearchText(self):
"""Return the current search text.
"""
return self.searchBox.text()
def getReplaceText(self):
"""Return the current replace text.
"""
return self.replaceBox.text()
##
# Internal Functions
##
def _doClose(self):
"""Hide the search/replace bar.
"""
self._replaceVisible(False)
self.setVisible(False)
return
def _doSearch(self):
"""Call the search action function for the document editor.
"""
modKey = qApp.keyboardModifiers()
if modKey == Qt.ShiftModifier:
self.theParent.docEditor.docAction(nwDocAction.GO_PREV)
else:
self.theParent.docEditor.docAction(nwDocAction.GO_NEXT)
return
def _doReplace(self):
"""Call the replace action function for the document editor.
"""
self.theParent.docEditor.docAction(nwDocAction.REPL_NEXT)
return
def _replaceVisible(self, isVisible):
"""Set the visibility of all the replace widgets.
"""
self.replaceLabel.setVisible(isVisible)
self.replaceBox.setVisible(isVisible)
self.replaceButton.setVisible(isVisible)
self.repVisible = isVisible
return True
# END Class GuiSearchBar
+448 -67
View File
@@ -6,9 +6,11 @@
Class holding the document editor
File History:
Created: 2018-09-29 [0.0.1] GuiDocEditor
Created: 2019-04-22 [0.0.1] WordCounter
Created: 2020-04-25 [0.4.5] GuiDocEditHeader
Created: 2018-09-29 [0.0.1] GuiDocEditor
Created: 2019-04-22 [0.0.1] BackgroundWordCounter
Created: 2019-09-29 [0.2.1] GuiDocEditSearch
Created: 2020-04-25 [0.4.5] GuiDocEditHeader
Rewritten: 2020-06-15 [0.9.0] GuiDocEditSearch
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
@@ -32,20 +34,22 @@ import nw
from time import time
from PyQt5.QtCore import Qt, QSize, QThread, QTimer, pyqtSlot
from PyQt5.QtCore import Qt, QSize, QThread, QTimer, pyqtSlot, QRegExp
from PyQt5.QtGui import (
QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette,
QTextDocument, QCursor
)
from PyQt5.QtWidgets import (
qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox, QWidget, QLabel,
QToolButton, QHBoxLayout
QToolBar, QToolButton, QHBoxLayout, QGridLayout, QLineEdit, QPushButton,
QFrame
)
from nw.core import NWDoc
from nw.gui.dochighlight import GuiDocHighlighter
from nw.core import NWSpellSimple, countWords
from nw.constants import nwUnicode, nwDocAction
from nw.common import transferCase
logger = logging.getLogger(__name__)
@@ -87,6 +91,7 @@ class GuiDocEditor(QTextEdit):
# Document Title
self.docHeader = GuiDocEditHeader(self)
self.docSearch = GuiDocEditSearch(self)
# Syntax
self.hLight = GuiDocHighlighter(self.qDocument, self.theParent)
@@ -98,6 +103,7 @@ class GuiDocEditor(QTextEdit):
# Editor State
self.hasSelection = False
self.setMinimumWidth(self.mainConf.pxInt(300))
self.setAutoFillBackground(True)
self.setAcceptRichText(False)
# Custom Shortcuts
@@ -126,7 +132,7 @@ class GuiDocEditor(QTextEdit):
self.wcTimer.setInterval(int(self.wcInterval*1000))
self.wcTimer.timeout.connect(self._runCounter)
self.wCounter = WordCounter(self)
self.wCounter = BackgroundWordCounter(self)
self.wCounter.finished.connect(self._updateCounts)
self.initEditor()
@@ -182,12 +188,15 @@ class GuiDocEditor(QTextEdit):
self.setFont(theFont)
docPalette = self.palette()
docPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
docPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
self.setPalette(docPalette)
# Set default text margins
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
cM = self.mainConf.getTextMargin()
self.qDocument.setDocumentMargin(0)
self.setViewportMargins(cM, cM, cM, cM)
# Also set the document text options for the document text flow
theOpt = QTextOption()
@@ -309,6 +318,7 @@ class GuiDocEditor(QTextEdit):
Config.textFixedW is enabled or we're in Zen mode. Otherwise,
just ensure the margins are set correctly.
"""
wW = self.width()
cM = self.mainConf.getTextMargin()
vBar = self.verticalScrollBar()
@@ -322,40 +332,27 @@ class GuiDocEditor(QTextEdit):
tW = self.mainConf.getZenWidth()
else:
tW = self.mainConf.getTextWidth()
wW = self.width()
tM = int((wW - sW - tW)/2)
tM = (wW - sW - tW)//2
if tM < cM:
tM = cM
else:
tM = cM
tB = self.frameWidth()
tW = self.width() - 2*tB - sW
tW = wW - 2*tB - sW
tH = self.docHeader.height()
tT = cM - tH
self.docHeader.setGeometry(tB, tB, tW, tH)
self.setViewportMargins(0, tH, 0, 0)
docFormat = self.qDocument.rootFrame().frameFormat()
docFormat.setLeftMargin(tM)
docFormat.setRightMargin(tM)
if tT > 0:
docFormat.setTopMargin(tT)
if self.docSearch.isVisible():
rH = self.docSearch.height()
rW = self.docSearch.width()
rL = wW - sW - rW - 2*tB
self.docSearch.move(rL, 2*tB)
else:
docFormat.setTopMargin(0)
rH = 0
# Updating root frame triggers a QTextDocument->contentsChange
# signal, which we do not want as it re-runs the syntax
# highlighter and spell checker, so we block it briefly.
# We then emit a signal that does not trigger re-highlighting.
self.qDocument.blockSignals(True)
self.qDocument.rootFrame().setFrameFormat(docFormat)
self.qDocument.blockSignals(False)
# The line below causes issues with large documents as it
# triggers an early repaint that seems to only render a part of
# the document. Leaving it here as a warning for now.
# self.qDocument.contentsChange.emit(0, 0, 0)
# print(tM, tH, rH, max(tM, tH, rH))
self.setViewportMargins(tM, max(cM, tH, rH), tM, cM)
return
@@ -532,7 +529,7 @@ class GuiDocEditor(QTextEdit):
elif theAction == nwDocAction.GO_NEXT:
self._findNext()
elif theAction == nwDocAction.GO_PREV:
self._findPrev()
self._findNext(isBackward=True)
elif theAction == nwDocAction.REPL_NEXT:
self._replaceNext()
elif theAction == nwDocAction.BLOCK_H1:
@@ -573,6 +570,12 @@ class GuiDocEditor(QTextEdit):
))
return
def closeSearch(self):
"""Close the search box.
"""
self.docSearch.closeSearch()
return self.docSearch.isVisible()
##
# Document Events and Maintenance
##
@@ -590,6 +593,9 @@ class GuiDocEditor(QTextEdit):
as it is triggered on every keypress when typing.
"""
self.hasSelection = self.textCursor().hasSelection()
if self.docSearch.searchBox.hasFocus():
# Block the event when the focus is on the search bar.
return
if keyEvent.modifiers() == Qt.ShiftModifier:
theKey = keyEvent.key()
@@ -1131,7 +1137,8 @@ class GuiDocEditor(QTextEdit):
selText = theCursor.selectedText()
else:
selText = ""
self.theParent.searchBar.setSearchText(selText)
self.docSearch.setSearchText(selText)
self.updateDocMargins()
return
def _beginReplace(self):
@@ -1139,54 +1146,82 @@ class GuiDocEditor(QTextEdit):
text.
"""
self._beginSearch()
self.theParent.searchBar.setReplaceText("")
self.docSearch.setReplaceText("")
return
def _findNext(self):
"""Searches for the next occurrence of the search bar text in
the document. Wraps back to the top if not found.
def _findNext(self, isBackward=False):
"""Searches for the next or previous occurrence of the search
bar text in the document. Wraps around if not found and loop is
enabled, or continues to next file if next file is enabled.
"""
searchFor = self.theParent.searchBar.getSearchText()
wasFound = self.find(searchFor)
if not wasFound:
theCursor = self.textCursor()
theCursor.movePosition(QTextCursor.Start)
self.setTextCursor(theCursor)
return
if not self.docSearch.isVisible():
self._beginSearch()
return
def _findPrev(self):
"""Searches for the previous occurrence of the search bar text
in the document. Wraps back to the end if not found.
"""
searchFor = self.theParent.searchBar.getSearchText()
wasFound = self.find(searchFor, QTextDocument.FindBackward)
findOpt = QTextDocument.FindFlag(0)
if isBackward:
findOpt |= QTextDocument.FindBackward
if self.docSearch.isCaseSense:
findOpt |= QTextDocument.FindCaseSensitively
if self.docSearch.isWholeWord:
findOpt |= QTextDocument.FindWholeWords
searchFor = self.docSearch.getSearchText()
wasFound = self.find(searchFor, findOpt)
if not wasFound:
theCursor = self.textCursor()
theCursor.movePosition(QTextCursor.End)
self.setTextCursor(theCursor)
if self.docSearch.doNextFile and not isBackward:
self.theParent.openNextDocument(
self.theHandle, wrapAround=self.docSearch.doLoop
)
elif self.docSearch.doLoop:
theCursor = self.textCursor()
theCursor.movePosition(
QTextCursor.End if isBackward else QTextCursor.Start
)
self.setTextCursor(theCursor)
self.find(searchFor, findOpt)
return
def _replaceNext(self):
"""Searches for the next occurrence of the search bar text in
the document and replaces it with the replace text. Wraps back
to the top if not found.
the document and replaces it with the replace text. Calls search
next automatically when done.
"""
if not self.docSearch.isVisible():
self._beginSearch()
return
if not theCursor.hasSelection():
return
theCursor = self.textCursor()
searchFor = self.theParent.searchBar.getSearchText()
replWith = self.theParent.searchBar.getReplaceText()
if theCursor.hasSelection() and theCursor.selectedText() == searchFor:
xPos = theCursor.selectionStart()
searchFor = self.docSearch.getSearchText()
replWith = self.docSearch.getReplaceText()
selText = theCursor.selectedText()
if self.docSearch.doMatchCap:
replWith = transferCase(selText, replWith)
if not self.docSearch.isCaseSense:
isMatch = searchFor.lower() == selText.lower()
else:
isMatch = searchFor == selText
if isMatch:
theCursor.beginEditBlock()
theCursor.removeSelectedText()
theCursor.insertText(replWith)
theCursor.endEditBlock()
theCursor.setPosition(xPos)
theCursor.setPosition(theCursor.selectionEnd())
self.setTextCursor(theCursor)
logger.verbose("Replaced occurrence of '%s' with '%s' on line %d" % (
searchFor, replWith, theCursor.blockNumber()
))
if searchFor != "":
if searchFor:
self._findNext()
return
def _setupSpellChecking(self):
@@ -1205,11 +1240,16 @@ class GuiDocEditor(QTextEdit):
# END Class GuiDocEditor
class WordCounter(QThread):
# =============================================================================================== #
# The Off GUI Thread Word Counter
# Runs the word counter in the background for the DocEditor
# =============================================================================================== #
def __init__(self, theParent):
QThread.__init__(self, theParent)
self.theParent = theParent
class BackgroundWordCounter(QThread):
def __init__(self, docEditor):
QThread.__init__(self, docEditor)
self.docEditor = docEditor
self.charCount = 0
self.wordCount = 0
self.paraCount = 0
@@ -1219,16 +1259,357 @@ class WordCounter(QThread):
"""Overloaded run function for the word counter, forwarding the
call to the function that does the actual counting.
"""
theText = self.theParent.getText()
theText = self.docEditor.getText()
cC, wC, pC = countWords(theText)
self.charCount = cC
self.wordCount = wC
self.paraCount = pC
return
## END Class BackgroundWordCounter
# =============================================================================================== #
# The Embedded Document Search/Replace Feature
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
# =============================================================================================== #
class GuiDocEditSearch(QFrame):
def __init__(self, docEditor):
QFrame.__init__(self, docEditor)
logger.debug("Initialising GuiDocEditSearch ...")
self.mainConf = nw.CONFIG
self.docEditor = docEditor
self.theParent = docEditor.theParent
self.theProject = docEditor.theProject
self.theTheme = docEditor.theTheme
self.repVisible = False
self.isCaseSense = self.mainConf.searchCase
self.isWholeWord = self.mainConf.searchWord
self.isRegEx = self.mainConf.searchRegEx
self.doLoop = self.mainConf.searchLoop
self.doNextFile = self.mainConf.searchNextFile
self.doMatchCap = self.mainConf.searchMatchCap
mPx = self.mainConf.pxInt(6)
fPx = int(0.9*self.theTheme.fontPixelSize)
tPx = int(0.8*self.theTheme.fontPixelSize)
boxFont = self.theTheme.guiFont
boxFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
self.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)
self.mainBox = QGridLayout(self)
self.setLayout(self.mainBox)
# Text Boxes
# ==========
self.searchBox = QLineEdit()
self.searchBox.setFont(boxFont)
self.searchBox.setPlaceholderText("Search")
self.searchBox.returnPressed.connect(self._doSearch)
self.replaceBox = QLineEdit()
self.replaceBox.setFont(boxFont)
self.replaceBox.setPlaceholderText("Replace")
self.replaceBox.returnPressed.connect(self._doSearch)
self.searchOpt = QToolBar(self)
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchOpt.setIconSize(QSize(tPx, tPx))
self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.searchOpt.setStyleSheet(r"QToolBar {padding: 0;}")
self.searchLabel = QLabel("Search")
self.searchLabel.setFont(boxFont)
self.searchLabel.setIndent(self.mainConf.pxInt(6))
self.toggleCase = QAction("Case Sensitive", self)
self.toggleCase.setToolTip("Match case")
self.toggleCase.setIcon(self.theTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction("Whole Words Only", self)
self.toggleWord.setToolTip("Match whole words")
self.toggleWord.setIcon(self.theTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction("RegEx Mode", self)
self.toggleRegEx.setToolTip("Use regular expressions")
self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction("Loop Search", self)
self.toggleLoop.setToolTip("Loop the search when reaching the end")
self.toggleLoop.setIcon(self.theTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction("Search Next File", self)
self.toggleProject.setToolTip("Continue searching in the next file")
self.toggleProject.setIcon(self.theTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile)
self.toggleProject.toggled.connect(self._doToggleProject)
self.searchOpt.addAction(self.toggleProject)
self.searchOpt.addSeparator()
self.toggleMatchCap = QAction("Preserve Case", self)
self.toggleMatchCap.setToolTip("Preserve case on replace")
self.toggleMatchCap.setIcon(self.theTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap)
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
self.searchOpt.addAction(self.toggleMatchCap)
self.searchOpt.addSeparator()
self.cancelSearch = QAction("Close Search", self)
self.cancelSearch.setToolTip("Close the search box [Esc]")
self.cancelSearch.setIcon(self.theTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch)
# Buttons
# =======
bPx = self.searchBox.sizeHint().height()
self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow)
self.showReplace.setCheckable(True)
self.showReplace.setStyleSheet(r"QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = QPushButton(self.theTheme.getIcon("search"),"")
self.searchButton.setFixedSize(QSize(bPx, bPx))
self.searchButton.setToolTip("Find in current document")
self.searchButton.clicked.connect(self._doSearch)
self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"),"")
self.replaceButton.setFixedSize(QSize(bPx, bPx))
self.replaceButton.setToolTip("Find and replace in current document")
self.replaceButton.clicked.connect(self._doReplace)
self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 2, Qt.AlignRight)
self.mainBox.addWidget(self.showReplace, 1, 0, 1, 1)
self.mainBox.addWidget(self.searchBox, 1, 1, 1, 2)
self.mainBox.addWidget(self.searchButton, 1, 3, 1, 1)
self.mainBox.addWidget(self.replaceBox, 2, 1, 1, 2)
self.mainBox.addWidget(self.replaceButton, 2, 3, 1, 1)
self.mainBox.setColumnStretch(0, 1)
self.mainBox.setColumnStretch(1, 0)
self.mainBox.setColumnStretch(2, 0)
self.mainBox.setColumnStretch(3, 0)
self.mainBox.setColumnStretch(4, 0)
self.mainBox.setSpacing(self.mainConf.pxInt(2))
self.mainBox.setContentsMargins(mPx, mPx, mPx, mPx)
boxWidth = self.mainConf.pxInt(200)
self.searchBox.setFixedWidth(boxWidth)
self.replaceBox.setFixedWidth(boxWidth)
self.replaceBox.setVisible(False)
self.replaceButton.setVisible(False)
self.adjustSize()
# Construct Box Colours
qPalette = self.searchBox.palette()
baseCol = qPalette.base().color()
rCol = baseCol.redF() + 0.1
gCol = baseCol.greenF() - 0.1
bCol = baseCol.blueF() - 0.1
mCol = max(rCol, gCol, bCol, 1.0)
errCol = QColor()
errCol.setRedF(rCol/mCol)
errCol.setGreenF(gCol/mCol)
errCol.setBlueF(bCol/mCol)
self.rxCol = {
True : baseCol,
False : errCol
}
logger.debug("GuiDocEditSearch initialisation complete")
return
## END Class WordCounter
def closeSearch(self):
"""Close the search box.
"""
self.mainConf.searchCase = self.isCaseSense
self.mainConf.searchWord = self.isWholeWord
self.mainConf.searchRegEx = self.isRegEx
self.mainConf.searchLoop = self.doLoop
self.mainConf.searchNextFile = self.doNextFile
self.mainConf.searchMatchCap = self.doMatchCap
self.showReplace.setChecked(False)
self.setVisible(False)
self.docEditor.updateDocMargins()
self.docEditor.setFocus()
return
##
# Get and Set Functions
##
def setSearchText(self, theText):
"""Open the search bar and set the search text to the text
provided, if any.
"""
if not self.isVisible():
self.setVisible(True)
self.searchBox.setText(theText)
self.searchBox.setFocus()
if self.isRegEx:
self._alertSearchValid(True)
logger.verbose("Setting search text to '%s'" % theText)
return True
def setReplaceText(self, theText):
"""Set the replace text.
"""
self.showReplace.setChecked(True)
self.replaceBox.setFocus()
self.replaceBox.setText(theText)
return True
def getSearchText(self):
"""Return the current search text either as text or as a regular
expression object.
"""
theText = self.searchBox.text()
if self.isRegEx and self.mainConf.verQtValue >= 50300:
if self.isCaseSense:
rxCase = Qt.CaseSensitive
else:
rxCase = Qt.CaseInsensitive
theRegEx = QRegExp(theText, rxCase)
self._alertSearchValid(theRegEx.isValid())
return theRegEx
return theText
def getReplaceText(self):
"""Return the current replace text.
"""
return self.replaceBox.text()
##
# Slots
##
def _doClose(self):
"""Hide the search/replace bar.
"""
self.closeSearch()
return
def _doSearch(self):
"""Call the search action function for the document editor.
"""
modKey = qApp.keyboardModifiers()
if modKey == Qt.ShiftModifier:
self.docEditor.docAction(nwDocAction.GO_PREV)
else:
self.docEditor.docAction(nwDocAction.GO_NEXT)
return
def _doReplace(self):
"""Call the replace action function for the document editor.
"""
self.docEditor.docAction(nwDocAction.REPL_NEXT)
return
def _doToggleReplace(self, theState):
"""Toggle the show/hide of the
"""
if theState:
self.showReplace.setArrowType(Qt.DownArrow)
else:
self.showReplace.setArrowType(Qt.RightArrow)
self.replaceBox.setVisible(theState)
self.replaceButton.setVisible(theState)
self.repVisible = theState
self.adjustSize()
self.docEditor.updateDocMargins()
return
def _doToggleCase(self, theState):
"""Enable/disable case sensitive mode.
"""
self.isCaseSense = theState
return
def _doToggleWord(self, theState):
"""Enable/disable whole word search mode.
"""
self.isWholeWord = theState
return
def _doToggleRegEx(self, theState):
"""Enable/disable regular expression search mode.
"""
self.isRegEx = theState
return
def _doToggleLoop(self, theState):
"""Enable/disable looping the search.
"""
self.doLoop = theState
return
def _doToggleProject(self, theState):
"""Enable/disable continuing search in next project file.
"""
self.doNextFile = theState
return
def _doToggleMatchCap(self, theState):
"""Enable/disable preserving capitalisation when replacing.
"""
self.doMatchCap = theState
return
##
# Internal Functions
##
def _alertSearchValid(self, isValid):
"""Highlight the search box to indicate the search string is or
isn't valid. Take the colour from the replace box.
"""
qPalette = self.replaceBox.palette()
qPalette.setColor(QPalette.Base, self.rxCol[isValid])
self.searchBox.setPalette(qPalette)
return
# END Class GuiDocEditSearch
# =============================================================================================== #
# The Embedded Document Header
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
# =============================================================================================== #
class GuiDocEditHeader(QWidget):
+12 -11
View File
@@ -236,13 +236,14 @@ class GuiDocViewer(QTextBrowser):
else:
sW = 0
cM = self.mainConf.getTextMargin()
tB = self.frameWidth()
tW = self.width() - 2*tB - sW
tH = self.docHeader.height()
fH = self.docFooter.height()
fY = self.height() - fH - tB
tT = self.mainConf.getTextMargin() - tH
bT = self.mainConf.getTextMargin() - fH
tT = cM - tH
bT = cM - fH
self.docHeader.setGeometry(tB, tB, tW, tH)
self.docFooter.setGeometry(tB, fY, tW, fH)
self.setViewportMargins(0, tH, 0, fH)
@@ -558,16 +559,16 @@ class GuiDocViewHeader(QWidget):
class GuiDocViewFooter(QWidget):
def __init__(self, theParent):
QWidget.__init__(self, theParent)
def __init__(self, docViewer):
QWidget.__init__(self, docViewer)
logger.debug("Initialising GuiDocViewFooter ...")
self.mainConf = nw.CONFIG
self.theParent = theParent
self.theTheme = theParent.theTheme
self.optState = theParent.theProject.optState
self.viewMeta = theParent.theParent.viewMeta
self.docViewer = docViewer
self.theParent = docViewer.theParent
self.theTheme = docViewer.theTheme
self.viewMeta = docViewer.theParent.viewMeta
self.theHandle = None
# Make a QPalette that matches the Syntax Theme
@@ -669,9 +670,9 @@ class GuiDocViewFooter(QWidget):
"""Toggle the sticky flag for the reference panel.
"""
logger.verbose("Reference sticky is %s" % str(theState))
self.theParent.stickyRef = theState
if not theState and self.theParent.theHandle is not None:
self.viewMeta.refreshReferences(self.theParent.theHandle)
self.docViewer.stickyRef = theState
if not theState and self.docViewer.theHandle is not None:
self.viewMeta.refreshReferences(self.docViewer.theHandle)
return
# END Class GuiDocViewFooter
+32 -7
View File
@@ -60,8 +60,9 @@ class GuiProjectTree(QTreeWidget):
self.theProject = theParent.theProject
# Tree Settings
self.theMap = None
self.orphRoot = None
self.theMap = None
self.orphRoot = None
self.treeChanged = False
self.ctxMenu = GuiProjectTreeMenu(self)
self.clearTree()
@@ -256,7 +257,7 @@ class GuiProjectTree(QTreeWidget):
pItem.insertChild(nIndex, cItem)
self.clearSelection()
cItem.setSelected(True)
self.theProject.setProjectChanged(True)
self._setTreeChanged(True)
else:
return False
return True
@@ -276,6 +277,16 @@ class GuiProjectTree(QTreeWidget):
self.theProject.setTreeOrder(theList)
return True
def flushTreeOrder(self):
"""Calls saveTreeOrder if there are unsaved changes, otherwise
does nothing.
"""
if self.treeChanged:
logger.verbose("Flushing project tree to project class")
self.saveTreeOrder()
self._setTreeChanged(False)
return
def getTreeFromHandle(self, tHandle):
"""Recursively return all the children items starting from a
given item handle.
@@ -332,6 +343,9 @@ class GuiProjectTree(QTreeWidget):
continue
self.deleteItem(tHandle, True)
if nTrash > 0:
self._setTreeChanged(True)
return True
def deleteItem(self, tHandle=None, alreadyAsked=False, askForTrash=False):
@@ -413,7 +427,7 @@ class GuiProjectTree(QTreeWidget):
trItemT.addChild(trItemC)
nwItemS.setParent(self.theProject.projTree.trashRoot())
self.theProject.setProjectChanged(True)
self._setTreeChanged(True)
self.theParent.theIndex.deleteHandle(tHandle)
elif nwItemS.itemType == nwItemType.FOLDER:
@@ -436,7 +450,7 @@ class GuiProjectTree(QTreeWidget):
if trItemS.childCount() == 0:
self.takeTopLevelItem(tIndex)
self.theParent.mainMenu.setAvailableRoot()
self.theProject.setProjectChanged(True)
self._setTreeChanged(True)
else:
self.makeAlert("Cannot delete root folder. It is not empty.", nwAlert.ERROR)
return False
@@ -732,6 +746,8 @@ class GuiProjectTree(QTreeWidget):
elif nwItem.itemType == nwItemType.TRASH:
newItem.setIcon(self.C_NAME, self.theTheme.getIcon(nwLabels.CLASS_ICON[tClass]))
self._setTreeChanged(True)
return newItem
def _addTrashRoot(self):
@@ -747,6 +763,7 @@ class GuiProjectTree(QTreeWidget):
self.theProject.projTree[trashHandle]
)
trItem.setExpanded(True)
self._setTreeChanged(True)
return trItem
def _addOrphanedRoot(self):
@@ -793,7 +810,7 @@ class GuiProjectTree(QTreeWidget):
nwItemS.setParent(pHandle)
self.propagateCount(tHandle, wC)
self.setTreeItemValues(tHandle)
self.theProject.setProjectChanged(True)
self._setTreeChanged(True)
logger.debug("The parent of item %s has been changed to %s" % (tHandle,pHandle))
@@ -815,7 +832,15 @@ class GuiProjectTree(QTreeWidget):
pHandle = trItemP.data(self.C_NAME, Qt.UserRole)
nwItemS.setParent(pHandle)
self.setTreeItemValues(tHandle)
self.theProject.setProjectChanged(True)
self._setTreeChanged(True)
return
def _setTreeChanged(self, theState):
"""Set the tree change flag, and propagate to the project.
"""
self.treeChanged = theState
if theState:
self.theProject.setProjectChanged(True)
return
# END Class GuiProjectTree
+43 -37
View File
@@ -495,52 +495,58 @@ class GuiIcons:
ICON_MAP = {
# Project and GUI icons
"cls_none" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_novel" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_plot" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_character" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_world" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_timeline" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_object" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_entity" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_custom" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_trash" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"proj_document" : (QStyle.SP_FileIcon, "x-office-document"),
"proj_folder" : (QStyle.SP_DirIcon, "folder"),
"proj_orphan" : (QStyle.SP_MessageBoxWarning, "dialog-warning"),
"proj_nwx" : (None, None),
"status_lang" : (None, None),
"status_time" : (None, None),
"status_stats" : (None, None),
"doc_h1" : (QStyle.SP_FileIcon, "x-office-document"),
"doc_h2" : (QStyle.SP_FileIcon, "x-office-document"),
"doc_h3" : (QStyle.SP_FileIcon, "x-office-document"),
"doc_h4" : (QStyle.SP_FileIcon, "x-office-document"),
"cls_none" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_novel" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_plot" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_character" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_world" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_timeline" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_object" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_entity" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_custom" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"cls_trash" : (QStyle.SP_DriveHDIcon, "drive-harddisk"),
"proj_document" : (QStyle.SP_FileIcon, "x-office-document"),
"proj_folder" : (QStyle.SP_DirIcon, "folder"),
"proj_orphan" : (QStyle.SP_MessageBoxWarning, "dialog-warning"),
"proj_nwx" : (None, None),
"status_lang" : (None, None),
"status_time" : (None, None),
"status_stats" : (None, None),
"doc_h1" : (QStyle.SP_FileIcon, "x-office-document"),
"doc_h2" : (QStyle.SP_FileIcon, "x-office-document"),
"doc_h3" : (QStyle.SP_FileIcon, "x-office-document"),
"doc_h4" : (QStyle.SP_FileIcon, "x-office-document"),
"search_case" : (None, None),
"search_regex" : (None, None),
"search_word" : (None, None),
"search_loop" : (None, None),
"search_project" : (None, None),
"search_cancel" : (None, None),
"search_preserve" : (None, None),
## General Button Icons
"folder-open" : (QStyle.SP_DirOpenIcon, "folder-open"),
"delete" : (QStyle.SP_DialogDiscardButton, "edit-delete"),
"add" : (None, "list-add"),
"remove" : (None, "list-remove"),
"close" : (QStyle.SP_DialogCloseButton, "window-close"),
"done" : (QStyle.SP_DialogApplyButton, None),
"search" : (None, "edit-find"),
"search-replace" : (None, "edit-find-replace"),
"clear" : (QStyle.SP_LineEditClearButton, "clear_left"),
"save" : (QStyle.SP_DialogSaveButton, "document-save"),
"edit" : (None, None),
"check" : (None, None),
"cross" : (None, None),
"hash" : (None, None),
"maximise" : (None, None),
"minimise" : (None, None),
"refresh" : (None, None),
"reference" : (None, None),
"sticky-on" : (None, None),
"sticky-off" : (None, None),
"add" : (None, "list-add"),
"remove" : (None, "list-remove"),
"search" : (None, "edit-find"),
"search-replace" : (None, "edit-find-replace"),
"edit" : (None, None),
"check" : (None, None),
"cross" : (None, None),
"hash" : (None, None),
"maximise" : (None, None),
"minimise" : (None, None),
"refresh" : (None, None),
"reference" : (None, None),
## Other Icons
"warning" : (QStyle.SP_MessageBoxWarning, "dialog-warning"),
## Switches
"sticky-on" : (None, None),
"sticky-off" : (None, None),
}
DECO_MAP = {
+42 -24
View File
@@ -42,8 +42,8 @@ from PyQt5.QtWidgets import (
from nw.gui import (
GuiBuildNovel, GuiDocEditor, GuiDocMerge, GuiDocSplit, GuiDocViewDetails,
GuiDocViewer, GuiItemDetails, GuiItemEditor, GuiMainMenu, GuiMainStatus,
GuiOutline, GuiOutlineDetails, GuiPreferences, GuiProjectLoad,
GuiProjectSettings, GuiProjectTree, GuiSearchBar, GuiSessionLogView, GuiTheme
GuiOutline, GuiOutlineDetails, GuiPreferences, GuiProjectLoad, GuiTheme,
GuiProjectSettings, GuiProjectTree, GuiSessionLogView
)
from nw.core import NWProject, NWDoc, NWIndex
from nw.constants import nwFiles, nwItemType, nwAlert
@@ -93,7 +93,6 @@ class GuiMain(QMainWindow):
self.docEditor = GuiDocEditor(self)
self.viewMeta = GuiDocViewDetails(self)
self.docViewer = GuiDocViewer(self)
self.searchBar = GuiSearchBar(self)
self.treeMeta = GuiItemDetails(self)
self.projView = GuiOutline(self)
self.projMeta = GuiOutlineDetails(self)
@@ -111,22 +110,13 @@ class GuiMain(QMainWindow):
self.treeBox.addWidget(self.treeMeta)
self.treePane.setLayout(self.treeBox)
self.editPane = QWidget()
self.docEdit = QVBoxLayout()
self.docEdit.setContentsMargins(0, 0, 0, 0)
self.docEdit.setSpacing(self.mainConf.pxInt(2))
self.docEdit.addWidget(self.searchBar)
self.docEdit.addWidget(self.docEditor)
self.editPane.setLayout(self.docEdit)
self.splitView = QSplitter(Qt.Vertical)
self.splitView.addWidget(self.docViewer)
self.splitView.addWidget(self.viewMeta)
self.splitView.setSizes(self.mainConf.getViewPanePos())
self.splitDocs = QSplitter(Qt.Horizontal)
self.splitDocs.setOpaqueResize(False)
self.splitDocs.addWidget(self.editPane)
self.splitDocs.addWidget(self.docEditor)
self.splitDocs.addWidget(self.splitView)
self.splitOutline = QSplitter(Qt.Vertical)
@@ -144,7 +134,6 @@ class GuiMain(QMainWindow):
xCM = self.mainConf.pxInt(4)
self.splitMain = QSplitter(Qt.Horizontal)
self.splitMain.setContentsMargins(xCM, xCM, xCM, xCM)
self.splitMain.setOpaqueResize(False)
self.splitMain.addWidget(self.treePane)
self.splitMain.addWidget(self.tabWidget)
self.splitMain.setSizes(self.mainConf.getMainPanePos())
@@ -153,7 +142,7 @@ class GuiMain(QMainWindow):
self.idxTree = self.splitMain.indexOf(self.treePane)
self.idxMain = self.splitMain.indexOf(self.tabWidget)
self.idxEditor = self.splitDocs.indexOf(self.editPane)
self.idxEditor = self.splitDocs.indexOf(self.docEditor)
self.idxViewer = self.splitDocs.indexOf(self.splitView)
self.idxViewDoc = self.splitView.indexOf(self.docViewer)
self.idxViewMeta = self.splitView.indexOf(self.viewMeta)
@@ -168,7 +157,7 @@ class GuiMain(QMainWindow):
self.splitView.setCollapsible(self.idxViewMeta, False)
self.splitView.setVisible(False)
self.searchBar.setVisible(False)
self.docEditor.closeSearch()
# Build the Tree View
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
@@ -487,6 +476,37 @@ class GuiMain(QMainWindow):
return False
return True
def openNextDocument(self, tHandle, wrapAround=False):
"""Opens the next document in the project tree, following the
document with the given handle. Stops when reaching the end.
"""
if self.hasProject:
self.treeView.flushTreeOrder()
nHandle = None # The next handle after tHandle
fHandle = None # The first file handle we encounter
foundIt = False # We've found tHandle, pick the next we see
for tItem in self.theProject.projTree:
if tItem is None:
continue
if tItem.itemType != nwItemType.FILE:
continue
if fHandle is None:
fHandle = tItem.itemHandle
if tItem.itemHandle == tHandle:
foundIt = True
elif foundIt:
nHandle = tItem.itemHandle
break
if nHandle is not None:
self.openDocument(nHandle, tLine=0)
return True
elif wrapAround:
self.openDocument(fHandle, tLine=0)
return False
return False
def saveDocument(self):
"""Save the current documents.
"""
@@ -601,15 +621,13 @@ class GuiMain(QMainWindow):
return True
def passDocumentAction(self, theAction):
"""Pass on document action theAction to whatever document has
the focus. If no document has focus, the action is discarded.
"""Pass on document action theAction to the document viewer if
it has focus, otherwise pass it to the document editor.
"""
if self.docEditor.hasFocus():
self.docEditor.docAction(theAction)
elif self.docViewer.hasFocus():
if self.docViewer.hasFocus():
self.docViewer.docAction(theAction)
else:
logger.debug("Document action requested, but no document has focus")
self.docEditor.docAction(theAction)
return True
##
@@ -1075,8 +1093,8 @@ class GuiMain(QMainWindow):
"""When the escape key is pressed somewhere in the main window,
do the following, in order:
"""
if self.searchBar.isVisible():
self.searchBar.setVisible(False)
if self.docEditor.docSearch.isVisible():
self.docEditor.closeSearch()
return
elif self.isZenMode:
self.toggleZenMode()
+7 -1
View File
@@ -1,5 +1,5 @@
[Main]
timestamp = 2020-06-13 22:59:36
timestamp = 2020-06-17 19:45:53
theme = default
syntax = default_light
icons = typicons_colour_light
@@ -55,6 +55,12 @@ askbeforebackup = True
showrefpanel = True
viewcomments = True
viewsynopsis = True
searchcase = False
searchword = False
searchregex = False
searchloop = False
searchnextfile = False
searchmatchcap = False
[Path]
lastpath =