@@ -3,12 +3,14 @@ codecov:
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
round: up
|
||||
range: "70...100"
|
||||
|
||||
status:
|
||||
project: yes
|
||||
patch: yes
|
||||
project:
|
||||
default:
|
||||
threshold: 1%
|
||||
patch: no
|
||||
changes: no
|
||||
|
||||
parsers:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# novelWriter ChangeLog
|
||||
|
||||
## Version 0.9rc1 [2020-xx-xx]
|
||||
|
||||
**Core Functionality**
|
||||
|
||||
* Underline text formatting has been removed. It is not standard HTML5, not Markdown, and was previously implemented using the double underscore notation that in standard Markdown is renderred as bold text. Instead, novelWriter now renders a single `*` or `_` wrapping a piece of text *within* a paragraphs as italicised text, and a double `**` or `__` as bold text. The keyboard shortcuts and automatic features **only** support the `*` notation. A triple set of `***` are treated as both bold and italicised. PR #310.
|
||||
* Strikethrough formatting has been added back into novelWriter using the standard Markdown `~~` wrapping. PR #310.
|
||||
* Added support for thin spaces and non-breaking thin spaces. PR #319.
|
||||
* The `Ctrl+Z` key sequence would not go through the wrapper function for document action for the document editor, but act directly on the document. This caused some of the logic preventing conflict between auto-replace and undo to be bypassed. This has now been resolved by blocking the keypress itself. Issue #320, PR #321.
|
||||
* The dialog window size and column width setting for the auto-replace feature in Project Settings are now preserved between closing and opening the dialog. Issue #322, PR #324.
|
||||
|
||||
**User Interface**
|
||||
|
||||
* The Open Project dialog will now ask before removing an entry from the recent projects list. PR #309.
|
||||
* The text emphasis functions, either selected from the menu or via keyboard shortcuts, will now try to respond to the command in a more meaningful way. That is, the text editor will try to toggle the bold or italics features independently of eachother on the selected text. A feature to apply both at the same time has also been added. PR #310.
|
||||
* The document editor search tool has been completely rewritten. It now appears as a search box at the top of the document, and has a number of toggle switches added to it. You can modify the search tool to be case sensitive, select only whole words, use regular expression search strings, loop when reaching the end, and continue the search in next file. For the replace feature, you can also select to have the feature try to preserve the case of the replaced word. Issues #84 and #305, PR #314.
|
||||
* A dialog has been added for selecting quotation mark style. These are now used in the Preferences dialog instead of a plain text box. PR #317.
|
||||
* Added an insert menu for inserting special symbols like dashes, ellipsis, thin and non-breaking spaces, and hard line breaks. PR #319.
|
||||
* A menu option to replace straight single and double quotes in a selected piece of text has been added. This uses the same logic as the auto-replace feature. Issue #312, PR #321.
|
||||
* When pressing `Ctrl+R` while the document editor has focus, the edited document will be viewed or refreshed in the document viewer. Previously, the selected document in the project tree had priority. The document is also now saved before loaded in the viewer, censuring that it shows the very latest changes. Issue #143, PR #323.
|
||||
* The selection in the project tree should not scroll into view when just opening the document. This can be quite annoying if loading several documents in sequence by double-clicking, as the target may move just when you're about to click. PR #325.
|
||||
|
||||
**Other Changes**
|
||||
|
||||
* Added the file's class and layout to the meta data string added as the first line of saved document files. This meta data is only used to restore the file meta information into the project if it was lost from the project file. It is also useful information when reading the file in external tools. PR #308.
|
||||
|
||||
|
||||
## Version 0.8 [2020-06-14]
|
||||
|
||||
**Bugfixes**
|
||||
|
||||
@@ -48,7 +48,7 @@ It allows for a minimal set of formatting needed for writing text documents for
|
||||
These are currently limited to:
|
||||
|
||||
* Headings level 1 to 4 using the `#` syntax only.
|
||||
* Bold, italic and underline text.
|
||||
* Bold, italic and strikethrough text.
|
||||
* Hard line breaks using two or more spaces at the end of a line.
|
||||
|
||||
That is it.
|
||||
|
||||
@@ -24,9 +24,9 @@ copyright = "2018-2020, Veronica Berglyd Olsen"
|
||||
author = "Veronica Berglyd Olsen"
|
||||
|
||||
# The short X.Y version
|
||||
version = "0.8"
|
||||
version = "0.9"
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = "0.8.0"
|
||||
release = "0.9.0rc1"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
@@ -33,8 +33,8 @@ Markdown Format
|
||||
===============
|
||||
|
||||
The document editor uses a simplified markdown format.
|
||||
That is, it supports basic formatting like bold, italics and underline, as well as four levels of headings.
|
||||
The formats are listed below.
|
||||
That is, it supports basic formatting like bold, italics and strikethrough, as well as four levels of headings.
|
||||
The preference of novelWriter is to use `*` for wrapping emphasised text, but `_` is partially supported when typed, but not by the automatic formatting features and keyboard shortcuts.
|
||||
|
||||
In addition to these standard markdown features, the editor also allows for comments, that is text that is ignored by the word counter and not exported or, optionally, hidden in the document viewer.
|
||||
If the first word of a comment is "Synopsis:" (with the colon), the comment is treated specially, and will show up in the Outline View.
|
||||
@@ -48,9 +48,12 @@ The editor also has a minimal set of keywords used for setting tags and referenc
|
||||
"``## Title``", "Heading level two. The space after the # is mandatory."
|
||||
"``### Title``", "Heading level three. The space after the # is mandatory."
|
||||
"``#### Title``", "Heading level four. The space after the # is mandatory."
|
||||
"``*text*``", "The text is rendered as italicised text."
|
||||
"``**text**``", "The text is rendered as bold text."
|
||||
"``_text_``", "The text is rendered as italicized text."
|
||||
"``__text__``", "The text is rendered as underlined text."
|
||||
"``***text***``", "The text is rendered as bold italicised text."
|
||||
"``_text_``", "Alternative format for italicised text."
|
||||
"``__text__``", "Alternative format for bold text."
|
||||
"``~~text~~``", "Strikethrough text."
|
||||
"``% text...``", "A comment. The text is not exported by default, seen in viewer, or counted towards word counts."
|
||||
"``% Synopsis: text...``", "A synopsis comment. Shows up in the Synopsis column of the Outline View, but is otherwise treated as a comment."
|
||||
"``@keyword: value``", "A keyword argument followed by a value, or a comma separated list of values."
|
||||
@@ -110,6 +113,7 @@ These are as following:
|
||||
":kbd:`Ctrl-.`", "Correct word under cursor."
|
||||
":kbd:`Ctrl-,`", "Open the Preferences dialog."
|
||||
":kbd:`Ctrl-/`", "Change block format to comment."
|
||||
":kbd:`Ctrl--`", "Strikethrough selected text, or word under cursor."
|
||||
":kbd:`Ctrl-0`", "Remove block formatting for block under cursor."
|
||||
":kbd:`Ctrl-1`", "Change block format to header level 1."
|
||||
":kbd:`Ctrl-2`", "Change block format to header level 2."
|
||||
@@ -122,7 +126,7 @@ These are as following:
|
||||
":kbd:`Ctrl-E`", "If in tree view, edit a document or folder settings. (Same as :kbd:`F2`)"
|
||||
":kbd:`Ctrl-F`", "Open the search bar and search for selected word, if any is selected."
|
||||
":kbd:`Ctrl-G`", "Find next occurrence of word in current document. (Same as :kbd:`F3`)"
|
||||
":kbd:`Ctrl-H`", "Open the search and replace bar and search for selected word, if any is selected. (On Mac, this is :kbd:`Cmd+=`)"
|
||||
":kbd:`Ctrl-H`", "Open the search and replace bar and search for selected word, if any is selected. (On Mac, this is :kbd:`Cmd-=`)"
|
||||
":kbd:`Ctrl-I`", "Format selected text, or word under cursor, as italic."
|
||||
":kbd:`Ctrl-N`", "Create new document."
|
||||
":kbd:`Ctrl-O`", "Open selected document."
|
||||
@@ -143,6 +147,7 @@ These are as following:
|
||||
":kbd:`Ctrl-Shift-/`", "Remove block formatting for block under cursor."
|
||||
":kbd:`Ctrl-Shift-1`", "Replace occurrence of word in current document, and search for next occurrence."
|
||||
":kbd:`Ctrl-Shift-A`", "Select all text in current paragraph."
|
||||
":kbd:`Ctrl-Shift-B`", "Format selected text, or word under cursor, as bold and italic."
|
||||
":kbd:`Ctrl-Shift-D`", "Wrap selected text, or word under cursor, in single quotes."
|
||||
":kbd:`Ctrl-Shift-G`", "Find previous occurrence of word in current document. (Same as :kbd:`Shift-F3`"
|
||||
":kbd:`Ctrl-Shift-I`", "Import text to the current document from a text file."
|
||||
|
||||
@@ -40,13 +40,14 @@ __package__ = "novelWriter"
|
||||
__author__ = "Veronica Berglyd Olsen"
|
||||
__copyright__ = "Copyright 2018–2020, Veronica Berglyd Olsen"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "0.8"
|
||||
__hexversion__ = "0x000800f0"
|
||||
__version__ = "0.9.0rc1"
|
||||
__hexversion__ = "0x000900c1"
|
||||
__date__ = "2020-06-14"
|
||||
__maintainer__ = "Veronica Berglyd Olsen"
|
||||
__email__ = "code@vkbo.net"
|
||||
__status__ = "Pre-Release"
|
||||
__url__ = "https://github.com/vkbo/novelWriter"
|
||||
__issuesurl__ = "https://github.com/vkbo/novelWriter/issues"
|
||||
__domain__ = "novelwriter.io"
|
||||
__docurl__ = "https://novelwriter.readthedocs.io"
|
||||
__credits__ = [
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from nw.constants.iso import isoLanguage, isoCountry
|
||||
from nw.constants.constants import (
|
||||
nwConst, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode
|
||||
nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode,
|
||||
nwInsertSymbols
|
||||
)
|
||||
from nw.constants.enum import (
|
||||
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
||||
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
|
||||
nwDocInsert
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"isoLanguage",
|
||||
"isoCountry",
|
||||
"nwConst",
|
||||
"nwRegEx",
|
||||
"nwFiles",
|
||||
"nwKeyWords",
|
||||
"nwLabels",
|
||||
"nwQuotes",
|
||||
"nwUnicode",
|
||||
"nwInsertSymbols",
|
||||
"nwAlert",
|
||||
"nwDocAction",
|
||||
"nwItemClass",
|
||||
"nwItemLayout",
|
||||
"nwItemType",
|
||||
"nwOutline",
|
||||
"nwDocInsert",
|
||||
]
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nw.constants.enum import nwItemClass, nwItemLayout, nwOutline
|
||||
from nw.constants.enum import nwItemClass, nwItemLayout, nwOutline, nwDocInsert
|
||||
|
||||
class nwConst():
|
||||
|
||||
@@ -34,6 +34,15 @@ class nwConst():
|
||||
|
||||
# END Class nwConst
|
||||
|
||||
class nwRegEx():
|
||||
|
||||
FMT_B = r"(?<![\w|\*|_|\\])([\*|_]{2})(?!\s|\*|_)(.+?)(?<![\s|\\])(\1)(?!\w)"
|
||||
FMT_I = r"(?<![\w|\*|_|\\])([\*|_])(?!\s|\*|_)(.+?)(?<![\s|\\])(\1)(?!\w)"
|
||||
FMT_BI = r"(?<![\w|\*|\\])([\*]{3})(?!\s|\*)(.+?)(?<![\s|\\])(\1)(?!\w)"
|
||||
FMT_ST = r"(?<![\w|~|\\])([~]{2})(?!\s|~)(.+?)(?<![\s|\\])(\1)(?!\w)"
|
||||
|
||||
# END Class nwRegEx
|
||||
|
||||
class nwFiles():
|
||||
|
||||
PROJ_FILE = "nwProject.nwx"
|
||||
@@ -160,32 +169,35 @@ class nwQuotes():
|
||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||
"""
|
||||
|
||||
SYMBOLS = [
|
||||
"\u0022", # Quotation mark
|
||||
"\u0027", # Apostrophe
|
||||
"\u00ab", # Left-pointing double angle quotation mark
|
||||
"\u00bb", # Right-pointing double angle quotation mark
|
||||
"\u2018", # Left single quotation mark
|
||||
"\u2019", # Right single quotation mark
|
||||
"\u201a", # Single low-9 quotation mark
|
||||
"\u201b", # Single high-reversed-9 quotation mark
|
||||
"\u201c", # Left double quotation mark
|
||||
"\u201d", # Right double quotation mark
|
||||
"\u201e", # Double low-9 quotation mark
|
||||
"\u201f", # Double high-reversed-9 quotation mark
|
||||
"\u2039", # Single left-pointing angle quotation mark
|
||||
"\u203a", # Single right-pointing angle quotation mark
|
||||
"\u2e42", # Double low-reversed-9 quotation mark
|
||||
"\u300c", # Left corner bracket
|
||||
"\u300d", # Right corner bracket
|
||||
"\u300e", # Left white corner bracket
|
||||
"\u300f", # Right white corner bracket
|
||||
]
|
||||
SYMBOLS = {
|
||||
"\u0027" : "Straight single quotation mark",
|
||||
"\u0022" : "Straight double quotation mark",
|
||||
|
||||
"\u2018" : "Left single quotation mark",
|
||||
"\u2019" : "Right single quotation mark",
|
||||
"\u201a" : "Single low-9 quotation mark",
|
||||
"\u201b" : "Single high-reversed-9 quotation mark",
|
||||
"\u201c" : "Left double quotation mark",
|
||||
"\u201d" : "Right double quotation mark",
|
||||
"\u201e" : "Double low-9 quotation mark",
|
||||
"\u201f" : "Double high-reversed-9 quotation mark",
|
||||
"\u2e42" : "Double low-reversed-9 quotation mark",
|
||||
|
||||
"\u2039" : "Single left-pointing angle quotation mark",
|
||||
"\u203a" : "Single right-pointing angle quotation mark",
|
||||
"\u00ab" : "Left-pointing double angle quotation mark",
|
||||
"\u00bb" : "Right-pointing double angle quotation mark",
|
||||
|
||||
"\u300c" : "Left corner bracket",
|
||||
"\u300d" : "Right corner bracket",
|
||||
"\u300e" : "Left white corner bracket",
|
||||
"\u300f" : "Right white corner bracket",
|
||||
}
|
||||
|
||||
# END Class nwQuotes
|
||||
|
||||
class nwUnicode:
|
||||
"""Suppoted unicode character constants and translation maps for HTML.
|
||||
"""Supported unicode character constants and translation maps for HTML.
|
||||
"""
|
||||
|
||||
# Unicode Constants
|
||||
@@ -217,8 +229,14 @@ class nwUnicode:
|
||||
U_EMDASH = "\u2014" # Long dash
|
||||
U_HELLIP = "\u2026" # Ellipsis
|
||||
|
||||
## Other
|
||||
## Spaces and Lines
|
||||
U_NBSP = "\u00a0" # Non-breaking space
|
||||
U_THNSP = "\u2009" # Thin space
|
||||
U_THNBSP = "\u202f" # Thin non-breaking space
|
||||
U_LSEP = "\u2028" # Line separator
|
||||
U_PSEP = "\u2029" # Paragraph separator
|
||||
|
||||
## Symbols
|
||||
U_CHECK = "\u2714" # Heavy check mark
|
||||
U_MULT = "\u2715" # Multiplication x
|
||||
|
||||
@@ -261,8 +279,12 @@ class nwUnicode:
|
||||
H_EMDASH = "—"
|
||||
H_HELLIP = "…"
|
||||
|
||||
## Other
|
||||
## Spaces
|
||||
H_NBSP = " "
|
||||
H_THNSP = " "
|
||||
H_THNBSP = " "
|
||||
|
||||
## Symbols
|
||||
H_CHECK = "✔"
|
||||
H_MULT = "✕"
|
||||
|
||||
@@ -277,3 +299,18 @@ class nwUnicode:
|
||||
H_LTRIS = "◂"
|
||||
|
||||
# END Class nwUnicode
|
||||
|
||||
class nwInsertSymbols():
|
||||
|
||||
SYMBOLS = {
|
||||
nwDocInsert.NO_INSERT : "",
|
||||
nwDocInsert.HARD_BREAK : " \n",
|
||||
nwDocInsert.NB_SPACE : nwUnicode.U_NBSP,
|
||||
nwDocInsert.THIN_SPACE : nwUnicode.U_THNSP,
|
||||
nwDocInsert.THIN_NB_SPACE : nwUnicode.U_THNBSP,
|
||||
nwDocInsert.SHORT_DASH : nwUnicode.U_ENDASH,
|
||||
nwDocInsert.LONG_DASH : nwUnicode.U_EMDASH,
|
||||
nwDocInsert.ELLIPSIS : nwUnicode.U_HELLIP,
|
||||
}
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
@@ -68,33 +68,49 @@ class nwItemLayout(Enum):
|
||||
|
||||
class nwDocAction(Enum):
|
||||
|
||||
NO_ACTION = 0
|
||||
UNDO = 1
|
||||
REDO = 2
|
||||
CUT = 3
|
||||
COPY = 4
|
||||
PASTE = 5
|
||||
BOLD = 6
|
||||
ITALIC = 7
|
||||
U_LINE = 8
|
||||
S_QUOTE = 9
|
||||
D_QUOTE = 10
|
||||
SEL_ALL = 11
|
||||
SEL_PARA = 12
|
||||
FIND = 13
|
||||
REPLACE = 14
|
||||
GO_NEXT = 15
|
||||
GO_PREV = 16
|
||||
REPL_NEXT = 17
|
||||
BLOCK_H1 = 18
|
||||
BLOCK_H2 = 19
|
||||
BLOCK_H3 = 20
|
||||
BLOCK_H4 = 21
|
||||
BLOCK_COM = 22
|
||||
BLOCK_TXT = 23
|
||||
NO_ACTION = 0
|
||||
UNDO = 1
|
||||
REDO = 2
|
||||
CUT = 3
|
||||
COPY = 4
|
||||
PASTE = 5
|
||||
ITALIC = 6
|
||||
BOLD = 7
|
||||
BOLDITALIC = 8
|
||||
STRIKE = 9
|
||||
S_QUOTE = 10
|
||||
D_QUOTE = 11
|
||||
SEL_ALL = 12
|
||||
SEL_PARA = 13
|
||||
FIND = 14
|
||||
REPLACE = 15
|
||||
GO_NEXT = 16
|
||||
GO_PREV = 17
|
||||
REPL_NEXT = 18
|
||||
BLOCK_H1 = 19
|
||||
BLOCK_H2 = 20
|
||||
BLOCK_H3 = 21
|
||||
BLOCK_H4 = 22
|
||||
BLOCK_COM = 23
|
||||
BLOCK_TXT = 24
|
||||
REPL_SNG = 25
|
||||
REPL_DBL = 26
|
||||
|
||||
# END Enum nwDocAction
|
||||
|
||||
class nwDocInsert(Enum):
|
||||
|
||||
NO_INSERT = 0
|
||||
HARD_BREAK = 1
|
||||
NB_SPACE = 2
|
||||
THIN_SPACE = 3
|
||||
THIN_NB_SPACE = 4
|
||||
SHORT_DASH = 5
|
||||
LONG_DASH = 6
|
||||
ELLIPSIS = 7
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
class nwAlert(Enum):
|
||||
|
||||
INFO = 0
|
||||
|
||||
@@ -30,8 +30,10 @@ import nw
|
||||
|
||||
from os import path, mkdir, rename, unlink
|
||||
|
||||
from nw.core.item import NWItem
|
||||
from nw.constants import nwAlert
|
||||
from nw.common import isHandle
|
||||
from nw.constants import nwItemLayout, nwItemClass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -139,8 +141,18 @@ class NWDoc():
|
||||
docPath = path.join(self.theProject.projContent, docFile)
|
||||
docTemp = path.join(self.theProject.projContent, docFile+"~")
|
||||
|
||||
itemPath = self.theProject.projTree.getItemPath(self.docHandle)
|
||||
docMeta = "%%~ "+":".join(itemPath)+":"+self.theItem.itemName+"\n"
|
||||
if isinstance(self.theItem, NWItem):
|
||||
itemPath = self.theProject.projTree.getItemPath(self.docHandle)
|
||||
docMeta = (
|
||||
"%%~ {handlepath:s}:{itemclass:s}:{itemlayout:s}:{itemname:s}\n"
|
||||
).format(
|
||||
handlepath = ":".join(itemPath),
|
||||
itemclass = self.theItem.itemClass.name,
|
||||
itemlayout = self.theItem.itemLayout.name,
|
||||
itemname = self.theItem.itemName,
|
||||
)
|
||||
else:
|
||||
docMeta = ""
|
||||
|
||||
try:
|
||||
with open(docTemp, mode="w", encoding="utf8") as outFile:
|
||||
@@ -194,7 +206,7 @@ class NWDoc():
|
||||
"""
|
||||
if len(self.docMeta) < 14:
|
||||
# Not enough information
|
||||
return "", []
|
||||
return "", [], None, None
|
||||
|
||||
theMeta = self.docMeta
|
||||
|
||||
@@ -213,6 +225,18 @@ class NWDoc():
|
||||
else:
|
||||
break
|
||||
|
||||
return theMeta, thePath
|
||||
theClass = nwItemClass.NO_CLASS
|
||||
for aClass in nwItemClass:
|
||||
if theMeta.startswith(aClass.name):
|
||||
theClass = aClass
|
||||
theMeta = theMeta.lstrip(aClass.name+":")
|
||||
|
||||
theLayout = nwItemLayout.NO_LAYOUT
|
||||
for aLayout in nwItemLayout:
|
||||
if theMeta.startswith(aLayout.name):
|
||||
theLayout = aLayout
|
||||
theMeta = theMeta.lstrip(aLayout.name+":")
|
||||
|
||||
return theMeta, thePath, theClass, theLayout
|
||||
|
||||
# END Class NWDoc
|
||||
|
||||
@@ -41,7 +41,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class NWIndex():
|
||||
|
||||
VALID_KEYS = set([
|
||||
VALID_KEYS = {
|
||||
nwKeyWords.TAG_KEY,
|
||||
nwKeyWords.PLOT_KEY,
|
||||
nwKeyWords.POV_KEY,
|
||||
@@ -51,7 +51,7 @@ class NWIndex():
|
||||
nwKeyWords.OBJECT_KEY,
|
||||
nwKeyWords.ENTITY_KEY,
|
||||
nwKeyWords.CUSTOM_KEY
|
||||
])
|
||||
}
|
||||
TAG_CLASS = {
|
||||
nwKeyWords.CHAR_KEY : nwItemClass.CHARACTER,
|
||||
nwKeyWords.POV_KEY : nwItemClass.CHARACTER,
|
||||
@@ -305,11 +305,12 @@ class NWIndex():
|
||||
|
||||
elif aLine.startswith(r"%"):
|
||||
if nTitle > 0:
|
||||
toCheck = aLine[1:].lstrip().lower()
|
||||
toCheck = aLine[1:].lstrip()
|
||||
synTag = toCheck[:9].lower()
|
||||
tLen = len(aLine)
|
||||
cLen = len(toCheck)
|
||||
cOff = tLen - cLen
|
||||
if toCheck.startswith("synopsis:"):
|
||||
if synTag == "synopsis:":
|
||||
self._indexSynopsis(tHandle, isNovel, aLine[cOff+9:].strip(), nTitle)
|
||||
|
||||
# Count words for remaining text after last heading
|
||||
|
||||
@@ -45,7 +45,7 @@ class OptionState():
|
||||
|
||||
self.theState = {}
|
||||
self.validMap = {
|
||||
"GuiSession": set([
|
||||
"GuiSession": {
|
||||
"widthCol0",
|
||||
"widthCol1",
|
||||
"widthCol2",
|
||||
@@ -53,11 +53,11 @@ class OptionState():
|
||||
"sortOrder",
|
||||
"hideZeros",
|
||||
"hideNegative",
|
||||
]),
|
||||
"GuiDocSplit": set([
|
||||
},
|
||||
"GuiDocSplit": {
|
||||
"spLevel",
|
||||
]),
|
||||
"GuiBuildNovel": set([
|
||||
},
|
||||
"GuiBuildNovel": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"addNovel",
|
||||
@@ -72,12 +72,17 @@ class OptionState():
|
||||
"incComments",
|
||||
"incKeywords",
|
||||
"incBodyText",
|
||||
]),
|
||||
"GuiOutline": set([
|
||||
},
|
||||
"GuiOutline": {
|
||||
"headerOrder",
|
||||
"columnWidth",
|
||||
"columnHidden",
|
||||
]),
|
||||
},
|
||||
"GuiProjectSettings": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"replaceColW",
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -1075,17 +1075,22 @@ class NWProject():
|
||||
# Look for meta data
|
||||
oName = ""
|
||||
if aDoc.openDocument(oHandle, showStatus=False, isOrphan=True):
|
||||
oName, oPath = aDoc.getMeta()
|
||||
oName, oPath, oClass, oLayout = aDoc.getMeta()
|
||||
|
||||
if oName == "":
|
||||
nOrph += 1
|
||||
oName = "Orphaned File %d" % nOrph
|
||||
|
||||
if oClass is None:
|
||||
oClass = nwItemClass.NO_CLASS
|
||||
if oLayout is None:
|
||||
oLayout = nwItemLayout.NO_LAYOUT
|
||||
|
||||
orphItem = NWItem(self)
|
||||
orphItem.setName(oName)
|
||||
orphItem.setType(nwItemType.FILE)
|
||||
orphItem.setClass(nwItemClass.NO_CLASS)
|
||||
orphItem.setLayout(nwItemLayout.NO_LAYOUT)
|
||||
orphItem.setClass(oClass)
|
||||
orphItem.setLayout(oLayout)
|
||||
self.projTree.append(oHandle, None, orphItem)
|
||||
|
||||
return
|
||||
|
||||
@@ -54,6 +54,8 @@ class ToHtml(Tokenizer):
|
||||
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
|
||||
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
|
||||
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
|
||||
nwUnicode.U_THNSP : nwUnicode.H_THNSP,
|
||||
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
|
||||
}
|
||||
self.revDict = {}
|
||||
self.reReplace = []
|
||||
@@ -117,14 +119,28 @@ class ToHtml(Tokenizer):
|
||||
"""Convert the list of text tokens into a HTML document saved
|
||||
to theResult.
|
||||
"""
|
||||
htmlTags = {
|
||||
self.FMT_B_B : "<strong>",
|
||||
self.FMT_B_E : "</strong>",
|
||||
self.FMT_I_B : "<em>",
|
||||
self.FMT_I_E : "</em>",
|
||||
self.FMT_U_B : "<u>",
|
||||
self.FMT_U_E : "</u>",
|
||||
}
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
htmlTags = { # HTML4 + CSS2
|
||||
self.FMT_B_B : "<b>",
|
||||
self.FMT_B_E : "</b>",
|
||||
self.FMT_I_B : "<i>",
|
||||
self.FMT_I_E : "</i>",
|
||||
self.FMT_S_B : "<b><i>",
|
||||
self.FMT_S_E : "</i></b>",
|
||||
self.FMT_D_B : "<span style='text-decoration: line-through;'>",
|
||||
self.FMT_D_E : "</span>",
|
||||
}
|
||||
else:
|
||||
htmlTags = { # HTML5
|
||||
self.FMT_B_B : "<strong>",
|
||||
self.FMT_B_E : "</strong>",
|
||||
self.FMT_I_B : "<em>",
|
||||
self.FMT_I_E : "</em>",
|
||||
self.FMT_S_B : "<strong><em>",
|
||||
self.FMT_S_E : "</em></strong>",
|
||||
self.FMT_D_B : "<del>",
|
||||
self.FMT_D_E : "</del>",
|
||||
}
|
||||
|
||||
if self.isNovel and self.genMode != self.M_PREVIEW:
|
||||
# For novel files for export, we bump the titles one level
|
||||
|
||||
@@ -34,7 +34,7 @@ from PyQt5.QtCore import QRegularExpression
|
||||
|
||||
from nw.core.document import NWDoc
|
||||
from nw.core.tools import numberToWord
|
||||
from nw.constants import nwItemLayout, nwItemType
|
||||
from nw.constants import nwItemLayout, nwItemType, nwRegEx
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -44,8 +44,10 @@ class Tokenizer():
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_U_B = 5 # Begin underline
|
||||
FMT_U_E = 6 # End underline
|
||||
FMT_S_B = 5 # Begin bold italic
|
||||
FMT_S_E = 6 # End bold italic
|
||||
FMT_D_B = 7 # Begin strikeout
|
||||
FMT_D_E = 8 # End strikeout
|
||||
|
||||
T_EMPTY = 1 # Empty line (new paragraph)
|
||||
T_SYNOPSIS = 2 # Synopsis comment
|
||||
@@ -292,23 +294,19 @@ class Tokenizer():
|
||||
The format of the token list is an entry with a four-tuple for
|
||||
each line in the file. The tuple is as follows:
|
||||
1: The type of the block, self.T_*
|
||||
2: The text content of the block, without leading tags
|
||||
3: The internal formatting map of the text, self.FMT_*
|
||||
4: The style of the block, self.A_*
|
||||
2: The line in file where this block occurred
|
||||
3: The text content of the block, without leading tags
|
||||
4: The internal formatting map of the text, self.FMT_*
|
||||
5: The style of the block, self.A_*
|
||||
"""
|
||||
|
||||
# RegExes for adding formatting tags within text lines
|
||||
# Keep in sync with the DocHighlighter class
|
||||
rxFormats = [(
|
||||
QRegularExpression(r"(?<![\w|\\])([\*]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)"),
|
||||
[None, self.FMT_B_B, None, self.FMT_B_E]
|
||||
),(
|
||||
QRegularExpression(r"(?<![\w|_|\\])([_])(?!\s|\1)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)"),
|
||||
[None, self.FMT_I_B, None, self.FMT_I_E]
|
||||
),(
|
||||
QRegularExpression(r"(?<![\w|\\])([_]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)"),
|
||||
[None, self.FMT_U_B, None, self.FMT_U_E]
|
||||
)]
|
||||
rxFormats = [
|
||||
(QRegularExpression(nwRegEx.FMT_I), [None, self.FMT_I_B, None, self.FMT_I_E]),
|
||||
(QRegularExpression(nwRegEx.FMT_B), [None, self.FMT_B_B, None, self.FMT_B_E]),
|
||||
(QRegularExpression(nwRegEx.FMT_BI), [None, self.FMT_S_B, None, self.FMT_S_E]),
|
||||
(QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]),
|
||||
]
|
||||
|
||||
self.theTokens = []
|
||||
self.theMarkdown = ""
|
||||
@@ -329,8 +327,9 @@ class Tokenizer():
|
||||
tmpMarkdown.append("\n")
|
||||
|
||||
elif aLine[0] == "%":
|
||||
cLine = aLine[1:].strip()
|
||||
if cLine.lower().startswith("synopsis:"):
|
||||
cLine = aLine[1:].lstrip()
|
||||
synTag = cLine[:9].lower()
|
||||
if synTag == "synopsis:":
|
||||
self.theTokens.append((
|
||||
self.T_SYNOPSIS,
|
||||
nLine,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
@@ -871,8 +871,8 @@ class GuiBuildNovel(QDialog):
|
||||
"section" : self.fmtSection.text().strip(),
|
||||
})
|
||||
|
||||
winWidth = self.mainConf.pxInt(self.width())
|
||||
winHeight = self.mainConf.pxInt(self.height())
|
||||
winWidth = self.mainConf.rpxInt(self.width())
|
||||
winHeight = self.mainConf.rpxInt(self.height())
|
||||
justifyText = self.justifyText.isChecked()
|
||||
noStyling = self.noStyling.isChecked()
|
||||
textFont = self.textFont.text()
|
||||
@@ -982,7 +982,9 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
"""
|
||||
if isinstance(theText, list):
|
||||
theText = "".join(theText)
|
||||
theText = theText.replace(" "," "*4)
|
||||
theText = theText.replace(" ", " "*4)
|
||||
theText = theText.replace("<del>", "<span style='text-decoration: line-through;'>")
|
||||
theText = theText.replace("</del>", "</span>")
|
||||
self.setHtml(theText)
|
||||
return
|
||||
|
||||
|
||||
@@ -30,17 +30,17 @@
|
||||
import logging
|
||||
import nw
|
||||
|
||||
from PyQt5.QtGui import QColor, QPalette, QPainter
|
||||
from PyQt5.QtGui import QColor, QPalette, QPainter, QFontMetrics
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QRect, QPoint, QSize, QRectF, QPropertyAnimation, pyqtProperty
|
||||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
QGridLayout, QLabel, QWidget, QVBoxLayout, QHBoxLayout, QSizePolicy,
|
||||
QAbstractButton, QDialog, QTabWidget, QTabBar, QStyle,
|
||||
QStylePainter, QStyleOptionTab
|
||||
QAbstractButton, QDialog, QTabWidget, QTabBar, QStyle, QDialogButtonBox,
|
||||
QStylePainter, QStyleOptionTab, QListWidget, QListWidgetItem, QFrame
|
||||
)
|
||||
|
||||
from nw.constants import nwUnicode
|
||||
from nw.constants import nwUnicode, nwQuotes
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -436,3 +436,99 @@ class VerticalTabBar(QTabBar):
|
||||
return
|
||||
|
||||
# END Class VerticalTabBar
|
||||
|
||||
# =============================================================================================== #
|
||||
# Quotes Dialog
|
||||
# =============================================================================================== #
|
||||
|
||||
class QuotesDialog(QDialog):
|
||||
|
||||
def __init__(self, theParent=None, currentQuote="\""):
|
||||
QDialog.__init__(self, parent=theParent)
|
||||
|
||||
self.mainConf = nw.CONFIG
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.labelBox = QVBoxLayout()
|
||||
|
||||
self.selectedQuote = currentQuote
|
||||
|
||||
qMetrics = QFontMetrics(self.font())
|
||||
pxW = 7*qMetrics.boundingRectChar("M").width()
|
||||
pxH = 7*qMetrics.boundingRectChar("M").height()
|
||||
pxH = 7*qMetrics.boundingRectChar("M").height()
|
||||
|
||||
lblFont = self.font()
|
||||
lblFont.setPointSizeF(4*lblFont.pointSizeF())
|
||||
|
||||
# Preview Label
|
||||
self.previewLabel = QLabel(currentQuote)
|
||||
self.previewLabel.setFont(lblFont)
|
||||
self.previewLabel.setFixedSize(QSize(pxW, pxH))
|
||||
self.previewLabel.setAlignment(Qt.AlignCenter)
|
||||
self.previewLabel.setFrameStyle(QFrame.Box | QFrame.Plain)
|
||||
|
||||
# Quote Symbols
|
||||
self.listBox = QListWidget()
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedSymbol)
|
||||
|
||||
minSize = 100
|
||||
for sKey, sLabel in nwQuotes.SYMBOLS.items():
|
||||
theText = "[ %s ] %s" % (sKey, sLabel)
|
||||
minSize = max(minSize, qMetrics.boundingRect(theText).width())
|
||||
qtItem = QListWidgetItem(theText)
|
||||
qtItem.setData(Qt.UserRole, sKey)
|
||||
self.listBox.addItem(qtItem)
|
||||
if sKey == currentQuote:
|
||||
self.listBox.setCurrentItem(qtItem)
|
||||
|
||||
self.listBox.setMinimumWidth(minSize + self.mainConf.pxInt(40))
|
||||
self.listBox.setMinimumHeight(self.mainConf.pxInt(150))
|
||||
|
||||
# Buttons
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
self.buttonBox.accepted.connect(self._doAccept)
|
||||
self.buttonBox.rejected.connect(self._doReject)
|
||||
|
||||
# Assemble
|
||||
self.labelBox.addWidget(self.previewLabel, 0, Qt.AlignTop)
|
||||
self.labelBox.addStretch(1)
|
||||
|
||||
self.innerBox.addLayout(self.labelBox)
|
||||
self.innerBox.addWidget(self.listBox)
|
||||
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
self.outerBox.addWidget(self.buttonBox)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
##
|
||||
|
||||
def _selectedSymbol(self):
|
||||
"""Update the preview label and the selected quote style.
|
||||
"""
|
||||
selItems = self.listBox.selectedItems()
|
||||
if selItems:
|
||||
theSymbol = selItems[0].data(Qt.UserRole)
|
||||
self.previewLabel.setText(theSymbol)
|
||||
self.selectedQuote = theSymbol
|
||||
return
|
||||
|
||||
def _doAccept(self):
|
||||
"""Ok button clicked.
|
||||
"""
|
||||
self.accept()
|
||||
return
|
||||
|
||||
def _doReject(self):
|
||||
"""Cancel button clicked.
|
||||
"""
|
||||
self.reject()
|
||||
return
|
||||
|
||||
# END Class QuotesDialog
|
||||
|
||||
@@ -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
|
||||
@@ -33,7 +33,7 @@ from PyQt5.QtGui import (
|
||||
QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QBrush
|
||||
)
|
||||
|
||||
from nw.constants import nwUnicode
|
||||
from nw.constants import nwUnicode, nwRegEx
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -78,7 +78,6 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
"""Initialise the syntax highlighter, setting all the colour
|
||||
rules and building the regexes.
|
||||
"""
|
||||
|
||||
logger.debug("Setting up highlighting rules")
|
||||
|
||||
self.colHead = QColor(*self.theTheme.colHead)
|
||||
@@ -98,28 +97,28 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
self.colTrail.setAlpha(64)
|
||||
|
||||
self.hStyles = {
|
||||
"header1" : self._makeFormat(self.colHead, "bold",1.8),
|
||||
"header2" : self._makeFormat(self.colHead, "bold",1.6),
|
||||
"header3" : self._makeFormat(self.colHead, "bold",1.4),
|
||||
"header4" : self._makeFormat(self.colHead, "bold",1.2),
|
||||
"header1h" : self._makeFormat(self.colHeadH,"bold",1.8),
|
||||
"header2h" : self._makeFormat(self.colHeadH,"bold",1.6),
|
||||
"header3h" : self._makeFormat(self.colHeadH,"bold",1.4),
|
||||
"header4h" : self._makeFormat(self.colHeadH,"bold",1.2),
|
||||
"bold" : self._makeFormat(self.colEmph, "bold"),
|
||||
"italic" : self._makeFormat(self.colEmph, "italic"),
|
||||
"strike" : self._makeFormat(self.colEmph, "strike"),
|
||||
"underline" : self._makeFormat(self.colEmph, "underline"),
|
||||
"trailing" : self._makeFormat(self.colTrail,"background"),
|
||||
"nobreak" : self._makeFormat(self.colTrail,"background"),
|
||||
"dialogue1" : self._makeFormat(self.colDialN),
|
||||
"dialogue2" : self._makeFormat(self.colDialD),
|
||||
"dialogue3" : self._makeFormat(self.colDialS),
|
||||
"replace" : self._makeFormat(self.colRepTag),
|
||||
"hidden" : self._makeFormat(self.colComm),
|
||||
"keyword" : self._makeFormat(self.colKey),
|
||||
"modifier" : self._makeFormat(self.colMod),
|
||||
"value" : self._makeFormat(self.colVal),
|
||||
"header1" : self._makeFormat(self.colHead, "bold", 1.8),
|
||||
"header2" : self._makeFormat(self.colHead, "bold", 1.6),
|
||||
"header3" : self._makeFormat(self.colHead, "bold", 1.4),
|
||||
"header4" : self._makeFormat(self.colHead, "bold", 1.2),
|
||||
"header1h" : self._makeFormat(self.colHeadH, "bold", 1.8),
|
||||
"header2h" : self._makeFormat(self.colHeadH, "bold", 1.6),
|
||||
"header3h" : self._makeFormat(self.colHeadH, "bold", 1.4),
|
||||
"header4h" : self._makeFormat(self.colHeadH, "bold", 1.2),
|
||||
"bold" : self._makeFormat(self.colEmph, "bold"),
|
||||
"italic" : self._makeFormat(self.colEmph, "italic"),
|
||||
"bolditalic" : self._makeFormat(self.colEmph, ("bold","italic")),
|
||||
"strike" : self._makeFormat(self.colEmph, "strike"),
|
||||
"trailing" : self._makeFormat(self.colTrail, "background"),
|
||||
"nobreak" : self._makeFormat(self.colTrail, "background"),
|
||||
"dialogue1" : self._makeFormat(self.colDialN),
|
||||
"dialogue2" : self._makeFormat(self.colDialD),
|
||||
"dialogue3" : self._makeFormat(self.colDialS),
|
||||
"replace" : self._makeFormat(self.colRepTag),
|
||||
"hidden" : self._makeFormat(self.colComm),
|
||||
"keyword" : self._makeFormat(self.colKey),
|
||||
"modifier" : self._makeFormat(self.colMod),
|
||||
"value" : self._makeFormat(self.colVal, "underline"),
|
||||
}
|
||||
|
||||
self.hRules = []
|
||||
@@ -131,32 +130,39 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
}
|
||||
))
|
||||
|
||||
# Non-breaking Space
|
||||
# Non-Breaking Spaces
|
||||
self.hRules.append((
|
||||
"[%s]+" % nwUnicode.U_NBSP, {
|
||||
"[%s%s]+" % (nwUnicode.U_NBSP, nwUnicode.U_THNBSP), {
|
||||
0 : self.hStyles["nobreak"],
|
||||
}
|
||||
))
|
||||
|
||||
# Markdown
|
||||
self.hRules.append((
|
||||
r"(?<![\w|\\])([\*]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
|
||||
1 : self.hStyles["hidden"],
|
||||
2 : self.hStyles["bold"],
|
||||
3 : self.hStyles["hidden"],
|
||||
}
|
||||
))
|
||||
self.hRules.append((
|
||||
r"(?<![\w|_|\\])([_])(?!\s|\1)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
|
||||
nwRegEx.FMT_I, {
|
||||
1 : self.hStyles["hidden"],
|
||||
2 : self.hStyles["italic"],
|
||||
3 : self.hStyles["hidden"],
|
||||
}
|
||||
))
|
||||
self.hRules.append((
|
||||
r"(?<![\w|\\])([_]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
|
||||
nwRegEx.FMT_B, {
|
||||
1 : self.hStyles["hidden"],
|
||||
2 : self.hStyles["underline"],
|
||||
2 : self.hStyles["bold"],
|
||||
3 : self.hStyles["hidden"],
|
||||
}
|
||||
))
|
||||
self.hRules.append((
|
||||
nwRegEx.FMT_BI, {
|
||||
1 : self.hStyles["hidden"],
|
||||
2 : self.hStyles["bolditalic"],
|
||||
3 : self.hStyles["hidden"],
|
||||
}
|
||||
))
|
||||
self.hRules.append((
|
||||
nwRegEx.FMT_ST, {
|
||||
1 : self.hStyles["hidden"],
|
||||
2 : self.hStyles["strike"],
|
||||
3 : self.hStyles["hidden"],
|
||||
}
|
||||
))
|
||||
@@ -196,9 +202,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
# Build a QRegExp for spell checker
|
||||
# Include additional characters that the highlighter should
|
||||
# consider to be word separators
|
||||
wordSep = "_+"
|
||||
wordSep = r"_\+"
|
||||
wordSep += nwUnicode.U_ENDASH
|
||||
wordSep += nwUnicode.U_EMDASH
|
||||
self.spellRx = QRegularExpression("\\b[^\\s%s]+\\b" % wordSep)
|
||||
self.spellRx = QRegularExpression(r"\b[^\s"+wordSep+r"]+\b")
|
||||
self.spellRx.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
||||
|
||||
return True
|
||||
@@ -233,7 +240,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
def highlightBlock(self, theText):
|
||||
"""Highlight a single block. Prefer to check first character for
|
||||
all formats that are defined by their initial characters. This
|
||||
is significantly faster than running the regex checks we use for
|
||||
is significantly faster than running the regex checks used for
|
||||
text paragraphs.
|
||||
"""
|
||||
if self.theHandle is None or not theText:
|
||||
@@ -244,9 +251,9 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
isValid, theBits, thePos = self.theIndex.scanThis(theText)
|
||||
isGood = self.theIndex.checkThese(theBits, tItem)
|
||||
if isValid:
|
||||
for n in range(len(theBits)):
|
||||
for n, theBit in enumerate(theBits):
|
||||
xPos = thePos[n]
|
||||
xLen = len(theBits[n])
|
||||
xLen = len(theBit)
|
||||
if isGood[n]:
|
||||
if n == 0:
|
||||
self.setFormat(xPos, xLen, self.hStyles["keyword"])
|
||||
@@ -279,11 +286,12 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
self.setFormat(4, len(theText), self.hStyles["header4"])
|
||||
|
||||
elif theText.startswith("%"): # Comments
|
||||
toCheck = theText[1:].lstrip().lower()
|
||||
toCheck = theText[1:].lstrip()
|
||||
synTag = toCheck[:9].lower()
|
||||
tLen = len(theText)
|
||||
cLen = len(toCheck)
|
||||
cOff = tLen - cLen
|
||||
if toCheck.startswith("synopsis:"):
|
||||
if synTag == "synopsis:":
|
||||
self.setFormat(0, cOff+9, self.hStyles["modifier"])
|
||||
self.setFormat(cOff+9, tLen, self.hStyles["hidden"])
|
||||
else:
|
||||
@@ -341,7 +349,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
if "underline" in fmtStyle:
|
||||
theFormat.setFontUnderline(True)
|
||||
if "background" in fmtStyle:
|
||||
theFormat.setBackground(QBrush(fmtCol,Qt.SolidPattern))
|
||||
theFormat.setBackground(QBrush(fmtCol, Qt.SolidPattern))
|
||||
|
||||
if fmtSize is not None:
|
||||
theFormat.setFontPointSize(round(fmtSize*self.mainConf.textSize))
|
||||
|
||||
@@ -60,6 +60,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
self.qDocument = self.document()
|
||||
self.setMinimumWidth(self.mainConf.pxInt(300))
|
||||
self.setAutoFillBackground(True)
|
||||
self.setOpenExternalLinks(False)
|
||||
self.initViewer()
|
||||
|
||||
@@ -104,11 +105,12 @@ class GuiDocViewer(QTextBrowser):
|
||||
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)
|
||||
|
||||
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
|
||||
self.qDocument.setDocumentMargin(0)
|
||||
theOpt = QTextOption()
|
||||
if self.mainConf.doJustify:
|
||||
theOpt.setAlignment(Qt.AlignJustify)
|
||||
@@ -236,24 +238,16 @@ 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
|
||||
|
||||
self.docHeader.setGeometry(tB, tB, tW, tH)
|
||||
self.docFooter.setGeometry(tB, fY, tW, fH)
|
||||
self.setViewportMargins(0, tH, 0, fH)
|
||||
|
||||
docFormat = self.qDocument.rootFrame().frameFormat()
|
||||
docFormat.setTopMargin(max(0, tT))
|
||||
docFormat.setBottomMargin(max(0, bT))
|
||||
|
||||
self.qDocument.blockSignals(True)
|
||||
self.qDocument.rootFrame().setFrameFormat(docFormat)
|
||||
self.qDocument.blockSignals(False)
|
||||
self.setViewportMargins(cM, max(cM, tH), cM, max(cM, fH))
|
||||
|
||||
return
|
||||
|
||||
@@ -535,6 +529,8 @@ class GuiDocViewHeader(QWidget):
|
||||
def _refreshDocument(self):
|
||||
"""Reload the content of the document.
|
||||
"""
|
||||
if self.docViewer.theHandle == self.theParent.docEditor.theHandle:
|
||||
self.theParent.saveDocument()
|
||||
self.docViewer.reloadText()
|
||||
return
|
||||
|
||||
@@ -546,7 +542,7 @@ class GuiDocViewHeader(QWidget):
|
||||
"""Capture a click on the title and ensure that the item is
|
||||
selected in the project tree.
|
||||
"""
|
||||
self.theParent.treeView.setSelectedHandle(self.theHandle)
|
||||
self.theParent.treeView.setSelectedHandle(self.theHandle, doScroll=True)
|
||||
return
|
||||
|
||||
# END Class GuiDocViewHeader
|
||||
@@ -558,16 +554,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 +665,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
|
||||
|
||||
@@ -56,8 +56,8 @@ class GuiItemDetails(QWidget):
|
||||
self.setLayout(self.mainBox)
|
||||
|
||||
self.pS = 0.9*self.theTheme.fontPointSize
|
||||
self.iPx = self.theTheme.textIconSize
|
||||
self.sPx = int(round(0.8*self.theTheme.textIconSize))
|
||||
self.iPx = self.theTheme.baseIconSize
|
||||
self.sPx = int(round(0.8*self.theTheme.baseIconSize))
|
||||
|
||||
self.expCheck = self.theTheme.getPixmap("check", (self.iPx, self.iPx))
|
||||
self.expCross = self.theTheme.getPixmap("cross", (self.iPx, self.iPx))
|
||||
|
||||
@@ -33,7 +33,7 @@ from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox
|
||||
|
||||
from nw.gui.about import GuiAbout
|
||||
from nw.constants import nwItemType, nwItemClass, nwDocAction
|
||||
from nw.constants import nwItemType, nwItemClass, nwDocAction, nwDocInsert
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -50,8 +50,9 @@ class GuiMainMenu(QMenuBar):
|
||||
self._buildProjectMenu()
|
||||
self._buildDocumentMenu()
|
||||
self._buildEditMenu()
|
||||
self._buildViewMenu()
|
||||
self._buildInsertMenu()
|
||||
self._buildFormatMenu()
|
||||
self._buildViewMenu()
|
||||
self._buildToolsMenu()
|
||||
self._buildHelpMenu()
|
||||
|
||||
@@ -59,6 +60,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self._docAction = self.theParent.passDocumentAction
|
||||
self._moveTreeItem = self.theParent.treeView.moveTreeItem
|
||||
self._newTreeItem = self.theParent.treeView.newTreeItem
|
||||
self._docInsert = self.theParent.docEditor.insertText
|
||||
|
||||
logger.debug("GuiMainMenu initialisation complete")
|
||||
|
||||
@@ -97,6 +99,8 @@ class GuiMainMenu(QMenuBar):
|
||||
##
|
||||
|
||||
def _menuExit(self):
|
||||
"""Exit novelWriter.
|
||||
"""
|
||||
self.theParent.closeMain()
|
||||
return
|
||||
|
||||
@@ -123,19 +127,33 @@ class GuiMainMenu(QMenuBar):
|
||||
return True
|
||||
|
||||
def _showAboutQt(self):
|
||||
"""Show Qt's own About dialog.
|
||||
"""
|
||||
msgBox = QMessageBox()
|
||||
msgBox.aboutQt(self.theParent,"About Qt")
|
||||
return True
|
||||
|
||||
def _openHelp(self):
|
||||
"""Open the documentation URL in the system's default browser.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(nw.__docurl__))
|
||||
return True
|
||||
|
||||
def _openIssue(self):
|
||||
"""Open the issue tracker URL in the system's default browser.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(nw.__issuesurl__))
|
||||
return True
|
||||
|
||||
def _showDocumentLocation(self):
|
||||
"""Open the dialog showing the location of the editor document.
|
||||
"""
|
||||
self.theParent.docEditor.revealLocation()
|
||||
return True
|
||||
|
||||
def _doBackup(self):
|
||||
"""Call the backup function for the project.
|
||||
"""
|
||||
self.theProject.zipIt(True)
|
||||
return True
|
||||
|
||||
@@ -228,14 +246,14 @@ class GuiMainMenu(QMenuBar):
|
||||
self.projMenu.addSeparator()
|
||||
|
||||
# Project > Edit
|
||||
self.aEditItem = QAction("&Edit Project Item", self)
|
||||
self.aEditItem = QAction("Edit Project Item", self)
|
||||
self.aEditItem.setStatusTip("Change item settings")
|
||||
self.aEditItem.setShortcuts(["Ctrl+E", "F2"])
|
||||
self.aEditItem.triggered.connect(self.theParent.editItem)
|
||||
self.projMenu.addAction(self.aEditItem)
|
||||
|
||||
# Project > Delete
|
||||
self.aDeleteItem = QAction("&Delete Project Item", self)
|
||||
self.aDeleteItem = QAction("Delete Project Item", self)
|
||||
self.aDeleteItem.setStatusTip("Delete selected item")
|
||||
self.aDeleteItem.setShortcut("Ctrl+Del")
|
||||
self.aDeleteItem.triggered.connect(lambda : self.theParent.treeView.deleteItem(None))
|
||||
@@ -265,21 +283,21 @@ class GuiMainMenu(QMenuBar):
|
||||
self.docuMenu = self.addMenu("&Document")
|
||||
|
||||
# Document > New
|
||||
self.aNewDoc = QAction("&New Document", self)
|
||||
self.aNewDoc = QAction("New Document", self)
|
||||
self.aNewDoc.setStatusTip("Create new document")
|
||||
self.aNewDoc.setShortcut("Ctrl+N")
|
||||
self.aNewDoc.triggered.connect(lambda : self._newTreeItem(nwItemType.FILE, None))
|
||||
self.docuMenu.addAction(self.aNewDoc)
|
||||
|
||||
# Document > Open
|
||||
self.aOpenDoc = QAction("&Open Document", self)
|
||||
self.aOpenDoc = QAction("Open Document", self)
|
||||
self.aOpenDoc.setStatusTip("Open selected document")
|
||||
self.aOpenDoc.setShortcut("Ctrl+O")
|
||||
self.aOpenDoc.triggered.connect(self.theParent.openSelectedItem)
|
||||
self.docuMenu.addAction(self.aOpenDoc)
|
||||
|
||||
# Document > Save
|
||||
self.aSaveDoc = QAction("&Save Document", self)
|
||||
self.aSaveDoc = QAction("Save Document", self)
|
||||
self.aSaveDoc.setStatusTip("Save current document")
|
||||
self.aSaveDoc.setShortcut("Ctrl+S")
|
||||
self.aSaveDoc.triggered.connect(self.theParent.saveDocument)
|
||||
@@ -341,53 +359,6 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
return
|
||||
|
||||
def _buildViewMenu(self):
|
||||
|
||||
# View
|
||||
self.viewMenu = self.addMenu("&View")
|
||||
|
||||
# View > TreeView
|
||||
self.aFocusTree = QAction("Focus Project Tree", self)
|
||||
self.aFocusTree.setStatusTip("Move focus to project tree")
|
||||
self.aFocusTree.setShortcut("Alt+1")
|
||||
self.aFocusTree.triggered.connect(lambda : self.theParent.setFocus(1))
|
||||
self.viewMenu.addAction(self.aFocusTree)
|
||||
|
||||
# View > Document Pane 1
|
||||
self.aFocusEditor = QAction("Focus Document Editor", self)
|
||||
self.aFocusEditor.setStatusTip("Move focus to left document pane")
|
||||
self.aFocusEditor.setShortcut("Alt+2")
|
||||
self.aFocusEditor.triggered.connect(lambda : self.theParent.setFocus(2))
|
||||
self.viewMenu.addAction(self.aFocusEditor)
|
||||
|
||||
# View > Document Pane 2
|
||||
self.aFocusView = QAction("Focus Document Viewer", self)
|
||||
self.aFocusView.setStatusTip("Move focus to right document pane")
|
||||
self.aFocusView.setShortcut("Alt+3")
|
||||
self.aFocusView.triggered.connect(lambda : self.theParent.setFocus(3))
|
||||
self.viewMenu.addAction(self.aFocusView)
|
||||
|
||||
# View > Separator
|
||||
self.viewMenu.addSeparator()
|
||||
|
||||
# View > Toggle Distraction Free Mode
|
||||
self.aZenMode = QAction("Zen Mode", self)
|
||||
self.aZenMode.setStatusTip("Toggles distraction free mode, only showing text editor")
|
||||
self.aZenMode.setShortcut("F8")
|
||||
self.aZenMode.setCheckable(True)
|
||||
self.aZenMode.setChecked(self.theParent.isZenMode)
|
||||
self.aZenMode.toggled.connect(self.theParent.toggleZenMode)
|
||||
self.viewMenu.addAction(self.aZenMode)
|
||||
|
||||
# View > Toggle Full Screen
|
||||
self.aFullScreen = QAction("Full Screen Mode", self)
|
||||
self.aFullScreen.setStatusTip("Maximises the main window")
|
||||
self.aFullScreen.setShortcut("F11")
|
||||
self.aFullScreen.triggered.connect(self.theParent.toggleFullScreenMode)
|
||||
self.viewMenu.addAction(self.aFullScreen)
|
||||
|
||||
return
|
||||
|
||||
def _buildEditMenu(self):
|
||||
|
||||
# Edit
|
||||
@@ -497,18 +468,70 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
return
|
||||
|
||||
def _buildInsertMenu(self):
|
||||
|
||||
# Insert
|
||||
self.insertMenu = self.addMenu("&Insert")
|
||||
|
||||
# Insert > Short Dash
|
||||
self.aInsENDash = QAction("Short Dash", self)
|
||||
self.aInsENDash.setStatusTip("Insert short dash")
|
||||
self.aInsENDash.setShortcut("Ctrl+K, -")
|
||||
self.aInsENDash.triggered.connect(lambda: self._docInsert(nwDocInsert.SHORT_DASH))
|
||||
self.insertMenu.addAction(self.aInsENDash)
|
||||
|
||||
# Insert > Long Dash
|
||||
self.aInsEMDash = QAction("Long Dash", self)
|
||||
self.aInsEMDash.setStatusTip("Insert long dash")
|
||||
self.aInsEMDash.setShortcut("Ctrl+K, _")
|
||||
self.aInsEMDash.triggered.connect(lambda: self._docInsert(nwDocInsert.LONG_DASH))
|
||||
self.insertMenu.addAction(self.aInsEMDash)
|
||||
|
||||
# Insert > Ellipsis
|
||||
self.aInsEllipsis = QAction("Ellipsis", self)
|
||||
self.aInsEllipsis.setStatusTip("Insert ellipsis")
|
||||
self.aInsEllipsis.setShortcut("Ctrl+K, .")
|
||||
self.aInsEllipsis.triggered.connect(lambda: self._docInsert(nwDocInsert.ELLIPSIS))
|
||||
self.insertMenu.addAction(self.aInsEllipsis)
|
||||
|
||||
# Insert > Separator
|
||||
self.insertMenu.addSeparator()
|
||||
|
||||
# Insert > Hard Line Break
|
||||
self.aInsHardBreak = QAction("Hard Line Break", self)
|
||||
self.aInsHardBreak.setStatusTip("Insert a hard line break")
|
||||
self.aInsHardBreak.setShortcut("Ctrl+K, Return")
|
||||
self.aInsHardBreak.triggered.connect(lambda: self._docInsert(nwDocInsert.HARD_BREAK))
|
||||
self.insertMenu.addAction(self.aInsHardBreak)
|
||||
|
||||
# Insert > Non-Breaking Space
|
||||
self.aInsNBSpace = QAction("Non-Breaking Space", self)
|
||||
self.aInsNBSpace.setStatusTip("Insert a non-breaking space")
|
||||
self.aInsNBSpace.setShortcut("Ctrl+K, Space")
|
||||
self.aInsNBSpace.triggered.connect(lambda: self._docInsert(nwDocInsert.NB_SPACE))
|
||||
self.insertMenu.addAction(self.aInsNBSpace)
|
||||
|
||||
# Insert > Thin Space
|
||||
self.aInsThinSpace = QAction("Thin Space", self)
|
||||
self.aInsThinSpace.setStatusTip("Insert a thin space")
|
||||
self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space")
|
||||
self.aInsThinSpace.triggered.connect(lambda: self._docInsert(nwDocInsert.THIN_SPACE))
|
||||
self.insertMenu.addAction(self.aInsThinSpace)
|
||||
|
||||
# Insert > Thin Non-Breaking Space
|
||||
self.aInsThinNBSpace = QAction("Thin Non-Breaking Space", self)
|
||||
self.aInsThinNBSpace.setStatusTip("Insert a thin non-breaking space")
|
||||
self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space")
|
||||
self.aInsThinNBSpace.triggered.connect(lambda: self._docInsert(nwDocInsert.THIN_NB_SPACE))
|
||||
self.insertMenu.addAction(self.aInsThinNBSpace)
|
||||
|
||||
return
|
||||
|
||||
def _buildFormatMenu(self):
|
||||
|
||||
# Format
|
||||
self.fmtMenu = self.addMenu("&Format")
|
||||
|
||||
# Format > Bold Text
|
||||
self.aFmtBold = QAction("Bold Text", self)
|
||||
self.aFmtBold.setStatusTip("Make selected text bold")
|
||||
self.aFmtBold.setShortcut("Ctrl+B")
|
||||
self.aFmtBold.triggered.connect(lambda: self._docAction(nwDocAction.BOLD))
|
||||
self.fmtMenu.addAction(self.aFmtBold)
|
||||
|
||||
# Format > Italic Text
|
||||
self.aFmtItalic = QAction("Italic Text", self)
|
||||
self.aFmtItalic.setStatusTip("Make selected text italic")
|
||||
@@ -516,12 +539,26 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFmtItalic.triggered.connect(lambda: self._docAction(nwDocAction.ITALIC))
|
||||
self.fmtMenu.addAction(self.aFmtItalic)
|
||||
|
||||
# Format > Bold Text
|
||||
self.aFmtBold = QAction("Bold Text", self)
|
||||
self.aFmtBold.setStatusTip("Make selected text bold")
|
||||
self.aFmtBold.setShortcut("Ctrl+B")
|
||||
self.aFmtBold.triggered.connect(lambda: self._docAction(nwDocAction.BOLD))
|
||||
self.fmtMenu.addAction(self.aFmtBold)
|
||||
|
||||
# Format > Underline Text
|
||||
self.aFmtULine = QAction("Underline Text", self)
|
||||
self.aFmtULine.setStatusTip("Underline selected text")
|
||||
self.aFmtULine.setShortcut("Ctrl+U")
|
||||
self.aFmtULine.triggered.connect(lambda: self._docAction(nwDocAction.U_LINE))
|
||||
self.fmtMenu.addAction(self.aFmtULine)
|
||||
self.aFmtBoldIt = QAction("Bold Italic Text", self)
|
||||
self.aFmtBoldIt.setStatusTip("Make selected text bold and italic")
|
||||
self.aFmtBoldIt.setShortcut("Ctrl+Shift+B")
|
||||
self.aFmtBoldIt.triggered.connect(lambda: self._docAction(nwDocAction.BOLDITALIC))
|
||||
self.fmtMenu.addAction(self.aFmtBoldIt)
|
||||
|
||||
# Format > Strikethrough
|
||||
self.aFmtStrike = QAction("Strikethrough Text", self)
|
||||
self.aFmtStrike.setStatusTip("Strikethrough selected text")
|
||||
self.aFmtStrike.setShortcut("Ctrl+-")
|
||||
self.aFmtStrike.triggered.connect(lambda: self._docAction(nwDocAction.STRIKE))
|
||||
self.fmtMenu.addAction(self.aFmtStrike)
|
||||
|
||||
# Edit > Separator
|
||||
self.fmtMenu.addSeparator()
|
||||
@@ -540,7 +577,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFmtSQuote.triggered.connect(lambda: self._docAction(nwDocAction.S_QUOTE))
|
||||
self.fmtMenu.addAction(self.aFmtSQuote)
|
||||
|
||||
# Edit > Separator
|
||||
# Format > Separator
|
||||
self.fmtMenu.addSeparator()
|
||||
|
||||
# Format > Header 1
|
||||
@@ -585,6 +622,68 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFmtNoFormat.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_TXT))
|
||||
self.fmtMenu.addAction(self.aFmtNoFormat)
|
||||
|
||||
# Format > Separator
|
||||
self.fmtMenu.addSeparator()
|
||||
|
||||
# Format > Replace Single Quotes
|
||||
self.aFmtReplSng = QAction("Replace Single Quotes", self)
|
||||
self.aFmtReplSng.setStatusTip("Replace all straight single quotes in selected text")
|
||||
self.aFmtReplSng.triggered.connect(lambda: self._docAction(nwDocAction.REPL_SNG))
|
||||
self.fmtMenu.addAction(self.aFmtReplSng)
|
||||
|
||||
# Format > Replace Double Quotes
|
||||
self.aFmtReplDbl = QAction("Replace Double Quotes", self)
|
||||
self.aFmtReplDbl.setStatusTip("Replace all straight double quotes in selected text")
|
||||
self.aFmtReplDbl.triggered.connect(lambda: self._docAction(nwDocAction.REPL_DBL))
|
||||
self.fmtMenu.addAction(self.aFmtReplDbl)
|
||||
|
||||
return
|
||||
|
||||
def _buildViewMenu(self):
|
||||
|
||||
# View
|
||||
self.viewMenu = self.addMenu("&View")
|
||||
|
||||
# View > TreeView
|
||||
self.aFocusTree = QAction("Focus Project Tree", self)
|
||||
self.aFocusTree.setStatusTip("Move focus to project tree")
|
||||
self.aFocusTree.setShortcut("Alt+1")
|
||||
self.aFocusTree.triggered.connect(lambda : self.theParent.setFocus(1))
|
||||
self.viewMenu.addAction(self.aFocusTree)
|
||||
|
||||
# View > Document Pane 1
|
||||
self.aFocusEditor = QAction("Focus Document Editor", self)
|
||||
self.aFocusEditor.setStatusTip("Move focus to left document pane")
|
||||
self.aFocusEditor.setShortcut("Alt+2")
|
||||
self.aFocusEditor.triggered.connect(lambda : self.theParent.setFocus(2))
|
||||
self.viewMenu.addAction(self.aFocusEditor)
|
||||
|
||||
# View > Document Pane 2
|
||||
self.aFocusView = QAction("Focus Document Viewer", self)
|
||||
self.aFocusView.setStatusTip("Move focus to right document pane")
|
||||
self.aFocusView.setShortcut("Alt+3")
|
||||
self.aFocusView.triggered.connect(lambda : self.theParent.setFocus(3))
|
||||
self.viewMenu.addAction(self.aFocusView)
|
||||
|
||||
# View > Separator
|
||||
self.viewMenu.addSeparator()
|
||||
|
||||
# View > Toggle Distraction Free Mode
|
||||
self.aZenMode = QAction("Zen Mode", self)
|
||||
self.aZenMode.setStatusTip("Toggles distraction free mode, only showing text editor")
|
||||
self.aZenMode.setShortcut("F8")
|
||||
self.aZenMode.setCheckable(True)
|
||||
self.aZenMode.setChecked(self.theParent.isZenMode)
|
||||
self.aZenMode.toggled.connect(self.theParent.toggleZenMode)
|
||||
self.viewMenu.addAction(self.aZenMode)
|
||||
|
||||
# View > Toggle Full Screen
|
||||
self.aFullScreen = QAction("Full Screen Mode", self)
|
||||
self.aFullScreen.setStatusTip("Maximises the main window")
|
||||
self.aFullScreen.setShortcut("F11")
|
||||
self.aFullScreen.triggered.connect(self.theParent.toggleFullScreenMode)
|
||||
self.viewMenu.addAction(self.aFullScreen)
|
||||
|
||||
return
|
||||
|
||||
def _buildToolsMenu(self):
|
||||
@@ -696,6 +795,12 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aHelp.triggered.connect(self._openHelp)
|
||||
self.helpMenu.addAction(self.aHelp)
|
||||
|
||||
# Document > Report Issue
|
||||
self.aIssue = QAction("Report an Issue", self)
|
||||
self.aIssue.setStatusTip("View online documentation")
|
||||
self.aIssue.triggered.connect(self._openIssue)
|
||||
self.helpMenu.addAction(self.aIssue)
|
||||
|
||||
return
|
||||
|
||||
# END Class GuiMainMenu
|
||||
|
||||
@@ -102,7 +102,7 @@ class GuiOutline(QTreeWidget):
|
||||
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||
self.itemSelectionChanged.connect(self._itemSelected)
|
||||
|
||||
iPx = self.theTheme.textIconSize
|
||||
iPx = self.theTheme.baseIconSize
|
||||
self.setIconSize(QSize(iPx, iPx))
|
||||
self.setIndentation(iPx)
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ from PyQt5.QtWidgets import (
|
||||
QDialogButtonBox, QFileDialog, QFontDialog
|
||||
)
|
||||
|
||||
from nw.gui.custom import QSwitch, QConfigLayout, PagedDialog
|
||||
from nw.gui.custom import QSwitch, QConfigLayout, PagedDialog, QuotesDialog
|
||||
from nw.core import NWSpellCheck, NWSpellSimple, NWSpellEnchant
|
||||
from nw.constants import nwAlert, nwQuotes
|
||||
from nw.constants import nwQuotes
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -816,51 +816,72 @@ class GuiConfigEditAutoReplaceTab(QWidget):
|
||||
self.mainForm.addGroupLabel("Quotation Style")
|
||||
|
||||
qWidth = self.mainConf.pxInt(40)
|
||||
bWidth = int(2.5*self.theTheme.getTextWidth("..."))
|
||||
|
||||
## Single Quote Style
|
||||
self.quoteSingleStyleO = QLineEdit()
|
||||
self.quoteSingleStyleO.setMaxLength(1)
|
||||
self.quoteSingleStyleO.setReadOnly(True)
|
||||
self.quoteSingleStyleO.setFixedWidth(qWidth)
|
||||
self.quoteSingleStyleO.setAlignment(Qt.AlignCenter)
|
||||
self.quoteSingleStyleO.setText(self.mainConf.fmtSingleQuotes[0])
|
||||
self.btnSingleStyleO = QPushButton("...")
|
||||
self.btnSingleStyleO.setMaximumWidth(bWidth)
|
||||
self.btnSingleStyleO.clicked.connect(self._getSingleOpen)
|
||||
self.mainForm.addRow(
|
||||
"Single quote open style",
|
||||
self.quoteSingleStyleO,
|
||||
"Auto-replaces apostrophe before words."
|
||||
"Auto-replaces apostrophe before words.",
|
||||
theButton=self.btnSingleStyleO
|
||||
)
|
||||
|
||||
self.quoteSingleStyleC = QLineEdit()
|
||||
self.quoteSingleStyleC.setMaxLength(1)
|
||||
self.quoteSingleStyleC.setReadOnly(True)
|
||||
self.quoteSingleStyleC.setFixedWidth(qWidth)
|
||||
self.quoteSingleStyleC.setAlignment(Qt.AlignCenter)
|
||||
self.quoteSingleStyleC.setText(self.mainConf.fmtSingleQuotes[1])
|
||||
self.btnSingleStyleC = QPushButton("...")
|
||||
self.btnSingleStyleC.setMaximumWidth(bWidth)
|
||||
self.btnSingleStyleC.clicked.connect(self._getSingleClose)
|
||||
self.mainForm.addRow(
|
||||
"Single quote close style",
|
||||
self.quoteSingleStyleC,
|
||||
"Auto-replaces apostrophe after words."
|
||||
"Auto-replaces apostrophe after words.",
|
||||
theButton=self.btnSingleStyleC
|
||||
)
|
||||
|
||||
## Double Quote Style
|
||||
self.quoteDoubleStyleO = QLineEdit()
|
||||
self.quoteDoubleStyleO.setMaxLength(1)
|
||||
self.quoteDoubleStyleO.setReadOnly(True)
|
||||
self.quoteDoubleStyleO.setFixedWidth(qWidth)
|
||||
self.quoteDoubleStyleO.setAlignment(Qt.AlignCenter)
|
||||
self.quoteDoubleStyleO.setText(self.mainConf.fmtDoubleQuotes[0])
|
||||
self.btnDoubleStyleO = QPushButton("...")
|
||||
self.btnDoubleStyleO.setMaximumWidth(bWidth)
|
||||
self.btnDoubleStyleO.clicked.connect(self._getDoubleOpen)
|
||||
self.mainForm.addRow(
|
||||
"Double quote open style",
|
||||
self.quoteDoubleStyleO,
|
||||
"Auto-replaces straight quotes before words."
|
||||
"Auto-replaces straight quotes before words.",
|
||||
theButton=self.btnDoubleStyleO
|
||||
)
|
||||
|
||||
self.quoteDoubleStyleC = QLineEdit()
|
||||
self.quoteDoubleStyleC.setMaxLength(1)
|
||||
self.quoteDoubleStyleC.setReadOnly(True)
|
||||
self.quoteDoubleStyleC.setFixedWidth(qWidth)
|
||||
self.quoteDoubleStyleC.setAlignment(Qt.AlignCenter)
|
||||
self.quoteDoubleStyleC.setText(self.mainConf.fmtDoubleQuotes[1])
|
||||
self.btnDoubleStyleC = QPushButton("...")
|
||||
self.btnDoubleStyleC.setMaximumWidth(bWidth)
|
||||
self.btnDoubleStyleC.clicked.connect(self._getDoubleClose)
|
||||
self.mainForm.addRow(
|
||||
"Double quote close style",
|
||||
self.quoteDoubleStyleC,
|
||||
"Auto-replaces straight quotes after words."
|
||||
"Auto-replaces straight quotes after words.",
|
||||
theButton=self.btnDoubleStyleC
|
||||
)
|
||||
|
||||
return
|
||||
@@ -889,37 +910,10 @@ class GuiConfigEditAutoReplaceTab(QWidget):
|
||||
fmtDoubleQuotesO = self.quoteDoubleStyleO.text()
|
||||
fmtDoubleQuotesC = self.quoteDoubleStyleC.text()
|
||||
|
||||
if self._checkQuoteSymbol(fmtSingleQuotesO):
|
||||
self.mainConf.fmtSingleQuotes[0] = fmtSingleQuotesO
|
||||
else:
|
||||
self.theParent.makeAlert(
|
||||
"Invalid quote symbol: %s" % fmtSingleQuotesO, nwAlert.ERROR
|
||||
)
|
||||
validEntries = False
|
||||
|
||||
if self._checkQuoteSymbol(fmtSingleQuotesC):
|
||||
self.mainConf.fmtSingleQuotes[1] = fmtSingleQuotesC
|
||||
else:
|
||||
self.theParent.makeAlert(
|
||||
"Invalid quote symbol: %s" % fmtSingleQuotesC, nwAlert.ERROR
|
||||
)
|
||||
validEntries = False
|
||||
|
||||
if self._checkQuoteSymbol(fmtDoubleQuotesO):
|
||||
self.mainConf.fmtDoubleQuotes[0] = fmtDoubleQuotesO
|
||||
else:
|
||||
self.theParent.makeAlert(
|
||||
"Invalid quote symbol: %s" % fmtDoubleQuotesO, nwAlert.ERROR
|
||||
)
|
||||
validEntries = False
|
||||
|
||||
if self._checkQuoteSymbol(fmtDoubleQuotesC):
|
||||
self.mainConf.fmtDoubleQuotes[1] = fmtDoubleQuotesC
|
||||
else:
|
||||
self.theParent.makeAlert(
|
||||
"Invalid quote symbol: %s" % fmtDoubleQuotesC, nwAlert.ERROR
|
||||
)
|
||||
validEntries = False
|
||||
self.mainConf.fmtSingleQuotes[0] = fmtSingleQuotesO
|
||||
self.mainConf.fmtSingleQuotes[1] = fmtSingleQuotesC
|
||||
self.mainConf.fmtDoubleQuotes[0] = fmtDoubleQuotesO
|
||||
self.mainConf.fmtDoubleQuotes[1] = fmtDoubleQuotesC
|
||||
|
||||
self.mainConf.confChanged = True
|
||||
|
||||
@@ -939,6 +933,38 @@ class GuiConfigEditAutoReplaceTab(QWidget):
|
||||
self.autoReplaceDots.setEnabled(theState)
|
||||
return
|
||||
|
||||
def _getSingleOpen(self):
|
||||
"""Dialog for single quote open.
|
||||
"""
|
||||
qtBox = QuotesDialog(self, currentQuote=self.quoteSingleStyleO.text())
|
||||
if qtBox.exec_() == QDialog.Accepted:
|
||||
self.quoteSingleStyleO.setText(qtBox.selectedQuote)
|
||||
return
|
||||
|
||||
def _getSingleClose(self):
|
||||
"""Dialog for single quote close.
|
||||
"""
|
||||
qtBox = QuotesDialog(self, currentQuote=self.quoteSingleStyleC.text())
|
||||
if qtBox.exec_() == QDialog.Accepted:
|
||||
self.quoteSingleStyleC.setText(qtBox.selectedQuote)
|
||||
return
|
||||
|
||||
def _getDoubleOpen(self):
|
||||
"""Dialog for double quote open.
|
||||
"""
|
||||
qtBox = QuotesDialog(self, currentQuote=self.quoteDoubleStyleO.text())
|
||||
if qtBox.exec_() == QDialog.Accepted:
|
||||
self.quoteDoubleStyleO.setText(qtBox.selectedQuote)
|
||||
return
|
||||
|
||||
def _getDoubleClose(self):
|
||||
"""Dialog for double quote close.
|
||||
"""
|
||||
qtBox = QuotesDialog(self, currentQuote=self.quoteDoubleStyleC.text())
|
||||
if qtBox.exec_() == QDialog.Accepted:
|
||||
self.quoteDoubleStyleC.setText(qtBox.selectedQuote)
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
@@ -36,7 +36,7 @@ from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
|
||||
QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut,
|
||||
QFileDialog, QLineEdit
|
||||
QFileDialog, QLineEdit, QMessageBox
|
||||
)
|
||||
|
||||
from nw.common import formatInt
|
||||
@@ -50,6 +50,10 @@ class GuiProjectLoad(QDialog):
|
||||
NEW_STATE = 1
|
||||
OPEN_STATE = 2
|
||||
|
||||
C_NAME = 0
|
||||
C_COUNT = 1
|
||||
C_TIME = 2
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
|
||||
@@ -62,7 +66,8 @@ class GuiProjectLoad(QDialog):
|
||||
self.openPath = None
|
||||
|
||||
sPx = self.mainConf.pxInt(16)
|
||||
iPx = self.mainConf.pxInt(96)
|
||||
nPx = self.mainConf.pxInt(96)
|
||||
iPx = self.theTheme.baseIconSize
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.innerBox = QHBoxLayout()
|
||||
@@ -74,13 +79,12 @@ class GuiProjectLoad(QDialog):
|
||||
self.setMinimumHeight(self.mainConf.pxInt(400))
|
||||
self.setModal(True)
|
||||
|
||||
self.guiDeco = self.theTheme.loadDecoration("nwicon", (iPx, iPx))
|
||||
self.guiDeco = self.theTheme.loadDecoration("nwicon", (nPx, nPx))
|
||||
self.innerBox.addWidget(self.guiDeco, 0, Qt.AlignTop)
|
||||
|
||||
self.projectForm = QGridLayout()
|
||||
self.projectForm.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
iPx = self.theTheme.textIconSize
|
||||
self.listBox = QTreeWidget()
|
||||
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
|
||||
@@ -92,8 +96,8 @@ class GuiProjectLoad(QDialog):
|
||||
self.listBox.setIconSize(QSize(iPx, iPx))
|
||||
|
||||
treeHead = self.listBox.headerItem()
|
||||
treeHead.setTextAlignment(1, Qt.AlignRight)
|
||||
treeHead.setTextAlignment(2, Qt.AlignRight)
|
||||
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
||||
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
|
||||
|
||||
self.lblRecent = QLabel("<b>Recently Opened Projects</b>")
|
||||
self.lblPath = QLabel("<b>Path</b>")
|
||||
@@ -150,7 +154,7 @@ class GuiProjectLoad(QDialog):
|
||||
self._saveDialogState()
|
||||
selItems = self.listBox.selectedItems()
|
||||
if selItems:
|
||||
self.openPath = selItems[0].data(0, Qt.UserRole)
|
||||
self.openPath = selItems[0].data(self.C_NAME, Qt.UserRole)
|
||||
self.openState = self.OPEN_STATE
|
||||
self.accept()
|
||||
else:
|
||||
@@ -163,7 +167,7 @@ class GuiProjectLoad(QDialog):
|
||||
"""
|
||||
selList = self.listBox.selectedItems()
|
||||
if selList:
|
||||
self.selPath.setText(selList[0].data(0, Qt.UserRole))
|
||||
self.selPath.setText(selList[0].data(self.C_NAME, Qt.UserRole))
|
||||
return
|
||||
|
||||
def _doBrowse(self):
|
||||
@@ -210,8 +214,23 @@ class GuiProjectLoad(QDialog):
|
||||
"""
|
||||
selList = self.listBox.selectedItems()
|
||||
if selList:
|
||||
self.mainConf.removeFromRecentCache(selList[0].text(3))
|
||||
self._populateList()
|
||||
doRemove = False
|
||||
if self.mainConf.showGUI:
|
||||
msgBox = QMessageBox()
|
||||
msgRes = msgBox.question(
|
||||
self, "Remove Entry",
|
||||
"Remove the selected entry from the recent projects list?"
|
||||
)
|
||||
doRemove = (msgRes == QMessageBox.Yes)
|
||||
else:
|
||||
doRemove = True
|
||||
|
||||
if doRemove:
|
||||
self.mainConf.removeFromRecentCache(
|
||||
selList[0].data(self.C_NAME, Qt.UserRole)
|
||||
)
|
||||
self._populateList()
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
@@ -221,9 +240,10 @@ class GuiProjectLoad(QDialog):
|
||||
def _saveDialogState(self):
|
||||
"""Save the changes made to the dialog.
|
||||
"""
|
||||
colWidths = [50]*3
|
||||
for i in range(3):
|
||||
colWidths[i] = self.listBox.columnWidth(i)
|
||||
colWidths = [0, 0, 0]
|
||||
colWidths[self.C_NAME] = self.listBox.columnWidth(self.C_NAME)
|
||||
colWidths[self.C_COUNT] = self.listBox.columnWidth(self.C_COUNT)
|
||||
colWidths[self.C_TIME] = self.listBox.columnWidth(self.C_TIME)
|
||||
self.mainConf.setProjColWidths(colWidths)
|
||||
return
|
||||
|
||||
@@ -251,22 +271,24 @@ class GuiProjectLoad(QDialog):
|
||||
hasSelection = False
|
||||
for timeStamp in sorted(listOrder, reverse=True):
|
||||
newItem = QTreeWidgetItem([""]*4)
|
||||
newItem.setIcon(0, self.theParent.theTheme.getIcon("proj_nwx"))
|
||||
newItem.setText(0, listData[timeStamp][0])
|
||||
newItem.setData(0, Qt.UserRole, listData[timeStamp][2])
|
||||
newItem.setText(1, formatInt(listData[timeStamp][1]))
|
||||
newItem.setText(2, datetime.fromtimestamp(timeStamp).strftime("%x %X"))
|
||||
newItem.setTextAlignment(0, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
newItem.setTextAlignment(1, Qt.AlignRight | Qt.AlignVCenter)
|
||||
newItem.setTextAlignment(2, Qt.AlignRight | Qt.AlignVCenter)
|
||||
newItem.setIcon(self.C_NAME, self.theParent.theTheme.getIcon("proj_nwx"))
|
||||
newItem.setText(self.C_NAME, listData[timeStamp][0])
|
||||
newItem.setData(self.C_NAME, Qt.UserRole, listData[timeStamp][2])
|
||||
newItem.setText(self.C_COUNT, formatInt(listData[timeStamp][1]))
|
||||
newItem.setText(self.C_TIME, datetime.fromtimestamp(timeStamp).strftime("%x %X"))
|
||||
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
|
||||
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
|
||||
self.listBox.addTopLevelItem(newItem)
|
||||
if not hasSelection:
|
||||
newItem.setSelected(True)
|
||||
hasSelection = True
|
||||
|
||||
projColWidth = self.mainConf.getProjColWidths()
|
||||
for i in range(3):
|
||||
self.listBox.setColumnWidth(i, projColWidth[i])
|
||||
if len(projColWidth) == 3:
|
||||
self.listBox.setColumnWidth(self.C_NAME, projColWidth[self.C_NAME])
|
||||
self.listBox.setColumnWidth(self.C_COUNT, projColWidth[self.C_COUNT])
|
||||
self.listBox.setColumnWidth(self.C_TIME, projColWidth[self.C_TIME])
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -31,10 +31,9 @@ import nw
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QLineEdit, QPlainTextEdit,
|
||||
QLabel, QWidget, QTabWidget, QDialogButtonBox, QListWidget, QPushButton,
|
||||
QListWidgetItem, QColorDialog, QAbstractItemView, QTreeWidget, QCheckBox,
|
||||
QTreeWidgetItem
|
||||
QHBoxLayout, QVBoxLayout, QGridLayout, QLineEdit, QPlainTextEdit, QLabel,
|
||||
QWidget, QDialogButtonBox, QListWidget, QPushButton, QListWidgetItem,
|
||||
QColorDialog, QAbstractItemView, QTreeWidget, QTreeWidgetItem
|
||||
)
|
||||
|
||||
from nw.constants import nwAlert
|
||||
@@ -52,9 +51,17 @@ class GuiProjectSettings(PagedDialog):
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theProject
|
||||
self.optState = theProject.optState
|
||||
|
||||
self.theProject.countStatus()
|
||||
self.setWindowTitle("Project Settings")
|
||||
self.setMinimumWidth(self.mainConf.pxInt(570))
|
||||
self.setMinimumHeight(self.mainConf.pxInt(355))
|
||||
|
||||
self.resize(
|
||||
self.mainConf.pxInt(self.optState.getInt("GuiProjectSettings", "winWidth", 570)),
|
||||
self.mainConf.pxInt(self.optState.getInt("GuiProjectSettings", "winHeight", 355))
|
||||
)
|
||||
|
||||
self.tabMain = GuiProjectEditMain(self.theParent, self.theProject)
|
||||
self.tabMeta = GuiProjectEditMeta(self.theParent, self.theProject)
|
||||
@@ -73,13 +80,17 @@ class GuiProjectSettings(PagedDialog):
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.addControls(self.buttonBox)
|
||||
|
||||
self.show()
|
||||
|
||||
logger.debug("GuiProjectSettings initialisation complete")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
##
|
||||
|
||||
def _doSave(self):
|
||||
"""Save settings and close dialog.
|
||||
"""
|
||||
logger.verbose("GuiProjectSettings save button clicked")
|
||||
|
||||
projName = self.tabMain.editName.text()
|
||||
@@ -103,13 +114,23 @@ class GuiProjectSettings(PagedDialog):
|
||||
newList = self.tabReplace.getNewList()
|
||||
self.theProject.setAutoReplace(newList)
|
||||
|
||||
self.close()
|
||||
self._doClose()
|
||||
|
||||
return
|
||||
|
||||
def _doClose(self):
|
||||
logger.verbose("GuiProjectSettings close button clicked")
|
||||
"""Close the dialog.
|
||||
"""
|
||||
winWidth = self.mainConf.rpxInt(self.width())
|
||||
winHeight = self.mainConf.rpxInt(self.height())
|
||||
replaceColW = self.mainConf.rpxInt(self.tabReplace.listBox.columnWidth(0))
|
||||
|
||||
self.optState.setValue("GuiProjectSettings", "winWidth", winWidth)
|
||||
self.optState.setValue("GuiProjectSettings", "winHeight", winHeight)
|
||||
self.optState.setValue("GuiProjectSettings", "replaceColW", replaceColW)
|
||||
|
||||
self.close()
|
||||
|
||||
return
|
||||
|
||||
# END Class GuiProjectSettings
|
||||
@@ -282,7 +303,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.colChanged = False
|
||||
self.selColour = None
|
||||
|
||||
self.iPx = self.theTheme.textIconSize
|
||||
self.iPx = self.theTheme.baseIconSize
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.mainBox = QHBoxLayout()
|
||||
@@ -455,16 +476,23 @@ class GuiProjectEditReplace(QWidget):
|
||||
def __init__(self, theParent, theProject):
|
||||
QWidget.__init__(self, theParent)
|
||||
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theProject
|
||||
self.optState = theProject.optState
|
||||
self.arChanged = False
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.bottomBox = QHBoxLayout()
|
||||
self.listBox = QTreeWidget()
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.bottomBox = QHBoxLayout()
|
||||
|
||||
wCol0 = self.mainConf.pxInt(
|
||||
self.optState.getInt("GuiProjectSettings", "replaceColW", 100)
|
||||
)
|
||||
self.listBox = QTreeWidget()
|
||||
self.listBox.setHeaderLabels(["Keyword","Replace With"])
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedItem)
|
||||
self.listBox.setColumnWidth(0, wCol0)
|
||||
self.listBox.setIndentation(0)
|
||||
|
||||
for aKey, aVal in self.theProject.autoReplace.items():
|
||||
|
||||
@@ -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
|
||||
@@ -555,14 +569,14 @@ class GuiProjectTree(QTreeWidget):
|
||||
selHandles.append(selItems[n].data(self.C_NAME, Qt.UserRole))
|
||||
return selHandles
|
||||
|
||||
def setSelectedHandle(self, tHandle):
|
||||
def setSelectedHandle(self, tHandle, doScroll=False):
|
||||
"""Set a specific handle as the selected item.
|
||||
"""
|
||||
if tHandle in self.theMap:
|
||||
self.clearSelection()
|
||||
self.theMap[tHandle].setSelected(True)
|
||||
selItems = self.selectedIndexes()
|
||||
if selItems:
|
||||
if selItems and doScroll:
|
||||
self.scrollTo(
|
||||
selItems[0], QAbstractItemView.PositionAtCenter
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -61,7 +61,7 @@ class GuiMainStatus(QStatusBar):
|
||||
colTrue = QColor(*self.theTheme.statUnsaved)
|
||||
colFalse = QColor(*self.theTheme.statSaved)
|
||||
|
||||
iPx = self.theTheme.textIconSize
|
||||
iPx = self.theTheme.baseIconSize
|
||||
|
||||
# Permanent Widgets
|
||||
# =================
|
||||
|
||||
@@ -143,7 +143,6 @@ class GuiTheme:
|
||||
self.fontPointSize = self.guiFont.pointSizeF()
|
||||
self.fontPixelSize = int(round(qMetric.height()))
|
||||
self.baseIconSize = int(round(qMetric.ascent()))
|
||||
self.textIconSize = int(round(qMetric.ascent() + qMetric.leading()))
|
||||
self.textNHeight = qMetric.boundingRect("N").height()
|
||||
self.textNWidth = qMetric.boundingRect("N").width()
|
||||
|
||||
@@ -151,7 +150,6 @@ class GuiTheme:
|
||||
logger.verbose("GUI Font Point Size: %.2f" % self.fontPointSize)
|
||||
logger.verbose("GUI Font Pixel Size: %d" % self.fontPixelSize)
|
||||
logger.verbose("GUI Base Icon Size: %d" % self.baseIconSize)
|
||||
logger.verbose("GUI Text Icon Size: %d" % self.textIconSize)
|
||||
logger.verbose("Text 'N' Height: %d" % self.textNHeight)
|
||||
logger.verbose("Text 'N' Width: %d" % self.textNWidth)
|
||||
|
||||
@@ -497,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,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.
|
||||
"""
|
||||
@@ -498,20 +518,30 @@ class GuiMain(QMainWindow):
|
||||
"""Load a document for viewing in the view panel.
|
||||
"""
|
||||
if tHandle is None:
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
if tHandle is None:
|
||||
logger.debug("No document selected, trying editor document")
|
||||
tHandle = self.docEditor.theHandle
|
||||
if tHandle is None:
|
||||
logger.debug("No document selected, trying last viewed")
|
||||
tHandle = self.theProject.lastViewed
|
||||
if tHandle is None:
|
||||
logger.debug("No document selected, giving up")
|
||||
return False
|
||||
logger.debug("Viewing document, but no handle provided")
|
||||
|
||||
if self.docEditor.hasFocus():
|
||||
logger.verbose("Trying editor document")
|
||||
tHandle = self.docEditor.theHandle
|
||||
|
||||
if tHandle is not None:
|
||||
self.saveDocument()
|
||||
else:
|
||||
logger.verbose("Trying selected document")
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
|
||||
if tHandle is None:
|
||||
logger.verbose("Trying last viewed document")
|
||||
tHandle = self.theProject.lastViewed
|
||||
|
||||
if tHandle is None:
|
||||
logger.verbose("No document to view, giving up")
|
||||
return False
|
||||
|
||||
# Make sure main tab is in Editor view
|
||||
self.tabWidget.setCurrentWidget(self.splitDocs)
|
||||
|
||||
logger.debug("Viewing document with handle %s" % tHandle)
|
||||
if self.docViewer.loadText(tHandle):
|
||||
if not self.splitView.isVisible():
|
||||
bPos = self.splitMain.sizes()
|
||||
@@ -601,15 +631,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
|
||||
|
||||
##
|
||||
@@ -948,12 +976,15 @@ class GuiMain(QMainWindow):
|
||||
"""Connect to the main window all menu actions that need to be
|
||||
available also when the main menu is hidden.
|
||||
"""
|
||||
# Project
|
||||
self.addAction(self.mainMenu.aSaveProject)
|
||||
self.addAction(self.mainMenu.aExitNW)
|
||||
|
||||
# Document
|
||||
self.addAction(self.mainMenu.aSaveDoc)
|
||||
self.addAction(self.mainMenu.aFileDetails)
|
||||
self.addAction(self.mainMenu.aZenMode)
|
||||
self.addAction(self.mainMenu.aFullScreen)
|
||||
|
||||
# Edit
|
||||
self.addAction(self.mainMenu.aEditUndo)
|
||||
self.addAction(self.mainMenu.aEditRedo)
|
||||
self.addAction(self.mainMenu.aEditCut)
|
||||
@@ -961,9 +992,20 @@ class GuiMain(QMainWindow):
|
||||
self.addAction(self.mainMenu.aEditPaste)
|
||||
self.addAction(self.mainMenu.aSelectAll)
|
||||
self.addAction(self.mainMenu.aSelectPar)
|
||||
self.addAction(self.mainMenu.aFmtBold)
|
||||
|
||||
# Insert
|
||||
self.addAction(self.mainMenu.aInsENDash)
|
||||
self.addAction(self.mainMenu.aInsEMDash)
|
||||
self.addAction(self.mainMenu.aInsEllipsis)
|
||||
self.addAction(self.mainMenu.aInsHardBreak)
|
||||
self.addAction(self.mainMenu.aInsNBSpace)
|
||||
self.addAction(self.mainMenu.aInsThinSpace)
|
||||
self.addAction(self.mainMenu.aInsThinNBSpace)
|
||||
|
||||
# Format
|
||||
self.addAction(self.mainMenu.aFmtItalic)
|
||||
self.addAction(self.mainMenu.aFmtULine)
|
||||
self.addAction(self.mainMenu.aFmtBold)
|
||||
self.addAction(self.mainMenu.aFmtBoldIt)
|
||||
self.addAction(self.mainMenu.aFmtDQuote)
|
||||
self.addAction(self.mainMenu.aFmtSQuote)
|
||||
self.addAction(self.mainMenu.aFmtHead1)
|
||||
@@ -972,10 +1014,19 @@ class GuiMain(QMainWindow):
|
||||
self.addAction(self.mainMenu.aFmtHead4)
|
||||
self.addAction(self.mainMenu.aFmtComment)
|
||||
self.addAction(self.mainMenu.aFmtNoFormat)
|
||||
|
||||
# View
|
||||
self.addAction(self.mainMenu.aZenMode)
|
||||
self.addAction(self.mainMenu.aFullScreen)
|
||||
|
||||
# Tools
|
||||
self.addAction(self.mainMenu.aSpellCheck)
|
||||
self.addAction(self.mainMenu.aReRunSpell)
|
||||
self.addAction(self.mainMenu.aPreferences)
|
||||
|
||||
# Help
|
||||
self.addAction(self.mainMenu.aHelp)
|
||||
|
||||
return True
|
||||
|
||||
def _setWindowTitle(self, projName=None):
|
||||
@@ -1075,8 +1126,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()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 14298de4d9524:f7e2d9f330615:f6622b4617424:John Smith
|
||||
%%~ 14298de4d9524:f7e2d9f330615:f6622b4617424:CHARACTER:NOTE:John Smith
|
||||
# John Smith
|
||||
|
||||
@tag: John
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 53b69b83cdafc:7031beac91f75:Title Page
|
||||
%%~ 53b69b83cdafc:7031beac91f75:NOVEL:TITLE:Title Page
|
||||
# My Novel
|
||||
|
||||
**By Jane Doh**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 5eaea4e8cdee8:15c4492bd5107:Mars
|
||||
%%~ 5eaea4e8cdee8:15c4492bd5107:WORLD:NOTE:Mars
|
||||
# Mars
|
||||
|
||||
@tag: Mars
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 636b6aa9b697b:e7ded148d6e4a:7031beac91f75:Making a Scene
|
||||
%%~ 636b6aa9b697b:e7ded148d6e4a:7031beac91f75:NOVEL:SCENE:Making a Scene
|
||||
### Making a Scene
|
||||
|
||||
@pov: Jane
|
||||
@@ -7,13 +7,15 @@
|
||||
|
||||
A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree.
|
||||
|
||||
Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, _italic_ and __underscore__.
|
||||
Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, *italic* and ***bold italic***. You can also ~~strike through~~ text.
|
||||
|
||||
In addition, the editor supports automatic formatting of “quotes”, both double and ‘single’. Depending on the syntax highlighter, these can be in different colours.
|
||||
|
||||
If you have the need for it, you can also add text that can be automatically replaced by other text when you generate a preview or export the project. Now, let’s auto-replace this A with <A>, and this C with <C>. While <E> is just <E>. Press Ctrl+R to see what this looks like in the view pane.
|
||||
|
||||
The editor also supports non breaking spaces, and the spell checker accepts long dashes—like this—as valid word separators.
|
||||
The editor also supports non breaking spaces, and the spell checker accepts long dashes—like this—as valid word separators. Regular dashes are also supported – and can be automatically inserted when typing two hyphens.
|
||||
|
||||
Thin spaces and thin non-breaking spaces are also supported from the Insert menu, and can be used to separate numbers from their units, like: 25 kg.
|
||||
|
||||
#### Some Section Here
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 6a2d6d5f4f401:e7ded148d6e4a:7031beac91f75:Chapter One
|
||||
%%~ 6a2d6d5f4f401:e7ded148d6e4a:7031beac91f75:NOVEL:CHAPTER:Chapter One
|
||||
## So it Begins
|
||||
|
||||
@pov: Jane
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 88706ddc78b1b:e7ded148d6e4a:7031beac91f75:Chapter Two
|
||||
%%~ 88706ddc78b1b:e7ded148d6e4a:7031beac91f75:NOVEL:CHAPTER:Chapter Two
|
||||
## Where has John Gone?
|
||||
|
||||
@pov: Jane
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 96b68994dfa3d:e7ded148d6e4a:7031beac91f75:A Note on Structure
|
||||
%%~ 96b68994dfa3d:e7ded148d6e4a:7031beac91f75:NOVEL:NOTE:A Note on Structure
|
||||
# A Note on Structure
|
||||
|
||||
This file is just a note. You can save notes anywhere you like in the project tree. Notes can be filtered out when you export the project.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 974e400180a99:7031beac91f75:Page
|
||||
%%~ 974e400180a99:7031beac91f75:NOVEL:PAGE:Page
|
||||
This is a plain page with some text on it.
|
||||
|
||||
This file should receive no special formatting, but the text will always be left aligned and the content will always start on a fresh page when the project is exported.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ ae7339df26ded:e7ded148d6e4a:7031beac91f75:We Found John!
|
||||
%%~ ae7339df26ded:e7ded148d6e4a:7031beac91f75:NOVEL:SCENE:We Found John!
|
||||
### We Found John!
|
||||
|
||||
@pov: John
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ b3e74dbc1f584:15c4492bd5107:Earth
|
||||
%%~ b3e74dbc1f584:15c4492bd5107:WORLD:NOTE:Earth
|
||||
# Earth
|
||||
|
||||
@tag: Earth
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ b8136a5a774a0:98acd8c76c93a:Delete Me!
|
||||
%%~ b8136a5a774a0:98acd8c76c93a:NOVEL:SCENE:Delete Me!
|
||||
### Delete Me!
|
||||
|
||||
This scene is trash.
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ ba8a28a246524:e7ded148d6e4a:7031beac91f75:Interlude
|
||||
%%~ ba8a28a246524:e7ded148d6e4a:7031beac91f75:NOVEL:UNNUMBERED:Interlude
|
||||
## Interlude
|
||||
|
||||
% Notice that this is a file with the flag ‘N.Un’. The ‘N’ means it’s a novel file, and the ‘Un’ means it’s an unnumbered chapter. Unnumbered chapters can be treated separately from numbered chapters during export. Perfect for when you want to add an interlude, or for a prologue or epilogue.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ bb2c23b3c42cc:f7e2d9f330615:f6622b4617424:Jane Smith
|
||||
%%~ bb2c23b3c42cc:f7e2d9f330615:f6622b4617424:CHARACTER:NOTE:Jane Smith
|
||||
# Jane Smith
|
||||
|
||||
@tag: Jane
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ bc0cbd2a407f3:e7ded148d6e4a:7031beac91f75:Another Scene
|
||||
%%~ bc0cbd2a407f3:e7ded148d6e4a:7031beac91f75:NOVEL:SCENE:Another Scene
|
||||
### Another Scene
|
||||
|
||||
@pov: John
|
||||
@@ -14,6 +14,3 @@ In fact, if you wish, you can add all the scenes in the chapter file too. All no
|
||||
@location: Earth
|
||||
|
||||
This is a second scene in the same file as the previous scene. You can always split the files up later.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ edca4be2fcaf8:7031beac91f75:Part 1
|
||||
%%~ edca4be2fcaf8:7031beac91f75:NOVEL:PARTITION:Part One
|
||||
# Part One
|
||||
|
||||
The first part.
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ f1471bef9f2ae:15c4492bd5107:Space
|
||||
%%~ f1471bef9f2ae:15c4492bd5107:WORLD:NOTE:Space
|
||||
# Space
|
||||
|
||||
@tag: Space
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.8" hexVersion="0x000800f0" fileVersion="1.1" timeStamp="2020-06-11 22:21:20">
|
||||
<novelWriterXML appVersion="0.9.0rc1" hexVersion="0x000900c1" fileVersion="1.1" timeStamp="2020-06-19 19:50:54">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
<saveCount>298</saveCount>
|
||||
<autoCount>42</autoCount>
|
||||
<editTime>5113</editTime>
|
||||
<saveCount>407</saveCount>
|
||||
<autoCount>71</autoCount>
|
||||
<editTime>15118</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
@@ -15,7 +15,7 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>636b6aa9b697b</lastEdited>
|
||||
<lastViewed>bb2c23b3c42cc</lastViewed>
|
||||
<lastWordCount>914</lastWordCount>
|
||||
<lastWordCount>967</lastWordCount>
|
||||
<autoReplace>
|
||||
<A>B</A>
|
||||
<B>E</B>
|
||||
@@ -114,10 +114,10 @@
|
||||
<status>1st Draft</status>
|
||||
<exported>True</exported>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>1199</charCount>
|
||||
<wordCount>216</wordCount>
|
||||
<paraCount>7</paraCount>
|
||||
<cursorPos>28</cursorPos>
|
||||
<charCount>1483</charCount>
|
||||
<wordCount>263</wordCount>
|
||||
<paraCount>8</paraCount>
|
||||
<cursorPos>1086</cursorPos>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||
<name>Another Scene</name>
|
||||
@@ -129,7 +129,7 @@
|
||||
<charCount>476</charCount>
|
||||
<wordCount>93</wordCount>
|
||||
<paraCount>3</paraCount>
|
||||
<cursorPos>551</cursorPos>
|
||||
<cursorPos>428</cursorPos>
|
||||
</item>
|
||||
<item handle="ba8a28a246524" order="3" parent="e7ded148d6e4a">
|
||||
<name>Interlude</name>
|
||||
@@ -274,9 +274,9 @@
|
||||
<status>New</status>
|
||||
<exported>True</exported>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>0</charCount>
|
||||
<wordCount>0</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<charCount>30</charCount>
|
||||
<wordCount>6</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>36</cursorPos>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -6,7 +6,7 @@ with open("README.md", "r") as inFile:
|
||||
|
||||
setuptools.setup(
|
||||
name = "novelWriter",
|
||||
version = "0.8",
|
||||
version = "0.9rc1",
|
||||
author = "Veronica Berglyd Olsen",
|
||||
author_email = "code@vkbo.net",
|
||||
description = "A markdown-like document editor for writing novels",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 04468803b92e1:60bdf227455cc:Ancient Europe
|
||||
%%~ 04468803b92e1:60bdf227455cc:WORLD:NOTE:Ancient Europe
|
||||
# Ancient Europe
|
||||
|
||||
@tag: Europe
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 2426c6f0ca922:6c6afb1247750:Main
|
||||
%%~ 2426c6f0ca922:6c6afb1247750:PLOT:NOTE:Main
|
||||
# Main Plot
|
||||
|
||||
@tag: Main
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 441420a886d82:6bd935d2490cd:b3643d0f92e32:Chapter Two
|
||||
%%~ 441420a886d82:6bd935d2490cd:b3643d0f92e32:NOVEL:CHAPTER:Chapter Two
|
||||
## Chapter Two
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 47666c91c7ccf:6bd935d2490cd:b3643d0f92e32:Scene Five
|
||||
%%~ 47666c91c7ccf:6bd935d2490cd:b3643d0f92e32:NOVEL:SCENE:Scene Five
|
||||
### Scene Five
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 4c4f28287af27:67a8707f2f249:Mr. Nobody
|
||||
%%~ 4c4f28287af27:67a8707f2f249:CHARACTER:NOTE:Mr. Nobody
|
||||
# Nobody Owens
|
||||
|
||||
@tag: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 7a992350f3eb6:b3643d0f92e32:Lorem Ipusm
|
||||
%%~ 7a992350f3eb6:b3643d0f92e32:NOVEL:TITLE:Lorem Ipusm
|
||||
# Lorem Ipsum
|
||||
|
||||
**By lipsum.com**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 846352075de7d:b3643d0f92e32:Interlude
|
||||
%%~ 846352075de7d:b3643d0f92e32:NOVEL:BOOK:Interlude
|
||||
## Why do we use it?
|
||||
|
||||
% Exctracted from the lipsum.com website.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 88243afbe5ed8:45e6b01ca35c1:b3643d0f92e32:Scene One
|
||||
%%~ 88243afbe5ed8:45e6b01ca35c1:b3643d0f92e32:NOVEL:SCENE:Scene One
|
||||
### Scene One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%~ 88d59a277361b:b3643d0f92e32:Prologue
|
||||
%%~ 88d59a277361b:b3643d0f92e32:NOVEL:UNNUMBERED:Prologue
|
||||
## Prologue
|
||||
|
||||
% Synopsis:Explanation from the lipsum.com website.
|
||||
|
||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
*Lorem Ipsum* is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 8c58a65414c23:b3643d0f92e32:Front Matter
|
||||
%%~ 8c58a65414c23:b3643d0f92e32:NOVEL:PAGE:Front Matter
|
||||
% Exctracted from the lipsum.com website.
|
||||
|
||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ db7e733775d4d:b3643d0f92e32:Act One
|
||||
%%~ db7e733775d4d:b3643d0f92e32:NOVEL:PARTITION:Act One
|
||||
# Act One
|
||||
|
||||
“Fusce maximus felis libero”
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ eb103bc70c90c:6bd935d2490cd:b3643d0f92e32:Scene Three
|
||||
%%~ eb103bc70c90c:6bd935d2490cd:b3643d0f92e32:NOVEL:SCENE:Scene Three
|
||||
### Scene Three
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ f8c0562e50f1b:6bd935d2490cd:b3643d0f92e32:Scene Four
|
||||
%%~ f8c0562e50f1b:6bd935d2490cd:b3643d0f92e32:NOVEL:SCENE:Scene Four
|
||||
### Scene Four
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ f96ec11c6a3da:45e6b01ca35c1:b3643d0f92e32:Scene Two
|
||||
%%~ f96ec11c6a3da:45e6b01ca35c1:b3643d0f92e32:NOVEL:SCENE:Scene Two
|
||||
### Scene Two
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ fb609cd8319dc:45e6b01ca35c1:b3643d0f92e32:Chapter One
|
||||
%%~ fb609cd8319dc:45e6b01ca35c1:b3643d0f92e32:NOVEL:CHAPTER:Chapter One
|
||||
## Chapter One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.7.0rc1" hexVersion="0x000700c1" fileVersion="1.1" saveCount="6" autoCount="20" timeStamp="2020-05-30 12:03:01" editTime="1408">
|
||||
<novelWriterXML appVersion="0.9.0rc1" hexVersion="0x000900c1" fileVersion="1.1" timeStamp="2020-06-13 00:37:49">
|
||||
<project>
|
||||
<name>Lorem Ipsum</name>
|
||||
<title>Lorem Ipsum</title>
|
||||
<author>lipsum.com</author>
|
||||
<backup>False</backup>
|
||||
<saveCount>7</saveCount>
|
||||
<autoCount>21</autoCount>
|
||||
<editTime>1459</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<spellCheck>False</spellCheck>
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>88d59a277361b</lastEdited>
|
||||
<lastEdited>04468803b92e1</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastWordCount>3397</lastWordCount>
|
||||
<lastWordCount>3847</lastWordCount>
|
||||
<autoReplace>
|
||||
<Rep1>Replace Text 1</Rep1>
|
||||
<Rep2>Replace Text 2</Rep2>
|
||||
@@ -22,9 +25,6 @@
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<withSynopsis>False</withSynopsis>
|
||||
<withComments>False</withComments>
|
||||
<withKeywords>False</withKeywords>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
@@ -81,7 +81,7 @@
|
||||
<charCount>584</charCount>
|
||||
<wordCount>92</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>35</cursorPos>
|
||||
<cursorPos>79</cursorPos>
|
||||
</item>
|
||||
<item handle="db7e733775d4d" order="3" parent="b3643d0f92e32">
|
||||
<name>Act One</name>
|
||||
@@ -238,9 +238,9 @@
|
||||
<status>Main</status>
|
||||
<exported>True</exported>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>9</charCount>
|
||||
<wordCount>2</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<charCount>1369</charCount>
|
||||
<wordCount>195</wordCount>
|
||||
<paraCount>2</paraCount>
|
||||
<cursorPos>1387</cursorPos>
|
||||
</item>
|
||||
<item handle="60bdf227455cc" order="3" parent="None">
|
||||
@@ -257,9 +257,9 @@
|
||||
<status>Minor</status>
|
||||
<exported>True</exported>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>14</charCount>
|
||||
<wordCount>2</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<charCount>1770</charCount>
|
||||
<wordCount>259</wordCount>
|
||||
<paraCount>3</paraCount>
|
||||
<cursorPos>1792</cursorPos>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -14,7 +14,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
|
||||
## Prologue
|
||||
|
||||
|
||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
*Lorem Ipsum* is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
|
||||
# Act One
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
|
||||
|
||||
% Synopsis:Explanation from the lipsum.com website.
|
||||
|
||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
*Lorem Ipsum* is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
|
||||
# Act One
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 0e17daca5f3e1:71ee45a3c0db9:New File
|
||||
%%~ 0e17daca5f3e1:71ee45a3c0db9:PLOT:NOTE:New File
|
||||
# Main Plot
|
||||
|
||||
@tag: MainPlot
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 1a6562590ef19:811786ad1ae74:New File
|
||||
%%~ 1a6562590ef19:811786ad1ae74:WORLD:NOTE:New File
|
||||
# Main Location
|
||||
|
||||
@tag: Home
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 31489056e0916:25fc0e7096fc6:73475cb40a568:New Scene
|
||||
%%~ 31489056e0916:25fc0e7096fc6:73475cb40a568:NOVEL:SCENE:New Scene
|
||||
# Novel
|
||||
|
||||
## Chapter
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 98010bd9270f9:44cb730c42048:New File
|
||||
%%~ 98010bd9270f9:44cb730c42048:CHARACTER:NOTE:New File
|
||||
# Jane Doe
|
||||
|
||||
@tag: Jane
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.8.0rc1" hexVersion="0x000800c1" fileVersion="1.1" timeStamp="2020-06-05 21:07:51">
|
||||
<novelWriterXML appVersion="0.9.0rc1" hexVersion="0x000900c1" fileVersion="1.1" timeStamp="2020-06-18 22:17:32">
|
||||
<project>
|
||||
<name>New Project</name>
|
||||
<title></title>
|
||||
@@ -11,9 +11,9 @@
|
||||
<doBackup>True</doBackup>
|
||||
<spellCheck>False</spellCheck>
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastEdited>31489056e0916</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<lastWordCount>59</lastWordCount>
|
||||
<autoReplace/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
@@ -57,9 +57,9 @@
|
||||
<status>Note</status>
|
||||
<exported>False</exported>
|
||||
<layout>PAGE</layout>
|
||||
<charCount>0</charCount>
|
||||
<wordCount>0</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<charCount>331</charCount>
|
||||
<wordCount>59</wordCount>
|
||||
<paraCount>2</paraCount>
|
||||
<cursorPos>0</cursorPos>
|
||||
</item>
|
||||
<item handle="44cb730c42048" order="1" parent="None">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 73475cb40a568:b3643d0f92e32:Chapter One
|
||||
%%~ 73475cb40a568:b3643d0f92e32:NOVEL:SCENE:Chapter One
|
||||
## Chapter One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 031b4af5197ec:0e17daca5f3e1:b3643d0f92e32:Scene One
|
||||
%%~ 031b4af5197ec:0e17daca5f3e1:b3643d0f92e32:NOVEL:SCENE:Scene One
|
||||
### Scene One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 25fc0e7096fc6:811786ad1ae74:b3643d0f92e32:Chapter One
|
||||
%%~ 25fc0e7096fc6:811786ad1ae74:b3643d0f92e32:NOVEL:CHAPTER:Chapter One
|
||||
## Chapter One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 2858dcd1057d3:0e17daca5f3e1:b3643d0f92e32:Scene Two
|
||||
%%~ 2858dcd1057d3:0e17daca5f3e1:b3643d0f92e32:NOVEL:SCENE:Scene Two
|
||||
### Scene Two
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 2fca346db6561:0e17daca5f3e1:b3643d0f92e32:Scene Two, Section Two
|
||||
%%~ 2fca346db6561:0e17daca5f3e1:b3643d0f92e32:NOVEL:SCENE:Scene Two, Section Two
|
||||
#### Scene Two, Section Two
|
||||
|
||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 31489056e0916:811786ad1ae74:b3643d0f92e32:Scene One
|
||||
%%~ 31489056e0916:811786ad1ae74:b3643d0f92e32:NOVEL:SCENE:Scene One
|
||||
### Scene One
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 41cfc0d1f2d12:0e17daca5f3e1:b3643d0f92e32:Scene One, Section Two
|
||||
%%~ 41cfc0d1f2d12:0e17daca5f3e1:b3643d0f92e32:NOVEL:SCENE:Scene One, Section Two
|
||||
#### Scene One, Section Two
|
||||
|
||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%%~ 98010bd9270f9:811786ad1ae74:b3643d0f92e32:Scene Two
|
||||
%%~ 98010bd9270f9:811786ad1ae74:b3643d0f92e32:NOVEL:SCENE:Scene Two
|
||||
### Scene Two
|
||||
|
||||
@pov: Bod
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -127,6 +127,11 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Add Some Text
|
||||
nwGUI.docEditor.replaceText("Hello World!")
|
||||
assert nwGUI.docEditor.getText() == "Hello World!"
|
||||
nwGUI.docEditor.replaceText("")
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.setFocus(2)
|
||||
for c in "# Main Location":
|
||||
@@ -273,6 +278,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||
nwGUI.mainConf.backupPath = nwTempGUI
|
||||
|
||||
projEdit = GuiProjectSettings(nwGUI, nwGUI.theProject)
|
||||
projEdit.show()
|
||||
qtbot.addWidget(projEdit)
|
||||
|
||||
projEdit.tabMain.editName.setText("")
|
||||
@@ -358,6 +364,7 @@ def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||
# Create new, save, open project
|
||||
nwGUI.theProject.projTree.setSeed(42)
|
||||
assert nwGUI.newProject(nwTempGUI, True)
|
||||
assert nwGUI.openDocument("31489056e0916")
|
||||
|
||||
itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "31489056e0916")
|
||||
qtbot.addWidget(itemEdit)
|
||||
@@ -374,7 +381,6 @@ def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||
|
||||
itemEdit.editExport.setChecked(False)
|
||||
assert not itemEdit.editExport.isChecked()
|
||||
|
||||
itemEdit._doSave()
|
||||
|
||||
itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "31489056e0916")
|
||||
@@ -382,9 +388,16 @@ def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
|
||||
assert itemEdit.editName.text() == "Just a Page"
|
||||
assert itemEdit.editStatus.currentData() == "Note"
|
||||
assert itemEdit.editLayout.currentData() == nwItemLayout.PAGE
|
||||
|
||||
itemEdit._doClose()
|
||||
|
||||
# Check that the header is updated
|
||||
nwGUI.docEditor.updateDocTitle("31489056e0916")
|
||||
assert nwGUI.docEditor.docHeader.theTitle.text() == "Novel › New Chapter › Just a Page"
|
||||
assert not nwGUI.docEditor.setCursorLine("where?")
|
||||
assert nwGUI.docEditor.setCursorLine(2)
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.docEditor.getCursorPosition() == 9
|
||||
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.saveProject()
|
||||
qtbot.wait(stepDelay)
|
||||
@@ -610,3 +623,136 @@ def testSplitTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
nwGUI.closeMain()
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDocAction(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
|
||||
|
||||
nwGUI = nw.main(["--testmode","--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
|
||||
qtbot.addWidget(nwGUI)
|
||||
nwGUI.show()
|
||||
qtbot.waitForWindowShown(nwGUI)
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
nwGUI.theProject.projTree.setSeed(42)
|
||||
assert nwGUI.openProject(nwLipsum)
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# CUT = 3
|
||||
# COPY = 4
|
||||
# PASTE = 5
|
||||
# SEL_ALL = 12
|
||||
# SEL_PARA = 13
|
||||
# FIND = 14
|
||||
# REPLACE = 15
|
||||
# GO_NEXT = 16
|
||||
# GO_PREV = 17
|
||||
# REPL_NEXT = 18
|
||||
|
||||
# Split By Chapter
|
||||
assert nwGUI.openDocument("4c4f28287af27")
|
||||
assert nwGUI.docEditor.setCursorPosition(30)
|
||||
|
||||
cleanText = nwGUI.docEditor.getText()[27:74]
|
||||
|
||||
# Bold
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLD)
|
||||
assert nwGUI.docEditor.getText()[27:78] == "**Pellentesque** nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLD)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Italic
|
||||
assert nwGUI.passDocumentAction(nwDocAction.ITALIC)
|
||||
assert nwGUI.docEditor.getText()[27:76] == "*Pellentesque* nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.ITALIC)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Bold-Italic
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLDITALIC)
|
||||
assert nwGUI.docEditor.getText()[27:80] == "***Pellentesque*** nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLDITALIC)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Strikethrough
|
||||
assert nwGUI.passDocumentAction(nwDocAction.STRIKE)
|
||||
assert nwGUI.docEditor.getText()[27:78] == "~~Pellentesque~~ nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.STRIKE)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Should get us back to plain
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLD)
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.ITALIC)
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.ITALIC)
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLD)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Equivalent of the above
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLDITALIC)
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.ITALIC)
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BOLD)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Double Quotes
|
||||
assert nwGUI.passDocumentAction(nwDocAction.D_QUOTE)
|
||||
assert nwGUI.docEditor.getText()[27:76] == "“Pellentesque” nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.UNDO)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Single Quotes
|
||||
assert nwGUI.passDocumentAction(nwDocAction.S_QUOTE)
|
||||
assert nwGUI.docEditor.getText()[27:76] == "‘Pellentesque’ nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.UNDO)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Block Formats
|
||||
assert nwGUI.docEditor.setCursorPosition(30)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_H1)
|
||||
assert nwGUI.docEditor.getText()[27:76] == "# Pellentesque nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_H2)
|
||||
assert nwGUI.docEditor.getText()[27:77] == "## Pellentesque nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_H3)
|
||||
assert nwGUI.docEditor.getText()[27:78] == "### Pellentesque nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_H4)
|
||||
assert nwGUI.docEditor.getText()[27:79] == "#### Pellentesque nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_TXT)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_COM)
|
||||
assert nwGUI.docEditor.getText()[27:76] == "% Pellentesque nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.BLOCK_TXT)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Undo/Redo
|
||||
assert nwGUI.passDocumentAction(nwDocAction.UNDO)
|
||||
assert nwGUI.docEditor.getText()[27:76] == "% Pellentesque nec erat ut nulla posuere commodo."
|
||||
qtbot.wait(stepDelay)
|
||||
assert nwGUI.passDocumentAction(nwDocAction.REDO)
|
||||
assert nwGUI.docEditor.getText()[27:74] == cleanText
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
nwGUI.closeMain()
|
||||
|
||||