Merge branch 'main' into i18n-de_DE-created
@@ -2,75 +2,131 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
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
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, 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
|
||||
further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project leader at issues (at) novelwriter.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
reported to the community leaders responsible for enforcement at issues (at)
|
||||
novelwriter.io. All complaints will be reviewed and investigated promptly and
|
||||
fairly.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## 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
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
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
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
@@ -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:`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:`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:`/`", "Toggle block format as comment."
|
||||
":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:`C`", "Copy selected text to clipboard."
|
||||
":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:`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:`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:`N`", "Create new document."
|
||||
":kbd:`Ctrl`:kbd:`N`", "Create new project item."
|
||||
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
||||
":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."
|
||||
@@ -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:`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:`N`", "Create new folder."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
|
||||
":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_novel = typ_book-grey.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_novel = typ_book-grey.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 |
@@ -57,6 +57,15 @@ 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:
|
||||
|
||||
PROJ_FILE = "nwProject.nwx"
|
||||
|
||||
@@ -34,7 +34,7 @@ from time import time
|
||||
|
||||
from novelwriter.enum import nwItemType, nwItemLayout
|
||||
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.common import (
|
||||
checkInt, isHandle, isItemClass, isTitleTag, jsonEncode
|
||||
@@ -42,10 +42,6 @@ from novelwriter.common import (
|
||||
|
||||
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:
|
||||
"""This class holds the entire index for a given project. The index
|
||||
@@ -477,7 +473,7 @@ class NWIndex:
|
||||
"""
|
||||
hCount = [0, 0, 0, 0, 0]
|
||||
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
|
||||
return hCount
|
||||
|
||||
@@ -502,11 +498,6 @@ class NWIndex:
|
||||
"""
|
||||
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):
|
||||
"""Generate a table of contents up to a maximum depth.
|
||||
"""
|
||||
@@ -515,7 +506,7 @@ class NWIndex:
|
||||
pKey = None
|
||||
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
|
||||
tKey = f"{tHandle}:{sTitle}"
|
||||
iLevel = H_LEVEL.get(hItem.level, 0)
|
||||
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
|
||||
if iLevel > maxDepth:
|
||||
if pKey in tData:
|
||||
tData[pKey]["words"] += hItem.wordCount
|
||||
@@ -665,7 +656,7 @@ class TagsIndex:
|
||||
"""
|
||||
if tagKey in self._tags:
|
||||
return self._tags.get(tagKey).get("heading")
|
||||
return TT_NONE
|
||||
return nwHeaders.TT_NONE
|
||||
|
||||
def tagClass(self, tagKey):
|
||||
"""Get the class of a given tag.
|
||||
@@ -911,7 +902,7 @@ class IndexItem:
|
||||
self._index = 0
|
||||
|
||||
# Add a placeholder heading
|
||||
self._headings[TT_NONE] = IndexHeading(TT_NONE)
|
||||
self._headings[nwHeaders.TT_NONE] = IndexHeading(nwHeaders.TT_NONE)
|
||||
|
||||
return
|
||||
|
||||
@@ -945,8 +936,8 @@ class IndexItem:
|
||||
"""Add a heading to the item. Also remove the placeholder entry
|
||||
if it exists.
|
||||
"""
|
||||
if TT_NONE in self._headings:
|
||||
self._headings.pop(TT_NONE)
|
||||
if nwHeaders.TT_NONE in self._headings:
|
||||
self._headings.pop(nwHeaders.TT_NONE)
|
||||
self._headings[tHeading.key] = tHeading
|
||||
return
|
||||
|
||||
@@ -1115,7 +1106,7 @@ class IndexHeading:
|
||||
def setLevel(self, level):
|
||||
"""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
|
||||
return
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkBool, checkFloat, checkInt, checkString
|
||||
from novelwriter.constants import nwFiles
|
||||
@@ -56,7 +58,8 @@ VALID_MAP = {
|
||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||
"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:
|
||||
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
|
||||
|
||||
@@ -186,4 +192,16 @@ class OptionState():
|
||||
return checkBool(self._theState[group].get(name, default), 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
|
||||
|
||||
@@ -45,7 +45,7 @@ from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import (
|
||||
checkString, checkBool, checkInt, isHandle, formatTimeStamp,
|
||||
makeFileNameSafe, hexToInt, simplified
|
||||
makeFileNameSafe, hexToInt, minmax, simplified
|
||||
)
|
||||
from novelwriter.constants import trConst, nwFiles, nwLabels
|
||||
|
||||
@@ -56,11 +56,11 @@ class NWProject():
|
||||
|
||||
FILE_VERSION = "1.4" # The current project file format version
|
||||
|
||||
def __init__(self, theParent):
|
||||
def __init__(self, mainGui):
|
||||
|
||||
# Internal
|
||||
self.theParent = theParent
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.mainGui = mainGui
|
||||
|
||||
# Core Elements
|
||||
self._optState = OptionState(self) # Project-specific GUI options
|
||||
@@ -102,6 +102,8 @@ class NWProject():
|
||||
self.importItems = None # Note file importance values
|
||||
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.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.lastNovelWC = 0 # The novel 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):
|
||||
"""Add a new folder with a given label and parent item.
|
||||
"""
|
||||
if pHandle not in self._projTree:
|
||||
return None
|
||||
newItem = NWItem(self)
|
||||
newItem.setName(label)
|
||||
newItem.setType(nwItemType.FOLDER)
|
||||
@@ -164,6 +168,8 @@ class NWProject():
|
||||
def newFile(self, label, pHandle):
|
||||
"""Add a new file with a given label and parent item.
|
||||
"""
|
||||
if pHandle not in self._projTree:
|
||||
return None
|
||||
newItem = NWItem(self)
|
||||
newItem.setName(label)
|
||||
newItem.setType(nwItemType.FILE)
|
||||
@@ -171,6 +177,32 @@ class NWProject():
|
||||
self._projTree.updateItemData(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):
|
||||
"""Add the special trash root folder to the project.
|
||||
"""
|
||||
@@ -384,7 +416,7 @@ class NWProject():
|
||||
if not os.path.isfile(fileName):
|
||||
fileName = os.path.join(fileName, nwFiles.PROJ_FILE)
|
||||
if not os.path.isfile(fileName):
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"File not found: {0}"
|
||||
).format(fileName), nwAlert.ERROR)
|
||||
return False
|
||||
@@ -408,7 +440,7 @@ class NWProject():
|
||||
legacyList = [] # Cleanup is done later
|
||||
for projItem in os.listdir(self.projPath):
|
||||
logger.verbose("Project contains: %s", projItem)
|
||||
if projItem.startswith("data_"):
|
||||
if projItem.startswith("data_") and len(projItem) == 6:
|
||||
legacyList.append(projItem)
|
||||
|
||||
# Project Lock
|
||||
@@ -435,20 +467,20 @@ class NWProject():
|
||||
try:
|
||||
nwXML = etree.parse(fileName)
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to parse project xml."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
|
||||
# Trying to open backup file instead
|
||||
backFile = fileName[:-3]+"bak"
|
||||
if os.path.isfile(backFile):
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Attempting to open backup project file instead."
|
||||
), nwAlert.INFO)
|
||||
try:
|
||||
nwXML = etree.parse(backFile)
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to parse project xml."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
self.clearProject()
|
||||
@@ -470,8 +502,8 @@ class NWProject():
|
||||
# Check File Type
|
||||
# ===============
|
||||
|
||||
if not nwxRoot == "novelWriterXML":
|
||||
self.theParent.makeAlert(self.tr(
|
||||
if nwxRoot != "novelWriterXML":
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Project file does not appear to be a novelWriterXML file."
|
||||
), nwAlert.ERROR)
|
||||
self.clearProject()
|
||||
@@ -497,7 +529,7 @@ class NWProject():
|
||||
# stored and handled. Introduced in version 1.7.
|
||||
|
||||
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. "
|
||||
"The project cannot be opened by this version of novelWriter. "
|
||||
"The file was saved with novelWriter version {0}."
|
||||
@@ -506,7 +538,7 @@ class NWProject():
|
||||
return False
|
||||
|
||||
if fileVersion != self.FILE_VERSION:
|
||||
msgYes = self.theParent.askQuestion(
|
||||
msgYes = self.mainGui.askQuestion(
|
||||
self.tr("File Version"),
|
||||
self.tr(
|
||||
"The file format of your project is about to be updated. "
|
||||
@@ -522,7 +554,7 @@ class NWProject():
|
||||
# =========================
|
||||
|
||||
if hexToInt(hexVersion) > hexToInt(novelwriter.__hexversion__):
|
||||
msgYes = self.theParent.askQuestion(
|
||||
msgYes = self.mainGui.askQuestion(
|
||||
self.tr("Version Conflict"),
|
||||
self.tr(
|
||||
"This project was saved by a newer version of "
|
||||
@@ -582,6 +614,10 @@ class NWProject():
|
||||
self.lastEdited = checkString(xItem.text, None, True)
|
||||
elif xItem.tag == "lastViewed":
|
||||
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":
|
||||
self.lastWCount = checkInt(xItem.text, 0, False)
|
||||
elif xItem.tag == "novelWordCount":
|
||||
@@ -612,11 +648,14 @@ class NWProject():
|
||||
|
||||
# Sort out old file locations
|
||||
if legacyList:
|
||||
errList = []
|
||||
for projItem in legacyList:
|
||||
errList = self._legacyDataFolder(projItem, errList)
|
||||
if errList:
|
||||
self.theParent.makeAlert(errList, nwAlert.ERROR)
|
||||
try:
|
||||
for projItem in legacyList:
|
||||
self._legacyDataFolder(projItem)
|
||||
except Exception:
|
||||
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
|
||||
self._deprecatedFiles()
|
||||
@@ -642,7 +681,7 @@ class NWProject():
|
||||
|
||||
self._writeLockFile()
|
||||
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
|
||||
|
||||
@@ -653,7 +692,7 @@ class NWProject():
|
||||
file.
|
||||
"""
|
||||
if self.projPath is None:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Project path not set, cannot save project."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
@@ -699,6 +738,8 @@ class NWProject():
|
||||
self._packProjectValue(xSettings, "autoOutline", self.autoOutline)
|
||||
self._packProjectValue(xSettings, "lastEdited", self.lastEdited)
|
||||
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, "novelWordCount", self.currNovelWC)
|
||||
self._packProjectValue(xSettings, "notesWordCount", self.currNotesWC)
|
||||
@@ -733,7 +774,7 @@ class NWProject():
|
||||
xml_declaration=True
|
||||
))
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to save project."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
@@ -745,7 +786,7 @@ class NWProject():
|
||||
os.replace(saveFile, backFile)
|
||||
os.replace(tempFile, saveFile)
|
||||
except OSError as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to save project."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
@@ -758,7 +799,7 @@ class NWProject():
|
||||
self.mainConf.saveRecentCache()
|
||||
|
||||
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)
|
||||
|
||||
return True
|
||||
@@ -806,22 +847,22 @@ class NWProject():
|
||||
def zipIt(self, doNotify):
|
||||
"""Create a zip file of the entire project.
|
||||
"""
|
||||
if not self.theParent.hasProject:
|
||||
if not self.mainGui.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
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)):
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Cannot backup project because no valid backup path is set. "
|
||||
"Please set a valid backup location in Preferences."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
if not self.projName:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Cannot backup project because no project name is set. "
|
||||
"Please set a Working Title in Project Settings."
|
||||
), nwAlert.ERROR)
|
||||
@@ -834,13 +875,13 @@ class NWProject():
|
||||
os.mkdir(baseDir)
|
||||
logger.debug("Created folder: %s", baseDir)
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Could not create backup folder."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
|
||||
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 "
|
||||
"project folder to be backed up. Please choose a different "
|
||||
"backup path in Preferences."
|
||||
@@ -856,17 +897,17 @@ class NWProject():
|
||||
self._writeLockFile()
|
||||
logger.info("Backup written to: %s", archName)
|
||||
if doNotify:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Backup archive file written to: {0}"
|
||||
).format(f"{os.path.join(cleanName, archName)}.zip"), nwAlert.INFO)
|
||||
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Could not write backup archive."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
|
||||
self.theParent.setStatus(self.tr(
|
||||
self.mainGui.setStatus(self.tr(
|
||||
"Project backed up to '{0}'"
|
||||
).format(f"{baseName}.zip"))
|
||||
|
||||
@@ -894,7 +935,7 @@ class NWProject():
|
||||
shutil.unpack_archive(pkgSample, projPath)
|
||||
isSuccess = True
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
|
||||
@@ -916,12 +957,12 @@ class NWProject():
|
||||
isSuccess = True
|
||||
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
|
||||
else:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project. "
|
||||
"Could not find the necessary files. "
|
||||
"They seem to be missing from this installation."
|
||||
@@ -929,8 +970,8 @@ class NWProject():
|
||||
|
||||
if isSuccess:
|
||||
self.clearProject()
|
||||
self.theParent.openProject(projPath)
|
||||
self.theParent.rebuildIndex()
|
||||
self.mainGui.openProject(projPath)
|
||||
self.mainGui.rebuildIndex()
|
||||
|
||||
return isSuccess
|
||||
|
||||
@@ -955,14 +996,14 @@ class NWProject():
|
||||
os.mkdir(projPath)
|
||||
logger.debug("Created folder: %s", projPath)
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Could not create new project folder."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
|
||||
if os.path.isdir(projPath):
|
||||
if os.listdir(self.projPath):
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"New project folder is not empty. "
|
||||
"Each project requires a dedicated project folder."
|
||||
), nwAlert.ERROR)
|
||||
@@ -1012,14 +1053,14 @@ class NWProject():
|
||||
self.doBackup = doBackup
|
||||
if doBackup:
|
||||
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 "
|
||||
"the automatic project backup feature."
|
||||
), nwAlert.WARN)
|
||||
return False
|
||||
|
||||
if self.projName == "":
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"You must set a valid project name in Project Settings to "
|
||||
"use the automatic project backup feature."
|
||||
), nwAlert.WARN)
|
||||
@@ -1090,6 +1131,14 @@ class NWProject():
|
||||
self.setProjectChanged(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):
|
||||
"""Update the list of novel file status flags.
|
||||
"""
|
||||
@@ -1124,7 +1173,7 @@ class NWProject():
|
||||
information to the GUI statusbar.
|
||||
"""
|
||||
self.projChanged = bValue
|
||||
self.theParent.statusBar.doUpdateProjectStatus(bValue)
|
||||
self.mainGui.statusBar.doUpdateProjectStatus(bValue)
|
||||
if bValue:
|
||||
# If we've changed the project at all, this should be True
|
||||
self.projAltered = True
|
||||
@@ -1354,7 +1403,7 @@ class NWProject():
|
||||
os.mkdir(thePath)
|
||||
logger.debug("Created folder: %s", thePath)
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Could not create folder."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
@@ -1417,7 +1466,7 @@ class NWProject():
|
||||
|
||||
# Report status
|
||||
if len(orphanFiles) > 0:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Found {0} orphaned file(s) in project folder."
|
||||
).format(len(orphanFiles)), nwAlert.WARN)
|
||||
else:
|
||||
@@ -1474,7 +1523,7 @@ class NWProject():
|
||||
self._projTree.updateItemData(orphItem.itemHandle)
|
||||
|
||||
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. "
|
||||
"Make sure at least a Novel root folder exists."
|
||||
), nwAlert.WARN)
|
||||
@@ -1528,82 +1577,37 @@ class NWProject():
|
||||
# Legacy Data Structure Handlers
|
||||
##
|
||||
|
||||
def _legacyDataFolder(self, theFolder, errList):
|
||||
def _legacyDataFolder(self, dataDir):
|
||||
"""Clean up legacy data folders.
|
||||
"""
|
||||
theData = os.path.join(self.projPath, theFolder)
|
||||
if not os.path.isdir(theData):
|
||||
errList.append(self.tr("Not a folder: {0}").format(theData))
|
||||
return errList
|
||||
dataPath = os.path.join(self.projPath, dataDir)
|
||||
if not os.path.isdir(dataPath):
|
||||
return False
|
||||
|
||||
logger.info("Old data folder %s found", theFolder)
|
||||
logger.info("Old data folder found: %s", dataDir)
|
||||
|
||||
# Move Documents to Content
|
||||
for dataItem in os.listdir(theData):
|
||||
theFile = os.path.join(theData, dataItem)
|
||||
if not os.path.isfile(theFile):
|
||||
theErr = self._moveUnknownItem(theData, dataItem)
|
||||
if theErr:
|
||||
errList.append(theErr)
|
||||
for dataItem in os.listdir(dataPath):
|
||||
dataFile = os.path.join(dataPath, dataItem)
|
||||
if not os.path.isfile(dataFile):
|
||||
continue
|
||||
|
||||
if len(dataItem) == 21 and dataItem.endswith("_main.nwd"):
|
||||
tHandle = theFolder[-1]+dataItem[:12]
|
||||
newPath = os.path.join(self.projContent, tHandle+".nwd")
|
||||
try:
|
||||
os.rename(theFile, newPath)
|
||||
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()
|
||||
tHandle = dataDir[-1] + dataItem[:12]
|
||||
newPath = os.path.join(self.projContent, f"{tHandle}.nwd")
|
||||
os.rename(dataFile, newPath)
|
||||
logger.info("Moved file: %s", dataFile)
|
||||
|
||||
elif len(dataItem) == 21 and dataItem.endswith("_main.bak"):
|
||||
try:
|
||||
os.unlink(theFile)
|
||||
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)
|
||||
os.unlink(dataFile)
|
||||
logger.info("Deleted file: %s", dataFile)
|
||||
|
||||
# Remove Data Folder
|
||||
try:
|
||||
os.rmdir(theData)
|
||||
logger.info("Deleted folder: %s", theFolder)
|
||||
except Exception:
|
||||
errList.append(self.tr("Could not delete: {0}").format(theFolder))
|
||||
logger.error("Could not delete: %s", theFolder)
|
||||
logException()
|
||||
if not os.listdir(dataPath):
|
||||
os.rmdir(dataPath)
|
||||
logger.info("Deleted folder: %s", dataDir)
|
||||
|
||||
return errList
|
||||
|
||||
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 ""
|
||||
return True
|
||||
|
||||
def _deprecatedFiles(self):
|
||||
"""Delete files that are no longer used by novelWriter.
|
||||
|
||||
@@ -82,7 +82,6 @@ class Tokenizer(ABC):
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
self.theParent = theProject.theParent
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
|
||||
# Data Variables
|
||||
|
||||
@@ -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.docmerge import GuiDocMerge
|
||||
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.projdetails import GuiProjectDetails
|
||||
from novelwriter.dialogs.projload import GuiProjectLoad
|
||||
@@ -35,7 +35,7 @@ __all__ = [
|
||||
"GuiAbout",
|
||||
"GuiDocMerge",
|
||||
"GuiDocSplit",
|
||||
"GuiItemEditor",
|
||||
"GuiEditLabel",
|
||||
"GuiPreferences",
|
||||
"GuiProjectDetails",
|
||||
"GuiProjectLoad",
|
||||
|
||||
@@ -43,15 +43,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiAbout(QDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiAbout ...")
|
||||
self.setObjectName("GuiAbout")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.innerBox = QHBoxLayout()
|
||||
@@ -63,7 +63,7 @@ class GuiAbout(QDialog):
|
||||
|
||||
nPx = self.mainConf.pxInt(96)
|
||||
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.lblVers = QLabel(f"v{novelwriter.__version__}")
|
||||
self.lblDate = QLabel(datetime.strptime(novelwriter.__date__, "%Y-%m-%d").strftime("%x"))
|
||||
@@ -192,37 +192,37 @@ class GuiAbout(QDialog):
|
||||
])
|
||||
)
|
||||
|
||||
theTheme = self.theParent.theTheme
|
||||
theIcons = self.theParent.theTheme.theIcons
|
||||
if theTheme.themeName and theTheme.themeAuthor != "N/A":
|
||||
licURL = f"<a href='{theTheme.themeLicenseUrl}'>{theTheme.themeLicense}</a>"
|
||||
mainTheme = self.mainGui.mainTheme
|
||||
iconCache = self.mainGui.mainTheme.iconCache
|
||||
if mainTheme.themeName and mainTheme.themeAuthor != "N/A":
|
||||
licURL = f"<a href='{mainTheme.themeLicenseUrl}'>{mainTheme.themeLicense}</a>"
|
||||
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.tr("Author"), theTheme.themeAuthor),
|
||||
(self.tr("Credit"), theTheme.themeCredit),
|
||||
(self.tr("Author"), mainTheme.themeAuthor),
|
||||
(self.tr("Credit"), mainTheme.themeCredit),
|
||||
(self.tr("Licence"), licURL),
|
||||
])
|
||||
)
|
||||
|
||||
if theIcons.themeName:
|
||||
licURL = f"<a href='{theIcons.themeLicenseUrl}'>{theIcons.themeLicense}</a>"
|
||||
if iconCache.themeName:
|
||||
licURL = f"<a href='{iconCache.themeLicenseUrl}'>{iconCache.themeLicense}</a>"
|
||||
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.tr("Author"), theIcons.themeAuthor),
|
||||
(self.tr("Credit"), theIcons.themeCredit),
|
||||
(self.tr("Author"), iconCache.themeAuthor),
|
||||
(self.tr("Credit"), iconCache.themeCredit),
|
||||
(self.tr("Licence"), licURL),
|
||||
])
|
||||
)
|
||||
|
||||
if theTheme.syntaxName:
|
||||
licURL = f"<a href='{theTheme.syntaxLicenseUrl}'>{theTheme.syntaxLicense}</a>"
|
||||
if mainTheme.syntaxName:
|
||||
licURL = f"<a href='{mainTheme.syntaxLicenseUrl}'>{mainTheme.syntaxLicense}</a>"
|
||||
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.tr("Author"), theTheme.syntaxAuthor),
|
||||
(self.tr("Credit"), theTheme.syntaxCredit),
|
||||
(self.tr("Author"), mainTheme.syntaxAuthor),
|
||||
(self.tr("Credit"), mainTheme.syntaxCredit),
|
||||
(self.tr("Licence"), licURL),
|
||||
])
|
||||
)
|
||||
@@ -280,12 +280,12 @@ class GuiAbout(QDialog):
|
||||
" padding-right: 0.8em;"
|
||||
"}}\n"
|
||||
).format(
|
||||
hColR=self.theParent.theTheme.colHead[0],
|
||||
hColG=self.theParent.theTheme.colHead[1],
|
||||
hColB=self.theParent.theTheme.colHead[2],
|
||||
kColR=self.theTheme.colKey[0],
|
||||
kColG=self.theTheme.colKey[1],
|
||||
kColB=self.theTheme.colKey[2],
|
||||
hColR=self.mainGui.mainTheme.colHead[0],
|
||||
hColG=self.mainGui.mainTheme.colHead[1],
|
||||
hColB=self.mainGui.mainTheme.colHead[2],
|
||||
kColR=self.mainTheme.colKey[0],
|
||||
kColG=self.mainTheme.colKey[1],
|
||||
kColB=self.mainTheme.colKey[2],
|
||||
)
|
||||
self.pageAbout.document().setDefaultStyleSheet(styleSheet)
|
||||
self.pageNotes.document().setDefaultStyleSheet(styleSheet)
|
||||
|
||||
@@ -41,15 +41,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiDocMerge(QDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiDocMerge ...")
|
||||
self.setObjectName("GuiDocMerge")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
self.sourceItem = None
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
@@ -57,7 +57,7 @@ class GuiDocMerge(QDialog):
|
||||
|
||||
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
|
||||
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()
|
||||
@@ -102,7 +102,7 @@ class GuiDocMerge(QDialog):
|
||||
finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
|
||||
|
||||
if len(finalOrder) == 0:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"No source documents found. Nothing to do."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
@@ -113,21 +113,21 @@ class GuiDocMerge(QDialog):
|
||||
docText = inDoc.readDocument()
|
||||
docErr = inDoc.getError()
|
||||
if docText is None and docErr:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("Failed to open document file."), docErr
|
||||
], nwAlert.ERROR)
|
||||
if docText:
|
||||
theText += docText.rstrip("\n")+"\n\n"
|
||||
|
||||
if self.sourceItem is None:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"No source folder selected. Nothing to do."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
srcItem = self.theProject.tree[self.sourceItem]
|
||||
if srcItem is None:
|
||||
self.theParent.makeAlert(self.tr("Internal error."), nwAlert.ERROR)
|
||||
self.mainGui.makeAlert(self.tr("Internal error."), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemParent)
|
||||
@@ -137,13 +137,13 @@ class GuiDocMerge(QDialog):
|
||||
|
||||
outDoc = NWDoc(self.theProject, nHandle)
|
||||
if not outDoc.writeDocument(theText):
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("Could not save document."), outDoc.getError()
|
||||
], nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
self.theParent.treeView.revealNewTreeItem(nHandle)
|
||||
self.theParent.openDocument(nHandle, doScroll=True)
|
||||
self.mainGui.projView.revealNewTreeItem(nHandle)
|
||||
self.mainGui.openDocument(nHandle, doScroll=True)
|
||||
|
||||
self._doClose()
|
||||
|
||||
@@ -165,7 +165,7 @@ class GuiDocMerge(QDialog):
|
||||
are then added to the list view in order. The list itself can be
|
||||
reordered by the user.
|
||||
"""
|
||||
tHandle = self.theParent.treeView.getSelectedHandle()
|
||||
tHandle = self.mainGui.projView.getSelectedHandle()
|
||||
self.sourceItem = tHandle
|
||||
if tHandle is None:
|
||||
return False
|
||||
@@ -175,12 +175,12 @@ class GuiDocMerge(QDialog):
|
||||
return False
|
||||
|
||||
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."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle):
|
||||
for sHandle in self.mainGui.projView.getTreeFromHandle(tHandle):
|
||||
newItem = QListWidgetItem()
|
||||
nwItem = self.theProject.tree[sHandle]
|
||||
if nwItem.itemType is not nwItemType.FILE:
|
||||
|
||||
@@ -41,15 +41,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiDocSplit(QDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiDocSplit ...")
|
||||
self.setObjectName("GuiDocSplit")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.sourceItem = None
|
||||
self.sourceText = []
|
||||
@@ -60,7 +60,7 @@ class GuiDocSplit(QDialog):
|
||||
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
|
||||
self.helpLabel = QHelpLabel(
|
||||
self.tr("Select the maximum level to split into files."),
|
||||
self.theParent.theTheme.helpText
|
||||
self.mainGui.mainTheme.helpText
|
||||
)
|
||||
|
||||
self.listBox = QListWidget()
|
||||
@@ -115,14 +115,14 @@ class GuiDocSplit(QDialog):
|
||||
logger.verbose("GuiDocSplit split button clicked")
|
||||
|
||||
if self.sourceItem is None:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"No source document selected. Nothing to do."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
srcItem = self.theProject.tree[self.sourceItem]
|
||||
if srcItem is None:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Could not parse source document."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
@@ -132,7 +132,7 @@ class GuiDocSplit(QDialog):
|
||||
|
||||
docErr = inDoc.getError()
|
||||
if theText is None and docErr:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("Failed to open document file."), docErr
|
||||
], nwAlert.ERROR)
|
||||
|
||||
@@ -153,12 +153,12 @@ class GuiDocSplit(QDialog):
|
||||
|
||||
nFiles = len(finalOrder)
|
||||
if nFiles == 0:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"No headers found. Nothing to do."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
msgYes = self.theParent.askQuestion(
|
||||
msgYes = self.mainGui.askQuestion(
|
||||
self.tr("Split Document"),
|
||||
"{0}<br><br>{1}".format(
|
||||
self.tr(
|
||||
@@ -175,7 +175,7 @@ class GuiDocSplit(QDialog):
|
||||
|
||||
# Create the folder
|
||||
fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemParent)
|
||||
self.theParent.treeView.revealNewTreeItem(fHandle)
|
||||
self.mainGui.projView.revealNewTreeItem(fHandle)
|
||||
logger.verbose("Creating folder '%s'", fHandle)
|
||||
|
||||
# Loop through, and create the files
|
||||
@@ -196,12 +196,12 @@ class GuiDocSplit(QDialog):
|
||||
|
||||
outDoc = NWDoc(self.theProject, nHandle)
|
||||
if not outDoc.writeDocument(theText):
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("Could not save document."), outDoc.getError()
|
||||
], nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
self.theParent.treeView.revealNewTreeItem(nHandle)
|
||||
self.mainGui.projView.revealNewTreeItem(nHandle)
|
||||
|
||||
self._doClose()
|
||||
|
||||
@@ -226,7 +226,7 @@ class GuiDocSplit(QDialog):
|
||||
"""
|
||||
self.listBox.clear()
|
||||
if self.sourceItem is None:
|
||||
self.sourceItem = self.theParent.treeView.getSelectedHandle()
|
||||
self.sourceItem = self.mainGui.projView.getSelectedHandle()
|
||||
|
||||
if self.sourceItem is None:
|
||||
return False
|
||||
@@ -236,7 +236,7 @@ class GuiDocSplit(QDialog):
|
||||
return False
|
||||
|
||||
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."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
@@ -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 2018–2022, 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
|
||||
@@ -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 2018–2022, 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
|
||||
@@ -43,25 +43,25 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiPreferences(PagedDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
PagedDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
PagedDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiPreferences ...")
|
||||
self.setObjectName("GuiPreferences")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.setWindowTitle(self.tr("Preferences"))
|
||||
|
||||
self.tabGeneral = GuiPreferencesGeneral(self.theParent)
|
||||
self.tabProjects = GuiPreferencesProjects(self.theParent)
|
||||
self.tabDocs = GuiPreferencesDocuments(self.theParent)
|
||||
self.tabEditor = GuiPreferencesEditor(self.theParent)
|
||||
self.tabSyntax = GuiPreferencesSyntax(self.theParent)
|
||||
self.tabAuto = GuiPreferencesAutomation(self.theParent)
|
||||
self.tabQuote = GuiPreferencesQuotes(self.theParent)
|
||||
self.tabGeneral = GuiPreferencesGeneral(self.mainGui)
|
||||
self.tabProjects = GuiPreferencesProjects(self.mainGui)
|
||||
self.tabDocs = GuiPreferencesDocuments(self.mainGui)
|
||||
self.tabEditor = GuiPreferencesEditor(self.mainGui)
|
||||
self.tabSyntax = GuiPreferencesSyntax(self.mainGui)
|
||||
self.tabAuto = GuiPreferencesAutomation(self.mainGui)
|
||||
self.tabQuote = GuiPreferencesQuotes(self.mainGui)
|
||||
|
||||
self.addTab(self.tabGeneral, self.tr("General"))
|
||||
self.addTab(self.tabProjects, self.tr("Projects"))
|
||||
@@ -102,12 +102,12 @@ class GuiPreferences(PagedDialog):
|
||||
self.tabQuote.saveValues()
|
||||
|
||||
if needsRestart:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Some changes will not be applied until novelWriter has been restarted."
|
||||
), nwAlert.INFO)
|
||||
|
||||
if refreshTree:
|
||||
self.theParent.treeView.buildTree()
|
||||
self.mainGui.projView.populateTree()
|
||||
|
||||
self._saveWindowSize()
|
||||
self.accept()
|
||||
@@ -138,16 +138,16 @@ class GuiPreferences(PagedDialog):
|
||||
|
||||
class GuiPreferencesGeneral(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
# Look and Feel
|
||||
@@ -174,7 +174,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
# Select Theme
|
||||
self.guiTheme = QComboBox()
|
||||
self.guiTheme.setMinimumWidth(minWidth)
|
||||
self.theThemes = self.theTheme.listThemes()
|
||||
self.theThemes = self.mainTheme.listThemes()
|
||||
for themeDir, themeName in self.theThemes:
|
||||
self.guiTheme.addItem(themeName, themeDir)
|
||||
themeIdx = self.guiTheme.findData(self.mainConf.guiTheme)
|
||||
@@ -190,8 +190,8 @@ class GuiPreferencesGeneral(QWidget):
|
||||
# Select Icon Theme
|
||||
self.guiIcons = QComboBox()
|
||||
self.guiIcons.setMinimumWidth(minWidth)
|
||||
self.theIcons = self.theTheme.theIcons.listThemes()
|
||||
for iconDir, iconName in self.theIcons:
|
||||
self.iconCache = self.mainTheme.iconCache.listThemes()
|
||||
for iconDir, iconName in self.iconCache:
|
||||
self.guiIcons.addItem(iconName, iconDir)
|
||||
iconIdx = self.guiIcons.findData(self.mainConf.guiIcons)
|
||||
if iconIdx != -1:
|
||||
@@ -206,7 +206,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
# Editor Theme
|
||||
self.guiSyntax = QComboBox()
|
||||
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
|
||||
self.theSyntaxes = self.theTheme.listSyntax()
|
||||
self.theSyntaxes = self.mainTheme.listSyntax()
|
||||
for syntaxFile, syntaxName in self.theSyntaxes:
|
||||
self.guiSyntax.addItem(syntaxName, syntaxFile)
|
||||
syntaxIdx = self.guiSyntax.findData(self.mainConf.guiSyntax)
|
||||
@@ -225,7 +225,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
self.guiFont.setFixedWidth(self.mainConf.pxInt(162))
|
||||
self.guiFont.setText(self.mainConf.guiFont)
|
||||
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.mainForm.addRow(
|
||||
self.tr("Font family"),
|
||||
@@ -344,16 +344,16 @@ class GuiPreferencesGeneral(QWidget):
|
||||
|
||||
class GuiPreferencesProjects(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
# Automatic Save
|
||||
@@ -505,16 +505,16 @@ class GuiPreferencesProjects(QWidget):
|
||||
|
||||
class GuiPreferencesDocuments(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
# Text Style
|
||||
@@ -527,7 +527,7 @@ class GuiPreferencesDocuments(QWidget):
|
||||
self.textFont.setFixedWidth(self.mainConf.pxInt(162))
|
||||
self.textFont.setText(self.mainConf.textFont)
|
||||
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.mainForm.addRow(
|
||||
self.tr("Font family"),
|
||||
@@ -666,16 +666,16 @@ class GuiPreferencesDocuments(QWidget):
|
||||
|
||||
class GuiPreferencesEditor(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
mW = self.mainConf.pxInt(250)
|
||||
@@ -688,7 +688,7 @@ class GuiPreferencesEditor(QWidget):
|
||||
self.spellLanguage = QComboBox(self)
|
||||
self.spellLanguage.setMaximumWidth(mW)
|
||||
|
||||
langAvail = self.theParent.docEditor.spEnchant.listDictionaries()
|
||||
langAvail = self.mainGui.docEditor.spEnchant.listDictionaries()
|
||||
if self.mainConf.hasEnchant:
|
||||
if langAvail:
|
||||
for spTag, spProv in langAvail:
|
||||
@@ -840,16 +840,16 @@ class GuiPreferencesEditor(QWidget):
|
||||
|
||||
class GuiPreferencesSyntax(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
# Quotes & Dialogue
|
||||
@@ -943,16 +943,16 @@ class GuiPreferencesSyntax(QWidget):
|
||||
|
||||
class GuiPreferencesAutomation(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
# Automatic Features
|
||||
@@ -1100,16 +1100,16 @@ class GuiPreferencesAutomation(QWidget):
|
||||
|
||||
class GuiPreferencesQuotes(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
# Quotation Style
|
||||
@@ -1117,7 +1117,7 @@ class GuiPreferencesQuotes(QWidget):
|
||||
self.mainForm.addGroupLabel(self.tr("Quotation Style"))
|
||||
|
||||
qWidth = self.mainConf.pxInt(40)
|
||||
bWidth = int(2.5*self.theTheme.getTextWidth("..."))
|
||||
bWidth = int(2.5*self.mainTheme.getTextWidth("..."))
|
||||
self.quoteSym = {}
|
||||
|
||||
# Single Quote Style
|
||||
|
||||
@@ -43,15 +43,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiProjectDetails(PagedDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
PagedDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
PagedDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiProjectDetails ...")
|
||||
self.setObjectName("GuiProjectDetails")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.setWindowTitle(self.tr("Project Details"))
|
||||
|
||||
@@ -66,8 +66,8 @@ class GuiProjectDetails(PagedDialog):
|
||||
self.mainConf.pxInt(pOptions.getInt("GuiProjectDetails", "winHeight", wH))
|
||||
)
|
||||
|
||||
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject)
|
||||
self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject)
|
||||
self.tabMain = GuiProjectDetailsMain(self.mainGui, self.theProject)
|
||||
self.tabContents = GuiProjectDetailsContents(self.mainGui, self.theProject)
|
||||
|
||||
self.addTab(self.tabMain, self.tr("Overview"))
|
||||
self.addTab(self.tabContents, self.tr("Contents"))
|
||||
@@ -139,16 +139,16 @@ class GuiProjectDetails(PagedDialog):
|
||||
|
||||
class GuiProjectDetailsMain(QWidget):
|
||||
|
||||
def __init__(self, theParent, theProject):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui, theProject):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theProject
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
fPx = self.theTheme.fontPixelSize
|
||||
fPt = self.theTheme.fontPointSize
|
||||
fPx = self.mainTheme.fontPixelSize
|
||||
fPt = self.mainTheme.fontPointSize
|
||||
vPx = self.mainConf.pxInt(4)
|
||||
hPx = self.mainConf.pxInt(12)
|
||||
|
||||
@@ -271,18 +271,18 @@ class GuiProjectDetailsContents(QWidget):
|
||||
C_PAGE = 3
|
||||
C_PROG = 4
|
||||
|
||||
def __init__(self, theParent, theProject):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui, theProject):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theProject
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# Internal
|
||||
self._theToC = []
|
||||
|
||||
iPx = self.theTheme.baseIconSize
|
||||
iPx = self.mainTheme.baseIconSize
|
||||
hPx = self.mainConf.pxInt(12)
|
||||
vPx = self.mainConf.pxInt(4)
|
||||
pOptions = self.theProject.options
|
||||
@@ -469,7 +469,7 @@ class GuiProjectDetailsContents(QWidget):
|
||||
if tTitle.strip() == "":
|
||||
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_WORDS, f"{wCount:n}")
|
||||
newItem.setText(self.C_PAGES, f"{pCount:n}")
|
||||
|
||||
@@ -53,21 +53,21 @@ class GuiProjectLoad(QDialog):
|
||||
C_COUNT = 1
|
||||
C_TIME = 2
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiProjectLoad ...")
|
||||
self.setObjectName("GuiProjectLoad")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.openState = self.NONE_STATE
|
||||
self.openPath = None
|
||||
|
||||
sPx = self.mainConf.pxInt(16)
|
||||
nPx = self.mainConf.pxInt(96)
|
||||
iPx = self.theTheme.baseIconSize
|
||||
iPx = self.mainTheme.baseIconSize
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.innerBox = QHBoxLayout()
|
||||
@@ -80,7 +80,7 @@ class GuiProjectLoad(QDialog):
|
||||
self.setModal(True)
|
||||
|
||||
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.projectForm = QGridLayout()
|
||||
@@ -110,7 +110,7 @@ class GuiProjectLoad(QDialog):
|
||||
self.selPath.setReadOnly(True)
|
||||
|
||||
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.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
|
||||
@@ -225,7 +225,7 @@ class GuiProjectLoad(QDialog):
|
||||
selList = self.listBox.selectedItems()
|
||||
if selList:
|
||||
projName = selList[0].text(self.C_NAME)
|
||||
msgYes = self.theParent.askQuestion(
|
||||
msgYes = self.mainGui.askQuestion(
|
||||
self.tr("Remove Entry"),
|
||||
self.tr(
|
||||
"Remove '{0}' from the recent projects list? "
|
||||
@@ -280,7 +280,7 @@ class GuiProjectLoad(QDialog):
|
||||
sortList = sorted(dataList, key=lambda x: x[1], reverse=True)
|
||||
for theTitle, theTime, theWords, projPath in sortList:
|
||||
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.setData(self.C_NAME, Qt.UserRole, projPath)
|
||||
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_COUNT, 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)
|
||||
|
||||
if self.listBox.topLevelItemCount() > 0:
|
||||
|
||||
@@ -43,15 +43,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiProjectSettings(PagedDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
PagedDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
PagedDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiProjectSettings ...")
|
||||
self.setObjectName("GuiProjectSettings")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.theProject.countStatus()
|
||||
self.setWindowTitle(self.tr("Project Settings"))
|
||||
@@ -67,10 +67,10 @@ class GuiProjectSettings(PagedDialog):
|
||||
self.mainConf.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
|
||||
)
|
||||
|
||||
self.tabMain = GuiProjectEditMain(self.theParent, self.theProject)
|
||||
self.tabStatus = GuiProjectEditStatus(self.theParent, self.theProject, True)
|
||||
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject, False)
|
||||
self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject)
|
||||
self.tabMain = GuiProjectEditMain(self.mainGui, self.theProject)
|
||||
self.tabStatus = GuiProjectEditStatus(self.mainGui, self.theProject, True)
|
||||
self.tabImport = GuiProjectEditStatus(self.mainGui, self.theProject, False)
|
||||
self.tabReplace = GuiProjectEditReplace(self.mainGui, self.theProject)
|
||||
|
||||
self.addTab(self.tabMain, self.tr("Settings"))
|
||||
self.addTab(self.tabStatus, self.tr("Status"))
|
||||
@@ -121,7 +121,7 @@ class GuiProjectSettings(PagedDialog):
|
||||
self.theProject.setImportColours(newList, delList)
|
||||
|
||||
if self.tabStatus.colChanged or self.tabImport.colChanged:
|
||||
self.theParent.rebuildTrees()
|
||||
self.mainGui.rebuildTrees()
|
||||
|
||||
if self.tabReplace.arChanged:
|
||||
newList = self.tabReplace.getNewList()
|
||||
@@ -166,22 +166,22 @@ class GuiProjectSettings(PagedDialog):
|
||||
|
||||
class GuiProjectEditMain(QWidget):
|
||||
|
||||
def __init__(self, theParent, theProject):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui, theProject):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.mainGui = mainGui
|
||||
self.theProject = theProject
|
||||
|
||||
# The Form
|
||||
self.mainForm = QConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(self.theParent.theTheme.helpText)
|
||||
self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
|
||||
self.mainForm.addGroupLabel(self.tr("Project Settings"))
|
||||
|
||||
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.setMaxLength(200)
|
||||
@@ -216,7 +216,7 @@ class GuiProjectEditMain(QWidget):
|
||||
self.spellLang = QComboBox(self)
|
||||
self.spellLang.setMaximumWidth(xW)
|
||||
self.spellLang.addItem(self.tr("Default"), "None")
|
||||
langAvail = self.theParent.docEditor.spEnchant.listDictionaries()
|
||||
langAvail = self.mainGui.docEditor.spEnchant.listDictionaries()
|
||||
for spTag, spProv in langAvail:
|
||||
qLocal = QLocale(spTag)
|
||||
spLang = qLocal.nativeLanguageName().title()
|
||||
@@ -256,13 +256,13 @@ class GuiProjectEditStatus(QWidget):
|
||||
COL_ROLE = Qt.UserRole + 1
|
||||
NUM_ROLE = Qt.UserRole + 2
|
||||
|
||||
def __init__(self, theParent, theProject, isStatus):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui, theProject, isStatus):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.mainGui = mainGui
|
||||
self.theProject = theProject
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
if isStatus:
|
||||
self.theStatus = self.theProject.statusItems
|
||||
@@ -281,7 +281,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.colChanged = False
|
||||
self.selColour = QColor(100, 100, 100)
|
||||
|
||||
self.iPx = self.theTheme.baseIconSize
|
||||
self.iPx = self.mainTheme.baseIconSize
|
||||
|
||||
# The List
|
||||
# ========
|
||||
@@ -300,16 +300,16 @@ class GuiProjectEditStatus(QWidget):
|
||||
# List Controls
|
||||
# =============
|
||||
|
||||
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
|
||||
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
|
||||
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.upButton = QPushButton(self.theTheme.getIcon("up"), "")
|
||||
self.upButton = QPushButton(self.mainTheme.getIcon("up"), "")
|
||||
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))
|
||||
|
||||
# Edit Form
|
||||
@@ -411,7 +411,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
if selItem is not None:
|
||||
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
||||
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."
|
||||
), nwAlert.ERROR)
|
||||
else:
|
||||
@@ -527,12 +527,12 @@ class GuiProjectEditReplace(QWidget):
|
||||
COL_KEY = 0
|
||||
COL_REPL = 1
|
||||
|
||||
def __init__(self, theParent, theProject):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui, theProject):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = theProject
|
||||
self.arChanged = False
|
||||
|
||||
@@ -563,10 +563,10 @@ class GuiProjectEditReplace(QWidget):
|
||||
# List Controls
|
||||
# =============
|
||||
|
||||
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
|
||||
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
|
||||
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)
|
||||
|
||||
# Edit Form
|
||||
|
||||
@@ -42,8 +42,8 @@ class GuiQuoteSelect(QDialog):
|
||||
|
||||
selectedQuote = ""
|
||||
|
||||
def __init__(self, theParent=None, currentQuote='"'):
|
||||
QDialog.__init__(self, parent=theParent)
|
||||
def __init__(self, parent=None, currentQuote='"'):
|
||||
QDialog.__init__(self, parent=parent)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
|
||||
|
||||
@@ -43,14 +43,14 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiUpdates(QDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiUpdates ...")
|
||||
self.setObjectName("GuiUpdates")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.mainGui = mainGui
|
||||
|
||||
self.setWindowTitle(self.tr("Check for Updates"))
|
||||
|
||||
@@ -61,7 +61,7 @@ class GuiUpdates(QDialog):
|
||||
|
||||
# Left Box
|
||||
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.addWidget(self.nwIcon)
|
||||
|
||||
@@ -42,16 +42,16 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiWordList(QDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiWordList ...")
|
||||
self.setObjectName("GuiWordList")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.setWindowTitle(self.tr("Project Word List"))
|
||||
|
||||
@@ -78,10 +78,10 @@ class GuiWordList(QDialog):
|
||||
|
||||
self.newEntry = QLineEdit()
|
||||
|
||||
self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
|
||||
self.addButton = QPushButton(self.mainTheme.getIcon("add"), "")
|
||||
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.editBox = QHBoxLayout()
|
||||
@@ -121,13 +121,13 @@ class GuiWordList(QDialog):
|
||||
"""
|
||||
newWord = self.newEntry.text().strip()
|
||||
if newWord == "":
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Cannot add a blank word."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
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."
|
||||
).format(newWord), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
@@ -23,9 +23,9 @@ from novelwriter.gui.doceditor import GuiDocEditor
|
||||
from novelwriter.gui.docviewer import GuiDocViewer, GuiDocViewDetails
|
||||
from novelwriter.gui.itemdetails import GuiItemDetails
|
||||
from novelwriter.gui.mainmenu import GuiMainMenu
|
||||
from novelwriter.gui.noveltree import GuiNovelTree
|
||||
from novelwriter.gui.outline import GuiOutline
|
||||
from novelwriter.gui.projtree import GuiProjectTree
|
||||
from novelwriter.gui.noveltree import GuiNovelView
|
||||
from novelwriter.gui.outline import GuiOutlineView
|
||||
from novelwriter.gui.projtree import GuiProjectView
|
||||
from novelwriter.gui.statusbar import GuiMainStatus
|
||||
from novelwriter.gui.theme import GuiTheme
|
||||
from novelwriter.gui.viewsbar import GuiViewsBar
|
||||
@@ -37,9 +37,9 @@ __all__ = [
|
||||
"GuiItemDetails",
|
||||
"GuiMainMenu",
|
||||
"GuiMainStatus",
|
||||
"GuiNovelTree",
|
||||
"GuiOutline",
|
||||
"GuiProjectTree",
|
||||
"GuiNovelView",
|
||||
"GuiOutlineView",
|
||||
"GuiProjectView",
|
||||
"GuiTheme",
|
||||
"GuiViewsBar",
|
||||
]
|
||||
|
||||
@@ -376,8 +376,8 @@ class QSwitch(QAbstractButton):
|
||||
|
||||
class PagedDialog(QDialog):
|
||||
|
||||
def __init__(self, theParent=None):
|
||||
QDialog.__init__(self, parent=theParent)
|
||||
def __init__(self, parent=None):
|
||||
QDialog.__init__(self, parent=parent)
|
||||
|
||||
self._tabBar = VerticalTabBar(self)
|
||||
self._tabBar.setExpanding(False)
|
||||
@@ -426,8 +426,8 @@ class PagedDialog(QDialog):
|
||||
|
||||
class VerticalTabBar(QTabBar):
|
||||
|
||||
def __init__(self, theParent=None):
|
||||
QTabBar.__init__(self, parent=theParent)
|
||||
def __init__(self, parent=None):
|
||||
QTabBar.__init__(self, parent=parent)
|
||||
self._mW = novelwriter.CONFIG.pxInt(150)
|
||||
return
|
||||
|
||||
|
||||
@@ -72,16 +72,16 @@ class GuiDocEditor(QTextEdit):
|
||||
docCountsChanged = pyqtSignal(str, int, int, int)
|
||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||
|
||||
def __init__(self, theParent):
|
||||
QTextEdit.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QTextEdit.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiDocEditor ...")
|
||||
|
||||
# Class Variables
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self._nwDocument = None
|
||||
self._nwItem = None
|
||||
@@ -124,7 +124,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
# Syntax
|
||||
self.spEnchant = NWSpellEnchant()
|
||||
self.highLight = GuiDocHighlighter(qDoc, self.theParent, self.spEnchant)
|
||||
self.highLight = GuiDocHighlighter(qDoc, self.mainGui, self.spEnchant)
|
||||
|
||||
# Context Menu
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
@@ -239,10 +239,10 @@ class GuiDocEditor(QTextEdit):
|
||||
if self.mainConf.textFont is None:
|
||||
# If none is defined, set a default font
|
||||
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.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.setPointSize(12)
|
||||
else:
|
||||
@@ -257,14 +257,14 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
# Set the widget colours to match syntax theme
|
||||
mainPalette = self.palette()
|
||||
mainPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
self.setPalette(mainPalette)
|
||||
|
||||
docPalette = self.viewport().palette()
|
||||
docPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack))
|
||||
docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
||||
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
self.viewport().setPalette(docPalette)
|
||||
|
||||
self.docHeader.matchColours()
|
||||
@@ -341,7 +341,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
docSize = len(theDoc)
|
||||
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 size is {0} MB. "
|
||||
"The maximum size allowed is {1} MB."
|
||||
@@ -417,7 +417,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
# Update the status bar
|
||||
if self._nwItem is not None:
|
||||
self.theParent.setStatus(
|
||||
self.mainGui.setStatus(
|
||||
self.tr("Opened Document: {0}").format(self._nwItem.itemName)
|
||||
)
|
||||
|
||||
@@ -442,7 +442,7 @@ class GuiDocEditor(QTextEdit):
|
||||
"""
|
||||
docSize = len(theText)
|
||||
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 size is {0} MB. "
|
||||
"The maximum size allowed is {1} MB."
|
||||
@@ -488,7 +488,7 @@ class GuiDocEditor(QTextEdit):
|
||||
if not self._nwDocument.writeDocument(docText):
|
||||
saveOk = False
|
||||
if self._nwDocument._currHash != self._nwDocument._prevHash:
|
||||
msgYes = self.theParent.askQuestion(
|
||||
msgYes = self.mainGui.askQuestion(
|
||||
self.tr("File Changed on Disk"),
|
||||
self.tr(
|
||||
"This document has been changed outside of novelWriter "
|
||||
@@ -499,7 +499,7 @@ class GuiDocEditor(QTextEdit):
|
||||
saveOk = self._nwDocument.writeDocument(docText, forceWrite=True)
|
||||
|
||||
if not saveOk:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("Could not save document."), self._nwDocument.getError()
|
||||
], nwAlert.ERROR)
|
||||
|
||||
@@ -511,18 +511,20 @@ class GuiDocEditor(QTextEdit):
|
||||
self.theProject.index.scanText(tHandle, docText)
|
||||
newHeader = self.theProject.index.getHandleHeaderLevel(tHandle)
|
||||
|
||||
# ToDo: This should be a signal
|
||||
if self._updateHeaders(checkLevel=True):
|
||||
self.theParent.requestNovelTreeRefresh()
|
||||
self.mainGui.requestNovelTreeRefresh()
|
||||
else:
|
||||
self.theParent.novelView.updateWordCounts(tHandle)
|
||||
self.mainGui.novelView.updateWordCounts(tHandle)
|
||||
|
||||
# ToDo: This should be a signal
|
||||
if oldHeader != newHeader:
|
||||
self.theParent.treeView.setTreeItemValues(tHandle)
|
||||
self.theParent.treeMeta.updateViewBox(tHandle)
|
||||
self.mainGui.projView.setTreeItemValues(tHandle)
|
||||
self.mainGui.itemDetails.updateViewBox(tHandle)
|
||||
self.docFooter.updateInfo()
|
||||
|
||||
# Update the status bar
|
||||
self.theParent.setStatus(
|
||||
self.mainGui.setStatus(
|
||||
self.tr("Saved Document: {0}").format(self._nwItem.itemName)
|
||||
)
|
||||
|
||||
@@ -544,8 +546,8 @@ class GuiDocEditor(QTextEdit):
|
||||
sH = hBar.height() if hBar.isVisible() else 0
|
||||
|
||||
tM = cM
|
||||
if self.mainConf.textWidth > 0 or self.theParent.isFocusMode:
|
||||
tW = self.mainConf.getTextWidth(self.theParent.isFocusMode)
|
||||
if self.mainConf.textWidth > 0 or self.mainGui.isFocusMode:
|
||||
tW = self.mainConf.getTextWidth(self.mainGui.isFocusMode)
|
||||
tM = max((wW - sW - tW)//2, cM)
|
||||
|
||||
tB = self.frameWidth()
|
||||
@@ -704,7 +706,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
if not self.mainConf.hasEnchant:
|
||||
if theMode:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Spell checking requires the package PyEnchant. "
|
||||
"It does not appear to be installed."
|
||||
), nwAlert.INFO)
|
||||
@@ -714,7 +716,7 @@ class GuiDocEditor(QTextEdit):
|
||||
theMode = False
|
||||
|
||||
self._spellCheck = theMode
|
||||
self.theParent.mainMenu.setSpellCheck(theMode)
|
||||
self.mainGui.mainMenu.setSpellCheck(theMode)
|
||||
self.theProject.setSpellCheck(theMode)
|
||||
self.highLight.setSpellCheck(theMode)
|
||||
if not self._bigDoc:
|
||||
@@ -742,7 +744,7 @@ class GuiDocEditor(QTextEdit):
|
||||
qApp.restoreOverrideCursor()
|
||||
afTime = time()
|
||||
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
|
||||
|
||||
@@ -1085,7 +1087,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self._lastFind = None
|
||||
|
||||
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 maximum size of a single novelWriter document is {0} MB."
|
||||
).format(
|
||||
@@ -1245,7 +1247,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
if time() - self._lastEdit < 5 * self.wcInterval:
|
||||
logger.verbose("Running word counter")
|
||||
self.theParent.threadPool.start(self.wCounterDoc)
|
||||
self.mainGui.threadPool.start(self.wCounterDoc)
|
||||
|
||||
return
|
||||
|
||||
@@ -1302,7 +1304,7 @@ class GuiDocEditor(QTextEdit):
|
||||
logger.verbose("Selection word counter is busy")
|
||||
return
|
||||
|
||||
self.theParent.threadPool.start(self.wCounterSel)
|
||||
self.mainGui.threadPool.start(self.wCounterSel)
|
||||
|
||||
return
|
||||
|
||||
@@ -1381,7 +1383,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.docSearch.setResultCount(0, 0)
|
||||
self._lastFind = None
|
||||
if self.docSearch.doNextFile and not goBack:
|
||||
self.theParent.openNextDocument(
|
||||
self.mainGui.openNextDocument(
|
||||
self._docHandle, wrapAround=self.docSearch.doLoop
|
||||
)
|
||||
self.beginSearch()
|
||||
@@ -1401,7 +1403,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
if resIdx > maxIdx:
|
||||
if self.docSearch.doNextFile and not goBack:
|
||||
self.theParent.openNextDocument(
|
||||
self.mainGui.openNextDocument(
|
||||
self._docHandle, wrapAround=self.docSearch.doLoop
|
||||
)
|
||||
self.beginSearch()
|
||||
@@ -1645,7 +1647,7 @@ class GuiDocEditor(QTextEdit):
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if not theCursor.hasSelection():
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Please select some text before calling replace quotes."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
@@ -2187,9 +2189,9 @@ class GuiDocEditSearch(QFrame):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.docEditor = docEditor
|
||||
self.theParent = docEditor.theParent
|
||||
self.mainGui = docEditor.mainGui
|
||||
self.theProject = docEditor.theProject
|
||||
self.theTheme = docEditor.theTheme
|
||||
self.mainTheme = docEditor.mainTheme
|
||||
|
||||
self.repVisible = False
|
||||
self.isCaseSense = self.mainConf.searchCase
|
||||
@@ -2200,9 +2202,9 @@ class GuiDocEditSearch(QFrame):
|
||||
self.doMatchCap = self.mainConf.searchMatchCap
|
||||
|
||||
mPx = self.mainConf.pxInt(6)
|
||||
tPx = int(0.8*self.theTheme.fontPixelSize)
|
||||
self.boxFont = self.theTheme.guiFont
|
||||
self.boxFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
tPx = int(0.8*self.mainTheme.fontPixelSize)
|
||||
self.boxFont = self.mainTheme.guiFont
|
||||
self.boxFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.setAutoFillBackground(True)
|
||||
@@ -2236,38 +2238,38 @@ class GuiDocEditSearch(QFrame):
|
||||
|
||||
self.resultLabel = QLabel("?/?")
|
||||
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.setIcon(self.theTheme.getIcon("search_case"))
|
||||
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
|
||||
self.toggleCase.setCheckable(True)
|
||||
self.toggleCase.setChecked(self.isCaseSense)
|
||||
self.toggleCase.toggled.connect(self._doToggleCase)
|
||||
self.searchOpt.addAction(self.toggleCase)
|
||||
|
||||
self.toggleWord = QAction(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.setChecked(self.isWholeWord)
|
||||
self.toggleWord.toggled.connect(self._doToggleWord)
|
||||
self.searchOpt.addAction(self.toggleWord)
|
||||
|
||||
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.setChecked(self.isRegEx)
|
||||
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
|
||||
self.searchOpt.addAction(self.toggleRegEx)
|
||||
|
||||
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.setChecked(self.doLoop)
|
||||
self.toggleLoop.toggled.connect(self._doToggleLoop)
|
||||
self.searchOpt.addAction(self.toggleLoop)
|
||||
|
||||
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.setChecked(self.doNextFile)
|
||||
self.toggleProject.toggled.connect(self._doToggleProject)
|
||||
@@ -2276,7 +2278,7 @@ class GuiDocEditSearch(QFrame):
|
||||
self.searchOpt.addSeparator()
|
||||
|
||||
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.setChecked(self.doMatchCap)
|
||||
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
|
||||
@@ -2285,7 +2287,7 @@ class GuiDocEditSearch(QFrame):
|
||||
self.searchOpt.addSeparator()
|
||||
|
||||
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.searchOpt.addAction(self.cancelSearch)
|
||||
|
||||
@@ -2300,12 +2302,12 @@ class GuiDocEditSearch(QFrame):
|
||||
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
|
||||
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.setToolTip(self.tr("Find in current document"))
|
||||
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.setToolTip(self.tr("Find and replace in current document"))
|
||||
self.replaceButton.clicked.connect(self._doReplace)
|
||||
@@ -2424,7 +2426,7 @@ class GuiDocEditSearch(QFrame):
|
||||
"""
|
||||
currRes = "?" if currRes is None else currRes
|
||||
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.setMinimumWidth(minWidth)
|
||||
self.adjustSize()
|
||||
@@ -2575,13 +2577,13 @@ class GuiDocEditHeader(QWidget):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.docEditor = docEditor
|
||||
self.theParent = docEditor.theParent
|
||||
self.mainGui = docEditor.mainGui
|
||||
self.theProject = docEditor.theProject
|
||||
self.theTheme = docEditor.theTheme
|
||||
self.mainTheme = docEditor.mainTheme
|
||||
|
||||
self._docHandle = None
|
||||
|
||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||
fPx = int(0.9*self.mainTheme.fontPixelSize)
|
||||
hSp = self.mainConf.pxInt(6)
|
||||
|
||||
# Main Widget Settings
|
||||
@@ -2598,17 +2600,17 @@ class GuiDocEditHeader(QWidget):
|
||||
self.theTitle.setFixedHeight(fPx)
|
||||
|
||||
lblFont = self.theTitle.font()
|
||||
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||
self.theTitle.setFont(lblFont)
|
||||
|
||||
buttonStyle = (
|
||||
"QToolButton {{border: none; background: transparent;}} "
|
||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||
).format(*self.theTheme.colText)
|
||||
).format(*self.mainTheme.colText)
|
||||
|
||||
# Buttons
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.editButton.setFixedSize(fPx, fPx)
|
||||
@@ -2619,7 +2621,7 @@ class GuiDocEditHeader(QWidget):
|
||||
self.editButton.clicked.connect(self._editDocument)
|
||||
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.searchButton.setFixedSize(fPx, fPx)
|
||||
@@ -2630,7 +2632,7 @@ class GuiDocEditHeader(QWidget):
|
||||
self.searchButton.clicked.connect(self._searchDocument)
|
||||
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.minmaxButton.setFixedSize(fPx, fPx)
|
||||
@@ -2641,7 +2643,7 @@ class GuiDocEditHeader(QWidget):
|
||||
self.minmaxButton.clicked.connect(self._minmaxDocument)
|
||||
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.closeButton.setFixedSize(fPx, fPx)
|
||||
@@ -2684,9 +2686,9 @@ class GuiDocEditHeader(QWidget):
|
||||
theme rather than the main GUI.
|
||||
"""
|
||||
thePalette = QPalette()
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
|
||||
self.setPalette(thePalette)
|
||||
self.theTitle.setPalette(thePalette)
|
||||
@@ -2733,10 +2735,10 @@ class GuiDocEditHeader(QWidget):
|
||||
This function is called by the GuiMain class via the
|
||||
toggleFocusMode function and should not be activated directly.
|
||||
"""
|
||||
if self.theParent.isFocusMode:
|
||||
self.minmaxButton.setIcon(self.theTheme.getIcon("minimise"))
|
||||
if self.mainGui.isFocusMode:
|
||||
self.minmaxButton.setIcon(self.mainTheme.getIcon("minimise"))
|
||||
else:
|
||||
self.minmaxButton.setIcon(self.theTheme.getIcon("maximise"))
|
||||
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
|
||||
return
|
||||
|
||||
##
|
||||
@@ -2746,7 +2748,7 @@ class GuiDocEditHeader(QWidget):
|
||||
def _editDocument(self):
|
||||
"""Open the edit item dialog from the main GUI.
|
||||
"""
|
||||
self.theParent.editItem(self._docHandle)
|
||||
self.mainGui.editItemLabel(self._docHandle)
|
||||
return
|
||||
|
||||
def _searchDocument(self):
|
||||
@@ -2758,7 +2760,7 @@ class GuiDocEditHeader(QWidget):
|
||||
def _closeDocument(self):
|
||||
"""Trigger the close editor on the main window.
|
||||
"""
|
||||
self.theParent.closeDocEditor()
|
||||
self.mainGui.closeDocEditor()
|
||||
self.editButton.setVisible(False)
|
||||
self.searchButton.setVisible(False)
|
||||
self.closeButton.setVisible(False)
|
||||
@@ -2768,7 +2770,7 @@ class GuiDocEditHeader(QWidget):
|
||||
def _minmaxDocument(self):
|
||||
"""Switch on or off Focus Mode.
|
||||
"""
|
||||
self.theParent.toggleFocusMode()
|
||||
self.mainGui.toggleFocusMode()
|
||||
return
|
||||
|
||||
##
|
||||
@@ -2779,7 +2781,7 @@ class GuiDocEditHeader(QWidget):
|
||||
"""Capture a click on the title and ensure that the item is
|
||||
selected in the project tree.
|
||||
"""
|
||||
self.theParent.treeView.setSelectedHandle(self._docHandle, doScroll=True)
|
||||
self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True)
|
||||
return
|
||||
|
||||
# END Class GuiDocEditHeader
|
||||
@@ -2799,22 +2801,22 @@ class GuiDocEditFooter(QWidget):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.docEditor = docEditor
|
||||
self.theParent = docEditor.theParent
|
||||
self.mainGui = docEditor.mainGui
|
||||
self.theProject = docEditor.theProject
|
||||
self.theTheme = docEditor.theTheme
|
||||
self.mainTheme = docEditor.mainTheme
|
||||
|
||||
self._theItem = None
|
||||
self._docHandle = None
|
||||
|
||||
self._docSelection = False
|
||||
|
||||
self.sPx = int(round(0.9*self.theTheme.baseIconSize))
|
||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||
self.sPx = int(round(0.9*self.mainTheme.baseIconSize))
|
||||
fPx = int(0.9*self.mainTheme.fontPixelSize)
|
||||
bSp = self.mainConf.pxInt(4)
|
||||
hSp = self.mainConf.pxInt(6)
|
||||
|
||||
lblFont = self.font()
|
||||
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||
|
||||
# Main Widget Settings
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
@@ -2837,7 +2839,7 @@ class GuiDocEditFooter(QWidget):
|
||||
|
||||
# Lines
|
||||
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.setFixedHeight(self.sPx)
|
||||
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||
@@ -2853,7 +2855,7 @@ class GuiDocEditFooter(QWidget):
|
||||
|
||||
# Words
|
||||
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.setFixedHeight(self.sPx)
|
||||
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||
@@ -2905,9 +2907,9 @@ class GuiDocEditFooter(QWidget):
|
||||
theme rather than the main GUI.
|
||||
"""
|
||||
thePalette = QPalette()
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
|
||||
self.setPalette(thePalette)
|
||||
self.statusText.setPalette(thePalette)
|
||||
|
||||
@@ -46,16 +46,16 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
BLOCK_META = 2
|
||||
BLOCK_TITLE = 4
|
||||
|
||||
def __init__(self, theDoc, theParent, spEnchant):
|
||||
def __init__(self, theDoc, mainGui, spEnchant):
|
||||
QSyntaxHighlighter.__init__(self, theDoc)
|
||||
|
||||
logger.debug("Initialising GuiDocHighlighter ...")
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theDoc = theDoc
|
||||
self.spEnchant = spEnchant
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = mainGui.theProject
|
||||
self.theHandle = None
|
||||
self.spellCheck = False
|
||||
self.spellRx = None
|
||||
@@ -87,24 +87,24 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
"""
|
||||
logger.debug("Setting up highlighting rules")
|
||||
|
||||
self.colHead = QColor(*self.theTheme.colHead)
|
||||
self.colHeadH = QColor(*self.theTheme.colHeadH)
|
||||
self.colDialN = QColor(*self.theTheme.colDialN)
|
||||
self.colDialD = QColor(*self.theTheme.colDialD)
|
||||
self.colDialS = QColor(*self.theTheme.colDialS)
|
||||
self.colHidden = QColor(*self.theTheme.colHidden)
|
||||
self.colKey = QColor(*self.theTheme.colKey)
|
||||
self.colVal = QColor(*self.theTheme.colVal)
|
||||
self.colSpell = QColor(*self.theTheme.colSpell)
|
||||
self.colError = QColor(*self.theTheme.colError)
|
||||
self.colRepTag = QColor(*self.theTheme.colRepTag)
|
||||
self.colMod = QColor(*self.theTheme.colMod)
|
||||
self.colBreak = QColor(*self.theTheme.colEmph)
|
||||
self.colHead = QColor(*self.mainTheme.colHead)
|
||||
self.colHeadH = QColor(*self.mainTheme.colHeadH)
|
||||
self.colDialN = QColor(*self.mainTheme.colDialN)
|
||||
self.colDialD = QColor(*self.mainTheme.colDialD)
|
||||
self.colDialS = QColor(*self.mainTheme.colDialS)
|
||||
self.colHidden = QColor(*self.mainTheme.colHidden)
|
||||
self.colKey = QColor(*self.mainTheme.colKey)
|
||||
self.colVal = QColor(*self.mainTheme.colVal)
|
||||
self.colSpell = QColor(*self.mainTheme.colSpell)
|
||||
self.colError = QColor(*self.mainTheme.colError)
|
||||
self.colRepTag = QColor(*self.mainTheme.colRepTag)
|
||||
self.colMod = QColor(*self.mainTheme.colMod)
|
||||
self.colBreak = QColor(*self.mainTheme.colEmph)
|
||||
self.colBreak.setAlpha(64)
|
||||
|
||||
self.colEmph = None
|
||||
if self.mainConf.highlightEmph:
|
||||
self.colEmph = QColor(*self.theTheme.colEmph)
|
||||
self.colEmph = QColor(*self.mainTheme.colEmph)
|
||||
|
||||
self.hStyles = {
|
||||
"header1": self._makeFormat(self.colHead, "bold", 1.8),
|
||||
@@ -288,7 +288,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
if theText.startswith("@"): # Keywords and commands
|
||||
self.setCurrentBlockState(self.BLOCK_META)
|
||||
pIndex = self.theProject.index
|
||||
tItem = self.theParent.theProject.tree[self.theHandle]
|
||||
tItem = self.mainGui.theProject.tree[self.theHandle]
|
||||
isValid, theBits, thePos = pIndex.scanThis(theText)
|
||||
isGood = pIndex.checkThese(theBits, tItem)
|
||||
if isValid:
|
||||
|
||||
@@ -53,16 +53,16 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||
|
||||
def __init__(self, theParent):
|
||||
QTextBrowser.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QTextBrowser.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiDocViewer ...")
|
||||
|
||||
# Class Variables
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
# Internal Variables
|
||||
self._docHandle = None
|
||||
@@ -118,14 +118,14 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
# Set the widget colours to match syntax theme
|
||||
mainPalette = self.palette()
|
||||
mainPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
||||
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
self.setPalette(mainPalette)
|
||||
|
||||
docPalette = self.viewport().palette()
|
||||
docPalette.setColor(QPalette.Base, QColor(*self.theTheme.colBack))
|
||||
docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
||||
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
self.viewport().setPalette(docPalette)
|
||||
|
||||
self.docHeader.matchColours()
|
||||
@@ -221,7 +221,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.updateDocMargins()
|
||||
|
||||
# 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
|
||||
# the document dirty again to make sure it's re-rendered properly.
|
||||
@@ -539,27 +539,27 @@ class GuiDocViewer(QTextBrowser):
|
||||
" text-align: center;"
|
||||
"}}\n"
|
||||
).format(
|
||||
tColR=self.theTheme.colText[0],
|
||||
tColG=self.theTheme.colText[1],
|
||||
tColB=self.theTheme.colText[2],
|
||||
hColR=self.theTheme.colHead[0],
|
||||
hColG=self.theTheme.colHead[1],
|
||||
hColB=self.theTheme.colHead[2],
|
||||
aColR=self.theTheme.colVal[0],
|
||||
aColG=self.theTheme.colVal[1],
|
||||
aColB=self.theTheme.colVal[2],
|
||||
eColR=self.theTheme.colEmph[0],
|
||||
eColG=self.theTheme.colEmph[1],
|
||||
eColB=self.theTheme.colEmph[2],
|
||||
kColR=self.theTheme.colKey[0],
|
||||
kColG=self.theTheme.colKey[1],
|
||||
kColB=self.theTheme.colKey[2],
|
||||
cColR=self.theTheme.colHidden[0],
|
||||
cColG=self.theTheme.colHidden[1],
|
||||
cColB=self.theTheme.colHidden[2],
|
||||
mColR=self.theTheme.colMod[0],
|
||||
mColG=self.theTheme.colMod[1],
|
||||
mColB=self.theTheme.colMod[2],
|
||||
tColR=self.mainTheme.colText[0],
|
||||
tColG=self.mainTheme.colText[1],
|
||||
tColB=self.mainTheme.colText[2],
|
||||
hColR=self.mainTheme.colHead[0],
|
||||
hColG=self.mainTheme.colHead[1],
|
||||
hColB=self.mainTheme.colHead[2],
|
||||
aColR=self.mainTheme.colVal[0],
|
||||
aColG=self.mainTheme.colVal[1],
|
||||
aColB=self.mainTheme.colVal[2],
|
||||
eColR=self.mainTheme.colEmph[0],
|
||||
eColG=self.mainTheme.colEmph[1],
|
||||
eColB=self.mainTheme.colEmph[2],
|
||||
kColR=self.mainTheme.colKey[0],
|
||||
kColG=self.mainTheme.colKey[1],
|
||||
kColB=self.mainTheme.colKey[2],
|
||||
cColR=self.mainTheme.colHidden[0],
|
||||
cColG=self.mainTheme.colHidden[1],
|
||||
cColB=self.mainTheme.colHidden[2],
|
||||
mColR=self.mainTheme.colMod[0],
|
||||
mColG=self.mainTheme.colMod[1],
|
||||
mColB=self.mainTheme.colMod[2],
|
||||
)
|
||||
self.document().setDefaultStyleSheet(styleSheet)
|
||||
|
||||
@@ -714,14 +714,14 @@ class GuiDocViewHeader(QWidget):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.docViewer = docViewer
|
||||
self.theParent = docViewer.theParent
|
||||
self.mainGui = docViewer.mainGui
|
||||
self.theProject = docViewer.theProject
|
||||
self.theTheme = docViewer.theTheme
|
||||
self.mainTheme = docViewer.mainTheme
|
||||
|
||||
# Internal Variables
|
||||
self._docHandle = None
|
||||
|
||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||
fPx = int(0.9*self.mainTheme.fontPixelSize)
|
||||
hSp = self.mainConf.pxInt(6)
|
||||
|
||||
# Main Widget Settings
|
||||
@@ -738,17 +738,17 @@ class GuiDocViewHeader(QWidget):
|
||||
self.theTitle.setFixedHeight(fPx)
|
||||
|
||||
lblFont = self.theTitle.font()
|
||||
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||
self.theTitle.setFont(lblFont)
|
||||
|
||||
buttonStyle = (
|
||||
"QToolButton {{border: none; background: transparent;}} "
|
||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||
).format(*self.theTheme.colText)
|
||||
).format(*self.mainTheme.colText)
|
||||
|
||||
# Buttons
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.backButton.setFixedSize(fPx, fPx)
|
||||
@@ -759,7 +759,7 @@ class GuiDocViewHeader(QWidget):
|
||||
self.backButton.clicked.connect(self.docViewer.navBackward)
|
||||
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.forwardButton.setFixedSize(fPx, fPx)
|
||||
@@ -770,7 +770,7 @@ class GuiDocViewHeader(QWidget):
|
||||
self.forwardButton.clicked.connect(self.docViewer.navForward)
|
||||
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.refreshButton.setFixedSize(fPx, fPx)
|
||||
@@ -781,7 +781,7 @@ class GuiDocViewHeader(QWidget):
|
||||
self.refreshButton.clicked.connect(self._refreshDocument)
|
||||
|
||||
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.setIconSize(QSize(fPx, fPx))
|
||||
self.closeButton.setFixedSize(fPx, fPx)
|
||||
@@ -824,9 +824,9 @@ class GuiDocViewHeader(QWidget):
|
||||
theme rather than the main GUI.
|
||||
"""
|
||||
thePalette = QPalette()
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
|
||||
self.setPalette(thePalette)
|
||||
self.theTitle.setPalette(thePalette)
|
||||
@@ -882,14 +882,14 @@ class GuiDocViewHeader(QWidget):
|
||||
def _closeDocument(self):
|
||||
"""Trigger the close editor/viewer on the main window.
|
||||
"""
|
||||
self.theParent.closeDocViewer()
|
||||
self.mainGui.closeDocViewer()
|
||||
return
|
||||
|
||||
def _refreshDocument(self):
|
||||
"""Reload the content of the document.
|
||||
"""
|
||||
if self.docViewer.docHandle() == self.theParent.docEditor.docHandle():
|
||||
self.theParent.saveDocument()
|
||||
if self.docViewer.docHandle() == self.mainGui.docEditor.docHandle():
|
||||
self.mainGui.saveDocument()
|
||||
self.docViewer.reloadText()
|
||||
return
|
||||
|
||||
@@ -901,7 +901,7 @@ class GuiDocViewHeader(QWidget):
|
||||
"""Capture a click on the title and ensure that the item is
|
||||
selected in the project tree.
|
||||
"""
|
||||
self.theParent.treeView.setSelectedHandle(self._docHandle, doScroll=True)
|
||||
self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True)
|
||||
return
|
||||
|
||||
# END Class GuiDocViewHeader
|
||||
@@ -921,26 +921,26 @@ class GuiDocViewFooter(QWidget):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.docViewer = docViewer
|
||||
self.theParent = docViewer.theParent
|
||||
self.theTheme = docViewer.theTheme
|
||||
self.viewMeta = docViewer.theParent.viewMeta
|
||||
self.mainGui = docViewer.mainGui
|
||||
self.mainTheme = docViewer.mainTheme
|
||||
self.viewMeta = docViewer.mainGui.viewMeta
|
||||
|
||||
# Internal Variables
|
||||
self._docHandle = None
|
||||
|
||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||
fPx = int(0.9*self.mainTheme.fontPixelSize)
|
||||
bSp = self.mainConf.pxInt(2)
|
||||
hSp = self.mainConf.pxInt(8)
|
||||
|
||||
# Icons
|
||||
stickyOn = self.theTheme.getPixmap("sticky-on", (fPx, fPx))
|
||||
stickyOff = self.theTheme.getPixmap("sticky-off", (fPx, fPx))
|
||||
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
|
||||
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
|
||||
stickyIcon = QIcon()
|
||||
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
|
||||
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
|
||||
|
||||
bulletOn = self.theTheme.getPixmap("bullet-on", (fPx, fPx))
|
||||
bulletOff = self.theTheme.getPixmap("bullet-off", (fPx, fPx))
|
||||
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
|
||||
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
|
||||
bulletIcon = QIcon()
|
||||
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
|
||||
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
|
||||
@@ -952,13 +952,13 @@ class GuiDocViewFooter(QWidget):
|
||||
buttonStyle = (
|
||||
"QToolButton {{border: none; background: transparent;}} "
|
||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||
).format(*self.theTheme.colText)
|
||||
).format(*self.mainTheme.colText)
|
||||
|
||||
# Show/Hide Details
|
||||
self.showHide = QToolButton(self)
|
||||
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||
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.setFixedSize(QSize(fPx, fPx))
|
||||
self.showHide.clicked.connect(self._doShowHide)
|
||||
@@ -1039,7 +1039,7 @@ class GuiDocViewFooter(QWidget):
|
||||
self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||
|
||||
lblFont = self.font()
|
||||
lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize)
|
||||
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||
self.lblRefs.setFont(lblFont)
|
||||
self.lblSticky.setFont(lblFont)
|
||||
self.lblComments.setFont(lblFont)
|
||||
@@ -1084,9 +1084,9 @@ class GuiDocViewFooter(QWidget):
|
||||
theme rather than the main GUI.
|
||||
"""
|
||||
thePalette = QPalette()
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText))
|
||||
thePalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||
thePalette.setColor(QPalette.WindowText, QColor(*self.mainTheme.colText))
|
||||
thePalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||
|
||||
self.setPalette(thePalette)
|
||||
self.lblRefs.setPalette(thePalette)
|
||||
@@ -1140,14 +1140,14 @@ class GuiDocViewFooter(QWidget):
|
||||
|
||||
class GuiDocViewDetails(QScrollArea):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QScrollArea.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QScrollArea.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiDocViewDetails ...")
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
self.refList = QLabel("")
|
||||
self.refList.setWordWrap(True)
|
||||
@@ -1156,7 +1156,7 @@ class GuiDocViewDetails(QScrollArea):
|
||||
self.refList.linkActivated.connect(self._linkClicked)
|
||||
|
||||
self.linkStyle = "style='color: rgb({0},{1},{2})'".format(
|
||||
*self.theTheme.colLink
|
||||
*self.mainTheme.colLink
|
||||
)
|
||||
|
||||
# Assemble
|
||||
@@ -1181,7 +1181,7 @@ class GuiDocViewDetails(QScrollArea):
|
||||
"""Update the current list of document references from the
|
||||
project index.
|
||||
"""
|
||||
if self.theParent.docViewer.stickyRef:
|
||||
if self.mainGui.docViewer.stickyRef:
|
||||
return
|
||||
|
||||
theRefs = self.theProject.index.getBackReferenceList(tHandle)
|
||||
@@ -1209,7 +1209,7 @@ class GuiDocViewDetails(QScrollArea):
|
||||
if len(theLink) == 21:
|
||||
tHandle = theLink[:13]
|
||||
tAnchor = theLink[13:]
|
||||
self.theParent.viewDocument(tHandle, tAnchor)
|
||||
self.mainGui.viewDocument(tHandle, tAnchor)
|
||||
return
|
||||
|
||||
# END Class GuiDocViewDetails
|
||||
|
||||
@@ -38,14 +38,14 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiItemDetails(QWidget):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiItemDetails ...")
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# Internal Variables
|
||||
self._itemHandle = None
|
||||
@@ -54,11 +54,11 @@ class GuiItemDetails(QWidget):
|
||||
hSp = self.mainConf.pxInt(6)
|
||||
vSp = self.mainConf.pxInt(1)
|
||||
mPx = self.mainConf.pxInt(6)
|
||||
iPx = self.theTheme.baseIconSize
|
||||
fPt = self.theTheme.fontPointSize
|
||||
iPx = self.mainTheme.baseIconSize
|
||||
fPt = self.mainTheme.fontPointSize
|
||||
|
||||
self._expCheck = self.theTheme.getPixmap("check", (iPx, iPx))
|
||||
self._expCross = self.theTheme.getPixmap("cross", (iPx, iPx))
|
||||
self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
|
||||
self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
|
||||
|
||||
fntLabel = QFont()
|
||||
fntLabel.setBold(True)
|
||||
@@ -181,8 +181,8 @@ class GuiItemDetails(QWidget):
|
||||
self.setLayout(self.mainBox)
|
||||
|
||||
# Make sure the columns for flags and counts don't resize too often
|
||||
flagWidth = self.theTheme.getTextWidth("Mm", fntValue)
|
||||
countWidth = self.theTheme.getTextWidth("99,999", fntValue)
|
||||
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
|
||||
countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
|
||||
self.mainBox.setColumnMinimumWidth(1, flagWidth)
|
||||
self.mainBox.setColumnMinimumWidth(4, countWidth)
|
||||
|
||||
@@ -238,7 +238,7 @@ class GuiItemDetails(QWidget):
|
||||
return
|
||||
|
||||
self._itemHandle = tHandle
|
||||
iPx = int(round(0.8*self.theTheme.baseIconSize))
|
||||
iPx = int(round(0.8*self.mainTheme.baseIconSize))
|
||||
|
||||
# Label
|
||||
# =====
|
||||
@@ -267,7 +267,7 @@ class GuiItemDetails(QWidget):
|
||||
# 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.classData.setText(trConst(nwLabels.CLASS_NAME[nwItem.itemClass]))
|
||||
|
||||
@@ -275,7 +275,7 @@ class GuiItemDetails(QWidget):
|
||||
# ======
|
||||
|
||||
hLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
|
||||
usageIcon = self.theTheme.getItemIcon(
|
||||
usageIcon = self.mainTheme.getItemIcon(
|
||||
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
|
||||
)
|
||||
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
|
||||
@@ -296,7 +296,7 @@ class GuiItemDetails(QWidget):
|
||||
return
|
||||
|
||||
@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
|
||||
already showing. Otherwise, do nothing.
|
||||
"""
|
||||
|
||||
@@ -33,7 +33,7 @@ from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
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
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -41,13 +41,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiMainMenu(QMenuBar):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QMenuBar.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QMenuBar.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiMainMenu ...")
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
# Build Menu
|
||||
self._buildProjectMenu()
|
||||
@@ -61,11 +61,9 @@ class GuiMainMenu(QMenuBar):
|
||||
self._buildHelpMenu()
|
||||
|
||||
# Function Pointers
|
||||
self._docAction = self.theParent.passDocumentAction
|
||||
self._moveTreeItem = self.theParent.treeView.moveTreeItem
|
||||
self._newTreeItem = self.theParent.treeView.newTreeItem
|
||||
self._docInsert = self.theParent.docEditor.insertText
|
||||
self._insertKeyWord = self.theParent.docEditor.insertKeyWord
|
||||
self._docAction = self.mainGui.passDocumentAction
|
||||
self._docInsert = self.mainGui.docEditor.insertText
|
||||
self._insertKeyWord = self.mainGui.docEditor.insertKeyWord
|
||||
|
||||
logger.debug("GuiMainMenu initialisation complete")
|
||||
|
||||
@@ -96,7 +94,7 @@ class GuiMainMenu(QMenuBar):
|
||||
flag is handled by the document editor class, so we make no
|
||||
decision, just pass a None to the function and let it decide.
|
||||
"""
|
||||
self.theParent.docEditor.toggleSpellCheck(None)
|
||||
self.mainGui.docEditor.toggleSpellCheck(None)
|
||||
return True
|
||||
|
||||
def _openWebsite(self, theUrl):
|
||||
@@ -125,25 +123,25 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Project > New Project
|
||||
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)
|
||||
|
||||
# Project > Open Project
|
||||
self.aOpenProject = QAction(self.tr("Open Project"), self)
|
||||
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)
|
||||
|
||||
# Project > Save Project
|
||||
self.aSaveProject = QAction(self.tr("Save Project"), self)
|
||||
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)
|
||||
|
||||
# Project > Close Project
|
||||
self.aCloseProject = QAction(self.tr("Close Project"), self)
|
||||
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)
|
||||
|
||||
# Project > Separator
|
||||
@@ -152,78 +150,33 @@ class GuiMainMenu(QMenuBar):
|
||||
# Project > Project Settings
|
||||
self.aProjectSettings = QAction(self.tr("Project Settings"), self)
|
||||
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)
|
||||
|
||||
# Project > Project Details
|
||||
self.aProjectDetails = QAction(self.tr("Project Details"), self)
|
||||
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)
|
||||
|
||||
# Project > Separator
|
||||
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
|
||||
self.aEditItem = QAction(self.tr("Edit Item"), self)
|
||||
self.aEditItem.setShortcuts(["Ctrl+E", "F2"])
|
||||
self.aEditItem.triggered.connect(lambda: self.theParent.editItem(None))
|
||||
self.aEditItem = QAction(self.tr("Rename Item"), self)
|
||||
self.aEditItem.setShortcuts(["F2"])
|
||||
self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None))
|
||||
self.projMenu.addAction(self.aEditItem)
|
||||
|
||||
# Project > Delete
|
||||
self.aDeleteItem = QAction(self.tr("Delete Item"), self)
|
||||
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)
|
||||
|
||||
# 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
|
||||
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)
|
||||
|
||||
# Project > Separator
|
||||
@@ -233,7 +186,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aExitNW = QAction(self.tr("Exit"), self)
|
||||
self.aExitNW.setShortcut("Ctrl+Q")
|
||||
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)
|
||||
|
||||
return
|
||||
@@ -244,28 +197,22 @@ class GuiMainMenu(QMenuBar):
|
||||
# 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
|
||||
self.aOpenDoc = QAction(self.tr("Open Document"), self)
|
||||
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)
|
||||
|
||||
# Document > Save
|
||||
self.aSaveDoc = QAction(self.tr("Save Document"), self)
|
||||
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)
|
||||
|
||||
# Document > Close
|
||||
self.aCloseDoc = QAction(self.tr("Close Document"), self)
|
||||
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)
|
||||
|
||||
# Document > Separator
|
||||
@@ -274,13 +221,13 @@ class GuiMainMenu(QMenuBar):
|
||||
# Document > Preview
|
||||
self.aViewDoc = QAction(self.tr("View Document"), self)
|
||||
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)
|
||||
|
||||
# Document > Close Preview
|
||||
self.aCloseView = QAction(self.tr("Close Document View"), self)
|
||||
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)
|
||||
|
||||
# Document > Separator
|
||||
@@ -288,23 +235,23 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Document > Show File Details
|
||||
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)
|
||||
|
||||
# Document > Import From File
|
||||
self.aImportFile = QAction(self.tr("Import Text from File"), self)
|
||||
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)
|
||||
|
||||
# Document > Merge Documents
|
||||
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)
|
||||
|
||||
# Document > Split Document
|
||||
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)
|
||||
|
||||
return
|
||||
@@ -377,7 +324,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFocusTree.setShortcut("Ctrl+Alt+1")
|
||||
else:
|
||||
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)
|
||||
|
||||
# View > Document Pane 1
|
||||
@@ -386,7 +333,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFocusEditor.setShortcut("Ctrl+Alt+2")
|
||||
else:
|
||||
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)
|
||||
|
||||
# View > Document Pane 2
|
||||
@@ -395,7 +342,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFocusView.setShortcut("Ctrl+Alt+3")
|
||||
else:
|
||||
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)
|
||||
|
||||
# View > Outline
|
||||
@@ -404,7 +351,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFocusOutline.setShortcut("Ctrl+Alt+4")
|
||||
else:
|
||||
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)
|
||||
|
||||
# View > Separator
|
||||
@@ -413,13 +360,13 @@ class GuiMainMenu(QMenuBar):
|
||||
# View > Go Backward
|
||||
self.aViewPrev = QAction(self.tr("Navigate Backward"), self)
|
||||
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)
|
||||
|
||||
# View > Go Forward
|
||||
self.aViewNext = QAction(self.tr("Navigate Forward"), self)
|
||||
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)
|
||||
|
||||
# View > Separator
|
||||
@@ -429,14 +376,14 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFocusMode = QAction(self.tr("Focus Mode"), self)
|
||||
self.aFocusMode.setShortcut("F8")
|
||||
self.aFocusMode.setCheckable(True)
|
||||
self.aFocusMode.setChecked(self.theParent.isFocusMode)
|
||||
self.aFocusMode.triggered.connect(lambda: self.theParent.toggleFocusMode())
|
||||
self.aFocusMode.setChecked(self.mainGui.isFocusMode)
|
||||
self.aFocusMode.triggered.connect(lambda: self.mainGui.toggleFocusMode())
|
||||
self.viewMenu.addAction(self.aFocusMode)
|
||||
|
||||
# View > Toggle Full Screen
|
||||
self.aFullScreen = QAction(self.tr("Full Screen Mode"), self)
|
||||
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)
|
||||
|
||||
return
|
||||
@@ -641,7 +588,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Insert > Placeholder Text
|
||||
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)
|
||||
|
||||
return
|
||||
@@ -805,7 +752,7 @@ class GuiMainMenu(QMenuBar):
|
||||
# Search > Find
|
||||
self.aFind = QAction(self.tr("Find"), self)
|
||||
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)
|
||||
|
||||
# Search > Replace
|
||||
@@ -814,7 +761,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aReplace.setShortcut("Ctrl+=")
|
||||
else:
|
||||
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)
|
||||
|
||||
# Search > Find Next
|
||||
@@ -823,7 +770,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFindNext.setShortcuts(["Ctrl+G", "F3"])
|
||||
else:
|
||||
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)
|
||||
|
||||
# Search > Find Prev
|
||||
@@ -832,13 +779,13 @@ class GuiMainMenu(QMenuBar):
|
||||
self.aFindPrev.setShortcuts(["Ctrl+Shift+G", "Shift+F3"])
|
||||
else:
|
||||
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)
|
||||
|
||||
# Search > Replace Next
|
||||
self.aReplaceNext = QAction(self.tr("Replace Next"), self)
|
||||
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)
|
||||
|
||||
return
|
||||
@@ -860,12 +807,12 @@ class GuiMainMenu(QMenuBar):
|
||||
# Tools > Re-Run Spell Check
|
||||
self.aReRunSpell = QAction(self.tr("Re-Run Spell Check"), self)
|
||||
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)
|
||||
|
||||
# Tools > Project Word List
|
||||
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)
|
||||
|
||||
# Tools > Separator
|
||||
@@ -874,7 +821,7 @@ class GuiMainMenu(QMenuBar):
|
||||
# Tools > Rebuild Indices
|
||||
self.aRebuildIndex = QAction(self.tr("Rebuild Index"), self)
|
||||
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)
|
||||
|
||||
# Tools > Separator
|
||||
@@ -888,20 +835,20 @@ class GuiMainMenu(QMenuBar):
|
||||
# Tools > Export Project
|
||||
self.aBuildProject = QAction(self.tr("Build Novel Project"), self)
|
||||
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)
|
||||
|
||||
# Tools > Writing Stats
|
||||
self.aWritingStats = QAction(self.tr("Writing Statistics"), self)
|
||||
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)
|
||||
|
||||
# Tools > Settings
|
||||
self.aPreferences = QAction(self.tr("Preferences"), self)
|
||||
self.aPreferences.setShortcut("Ctrl+,")
|
||||
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)
|
||||
|
||||
return
|
||||
@@ -915,13 +862,13 @@ class GuiMainMenu(QMenuBar):
|
||||
# Help > About
|
||||
self.aAboutNW = QAction(self.tr("About novelWriter"), self)
|
||||
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)
|
||||
|
||||
# Help > About Qt5
|
||||
self.aAboutQt = QAction(self.tr("About Qt5"), self)
|
||||
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)
|
||||
|
||||
# Help > Separator
|
||||
@@ -968,7 +915,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Document > Check for Updates
|
||||
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)
|
||||
|
||||
return
|
||||
|
||||
@@ -4,7 +4,9 @@ novelWriter – GUI Novel Tree
|
||||
GUI classe for the main window novel tree
|
||||
|
||||
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
|
||||
Copyright 2018–2020, Veronica Berglyd Olsen
|
||||
@@ -26,84 +28,376 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
from enum import Enum
|
||||
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 (
|
||||
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.constants import nwKeyWords
|
||||
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
|
||||
|
||||
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):
|
||||
|
||||
C_TITLE = 0
|
||||
C_WORDS = 1
|
||||
C_POV = 2
|
||||
C_LAST = 2
|
||||
|
||||
def __init__(self, theParent):
|
||||
QTreeWidget.__init__(self, theParent)
|
||||
def __init__(self, novelView):
|
||||
QTreeWidget.__init__(self, novelView)
|
||||
|
||||
logger.debug("Initialising GuiNovelTree ...")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.novelView = novelView
|
||||
self.mainGui = novelView.mainGui
|
||||
self.mainTheme = novelView.mainGui.mainTheme
|
||||
self.theProject = novelView.mainGui.theProject
|
||||
|
||||
# Internal Variables
|
||||
self._treeMap = {}
|
||||
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
|
||||
iPx = self.theTheme.baseIconSize
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
# =========
|
||||
|
||||
iPx = self.mainTheme.baseIconSize
|
||||
cMg = self.mainConf.pxInt(6)
|
||||
|
||||
self.setIconSize(QSize(iPx, iPx))
|
||||
self.setIndentation(iPx)
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
self.setHeaderHidden(True)
|
||||
self.setIndentation(0)
|
||||
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.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.setExpandsOnDoubleClick(False)
|
||||
self.setDragEnabled(False)
|
||||
|
||||
treeHeadItem = self.headerItem()
|
||||
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"))
|
||||
|
||||
# Lock the column sizes
|
||||
treeHeader = self.header()
|
||||
treeHeader.setStretchLastSection(True)
|
||||
treeHeader.setMinimumSectionSize(iPx + 6)
|
||||
treeHeader.setStretchLastSection(False)
|
||||
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
|
||||
treeColWidth = self.mainConf.getNovelColWidths()
|
||||
if len(treeColWidth) <= 3:
|
||||
for colN, colW in enumerate(treeColWidth):
|
||||
self.setColumnWidth(colN, colW)
|
||||
# Pre-Generate Tree Formatting
|
||||
fH1 = self.font()
|
||||
fH1.setBold(True)
|
||||
fH1.setUnderline(True)
|
||||
|
||||
# The last column should just auto-scale
|
||||
self.resizeColumnToContents(self.C_POV)
|
||||
fH2 = self.font()
|
||||
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
|
||||
self.initTree()
|
||||
self.initSettings()
|
||||
|
||||
logger.debug("GuiNovelTree initialisation complete")
|
||||
|
||||
return
|
||||
|
||||
def initTree(self):
|
||||
def initSettings(self):
|
||||
"""Set or update tree widget settings.
|
||||
"""
|
||||
# Scroll bars
|
||||
@@ -119,11 +413,19 @@ class GuiNovelTree(QTreeWidget):
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def lastColType(self):
|
||||
return self._lastCol
|
||||
|
||||
##
|
||||
# Class Methods
|
||||
##
|
||||
|
||||
def clearTree(self):
|
||||
def clearContent(self):
|
||||
"""Clear the GUI content and the related maps.
|
||||
"""
|
||||
self.clear()
|
||||
@@ -131,12 +433,15 @@ class GuiNovelTree(QTreeWidget):
|
||||
self._lastBuild = 0
|
||||
return
|
||||
|
||||
def refreshTree(self, overRide=False):
|
||||
def refreshTree(self, rootHandle=None, overRide=False):
|
||||
"""Called whenever the Novel tab is activated.
|
||||
"""
|
||||
logger.verbose("Requesting refresh of the novel tree")
|
||||
treeChanged = self.theParent.treeView.changedSince(self._lastBuild)
|
||||
indexChanged = self.theProject.index.indexChangedSince(self._lastBuild)
|
||||
if rootHandle is None:
|
||||
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):
|
||||
logger.verbose("No changes have been made to the novel index")
|
||||
return
|
||||
@@ -146,7 +451,8 @@ class GuiNovelTree(QTreeWidget):
|
||||
if selItem:
|
||||
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:
|
||||
self._treeMap[titleKey].setSelected(True)
|
||||
@@ -162,15 +468,6 @@ class GuiNovelTree(QTreeWidget):
|
||||
self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}")
|
||||
return
|
||||
|
||||
def getColumnSizes(self):
|
||||
"""Return the column widths for the tree columns.
|
||||
"""
|
||||
retVals = [
|
||||
self.columnWidth(0),
|
||||
self.columnWidth(1),
|
||||
]
|
||||
return retVals
|
||||
|
||||
def getSelectedHandle(self):
|
||||
"""Get the currently selected handle. If multiple items are
|
||||
selected, return the first.
|
||||
@@ -184,6 +481,17 @@ class GuiNovelTree(QTreeWidget):
|
||||
|
||||
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
|
||||
##
|
||||
@@ -209,113 +517,94 @@ class GuiNovelTree(QTreeWidget):
|
||||
if tHandle is None:
|
||||
return
|
||||
|
||||
self.theParent.viewDocument(tHandle)
|
||||
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
|
||||
|
||||
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-
|
||||
clicked, and send it to the main gui class for opening in the
|
||||
document editor.
|
||||
"""
|
||||
tHandle, tLine = self.getSelectedHandle()
|
||||
self.theParent.openDocument(tHandle, tLine=tLine-1, doScroll=True)
|
||||
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)
|
||||
|
||||
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, tLine, "")
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _populateTree(self):
|
||||
def _populateTree(self, rootHandle):
|
||||
"""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
|
||||
currChapter = None
|
||||
currScene = None
|
||||
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
|
||||
for tKey, tHandle, sTitle, novIdx in novStruct:
|
||||
|
||||
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)
|
||||
self._treeMap[tKey] = tItem
|
||||
newItem = QTreeWidgetItem()
|
||||
theData = (tHandle, sTitle[1:].lstrip("0"), tKey)
|
||||
|
||||
tLevel = novIdx.level
|
||||
if tLevel == "H1":
|
||||
self.addTopLevelItem(tItem)
|
||||
currTitle = tItem
|
||||
currChapter = None
|
||||
currScene = None
|
||||
newItem.setData(self.C_TITLE, Qt.DecorationRole, self._pIndent[iLevel])
|
||||
newItem.setText(self.C_TITLE, novIdx.title)
|
||||
newItem.setData(self.C_TITLE, Qt.UserRole, theData)
|
||||
newItem.setFont(self.C_TITLE, self._hFonts[iLevel])
|
||||
newItem.setText(self.C_WORDS, f"{novIdx.wordCount:n}")
|
||||
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
|
||||
|
||||
elif tLevel == "H2":
|
||||
if currTitle is None:
|
||||
self.addTopLevelItem(tItem)
|
||||
else:
|
||||
currTitle.addChild(tItem)
|
||||
currChapter = tItem
|
||||
currScene = None
|
||||
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
|
||||
newItem.setText(self.C_LAST, lastText)
|
||||
if lastText:
|
||||
newItem.setToolTip(self.C_LAST, toolTip)
|
||||
|
||||
elif tLevel == "H3":
|
||||
if currChapter is None:
|
||||
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)
|
||||
self._treeMap[tKey] = newItem
|
||||
self.addTopLevelItem(newItem)
|
||||
|
||||
logger.verbose("Novel Tree built in %.3f ms", (time() - tStart)*1000)
|
||||
self._lastBuild = time()
|
||||
|
||||
return
|
||||
|
||||
def _createTreeItem(self, tHandle, sTitle, titleKey, novIdx):
|
||||
"""Populate a tree item with all the column values.
|
||||
def _getLastColumnText(self, tHandle, sTitle):
|
||||
"""Generate the text for the last column based on user settings.
|
||||
"""
|
||||
newItem = QTreeWidgetItem()
|
||||
hIcon = "doc_%s" % novIdx.level.lower()
|
||||
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)
|
||||
if self._lastCol == NovelTreeColumn.HIDDEN:
|
||||
return "", ""
|
||||
|
||||
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
|
||||
|
||||
@@ -4,9 +4,9 @@ novelWriter – GUI Project Outline
|
||||
GUI class for the project outline view
|
||||
|
||||
File History:
|
||||
Created: 2022-05-15 [1.7b1] GuiOutline
|
||||
Created: 2022-05-15 [1.7b1] GuiOutlineView
|
||||
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: 2020-06-02 [0.7.0] GuiOutlineDetails
|
||||
|
||||
@@ -52,22 +52,23 @@ from novelwriter.constants import trConst, nwKeyWords, nwLabels
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GuiOutline(QWidget):
|
||||
class GuiOutlineView(QWidget):
|
||||
|
||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWidget.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWidget.__init__(self, mainGui)
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.mainGui = mainGui
|
||||
|
||||
# Build GUI
|
||||
self.outlineBar = GuiOutlineToolBar(self)
|
||||
self.outlineView = GuiOutlineView(self)
|
||||
self.outlineTree = GuiOutlineTree(self)
|
||||
self.outlineData = GuiOutlineDetails(self)
|
||||
|
||||
self.splitOutline = QSplitter(Qt.Vertical)
|
||||
self.splitOutline.addWidget(self.outlineView)
|
||||
self.splitOutline.addWidget(self.outlineTree)
|
||||
self.splitOutline.addWidget(self.outlineData)
|
||||
self.splitOutline.setSizes(self.mainConf.getOutlinePanePos())
|
||||
|
||||
@@ -80,14 +81,14 @@ class GuiOutline(QWidget):
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
# Connect Signals
|
||||
self.outlineView.hiddenStateChanged.connect(self._updateMenuColumns)
|
||||
self.outlineView.activeItemChanged.connect(self.outlineData.showItem)
|
||||
self.outlineTree.hiddenStateChanged.connect(self._updateMenuColumns)
|
||||
self.outlineTree.activeItemChanged.connect(self.outlineData.showItem)
|
||||
self.outlineData.itemTagClicked.connect(self._tagClicked)
|
||||
self.outlineBar.loadNovelRootRequest.connect(self._rootItemChanged)
|
||||
self.outlineBar.viewColumnToggled.connect(self.outlineView.menuColumnToggled)
|
||||
self.outlineBar.viewColumnToggled.connect(self.outlineTree.menuColumnToggled)
|
||||
|
||||
# Function Mappings
|
||||
self.getSelectedHandle = self.outlineView.getSelectedHandle
|
||||
self.getSelectedHandle = self.outlineTree.getSelectedHandle
|
||||
|
||||
return
|
||||
|
||||
@@ -103,24 +104,24 @@ class GuiOutline(QWidget):
|
||||
return
|
||||
|
||||
def initOutline(self):
|
||||
self.outlineView.initOutline()
|
||||
self.outlineTree.initOutline()
|
||||
self.outlineData.initDetails()
|
||||
return
|
||||
|
||||
def closeOutline(self):
|
||||
self.outlineView.closeOutline()
|
||||
self.outlineTree.closeOutline()
|
||||
self.outlineData.updateClasses()
|
||||
return
|
||||
|
||||
def refreshView(self, overRide=False, novelChanged=False):
|
||||
self.outlineView.refreshTree(overRide=overRide, novelChanged=novelChanged)
|
||||
self.outlineTree.refreshTree(overRide=overRide, novelChanged=novelChanged)
|
||||
return
|
||||
|
||||
def treeFocus(self):
|
||||
return self.outlineView.hasFocus()
|
||||
def treeHasFocus(self):
|
||||
return self.outlineTree.hasFocus()
|
||||
|
||||
def setTreeFocus(self):
|
||||
return self.outlineView.setFocus()
|
||||
return self.outlineTree.setFocus()
|
||||
|
||||
##
|
||||
# Public Slots
|
||||
@@ -144,7 +145,7 @@ class GuiOutline(QWidget):
|
||||
checkboxes whenever a signal is received that the hidden state
|
||||
of columns has changed.
|
||||
"""
|
||||
self.outlineBar.setColumnHiddenState(self.outlineView.hiddenColumns)
|
||||
self.outlineBar.setColumnHiddenState(self.outlineTree.hiddenColumns)
|
||||
return
|
||||
|
||||
@pyqtSlot(str)
|
||||
@@ -159,10 +160,10 @@ class GuiOutline(QWidget):
|
||||
def _rootItemChanged(self, handle):
|
||||
"""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
|
||||
|
||||
# END Class GuiOutline
|
||||
# END Class GuiOutlineView
|
||||
|
||||
|
||||
class GuiOutlineToolBar(QToolBar):
|
||||
@@ -176,9 +177,9 @@ class GuiOutlineToolBar(QToolBar):
|
||||
logger.debug("Initialising GuiOutlineToolBar ...")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theOutline.theParent
|
||||
self.theProject = theOutline.theParent.theProject
|
||||
self.theTheme = theOutline.theParent.theTheme
|
||||
self.mainGui = theOutline.mainGui
|
||||
self.theProject = theOutline.mainGui.theProject
|
||||
self.mainTheme = theOutline.mainGui.mainTheme
|
||||
|
||||
iPx = self.mainConf.pxInt(22)
|
||||
mPx = self.mainConf.pxInt(12)
|
||||
@@ -201,7 +202,7 @@ class GuiOutlineToolBar(QToolBar):
|
||||
|
||||
# Actions
|
||||
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)
|
||||
|
||||
# Column Menu
|
||||
@@ -211,7 +212,7 @@ class GuiOutlineToolBar(QToolBar):
|
||||
)
|
||||
|
||||
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.setPopupMode(QToolButton.InstantPopup)
|
||||
|
||||
@@ -225,6 +226,8 @@ class GuiOutlineToolBar(QToolBar):
|
||||
|
||||
logger.debug("GuiOutlineToolBar initialisation complete")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
@@ -233,7 +236,7 @@ class GuiOutlineToolBar(QToolBar):
|
||||
"""Fill the novel combo box with a list of all novel folders.
|
||||
"""
|
||||
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):
|
||||
self.novelValue.addItem(tIcon, nwItem.itemName, tHandle)
|
||||
self.novelValue.insertSeparator(self.novelValue.count())
|
||||
@@ -268,7 +271,7 @@ class GuiOutlineToolBar(QToolBar):
|
||||
# END Class GuiOutlineToolBar
|
||||
|
||||
|
||||
class GuiOutlineView(QTreeWidget):
|
||||
class GuiOutlineTree(QTreeWidget):
|
||||
|
||||
DEF_WIDTH = {
|
||||
nwOutline.TITLE: 200,
|
||||
@@ -316,12 +319,12 @@ class GuiOutlineView(QTreeWidget):
|
||||
def __init__(self, theOutline):
|
||||
QTreeWidget.__init__(self, theOutline)
|
||||
|
||||
logger.debug("Initialising GuiOutlineView ...")
|
||||
logger.debug("Initialising GuiOutlineTree ...")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theOutline.theParent
|
||||
self.theProject = theOutline.theParent.theProject
|
||||
self.theTheme = theOutline.theParent.theTheme
|
||||
self.mainGui = theOutline.mainGui
|
||||
self.theProject = theOutline.mainGui.theProject
|
||||
self.mainTheme = theOutline.mainGui.mainTheme
|
||||
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
@@ -331,7 +334,7 @@ class GuiOutlineView(QTreeWidget):
|
||||
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||
self.itemSelectionChanged.connect(self._itemSelected)
|
||||
|
||||
iPx = self.theTheme.baseIconSize
|
||||
iPx = self.mainTheme.baseIconSize
|
||||
self.setIconSize(QSize(iPx, iPx))
|
||||
self.setIndentation(iPx)
|
||||
|
||||
@@ -352,7 +355,7 @@ class GuiOutlineView(QTreeWidget):
|
||||
|
||||
self.hiddenStateChanged.emit()
|
||||
|
||||
logger.debug("GuiOutlineView initialisation complete")
|
||||
logger.debug("GuiOutlineTree initialisation complete")
|
||||
|
||||
return
|
||||
|
||||
@@ -461,7 +464,7 @@ class GuiOutlineView(QTreeWidget):
|
||||
document editor.
|
||||
"""
|
||||
tHandle, tLine = self.getSelectedHandle()
|
||||
self.theParent.openDocument(tHandle, tLine=tLine - 1, doScroll=True)
|
||||
self.mainGui.openDocument(tHandle, tLine=tLine - 1, doScroll=True)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -679,7 +682,7 @@ class GuiOutlineView(QTreeWidget):
|
||||
hIcon = "doc_%s" % novIdx.level.lower()
|
||||
|
||||
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)
|
||||
wC = int(novIdx.wordCount)
|
||||
@@ -687,7 +690,7 @@ class GuiOutlineView(QTreeWidget):
|
||||
|
||||
newItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title)
|
||||
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.LABEL], nwItem.itemName)
|
||||
newItem.setIcon(self._colIdx[nwOutline.LABEL], dIcon)
|
||||
@@ -714,7 +717,7 @@ class GuiOutlineView(QTreeWidget):
|
||||
|
||||
return newItem
|
||||
|
||||
# END Class GuiOutlineView
|
||||
# END Class GuiOutlineTree
|
||||
|
||||
|
||||
class GuiOutlineHeaderMenu(QMenu):
|
||||
@@ -779,14 +782,14 @@ class GuiOutlineDetails(QScrollArea):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theOutline = theOutline
|
||||
self.theParent = theOutline.theParent
|
||||
self.theProject = theOutline.theParent.theProject
|
||||
self.theTheme = theOutline.theParent.theTheme
|
||||
self.mainGui = theOutline.mainGui
|
||||
self.theProject = theOutline.mainGui.theProject
|
||||
self.mainTheme = theOutline.mainGui.mainTheme
|
||||
|
||||
# Sizes
|
||||
minTitle = 30*self.theTheme.textNWidth
|
||||
maxTitle = 40*self.theTheme.textNWidth
|
||||
wCount = self.theTheme.getTextWidth("999,999")
|
||||
minTitle = 30*self.mainTheme.textNWidth
|
||||
maxTitle = 40*self.mainTheme.textNWidth
|
||||
wCount = self.mainTheme.getTextWidth("999,999")
|
||||
hSpace = int(self.mainConf.pxInt(10))
|
||||
vSpace = int(self.mainConf.pxInt(4))
|
||||
|
||||
|
||||
@@ -41,22 +41,22 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiMainStatus(QStatusBar):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QStatusBar.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QStatusBar.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiMainStatus ...")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.refTime = None
|
||||
self.userIdle = False
|
||||
|
||||
colNone = QColor(*self.theTheme.statNone)
|
||||
colTrue = QColor(*self.theTheme.statUnsaved)
|
||||
colFalse = QColor(*self.theTheme.statSaved)
|
||||
colNone = QColor(*self.mainTheme.statNone)
|
||||
colTrue = QColor(*self.mainTheme.statUnsaved)
|
||||
colFalse = QColor(*self.mainTheme.statSaved)
|
||||
|
||||
iPx = self.theTheme.baseIconSize
|
||||
iPx = self.mainTheme.baseIconSize
|
||||
|
||||
# Permanent Widgets
|
||||
# =================
|
||||
@@ -66,7 +66,7 @@ class GuiMainStatus(QStatusBar):
|
||||
# The Spell Checker Language
|
||||
self.langIcon = QLabel("")
|
||||
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.langText.setContentsMargins(0, 0, xM, 0)
|
||||
self.addPermanentWidget(self.langIcon)
|
||||
@@ -91,7 +91,7 @@ class GuiMainStatus(QStatusBar):
|
||||
# The Project and Session Stats
|
||||
self.statsIcon = 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.statsText.setContentsMargins(0, 0, xM, 0)
|
||||
self.addPermanentWidget(self.statsIcon)
|
||||
@@ -99,14 +99,14 @@ class GuiMainStatus(QStatusBar):
|
||||
|
||||
# The Session Clock
|
||||
# Set the mimimum width so the label doesn't rescale every second
|
||||
self.timePixmap = self.theTheme.getPixmap("status_time", (iPx, iPx))
|
||||
self.idlePixmap = self.theTheme.getPixmap("status_idle", (iPx, iPx))
|
||||
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
|
||||
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
|
||||
|
||||
self.timeIcon = QLabel()
|
||||
self.timeText = QLabel("")
|
||||
self.timeIcon.setPixmap(self.timePixmap)
|
||||
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.timeText.setContentsMargins(0, 0, 0, 0)
|
||||
self.addPermanentWidget(self.timeIcon)
|
||||
|
||||
@@ -54,7 +54,7 @@ class GuiTheme:
|
||||
def __init__(self):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theIcons = GuiIcons(self)
|
||||
self.iconCache = GuiIcons(self)
|
||||
|
||||
# Loaded Theme Settings
|
||||
# =====================
|
||||
@@ -127,13 +127,13 @@ class GuiTheme:
|
||||
|
||||
self.updateFont()
|
||||
self.updateTheme()
|
||||
self.theIcons.updateTheme()
|
||||
self.iconCache.updateTheme()
|
||||
|
||||
# Icon Functions
|
||||
self.getIcon = self.theIcons.getIcon
|
||||
self.getPixmap = self.theIcons.getPixmap
|
||||
self.getItemIcon = self.theIcons.getItemIcon
|
||||
self.loadDecoration = self.theIcons.loadDecoration
|
||||
self.getIcon = self.iconCache.getIcon
|
||||
self.getPixmap = self.iconCache.getPixmap
|
||||
self.getItemIcon = self.iconCache.getItemIcon
|
||||
self.loadDecoration = self.iconCache.loadDecoration
|
||||
|
||||
# Extract Other Info
|
||||
self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
|
||||
@@ -472,16 +472,19 @@ class GuiIcons:
|
||||
# Switches
|
||||
"sticky-on", "sticky-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",
|
||||
}
|
||||
|
||||
def __init__(self, theTheme):
|
||||
def __init__(self, mainTheme):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theTheme = theTheme
|
||||
self.mainTheme = mainTheme
|
||||
|
||||
# Storage
|
||||
self._qIcons = {}
|
||||
@@ -573,19 +576,22 @@ class GuiIcons:
|
||||
# Access Functions
|
||||
##
|
||||
|
||||
def loadDecoration(self, decoKey, pxW, pxH):
|
||||
def loadDecoration(self, decoKey, pxW=None, pxH=None):
|
||||
"""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)
|
||||
return QPixmap()
|
||||
|
||||
imgPath = os.path.join(
|
||||
self.mainConf.assetPath, "images", self.DECO_MAP[decoKey]
|
||||
)
|
||||
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()
|
||||
|
||||
theDeco = QPixmap(imgPath)
|
||||
|
||||
@@ -40,21 +40,21 @@ class GuiViewsBar(QToolBar):
|
||||
|
||||
viewChangeRequested = pyqtSignal(nwView)
|
||||
|
||||
def __init__(self, theParent):
|
||||
QToolBar.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QToolBar.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiViewsBar ...")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
# Style
|
||||
iPx = self.mainConf.pxInt(22)
|
||||
mPx = self.mainConf.pxInt(60)
|
||||
|
||||
lblFont = self.theTheme.guiFont
|
||||
lblFont.setPointSizeF(0.65*self.theTheme.fontPointSize)
|
||||
lblFont = self.mainTheme.guiFont
|
||||
lblFont.setPointSizeF(0.65*self.mainTheme.fontPointSize)
|
||||
|
||||
self.setMovable(False)
|
||||
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||
@@ -70,54 +70,54 @@ class GuiViewsBar(QToolBar):
|
||||
self.aProject = QAction(self.tr("Project"))
|
||||
self.aProject.setFont(lblFont)
|
||||
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.aNovel = QAction(self.tr("Novel"))
|
||||
self.aNovel.setFont(lblFont)
|
||||
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.aOutline = QAction(self.tr("Outline"))
|
||||
self.aOutline.setFont(lblFont)
|
||||
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.aBuild = QAction(self.tr("Build"))
|
||||
self.aBuild.setFont(lblFont)
|
||||
self.aBuild.setToolTip(self.tr("Build novel project"))
|
||||
self.aBuild.setIcon(self.theTheme.getIcon("view_build"))
|
||||
self.aBuild.triggered.connect(lambda: self.theParent.showBuildProjectDialog())
|
||||
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
|
||||
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
||||
|
||||
self.aDetails = QAction(self.tr("Details"))
|
||||
self.aDetails.setFont(lblFont)
|
||||
self.aDetails.setToolTip(self.tr("Show project details"))
|
||||
self.aDetails.setIcon(self.theTheme.getIcon("proj_details"))
|
||||
self.aDetails.triggered.connect(lambda: self.theParent.showProjectDetailsDialog())
|
||||
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
|
||||
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
|
||||
|
||||
self.aStats = QAction(self.tr("Stats"))
|
||||
self.aStats.setFont(lblFont)
|
||||
self.aStats.setToolTip(self.tr("Show project statistics"))
|
||||
self.aStats.setIcon(self.theTheme.getIcon("proj_stats"))
|
||||
self.aStats.triggered.connect(lambda: self.theParent.showWritingStatsDialog())
|
||||
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
|
||||
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
||||
|
||||
# Settings Menu
|
||||
self.mSettings = QMenu()
|
||||
|
||||
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.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.tbSettings = QToolButton(self)
|
||||
self.tbSettings.setFont(lblFont)
|
||||
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.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||
self.tbSettings.setPopupMode(QToolButton.InstantPopup)
|
||||
|
||||
@@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
|
||||
|
||||
from novelwriter.gui import (
|
||||
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
|
||||
GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectTree, GuiTheme,
|
||||
GuiMainStatus, GuiNovelView, GuiOutlineView, GuiProjectView, GuiTheme,
|
||||
GuiViewsBar
|
||||
)
|
||||
from novelwriter.dialogs import (
|
||||
@@ -84,7 +84,7 @@ class GuiMain(QMainWindow):
|
||||
# ============
|
||||
|
||||
# Core Classes and Settings
|
||||
self.theTheme = GuiTheme()
|
||||
self.mainTheme = GuiTheme()
|
||||
self.theProject = NWProject(self)
|
||||
self.hasProject = False
|
||||
self.isFocusMode = False
|
||||
@@ -104,42 +104,20 @@ class GuiMain(QMainWindow):
|
||||
hWd = self.mainConf.pxInt(4)
|
||||
|
||||
# Main GUI Elements
|
||||
self.statusBar = GuiMainStatus(self)
|
||||
self.treeView = GuiProjectTree(self)
|
||||
self.novelView = GuiNovelTree(self)
|
||||
self.docEditor = GuiDocEditor(self)
|
||||
self.viewMeta = GuiDocViewDetails(self)
|
||||
self.docViewer = GuiDocViewer(self)
|
||||
self.treeMeta = GuiItemDetails(self)
|
||||
self.projView = GuiOutline(self)
|
||||
self.mainMenu = GuiMainMenu(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)
|
||||
self.statusBar = GuiMainStatus(self)
|
||||
self.projView = GuiProjectView(self)
|
||||
self.novelView = GuiNovelView(self)
|
||||
self.docEditor = GuiDocEditor(self)
|
||||
self.viewMeta = GuiDocViewDetails(self)
|
||||
self.docViewer = GuiDocViewer(self)
|
||||
self.itemDetails = GuiItemDetails(self)
|
||||
self.outlineView = GuiOutlineView(self)
|
||||
self.mainMenu = GuiMainMenu(self)
|
||||
self.viewsBar = GuiViewsBar(self)
|
||||
|
||||
# Project Tree Stack
|
||||
self.projStack = QStackedWidget()
|
||||
self.projStack.addWidget(self.treeView)
|
||||
self.projStack.addWidget(self.projView)
|
||||
self.projStack.addWidget(self.novelView)
|
||||
self.projStack.currentChanged.connect(self._projStackChanged)
|
||||
|
||||
@@ -149,7 +127,7 @@ class GuiMain(QMainWindow):
|
||||
self.treeBox.setContentsMargins(0, 0, 0, 0)
|
||||
self.treeBox.setSpacing(mPx)
|
||||
self.treeBox.addWidget(self.projStack)
|
||||
self.treeBox.addWidget(self.treeMeta)
|
||||
self.treeBox.addWidget(self.itemDetails)
|
||||
self.treePane.setLayout(self.treeBox)
|
||||
|
||||
# Splitter : Document Viewer / Document Meta
|
||||
@@ -176,7 +154,7 @@ class GuiMain(QMainWindow):
|
||||
# Main Stack : Editor / Outline
|
||||
self.mainStack = QStackedWidget()
|
||||
self.mainStack.addWidget(self.splitMain)
|
||||
self.mainStack.addWidget(self.projView)
|
||||
self.mainStack.addWidget(self.outlineView)
|
||||
self.mainStack.currentChanged.connect(self._mainStackChanged)
|
||||
|
||||
# Indices of Splitter Widgets
|
||||
@@ -189,15 +167,15 @@ class GuiMain(QMainWindow):
|
||||
|
||||
# Indices of Tab Widgets
|
||||
self.idxEditorView = self.mainStack.indexOf(self.splitMain)
|
||||
self.idxOutlineView = self.mainStack.indexOf(self.projView)
|
||||
self.idxTreeView = self.projStack.indexOf(self.treeView)
|
||||
self.idxOutlineView = self.mainStack.indexOf(self.outlineView)
|
||||
self.idxProjView = self.projStack.indexOf(self.projView)
|
||||
self.idxNovelView = self.projStack.indexOf(self.novelView)
|
||||
|
||||
# Splitter Behaviour
|
||||
self.splitMain.setCollapsible(self.idxTree, False)
|
||||
self.splitMain.setCollapsible(self.idxMain, 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.idxViewMeta, False)
|
||||
|
||||
@@ -214,6 +192,34 @@ class GuiMain(QMainWindow):
|
||||
self.setStatusBar(self.statusBar)
|
||||
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
|
||||
# =======================
|
||||
|
||||
@@ -289,15 +295,15 @@ class GuiMain(QMainWindow):
|
||||
"""Wrapper function to clear all sub-elements of the main GUI.
|
||||
"""
|
||||
# Project Area
|
||||
self.treeView.clearTree()
|
||||
self.novelView.clearTree()
|
||||
self.treeMeta.clearDetails()
|
||||
self.projView.clearProject()
|
||||
self.novelView.clearProject()
|
||||
self.itemDetails.clearDetails()
|
||||
|
||||
# Work Area
|
||||
self.docEditor.clearEditor()
|
||||
self.docEditor.setDictionaries()
|
||||
self.closeDocViewer()
|
||||
self.projView.clearOutline()
|
||||
self.outlineView.clearOutline()
|
||||
|
||||
# General
|
||||
self.statusBar.clearStatus()
|
||||
@@ -359,7 +365,8 @@ class GuiMain(QMainWindow):
|
||||
self.rebuildTrees()
|
||||
self.saveProject()
|
||||
self.docEditor.setDictionaries()
|
||||
self.projView.updateRootItem(None)
|
||||
self.outlineView.updateRootItem(None)
|
||||
self.novelView.openProjectTasks()
|
||||
self.rebuildIndex(beQuiet=True)
|
||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
||||
self.statusBar.setProjectStatus(nwState.GOOD)
|
||||
@@ -415,7 +422,8 @@ class GuiMain(QMainWindow):
|
||||
if saveOK:
|
||||
self.closeDocument()
|
||||
self.docViewer.clearNavHistory()
|
||||
self.projView.closeOutline()
|
||||
self.outlineView.closeOutline()
|
||||
self.novelView.closeProjectTasks()
|
||||
|
||||
self.theProject.closeProject(self.idleTime)
|
||||
self.idleRefTime = time()
|
||||
@@ -506,7 +514,8 @@ class GuiMain(QMainWindow):
|
||||
self.docEditor.setDictionaries()
|
||||
self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
|
||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
||||
self.projView.updateRootItem(None)
|
||||
self.outlineView.updateRootItem(None)
|
||||
self.novelView.openProjectTasks()
|
||||
self._updateStatusWordCount()
|
||||
|
||||
# Restore previously open documents, if any
|
||||
@@ -539,7 +548,7 @@ class GuiMain(QMainWindow):
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.projView.saveProjectTree()
|
||||
if self.theProject.saveProject(autoSave=autoSave):
|
||||
self.theProject.index.saveIndex()
|
||||
|
||||
@@ -584,7 +593,7 @@ class GuiMain(QMainWindow):
|
||||
if changeFocus:
|
||||
self.docEditor.setFocus()
|
||||
self.theProject.setLastEdited(tHandle)
|
||||
self.treeView.setSelectedHandle(tHandle, doScroll=doScroll)
|
||||
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -650,7 +659,7 @@ class GuiMain(QMainWindow):
|
||||
self.saveDocument()
|
||||
else:
|
||||
logger.verbose("Trying selected document")
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
tHandle = self.projView.getSelectedHandle()
|
||||
|
||||
if tHandle is None:
|
||||
logger.verbose("Trying last viewed document")
|
||||
@@ -787,12 +796,12 @@ class GuiMain(QMainWindow):
|
||||
|
||||
tHandle = None
|
||||
tLine = None
|
||||
if self.treeView.hasFocus():
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
elif self.novelView.hasFocus():
|
||||
if self.projView.treeHasFocus():
|
||||
tHandle = self.projView.getSelectedHandle()
|
||||
elif self.novelView.treeHasFocus():
|
||||
tHandle, tLine = self.novelView.getSelectedHandle()
|
||||
elif self.projView.treeFocus():
|
||||
tHandle, tLine = self.projView.getSelectedHandle()
|
||||
elif self.outlineView.treeHasFocus():
|
||||
tHandle, tLine = self.outlineView.getSelectedHandle()
|
||||
else:
|
||||
logger.warning("No item selected")
|
||||
return False
|
||||
@@ -802,7 +811,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return True
|
||||
|
||||
def editItem(self, tHandle=None):
|
||||
def editItemLabel(self, tHandle=None):
|
||||
"""Open the edit item dialog.
|
||||
"""
|
||||
if not self.hasProject:
|
||||
@@ -813,17 +822,17 @@ class GuiMain(QMainWindow):
|
||||
if self.docEditor.anyFocus() or self.isFocusMode:
|
||||
tHandle = self.docEditor.docHandle()
|
||||
else:
|
||||
tHandle = self.treeView.getSelectedHandle()
|
||||
tHandle = self.projView.getSelectedHandle()
|
||||
if tHandle:
|
||||
return self.treeView.editTreeItem(tHandle)
|
||||
return self.projView.renameTreeItem(tHandle)
|
||||
|
||||
return False
|
||||
|
||||
def rebuildTrees(self):
|
||||
"""Rebuild the project tree.
|
||||
"""
|
||||
self.treeView.buildTree()
|
||||
self.novelView.refreshTree()
|
||||
self.projView.populateTree()
|
||||
# self.novelView.refreshTree()
|
||||
return
|
||||
|
||||
def requestNovelTreeRefresh(self):
|
||||
@@ -845,7 +854,7 @@ class GuiMain(QMainWindow):
|
||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
tStart = time()
|
||||
|
||||
self.treeView.saveTreeOrder()
|
||||
self.projView.saveProjectTree()
|
||||
self.theProject.index.clearIndex()
|
||||
|
||||
for tItem in self.theProject.tree:
|
||||
@@ -855,8 +864,8 @@ class GuiMain(QMainWindow):
|
||||
logger.verbose("Indexing '%s'", tItem.itemName)
|
||||
if self.theProject.index.reIndexHandle(tItem.itemHandle):
|
||||
# Update Word Counts
|
||||
self.treeView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True)
|
||||
self.treeView.setTreeItemValues(tItem.itemHandle)
|
||||
self.projView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True)
|
||||
self.projView.setTreeItemValues(tItem.itemHandle)
|
||||
|
||||
tEnd = time()
|
||||
self.setStatus(
|
||||
@@ -913,13 +922,13 @@ class GuiMain(QMainWindow):
|
||||
if dlgConf.result() == QDialog.Accepted:
|
||||
logger.debug("Applying new preferences")
|
||||
self.initMain()
|
||||
self.theTheme.updateTheme()
|
||||
self.mainTheme.updateTheme()
|
||||
self.saveDocument()
|
||||
self.docEditor.initEditor()
|
||||
self.docViewer.initViewer()
|
||||
self.treeView.initTree()
|
||||
self.novelView.initTree()
|
||||
self.projView.initOutline()
|
||||
self.projView.initSettings()
|
||||
self.novelView.initSettings()
|
||||
self.outlineView.initOutline()
|
||||
self._updateStatusWordCount()
|
||||
|
||||
return
|
||||
@@ -938,7 +947,7 @@ class GuiMain(QMainWindow):
|
||||
logger.debug("Applying new project settings")
|
||||
if dlgProj.spellChanged:
|
||||
self.docEditor.setDictionaries()
|
||||
self.treeMeta.refreshDetails()
|
||||
self.itemDetails.refreshDetails()
|
||||
self._updateWindowTitle(self.theProject.projName)
|
||||
|
||||
return True
|
||||
@@ -1154,13 +1163,11 @@ class GuiMain(QMainWindow):
|
||||
if not self.isFocusMode:
|
||||
self.mainConf.setMainPanePos(self.splitMain.sizes())
|
||||
self.mainConf.setDocPanePos(self.splitDocs.sizes())
|
||||
self.mainConf.setOutlinePanePos(self.projView.splitSizes())
|
||||
self.mainConf.setOutlinePanePos(self.outlineView.splitSizes())
|
||||
if self.viewMeta.isVisible():
|
||||
self.mainConf.setViewPanePos(self.splitView.sizes())
|
||||
|
||||
self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
|
||||
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
|
||||
self.mainConf.setNovelColWidths(self.novelView.getColumnSizes())
|
||||
if not self.mainConf.isFullScreen:
|
||||
self.mainConf.setWinSize(self.width(), self.height())
|
||||
|
||||
@@ -1179,8 +1186,8 @@ class GuiMain(QMainWindow):
|
||||
"""
|
||||
if paneNo == nwWidget.TREE:
|
||||
tabIdx = self.projStack.currentIndex()
|
||||
if tabIdx == self.idxTreeView:
|
||||
self.treeView.setFocus()
|
||||
if tabIdx == self.idxProjView:
|
||||
self.projView.setFocus()
|
||||
elif tabIdx == self.idxNovelView:
|
||||
self.novelView.setFocus()
|
||||
elif paneNo == nwWidget.EDITOR:
|
||||
@@ -1191,7 +1198,7 @@ class GuiMain(QMainWindow):
|
||||
self.docViewer.setFocus()
|
||||
elif paneNo == nwWidget.OUTLINE:
|
||||
self._changeView(nwView.OUTLINE)
|
||||
self.projView.setTreeFocus()
|
||||
self.outlineView.setTreeFocus()
|
||||
return
|
||||
|
||||
def closeDocEditor(self):
|
||||
@@ -1468,6 +1475,17 @@ class GuiMain(QMainWindow):
|
||||
self.viewDocument(tHandle=tHandle, tAnchor=f"#{sTitle}")
|
||||
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)
|
||||
def _changeView(self, view):
|
||||
"""Handle the requested change of view from the GuiViewBar.
|
||||
@@ -1478,14 +1496,14 @@ class GuiMain(QMainWindow):
|
||||
|
||||
elif view == nwView.PROJECT:
|
||||
self.mainStack.setCurrentWidget(self.splitMain)
|
||||
self.projStack.setCurrentWidget(self.treeView)
|
||||
self.projStack.setCurrentWidget(self.projView)
|
||||
|
||||
elif view == nwView.NOVEL:
|
||||
self.mainStack.setCurrentWidget(self.splitMain)
|
||||
self.projStack.setCurrentWidget(self.novelView)
|
||||
|
||||
elif view == nwView.OUTLINE:
|
||||
self.mainStack.setCurrentWidget(self.projView)
|
||||
self.mainStack.setCurrentWidget(self.outlineView)
|
||||
|
||||
return
|
||||
|
||||
@@ -1531,32 +1549,6 @@ class GuiMain(QMainWindow):
|
||||
|
||||
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()
|
||||
def _treeNovelItemChanged(self):
|
||||
"""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:
|
||||
logger.verbose("Novel tree changed while Outline tab active")
|
||||
if self.hasProject:
|
||||
self.projView.refreshView(novelChanged=True)
|
||||
self.outlineView.refreshView(novelChanged=True)
|
||||
|
||||
return
|
||||
|
||||
@@ -1590,35 +1582,35 @@ class GuiMain(QMainWindow):
|
||||
return
|
||||
|
||||
@pyqtSlot(int)
|
||||
def _mainStackChanged(self, tabIndex):
|
||||
def _mainStackChanged(self, stIndex):
|
||||
"""Activated when the main window tab is changed.
|
||||
"""
|
||||
if tabIndex == self.idxEditorView:
|
||||
logger.verbose("Editor tab activated")
|
||||
elif tabIndex == self.idxOutlineView:
|
||||
logger.verbose("Project outline tab activated")
|
||||
if stIndex == self.idxEditorView:
|
||||
logger.verbose("Editor View activated")
|
||||
elif stIndex == self.idxOutlineView:
|
||||
logger.verbose("Outline View activated")
|
||||
if self.hasProject:
|
||||
self.projView.refreshView()
|
||||
self.outlineView.refreshView()
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot(int)
|
||||
def _projStackChanged(self, tabIndex):
|
||||
def _projStackChanged(self, stIndex):
|
||||
"""Activated when the project view tab is changed.
|
||||
"""
|
||||
sHandle = None
|
||||
|
||||
if tabIndex == self.idxTreeView:
|
||||
logger.verbose("Project tree tab activated")
|
||||
sHandle = self.treeView.getSelectedHandle()
|
||||
if stIndex == self.idxProjView:
|
||||
logger.verbose("Project Tree View activated")
|
||||
sHandle = self.projView.getSelectedHandle()
|
||||
|
||||
elif tabIndex == self.idxNovelView:
|
||||
logger.verbose("Novel tree tab activated")
|
||||
elif stIndex == self.idxNovelView:
|
||||
logger.verbose("Novel Tree View activated")
|
||||
if self.hasProject:
|
||||
self.novelView.refreshTree()
|
||||
sHandle, _ = self.novelView.getSelectedHandle()
|
||||
|
||||
self.treeMeta.updateViewBox(sHandle)
|
||||
self.itemDetails.updateViewBox(sHandle)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -65,16 +65,16 @@ class GuiBuildNovel(QDialog):
|
||||
FMT_JSON_H = 8 # HTML5 wrapped in JSON
|
||||
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiBuildNovel ...")
|
||||
self.setObjectName("GuiBuildNovel")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.htmlText = [] # List of html documents
|
||||
self.htmlStyle = [] # List of html styles
|
||||
@@ -93,7 +93,7 @@ class GuiBuildNovel(QDialog):
|
||||
|
||||
self.docView = GuiBuildNovelDocView(self, self.theProject)
|
||||
|
||||
hS = self.theTheme.fontPixelSize
|
||||
hS = self.mainTheme.fontPixelSize
|
||||
wS = 2*hS
|
||||
|
||||
# Title Formats
|
||||
@@ -238,11 +238,11 @@ class GuiBuildNovel(QDialog):
|
||||
pOptions.getString("GuiBuildNovel", "textFont", self.mainConf.textFont)
|
||||
)
|
||||
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.textSize = QSpinBox(self)
|
||||
self.textSize.setFixedWidth(6*self.theTheme.textNWidth)
|
||||
self.textSize.setFixedWidth(6*self.mainTheme.textNWidth)
|
||||
self.textSize.setMinimum(6)
|
||||
self.textSize.setMaximum(72)
|
||||
self.textSize.setSingleStep(1)
|
||||
@@ -251,7 +251,7 @@ class GuiBuildNovel(QDialog):
|
||||
)
|
||||
|
||||
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.setMaximum(3.0)
|
||||
self.lineHeight.setSingleStep(0.05)
|
||||
@@ -711,7 +711,7 @@ class GuiBuildNovel(QDialog):
|
||||
bldObj.initDocument()
|
||||
|
||||
# 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.setValue(0)
|
||||
@@ -760,7 +760,7 @@ class GuiBuildNovel(QDialog):
|
||||
logger.debug("Built project in %.3f ms", 1000*(tEnd - tStart))
|
||||
|
||||
if bldObj.errData:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("There were problems when building the project:")
|
||||
] + bldObj.errData, nwAlert.ERROR)
|
||||
|
||||
@@ -1003,11 +1003,11 @@ class GuiBuildNovel(QDialog):
|
||||
# ==============
|
||||
|
||||
if wSuccess:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("{0} file successfully written to:").format(textFmt), savePath
|
||||
], nwAlert.INFO)
|
||||
else:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to write {0} file. {1}"
|
||||
).format(textFmt, errMsg), nwAlert.ERROR)
|
||||
|
||||
@@ -1193,18 +1193,18 @@ class GuiBuildNovel(QDialog):
|
||||
|
||||
class GuiBuildNovelDocView(QTextBrowser):
|
||||
|
||||
def __init__(self, theParent, theProject):
|
||||
QTextBrowser.__init__(self, theParent)
|
||||
def __init__(self, mainGui, theProject):
|
||||
QTextBrowser.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiBuildNovelDocView ...")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theProject = theProject
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.buildTime = 0
|
||||
|
||||
self.setMinimumWidth(40*self.theParent.theTheme.textNWidth)
|
||||
self.setMinimumWidth(40*self.mainGui.mainTheme.textNWidth)
|
||||
self.setOpenExternalLinks(False)
|
||||
|
||||
self.document().setDocumentMargin(self.mainConf.getTextMargin())
|
||||
@@ -1238,9 +1238,9 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
lblPalette.setColor(QPalette.Foreground, lblPalette.toolTipText().color())
|
||||
|
||||
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.setIndent(0)
|
||||
|
||||
@@ -42,15 +42,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiLipsum(QDialog):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiLipsum ...")
|
||||
self.setObjectName("GuiLipsum")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
self.setWindowTitle(self.tr("Insert Placeholder Text"))
|
||||
|
||||
@@ -61,7 +61,7 @@ class GuiLipsum(QDialog):
|
||||
nPx = self.mainConf.pxInt(64)
|
||||
vSp = self.mainConf.pxInt(4)
|
||||
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.setSpacing(vSp)
|
||||
@@ -129,7 +129,7 @@ class GuiLipsum(QDialog):
|
||||
pCount = self.paraCount.value()
|
||||
inText = "\n\n".join(lipsumText[0:pCount]) + "\n\n"
|
||||
|
||||
self.theParent.docEditor.insertText(inText)
|
||||
self.mainGui.docEditor.insertText(inText)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -48,17 +48,17 @@ PAGE_FINAL = 4
|
||||
|
||||
class GuiProjectWizard(QWizard):
|
||||
|
||||
def __init__(self, theParent):
|
||||
QWizard.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QWizard.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiProjectWizard ...")
|
||||
self.setObjectName("GuiProjectWizard")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
|
||||
self.sideImage = self.theTheme.loadDecoration(
|
||||
self.sideImage = self.mainTheme.loadDecoration(
|
||||
"wiz-back", None, self.mainConf.pxInt(370)
|
||||
)
|
||||
self.setWizardStyle(QWizard.ModernStyle)
|
||||
@@ -92,7 +92,7 @@ class ProjWizardIntroPage(QWizardPage):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theWizard = theWizard
|
||||
self.theTheme = theWizard.theTheme
|
||||
self.mainTheme = theWizard.mainTheme
|
||||
|
||||
self.setTitle(self.tr("Create New Project"))
|
||||
self.theText = QLabel(self.tr(
|
||||
@@ -107,7 +107,7 @@ class ProjWizardIntroPage(QWizardPage):
|
||||
"Peter Mitterhofer", "CC BY-SA 4.0"
|
||||
))
|
||||
lblFont = self.imgCredit.font()
|
||||
lblFont.setPointSizeF(0.6*self.theTheme.fontPointSize)
|
||||
lblFont.setPointSizeF(0.6*self.mainTheme.fontPointSize)
|
||||
self.imgCredit.setFont(lblFont)
|
||||
|
||||
xW = self.mainConf.pxInt(300)
|
||||
@@ -162,7 +162,7 @@ class ProjWizardFolderPage(QWizardPage):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theWizard = theWizard
|
||||
self.theTheme = theWizard.theTheme
|
||||
self.mainTheme = theWizard.mainTheme
|
||||
|
||||
self.setTitle(self.tr("Select Project Folder"))
|
||||
self.theText = QLabel(self.tr(
|
||||
@@ -180,7 +180,7 @@ class ProjWizardFolderPage(QWizardPage):
|
||||
self.projPath.setPlaceholderText(self.tr("Required"))
|
||||
|
||||
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.errLabel = QLabel("")
|
||||
|
||||
@@ -57,16 +57,16 @@ class GuiWritingStats(QDialog):
|
||||
FMT_JSON = 0
|
||||
FMT_CSV = 1
|
||||
|
||||
def __init__(self, theParent):
|
||||
QDialog.__init__(self, theParent)
|
||||
def __init__(self, mainGui):
|
||||
QDialog.__init__(self, mainGui)
|
||||
|
||||
logger.debug("Initialising GuiWritingStats ...")
|
||||
self.setObjectName("GuiWritingStats")
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theParent = theParent
|
||||
self.theTheme = theParent.theTheme
|
||||
self.theProject = theParent.theProject
|
||||
self.mainGui = mainGui
|
||||
self.mainTheme = mainGui.mainTheme
|
||||
self.theProject = mainGui.theProject
|
||||
|
||||
self.logData = []
|
||||
self.filterData = []
|
||||
@@ -125,7 +125,7 @@ class GuiWritingStats(QDialog):
|
||||
self.listBox.setSortingEnabled(True)
|
||||
|
||||
# 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.barImage = QPixmap(self.barHeight, self.barHeight)
|
||||
self.barImage.fill(self.palette().highlight().color())
|
||||
@@ -136,27 +136,27 @@ class GuiWritingStats(QDialog):
|
||||
self.infoBox.setLayout(self.infoForm)
|
||||
|
||||
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.labelIdleT = QLabel(formatTime(0))
|
||||
self.labelIdleT.setFont(self.theTheme.guiFontFixed)
|
||||
self.labelIdleT.setFont(self.mainTheme.guiFontFixed)
|
||||
self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
|
||||
|
||||
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.novelWords = QLabel("0")
|
||||
self.novelWords.setFont(self.theTheme.guiFontFixed)
|
||||
self.novelWords.setFont(self.mainTheme.guiFontFixed)
|
||||
self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
|
||||
|
||||
self.notesWords = QLabel("0")
|
||||
self.notesWords.setFont(self.theTheme.guiFontFixed)
|
||||
self.notesWords.setFont(self.mainTheme.guiFontFixed)
|
||||
self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
|
||||
|
||||
self.totalWords = QLabel("0")
|
||||
self.totalWords.setFont(self.theTheme.guiFontFixed)
|
||||
self.totalWords.setFont(self.mainTheme.guiFontFixed)
|
||||
self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
|
||||
|
||||
lblTTime = QLabel(self.tr("Total Time:"))
|
||||
@@ -183,7 +183,7 @@ class GuiWritingStats(QDialog):
|
||||
self.infoForm.setRowStretch(6, 1)
|
||||
|
||||
# Filter Options
|
||||
sPx = self.theTheme.baseIconSize
|
||||
sPx = self.mainTheme.baseIconSize
|
||||
|
||||
self.filterBox = QGroupBox(self.tr("Filters"), self)
|
||||
self.filterForm = QGridLayout(self)
|
||||
@@ -411,11 +411,11 @@ class GuiWritingStats(QDialog):
|
||||
|
||||
# Report to user
|
||||
if wSuccess:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("{0} file successfully written to:").format(textFmt), savePath
|
||||
], nwAlert.INFO)
|
||||
else:
|
||||
self.theParent.makeAlert([
|
||||
self.mainGui.makeAlert([
|
||||
self.tr("Failed to write {0} file.").format(textFmt), errMsg
|
||||
], nwAlert.ERROR)
|
||||
|
||||
@@ -478,7 +478,7 @@ class GuiWritingStats(QDialog):
|
||||
self.logData.append((dStart, sDiff, wcNovel, wcNotes, sIdle))
|
||||
|
||||
except Exception as exc:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to read session log file."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
@@ -605,13 +605,13 @@ class GuiWritingStats(QDialog):
|
||||
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
||||
newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
|
||||
newItem.setFont(self.C_TIME, self.theTheme.guiFontFixed)
|
||||
newItem.setFont(self.C_LENGTH, self.theTheme.guiFontFixed)
|
||||
newItem.setFont(self.C_COUNT, self.theTheme.guiFontFixed)
|
||||
newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
|
||||
newItem.setFont(self.C_LENGTH, self.mainTheme.guiFontFixed)
|
||||
newItem.setFont(self.C_COUNT, self.mainTheme.guiFontFixed)
|
||||
if showIdleTime:
|
||||
newItem.setFont(self.C_IDLE, self.theTheme.guiFontFixed)
|
||||
newItem.setFont(self.C_IDLE, self.mainTheme.guiFontFixed)
|
||||
else:
|
||||
newItem.setFont(self.C_IDLE, self.theTheme.guiFont)
|
||||
newItem.setFont(self.C_IDLE, self.mainTheme.guiFont)
|
||||
|
||||
self.listBox.addTopLevelItem(newItem)
|
||||
self.timeFilter += sDiff
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%%~name: Chapter One
|
||||
%%~path: e5e47ebf63b1c/a520879ca0b45
|
||||
%%~kind: NOVEL/DOCUMENT
|
||||
### Chapter One
|
||||
## Chapter One
|
||||
|
||||
@pov: Jane
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
%%~kind: NOVEL/DOCUMENT
|
||||
# Part One
|
||||
|
||||
>> In the beginning … <<
|
||||
>> In the beginning … <<
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?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>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
<saveCount>1334</saveCount>
|
||||
<autoCount>225</autoCount>
|
||||
<editTime>67746</editTime>
|
||||
<saveCount>1371</saveCount>
|
||||
<autoCount>236</autoCount>
|
||||
<editTime>69222</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
@@ -15,8 +15,10 @@
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>a520879ca0b45</lastEdited>
|
||||
<lastEdited>636b6aa9b697b</lastEdited>
|
||||
<lastViewed>636b6aa9b697b</lastViewed>
|
||||
<lastNovel>7031beac91f75</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>1363</lastWordCount>
|
||||
<novelWordCount>954</novelWordCount>
|
||||
<notesWordCount>409</notesWordCount>
|
||||
@@ -33,13 +35,13 @@
|
||||
<section></section>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="sf12341" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="sf24ce6" count="1" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s90e6c9" count="6" 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="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st 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="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>
|
||||
<importance>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
||||
@@ -63,34 +65,34 @@
|
||||
</item>
|
||||
<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"/>
|
||||
<name status="sf12341" import="ia857f0" exported="True">Part One</name>
|
||||
<name status="s90e6c9" import="ia857f0" exported="True">Part One</name>
|
||||
</item>
|
||||
<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"/>
|
||||
<name status="sf24ce6" import="ia857f0" exported="True">Chapter One</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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"/>
|
||||
<name status="sf12341" import="ia857f0" exported="True">Interlude</name>
|
||||
<name status="s78ea90" import="ia857f0" exported="True">Interlude</name>
|
||||
</item>
|
||||
<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"/>
|
||||
<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 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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||
@@ -99,7 +101,7 @@
|
||||
</item>
|
||||
<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"/>
|
||||
<name status="sf12341" import="ia857f0" exported="True">Title Page</name>
|
||||
<name status="sc24b8f" import="ia857f0" exported="True">Title Page</name>
|
||||
</item>
|
||||
<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"/>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>7a992350f3eb6</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>3847</lastWordCount>
|
||||
<novelWordCount>3109</novelWordCount>
|
||||
<notesWordCount>738</notesWordCount>
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>10</lastWordCount>
|
||||
<novelWordCount>10</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>Test Custom</name>
|
||||
<title>Test Novel</title>
|
||||
@@ -17,6 +17,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>Test Custom</name>
|
||||
<title>Test Novel</title>
|
||||
@@ -17,6 +17,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -16,9 +16,11 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>2</lastWordCount>
|
||||
<novelWordCount>1</novelWordCount>
|
||||
<notesWordCount>1</notesWordCount>
|
||||
<autoReplace/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
@@ -28,19 +30,19 @@
|
||||
<section></section>
|
||||
</titleFormat>
|
||||
<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="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
</status>
|
||||
<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="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="10">
|
||||
<content count="11">
|
||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<name status="s000008" import="i00000c">Novel</name>
|
||||
@@ -73,13 +75,17 @@
|
||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" exported="True">New Scene</name>
|
||||
</item>
|
||||
<item handle="0000000000028" parent="31489056e0916" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT">
|
||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="None" import="None" exported="True">Hello</name>
|
||||
<item handle="0000000000028" parent="0000000000015" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<name status="s000008" import="i00000c">Stuff</name>
|
||||
</item>
|
||||
<item handle="0000000000029" parent="71ee45a3c0db9" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT">
|
||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="None" import="None" exported="True">Jane</name>
|
||||
<item handle="0000000000029" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||
<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>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title></title>
|
||||
@@ -15,6 +15,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -16,6 +16,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -16,6 +16,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>000000000000f</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>0000000000008</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>129</lastWordCount>
|
||||
<novelWordCount>102</novelWordCount>
|
||||
<notesWordCount>27</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -16,6 +16,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>9</lastWordCount>
|
||||
<novelWordCount>9</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>Project Name</name>
|
||||
<title>Project Title</title>
|
||||
@@ -17,6 +17,8 @@
|
||||
<autoOutline>True</autoOutline>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>9</lastWordCount>
|
||||
<novelWordCount>9</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
|
||||
@@ -232,9 +232,6 @@ def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd):
|
||||
|
||||
assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
|
||||
assert theIndex.getHandleHeaderLevel(nHandle) == "H1"
|
||||
assert theIndex.getHandleHeaderIntLevel(cHandle) == 1
|
||||
assert theIndex.getHandleHeaderIntLevel(nHandle) == 1
|
||||
assert theIndex.getHandleHeaderIntLevel("stuff") == 0
|
||||
|
||||
# Zero Items
|
||||
assert theIndex.checkThese([], cItem) == []
|
||||
|
||||
@@ -29,10 +29,14 @@ from lxml import etree
|
||||
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
|
||||
from mock import causeOSError
|
||||
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from novelwriter.common import formatTimeStamp
|
||||
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
|
||||
@@ -280,12 +284,12 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreProject_NewFileFolder_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewFileFolder_nwProject.nwx")
|
||||
|
||||
theProject = NWProject(mockGUI)
|
||||
buildTestProject(theProject, fncDir)
|
||||
@@ -295,9 +299,33 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
assert theProject.closeProject() is True
|
||||
assert theProject.openProject(projFile) is True
|
||||
|
||||
assert isinstance(theProject.newFile("Hello", "31489056e0916"), str)
|
||||
assert isinstance(theProject.newFile("Jane", "71ee45a3c0db9"), str)
|
||||
assert theProject.projChanged
|
||||
# Invalid call
|
||||
assert theProject.newFolder("New Folder", "1234567890abc") is None
|
||||
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.closeProject() is True
|
||||
|
||||
@@ -305,7 +333,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||
assert theProject.projChanged is False
|
||||
|
||||
# END Test testCoreProject_NewFile
|
||||
# END Test testCoreProject_NewFileFolder
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -435,12 +463,15 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
||||
os.rename(oName, rName)
|
||||
|
||||
# Add some legacy stuff that cannot be removed
|
||||
writeFile(os.path.join(nwMinimal, "junk"), "stuff")
|
||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
||||
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff")
|
||||
mockGUI.clear()
|
||||
assert theProject.openProject(nwMinimal) is True
|
||||
assert "data_0" in mockGUI.lastAlert
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(theProject, "_legacyDataFolder", causeOSError)
|
||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
||||
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.nwd"), "stuff")
|
||||
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.bak"), "stuff")
|
||||
mockGUI.clear()
|
||||
assert theProject.openProject(nwMinimal) is True
|
||||
assert "There was an error updating the project." in mockGUI.lastAlert
|
||||
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreProject_Open
|
||||
@@ -613,6 +644,11 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||
theProject = NWProject(mockGUI)
|
||||
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
|
||||
oldOrder = [
|
||||
"a508bb932959c", # ROOT: Novel
|
||||
@@ -722,7 +758,7 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
||||
# Change Importance
|
||||
# =================
|
||||
|
||||
fHandle = theProject.newFile("Jane Doe", "8b9d2e465e150")
|
||||
fHandle = theProject.newFile("Jane Doe", "0000000000012")
|
||||
theProject.tree[fHandle].setImport("Main")
|
||||
|
||||
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.projLang == "en_GB"
|
||||
|
||||
# Language Lookup
|
||||
assert theProject.localLookup(1) == "One"
|
||||
assert theProject.localLookup(10) == "Ten"
|
||||
|
||||
# Automatic outline update
|
||||
theProject.projChanged = False
|
||||
assert theProject.setAutoOutline(True)
|
||||
@@ -1007,7 +1047,8 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
||||
|
||||
# Add a file with non-existent parent
|
||||
# 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
|
||||
assert theProject.saveProject() is True
|
||||
@@ -1103,14 +1144,6 @@ def testCoreProject_OldFormat(mockGUI, nwOldProj):
|
||||
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
|
||||
os.mkdir(os.path.join(nwOldProj, "cache"))
|
||||
for aFile in deleteFiles:
|
||||
@@ -1124,7 +1157,6 @@ def testCoreProject_OldFormat(mockGUI, nwOldProj):
|
||||
for aFile in deleteFiles:
|
||||
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_7"))
|
||||
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_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
|
||||
assert os.path.isdir(os.path.join(nwOldProj, "cache"))
|
||||
assert os.path.isdir(os.path.join(nwOldProj, "content"))
|
||||
@@ -1179,7 +1205,7 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.unlink", causeOSError)
|
||||
assert not theProject._deprecatedFiles()
|
||||
assert theProject._deprecatedFiles() is False
|
||||
|
||||
assert theProject._deprecatedFiles()
|
||||
assert not os.path.isfile(tstFile)
|
||||
@@ -1188,63 +1214,36 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
|
||||
tstFile = os.path.join(fncDir, "data_0")
|
||||
writeFile(tstFile, "stuff")
|
||||
assert os.path.isfile(tstFile)
|
||||
|
||||
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"))
|
||||
assert theProject._legacyDataFolder(tstFile) is False
|
||||
|
||||
# Check renaming/deleting of old document files
|
||||
tstData = os.path.join(fncDir, "data_2")
|
||||
tstDoc1m = os.path.join(tstData, "000000000001_main.nwd")
|
||||
tstDoc1b = os.path.join(tstData, "000000000001_main.bak")
|
||||
tstDoc2m = os.path.join(tstData, "000000000002_main.nwd")
|
||||
tstDoc2b = os.path.join(tstData, "000000000002_main.bak")
|
||||
tstDoc3m = os.path.join(tstData, "tooshort003_main.nwd")
|
||||
tstDoc3b = os.path.join(tstData, "tooshort003_main.bak")
|
||||
tstData2 = os.path.join(fncDir, "data_2")
|
||||
tstData3 = os.path.join(fncDir, "data_3")
|
||||
tstDoc1m = os.path.join(tstData2, "000000000001_main.nwd")
|
||||
tstDoc1b = os.path.join(tstData2, "000000000001_main.bak")
|
||||
tstDoc2m = os.path.join(tstData2, "000000000002_main.nwd")
|
||||
tstDoc2b = os.path.join(tstData2, "000000000002_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(tstDoc1b, "stuff")
|
||||
writeFile(tstDoc2m, "stuff")
|
||||
writeFile(tstDoc2b, "stuff")
|
||||
writeFile(tstDoc3m, "stuff")
|
||||
writeFile(tstDoc3b, "stuff")
|
||||
os.mkdir(tstDir4a)
|
||||
|
||||
# Make the above fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.rename", causeOSError)
|
||||
mp.setattr("os.unlink", causeOSError)
|
||||
errList = []
|
||||
errList = theProject._legacyDataFolder(tstData, errList)
|
||||
assert len(errList) > 0
|
||||
with pytest.raises(OSError):
|
||||
theProject._legacyDataFolder(tstData2)
|
||||
theProject._legacyDataFolder(tstData3)
|
||||
assert os.path.isfile(tstDoc1m)
|
||||
assert os.path.isfile(tstDoc1b)
|
||||
assert os.path.isfile(tstDoc2m)
|
||||
@@ -1253,15 +1252,16 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
|
||||
assert os.path.isfile(tstDoc3b)
|
||||
|
||||
# And succeed ...
|
||||
errList = []
|
||||
errList = theProject._legacyDataFolder(tstData, errList)
|
||||
assert len(errList) == 0
|
||||
assert theProject._legacyDataFolder(tstData2) is True
|
||||
assert theProject._legacyDataFolder(tstData3) is True
|
||||
|
||||
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", "2000000000002.nwd"))
|
||||
assert os.path.isfile(os.path.join(fncDir, "junk", "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.nwd"))
|
||||
assert os.path.isfile(os.path.join(fncDir, tstData3, "tooshort003_main.bak"))
|
||||
assert os.path.isdir(tstDir4a)
|
||||
|
||||
# END Test testCoreProject_LegacyData
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||
|
||||
# NW About
|
||||
nwGUI.theTheme.themeName = "A Theme"
|
||||
nwGUI.theTheme.themeAuthor = "An Author"
|
||||
nwGUI.mainTheme.themeName = "A Theme"
|
||||
nwGUI.mainTheme.themeAuthor = "An Author"
|
||||
assert nwGUI.showAboutNWDialog(showNotes=True) is True
|
||||
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
|
||||
|
||||
@@ -24,11 +24,7 @@ import pytest
|
||||
from PyQt5.QtCore import QItemSelectionModel
|
||||
from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox
|
||||
|
||||
from novelwriter.dialogs import GuiQuoteSelect, GuiUpdates
|
||||
|
||||
keyDelay = 2
|
||||
typeDelay = 1
|
||||
stepDelay = 20
|
||||
from novelwriter.dialogs import GuiQuoteSelect, GuiUpdates, GuiEditLabel
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
@@ -101,3 +97,24 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
|
||||
nwUpdate._doClose()
|
||||
|
||||
# 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
|
||||
|
||||
@@ -25,10 +25,10 @@ import pytest
|
||||
from mock import causeOSError
|
||||
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.dialogs import GuiDocMerge, GuiItemEditor
|
||||
from novelwriter.dialogs import GuiDocMerge, GuiEditLabel
|
||||
from novelwriter.core.tree import NWTree
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||
|
||||
# Create a new project
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
@@ -54,13 +55,12 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
hMergedDoc = "0000000000023"
|
||||
|
||||
# Add Project Content
|
||||
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
|
||||
assert nwGUI.saveProject() is True
|
||||
assert nwGUI.closeProject() is True
|
||||
@@ -82,8 +82,8 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
|
||||
# Open the Merge tool
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||
|
||||
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
|
||||
nwGUI.mainMenu.aMergeDocs.activate(QAction.Trigger)
|
||||
@@ -101,27 +101,27 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
assert nwMerge.listBox.count() == 0
|
||||
|
||||
# No item selected
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
assert nwMerge._populateList() is False
|
||||
assert nwMerge.listBox.count() == 0
|
||||
|
||||
# Non-existing item
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(NWTree, "__getitem__", lambda *a: None)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||
assert nwMerge._populateList() is False
|
||||
assert nwMerge.listBox.count() == 0
|
||||
|
||||
# Select a non-folder
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hChapterOne).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hChapterOne).setSelected(True)
|
||||
assert nwMerge._populateList() is False
|
||||
assert nwMerge.listBox.count() == 0
|
||||
|
||||
# Select the chapter folder
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||
assert nwMerge._populateList() is True
|
||||
assert nwMerge.listBox.count() == 5
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ import pytest
|
||||
from mock import causeOSError
|
||||
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.dialogs import GuiDocSplit, GuiItemEditor
|
||||
from novelwriter.dialogs import GuiDocSplit, GuiEditLabel
|
||||
from novelwriter.core.tree import NWTree
|
||||
from novelwriter.core.document import NWDoc
|
||||
|
||||
@@ -40,6 +40,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||
|
||||
# Create a new project
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
@@ -58,11 +59,10 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
hSceneFive = "0000000000028"
|
||||
|
||||
# Add Project Content
|
||||
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hNovelRoot).setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hNovelRoot).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
|
||||
assert nwGUI.saveProject() is True
|
||||
assert nwGUI.closeProject() is True
|
||||
@@ -89,8 +89,8 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
|
||||
# Open the Split tool
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hToSplit).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hToSplit).setSelected(True)
|
||||
|
||||
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
|
||||
nwGUI.mainMenu.aSplitDoc.activate(QAction.Trigger)
|
||||
@@ -109,7 +109,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
|
||||
# No item selected
|
||||
nwSplit.sourceItem = None
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
assert nwSplit._populateList() is False
|
||||
assert nwSplit.listBox.count() == 0
|
||||
|
||||
@@ -117,15 +117,15 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(NWTree, "__getitem__", lambda *a: None)
|
||||
nwSplit.sourceItem = None
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hToSplit).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hToSplit).setSelected(True)
|
||||
assert nwSplit._populateList() is False
|
||||
assert nwSplit.listBox.count() == 0
|
||||
|
||||
# Select a non-file
|
||||
nwSplit.sourceItem = None
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem(hChapterDir).setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
|
||||
assert nwSplit._populateList() is False
|
||||
assert nwSplit.listBox.count() == 0
|
||||
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
"""
|
||||
novelWriter – Item Editor Dialog Class Tester
|
||||
=============================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2022, 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
|
||||
@@ -1147,7 +1147,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||
assert nwGUI.openDocument(cHandle) is True
|
||||
assert nwGUI.docEditor.replaceText(theText) is True
|
||||
assert nwGUI.saveDocument() is True
|
||||
assert nwGUI.treeView.revealNewTreeItem(cHandle)
|
||||
assert nwGUI.projView.revealNewTreeItem(cHandle)
|
||||
nwGUI.docEditor.updateTagHighLighting()
|
||||
|
||||
# Follow Tag
|
||||
|
||||
@@ -51,12 +51,12 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
assert nwGUI.theProject.index._itemIndex._items != {}
|
||||
|
||||
# Select a document in the project tree
|
||||
nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||
nwGUI.projView.setSelectedHandle("88243afbe5ed8")
|
||||
|
||||
# Middle-click the selected item
|
||||
theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8")
|
||||
theRect = nwGUI.treeView.visualItemRect(theItem)
|
||||
qtbot.mouseClick(nwGUI.treeView.viewport(), Qt.MidButton, pos=theRect.center())
|
||||
theItem = nwGUI.projView.projTree._getTreeItem("88243afbe5ed8")
|
||||
theRect = nwGUI.projView.projTree.visualItemRect(theItem)
|
||||
qtbot.mouseClick(nwGUI.projView.projTree.viewport(), Qt.MidButton, pos=theRect.center())
|
||||
assert nwGUI.docViewer.docHandle() == "88243afbe5ed8"
|
||||
|
||||
# Reload the text
|
||||
@@ -117,7 +117,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.COPY) is False
|
||||
|
||||
# Open again via menu
|
||||
assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||
assert nwGUI.projView.setSelectedHandle("88243afbe5ed8")
|
||||
nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger)
|
||||
|
||||
# Select "Bod" link
|
||||
|
||||
@@ -26,14 +26,13 @@ from shutil import copyfile
|
||||
from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QMessageBox, QDialog
|
||||
from PyQt5.QtWidgets import QMessageBox, QInputDialog
|
||||
|
||||
from novelwriter.gui import (
|
||||
GuiDocEditor, GuiProjectTree, GuiNovelTree, GuiOutline
|
||||
)
|
||||
from novelwriter.enum import nwItemType, nwWidget
|
||||
from novelwriter.gui import GuiDocEditor, GuiNovelView, GuiOutlineView
|
||||
from novelwriter.enum import nwItemType, nwView, nwWidget
|
||||
from novelwriter.tools import GuiProjectWizard
|
||||
from novelwriter.dialogs.itemeditor import GuiItemEditor
|
||||
from novelwriter.gui.projtree import GuiProjectTree
|
||||
from novelwriter.dialogs import GuiEditLabel
|
||||
|
||||
keyDelay = 2
|
||||
typeDelay = 1
|
||||
@@ -58,7 +57,7 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
|
||||
assert nwGUI.mergeDocuments() is False
|
||||
assert nwGUI.splitDocument() is False
|
||||
assert nwGUI.openSelectedItem() is False
|
||||
assert nwGUI.editItem() is False
|
||||
assert nwGUI.editItemLabel() is False
|
||||
assert nwGUI.requestNovelTreeRefresh() is False
|
||||
assert nwGUI.rebuildIndex() is False
|
||||
assert nwGUI.showProjectSettingsDialog() is False
|
||||
@@ -121,44 +120,44 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
assert nwGUI.openSelectedItem() is False
|
||||
|
||||
# Project Tree has focus
|
||||
nwGUI._changeView(nwView.PROJECT)
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.projStack.setCurrentIndex(0)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
nwGUI.treeView._getTreeItem(sHandle).setSelected(True)
|
||||
nwGUI.projView.projTree._getTreeItem(sHandle).setSelected(True)
|
||||
nwGUI._keyPressReturn()
|
||||
assert nwGUI.docEditor.docHandle() == sHandle
|
||||
assert nwGUI.closeDocument() is True
|
||||
|
||||
# Novel Tree has focus
|
||||
nwGUI.projStack.setCurrentIndex(1)
|
||||
nwGUI.novelView.refreshTree(True)
|
||||
nwGUI._changeView(nwView.NOVEL)
|
||||
nwGUI.novelView.novelTree.refreshTree(rootHandle=None, overRide=True)
|
||||
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
|
||||
actItem = nwGUI.novelView.topLevelItem(0)
|
||||
chpItem = actItem.child(0)
|
||||
selItem = chpItem.child(0)
|
||||
nwGUI.novelView.setCurrentItem(selItem)
|
||||
selItem = nwGUI.novelView.novelTree.topLevelItem(2)
|
||||
nwGUI.novelView.novelTree.setCurrentItem(selItem)
|
||||
nwGUI._keyPressReturn()
|
||||
assert nwGUI.docEditor.docHandle() == sHandle
|
||||
assert nwGUI.closeDocument() is True
|
||||
|
||||
# Project Outline has focus
|
||||
nwGUI._changeView(nwView.OUTLINE)
|
||||
nwGUI.switchFocus(nwWidget.OUTLINE)
|
||||
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
|
||||
actItem = nwGUI.projView.outlineView.topLevelItem(0)
|
||||
actItem = nwGUI.outlineView.outlineTree.topLevelItem(0)
|
||||
chpItem = actItem.child(0)
|
||||
selItem = chpItem.child(0)
|
||||
nwGUI.projView.outlineView.setCurrentItem(selItem)
|
||||
nwGUI.outlineView.outlineTree.setCurrentItem(selItem)
|
||||
nwGUI._keyPressReturn()
|
||||
assert nwGUI.docEditor.docHandle() == sHandle
|
||||
assert nwGUI.closeDocument() is True
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testGuiMain_ProjectTreeItems
|
||||
|
||||
@@ -170,10 +169,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", 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(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
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
@@ -220,14 +219,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
assert nwGUI.theProject.spellCheck is False
|
||||
|
||||
# Check that tree items have been created
|
||||
assert nwGUI.treeView._getTreeItem("0000000000008") is not None
|
||||
assert nwGUI.treeView._getTreeItem("0000000000009") is not None
|
||||
assert nwGUI.treeView._getTreeItem("000000000000a") is not None
|
||||
assert nwGUI.treeView._getTreeItem("000000000000b") is not None
|
||||
assert nwGUI.treeView._getTreeItem("000000000000c") is not None
|
||||
assert nwGUI.treeView._getTreeItem("000000000000d") is not None
|
||||
assert nwGUI.treeView._getTreeItem("000000000000e") is not None
|
||||
assert nwGUI.treeView._getTreeItem("000000000000f") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("0000000000008") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("0000000000009") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("000000000000a") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("000000000000b") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("000000000000c") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("000000000000d") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("000000000000e") is not None
|
||||
assert nwGUI.projView.projTree._getTreeItem("000000000000f") is not None
|
||||
|
||||
nwGUI.mainMenu.aSpellCheck.setChecked(True)
|
||||
assert nwGUI.mainMenu._toggleSpellCheck()
|
||||
@@ -240,9 +239,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
|
||||
# Add a Character File
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
@@ -262,9 +261,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
|
||||
# Add a Plot File
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem("0000000000009").setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem("0000000000009").setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
@@ -284,9 +283,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
|
||||
# Add a World File
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem("000000000000b").setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem("000000000000b").setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Add Some Text
|
||||
@@ -315,10 +314,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
|
||||
# Select the 'New Scene' file
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem("0000000000008").setExpanded(True)
|
||||
nwGUI.treeView._getTreeItem("000000000000d").setExpanded(True)
|
||||
nwGUI.treeView._getTreeItem("000000000000f").setSelected(True)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem("0000000000008").setExpanded(True)
|
||||
nwGUI.projView.projTree._getTreeItem("000000000000d").setExpanded(True)
|
||||
nwGUI.projView.projTree._getTreeItem("000000000000f").setSelected(True)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
@@ -461,12 +460,12 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Check a Quick Create and Delete
|
||||
assert nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
newHandle = nwGUI.treeView.getSelectedHandle()
|
||||
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
|
||||
newHandle = nwGUI.projView.getSelectedHandle()
|
||||
assert nwGUI.theProject.tree["0000000000020"] is not None
|
||||
assert nwGUI.treeView.deleteItem()
|
||||
assert nwGUI.treeView.setSelectedHandle(newHandle)
|
||||
assert nwGUI.treeView.deleteItem()
|
||||
assert nwGUI.projView.deleteItem()
|
||||
assert nwGUI.projView.setSelectedHandle(newHandle)
|
||||
assert nwGUI.projView.deleteItem()
|
||||
assert nwGUI.theProject.tree["0000000000024"] is not None # Trash
|
||||
assert nwGUI.saveProject()
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||
|
||||
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
|
||||
nwGUI.docEditor.clear()
|
||||
|
||||
@@ -476,10 +476,10 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||
assert nwGUI.docEditor.getText() == "hello world"
|
||||
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 not nwGUI.docEditor.insertText(None)
|
||||
assert nwGUI.docEditor.insertText(None) is False
|
||||
assert nwGUI.docEditor.isEmpty()
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
|
||||
@@ -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/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from tools import writeFile
|
||||
from tools import buildTestProject, writeFile
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
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
|
||||
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
"""Test navigating the novel tree.
|
||||
"""
|
||||
# Block message box
|
||||
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))
|
||||
|
||||
nwGUI.openProject(nwMinimal)
|
||||
nwTree = nwGUI.novelView
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
|
||||
##
|
||||
# Show/Hide Scrollbars
|
||||
##
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
|
||||
nwTree.mainConf.hideVScroll = True
|
||||
nwTree.mainConf.hideHScroll = True
|
||||
nwTree.initTree()
|
||||
assert not nwTree.verticalScrollBar().isVisible()
|
||||
assert not nwTree.horizontalScrollBar().isVisible()
|
||||
writeFile(
|
||||
os.path.join(nwGUI.theProject.projContent, "0000000000010.nwd"),
|
||||
"# Jane Doe\n\n@tag: Jane\n\n"
|
||||
)
|
||||
writeFile(
|
||||
os.path.join(nwGUI.theProject.projContent, "000000000000f.nwd"),
|
||||
"### Scene One\n\n@pov: Jane\n@focus: Jane\n\n"
|
||||
)
|
||||
|
||||
nwTree.mainConf.hideVScroll = False
|
||||
nwTree.mainConf.hideHScroll = False
|
||||
nwTree.initTree()
|
||||
assert nwTree.verticalScrollBar().isEnabled()
|
||||
assert nwTree.horizontalScrollBar().isEnabled()
|
||||
novelView = nwGUI.novelView
|
||||
novelTree = novelView.novelTree
|
||||
novelBar = novelView.novelBar
|
||||
|
||||
##
|
||||
# Populate Tree
|
||||
##
|
||||
# Show/Hide Scrollbars
|
||||
# ====================
|
||||
|
||||
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.rebuildIndex()
|
||||
nwTree._populateTree()
|
||||
assert nwTree.topLevelItemCount() == 1
|
||||
novelTree._populateTree(rootHandle=None)
|
||||
assert novelTree.topLevelItemCount() == 3
|
||||
|
||||
# Rebuild should preserve selection
|
||||
topItem = nwTree.topLevelItem(0)
|
||||
topItem = novelTree.topLevelItem(0)
|
||||
assert not topItem.isSelected()
|
||||
topItem.setSelected(True)
|
||||
assert nwTree.selectedItems()[0] == topItem
|
||||
assert nwTree.getSelectedHandle() == ("a35baf2e93843", 0)
|
||||
assert novelTree.selectedItems()[0] == topItem
|
||||
assert novelView.getSelectedHandle() == ("000000000000c", 0)
|
||||
|
||||
nwTree.refreshTree()
|
||||
assert nwTree.topLevelItem(0).isSelected()
|
||||
# Refresh using the slot for the butoom
|
||||
novelBar._refreshNovelTree()
|
||||
assert novelTree.topLevelItem(0).isSelected()
|
||||
|
||||
##
|
||||
# Open Items
|
||||
##
|
||||
# Open Items
|
||||
# ==========
|
||||
|
||||
# Clear selection
|
||||
nwTree.clearSelection()
|
||||
scItem = nwTree.topLevelItem(0).child(0).child(0)
|
||||
novelTree.clearSelection()
|
||||
scItem = novelTree.topLevelItem(2)
|
||||
scItem.setSelected(True)
|
||||
assert scItem.isSelected()
|
||||
|
||||
# Clear selection with mouse
|
||||
vPort = nwTree.viewport()
|
||||
vPort = novelTree.viewport()
|
||||
qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10)
|
||||
assert not scItem.isSelected()
|
||||
|
||||
@@ -93,8 +113,8 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
scItem.setSelected(True)
|
||||
assert scItem.isSelected()
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
nwTree._treeDoubleClick(scItem, 0)
|
||||
assert nwGUI.docEditor.docHandle() == "8c659a11cd429"
|
||||
novelTree._treeDoubleClick(scItem, 0)
|
||||
assert nwGUI.docEditor.docHandle() == "000000000000f"
|
||||
|
||||
# Open item with middle mouse button
|
||||
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)
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
|
||||
scRect = nwTree.visualItemRect(scItem)
|
||||
oldData = scItem.data(nwTree.C_TITLE, Qt.UserRole)
|
||||
scItem.setData(nwTree.C_TITLE, Qt.UserRole, (None, "", ""))
|
||||
scRect = novelTree.visualItemRect(scItem)
|
||||
oldData = scItem.data(novelTree.C_TITLE, Qt.UserRole)
|
||||
scItem.setData(novelTree.C_TITLE, Qt.UserRole, (None, "", ""))
|
||||
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
|
||||
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)
|
||||
assert nwGUI.docViewer.docHandle() == "8c659a11cd429"
|
||||
assert nwGUI.docViewer.docHandle() == "000000000000f"
|
||||
|
||||
##
|
||||
# Populate Tree
|
||||
##
|
||||
# Last Column
|
||||
# ===========
|
||||
|
||||
# Add weird titles to first file to check hnadling of non-standard
|
||||
# order of title levels.
|
||||
writeFile(os.path.join(nwMinimal, "content", "a35baf2e93843.nwd"), (
|
||||
"#### Section wo/Scene\n\n"
|
||||
"### 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"
|
||||
novelBar.setLastColType(NovelTreeColumn.HIDDEN)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is True
|
||||
assert novelTree.lastColType == NovelTreeColumn.HIDDEN
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == ("", "")
|
||||
|
||||
tTitle = nwTree.topLevelItem(3)
|
||||
assert tTitle.child(0).text(nwTree.C_TITLE) == "Section w/Title, wo/Scene"
|
||||
assert tTitle.child(1).text(nwTree.C_TITLE) == "Scene w/Title, wo/Chapter"
|
||||
assert tTitle.child(2).text(nwTree.C_TITLE) == "Chapter"
|
||||
novelBar.setLastColType(NovelTreeColumn.POV)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is False
|
||||
assert novelTree.lastColType == NovelTreeColumn.POV
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
|
||||
"Jane", "Point of View: Jane"
|
||||
)
|
||||
|
||||
tChap = tTitle.child(2)
|
||||
assert tChap.child(0).text(nwTree.C_TITLE) == "Section w/Chapter, wo/Scene"
|
||||
assert tChap.child(1).text(nwTree.C_TITLE) == "Scene"
|
||||
novelBar.setLastColType(NovelTreeColumn.FOCUS)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is False
|
||||
assert novelTree.lastColType == NovelTreeColumn.FOCUS
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
|
||||
"Jane", "Focus: Jane"
|
||||
)
|
||||
|
||||
tScene = tChap.child(1)
|
||||
assert tScene.child(0).text(nwTree.C_TITLE) == "Section"
|
||||
novelBar.setLastColType(NovelTreeColumn.PLOT)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is False
|
||||
assert novelTree.lastColType == NovelTreeColumn.PLOT
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
|
||||
"", "Plot: "
|
||||
)
|
||||
|
||||
##
|
||||
# Close
|
||||
##
|
||||
novelTree._lastCol = None
|
||||
assert novelTree._getLastColumnText("0000000000000", "T000000") == ("", "")
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
# Close
|
||||
# =====
|
||||
|
||||
# qtbot.stop()
|
||||
nwGUI.closeProject()
|
||||
|
||||
# END Test testGuiNovelTree_TreeItems
|
||||
|
||||
@@ -46,57 +46,57 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
|
||||
nwGUI.rebuildIndex()
|
||||
nwGUI._changeView(nwView.OUTLINE)
|
||||
|
||||
outlineMain = nwGUI.projView
|
||||
outlineView = outlineMain.outlineView
|
||||
outlineData = outlineMain.outlineData
|
||||
outlineMenu = outlineMain.outlineBar.mColumns
|
||||
outlineView = nwGUI.outlineView
|
||||
outlineTree = outlineView.outlineTree
|
||||
outlineData = outlineView.outlineData
|
||||
outlineMenu = outlineView.outlineBar.mColumns
|
||||
|
||||
# Toggle scrollbars
|
||||
nwGUI.mainConf.hideVScroll = True
|
||||
nwGUI.mainConf.hideHScroll = True
|
||||
nwGUI.projView.initOutline()
|
||||
assert outlineView.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||
assert outlineView.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||
outlineView.initOutline()
|
||||
assert outlineTree.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||
assert outlineTree.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||
assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||
assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||
|
||||
nwGUI.mainConf.hideVScroll = False
|
||||
nwGUI.mainConf.hideHScroll = False
|
||||
nwGUI.projView.initOutline()
|
||||
assert outlineView.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
|
||||
assert outlineView.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
|
||||
outlineView.initOutline()
|
||||
assert outlineTree.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
|
||||
assert outlineTree.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
|
||||
assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
|
||||
assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
|
||||
|
||||
# Check focus
|
||||
with monkeypatch.context() as mp:
|
||||
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
|
||||
# ============
|
||||
pOptions = nwGUI.theProject.options
|
||||
colNames = [h.name for h in nwOutline]
|
||||
colItems = [h for h in nwOutline]
|
||||
colWidth = {h: outlineView.DEF_WIDTH[h] for h in nwOutline}
|
||||
colHidden = {h: outlineView.DEF_HIDDEN[h] for h in nwOutline}
|
||||
colWidth = {h: outlineTree.DEF_WIDTH[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
|
||||
outlineView._lastBuild = 0
|
||||
outlineView._saveHeaderState()
|
||||
outlineTree._lastBuild = 0
|
||||
outlineTree._saveHeaderState()
|
||||
assert pOptions.getValue("GuiOutline", "headerOrder", []) == []
|
||||
|
||||
# Allow saving header state
|
||||
outlineView._lastBuild = time.time()
|
||||
outlineView._saveHeaderState()
|
||||
outlineTree._lastBuild = time.time()
|
||||
outlineTree._saveHeaderState()
|
||||
assert pOptions.getValue("GuiOutline", "headerOrder", []) == colNames
|
||||
assert outlineView._treeOrder == colItems
|
||||
assert outlineView._colWidth == colWidth
|
||||
assert outlineView._colHidden == colHidden
|
||||
assert outlineTree._treeOrder == colItems
|
||||
assert outlineTree._colWidth == colWidth
|
||||
assert outlineTree._colHidden == colHidden
|
||||
|
||||
# Get default values
|
||||
optItems = pOptions.getValue("GuiOutline", "headerOrder", [])
|
||||
@@ -105,49 +105,49 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
|
||||
|
||||
# Add invalid column name
|
||||
pOptions.setValue("GuiOutline", "headerOrder", optItems + ["blabla"])
|
||||
outlineView._loadHeaderState()
|
||||
assert outlineView._treeOrder == colItems
|
||||
assert outlineView._colHidden == colHidden
|
||||
outlineTree._loadHeaderState()
|
||||
assert outlineTree._treeOrder == colItems
|
||||
assert outlineTree._colHidden == colHidden
|
||||
|
||||
# Add duplicate column name
|
||||
pOptions.setValue("GuiOutline", "headerOrder", optItems + [optItems[-1]])
|
||||
outlineView._loadHeaderState()
|
||||
assert outlineView._treeOrder == colItems
|
||||
assert outlineView._colHidden == colHidden
|
||||
outlineTree._loadHeaderState()
|
||||
assert outlineTree._treeOrder == colItems
|
||||
assert outlineTree._colHidden == colHidden
|
||||
|
||||
# Invalid column width data
|
||||
pOptions.setValue("GuiOutline", "headerOrder", optItems)
|
||||
pOptions.setValue("GuiOutline", "columnWidth", {"blabla": None})
|
||||
outlineView._loadHeaderState()
|
||||
assert outlineView._treeOrder == colItems
|
||||
assert outlineView._colHidden == colHidden
|
||||
outlineTree._loadHeaderState()
|
||||
assert outlineTree._treeOrder == colItems
|
||||
assert outlineTree._colHidden == colHidden
|
||||
|
||||
# Invalid column width data
|
||||
pOptions.setValue("GuiOutline", "headerOrder", optItems)
|
||||
pOptions.setValue("GuiOutline", "columnWidth", optWidth)
|
||||
pOptions.setValue("GuiOutline", "columnHidden", {"bloabla": None})
|
||||
outlineView._loadHeaderState()
|
||||
assert outlineView._treeOrder == colItems
|
||||
assert outlineView._colHidden == colHidden
|
||||
outlineTree._loadHeaderState()
|
||||
assert outlineTree._treeOrder == colItems
|
||||
assert outlineTree._colHidden == colHidden
|
||||
|
||||
# Valid settings
|
||||
pOptions.setValue("GuiOutline", "headerOrder", optItems)
|
||||
pOptions.setValue("GuiOutline", "columnWidth", optWidth)
|
||||
pOptions.setValue("GuiOutline", "columnHidden", optHidden)
|
||||
outlineView._loadHeaderState()
|
||||
assert outlineView._treeOrder == colItems
|
||||
assert outlineView._colHidden == colHidden
|
||||
outlineTree._loadHeaderState()
|
||||
assert outlineTree._treeOrder == colItems
|
||||
assert outlineTree._colHidden == colHidden
|
||||
|
||||
# Header Menu
|
||||
# ===========
|
||||
|
||||
# Trigger the menu entry for all hidden columns
|
||||
for hItem in nwOutline:
|
||||
if outlineView.DEF_HIDDEN[hItem]:
|
||||
if outlineTree.DEF_HIDDEN[hItem]:
|
||||
outlineMenu.actionMap[hItem].activate(QAction.Trigger)
|
||||
|
||||
# Now no columns should be hidden
|
||||
outlineView._saveHeaderState()
|
||||
outlineTree._saveHeaderState()
|
||||
assert not any(pOptions.getValue("GuiOutline", "columnHidden", None).values())
|
||||
|
||||
# qtbot.stop()
|
||||
@@ -169,10 +169,10 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
nwGUI.rebuildIndex()
|
||||
nwGUI._changeView(nwView.OUTLINE)
|
||||
|
||||
outlineMain = nwGUI.projView
|
||||
outlineBar = outlineMain.outlineBar
|
||||
outlineView = outlineMain.outlineView
|
||||
outlineData = outlineMain.outlineData
|
||||
outlineView = nwGUI.outlineView
|
||||
outlineBar = outlineView.outlineBar
|
||||
outlineTree = outlineView.outlineTree
|
||||
outlineData = outlineView.outlineData
|
||||
|
||||
lipHandle = "b3643d0f92e32"
|
||||
|
||||
@@ -183,7 +183,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
|
||||
# Add a second novel folder
|
||||
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
|
||||
nwGUI.treeView.revealNewTreeItem(newHandle)
|
||||
nwGUI.projView.revealNewTreeItem(newHandle)
|
||||
|
||||
# Check new values in dropdown list
|
||||
assert outlineBar.novelValue.itemData(0) == lipHandle
|
||||
@@ -202,7 +202,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
|
||||
hHash = "#"*hLevel
|
||||
writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
|
||||
nwGUI.treeView.revealNewTreeItem(aHandle)
|
||||
nwGUI.projView.revealNewTreeItem(aHandle)
|
||||
|
||||
nwGUI.rebuildIndex()
|
||||
|
||||
@@ -218,10 +218,10 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
# =============
|
||||
|
||||
# First Item
|
||||
outlineView.refreshTree()
|
||||
selItem = outlineView.topLevelItem(0)
|
||||
outlineTree.refreshTree()
|
||||
selItem = outlineTree.topLevelItem(0)
|
||||
|
||||
outlineView.setCurrentItem(selItem)
|
||||
outlineTree.setCurrentItem(selItem)
|
||||
assert outlineData.titleLabel.text() == "<b>Title</b>"
|
||||
assert outlineData.titleValue.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"
|
||||
|
||||
# Scene One
|
||||
actItem = outlineView.topLevelItem(1)
|
||||
actItem = outlineTree.topLevelItem(1)
|
||||
chpItem = actItem.child(0)
|
||||
selItem = chpItem.child(0)
|
||||
|
||||
outlineView.setCurrentItem(selItem)
|
||||
tHandle, tLine = outlineView.getSelectedHandle()
|
||||
outlineTree.setCurrentItem(selItem)
|
||||
tHandle, tLine = outlineTree.getSelectedHandle()
|
||||
assert tHandle == "88243afbe5ed8"
|
||||
assert tLine == 0
|
||||
|
||||
@@ -248,17 +248,17 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
|
||||
# Click POV Link
|
||||
assert outlineData.povKeyValue.text() == "<a href='Bod'>Bod</a>"
|
||||
nwGUI.projView._tagClicked("Bod")
|
||||
outlineView._tagClicked("Bod")
|
||||
assert nwGUI.docViewer.docHandle() == "4c4f28287af27"
|
||||
|
||||
# Scene One, Section Two
|
||||
actItem = outlineView.topLevelItem(1)
|
||||
actItem = outlineTree.topLevelItem(1)
|
||||
chpItem = actItem.child(0)
|
||||
scnItem = chpItem.child(0)
|
||||
selItem = scnItem.child(0)
|
||||
|
||||
outlineView.setCurrentItem(selItem)
|
||||
tHandle, tLine = outlineView.getSelectedHandle()
|
||||
outlineTree.setCurrentItem(selItem)
|
||||
tHandle, tLine = outlineTree.getSelectedHandle()
|
||||
assert tHandle == "88243afbe5ed8"
|
||||
assert tLine == 12
|
||||
|
||||
@@ -267,7 +267,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
assert outlineData.fileValue.text() == "Scene One"
|
||||
assert outlineData.itemValue.text() == "Finished"
|
||||
|
||||
outlineView._treeDoubleClick(selItem, 0)
|
||||
outlineTree._treeDoubleClick(selItem, 0)
|
||||
assert nwGUI.docEditor.docHandle() == "88243afbe5ed8"
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
@@ -24,11 +24,11 @@ import os
|
||||
|
||||
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.enum import nwItemType, nwItemClass
|
||||
|
||||
|
||||
@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, "question", 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
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||
|
||||
# Create a project
|
||||
prjDir = os.path.join(fncDir, "project")
|
||||
buildTestProject(nwGUI, prjDir)
|
||||
|
||||
# No itemType set
|
||||
nwTree.clearSelection()
|
||||
assert nwTree.newTreeItem(None) is False
|
||||
nwTree.projTree.clearSelection()
|
||||
assert nwTree.projTree.newTreeItem(None) is False
|
||||
|
||||
# Root Items
|
||||
# ==========
|
||||
|
||||
# No class set
|
||||
assert nwTree.newTreeItem(nwItemType.ROOT) is False
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.ROOT) is False
|
||||
|
||||
# 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
|
||||
|
||||
# File/Folder Items
|
||||
# =================
|
||||
|
||||
# No location selected for new item
|
||||
nwTree.clearSelection()
|
||||
nwTree.projTree.clearSelection()
|
||||
caplog.clear()
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is False
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is False
|
||||
assert "Did not find anywhere" in caplog.text
|
||||
|
||||
# Create new folder as child of Novel folder
|
||||
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"].itemRoot == "0000000000008"
|
||||
assert nwGUI.theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL
|
||||
|
||||
# Add a new file in the new folder
|
||||
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"].itemRoot == "0000000000008"
|
||||
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")
|
||||
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"].itemRoot == "0000000000008"
|
||||
assert nwGUI.theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL
|
||||
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
|
||||
nwTree.setSelectedHandle("000000000000a")
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwGUI.theProject.tree["0000000000014"].itemParent == "000000000000a"
|
||||
assert nwGUI.theProject.tree["0000000000014"].itemRoot == "000000000000a"
|
||||
assert nwGUI.theProject.tree["0000000000014"].itemClass == nwItemClass.CHARACTER
|
||||
assert nwGUI.openDocument("0000000000014")
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) is True
|
||||
assert nwGUI.theProject.tree["0000000000015"].itemParent == "000000000000a"
|
||||
assert nwGUI.theProject.tree["0000000000015"].itemRoot == "000000000000a"
|
||||
assert nwGUI.theProject.tree["0000000000015"].itemClass == nwItemClass.CHARACTER
|
||||
assert nwGUI.openDocument("0000000000015")
|
||||
assert nwGUI.docEditor.getText() == "# New Note\n\n"
|
||||
|
||||
# Make sure the sibling folder bug trap works
|
||||
nwTree.setSelectedHandle("0000000000013")
|
||||
nwGUI.theProject.tree["0000000000013"].setParent(None) # This should not happen
|
||||
caplog.clear()
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert "Internal error" in caplog.text
|
||||
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
|
||||
nwTree._addTrashRoot()
|
||||
nwTree.projTree._addTrashRoot()
|
||||
trashHandle = nwGUI.theProject.trashFolder()
|
||||
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
|
||||
|
||||
# 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, "question", 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
|
||||
assert nwTree.moveTreeItem(1) is False
|
||||
assert nwTree.projTree.moveTreeItem(1) is False
|
||||
|
||||
# Create a project
|
||||
prjDir = os.path.join(fncDir, "project")
|
||||
@@ -164,42 +179,33 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||
|
||||
# Add some files
|
||||
nwTree.setSelectedHandle("000000000000d")
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"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
|
||||
nwTree.clearSelection()
|
||||
assert nwTree.moveTreeItem(1) is False
|
||||
nwTree.projTree.clearSelection()
|
||||
assert nwTree.projTree.moveTreeItem(1) is False
|
||||
|
||||
# Move second item up twice (should give same result)
|
||||
nwTree.setSelectedHandle("000000000000f")
|
||||
assert nwTree.moveTreeItem(-1) is True
|
||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000f", "000000000000e",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
]
|
||||
assert nwTree.moveTreeItem(-1) is False
|
||||
assert nwTree.projTree.moveTreeItem(-1) is False
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000f", "000000000000e",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
]
|
||||
|
||||
# Restore via menu entry
|
||||
nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger)
|
||||
# Restore
|
||||
assert nwTree.projTree.moveTreeItem(1) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
@@ -207,19 +213,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||
|
||||
# Move fifth item down twice (should give same result)
|
||||
nwTree.setSelectedHandle("0000000000011")
|
||||
assert nwTree.moveTreeItem(1) is True
|
||||
assert nwTree.projTree.moveTreeItem(1) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000012", "0000000000011",
|
||||
]
|
||||
assert nwTree.moveTreeItem(1) is False
|
||||
assert nwTree.projTree.moveTreeItem(1) is False
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000012", "0000000000011",
|
||||
]
|
||||
|
||||
# Restore via menu entry
|
||||
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
|
||||
# Restore
|
||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
@@ -227,12 +233,12 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||
|
||||
# Move down again, and restore via undo
|
||||
nwTree.setSelectedHandle("0000000000011")
|
||||
assert nwTree.moveTreeItem(1) is True
|
||||
assert nwTree.projTree.moveTreeItem(1) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000012", "0000000000011",
|
||||
]
|
||||
nwGUI.mainMenu.aMoveUndo.activate(QAction.Trigger)
|
||||
assert nwTree.projTree.undoLastMove() is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
@@ -245,15 +251,15 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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, "question", 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
|
||||
assert nwTree.emptyTrash() is False
|
||||
@@ -289,9 +295,9 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
||||
|
||||
# Add some files
|
||||
nwTree.setSelectedHandle("000000000000d")
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
||||
"000000000000d", "000000000000e", "000000000000f",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
@@ -304,13 +310,13 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||
|
||||
# No selection made
|
||||
nwTree.clearSelection()
|
||||
nwTree.projTree.clearSelection()
|
||||
caplog.clear()
|
||||
assert nwTree.deleteItem() is False
|
||||
assert "no item to delete" in caplog.text
|
||||
|
||||
# Not a valid handle
|
||||
nwTree.clearSelection()
|
||||
nwTree.projTree.clearSelection()
|
||||
caplog.clear()
|
||||
assert nwTree.deleteItem("0000000000000") is False
|
||||
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
|
||||
funcPointer = nwTree._addTrashRoot
|
||||
nwTree._addTrashRoot = lambda *a: None
|
||||
funcPointer = nwTree.projTree._addTrashRoot
|
||||
nwTree.projTree._addTrashRoot = lambda *a: None
|
||||
assert nwTree.deleteItem("0000000000012") is False
|
||||
nwTree._addTrashRoot = funcPointer
|
||||
nwTree.projTree._addTrashRoot = funcPointer
|
||||
|
||||
# Delete last two documents, which also adds the trash folder
|
||||
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
|
||||
nwTree.setSelectedHandle("0000000000009")
|
||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
|
||||
nwTree.setSelectedHandle("0000000000014")
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.newTreeItem(nwItemType.FILE) is True
|
||||
assert nwTree.projTree.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", "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
|
||||
nwTree.setSelectedHandle("0000000000009")
|
||||
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
|
||||
assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True
|
||||
assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009", "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"))
|
||||
|
||||
# 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"))
|
||||
|
||||
# Clean up
|
||||
@@ -449,3 +455,95 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
|
||||
nwGUI.closeProject()
|
||||
|
||||
# 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
|
||||
|
||||
@@ -40,7 +40,7 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
cHandle = nwGUI.theProject.newFile("A Note", "000000000000a")
|
||||
newDoc = NWDoc(nwGUI.theProject, cHandle)
|
||||
newDoc.writeDocument("# A Note\n\n")
|
||||
nwGUI.treeView.revealNewTreeItem(cHandle)
|
||||
nwGUI.projView.revealNewTreeItem(cHandle)
|
||||
nwGUI.rebuildIndex(beQuiet=True)
|
||||
|
||||
# Reference Time
|
||||
|
||||
@@ -93,82 +93,82 @@ def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
|
||||
assert thePalette.link().color() == QColor(44, 152, 247)
|
||||
assert thePalette.linkVisited().color() == QColor(44, 152, 247)
|
||||
|
||||
assert nwGUI.theTheme.statNone == [150, 152, 150]
|
||||
assert nwGUI.theTheme.statSaved == [39, 135, 78]
|
||||
assert nwGUI.theTheme.statUnsaved == [138, 32, 32]
|
||||
assert nwGUI.mainTheme.statNone == [150, 152, 150]
|
||||
assert nwGUI.mainTheme.statSaved == [39, 135, 78]
|
||||
assert nwGUI.mainTheme.statUnsaved == [138, 32, 32]
|
||||
|
||||
# Check Syntax Colours
|
||||
assert nwGUI.theTheme.colBack == [45, 45, 45]
|
||||
assert nwGUI.theTheme.colText == [204, 204, 204]
|
||||
assert nwGUI.theTheme.colLink == [102, 153, 204]
|
||||
assert nwGUI.theTheme.colHead == [102, 153, 204]
|
||||
assert nwGUI.theTheme.colHeadH == [102, 153, 204]
|
||||
assert nwGUI.theTheme.colEmph == [249, 145, 57]
|
||||
assert nwGUI.theTheme.colDialN == [242, 119, 122]
|
||||
assert nwGUI.theTheme.colDialD == [153, 204, 153]
|
||||
assert nwGUI.theTheme.colDialS == [255, 204, 102]
|
||||
assert nwGUI.theTheme.colHidden == [153, 153, 153]
|
||||
assert nwGUI.theTheme.colKey == [242, 119, 122]
|
||||
assert nwGUI.theTheme.colVal == [204, 153, 204]
|
||||
assert nwGUI.theTheme.colSpell == [242, 119, 122]
|
||||
assert nwGUI.theTheme.colError == [153, 204, 153]
|
||||
assert nwGUI.theTheme.colRepTag == [102, 204, 204]
|
||||
assert nwGUI.theTheme.colMod == [249, 145, 57]
|
||||
assert nwGUI.mainTheme.colBack == [45, 45, 45]
|
||||
assert nwGUI.mainTheme.colText == [204, 204, 204]
|
||||
assert nwGUI.mainTheme.colLink == [102, 153, 204]
|
||||
assert nwGUI.mainTheme.colHead == [102, 153, 204]
|
||||
assert nwGUI.mainTheme.colHeadH == [102, 153, 204]
|
||||
assert nwGUI.mainTheme.colEmph == [249, 145, 57]
|
||||
assert nwGUI.mainTheme.colDialN == [242, 119, 122]
|
||||
assert nwGUI.mainTheme.colDialD == [153, 204, 153]
|
||||
assert nwGUI.mainTheme.colDialS == [255, 204, 102]
|
||||
assert nwGUI.mainTheme.colHidden == [153, 153, 153]
|
||||
assert nwGUI.mainTheme.colKey == [242, 119, 122]
|
||||
assert nwGUI.mainTheme.colVal == [204, 153, 204]
|
||||
assert nwGUI.mainTheme.colSpell == [242, 119, 122]
|
||||
assert nwGUI.mainTheme.colError == [153, 204, 153]
|
||||
assert nwGUI.mainTheme.colRepTag == [102, 204, 204]
|
||||
assert nwGUI.mainTheme.colMod == [249, 145, 57]
|
||||
|
||||
# Test Icon class
|
||||
theIcons = nwGUI.theTheme.theIcons
|
||||
iconCache = nwGUI.mainTheme.iconCache
|
||||
novelwriter.CONFIG.guiIcons = "invalid"
|
||||
assert theIcons.updateTheme() is True
|
||||
assert iconCache.updateTheme() is True
|
||||
assert novelwriter.CONFIG.guiIcons == "typicons_light"
|
||||
|
||||
# Ask for a non-existent key
|
||||
anImg = theIcons.loadDecoration("nonsense", 20, 20)
|
||||
anImg = iconCache.loadDecoration("nonsense", 20, 20)
|
||||
assert isinstance(anImg, QPixmap)
|
||||
assert anImg.isNull()
|
||||
|
||||
# Add a non-existent file and request it
|
||||
theIcons.DECO_MAP["nonsense"] = "nofile.jpg"
|
||||
anImg = theIcons.loadDecoration("nonsense", 20, 20)
|
||||
iconCache.IMAGE_MAP["nonsense"] = "nofile.jpg"
|
||||
anImg = iconCache.loadDecoration("nonsense", 20, 20)
|
||||
assert isinstance(anImg, QPixmap)
|
||||
assert anImg.isNull()
|
||||
|
||||
# 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 not anImg.isNull()
|
||||
assert anImg.width() == 20
|
||||
assert anImg.height() >= 56
|
||||
|
||||
anImg = theIcons.loadDecoration("wiz-back", None, 70)
|
||||
anImg = iconCache.loadDecoration("wiz-back", None, 70)
|
||||
assert isinstance(anImg, QPixmap)
|
||||
assert not anImg.isNull()
|
||||
assert anImg.height() == 70
|
||||
assert anImg.width() >= 24
|
||||
|
||||
anImg = theIcons.loadDecoration("wiz-back", 30, 70)
|
||||
anImg = iconCache.loadDecoration("wiz-back", 30, 70)
|
||||
assert isinstance(anImg, QPixmap)
|
||||
assert not anImg.isNull()
|
||||
assert anImg.height() == 70
|
||||
assert anImg.width() == 30
|
||||
|
||||
anImg = theIcons.loadDecoration("wiz-back", None, None)
|
||||
anImg = iconCache.loadDecoration("wiz-back", None, None)
|
||||
assert isinstance(anImg, QPixmap)
|
||||
assert not anImg.isNull()
|
||||
assert anImg.height() >= 1500
|
||||
assert anImg.width() >= 500
|
||||
|
||||
# Load icons
|
||||
anIcon = theIcons.getIcon("nonsense")
|
||||
anIcon = iconCache.getIcon("nonsense")
|
||||
assert isinstance(anIcon, QIcon)
|
||||
assert anIcon.isNull()
|
||||
|
||||
anIcon = theIcons.getIcon("novelwriter")
|
||||
anIcon = iconCache.getIcon("novelwriter")
|
||||
assert isinstance(anIcon, QIcon)
|
||||
assert not anIcon.isNull()
|
||||
|
||||
# Check return empty icon if file not found
|
||||
theIcons.ICON_KEYS.add("testicon3")
|
||||
anIcon = theIcons.getIcon("testicon3")
|
||||
iconCache.ICON_KEYS.add("testicon3")
|
||||
anIcon = iconCache.getIcon("testicon3")
|
||||
assert isinstance(anIcon, QIcon)
|
||||
assert anIcon.isNull()
|
||||
|
||||
|
||||