Merge branch 'main' into i18n-de_DE-created

This commit is contained in:
Veronica Berglyd Olsen
2022-06-15 17:18:47 +02:00
committed by GitHub
79 changed files with 2985 additions and 2317 deletions
+103 -47
View File
@@ -2,75 +2,131 @@
## Our Pledge ## Our Pledge
In the interest of fostering an open and welcoming environment, we as We as members, contributors, and leaders pledge to make participation in our
contributors and maintainers pledge to making participation in our project and community a harassment-free experience for everyone, regardless of age, body
our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender
size, disability, ethnicity, sex characteristics, gender identity and expression, identity and expression, level of experience, education, socio-economic status,
level of experience, education, socio-economic status, nationality, personal nationality, personal appearance, race, caste, color, religion, or sexual
appearance, race, religion, or sexual identity and orientation. identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards ## Our Standards
Examples of behavior that contributes to creating a positive environment Examples of behavior that contributes to a positive environment for our
include: community include:
* Using welcoming and inclusive language * Demonstrating empathy and kindness toward other people
* Being respectful of differing viewpoints and experiences * Being respectful of differing opinions, viewpoints, and experiences
* Gracefully accepting constructive criticism * Giving and gracefully accepting constructive feedback
* Focusing on what is best for the community * Accepting responsibility and apologizing to those affected by our mistakes,
* Showing empathy towards other community members and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior include:
* The use of sexualized language or imagery and unwelcome sexual attention or * The use of sexualized language or imagery, and sexual attention or advances of
advances any kind
* Trolling, insulting/derogatory comments, and personal or political attacks * Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment * Public or private harassment
* Publishing others' private information, such as a physical or electronic * Publishing others' private information, such as a physical or email address,
address, without explicit permission without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a * Other conduct which could reasonably be considered inappropriate in a
professional setting professional setting
## Our Responsibilities ## Enforcement Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable Community leaders are responsible for clarifying and enforcing our standards of
behavior and are expected to take appropriate and fair corrective action in acceptable behavior and will take appropriate and fair corrective action in
response to any instances of unacceptable behavior. response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Project maintainers have the right and responsibility to remove, edit, or Community leaders have the right and responsibility to remove, edit, or reject
reject comments, commits, code, wiki edits, issues, and other contributions comments, commits, code, wiki edits, issues, and other contributions that are
that are not aligned to this Code of Conduct, or to ban temporarily or not aligned to this Code of Conduct, and will communicate reasons for moderation
permanently any contributor for other behaviors that they deem inappropriate, decisions when appropriate.
threatening, offensive, or harmful.
## Scope ## Scope
This Code of Conduct applies both within project spaces and in public spaces This Code of Conduct applies within all community spaces, and also applies when
when an individual is representing the project or its community. Examples of an individual is officially representing the community in public spaces.
representing a project or community include using an official project e-mail Examples of representing our community include using an official e-mail address,
address, posting via an official social media account, or acting as an appointed posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be representative at an online or offline event.
further defined and clarified by project maintainers.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project leader at issues (at) novelwriter.io. All reported to the community leaders responsible for enforcement at issues (at)
complaints will be reviewed and investigated and will result in a response that novelwriter.io. All complaints will be reviewed and investigated promptly and
is deemed necessary and appropriate to the circumstances. The project team is fairly.
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good All community leaders are obligated to respect the privacy and security of the
faith may face temporary or permanent repercussions as determined by other reporter of any incident.
members of the project's leadership.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, This Code of Conduct is adapted from the [Contributor Covenant][homepage],
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org [homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
For answers to common questions about this code of conduct, see [Mozilla CoC]: https://github.com/mozilla/diversity
https://www.contributor-covenant.org/faq [FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
+2 -4
View File
@@ -25,7 +25,7 @@ The main shorcuts are as follows:
":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`." ":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`."
":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer." ":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer."
":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer." ":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer."
":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor." ":kbd:`Ctrl`:kbd:`.`", "Open the context menu in the project tree or the document editor."
":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog." ":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog."
":kbd:`Ctrl`:kbd:`/`", "Toggle block format as comment." ":kbd:`Ctrl`:kbd:`/`", "Toggle block format as comment."
":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor." ":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor."
@@ -42,13 +42,12 @@ The main shorcuts are as follows:
":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)." ":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)."
":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard." ":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard."
":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor." ":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor."
":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings."
":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected." ":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected."
":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document." ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document."
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)" ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)." ":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`." ":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
":kbd:`Ctrl`:kbd:`N`", "Create new document." ":kbd:`Ctrl`:kbd:`N`", "Create new project item."
":kbd:`Ctrl`:kbd:`O`", "Open selected document." ":kbd:`Ctrl`:kbd:`O`", "Open selected document."
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter." ":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing." ":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
@@ -72,7 +71,6 @@ The main shorcuts are as follows:
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
@@ -83,3 +83,8 @@ view_build = typ_export.svg
view_editor = mixed_edit.svg view_editor = mixed_edit.svg
view_novel = typ_book-grey.svg view_novel = typ_book-grey.svg
view_outline = typ_puzzle-outline.svg view_outline = typ_puzzle-outline.svg
deco_doc_h1 = nw_deco-h1.svg
deco_doc_h2 = nw_deco-h2.svg
deco_doc_h3 = nw_deco-h3.svg
deco_doc_h4 = nw_deco-h4.svg
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="4"
height="24"
viewBox="0 0 4 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#848484;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="0"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 902 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="4"
height="24"
viewBox="0 0 4 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#99cc99;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="0"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 902 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="20"
height="24"
viewBox="0 0 20 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#d64848;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="16"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 905 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="36"
height="24"
viewBox="0 0 36 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#6699cc;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="32"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 905 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="52"
height="24"
viewBox="0 0 52 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="48"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 905 B

@@ -83,3 +83,8 @@ view_build = typ_export.svg
view_editor = mixed_edit.svg view_editor = mixed_edit.svg
view_novel = typ_book-grey.svg view_novel = typ_book-grey.svg
view_outline = typ_puzzle-outline.svg view_outline = typ_puzzle-outline.svg
deco_doc_h1 = nw_deco-h1.svg
deco_doc_h2 = nw_deco-h2.svg
deco_doc_h3 = nw_deco-h3.svg
deco_doc_h4 = nw_deco-h4.svg
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="4"
height="24"
viewBox="0 0 4 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#848484;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="0"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 902 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="4"
height="24"
viewBox="0 0 4 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#718c00;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="0"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 902 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="20"
height="24"
viewBox="0 0 20 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#c82829;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="16"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 905 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="36"
height="24"
viewBox="0 0 36 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#4271ae;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="32"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 905 B

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="52"
height="24"
viewBox="0 0 52 24"
id="svg2161"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata2167">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs2165" />
<rect
style="fill:#333333;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
id="rect1955"
width="4"
height="22"
x="48"
y="1"
rx="2"
ry="2" />
</svg>

After

Width:  |  Height:  |  Size: 905 B

+9
View File
@@ -57,6 +57,15 @@ class nwRegEx:
# END Class nwRegEx # END Class nwRegEx
class nwHeaders:
H_VALID = ("H0", "H1", "H2", "H3", "H4")
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
TT_NONE = "T000000"
# END Class nwHeaders
class nwFiles: class nwFiles:
PROJ_FILE = "nwProject.nwx" PROJ_FILE = "nwProject.nwx"
+8 -17
View File
@@ -34,7 +34,7 @@ from time import time
from novelwriter.enum import nwItemType, nwItemLayout from novelwriter.enum import nwItemType, nwItemLayout
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.constants import nwFiles, nwKeyWords, nwUnicode from novelwriter.constants import nwFiles, nwKeyWords, nwUnicode, nwHeaders
from novelwriter.core.document import NWDoc from novelwriter.core.document import NWDoc
from novelwriter.common import ( from novelwriter.common import (
checkInt, isHandle, isItemClass, isTitleTag, jsonEncode checkInt, isHandle, isItemClass, isTitleTag, jsonEncode
@@ -42,10 +42,6 @@ from novelwriter.common import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
H_VALID = ("H0", "H1", "H2", "H3", "H4")
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
TT_NONE = "T000000"
class NWIndex: class NWIndex:
"""This class holds the entire index for a given project. The index """This class holds the entire index for a given project. The index
@@ -477,7 +473,7 @@ class NWIndex:
""" """
hCount = [0, 0, 0, 0, 0] hCount = [0, 0, 0, 0, 0]
for _, _, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl): for _, _, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
iLevel = H_LEVEL.get(hItem.level, 0) iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
hCount[iLevel] += 1 hCount[iLevel] += 1
return hCount return hCount
@@ -502,11 +498,6 @@ class NWIndex:
""" """
return self._itemIndex.mainItemHeader(tHandle) return self._itemIndex.mainItemHeader(tHandle)
def getHandleHeaderIntLevel(self, tHandle):
"""Get the integer header level of the first header of a handle.
"""
return H_LEVEL.get(self._itemIndex.mainItemHeader(tHandle), 0)
def getTableOfContents(self, maxDepth, skipExcl=True): def getTableOfContents(self, maxDepth, skipExcl=True):
"""Generate a table of contents up to a maximum depth. """Generate a table of contents up to a maximum depth.
""" """
@@ -515,7 +506,7 @@ class NWIndex:
pKey = None pKey = None
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl): for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
tKey = f"{tHandle}:{sTitle}" tKey = f"{tHandle}:{sTitle}"
iLevel = H_LEVEL.get(hItem.level, 0) iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
if iLevel > maxDepth: if iLevel > maxDepth:
if pKey in tData: if pKey in tData:
tData[pKey]["words"] += hItem.wordCount tData[pKey]["words"] += hItem.wordCount
@@ -665,7 +656,7 @@ class TagsIndex:
""" """
if tagKey in self._tags: if tagKey in self._tags:
return self._tags.get(tagKey).get("heading") return self._tags.get(tagKey).get("heading")
return TT_NONE return nwHeaders.TT_NONE
def tagClass(self, tagKey): def tagClass(self, tagKey):
"""Get the class of a given tag. """Get the class of a given tag.
@@ -911,7 +902,7 @@ class IndexItem:
self._index = 0 self._index = 0
# Add a placeholder heading # Add a placeholder heading
self._headings[TT_NONE] = IndexHeading(TT_NONE) self._headings[nwHeaders.TT_NONE] = IndexHeading(nwHeaders.TT_NONE)
return return
@@ -945,8 +936,8 @@ class IndexItem:
"""Add a heading to the item. Also remove the placeholder entry """Add a heading to the item. Also remove the placeholder entry
if it exists. if it exists.
""" """
if TT_NONE in self._headings: if nwHeaders.TT_NONE in self._headings:
self._headings.pop(TT_NONE) self._headings.pop(nwHeaders.TT_NONE)
self._headings[tHeading.key] = tHeading self._headings[tHeading.key] = tHeading
return return
@@ -1115,7 +1106,7 @@ class IndexHeading:
def setLevel(self, level): def setLevel(self, level):
"""Set the level of the header if it's a valid value. """Set the level of the header if it's a valid value.
""" """
if level in H_VALID: if level in nwHeaders.H_VALID:
self._level = level self._level = level
return return
+20 -2
View File
@@ -28,6 +28,8 @@ import os
import json import json
import logging import logging
from enum import Enum
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.common import checkBool, checkFloat, checkInt, checkString from novelwriter.common import checkBool, checkFloat, checkInt, checkString
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
@@ -56,7 +58,8 @@ VALID_MAP = {
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2", "winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble" "widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble"
}, },
"GuiWordList": {"winWidth", "winHeight"} "GuiWordList": {"winWidth", "winHeight"},
"GuiNovelView": {"lastCol"},
} }
@@ -137,7 +140,10 @@ class OptionState():
if group not in self._theState: if group not in self._theState:
self._theState[group] = {} self._theState[group] = {}
self._theState[group][name] = value if isinstance(value, Enum):
self._theState[group][name] = value.name
else:
self._theState[group][name] = value
return True return True
@@ -186,4 +192,16 @@ class OptionState():
return checkBool(self._theState[group].get(name, default), default) return checkBool(self._theState[group].get(name, default), default)
return default return default
def getEnum(self, group, name, lookup, default):
"""Return the value mapped to an enum. Otherwise return the
default value
"""
if issubclass(lookup, Enum):
if group in self._theState:
if name in self._theState[group]:
value = self._theState[group][name]
if value in lookup.__members__:
return lookup[value]
return default
# END Class OptionState # END Class OptionState
+113 -109
View File
@@ -45,7 +45,7 @@ from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.common import ( from novelwriter.common import (
checkString, checkBool, checkInt, isHandle, formatTimeStamp, checkString, checkBool, checkInt, isHandle, formatTimeStamp,
makeFileNameSafe, hexToInt, simplified makeFileNameSafe, hexToInt, minmax, simplified
) )
from novelwriter.constants import trConst, nwFiles, nwLabels from novelwriter.constants import trConst, nwFiles, nwLabels
@@ -56,11 +56,11 @@ class NWProject():
FILE_VERSION = "1.4" # The current project file format version FILE_VERSION = "1.4" # The current project file format version
def __init__(self, theParent): def __init__(self, mainGui):
# Internal # Internal
self.theParent = theParent self.mainConf = novelwriter.CONFIG
self.mainConf = novelwriter.CONFIG self.mainGui = mainGui
# Core Elements # Core Elements
self._optState = OptionState(self) # Project-specific GUI options self._optState = OptionState(self) # Project-specific GUI options
@@ -102,6 +102,8 @@ class NWProject():
self.importItems = None # Note file importance values self.importItems = None # Note file importance values
self.lastEdited = None # The handle of the last file to be edited self.lastEdited = None # The handle of the last file to be edited
self.lastViewed = None # The handle of the last file to be viewed self.lastViewed = None # The handle of the last file to be viewed
self.lastNovel = None # The handle of the last novel root viewed
self.lastOutline = None # The handle of the last outline root viewed
self.lastWCount = 0 # The project word count from last session self.lastWCount = 0 # The project word count from last session
self.lastNovelWC = 0 # The novel files word count from last session self.lastNovelWC = 0 # The novel files word count from last session
self.lastNotesWC = 0 # The note files word count from last session self.lastNotesWC = 0 # The note files word count from last session
@@ -154,6 +156,8 @@ class NWProject():
def newFolder(self, label, pHandle): def newFolder(self, label, pHandle):
"""Add a new folder with a given label and parent item. """Add a new folder with a given label and parent item.
""" """
if pHandle not in self._projTree:
return None
newItem = NWItem(self) newItem = NWItem(self)
newItem.setName(label) newItem.setName(label)
newItem.setType(nwItemType.FOLDER) newItem.setType(nwItemType.FOLDER)
@@ -164,6 +168,8 @@ class NWProject():
def newFile(self, label, pHandle): def newFile(self, label, pHandle):
"""Add a new file with a given label and parent item. """Add a new file with a given label and parent item.
""" """
if pHandle not in self._projTree:
return None
newItem = NWItem(self) newItem = NWItem(self)
newItem.setName(label) newItem.setName(label)
newItem.setType(nwItemType.FILE) newItem.setType(nwItemType.FILE)
@@ -171,6 +177,32 @@ class NWProject():
self._projTree.updateItemData(newItem.itemHandle) self._projTree.updateItemData(newItem.itemHandle)
return newItem.itemHandle return newItem.itemHandle
def writeNewFile(self, tHandle, hLevel, isDocument):
"""Write content to a new document after it is created. This
will not run if the file exists and is not empty.
"""
tItem = self._projTree[tHandle]
if tItem is None:
return False
if tItem.itemType != nwItemType.FILE:
return False
newDoc = NWDoc(self, tHandle)
if newDoc.readDocument().strip():
return False
hshText = "#"*minmax(hLevel, 1, 4)
newText = f"{hshText} {tItem.itemName}\n\n"
if tItem.isNovelLike() and isDocument:
tItem.setLayout(nwItemLayout.DOCUMENT)
else:
tItem.setLayout(nwItemLayout.NOTE)
newDoc.writeDocument(newText)
self._projIndex.scanText(tHandle, newText)
return True
def trashFolder(self): def trashFolder(self):
"""Add the special trash root folder to the project. """Add the special trash root folder to the project.
""" """
@@ -384,7 +416,7 @@ class NWProject():
if not os.path.isfile(fileName): if not os.path.isfile(fileName):
fileName = os.path.join(fileName, nwFiles.PROJ_FILE) fileName = os.path.join(fileName, nwFiles.PROJ_FILE)
if not os.path.isfile(fileName): if not os.path.isfile(fileName):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"File not found: {0}" "File not found: {0}"
).format(fileName), nwAlert.ERROR) ).format(fileName), nwAlert.ERROR)
return False return False
@@ -408,7 +440,7 @@ class NWProject():
legacyList = [] # Cleanup is done later legacyList = [] # Cleanup is done later
for projItem in os.listdir(self.projPath): for projItem in os.listdir(self.projPath):
logger.verbose("Project contains: %s", projItem) logger.verbose("Project contains: %s", projItem)
if projItem.startswith("data_"): if projItem.startswith("data_") and len(projItem) == 6:
legacyList.append(projItem) legacyList.append(projItem)
# Project Lock # Project Lock
@@ -435,20 +467,20 @@ class NWProject():
try: try:
nwXML = etree.parse(fileName) nwXML = etree.parse(fileName)
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to parse project xml." "Failed to parse project xml."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
# Trying to open backup file instead # Trying to open backup file instead
backFile = fileName[:-3]+"bak" backFile = fileName[:-3]+"bak"
if os.path.isfile(backFile): if os.path.isfile(backFile):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Attempting to open backup project file instead." "Attempting to open backup project file instead."
), nwAlert.INFO) ), nwAlert.INFO)
try: try:
nwXML = etree.parse(backFile) nwXML = etree.parse(backFile)
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to parse project xml." "Failed to parse project xml."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
self.clearProject() self.clearProject()
@@ -470,8 +502,8 @@ class NWProject():
# Check File Type # Check File Type
# =============== # ===============
if not nwxRoot == "novelWriterXML": if nwxRoot != "novelWriterXML":
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Project file does not appear to be a novelWriterXML file." "Project file does not appear to be a novelWriterXML file."
), nwAlert.ERROR) ), nwAlert.ERROR)
self.clearProject() self.clearProject()
@@ -497,7 +529,7 @@ class NWProject():
# stored and handled. Introduced in version 1.7. # stored and handled. Introduced in version 1.7.
if fileVersion not in ("1.0", "1.1", "1.2", "1.3", "1.4"): if fileVersion not in ("1.0", "1.1", "1.2", "1.3", "1.4"):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Unknown or unsupported novelWriter project file format. " "Unknown or unsupported novelWriter project file format. "
"The project cannot be opened by this version of novelWriter. " "The project cannot be opened by this version of novelWriter. "
"The file was saved with novelWriter version {0}." "The file was saved with novelWriter version {0}."
@@ -506,7 +538,7 @@ class NWProject():
return False return False
if fileVersion != self.FILE_VERSION: if fileVersion != self.FILE_VERSION:
msgYes = self.theParent.askQuestion( msgYes = self.mainGui.askQuestion(
self.tr("File Version"), self.tr("File Version"),
self.tr( self.tr(
"The file format of your project is about to be updated. " "The file format of your project is about to be updated. "
@@ -522,7 +554,7 @@ class NWProject():
# ========================= # =========================
if hexToInt(hexVersion) > hexToInt(novelwriter.__hexversion__): if hexToInt(hexVersion) > hexToInt(novelwriter.__hexversion__):
msgYes = self.theParent.askQuestion( msgYes = self.mainGui.askQuestion(
self.tr("Version Conflict"), self.tr("Version Conflict"),
self.tr( self.tr(
"This project was saved by a newer version of " "This project was saved by a newer version of "
@@ -582,6 +614,10 @@ class NWProject():
self.lastEdited = checkString(xItem.text, None, True) self.lastEdited = checkString(xItem.text, None, True)
elif xItem.tag == "lastViewed": elif xItem.tag == "lastViewed":
self.lastViewed = checkString(xItem.text, None, True) self.lastViewed = checkString(xItem.text, None, True)
elif xItem.tag == "lastNovel":
self.lastNovel = checkString(xItem.text, None, True)
elif xItem.tag == "lastOutline":
self.lastOutline = checkString(xItem.text, None, True)
elif xItem.tag == "lastWordCount": elif xItem.tag == "lastWordCount":
self.lastWCount = checkInt(xItem.text, 0, False) self.lastWCount = checkInt(xItem.text, 0, False)
elif xItem.tag == "novelWordCount": elif xItem.tag == "novelWordCount":
@@ -612,11 +648,14 @@ class NWProject():
# Sort out old file locations # Sort out old file locations
if legacyList: if legacyList:
errList = [] try:
for projItem in legacyList: for projItem in legacyList:
errList = self._legacyDataFolder(projItem, errList) self._legacyDataFolder(projItem)
if errList: except Exception:
self.theParent.makeAlert(errList, nwAlert.ERROR) self.mainGui.makeAlert(self.tr(
"There was an error updating the project. "
"Some data may not have been preserved."
), nwAlert.ERROR)
# Clean up no longer used files # Clean up no longer used files
self._deprecatedFiles() self._deprecatedFiles()
@@ -642,7 +681,7 @@ class NWProject():
self._writeLockFile() self._writeLockFile()
self.setProjectChanged(False) self.setProjectChanged(False)
self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName)) self.mainGui.setStatus(self.tr("Opened Project: {0}").format(self.projName))
return True return True
@@ -653,7 +692,7 @@ class NWProject():
file. file.
""" """
if self.projPath is None: if self.projPath is None:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Project path not set, cannot save project." "Project path not set, cannot save project."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
@@ -699,6 +738,8 @@ class NWProject():
self._packProjectValue(xSettings, "autoOutline", self.autoOutline) self._packProjectValue(xSettings, "autoOutline", self.autoOutline)
self._packProjectValue(xSettings, "lastEdited", self.lastEdited) self._packProjectValue(xSettings, "lastEdited", self.lastEdited)
self._packProjectValue(xSettings, "lastViewed", self.lastViewed) self._packProjectValue(xSettings, "lastViewed", self.lastViewed)
self._packProjectValue(xSettings, "lastNovel", self.lastNovel)
self._packProjectValue(xSettings, "lastOutline", self.lastOutline)
self._packProjectValue(xSettings, "lastWordCount", self.currWCount) self._packProjectValue(xSettings, "lastWordCount", self.currWCount)
self._packProjectValue(xSettings, "novelWordCount", self.currNovelWC) self._packProjectValue(xSettings, "novelWordCount", self.currNovelWC)
self._packProjectValue(xSettings, "notesWordCount", self.currNotesWC) self._packProjectValue(xSettings, "notesWordCount", self.currNotesWC)
@@ -733,7 +774,7 @@ class NWProject():
xml_declaration=True xml_declaration=True
)) ))
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to save project." "Failed to save project."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
@@ -745,7 +786,7 @@ class NWProject():
os.replace(saveFile, backFile) os.replace(saveFile, backFile)
os.replace(tempFile, saveFile) os.replace(tempFile, saveFile)
except OSError as exc: except OSError as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to save project." "Failed to save project."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
@@ -758,7 +799,7 @@ class NWProject():
self.mainConf.saveRecentCache() self.mainConf.saveRecentCache()
self._writeLockFile() self._writeLockFile()
self.theParent.setStatus(self.tr("Saved Project: {0}").format(self.projName)) self.mainGui.setStatus(self.tr("Saved Project: {0}").format(self.projName))
self.setProjectChanged(False) self.setProjectChanged(False)
return True return True
@@ -806,22 +847,22 @@ class NWProject():
def zipIt(self, doNotify): def zipIt(self, doNotify):
"""Create a zip file of the entire project. """Create a zip file of the entire project.
""" """
if not self.theParent.hasProject: if not self.mainGui.hasProject:
logger.error("No project open") logger.error("No project open")
return False return False
logger.info("Backing up project") logger.info("Backing up project")
self.theParent.setStatus(self.tr("Backing up project ...")) self.mainGui.setStatus(self.tr("Backing up project ..."))
if not (self.mainConf.backupPath and os.path.isdir(self.mainConf.backupPath)): if not (self.mainConf.backupPath and os.path.isdir(self.mainConf.backupPath)):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot backup project because no valid backup path is set. " "Cannot backup project because no valid backup path is set. "
"Please set a valid backup location in Preferences." "Please set a valid backup location in Preferences."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
if not self.projName: if not self.projName:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot backup project because no project name is set. " "Cannot backup project because no project name is set. "
"Please set a Working Title in Project Settings." "Please set a Working Title in Project Settings."
), nwAlert.ERROR) ), nwAlert.ERROR)
@@ -834,13 +875,13 @@ class NWProject():
os.mkdir(baseDir) os.mkdir(baseDir)
logger.debug("Created folder: %s", baseDir) logger.debug("Created folder: %s", baseDir)
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not create backup folder." "Could not create backup folder."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
if baseDir and baseDir.startswith(self.projPath): if baseDir and baseDir.startswith(self.projPath):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot backup project because the backup path is within the " "Cannot backup project because the backup path is within the "
"project folder to be backed up. Please choose a different " "project folder to be backed up. Please choose a different "
"backup path in Preferences." "backup path in Preferences."
@@ -856,17 +897,17 @@ class NWProject():
self._writeLockFile() self._writeLockFile()
logger.info("Backup written to: %s", archName) logger.info("Backup written to: %s", archName)
if doNotify: if doNotify:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Backup archive file written to: {0}" "Backup archive file written to: {0}"
).format(f"{os.path.join(cleanName, archName)}.zip"), nwAlert.INFO) ).format(f"{os.path.join(cleanName, archName)}.zip"), nwAlert.INFO)
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not write backup archive." "Could not write backup archive."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
self.theParent.setStatus(self.tr( self.mainGui.setStatus(self.tr(
"Project backed up to '{0}'" "Project backed up to '{0}'"
).format(f"{baseName}.zip")) ).format(f"{baseName}.zip"))
@@ -894,7 +935,7 @@ class NWProject():
shutil.unpack_archive(pkgSample, projPath) shutil.unpack_archive(pkgSample, projPath)
isSuccess = True isSuccess = True
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to create a new example project." "Failed to create a new example project."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
@@ -916,12 +957,12 @@ class NWProject():
isSuccess = True isSuccess = True
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to create a new example project." "Failed to create a new example project."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
else: else:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to create a new example project. " "Failed to create a new example project. "
"Could not find the necessary files. " "Could not find the necessary files. "
"They seem to be missing from this installation." "They seem to be missing from this installation."
@@ -929,8 +970,8 @@ class NWProject():
if isSuccess: if isSuccess:
self.clearProject() self.clearProject()
self.theParent.openProject(projPath) self.mainGui.openProject(projPath)
self.theParent.rebuildIndex() self.mainGui.rebuildIndex()
return isSuccess return isSuccess
@@ -955,14 +996,14 @@ class NWProject():
os.mkdir(projPath) os.mkdir(projPath)
logger.debug("Created folder: %s", projPath) logger.debug("Created folder: %s", projPath)
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not create new project folder." "Could not create new project folder."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
if os.path.isdir(projPath): if os.path.isdir(projPath):
if os.listdir(self.projPath): if os.listdir(self.projPath):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"New project folder is not empty. " "New project folder is not empty. "
"Each project requires a dedicated project folder." "Each project requires a dedicated project folder."
), nwAlert.ERROR) ), nwAlert.ERROR)
@@ -1012,14 +1053,14 @@ class NWProject():
self.doBackup = doBackup self.doBackup = doBackup
if doBackup: if doBackup:
if not os.path.isdir(self.mainConf.backupPath): if not os.path.isdir(self.mainConf.backupPath):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"You must set a valid backup path in Preferences to use " "You must set a valid backup path in Preferences to use "
"the automatic project backup feature." "the automatic project backup feature."
), nwAlert.WARN) ), nwAlert.WARN)
return False return False
if self.projName == "": if self.projName == "":
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"You must set a valid project name in Project Settings to " "You must set a valid project name in Project Settings to "
"use the automatic project backup feature." "use the automatic project backup feature."
), nwAlert.WARN) ), nwAlert.WARN)
@@ -1090,6 +1131,14 @@ class NWProject():
self.setProjectChanged(True) self.setProjectChanged(True)
return True return True
def setLastNovelViewed(self, tHandle):
"""Set last viewed novel root in the novel tree.
"""
if self.lastNovel != tHandle:
self.lastNovel = tHandle
self.setProjectChanged(True)
return True
def setStatusColours(self, newCols, delCols): def setStatusColours(self, newCols, delCols):
"""Update the list of novel file status flags. """Update the list of novel file status flags.
""" """
@@ -1124,7 +1173,7 @@ class NWProject():
information to the GUI statusbar. information to the GUI statusbar.
""" """
self.projChanged = bValue self.projChanged = bValue
self.theParent.statusBar.doUpdateProjectStatus(bValue) self.mainGui.statusBar.doUpdateProjectStatus(bValue)
if bValue: if bValue:
# If we've changed the project at all, this should be True # If we've changed the project at all, this should be True
self.projAltered = True self.projAltered = True
@@ -1354,7 +1403,7 @@ class NWProject():
os.mkdir(thePath) os.mkdir(thePath)
logger.debug("Created folder: %s", thePath) logger.debug("Created folder: %s", thePath)
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not create folder." "Could not create folder."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
@@ -1417,7 +1466,7 @@ class NWProject():
# Report status # Report status
if len(orphanFiles) > 0: if len(orphanFiles) > 0:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Found {0} orphaned file(s) in project folder." "Found {0} orphaned file(s) in project folder."
).format(len(orphanFiles)), nwAlert.WARN) ).format(len(orphanFiles)), nwAlert.WARN)
else: else:
@@ -1474,7 +1523,7 @@ class NWProject():
self._projTree.updateItemData(orphItem.itemHandle) self._projTree.updateItemData(orphItem.itemHandle)
if noWhere: if noWhere:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"One or more orphaned files could not be added back into the project. " "One or more orphaned files could not be added back into the project. "
"Make sure at least a Novel root folder exists." "Make sure at least a Novel root folder exists."
), nwAlert.WARN) ), nwAlert.WARN)
@@ -1528,82 +1577,37 @@ class NWProject():
# Legacy Data Structure Handlers # Legacy Data Structure Handlers
## ##
def _legacyDataFolder(self, theFolder, errList): def _legacyDataFolder(self, dataDir):
"""Clean up legacy data folders. """Clean up legacy data folders.
""" """
theData = os.path.join(self.projPath, theFolder) dataPath = os.path.join(self.projPath, dataDir)
if not os.path.isdir(theData): if not os.path.isdir(dataPath):
errList.append(self.tr("Not a folder: {0}").format(theData)) return False
return errList
logger.info("Old data folder %s found", theFolder) logger.info("Old data folder found: %s", dataDir)
# Move Documents to Content # Move Documents to Content
for dataItem in os.listdir(theData): for dataItem in os.listdir(dataPath):
theFile = os.path.join(theData, dataItem) dataFile = os.path.join(dataPath, dataItem)
if not os.path.isfile(theFile): if not os.path.isfile(dataFile):
theErr = self._moveUnknownItem(theData, dataItem)
if theErr:
errList.append(theErr)
continue continue
if len(dataItem) == 21 and dataItem.endswith("_main.nwd"): if len(dataItem) == 21 and dataItem.endswith("_main.nwd"):
tHandle = theFolder[-1]+dataItem[:12] tHandle = dataDir[-1] + dataItem[:12]
newPath = os.path.join(self.projContent, tHandle+".nwd") newPath = os.path.join(self.projContent, f"{tHandle}.nwd")
try: os.rename(dataFile, newPath)
os.rename(theFile, newPath) logger.info("Moved file: %s", dataFile)
logger.info("Moved file: %s", theFile)
logger.info("New location: %s", newPath)
except Exception:
errList.append(self.tr("Could not move: {0}").format(theFile))
logger.error("Could not move: %s", theFile)
logException()
elif len(dataItem) == 21 and dataItem.endswith("_main.bak"): elif len(dataItem) == 21 and dataItem.endswith("_main.bak"):
try: os.unlink(dataFile)
os.unlink(theFile) logger.info("Deleted file: %s", dataFile)
logger.info("Deleted file: %s", theFile)
except Exception:
errList.append(self.tr("Could not delete: {0}").format(theFile))
logger.error("Could not delete: %s", theFile)
logException()
else:
theErr = self._moveUnknownItem(theData, dataItem)
if theErr:
errList.append(theErr)
# Remove Data Folder # Remove Data Folder
try: if not os.listdir(dataPath):
os.rmdir(theData) os.rmdir(dataPath)
logger.info("Deleted folder: %s", theFolder) logger.info("Deleted folder: %s", dataDir)
except Exception:
errList.append(self.tr("Could not delete: {0}").format(theFolder))
logger.error("Could not delete: %s", theFolder)
logException()
return errList return True
def _moveUnknownItem(self, theDir, theItem):
"""Move an item that doesn't belong in the project folder to
a junk folder.
"""
theJunk = os.path.join(self.projPath, "junk")
if not self._checkFolder(theJunk):
return self.tr("Could not make folder: {0}").format(theJunk)
theSrc = os.path.join(theDir, theItem)
theDst = os.path.join(theJunk, theItem)
try:
os.rename(theSrc, theDst)
logger.info("Moved to junk: %s", theSrc)
except Exception:
logger.error("Could not move item %s to junk", theSrc)
logException()
return self.tr("Could not move item {0} to {1}.").format(theSrc, theJunk)
return ""
def _deprecatedFiles(self): def _deprecatedFiles(self):
"""Delete files that are no longer used by novelWriter. """Delete files that are no longer used by novelWriter.
-1
View File
@@ -82,7 +82,6 @@ class Tokenizer(ABC):
def __init__(self, theProject): def __init__(self, theProject):
self.theProject = theProject self.theProject = theProject
self.theParent = theProject.theParent
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
# Data Variables # Data Variables
+2 -2
View File
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from novelwriter.dialogs.about import GuiAbout from novelwriter.dialogs.about import GuiAbout
from novelwriter.dialogs.docmerge import GuiDocMerge from novelwriter.dialogs.docmerge import GuiDocMerge
from novelwriter.dialogs.docsplit import GuiDocSplit from novelwriter.dialogs.docsplit import GuiDocSplit
from novelwriter.dialogs.itemeditor import GuiItemEditor from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.preferences import GuiPreferences from novelwriter.dialogs.preferences import GuiPreferences
from novelwriter.dialogs.projdetails import GuiProjectDetails from novelwriter.dialogs.projdetails import GuiProjectDetails
from novelwriter.dialogs.projload import GuiProjectLoad from novelwriter.dialogs.projload import GuiProjectLoad
@@ -35,7 +35,7 @@ __all__ = [
"GuiAbout", "GuiAbout",
"GuiDocMerge", "GuiDocMerge",
"GuiDocSplit", "GuiDocSplit",
"GuiItemEditor", "GuiEditLabel",
"GuiPreferences", "GuiPreferences",
"GuiProjectDetails", "GuiProjectDetails",
"GuiProjectLoad", "GuiProjectLoad",
+28 -28
View File
@@ -43,15 +43,15 @@ logger = logging.getLogger(__name__)
class GuiAbout(QDialog): class GuiAbout(QDialog):
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiAbout ...") logger.debug("Initialising GuiAbout ...")
self.setObjectName("GuiAbout") self.setObjectName("GuiAbout")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout() self.innerBox = QHBoxLayout()
@@ -63,7 +63,7 @@ class GuiAbout(QDialog):
nPx = self.mainConf.pxInt(96) nPx = self.mainConf.pxInt(96)
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx)))
self.lblName = QLabel("<b>novelWriter</b>") self.lblName = QLabel("<b>novelWriter</b>")
self.lblVers = QLabel(f"v{novelwriter.__version__}") self.lblVers = QLabel(f"v{novelwriter.__version__}")
self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x")) self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x"))
@@ -192,37 +192,37 @@ class GuiAbout(QDialog):
]) ])
) )
theTheme = self.theParent.theTheme mainTheme = self.mainGui.mainTheme
theIcons = self.theParent.theTheme.theIcons iconCache = self.mainGui.mainTheme.iconCache
if theTheme.themeName and theTheme.themeAuthor != "N/A": if mainTheme.themeName and mainTheme.themeAuthor != "N/A":
licURL = f"<a href='{theTheme.themeLicenseUrl}'>{theTheme.themeLicense}</a>" licURL = f"<a href='{mainTheme.themeLicenseUrl}'>{mainTheme.themeLicense}</a>"
aboutMsg += "<h4>{0}</h4><p>{1}</p>".format( aboutMsg += "<h4>{0}</h4><p>{1}</p>".format(
self.tr("Theme: {0}").format(theTheme.themeName), self.tr("Theme: {0}").format(mainTheme.themeName),
self._wrapTable([ self._wrapTable([
(self.tr("Author"), theTheme.themeAuthor), (self.tr("Author"), mainTheme.themeAuthor),
(self.tr("Credit"), theTheme.themeCredit), (self.tr("Credit"), mainTheme.themeCredit),
(self.tr("Licence"), licURL), (self.tr("Licence"), licURL),
]) ])
) )
if theIcons.themeName: if iconCache.themeName:
licURL = f"<a href='{theIcons.themeLicenseUrl}'>{theIcons.themeLicense}</a>" licURL = f"<a href='{iconCache.themeLicenseUrl}'>{iconCache.themeLicense}</a>"
aboutMsg += "<h4>{0}</h4><p>{1}</p>".format( aboutMsg += "<h4>{0}</h4><p>{1}</p>".format(
self.tr("Icons: {0}").format(theIcons.themeName), self.tr("Icons: {0}").format(iconCache.themeName),
self._wrapTable([ self._wrapTable([
(self.tr("Author"), theIcons.themeAuthor), (self.tr("Author"), iconCache.themeAuthor),
(self.tr("Credit"), theIcons.themeCredit), (self.tr("Credit"), iconCache.themeCredit),
(self.tr("Licence"), licURL), (self.tr("Licence"), licURL),
]) ])
) )
if theTheme.syntaxName: if mainTheme.syntaxName:
licURL = f"<a href='{theTheme.syntaxLicenseUrl}'>{theTheme.syntaxLicense}</a>" licURL = f"<a href='{mainTheme.syntaxLicenseUrl}'>{mainTheme.syntaxLicense}</a>"
aboutMsg += "<h4>{0}</h4><p>{1}</p>".format( aboutMsg += "<h4>{0}</h4><p>{1}</p>".format(
self.tr("Syntax: {0}").format(theTheme.syntaxName), self.tr("Syntax: {0}").format(mainTheme.syntaxName),
self._wrapTable([ self._wrapTable([
(self.tr("Author"), theTheme.syntaxAuthor), (self.tr("Author"), mainTheme.syntaxAuthor),
(self.tr("Credit"), theTheme.syntaxCredit), (self.tr("Credit"), mainTheme.syntaxCredit),
(self.tr("Licence"), licURL), (self.tr("Licence"), licURL),
]) ])
) )
@@ -280,12 +280,12 @@ class GuiAbout(QDialog):
" padding-right: 0.8em;" " padding-right: 0.8em;"
"}}\n" "}}\n"
).format( ).format(
hColR=self.theParent.theTheme.colHead[0], hColR=self.mainGui.mainTheme.colHead[0],
hColG=self.theParent.theTheme.colHead[1], hColG=self.mainGui.mainTheme.colHead[1],
hColB=self.theParent.theTheme.colHead[2], hColB=self.mainGui.mainTheme.colHead[2],
kColR=self.theTheme.colKey[0], kColR=self.mainTheme.colKey[0],
kColG=self.theTheme.colKey[1], kColG=self.mainTheme.colKey[1],
kColB=self.theTheme.colKey[2], kColB=self.mainTheme.colKey[2],
) )
self.pageAbout.document().setDefaultStyleSheet(styleSheet) self.pageAbout.document().setDefaultStyleSheet(styleSheet)
self.pageNotes.document().setDefaultStyleSheet(styleSheet) self.pageNotes.document().setDefaultStyleSheet(styleSheet)
+15 -15
View File
@@ -41,15 +41,15 @@ logger = logging.getLogger(__name__)
class GuiDocMerge(QDialog): class GuiDocMerge(QDialog):
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiDocMerge ...") logger.debug("Initialising GuiDocMerge ...")
self.setObjectName("GuiDocMerge") self.setObjectName("GuiDocMerge")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.sourceItem = None self.sourceItem = None
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
@@ -57,7 +57,7 @@ class GuiDocMerge(QDialog):
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge"))) self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
self.helpLabel = QHelpLabel( self.helpLabel = QHelpLabel(
self.tr("Drag and drop items to change the order."), self.theParent.theTheme.helpText self.tr("Drag and drop items to change the order."), self.mainGui.mainTheme.helpText
) )
self.listBox = QListWidget() self.listBox = QListWidget()
@@ -102,7 +102,7 @@ class GuiDocMerge(QDialog):
finalOrder.append(self.listBox.item(i).data(Qt.UserRole)) finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
if len(finalOrder) == 0: if len(finalOrder) == 0:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"No source documents found. Nothing to do." "No source documents found. Nothing to do."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
@@ -113,21 +113,21 @@ class GuiDocMerge(QDialog):
docText = inDoc.readDocument() docText = inDoc.readDocument()
docErr = inDoc.getError() docErr = inDoc.getError()
if docText is None and docErr: if docText is None and docErr:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("Failed to open document file."), docErr self.tr("Failed to open document file."), docErr
], nwAlert.ERROR) ], nwAlert.ERROR)
if docText: if docText:
theText += docText.rstrip("\n")+"\n\n" theText += docText.rstrip("\n")+"\n\n"
if self.sourceItem is None: if self.sourceItem is None:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"No source folder selected. Nothing to do." "No source folder selected. Nothing to do."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
srcItem = self.theProject.tree[self.sourceItem] srcItem = self.theProject.tree[self.sourceItem]
if srcItem is None: if srcItem is None:
self.theParent.makeAlert(self.tr("Internal error."), nwAlert.ERROR) self.mainGui.makeAlert(self.tr("Internal error."), nwAlert.ERROR)
return False return False
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemParent) nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemParent)
@@ -137,13 +137,13 @@ class GuiDocMerge(QDialog):
outDoc = NWDoc(self.theProject, nHandle) outDoc = NWDoc(self.theProject, nHandle)
if not outDoc.writeDocument(theText): if not outDoc.writeDocument(theText):
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("Could not save document."), outDoc.getError() self.tr("Could not save document."), outDoc.getError()
], nwAlert.ERROR) ], nwAlert.ERROR)
return False return False
self.theParent.treeView.revealNewTreeItem(nHandle) self.mainGui.projView.revealNewTreeItem(nHandle)
self.theParent.openDocument(nHandle, doScroll=True) self.mainGui.openDocument(nHandle, doScroll=True)
self._doClose() self._doClose()
@@ -165,7 +165,7 @@ class GuiDocMerge(QDialog):
are then added to the list view in order. The list itself can be are then added to the list view in order. The list itself can be
reordered by the user. reordered by the user.
""" """
tHandle = self.theParent.treeView.getSelectedHandle() tHandle = self.mainGui.projView.getSelectedHandle()
self.sourceItem = tHandle self.sourceItem = tHandle
if tHandle is None: if tHandle is None:
return False return False
@@ -175,12 +175,12 @@ class GuiDocMerge(QDialog):
return False return False
if nwItem.itemType is not nwItemType.FOLDER: if nwItem.itemType is not nwItemType.FOLDER:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Element selected in the project tree must be a folder." "Element selected in the project tree must be a folder."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle): for sHandle in self.mainGui.projView.getTreeFromHandle(tHandle):
newItem = QListWidgetItem() newItem = QListWidgetItem()
nwItem = self.theProject.tree[sHandle] nwItem = self.theProject.tree[sHandle]
if nwItem.itemType is not nwItemType.FILE: if nwItem.itemType is not nwItemType.FILE:
+15 -15
View File
@@ -41,15 +41,15 @@ logger = logging.getLogger(__name__)
class GuiDocSplit(QDialog): class GuiDocSplit(QDialog):
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiDocSplit ...") logger.debug("Initialising GuiDocSplit ...")
self.setObjectName("GuiDocSplit") self.setObjectName("GuiDocSplit")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.sourceItem = None self.sourceItem = None
self.sourceText = [] self.sourceText = []
@@ -60,7 +60,7 @@ class GuiDocSplit(QDialog):
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers"))) self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
self.helpLabel = QHelpLabel( self.helpLabel = QHelpLabel(
self.tr("Select the maximum level to split into files."), self.tr("Select the maximum level to split into files."),
self.theParent.theTheme.helpText self.mainGui.mainTheme.helpText
) )
self.listBox = QListWidget() self.listBox = QListWidget()
@@ -115,14 +115,14 @@ class GuiDocSplit(QDialog):
logger.verbose("GuiDocSplit split button clicked") logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None: if self.sourceItem is None:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"No source document selected. Nothing to do." "No source document selected. Nothing to do."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
srcItem = self.theProject.tree[self.sourceItem] srcItem = self.theProject.tree[self.sourceItem]
if srcItem is None: if srcItem is None:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Could not parse source document." "Could not parse source document."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
@@ -132,7 +132,7 @@ class GuiDocSplit(QDialog):
docErr = inDoc.getError() docErr = inDoc.getError()
if theText is None and docErr: if theText is None and docErr:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("Failed to open document file."), docErr self.tr("Failed to open document file."), docErr
], nwAlert.ERROR) ], nwAlert.ERROR)
@@ -153,12 +153,12 @@ class GuiDocSplit(QDialog):
nFiles = len(finalOrder) nFiles = len(finalOrder)
if nFiles == 0: if nFiles == 0:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"No headers found. Nothing to do." "No headers found. Nothing to do."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
msgYes = self.theParent.askQuestion( msgYes = self.mainGui.askQuestion(
self.tr("Split Document"), self.tr("Split Document"),
"{0}<br><br>{1}".format( "{0}<br><br>{1}".format(
self.tr( self.tr(
@@ -175,7 +175,7 @@ class GuiDocSplit(QDialog):
# Create the folder # Create the folder
fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemParent) fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemParent)
self.theParent.treeView.revealNewTreeItem(fHandle) self.mainGui.projView.revealNewTreeItem(fHandle)
logger.verbose("Creating folder '%s'", fHandle) logger.verbose("Creating folder '%s'", fHandle)
# Loop through, and create the files # Loop through, and create the files
@@ -196,12 +196,12 @@ class GuiDocSplit(QDialog):
outDoc = NWDoc(self.theProject, nHandle) outDoc = NWDoc(self.theProject, nHandle)
if not outDoc.writeDocument(theText): if not outDoc.writeDocument(theText):
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("Could not save document."), outDoc.getError() self.tr("Could not save document."), outDoc.getError()
], nwAlert.ERROR) ], nwAlert.ERROR)
return False return False
self.theParent.treeView.revealNewTreeItem(nHandle) self.mainGui.projView.revealNewTreeItem(nHandle)
self._doClose() self._doClose()
@@ -226,7 +226,7 @@ class GuiDocSplit(QDialog):
""" """
self.listBox.clear() self.listBox.clear()
if self.sourceItem is None: if self.sourceItem is None:
self.sourceItem = self.theParent.treeView.getSelectedHandle() self.sourceItem = self.mainGui.projView.getSelectedHandle()
if self.sourceItem is None: if self.sourceItem is None:
return False return False
@@ -236,7 +236,7 @@ class GuiDocSplit(QDialog):
return False return False
if nwItem.itemType is not nwItemType.FILE: if nwItem.itemType is not nwItemType.FILE:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Element selected in the project tree must be a file." "Element selected in the project tree must be a file."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
+84
View File
@@ -0,0 +1,84 @@
"""
novelWriter Edit Label Dialog
===============================
A simple dialog for editing a label
File History:
Created: 2022-06-11 [1.7b1]
This file is a part of novelWriter
Copyright 20182022, 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 novelwriter
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QLineEdit, QLabel, QDialogButtonBox, QHBoxLayout
)
logger = logging.getLogger(__name__)
class GuiEditLabel(QDialog):
def __init__(self, parent, text=""):
QDialog.__init__(self, parent=parent)
self.setObjectName("GuiEditLabel")
self.setWindowTitle(self.tr("Item Label"))
mVd = novelwriter.CONFIG.pxInt(220)
mSp = novelwriter.CONFIG.pxInt(12)
# Item Label
self.labelValue = QLineEdit()
self.labelValue.setMinimumWidth(mVd)
self.labelValue.setMaxLength(200)
self.labelValue.setText(text)
self.labelValue.selectAll()
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
# Assemble
self.innerBox = QHBoxLayout()
self.innerBox.addWidget(QLabel(self.tr("Label")), 0)
self.innerBox.addWidget(self.labelValue, 1)
self.innerBox.setSpacing(mSp)
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(mSp)
self.outerBox.addLayout(self.innerBox, 1)
self.outerBox.addWidget(self.buttonBox, 0)
self.setLayout(self.outerBox)
return
@property
def itemLabel(self):
return self.labelValue.text()
@classmethod
def getLabel(cls, parent, text):
cls = GuiEditLabel(parent, text=text)
cls.exec_()
return cls.itemLabel, cls.result() == QDialog.Accepted
# END Class GuiEditLabel
-203
View File
@@ -1,203 +0,0 @@
"""
novelWriter GUI Item Editor
=============================
GUI class for the item editor dialog
File History:
Created: 2019-04-27 [0.0.1]
This file is a part of novelWriter
Copyright 20182022, 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 novelwriter
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QGridLayout, QLineEdit, QComboBox, QLabel,
QDialogButtonBox
)
from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.constants import trConst, nwLabels
from novelwriter.gui.custom import QSwitch
logger = logging.getLogger(__name__)
class GuiItemEditor(QDialog):
def __init__(self, theParent, tHandle):
QDialog.__init__(self, theParent)
logger.debug("Initialising GuiItemEditor ...")
self.setObjectName("GuiItemEditor")
self.mainConf = novelwriter.CONFIG
self.theParent = theParent
self.theProject = theParent.theProject
##
# Build GUI
##
self.theItem = self.theProject.tree[tHandle]
if self.theItem is None:
self.close()
return
self.setWindowTitle(self.tr("Item Settings"))
mVd = self.mainConf.pxInt(220)
mSp = self.mainConf.pxInt(16)
vSp = self.mainConf.pxInt(4)
# Item Label
self.editName = QLineEdit()
self.editName.setMinimumWidth(mVd)
self.editName.setMaxLength(200)
# Item Status
self.editStatus = QComboBox()
self.editStatus.setMinimumWidth(mVd)
if self.theItem.isNovelLike():
for key, entry in self.theProject.statusItems.items():
self.editStatus.addItem(entry["icon"], entry["name"], key)
index = self.editStatus.findData(self.theItem.itemStatus)
if index != -1:
self.editStatus.setCurrentIndex(index)
else:
for key, entry in self.theProject.importItems.items():
self.editStatus.addItem(entry["icon"], entry["name"], key)
index = self.editStatus.findData(self.theItem.itemImport)
if index != -1:
self.editStatus.setCurrentIndex(index)
# Item Layout
self.editLayout = QComboBox()
self.editLayout.setMinimumWidth(mVd)
validLayouts = []
if self.theItem.itemType == nwItemType.FILE:
if self.theItem.documentAllowed():
validLayouts.append(nwItemLayout.DOCUMENT)
validLayouts.append(nwItemLayout.NOTE)
else:
validLayouts.append(nwItemLayout.NO_LAYOUT)
self.editLayout.setEnabled(False)
for itemLayout in nwItemLayout:
if itemLayout in validLayouts:
self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
index = self.editLayout.findData(self.theItem.itemLayout)
if index != -1:
self.editLayout.setCurrentIndex(index)
# Export Switch
self.textExport = QLabel(self.tr("Include when building project"))
self.editExport = QSwitch()
if self.theItem.itemType == nwItemType.FILE:
self.editExport.setEnabled(True)
self.editExport.setChecked(self.theItem.isExported)
else:
self.editExport.setEnabled(False)
self.editExport.setChecked(False)
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
# Set Current Values
self.editName.setText(self.theItem.itemName)
self.editName.selectAll()
##
# Assemble
##
nameLabel = QLabel(self.tr("Label"))
statusLabel = QLabel(self.tr("Status"))
layoutLabel = QLabel(self.tr("Layout"))
self.mainForm = QGridLayout()
self.mainForm.setVerticalSpacing(vSp)
self.mainForm.setHorizontalSpacing(mSp)
self.mainForm.addWidget(nameLabel, 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(statusLabel, 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(layoutLabel, 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.setColumnStretch(0, 0)
self.mainForm.setColumnStretch(1, 1)
self.mainForm.setColumnStretch(2, 0)
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(mSp)
self.outerBox.addLayout(self.mainForm)
self.outerBox.addStretch(1)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
self.rejected.connect(self._doClose)
logger.debug("GuiItemEditor initialisation complete")
return
##
# Slots
##
@pyqtSlot()
def _doSave(self):
"""Save the setting to the item.
"""
logger.verbose("ItemEditor save button clicked")
itemName = self.editName.text()
itemStatus = self.editStatus.currentData()
itemLayout = self.editLayout.currentData()
isExported = self.editExport.isChecked()
self.theItem.setName(itemName)
self.theItem.setImportStatus(itemStatus)
self.theItem.setLayout(itemLayout)
self.theItem.setExported(isExported)
self.theProject.setProjectChanged(True)
self.accept()
self.close()
return
@pyqtSlot()
def _doClose(self):
"""Close the dialog without saving the settings.
"""
logger.verbose("ItemEditor cancel button clicked")
self.close()
return
# END Class GuiItemEditor
+56 -56
View File
@@ -43,25 +43,25 @@ logger = logging.getLogger(__name__)
class GuiPreferences(PagedDialog): class GuiPreferences(PagedDialog):
def __init__(self, theParent): def __init__(self, mainGui):
PagedDialog.__init__(self, theParent) PagedDialog.__init__(self, mainGui)
logger.debug("Initialising GuiPreferences ...") logger.debug("Initialising GuiPreferences ...")
self.setObjectName("GuiPreferences") self.setObjectName("GuiPreferences")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Preferences")) self.setWindowTitle(self.tr("Preferences"))
self.tabGeneral = GuiPreferencesGeneral(self.theParent) self.tabGeneral = GuiPreferencesGeneral(self.mainGui)
self.tabProjects = GuiPreferencesProjects(self.theParent) self.tabProjects = GuiPreferencesProjects(self.mainGui)
self.tabDocs = GuiPreferencesDocuments(self.theParent) self.tabDocs = GuiPreferencesDocuments(self.mainGui)
self.tabEditor = GuiPreferencesEditor(self.theParent) self.tabEditor = GuiPreferencesEditor(self.mainGui)
self.tabSyntax = GuiPreferencesSyntax(self.theParent) self.tabSyntax = GuiPreferencesSyntax(self.mainGui)
self.tabAuto = GuiPreferencesAutomation(self.theParent) self.tabAuto = GuiPreferencesAutomation(self.mainGui)
self.tabQuote = GuiPreferencesQuotes(self.theParent) self.tabQuote = GuiPreferencesQuotes(self.mainGui)
self.addTab(self.tabGeneral, self.tr("General")) self.addTab(self.tabGeneral, self.tr("General"))
self.addTab(self.tabProjects, self.tr("Projects")) self.addTab(self.tabProjects, self.tr("Projects"))
@@ -102,12 +102,12 @@ class GuiPreferences(PagedDialog):
self.tabQuote.saveValues() self.tabQuote.saveValues()
if needsRestart: if needsRestart:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Some changes will not be applied until novelWriter has been restarted." "Some changes will not be applied until novelWriter has been restarted."
), nwAlert.INFO) ), nwAlert.INFO)
if refreshTree: if refreshTree:
self.theParent.treeView.buildTree() self.mainGui.projView.populateTree()
self._saveWindowSize() self._saveWindowSize()
self.accept() self.accept()
@@ -138,16 +138,16 @@ class GuiPreferences(PagedDialog):
class GuiPreferencesGeneral(QWidget): class GuiPreferencesGeneral(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Look and Feel # Look and Feel
@@ -174,7 +174,7 @@ class GuiPreferencesGeneral(QWidget):
# Select Theme # Select Theme
self.guiTheme = QComboBox() self.guiTheme = QComboBox()
self.guiTheme.setMinimumWidth(minWidth) self.guiTheme.setMinimumWidth(minWidth)
self.theThemes = self.theTheme.listThemes() self.theThemes = self.mainTheme.listThemes()
for themeDir, themeName in self.theThemes: for themeDir, themeName in self.theThemes:
self.guiTheme.addItem(themeName, themeDir) self.guiTheme.addItem(themeName, themeDir)
themeIdx = self.guiTheme.findData(self.mainConf.guiTheme) themeIdx = self.guiTheme.findData(self.mainConf.guiTheme)
@@ -190,8 +190,8 @@ class GuiPreferencesGeneral(QWidget):
# Select Icon Theme # Select Icon Theme
self.guiIcons = QComboBox() self.guiIcons = QComboBox()
self.guiIcons.setMinimumWidth(minWidth) self.guiIcons.setMinimumWidth(minWidth)
self.theIcons = self.theTheme.theIcons.listThemes() self.iconCache = self.mainTheme.iconCache.listThemes()
for iconDir, iconName in self.theIcons: for iconDir, iconName in self.iconCache:
self.guiIcons.addItem(iconName, iconDir) self.guiIcons.addItem(iconName, iconDir)
iconIdx = self.guiIcons.findData(self.mainConf.guiIcons) iconIdx = self.guiIcons.findData(self.mainConf.guiIcons)
if iconIdx != -1: if iconIdx != -1:
@@ -206,7 +206,7 @@ class GuiPreferencesGeneral(QWidget):
# Editor Theme # Editor Theme
self.guiSyntax = QComboBox() self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200)) self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
self.theSyntaxes = self.theTheme.listSyntax() self.theSyntaxes = self.mainTheme.listSyntax()
for syntaxFile, syntaxName in self.theSyntaxes: for syntaxFile, syntaxName in self.theSyntaxes:
self.guiSyntax.addItem(syntaxName, syntaxFile) self.guiSyntax.addItem(syntaxName, syntaxFile)
syntaxIdx = self.guiSyntax.findData(self.mainConf.guiSyntax) syntaxIdx = self.guiSyntax.findData(self.mainConf.guiSyntax)
@@ -225,7 +225,7 @@ class GuiPreferencesGeneral(QWidget):
self.guiFont.setFixedWidth(self.mainConf.pxInt(162)) self.guiFont.setFixedWidth(self.mainConf.pxInt(162))
self.guiFont.setText(self.mainConf.guiFont) self.guiFont.setText(self.mainConf.guiFont)
self.fontButton = QPushButton("...") self.fontButton = QPushButton("...")
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Font family"), self.tr("Font family"),
@@ -344,16 +344,16 @@ class GuiPreferencesGeneral(QWidget):
class GuiPreferencesProjects(QWidget): class GuiPreferencesProjects(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Automatic Save # Automatic Save
@@ -505,16 +505,16 @@ class GuiPreferencesProjects(QWidget):
class GuiPreferencesDocuments(QWidget): class GuiPreferencesDocuments(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Text Style # Text Style
@@ -527,7 +527,7 @@ class GuiPreferencesDocuments(QWidget):
self.textFont.setFixedWidth(self.mainConf.pxInt(162)) self.textFont.setFixedWidth(self.mainConf.pxInt(162))
self.textFont.setText(self.mainConf.textFont) self.textFont.setText(self.mainConf.textFont)
self.fontButton = QPushButton("...") self.fontButton = QPushButton("...")
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Font family"), self.tr("Font family"),
@@ -666,16 +666,16 @@ class GuiPreferencesDocuments(QWidget):
class GuiPreferencesEditor(QWidget): class GuiPreferencesEditor(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
mW = self.mainConf.pxInt(250) mW = self.mainConf.pxInt(250)
@@ -688,7 +688,7 @@ class GuiPreferencesEditor(QWidget):
self.spellLanguage = QComboBox(self) self.spellLanguage = QComboBox(self)
self.spellLanguage.setMaximumWidth(mW) self.spellLanguage.setMaximumWidth(mW)
langAvail = self.theParent.docEditor.spEnchant.listDictionaries() langAvail = self.mainGui.docEditor.spEnchant.listDictionaries()
if self.mainConf.hasEnchant: if self.mainConf.hasEnchant:
if langAvail: if langAvail:
for spTag, spProv in langAvail: for spTag, spProv in langAvail:
@@ -840,16 +840,16 @@ class GuiPreferencesEditor(QWidget):
class GuiPreferencesSyntax(QWidget): class GuiPreferencesSyntax(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Quotes & Dialogue # Quotes & Dialogue
@@ -943,16 +943,16 @@ class GuiPreferencesSyntax(QWidget):
class GuiPreferencesAutomation(QWidget): class GuiPreferencesAutomation(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Automatic Features # Automatic Features
@@ -1100,16 +1100,16 @@ class GuiPreferencesAutomation(QWidget):
class GuiPreferencesQuotes(QWidget): class GuiPreferencesQuotes(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
# Quotation Style # Quotation Style
@@ -1117,7 +1117,7 @@ class GuiPreferencesQuotes(QWidget):
self.mainForm.addGroupLabel(self.tr("Quotation Style")) self.mainForm.addGroupLabel(self.tr("Quotation Style"))
qWidth = self.mainConf.pxInt(40) qWidth = self.mainConf.pxInt(40)
bWidth = int(2.5*self.theTheme.getTextWidth("...")) bWidth = int(2.5*self.mainTheme.getTextWidth("..."))
self.quoteSym = {} self.quoteSym = {}
# Single Quote Style # Single Quote Style
+18 -18
View File
@@ -43,15 +43,15 @@ logger = logging.getLogger(__name__)
class GuiProjectDetails(PagedDialog): class GuiProjectDetails(PagedDialog):
def __init__(self, theParent): def __init__(self, mainGui):
PagedDialog.__init__(self, theParent) PagedDialog.__init__(self, mainGui)
logger.debug("Initialising GuiProjectDetails ...") logger.debug("Initialising GuiProjectDetails ...")
self.setObjectName("GuiProjectDetails") self.setObjectName("GuiProjectDetails")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Project Details")) self.setWindowTitle(self.tr("Project Details"))
@@ -66,8 +66,8 @@ class GuiProjectDetails(PagedDialog):
self.mainConf.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH)) self.mainConf.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH))
) )
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject) self.tabMain = GuiProjectDetailsMain(self.mainGui, self.theProject)
self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject) self.tabContents = GuiProjectDetailsContents(self.mainGui, self.theProject)
self.addTab(self.tabMain, self.tr("Overview")) self.addTab(self.tabMain, self.tr("Overview"))
self.addTab(self.tabContents, self.tr("Contents")) self.addTab(self.tabContents, self.tr("Contents"))
@@ -139,16 +139,16 @@ class GuiProjectDetails(PagedDialog):
class GuiProjectDetailsMain(QWidget): class GuiProjectDetailsMain(QWidget):
def __init__(self, theParent, theProject): def __init__(self, mainGui, theProject):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent
self.theProject = theProject self.theProject = theProject
self.theTheme = theParent.theTheme self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
fPx = self.theTheme.fontPixelSize fPx = self.mainTheme.fontPixelSize
fPt = self.theTheme.fontPointSize fPt = self.mainTheme.fontPointSize
vPx = self.mainConf.pxInt(4) vPx = self.mainConf.pxInt(4)
hPx = self.mainConf.pxInt(12) hPx = self.mainConf.pxInt(12)
@@ -271,18 +271,18 @@ class GuiProjectDetailsContents(QWidget):
C_PAGE = 3 C_PAGE = 3
C_PROG = 4 C_PROG = 4
def __init__(self, theParent, theProject): def __init__(self, mainGui, theProject):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent
self.theProject = theProject self.theProject = theProject
self.theTheme = theParent.theTheme self.mainGui = mainGui
self.mainTheme = mainGui.mainTheme
# Internal # Internal
self._theToC = [] self._theToC = []
iPx = self.theTheme.baseIconSize iPx = self.mainTheme.baseIconSize
hPx = self.mainConf.pxInt(12) hPx = self.mainConf.pxInt(12)
vPx = self.mainConf.pxInt(4) vPx = self.mainConf.pxInt(4)
pOptions = self.theProject.options pOptions = self.theProject.options
@@ -469,7 +469,7 @@ class GuiProjectDetailsContents(QWidget):
if tTitle.strip() == "": if tTitle.strip() == "":
tTitle = self.tr("Untitled") tTitle = self.tr("Untitled")
newItem.setIcon(self.C_TITLE, self.theTheme.getIcon("doc_h%d" % tLevel)) newItem.setIcon(self.C_TITLE, self.mainTheme.getIcon("doc_h%d" % tLevel))
newItem.setText(self.C_TITLE, tTitle) newItem.setText(self.C_TITLE, tTitle)
newItem.setText(self.C_WORDS, f"{wCount:n}") newItem.setText(self.C_WORDS, f"{wCount:n}")
newItem.setText(self.C_PAGES, f"{pCount:n}") newItem.setText(self.C_PAGES, f"{pCount:n}")
+10 -10
View File
@@ -53,21 +53,21 @@ class GuiProjectLoad(QDialog):
C_COUNT = 1 C_COUNT = 1
C_TIME = 2 C_TIME = 2
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiProjectLoad ...") logger.debug("Initialising GuiProjectLoad ...")
self.setObjectName("GuiProjectLoad") self.setObjectName("GuiProjectLoad")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.openState = self.NONE_STATE self.openState = self.NONE_STATE
self.openPath = None self.openPath = None
sPx = self.mainConf.pxInt(16) sPx = self.mainConf.pxInt(16)
nPx = self.mainConf.pxInt(96) nPx = self.mainConf.pxInt(96)
iPx = self.theTheme.baseIconSize iPx = self.mainTheme.baseIconSize
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout() self.innerBox = QHBoxLayout()
@@ -80,7 +80,7 @@ class GuiProjectLoad(QDialog):
self.setModal(True) self.setModal(True)
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx)))
self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop) self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop)
self.projectForm = QGridLayout() self.projectForm = QGridLayout()
@@ -110,7 +110,7 @@ class GuiProjectLoad(QDialog):
self.selPath.setReadOnly(True) self.selPath.setReadOnly(True)
self.browseButton = QPushButton("...") self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.browseButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse) self.browseButton.clicked.connect(self._doBrowse)
self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3) self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
@@ -225,7 +225,7 @@ class GuiProjectLoad(QDialog):
selList = self.listBox.selectedItems() selList = self.listBox.selectedItems()
if selList: if selList:
projName = selList[0].text(self.C_NAME) projName = selList[0].text(self.C_NAME)
msgYes = self.theParent.askQuestion( msgYes = self.mainGui.askQuestion(
self.tr("Remove Entry"), self.tr("Remove Entry"),
self.tr( self.tr(
"Remove '{0}' from the recent projects list? " "Remove '{0}' from the recent projects list? "
@@ -280,7 +280,7 @@ class GuiProjectLoad(QDialog):
sortList = sorted(dataList, key=lambda x: x[1], reverse=True) sortList = sorted(dataList, key=lambda x: x[1], reverse=True)
for theTitle, theTime, theWords, projPath in sortList: for theTitle, theTime, theWords, projPath in sortList:
newItem = QTreeWidgetItem([""]*4) newItem = QTreeWidgetItem([""]*4)
newItem.setIcon(self.C_NAME, self.theParent.theTheme.getIcon("proj_nwx")) newItem.setIcon(self.C_NAME, self.mainGui.mainTheme.getIcon("proj_nwx"))
newItem.setText(self.C_NAME, theTitle) newItem.setText(self.C_NAME, theTitle)
newItem.setData(self.C_NAME, Qt.UserRole, projPath) newItem.setData(self.C_NAME, Qt.UserRole, projPath)
newItem.setText(self.C_COUNT, formatInt(theWords)) newItem.setText(self.C_COUNT, formatInt(theWords))
@@ -288,7 +288,7 @@ class GuiProjectLoad(QDialog):
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter) newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter) newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter) newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
newItem.setFont(self.C_TIME, self.theTheme.guiFontFixed) newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
self.listBox.addTopLevelItem(newItem) self.listBox.addTopLevelItem(newItem)
if self.listBox.topLevelItemCount() > 0: if self.listBox.topLevelItemCount() > 0:
+31 -31
View File
@@ -43,15 +43,15 @@ logger = logging.getLogger(__name__)
class GuiProjectSettings(PagedDialog): class GuiProjectSettings(PagedDialog):
def __init__(self, theParent): def __init__(self, mainGui):
PagedDialog.__init__(self, theParent) PagedDialog.__init__(self, mainGui)
logger.debug("Initialising GuiProjectSettings ...") logger.debug("Initialising GuiProjectSettings ...")
self.setObjectName("GuiProjectSettings") self.setObjectName("GuiProjectSettings")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.theProject.countStatus() self.theProject.countStatus()
self.setWindowTitle(self.tr("Project Settings")) self.setWindowTitle(self.tr("Project Settings"))
@@ -67,10 +67,10 @@ class GuiProjectSettings(PagedDialog):
self.mainConf.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH)) self.mainConf.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
) )
self.tabMain = GuiProjectEditMain(self.theParent, self.theProject) self.tabMain = GuiProjectEditMain(self.mainGui, self.theProject)
self.tabStatus = GuiProjectEditStatus(self.theParent, self.theProject, True) self.tabStatus = GuiProjectEditStatus(self.mainGui, self.theProject, True)
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject, False) self.tabImport = GuiProjectEditStatus(self.mainGui, self.theProject, False)
self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject) self.tabReplace = GuiProjectEditReplace(self.mainGui, self.theProject)
self.addTab(self.tabMain, self.tr("Settings")) self.addTab(self.tabMain, self.tr("Settings"))
self.addTab(self.tabStatus, self.tr("Status")) self.addTab(self.tabStatus, self.tr("Status"))
@@ -121,7 +121,7 @@ class GuiProjectSettings(PagedDialog):
self.theProject.setImportColours(newList, delList) self.theProject.setImportColours(newList, delList)
if self.tabStatus.colChanged or self.tabImport.colChanged: if self.tabStatus.colChanged or self.tabImport.colChanged:
self.theParent.rebuildTrees() self.mainGui.rebuildTrees()
if self.tabReplace.arChanged: if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList() newList = self.tabReplace.getNewList()
@@ -166,22 +166,22 @@ class GuiProjectSettings(PagedDialog):
class GuiProjectEditMain(QWidget): class GuiProjectEditMain(QWidget):
def __init__(self, theParent, theProject): def __init__(self, mainGui, theProject):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theProject self.theProject = theProject
# The Form # The Form
self.mainForm = QConfigLayout() self.mainForm = QConfigLayout()
self.mainForm.setHelpTextStyle(self.theParent.theTheme.helpText) self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText)
self.setLayout(self.mainForm) self.setLayout(self.mainForm)
self.mainForm.addGroupLabel(self.tr("Project Settings")) self.mainForm.addGroupLabel(self.tr("Project Settings"))
xW = self.mainConf.pxInt(250) xW = self.mainConf.pxInt(250)
xH = round(4.8*self.theParent.theTheme.fontPixelSize) xH = round(4.8*self.mainGui.mainTheme.fontPixelSize)
self.editName = QLineEdit() self.editName = QLineEdit()
self.editName.setMaxLength(200) self.editName.setMaxLength(200)
@@ -216,7 +216,7 @@ class GuiProjectEditMain(QWidget):
self.spellLang = QComboBox(self) self.spellLang = QComboBox(self)
self.spellLang.setMaximumWidth(xW) self.spellLang.setMaximumWidth(xW)
self.spellLang.addItem(self.tr("Default"), "None") self.spellLang.addItem(self.tr("Default"), "None")
langAvail = self.theParent.docEditor.spEnchant.listDictionaries() langAvail = self.mainGui.docEditor.spEnchant.listDictionaries()
for spTag, spProv in langAvail: for spTag, spProv in langAvail:
qLocal = QLocale(spTag) qLocal = QLocale(spTag)
spLang = qLocal.nativeLanguageName().title() spLang = qLocal.nativeLanguageName().title()
@@ -256,13 +256,13 @@ class GuiProjectEditStatus(QWidget):
COL_ROLE = Qt.UserRole + 1 COL_ROLE = Qt.UserRole + 1
NUM_ROLE = Qt.UserRole + 2 NUM_ROLE = Qt.UserRole + 2
def __init__(self, theParent, theProject, isStatus): def __init__(self, mainGui, theProject, isStatus):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theProject self.theProject = theProject
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
if isStatus: if isStatus:
self.theStatus = self.theProject.statusItems self.theStatus = self.theProject.statusItems
@@ -281,7 +281,7 @@ class GuiProjectEditStatus(QWidget):
self.colChanged = False self.colChanged = False
self.selColour = QColor(100, 100, 100) self.selColour = QColor(100, 100, 100)
self.iPx = self.theTheme.baseIconSize self.iPx = self.mainTheme.baseIconSize
# The List # The List
# ======== # ========
@@ -300,16 +300,16 @@ class GuiProjectEditStatus(QWidget):
# List Controls # List Controls
# ============= # =============
self.addButton = QPushButton(self.theTheme.getIcon("add"), "") self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
self.addButton.clicked.connect(self._newItem) self.addButton.clicked.connect(self._newItem)
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "") self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "")
self.delButton.clicked.connect(self._delItem) self.delButton.clicked.connect(self._delItem)
self.upButton = QPushButton(self.theTheme.getIcon("up"), "") self.upButton = QPushButton(self.mainTheme.getIcon("up"), "")
self.upButton.clicked.connect(lambda: self._moveItem(-1)) self.upButton.clicked.connect(lambda: self._moveItem(-1))
self.dnButton = QPushButton(self.theTheme.getIcon("down"), "") self.dnButton = QPushButton(self.mainTheme.getIcon("down"), "")
self.dnButton.clicked.connect(lambda: self._moveItem(1)) self.dnButton.clicked.connect(lambda: self._moveItem(1))
# Edit Form # Edit Form
@@ -411,7 +411,7 @@ class GuiProjectEditStatus(QWidget):
if selItem is not None: if selItem is not None:
iRow = self.listBox.indexOfTopLevelItem(selItem) iRow = self.listBox.indexOfTopLevelItem(selItem)
if selItem.data(self.COL_LABEL, self.NUM_ROLE) > 0: if selItem.data(self.COL_LABEL, self.NUM_ROLE) > 0:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot delete a status item that is in use." "Cannot delete a status item that is in use."
), nwAlert.ERROR) ), nwAlert.ERROR)
else: else:
@@ -527,12 +527,12 @@ class GuiProjectEditReplace(QWidget):
COL_KEY = 0 COL_KEY = 0
COL_REPL = 1 COL_REPL = 1
def __init__(self, theParent, theProject): def __init__(self, mainGui, theProject):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theProject self.theProject = theProject
self.arChanged = False self.arChanged = False
@@ -563,10 +563,10 @@ class GuiProjectEditReplace(QWidget):
# List Controls # List Controls
# ============= # =============
self.addButton = QPushButton(self.theTheme.getIcon("add"), "") self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
self.addButton.clicked.connect(self._addEntry) self.addButton.clicked.connect(self._addEntry)
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "") self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "")
self.delButton.clicked.connect(self._delEntry) self.delButton.clicked.connect(self._delEntry)
# Edit Form # Edit Form
+2 -2
View File
@@ -42,8 +42,8 @@ class GuiQuoteSelect(QDialog):
selectedQuote = "" selectedQuote = ""
def __init__(self, theParent=None, currentQuote='"'): def __init__(self, parent=None, currentQuote='"'):
QDialog.__init__(self, parent=theParent) QDialog.__init__(self, parent=parent)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
+5 -5
View File
@@ -43,14 +43,14 @@ logger = logging.getLogger(__name__)
class GuiUpdates(QDialog): class GuiUpdates(QDialog):
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiUpdates ...") logger.debug("Initialising GuiUpdates ...")
self.setObjectName("GuiUpdates") self.setObjectName("GuiUpdates")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.setWindowTitle(self.tr("Check for Updates")) self.setWindowTitle(self.tr("Check for Updates"))
@@ -61,7 +61,7 @@ class GuiUpdates(QDialog):
# Left Box # Left Box
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(self.mainGui.mainTheme.getPixmap("novelwriter", (nPx, nPx)))
self.leftBox = QVBoxLayout() self.leftBox = QVBoxLayout()
self.leftBox.addWidget(self.nwIcon) self.leftBox.addWidget(self.nwIcon)
+9 -9
View File
@@ -42,16 +42,16 @@ logger = logging.getLogger(__name__)
class GuiWordList(QDialog): class GuiWordList(QDialog):
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiWordList ...") logger.debug("Initialising GuiWordList ...")
self.setObjectName("GuiWordList") self.setObjectName("GuiWordList")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.setWindowTitle(self.tr("Project Word List")) self.setWindowTitle(self.tr("Project Word List"))
@@ -78,10 +78,10 @@ class GuiWordList(QDialog):
self.newEntry = QLineEdit() self.newEntry = QLineEdit()
self.addButton = QPushButton(self.theTheme.getIcon("add"), "") self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
self.addButton.clicked.connect(self._doAdd) self.addButton.clicked.connect(self._doAdd)
self.delButton = QPushButton(self.theTheme.getIcon("remove"), "") self.delButton = QPushButton(self.mainTheme.getIcon("remove"), "")
self.delButton.clicked.connect(self._doDelete) self.delButton.clicked.connect(self._doDelete)
self.editBox = QHBoxLayout() self.editBox = QHBoxLayout()
@@ -121,13 +121,13 @@ class GuiWordList(QDialog):
""" """
newWord = self.newEntry.text().strip() newWord = self.newEntry.text().strip()
if newWord == "": if newWord == "":
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Cannot add a blank word." "Cannot add a blank word."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
if self.listBox.findItems(newWord, Qt.MatchExactly): if self.listBox.findItems(newWord, Qt.MatchExactly):
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"The word '{0}' is already in the word list." "The word '{0}' is already in the word list."
).format(newWord), nwAlert.ERROR) ).format(newWord), nwAlert.ERROR)
return False return False
+6 -6
View File
@@ -23,9 +23,9 @@ from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.gui.docviewer import GuiDocViewer, GuiDocViewDetails from novelwriter.gui.docviewer import GuiDocViewer, GuiDocViewDetails
from novelwriter.gui.itemdetails import GuiItemDetails from novelwriter.gui.itemdetails import GuiItemDetails
from novelwriter.gui.mainmenu import GuiMainMenu from novelwriter.gui.mainmenu import GuiMainMenu
from novelwriter.gui.noveltree import GuiNovelTree from novelwriter.gui.noveltree import GuiNovelView
from novelwriter.gui.outline import GuiOutline from novelwriter.gui.outline import GuiOutlineView
from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.projtree import GuiProjectView
from novelwriter.gui.statusbar import GuiMainStatus from novelwriter.gui.statusbar import GuiMainStatus
from novelwriter.gui.theme import GuiTheme from novelwriter.gui.theme import GuiTheme
from novelwriter.gui.viewsbar import GuiViewsBar from novelwriter.gui.viewsbar import GuiViewsBar
@@ -37,9 +37,9 @@ __all__ = [
"GuiItemDetails", "GuiItemDetails",
"GuiMainMenu", "GuiMainMenu",
"GuiMainStatus", "GuiMainStatus",
"GuiNovelTree", "GuiNovelView",
"GuiOutline", "GuiOutlineView",
"GuiProjectTree", "GuiProjectView",
"GuiTheme", "GuiTheme",
"GuiViewsBar", "GuiViewsBar",
] ]
+4 -4
View File
@@ -376,8 +376,8 @@ class QSwitch(QAbstractButton):
class PagedDialog(QDialog): class PagedDialog(QDialog):
def __init__(self, theParent=None): def __init__(self, parent=None):
QDialog.__init__(self, parent=theParent) QDialog.__init__(self, parent=parent)
self._tabBar = VerticalTabBar(self) self._tabBar = VerticalTabBar(self)
self._tabBar.setExpanding(False) self._tabBar.setExpanding(False)
@@ -426,8 +426,8 @@ class PagedDialog(QDialog):
class VerticalTabBar(QTabBar): class VerticalTabBar(QTabBar):
def __init__(self, theParent=None): def __init__(self, parent=None):
QTabBar.__init__(self, parent=theParent) QTabBar.__init__(self, parent=parent)
self._mW = novelwriter.CONFIG.pxInt(150) self._mW = novelwriter.CONFIG.pxInt(150)
return return
+81 -79
View File
@@ -72,16 +72,16 @@ class GuiDocEditor(QTextEdit):
docCountsChanged = pyqtSignal(str, int, int, int) docCountsChanged = pyqtSignal(str, int, int, int)
loadDocumentTagRequest = pyqtSignal(str, Enum) loadDocumentTagRequest = pyqtSignal(str, Enum)
def __init__(self, theParent): def __init__(self, mainGui):
QTextEdit.__init__(self, theParent) QTextEdit.__init__(self, mainGui)
logger.debug("Initialising GuiDocEditor ...") logger.debug("Initialising GuiDocEditor ...")
# Class Variables # Class Variables
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self._nwDocument = None self._nwDocument = None
self._nwItem = None self._nwItem = None
@@ -124,7 +124,7 @@ class GuiDocEditor(QTextEdit):
# Syntax # Syntax
self.spEnchant = NWSpellEnchant() self.spEnchant = NWSpellEnchant()
self.highLight = GuiDocHighlighter(qDoc, self.theParent, self.spEnchant) self.highLight = GuiDocHighlighter(qDoc, self.mainGui, self.spEnchant)
# Context Menu # Context Menu
self.setContextMenuPolicy(Qt.CustomContextMenu) self.setContextMenuPolicy(Qt.CustomContextMenu)
@@ -239,10 +239,10 @@ class GuiDocEditor(QTextEdit):
if self.mainConf.textFont is None: if self.mainConf.textFont is None:
# If none is defined, set a default font # If none is defined, set a default font
theFont = QFont() theFont = QFont()
if self.mainConf.osWindows and "Arial" in self.theTheme.guiFontDB.families(): if self.mainConf.osWindows and "Arial" in self.mainTheme.guiFontDB.families():
theFont.setFamily("Arial") theFont.setFamily("Arial")
theFont.setPointSize(12) theFont.setPointSize(12)
elif self.mainConf.osDarwin and "Courier" in self.theTheme.guiFontDB.families(): elif self.mainConf.osDarwin and "Courier" in self.mainTheme.guiFontDB.families():
theFont.setFamily("Courier") theFont.setFamily("Courier")
theFont.setPointSize(12) theFont.setPointSize(12)
else: else:
@@ -257,14 +257,14 @@ class GuiDocEditor(QTextEdit):
# Set the widget colours to match syntax theme # Set the widget colours to match syntax theme
mainPalette = self.palette() mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack)) mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(mainPalette) self.setPalette(mainPalette)
docPalette = self.viewport().palette() docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack)) docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.viewport().setPalette(docPalette) self.viewport().setPalette(docPalette)
self.docHeader.matchColours() self.docHeader.matchColours()
@@ -341,7 +341,7 @@ class GuiDocEditor(QTextEdit):
docSize = len(theDoc) docSize = len(theDoc)
if docSize > nwConst.MAX_DOCSIZE: if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"The document you are trying to open is too big. " "The document you are trying to open is too big. "
"The document size is {0} MB. " "The document size is {0} MB. "
"The maximum size allowed is {1} MB." "The maximum size allowed is {1} MB."
@@ -417,7 +417,7 @@ class GuiDocEditor(QTextEdit):
# Update the status bar # Update the status bar
if self._nwItem is not None: if self._nwItem is not None:
self.theParent.setStatus( self.mainGui.setStatus(
self.tr("Opened Document: {0}").format(self._nwItem.itemName) self.tr("Opened Document: {0}").format(self._nwItem.itemName)
) )
@@ -442,7 +442,7 @@ class GuiDocEditor(QTextEdit):
""" """
docSize = len(theText) docSize = len(theText)
if docSize > nwConst.MAX_DOCSIZE: if docSize > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"The text you are trying to add is too big. " "The text you are trying to add is too big. "
"The text size is {0} MB. " "The text size is {0} MB. "
"The maximum size allowed is {1} MB." "The maximum size allowed is {1} MB."
@@ -488,7 +488,7 @@ class GuiDocEditor(QTextEdit):
if not self._nwDocument.writeDocument(docText): if not self._nwDocument.writeDocument(docText):
saveOk = False saveOk = False
if self._nwDocument._currHash != self._nwDocument._prevHash: if self._nwDocument._currHash != self._nwDocument._prevHash:
msgYes = self.theParent.askQuestion( msgYes = self.mainGui.askQuestion(
self.tr("File Changed on Disk"), self.tr("File Changed on Disk"),
self.tr( self.tr(
"This document has been changed outside of novelWriter " "This document has been changed outside of novelWriter "
@@ -499,7 +499,7 @@ class GuiDocEditor(QTextEdit):
saveOk = self._nwDocument.writeDocument(docText, forceWrite=True) saveOk = self._nwDocument.writeDocument(docText, forceWrite=True)
if not saveOk: if not saveOk:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("Could not save document."), self._nwDocument.getError() self.tr("Could not save document."), self._nwDocument.getError()
], nwAlert.ERROR) ], nwAlert.ERROR)
@@ -511,18 +511,20 @@ class GuiDocEditor(QTextEdit):
self.theProject.index.scanText(tHandle, docText) self.theProject.index.scanText(tHandle, docText)
newHeader = self.theProject.index.getHandleHeaderLevel(tHandle) newHeader = self.theProject.index.getHandleHeaderLevel(tHandle)
# ToDo: This should be a signal
if self._updateHeaders(checkLevel=True): if self._updateHeaders(checkLevel=True):
self.theParent.requestNovelTreeRefresh() self.mainGui.requestNovelTreeRefresh()
else: else:
self.theParent.novelView.updateWordCounts(tHandle) self.mainGui.novelView.updateWordCounts(tHandle)
# ToDo: This should be a signal
if oldHeader != newHeader: if oldHeader != newHeader:
self.theParent.treeView.setTreeItemValues(tHandle) self.mainGui.projView.setTreeItemValues(tHandle)
self.theParent.treeMeta.updateViewBox(tHandle) self.mainGui.itemDetails.updateViewBox(tHandle)
self.docFooter.updateInfo() self.docFooter.updateInfo()
# Update the status bar # Update the status bar
self.theParent.setStatus( self.mainGui.setStatus(
self.tr("Saved Document: {0}").format(self._nwItem.itemName) self.tr("Saved Document: {0}").format(self._nwItem.itemName)
) )
@@ -544,8 +546,8 @@ class GuiDocEditor(QTextEdit):
sH = hBar.height() if hBar.isVisible() else 0 sH = hBar.height() if hBar.isVisible() else 0
tM = cM tM = cM
if self.mainConf.textWidth > 0 or self.theParent.isFocusMode: if self.mainConf.textWidth > 0 or self.mainGui.isFocusMode:
tW = self.mainConf.getTextWidth(self.theParent.isFocusMode) tW = self.mainConf.getTextWidth(self.mainGui.isFocusMode)
tM = max((wW - sW - tW)//2, cM) tM = max((wW - sW - tW)//2, cM)
tB = self.frameWidth() tB = self.frameWidth()
@@ -704,7 +706,7 @@ class GuiDocEditor(QTextEdit):
if not self.mainConf.hasEnchant: if not self.mainConf.hasEnchant:
if theMode: if theMode:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Spell checking requires the package PyEnchant. " "Spell checking requires the package PyEnchant. "
"It does not appear to be installed." "It does not appear to be installed."
), nwAlert.INFO) ), nwAlert.INFO)
@@ -714,7 +716,7 @@ class GuiDocEditor(QTextEdit):
theMode = False theMode = False
self._spellCheck = theMode self._spellCheck = theMode
self.theParent.mainMenu.setSpellCheck(theMode) self.mainGui.mainMenu.setSpellCheck(theMode)
self.theProject.setSpellCheck(theMode) self.theProject.setSpellCheck(theMode)
self.highLight.setSpellCheck(theMode) self.highLight.setSpellCheck(theMode)
if not self._bigDoc: if not self._bigDoc:
@@ -742,7 +744,7 @@ class GuiDocEditor(QTextEdit):
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
afTime = time() afTime = time()
logger.debug("Document highlighted in %.3f ms", 1000*(afTime-bfTime)) logger.debug("Document highlighted in %.3f ms", 1000*(afTime-bfTime))
self.theParent.statusBar.setStatus(self.tr("Spell check complete")) self.mainGui.statusBar.setStatus(self.tr("Spell check complete"))
return True return True
@@ -1085,7 +1087,7 @@ class GuiDocEditor(QTextEdit):
self._lastFind = None self._lastFind = None
if self.document().characterCount() > nwConst.MAX_DOCSIZE: if self.document().characterCount() > nwConst.MAX_DOCSIZE:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"The document has grown too big and you cannot add more text to it. " "The document has grown too big and you cannot add more text to it. "
"The maximum size of a single novelWriter document is {0} MB." "The maximum size of a single novelWriter document is {0} MB."
).format( ).format(
@@ -1245,7 +1247,7 @@ class GuiDocEditor(QTextEdit):
if time() - self._lastEdit < 5 * self.wcInterval: if time() - self._lastEdit < 5 * self.wcInterval:
logger.verbose("Running word counter") logger.verbose("Running word counter")
self.theParent.threadPool.start(self.wCounterDoc) self.mainGui.threadPool.start(self.wCounterDoc)
return return
@@ -1302,7 +1304,7 @@ class GuiDocEditor(QTextEdit):
logger.verbose("Selection word counter is busy") logger.verbose("Selection word counter is busy")
return return
self.theParent.threadPool.start(self.wCounterSel) self.mainGui.threadPool.start(self.wCounterSel)
return return
@@ -1381,7 +1383,7 @@ class GuiDocEditor(QTextEdit):
self.docSearch.setResultCount(0, 0) self.docSearch.setResultCount(0, 0)
self._lastFind = None self._lastFind = None
if self.docSearch.doNextFile and not goBack: if self.docSearch.doNextFile and not goBack:
self.theParent.openNextDocument( self.mainGui.openNextDocument(
self._docHandle, wrapAround=self.docSearch.doLoop self._docHandle, wrapAround=self.docSearch.doLoop
) )
self.beginSearch() self.beginSearch()
@@ -1401,7 +1403,7 @@ class GuiDocEditor(QTextEdit):
if resIdx > maxIdx: if resIdx > maxIdx:
if self.docSearch.doNextFile and not goBack: if self.docSearch.doNextFile and not goBack:
self.theParent.openNextDocument( self.mainGui.openNextDocument(
self._docHandle, wrapAround=self.docSearch.doLoop self._docHandle, wrapAround=self.docSearch.doLoop
) )
self.beginSearch() self.beginSearch()
@@ -1645,7 +1647,7 @@ class GuiDocEditor(QTextEdit):
""" """
theCursor = self.textCursor() theCursor = self.textCursor()
if not theCursor.hasSelection(): if not theCursor.hasSelection():
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Please select some text before calling replace quotes." "Please select some text before calling replace quotes."
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
@@ -2187,9 +2189,9 @@ class GuiDocEditSearch(QFrame):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.docEditor = docEditor self.docEditor = docEditor
self.theParent = docEditor.theParent self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject self.theProject = docEditor.theProject
self.theTheme = docEditor.theTheme self.mainTheme = docEditor.mainTheme
self.repVisible = False self.repVisible = False
self.isCaseSense = self.mainConf.searchCase self.isCaseSense = self.mainConf.searchCase
@@ -2200,9 +2202,9 @@ class GuiDocEditSearch(QFrame):
self.doMatchCap = self.mainConf.searchMatchCap self.doMatchCap = self.mainConf.searchMatchCap
mPx = self.mainConf.pxInt(6) mPx = self.mainConf.pxInt(6)
tPx = int(0.8*self.theTheme.fontPixelSize) tPx = int(0.8*self.mainTheme.fontPixelSize)
self.boxFont = self.theTheme.guiFont self.boxFont = self.mainTheme.guiFont
self.boxFont.setPointSizeF(0.9*self.theTheme.fontPointSize) self.boxFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
@@ -2236,38 +2238,38 @@ class GuiDocEditSearch(QFrame):
self.resultLabel = QLabel("?/?") self.resultLabel = QLabel("?/?")
self.resultLabel.setFont(self.boxFont) self.resultLabel.setFont(self.boxFont)
self.resultLabel.setMinimumWidth(self.theTheme.getTextWidth("?/?", self.boxFont)) self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont))
self.toggleCase = QAction(self.tr("Case Sensitive"), self) self.toggleCase = QAction(self.tr("Case Sensitive"), self)
self.toggleCase.setIcon(self.theTheme.getIcon("search_case")) self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True) self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense) self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase) self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase) self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction(self.tr("Whole Words Only"), self) self.toggleWord = QAction(self.tr("Whole Words Only"), self)
self.toggleWord.setIcon(self.theTheme.getIcon("search_word")) self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True) self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord) self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord) self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord) self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self) self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
self.toggleRegEx.setIcon(self.theTheme.getIcon("search_regex")) self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True) self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx) self.toggleRegEx.setChecked(self.isRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx) self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx) self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction(self.tr("Loop Search"), self) self.toggleLoop = QAction(self.tr("Loop Search"), self)
self.toggleLoop.setIcon(self.theTheme.getIcon("search_loop")) self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True) self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop) self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop) self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop) self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction(self.tr("Search Next File"), self) self.toggleProject = QAction(self.tr("Search Next File"), self)
self.toggleProject.setIcon(self.theTheme.getIcon("search_project")) self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True) self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile) self.toggleProject.setChecked(self.doNextFile)
self.toggleProject.toggled.connect(self._doToggleProject) self.toggleProject.toggled.connect(self._doToggleProject)
@@ -2276,7 +2278,7 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self) self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
self.toggleMatchCap.setIcon(self.theTheme.getIcon("search_preserve")) self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True) self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap) self.toggleMatchCap.setChecked(self.doMatchCap)
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap) self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
@@ -2285,7 +2287,7 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.cancelSearch = QAction(self.tr("Close Search"), self) self.cancelSearch = QAction(self.tr("Close Search"), self)
self.cancelSearch.setIcon(self.theTheme.getIcon("search_cancel")) self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose) self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch) self.searchOpt.addAction(self.cancelSearch)
@@ -2300,12 +2302,12 @@ class GuiDocEditSearch(QFrame):
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}") self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace) self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "") self.searchButton = QPushButton(self.mainTheme.getIcon("search"), "")
self.searchButton.setFixedSize(QSize(bPx, bPx)) self.searchButton.setFixedSize(QSize(bPx, bPx))
self.searchButton.setToolTip(self.tr("Find in current document")) self.searchButton.setToolTip(self.tr("Find in current document"))
self.searchButton.clicked.connect(self._doSearch) self.searchButton.clicked.connect(self._doSearch)
self.replaceButton = QPushButton(self.theTheme.getIcon("search_replace"), "") self.replaceButton = QPushButton(self.mainTheme.getIcon("search_replace"), "")
self.replaceButton.setFixedSize(QSize(bPx, bPx)) self.replaceButton.setFixedSize(QSize(bPx, bPx))
self.replaceButton.setToolTip(self.tr("Find and replace in current document")) self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
self.replaceButton.clicked.connect(self._doReplace) self.replaceButton.clicked.connect(self._doReplace)
@@ -2424,7 +2426,7 @@ class GuiDocEditSearch(QFrame):
""" """
currRes = "?" if currRes is None else currRes currRes = "?" if currRes is None else currRes
resCount = "?" if resCount is None else "1000+" if resCount > 1000 else resCount resCount = "?" if resCount is None else "1000+" if resCount > 1000 else resCount
minWidth = self.theTheme.getTextWidth(f"{resCount}//{resCount}", self.boxFont) minWidth = self.mainTheme.getTextWidth(f"{resCount}//{resCount}", self.boxFont)
self.resultLabel.setText(f"{currRes}/{resCount}") self.resultLabel.setText(f"{currRes}/{resCount}")
self.resultLabel.setMinimumWidth(minWidth) self.resultLabel.setMinimumWidth(minWidth)
self.adjustSize() self.adjustSize()
@@ -2575,13 +2577,13 @@ class GuiDocEditHeader(QWidget):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.docEditor = docEditor self.docEditor = docEditor
self.theParent = docEditor.theParent self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject self.theProject = docEditor.theProject
self.theTheme = docEditor.theTheme self.mainTheme = docEditor.mainTheme
self._docHandle = None self._docHandle = None
fPx = int(0.9*self.theTheme.fontPixelSize) fPx = int(0.9*self.mainTheme.fontPixelSize)
hSp = self.mainConf.pxInt(6) hSp = self.mainConf.pxInt(6)
# Main Widget Settings # Main Widget Settings
@@ -2598,17 +2600,17 @@ class GuiDocEditHeader(QWidget):
self.theTitle.setFixedHeight(fPx) self.theTitle.setFixedHeight(fPx)
lblFont = self.theTitle.font() lblFont = self.theTitle.font()
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
buttonStyle = ( buttonStyle = (
"QToolButton {{border: none; background: transparent;}} " "QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.theTheme.colText) ).format(*self.mainTheme.colText)
# Buttons # Buttons
self.editButton = QToolButton(self) self.editButton = QToolButton(self)
self.editButton.setIcon(self.theTheme.getIcon("edit")) self.editButton.setIcon(self.mainTheme.getIcon("edit"))
self.editButton.setContentsMargins(0, 0, 0, 0) self.editButton.setContentsMargins(0, 0, 0, 0)
self.editButton.setIconSize(QSize(fPx, fPx)) self.editButton.setIconSize(QSize(fPx, fPx))
self.editButton.setFixedSize(fPx, fPx) self.editButton.setFixedSize(fPx, fPx)
@@ -2619,7 +2621,7 @@ class GuiDocEditHeader(QWidget):
self.editButton.clicked.connect(self._editDocument) self.editButton.clicked.connect(self._editDocument)
self.searchButton = QToolButton(self) self.searchButton = QToolButton(self)
self.searchButton.setIcon(self.theTheme.getIcon("search")) self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.searchButton.setContentsMargins(0, 0, 0, 0) self.searchButton.setContentsMargins(0, 0, 0, 0)
self.searchButton.setIconSize(QSize(fPx, fPx)) self.searchButton.setIconSize(QSize(fPx, fPx))
self.searchButton.setFixedSize(fPx, fPx) self.searchButton.setFixedSize(fPx, fPx)
@@ -2630,7 +2632,7 @@ class GuiDocEditHeader(QWidget):
self.searchButton.clicked.connect(self._searchDocument) self.searchButton.clicked.connect(self._searchDocument)
self.minmaxButton = QToolButton(self) self.minmaxButton = QToolButton(self)
self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
self.minmaxButton.setContentsMargins(0, 0, 0, 0) self.minmaxButton.setContentsMargins(0, 0, 0, 0)
self.minmaxButton.setIconSize(QSize(fPx, fPx)) self.minmaxButton.setIconSize(QSize(fPx, fPx))
self.minmaxButton.setFixedSize(fPx, fPx) self.minmaxButton.setFixedSize(fPx, fPx)
@@ -2641,7 +2643,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.clicked.connect(self._minmaxDocument) self.minmaxButton.clicked.connect(self._minmaxDocument)
self.closeButton = QToolButton(self) self.closeButton = QToolButton(self)
self.closeButton.setIcon(self.theTheme.getIcon("close")) self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.closeButton.setContentsMargins(0, 0, 0, 0) self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx)) self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx) self.closeButton.setFixedSize(fPx, fPx)
@@ -2684,9 +2686,9 @@ class GuiDocEditHeader(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.theTitle.setPalette(thePalette) self.theTitle.setPalette(thePalette)
@@ -2733,10 +2735,10 @@ class GuiDocEditHeader(QWidget):
This function is called by the GuiMain class via the This function is called by the GuiMain class via the
toggleFocusMode function and should not be activated directly. toggleFocusMode function and should not be activated directly.
""" """
if self.theParent.isFocusMode: if self.mainGui.isFocusMode:
self.minmaxButton.setIcon(self.theTheme.getIcon("minimise")) self.minmaxButton.setIcon(self.mainTheme.getIcon("minimise"))
else: else:
self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
return return
## ##
@@ -2746,7 +2748,7 @@ class GuiDocEditHeader(QWidget):
def _editDocument(self): def _editDocument(self):
"""Open the edit item dialog from the main GUI. """Open the edit item dialog from the main GUI.
""" """
self.theParent.editItem(self._docHandle) self.mainGui.editItemLabel(self._docHandle)
return return
def _searchDocument(self): def _searchDocument(self):
@@ -2758,7 +2760,7 @@ class GuiDocEditHeader(QWidget):
def _closeDocument(self): def _closeDocument(self):
"""Trigger the close editor on the main window. """Trigger the close editor on the main window.
""" """
self.theParent.closeDocEditor() self.mainGui.closeDocEditor()
self.editButton.setVisible(False) self.editButton.setVisible(False)
self.searchButton.setVisible(False) self.searchButton.setVisible(False)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
@@ -2768,7 +2770,7 @@ class GuiDocEditHeader(QWidget):
def _minmaxDocument(self): def _minmaxDocument(self):
"""Switch on or off Focus Mode. """Switch on or off Focus Mode.
""" """
self.theParent.toggleFocusMode() self.mainGui.toggleFocusMode()
return return
## ##
@@ -2779,7 +2781,7 @@ class GuiDocEditHeader(QWidget):
"""Capture a click on the title and ensure that the item is """Capture a click on the title and ensure that the item is
selected in the project tree. selected in the project tree.
""" """
self.theParent.treeView.setSelectedHandle(self._docHandle, doScroll=True) self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True)
return return
# END Class GuiDocEditHeader # END Class GuiDocEditHeader
@@ -2799,22 +2801,22 @@ class GuiDocEditFooter(QWidget):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.docEditor = docEditor self.docEditor = docEditor
self.theParent = docEditor.theParent self.mainGui = docEditor.mainGui
self.theProject = docEditor.theProject self.theProject = docEditor.theProject
self.theTheme = docEditor.theTheme self.mainTheme = docEditor.mainTheme
self._theItem = None self._theItem = None
self._docHandle = None self._docHandle = None
self._docSelection = False self._docSelection = False
self.sPx = int(round(0.9*self.theTheme.baseIconSize)) self.sPx = int(round(0.9*self.mainTheme.baseIconSize))
fPx = int(0.9*self.theTheme.fontPixelSize) fPx = int(0.9*self.mainTheme.fontPixelSize)
bSp = self.mainConf.pxInt(4) bSp = self.mainConf.pxInt(4)
hSp = self.mainConf.pxInt(6) hSp = self.mainConf.pxInt(6)
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
# Main Widget Settings # Main Widget Settings
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
@@ -2837,7 +2839,7 @@ class GuiDocEditFooter(QWidget):
# Lines # Lines
self.linesIcon = QLabel("") self.linesIcon = QLabel("")
self.linesIcon.setPixmap(self.theTheme.getPixmap("status_lines", (self.sPx, self.sPx))) self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.linesIcon.setContentsMargins(0, 0, 0, 0) self.linesIcon.setContentsMargins(0, 0, 0, 0)
self.linesIcon.setFixedHeight(self.sPx) self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
@@ -2853,7 +2855,7 @@ class GuiDocEditFooter(QWidget):
# Words # Words
self.wordsIcon = QLabel("") self.wordsIcon = QLabel("")
self.wordsIcon.setPixmap(self.theTheme.getPixmap("status_stats", (self.sPx, self.sPx))) self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.wordsIcon.setContentsMargins(0, 0, 0, 0) self.wordsIcon.setContentsMargins(0, 0, 0, 0)
self.wordsIcon.setFixedHeight(self.sPx) self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
@@ -2905,9 +2907,9 @@ class GuiDocEditFooter(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.statusText.setPalette(thePalette) self.statusText.setPalette(thePalette)
+19 -19
View File
@@ -46,16 +46,16 @@ class GuiDocHighlighter(QSyntaxHighlighter):
BLOCK_META = 2 BLOCK_META = 2
BLOCK_TITLE = 4 BLOCK_TITLE = 4
def __init__(self, theDoc, theParent, spEnchant): def __init__(self, theDoc, mainGui, spEnchant):
QSyntaxHighlighter.__init__(self, theDoc) QSyntaxHighlighter.__init__(self, theDoc)
logger.debug("Initialising GuiDocHighlighter ...") logger.debug("Initialising GuiDocHighlighter ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theDoc = theDoc self.theDoc = theDoc
self.spEnchant = spEnchant self.spEnchant = spEnchant
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.theHandle = None self.theHandle = None
self.spellCheck = False self.spellCheck = False
self.spellRx = None self.spellRx = None
@@ -87,24 +87,24 @@ class GuiDocHighlighter(QSyntaxHighlighter):
""" """
logger.debug("Setting up highlighting rules") logger.debug("Setting up highlighting rules")
self.colHead = QColor(*self.theTheme.colHead) self.colHead = QColor(*self.mainTheme.colHead)
self.colHeadH = QColor(*self.theTheme.colHeadH) self.colHeadH = QColor(*self.mainTheme.colHeadH)
self.colDialN = QColor(*self.theTheme.colDialN) self.colDialN = QColor(*self.mainTheme.colDialN)
self.colDialD = QColor(*self.theTheme.colDialD) self.colDialD = QColor(*self.mainTheme.colDialD)
self.colDialS = QColor(*self.theTheme.colDialS) self.colDialS = QColor(*self.mainTheme.colDialS)
self.colHidden = QColor(*self.theTheme.colHidden) self.colHidden = QColor(*self.mainTheme.colHidden)
self.colKey = QColor(*self.theTheme.colKey) self.colKey = QColor(*self.mainTheme.colKey)
self.colVal = QColor(*self.theTheme.colVal) self.colVal = QColor(*self.mainTheme.colVal)
self.colSpell = QColor(*self.theTheme.colSpell) self.colSpell = QColor(*self.mainTheme.colSpell)
self.colError = QColor(*self.theTheme.colError) self.colError = QColor(*self.mainTheme.colError)
self.colRepTag = QColor(*self.theTheme.colRepTag) self.colRepTag = QColor(*self.mainTheme.colRepTag)
self.colMod = QColor(*self.theTheme.colMod) self.colMod = QColor(*self.mainTheme.colMod)
self.colBreak = QColor(*self.theTheme.colEmph) self.colBreak = QColor(*self.mainTheme.colEmph)
self.colBreak.setAlpha(64) self.colBreak.setAlpha(64)
self.colEmph = None self.colEmph = None
if self.mainConf.highlightEmph: if self.mainConf.highlightEmph:
self.colEmph = QColor(*self.theTheme.colEmph) self.colEmph = QColor(*self.mainTheme.colEmph)
self.hStyles = { self.hStyles = {
"header1": self._makeFormat(self.colHead, "bold", 1.8), "header1": self._makeFormat(self.colHead, "bold", 1.8),
@@ -288,7 +288,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if theText.startswith("@"): # Keywords and commands if theText.startswith("@"): # Keywords and commands
self.setCurrentBlockState(self.BLOCK_META) self.setCurrentBlockState(self.BLOCK_META)
pIndex = self.theProject.index pIndex = self.theProject.index
tItem = self.theParent.theProject.tree[self.theHandle] tItem = self.mainGui.theProject.tree[self.theHandle]
isValid, theBits, thePos = pIndex.scanThis(theText) isValid, theBits, thePos = pIndex.scanThis(theText)
isGood = pIndex.checkThese(theBits, tItem) isGood = pIndex.checkThese(theBits, tItem)
if isValid: if isValid:
+70 -70
View File
@@ -53,16 +53,16 @@ class GuiDocViewer(QTextBrowser):
loadDocumentTagRequest = pyqtSignal(str, Enum) loadDocumentTagRequest = pyqtSignal(str, Enum)
def __init__(self, theParent): def __init__(self, mainGui):
QTextBrowser.__init__(self, theParent) QTextBrowser.__init__(self, mainGui)
logger.debug("Initialising GuiDocViewer ...") logger.debug("Initialising GuiDocViewer ...")
# Class Variables # Class Variables
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theParent.theProject self.theProject = mainGui.theProject
# Internal Variables # Internal Variables
self._docHandle = None self._docHandle = None
@@ -118,14 +118,14 @@ class GuiDocViewer(QTextBrowser):
# Set the widget colours to match syntax theme # Set the widget colours to match syntax theme
mainPalette = self.palette() mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack)) mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(mainPalette) self.setPalette(mainPalette)
docPalette = self.viewport().palette() docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack)) docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.viewport().setPalette(docPalette) self.viewport().setPalette(docPalette)
self.docHeader.matchColours() self.docHeader.matchColours()
@@ -221,7 +221,7 @@ class GuiDocViewer(QTextBrowser):
self.updateDocMargins() self.updateDocMargins()
# Make sure the main GUI knows we changed the content # Make sure the main GUI knows we changed the content
self.theParent.viewMeta.refreshReferences(tHandle) self.mainGui.viewMeta.refreshReferences(tHandle)
# Since we change the content while it may still be rendering, we mark # Since we change the content while it may still be rendering, we mark
# the document dirty again to make sure it's re-rendered properly. # the document dirty again to make sure it's re-rendered properly.
@@ -539,27 +539,27 @@ class GuiDocViewer(QTextBrowser):
" text-align: center;" " text-align: center;"
"}}\n" "}}\n"
).format( ).format(
tColR=self.theTheme.colText[0], tColR=self.mainTheme.colText[0],
tColG=self.theTheme.colText[1], tColG=self.mainTheme.colText[1],
tColB=self.theTheme.colText[2], tColB=self.mainTheme.colText[2],
hColR=self.theTheme.colHead[0], hColR=self.mainTheme.colHead[0],
hColG=self.theTheme.colHead[1], hColG=self.mainTheme.colHead[1],
hColB=self.theTheme.colHead[2], hColB=self.mainTheme.colHead[2],
aColR=self.theTheme.colVal[0], aColR=self.mainTheme.colVal[0],
aColG=self.theTheme.colVal[1], aColG=self.mainTheme.colVal[1],
aColB=self.theTheme.colVal[2], aColB=self.mainTheme.colVal[2],
eColR=self.theTheme.colEmph[0], eColR=self.mainTheme.colEmph[0],
eColG=self.theTheme.colEmph[1], eColG=self.mainTheme.colEmph[1],
eColB=self.theTheme.colEmph[2], eColB=self.mainTheme.colEmph[2],
kColR=self.theTheme.colKey[0], kColR=self.mainTheme.colKey[0],
kColG=self.theTheme.colKey[1], kColG=self.mainTheme.colKey[1],
kColB=self.theTheme.colKey[2], kColB=self.mainTheme.colKey[2],
cColR=self.theTheme.colHidden[0], cColR=self.mainTheme.colHidden[0],
cColG=self.theTheme.colHidden[1], cColG=self.mainTheme.colHidden[1],
cColB=self.theTheme.colHidden[2], cColB=self.mainTheme.colHidden[2],
mColR=self.theTheme.colMod[0], mColR=self.mainTheme.colMod[0],
mColG=self.theTheme.colMod[1], mColG=self.mainTheme.colMod[1],
mColB=self.theTheme.colMod[2], mColB=self.mainTheme.colMod[2],
) )
self.document().setDefaultStyleSheet(styleSheet) self.document().setDefaultStyleSheet(styleSheet)
@@ -714,14 +714,14 @@ class GuiDocViewHeader(QWidget):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.docViewer = docViewer self.docViewer = docViewer
self.theParent = docViewer.theParent self.mainGui = docViewer.mainGui
self.theProject = docViewer.theProject self.theProject = docViewer.theProject
self.theTheme = docViewer.theTheme self.mainTheme = docViewer.mainTheme
# Internal Variables # Internal Variables
self._docHandle = None self._docHandle = None
fPx = int(0.9*self.theTheme.fontPixelSize) fPx = int(0.9*self.mainTheme.fontPixelSize)
hSp = self.mainConf.pxInt(6) hSp = self.mainConf.pxInt(6)
# Main Widget Settings # Main Widget Settings
@@ -738,17 +738,17 @@ class GuiDocViewHeader(QWidget):
self.theTitle.setFixedHeight(fPx) self.theTitle.setFixedHeight(fPx)
lblFont = self.theTitle.font() lblFont = self.theTitle.font()
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
buttonStyle = ( buttonStyle = (
"QToolButton {{border: none; background: transparent;}} " "QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.theTheme.colText) ).format(*self.mainTheme.colText)
# Buttons # Buttons
self.backButton = QToolButton(self) self.backButton = QToolButton(self)
self.backButton.setIcon(self.theTheme.getIcon("backward")) self.backButton.setIcon(self.mainTheme.getIcon("backward"))
self.backButton.setContentsMargins(0, 0, 0, 0) self.backButton.setContentsMargins(0, 0, 0, 0)
self.backButton.setIconSize(QSize(fPx, fPx)) self.backButton.setIconSize(QSize(fPx, fPx))
self.backButton.setFixedSize(fPx, fPx) self.backButton.setFixedSize(fPx, fPx)
@@ -759,7 +759,7 @@ class GuiDocViewHeader(QWidget):
self.backButton.clicked.connect(self.docViewer.navBackward) self.backButton.clicked.connect(self.docViewer.navBackward)
self.forwardButton = QToolButton(self) self.forwardButton = QToolButton(self)
self.forwardButton.setIcon(self.theTheme.getIcon("forward")) self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
self.forwardButton.setContentsMargins(0, 0, 0, 0) self.forwardButton.setContentsMargins(0, 0, 0, 0)
self.forwardButton.setIconSize(QSize(fPx, fPx)) self.forwardButton.setIconSize(QSize(fPx, fPx))
self.forwardButton.setFixedSize(fPx, fPx) self.forwardButton.setFixedSize(fPx, fPx)
@@ -770,7 +770,7 @@ class GuiDocViewHeader(QWidget):
self.forwardButton.clicked.connect(self.docViewer.navForward) self.forwardButton.clicked.connect(self.docViewer.navForward)
self.refreshButton = QToolButton(self) self.refreshButton = QToolButton(self)
self.refreshButton.setIcon(self.theTheme.getIcon("refresh")) self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
self.refreshButton.setContentsMargins(0, 0, 0, 0) self.refreshButton.setContentsMargins(0, 0, 0, 0)
self.refreshButton.setIconSize(QSize(fPx, fPx)) self.refreshButton.setIconSize(QSize(fPx, fPx))
self.refreshButton.setFixedSize(fPx, fPx) self.refreshButton.setFixedSize(fPx, fPx)
@@ -781,7 +781,7 @@ class GuiDocViewHeader(QWidget):
self.refreshButton.clicked.connect(self._refreshDocument) self.refreshButton.clicked.connect(self._refreshDocument)
self.closeButton = QToolButton(self) self.closeButton = QToolButton(self)
self.closeButton.setIcon(self.theTheme.getIcon("close")) self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.closeButton.setContentsMargins(0, 0, 0, 0) self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx)) self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx) self.closeButton.setFixedSize(fPx, fPx)
@@ -824,9 +824,9 @@ class GuiDocViewHeader(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.theTitle.setPalette(thePalette) self.theTitle.setPalette(thePalette)
@@ -882,14 +882,14 @@ class GuiDocViewHeader(QWidget):
def _closeDocument(self): def _closeDocument(self):
"""Trigger the close editor/viewer on the main window. """Trigger the close editor/viewer on the main window.
""" """
self.theParent.closeDocViewer() self.mainGui.closeDocViewer()
return return
def _refreshDocument(self): def _refreshDocument(self):
"""Reload the content of the document. """Reload the content of the document.
""" """
if self.docViewer.docHandle() == self.theParent.docEditor.docHandle(): if self.docViewer.docHandle() == self.mainGui.docEditor.docHandle():
self.theParent.saveDocument() self.mainGui.saveDocument()
self.docViewer.reloadText() self.docViewer.reloadText()
return return
@@ -901,7 +901,7 @@ class GuiDocViewHeader(QWidget):
"""Capture a click on the title and ensure that the item is """Capture a click on the title and ensure that the item is
selected in the project tree. selected in the project tree.
""" """
self.theParent.treeView.setSelectedHandle(self._docHandle, doScroll=True) self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True)
return return
# END Class GuiDocViewHeader # END Class GuiDocViewHeader
@@ -921,26 +921,26 @@ class GuiDocViewFooter(QWidget):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.docViewer = docViewer self.docViewer = docViewer
self.theParent = docViewer.theParent self.mainGui = docViewer.mainGui
self.theTheme = docViewer.theTheme self.mainTheme = docViewer.mainTheme
self.viewMeta = docViewer.theParent.viewMeta self.viewMeta = docViewer.mainGui.viewMeta
# Internal Variables # Internal Variables
self._docHandle = None self._docHandle = None
fPx = int(0.9*self.theTheme.fontPixelSize) fPx = int(0.9*self.mainTheme.fontPixelSize)
bSp = self.mainConf.pxInt(2) bSp = self.mainConf.pxInt(2)
hSp = self.mainConf.pxInt(8) hSp = self.mainConf.pxInt(8)
# Icons # Icons
stickyOn = self.theTheme.getPixmap("sticky-on", (fPx, fPx)) stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
stickyOff = self.theTheme.getPixmap("sticky-off", (fPx, fPx)) stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
stickyIcon = QIcon() stickyIcon = QIcon()
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On) stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off) stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
bulletOn = self.theTheme.getPixmap("bullet-on", (fPx, fPx)) bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
bulletOff = self.theTheme.getPixmap("bullet-off", (fPx, fPx)) bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
bulletIcon = QIcon() bulletIcon = QIcon()
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On) bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off) bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
@@ -952,13 +952,13 @@ class GuiDocViewFooter(QWidget):
buttonStyle = ( buttonStyle = (
"QToolButton {{border: none; background: transparent;}} " "QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.theTheme.colText) ).format(*self.mainTheme.colText)
# Show/Hide Details # Show/Hide Details
self.showHide = QToolButton(self) self.showHide = QToolButton(self)
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.showHide.setStyleSheet(buttonStyle) self.showHide.setStyleSheet(buttonStyle)
self.showHide.setIcon(self.theTheme.getIcon("reference")) self.showHide.setIcon(self.mainTheme.getIcon("reference"))
self.showHide.setIconSize(QSize(fPx, fPx)) self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx)) self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(self._doShowHide) self.showHide.clicked.connect(self._doShowHide)
@@ -1039,7 +1039,7 @@ class GuiDocViewFooter(QWidget):
self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop)
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.lblRefs.setFont(lblFont) self.lblRefs.setFont(lblFont)
self.lblSticky.setFont(lblFont) self.lblSticky.setFont(lblFont)
self.lblComments.setFont(lblFont) self.lblComments.setFont(lblFont)
@@ -1084,9 +1084,9 @@ class GuiDocViewFooter(QWidget):
theme rather than the main GUI. theme rather than the main GUI.
""" """
thePalette = QPalette() thePalette = QPalette()
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(thePalette) self.setPalette(thePalette)
self.lblRefs.setPalette(thePalette) self.lblRefs.setPalette(thePalette)
@@ -1140,14 +1140,14 @@ class GuiDocViewFooter(QWidget):
class GuiDocViewDetails(QScrollArea): class GuiDocViewDetails(QScrollArea):
def __init__(self, theParent): def __init__(self, mainGui):
QScrollArea.__init__(self, theParent) QScrollArea.__init__(self, mainGui)
logger.debug("Initialising GuiDocViewDetails ...") logger.debug("Initialising GuiDocViewDetails ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.refList = QLabel("") self.refList = QLabel("")
self.refList.setWordWrap(True) self.refList.setWordWrap(True)
@@ -1156,7 +1156,7 @@ class GuiDocViewDetails(QScrollArea):
self.refList.linkActivated.connect(self._linkClicked) self.refList.linkActivated.connect(self._linkClicked)
self.linkStyle = "style='color: rgb({0},{1},{2})'".format( self.linkStyle = "style='color: rgb({0},{1},{2})'".format(
*self.theTheme.colLink *self.mainTheme.colLink
) )
# Assemble # Assemble
@@ -1181,7 +1181,7 @@ class GuiDocViewDetails(QScrollArea):
"""Update the current list of document references from the """Update the current list of document references from the
project index. project index.
""" """
if self.theParent.docViewer.stickyRef: if self.mainGui.docViewer.stickyRef:
return return
theRefs = self.theProject.index.getBackReferenceList(tHandle) theRefs = self.theProject.index.getBackReferenceList(tHandle)
@@ -1209,7 +1209,7 @@ class GuiDocViewDetails(QScrollArea):
if len(theLink) == 21: if len(theLink) == 21:
tHandle = theLink[:13] tHandle = theLink[:13]
tAnchor = theLink[13:] tAnchor = theLink[13:]
self.theParent.viewDocument(tHandle, tAnchor) self.mainGui.viewDocument(tHandle, tAnchor)
return return
# END Class GuiDocViewDetails # END Class GuiDocViewDetails
+15 -15
View File
@@ -38,14 +38,14 @@ logger = logging.getLogger(__name__)
class GuiItemDetails(QWidget): class GuiItemDetails(QWidget):
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
logger.debug("Initialising GuiItemDetails ...") logger.debug("Initialising GuiItemDetails ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# Internal Variables # Internal Variables
self._itemHandle = None self._itemHandle = None
@@ -54,11 +54,11 @@ class GuiItemDetails(QWidget):
hSp = self.mainConf.pxInt(6) hSp = self.mainConf.pxInt(6)
vSp = self.mainConf.pxInt(1) vSp = self.mainConf.pxInt(1)
mPx = self.mainConf.pxInt(6) mPx = self.mainConf.pxInt(6)
iPx = self.theTheme.baseIconSize iPx = self.mainTheme.baseIconSize
fPt = self.theTheme.fontPointSize fPt = self.mainTheme.fontPointSize
self._expCheck = self.theTheme.getPixmap("check", (iPx, iPx)) self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
self._expCross = self.theTheme.getPixmap("cross", (iPx, iPx)) self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
fntLabel = QFont() fntLabel = QFont()
fntLabel.setBold(True) fntLabel.setBold(True)
@@ -181,8 +181,8 @@ class GuiItemDetails(QWidget):
self.setLayout(self.mainBox) self.setLayout(self.mainBox)
# Make sure the columns for flags and counts don't resize too often # Make sure the columns for flags and counts don't resize too often
flagWidth = self.theTheme.getTextWidth("Mm", fntValue) flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
countWidth = self.theTheme.getTextWidth("99,999", fntValue) countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
self.mainBox.setColumnMinimumWidth(1, flagWidth) self.mainBox.setColumnMinimumWidth(1, flagWidth)
self.mainBox.setColumnMinimumWidth(4, countWidth) self.mainBox.setColumnMinimumWidth(4, countWidth)
@@ -238,7 +238,7 @@ class GuiItemDetails(QWidget):
return return
self._itemHandle = tHandle self._itemHandle = tHandle
iPx = int(round(0.8*self.theTheme.baseIconSize)) iPx = int(round(0.8*self.mainTheme.baseIconSize))
# Label # Label
# ===== # =====
@@ -267,7 +267,7 @@ class GuiItemDetails(QWidget):
# Class # Class
# ===== # =====
classIcon = self.theTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]) classIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass])
self.classIcon.setPixmap(classIcon.pixmap(iPx, iPx)) self.classIcon.setPixmap(classIcon.pixmap(iPx, iPx))
self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass])) self.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
@@ -275,7 +275,7 @@ class GuiItemDetails(QWidget):
# ====== # ======
hLevel = self.theProject.index.getHandleHeaderLevel(tHandle) hLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
usageIcon = self.theTheme.getItemIcon( usageIcon = self.mainTheme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
) )
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx)) self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
@@ -296,7 +296,7 @@ class GuiItemDetails(QWidget):
return return
@pyqtSlot(str, int, int, int) @pyqtSlot(str, int, int, int)
def doUpdateCounts(self, tHandle, cC, wC, pC): def updateCounts(self, tHandle, cC, wC, pC):
"""Update the counts if the handle is the same as the one we're """Update the counts if the handle is the same as the one we're
already showing. Otherwise, do nothing. already showing. Otherwise, do nothing.
""" """
+54 -107
View File
@@ -33,7 +33,7 @@ from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QMenuBar, QAction from PyQt5.QtWidgets import QMenuBar, QAction
from novelwriter.enum import nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwWidget from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget
from novelwriter.constants import trConst, nwKeyWords, nwLabels, nwUnicode from novelwriter.constants import trConst, nwKeyWords, nwLabels, nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -41,13 +41,13 @@ logger = logging.getLogger(__name__)
class GuiMainMenu(QMenuBar): class GuiMainMenu(QMenuBar):
def __init__(self, theParent): def __init__(self, mainGui):
QMenuBar.__init__(self, theParent) QMenuBar.__init__(self, mainGui)
logger.debug("Initialising GuiMainMenu ...") logger.debug("Initialising GuiMainMenu ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theProject = theParent.theProject self.theProject = mainGui.theProject
# Build Menu # Build Menu
self._buildProjectMenu() self._buildProjectMenu()
@@ -61,11 +61,9 @@ class GuiMainMenu(QMenuBar):
self._buildHelpMenu() self._buildHelpMenu()
# Function Pointers # Function Pointers
self._docAction = self.theParent.passDocumentAction self._docAction = self.mainGui.passDocumentAction
self._moveTreeItem = self.theParent.treeView.moveTreeItem self._docInsert = self.mainGui.docEditor.insertText
self._newTreeItem = self.theParent.treeView.newTreeItem self._insertKeyWord = self.mainGui.docEditor.insertKeyWord
self._docInsert = self.theParent.docEditor.insertText
self._insertKeyWord = self.theParent.docEditor.insertKeyWord
logger.debug("GuiMainMenu initialisation complete") logger.debug("GuiMainMenu initialisation complete")
@@ -96,7 +94,7 @@ class GuiMainMenu(QMenuBar):
flag is handled by the document editor class, so we make no flag is handled by the document editor class, so we make no
decision, just pass a None to the function and let it decide. decision, just pass a None to the function and let it decide.
""" """
self.theParent.docEditor.toggleSpellCheck(None) self.mainGui.docEditor.toggleSpellCheck(None)
return True return True
def _openWebsite(self, theUrl): def _openWebsite(self, theUrl):
@@ -125,25 +123,25 @@ class GuiMainMenu(QMenuBar):
# Project > New Project # Project > New Project
self.aNewProject = QAction(self.tr("New Project"), self) self.aNewProject = QAction(self.tr("New Project"), self)
self.aNewProject.triggered.connect(lambda: self.theParent.newProject(None)) self.aNewProject.triggered.connect(lambda: self.mainGui.newProject(None))
self.projMenu.addAction(self.aNewProject) self.projMenu.addAction(self.aNewProject)
# Project > Open Project # Project > Open Project
self.aOpenProject = QAction(self.tr("Open Project"), self) self.aOpenProject = QAction(self.tr("Open Project"), self)
self.aOpenProject.setShortcut("Ctrl+Shift+O") self.aOpenProject.setShortcut("Ctrl+Shift+O")
self.aOpenProject.triggered.connect(lambda: self.theParent.showProjectLoadDialog()) self.aOpenProject.triggered.connect(lambda: self.mainGui.showProjectLoadDialog())
self.projMenu.addAction(self.aOpenProject) self.projMenu.addAction(self.aOpenProject)
# Project > Save Project # Project > Save Project
self.aSaveProject = QAction(self.tr("Save Project"), self) self.aSaveProject = QAction(self.tr("Save Project"), self)
self.aSaveProject.setShortcut("Ctrl+Shift+S") self.aSaveProject.setShortcut("Ctrl+Shift+S")
self.aSaveProject.triggered.connect(lambda: self.theParent.saveProject()) self.aSaveProject.triggered.connect(lambda: self.mainGui.saveProject())
self.projMenu.addAction(self.aSaveProject) self.projMenu.addAction(self.aSaveProject)
# Project > Close Project # Project > Close Project
self.aCloseProject = QAction(self.tr("Close Project"), self) self.aCloseProject = QAction(self.tr("Close Project"), self)
self.aCloseProject.setShortcut("Ctrl+Shift+W") self.aCloseProject.setShortcut("Ctrl+Shift+W")
self.aCloseProject.triggered.connect(lambda: self.theParent.closeProject(False)) self.aCloseProject.triggered.connect(lambda: self.mainGui.closeProject(False))
self.projMenu.addAction(self.aCloseProject) self.projMenu.addAction(self.aCloseProject)
# Project > Separator # Project > Separator
@@ -152,78 +150,33 @@ class GuiMainMenu(QMenuBar):
# Project > Project Settings # Project > Project Settings
self.aProjectSettings = QAction(self.tr("Project Settings"), self) self.aProjectSettings = QAction(self.tr("Project Settings"), self)
self.aProjectSettings.setShortcut("Ctrl+Shift+,") self.aProjectSettings.setShortcut("Ctrl+Shift+,")
self.aProjectSettings.triggered.connect(lambda: self.theParent.showProjectSettingsDialog()) self.aProjectSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog())
self.projMenu.addAction(self.aProjectSettings) self.projMenu.addAction(self.aProjectSettings)
# Project > Project Details # Project > Project Details
self.aProjectDetails = QAction(self.tr("Project Details"), self) self.aProjectDetails = QAction(self.tr("Project Details"), self)
self.aProjectDetails.setShortcut("Shift+F6") self.aProjectDetails.setShortcut("Shift+F6")
self.aProjectDetails.triggered.connect(lambda: self.theParent.showProjectDetailsDialog()) self.aProjectDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
self.projMenu.addAction(self.aProjectDetails) self.projMenu.addAction(self.aProjectDetails)
# Project > Separator # Project > Separator
self.projMenu.addSeparator() self.projMenu.addSeparator()
# Project > New Root
self.rootMenu = self.projMenu.addMenu(self.tr("Create Root Folder"))
self.rootItems = {}
self.rootItems[nwItemClass.NOVEL] = QAction(self.tr("Novel Root"), self.rootMenu)
self.rootItems[nwItemClass.PLOT] = QAction(self.tr("Plot Root"), self.rootMenu)
self.rootItems[nwItemClass.CHARACTER] = QAction(self.tr("Character Root"), self.rootMenu)
self.rootItems[nwItemClass.WORLD] = QAction(self.tr("Location Root"), self.rootMenu)
self.rootItems[nwItemClass.TIMELINE] = QAction(self.tr("Timeline Root"), self.rootMenu)
self.rootItems[nwItemClass.OBJECT] = QAction(self.tr("Object Root"), self.rootMenu)
self.rootItems[nwItemClass.ENTITY] = QAction(self.tr("Entity Root"), self.rootMenu)
self.rootItems[nwItemClass.CUSTOM] = QAction(self.tr("Custom Root"), self.rootMenu)
self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Archive Root"), self.rootMenu)
for n, itemClass in enumerate(self.rootItems.keys()):
self.rootItems[itemClass].triggered.connect(
lambda n, itemClass=itemClass: self._newTreeItem(nwItemType.ROOT, itemClass)
)
self.rootMenu.addAction(self.rootItems[itemClass])
# Project > New Folder
self.aCreateFolder = QAction(self.tr("Create Folder"), self)
self.aCreateFolder.setShortcut("Ctrl+Shift+N")
self.aCreateFolder.triggered.connect(lambda: self._newTreeItem(nwItemType.FOLDER))
self.projMenu.addAction(self.aCreateFolder)
# Project > Separator
self.projMenu.addSeparator()
# Project > Edit # Project > Edit
self.aEditItem = QAction(self.tr("Edit Item"), self) self.aEditItem = QAction(self.tr("Rename Item"), self)
self.aEditItem.setShortcuts(["Ctrl+E", "F2"]) self.aEditItem.setShortcuts(["F2"])
self.aEditItem.triggered.connect(lambda: self.theParent.editItem(None)) self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None))
self.projMenu.addAction(self.aEditItem) self.projMenu.addAction(self.aEditItem)
# Project > Delete # Project > Delete
self.aDeleteItem = QAction(self.tr("Delete Item"), self) self.aDeleteItem = QAction(self.tr("Delete Item"), self)
self.aDeleteItem.setShortcut("Ctrl+Shift+Del") self.aDeleteItem.setShortcut("Ctrl+Shift+Del")
self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None)) self.aDeleteItem.triggered.connect(lambda: self.mainGui.projView.deleteItem(None))
self.projMenu.addAction(self.aDeleteItem) self.projMenu.addAction(self.aDeleteItem)
# Project > Move Up
self.aMoveUp = QAction(self.tr("Move Item Up"), self)
self.aMoveUp.setShortcut("Ctrl+Up")
self.aMoveUp.triggered.connect(lambda: self._moveTreeItem(-1))
self.projMenu.addAction(self.aMoveUp)
# Project > Move Down
self.aMoveDown = QAction(self.tr("Move Item Down"), self)
self.aMoveDown.setShortcut("Ctrl+Down")
self.aMoveDown.triggered.connect(lambda: self._moveTreeItem(1))
self.projMenu.addAction(self.aMoveDown)
# Project > Undo Last Action
self.aMoveUndo = QAction(self.tr("Undo Last Move"), self)
self.aMoveUndo.setShortcut("Ctrl+Shift+Z")
self.aMoveUndo.triggered.connect(lambda: self.theParent.treeView.undoLastMove())
self.projMenu.addAction(self.aMoveUndo)
# Project > Empty Trash # Project > Empty Trash
self.aEmptyTrash = QAction(self.tr("Empty Trash"), self) self.aEmptyTrash = QAction(self.tr("Empty Trash"), self)
self.aEmptyTrash.triggered.connect(lambda: self.theParent.treeView.emptyTrash()) self.aEmptyTrash.triggered.connect(lambda: self.mainGui.projView.emptyTrash())
self.projMenu.addAction(self.aEmptyTrash) self.projMenu.addAction(self.aEmptyTrash)
# Project > Separator # Project > Separator
@@ -233,7 +186,7 @@ class GuiMainMenu(QMenuBar):
self.aExitNW = QAction(self.tr("Exit"), self) self.aExitNW = QAction(self.tr("Exit"), self)
self.aExitNW.setShortcut("Ctrl+Q") self.aExitNW.setShortcut("Ctrl+Q")
self.aExitNW.setMenuRole(QAction.QuitRole) self.aExitNW.setMenuRole(QAction.QuitRole)
self.aExitNW.triggered.connect(lambda: self.theParent.closeMain()) self.aExitNW.triggered.connect(lambda: self.mainGui.closeMain())
self.projMenu.addAction(self.aExitNW) self.projMenu.addAction(self.aExitNW)
return return
@@ -244,28 +197,22 @@ class GuiMainMenu(QMenuBar):
# Document # Document
self.docuMenu = self.addMenu(self.tr("&Document")) self.docuMenu = self.addMenu(self.tr("&Document"))
# Document > New
self.aNewDoc = QAction(self.tr("New Document"), self)
self.aNewDoc.setShortcut("Ctrl+N")
self.aNewDoc.triggered.connect(lambda: self._newTreeItem(nwItemType.FILE))
self.docuMenu.addAction(self.aNewDoc)
# Document > Open # Document > Open
self.aOpenDoc = QAction(self.tr("Open Document"), self) self.aOpenDoc = QAction(self.tr("Open Document"), self)
self.aOpenDoc.setShortcut("Ctrl+O") self.aOpenDoc.setShortcut("Ctrl+O")
self.aOpenDoc.triggered.connect(lambda: self.theParent.openSelectedItem()) self.aOpenDoc.triggered.connect(lambda: self.mainGui.openSelectedItem())
self.docuMenu.addAction(self.aOpenDoc) self.docuMenu.addAction(self.aOpenDoc)
# Document > Save # Document > Save
self.aSaveDoc = QAction(self.tr("Save Document"), self) self.aSaveDoc = QAction(self.tr("Save Document"), self)
self.aSaveDoc.setShortcut("Ctrl+S") self.aSaveDoc.setShortcut("Ctrl+S")
self.aSaveDoc.triggered.connect(lambda: self.theParent.saveDocument()) self.aSaveDoc.triggered.connect(lambda: self.mainGui.saveDocument())
self.docuMenu.addAction(self.aSaveDoc) self.docuMenu.addAction(self.aSaveDoc)
# Document > Close # Document > Close
self.aCloseDoc = QAction(self.tr("Close Document"), self) self.aCloseDoc = QAction(self.tr("Close Document"), self)
self.aCloseDoc.setShortcut("Ctrl+W") self.aCloseDoc.setShortcut("Ctrl+W")
self.aCloseDoc.triggered.connect(lambda: self.theParent.closeDocEditor()) self.aCloseDoc.triggered.connect(lambda: self.mainGui.closeDocEditor())
self.docuMenu.addAction(self.aCloseDoc) self.docuMenu.addAction(self.aCloseDoc)
# Document > Separator # Document > Separator
@@ -274,13 +221,13 @@ class GuiMainMenu(QMenuBar):
# Document > Preview # Document > Preview
self.aViewDoc = QAction(self.tr("View Document"), self) self.aViewDoc = QAction(self.tr("View Document"), self)
self.aViewDoc.setShortcut("Ctrl+R") self.aViewDoc.setShortcut("Ctrl+R")
self.aViewDoc.triggered.connect(lambda: self.theParent.viewDocument(None)) self.aViewDoc.triggered.connect(lambda: self.mainGui.viewDocument(None))
self.docuMenu.addAction(self.aViewDoc) self.docuMenu.addAction(self.aViewDoc)
# Document > Close Preview # Document > Close Preview
self.aCloseView = QAction(self.tr("Close Document View"), self) self.aCloseView = QAction(self.tr("Close Document View"), self)
self.aCloseView.setShortcut("Ctrl+Shift+R") self.aCloseView.setShortcut("Ctrl+Shift+R")
self.aCloseView.triggered.connect(lambda: self.theParent.closeDocViewer()) self.aCloseView.triggered.connect(lambda: self.mainGui.closeDocViewer())
self.docuMenu.addAction(self.aCloseView) self.docuMenu.addAction(self.aCloseView)
# Document > Separator # Document > Separator
@@ -288,23 +235,23 @@ class GuiMainMenu(QMenuBar):
# Document > Show File Details # Document > Show File Details
self.aFileDetails = QAction(self.tr("Show File Details"), self) self.aFileDetails = QAction(self.tr("Show File Details"), self)
self.aFileDetails.triggered.connect(lambda: self.theParent.docEditor.revealLocation()) self.aFileDetails.triggered.connect(lambda: self.mainGui.docEditor.revealLocation())
self.docuMenu.addAction(self.aFileDetails) self.docuMenu.addAction(self.aFileDetails)
# Document > Import From File # Document > Import From File
self.aImportFile = QAction(self.tr("Import Text from File"), self) self.aImportFile = QAction(self.tr("Import Text from File"), self)
self.aImportFile.setShortcut("Ctrl+Shift+I") self.aImportFile.setShortcut("Ctrl+Shift+I")
self.aImportFile.triggered.connect(lambda: self.theParent.importDocument()) self.aImportFile.triggered.connect(lambda: self.mainGui.importDocument())
self.docuMenu.addAction(self.aImportFile) self.docuMenu.addAction(self.aImportFile)
# Document > Merge Documents # Document > Merge Documents
self.aMergeDocs = QAction(self.tr("Merge Folder to Document"), self) self.aMergeDocs = QAction(self.tr("Merge Folder to Document"), self)
self.aMergeDocs.triggered.connect(lambda: self.theParent.mergeDocuments()) self.aMergeDocs.triggered.connect(lambda: self.mainGui.mergeDocuments())
self.docuMenu.addAction(self.aMergeDocs) self.docuMenu.addAction(self.aMergeDocs)
# Document > Split Document # Document > Split Document
self.aSplitDoc = QAction(self.tr("Split Document to Folder"), self) self.aSplitDoc = QAction(self.tr("Split Document to Folder"), self)
self.aSplitDoc.triggered.connect(lambda: self.theParent.splitDocument()) self.aSplitDoc.triggered.connect(lambda: self.mainGui.splitDocument())
self.docuMenu.addAction(self.aSplitDoc) self.docuMenu.addAction(self.aSplitDoc)
return return
@@ -377,7 +324,7 @@ class GuiMainMenu(QMenuBar):
self.aFocusTree.setShortcut("Ctrl+Alt+1") self.aFocusTree.setShortcut("Ctrl+Alt+1")
else: else:
self.aFocusTree.setShortcut("Alt+1") self.aFocusTree.setShortcut("Alt+1")
self.aFocusTree.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.TREE)) self.aFocusTree.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.TREE))
self.viewMenu.addAction(self.aFocusTree) self.viewMenu.addAction(self.aFocusTree)
# View > Document Pane 1 # View > Document Pane 1
@@ -386,7 +333,7 @@ class GuiMainMenu(QMenuBar):
self.aFocusEditor.setShortcut("Ctrl+Alt+2") self.aFocusEditor.setShortcut("Ctrl+Alt+2")
else: else:
self.aFocusEditor.setShortcut("Alt+2") self.aFocusEditor.setShortcut("Alt+2")
self.aFocusEditor.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.EDITOR)) self.aFocusEditor.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.EDITOR))
self.viewMenu.addAction(self.aFocusEditor) self.viewMenu.addAction(self.aFocusEditor)
# View > Document Pane 2 # View > Document Pane 2
@@ -395,7 +342,7 @@ class GuiMainMenu(QMenuBar):
self.aFocusView.setShortcut("Ctrl+Alt+3") self.aFocusView.setShortcut("Ctrl+Alt+3")
else: else:
self.aFocusView.setShortcut("Alt+3") self.aFocusView.setShortcut("Alt+3")
self.aFocusView.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.VIEWER)) self.aFocusView.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.VIEWER))
self.viewMenu.addAction(self.aFocusView) self.viewMenu.addAction(self.aFocusView)
# View > Outline # View > Outline
@@ -404,7 +351,7 @@ class GuiMainMenu(QMenuBar):
self.aFocusOutline.setShortcut("Ctrl+Alt+4") self.aFocusOutline.setShortcut("Ctrl+Alt+4")
else: else:
self.aFocusOutline.setShortcut("Alt+4") self.aFocusOutline.setShortcut("Alt+4")
self.aFocusOutline.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.OUTLINE)) self.aFocusOutline.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.OUTLINE))
self.viewMenu.addAction(self.aFocusOutline) self.viewMenu.addAction(self.aFocusOutline)
# View > Separator # View > Separator
@@ -413,13 +360,13 @@ class GuiMainMenu(QMenuBar):
# View > Go Backward # View > Go Backward
self.aViewPrev = QAction(self.tr("Navigate Backward"), self) self.aViewPrev = QAction(self.tr("Navigate Backward"), self)
self.aViewPrev.setShortcut("Alt+Left") self.aViewPrev.setShortcut("Alt+Left")
self.aViewPrev.triggered.connect(lambda: self.theParent.docViewer.navBackward()) self.aViewPrev.triggered.connect(lambda: self.mainGui.docViewer.navBackward())
self.viewMenu.addAction(self.aViewPrev) self.viewMenu.addAction(self.aViewPrev)
# View > Go Forward # View > Go Forward
self.aViewNext = QAction(self.tr("Navigate Forward"), self) self.aViewNext = QAction(self.tr("Navigate Forward"), self)
self.aViewNext.setShortcut("Alt+Right") self.aViewNext.setShortcut("Alt+Right")
self.aViewNext.triggered.connect(lambda: self.theParent.docViewer.navForward()) self.aViewNext.triggered.connect(lambda: self.mainGui.docViewer.navForward())
self.viewMenu.addAction(self.aViewNext) self.viewMenu.addAction(self.aViewNext)
# View > Separator # View > Separator
@@ -429,14 +376,14 @@ class GuiMainMenu(QMenuBar):
self.aFocusMode = QAction(self.tr("Focus Mode"), self) self.aFocusMode = QAction(self.tr("Focus Mode"), self)
self.aFocusMode.setShortcut("F8") self.aFocusMode.setShortcut("F8")
self.aFocusMode.setCheckable(True) self.aFocusMode.setCheckable(True)
self.aFocusMode.setChecked(self.theParent.isFocusMode) self.aFocusMode.setChecked(self.mainGui.isFocusMode)
self.aFocusMode.triggered.connect(lambda: self.theParent.toggleFocusMode()) self.aFocusMode.triggered.connect(lambda: self.mainGui.toggleFocusMode())
self.viewMenu.addAction(self.aFocusMode) self.viewMenu.addAction(self.aFocusMode)
# View > Toggle Full Screen # View > Toggle Full Screen
self.aFullScreen = QAction(self.tr("Full Screen Mode"), self) self.aFullScreen = QAction(self.tr("Full Screen Mode"), self)
self.aFullScreen.setShortcut("F11") self.aFullScreen.setShortcut("F11")
self.aFullScreen.triggered.connect(lambda: self.theParent.toggleFullScreenMode()) self.aFullScreen.triggered.connect(lambda: self.mainGui.toggleFullScreenMode())
self.viewMenu.addAction(self.aFullScreen) self.viewMenu.addAction(self.aFullScreen)
return return
@@ -641,7 +588,7 @@ class GuiMainMenu(QMenuBar):
# Insert > Placeholder Text # Insert > Placeholder Text
self.aLipsumText = QAction(self.tr("Placeholder Text"), self) self.aLipsumText = QAction(self.tr("Placeholder Text"), self)
self.aLipsumText.triggered.connect(lambda: self.theParent.showLoremIpsumDialog()) self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog())
self.insertMenu.addAction(self.aLipsumText) self.insertMenu.addAction(self.aLipsumText)
return return
@@ -805,7 +752,7 @@ class GuiMainMenu(QMenuBar):
# Search > Find # Search > Find
self.aFind = QAction(self.tr("Find"), self) self.aFind = QAction(self.tr("Find"), self)
self.aFind.setShortcut("Ctrl+F") self.aFind.setShortcut("Ctrl+F")
self.aFind.triggered.connect(lambda: self.theParent.docEditor.beginSearch()) self.aFind.triggered.connect(lambda: self.mainGui.docEditor.beginSearch())
self.srcMenu.addAction(self.aFind) self.srcMenu.addAction(self.aFind)
# Search > Replace # Search > Replace
@@ -814,7 +761,7 @@ class GuiMainMenu(QMenuBar):
self.aReplace.setShortcut("Ctrl+=") self.aReplace.setShortcut("Ctrl+=")
else: else:
self.aReplace.setShortcut("Ctrl+H") self.aReplace.setShortcut("Ctrl+H")
self.aReplace.triggered.connect(lambda: self.theParent.docEditor.beginReplace()) self.aReplace.triggered.connect(lambda: self.mainGui.docEditor.beginReplace())
self.srcMenu.addAction(self.aReplace) self.srcMenu.addAction(self.aReplace)
# Search > Find Next # Search > Find Next
@@ -823,7 +770,7 @@ class GuiMainMenu(QMenuBar):
self.aFindNext.setShortcuts(["Ctrl+G", "F3"]) self.aFindNext.setShortcuts(["Ctrl+G", "F3"])
else: else:
self.aFindNext.setShortcuts(["F3", "Ctrl+G"]) self.aFindNext.setShortcuts(["F3", "Ctrl+G"])
self.aFindNext.triggered.connect(lambda: self.theParent.docEditor.findNext()) self.aFindNext.triggered.connect(lambda: self.mainGui.docEditor.findNext())
self.srcMenu.addAction(self.aFindNext) self.srcMenu.addAction(self.aFindNext)
# Search > Find Prev # Search > Find Prev
@@ -832,13 +779,13 @@ class GuiMainMenu(QMenuBar):
self.aFindPrev.setShortcuts(["Ctrl+Shift+G", "Shift+F3"]) self.aFindPrev.setShortcuts(["Ctrl+Shift+G", "Shift+F3"])
else: else:
self.aFindPrev.setShortcuts(["Shift+F3", "Ctrl+Shift+G"]) self.aFindPrev.setShortcuts(["Shift+F3", "Ctrl+Shift+G"])
self.aFindPrev.triggered.connect(lambda: self.theParent.docEditor.findNext(goBack=True)) self.aFindPrev.triggered.connect(lambda: self.mainGui.docEditor.findNext(goBack=True))
self.srcMenu.addAction(self.aFindPrev) self.srcMenu.addAction(self.aFindPrev)
# Search > Replace Next # Search > Replace Next
self.aReplaceNext = QAction(self.tr("Replace Next"), self) self.aReplaceNext = QAction(self.tr("Replace Next"), self)
self.aReplaceNext.setShortcut("Ctrl+Shift+1") self.aReplaceNext.setShortcut("Ctrl+Shift+1")
self.aReplaceNext.triggered.connect(lambda: self.theParent.docEditor.replaceNext()) self.aReplaceNext.triggered.connect(lambda: self.mainGui.docEditor.replaceNext())
self.srcMenu.addAction(self.aReplaceNext) self.srcMenu.addAction(self.aReplaceNext)
return return
@@ -860,12 +807,12 @@ class GuiMainMenu(QMenuBar):
# Tools > Re-Run Spell Check # Tools > Re-Run Spell Check
self.aReRunSpell = QAction(self.tr("Re-Run Spell Check"), self) self.aReRunSpell = QAction(self.tr("Re-Run Spell Check"), self)
self.aReRunSpell.setShortcut("F7") self.aReRunSpell.setShortcut("F7")
self.aReRunSpell.triggered.connect(lambda: self.theParent.docEditor.spellCheckDocument()) self.aReRunSpell.triggered.connect(lambda: self.mainGui.docEditor.spellCheckDocument())
self.toolsMenu.addAction(self.aReRunSpell) self.toolsMenu.addAction(self.aReRunSpell)
# Tools > Project Word List # Tools > Project Word List
self.aEditWordList = QAction(self.tr("Project Word List"), self) self.aEditWordList = QAction(self.tr("Project Word List"), self)
self.aEditWordList.triggered.connect(lambda: self.theParent.showProjectWordListDialog()) self.aEditWordList.triggered.connect(lambda: self.mainGui.showProjectWordListDialog())
self.toolsMenu.addAction(self.aEditWordList) self.toolsMenu.addAction(self.aEditWordList)
# Tools > Separator # Tools > Separator
@@ -874,7 +821,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Rebuild Indices # Tools > Rebuild Indices
self.aRebuildIndex = QAction(self.tr("Rebuild Index"), self) self.aRebuildIndex = QAction(self.tr("Rebuild Index"), self)
self.aRebuildIndex.setShortcut("F9") self.aRebuildIndex.setShortcut("F9")
self.aRebuildIndex.triggered.connect(lambda: self.theParent.rebuildIndex()) self.aRebuildIndex.triggered.connect(lambda: self.mainGui.rebuildIndex())
self.toolsMenu.addAction(self.aRebuildIndex) self.toolsMenu.addAction(self.aRebuildIndex)
# Tools > Separator # Tools > Separator
@@ -888,20 +835,20 @@ class GuiMainMenu(QMenuBar):
# Tools > Export Project # Tools > Export Project
self.aBuildProject = QAction(self.tr("Build Novel Project"), self) self.aBuildProject = QAction(self.tr("Build Novel Project"), self)
self.aBuildProject.setShortcut("F5") self.aBuildProject.setShortcut("F5")
self.aBuildProject.triggered.connect(lambda: self.theParent.showBuildProjectDialog()) self.aBuildProject.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
self.toolsMenu.addAction(self.aBuildProject) self.toolsMenu.addAction(self.aBuildProject)
# Tools > Writing Stats # Tools > Writing Stats
self.aWritingStats = QAction(self.tr("Writing Statistics"), self) self.aWritingStats = QAction(self.tr("Writing Statistics"), self)
self.aWritingStats.setShortcut("F6") self.aWritingStats.setShortcut("F6")
self.aWritingStats.triggered.connect(lambda: self.theParent.showWritingStatsDialog()) self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
self.toolsMenu.addAction(self.aWritingStats) self.toolsMenu.addAction(self.aWritingStats)
# Tools > Settings # Tools > Settings
self.aPreferences = QAction(self.tr("Preferences"), self) self.aPreferences = QAction(self.tr("Preferences"), self)
self.aPreferences.setShortcut("Ctrl+,") self.aPreferences.setShortcut("Ctrl+,")
self.aPreferences.setMenuRole(QAction.PreferencesRole) self.aPreferences.setMenuRole(QAction.PreferencesRole)
self.aPreferences.triggered.connect(lambda: self.theParent.showPreferencesDialog()) self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog())
self.toolsMenu.addAction(self.aPreferences) self.toolsMenu.addAction(self.aPreferences)
return return
@@ -915,13 +862,13 @@ class GuiMainMenu(QMenuBar):
# Help > About # Help > About
self.aAboutNW = QAction(self.tr("About novelWriter"), self) self.aAboutNW = QAction(self.tr("About novelWriter"), self)
self.aAboutNW.setMenuRole(QAction.AboutRole) self.aAboutNW.setMenuRole(QAction.AboutRole)
self.aAboutNW.triggered.connect(lambda: self.theParent.showAboutNWDialog()) self.aAboutNW.triggered.connect(lambda: self.mainGui.showAboutNWDialog())
self.helpMenu.addAction(self.aAboutNW) self.helpMenu.addAction(self.aAboutNW)
# Help > About Qt5 # Help > About Qt5
self.aAboutQt = QAction(self.tr("About Qt5"), self) self.aAboutQt = QAction(self.tr("About Qt5"), self)
self.aAboutQt.setMenuRole(QAction.AboutQtRole) self.aAboutQt.setMenuRole(QAction.AboutQtRole)
self.aAboutQt.triggered.connect(lambda: self.theParent.showAboutQtDialog()) self.aAboutQt.triggered.connect(lambda: self.mainGui.showAboutQtDialog())
self.helpMenu.addAction(self.aAboutQt) self.helpMenu.addAction(self.aAboutQt)
# Help > Separator # Help > Separator
@@ -968,7 +915,7 @@ class GuiMainMenu(QMenuBar):
# Document > Check for Updates # Document > Check for Updates
self.aUpdates = QAction(self.tr("Check for New Release"), self) self.aUpdates = QAction(self.tr("Check for New Release"), self)
self.aUpdates.triggered.connect(lambda: self.theParent.showUpdatesDialog()) self.aUpdates.triggered.connect(lambda: self.mainGui.showUpdatesDialog())
self.helpMenu.addAction(self.aUpdates) self.helpMenu.addAction(self.aUpdates)
return return
+414 -125
View File
@@ -4,7 +4,9 @@ novelWriter GUI Novel Tree
GUI classe for the main window novel tree GUI classe for the main window novel tree
File History: File History:
Created: 2020-12-20 [1.1a0] Created: 2020-12-20 [1.1a0] GuiNovelTree
Created: 2022-06-12 [1.7b1] GuiNovelView
Created: 2022-06-12 [1.7b1] GuiNovelToolBar
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182020, Veronica Berglyd Olsen Copyright 20182020, Veronica Berglyd Olsen
@@ -26,84 +28,376 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging import logging
import novelwriter import novelwriter
from enum import Enum
from time import time from time import time
from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QPalette
from PyQt5.QtCore import Qt, QSize, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QFrame QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView, QLabel,
QMenu, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QWidget
) )
from novelwriter.enum import nwDocMode, nwItemClass
from novelwriter.common import checkInt from novelwriter.common import checkInt
from novelwriter.constants import nwKeyWords from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NovelTreeColumn(Enum):
HIDDEN = 0
POV = 1
FOCUS = 2
PLOT = 3
# END Enum NovelTreeColumn
class GuiNovelView(QWidget):
# Signals for user interaction with the novel tree
selectedItemChanged = pyqtSignal(str)
openDocumentRequest = pyqtSignal(str, Enum, int, str)
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
self.mainGui = mainGui
self.theProject = mainGui.theProject
# Build GUI
self.novelTree = GuiNovelTree(self)
self.novelBar = GuiNovelToolBar(self)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(self.novelBar, 0)
self.outerBox.addWidget(self.novelTree, 1)
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
# Function Mappings
self.updateWordCounts = self.novelTree.updateWordCounts
self.getSelectedHandle = self.novelTree.getSelectedHandle
return
##
# Methods
##
def initSettings(self):
self.novelTree.initSettings()
return
def refreshTree(self):
"""Refresh the current tree.
"""
self.novelTree.refreshTree(rootHandle=self.theProject.lastNovel)
return
def clearProject(self):
"""Clear project-related GUI content.
"""
self.novelTree.clearContent()
self.novelBar.clearContent()
return
def openProjectTasks(self):
"""Run opening project tasks.
"""
lastNovel = self.theProject.lastNovel
if lastNovel not in self.theProject.tree:
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL)
logger.debug("Setting novel tree to root item '%s'", lastNovel)
lastCol = self.theProject.options.getEnum(
"GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.HIDDEN
)
self.clearProject()
self.novelBar.buildNovelRootMenu()
self.novelBar.setLastColType(lastCol, doRefresh=False)
self.novelBar.setCurrentRoot(lastNovel)
return
def closeProjectTasks(self):
"""Run closing project tasks.
"""
lastColType = self.novelTree.lastColType
self.theProject.options.setValue("GuiNovelView", "lastCol", lastColType)
return
def setFocus(self):
"""Forward the set focus call to the tree widget.
"""
self.novelTree.setFocus()
return
def treeHasFocus(self):
"""Check if the novel tree has focus.
"""
return self.novelTree.hasFocus()
##
# Public Slots
##
@pyqtSlot(str)
def updateRootItem(self, tHandle):
"""If any root item changes, rebuild the novel root menu.
"""
self.novelBar.buildNovelRootMenu()
return
# END Class GuiNovelView
class GuiNovelToolBar(QWidget):
def __init__(self, novelView):
QTreeWidget.__init__(self, novelView)
logger.debug("Initialising GuiNovelToolBar ...")
self.mainConf = novelwriter.CONFIG
self.novelView = novelView
self.theProject = novelView.mainGui.theProject
self.mainTheme = novelView.mainGui.mainTheme
iPx = self.mainTheme.baseIconSize
mPx = self.mainConf.pxInt(3)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
self.setPalette(qPalette)
fadeCol = qPalette.text().color()
buttonStyle = (
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
# Widget Label
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Novel Outline"))
self.viewLabel.setContentsMargins(0, 0, 0, 0)
self.viewLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# Refresh Button
self.tbRefresh = QToolButton(self)
self.tbRefresh.setToolTip(self.tr("Refresh"))
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbRefresh.setIconSize(QSize(iPx, iPx))
self.tbRefresh.setStyleSheet(buttonStyle)
self.tbRefresh.clicked.connect(self._refreshNovelTree)
# Novel Root Menu
self.mRoot = QMenu()
self.gRoot = QActionGroup(self.mRoot)
self.aRoot = {}
self.tbRoot = QToolButton(self)
self.tbRoot.setToolTip(self.tr("Novel Root"))
self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
self.tbRoot.setIconSize(QSize(iPx, iPx))
self.tbRoot.setStyleSheet(buttonStyle)
self.tbRoot.setMenu(self.mRoot)
self.tbRoot.setPopupMode(QToolButton.InstantPopup)
# More Options Menu
self.mMore = QMenu()
self.mLastCol = self.mMore.addMenu(self.tr("Last Column"))
self.gLastCol = QActionGroup(self.mMore)
self.aLastCol = {}
self._addLastColAction(NovelTreeColumn.HIDDEN, self.tr("Hidden"))
self._addLastColAction(NovelTreeColumn.POV, self.tr("Point of View Character"))
self._addLastColAction(NovelTreeColumn.FOCUS, self.tr("Focus Character"))
self._addLastColAction(NovelTreeColumn.PLOT, self.tr("Novel Plot"))
self.tbMore = QToolButton(self)
self.tbMore.setToolTip(self.tr("More Options"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbMore.setIconSize(QSize(iPx, iPx))
self.tbMore.setStyleSheet(buttonStyle)
self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup)
# Assemble
self.outerBox = QHBoxLayout()
self.outerBox.addWidget(self.viewLabel)
self.outerBox.addWidget(self.tbRefresh)
self.outerBox.addWidget(self.tbRoot)
self.outerBox.addWidget(self.tbMore)
self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
logger.debug("GuiNovelToolBar initialisation complete")
return
##
# Methods
##
def clearContent(self):
"""Run clearing project tasks.
"""
self.mRoot.clear()
self.aRoot = {}
return
def buildNovelRootMenu(self):
"""Build the novel root menu.
"""
self.mRoot.clear()
self.aRoot = {}
for n, (tHandle, nwItem) in enumerate(self.theProject.tree.iterRoots(nwItemClass.NOVEL)):
aRoot = self.mRoot.addAction(nwItem.itemName)
aRoot.setData(tHandle)
aRoot.setCheckable(True)
aRoot.triggered.connect(lambda n, tHandle=tHandle: self.setCurrentRoot(tHandle))
self.gRoot.addAction(aRoot)
self.aRoot[tHandle] = aRoot
return
def setCurrentRoot(self, rootHandle):
"""Set the current active root handle.
"""
if rootHandle in self.aRoot:
self.aRoot[rootHandle].setChecked(True)
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
return
def setLastColType(self, colType, doRefresh=True):
"""Set the last column type.
"""
self.aLastCol[colType].setChecked(True)
self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh)
return
##
# Private Slots
##
@pyqtSlot()
def _refreshNovelTree(self):
"""Rebuild the current tree.
"""
rootHandle = self.theProject.lastNovel
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
return
##
# Internal Functions
##
def _addLastColAction(self, colType, actionLabel):
"""Add a column selection entry to the last column menu.
"""
aLast = self.mLastCol.addAction(actionLabel)
aLast.setCheckable(True)
aLast.setActionGroup(self.gLastCol)
aLast.triggered.connect(lambda: self.setLastColType(colType))
self.aLastCol[colType] = aLast
return
# END Class GuiNovelToolBar
class GuiNovelTree(QTreeWidget): class GuiNovelTree(QTreeWidget):
C_TITLE = 0 C_TITLE = 0
C_WORDS = 1 C_WORDS = 1
C_POV = 2 C_LAST = 2
def __init__(self, theParent): def __init__(self, novelView):
QTreeWidget.__init__(self, theParent) QTreeWidget.__init__(self, novelView)
logger.debug("Initialising GuiNovelTree ...") logger.debug("Initialising GuiNovelTree ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.novelView = novelView
self.theTheme = theParent.theTheme self.mainGui = novelView.mainGui
self.theProject = theParent.theProject self.mainTheme = novelView.mainGui.mainTheme
self.theProject = novelView.mainGui.theProject
# Internal Variables # Internal Variables
self._treeMap = {} self._treeMap = {}
self._lastBuild = 0 self._lastBuild = 0
self._lastCol = NovelTreeColumn.POV
# Cached Strings
self._povLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
self._focLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
self._pltLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
# Build GUI # Build GUI
iPx = self.theTheme.baseIconSize # =========
self.setFrameStyle(QFrame.NoFrame)
iPx = self.mainTheme.baseIconSize
cMg = self.mainConf.pxInt(6)
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setIndentation(iPx) self.setFrameStyle(QFrame.NoFrame)
self.setHeaderHidden(True)
self.setIndentation(0)
self.setColumnCount(3) self.setColumnCount(3)
self.setHeaderLabels([
self.tr("Novel Outline"),
self.tr("Words"),
self.tr("POV")
])
self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected)
self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection) self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setExpandsOnDoubleClick(False) self.setExpandsOnDoubleClick(False)
self.setDragEnabled(False) self.setDragEnabled(False)
treeHeadItem = self.headerItem() # Lock the column sizes
treeHeadItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
treeHeadItem.setToolTip(self.C_TITLE, self.tr("Section title"))
treeHeadItem.setToolTip(self.C_WORDS, self.tr("Word count"))
treeHeadItem.setToolTip(self.C_POV, self.tr("Point-of-view character"))
treeHeader = self.header() treeHeader = self.header()
treeHeader.setStretchLastSection(True) treeHeader.setStretchLastSection(False)
treeHeader.setMinimumSectionSize(iPx + 6) treeHeader.setMinimumSectionSize(iPx + cMg)
treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.Stretch)
treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_LAST, QHeaderView.ResizeToContents)
# Get user's column width preferences for NAME and COUNT # Pre-Generate Tree Formatting
treeColWidth = self.mainConf.getNovelColWidths() fH1 = self.font()
if len(treeColWidth) <= 3: fH1.setBold(True)
for colN, colW in enumerate(treeColWidth): fH1.setUnderline(True)
self.setColumnWidth(colN, colW)
# The last column should just auto-scale fH2 = self.font()
self.resizeColumnToContents(self.C_POV) fH2.setBold(True)
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
self._pIndent = [
self.mainTheme.loadDecoration("deco_doc_h0", pxH=iPx),
self.mainTheme.loadDecoration("deco_doc_h1", pxH=iPx),
self.mainTheme.loadDecoration("deco_doc_h2", pxH=iPx),
self.mainTheme.loadDecoration("deco_doc_h3", pxH=iPx),
self.mainTheme.loadDecoration("deco_doc_h4", pxH=iPx),
]
# Connect signals
self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._treeSelectionChange)
# Set custom settings # Set custom settings
self.initTree() self.initSettings()
logger.debug("GuiNovelTree initialisation complete") logger.debug("GuiNovelTree initialisation complete")
return return
def initTree(self): def initSettings(self):
"""Set or update tree widget settings. """Set or update tree widget settings.
""" """
# Scroll bars # Scroll bars
@@ -119,11 +413,19 @@ class GuiNovelTree(QTreeWidget):
return return
##
# Properties
##
@property
def lastColType(self):
return self._lastCol
## ##
# Class Methods # Class Methods
## ##
def clearTree(self): def clearContent(self):
"""Clear the GUI content and the related maps. """Clear the GUI content and the related maps.
""" """
self.clear() self.clear()
@@ -131,12 +433,15 @@ class GuiNovelTree(QTreeWidget):
self._lastBuild = 0 self._lastBuild = 0
return return
def refreshTree(self, overRide=False): def refreshTree(self, rootHandle=None, overRide=False):
"""Called whenever the Novel tab is activated. """Called whenever the Novel tab is activated.
""" """
logger.verbose("Requesting refresh of the novel tree") logger.verbose("Requesting refresh of the novel tree")
treeChanged = self.theParent.treeView.changedSince(self._lastBuild) if rootHandle is None:
indexChanged = self.theProject.index.indexChangedSince(self._lastBuild) rootHandle = self.theProject.tree.findRoot(nwItemClass.NOVEL)
treeChanged = self.mainGui.projView.changedSince(self._lastBuild)
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
if not (treeChanged or indexChanged or overRide): if not (treeChanged or indexChanged or overRide):
logger.verbose("No changes have been made to the novel index") logger.verbose("No changes have been made to the novel index")
return return
@@ -146,7 +451,8 @@ class GuiNovelTree(QTreeWidget):
if selItem: if selItem:
titleKey = selItem[0].data(self.C_TITLE, Qt.UserRole)[2] titleKey = selItem[0].data(self.C_TITLE, Qt.UserRole)[2]
self._populateTree() self._populateTree(rootHandle)
self.theProject.setLastNovelViewed(rootHandle)
if titleKey is not None and titleKey in self._treeMap: if titleKey is not None and titleKey in self._treeMap:
self._treeMap[titleKey].setSelected(True) self._treeMap[titleKey].setSelected(True)
@@ -162,15 +468,6 @@ class GuiNovelTree(QTreeWidget):
self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}") self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}")
return return
def getColumnSizes(self):
"""Return the column widths for the tree columns.
"""
retVals = [
self.columnWidth(0),
self.columnWidth(1),
]
return retVals
def getSelectedHandle(self): def getSelectedHandle(self):
"""Get the currently selected handle. If multiple items are """Get the currently selected handle. If multiple items are
selected, return the first. selected, return the first.
@@ -184,6 +481,17 @@ class GuiNovelTree(QTreeWidget):
return tHandle, tLine return tHandle, tLine
def setLastColType(self, colType, doRefresh=True):
"""Change the content type of the last column and rebuild.
"""
if self._lastCol != colType:
logger.debug("Changing last column to %s", colType.name)
self._lastCol = colType
self.setColumnHidden(self.C_LAST, colType == NovelTreeColumn.HIDDEN)
if doRefresh:
self.refreshTree(rootHandle=self.theProject.lastNovel, overRide=True)
return
## ##
# Events # Events
## ##
@@ -209,113 +517,94 @@ class GuiNovelTree(QTreeWidget):
if tHandle is None: if tHandle is None:
return return
self.theParent.viewDocument(tHandle) self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
return return
## ##
# Slots # Private Slots
## ##
def _treeDoubleClick(self, tItem, tCol): @pyqtSlot()
def _treeSelectionChange(self):
"""Extract the handle and line number of the currently selected
title, and send it to the tree meta panel.
"""
tHandle, _ = self.getSelectedHandle()
if tHandle is not None:
self.novelView.selectedItemChanged.emit(tHandle)
return
@pyqtSlot("QTreeWidgetItem*", int)
def _treeDoubleClick(self, tItem, colNo):
"""Extract the handle and line number of the title double- """Extract the handle and line number of the title double-
clicked, and send it to the main gui class for opening in the clicked, and send it to the main gui class for opening in the
document editor. document editor.
""" """
tHandle, tLine = self.getSelectedHandle() tHandle, tLine = self.getSelectedHandle()
self.theParent.openDocument(tHandle, tLine=tLine-1, doScroll=True) self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, tLine, "")
return
def _itemSelected(self):
"""Extract the handle and line number of the currently selected
title, and send it to the tree meta panel.
"""
selItems = self.selectedItems()
if selItems:
tHandle = selItems[0].data(self.C_TITLE, Qt.UserRole)[0]
self.theParent.treeMeta.updateViewBox(tHandle)
return return
## ##
# Internal Functions # Internal Functions
## ##
def _populateTree(self): def _populateTree(self, rootHandle):
"""Build the tree based on the project index. """Build the tree based on the project index.
""" """
self.clearTree() self.clearContent()
tStart = time()
logger.verbose("Building novel tree for root item '%s'", rootHandle)
currTitle = None novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
currChapter = None for tKey, tHandle, sTitle, novIdx in novStruct:
currScene = None
for tKey, tHandle, sTitle, novIdx in self.theProject.index.novelStructure(skipExcl=True): iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
if iLevel == 0:
continue
tItem = self._createTreeItem(tHandle, sTitle, tKey, novIdx) newItem = QTreeWidgetItem()
self._treeMap[tKey] = tItem theData = (tHandle, sTitle[1:].lstrip("0"), tKey)
tLevel = novIdx.level newItem.setData(self.C_TITLE, Qt.DecorationRole, self._pIndent[iLevel])
if tLevel == "H1": newItem.setText(self.C_TITLE, novIdx.title)
self.addTopLevelItem(tItem) newItem.setData(self.C_TITLE, Qt.UserRole, theData)
currTitle = tItem newItem.setFont(self.C_TITLE, self._hFonts[iLevel])
currChapter = None newItem.setText(self.C_WORDS, f"{novIdx.wordCount:n}")
currScene = None newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
elif tLevel == "H2": lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
if currTitle is None: newItem.setText(self.C_LAST, lastText)
self.addTopLevelItem(tItem) if lastText:
else: newItem.setToolTip(self.C_LAST, toolTip)
currTitle.addChild(tItem)
currChapter = tItem
currScene = None
elif tLevel == "H3": self._treeMap[tKey] = newItem
if currChapter is None: self.addTopLevelItem(newItem)
if currTitle is None:
self.addTopLevelItem(tItem)
else:
currTitle.addChild(tItem)
else:
currChapter.addChild(tItem)
currScene = tItem
elif tLevel == "H4":
if currScene is None:
if currChapter is None:
if currTitle is None:
self.addTopLevelItem(tItem)
else:
currTitle.addChild(tItem)
else:
currChapter.addChild(tItem)
else:
currScene.addChild(tItem)
tItem.setExpanded(True)
logger.verbose("Novel Tree built in %.3f ms", (time() - tStart)*1000)
self._lastBuild = time() self._lastBuild = time()
return return
def _createTreeItem(self, tHandle, sTitle, titleKey, novIdx): def _getLastColumnText(self, tHandle, sTitle):
"""Populate a tree item with all the column values. """Generate the text for the last column based on user settings.
""" """
newItem = QTreeWidgetItem() if self._lastCol == NovelTreeColumn.HIDDEN:
hIcon = "doc_%s" % novIdx.level.lower() return "", ""
theData = (tHandle, sTitle[1:].lstrip("0"), titleKey)
wC = int(novIdx.wordCount)
newItem.setText(self.C_TITLE, novIdx.title)
newItem.setData(self.C_TITLE, Qt.UserRole, theData)
newItem.setIcon(self.C_TITLE, self.theTheme.getIcon(hIcon))
newItem.setText(self.C_WORDS, f"{wC:n}")
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
theRefs = self.theProject.index.getReferences(tHandle, sTitle) theRefs = self.theProject.index.getReferences(tHandle, sTitle)
newItem.setText(self.C_POV, ", ".join(theRefs[nwKeyWords.POV_KEY])) if self._lastCol == NovelTreeColumn.POV:
newText = ", ".join(theRefs[nwKeyWords.POV_KEY])
return newText, f"{self._povLabel}: {newText}"
return newItem elif self._lastCol == NovelTreeColumn.FOCUS:
newText = ", ".join(theRefs[nwKeyWords.FOCUS_KEY])
return newText, f"{self._focLabel}: {newText}"
elif self._lastCol == NovelTreeColumn.PLOT:
newText = ", ".join(theRefs[nwKeyWords.PLOT_KEY])
return newText, f"{self._pltLabel}: {newText}"
return "", ""
# END Class GuiNovelTree # END Class GuiNovelTree
+48 -45
View File
@@ -4,9 +4,9 @@ novelWriter GUI Project Outline
GUI class for the project outline view GUI class for the project outline view
File History: File History:
Created: 2022-05-15 [1.7b1] GuiOutline Created: 2022-05-15 [1.7b1] GuiOutlineView
Created: 2022-05-22 [1.7b1] GuiOutlineToolBar Created: 2022-05-22 [1.7b1] GuiOutlineToolBar
Created: 2019-11-16 [0.4.1] GuiOutlineView Created: 2019-11-16 [0.4.1] GuiOutlineTree
Created: 2019-11-16 [0.4.1] GuiOutlineHeaderMenu Created: 2019-11-16 [0.4.1] GuiOutlineHeaderMenu
Created: 2020-06-02 [0.7.0] GuiOutlineDetails Created: 2020-06-02 [0.7.0] GuiOutlineDetails
@@ -52,22 +52,23 @@ from novelwriter.constants import trConst, nwKeyWords, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiOutline(QWidget): class GuiOutlineView(QWidget):
loadDocumentTagRequest = pyqtSignal(str, Enum) loadDocumentTagRequest = pyqtSignal(str, Enum)
def __init__(self, theParent): def __init__(self, mainGui):
QWidget.__init__(self, theParent) QWidget.__init__(self, mainGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
# Build GUI
self.outlineBar = GuiOutlineToolBar(self) self.outlineBar = GuiOutlineToolBar(self)
self.outlineView = GuiOutlineView(self) self.outlineTree = GuiOutlineTree(self)
self.outlineData = GuiOutlineDetails(self) self.outlineData = GuiOutlineDetails(self)
self.splitOutline = QSplitter(Qt.Vertical) self.splitOutline = QSplitter(Qt.Vertical)
self.splitOutline.addWidget(self.outlineView) self.splitOutline.addWidget(self.outlineTree)
self.splitOutline.addWidget(self.outlineData) self.splitOutline.addWidget(self.outlineData)
self.splitOutline.setSizes(self.mainConf.getOutlinePanePos()) self.splitOutline.setSizes(self.mainConf.getOutlinePanePos())
@@ -80,14 +81,14 @@ class GuiOutline(QWidget):
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
# Connect Signals # Connect Signals
self.outlineView.hiddenStateChanged.connect(self._updateMenuColumns) self.outlineTree.hiddenStateChanged.connect(self._updateMenuColumns)
self.outlineView.activeItemChanged.connect(self.outlineData.showItem) self.outlineTree.activeItemChanged.connect(self.outlineData.showItem)
self.outlineData.itemTagClicked.connect(self._tagClicked) self.outlineData.itemTagClicked.connect(self._tagClicked)
self.outlineBar.loadNovelRootRequest.connect(self._rootItemChanged) self.outlineBar.loadNovelRootRequest.connect(self._rootItemChanged)
self.outlineBar.viewColumnToggled.connect(self.outlineView.menuColumnToggled) self.outlineBar.viewColumnToggled.connect(self.outlineTree.menuColumnToggled)
# Function Mappings # Function Mappings
self.getSelectedHandle = self.outlineView.getSelectedHandle self.getSelectedHandle = self.outlineTree.getSelectedHandle
return return
@@ -103,24 +104,24 @@ class GuiOutline(QWidget):
return return
def initOutline(self): def initOutline(self):
self.outlineView.initOutline() self.outlineTree.initOutline()
self.outlineData.initDetails() self.outlineData.initDetails()
return return
def closeOutline(self): def closeOutline(self):
self.outlineView.closeOutline() self.outlineTree.closeOutline()
self.outlineData.updateClasses() self.outlineData.updateClasses()
return return
def refreshView(self, overRide=False, novelChanged=False): def refreshView(self, overRide=False, novelChanged=False):
self.outlineView.refreshTree(overRide=overRide, novelChanged=novelChanged) self.outlineTree.refreshTree(overRide=overRide, novelChanged=novelChanged)
return return
def treeFocus(self): def treeHasFocus(self):
return self.outlineView.hasFocus() return self.outlineTree.hasFocus()
def setTreeFocus(self): def setTreeFocus(self):
return self.outlineView.setFocus() return self.outlineTree.setFocus()
## ##
# Public Slots # Public Slots
@@ -144,7 +145,7 @@ class GuiOutline(QWidget):
checkboxes whenever a signal is received that the hidden state checkboxes whenever a signal is received that the hidden state
of columns has changed. of columns has changed.
""" """
self.outlineBar.setColumnHiddenState(self.outlineView.hiddenColumns) self.outlineBar.setColumnHiddenState(self.outlineTree.hiddenColumns)
return return
@pyqtSlot(str) @pyqtSlot(str)
@@ -159,10 +160,10 @@ class GuiOutline(QWidget):
def _rootItemChanged(self, handle): def _rootItemChanged(self, handle):
"""The root novel handle has changed or needs to be refreshed. """The root novel handle has changed or needs to be refreshed.
""" """
self.outlineView.refreshTree(rootHandle=(handle or None), overRide=True) self.outlineTree.refreshTree(rootHandle=(handle or None), overRide=True)
return return
# END Class GuiOutline # END Class GuiOutlineView
class GuiOutlineToolBar(QToolBar): class GuiOutlineToolBar(QToolBar):
@@ -176,9 +177,9 @@ class GuiOutlineToolBar(QToolBar):
logger.debug("Initialising GuiOutlineToolBar ...") logger.debug("Initialising GuiOutlineToolBar ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theOutline.theParent self.mainGui = theOutline.mainGui
self.theProject = theOutline.theParent.theProject self.theProject = theOutline.mainGui.theProject
self.theTheme = theOutline.theParent.theTheme self.mainTheme = theOutline.mainGui.mainTheme
iPx = self.mainConf.pxInt(22) iPx = self.mainConf.pxInt(22)
mPx = self.mainConf.pxInt(12) mPx = self.mainConf.pxInt(12)
@@ -201,7 +202,7 @@ class GuiOutlineToolBar(QToolBar):
# Actions # Actions
self.aRefresh = QAction(self.tr("Refresh"), self) self.aRefresh = QAction(self.tr("Refresh"), self)
self.aRefresh.setIcon(self.theTheme.getIcon("refresh")) self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.aRefresh.triggered.connect(self._refreshRequested) self.aRefresh.triggered.connect(self._refreshRequested)
# Column Menu # Column Menu
@@ -211,7 +212,7 @@ class GuiOutlineToolBar(QToolBar):
) )
self.tbColumns = QToolButton(self) self.tbColumns = QToolButton(self)
self.tbColumns.setIcon(self.theTheme.getIcon("menu")) self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
self.tbColumns.setMenu(self.mColumns) self.tbColumns.setMenu(self.mColumns)
self.tbColumns.setPopupMode(QToolButton.InstantPopup) self.tbColumns.setPopupMode(QToolButton.InstantPopup)
@@ -225,6 +226,8 @@ class GuiOutlineToolBar(QToolBar):
logger.debug("GuiOutlineToolBar initialisation complete") logger.debug("GuiOutlineToolBar initialisation complete")
return
## ##
# Methods # Methods
## ##
@@ -233,7 +236,7 @@ class GuiOutlineToolBar(QToolBar):
"""Fill the novel combo box with a list of all novel folders. """Fill the novel combo box with a list of all novel folders.
""" """
self.novelValue.clear() self.novelValue.clear()
tIcon = self.theTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]) tIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
for tHandle, nwItem in self.theProject.tree.iterRoots(nwItemClass.NOVEL): for tHandle, nwItem in self.theProject.tree.iterRoots(nwItemClass.NOVEL):
self.novelValue.addItem(tIcon, nwItem.itemName, tHandle) self.novelValue.addItem(tIcon, nwItem.itemName, tHandle)
self.novelValue.insertSeparator(self.novelValue.count()) self.novelValue.insertSeparator(self.novelValue.count())
@@ -268,7 +271,7 @@ class GuiOutlineToolBar(QToolBar):
# END Class GuiOutlineToolBar # END Class GuiOutlineToolBar
class GuiOutlineView(QTreeWidget): class GuiOutlineTree(QTreeWidget):
DEF_WIDTH = { DEF_WIDTH = {
nwOutline.TITLE: 200, nwOutline.TITLE: 200,
@@ -316,12 +319,12 @@ class GuiOutlineView(QTreeWidget):
def __init__(self, theOutline): def __init__(self, theOutline):
QTreeWidget.__init__(self, theOutline) QTreeWidget.__init__(self, theOutline)
logger.debug("Initialising GuiOutlineView ...") logger.debug("Initialising GuiOutlineTree ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theOutline.theParent self.mainGui = theOutline.mainGui
self.theProject = theOutline.theParent.theProject self.theProject = theOutline.mainGui.theProject
self.theTheme = theOutline.theParent.theTheme self.mainTheme = theOutline.mainGui.mainTheme
self.setFrameStyle(QFrame.NoFrame) self.setFrameStyle(QFrame.NoFrame)
self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSelectionBehavior(QAbstractItemView.SelectRows)
@@ -331,7 +334,7 @@ class GuiOutlineView(QTreeWidget):
self.itemDoubleClicked.connect(self._treeDoubleClick) self.itemDoubleClicked.connect(self._treeDoubleClick)
self.itemSelectionChanged.connect(self._itemSelected) self.itemSelectionChanged.connect(self._itemSelected)
iPx = self.theTheme.baseIconSize iPx = self.mainTheme.baseIconSize
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setIndentation(iPx) self.setIndentation(iPx)
@@ -352,7 +355,7 @@ class GuiOutlineView(QTreeWidget):
self.hiddenStateChanged.emit() self.hiddenStateChanged.emit()
logger.debug("GuiOutlineView initialisation complete") logger.debug("GuiOutlineTree initialisation complete")
return return
@@ -461,7 +464,7 @@ class GuiOutlineView(QTreeWidget):
document editor. document editor.
""" """
tHandle, tLine = self.getSelectedHandle() tHandle, tLine = self.getSelectedHandle()
self.theParent.openDocument(tHandle, tLine=tLine - 1, doScroll=True) self.mainGui.openDocument(tHandle, tLine=tLine - 1, doScroll=True)
return return
@pyqtSlot() @pyqtSlot()
@@ -679,7 +682,7 @@ class GuiOutlineView(QTreeWidget):
hIcon = "doc_%s" % novIdx.level.lower() hIcon = "doc_%s" % novIdx.level.lower()
hLevel = self.theProject.index.getHandleHeaderLevel(tHandle) hLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
dIcon = self.theTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, hLevel) dIcon = self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, hLevel)
cC = int(novIdx.charCount) cC = int(novIdx.charCount)
wC = int(novIdx.wordCount) wC = int(novIdx.wordCount)
@@ -687,7 +690,7 @@ class GuiOutlineView(QTreeWidget):
newItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title) newItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title)
newItem.setData(self._colIdx[nwOutline.TITLE], Qt.UserRole, tHandle) newItem.setData(self._colIdx[nwOutline.TITLE], Qt.UserRole, tHandle)
newItem.setIcon(self._colIdx[nwOutline.TITLE], self.theTheme.getIcon(hIcon)) newItem.setIcon(self._colIdx[nwOutline.TITLE], self.mainTheme.getIcon(hIcon))
newItem.setText(self._colIdx[nwOutline.LEVEL], novIdx.level) newItem.setText(self._colIdx[nwOutline.LEVEL], novIdx.level)
newItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName) newItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
newItem.setIcon(self._colIdx[nwOutline.LABEL], dIcon) newItem.setIcon(self._colIdx[nwOutline.LABEL], dIcon)
@@ -714,7 +717,7 @@ class GuiOutlineView(QTreeWidget):
return newItem return newItem
# END Class GuiOutlineView # END Class GuiOutlineTree
class GuiOutlineHeaderMenu(QMenu): class GuiOutlineHeaderMenu(QMenu):
@@ -779,14 +782,14 @@ class GuiOutlineDetails(QScrollArea):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theOutline = theOutline self.theOutline = theOutline
self.theParent = theOutline.theParent self.mainGui = theOutline.mainGui
self.theProject = theOutline.theParent.theProject self.theProject = theOutline.mainGui.theProject
self.theTheme = theOutline.theParent.theTheme self.mainTheme = theOutline.mainGui.mainTheme
# Sizes # Sizes
minTitle = 30*self.theTheme.textNWidth minTitle = 30*self.mainTheme.textNWidth
maxTitle = 40*self.theTheme.textNWidth maxTitle = 40*self.mainTheme.textNWidth
wCount = self.theTheme.getTextWidth("999,999") wCount = self.mainTheme.getTextWidth("999,999")
hSpace = int(self.mainConf.pxInt(10)) hSpace = int(self.mainConf.pxInt(10))
vSpace = int(self.mainConf.pxInt(4)) vSpace = int(self.mainConf.pxInt(4))
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -41,22 +41,22 @@ logger = logging.getLogger(__name__)
class GuiMainStatus(QStatusBar): class GuiMainStatus(QStatusBar):
def __init__(self, theParent): def __init__(self, mainGui):
QStatusBar.__init__(self, theParent) QStatusBar.__init__(self, mainGui)
logger.debug("Initialising GuiMainStatus ...") logger.debug("Initialising GuiMainStatus ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.refTime = None self.refTime = None
self.userIdle = False self.userIdle = False
colNone = QColor(*self.theTheme.statNone) colNone = QColor(*self.mainTheme.statNone)
colTrue = QColor(*self.theTheme.statUnsaved) colTrue = QColor(*self.mainTheme.statUnsaved)
colFalse = QColor(*self.theTheme.statSaved) colFalse = QColor(*self.mainTheme.statSaved)
iPx = self.theTheme.baseIconSize iPx = self.mainTheme.baseIconSize
# Permanent Widgets # Permanent Widgets
# ================= # =================
@@ -66,7 +66,7 @@ class GuiMainStatus(QStatusBar):
# The Spell Checker Language # The Spell Checker Language
self.langIcon = QLabel("") self.langIcon = QLabel("")
self.langText = QLabel(self.tr("None")) self.langText = QLabel(self.tr("None"))
self.langIcon.setPixmap(self.theTheme.getPixmap("status_lang", (iPx, iPx))) self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
self.langIcon.setContentsMargins(0, 0, 0, 0) self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0) self.langText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.langIcon) self.addPermanentWidget(self.langIcon)
@@ -91,7 +91,7 @@ class GuiMainStatus(QStatusBar):
# The Project and Session Stats # The Project and Session Stats
self.statsIcon = QLabel() self.statsIcon = QLabel()
self.statsText = QLabel("") self.statsText = QLabel("")
self.statsIcon.setPixmap(self.theTheme.getPixmap("status_stats", (iPx, iPx))) self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
self.statsIcon.setContentsMargins(0, 0, 0, 0) self.statsIcon.setContentsMargins(0, 0, 0, 0)
self.statsText.setContentsMargins(0, 0, xM, 0) self.statsText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.statsIcon) self.addPermanentWidget(self.statsIcon)
@@ -99,14 +99,14 @@ class GuiMainStatus(QStatusBar):
# The Session Clock # The Session Clock
# Set the mimimum width so the label doesn't rescale every second # Set the mimimum width so the label doesn't rescale every second
self.timePixmap = self.theTheme.getPixmap("status_time", (iPx, iPx)) self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = self.theTheme.getPixmap("status_idle", (iPx, iPx)) self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
self.timeIcon = QLabel() self.timeIcon = QLabel()
self.timeText = QLabel("") self.timeText = QLabel("")
self.timeIcon.setPixmap(self.timePixmap) self.timeIcon.setPixmap(self.timePixmap)
self.timeText.setToolTip(self.tr("Session Time")) self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(self.theTheme.getTextWidth("00:00:00:")) self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0) self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.setContentsMargins(0, 0, 0, 0) self.timeText.setContentsMargins(0, 0, 0, 0)
self.addPermanentWidget(self.timeIcon) self.addPermanentWidget(self.timeIcon)
+22 -16
View File
@@ -54,7 +54,7 @@ class GuiTheme:
def __init__(self): def __init__(self):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theIcons = GuiIcons(self) self.iconCache = GuiIcons(self)
# Loaded Theme Settings # Loaded Theme Settings
# ===================== # =====================
@@ -127,13 +127,13 @@ class GuiTheme:
self.updateFont() self.updateFont()
self.updateTheme() self.updateTheme()
self.theIcons.updateTheme() self.iconCache.updateTheme()
# Icon Functions # Icon Functions
self.getIcon = self.theIcons.getIcon self.getIcon = self.iconCache.getIcon
self.getPixmap = self.theIcons.getPixmap self.getPixmap = self.iconCache.getPixmap
self.getItemIcon = self.theIcons.getItemIcon self.getItemIcon = self.iconCache.getItemIcon
self.loadDecoration = self.theIcons.loadDecoration self.loadDecoration = self.iconCache.loadDecoration
# Extract Other Info # Extract Other Info
self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX() self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
@@ -472,16 +472,19 @@ class GuiIcons:
# Switches # Switches
"sticky-on", "sticky-off", "sticky-on", "sticky-off",
"bullet-on", "bullet-off", "bullet-on", "bullet-off",
# Decorations
"deco_doc_h0", "deco_doc_h1", "deco_doc_h2", "deco_doc_h3", "deco_doc_h4",
} }
DECO_MAP = { IMAGE_MAP = {
"wiz-back": "wizard-back.jpg", "wiz-back": "wizard-back.jpg",
} }
def __init__(self, theTheme): def __init__(self, mainTheme):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theTheme = theTheme self.mainTheme = mainTheme
# Storage # Storage
self._qIcons = {} self._qIcons = {}
@@ -573,19 +576,22 @@ class GuiIcons:
# Access Functions # Access Functions
## ##
def loadDecoration(self, decoKey, pxW, pxH): def loadDecoration(self, decoKey, pxW=None, pxH=None):
"""Load graphical decoration element based on the decoration """Load graphical decoration element based on the decoration
map. This function always returns a QSwgWidget. map or the icon map. This function always returns a QPixmap.
""" """
if decoKey not in self.DECO_MAP: if decoKey in self._themeMap:
imgPath = self._themeMap[decoKey]
elif decoKey in self.IMAGE_MAP:
imgPath = os.path.join(
self.mainConf.assetPath, "images", self.IMAGE_MAP[decoKey]
)
else:
logger.error("Decoration with name '%s' does not exist", decoKey) logger.error("Decoration with name '%s' does not exist", decoKey)
return QPixmap() return QPixmap()
imgPath = os.path.join(
self.mainConf.assetPath, "images", self.DECO_MAP[decoKey]
)
if not os.path.isfile(imgPath): if not os.path.isfile(imgPath):
logger.error("Decoration file '%s' not in assets folder", self.DECO_MAP[decoKey]) logger.error("Asset '%s' not found", self.IMAGE_MAP[decoKey])
return QPixmap() return QPixmap()
theDeco = QPixmap(imgPath) theDeco = QPixmap(imgPath)
+18 -18
View File
@@ -40,21 +40,21 @@ class GuiViewsBar(QToolBar):
viewChangeRequested = pyqtSignal(nwView) viewChangeRequested = pyqtSignal(nwView)
def __init__(self, theParent): def __init__(self, mainGui):
QToolBar.__init__(self, theParent) QToolBar.__init__(self, mainGui)
logger.debug("Initialising GuiViewsBar ...") logger.debug("Initialising GuiViewsBar ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
# Style # Style
iPx = self.mainConf.pxInt(22) iPx = self.mainConf.pxInt(22)
mPx = self.mainConf.pxInt(60) mPx = self.mainConf.pxInt(60)
lblFont = self.theTheme.guiFont lblFont = self.mainTheme.guiFont
lblFont.setPointSizeF(0.65*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.65*self.mainTheme.fontPointSize)
self.setMovable(False) self.setMovable(False)
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
@@ -70,54 +70,54 @@ class GuiViewsBar(QToolBar):
self.aProject = QAction(self.tr("Project")) self.aProject = QAction(self.tr("Project"))
self.aProject.setFont(lblFont) self.aProject.setFont(lblFont)
self.aProject.setToolTip(self.tr("Show project tree and editor")) self.aProject.setToolTip(self.tr("Show project tree and editor"))
self.aProject.setIcon(self.theTheme.getIcon("view_editor")) self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT)) self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
self.aNovel = QAction(self.tr("Novel")) self.aNovel = QAction(self.tr("Novel"))
self.aNovel.setFont(lblFont) self.aNovel.setFont(lblFont)
self.aNovel.setToolTip(self.tr("Show novel tree and editor")) self.aNovel.setToolTip(self.tr("Show novel tree and editor"))
self.aNovel.setIcon(self.theTheme.getIcon("view_novel")) self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL)) self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
self.aOutline = QAction(self.tr("Outline")) self.aOutline = QAction(self.tr("Outline"))
self.aOutline.setFont(lblFont) self.aOutline.setFont(lblFont)
self.aOutline.setToolTip(self.tr("Show novel outline")) self.aOutline.setToolTip(self.tr("Show novel outline"))
self.aOutline.setIcon(self.theTheme.getIcon("view_outline")) self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE)) self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
self.aBuild = QAction(self.tr("Build")) self.aBuild = QAction(self.tr("Build"))
self.aBuild.setFont(lblFont) self.aBuild.setFont(lblFont)
self.aBuild.setToolTip(self.tr("Build novel project")) self.aBuild.setToolTip(self.tr("Build novel project"))
self.aBuild.setIcon(self.theTheme.getIcon("view_build")) self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
self.aBuild.triggered.connect(lambda: self.theParent.showBuildProjectDialog()) self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
self.aDetails = QAction(self.tr("Details")) self.aDetails = QAction(self.tr("Details"))
self.aDetails.setFont(lblFont) self.aDetails.setFont(lblFont)
self.aDetails.setToolTip(self.tr("Show project details")) self.aDetails.setToolTip(self.tr("Show project details"))
self.aDetails.setIcon(self.theTheme.getIcon("proj_details")) self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
self.aDetails.triggered.connect(lambda: self.theParent.showProjectDetailsDialog()) self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
self.aStats = QAction(self.tr("Stats")) self.aStats = QAction(self.tr("Stats"))
self.aStats.setFont(lblFont) self.aStats.setFont(lblFont)
self.aStats.setToolTip(self.tr("Show project statistics")) self.aStats.setToolTip(self.tr("Show project statistics"))
self.aStats.setIcon(self.theTheme.getIcon("proj_stats")) self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
self.aStats.triggered.connect(lambda: self.theParent.showWritingStatsDialog()) self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
# Settings Menu # Settings Menu
self.mSettings = QMenu() self.mSettings = QMenu()
self.aPrjSettings = QAction(self.tr("Project Settings")) self.aPrjSettings = QAction(self.tr("Project Settings"))
self.aPrjSettings.triggered.connect(lambda: self.theParent.showProjectSettingsDialog()) self.aPrjSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog())
self.mSettings.addAction(self.aPrjSettings) self.mSettings.addAction(self.aPrjSettings)
self.aPreferences = QAction(self.tr("Preferences")) self.aPreferences = QAction(self.tr("Preferences"))
self.aPreferences.triggered.connect(lambda: self.theParent.showPreferencesDialog()) self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog())
self.mSettings.addAction(self.aPreferences) self.mSettings.addAction(self.aPreferences)
self.tbSettings = QToolButton(self) self.tbSettings = QToolButton(self)
self.tbSettings.setFont(lblFont) self.tbSettings.setFont(lblFont)
self.tbSettings.setText(self.tr("Settings")) self.tbSettings.setText(self.tr("Settings"))
self.tbSettings.setIcon(self.theTheme.getIcon("settings")) self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
self.tbSettings.setMenu(self.mSettings) self.tbSettings.setMenu(self.mSettings)
self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.tbSettings.setPopupMode(QToolButton.InstantPopup) self.tbSettings.setPopupMode(QToolButton.InstantPopup)
+108 -116
View File
@@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
from novelwriter.gui import ( from novelwriter.gui import (
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu, GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectTree, GuiTheme, GuiMainStatus, GuiNovelView, GuiOutlineView, GuiProjectView, GuiTheme,
GuiViewsBar GuiViewsBar
) )
from novelwriter.dialogs import ( from novelwriter.dialogs import (
@@ -84,7 +84,7 @@ class GuiMain(QMainWindow):
# ============ # ============
# Core Classes and Settings # Core Classes and Settings
self.theTheme = GuiTheme() self.mainTheme = GuiTheme()
self.theProject = NWProject(self) self.theProject = NWProject(self)
self.hasProject = False self.hasProject = False
self.isFocusMode = False self.isFocusMode = False
@@ -104,42 +104,20 @@ class GuiMain(QMainWindow):
hWd = self.mainConf.pxInt(4) hWd = self.mainConf.pxInt(4)
# Main GUI Elements # Main GUI Elements
self.statusBar = GuiMainStatus(self) self.statusBar = GuiMainStatus(self)
self.treeView = GuiProjectTree(self) self.projView = GuiProjectView(self)
self.novelView = GuiNovelTree(self) self.novelView = GuiNovelView(self)
self.docEditor = GuiDocEditor(self) self.docEditor = GuiDocEditor(self)
self.viewMeta = GuiDocViewDetails(self) self.viewMeta = GuiDocViewDetails(self)
self.docViewer = GuiDocViewer(self) self.docViewer = GuiDocViewer(self)
self.treeMeta = GuiItemDetails(self) self.itemDetails = GuiItemDetails(self)
self.projView = GuiOutline(self) self.outlineView = GuiOutlineView(self)
self.mainMenu = GuiMainMenu(self) self.mainMenu = GuiMainMenu(self)
self.viewsBar = GuiViewsBar(self) self.viewsBar = GuiViewsBar(self)
# Connect Signals Between Main Elements
self.viewsBar.viewChangeRequested.connect(self._changeView)
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
self.treeView.novelItemChanged.connect(self._treeNovelItemChanged)
self.treeView.wordCountsChanged.connect(self._updateStatusWordCount)
self.treeView.treeItemChanged.connect(self.docEditor.updateDocInfo)
self.treeView.treeItemChanged.connect(self.docViewer.updateDocInfo)
self.treeView.treeItemChanged.connect(self.treeMeta.updateViewBox)
self.treeView.rootFolderChanged.connect(self.projView.updateRootItem)
self.docEditor.spellDictionaryChanged.connect(self.statusBar.setLanguage)
self.docEditor.docEditedStatusChanged.connect(self.statusBar.doUpdateDocumentStatus)
self.docEditor.docCountsChanged.connect(self.treeMeta.doUpdateCounts)
self.docEditor.docCountsChanged.connect(self.treeView.doUpdateCounts)
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
self.projView.loadDocumentTagRequest.connect(self._followTag)
# Project Tree Stack # Project Tree Stack
self.projStack = QStackedWidget() self.projStack = QStackedWidget()
self.projStack.addWidget(self.treeView) self.projStack.addWidget(self.projView)
self.projStack.addWidget(self.novelView) self.projStack.addWidget(self.novelView)
self.projStack.currentChanged.connect(self._projStackChanged) self.projStack.currentChanged.connect(self._projStackChanged)
@@ -149,7 +127,7 @@ class GuiMain(QMainWindow):
self.treeBox.setContentsMargins(0, 0, 0, 0) self.treeBox.setContentsMargins(0, 0, 0, 0)
self.treeBox.setSpacing(mPx) self.treeBox.setSpacing(mPx)
self.treeBox.addWidget(self.projStack) self.treeBox.addWidget(self.projStack)
self.treeBox.addWidget(self.treeMeta) self.treeBox.addWidget(self.itemDetails)
self.treePane.setLayout(self.treeBox) self.treePane.setLayout(self.treeBox)
# Splitter : Document Viewer / Document Meta # Splitter : Document Viewer / Document Meta
@@ -176,7 +154,7 @@ class GuiMain(QMainWindow):
# Main Stack : Editor / Outline # Main Stack : Editor / Outline
self.mainStack = QStackedWidget() self.mainStack = QStackedWidget()
self.mainStack.addWidget(self.splitMain) self.mainStack.addWidget(self.splitMain)
self.mainStack.addWidget(self.projView) self.mainStack.addWidget(self.outlineView)
self.mainStack.currentChanged.connect(self._mainStackChanged) self.mainStack.currentChanged.connect(self._mainStackChanged)
# Indices of Splitter Widgets # Indices of Splitter Widgets
@@ -189,15 +167,15 @@ class GuiMain(QMainWindow):
# Indices of Tab Widgets # Indices of Tab Widgets
self.idxEditorView = self.mainStack.indexOf(self.splitMain) self.idxEditorView = self.mainStack.indexOf(self.splitMain)
self.idxOutlineView = self.mainStack.indexOf(self.projView) self.idxOutlineView = self.mainStack.indexOf(self.outlineView)
self.idxTreeView = self.projStack.indexOf(self.treeView) self.idxProjView = self.projStack.indexOf(self.projView)
self.idxNovelView = self.projStack.indexOf(self.novelView) self.idxNovelView = self.projStack.indexOf(self.novelView)
# Splitter Behaviour # Splitter Behaviour
self.splitMain.setCollapsible(self.idxTree, False) self.splitMain.setCollapsible(self.idxTree, False)
self.splitMain.setCollapsible(self.idxMain, False) self.splitMain.setCollapsible(self.idxMain, False)
self.splitDocs.setCollapsible(self.idxEditor, False) self.splitDocs.setCollapsible(self.idxEditor, False)
self.splitDocs.setCollapsible(self.idxViewer, True) self.splitDocs.setCollapsible(self.idxViewer, False)
self.splitView.setCollapsible(self.idxViewDoc, False) self.splitView.setCollapsible(self.idxViewDoc, False)
self.splitView.setCollapsible(self.idxViewMeta, False) self.splitView.setCollapsible(self.idxViewMeta, False)
@@ -214,6 +192,34 @@ class GuiMain(QMainWindow):
self.setStatusBar(self.statusBar) self.setStatusBar(self.statusBar)
self.addToolBar(Qt.LeftToolBarArea, self.viewsBar) self.addToolBar(Qt.LeftToolBarArea, self.viewsBar)
# Connect Signals
# ===============
self.viewsBar.viewChangeRequested.connect(self._changeView)
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
self.projView.openDocumentRequest.connect(self._openDocument)
self.projView.novelItemChanged.connect(self._treeNovelItemChanged)
self.projView.wordCountsChanged.connect(self._updateStatusWordCount)
self.projView.treeItemChanged.connect(self.docEditor.updateDocInfo)
self.projView.treeItemChanged.connect(self.docViewer.updateDocInfo)
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
self.novelView.openDocumentRequest.connect(self._openDocument)
self.docEditor.spellDictionaryChanged.connect(self.statusBar.setLanguage)
self.docEditor.docEditedStatusChanged.connect(self.statusBar.doUpdateDocumentStatus)
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
self.outlineView.loadDocumentTagRequest.connect(self._followTag)
# Finalise Initialisation # Finalise Initialisation
# ======================= # =======================
@@ -289,15 +295,15 @@ class GuiMain(QMainWindow):
"""Wrapper function to clear all sub-elements of the main GUI. """Wrapper function to clear all sub-elements of the main GUI.
""" """
# Project Area # Project Area
self.treeView.clearTree() self.projView.clearProject()
self.novelView.clearTree() self.novelView.clearProject()
self.treeMeta.clearDetails() self.itemDetails.clearDetails()
# Work Area # Work Area
self.docEditor.clearEditor() self.docEditor.clearEditor()
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.closeDocViewer() self.closeDocViewer()
self.projView.clearOutline() self.outlineView.clearOutline()
# General # General
self.statusBar.clearStatus() self.statusBar.clearStatus()
@@ -359,7 +365,8 @@ class GuiMain(QMainWindow):
self.rebuildTrees() self.rebuildTrees()
self.saveProject() self.saveProject()
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.projView.updateRootItem(None) self.outlineView.updateRootItem(None)
self.novelView.openProjectTasks()
self.rebuildIndex(beQuiet=True) self.rebuildIndex(beQuiet=True)
self.statusBar.setRefTime(self.theProject.projOpened) self.statusBar.setRefTime(self.theProject.projOpened)
self.statusBar.setProjectStatus(nwState.GOOD) self.statusBar.setProjectStatus(nwState.GOOD)
@@ -415,7 +422,8 @@ class GuiMain(QMainWindow):
if saveOK: if saveOK:
self.closeDocument() self.closeDocument()
self.docViewer.clearNavHistory() self.docViewer.clearNavHistory()
self.projView.closeOutline() self.outlineView.closeOutline()
self.novelView.closeProjectTasks()
self.theProject.closeProject(self.idleTime) self.theProject.closeProject(self.idleTime)
self.idleRefTime = time() self.idleRefTime = time()
@@ -506,7 +514,8 @@ class GuiMain(QMainWindow):
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.docEditor.toggleSpellCheck(self.theProject.spellCheck) self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
self.statusBar.setRefTime(self.theProject.projOpened) self.statusBar.setRefTime(self.theProject.projOpened)
self.projView.updateRootItem(None) self.outlineView.updateRootItem(None)
self.novelView.openProjectTasks()
self._updateStatusWordCount() self._updateStatusWordCount()
# Restore previously open documents, if any # Restore previously open documents, if any
@@ -539,7 +548,7 @@ class GuiMain(QMainWindow):
logger.error("No project open") logger.error("No project open")
return False return False
self.treeView.saveTreeOrder() self.projView.saveProjectTree()
if self.theProject.saveProject(autoSave=autoSave): if self.theProject.saveProject(autoSave=autoSave):
self.theProject.index.saveIndex() self.theProject.index.saveIndex()
@@ -584,7 +593,7 @@ class GuiMain(QMainWindow):
if changeFocus: if changeFocus:
self.docEditor.setFocus() self.docEditor.setFocus()
self.theProject.setLastEdited(tHandle) self.theProject.setLastEdited(tHandle)
self.treeView.setSelectedHandle(tHandle, doScroll=doScroll) self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
else: else:
return False return False
@@ -650,7 +659,7 @@ class GuiMain(QMainWindow):
self.saveDocument() self.saveDocument()
else: else:
logger.verbose("Trying selected document") logger.verbose("Trying selected document")
tHandle = self.treeView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
if tHandle is None: if tHandle is None:
logger.verbose("Trying last viewed document") logger.verbose("Trying last viewed document")
@@ -787,12 +796,12 @@ class GuiMain(QMainWindow):
tHandle = None tHandle = None
tLine = None tLine = None
if self.treeView.hasFocus(): if self.projView.treeHasFocus():
tHandle = self.treeView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
elif self.novelView.hasFocus(): elif self.novelView.treeHasFocus():
tHandle, tLine = self.novelView.getSelectedHandle() tHandle, tLine = self.novelView.getSelectedHandle()
elif self.projView.treeFocus(): elif self.outlineView.treeHasFocus():
tHandle, tLine = self.projView.getSelectedHandle() tHandle, tLine = self.outlineView.getSelectedHandle()
else: else:
logger.warning("No item selected") logger.warning("No item selected")
return False return False
@@ -802,7 +811,7 @@ class GuiMain(QMainWindow):
return True return True
def editItem(self, tHandle=None): def editItemLabel(self, tHandle=None):
"""Open the edit item dialog. """Open the edit item dialog.
""" """
if not self.hasProject: if not self.hasProject:
@@ -813,17 +822,17 @@ class GuiMain(QMainWindow):
if self.docEditor.anyFocus() or self.isFocusMode: if self.docEditor.anyFocus() or self.isFocusMode:
tHandle = self.docEditor.docHandle() tHandle = self.docEditor.docHandle()
else: else:
tHandle = self.treeView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
if tHandle: if tHandle:
return self.treeView.editTreeItem(tHandle) return self.projView.renameTreeItem(tHandle)
return False return False
def rebuildTrees(self): def rebuildTrees(self):
"""Rebuild the project tree. """Rebuild the project tree.
""" """
self.treeView.buildTree() self.projView.populateTree()
self.novelView.refreshTree() # self.novelView.refreshTree()
return return
def requestNovelTreeRefresh(self): def requestNovelTreeRefresh(self):
@@ -845,7 +854,7 @@ class GuiMain(QMainWindow):
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
tStart = time() tStart = time()
self.treeView.saveTreeOrder() self.projView.saveProjectTree()
self.theProject.index.clearIndex() self.theProject.index.clearIndex()
for tItem in self.theProject.tree: for tItem in self.theProject.tree:
@@ -855,8 +864,8 @@ class GuiMain(QMainWindow):
logger.verbose("Indexing '%s'", tItem.itemName) logger.verbose("Indexing '%s'", tItem.itemName)
if self.theProject.index.reIndexHandle(tItem.itemHandle): if self.theProject.index.reIndexHandle(tItem.itemHandle):
# Update Word Counts # Update Word Counts
self.treeView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True) self.projView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True)
self.treeView.setTreeItemValues(tItem.itemHandle) self.projView.setTreeItemValues(tItem.itemHandle)
tEnd = time() tEnd = time()
self.setStatus( self.setStatus(
@@ -913,13 +922,13 @@ class GuiMain(QMainWindow):
if dlgConf.result() == QDialog.Accepted: if dlgConf.result() == QDialog.Accepted:
logger.debug("Applying new preferences") logger.debug("Applying new preferences")
self.initMain() self.initMain()
self.theTheme.updateTheme() self.mainTheme.updateTheme()
self.saveDocument() self.saveDocument()
self.docEditor.initEditor() self.docEditor.initEditor()
self.docViewer.initViewer() self.docViewer.initViewer()
self.treeView.initTree() self.projView.initSettings()
self.novelView.initTree() self.novelView.initSettings()
self.projView.initOutline() self.outlineView.initOutline()
self._updateStatusWordCount() self._updateStatusWordCount()
return return
@@ -938,7 +947,7 @@ class GuiMain(QMainWindow):
logger.debug("Applying new project settings") logger.debug("Applying new project settings")
if dlgProj.spellChanged: if dlgProj.spellChanged:
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.treeMeta.refreshDetails() self.itemDetails.refreshDetails()
self._updateWindowTitle(self.theProject.projName) self._updateWindowTitle(self.theProject.projName)
return True return True
@@ -1154,13 +1163,11 @@ class GuiMain(QMainWindow):
if not self.isFocusMode: if not self.isFocusMode:
self.mainConf.setMainPanePos(self.splitMain.sizes()) self.mainConf.setMainPanePos(self.splitMain.sizes())
self.mainConf.setDocPanePos(self.splitDocs.sizes()) self.mainConf.setDocPanePos(self.splitDocs.sizes())
self.mainConf.setOutlinePanePos(self.projView.splitSizes()) self.mainConf.setOutlinePanePos(self.outlineView.splitSizes())
if self.viewMeta.isVisible(): if self.viewMeta.isVisible():
self.mainConf.setViewPanePos(self.splitView.sizes()) self.mainConf.setViewPanePos(self.splitView.sizes())
self.mainConf.setShowRefPanel(self.viewMeta.isVisible()) self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
self.mainConf.setNovelColWidths(self.novelView.getColumnSizes())
if not self.mainConf.isFullScreen: if not self.mainConf.isFullScreen:
self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setWinSize(self.width(), self.height())
@@ -1179,8 +1186,8 @@ class GuiMain(QMainWindow):
""" """
if paneNo == nwWidget.TREE: if paneNo == nwWidget.TREE:
tabIdx = self.projStack.currentIndex() tabIdx = self.projStack.currentIndex()
if tabIdx == self.idxTreeView: if tabIdx == self.idxProjView:
self.treeView.setFocus() self.projView.setFocus()
elif tabIdx == self.idxNovelView: elif tabIdx == self.idxNovelView:
self.novelView.setFocus() self.novelView.setFocus()
elif paneNo == nwWidget.EDITOR: elif paneNo == nwWidget.EDITOR:
@@ -1191,7 +1198,7 @@ class GuiMain(QMainWindow):
self.docViewer.setFocus() self.docViewer.setFocus()
elif paneNo == nwWidget.OUTLINE: elif paneNo == nwWidget.OUTLINE:
self._changeView(nwView.OUTLINE) self._changeView(nwView.OUTLINE)
self.projView.setTreeFocus() self.outlineView.setTreeFocus()
return return
def closeDocEditor(self): def closeDocEditor(self):
@@ -1468,6 +1475,17 @@ class GuiMain(QMainWindow):
self.viewDocument(tHandle=tHandle, tAnchor=f"#{sTitle}") self.viewDocument(tHandle=tHandle, tAnchor=f"#{sTitle}")
return return
@pyqtSlot(str, Enum, int, str)
def _openDocument(self, tHandle, tMode, tLine, tAnchor):
"""Handle an open document request from one of the tree views.
"""
if tHandle is not None:
if tMode == nwDocMode.EDIT:
self.openDocument(tHandle, tLine=tLine, changeFocus=False)
elif tMode == nwDocMode.VIEW:
self.viewDocument(tHandle=tHandle, tAnchor=(tAnchor or None))
return
@pyqtSlot(nwView) @pyqtSlot(nwView)
def _changeView(self, view): def _changeView(self, view):
"""Handle the requested change of view from the GuiViewBar. """Handle the requested change of view from the GuiViewBar.
@@ -1478,14 +1496,14 @@ class GuiMain(QMainWindow):
elif view == nwView.PROJECT: elif view == nwView.PROJECT:
self.mainStack.setCurrentWidget(self.splitMain) self.mainStack.setCurrentWidget(self.splitMain)
self.projStack.setCurrentWidget(self.treeView) self.projStack.setCurrentWidget(self.projView)
elif view == nwView.NOVEL: elif view == nwView.NOVEL:
self.mainStack.setCurrentWidget(self.splitMain) self.mainStack.setCurrentWidget(self.splitMain)
self.projStack.setCurrentWidget(self.novelView) self.projStack.setCurrentWidget(self.novelView)
elif view == nwView.OUTLINE: elif view == nwView.OUTLINE:
self.mainStack.setCurrentWidget(self.projView) self.mainStack.setCurrentWidget(self.outlineView)
return return
@@ -1531,32 +1549,6 @@ class GuiMain(QMainWindow):
return return
@pyqtSlot()
def _treeSingleClick(self):
"""Single click on a project tree item just updates the details
panel below the tree.
"""
tHandle = self.treeView.getSelectedHandle()
if tHandle is not None:
self.treeMeta.updateViewBox(tHandle)
return
@pyqtSlot("QTreeWidgetItem*", int)
def _treeDoubleClick(self, tItem, colNo):
"""The user double-clicked an item in the tree. If it is a file,
we open it. Otherwise, we toggle the expanded status.
"""
tHandle = self.treeView.getSelectedHandle()
if tHandle is not None:
tItem = self.theProject.tree[tHandle]
if tItem is None:
return
if tItem.itemType == nwItemType.FILE:
self.openDocument(tHandle, changeFocus=False, doScroll=False)
else:
self.treeView.toggleExpanded(tHandle)
return
@pyqtSlot() @pyqtSlot()
def _treeNovelItemChanged(self): def _treeNovelItemChanged(self):
"""Triggered when there is a change to a novel item in the """Triggered when there is a change to a novel item in the
@@ -1565,7 +1557,7 @@ class GuiMain(QMainWindow):
if self.mainStack.currentIndex() == self.idxOutlineView: if self.mainStack.currentIndex() == self.idxOutlineView:
logger.verbose("Novel tree changed while Outline tab active") logger.verbose("Novel tree changed while Outline tab active")
if self.hasProject: if self.hasProject:
self.projView.refreshView(novelChanged=True) self.outlineView.refreshView(novelChanged=True)
return return
@@ -1590,35 +1582,35 @@ class GuiMain(QMainWindow):
return return
@pyqtSlot(int) @pyqtSlot(int)
def _mainStackChanged(self, tabIndex): def _mainStackChanged(self, stIndex):
"""Activated when the main window tab is changed. """Activated when the main window tab is changed.
""" """
if tabIndex == self.idxEditorView: if stIndex == self.idxEditorView:
logger.verbose("Editor tab activated") logger.verbose("Editor View activated")
elif tabIndex == self.idxOutlineView: elif stIndex == self.idxOutlineView:
logger.verbose("Project outline tab activated") logger.verbose("Outline View activated")
if self.hasProject: if self.hasProject:
self.projView.refreshView() self.outlineView.refreshView()
return return
@pyqtSlot(int) @pyqtSlot(int)
def _projStackChanged(self, tabIndex): def _projStackChanged(self, stIndex):
"""Activated when the project view tab is changed. """Activated when the project view tab is changed.
""" """
sHandle = None sHandle = None
if tabIndex == self.idxTreeView: if stIndex == self.idxProjView:
logger.verbose("Project tree tab activated") logger.verbose("Project Tree View activated")
sHandle = self.treeView.getSelectedHandle() sHandle = self.projView.getSelectedHandle()
elif tabIndex == self.idxNovelView: elif stIndex == self.idxNovelView:
logger.verbose("Novel tree tab activated") logger.verbose("Novel Tree View activated")
if self.hasProject: if self.hasProject:
self.novelView.refreshTree() self.novelView.refreshTree()
sHandle, _ = self.novelView.getSelectedHandle() sHandle, _ = self.novelView.getSelectedHandle()
self.treeMeta.updateViewBox(sHandle) self.itemDetails.updateViewBox(sHandle)
return return
+20 -20
View File
@@ -65,16 +65,16 @@ class GuiBuildNovel(QDialog):
FMT_JSON_H = 8 # HTML5 wrapped in JSON FMT_JSON_H = 8 # HTML5 wrapped in JSON
FMT_JSON_M = 9 # nW Markdown wrapped in JSON FMT_JSON_M = 9 # nW Markdown wrapped in JSON
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiBuildNovel ...") logger.debug("Initialising GuiBuildNovel ...")
self.setObjectName("GuiBuildNovel") self.setObjectName("GuiBuildNovel")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.htmlText = [] # List of html documents self.htmlText = [] # List of html documents
self.htmlStyle = [] # List of html styles self.htmlStyle = [] # List of html styles
@@ -93,7 +93,7 @@ class GuiBuildNovel(QDialog):
self.docView = GuiBuildNovelDocView(self, self.theProject) self.docView = GuiBuildNovelDocView(self, self.theProject)
hS = self.theTheme.fontPixelSize hS = self.mainTheme.fontPixelSize
wS = 2*hS wS = 2*hS
# Title Formats # Title Formats
@@ -238,11 +238,11 @@ class GuiBuildNovel(QDialog):
pOptions.getString("GuiBuildNovel", "textFont", self.mainConf.textFont) pOptions.getString("GuiBuildNovel", "textFont", self.mainConf.textFont)
) )
self.fontButton = QPushButton("...") self.fontButton = QPushButton("...")
self.fontButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.fontButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("...")))
self.fontButton.clicked.connect(self._selectFont) self.fontButton.clicked.connect(self._selectFont)
self.textSize = QSpinBox(self) self.textSize = QSpinBox(self)
self.textSize.setFixedWidth(6*self.theTheme.textNWidth) self.textSize.setFixedWidth(6*self.mainTheme.textNWidth)
self.textSize.setMinimum(6) self.textSize.setMinimum(6)
self.textSize.setMaximum(72) self.textSize.setMaximum(72)
self.textSize.setSingleStep(1) self.textSize.setSingleStep(1)
@@ -251,7 +251,7 @@ class GuiBuildNovel(QDialog):
) )
self.lineHeight = QDoubleSpinBox(self) self.lineHeight = QDoubleSpinBox(self)
self.lineHeight.setFixedWidth(6*self.theTheme.textNWidth) self.lineHeight.setFixedWidth(6*self.mainTheme.textNWidth)
self.lineHeight.setMinimum(0.8) self.lineHeight.setMinimum(0.8)
self.lineHeight.setMaximum(3.0) self.lineHeight.setMaximum(3.0)
self.lineHeight.setSingleStep(0.05) self.lineHeight.setSingleStep(0.05)
@@ -711,7 +711,7 @@ class GuiBuildNovel(QDialog):
bldObj.initDocument() bldObj.initDocument()
# Make sure the project and document is up to date # Make sure the project and document is up to date
self.theParent.saveDocument() self.mainGui.saveDocument()
self.buildProgress.setMaximum(len(self.theProject.tree)) self.buildProgress.setMaximum(len(self.theProject.tree))
self.buildProgress.setValue(0) self.buildProgress.setValue(0)
@@ -760,7 +760,7 @@ class GuiBuildNovel(QDialog):
logger.debug("Built project in %.3f ms", 1000*(tEnd - tStart)) logger.debug("Built project in %.3f ms", 1000*(tEnd - tStart))
if bldObj.errData: if bldObj.errData:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("There were problems when building the project:") self.tr("There were problems when building the project:")
] + bldObj.errData, nwAlert.ERROR) ] + bldObj.errData, nwAlert.ERROR)
@@ -1003,11 +1003,11 @@ class GuiBuildNovel(QDialog):
# ============== # ==============
if wSuccess: if wSuccess:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("{0} file successfully written to:").format(textFmt), savePath self.tr("{0} file successfully written to:").format(textFmt), savePath
], nwAlert.INFO) ], nwAlert.INFO)
else: else:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to write {0} file. {1}" "Failed to write {0} file. {1}"
).format(textFmt, errMsg), nwAlert.ERROR) ).format(textFmt, errMsg), nwAlert.ERROR)
@@ -1193,18 +1193,18 @@ class GuiBuildNovel(QDialog):
class GuiBuildNovelDocView(QTextBrowser): class GuiBuildNovelDocView(QTextBrowser):
def __init__(self, theParent, theProject): def __init__(self, mainGui, theProject):
QTextBrowser.__init__(self, theParent) QTextBrowser.__init__(self, mainGui)
logger.debug("Initialising GuiBuildNovelDocView ...") logger.debug("Initialising GuiBuildNovelDocView ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theProject = theProject self.theProject = theProject
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.buildTime = 0 self.buildTime = 0
self.setMinimumWidth(40*self.theParent.theTheme.textNWidth) self.setMinimumWidth(40*self.mainGui.mainTheme.textNWidth)
self.setOpenExternalLinks(False) self.setOpenExternalLinks(False)
self.document().setDocumentMargin(self.mainConf.getTextMargin()) self.document().setDocumentMargin(self.mainConf.getTextMargin())
@@ -1238,9 +1238,9 @@ class GuiBuildNovelDocView(QTextBrowser):
lblPalette.setColor(QPalette.Foreground, lblPalette.toolTipText().color()) lblPalette.setColor(QPalette.Foreground, lblPalette.toolTipText().color())
lblFont = self.font() lblFont = self.font()
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
fPx = int(1.1*self.theTheme.fontPixelSize) fPx = int(1.1*self.mainTheme.fontPixelSize)
self.theTitle = QLabel("", self) self.theTitle = QLabel("", self)
self.theTitle.setIndent(0) self.theTitle.setIndent(0)
+6 -6
View File
@@ -42,15 +42,15 @@ logger = logging.getLogger(__name__)
class GuiLipsum(QDialog): class GuiLipsum(QDialog):
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiLipsum ...") logger.debug("Initialising GuiLipsum ...")
self.setObjectName("GuiLipsum") self.setObjectName("GuiLipsum")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.setWindowTitle(self.tr("Insert Placeholder Text")) self.setWindowTitle(self.tr("Insert Placeholder Text"))
@@ -61,7 +61,7 @@ class GuiLipsum(QDialog):
nPx = self.mainConf.pxInt(64) nPx = self.mainConf.pxInt(64)
vSp = self.mainConf.pxInt(4) vSp = self.mainConf.pxInt(4)
self.docIcon = QLabel() self.docIcon = QLabel()
self.docIcon.setPixmap(self.theParent.theTheme.getPixmap("proj_document", (nPx, nPx))) self.docIcon.setPixmap(self.mainTheme.getPixmap("proj_document", (nPx, nPx)))
self.leftBox = QVBoxLayout() self.leftBox = QVBoxLayout()
self.leftBox.setSpacing(vSp) self.leftBox.setSpacing(vSp)
@@ -129,7 +129,7 @@ class GuiLipsum(QDialog):
pCount = self.paraCount.value() pCount = self.paraCount.value()
inText = "\n\n".join(lipsumText[0:pCount]) + "\n\n" inText = "\n\n".join(lipsumText[0:pCount]) + "\n\n"
self.theParent.docEditor.insertText(inText) self.mainGui.docEditor.insertText(inText)
return return
+9 -9
View File
@@ -48,17 +48,17 @@ PAGE_FINAL = 4
class GuiProjectWizard(QWizard): class GuiProjectWizard(QWizard):
def __init__(self, theParent): def __init__(self, mainGui):
QWizard.__init__(self, theParent) QWizard.__init__(self, mainGui)
logger.debug("Initialising GuiProjectWizard ...") logger.debug("Initialising GuiProjectWizard ...")
self.setObjectName("GuiProjectWizard") self.setObjectName("GuiProjectWizard")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.sideImage = self.theTheme.loadDecoration( self.sideImage = self.mainTheme.loadDecoration(
"wiz-back", None, self.mainConf.pxInt(370) "wiz-back", None, self.mainConf.pxInt(370)
) )
self.setWizardStyle(QWizard.ModernStyle) self.setWizardStyle(QWizard.ModernStyle)
@@ -92,7 +92,7 @@ class ProjWizardIntroPage(QWizardPage):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theWizard = theWizard self.theWizard = theWizard
self.theTheme = theWizard.theTheme self.mainTheme = theWizard.mainTheme
self.setTitle(self.tr("Create New Project")) self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
@@ -107,7 +107,7 @@ class ProjWizardIntroPage(QWizardPage):
"Peter Mitterhofer", "CC BY-SA 4.0" "Peter Mitterhofer", "CC BY-SA 4.0"
)) ))
lblFont = self.imgCredit.font() lblFont = self.imgCredit.font()
lblFont.setPointSizeF(0.6*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.6*self.mainTheme.fontPointSize)
self.imgCredit.setFont(lblFont) self.imgCredit.setFont(lblFont)
xW = self.mainConf.pxInt(300) xW = self.mainConf.pxInt(300)
@@ -162,7 +162,7 @@ class ProjWizardFolderPage(QWizardPage):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theWizard = theWizard self.theWizard = theWizard
self.theTheme = theWizard.theTheme self.mainTheme = theWizard.mainTheme
self.setTitle(self.tr("Select Project Folder")) self.setTitle(self.tr("Select Project Folder"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
@@ -180,7 +180,7 @@ class ProjWizardFolderPage(QWizardPage):
self.projPath.setPlaceholderText(self.tr("Required")) self.projPath.setPlaceholderText(self.tr("Required"))
self.browseButton = QPushButton("...") self.browseButton = QPushButton("...")
self.browseButton.setMaximumWidth(int(2.5*self.theTheme.getTextWidth("..."))) self.browseButton.setMaximumWidth(int(2.5*self.mainTheme.getTextWidth("...")))
self.browseButton.clicked.connect(self._doBrowse) self.browseButton.clicked.connect(self._doBrowse)
self.errLabel = QLabel("") self.errLabel = QLabel("")
+21 -21
View File
@@ -57,16 +57,16 @@ class GuiWritingStats(QDialog):
FMT_JSON = 0 FMT_JSON = 0
FMT_CSV = 1 FMT_CSV = 1
def __init__(self, theParent): def __init__(self, mainGui):
QDialog.__init__(self, theParent) QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiWritingStats ...") logger.debug("Initialising GuiWritingStats ...")
self.setObjectName("GuiWritingStats") self.setObjectName("GuiWritingStats")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theParent = theParent self.mainGui = mainGui
self.theTheme = theParent.theTheme self.mainTheme = mainGui.mainTheme
self.theProject = theParent.theProject self.theProject = mainGui.theProject
self.logData = [] self.logData = []
self.filterData = [] self.filterData = []
@@ -125,7 +125,7 @@ class GuiWritingStats(QDialog):
self.listBox.setSortingEnabled(True) self.listBox.setSortingEnabled(True)
# Word Bar # Word Bar
self.barHeight = int(round(0.5*self.theTheme.fontPixelSize)) self.barHeight = int(round(0.5*self.mainTheme.fontPixelSize))
self.barWidth = self.mainConf.pxInt(200) self.barWidth = self.mainConf.pxInt(200)
self.barImage = QPixmap(self.barHeight, self.barHeight) self.barImage = QPixmap(self.barHeight, self.barHeight)
self.barImage.fill(self.palette().highlight().color()) self.barImage.fill(self.palette().highlight().color())
@@ -136,27 +136,27 @@ class GuiWritingStats(QDialog):
self.infoBox.setLayout(self.infoForm) self.infoBox.setLayout(self.infoForm)
self.labelTotal = QLabel(formatTime(0)) self.labelTotal = QLabel(formatTime(0))
self.labelTotal.setFont(self.theTheme.guiFontFixed) self.labelTotal.setFont(self.mainTheme.guiFontFixed)
self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.labelIdleT = QLabel(formatTime(0)) self.labelIdleT = QLabel(formatTime(0))
self.labelIdleT.setFont(self.theTheme.guiFontFixed) self.labelIdleT.setFont(self.mainTheme.guiFontFixed)
self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.labelFilter = QLabel(formatTime(0)) self.labelFilter = QLabel(formatTime(0))
self.labelFilter.setFont(self.theTheme.guiFontFixed) self.labelFilter.setFont(self.mainTheme.guiFontFixed)
self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.novelWords = QLabel("0") self.novelWords = QLabel("0")
self.novelWords.setFont(self.theTheme.guiFontFixed) self.novelWords.setFont(self.mainTheme.guiFontFixed)
self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.notesWords = QLabel("0") self.notesWords = QLabel("0")
self.notesWords.setFont(self.theTheme.guiFontFixed) self.notesWords.setFont(self.mainTheme.guiFontFixed)
self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.totalWords = QLabel("0") self.totalWords = QLabel("0")
self.totalWords.setFont(self.theTheme.guiFontFixed) self.totalWords.setFont(self.mainTheme.guiFontFixed)
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
lblTTime = QLabel(self.tr("Total Time:")) lblTTime = QLabel(self.tr("Total Time:"))
@@ -183,7 +183,7 @@ class GuiWritingStats(QDialog):
self.infoForm.setRowStretch(6, 1) self.infoForm.setRowStretch(6, 1)
# Filter Options # Filter Options
sPx = self.theTheme.baseIconSize sPx = self.mainTheme.baseIconSize
self.filterBox = QGroupBox(self.tr("Filters"), self) self.filterBox = QGroupBox(self.tr("Filters"), self)
self.filterForm = QGridLayout(self) self.filterForm = QGridLayout(self)
@@ -411,11 +411,11 @@ class GuiWritingStats(QDialog):
# Report to user # Report to user
if wSuccess: if wSuccess:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("{0} file successfully written to:").format(textFmt), savePath self.tr("{0} file successfully written to:").format(textFmt), savePath
], nwAlert.INFO) ], nwAlert.INFO)
else: else:
self.theParent.makeAlert([ self.mainGui.makeAlert([
self.tr("Failed to write {0} file.").format(textFmt), errMsg self.tr("Failed to write {0} file.").format(textFmt), errMsg
], nwAlert.ERROR) ], nwAlert.ERROR)
@@ -478,7 +478,7 @@ class GuiWritingStats(QDialog):
self.logData.append((dStart, sDiff, wcNovel, wcNotes, sIdle)) self.logData.append((dStart, sDiff, wcNovel, wcNotes, sIdle))
except Exception as exc: except Exception as exc:
self.theParent.makeAlert(self.tr( self.mainGui.makeAlert(self.tr(
"Failed to read session log file." "Failed to read session log file."
), nwAlert.ERROR, exception=exc) ), nwAlert.ERROR, exception=exc)
return False return False
@@ -605,13 +605,13 @@ class GuiWritingStats(QDialog):
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight) newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter) newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setFont(self.C_TIME, self.theTheme.guiFontFixed) newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
newItem.setFont(self.C_LENGTH, self.theTheme.guiFontFixed) newItem.setFont(self.C_LENGTH, self.mainTheme.guiFontFixed)
newItem.setFont(self.C_COUNT, self.theTheme.guiFontFixed) newItem.setFont(self.C_COUNT, self.mainTheme.guiFontFixed)
if showIdleTime: if showIdleTime:
newItem.setFont(self.C_IDLE, self.theTheme.guiFontFixed) newItem.setFont(self.C_IDLE, self.mainTheme.guiFontFixed)
else: else:
newItem.setFont(self.C_IDLE, self.theTheme.guiFont) newItem.setFont(self.C_IDLE, self.mainTheme.guiFont)
self.listBox.addTopLevelItem(newItem) self.listBox.addTopLevelItem(newItem)
self.timeFilter += sDiff self.timeFilter += sDiff
+1 -1
View File
@@ -1,7 +1,7 @@
%%~name: Chapter One %%~name: Chapter One
%%~path: e5e47ebf63b1c/a520879ca0b45 %%~path: e5e47ebf63b1c/a520879ca0b45
%%~kind: NOVEL/DOCUMENT %%~kind: NOVEL/DOCUMENT
### Chapter One ## Chapter One
@pov: Jane @pov: Jane
+21 -19
View File
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-05 15:03:00"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-15 13:07:17">
<project> <project>
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
<author>Jane Smith</author> <author>Jane Smith</author>
<author>Jay Doh</author> <author>Jay Doh</author>
<saveCount>1334</saveCount> <saveCount>1371</saveCount>
<autoCount>225</autoCount> <autoCount>236</autoCount>
<editTime>67746</editTime> <editTime>69222</editTime>
</project> </project>
<settings> <settings>
<doBackup>False</doBackup> <doBackup>False</doBackup>
@@ -15,8 +15,10 @@
<spellCheck>True</spellCheck> <spellCheck>True</spellCheck>
<spellLang>None</spellLang> <spellLang>None</spellLang>
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>a520879ca0b45</lastEdited> <lastEdited>636b6aa9b697b</lastEdited>
<lastViewed>636b6aa9b697b</lastViewed> <lastViewed>636b6aa9b697b</lastViewed>
<lastNovel>7031beac91f75</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>1363</lastWordCount> <lastWordCount>1363</lastWordCount>
<novelWordCount>954</novelWordCount> <novelWordCount>954</novelWordCount>
<notesWordCount>409</notesWordCount> <notesWordCount>409</notesWordCount>
@@ -33,13 +35,13 @@
<section></section> <section></section>
</titleFormat> </titleFormat>
<status> <status>
<entry key="sf12341" count="7" red="100" green="100" blue="100">New</entry> <entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
<entry key="sf24ce6" count="1" red="200" green="50" blue="0">Notes</entry> <entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
<entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry> <entry key="sc24b8f" count="3" red="182" green="60" blue="0">Started</entry>
<entry key="s90e6c9" count="6" red="193" green="129" blue="0">1st Draft</entry> <entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st Draft</entry>
<entry key="sd51c5b" count="1" red="193" green="129" blue="0">2nd Draft</entry> <entry key="sd51c5b" count="0" red="193" green="129" blue="0">2nd Draft</entry>
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry> <entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
<entry key="s78ea90" count="0" red="58" green="180" blue="58">Finished</entry> <entry key="s78ea90" count="1" red="58" green="180" blue="58">Finished</entry>
</status> </status>
<importance> <importance>
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry> <entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
@@ -63,34 +65,34 @@
</item> </item>
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/> <meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
<name status="sf12341" import="ia857f0" exported="True">Part One</name> <name status="s90e6c9" import="ia857f0" exported="True">Part One</name>
</item> </item>
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/> <meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
<name status="sf24ce6" import="ia857f0" exported="True">Chapter One</name> <name status="sf24ce6" import="ia857f0" exported="True">Chapter One</name>
</item> </item>
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="2687" wordCount="479" paraCount="14" cursorPos="61"/> <meta expanded="False" charCount="2687" wordCount="479" paraCount="14" cursorPos="2183"/>
<name status="s90e6c9" import="ia857f0" exported="True">Making a Scene</name> <name status="s90e6c9" import="ia857f0" exported="True">Making a Scene</name>
</item> </item>
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="548" wordCount="108" paraCount="3" cursorPos="649"/> <meta expanded="False" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
<name status="s90e6c9" import="ia857f0" exported="True">Another Scene</name> <name status="s90e6c9" import="ia857f0" exported="True">Another Scene</name>
</item> </item>
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/> <meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
<name status="sf12341" import="ia857f0" exported="True">Interlude</name> <name status="s78ea90" import="ia857f0" exported="True">Interlude</name>
</item> </item>
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE"> <item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
<meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/> <meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
<name status="sd51c5b" import="ia857f0" exported="False">A Note on Structure</name> <name status="sf24ce6" import="ia857f0" exported="False">A Note on Structure</name>
</item> </item>
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/> <meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
<name status="s90e6c9" import="ia857f0" exported="True">Chapter Two</name> <name status="s90e6c9" import="ia857f0" exported="True">Chapter Two</name>
</item> </item>
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/> <meta expanded="False" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
<name status="s90e6c9" import="ia857f0" exported="True">We Found John!</name> <name status="s90e6c9" import="ia857f0" exported="True">We Found John!</name>
</item> </item>
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL"> <item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
@@ -99,7 +101,7 @@
</item> </item>
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/> <meta expanded="False" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
<name status="sf12341" import="ia857f0" exported="True">Title Page</name> <name status="sc24b8f" import="ia857f0" exported="True">Title Page</name>
</item> </item>
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT"> <item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/> <meta expanded="False" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
+2
View File
@@ -16,6 +16,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>7a992350f3eb6</lastEdited> <lastEdited>7a992350f3eb6</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>3847</lastWordCount> <lastWordCount>3847</lastWordCount>
<novelWordCount>3109</novelWordCount> <novelWordCount>3109</novelWordCount>
<notesWordCount>738</notesWordCount> <notesWordCount>738</notesWordCount>
+2
View File
@@ -17,6 +17,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>10</lastWordCount> <lastWordCount>10</lastWordCount>
<novelWordCount>10</novelWordCount> <novelWordCount>10</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:49:23"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:51">
<project> <project>
<name>Test Custom</name> <name>Test Custom</name>
<title>Test Novel</title> <title>Test Novel</title>
@@ -17,6 +17,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>0</lastWordCount> <lastWordCount>0</lastWordCount>
<novelWordCount>0</novelWordCount> <novelWordCount>0</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:48:40"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:51">
<project> <project>
<name>Test Custom</name> <name>Test Custom</name>
<title>Test Novel</title> <title>Test Novel</title>
@@ -17,6 +17,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>0</lastWordCount> <lastWordCount>0</lastWordCount>
<novelWordCount>0</novelWordCount> <novelWordCount>0</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:08:21"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:51">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title>New Novel</title> <title>New Novel</title>
@@ -16,9 +16,11 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastWordCount>0</lastWordCount> <lastNovel>None</lastNovel>
<novelWordCount>0</novelWordCount> <lastOutline>None</lastOutline>
<notesWordCount>0</notesWordCount> <lastWordCount>2</lastWordCount>
<novelWordCount>1</novelWordCount>
<notesWordCount>1</notesWordCount>
<autoReplace/> <autoReplace/>
<titleFormat> <titleFormat>
<title>%title%</title> <title>%title%</title>
@@ -28,19 +30,19 @@
<section></section> <section></section>
</titleFormat> </titleFormat>
<status> <status>
<entry key="s000008" count="5" red="100" green="100" blue="100">New</entry> <entry key="s000008" count="7" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry> <entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry> <entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry> <entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status> </status>
<importance> <importance>
<entry key="i00000c" count="3" red="100" green="100" blue="100">New</entry> <entry key="i00000c" count="4" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry> <entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry> <entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry> <entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance> </importance>
</settings> </settings>
<content count="10"> <content count="11">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL"> <item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name> <name status="s000008" import="i00000c">Novel</name>
@@ -73,13 +75,17 @@
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">New Scene</name> <name status="s000008" import="i00000c" exported="True">New Scene</name>
</item> </item>
<item handle="0000000000028" parent="31489056e0916" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT"> <item handle="0000000000028" parent="0000000000015" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False"/>
<name status="None" import="None" exported="True">Hello</name> <name status="s000008" import="i00000c">Stuff</name>
</item> </item>
<item handle="0000000000029" parent="71ee45a3c0db9" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT"> <item handle="0000000000029" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
<name status="None" import="None" exported="True">Jane</name> <name status="s000008" import="i00000c" exported="True">Hello</name>
</item>
<item handle="000000000002a" parent="0000000000012" root="0000000000012" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="False" charCount="4" wordCount="1" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Jane</name>
</item> </item>
</content> </content>
</novelWriterXML> </novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:50:26"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:51">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title></title> <title></title>
@@ -15,6 +15,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>0</lastWordCount> <lastWordCount>0</lastWordCount>
<novelWordCount>0</novelWordCount> <novelWordCount>0</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 22:07:28"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:51">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title>New Novel</title> <title>New Novel</title>
@@ -16,6 +16,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>0</lastWordCount> <lastWordCount>0</lastWordCount>
<novelWordCount>0</novelWordCount> <novelWordCount>0</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:27:16"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:52:44">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title>New Novel</title> <title>New Novel</title>
@@ -16,6 +16,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>000000000000f</lastEdited> <lastEdited>000000000000f</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>0000000000008</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>129</lastWordCount> <lastWordCount>129</lastWordCount>
<novelWordCount>102</novelWordCount> <novelWordCount>102</novelWordCount>
<notesWordCount>27</notesWordCount> <notesWordCount>27</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:25:41"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:57">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title>New Novel</title> <title>New Novel</title>
@@ -16,6 +16,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>9</lastWordCount> <lastWordCount>9</lastWordCount>
<novelWordCount>9</novelWordCount> <novelWordCount>9</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:31:17"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:53">
<project> <project>
<name>Project Name</name> <name>Project Name</name>
<title>Project Title</title> <title>Project Title</title>
@@ -17,6 +17,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastNovel>None</lastNovel>
<lastOutline>None</lastOutline>
<lastWordCount>9</lastWordCount> <lastWordCount>9</lastWordCount>
<novelWordCount>9</novelWordCount> <novelWordCount>9</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
-3
View File
@@ -232,9 +232,6 @@ def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd):
assert theIndex.getHandleHeaderLevel(cHandle) == "H1" assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
assert theIndex.getHandleHeaderLevel(nHandle) == "H1" assert theIndex.getHandleHeaderLevel(nHandle) == "H1"
assert theIndex.getHandleHeaderIntLevel(cHandle) == 1
assert theIndex.getHandleHeaderIntLevel(nHandle) == 1
assert theIndex.getHandleHeaderIntLevel("stuff") == 0
# Zero Items # Zero Items
assert theIndex.checkThese([], cItem) == [] assert theIndex.checkThese([], cItem) == []
+81 -81
View File
@@ -29,10 +29,14 @@ from lxml import etree
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
from mock import causeOSError from mock import causeOSError
from novelwriter.core.project import NWProject
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
from novelwriter.common import formatTimeStamp from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
from novelwriter.core.tree import NWTree
from novelwriter.core.index import NWIndex
from novelwriter.core.project import NWProject
from novelwriter.core.options import OptionState
from novelwriter.core.document import NWDoc
@pytest.mark.core @pytest.mark.core
@@ -280,12 +284,12 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
@pytest.mark.core @pytest.mark.core
def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd): def testCoreProject_NewFileFolder(fncDir, outDir, refDir, mockGUI, mockRnd):
"""Check that new files can be added to the project. """Check that new files can be added to the project.
""" """
projFile = os.path.join(fncDir, "nwProject.nwx") projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx") testFile = os.path.join(outDir, "coreProject_NewFileFolder_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx") compFile = os.path.join(refDir, "coreProject_NewFileFolder_nwProject.nwx")
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
buildTestProject(theProject, fncDir) buildTestProject(theProject, fncDir)
@@ -295,9 +299,33 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True assert theProject.closeProject() is True
assert theProject.openProject(projFile) is True assert theProject.openProject(projFile) is True
assert isinstance(theProject.newFile("Hello", "31489056e0916"), str) # Invalid call
assert isinstance(theProject.newFile("Jane", "71ee45a3c0db9"), str) assert theProject.newFolder("New Folder", "1234567890abc") is None
assert theProject.projChanged assert theProject.newFile("New File", "1234567890abc") is None
# Add files properly
assert theProject.newFolder("Stuff", "0000000000015") == "0000000000028"
assert theProject.newFile("Hello", "0000000000015") == "0000000000029"
assert theProject.newFile("Jane", "0000000000012") == "000000000002a"
assert "0000000000028" in theProject.tree
assert "0000000000029" in theProject.tree
assert "000000000002a" in theProject.tree
# Write to file, failed
assert theProject.writeNewFile("blabla", 1, True) is False # Not a handle
assert theProject.writeNewFile("0000000000028", 1, True) is False # Not a file
assert theProject.writeNewFile("0000000000014", 1, True) is False # Already has content
# Write to file, success
assert theProject.writeNewFile("0000000000029", 2, True) is True
assert NWDoc(theProject, "0000000000029").readDocument() == "## Hello\n\n"
assert theProject.writeNewFile("000000000002a", 1, False) is True
assert NWDoc(theProject, "000000000002a").readDocument() == "# Jane\n\n"
# Save, close and check
assert theProject.projChanged is True
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.closeProject() is True assert theProject.closeProject() is True
@@ -305,7 +333,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False assert theProject.projChanged is False
# END Test testCoreProject_NewFile # END Test testCoreProject_NewFileFolder
@pytest.mark.core @pytest.mark.core
@@ -435,12 +463,15 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
os.rename(oName, rName) os.rename(oName, rName)
# Add some legacy stuff that cannot be removed # Add some legacy stuff that cannot be removed
writeFile(os.path.join(nwMinimal, "junk"), "stuff") with monkeypatch.context() as mp:
os.mkdir(os.path.join(nwMinimal, "data_0")) mp.setattr(theProject, "_legacyDataFolder", causeOSError)
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff") os.mkdir(os.path.join(nwMinimal, "data_0"))
mockGUI.clear() writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.nwd"), "stuff")
assert theProject.openProject(nwMinimal) is True writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.bak"), "stuff")
assert "data_0" in mockGUI.lastAlert mockGUI.clear()
assert theProject.openProject(nwMinimal) is True
assert "There was an error updating the project." in mockGUI.lastAlert
assert theProject.closeProject() assert theProject.closeProject()
# END Test testCoreProject_Open # END Test testCoreProject_Open
@@ -613,6 +644,11 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theProject.openProject(nwMinimal) theProject.openProject(nwMinimal)
# Storage Objects
assert isinstance(theProject.index, NWIndex)
assert isinstance(theProject.tree, NWTree)
assert isinstance(theProject.options, OptionState)
# Move Novel ROOT to after its files # Move Novel ROOT to after its files
oldOrder = [ oldOrder = [
"a508bb932959c", # ROOT: Novel "a508bb932959c", # ROOT: Novel
@@ -722,7 +758,7 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
# Change Importance # Change Importance
# ================= # =================
fHandle = theProject.newFile("Jane Doe", "8b9d2e465e150") fHandle = theProject.newFile("Jane Doe", "0000000000012")
theProject.tree[fHandle].setImport("Main") theProject.tree[fHandle].setImport("Main")
assert theProject.tree[fHandle].itemImport == importKeys[3] assert theProject.tree[fHandle].itemImport == importKeys[3]
@@ -894,6 +930,10 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
assert theProject.setProjectLang("en_GB") is True assert theProject.setProjectLang("en_GB") is True
assert theProject.projLang == "en_GB" assert theProject.projLang == "en_GB"
# Language Lookup
assert theProject.localLookup(1) == "One"
assert theProject.localLookup(10) == "Ten"
# Automatic outline update # Automatic outline update
theProject.projChanged = False theProject.projChanged = False
assert theProject.setAutoOutline(True) assert theProject.setAutoOutline(True)
@@ -1007,7 +1047,8 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
# Add a file with non-existent parent # Add a file with non-existent parent
# This file will be renoved from the project on open # This file will be renoved from the project on open
assert theProject.newFile("Oops", "0000000000000") oHandle = theProject.newFile("Oops", "b3643d0f92e32")
theProject.tree[oHandle].setParent("1234567890abc")
# Save and close # Save and close
assert theProject.saveProject() is True assert theProject.saveProject() is True
@@ -1103,14 +1144,6 @@ def testCoreProject_OldFormat(mockGUI, nwOldProj):
os.path.join(nwOldProj, "meta", "sessionLogOptions.json"), os.path.join(nwOldProj, "meta", "sessionLogOptions.json"),
] ]
# Add some files that shouldn't be there
deleteFiles.append(os.path.join(nwOldProj, "data_f", "whatnow.nwd"))
deleteFiles.append(os.path.join(nwOldProj, "data_f", "whatnow.txt"))
# Add some folders that shouldn't be there
os.mkdir(os.path.join(nwOldProj, "stuff"))
os.mkdir(os.path.join(nwOldProj, "data_1", "stuff"))
# Create mock files # Create mock files
os.mkdir(os.path.join(nwOldProj, "cache")) os.mkdir(os.path.join(nwOldProj, "cache"))
for aFile in deleteFiles: for aFile in deleteFiles:
@@ -1124,7 +1157,6 @@ def testCoreProject_OldFormat(mockGUI, nwOldProj):
for aFile in deleteFiles: for aFile in deleteFiles:
assert not os.path.isfile(aFile) assert not os.path.isfile(aFile)
assert not os.path.isdir(os.path.join(nwOldProj, "data_1", "stuff"))
assert not os.path.isdir(os.path.join(nwOldProj, "data_1")) assert not os.path.isdir(os.path.join(nwOldProj, "data_1"))
assert not os.path.isdir(os.path.join(nwOldProj, "data_7")) assert not os.path.isdir(os.path.join(nwOldProj, "data_7"))
assert not os.path.isdir(os.path.join(nwOldProj, "data_8")) assert not os.path.isdir(os.path.join(nwOldProj, "data_8"))
@@ -1132,12 +1164,6 @@ def testCoreProject_OldFormat(mockGUI, nwOldProj):
assert not os.path.isdir(os.path.join(nwOldProj, "data_a")) assert not os.path.isdir(os.path.join(nwOldProj, "data_a"))
assert not os.path.isdir(os.path.join(nwOldProj, "data_f")) assert not os.path.isdir(os.path.join(nwOldProj, "data_f"))
# Check stuff that has been moved
assert os.path.isdir(os.path.join(nwOldProj, "junk"))
assert os.path.isdir(os.path.join(nwOldProj, "junk", "stuff"))
assert os.path.isfile(os.path.join(nwOldProj, "junk", "whatnow.nwd"))
assert os.path.isfile(os.path.join(nwOldProj, "junk", "whatnow.txt"))
# Check that files we want to keep are in the right place # Check that files we want to keep are in the right place
assert os.path.isdir(os.path.join(nwOldProj, "cache")) assert os.path.isdir(os.path.join(nwOldProj, "cache"))
assert os.path.isdir(os.path.join(nwOldProj, "content")) assert os.path.isdir(os.path.join(nwOldProj, "content"))
@@ -1179,7 +1205,7 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.unlink", causeOSError) mp.setattr("os.unlink", causeOSError)
assert not theProject._deprecatedFiles() assert theProject._deprecatedFiles() is False
assert theProject._deprecatedFiles() assert theProject._deprecatedFiles()
assert not os.path.isfile(tstFile) assert not os.path.isfile(tstFile)
@@ -1188,63 +1214,36 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
tstFile = os.path.join(fncDir, "data_0") tstFile = os.path.join(fncDir, "data_0")
writeFile(tstFile, "stuff") writeFile(tstFile, "stuff")
assert os.path.isfile(tstFile) assert os.path.isfile(tstFile)
assert theProject._legacyDataFolder(tstFile) is False
errList = []
errList = theProject._legacyDataFolder(tstFile, errList)
assert len(errList) > 0
# Move folder in data folder, shouldn't be there
tstData = os.path.join(fncDir, "data_1")
errItem = os.path.join(fncDir, "data_1", "stuff")
os.mkdir(tstData)
os.mkdir(errItem)
assert os.path.isdir(tstData)
assert os.path.isdir(errItem)
# This causes a failure to create the 'junk' folder
with monkeypatch.context() as mp:
mp.setattr("os.mkdir", causeOSError)
errList = []
errList = theProject._legacyDataFolder(tstData, errList)
assert len(errList) > 0
# This causes a failure to move 'stuff' to 'junk'
with monkeypatch.context() as mp:
mp.setattr("os.rename", causeOSError)
errList = []
errList = theProject._legacyDataFolder(tstData, errList)
assert len(errList) > 0
# This should be successful
errList = []
errList = theProject._legacyDataFolder(tstData, errList)
assert len(errList) == 0
assert os.path.isdir(os.path.join(fncDir, "junk", "stuff"))
# Check renaming/deleting of old document files # Check renaming/deleting of old document files
tstData = os.path.join(fncDir, "data_2") tstData2 = os.path.join(fncDir, "data_2")
tstDoc1m = os.path.join(tstData, "000000000001_main.nwd") tstData3 = os.path.join(fncDir, "data_3")
tstDoc1b = os.path.join(tstData, "000000000001_main.bak") tstDoc1m = os.path.join(tstData2, "000000000001_main.nwd")
tstDoc2m = os.path.join(tstData, "000000000002_main.nwd") tstDoc1b = os.path.join(tstData2, "000000000001_main.bak")
tstDoc2b = os.path.join(tstData, "000000000002_main.bak") tstDoc2m = os.path.join(tstData2, "000000000002_main.nwd")
tstDoc3m = os.path.join(tstData, "tooshort003_main.nwd") tstDoc2b = os.path.join(tstData2, "000000000002_main.bak")
tstDoc3b = os.path.join(tstData, "tooshort003_main.bak") tstDoc3m = os.path.join(tstData3, "tooshort003_main.nwd")
tstDoc3b = os.path.join(tstData3, "tooshort003_main.bak")
tstDir4a = os.path.join(tstData3, "stuff")
os.mkdir(tstData) os.mkdir(tstData2)
os.mkdir(tstData3)
writeFile(tstDoc1m, "stuff") writeFile(tstDoc1m, "stuff")
writeFile(tstDoc1b, "stuff") writeFile(tstDoc1b, "stuff")
writeFile(tstDoc2m, "stuff") writeFile(tstDoc2m, "stuff")
writeFile(tstDoc2b, "stuff") writeFile(tstDoc2b, "stuff")
writeFile(tstDoc3m, "stuff") writeFile(tstDoc3m, "stuff")
writeFile(tstDoc3b, "stuff") writeFile(tstDoc3b, "stuff")
os.mkdir(tstDir4a)
# Make the above fail # Make the above fail
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.rename", causeOSError) mp.setattr("os.rename", causeOSError)
mp.setattr("os.unlink", causeOSError) mp.setattr("os.unlink", causeOSError)
errList = [] with pytest.raises(OSError):
errList = theProject._legacyDataFolder(tstData, errList) theProject._legacyDataFolder(tstData2)
assert len(errList) > 0 theProject._legacyDataFolder(tstData3)
assert os.path.isfile(tstDoc1m) assert os.path.isfile(tstDoc1m)
assert os.path.isfile(tstDoc1b) assert os.path.isfile(tstDoc1b)
assert os.path.isfile(tstDoc2m) assert os.path.isfile(tstDoc2m)
@@ -1253,15 +1252,16 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
assert os.path.isfile(tstDoc3b) assert os.path.isfile(tstDoc3b)
# And succeed ... # And succeed ...
errList = [] assert theProject._legacyDataFolder(tstData2) is True
errList = theProject._legacyDataFolder(tstData, errList) assert theProject._legacyDataFolder(tstData3) is True
assert len(errList) == 0
assert not os.path.isdir(tstData) assert not os.path.isdir(tstData2)
assert os.path.isdir(tstData3)
assert os.path.isfile(os.path.join(fncDir, "content", "2000000000001.nwd")) assert os.path.isfile(os.path.join(fncDir, "content", "2000000000001.nwd"))
assert os.path.isfile(os.path.join(fncDir, "content", "2000000000002.nwd")) assert os.path.isfile(os.path.join(fncDir, "content", "2000000000002.nwd"))
assert os.path.isfile(os.path.join(fncDir, "junk", "tooshort003_main.nwd")) assert os.path.isfile(os.path.join(fncDir, tstData3, "tooshort003_main.nwd"))
assert os.path.isfile(os.path.join(fncDir, "junk", "tooshort003_main.bak")) assert os.path.isfile(os.path.join(fncDir, tstData3, "tooshort003_main.bak"))
assert os.path.isdir(tstDir4a)
# END Test testCoreProject_LegacyData # END Test testCoreProject_LegacyData
+2 -2
View File
@@ -36,8 +36,8 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# NW About # NW About
nwGUI.theTheme.themeName = "A Theme" nwGUI.mainTheme.themeName = "A Theme"
nwGUI.theTheme.themeAuthor = "An Author" nwGUI.mainTheme.themeAuthor = "An Author"
assert nwGUI.showAboutNWDialog(showNotes=True) is True assert nwGUI.showAboutNWDialog(showNotes=True) is True
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
+22 -5
View File
@@ -24,11 +24,7 @@ import pytest
from PyQt5.QtCore import QItemSelectionModel from PyQt5.QtCore import QItemSelectionModel
from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox
from novelwriter.dialogs import GuiQuoteSelect, GuiUpdates from novelwriter.dialogs import GuiQuoteSelect, GuiUpdates, GuiEditLabel
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
@@ -101,3 +97,24 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
nwUpdate._doClose() nwUpdate._doClose()
# END Test testDlgOther_Updates # END Test testDlgOther_Updates
@pytest.mark.gui
def testDlgOther_EditLabel(qtbot, monkeypatch):
"""Test the label editor dialog.
"""
monkeypatch.setattr(GuiEditLabel, "exec_", lambda *a: None)
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.Accepted)
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World")
assert dlgOk is True
assert newLabel == "Hello World"
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.Rejected)
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World")
assert dlgOk is False
assert newLabel == "Hello World"
# END Test testDlgOther_EditLabel
+17 -17
View File
@@ -25,10 +25,10 @@ import pytest
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, readFile, writeFile, buildTestProject from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog from PyQt5.QtWidgets import QAction, QMessageBox
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.dialogs import GuiDocMerge, GuiItemEditor from novelwriter.dialogs import GuiDocMerge, GuiEditLabel
from novelwriter.core.tree import NWTree from novelwriter.core.tree import NWTree
@@ -39,6 +39,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create a new project # Create a new project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
@@ -54,13 +55,12 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
hMergedDoc = "0000000000023" hMergedDoc = "0000000000023"
# Add Project Content # Add Project Content
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
nwGUI.treeView.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
nwGUI.treeView.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
assert nwGUI.saveProject() is True assert nwGUI.saveProject() is True
assert nwGUI.closeProject() is True assert nwGUI.closeProject() is True
@@ -82,8 +82,8 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Open the Merge tool # Open the Merge tool
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None) monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
nwGUI.mainMenu.aMergeDocs.activate(QAction.Trigger) nwGUI.mainMenu.aMergeDocs.activate(QAction.Trigger)
@@ -101,27 +101,27 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
assert nwMerge.listBox.count() == 0 assert nwMerge.listBox.count() == 0
# No item selected # No item selected
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
assert nwMerge._populateList() is False assert nwMerge._populateList() is False
assert nwMerge.listBox.count() == 0 assert nwMerge.listBox.count() == 0
# Non-existing item # Non-existing item
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(NWTree, "__getitem__", lambda *a: None) mp.setattr(NWTree, "__getitem__", lambda *a: None)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
assert nwMerge._populateList() is False assert nwMerge._populateList() is False
assert nwMerge.listBox.count() == 0 assert nwMerge.listBox.count() == 0
# Select a non-folder # Select a non-folder
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hChapterOne).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterOne).setSelected(True)
assert nwMerge._populateList() is False assert nwMerge._populateList() is False
assert nwMerge.listBox.count() == 0 assert nwMerge.listBox.count() == 0
# Select the chapter folder # Select the chapter folder
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
assert nwMerge._populateList() is True assert nwMerge._populateList() is True
assert nwMerge.listBox.count() == 5 assert nwMerge.listBox.count() == 5
+13 -13
View File
@@ -25,10 +25,10 @@ import pytest
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, readFile, writeFile, buildTestProject from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog from PyQt5.QtWidgets import QAction, QMessageBox
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.dialogs import GuiDocSplit, GuiItemEditor from novelwriter.dialogs import GuiDocSplit, GuiEditLabel
from novelwriter.core.tree import NWTree from novelwriter.core.tree import NWTree
from novelwriter.core.document import NWDoc from novelwriter.core.document import NWDoc
@@ -40,6 +40,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create a new project # Create a new project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
@@ -58,11 +59,10 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
hSceneFive = "0000000000028" hSceneFive = "0000000000028"
# Add Project Content # Add Project Content
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hNovelRoot).setSelected(True) nwGUI.projView.projTree._getTreeItem(hNovelRoot).setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
assert nwGUI.saveProject() is True assert nwGUI.saveProject() is True
assert nwGUI.closeProject() is True assert nwGUI.closeProject() is True
@@ -89,8 +89,8 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Open the Split tool # Open the Split tool
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hToSplit).setSelected(True) nwGUI.projView.projTree._getTreeItem(hToSplit).setSelected(True)
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None) monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
nwGUI.mainMenu.aSplitDoc.activate(QAction.Trigger) nwGUI.mainMenu.aSplitDoc.activate(QAction.Trigger)
@@ -109,7 +109,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# No item selected # No item selected
nwSplit.sourceItem = None nwSplit.sourceItem = None
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
assert nwSplit._populateList() is False assert nwSplit._populateList() is False
assert nwSplit.listBox.count() == 0 assert nwSplit.listBox.count() == 0
@@ -117,15 +117,15 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(NWTree, "__getitem__", lambda *a: None) mp.setattr(NWTree, "__getitem__", lambda *a: None)
nwSplit.sourceItem = None nwSplit.sourceItem = None
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hToSplit).setSelected(True) nwGUI.projView.projTree._getTreeItem(hToSplit).setSelected(True)
assert nwSplit._populateList() is False assert nwSplit._populateList() is False
assert nwSplit.listBox.count() == 0 assert nwSplit.listBox.count() == 0
# Select a non-file # Select a non-file
nwSplit.sourceItem = None nwSplit.sourceItem = None
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
assert nwSplit._populateList() is False assert nwSplit._populateList() is False
assert nwSplit.listBox.count() == 0 assert nwSplit.listBox.count() == 0
-245
View File
@@ -1,245 +0,0 @@
"""
novelWriter Item Editor Dialog Class Tester
=============================================
This file is a part of novelWriter
Copyright 20182022, 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 pytest
from tools import getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QDialog, QMessageBox
from novelwriter.gui import GuiProjectTree
from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.dialogs import GuiItemEditor
from novelwriter.core.tree import NWTree
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
@pytest.mark.gui
def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test launching the item editor dialog from GuiMain.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# Block Dialog exec_
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: None)
# Open Editor wo/Project
assert nwGUI.editItem() is False
# Create and Open Project
buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f"
# No Selection
nwGUI.treeView.clearSelection()
assert nwGUI.editItem() is False
# Force opening from editor
assert nwGUI.openDocument(tHandle)
nwGUI.isFocusMode = True
# Block Tree Lookup
with monkeypatch.context() as mp:
mp.setattr(NWTree, "__getitem__", lambda *a: None)
assert nwGUI.editItem() is False
# Invalid Type
nwGUI.theProject.tree[tHandle]._type = nwItemType.NO_TYPE
assert nwGUI.editItem() is False
nwGUI.theProject.tree[tHandle]._type = nwItemType.FILE
# Open Properly
assert nwGUI.editItem() is True
qtbot.waitUntil(lambda: getGuiItem("GuiItemEditor") is not None, timeout=1000)
itemEdit = getGuiItem("GuiItemEditor")
assert itemEdit is not None
itemEdit.close()
# Open Via Menu
with monkeypatch.context() as mp:
mp.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aEditItem.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiItemEditor") is not None, timeout=1000)
itemEdit = getGuiItem("GuiItemEditor")
assert itemEdit is not None
itemEdit.close()
nwGUI.isFocusMode = False
# END Test testDlgItemEditor_Dialog
@pytest.mark.gui
def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a novel document.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f"
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.openDocument(tHandle) is True
# Check that an invalid handle is managed
itemEdit = GuiItemEditor(nwGUI, "whatever")
itemEdit.show()
itemEdit._doClose()
# Edit a Document
itemEdit = GuiItemEditor(nwGUI, tHandle)
itemEdit.show()
# Check Existing Settings
assert itemEdit.editName.text() == "New Scene"
assert itemEdit.editStatus.currentData() == statusKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.DOCUMENT
assert itemEdit.editExport.isChecked() is True
# Change Settings
layoutIdx = itemEdit.editLayout.findData(nwItemLayout.NOTE)
itemEdit.editName.setText("Great Scene")
itemEdit.editStatus.setCurrentIndex(1)
itemEdit.editLayout.setCurrentIndex(layoutIdx)
itemEdit.editExport.setChecked(False)
# Check New Settings
itemEdit._doSave()
assert itemEdit.theItem.itemName == "Great Scene"
assert itemEdit.theItem.itemStatus == statusKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NOTE
assert itemEdit.theItem.isExported is False
# Check that the editor header is updated
nwGUI.docEditor.updateDocInfo(tHandle)
assert nwGUI.docEditor.docHeader.theTitle.text() == "Novel New Chapter Great Scene"
itemEdit.close()
del itemEdit
# qtbot.stopForInteraction()
# END Test testDlgItemEditor_Dialog
@pytest.mark.gui
def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a project note.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
buildTestProject(nwGUI, fncProj)
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.theProject.importItems.name(importKeys[0]) == "New"
assert nwGUI.theProject.importItems.name(importKeys[1]) == "Minor"
# Create Note
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
# Open Note
assert nwGUI.openDocument("0000000000010")
# Edit a Document
itemEdit = GuiItemEditor(nwGUI, "0000000000010")
itemEdit.show()
# Check Existing Settings
assert itemEdit.editName.text() == "New Note"
assert itemEdit.editStatus.currentData() == importKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.NOTE
assert itemEdit.editExport.isChecked() is True
# Change Settings
itemEdit.editName.setText("New Character")
itemEdit.editStatus.setCurrentIndex(1)
itemEdit.editExport.setChecked(False)
itemEdit._doSave()
# Check New Settings
assert itemEdit.theItem.itemName == "New Character"
assert itemEdit.theItem.itemStatus == statusKeys[0]
assert itemEdit.theItem.itemImport == importKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NOTE
assert itemEdit.theItem.isExported is False
itemEdit.close()
del itemEdit
# qtbot.stopForInteraction()
# END Test testDlgItemEditor_Note
@pytest.mark.gui
def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a folder.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
buildTestProject(nwGUI, fncProj)
# Edit a Folder
itemEdit = GuiItemEditor(nwGUI, "000000000000d")
itemEdit.show()
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
# Check Existing Settings
assert itemEdit.editName.text() == "New Chapter"
assert itemEdit.editStatus.currentData() == statusKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.NO_LAYOUT
assert itemEdit.editExport.isChecked() is False
assert itemEdit.editLayout.isEnabled() is False
assert itemEdit.editExport.isEnabled() is False
# Change Settings
itemEdit.editName.setText("Chapter One")
itemEdit.editStatus.setCurrentIndex(1)
# Check New Settings
itemEdit._doSave()
assert itemEdit.theItem.itemName == "Chapter One"
assert itemEdit.theItem.itemStatus == statusKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert itemEdit.theItem.isExported is False
itemEdit.close()
del itemEdit
# qtbot.stopForInteraction()
# END Test testDlgItemEditor_Folder
+1 -1
View File
@@ -1147,7 +1147,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
assert nwGUI.openDocument(cHandle) is True assert nwGUI.openDocument(cHandle) is True
assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.docEditor.replaceText(theText) is True
assert nwGUI.saveDocument() is True assert nwGUI.saveDocument() is True
assert nwGUI.treeView.revealNewTreeItem(cHandle) assert nwGUI.projView.revealNewTreeItem(cHandle)
nwGUI.docEditor.updateTagHighLighting() nwGUI.docEditor.updateTagHighLighting()
# Follow Tag # Follow Tag
+5 -5
View File
@@ -51,12 +51,12 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.theProject.index._itemIndex._items != {} assert nwGUI.theProject.index._itemIndex._items != {}
# Select a document in the project tree # Select a document in the project tree
nwGUI.treeView.setSelectedHandle("88243afbe5ed8") nwGUI.projView.setSelectedHandle("88243afbe5ed8")
# Middle-click the selected item # Middle-click the selected item
theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8") theItem = nwGUI.projView.projTree._getTreeItem("88243afbe5ed8")
theRect = nwGUI.treeView.visualItemRect(theItem) theRect = nwGUI.projView.projTree.visualItemRect(theItem)
qtbot.mouseClick(nwGUI.treeView.viewport(), Qt.MidButton, pos=theRect.center()) qtbot.mouseClick(nwGUI.projView.projTree.viewport(), Qt.MidButton, pos=theRect.center())
assert nwGUI.docViewer.docHandle() == "88243afbe5ed8" assert nwGUI.docViewer.docHandle() == "88243afbe5ed8"
# Reload the text # Reload the text
@@ -117,7 +117,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.docAction(nwDocAction.COPY) is False assert nwGUI.docViewer.docAction(nwDocAction.COPY) is False
# Open again via menu # Open again via menu
assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") assert nwGUI.projView.setSelectedHandle("88243afbe5ed8")
nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger) nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger)
# Select "Bod" link # Select "Bod" link
+46 -47
View File
@@ -26,14 +26,13 @@ from shutil import copyfile
from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QDialog from PyQt5.QtWidgets import QMessageBox, QInputDialog
from novelwriter.gui import ( from novelwriter.gui import GuiDocEditor, GuiNovelView, GuiOutlineView
GuiDocEditor, GuiProjectTree, GuiNovelTree, GuiOutline from novelwriter.enum import nwItemType, nwView, nwWidget
)
from novelwriter.enum import nwItemType, nwWidget
from novelwriter.tools import GuiProjectWizard from novelwriter.tools import GuiProjectWizard
from novelwriter.dialogs.itemeditor import GuiItemEditor from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.dialogs import GuiEditLabel
keyDelay = 2 keyDelay = 2
typeDelay = 1 typeDelay = 1
@@ -58,7 +57,7 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
assert nwGUI.mergeDocuments() is False assert nwGUI.mergeDocuments() is False
assert nwGUI.splitDocument() is False assert nwGUI.splitDocument() is False
assert nwGUI.openSelectedItem() is False assert nwGUI.openSelectedItem() is False
assert nwGUI.editItem() is False assert nwGUI.editItemLabel() is False
assert nwGUI.requestNovelTreeRefresh() is False assert nwGUI.requestNovelTreeRefresh() is False
assert nwGUI.rebuildIndex() is False assert nwGUI.rebuildIndex() is False
assert nwGUI.showProjectSettingsDialog() is False assert nwGUI.showProjectSettingsDialog() is False
@@ -121,44 +120,44 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
assert nwGUI.openSelectedItem() is False assert nwGUI.openSelectedItem() is False
# Project Tree has focus # Project Tree has focus
nwGUI._changeView(nwView.PROJECT)
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.projStack.setCurrentIndex(0) nwGUI.projStack.setCurrentIndex(0)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True) mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
assert nwGUI.docEditor.docHandle() is None assert nwGUI.docEditor.docHandle() is None
nwGUI.treeView._getTreeItem(sHandle).setSelected(True) nwGUI.projView.projTree._getTreeItem(sHandle).setSelected(True)
nwGUI._keyPressReturn() nwGUI._keyPressReturn()
assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.docEditor.docHandle() == sHandle
assert nwGUI.closeDocument() is True assert nwGUI.closeDocument() is True
# Novel Tree has focus # Novel Tree has focus
nwGUI.projStack.setCurrentIndex(1) nwGUI._changeView(nwView.NOVEL)
nwGUI.novelView.refreshTree(True) nwGUI.novelView.novelTree.refreshTree(rootHandle=None, overRide=True)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiNovelTree, "hasFocus", lambda *a: True) mp.setattr(GuiNovelView, "treeHasFocus", lambda *a: True)
assert nwGUI.docEditor.docHandle() is None assert nwGUI.docEditor.docHandle() is None
actItem = nwGUI.novelView.topLevelItem(0) selItem = nwGUI.novelView.novelTree.topLevelItem(2)
chpItem = actItem.child(0) nwGUI.novelView.novelTree.setCurrentItem(selItem)
selItem = chpItem.child(0)
nwGUI.novelView.setCurrentItem(selItem)
nwGUI._keyPressReturn() nwGUI._keyPressReturn()
assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.docEditor.docHandle() == sHandle
assert nwGUI.closeDocument() is True assert nwGUI.closeDocument() is True
# Project Outline has focus # Project Outline has focus
nwGUI._changeView(nwView.OUTLINE)
nwGUI.switchFocus(nwWidget.OUTLINE) nwGUI.switchFocus(nwWidget.OUTLINE)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiOutline, "treeFocus", lambda *a: True) mp.setattr(GuiOutlineView, "treeHasFocus", lambda *a: True)
assert nwGUI.docEditor.docHandle() is None assert nwGUI.docEditor.docHandle() is None
actItem = nwGUI.projView.outlineView.topLevelItem(0) actItem = nwGUI.outlineView.outlineTree.topLevelItem(0)
chpItem = actItem.child(0) chpItem = actItem.child(0)
selItem = chpItem.child(0) selItem = chpItem.child(0)
nwGUI.projView.outlineView.setCurrentItem(selItem) nwGUI.outlineView.outlineTree.setCurrentItem(selItem)
nwGUI._keyPressReturn() nwGUI._keyPressReturn()
assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.docEditor.docHandle() == sHandle
assert nwGUI.closeDocument() is True assert nwGUI.closeDocument() is True
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiMain_ProjectTreeItems # END Test testGuiMain_ProjectTreeItems
@@ -170,10 +169,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: None)
monkeypatch.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create new, save, close project # Create new, save, close project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
@@ -220,14 +219,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
assert nwGUI.theProject.spellCheck is False assert nwGUI.theProject.spellCheck is False
# Check that tree items have been created # Check that tree items have been created
assert nwGUI.treeView._getTreeItem("0000000000008") is not None assert nwGUI.projView.projTree._getTreeItem("0000000000008") is not None
assert nwGUI.treeView._getTreeItem("0000000000009") is not None assert nwGUI.projView.projTree._getTreeItem("0000000000009") is not None
assert nwGUI.treeView._getTreeItem("000000000000a") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000a") is not None
assert nwGUI.treeView._getTreeItem("000000000000b") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000b") is not None
assert nwGUI.treeView._getTreeItem("000000000000c") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000c") is not None
assert nwGUI.treeView._getTreeItem("000000000000d") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000d") is not None
assert nwGUI.treeView._getTreeItem("000000000000e") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000e") is not None
assert nwGUI.treeView._getTreeItem("000000000000f") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000f") is not None
nwGUI.mainMenu.aSpellCheck.setChecked(True) nwGUI.mainMenu.aSpellCheck.setChecked(True)
assert nwGUI.mainMenu._toggleSpellCheck() assert nwGUI.mainMenu._toggleSpellCheck()
@@ -240,9 +239,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Add a Character File # Add a Character File
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True) nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
assert nwGUI.openSelectedItem() assert nwGUI.openSelectedItem()
# Type something into the document # Type something into the document
@@ -262,9 +261,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Add a Plot File # Add a Plot File
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem("0000000000009").setSelected(True) nwGUI.projView.projTree._getTreeItem("0000000000009").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
assert nwGUI.openSelectedItem() assert nwGUI.openSelectedItem()
# Type something into the document # Type something into the document
@@ -284,9 +283,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Add a World File # Add a World File
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem("000000000000b").setSelected(True) nwGUI.projView.projTree._getTreeItem("000000000000b").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
assert nwGUI.openSelectedItem() assert nwGUI.openSelectedItem()
# Add Some Text # Add Some Text
@@ -315,10 +314,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Select the 'New Scene' file # Select the 'New Scene' file
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.treeView._getTreeItem("0000000000008").setExpanded(True) nwGUI.projView.projTree._getTreeItem("0000000000008").setExpanded(True)
nwGUI.treeView._getTreeItem("000000000000d").setExpanded(True) nwGUI.projView.projTree._getTreeItem("000000000000d").setExpanded(True)
nwGUI.treeView._getTreeItem("000000000000f").setSelected(True) nwGUI.projView.projTree._getTreeItem("000000000000f").setSelected(True)
assert nwGUI.openSelectedItem() assert nwGUI.openSelectedItem()
# Type something into the document # Type something into the document
@@ -461,12 +460,12 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
# Check a Quick Create and Delete # Check a Quick Create and Delete
assert nwGUI.treeView.newTreeItem(nwItemType.FILE, None) assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
newHandle = nwGUI.treeView.getSelectedHandle() newHandle = nwGUI.projView.getSelectedHandle()
assert nwGUI.theProject.tree["0000000000020"] is not None assert nwGUI.theProject.tree["0000000000020"] is not None
assert nwGUI.treeView.deleteItem() assert nwGUI.projView.deleteItem()
assert nwGUI.treeView.setSelectedHandle(newHandle) assert nwGUI.projView.setSelectedHandle(newHandle)
assert nwGUI.treeView.deleteItem() assert nwGUI.projView.deleteItem()
assert nwGUI.theProject.tree["0000000000024"] is not None # Trash assert nwGUI.theProject.tree["0000000000024"] is not None # Trash
assert nwGUI.saveProject() assert nwGUI.saveProject()
+3 -3
View File
@@ -467,7 +467,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
assert nwGUI.treeView._getTreeItem("000000000000f") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000f") is not None
assert nwGUI.openDocument("000000000000f") is True assert nwGUI.openDocument("000000000000f") is True
nwGUI.docEditor.clear() nwGUI.docEditor.clear()
@@ -476,10 +476,10 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert nwGUI.docEditor.getText() == "hello world" assert nwGUI.docEditor.getText() == "hello world"
nwGUI.docEditor.clear() nwGUI.docEditor.clear()
assert not nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) assert nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) is False
assert nwGUI.docEditor.isEmpty() assert nwGUI.docEditor.isEmpty()
assert not nwGUI.docEditor.insertText(None) assert nwGUI.docEditor.insertText(None) is False
assert nwGUI.docEditor.isEmpty() assert nwGUI.docEditor.isEmpty()
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
+91 -77
View File
@@ -19,73 +19,93 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import pytest
import os import os
import pytest
from tools import writeFile from tools import buildTestProject, writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from novelwriter.enum import nwWidget, nwItemType
from novelwriter.dialogs import GuiEditLabel
from novelwriter.gui.noveltree import NovelTreeColumn
@pytest.mark.gui @pytest.mark.gui
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal): def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test navigating the novel tree. """Test navigating the novel tree.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwGUI.openProject(nwMinimal) buildTestProject(nwGUI, fncProj)
nwTree = nwGUI.novelView
## nwGUI.switchFocus(nwWidget.TREE)
# Show/Hide Scrollbars nwGUI.projView.projTree.clearSelection()
## nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True)
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
nwTree.mainConf.hideVScroll = True writeFile(
nwTree.mainConf.hideHScroll = True os.path.join(nwGUI.theProject.projContent, "0000000000010.nwd"),
nwTree.initTree() "# Jane Doe\n\n@tag: Jane\n\n"
assert not nwTree.verticalScrollBar().isVisible() )
assert not nwTree.horizontalScrollBar().isVisible() writeFile(
os.path.join(nwGUI.theProject.projContent, "000000000000f.nwd"),
"### Scene One\n\n@pov: Jane\n@focus: Jane\n\n"
)
nwTree.mainConf.hideVScroll = False novelView = nwGUI.novelView
nwTree.mainConf.hideHScroll = False novelTree = novelView.novelTree
nwTree.initTree() novelBar = novelView.novelBar
assert nwTree.verticalScrollBar().isEnabled()
assert nwTree.horizontalScrollBar().isEnabled()
## # Show/Hide Scrollbars
# Populate Tree # ====================
##
nwGUI.mainConf.hideVScroll = True
nwGUI.mainConf.hideHScroll = True
novelView.initSettings()
assert not novelTree.verticalScrollBar().isVisible()
assert not novelTree.horizontalScrollBar().isVisible()
nwGUI.mainConf.hideVScroll = False
nwGUI.mainConf.hideHScroll = False
novelView.initSettings()
assert novelTree.verticalScrollBar().isEnabled()
assert novelTree.horizontalScrollBar().isEnabled()
# Populate Tree
# =============
nwGUI.projStack.setCurrentIndex(nwGUI.idxNovelView) nwGUI.projStack.setCurrentIndex(nwGUI.idxNovelView)
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
nwTree._populateTree() novelTree._populateTree(rootHandle=None)
assert nwTree.topLevelItemCount() == 1 assert novelTree.topLevelItemCount() == 3
# Rebuild should preserve selection # Rebuild should preserve selection
topItem = nwTree.topLevelItem(0) topItem = novelTree.topLevelItem(0)
assert not topItem.isSelected() assert not topItem.isSelected()
topItem.setSelected(True) topItem.setSelected(True)
assert nwTree.selectedItems()[0] == topItem assert novelTree.selectedItems()[0] == topItem
assert nwTree.getSelectedHandle() == ("a35baf2e93843", 0) assert novelView.getSelectedHandle() == ("000000000000c", 0)
nwTree.refreshTree() # Refresh using the slot for the butoom
assert nwTree.topLevelItem(0).isSelected() novelBar._refreshNovelTree()
assert novelTree.topLevelItem(0).isSelected()
## # Open Items
# Open Items # ==========
##
# Clear selection # Clear selection
nwTree.clearSelection() novelTree.clearSelection()
scItem = nwTree.topLevelItem(0).child(0).child(0) scItem = novelTree.topLevelItem(2)
scItem.setSelected(True) scItem.setSelected(True)
assert scItem.isSelected() assert scItem.isSelected()
# Clear selection with mouse # Clear selection with mouse
vPort = nwTree.viewport() vPort = novelTree.viewport()
qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10) qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10)
assert not scItem.isSelected() assert not scItem.isSelected()
@@ -93,8 +113,8 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
scItem.setSelected(True) scItem.setSelected(True)
assert scItem.isSelected() assert scItem.isSelected()
assert nwGUI.docEditor.docHandle() is None assert nwGUI.docEditor.docHandle() is None
nwTree._treeDoubleClick(scItem, 0) novelTree._treeDoubleClick(scItem, 0)
assert nwGUI.docEditor.docHandle() == "8c659a11cd429" assert nwGUI.docEditor.docHandle() == "000000000000f"
# Open item with middle mouse button # Open item with middle mouse button
scItem.setSelected(True) scItem.setSelected(True)
@@ -103,58 +123,52 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=vPort.rect().center(), delay=10) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=vPort.rect().center(), delay=10)
assert nwGUI.docViewer.docHandle() is None assert nwGUI.docViewer.docHandle() is None
scRect = nwTree.visualItemRect(scItem) scRect = novelTree.visualItemRect(scItem)
oldData = scItem.data(nwTree.C_TITLE, Qt.UserRole) oldData = scItem.data(novelTree.C_TITLE, Qt.UserRole)
scItem.setData(nwTree.C_TITLE, Qt.UserRole, (None, "", "")) scItem.setData(novelTree.C_TITLE, Qt.UserRole, (None, "", ""))
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
assert nwGUI.docViewer.docHandle() is None assert nwGUI.docViewer.docHandle() is None
scItem.setData(nwTree.C_TITLE, Qt.UserRole, oldData) scItem.setData(novelTree.C_TITLE, Qt.UserRole, oldData)
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
assert nwGUI.docViewer.docHandle() == "8c659a11cd429" assert nwGUI.docViewer.docHandle() == "000000000000f"
## # Last Column
# Populate Tree # ===========
##
# Add weird titles to first file to check hnadling of non-standard novelBar.setLastColType(NovelTreeColumn.HIDDEN)
# order of title levels. assert novelTree.isColumnHidden(novelTree.C_LAST) is True
writeFile(os.path.join(nwMinimal, "content", "a35baf2e93843.nwd"), ( assert novelTree.lastColType == NovelTreeColumn.HIDDEN
"#### Section wo/Scene\n\n" assert novelTree._getLastColumnText("000000000000f", "T000001") == ("", "")
"### Scene wo/Chapter\n\n"
"## Chapter wo/Title\n\n"
"# Title\n\n"
"#### Section w/Title, wo/Scene\n\n"
"### Scene w/Title, wo/Chapter\n\n"
"## Chapter\n\n"
"#### Section w/Chapter, wo/Scene\n\n"
"### Scene\n\n"
"#### Section\n\n"
))
nwGUI.rebuildIndex()
nwTree._populateTree()
assert nwTree.topLevelItem(0).text(nwTree.C_TITLE) == "Section wo/Scene"
assert nwTree.topLevelItem(1).text(nwTree.C_TITLE) == "Scene wo/Chapter"
assert nwTree.topLevelItem(2).text(nwTree.C_TITLE) == "Chapter wo/Title"
assert nwTree.topLevelItem(3).text(nwTree.C_TITLE) == "Title"
tTitle = nwTree.topLevelItem(3) novelBar.setLastColType(NovelTreeColumn.POV)
assert tTitle.child(0).text(nwTree.C_TITLE) == "Section w/Title, wo/Scene" assert novelTree.isColumnHidden(novelTree.C_LAST) is False
assert tTitle.child(1).text(nwTree.C_TITLE) == "Scene w/Title, wo/Chapter" assert novelTree.lastColType == NovelTreeColumn.POV
assert tTitle.child(2).text(nwTree.C_TITLE) == "Chapter" assert novelTree._getLastColumnText("000000000000f", "T000001") == (
"Jane", "Point of View: Jane"
)
tChap = tTitle.child(2) novelBar.setLastColType(NovelTreeColumn.FOCUS)
assert tChap.child(0).text(nwTree.C_TITLE) == "Section w/Chapter, wo/Scene" assert novelTree.isColumnHidden(novelTree.C_LAST) is False
assert tChap.child(1).text(nwTree.C_TITLE) == "Scene" assert novelTree.lastColType == NovelTreeColumn.FOCUS
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
"Jane", "Focus: Jane"
)
tScene = tChap.child(1) novelBar.setLastColType(NovelTreeColumn.PLOT)
assert tScene.child(0).text(nwTree.C_TITLE) == "Section" assert novelTree.isColumnHidden(novelTree.C_LAST) is False
assert novelTree.lastColType == NovelTreeColumn.PLOT
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
"", "Plot: "
)
## novelTree._lastCol = None
# Close assert novelTree._getLastColumnText("0000000000000", "T000000") == ("", "")
##
# qtbot.stopForInteraction() # Close
# =====
# qtbot.stop()
nwGUI.closeProject() nwGUI.closeProject()
# END Test testGuiNovelTree_TreeItems # END Test testGuiNovelTree_TreeItems
+56 -56
View File
@@ -46,57 +46,57 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
nwGUI._changeView(nwView.OUTLINE) nwGUI._changeView(nwView.OUTLINE)
outlineMain = nwGUI.projView outlineView = nwGUI.outlineView
outlineView = outlineMain.outlineView outlineTree = outlineView.outlineTree
outlineData = outlineMain.outlineData outlineData = outlineView.outlineData
outlineMenu = outlineMain.outlineBar.mColumns outlineMenu = outlineView.outlineBar.mColumns
# Toggle scrollbars # Toggle scrollbars
nwGUI.mainConf.hideVScroll = True nwGUI.mainConf.hideVScroll = True
nwGUI.mainConf.hideHScroll = True nwGUI.mainConf.hideHScroll = True
nwGUI.projView.initOutline() outlineView.initOutline()
assert outlineView.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineTree.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
assert outlineView.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineTree.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
nwGUI.mainConf.hideVScroll = False nwGUI.mainConf.hideVScroll = False
nwGUI.mainConf.hideHScroll = False nwGUI.mainConf.hideHScroll = False
nwGUI.projView.initOutline() outlineView.initOutline()
assert outlineView.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineTree.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
assert outlineView.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineTree.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
# Check focus # Check focus
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QWidget, "hasFocus", lambda *a: True) mp.setattr(QWidget, "hasFocus", lambda *a: True)
assert outlineMain.treeFocus() is True assert outlineView.treeHasFocus() is True
outlineMain.setTreeFocus() # Can't check. just ensures that it doesn't error outlineView.setTreeFocus() # Can't check. just ensures that it doesn't error
# Option State # Option State
# ============ # ============
pOptions = nwGUI.theProject.options pOptions = nwGUI.theProject.options
colNames = [h.name for h in nwOutline] colNames = [h.name for h in nwOutline]
colItems = [h for h in nwOutline] colItems = [h for h in nwOutline]
colWidth = {h: outlineView.DEF_WIDTH[h] for h in nwOutline} colWidth = {h: outlineTree.DEF_WIDTH[h] for h in nwOutline}
colHidden = {h: outlineView.DEF_HIDDEN[h] for h in nwOutline} colHidden = {h: outlineTree.DEF_HIDDEN[h] for h in nwOutline}
assert outlineView.topLevelItemCount() > 0 assert outlineTree.topLevelItemCount() > 0
# Save header state not allowed # Save header state not allowed
outlineView._lastBuild = 0 outlineTree._lastBuild = 0
outlineView._saveHeaderState() outlineTree._saveHeaderState()
assert pOptions.getValue("GuiOutline", "headerOrder", []) == [] assert pOptions.getValue("GuiOutline", "headerOrder", []) == []
# Allow saving header state # Allow saving header state
outlineView._lastBuild = time.time() outlineTree._lastBuild = time.time()
outlineView._saveHeaderState() outlineTree._saveHeaderState()
assert pOptions.getValue("GuiOutline", "headerOrder", []) == colNames assert pOptions.getValue("GuiOutline", "headerOrder", []) == colNames
assert outlineView._treeOrder == colItems assert outlineTree._treeOrder == colItems
assert outlineView._colWidth == colWidth assert outlineTree._colWidth == colWidth
assert outlineView._colHidden == colHidden assert outlineTree._colHidden == colHidden
# Get default values # Get default values
optItems = pOptions.getValue("GuiOutline", "headerOrder", []) optItems = pOptions.getValue("GuiOutline", "headerOrder", [])
@@ -105,49 +105,49 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
# Add invalid column name # Add invalid column name
pOptions.setValue("GuiOutline", "headerOrder", optItems + ["blabla"]) pOptions.setValue("GuiOutline", "headerOrder", optItems + ["blabla"])
outlineView._loadHeaderState() outlineTree._loadHeaderState()
assert outlineView._treeOrder == colItems assert outlineTree._treeOrder == colItems
assert outlineView._colHidden == colHidden assert outlineTree._colHidden == colHidden
# Add duplicate column name # Add duplicate column name
pOptions.setValue("GuiOutline", "headerOrder", optItems + [optItems[-1]]) pOptions.setValue("GuiOutline", "headerOrder", optItems + [optItems[-1]])
outlineView._loadHeaderState() outlineTree._loadHeaderState()
assert outlineView._treeOrder == colItems assert outlineTree._treeOrder == colItems
assert outlineView._colHidden == colHidden assert outlineTree._colHidden == colHidden
# Invalid column width data # Invalid column width data
pOptions.setValue("GuiOutline", "headerOrder", optItems) pOptions.setValue("GuiOutline", "headerOrder", optItems)
pOptions.setValue("GuiOutline", "columnWidth", {"blabla": None}) pOptions.setValue("GuiOutline", "columnWidth", {"blabla": None})
outlineView._loadHeaderState() outlineTree._loadHeaderState()
assert outlineView._treeOrder == colItems assert outlineTree._treeOrder == colItems
assert outlineView._colHidden == colHidden assert outlineTree._colHidden == colHidden
# Invalid column width data # Invalid column width data
pOptions.setValue("GuiOutline", "headerOrder", optItems) pOptions.setValue("GuiOutline", "headerOrder", optItems)
pOptions.setValue("GuiOutline", "columnWidth", optWidth) pOptions.setValue("GuiOutline", "columnWidth", optWidth)
pOptions.setValue("GuiOutline", "columnHidden", {"bloabla": None}) pOptions.setValue("GuiOutline", "columnHidden", {"bloabla": None})
outlineView._loadHeaderState() outlineTree._loadHeaderState()
assert outlineView._treeOrder == colItems assert outlineTree._treeOrder == colItems
assert outlineView._colHidden == colHidden assert outlineTree._colHidden == colHidden
# Valid settings # Valid settings
pOptions.setValue("GuiOutline", "headerOrder", optItems) pOptions.setValue("GuiOutline", "headerOrder", optItems)
pOptions.setValue("GuiOutline", "columnWidth", optWidth) pOptions.setValue("GuiOutline", "columnWidth", optWidth)
pOptions.setValue("GuiOutline", "columnHidden", optHidden) pOptions.setValue("GuiOutline", "columnHidden", optHidden)
outlineView._loadHeaderState() outlineTree._loadHeaderState()
assert outlineView._treeOrder == colItems assert outlineTree._treeOrder == colItems
assert outlineView._colHidden == colHidden assert outlineTree._colHidden == colHidden
# Header Menu # Header Menu
# =========== # ===========
# Trigger the menu entry for all hidden columns # Trigger the menu entry for all hidden columns
for hItem in nwOutline: for hItem in nwOutline:
if outlineView.DEF_HIDDEN[hItem]: if outlineTree.DEF_HIDDEN[hItem]:
outlineMenu.actionMap[hItem].activate(QAction.Trigger) outlineMenu.actionMap[hItem].activate(QAction.Trigger)
# Now no columns should be hidden # Now no columns should be hidden
outlineView._saveHeaderState() outlineTree._saveHeaderState()
assert not any(pOptions.getValue("GuiOutline", "columnHidden", None).values()) assert not any(pOptions.getValue("GuiOutline", "columnHidden", None).values())
# qtbot.stop() # qtbot.stop()
@@ -169,10 +169,10 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
nwGUI._changeView(nwView.OUTLINE) nwGUI._changeView(nwView.OUTLINE)
outlineMain = nwGUI.projView outlineView = nwGUI.outlineView
outlineBar = outlineMain.outlineBar outlineBar = outlineView.outlineBar
outlineView = outlineMain.outlineView outlineTree = outlineView.outlineTree
outlineData = outlineMain.outlineData outlineData = outlineView.outlineData
lipHandle = "b3643d0f92e32" lipHandle = "b3643d0f92e32"
@@ -183,7 +183,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
# Add a second novel folder # Add a second novel folder
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL) newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
nwGUI.treeView.revealNewTreeItem(newHandle) nwGUI.projView.revealNewTreeItem(newHandle)
# Check new values in dropdown list # Check new values in dropdown list
assert outlineBar.novelValue.itemData(0) == lipHandle assert outlineBar.novelValue.itemData(0) == lipHandle
@@ -202,7 +202,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
aHandle = nwGUI.theProject.newFile(dTitle, newHandle) aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
hHash = "#"*hLevel hHash = "#"*hLevel
writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n") writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
nwGUI.treeView.revealNewTreeItem(aHandle) nwGUI.projView.revealNewTreeItem(aHandle)
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
@@ -218,10 +218,10 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
# ============= # =============
# First Item # First Item
outlineView.refreshTree() outlineTree.refreshTree()
selItem = outlineView.topLevelItem(0) selItem = outlineTree.topLevelItem(0)
outlineView.setCurrentItem(selItem) outlineTree.setCurrentItem(selItem)
assert outlineData.titleLabel.text() == "<b>Title</b>" assert outlineData.titleLabel.text() == "<b>Title</b>"
assert outlineData.titleValue.text() == "Lorem Ipsum" assert outlineData.titleValue.text() == "Lorem Ipsum"
assert outlineData.fileValue.text() == "Lorem Ipsum" assert outlineData.fileValue.text() == "Lorem Ipsum"
@@ -232,12 +232,12 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
assert outlineData.pCValue.text() == "3" assert outlineData.pCValue.text() == "3"
# Scene One # Scene One
actItem = outlineView.topLevelItem(1) actItem = outlineTree.topLevelItem(1)
chpItem = actItem.child(0) chpItem = actItem.child(0)
selItem = chpItem.child(0) selItem = chpItem.child(0)
outlineView.setCurrentItem(selItem) outlineTree.setCurrentItem(selItem)
tHandle, tLine = outlineView.getSelectedHandle() tHandle, tLine = outlineTree.getSelectedHandle()
assert tHandle == "88243afbe5ed8" assert tHandle == "88243afbe5ed8"
assert tLine == 0 assert tLine == 0
@@ -248,17 +248,17 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
# Click POV Link # Click POV Link
assert outlineData.povKeyValue.text() == "<a href='Bod'>Bod</a>" assert outlineData.povKeyValue.text() == "<a href='Bod'>Bod</a>"
nwGUI.projView._tagClicked("Bod") outlineView._tagClicked("Bod")
assert nwGUI.docViewer.docHandle() == "4c4f28287af27" assert nwGUI.docViewer.docHandle() == "4c4f28287af27"
# Scene One, Section Two # Scene One, Section Two
actItem = outlineView.topLevelItem(1) actItem = outlineTree.topLevelItem(1)
chpItem = actItem.child(0) chpItem = actItem.child(0)
scnItem = chpItem.child(0) scnItem = chpItem.child(0)
selItem = scnItem.child(0) selItem = scnItem.child(0)
outlineView.setCurrentItem(selItem) outlineTree.setCurrentItem(selItem)
tHandle, tLine = outlineView.getSelectedHandle() tHandle, tLine = outlineTree.getSelectedHandle()
assert tHandle == "88243afbe5ed8" assert tHandle == "88243afbe5ed8"
assert tLine == 12 assert tLine == 12
@@ -267,7 +267,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
assert outlineData.fileValue.text() == "Scene One" assert outlineData.fileValue.text() == "Scene One"
assert outlineData.itemValue.text() == "Finished" assert outlineData.itemValue.text() == "Finished"
outlineView._treeDoubleClick(selItem, 0) outlineTree._treeDoubleClick(selItem, 0)
assert nwGUI.docEditor.docHandle() == "88243afbe5ed8" assert nwGUI.docEditor.docHandle() == "88243afbe5ed8"
# qtbot.stop() # qtbot.stop()
+169 -71
View File
@@ -24,11 +24,11 @@ import os
from tools import buildTestProject from tools import buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtWidgets import QMessageBox, QMenu
from novelwriter.guimain import GuiMain from novelwriter.enum import nwItemLayout, nwItemType, nwItemClass
from novelwriter.dialogs import GuiEditLabel
from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.enum import nwItemType, nwItemClass
@pytest.mark.gui @pytest.mark.gui
@@ -40,86 +40,101 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiMain, "editItem", lambda *a: None) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwTree = nwGUI.treeView nwTree = nwGUI.projView
# Try to add item with no project # Try to add item with no project
assert nwTree.newTreeItem(nwItemType.FILE) is False assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir) buildTestProject(nwGUI, prjDir)
# No itemType set # No itemType set
nwTree.clearSelection() nwTree.projTree.clearSelection()
assert nwTree.newTreeItem(None) is False assert nwTree.projTree.newTreeItem(None) is False
# Root Items # Root Items
# ========== # ==========
# No class set # No class set
assert nwTree.newTreeItem(nwItemType.ROOT) is False assert nwTree.projTree.newTreeItem(nwItemType.ROOT) is False
# Create root item # Create root item
assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True assert nwTree.projTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
assert "0000000000010" in nwGUI.theProject.tree assert "0000000000010" in nwGUI.theProject.tree
# File/Folder Items # File/Folder Items
# ================= # =================
# No location selected for new item # No location selected for new item
nwTree.clearSelection() nwTree.projTree.clearSelection()
caplog.clear() caplog.clear()
assert nwTree.newTreeItem(nwItemType.FILE) is False assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
assert nwTree.newTreeItem(nwItemType.FOLDER) is False assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is False
assert "Did not find anywhere" in caplog.text assert "Did not find anywhere" in caplog.text
# Create new folder as child of Novel folder # Create new folder as child of Novel folder
nwTree.setSelectedHandle("0000000000008") nwTree.setSelectedHandle("0000000000008")
assert nwTree.newTreeItem(nwItemType.FOLDER) is True assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
assert nwGUI.theProject.tree["0000000000011"].itemParent == "0000000000008" assert nwGUI.theProject.tree["0000000000011"].itemParent == "0000000000008"
assert nwGUI.theProject.tree["0000000000011"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000011"].itemRoot == "0000000000008"
assert nwGUI.theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL assert nwGUI.theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL
# Add a new file in the new folder # Add a new file in the new folder
nwTree.setSelectedHandle("0000000000011") nwTree.setSelectedHandle("0000000000011")
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwGUI.theProject.tree["0000000000012"].itemParent == "0000000000011" assert nwGUI.theProject.tree["0000000000012"].itemParent == "0000000000011"
assert nwGUI.theProject.tree["0000000000012"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000012"].itemRoot == "0000000000008"
assert nwGUI.theProject.tree["0000000000012"].itemClass == nwItemClass.NOVEL assert nwGUI.theProject.tree["0000000000012"].itemClass == nwItemClass.NOVEL
# Add a new file next to the other new file # Add a new chapter next to the other new file
nwTree.setSelectedHandle("0000000000012") nwTree.setSelectedHandle("0000000000012")
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=2) is True
assert nwGUI.theProject.tree["0000000000013"].itemParent == "0000000000011" assert nwGUI.theProject.tree["0000000000013"].itemParent == "0000000000011"
assert nwGUI.theProject.tree["0000000000013"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000013"].itemRoot == "0000000000008"
assert nwGUI.theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL assert nwGUI.theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL
assert nwGUI.openDocument("0000000000013") assert nwGUI.openDocument("0000000000013")
assert nwGUI.docEditor.getText() == "## New Document\n\n" assert nwGUI.docEditor.getText() == "## New Chapter\n\n"
# Add a new scene next to the other new file
nwTree.setSelectedHandle("0000000000012")
assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=3) is True
assert nwGUI.theProject.tree["0000000000014"].itemParent == "0000000000011"
assert nwGUI.theProject.tree["0000000000014"].itemRoot == "0000000000008"
assert nwGUI.theProject.tree["0000000000014"].itemClass == nwItemClass.NOVEL
assert nwGUI.openDocument("0000000000014")
assert nwGUI.docEditor.getText() == "### New Scene\n\n"
# Add a new file to the characters folder # Add a new file to the characters folder
nwTree.setSelectedHandle("000000000000a") nwTree.setSelectedHandle("000000000000a")
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) is True
assert nwGUI.theProject.tree["0000000000014"].itemParent == "000000000000a" assert nwGUI.theProject.tree["0000000000015"].itemParent == "000000000000a"
assert nwGUI.theProject.tree["0000000000014"].itemRoot == "000000000000a" assert nwGUI.theProject.tree["0000000000015"].itemRoot == "000000000000a"
assert nwGUI.theProject.tree["0000000000014"].itemClass == nwItemClass.CHARACTER assert nwGUI.theProject.tree["0000000000015"].itemClass == nwItemClass.CHARACTER
assert nwGUI.openDocument("0000000000014") assert nwGUI.openDocument("0000000000015")
assert nwGUI.docEditor.getText() == "# New Note\n\n" assert nwGUI.docEditor.getText() == "# New Note\n\n"
# Make sure the sibling folder bug trap works # Make sure the sibling folder bug trap works
nwTree.setSelectedHandle("0000000000013") nwTree.setSelectedHandle("0000000000013")
nwGUI.theProject.tree["0000000000013"].setParent(None) # This should not happen nwGUI.theProject.tree["0000000000013"].setParent(None) # This should not happen
caplog.clear() caplog.clear()
assert nwTree.newTreeItem(nwItemType.FILE) is False assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
assert "Internal error" in caplog.text assert "Internal error" in caplog.text
nwGUI.theProject.tree["0000000000013"].setParent("0000000000011") nwGUI.theProject.tree["0000000000013"].setParent("0000000000011")
# Cancel during creation
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("", False))
nwTree.setSelectedHandle("0000000000013")
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
# Get the trash folder # Get the trash folder
nwTree._addTrashRoot() nwTree.projTree._addTrashRoot()
trashHandle = nwGUI.theProject.trashFolder() trashHandle = nwGUI.theProject.trashFolder()
nwTree.setSelectedHandle(trashHandle) nwTree.setSelectedHandle(trashHandle)
assert nwTree.newTreeItem(nwItemType.FILE) is False assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
assert "Cannot add new files or folders to the Trash folder" in caplog.text assert "Cannot add new files or folders to the Trash folder" in caplog.text
# Other Checks # Other Checks
@@ -148,12 +163,12 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiMain, "editItem", lambda *a: None) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwTree = nwGUI.treeView nwTree = nwGUI.projView
# Try to move item with no project # Try to move item with no project
assert nwTree.moveTreeItem(1) is False assert nwTree.projTree.moveTreeItem(1) is False
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
@@ -164,42 +179,33 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Add some files # Add some files
nwTree.setSelectedHandle("000000000000d") nwTree.setSelectedHandle("000000000000d")
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
] ]
# Move item without focus
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
assert nwTree.moveTreeItem(1) is False
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Move with no selections # Move with no selections
nwTree.clearSelection() nwTree.projTree.clearSelection()
assert nwTree.moveTreeItem(1) is False assert nwTree.projTree.moveTreeItem(1) is False
# Move second item up twice (should give same result) # Move second item up twice (should give same result)
nwTree.setSelectedHandle("000000000000f") nwTree.setSelectedHandle("000000000000f")
assert nwTree.moveTreeItem(-1) is True assert nwTree.projTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000f", "000000000000e", "000000000000d", "000000000000f", "000000000000e",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
] ]
assert nwTree.moveTreeItem(-1) is False assert nwTree.projTree.moveTreeItem(-1) is False
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000f", "000000000000e", "000000000000d", "000000000000f", "000000000000e",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
] ]
# Restore via menu entry # Restore
nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
@@ -207,19 +213,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Move fifth item down twice (should give same result) # Move fifth item down twice (should give same result)
nwTree.setSelectedHandle("0000000000011") nwTree.setSelectedHandle("0000000000011")
assert nwTree.moveTreeItem(1) is True assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
] ]
assert nwTree.moveTreeItem(1) is False assert nwTree.projTree.moveTreeItem(1) is False
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
] ]
# Restore via menu entry # Restore
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) assert nwTree.projTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
@@ -227,12 +233,12 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Move down again, and restore via undo # Move down again, and restore via undo
nwTree.setSelectedHandle("0000000000011") nwTree.setSelectedHandle("0000000000011")
assert nwTree.moveTreeItem(1) is True assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
] ]
nwGUI.mainMenu.aMoveUndo.activate(QAction.Trigger) assert nwTree.projTree.undoLastMove() is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
@@ -245,15 +251,15 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
# Move novel folder up # Move novel folder up
assert nwTree.moveTreeItem(-1) is False assert nwTree.projTree.moveTreeItem(-1) is False
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
# Move novel folder down # Move novel folder down
assert nwTree.moveTreeItem(1) is True assert nwTree.projTree.moveTreeItem(1) is True
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1
# Move novel folder up again # Move novel folder up again
assert nwTree.moveTreeItem(-1) is True assert nwTree.projTree.moveTreeItem(-1) is True
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
# Clean up # Clean up
@@ -272,9 +278,9 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiMain, "editItem", lambda *a: None) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwTree = nwGUI.treeView nwTree = nwGUI.projView
# Try to run with no project # Try to run with no project
assert nwTree.emptyTrash() is False assert nwTree.emptyTrash() is False
@@ -289,9 +295,9 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
# Add some files # Add some files
nwTree.setSelectedHandle("000000000000d") nwTree.setSelectedHandle("000000000000d")
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
@@ -304,13 +310,13 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# No selection made # No selection made
nwTree.clearSelection() nwTree.projTree.clearSelection()
caplog.clear() caplog.clear()
assert nwTree.deleteItem() is False assert nwTree.deleteItem() is False
assert "no item to delete" in caplog.text assert "no item to delete" in caplog.text
# Not a valid handle # Not a valid handle
nwTree.clearSelection() nwTree.projTree.clearSelection()
caplog.clear() caplog.clear()
assert nwTree.deleteItem("0000000000000") is False assert nwTree.deleteItem("0000000000000") is False
assert "Could not find tree item" in caplog.text assert "Could not find tree item" in caplog.text
@@ -326,10 +332,10 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
# =========== # ===========
# Block adding trash folder # Block adding trash folder
funcPointer = nwTree._addTrashRoot funcPointer = nwTree.projTree._addTrashRoot
nwTree._addTrashRoot = lambda *a: None nwTree.projTree._addTrashRoot = lambda *a: None
assert nwTree.deleteItem("0000000000012") is False assert nwTree.deleteItem("0000000000012") is False
nwTree._addTrashRoot = funcPointer nwTree.projTree._addTrashRoot = funcPointer
# Delete last two documents, which also adds the trash folder # Delete last two documents, which also adds the trash folder
assert nwTree.deleteItem("0000000000012") is True assert nwTree.deleteItem("0000000000012") is True
@@ -373,10 +379,10 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
# Add a folder with two files # Add a folder with two files
nwTree.setSelectedHandle("0000000000009") nwTree.setSelectedHandle("0000000000009")
assert nwTree.newTreeItem(nwItemType.FOLDER) is True assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
nwTree.setSelectedHandle("0000000000014") nwTree.setSelectedHandle("0000000000014")
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd")) assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd"))
assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd")) assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd"))
@@ -399,7 +405,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
# Add an empty folder, which can be deleted with no further restrictions # Add an empty folder, which can be deleted with no further restrictions
nwTree.setSelectedHandle("0000000000009") nwTree.setSelectedHandle("0000000000009")
assert nwTree.newTreeItem(nwItemType.FOLDER) is True assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009", "0000000000017"] assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009", "0000000000017"]
nwTree.setSelectedHandle("0000000000017") nwTree.setSelectedHandle("0000000000017")
@@ -441,7 +447,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
assert os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd")) assert os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd"))
# Delete proper # Delete proper
assert nwTree._deleteTreeItem("000000000000e") is True assert nwTree.projTree._deleteTreeItem("000000000000e") is True
assert not os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd")) assert not os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd"))
# Clean up # Clean up
@@ -449,3 +455,95 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
nwGUI.closeProject() nwGUI.closeProject()
# END Test testGuiProjTree_DeleteItems # END Test testGuiProjTree_DeleteItems
@pytest.mark.gui
def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test the building of the project tree context menu. All this does
is test that the menu builds. It doesn't open the actual menu,
"""
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
# Create a project
prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir)
# Handles for new objects
hNovelRoot = "0000000000008"
hTitlePage = "000000000000c"
hChapterDir = "000000000000d"
hChapterFile = "000000000000e"
hCharRoot = "000000000000a"
hCharNote = "0000000000011"
hNovelNote = "0000000000012"
projTree = nwGUI.projView.projTree
projTree._getTreeItem(hNovelRoot).setExpanded(True)
projTree._getTreeItem(hChapterDir).setExpanded(True)
projTree._addTrashRoot()
hTrashRoot = projTree.theProject.tree.trashRoot()
projTree.setSelectedHandle(hCharRoot)
projTree.newTreeItem(nwItemType.FILE)
projTree.setSelectedHandle(hNovelRoot)
projTree.newTreeItem(nwItemType.FILE, isNote=True)
def itemPos(tHandle):
return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center()
# No item under menu
assert projTree._openContextMenu(projTree.viewport().rect().bottomRight()) is False
# Generate the possible menu combinarions
assert projTree._openContextMenu(itemPos(hTrashRoot)) is True
assert projTree._openContextMenu(itemPos(hNovelRoot)) is True
assert projTree._openContextMenu(itemPos(hNovelNote)) is True
assert projTree._openContextMenu(itemPos(hTitlePage)) is True
assert projTree._openContextMenu(itemPos(hChapterDir)) is True
assert projTree._openContextMenu(itemPos(hChapterFile)) is True
assert projTree._openContextMenu(itemPos(hCharRoot)) is True
assert projTree._openContextMenu(itemPos(hCharNote)) is True
# Check the keyboard shortcut handler as well
projTree.setSelectedHandle(hNovelRoot)
assert projTree.openContextOnSelected() is True
projTree.clearSelection()
assert projTree.openContextOnSelected() is False
# Direct Edit Functions
# =====================
# Trigger the dedicated functions the menu entries connect to
nwItem = projTree.theProject.tree[hNovelNote]
# Toggle exported flag
assert nwItem.isExported is True
projTree._toggleItemExported(hNovelNote)
assert nwItem.isExported is False
# Change item status
assert nwItem.itemStatus == "s000000"
projTree._changeItemStatus(hNovelNote, "s000001")
assert nwItem.itemStatus == "s000001"
# Change item importance
assert nwItem.itemImport == "i000004"
projTree._changeItemImport(hNovelNote, "i000005")
assert nwItem.itemImport == "i000005"
# Change item layout
assert nwItem.itemLayout == nwItemLayout.NOTE
projTree._changeItemLayout(hNovelNote, nwItemLayout.DOCUMENT)
assert nwItem.itemLayout == nwItemLayout.DOCUMENT
projTree._changeItemLayout(hNovelNote, nwItemLayout.NOTE)
assert nwItem.itemLayout == nwItemLayout.NOTE
# qtbot.stop()
# END Test testGuiProjTree_ContextMenu
+1 -1
View File
@@ -40,7 +40,7 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
cHandle = nwGUI.theProject.newFile("A Note", "000000000000a") cHandle = nwGUI.theProject.newFile("A Note", "000000000000a")
newDoc = NWDoc(nwGUI.theProject, cHandle) newDoc = NWDoc(nwGUI.theProject, cHandle)
newDoc.writeDocument("# A Note\n\n") newDoc.writeDocument("# A Note\n\n")
nwGUI.treeView.revealNewTreeItem(cHandle) nwGUI.projView.revealNewTreeItem(cHandle)
nwGUI.rebuildIndex(beQuiet=True) nwGUI.rebuildIndex(beQuiet=True)
# Reference Time # Reference Time
+32 -32
View File
@@ -93,82 +93,82 @@ def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
assert thePalette.link().color() == QColor(44, 152, 247) assert thePalette.link().color() == QColor(44, 152, 247)
assert thePalette.linkVisited().color() == QColor(44, 152, 247) assert thePalette.linkVisited().color() == QColor(44, 152, 247)
assert nwGUI.theTheme.statNone == [150, 152, 150] assert nwGUI.mainTheme.statNone == [150, 152, 150]
assert nwGUI.theTheme.statSaved == [39, 135, 78] assert nwGUI.mainTheme.statSaved == [39, 135, 78]
assert nwGUI.theTheme.statUnsaved == [138, 32, 32] assert nwGUI.mainTheme.statUnsaved == [138, 32, 32]
# Check Syntax Colours # Check Syntax Colours
assert nwGUI.theTheme.colBack == [45, 45, 45] assert nwGUI.mainTheme.colBack == [45, 45, 45]
assert nwGUI.theTheme.colText == [204, 204, 204] assert nwGUI.mainTheme.colText == [204, 204, 204]
assert nwGUI.theTheme.colLink == [102, 153, 204] assert nwGUI.mainTheme.colLink == [102, 153, 204]
assert nwGUI.theTheme.colHead == [102, 153, 204] assert nwGUI.mainTheme.colHead == [102, 153, 204]
assert nwGUI.theTheme.colHeadH == [102, 153, 204] assert nwGUI.mainTheme.colHeadH == [102, 153, 204]
assert nwGUI.theTheme.colEmph == [249, 145, 57] assert nwGUI.mainTheme.colEmph == [249, 145, 57]
assert nwGUI.theTheme.colDialN == [242, 119, 122] assert nwGUI.mainTheme.colDialN == [242, 119, 122]
assert nwGUI.theTheme.colDialD == [153, 204, 153] assert nwGUI.mainTheme.colDialD == [153, 204, 153]
assert nwGUI.theTheme.colDialS == [255, 204, 102] assert nwGUI.mainTheme.colDialS == [255, 204, 102]
assert nwGUI.theTheme.colHidden == [153, 153, 153] assert nwGUI.mainTheme.colHidden == [153, 153, 153]
assert nwGUI.theTheme.colKey == [242, 119, 122] assert nwGUI.mainTheme.colKey == [242, 119, 122]
assert nwGUI.theTheme.colVal == [204, 153, 204] assert nwGUI.mainTheme.colVal == [204, 153, 204]
assert nwGUI.theTheme.colSpell == [242, 119, 122] assert nwGUI.mainTheme.colSpell == [242, 119, 122]
assert nwGUI.theTheme.colError == [153, 204, 153] assert nwGUI.mainTheme.colError == [153, 204, 153]
assert nwGUI.theTheme.colRepTag == [102, 204, 204] assert nwGUI.mainTheme.colRepTag == [102, 204, 204]
assert nwGUI.theTheme.colMod == [249, 145, 57] assert nwGUI.mainTheme.colMod == [249, 145, 57]
# Test Icon class # Test Icon class
theIcons = nwGUI.theTheme.theIcons iconCache = nwGUI.mainTheme.iconCache
novelwriter.CONFIG.guiIcons = "invalid" novelwriter.CONFIG.guiIcons = "invalid"
assert theIcons.updateTheme() is True assert iconCache.updateTheme() is True
assert novelwriter.CONFIG.guiIcons == "typicons_light" assert novelwriter.CONFIG.guiIcons == "typicons_light"
# Ask for a non-existent key # Ask for a non-existent key
anImg = theIcons.loadDecoration("nonsense", 20, 20) anImg = iconCache.loadDecoration("nonsense", 20, 20)
assert isinstance(anImg, QPixmap) assert isinstance(anImg, QPixmap)
assert anImg.isNull() assert anImg.isNull()
# Add a non-existent file and request it # Add a non-existent file and request it
theIcons.DECO_MAP["nonsense"] = "nofile.jpg" iconCache.IMAGE_MAP["nonsense"] = "nofile.jpg"
anImg = theIcons.loadDecoration("nonsense", 20, 20) anImg = iconCache.loadDecoration("nonsense", 20, 20)
assert isinstance(anImg, QPixmap) assert isinstance(anImg, QPixmap)
assert anImg.isNull() assert anImg.isNull()
# Get a real image, with different size parameters # Get a real image, with different size parameters
anImg = theIcons.loadDecoration("wiz-back", 20, None) anImg = iconCache.loadDecoration("wiz-back", 20, None)
assert isinstance(anImg, QPixmap) assert isinstance(anImg, QPixmap)
assert not anImg.isNull() assert not anImg.isNull()
assert anImg.width() == 20 assert anImg.width() == 20
assert anImg.height() >= 56 assert anImg.height() >= 56
anImg = theIcons.loadDecoration("wiz-back", None, 70) anImg = iconCache.loadDecoration("wiz-back", None, 70)
assert isinstance(anImg, QPixmap) assert isinstance(anImg, QPixmap)
assert not anImg.isNull() assert not anImg.isNull()
assert anImg.height() == 70 assert anImg.height() == 70
assert anImg.width() >= 24 assert anImg.width() >= 24
anImg = theIcons.loadDecoration("wiz-back", 30, 70) anImg = iconCache.loadDecoration("wiz-back", 30, 70)
assert isinstance(anImg, QPixmap) assert isinstance(anImg, QPixmap)
assert not anImg.isNull() assert not anImg.isNull()
assert anImg.height() == 70 assert anImg.height() == 70
assert anImg.width() == 30 assert anImg.width() == 30
anImg = theIcons.loadDecoration("wiz-back", None, None) anImg = iconCache.loadDecoration("wiz-back", None, None)
assert isinstance(anImg, QPixmap) assert isinstance(anImg, QPixmap)
assert not anImg.isNull() assert not anImg.isNull()
assert anImg.height() >= 1500 assert anImg.height() >= 1500
assert anImg.width() >= 500 assert anImg.width() >= 500
# Load icons # Load icons
anIcon = theIcons.getIcon("nonsense") anIcon = iconCache.getIcon("nonsense")
assert isinstance(anIcon, QIcon) assert isinstance(anIcon, QIcon)
assert anIcon.isNull() assert anIcon.isNull()
anIcon = theIcons.getIcon("novelwriter") anIcon = iconCache.getIcon("novelwriter")
assert isinstance(anIcon, QIcon) assert isinstance(anIcon, QIcon)
assert not anIcon.isNull() assert not anIcon.isNull()
# Check return empty icon if file not found # Check return empty icon if file not found
theIcons.ICON_KEYS.add("testicon3") iconCache.ICON_KEYS.add("testicon3")
anIcon = theIcons.getIcon("testicon3") anIcon = iconCache.getIcon("testicon3")
assert isinstance(anIcon, QIcon) assert isinstance(anIcon, QIcon)
assert anIcon.isNull() assert anIcon.isNull()