diff --git a/.flake8 b/.flake8
index adf39902..15525dce 100644
--- a/.flake8
+++ b/.flake8
@@ -1,4 +1,5 @@
[flake8]
-ignore = E133,E221,E226,E228,E241,W503
+ignore = E133,E221,E226,E228,E241,W503,ANN101,ANN102,ANN401
+per-file-ignores=tests/*:ANN
max-line-length = 99
exclude = docs/*
diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml
index 1a8a3dd7..7a2f2844 100644
--- a/.github/workflows/syntax.yml
+++ b/.github/workflows/syntax.yml
@@ -4,11 +4,11 @@ on:
push:
branches:
- main
- - releases/**
+ - release
pull_request:
branches:
- main
- - releases/**
+ - release
jobs:
checkSyntax:
@@ -22,9 +22,9 @@ jobs:
- name: Checkout Source
uses: actions/checkout@v4
- name: Install flake8
- run: pip install flake8 flake8-pep585
+ run: pip install -r requirements-dev.txt
- name: Syntax Check
run: |
flake8 --version
flake8 novelwriter --count --show-source --statistics
- flake8 tests --count --show-source --statistics
+ flake8 tests --count --show-source --statistics --extend-ignore ANN
diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml
index f15cc424..2ca682ca 100644
--- a/.github/workflows/test_linux.yml
+++ b/.github/workflows/test_linux.yml
@@ -4,11 +4,11 @@ on:
push:
branches:
- main
- - releases/**
+ - release
pull_request:
branches:
- main
- - releases/**
+ - release
jobs:
testLinux:
@@ -38,4 +38,6 @@ jobs:
export QT_QPA_PLATFORM=offscreen
pytest -v --cov=novelwriter --timeout=60
- name: Upload to Codecov
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@v4
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml
index eb176743..de4aebc5 100644
--- a/.github/workflows/test_mac.yml
+++ b/.github/workflows/test_mac.yml
@@ -4,11 +4,11 @@ on:
push:
branches:
- main
- - releases/**
+ - release
pull_request:
branches:
- main
- - releases/**
+ - release
jobs:
testMac:
@@ -32,4 +32,6 @@ jobs:
export QT_QPA_PLATFORM=offscreen
pytest -v --cov=novelwriter --timeout=60
- name: Upload to Codecov
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@v4
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/test_win.yml b/.github/workflows/test_win.yml
index 09d935ea..9cb28ba0 100644
--- a/.github/workflows/test_win.yml
+++ b/.github/workflows/test_win.yml
@@ -4,11 +4,11 @@ on:
push:
branches:
- main
- - releases/**
+ - release
pull_request:
branches:
- main
- - releases/**
+ - release
jobs:
testWin:
@@ -28,4 +28,6 @@ jobs:
run: |
pytest -v --cov=novelwriter --timeout=60
- name: Upload to Codecov
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@v4
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4737c3b8..50d50e09 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,14 +22,13 @@ just make a pull request directly.
## Picking the Correct Branch for a Pull Request
-As of April 2024, pre-releases and point releases (like 2.x) are made from the `main` branch. The
-`main` branch is the default branch. For general changes, please make a new branch in your own fork
-from the current `main` branch. Do not make pull requests from your copy of the `main` branch.
+The `main` branch is the default branch. For general changes, please make a new branch in your own
+fork from the current `main` branch. Do not make pull requests from your copy of the `main` branch.
-If you are submitting a fix to a current release, say 2.4, you must do so from the correct release
-branch. For 2.4 this is `releases/v2.4`. If you make such a fix on the `main` branch, **it cannot
-be included in a 2.4.x release**. This also applies to the current documentation published on the
-main website.
+As of April 2024, releases are made from the `release` branch. If you are submitting a fix to a
+current release you must do so from the `release` branch. If you make such a fix on the `main`
+branch, **it cannot be included in a patch release**. This also applies to the documentation for
+the latest release published on the main website.
**Also check the following:**
diff --git a/README.md b/README.md
index f7323f56..041126c5 100644
--- a/README.md
+++ b/README.md
@@ -49,9 +49,9 @@ code, please also read the full
Project credits are available in [CREDITS.md](https://github.com/vkbo/novelWriter/blob/main/CREDITS.md).
-**Note:** As of April 2024, pre-releases and point releases (2.x) are made from the `main` branch.
-Patches are made from `releases/v2.x` branches. So if you're submitting a fix to a current release,
-**including changes to documentation**, they must be made to the correct branch.
+**Note:** As of April 2024 only pre-releases are made from the `main` branch. Full releases are
+made from the `release` branch. So if you're submitting a fix to a current release, **including
+changes to documentation**, they must be made to the `release` branch.
### Translations
diff --git a/docs/FileFormatSpec-1.5.fodt b/docs/FileFormatSpec-1.5.fodt
index 5721dff1..b3a392aa 100644
--- a/docs/FileFormatSpec-1.5.fodt
+++ b/docs/FileFormatSpec-1.5.fodt
@@ -1,24 +1,24 @@
- Veronica Olsen2022-11-05T18:25:44.8874731592024-03-11T11:06:21.968626872PT4H19M4S57LibreOffice/7.4.7.2$Linux_X86_64 LibreOffice_project/40$Build-2
+ Veronica Olsen2022-11-05T18:25:44.8874731592024-04-13T01:32:31.183366872PT4H34M31S61LibreOffice/7.4.7.2$Linux_X86_64 LibreOffice_project/40$Build-2Veronica Olsen
- 141
+ 1006120
- 27406
- 16198
+ 26857
+ 22264truefalseview2
- 8186
- 12282
+ 14092
+ 1113910
- 141
- 27404
- 16337
+ 100612
+ 26855
+ 12287401false
@@ -90,7 +90,7 @@
truetrue
- 928650
+ 1056439truefalse
@@ -158,7 +158,7 @@
-
+
@@ -682,6 +682,15 @@
+
+
+
+
+
+
+
+
+
@@ -1130,147 +1139,163 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1290,78 +1315,87 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
@@ -1440,21 +1474,24 @@
novelWriter
- Project File Format 1.5 Specification
- This document covers the file format specification for the 1.5 file format for novelWriter project files. See the documentation1
- See: https://docs.novelwriter.io for a full description of the app’s functionality. The file format was intrioduced in Release 2.0.
+ Project File Format 1.5 Specification
+ This document covers the file format specification for the 1.5 file format for novelWriter project files. See the documentation1
+ See: https://docs.novelwriter.io for a full description of the app’s functionality. The file format was intrioduced in Release 2.0.The target audience of this document is developers who intend to write a tool or script that generates valid novelWriter project files from data from other applications, or for templating purposes.
- Note that flags are generally written out as yes/no values, but also true/false and on/off are understood by the application. The None value that occasionally occurs in actual project files is the text representation of the Python None value, and indicates that there is no value set.
+ Note that flags are generally written out as yes/no values, but also true/false and on/off are understood by the application. The None value that occasionally occurs in actual project files is the text representation of the Python None value, and indicates that there is no value set.Revisions
- From Release 2.1 Beta 1: Removed the titleFormat node from settings.
+ From Release 2.1 Beta 1: Removed the titleFormat node from settings.
- From Release 2.3 Beta 1: Removed the title node from project.
+ From Release 2.3 Beta 1: Removed the title node from project.
- From Release 2.3: Added TEMPLATE item class.
+ From Release 2.3: Added TEMPLATE item class.
+
+
+ From Release 2.5: Added the shape node XML Root
@@ -1478,7 +1515,7 @@
- appVersion
+ appVersionNo
@@ -1489,7 +1526,7 @@
- hexVersion
+ hexVersionNo
@@ -1500,18 +1537,18 @@
- fileVersion
+ fileVersionYes
- The file format version used when writing the project file. This determines how the file is parsed, so it is important that it is set correctly. Valid values are “1.0”, “1.1”, “1.2”, “1.3”, “1.4”, and “1.5”.
+ The file format version used when writing the project file. This determines how the file is parsed, so it is important that it is set correctly. Valid values are “1.0”, “1.1”, “1.2”, “1.3”, “1.4”, and “1.5”.
- fileRevision
+ fileRevisionNo
@@ -1522,7 +1559,7 @@
- timeStamp
+ timeStampNo
@@ -1533,12 +1570,12 @@
Example
- <novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1"
- fileVersion="1.5" fileRevision="1"
- timeStamp="2022-11-05 17:46:51">
+ <novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1"
+ fileVersion="1.5" fileRevision="1"
+ timeStamp="2022-11-05 17:46:51">Project NodeTag: project
- The project section is a level one node under the root. It stores the primary settings for the project. The only required values are the id attribute and the name value. All other entries are set to default values during parsing if ommitted from the XML.
+ The project section is a level one node under the root. It stores the primary settings for the project. The only required values are the id attribute and the name value. All other entries are set to default values during parsing if ommitted from the XML.Attributes
@@ -1557,18 +1594,18 @@
- id
+ id
- Yes
+ Yes
- A UUID that is unique for the project and does not change during the lifetime of the project. It is used by novelWriter to keep track of temporary files and to associate cross-project settings with specific projects without relying on the project name.
+ A UUID that is unique for the project and does not change during the lifetime of the project. It is used by novelWriter to keep track of temporary files and to associate cross-project settings with specific projects without relying on the project name.
- saveCount
+ saveCountNo
@@ -1579,7 +1616,7 @@
- autoCount
+ autoCountNo
@@ -1590,7 +1627,7 @@
- editTime
+ editTimeNo
@@ -1618,48 +1655,48 @@
- name
+ name
- Yes
+ Yes
- The name the user has given to the project.
+ The name the user has given to the project.
- title
+ title
- No
+ No
- Removed in Revision 2.
+ Removed in Revision 2.
- author
+ authorNo
- The name of the novel author or authors. The value is used in some places in the user interface, and can be added to manuscript builds.
+ The name of the novel author or authors. The value is used in some places in the user interface, and can be added to manuscript builds.Example
- <project id="e2be99af-f9bf-4403-857a-c3d1ac25abea"
- saveCount="5"autoCount="10"editTime="1000">
- <name>Sample Project</name>
- <author>Jane Smith</author>
+ <project id="e2be99af-f9bf-4403-857a-c3d1ac25abea"
+ saveCount="5"autoCount="10"editTime="1000">
+ <name>Sample Project</name>
+ <author>Jane Smith</author></project>
-
- Settings Node
+
+ Settings NodeTag: settings
- The settings section is a level one node under the root. It stores the various runtime values for the project, and the settings available from the Project Settings panel. None of these settings are required, and if missing, will be set to default values. The entire settings section can thus be ommitted.
+ The settings section is a level one node under the root. It stores the various runtime values for the project, and the settings available from the Project Settings panel. None of these settings are required, and if missing, will be set to default values. The entire settings section can thus be ommitted.AttributesNoneValues
@@ -1680,18 +1717,18 @@
- doBackup
+ doBackup
- No
+ No
- User-controlled setting for whether or not to run backup when the project is closed.
+ User-controlled setting for whether or not to run backup when the project is closed.
- language
+ languageNo
@@ -1702,7 +1739,7 @@
- spellChecking
+ spellCheckingNo
@@ -1715,18 +1752,18 @@
- lastHandle
+ lastHandleNo
- An auto-generated list of the last documents open in the editor and viewer, and which novel root folder was last viewed in the Novel Tree and Outline View. These are saved as key/value pairs. See separate section for how they are stored as XML.
+ An auto-generated list of the last documents open in the editor and viewer, and which novel root folder was last viewed in the Novel Tree and Outline View. These are saved as key/value pairs. See separate section for how they are stored as XML.
- autoReplace
+ autoReplaceNo
@@ -1737,7 +1774,7 @@
- titleFormat
+ titleFormatNo
@@ -1748,39 +1785,39 @@
- status
+ statusNo
- The status labels as defined by the user in Project Settings. These are saved as special key/value pars. See separate section for how they are stored as XML.
+ The status labels as defined by the user in Project Settings. These are saved as special key/value pars. See separate section for how they are stored as XML.
- importance
+ importanceNo
- The importance labels as defined by the user in Project Settings. These are saved as special key/value pars. See separate section for how they are stored as XML.
+ The importance labels as defined by the user in Project Settings. These are saved as special key/value pars. See separate section for how they are stored as XML.Example
- <settings>
- <doBackup>yes</doBackup>
- <language>en_GB</language>
- <spellChecking auto="yes">en_GB</spellChecking>
- <lastHandle />
- <autoReplace />
- <status />
- <importance />
- </settings>
-
- Key/Value Nodes
+ <settings>
+ <doBackup>yes</doBackup>
+ <language>en_GB</language>
+ <spellChecking auto="yes">en_GB</spellChecking>
+ <lastHandle />
+ <autoReplace />
+ <status />
+ <importance />
+ </settings>
+
+ Key/Value NodesThe key/value nodes are used for lastHandle, autoReplace and titleFormat settings. The lookup key is stored as an attribute, and the value is the text of the node.Attributes
@@ -1800,25 +1837,25 @@
- key
+ key
- Yes
+ Yes
- The lookup key.
+ The lookup key.Example
- <lastHandle>
- <entry key="editor">636b6aa9b697b</entry>
- <entry key="viewer">636b6aa9b697b</entry>
- <entry key="novelTree">7031beac91f75</entry>
- <entry key="outline">7031beac91f75</entry>
- </lastHandle>
+ <lastHandle>
+ <entry key="editor">636b6aa9b697b</entry>
+ <entry key="viewer">636b6aa9b697b</entry>
+ <entry key="novelTree">7031beac91f75</entry>
+ <entry key="outline">7031beac91f75</entry>
+ </lastHandle>Status/Importance Key/Value Nodes
- The status and importance settings are stored as key/value nodes with additional data attributes. The label of the status or importance setting is saved as the node text value.
+ The status and importance settings are stored as key/value nodes with additional data attributes. The label of the status or importance setting is saved as the node text value.Attributes
@@ -1837,18 +1874,18 @@
- key
+ key
- Yes
+ Yes
- The lookup key. For status labels, it must consist of an “s” followed by six hexadecimal numbers. For importance labels it must consist of an “i” followed by six hexadecimal numbers. Each key string must be unique. Internally, they are random numbers, but also sequential values are valid.
+ The lookup key. For status labels, it must consist of an “s” followed by six hexadecimal numbers. For importance labels it must consist of an “i” followed by six hexadecimal numbers. Each key string must be unique. Internally, they are random numbers, but also sequential values are valid.
- count
+ countNo
@@ -1859,7 +1896,7 @@
- red
+ redYes
@@ -1870,47 +1907,61 @@
- green
+ greenYes
- A number between 0 and 255 representing the green component of the label colour.
+ A number between 0 and 255 representing the green component of the label colour.
- blue
+ blueYes
- A number between 0 and 255 representing the blue component of the label colour.
+ A number between 0 and 255 representing the blue component of the label colour.
+
+
+
+
+ shape
+
+
+ No
+
+
+ An icon shape. Allowed vaues are SQUARE, TRIANGLE, NABLA, DIAMOND, PENTAGON, HEXAGON, STAR, PACMAN, CIRCLE_Q, CIRCLE_H, CIRCLE_T, CIRCLE, BARS_1, BARS_2, BARS_3, BARS_4, BLOCK_1, BLOCK_2, BLOCK_3, BLOCK_4. Defaults to SQUARE.
+
+ Added in Revesion 4.
- Example
- <status>
- <entry key="sf12341"count="4"
- red="100"green="100"blue="100">New</entry>
- <entry key="sd51c5b"count="0"
- red="193"green="129"blue="0">Draft</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>
- <entry key="icfb3a5"count="2"
- red="0"green="122"blue="188">Minor</entry>
- <entry key="i2d7a54"count="2"
- red="21"green="0"blue="180">Major</entry>
- </importance>
- Content Node
+
+ Example
+ <status>
+ <entry key="sf12341"count="4" red="100"green="100"blue="100"
+ shape="SQUARE">New</entry>
+ <entry key="sd51c5b"count="0" red="193"green="129"blue="0"
+ shape="SQUARE">Draft</entry>
+ <entry key="s78ea90"count="1" red="58"green="180"blue="58"
+ shape="SQUARE">Finished</entry>
+ </status>
+ <importance>
+ <entry key="ia857f0"count="5" red="100"green="100"blue="100"
+ shape="CIRCLE">None</entry>
+ <entry key="icfb3a5"count="2" red="0"green="122"blue="188"
+ shape="CIRCLE">Minor</entry>
+ <entry key="i2d7a54"count="2" red="21"green="0"blue="180"
+ shape="CIRCLE">Major</entry>
+ </importance>
+ Content NodeTag: content
- The content section is a level one node under the root. It stores all the project items of the project. The items are stored in the order in which they appear in the project tree, so altering the order will affect the project structure. The attribute with the order number is not used when processing the data. It is written to the XML for debugging purposes.
+ The content section is a level one node under the root. It stores all the project items of the project. The items are stored in the order in which they appear in the project tree, so altering the order will affect the project structure. The attribute with the order number is not used when processing the data. It is written to the XML for debugging purposes.Attributes
@@ -1929,35 +1980,35 @@
- items
+ itemsNo
- The number of project items in the content section.
+ The number of project items in the content section.
- novelWords
+ novelWordsNo
- The number of words in total for all document nodes with layout DOCUMENT. The value defaults to 0, but this number forms the basis of computing writing statistics during a session, so the number should be properly set to ensure correct statistics.
+ The number of words in total for all document nodes with layout DOCUMENT. The value defaults to 0, but this number forms the basis of computing writing statistics during a session, so the number should be properly set to ensure correct statistics.
- notesWords
+ notesWordsNo
- The number of words in total for all document nodes with layout NOTE. The value defaults to 0, but this number forms the basis of computing writing statistics during a session, so the number should be properly set to ensure correct statistics.
+ The number of words in total for all document nodes with layout NOTE. The value defaults to 0, but this number forms the basis of computing writing statistics during a session, so the number should be properly set to ensure correct statistics.
@@ -1979,32 +2030,32 @@
- item
+ item
- No
+ No
- A node representing a project item.
+ A node representing a project item.Example
- <content items="27"novelWords="954"notesWords="409">
- <item handle="7031beac91f75"parent="None"root="7031beac91f75"
- order="0"type="ROOT"class="NOVEL">
- <meta expanded="yes"/>
- <name status="sc24b8f"import="ia857f0">Novel</name>
- </item>
- ...
- </content>
- ItemNodes
- The item nodes make up the actual project content of the project. Each node represents either a root folder, a regular folder, or a document. Each node has a type, class and layout setting that determine its category. Each item is given a handle that is a random hexadecimal string of length 13.
- For the items that are document files, the handle corresponds to its expected filename. It is therefore important that they match in generated projects. Each document node is expected to correspond to a file in the contents folder of the project named “content/7031beac91f75.nwd” for the item handle “7031beac91f75”. If there is no such file, the document item is assumed to contain no text, and a file is created when the user tries to save text to it.
- Each item node has a meta data node and a name node. The meta data node contains only collected information, and is thus not strictly required. However, an accurate word count and setting the correct icon in the project tree depend on these values. These values are set by the indexer class, so rebuilding the index should restore the data.
- The name nod
- e contains the primary user defined settings for an item, and is required for the entry to be valid.
- Attributes
+ <content items="27"novelWords="954"notesWords="409">
+ <item handle="7031beac91f75"parent="None"root="7031beac91f75"
+ order="0"type="ROOT"class="NOVEL">
+ <meta expanded="yes"/>
+ <name status="sc24b8f"import="ia857f0">Novel</name>
+ </item>
+ ...
+ </content>
+ ItemNodes
+ The item nodes make up the actual project content of the project. Each node represents either a root folder, a regular folder, or a document. Each node has a type, class and layout setting that determine its category. Each item is given a handle that is a random hexadecimal string of length 13.
+ For the items that are document files, the handle corresponds to its expected filename. It is therefore important that they match in generated projects. Each document node is expected to correspond to a file in the contents folder of the project named “content/7031beac91f75.nwd” for the item handle “7031beac91f75”. If there is no such file, the document item is assumed to contain no text, and a file is created when the user tries to save text to it.
+ Each item node has a meta data node and a name node. The meta data node contains only collected information, and is thus not strictly required. However, an accurate word count and setting the correct icon in the project tree depend on these values. These values are set by the indexer class, so rebuilding the index should restore the data.
+ The name node contains the primary user defined settings for an item, and is required for the entry to be valid.
+
+ Attributes
@@ -2022,81 +2073,81 @@
- handle
+ handle
- Yes
+ Yes
- The 13 value hexadecimal string that represents the item in the project. This is the primary identifier of a project item, and is required. It must be unique withing a project. Internally, it is generated as a random value, but it can also be sequential.
+ The 13 value hexadecimal string that represents the item in the project. This is the primary identifier of a project item, and is required. It must be unique withing a project. Internally, it is generated as a random value, but it can also be sequential.
- parent
+ parent
- Yes
+ Yes
- The handle of the parent item in the tree. This value should only be None for root folders. All other items must have a parent handle set. If the parent handle is None for an item that isn’t a root folder, the item will be treated as orphaned during project loading.
+ The handle of the parent item in the tree. This value should only be None for root folders. All other items must have a parent handle set. If the parent handle is None for an item that isn’t a root folder, the item will be treated as orphaned during project loading.
- root
+ root
- No
+ No
- The handle of the top of its hierarchy of parent items. That is, the root folder which it ultimately sits under. If the attribute is not set, it will be computed during loading. It is saved to the XML file for efficiency reasons.
+ The handle of the top of its hierarchy of parent items. That is, the root folder which it ultimately sits under. If the attribute is not set, it will be computed during loading. It is saved to the XML file for efficiency reasons.
- order
+ order
- No
+ No
- The numerical order of the item under its parent item. This value is not used during the loading process as the physical item order in the content node is used instead. It is primarily saved to the XML for debugging purposes.
+ The numerical order of the item under its parent item. This value is not used during the loading process as the physical item order in the content node is used instead. It is primarily saved to the XML for debugging purposes.
- type
+ type
- Yes
+ Yes
- The item type of the item node. Allowed values are ROOT, FOLDER and FILE.
+ The item type of the item node. Allowed values are ROOT, FOLDER and FILE.
- class
+ class
- Yes
+ Yes
- The item class of the item node. There are a number of item classes available in the app, all corresponding to a specific type of root folder. Each item in a root folder should have the same class set as the root folder itself. The attribute is not strictly required for items that aren’t root folders as it will be automatically set to match during loading. Allowed values are NOVEL, PLOT, CHARACTER, WORLD, TIMELINE, OBJECT, ENTITY, CUSTOM, ARCHIVE and TRASH.
-
- Added in Revision 3: TEMPLATE
+ The item class of the item node. There are a number of item classes available in the app, all corresponding to a specific type of root folder. Each item in a root folder should have the same class set as the root folder itself. The attribute is not strictly required for items that aren’t root folders as it will be automatically set to match during loading. Allowed values are NOVEL, PLOT, CHARACTER, WORLD, TIMELINE, OBJECT, ENTITY, CUSTOM, ARCHIVEand TRASH.
+
+ Added in Revision 3: TEMPLATE
- layout
+ layout
- Yes
+ Yes
- The item layout of the item node. Allowed values are DOCUMENT and NOTE. This is the attribute that determines if a FILE type item is a Novel Document or a Project Note.
+ The item layout of the item node. Allowed values are DOCUMENT and NOTE. This is the attribute that determines if a FILE type item is a Novel Document or a Project Note.
@@ -2118,29 +2169,29 @@
- meta
+ meta
- No
+ No
- A node of meta data attributes for the item.
+ A node of meta data attributes for the item.
- name
+ name
- Yes
+ Yes
- A node of user settings for the item.
+ A node of user settings for the item.
- MetaNodes
- The meta data collected for the current item. This data either represents the item’s last state in the app, or consists of data collected by the indexer. It can be restored by rebuilding the index, so the data is not essential.
+ MetaNodes
+ The meta data collected for the current item. This data either represents the item’s last state in the app, or consists of data collected by the indexer. It can be restored by rebuilding the index, so the data is not essential.Attributes
@@ -2159,73 +2210,73 @@
- expanded
+ expanded
- No
+ No
- Whether the tree node in the project tree was expanded or collapsed during the last session. Applies to all item types, but is ignored for items without child items.
+ Whether the tree node in the project tree was expanded or collapsed during the last session. Applies to all item types, but is ignored for items without child items.
- heading
+ heading
- No
+ No
- The heading level of the first heading of the text of the item. Only applies to FILE item types. Allowed values are H0, H1, H2, H3, and H4. Other values are reset to H0.
+ The heading level of the first heading of the text of the item. Only applies to FILE item types. Allowed values are H0, H1, H2, H3, and H4. Other values are reset to H0.
- charCount
+ charCount
- No
+ No
- The number of characters in the text of the item. Only applies to FILE item types.
+ The number of characters in the text of the item. Only applies to FILE item types.
- wordCount
+ wordCount
- No
+ No
- The number of words in the text of the item. Only applies to FILE item types.
+ The number of words in the text of the item. Only applies to FILE item types.
- paraCount
+ paraCount
- No
+ No
- The number of paragraphs in the text of the item. Only applies to FILE item types.
+ The number of paragraphs in the text of the item. Only applies to FILE item types.
- cursorPos
+ cursorPos
- No
+ No
- The last cursor position in the text of the item from the last editing session. Only applies to FILE item types. The value is used to restore the cursor position when the document is opened in the editor.
+ The last cursor position in the text of the item from the last editing session. Only applies to FILE item types. The value is used to restore the cursor position when the document is opened in the editor.
- NameNodes
- The name node contains information about an item that is set by the user. Including its label, which is the text value of the node. This is the label that is displayed in the project tree.
+ NameNodes
+ The name node contains information about an item that is set by the user. Including its label, which is the text value of the node. This is the label that is displayed in the project tree.Attributes
@@ -2244,45 +2295,45 @@
- status
+ status
- No
+ No
- The ID of the status label that has been set for this item. Defaults to the first status item defined in the status section of the settings node.
+ The ID of the status label that has been set for this item. Defaults to the first status item defined in the status section of the settings node.
- import
+ import
- No
+ No
- The ID of the importance label that has been set for this item. Defaults to the first importance item defined in the importance section of the settings node.
+ The ID of the importance label that has been set for this item. Defaults to the first importance item defined in the importance section of the settings node.
- active
+ active
- No
+ No
- The active/inactive status of the document. Only applies to FILE item types.
+ The active/inactive status of the document. Only applies to FILE item types.Example
- <item handle="53b69b83cdafc"parent="7031beac91f75"root="7031beac91f75"
- order="0"type="FILE"class="NOVEL"layout="DOCUMENT">
- <meta expanded="no"heading="H1"charCount="93"wordCount="19"
- paraCount="2"cursorPos="119"/>
- <name status="sc24b8f"import="ia857f0"active="yes">Title Page</name>
- </item>
+ <item handle="53b69b83cdafc"parent="7031beac91f75"root="7031beac91f75"
+ order="0"type="FILE"class="NOVEL"layout="DOCUMENT">
+ <meta expanded="no"heading="H1"charCount="93"wordCount="19"
+ paraCount="2"cursorPos="119"/>
+ <name status="sc24b8f"import="ia857f0"active="yes">Title Page</name>
+ </item>
\ No newline at end of file
diff --git a/docs/source/fileformatspec15.pdf b/docs/source/fileformatspec15.pdf
index 0edf908e..d23776a8 100644
Binary files a/docs/source/fileformatspec15.pdf and b/docs/source/fileformatspec15.pdf differ
diff --git a/docs/source/images/fig_build_settings_headings.png b/docs/source/images/fig_build_settings_headings.png
index a7262725..bba80c30 100644
Binary files a/docs/source/images/fig_build_settings_headings.png and b/docs/source/images/fig_build_settings_headings.png differ
diff --git a/docs/source/images/fig_editor.png b/docs/source/images/fig_editor.png
index f8aee5bb..b2f772bf 100644
Binary files a/docs/source/images/fig_editor.png and b/docs/source/images/fig_editor.png differ
diff --git a/docs/source/images/fig_manuscript_build.png b/docs/source/images/fig_manuscript_build.png
index 31605cd4..b6a92bfc 100644
Binary files a/docs/source/images/fig_manuscript_build.png and b/docs/source/images/fig_manuscript_build.png differ
diff --git a/docs/source/images/fig_manuscript_build_outline.png b/docs/source/images/fig_manuscript_build_outline.png
new file mode 100644
index 00000000..1ec9ffad
Binary files /dev/null and b/docs/source/images/fig_manuscript_build_outline.png differ
diff --git a/docs/source/images/fig_novel_tree_view.png b/docs/source/images/fig_novel_tree_view.png
index b0f92b8f..41eaf1fa 100644
Binary files a/docs/source/images/fig_novel_tree_view.png and b/docs/source/images/fig_novel_tree_view.png differ
diff --git a/docs/source/images/fig_outline_view.png b/docs/source/images/fig_outline_view.png
index 90b4f3d1..3142af04 100644
Binary files a/docs/source/images/fig_outline_view.png and b/docs/source/images/fig_outline_view.png differ
diff --git a/docs/source/images/fig_project_split_tool.png b/docs/source/images/fig_project_split_tool.png
index a7809f82..cb3c498c 100644
Binary files a/docs/source/images/fig_project_split_tool.png and b/docs/source/images/fig_project_split_tool.png differ
diff --git a/docs/source/images/fig_project_tree_view.png b/docs/source/images/fig_project_tree_view.png
index f5058038..b891d3f1 100644
Binary files a/docs/source/images/fig_project_tree_view.png and b/docs/source/images/fig_project_tree_view.png differ
diff --git a/docs/source/images/fig_viewer.png b/docs/source/images/fig_viewer.png
index 7fbdc71d..d53beee1 100644
Binary files a/docs/source/images/fig_viewer.png and b/docs/source/images/fig_viewer.png differ
diff --git a/docs/source/images/python_powered.png b/docs/source/images/python_powered.png
deleted file mode 100644
index d51df248..00000000
Binary files a/docs/source/images/python_powered.png and /dev/null differ
diff --git a/docs/source/images/screenshot_dark.png b/docs/source/images/screenshot_dark.png
index 7f581b72..03433a38 100644
Binary files a/docs/source/images/screenshot_dark.png and b/docs/source/images/screenshot_dark.png differ
diff --git a/docs/source/images/screenshot_default.png b/docs/source/images/screenshot_default.png
deleted file mode 100644
index 93ba2ac8..00000000
Binary files a/docs/source/images/screenshot_default.png and /dev/null differ
diff --git a/docs/source/images/screenshot_light.png b/docs/source/images/screenshot_light.png
new file mode 100644
index 00000000..bb7e22c7
Binary files /dev/null and b/docs/source/images/screenshot_light.png differ
diff --git a/docs/source/index.rst b/docs/source/index.rst
index b0020fc6..7c8d75c3 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -17,14 +17,12 @@ storage for robustness.
:width: 500
The project storage is suitable for version control software, and also well suited for file
-synchronisation tools. All text is saved as plain text files with a meta data header. The core
-project structure is stored in a single project XML file. Other meta data is saved as JSON files.
-See the :ref:`a_breakdown_storage` section for more details.
+synchronisation tools. All text is saved as plain text files, and your project data as standard
+data formats in XML and JSON. See :ref:`a_storage` for more details.
-Any operating system that can run Python 3 and has the Qt 5 libraries should be able to run
-novelWriter. It runs fine on Linux, Windows and MacOS, and users have tested it on other platforms
-as well. novelWriter can be run directly from the Python source, or installed from packages or with
-pip. See :ref:`a_started` for more details.
+Any operating system that has Python 3 and the Qt 5 libraries should be able to run novelWriter.
+It runs fine on Linux, Windows and MacOS, and users have tested it on other platforms as well.
+See :ref:`a_started` for more details.
**Useful Links**
@@ -53,7 +51,6 @@ pip. See :ref:`a_started` for more details.
int_overview
int_started
int_howto
- int_customise
int_glossary
.. toctree::
@@ -83,6 +80,7 @@ pip. See :ref:`a_started` for more details.
:caption: Additional Details
:hidden:
+ more_customise
more_projectformat
more_counting
diff --git a/docs/source/int_howto.rst b/docs/source/int_howto.rst
index fe5b22fa..bab60b39 100644
--- a/docs/source/int_howto.rst
+++ b/docs/source/int_howto.rst
@@ -19,9 +19,9 @@ Managing the Project
.. dropdown:: Merge Multiple Documents Into One
:animate: fade-in-slide-down
- If you need to merge a set of documents in your project into a single document, you can achieve
- this by first making a new folder for just that purpose, and drag all the files you want merged
- into this folder. Then you can right click the folder, select :guilabel:`Transform` and
+ If you need to merge a selection of documents in your project into a single document, you can
+ achieve this by first making a new folder for just that purpose, and drag all the files you want
+ merged into this folder. Then you can right click the folder, select :guilabel:`Transform` and
:guilabel:`Merge Documents in Folder`.
In the dialog that pops up, the documents will be in the same order as in the folder, but you
@@ -36,7 +36,7 @@ Layout Tricks
The formatting tools available in novelWriter don't allow for complex structures like tables.
However, the editor does render tabs in a similar way that regular word processors do. You can
- set the width of a tab in :guilabel:`Preferences`.
+ set the width of a tab in **Preferences**.
The tab key should have the same distance in the editor as in the viewer, so you can align text
in columns using the tab key, and it should look the same when viewed next to the editor.
@@ -66,18 +66,13 @@ Organising Your Text
Depending on your writing style, you may need to separate between soft and hard scene breaks
within chapters. Like for instance if you switch point-of-view character often.
- In such cases you may want to use a scene heading for hard scene breaks and a section heading
- for soft scene breaks. The :guilabel:`Build Manuscript` tool will let you add separate
- formatting for the two when you generate your manuscript. You can for instance add the common
- "``* * *``" for hard breaks and select to hide section breaks, which will just insert an empty
- paragraph in their place. See :ref:`a_manuscript_settings` for more details.
-
- Keep in mind that this is not what the section heading is intended for, so the app will not
- understand the section heading as a scene, but it will be formatted correctly in the manuscript.
+ In such cases you may want to use different scene headings for hard and soft scene breaks. The
+ **Build Manuscript** tool will let you define a different format for scenes using the ``###``
+ and ``###!`` heading codes when you generate your manuscript. You can for instance add the
+ common "``* * *``" for hard breaks and select to soft scene breaks, which will just insert an
+ empty paragraph in their place. See :ref:`a_manuscript_settings` for more details.
.. versionadded:: 2.4
- You can now distinguish between soft and hard scene breaks with a modified scene header. See
- :ref:`a_manuscript_settings_head_hard` for more details.
Other Tools
diff --git a/docs/source/int_introduction.rst b/docs/source/int_introduction.rst
index a23810bd..c67588e7 100644
--- a/docs/source/int_introduction.rst
+++ b/docs/source/int_introduction.rst
@@ -1,15 +1,21 @@
.. _a_intro:
-************
-Key Features
-************
+********
+Overview
+********
.. _Snowflake: https://www.advancedfictionwriting.com/articles/snowflake-method/
.. _Markdown: https://en.wikipedia.org/wiki/Markdown
-At its core, novelWriter is a multi-document plain text editor. It uses a markup syntax inspired by
-Markdown_ to apply simple formatting to the text. It also allows for some extended formatting codes
-using a shortcode format. See :ref:`a_fmt_shortcodes` for more details.
+At its core, novelWriter is a multi-document plain text editor. The idea is to let you edit your
+text without having to deal with formatting until you generate a draft document or manuscript.
+Instead, you can focus on the writing right from the start.
+
+Of course, you probably need *some* formatting for your text. At the very least you need emphasis.
+Most people are familiar with adding emphasis using underscores and asterisks. This formatting
+standard comes from Markdown_ and is supported by novelWriter. It also uses Markdown formatting for
+defining document headings. If you need more specialised formatting, additional formatting options
+are available using a shortcode format. See :ref:`a_fmt_shortcodes` for more details.
.. admonition:: Limitations
@@ -17,22 +23,28 @@ using a shortcode format. See :ref:`a_fmt_shortcodes` for more details.
those relevant for this purpose. It is **not** suitable for technical writing, and it is **not**
a full-featured Markdown editor.
- It is also not intended as a tool to organise research for writing, and therefore lacks
+ It is also not intended as a tool for organising research for writing, and therefore lacks
formatting features you may need for this purpose. The notes feature in novelWriter is mainly
intended for character profiles and plot outlines.
-Your novel project is organised as a collection of separate plain text documents instead of a
-single, large document. The idea is to make it easier to reorganise your project structure without
-having to cut and paste text between chapters.
+Your novel project in novelWriter is organised as a collection of separate plain text documents
+instead of a single, large document. The idea is to make it easier to reorganise your project
+structure without having to cut and paste text between chapters and scenes.
There are two kinds of documents in your project: :term:`Novel Documents` are documents that are
part of your story. The other kind of documents are :term:`Project Notes`. These are intended for
your notes about your characters, your world building, and so on.
You can at any point split the individual documents by their headings up into multiple documents,
-or merge multiple documents into single documents. This makes it easier to use variations of the
+or merge multiple documents into a single document. This makes it easier to use variations of the
Snowflake_ method for writing. You can start by writing larger structure-focused documents, like
-one per act for instance, and later effortlessly split these up into chapters or scenes.
+for instance one document per act, and later effortlessly split these up into chapters or scenes.
+
+
+.. _a_intro_features:
+
+Key Features
+============
Below are some key features of novelWriter.
@@ -40,12 +52,12 @@ Below are some key features of novelWriter.
The aim of the user interface is to let you focus on writing instead of spending time formatting
text. Formatting is therefore limited to a small set of formatting tags for simple things like
text emphasis and paragraph alignment. When you really want to focus on just writing, you can
- switch the editor into :guilabel:`Focus Mode` where only the text editor panel itself is
- visible, and the project structure view is hidden away.
+ switch the editor into **Focus Mode** where only the text editor panel itself is visible, and
+ the project structure view is hidden away.
**Keep an eye on your notes**
- The main window can optionally show a document viewer to the right of the editor. This view
- panel is intended for displaying another scene document, your character notes, plot notes, or any
+ The main window can optionally show a document viewer to the right of the editor. The viewer
+ is intended for displaying another scene document, your character notes, plot notes, or any
other document you may need to reference while writing. It is not intended as a preview panel
for the document you're editing, but if you wish, you can also use it for this purpose.
@@ -58,32 +70,35 @@ Below are some key features of novelWriter.
later split them into multiple documents based on chapter and scene headings.
**Multi-novel project support**
- You can have multiple Novel type root folders in a project. This allows you to keep a series of
- individual novels with the same characters and world building in the same project, and create
- manuscripts for them individually.
+ The main parts of your project is split up into top level special folders called "Root" folders.
+ Your main story text lives in the "Novel" root folder. You can have multiple such folders in a
+ project. This allows you to keep a series of individual novels with the same characters and
+ world building in the same project, and create manuscripts for them individually.
**Keep track of your story elements**
- All notes in your project can be assigned a :term:`tag` that you can :term:`reference` from any
- other document or note. In fact, you can add a new tag under each heading of a note if you need
- to be able to reference specific sections of it.
+ All notes in your project can be assigned a :term:`tag` that you can then :term:`reference` from
+ any other document or note. In fact, you can add a new tag under each heading of a note if you
+ need to be able to reference specific sections of it.
**Get an overview of your story**
- In the :guilabel:`Outline View` on the main window you can see an outline of all the chapters,
- scenes, and sections of your project. If they have any references in them, these are listed in
- additional columns. You can also add a synopsis to each chapter or scene, which can be listed
- here as well. You have the option to add or remove columns of information from this outline. A
- subset of the outline information is also available in the :guilabel:`Novel View` as an
- alternative view to the project tree.
+ It is not the documents themselves that define the chapters and scenes of your story, but the
+ headings that separate them. In the **Outline View** on the main window you can see an outline
+ of all the chapter and scene headings of your project. If they have any references in them, like
+ which character is in what chapter and scene, these are listed in additional columns.
+
+ You can also add a synopsis to each chapter or scene, which can be listed here as well. You have
+ the option to add or remove columns of information from this outline. A subset of the outline
+ information is also available in the **Novel View** as an alternative view to the project tree.
**Get an overview of your story elements**
- Under the document viewer panel you will find a series of tabs that shows the different story
- elements you have created tags for. The tabs are sorted into Characters, Plots, etc, depending
- on which categories you are using in your story. This panel can be hidden to free up space when
- you don't need it.
+ Under the document viewer panel you will find a series of tabs that show the different story
+ elements you have created tags for. The tabs are sorted into **Characters**, **Plots**, etc,
+ depending on which categories you are using in your story. This panel can be hidden to free up
+ space when you don't need it.
**Assembling your manuscript**
Whether you want to assemble a manuscript, or export all your notes, or generate an outline of
- your chapters and scenes with a synopsis, you can use the :guilabel:`Build Manuscript` tool to
+ your chapters and scenes with a synopsis included, you can use the **Build Manuscript** tool to
do so. The tool lets you select what information you want to include in the generated document,
and how it is formatted. You can send the result to a printer, a PDF, or to an Open Document
file that can be opened by most office type word processors. You can also generate the result
@@ -95,7 +110,7 @@ Below are some key features of novelWriter.
Screenshots
===========
-.. figure:: images/screenshot_default.png
+.. figure:: images/screenshot_light.png
:class: dark-light
novelWriter with light colour theme
diff --git a/docs/source/int_overview.rst b/docs/source/int_overview.rst
index e83dcd58..96c938b7 100644
--- a/docs/source/int_overview.rst
+++ b/docs/source/int_overview.rst
@@ -1,28 +1,22 @@
-.. _a_overview:
+.. _a_reading:
-********
-Overview
-********
+******************
+What to Read First
+******************
-.. only:: html
+The documentation of novelWriter is quite extensive. There are a lot of features to get used to,
+but you don't need all of them to get started.
- .. image:: images/python_powered.png
- :align: right
- :width: 220
+The chapters below labelled "Essential Information" are the ones you need to know to use the
+application correctly. By "correctly", it is meant: in a way so novelWriter understands the basic
+structure of your text. It collects a lot of information from your text and uses it to display the
+structure of it in various ways to help you get an overview of your project.
-novelWriter is built as a cross-platform application using `Python 3 `_ as
-the programming language, and `Qt 5 `_ framework for the user interface.
+The chapters labelled "Recommended Reading" includes additional information on how the different
+parts if the application work and what the features do.
-novelWriter is built for Linux first, so this is where it works best. However, it also runs fine
-on Windows and MacOS due to the cross-platform framework it's built on. The author of the
-application doesn't own a Mac, so on-going Mac support is dependent on user feedback and user
-contributions.
-
-Spell checking in novelWriter is provided by a third party library called
-`Enchant `_. Please see the section on :ref:`a_custom_dict` for
-how to install spell checking languages.
-
-For install instructions for novelWriter, see :ref:`a_started`.
+The "Optional" and "Lookup" chapters contain additional information or lookup tables that are not
+essential for using the application.
Using novelWriter
@@ -82,8 +76,8 @@ meta data for it to extract.
the application interface.
:ref:`a_manuscript` - Recommended Reading
- This chapter explains how the :guilabel:`Manuscript Build` tool works, how you can control the
- way chapter titles are formatted, and how scene and section breaks are handled.
+ This chapter explains how the **Manuscript Build** tool works, how you can control the way
+ chapter titles are formatted, and how scene and section breaks are handled.
Additional Details & Technical Topics
diff --git a/docs/source/int_started.rst b/docs/source/int_started.rst
index d53b68cb..d361de2f 100644
--- a/docs/source/int_started.rst
+++ b/docs/source/int_started.rst
@@ -1,8 +1,8 @@
.. _a_started:
-***************
-Getting Started
-***************
+**********************
+Setup and Installation
+**********************
.. _Enchant: https://abiword.github.io/enchant/
.. _GitHub: https://github.com/vkbo/novelWriter
@@ -15,8 +15,8 @@ Getting Started
.. _AppImage: https://appimage.org/
Ready-made packages and installers for novelWriter are available for all major platforms, including
-Linux, Windows and MacOS, from the `Downloads page`_. See below for install instructions for each
-platform.
+Linux, Windows and MacOS, from the `Downloads page`_. See below for install additional instructions
+for each platform.
You can also install novelWriter from the Python Package Index (PyPi_). See :ref:`a_started_pip`.
Installing from PyPi does not set up icon launchers, so you will either have to do this yourself,
@@ -44,7 +44,7 @@ multiple installations has been known to cause problems.
The novelWriter installer is not signed because Microsoft doesn't currently provide a way for
non-profit open source projects to properly sign their installers. The novelWriter project
doesn't have the funding to pay for commercial software signing certificates. You will therefore
- see an additional warning about this when you download the installer.
+ see an additional warning about this when you download and run the installer.
.. _a_started_linux:
@@ -56,6 +56,7 @@ A Debian package can be downloaded from the `Downloads page`_, or from the Relea
GitHub_. This package should work on both Debian, Ubuntu and Linux Mint, at least.
If you prefer, you can also add the novelWriter repository on Launchpad to your package manager.
+The Launchpad packages `are signed by the author `__.
Ubuntu
diff --git a/docs/source/more_counting.rst b/docs/source/more_counting.rst
index ac408937..7272c9fc 100644
--- a/docs/source/more_counting.rst
+++ b/docs/source/more_counting.rst
@@ -8,8 +8,8 @@ This is an overview of how words and other counts of your text are performed. Th
should be relatively standard, and are compared to Libre Office Writer rules.
The counts provided in the app on the raw text is meant to be approximate. For more accurate
-counts, you need to build your manuscript in the :guilabel:`Manuscript Tool` and check the counts
-on the generated preview.
+counts, you need to build your manuscript in the **Manuscript Tool** and check the counts on the
+generated preview.
Text Word Counts and Stats
@@ -31,11 +31,11 @@ After the above preparation of the text, the following counts are available.
**Character Count**
The character count is the sum of characters per line, including leading and in-text white space
characters, but excluding trailing white space characters. Shortcodes in the text are not
- included, but Markdown codes are. Only headers and text are counted.
+ included, but Markdown codes are. Only headings and text are counted.
**Word Count**
The words count is the sum of blocks of continuous character per line separated by any number of
- white space characters or dashes. Only headers and text are counted.
+ white space characters or dashes. Only headings and text are counted.
**Paragraph Count**
The paragraph count is the number of text blocks separated by one or more empty line. A line
@@ -45,10 +45,10 @@ After the above preparation of the text, the following counts are available.
Manuscript Counts
=================
-These are the rules for the counts available for a manuscript in the :guilabel:`Manuscript Tool`.
-The rules have been tuned to agree with LibreOffice Writer, but will vary slightly depending on the
-content of your text. LibreOffice Writer also counts the text in the page header, which the
-Manuscript Tool does not.
+These are the rules for the counts available for a manuscript in the **Manuscript Tool**. The rules
+have been tuned to agree with LibreOffice Writer, but will vary slightly depending on the content
+of your text. LibreOffice Writer also counts the text in the page header, which the **Manuscript
+Tool** does not.
The content of each line is counted after all formatting has been processed, so the result will be
more accurate than the counts for text documents elsewhere in the app. The following rules apply:
@@ -63,36 +63,36 @@ more accurate than the counts for text documents elsewhere in the app. The follo
The following counts are available:
-**Header Count**
- The number of headers in the manuscript.
+**Headings**
+ The number of headings in the manuscript.
-**Paragraph Count**
+**Paragraphs**
The number of body text paragraphs in the manuscript.
-**Total Word Count**
+**Words**
The number of words in the manuscript, including any comments and meta data text.
-**Text Word Count**
+**Words in Text**
The number of words in body text paragraphs, excluding all other text.
-**Header Word Count**
- The number of words in headers, including inserted formatting like chapter numbers, etc.
+**Words in Headings**
+ The number of words in headings, including inserted formatting like chapter numbers, etc.
-**Total Character Count**
- The number of characters on all lines, including any comments and meta data text. Paragraph
+**Characters**
+ The number of characters in all lines, including any comments and meta data text. Paragraph
breaks are not counted, but in-paragraph hard line breaks are.
-**Text Character Count**
+**Character in Text**
The number of characters in body text paragraphs. Paragraph breaks are not counted, but
in-paragraph hard line breaks are.
-**Header Character Count**
+**Characters in Headings**
The number of characters in headings.
-**Text Words Character Count**
+**Character in Text, No Spaces**
The number of characters in body text paragraphs considered part of a word or punctuation. That
is, white space characters are not counted.
-**Header Words Character Count**
- The number of characters in headers considered part of a word or punctuation. That is, white
+**Character in Headings, No Spaces**
+ The number of characters in headings considered part of a word or punctuation. That is, white
space characters are not counted.
diff --git a/docs/source/int_customise.rst b/docs/source/more_customise.rst
similarity index 90%
rename from docs/source/int_customise.rst
rename to docs/source/more_customise.rst
index f9a3b9fe..6e6c09da 100644
--- a/docs/source/int_customise.rst
+++ b/docs/source/more_customise.rst
@@ -34,10 +34,10 @@ and add dictionaries yourself.
**Install Tool**
-A small tool to assist with this can be found under :guilabel:`Tools > Add Dictionaries`. It will
-import spell checking dictionaries from Free Office or Libre Office extensions. The dictionaries
-are then installed in the install location for the Enchant library and should thus work for any
-application that uses Enchant for spell checking.
+A small tool to assist with this can be found under **Tools > Add Dictionaries**. It will import
+spell checking dictionaries from Free Office or Libre Office extensions. The dictionaries are then
+installed in the install location for the Enchant library and should thus work for any application
+that uses Enchant for spell checking.
**Manual Install**
@@ -81,14 +81,14 @@ modify it as you like.
`novelwriter/assets/icons `_.
Remember to also change the name of your theme by modifying the ``name`` setting at the top of the
-file, otherwise you may not be able to distinguish them in :guilabel:`Preferences`.
+file, otherwise you may not be able to distinguish them in **Preferences**.
For novelWriter to be able to locate the custom theme files, you must copy them to the
:ref:`a_locations_data` location in your home or user area. There should be a folder there named
``syntax`` for syntax themes, just ``themes`` for GUI themes, and ``icons`` for icon themes. These
folders are created the first time you start novelWriter.
-Once the files are copied there, they should show up in :guilabel:`Preferences` with the label you
+Once the files are copied there, they should show up in **Preferences** with the label you
set as ``name`` inside the file.
.. versionadded:: 2.0
@@ -190,10 +190,10 @@ Omitted syntax colours default to black, except ``background`` which defaults to
``texthighlight`` which defaults to white with half transparency.
.. versionadded:: 2.2
- The `shortcode` syntax colour entry was added.
+ The ``shortcode`` syntax colour entry was added.
.. versionadded:: 2.3
- The `optional` syntax colour entry was added.
+ The ``optional`` syntax colour entry was added.
.. versionadded:: 2.4
- The `texthighlight` syntax colour entry was added.
+ The ``texthighlight`` syntax colour entry was added.
diff --git a/docs/source/project_manuscript.rst b/docs/source/project_manuscript.rst
index 9383079d..4f4657bc 100644
--- a/docs/source/project_manuscript.rst
+++ b/docs/source/project_manuscript.rst
@@ -4,9 +4,11 @@
Building the Manuscript
***********************
+.. _Pandoc: https://pandoc.org/
+
You can at any time build a manuscript, an outline of your notes, or any other type of document
-from the text in your project. All of this is handled by the :guilabel:`Manuscript Build` tool.
-You can activate it from the sidebar, the :guilabel:`Tools` menu, or by pressing :kbd:`F5`.
+from the text in your project. All of this is handled by the **Manuscript Build** tool. You can
+activate it from the sidebar, the **Tools** menu, or by pressing :kbd:`F5`.
.. versionadded:: 2.1
This tool is new for version 2.1. A simpler tool was used for earlier versions. The simpler tool
@@ -20,40 +22,60 @@ The Manuscript Build Tool
=========================
.. figure:: images/fig_manuscript_build.png
+ :width: 80%
- The :guilabel:`Manuscript Build` tool main window.
+ The **Manuscript Build** tool main window.
-The main window of the :guilabel:`Manuscript Build` tool contains a list of all the builds you have
+The main window of the **Manuscript Build** tool contains a list of all the builds you have
defined, a selection of settings, and a few buttons to generate preview, open the print dialog, or
run the build to create a manuscript document.
+Outline and Word Counts
+-----------------------
+
+.. figure:: images/fig_manuscript_build_outline.png
+ :width: 80%
+
+ The **Manuscript Build** tool main window with the **Outline** visible.
+
+The **Outline** tab on the left lets you navigate the headings in the preview document. It will
+show up to scene level headings for novel documents, and level 2 headings for notes.
+
+A collapsible panel of word and character counts are also available below the preview document.
+These are calculated from the text you have included in the document, and are more accurate counts
+than what's available in the project tree since they are counted *after formatting*.
+
+For a detailed description on how they are counted, see :ref:`a_counting`.
+
+
.. _a_manuscript_settings:
Build Settings
==============
-Each build definition can be edited by opening it in the :guilabel:`Manuscript Build Settings`
-dialog, either by double-clicking or by selecting it and pressing the edit button in the toolbar.
+Each build definition can be edited by opening it in the **Manuscript Build Settings** dialog,
+either by double-clicking or by selecting it and pressing the edit button in the toolbar.
.. tip::
- You can keep the :guilabel:`Manuscript Build Settings` dialog open while testing the different
- options, and just hit the :guilabel:`Apply` button. You can test the result of your settings
- by pressing the :guilabel:`Preview` button in the main :guilabel:`Manuscript Build` window.
- When you're happy with the result, you can close the settings.
+ You can keep the **Manuscript Build Settings** dialog open while testing the different options,
+ and just hit the :guilabel:`Apply` button. You can test the result of your settings by pressing
+ the :guilabel:`Preview` button in the main **Manuscript Build** window. When you're happy with
+ the result, you can close the settings.
Document Selection
------------------
.. figure:: images/fig_build_settings_selections.png
+ :width: 80%
- The :guilabel:`Selections` page of the :guilabel:`Manuscript Build Settings` dialog.
+ The **Selections** page of the **Manuscript Build Settings** dialog.
-The :guilabel:`Selections` page of the :guilabel:`Manuscript Build Settings` dialog allows you to
-fine tune which documents are included in the build. They are indicated by a green arrow icon in
-the last column. On the right you have some filter options for selecting content of a specific
-type, and a set of switches for which root folders to include.
+The **Selections** page of the **Manuscript Build Settings** dialog allows you to fine tune which
+documents are included in the build. They are indicated by a green arrow icon in the last column.
+On the right you have some filter options for selecting content of a specific type, and a set of
+switches for which root folders to include.
You can override the result of these filters by marking one or more documents and selecting to
explicitly include or exclude them by using the buttons below the tree view. The last button can be
@@ -69,13 +91,14 @@ Formatting Headings
-------------------
.. figure:: images/fig_build_settings_headings.png
+ :width: 80%
- The :guilabel:`Headings` page of the :guilabel:`Manuscript Build Settings` dialog.
+ The **Headings** page of the **Manuscript Build Settings** dialog.
-The :guilabel:`Headings` page of the :guilabel:`Manuscript Build Settings` dialog allows you to set
-how the headings in your :term:`Novel Documents` are formatted. By default, the title is just
-copied as-is, indicated by the ``{Title}`` format. You can change this to for instance add chapter
-numbers and scene numbers, or insert character names, like shown in the figure above.
+The **Headings** page of the **Manuscript Build Settings** dialog allows you to set how the
+headings in your :term:`Novel Documents` are formatted. By default, the title is just copied as-is,
+indicated by the ``{Title}`` format. You can change this to for instance add chapter numbers and
+scene numbers, or insert character names, like shown in the figure above.
Clicking the edit button next to a format will copy the formatting string into the edit box where
it can be modified, and where a syntax highlighter will help indicate which parts are automatically
@@ -86,7 +109,7 @@ Any text you add that isn't highlighted in colours will remain in your formatted
``{Title}`` will always be replaced by the text in the heading from your documents.
You can preview the result of these format strings by clicking :guilabel:`Apply`, and then clicking
-:guilabel:`Preview` in the :guilabel:`Manuscript Build` tool main window.
+:guilabel:`Preview` in the **Manuscript Build** tool main window.
Scene Separators
@@ -107,10 +130,9 @@ be treated as a separator.
Hard and Soft Scenes
^^^^^^^^^^^^^^^^^^^^
-If you wish to distinguish between so-called soft and hard scene breaks, where a hard scene break
-is understood as a scene break that changes point-of-view character, you can use the modified scene
-heading format in your text. You can then give these headings a different formatting in the
-:guilabel:`Headings` settings.
+If you wish to distinguish between so-called soft and hard scene breaks, you can use the
+alternative scene heading format in your text. You can then give these headings a different
+formatting in the **Headings** settings.
See :ref:`a_fmt_head` for more info on how to format headings in your text.
@@ -118,10 +140,9 @@ See :ref:`a_fmt_head` for more info on how to format headings in your text.
Output Settings
---------------
-The :guilabel:`Content`, :guilabel:`Format` and :guilabel:`Output` pages of the
-:guilabel:`Manuscript Build Settings` dialog control a number of other settings for the output.
-Some of these only apply to specific output formats, which is indicated by the section headings on
-the settings pages.
+The **Content**, **Format** and **Output** pages of the **Manuscript Build Settings** dialog
+control a number of other settings for the output. Some of these only apply to specific output
+formats, which is indicated by the section headings on the settings pages.
.. _a_manuscript_build:
@@ -130,12 +151,13 @@ Building Manuscript Documents
=============================
.. figure:: images/fig_build_build.png
+ :width: 80%
- The :guilabel:`Manuscript Build` dialog used for writing the actual manuscript documents.
+ The **Manuscript Build** dialog used for writing the actual manuscript documents.
-When you press the :guilabel:`Build` button on the :guilabel:`Build Manuscript` tool main window, a
-special file dialog opens up. This is where you pick your desired output format and where to write
-the file.
+When you press the :guilabel:`Build` button on the **Build Manuscript** tool main window, a special
+file dialog opens up. This is where you pick your desired output format and where to write the
+file.
On the left side of the dialog is a list of all the available file formats, and on the right, a
list of the documents which are included based on the build definition you selected. You can choose
@@ -157,7 +179,7 @@ Open Document Format
novelWriter HTML
The HTML format writes a single ``.htm`` file with minimal style formatting. The HTML document
- is suitable for further processing by document conversion tools like Pandoc, for importing in
+ is suitable for further processing by document conversion tools like Pandoc_, for importing in
word processors, or for printing from browser.
novelWriter Markup
@@ -198,4 +220,4 @@ on the print dialog.
.. note::
The paper format should in all cases default to whatever your system default is. If you want to
- change it, you have to select it from the :guilabel:`Print Preview` dialog.
+ change it, you have to select it from the **Print Preview** dialog.
diff --git a/docs/source/project_overview.rst b/docs/source/project_overview.rst
index d546992f..9d89759f 100644
--- a/docs/source/project_overview.rst
+++ b/docs/source/project_overview.rst
@@ -4,25 +4,27 @@
Novel Projects
**************
-New projects can be created from the :guilabel:`Project` menu by selecting
-:guilabel:`Create or Open Project`. This will open the :guilabel:`Welcome` dialog, where you can
-select the :guilabel:`New` button that will assist you in creating a new project.
+New projects can be created from the **Project** menu by selecting **Create or Open Project**. This
+will open the **Welcome** dialog, where you can select the :guilabel:`New` button that will assist
+you in creating a new project. This dialog is also displayed when you start novelWriter.
A novelWriter project requires a dedicated folder for storing its files on the local file system.
If you're interested in the details, you can have a look at the chapter :ref:`a_storage`.
-A list of recently opened projects is maintained, and displayed in the :guilabel:`Welcome`
-dialog. A project can be removed from this list by selecting it and pressing the :kbd:`Del` key or
-by right-clicking it and selecting the :guilabel:`Remove Project` option.
+A list of recently opened projects is maintained, and displayed in the **Welcome** dialog. A
+project can be removed from this list by selecting it and pressing the :kbd:`Del` key or by
+right-clicking it and selecting the **Remove Project** option.
.. figure:: images/fig_welcome.jpg
The project list (left) and new project form (right) of the :guilabel:`Welcome` dialog.
-Project-specific settings are available in :guilabel:`Project Settings` in the :guilabel:`Project`
-menu. See further details below in the :ref:`a_proj_settings` section. Details about the project's
-novel text, including word counts, and a table of contents with word and page counts, is available
-through the :guilabel:`Novel Details` dialog.
+Project-specific settings are available in **Project Settings** in the **Project** menu. See
+further details below in the :ref:`a_proj_settings` section.
+
+Details about the project's novel text, including word counts, and a table of contents with word
+and page counts, is available through the **Novel Details** dialog. Statistics about the project
+is also available in the **Manuscript Build** tool.
.. _a_proj_roots:
@@ -33,64 +35,70 @@ Project Roots
Projects are structured into a set of top level folders called "Root Folders". They are visible in
the project tree at the left side of the main window.
-The :term:`novel documents` go into a root folder of type :guilabel:`Novel`. :term:`Project notes`
-go into the other root folders. These other root folder types are intended for your notes on the
-various elements of your story. Using them is of course entirely optional.
+The :term:`novel documents` go into a root folder of type **Novel**. :term:`Project notes` go into
+the other root folders. These other root folder types are intended for your notes on the various
+elements of your story. Using them is of course entirely optional.
A new project may not have all of the root folders present, but you can add the ones you want from
the project tree tool bar.
Each root folder has one or more :term:`reference` :term:`keyword` associated with it that is used
to reference them from other documents and notes. The intended usage of each type of root folder is
-listed below. However, aside from the :guilabel:`Novel` folder, no restrictions are applied by the
+listed below. However, aside from the **Novel** folder, no restrictions are applied by the
application on what you put in them. You can use them however you want.
The root folder system is closely connected to how the Tags and References system works. For more
details, see the :ref:`a_references` chapter.
-:guilabel:`Novel`
+
+Root Folder Types
+-----------------
+
+**Novel**
This is the root folder type for text that goes into the final novel or novels. This class of
documents have other rules and features than the project notes. See :ref:`a_struct` for more
details.
-:guilabel:`Plot`
+**Plot**
This is the root folder type where main plots can be outlined. It is optional, but adding at
- least brief notes can be useful in order to tag plot elements for the :guilabel:`Outline View`.
- Tags in this folder can be references using the ``@plot`` keyword.
+ least brief notes can be useful in order to tag plot elements for the **Outline View**. Tags in
+ this folder can be references using the ``@plot`` keyword.
-:guilabel:`Characters`
+**Characters**
Character notes go in this root folder type. These are especially important if you want to use
- the :guilabel:`Outline View` to see which character appears where, which part of the story is
- told from a specific character's point-of-view, or focusing on a particular character's
- storyline. The character names can also be inserted into for instance chapter titles when you
- create your manuscript. Tags in this type of folder can be referenced using the ``@pov`` keyword
- for point-of-view characters, ``@focus`` for a focus character, or the ``@char`` keyword for any
+ the **Outline View** to see which character appears where, which part of the story is told from
+ a specific character's point-of-view, or focusing on a particular character's storyline.
+
+ The character names can also be inserted into for instance chapter titles when you create your
+ manuscript. Tags in this type of folder can be referenced using the ``@pov`` keyword for
+ point-of-view characters, ``@focus`` for a focus character, or the ``@char`` keyword for any
other character present.
-:guilabel:`Locations`
+**Locations**
The locations folder type is for various scene locations that you want to track. Tags in this
folder can be references using the ``@location`` keyword.
-:guilabel:`Timeline`
+**Timeline**
If the story has multiple plot timelines or jumps in time within the same plot, this folder type
can be used to track this. Tags in this type of folder can be references using the ``@time``
keyword.
-:guilabel:`Objects`
- Important objects in the story, for instance objects that change hands often, can be tracked
- here. Tags in this type of folder can be references using the ``@object`` keyword.
+**Objects**
+ Important objects in the story, for instance physical objects that change hands often, can be
+ tracked here. Tags in this type of folder can be references using the ``@object`` keyword.
-:guilabel:`Entities`
+**Entities**
Does your plot have many powerful organisations or companies? Or other entities that are part of
the plot? They can be organised here. Tags in this type of folder can be references using the
``@entity`` keyword.
-:guilabel:`Custom`
+**Custom**
The custom root folder type can be used for tracking anything else not covered by the above
options. Tags in this folder type can be references using the ``@custom`` keyword.
-The root folders correspond to the categories of tags that can be used to reference them. For more
-information about the tags listed, see :ref:`a_references_references`.
+The root folders are closely tied to the tags and reference system. Each folder type corresponds to
+the categories of tags that can be used to reference them. For more information about the tags
+listed, see :ref:`a_references_references`.
.. note::
You can rename root folders to whatever you want. However, this doesn't change the reference
@@ -105,12 +113,11 @@ information about the tags listed, see :ref:`a_references_references`.
Deleted Documents
-----------------
-Deleted documents will be moved into a special :guilabel:`Trash` root folder. Documents in the
-trash folder can then be deleted permanently, either individually, or by emptying the trash from
-the menu. Documents in the trash folder are removed from the :term:`project index` and cannot be
-referenced.
+Deleted documents are moved into a special **Trash** root folder. Documents in the trash folder can
+then be deleted permanently, either individually, or by emptying the trash from the menu. Documents
+in the trash folder are removed from the :term:`project index` and cannot be referenced.
-A document or a folder can be moved to trash from the :guilabel:`Project` menu, or by pressing
+A document or a folder can be moved to trash from the **Project** menu, or by pressing
:kbd:`Ctrl+Shift+Del`. Root folders can only be removed when they are empty.
@@ -119,13 +126,34 @@ A document or a folder can be moved to trash from the :guilabel:`Project` menu,
Archived Documents
------------------
-If you don't want to delete a document, or put it in the :guilabel:`Trash` folder where it may be
-deleted accidentally, but still want it out of your main project tree, you can create an
-:guilabel:`Archive` root folder and move it there.
+If you don't want to delete a document, or put it in the **Trash** folder where it may be deleted
+accidentally, but still want it out of your main project tree, you can create an **Archive** root
+folder instead and move it there. It has the same effect as moving it to **Trash**, but it is safe
+from deletion.
You can drag any document to this folder and preserve its settings. The document will always be
-excluded from the :guilabel:`Build Manuscript` tool. It is also removed from the
-:term:`project index`, so the tags and references defined in it will not show up anywhere else.
+excluded from the **Build Manuscript** tool. It is also removed from the :term:`project index`, so
+the tags and references defined in it will not show up anywhere else.
+
+
+.. _a_proj_roots_dirs:
+
+Using Folders in the Project Tree
+---------------------------------
+
+Regular folders, those that are not root folders, have no structural significance to the project.
+When novelWriter is processing the documents in a project, like for instance when you create a
+manuscript from it, these folders are ignored. Only the order of the documents themselves matter.
+
+The folders are there purely as a way for you to organise the documents in meaningful sections and
+to be able to collapse and hide them in the project tree when you're not working on those
+documents.
+
+.. versionadded:: 2.0
+ As of version 2.0 it is possible to add child documents to other documents. This is particularly
+ useful when you create chapters and scenes. If you add separate scene documents, you should also
+ add separate chapter documents, even if they only contain a chapter heading. You can then add
+ scene documents as child items to the chapters.
.. _a_proj_roots_orphaned:
@@ -143,8 +171,8 @@ previously have been located in the project. The project loading routine will tr
as close as possible to this location, if it still exists. Generally, it will be appended to the
end of the folder where it previously was located. If that folder doesn't exist, it will try to add
it to the correct root folder type. If it cannot figure out which root folder is correct, the
-document will be added to the :guilabel:`Novel` root folder. Finally, if the :guilabel:`Novel`
-folder is missing, one will be created.
+document will be added to the **Novel** root folder. Finally, if a **Novel** does not exist, one
+will be created.
If the title of the document can be recovered, the word "Recovered:" will be added as a prefix to
indicate that it may need further attention. If the title cannot be determined, the document will
@@ -157,15 +185,16 @@ Project Lockfile
----------------
To prevent lost documents caused by file conflicts when novelWriter projects are synchronised via
-file synchronisation tools, a project lockfile is written to the project storage folder. If you try
-to open a project which has such a file present, you will be presented with a warning, and some
-information about where else novelWriter thinks the project is also open. You will be given the
-option to ignore this warning, and continue opening the project at your own risk.
+file synchronisation tools, a project lockfile is written to the project storage folder when a
+project is open. If you try to open a project which already has such a file present, you will be
+presented with a warning, and some information about where else novelWriter thinks the project is
+also open. You will be given the option to ignore this warning, and continue opening the project at
+your own risk.
.. note::
- If, for some reason, novelWriter crashes, the lock file may remain even if there are no other
- instances keeping the project open. In such a case it is safe to ignore the lock file warning
- when re-opening the project.
+ If, for some reason, novelWriter or your computer crashes, the lock file may remain even if
+ there are no other instances keeping the project open. In such a case it is safe to ignore the
+ lock file warning when re-opening the project.
.. warning::
If you choose to ignore the warning and continue opening the project, and multiple instances of
@@ -175,52 +204,31 @@ option to ignore this warning, and continue opening the project at your own risk
novelWriter will try to resolve project inconsistencies the next time you open the project.
-.. _a_proj_roots_dirs:
-
-Using Folders in the Project Tree
----------------------------------
-
-Folders, aside from root folders, have no structural significance to the project. When novelWriter
-is processing the documents in a project, like for instance when you create a manuscript from it,
-these folders are ignored. Only the order of the documents themselves matter.
-
-The folders are there purely as a way for you to organise the documents in meaningful sections and
-to be able to collapse and hide them in the project tree when you're not working on those
-documents.
-
-.. versionadded:: 2.0
- As of version 2.0 it is possible to add child documents to other documents. This is particularly
- useful when you create chapters and scenes. If you add separate scene documents, you should also
- add separate chapter documents, even if they only contain a chapter heading. You can then add
- scene documents as child items to the chapters.
-
-
.. _a_proj_files:
Project Documents
=================
-New documents can be created from the toolbar in the :guilabel:`Project Tree`, or by pressing
-:kbd:`Ctrl+N`. This will open the create new item menu and let you choose between a number of
-pre-defined documents and folders. You will be prompted for a label for the new item.
+New documents can be created from the toolbar in the project tree, or by pressing :kbd:`Ctrl+N`.
+This will open the create new item menu and let you choose between a number of pre-defined
+documents and folders. You will be prompted for a label for the new item.
-You can always rename an item by selecting :guilabel:`Rename Item` from the :guilabel:`Project`
-menu, or by pressing :kbd:`F2`.
+You can always rename an item by selecting **Rename Item** from the **Project** menu, or by
+pressing :kbd:`F2` when a document or folder is selected.
-Other settings for project items are available from the context menu that you can activate by
-right-clicking on an item in the tree. The :guilabel:`Transform` submenu includes options for
-converting, splitting, or merging items. See :ref:`a_ui_tree_split_merge` for more details on the
-latter two.
+Other settings for project documents and folders are available from the context menu that you can
+activate by right-clicking on an it in the tree. The **Transform** submenu includes options for
+converting, splitting, or merging documents. See :ref:`a_ui_tree_split_merge` for more details on
+the latter two.
Document Templates
------------------
If you wish to create template documents to be used when creating new project documents, like for
-instance a character note template, you can add a :guilabel:`Templates` root folder to your
-project. Any document added to this root folder will show up in the :guilabel:`Add Item` menu in
-the project tree toolbar. When selected, a new document is created with its content copied from the
-chosen template.
+instance a character note template, you can add a **Templates** root folder to your project. Any
+document added to this root folder will show up in the **Add Item** menu in the project tree
+toolbar. When selected, a new document is created with its content copied from the chosen template.
.. versionadded:: 2.3
@@ -233,8 +241,8 @@ Word Counts
A character, word and paragraph count is maintained for each document, as well as for each section
of a document following a :term:`heading`. The word count and change of words in the
current session is displayed in the footer of any document open in the editor, and all stats are
-shown in the details panel below the :guilabel:`Project Tree` for any document selected in the
-project or novel trees.
+shown in the details panel below the project tree for any document selected in the project or novel
+trees.
The word counts are not updated in real time, but run in the background every few seconds for as
long as the document is being actively edited.
@@ -242,7 +250,7 @@ long as the document is being actively edited.
A total project word count is displayed in the status bar. The total count depends on the sum of
the values in the project tree, which again depend on an up to date :term:`project index`. If the
counts seem wrong, a full project word recount can be initiated by rebuilding the project's index.
-Either from the :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
+Either from the **Tools** menu, or by pressing :kbd:`F9`.
The rules for how the counts are made is covered in more detail in :ref:`a_counting`.
@@ -252,33 +260,35 @@ The rules for how the counts are made is covered in more detail in :ref:`a_count
Project Settings
================
-The :guilabel:`Project Settings` can be accessed from the :guilabel:`Project` menu, or by pressing
+The **Project Settings** can be accessed from the **Project** menu, or by pressing
:kbd:`Ctrl+Shift+,`. This will open a dialog box, with a set of tabs.
Settings Tab
------------
-The :guilabel:`Settings` tab holds the project name, title, and author settings.
+The **Settings** tab holds the project name, author, and language settings.
-The :guilabel:`Project Name` can be edited here. It is used for the GUI (main window title) and for
-generating backup files. So keep in mind that if you do change this setting, the backup file names
-will change too.
+The **Project Name** can be edited here. It is used for the main window title and for generating
+backup files. So keep in mind that if you do change this setting, the backup file names will change
+too.
-You can also change the :guilabel:`Authors` and :guilabel:`Project Language` setting. These are
-only used when building the manuscript, for some formats.
+You can also change the **Authors** and **Project Language** setting. These are only used when
+building the manuscript, for some formats. The language setting is also used when inserting text
+into documents in the viewer, like for instance labels for keywords and special comments.
If your project is in a different language than your main spell checking language is set to, you
-can override the default setting here. The project language can also be changed from the
-:guilabel:`Tools` menu. You can also override the automatic backup setting if you wish.
+can override the default setting here. The project language can also be changed from the **Tools**
+menu.
+
+You can also override the automatic backup setting for the project if you wish.
Status and Importance Tabs
--------------------------
-Each document or folder of type :guilabel:`Novel` can be given a "Status" label accompanied by a
-coloured icon, and each document or folder of the remaining types can be given an "Importance"
-label.
+Each document or folder of type **Novel** can be given a "Status" label accompanied by a coloured
+icon, and each document or folder of the remaining types can be given an "Importance" label.
These labels are there purely for your convenience, and you are not required to use them for any
other features to work. No other part of novelWriter accesses this information. The intention is to
@@ -316,22 +326,22 @@ Backup
======
An automatic backup system is built into novelWriter. In order to use it, a backup path to where
-the backup files are to be stored must be provided in :guilabel:`Preferences`.
+the backup files are to be stored must be provided in **Preferences**. The path defaults to a
+folder named "Backups" in your home directory.
Backups can be run automatically when a project is closed, which also implies it is run when the
application itself is closed. Backups are date stamped zip files of the project files in the
project folder (files not strictly a part of the project are ignored). The zip archives are stored
-in a subfolder of the backup path. The subfolder will have the same name as the
-:guilabel:`Project Name` as defined in :ref:`a_proj_settings`.
+in a subfolder of the backup path. The subfolder will have the same name as the **Project Name** as
+defined in :ref:`a_proj_settings`.
-The backup feature, when configured, can also be run manually from the :guilabel:`Tools` menu.
-It is also possible to disable automated backups for a given project in
-:guilabel:`Project Settings`.
+The backup feature, when configured, can also be run manually from the **Tools** menu. It is also
+possible to disable automated backups for a given project in **Project Settings**.
.. note::
- For the backup to be able to run, the :guilabel:`Project Name` must be set in
- :guilabel:`Project Settings`. This value is used to generate the name and path of the backups.
- Without it, the backup will not run at all, but it will produce a warning message.
+ For the backup to be able to run, the **Project Name** must be set in **Project Settings**. This
+ value is used to generate the name and path of the backups. Without it, the backup will not run
+ at all, but it will produce a warning message.
.. _a_proj_stats:
@@ -344,9 +354,8 @@ total word counts of your novel documents and notes at the end of the session, p
session lasted either more than 5 minutes, or that the total word count changed. For more details
about the log file, see :ref:`a_storage`.
-A tool to view the content of the log file is available in the :guilabel:`Tools` menu under
-:guilabel:`Writing Statistics`. You can also launch it by pressing :kbd:`F6`, or find it on the
-sidebar.
+A tool to view the content of the log file is available in the **Tools** menu under **Writing
+Statistics**. You can also launch it by pressing :kbd:`F6`, or find it on the sidebar.
The tool will show a list of all your sessions, and a set of filters to apply to the data. You can
also export the filtered data to a JSON file or to a CSV file that can be opened by a spreadsheet
@@ -356,4 +365,4 @@ application like for instance Libre Office Calc or Excel.
As of version 1.2, the log file also stores how much of the session time was spent idle. The
definition of idle here is that the novelWriter main window loses focus, or the user hasn't made
any changes to the currently open document in five minutes. The number of minutes can be altered
- in :guilabel:`Preferences`.
+ in **Preferences**.
diff --git a/docs/source/project_references.rst b/docs/source/project_references.rst
index cdd29536..7b695801 100644
--- a/docs/source/project_references.rst
+++ b/docs/source/project_references.rst
@@ -16,9 +16,9 @@ this chapter hopes to explain in more detail how to use this tags and references
.. tip::
If you find the Tags and Reference system difficult to follow just from reading this chapter,
- you can create a new project in the :guilabel:`Welcome` dialog's New project form and select
- "Create an example project" from the "Pre-fill project" option. The example project contains
- several examples of tags and references.
+ you can create a new project in the **Welcome** dialog's New project form and select "Create an
+ example project" from the "Pre-fill project" option. The example project contains several
+ examples of tags and references.
.. _a_references_metadata:
@@ -68,7 +68,7 @@ You can only set **one** tag per heading, and the tag has to be unique across **
the project.
After a tag has been defined, it can be referenced in novel documents, or cross-referenced in other
-notes. Tags will also show up in the :guilabel:`Outline View` and in the references panel under the
+notes. Tags will also show up in the **Outline View** and in the references panel under the
document viewer when a document is open in the viewer.
The syntax highlighter will indicate to you that the keyword is correctly used and that the tag is
@@ -79,7 +79,7 @@ that valid tags do.
The tag is the only part of these notes that novelWriter uses. The rest of the document content is
there for you to use in whatever way you wish. Of course, the content of the documents can be added
to the manuscript, or an outline document. If you want to compile a single document of all your
-notes, you can do this from the :guilabel:`Manuscript Build` tool.
+notes, you can do this from the **Manuscript Build** tool.
.. versionadded:: 2.2
Tags are no longer case sensitive. The tags are by default displayed with the capitalisation you
@@ -117,8 +117,8 @@ How to Use References
=====================
Each heading of any level in your project can contain references to tags set in project notes. The
-references are gathered by the indexer and used to generate the :guilabel:`Outline View`, among
-other things.
+references are gathered by the indexer and used to generate the **Outline View**, among other
+things.
References are set as a :term:`keyword` and a list of corresponding tags. The valid keywords are
listed below. The format of a reference line is ``@keyword: value1, [value2] ... [valueN]``. All
@@ -126,70 +126,69 @@ reference keywords allow multiple values.
``@pov``
The point-of-view character for the current section. The target must be a note tag in a
- :guilabel:`Character` type root folder.
+ **Character** type root folder.
``@focus``
The character that has the focus for the current section. This can be used in cases where the
- focus is not a point-of-view character. The target must be a note tag in a :guilabel:`Character`
- type root folder.
+ focus is not a point-of-view character. The target must be a note tag in a **Character** type
+ root folder.
``@char``
- Other characters in the current section. The target must be a note tag in a
- :guilabel:`Character` type root folder. This should not include the point-of-view or focus
- character if those references are used.
+ Other characters in the current section. The target must be a note tag in a **Character** type
+ root folder. This should not include the point-of-view or focus character if those references
+ are used.
``@plot``
- The plot or subplot advanced in the current section. The target must be a note tag in a
- :guilabel:`Plot` type root folder.
+ The plot or subplot advanced in the current section. The target must be a note tag in a **Plot**
+ type root folder.
``@time``
- The timelines touched by the current section. The target must be a note tag in a
- :guilabel:`Timeline` type root folder.
+ The timelines touched by the current section. The target must be a note tag in a **Timeline**
+ type root folder.
``@location``
The location the current section takes place in. The target must be a note tag in a
- :guilabel:`Locations` type root folder.
+ **Locations** type root folder.
``@object``
- Objects present in the current section. The target must be a note tag in a :guilabel:`Object`
- type root folder.
+ Objects present in the current section. The target must be a note tag in a **Object** type root
+ folder.
``@entity``
- Entities present in the current section. The target must be a note tag in a
- :guilabel:`Entities` type root folder.
+ Entities present in the current section. The target must be a note tag in an **Entities** type
+ root folder.
``@custom``
- Custom references in the current section. The target must be a note tag in a :guilabel:`Custom`
- type root folder. The custom folder are for any other category of notes you may want to use.
+ Custom references in the current section. The target must be a note tag in a **Custom** type
+ root folder. The custom folder are for any other category of notes you may want to use.
The syntax highlighter will alert the user that the tags and references are used correctly, and
that the tags referenced exist.
.. note::
The highlighter may be mistaken if the index of defined tags is out of date. If so, press
- :kbd:`F9` to regenerate it, or select :guilabel:`Rebuild Index` from the :guilabel:`Tools` menu.
- In general, the index for a document is regenerated when it is saved, so this shouldn't normally
- be necessary.
+ :kbd:`F9` to regenerate it, or select **Rebuild Index** from the **Tools** menu. In general, the
+ index for a document is regenerated when it is saved, so this shouldn't normally be necessary.
.. tip::
If you add a reference in the editor to a tag that doesn't yet exist, you can right-click it and
- select :guilabel:`Create Note for Tag`. This will generate a new project note automatically with
- the new tag defined. In order for this to be possible, a root folder for that category of
- references must already exist.
+ select **Create Note for Tag**. This will generate a new project note automatically with the new
+ tag defined. In order for this to be possible, a root folder for that category of references
+ must already exist.
One note can also reference another note in the same way novel documents do. When the note is
opened in the document viewer, the references become clickable links, making it easier to follow
connections in the plot. You can follow links in the document editor by clicking them with the
mouse while holding down the :kbd:`Ctrl` key. Clicked links are always opened in the view panel.
-Project notes don't show up in the :guilabel:`Outline View`, so referencing between notes is only
+Project notes don't show up in the **Outline View**, so referencing between notes is only
meaningful if you want to be able to click-navigate between them, or of course if you just want to
highlight that two notes are related.
.. tip::
If you cross-reference between notes and export your project as an HTML document using the
- :guilabel:`Manuscript Build` tool, the cross-references become clickable links in the exported
- HTML document as well.
+ **Manuscript Build** tool, the cross-references become clickable links in the exported HTML
+ document as well.
Example of a novel document with references to characters and plots:
@@ -217,7 +216,7 @@ character ``@`` on a new line. It will first suggest tag or reference keywords f
after the ``:`` has been added, suggest references from the list of tags you have already defined.
You can use the auto-completer to add multiple references with a ``,`` between them, and even type
-new ones. New references can be created by right-clicking on them and selecting
-:guilabel:`Create Note for Tag` from the menu.
+new ones. New references can be created by right-clicking on them and selecting **Create Note for
+Tag** from the menu.
.. versionadded:: 2.2
diff --git a/docs/source/project_structure.rst b/docs/source/project_structure.rst
index 0763fed7..e344ecd8 100644
--- a/docs/source/project_structure.rst
+++ b/docs/source/project_structure.rst
@@ -6,14 +6,13 @@ Novel Structure
This chapter covers the structure of a novel project.
-There are two different types of documents in a project, :guilabel:`Novel Documents` and
-:guilabel:`Project Notes`. Active novel documents can only live in a :guilabel:`Novel` type root
-folder. You can also move them to :guilabel:`Archive` and :guilabel:`Trash` of course, where they
-become inactive.
+There are two different types of documents in a project, **Novel Documents** and **Project Notes**.
+Active novel documents can only live in a **Novel** type root folder. You can also move them to
+**Archive** and **Trash** of course, where they become inactive.
-The :guilabel:`Project Tree` can distinguish between the different heading levels of the novel
-documents using coloured icons, and optionally add emphasis on the label, set in
-:guilabel:`Preferences`.
+The project tree can distinguish between the different heading levels of the novel documents using
+coloured icons, and optionally add emphasis on the label, set in **Preferences** for easier
+identification.
.. _a_struct_heads:
@@ -29,40 +28,41 @@ title. See also the :ref:`a_fmt` section for more details about the markup synta
.. note::
The heading levels are not only important when generating the manuscript, they are also used by
- the indexer when building the outline tree in the :guilabel:`Outline View` as well as in the
- :guilabel:`Novel Tree`. Each heading also starts a new region where new Tags and References
- can be defined. See :ref:`a_references` for more details.
+ the indexer when building the outline tree in the **Outline View** as well as in the **Novel
+ Tree**. Each heading also starts a new region where new Tags and References can be defined. See
+ :ref:`a_references` for more details.
-The syntax for the four basic heading types, and the two special types, is listed in section
+The syntax for the four basic heading types, and the three special types, is listed in section
:ref:`a_fmt_head`. The meaning of the four levels for the structure of your novel is as follows:
**Heading Level 1: Partition**
- This heading level signifies that the text refers to a top level partition. This is useful when
+ This heading level signifies that the text refers to a top level heading. This is useful when
you want to split the manuscript up into books, parts, or acts. These headings are not required.
The novel title itself should use the special heading level ``#!`` covered in :ref:`a_fmt_head`.
**Heading Level 2: Chapter**
- This heading level signifies a chapter level partition. Each time you want to start a new
- chapter, you must add such a heading. If you choose to split your manuscript up into one
- document per scene, you need a single chapter document with just the heading. You can of course
- also add a synopsis and reference keywords to the chapter document. If you want to open the
- chapter with a quote or other introductory text that isn't part of a scene, this is also where
- you'd put that text.
+ This heading level signifies a chapter. Each time you want to start a new chapter, you must add
+ such a heading. If you choose to split your manuscript up into one document per scene, you need
+ a single chapter document with just the heading. You can of course also add a synopsis and
+ reference keywords to the chapter document. If you want to open the chapter with a quote or
+ other introductory text that isn't part of a scene, this is also where you'd put that text.
**Heading Level 3: Scene**
- This heading level signifies a scene level partition. You must provide a title text, but the
- title text can be replaced with a scene separator or just skipped entirely when you build your
- manuscript.
+ This heading level signifies a scene. You must provide a title text, but the title text can be
+ replaced with a scene separator or just skipped entirely when you build your manuscript. If you
+ need to distinguish between hard and soft scene breaks, there is an alternative format for
+ scenes you can use for this distinction. The formatting is covered in :ref:`a_fmt_head`. See
+ also :ref:`a_struct_heads_scenes`.
**Heading Level 4: Section**
- This heading level signifies a sub-scene level partition, usually called a "section" in the
+ This heading level can be used to split up a scene, usually called a "section" in the
documentation and the user interface. These can be useful if you want to change references
mid-scene, like if you change the point-of-view character. You are free to use sections as you
- wish, and you can filter them out of the final manuscript just like with scene titles.
+ wish, and you can filter them out of the final manuscript.
-Page breaks are automatically added before partition and chapter headings when you build your
-project to a format that supports page breaks. If you want page breaks in other places, you have to
-specify them manually. See :ref:`a_fmt_break`.
+Page breaks can be automatically added before partition, chapter and scene headings from the
+**Manuscript Build** tool when you build your project to a format that supports page breaks. If you
+want page breaks in other places, you have to specify them manually. See :ref:`a_fmt_break`.
.. tip::
There are multiple options of how to process novel headings when building the manuscript. For
@@ -76,9 +76,9 @@ specify them manually. See :ref:`a_fmt_break`.
Novel Title and Front Matter
----------------------------
-It is recommended that you add a document at the very top of each Novel root folder with the novel
-title as the first line. You should modify the level 1 heading format code with an ``!`` in order
-to render it as a document title that is excluded from any automatic Table of Content in a
+It is recommended that you add a document at the very top of each **Novel** root folder with the
+novel title as the first line. You should modify the level 1 heading format code with an ``!`` in
+order to render it as a document title that is excluded from any automatic Table of Content in a
manuscript build document, like so:
.. code-block:: md
@@ -102,7 +102,7 @@ Unnumbered Chapter Headings
If you use the automatic numbering feature for your chapters, but you want to keep some special
chapters separate from this, you can add an ``!`` to the level 2 heading formatting code to tell
-the build tool to skip these chapters.
+the build tool to skip these chapters when adding numbers.
.. code-block:: md
@@ -110,13 +110,31 @@ the build tool to skip these chapters.
Chapter Text
-There is a separate formatting feature for such chapter titles in the :guilabel:`Manuscript Build`
-tool as well. See the :ref:`a_manuscript` page for more details. When building a document of a
-format that supports page breaks, also unnumbered chapters will have a page break added just like
-for normal chapters.
+There is a separate formatting feature for such chapter titles in the **Manuscript Build** tool as
+well. See the :ref:`a_manuscript` page for more details. When building a document of a format that
+supports page breaks, also unnumbered chapters can have a page break added just like for normal
+chapters.
-.. Note::
- Previously, you could also disable the automatic numbering of a chapter by adding an ``*`` as
- the first character of the chapter title itself. This feature has been dropped in favour of the
- current format in order to keep level 1 and 2 headers consistent. Please update your chapter
- headings if you've used this syntax.
+
+.. _a_struct_heads_scenes:
+
+Hard and Soft Scene Breaks
+--------------------------
+
+If you need two different ways to style scenes in your manuscript, like if you want to insert
+different scene separators for soft and hard scene breaks, there is an alternative scene format
+available for scene headings with a ``!`` added to the formatting code.
+
+.. code-block:: md
+
+ ### Soft Scene Transition
+
+ A soft scene break.
+
+ ###! Hard Scene Transition
+
+ A hard scene break.
+
+There is a separate formatting feature for these titles in the **Manuscript Build** tool.
+
+.. versionadded:: 2.4
diff --git a/docs/source/tech_locations.rst b/docs/source/tech_locations.rst
index da199292..3cd09e60 100644
--- a/docs/source/tech_locations.rst
+++ b/docs/source/tech_locations.rst
@@ -15,10 +15,9 @@ file locations are described in this chapter.
Configuration
=============
-The general configuration of novelWriter, including everything that is in :guilabel:`Preferences`,
-is saved in one central configuration file. The location of this file depends on your operating
-system. The system paths are provided by the Qt QStandardPaths_ class and its ``ConfigLocation``
-value.
+The general configuration of novelWriter, including everything that is in **Preferences**, is saved
+in one central configuration file. The location of this file depends on your operating system. The
+system paths are provided by the Qt QStandardPaths_ class and its ``ConfigLocation`` value.
The standard paths are:
@@ -40,9 +39,8 @@ Application Data
================
novelWriter also stores a bit of data that is generated by the user's actions. This includes the
-list of recent projects form the :guilabel:`Open Project` dialog. Custom themes should also be
-saved here. The system paths are provided by the Qt QStandardPaths_ class and its
-``AppDataLocation`` value.
+list of recent projects form the **Welcome** dialog. Custom themes should also be saved here. The
+system paths are provided by the Qt QStandardPaths_ class and its ``AppDataLocation`` value.
The standard paths are:
@@ -56,3 +54,12 @@ user's username on Windows.
.. note::
These are the standard operating system defined locations. If your system has been set up in a
different way, these locations may also be different.
+
+The Application Data location also holds several folders:
+
+``cache``
+ This folder is used to save the preview data for the **Manuscript Build** tool.
+
+``icons``, ``syntax`` and ``themes``
+ These folders are empty by default, but this is where the user can store custom theme files.
+ See :ref:`a_custom` for more details.
diff --git a/docs/source/tech_source.rst b/docs/source/tech_source.rst
index 8c23463e..87d616a3 100644
--- a/docs/source/tech_source.rst
+++ b/docs/source/tech_source.rst
@@ -159,6 +159,5 @@ You can also build a PDF manual from the documentation using the ``pkgutils.py``
python pkgutils.py manual
This will build the documentation as a PDF using LaTeX. The file will then be copied into the
-assets folder and made available in the :guilabel:`Help` menu in novelWriter. The Sphinx build
-system has a few extra dependencies when building the PDF. Please check the `Sphinx Docs`_ for more
-details.
+assets folder and made available in the **Help** menu in novelWriter. The Sphinx build system has a
+few extra dependencies when building the PDF. Please check the `Sphinx Docs`_ for more details.
diff --git a/docs/source/tech_storage.rst b/docs/source/tech_storage.rst
index 7fb9d39b..79d5890a 100644
--- a/docs/source/tech_storage.rst
+++ b/docs/source/tech_storage.rst
@@ -55,10 +55,10 @@ string. The documents are saved with a filename assembled from this handle and t
``.nwd``.
If you wish to find the file system location of a document in the project, you can either look it
-up in the project XML file, select :guilabel:`Show File Details` from the :guilabel:`Document` menu
-when having the document open in the editor, or look in the ``ToC.txt`` file in the root of the
-project folder. The ``ToC.txt`` file has a list of all documents in the project, referenced by
-their label, and where they are saved.
+up in the project XML file, select **Show File Details** from the **Document** menu when having the
+document open in the editor, or look in the ``ToC.txt`` file in the root of the project folder. The
+``ToC.txt`` file has a list of all documents in the project, referenced by their label, and where
+they are saved.
The reason for this cryptic file naming is to avoid issues with file naming conventions and
restrictions on different operating systems, and also to have a file name that does not depend on
@@ -109,7 +109,7 @@ The Project Index
Between writing sessions, the project index is saved in a JSON file in ``meta/index.json``.
This file is not critical. If it is lost, it can be completely rebuilt from within novelWriter from
-the :guilabel:`Tools` menu.
+the **Tools** menu.
The index is maintained and updated whenever a document or note is saved in the editor. It contains
all references and tags in documents and notes, as well as the location of all headers in the
@@ -125,8 +125,8 @@ the index. If this too fails, you have likely encountered a bug.
Build Definitions
-----------------
-The build definitions from the :guilabel:`Manuscript Build` tool are kept in the
-``meta/builds.json`` file. If this file is lost, all custom build definitions are lost too.
+The build definitions from the **Manuscript Build** tool are kept in the ``meta/builds.json`` file.
+If this file is lost, all custom build definitions are lost too.
Cached GUI Options
@@ -144,17 +144,17 @@ Custom Word List
----------------
A file named ``meta/userdict.json`` contains all the custom words you've added to the project for
-spell checking purposes. The content of the file can be edited from the :guilabel:`Tools` menu. If
-you lose this file, all your custom spell check words will be lost too.
+spell checking purposes. The content of the file can be edited from the **Tools** menu. If you lose
+this file, all your custom spell check words will be lost too.
Session Stats
-------------
The writing progress is saved in the ``meta/sessions.jsonl`` file. This file records the length
-and word counts of each writing session on the given project. The file is used by the
-:guilabel:`Writing Statistics` tool. If this file is lost, the history it contains is also lost,
-but it has otherwise no impact on the project.
+and word counts of each writing session on the given project. The file is used by the **Writing
+Statistics** tool. If this file is lost, the history it contains is also lost, but it has otherwise
+no impact on the project.
Each session is recorded as a JSON object on a single line of the file. Each session record is
appended tot he file.
diff --git a/docs/source/usage_breakdown.rst b/docs/source/usage_breakdown.rst
index c0f14968..f34f40ec 100644
--- a/docs/source/usage_breakdown.rst
+++ b/docs/source/usage_breakdown.rst
@@ -21,14 +21,15 @@ GUI Layout and Design
The user interface of novelWriter is intended to be as minimalistic as practically possible, while
at the same time provide useful features needed for writing a novel.
-The main window does not have an editor toolbar like many other applications do. This reduces
-clutter, and since the documents are formatted with style tags, it is more or less redundant.
-Still, a small formatting toolbar can be popped out by clicking the three dots in the header of the
-document editor to give quick access to standard formatting codes.
+The main window does not by default have an editor toolbar like many other applications do. This
+reduces clutter, and since the documents are formatted with style tags, it is not needed most of
+the time. Still, a small formatting toolbar can be popped out by clicking the left-most button in
+the header of the document editor. It gives quick access to standard formatting codes.
Most formatting features supported are available through convenient keyboard shortcuts. They are
-also available in the main menu, so you don't have to look up formatting codes every time you need
-them. For reference, a list of all shortcuts can be found in the :ref:`a_kb` chapter.
+also available in the main menu under **Format**, so you don't have to look up formatting codes
+every time you need them. For reference, a list of all shortcuts can be found in the :ref:`a_kb`
+chapter.
.. note::
novelWriter is not intended to be a full office type word processor. It doesn't support images,
@@ -37,13 +38,13 @@ them. For reference, a list of all shortcuts can be found in the :ref:`a_kb` cha
simple features.
On the left side of the main window, you will find a sidebar. This bar has buttons for the standard
-views you can switch between, a quick link to the :guilabel:`Build Manuscript` tool, and a set of
+views you can switch between, a quick link to the **Build Manuscript** tool, and a set of
project-related tools and quick access to settings at the bottom.
.. versionadded:: 2.2
A number of new formatting options were added in 2.2 to allow for some special formatting cases.
At the same time, a small formatting toolbar was added in the editor. It is hidden by default,
- but can be opened by pressing the three dots icon in the top right corner.
+ but can be opened by pressing the icon button in the top right corner.
Project Tree and Editor View
@@ -53,19 +54,19 @@ Project Tree and Editor View
A screenshot of the Project Tree and Editor View.
-When in :guilabel:`Project Tree View` mode, the main work area of the main window is split in two,
-or optionally three, panels. The left-most panel contains the project tree and all the documents in
-your project. The second panel is the document editor.
+When the application is in **Project Tree View** mode, the main work area of the main window is
+split in two, or optionally three, panels. The left-most panel contains the project tree and all
+the documents in your project. The second panel is the document editor.
An optional third panel on the right side contains a document viewer which can view any document in
your project independently of what is open in the document editor. This panel is not intended as a
-preview window, although you can use it for this purpose if you wish if you need to check that the
-formatting tags behave as you expected. The main purpose of the viewer is for viewing your notes
-next to your editor while you're writing.
+preview window, although you can use it for this purpose if you wish. For instance if you need to
+check that the formatting tags behave as you expect. However, the main purpose of the viewer is for
+viewing your notes next to your editor while you're writing.
-The editor also has a :guilabel:`Focus Mode` you can toggle either from the menu, from the icon in
-the editor's header, or by pressing :kbd:`F8`. When :guilabel:`Focus Mode` is enabled, all the user
-interface elements other than the document editor itself are hidden away.
+The editor also has a **Focus Mode** you can toggle either from the menu, from the icon in the
+editor's header, or by pressing :kbd:`F8`. When **Focus Mode** is enabled, all the user interface
+elements other than the document editor itself are hidden away.
Novel View and Editor View
@@ -73,31 +74,32 @@ Novel View and Editor View
.. figure:: images/fig_novel_tree_view.png
- A screenshot of the Novel Tree View.
+ A screenshot of the Novel Tree and Editor View.
-When in :guilabel:`Novel Tree View` mode, the project tree is replaced by an overview of your novel
-structure for a specific Novel :term:`root folder`. Instead of showing individual documents, the
-tree now shows all headings of your novel text. This includes multiple headings within the same
-document.
+When the application is in **Novel Tree View** mode, the project tree is replaced by an overview of
+your novel structure for a specific Novel :term:`root folder`. Instead of showing individual
+documents, the tree now shows all headings of your novel text. This includes multiple headings
+within the same document.
-Each heading is indented according to the heading level. You can open and edit your novel documents
-from this view as well. All headings contained in the currently open document should be highlighted
-in the view to indicate which ones belong together in the same document.
+Each heading is indented according to the heading level, not its parent/child relationship to other
+elements of your project. You can open and edit your novel documents from this view as well. All
+headings contained in the currently open document should be highlighted in the view to indicate
+which ones belong together in the same document.
-If you have multiple "Novel" type root folders, the header of the novel view becomes a dropdown
+If you have multiple **Novel** type root folders, the header of the novel view becomes a dropdown
box. You can then switch between them by clicking the :guilabel:`Outline of ...` text. You can also
click the novel icon button next to it.
Generally, the novel view should update when you make changes to the novel structure, including
edits of the current document in the editor. The information is only updated when the automatic
save of the document is triggered, or you manually press :kbd:`Ctrl+S` to save changes. (You can
-adjust the auto-save interval in :guilabel:`Preferences`.) You can also regenerate the whole novel
-view by pressing the refresh button in the novel view header.
+adjust the auto-save interval in **Preferences**.) You can also regenerate the whole novel view by
+pressing the refresh button in the novel view header.
It is possible to show an optional third column in the novel view. The settings are available from
the menu button in the toolbar.
-If you click the arrow icon to the right of each item, a tooltip will pop out showing you all the
+If you click the triangular icon to the right of each item, a tooltip will pop out showing all the
meta data collected for that heading.
@@ -108,9 +110,9 @@ Novel Outline View
A screenshot of the Novel Outline View.
-When in :guilabel:`Novel Outline View` mode, the tree, editor and viewer will be replaced by a
-large table that shows the entire novel structure with all the tags and references listed. Pretty
-much all collected meta data is available here in different columns.
+When the application is in **Novel Outline View** mode, the tree, editor and viewer will be
+replaced by a large table that shows the entire novel structure with all the tags and references
+listed. Pretty much all collected meta data is available here in different columns.
You can select which novel root folder to display from the dropdown box, and you can select which
columns to show or hide from the menu button. You can also rearrange the columns by drag and drop.
@@ -122,18 +124,30 @@ Colour Themes
By default, novelWriter will use the colour theme provided by the Qt library, which is determined
by the Fusion_ style setting. You can also choose between a standard dark and light theme that have
-neutral colours from :guilabel:`Preferences`. Other colour themes are also available. More themes
-can be contributed to novelWriter on GitHub.
+neutral colours from **Preferences**.
+
+If you wish, you *can* create your own colour themes, and even have them added to the application.
+See :ref:`a_custom_theme` for more details.
Switching the GUI colour theme does not affect the colours of the editor and viewer. They have
-separate colour selectable from the "Document colour theme" setting in :guilabel:`Preferences`.
-They are separated because there are a lot more options to choose from for the editor and viewer.
+separate colour selectable from the "Document colour theme" setting in **Preferences**. They are
+separated because there are a lot more options to choose from for the editor and viewer.
.. note::
If you switch between light and dark mode on the GUI, you should also switch editor theme to
match, otherwise icons may be hard to see in the editor and viewer.
+Project Search
+--------------
+
+A global search tool is available from the side bar. It allows you to search through your entire
+project. The tool does not provide a replace feature. There is a search and replace tool available
+in the document editor that acts on the open document.
+
+.. versionadded:: 2.4
+
+
.. _a_breakdown_project:
Project Layout
@@ -152,12 +166,12 @@ within those documents.
An illustration of how heading levels correspond to the novel structure.
-The four heading levels (**H1** to **H4**) are treated as follows:
+The four heading levels, **Level 1** to **Level 4**, are treated as follows:
-* **H1** is used for the novel title, and for partitions.
-* **H2** is used for chapter tiles.
-* **H3** is used for scene titles -- optionally replaced by separators.
-* **H4** is for section titles within scenes, if such granularity is needed.
+* **Level 1** is used for the novel title, and for partitions.
+* **Level 2** is used for chapter tiles.
+* **Level 3** is used for scene titles -- optionally replaced by separators.
+* **Level 4** is for section titles within scenes, if such granularity is needed.
The project tree will select an icon for the document based on the first heading in it.
@@ -178,25 +192,25 @@ Building a Manuscript
=====================
The project can at any time be assembled into a range of different formats through the
-:guilabel:`Build Manuscript` tool. Natively, novelWriter supports `Open Document`_, HTML5, and
+**Build Manuscript** tool. Natively, novelWriter supports `Open Document`_, HTML5, and
various flavours of Markdown.
The HTML5 format is suitable for conversion by a number of other tools like Pandoc_, or for
importing into word processors if the Open Document format isn't suitable. The Open Document format
-is supported by most Office type applications. In addition, printing is also possible. Print to PDF
+is supported by most office type applications. In addition, printing is also possible. Print to PDF
is available from the print dialog.
-In addition, you can export the content of the project to a JSON file. This is useful if you want
-to write your own custom processing script in for instance Python, as the entire novel can be read
-into a Python dictionary with a couple of lines of code. The JSON file can be populated with either
-HTML formatted text, or with the raw text as typed into the novel documents.
+For advanced processing, you can export the content of the project to a JSON file. This is useful
+if you want to write your own custom processing script in for instance Python, as the entire novel
+can be read into a Python dictionary with a couple of lines of code. The JSON file can be populated
+with either HTML formatted text, or with the raw text as typed into the novel documents.
See :ref:`a_manuscript` for more details.
.. versionadded:: 2.1
- You can now define multiple build definitions in the :guilabel:`Build Manuscript` tool. This
- allows you to define specific settings for various types of draft documents, outline documents,
- and manuscript formats. See :ref:`a_manuscript` for more details.
+ You can now define multiple build definitions in the **Build Manuscript** tool. This allows you
+ to define specific settings for various types of draft documents, outline documents, and
+ manuscript formats. See :ref:`a_manuscript` for more details.
.. _a_breakdown_storage:
diff --git a/docs/source/usage_format.rst b/docs/source/usage_format.rst
index f452c5fd..85b79c54 100644
--- a/docs/source/usage_format.rst
+++ b/docs/source/usage_format.rst
@@ -10,7 +10,7 @@ values and allowing for some text formatting. The syntax is based on Markdown, b
(bold) and strike through text, as well as four levels of headings. For some further complex
formatting needs, a set of shortcodes can be used.
-In addition to formatting codes, novelWriter allows for comments, a synopsis tag, and a set of
+In addition to formatting codes, novelWriter allows for comments, a synopsis tag, and a number of
keyword and value sets used for :term:`tags` and :term:`references`. There are also
some codes that apply to whole paragraphs. See :ref:`a_fmt_text` for more details.
@@ -35,7 +35,7 @@ have a distinct colour, and the references themselves will get a colour if they
references will get a squiggly error line underneath. The same applies to duplicate tags.
There are a number of syntax highlighter colour themes available, both for light and dark GUIs. You
-can select them from :guilabel:`Preferences`.
+can select them from **Preferences**.
.. _a_fmt_head:
@@ -54,6 +54,9 @@ correctly to produce the intended result. See :ref:`a_struct_heads` for more det
``# Title Text``
Heading level one. For novel documents, the level indicates the start of a new partition.
+ Partitions are for when you want to split your story into "Part 1", "Part 2", etc. You can also
+ choose to use them for splitting the text up into acts, and then hide these headings in your
+ manuscript.
``## Title Text``
Heading level two. For novel documents, the level indicates the start of a new chapter. Chapter
@@ -62,16 +65,17 @@ correctly to produce the intended result. See :ref:`a_struct_heads` for more det
``### Title Text``
Heading level three. For novel documents, the level indicates the start of a new scene. Scene
numbers or scene separators can be inserted automatically when building the manuscript, so you
- can use the title field as a working title for your scenes if you wish.
+ can use the title field as a working title for your scenes if you wish, but you must provide a
+ minimal title.
``#### Title Text``
Heading level four. For novel documents, the level indicates the start of a new section. Section
titles can be replaced by separators or ignored completely when building the manuscript.
-For headings level one through three, adding a ``!`` modifies its meaning:
+For headings level one through three, adding a ``!`` modifies the meaning of the heading:
``#! Title Text``
- This tells the build tool that the level one heading is intended to be used for the novel's or
+ This tells the build tool that the level one heading is intended to be used for the novel or
notes folder's main title, like for instance on the front page. When building the manuscript,
this will use a different styling and will exclude the title from, for instance, a Table of
Contents in Libre Office.
@@ -82,9 +86,10 @@ For headings level one through three, adding a ``!`` modifies its meaning:
:ref:`a_struct_heads_unnum` for more details.
``###! Title Text``
- This is an alternative scene heading that can be formatted differently in the
- :guilabel:`Manuscript Build` tool. It is intended for separating "soft" and "hard" scene breaks.
- Otherwise, it behaves identically to a regular scene heading.
+ This is an alternative scene heading that can be formatted differently in the **Manuscript
+ Build** tool. It is intended for separating "soft" and "hard" scene breaks. Aside from this, it
+ behaves identically to a regular scene heading. See :ref:`a_struct_heads_scenes` for more
+ details.
.. note::
The space after the ``#`` or ``!`` character is mandatory. The syntax highlighter will change
@@ -106,8 +111,8 @@ In addition, the editor supports a few additional types of white spaces:
* Thin spaces are also supported, and can be inserted with :kbd:`Ctrl+K`, :kbd:`Shift+Space`.
* Non-breaking thin space can be inserted with :kbd:`Ctrl+K`, :kbd:`Ctrl+Space`.
-These are all insert features, and the :guilabel:`Insert` menu has more. The keyboard shortcuts for
-them are also listed in :ref:`a_kb_ins`.
+These are all insert features, and the **Insert** menu has more. The keyboard shortcuts for them
+are also listed in :ref:`a_kb_ins`.
Non-breaking spaces are highlighted by the syntax highlighter with an alternate coloured
background, depending on the selected theme.
@@ -129,12 +134,12 @@ A minimal set of Markdown text emphasis styles are supported for text paragraphs
The text is rendered as emphasised text (italicised).
``**text**``
- The text is rendered as strongly important text (bold).
+ The text is rendered as strongly emphasised text (bold).
``~~text~~``
Strike through text.
-In Markdown guides it is often recommended to differentiate between strong importance and emphasis
+In Markdown guides it is often recommended to differentiate between strong emphasis and emphasis
by using ``**`` for strong and ``_`` for emphasis, although Markdown generally also supports ``__``
for strong and ``*`` for emphasis. However, since the differentiation makes the highlighting and
conversion significantly simpler and faster, in novelWriter this is a rule, not just a
@@ -182,14 +187,15 @@ solved with simple Markdown-like formatting codes. Available shortcodes are list
"``[i]text[/i]``", "Text is rendered as italicised text."
"``[s]text[/s]``", "Text is rendered as strike through text."
"``[u]text[/u]``", "Text is rendered as underlined text."
+ "``[m]text[/m]``", "Text is rendered as highlighted text."
"``[sup]text[/sup]``", "Text is rendered as superscript text."
"``[sub]text[/sub]``", "Text is rendered as subscript text."
Unlike Markdown style codes, these can be used anywhere within a paragraph. Even in the middle of a
word if you need to. You can also freely combine them to form more complex formatting.
-The shortcodes are available from the :guilabel:`Format` menu and in the editor toolbar, which can
-be activated by clicking the three dots in the editor header.
+The shortcodes are available from the **Format** menu and in the editor toolbar, which can be
+activated by clicking the left-most icon button in the editor header.
.. versionadded:: 2.2
@@ -199,30 +205,39 @@ be activated by clicking the three dots in the editor header.
Comments and Synopsis
=====================
-In addition to these standard Markdown features, novelWriter also allows for comments in documents.
-The text of a comment is ignored by the word counter. The text can also be filtered out when
-building the manuscript or viewing the document.
+In addition to the above formatting features, novelWriter also allows for comments in documents.
+The text of a comment is always ignored by the word counter. The text can also be filtered out
+when building the manuscript or viewing the document.
-If the first word of a comment is ``Synopsis:`` (with the colon included), the comment is treated
-in a special manner and will show up in the :ref:`a_ui_outline` in a dedicated column. The word
-``synopsis`` is not case sensitive. If it is correctly formatted, the syntax highlighter will
-indicate this by altering the colour of the word.
+The first word of a comment, followed by a colon, can be one of a small set of modifiers that
+indicates the comment is intended for a specific purpose. For instance, if the comment starts with
+``Synopsis:``, the comment is treated in a special manner and will show up in the
+:ref:`a_ui_outline` in a dedicated column. The word ``synopsis`` is not case sensitive. If it is
+correctly formatted, the syntax highlighter will indicate this by altering the colour of the word.
-``% text ...``
+The different styles of comments are as follows:
+
+``% Comment text ...``
This is a comment. The text is not rendered by default (this can be overridden), seen in the
- document viewer, or counted towards word counts.
+ document viewer, or counted towards word counts. It is intended for you to make notes in your
+ text for your own sake, whatever that may be, that isn't part of the story text. This is the
+ general format of a comment.
-``%Synopsis: text ...``
+``%Synopsis: Comment text ...``
This is a synopsis comment. It is generally treated in the same way as a regular comment, except
that it is also captured by the indexing algorithm and displayed in the :ref:`a_ui_outline`. It
can also be filtered separately when building the project to for instance generate an outline
document of the whole project.
-``%Short: text ...``
+``%Short: Comment text ...``
This is a short description comment. It is identical to the synopsis comment (they are
interchangeable), but is intended to be used for project notes. The text shows up in the
- Reference panel below the document viewer in the last column labelled
- :guilabel:`Short Description`.
+ Reference panel below the document viewer in the last column labelled **Short Description**.
+
+``%~ Comment text ...``
+ This can be used to exclude story text from your manuscript without having to delete it from
+ your text. Comments with the ``~`` will *never* be included in the manuscript, even if you have
+ chosen to include comments in it. That is the main difference between these two formats.
.. note::
Only one comment can be flagged as a synopsis or short comment for each heading. If multiple
@@ -245,14 +260,14 @@ heading. Setting it multiple times under the same heading will just override the
A tag keyword followed by the tag value, like for instance the name of a character.
References can be set anywhere within a section, and are collected according to their category.
-References are in the form:
+References are on the form:
-``@keyword: value``
+``@keyword: value1, value2, ..., valueN``
A reference keyword followed by a value, or a comma separated list of values.
Tags and references are covered in detail in the :ref:`a_references` chapter. The keywords can be
-inserted at the cursor position in the editor via the :guilabel:`Insert` menu. If you start typing
-an ``@`` on a new line, and auto-complete menu will also pop up suggesting keywords.
+inserted at the cursor position in the editor via the **Insert** menu. If you start typing an ``@``
+on a new line, and auto-complete menu will also pop up suggesting keywords.
.. _a_fmt_align:
@@ -260,8 +275,8 @@ an ``@`` on a new line, and auto-complete menu will also pop up suggesting keywo
Paragraph Alignment and Indentation
===================================
-All documents have the text by default aligned to the left or justified, depending on your
-settings in :guilabel:`Preferences`.
+All documents have the text by default aligned to the left or justified, depending on your setting
+in **Preferences**.
You can override the default text alignment on individual paragraphs by specifying alignment tags.
These tags are double angle brackets. Either ``>>`` or ``<<``. You put them either before or after
@@ -296,6 +311,10 @@ Examples:
Vertical Space and Page Breaks
==============================
+You can apply page breaks to partition, chapter and scene headings for novel documents from the
+**Manuscript Build** tool. If you need to add a page break or additional vertical spacing in other
+places, there are special codes available for this purpose.
+
Adding more than one line break between paragraphs will **not** increase the space between those
paragraphs when building the project. To add additional space between paragraphs, add the text
``[vspace]`` on a line of its own, and the build tool will insert a blank paragraph in its place.
@@ -303,13 +322,8 @@ paragraphs when building the project. To add additional space between paragraphs
If you need multiple blank paragraphs just add a colon and a number to the above code. For
instance, writing ``[vspace:3]`` will insert three blank paragraphs.
-Normally, the manuscript build tool will insert a page break before all headings of level one and
-for all headings of level two for novel documents, i.e. chapters, but not for project notes.
-
-If you need to add a page break somewhere else, put the text ``[new page]`` on a line by itself
-before the text you wish to start on a new page.
-
-If you want page breaks for scenes and sections, you must add them manually.
+If you need to add a page break somewhere, put the text ``[new page]`` on a line by itself before
+the text you wish to start on a new page.
.. note::
The page break code is applied to the text that follows it. It adds a "page break before" mark
@@ -325,8 +339,8 @@ If you want page breaks for scenes and sections, you must add them manually.
[vspace:2]
This is another text paragraph, but there will be two empty paragraphs
- in-between them.
+ between them.
[new page]
- This text will always start on a new page if the build format has pages.
+ This text will start on a new page if the build format has pages.
diff --git a/docs/source/usage_project.rst b/docs/source/usage_project.rst
index 76bccb48..fceca8b5 100644
--- a/docs/source/usage_project.rst
+++ b/docs/source/usage_project.rst
@@ -8,7 +8,7 @@ This chapter covers in more detail the different project views available in nove
.. figure:: images/fig_project_tree_detailed.png
- The Project Tree as it appears when loading a sample project.
+ The **Project Content** tree as it appears when loading a sample project.
.. _a_ui_tree:
@@ -21,33 +21,32 @@ the project, and has four columns.
**Column 1**
The first column shows the icon and label of each folder, document, or note in your project. The
- label is not the same as the title you set inside the document. However, the document's label
- will appear in the header above the document text itself so you know where in the project an
- open document belongs. The icon is selected based on the type of item, and for novel documents,
- the level of the first header in the document text.
+ label is not the same as the heading title you set inside the document. However, the document's
+ label will appear in the header above the document text itself so you know where in the project
+ an open document belongs. The icon is selected based on the type of item, and for novel
+ documents, the level of the first heading in the document text.
**Column 2**
The second column shows the word count of the document, or the sum of words of the child items
for folders and documents with sub-documents. If the counts seem incorrect, they can be updated
- by rebuilding the :term:`project index` from the :guilabel:`Tools` menu, or by pressing
- :kbd:`F9`.
+ by rebuilding the :term:`project index` from the **Tools** menu, or by pressing :kbd:`F9`.
**Column 3**
The third column indicates whether the document is considered active or inactive in the project.
You can use this flag to indicate that a document is still in the project, but should not be
- considered an active part of it. When you run the :guilabel:`Build Manuscript` tool, you can
- include or exclude documents based on this flag. You can change this value from the
+ considered an active part of it. When you run the **Build Manuscript** tool, you can include or
+ exclude documents based on this flag. You can change this value from the right-click
:term:`context menu`.
**Column 4**
The fourth column shows the user-defined status or importance labels you've assigned to each
project item. See :ref:`a_ui_tree_status` for more details on how to uses these labels. You can
- change these labels from the :term:`context menu`.
+ select these labels from the :term:`context menu`, and define them in **Project Settings**.
Right-clicking an item in the project tree will open a context menu under the cursor, displaying
a selection of actions that can be performed on the selected item.
-At the top of the tree, you will find a set of buttons.
+At the top of the project tree, you will find a set of buttons.
* The first button is a quick links button that will show you a dropdown menu of all the
:term:`root folders` in your project. Selecting one will move to that position in
@@ -73,10 +72,10 @@ addition to the word count.
Splitting and Merging Documents
-------------------------------
-Under the :guilabel:`Transform` submenu in the context menu of an item in the project tree, you
-will find several options on how to change a document or folder. This includes changing between
-document and note, but also splitting them into multiple documents, or merging child items into a
-single document.
+Under the **Transform** submenu in the context menu of an item in the project tree, you will find
+several options on how to change a document or folder. This includes changing between document and
+note, but also splitting them into multiple documents, or merging child items into a single
+document.
Splitting Documents
@@ -84,12 +83,12 @@ Splitting Documents
.. figure:: images/fig_project_split_tool.png
- The :guilabel:`Split Document` dialog.
+ The **Split Document** dialog.
-The :guilabel:`Split Document by Heading` option will open a dialog that allows you to split the
-selected document into multiple new documents based on the headings it contains. You can select at
-which heading level the split is to be performed from the dropdown box. The list box will preview
-which headings will be split into new documents.
+The **Split Document by Headings** option will open a dialog that allows you to split the selected
+document into multiple new documents based on the headings it contains. You can select at which
+heading level the split is to be performed from the dropdown box. The list box will preview which
+headings will be split into new documents.
You are given the option to create a folder for these new documents, and whether or not to create a
hierarchy of documents. That is, put sections under scenes, and scenes under chapters.
@@ -103,18 +102,18 @@ Merging Documents
.. figure:: images/fig_project_merge_tool.png
- The :guilabel:`Merge Documents` dialog.
+ The **Merge Documents** dialog.
You have two options for merging documents that are child elements of another document. You can
-either :guilabel:`Merge Child Items into Self` and :guilabel:`Merge Child Items into New`. The
-first option will pull all content of child items and merge them into the parent document, while
-the second option will create a new document in the process.
+either **Merge Child Items into Self** and **Merge Child Items into New**. The first option will
+pull all content of child items and merge them into the parent document, while the second option
+will create a new document in the process.
When merging documents in a folder, you only have the latter process is possible, so only the
-choice :guilabel:`Merge Documents in Folder` is available.
+choice **Merge Documents in Folder** is available.
-In either case, the :guilabel:`Merge Documents` dialog will let you exclude documents you don't
-want to include, and it also lets you reorder them if you wish.
+In either case, the **Merge Documents** dialog will let you exclude documents you don't want to
+include, and it also lets you reorder them if you wish.
.. _a_ui_tree_status:
@@ -124,14 +123,14 @@ Document Importance and Status
Each document or folder in your project can have either a "Status" or "Importance" flag set. These
are flags that you control and define yourself, and novelWriter doesn't use them for anything. To
-modify the labels, go to their respective tabs in :guilabel:`Project Settings`.
+modify the labels, go to their respective tabs in **Project Settings**.
The "Status" flag is intended to tag a :term:`novel document` as for instance a
draft or as completed, and the "Importance" flag is intended to tag character notes, or other
:term:`project notes`, as for instance a main, major, or minor character or story element.
Whether a document uses a "Status" or "Importance" flag depends on which :term:`root folder` it
-lives in. If it's in a Novel type folder, it uses the "Status" flag, otherwise it uses an
+lives in. If it's in a **Novel** type folder, it uses the "Status" flag, otherwise it uses an
"Importance" flag.
@@ -142,7 +141,7 @@ Project Tree Drag & Drop
The project tree allows drag & drop to a certain extent to allow you to reorder your documents and
folders. Moving a document in the project tree will affect the text's position when you assemble
-your manuscript in the :guilabel:`Manuscript Build` tool.
+your manuscript in the **Manuscript Build** tool.
.. versionadded:: 2.2
You can now select multiple items in the project tree by holding down the :kbd:`Ctrl` or
@@ -150,14 +149,15 @@ your manuscript in the :guilabel:`Manuscript Build` tool.
You can drag and drop documents and regular folders, but not root folders. If you select multiple
items, they can only be dragged and dropped if they are siblings. That is, they have the same
-parent item in the project. This is due to the way drag and drop is implemented in the user
-interface framework novelWriter is built upon.
+parent item in the project. This limitation is due to the way drag and drop is implemented in the
+user interface framework novelWriter is built upon.
-Documents and their folders can be rearranged freely within their root folders. If you move a Novel
-document out of a Novel folder, it will be converted to a project note. Notes can be moved freely
-between all root folders, but keep in mind that if you move a note into a Novel type root folder,
-its "Importance" setting will be switched with a "Status" setting. See :ref:`a_ui_tree_status`. The
-old value will not be overwritten though, and should be restored if you move it back at some point.
+Documents and their folders can be rearranged freely within their root folders. If you move a
+**Novel Document** out of a **Novel** folder, it will be converted to a **Project Note**. Notes can
+be moved freely between all root folders, but keep in mind that if you move a note into a **Novel**
+type root folder, its "Importance" setting will be switched with a "Status" setting. See
+:ref:`a_ui_tree_status`. The old value will not be overwritten though, and should be restored if
+you move it back at some point.
Root folders in the project tree cannot be dragged and dropped at all. If you want to reorder them,
you can move them up or down with respect to each other from the arrow buttons at the top of the
@@ -175,8 +175,8 @@ The Novel Tree View
An alternative way to view the project structure is the novel view. You can switch to this view by
selecting the :guilabel:`Novel View` button in the sidebar. This view is a simplified version of
-the view in the :guilabel:`Outline View`. It is convenient when you want to browse the structure
-of the story itself rather than the document files.
+the view in the **Outline View**. It is convenient when you want to browse the structure of the
+story itself rather than the document files.
.. note::
You cannot reorganise the entries in the novel view, or add any new documents, as that would
@@ -194,14 +194,14 @@ The Novel Outline View
A screenshot of the Novel Outline View.
-The project's :guilabel:`Novel Outline View` is available as another view option from the sidebar.
-The outline provides an overview of the novel structure, displaying a tree hierarchy of the
-elements of the novel, that is, the level 1 to 4 headings representing partitions, chapters, scenes
-and sections.
+The project's **Novel Outline View** is available as another view option from the sidebar. The
+outline provides an overview of the novel structure, displaying a tree hierarchy of the elements of
+the novel, that is, the level 1 to 4 headings representing partitions, chapters, scenes and
+sections.
The document containing the heading can also be displayed as a separate column, as well as the line
number where the heading is defined. Double-clicking an entry will open the corresponding document
-in the editor and switch to :guilabel:`Project Tree View` mode.
+in the editor and switch to **Project Tree View** mode.
You can select which novel folder to display from the dropdown menu. You can optionally also choose
to show a combination of all novel folders.
@@ -217,7 +217,7 @@ the menu button in the toolbar. The order of the columns can also be rearranged
a different position. You column settings are saved between sessions on a per-project basis.
.. note::
- The :guilabel:`Title` column cannot be disabled or moved.
+ The **Title** column cannot be disabled or moved.
The information viewed in the outline is based on the :term:`project index`. While novelWriter does
its best to keep the index up to date when contents change, you can always rebuild it manually by
@@ -226,5 +226,5 @@ pressing :kbd:`F9` if something isn't right.
The outline view itself can be regenerated by pressing the refresh button. By default, the content
is refreshed each time you switch to this view.
-The :guilabel:`Synopsis` column of the outline view takes its information from a specially
-formatted comment. See :ref:`a_fmt_comm`.
+The **Synopsis** column of the outline view takes its information from a specially formatted
+comment. See :ref:`a_fmt_comm`.
diff --git a/docs/source/usage_shortcuts.rst b/docs/source/usage_shortcuts.rst
index af54254b..a8f8163f 100644
--- a/docs/source/usage_shortcuts.rst
+++ b/docs/source/usage_shortcuts.rst
@@ -20,22 +20,22 @@ Main Window Shortcuts
:header: "Shortcut", "Description"
":kbd:`F1`", "Open the online user manual"
- ":kbd:`F5`", "Open the :guilabel:`Build Manuscript` tool"
- ":kbd:`F6`", "Open the :guilabel:`Writing Statistics` tool"
- ":kbd:`F8`", "Toggle :guilabel:`Focus Mode`"
+ ":kbd:`F5`", "Open the **Build Manuscript** tool"
+ ":kbd:`F6`", "Open the **Writing Statistics** tool"
+ ":kbd:`F8`", "Toggle **Focus Mode**"
":kbd:`F9`", "Re-build the project's index"
":kbd:`F11`", "Toggle full screen mode"
- ":kbd:`Ctrl+,`", "Open the :guilabel:`Preferences` dialog"
+ ":kbd:`Ctrl+,`", "Open the **Preferences** dialog"
":kbd:`Ctrl+E`", "Switch focus to the document editor"
":kbd:`Ctrl+T`", "Switch or toggle focus for the project tree or novel view"
":kbd:`Ctrl+Q`", "Exit novelWriter"
- ":kbd:`Ctrl+Shift+,`", "Open the :guilabel:`Project Settings` dialog"
+ ":kbd:`Ctrl+Shift+,`", "Open the **Project Settings** dialog"
":kbd:`Ctrl+Shift+O`", "Open the Welcome dialog to open or create a project"
":kbd:`Ctrl+Shift+S`", "Save the current project"
":kbd:`Ctrl+Shift+T`", "Switch focus to the outline view"
":kbd:`Ctrl+Shift+W`", "Close the current project"
":kbd:`Shift+F1`", "Open the local user manual (PDF) if it is available"
- ":kbd:`Shift+F6`", "Open the :guilabel:`Project Details` dialog"
+ ":kbd:`Shift+F6`", "Open the **Project Details** dialog"
.. _a_kb_tree:
@@ -53,8 +53,8 @@ Project Tree Shortcuts
":kbd:`Alt+Left`", "Jump to the parent item in the tree"
":kbd:`Alt+Right`", "Jump to the first child item in the project tree"
":kbd:`Ctrl+.`", "Open the context menu on the selected item"
- ":kbd:`Ctrl+L`", "Open the :guilabel:`Quick Links` menu"
- ":kbd:`Ctrl+N`", "Open the :guilabel:`Create New Item` menu"
+ ":kbd:`Ctrl+L`", "Open the **Quick Links** menu"
+ ":kbd:`Ctrl+N`", "Open the **Create New Item** menu"
":kbd:`Ctrl+O`", "Open the selected document in the editor"
":kbd:`Ctrl+R`", "Open the selected document in the viewer"
":kbd:`Ctrl+Up`", "Move selected item one step up in the tree"
@@ -75,11 +75,12 @@ Text Search Shortcuts
:header: "Shortcut", "Description"
":kbd:`F3`", "Find the next occurrence of the search word"
- ":kbd:`Ctrl+F`", "Open the search bar and search for the selected word, if any is selected"
+ ":kbd:`Ctrl+F`", "Open search and look for the selected word"
":kbd:`Ctrl+G`", "Find the next occurrence of the search word"
- ":kbd:`Ctrl+H`", "Open the search tool and populate with the selected word (Mac :kbd:`Cmd+=`)"
- ":kbd:`Ctrl+Shift+1`", "Replace selected occurrence of the search word, and move to the next"
+ ":kbd:`Ctrl+H`", "Open replace and look for the selected word (Mac :kbd:`Cmd+=`)"
+ ":kbd:`Ctrl+Shift+1`", "Replace selected occurrence, and move to the next"
":kbd:`Ctrl+Shift+G`", "Find the previous occurrence of the search word"
+ ":kbd:`Ctrl+Shift+F`", "Open project search and look for the selected word"
":kbd:`Shift+F3`", "Find the previous occurrence of the search word"
@@ -91,8 +92,8 @@ Text Formatting Shortcuts
":kbd:`Ctrl+'`", "Wrap selected text, or word under cursor, in single quotes"
":kbd:`Ctrl+""`", "Wrap selected text, or word under cursor, in double quotes"
- ":kbd:`Ctrl+/`", "Toggle block format as comment for block under cursor, or selected text"
- ":kbd:`Ctrl+0`", "Remove block formatting for block under cursor, or selected text"
+ ":kbd:`Ctrl+/`", "Toggle comment format for block or selected text"
+ ":kbd:`Ctrl+0`", "Remove format for block or selected text"
":kbd:`Ctrl+1`", "Change block format to heading level 1"
":kbd:`Ctrl+2`", "Change block format to heading level 2"
":kbd:`Ctrl+3`", "Change block format to heading level 3"
@@ -102,11 +103,11 @@ Text Formatting Shortcuts
":kbd:`Ctrl+7`", "Change block alignment to right-aligned"
":kbd:`Ctrl+8`", "Add a left margin to the block"
":kbd:`Ctrl+9`", "Add a right margin to the block"
- ":kbd:`Ctrl+B`", "Format selected text, or word under cursor, with strong emphasis (bold)"
+ ":kbd:`Ctrl+B`", "Format selected text, or word under cursor, with bold"
":kbd:`Ctrl+D`", "Format selected text, or word under cursor, with strike through"
- ":kbd:`Ctrl+I`", "Format selected text, or word under cursor, with emphasis (italic)"
- ":kbd:`Ctrl+Shift+/`", "Remove block formatting for block under cursor, or selected text"
- ":kbd:`Ctrl+Shift+D`", "Toggle block format as ignored text for block under cursor, or selected text"
+ ":kbd:`Ctrl+I`", "Format selected text, or word under cursor, with italic"
+ ":kbd:`Ctrl+Shift+/`", "Remove format for block or selected text"
+ ":kbd:`Ctrl+Shift+D`", "Toggle ignored text format for block or selected text"
Other Editor Shortcuts
diff --git a/docs/source/usage_typography.rst b/docs/source/usage_typography.rst
index e96d6185..189e0dd1 100644
--- a/docs/source/usage_typography.rst
+++ b/docs/source/usage_typography.rst
@@ -11,7 +11,7 @@ Typographical Notes
novelWriter has some support for typographical symbols that are not usually easily available in
many text editors. This includes for instance the proper unicode quotation marks, dashes, ellipsis,
-thin spaces, etc. All these symbols are available from the :guilabel:`Insert` menu, and via
+thin spaces, etc. All these symbols are available from the **Insert** menu, and via
keyboard shortcuts. See :ref:`a_kb_ins`.
This chapter provides some additional information on how novelWriter handles these symbols.
@@ -43,22 +43,22 @@ Single and Double Quotes
All the different quotation marks listed on the `Quotation Mark`_ Wikipedia page are available, and
can be selected as auto-replaced symbols for straight single and double quote key strokes. The
-settings can be found in :guilabel:`Preferences`.
+settings can be found in **Preferences**.
Ordinarily, text wrapped in quotes are highlighted by the editor. This is meant as a convenience
for highlighting dialogue between characters. This feature can be disabled in
-:guilabel:`Preferences` if this feature isn't wanted.
+**Preferences** if this feature isn't wanted.
The editor distinguishes between text wrapped in regular straight double quotes and the
user-selected double quote symbols. This is to help the writer recognise which parts of the text
-are not using the chosen quote symbols. Two convenience functions in the :guilabel:`Format` menu
+are not using the chosen quote symbols. Two convenience functions in the **Format** menu
can be used to re-format a selected section of text with the correct quote symbols.
Single and Double Prime
------------------------
-Both single and double prime symbols are available in the :guilabel:`Insert` menu. These symbols
+Both single and double prime symbols are available in the **Insert** menu. These symbols
are the correct symbols to use for unit symbols for feet, inches, minutes, and seconds. The usage
of these is described in more detail on the Wikipedia Prime_ page. They look very similar to single
and double straight quotes, and may be rendered similarly by the font, but they have different
@@ -79,7 +79,7 @@ right single quotation marks, depending on the font. There is a Wikipedia articl
`Modifier letter apostrophe`_ with more details.
.. note::
- On export with the :guilabel:`Build Manuscript` tool, these apostrophes will be replaced
+ On export with the **Build Manuscript** tool, these apostrophes will be replaced
automatically with the corresponding right hand single quote symbol as is generally recommended.
Therefore it doesn't really matter if you only use them to correct syntax highlighting.
diff --git a/docs/source/usage_writing.rst b/docs/source/usage_writing.rst
index d9d6400f..a1b59c67 100644
--- a/docs/source/usage_writing.rst
+++ b/docs/source/usage_writing.rst
@@ -23,13 +23,14 @@ having it selected. This will open the document in the document editor. The edit
Markdown-like syntax for some features, and a novelWriter-specific syntax for others. The syntax
format is described in the :ref:`a_fmt` chapter.
-The editor has a maximise button (toggles the :guilabel:`Focus Mode`) and a close button in the
+The editor has a maximise button, which toggles the **Focus Mode**, and a close button in the
top--right corner. On the top--left side you will find a tools button that opens a toolbar with a
-few buttons for applying text formatting, and a search button to open the search dialog.
+few buttons for applying text formatting, a drop down menu for navigating between headings, and a
+search button to open the search dialog.
Both the document editor and viewer will show the label of the currently open document in the
header at the top of the edit or view panel. Optionally, the full project path to the document can
-be shown. This can be set in :guilabel:`Preferences`.
+be shown. This can be set in **Preferences**.
.. tip::
Clicking on the document title bar will select the document in the project tree and thus reveal
@@ -42,10 +43,10 @@ the label and pressing :kbd:`Ctrl+Return`. You can also control-click them with
Editor Auto-Completer
---------------------
-If you type the character ``@`` on a new line, a menu will appear showing the different available
-keywords. The list will shorten as you type. Once a keyword command has been selected or typed, the
-editor may suggest further content based on your project content. See :ref:`a_references_completer`
-for more details.
+If you type the character ``@`` on a new line, a pop-up menu will appear showing the different
+available keywords. The list will shorten as you type. Once a keyword command has been selected or
+typed, the editor may suggest further options based on your project content. See
+:ref:`a_references_completer` for more details.
.. versionadded:: 2.2
@@ -60,9 +61,9 @@ Viewing a Document
A screenshot of the Document Viewer panel.
Any document in the project tree can also be viewed in parallel in a right hand side document
-viewer. To view a document, press :kbd:`Ctrl+R`, or select :guilabel:`View Document` in the menu or
-context menu. If you have a middle mouse button, middle-clicking on the document will also open it
-in the viewer.
+viewer. To view a document, press :kbd:`Ctrl+R`, or select **View Document** in the menu or context
+menu. If you have a middle mouse button, middle-clicking on the document will also open it in the
+viewer.
The document viewed does not have to be the same document as the one currently being edited.
However, If you *are* viewing the same document, pressing :kbd:`Ctrl+R` again will update the
@@ -75,19 +76,20 @@ content of the viewer with the content of the document the reference points to.
The document viewer keeps a history of viewed documents, which you can navigate with the arrow
buttons in the top--left corner of the viewer. If your mouse has backward and forward navigation
buttons, these can be used as well. They work just like the backward and forward features in a
-browser.
+browser. The left-most button is a dropdown menu for quickly navigation between headings in the
+document.
-At the bottom of the view panel there is a :guilabel:`References` panel. (If it is hidden, click
-the button on the left side of the footer area to reveal it.) This panel contains a References tab
-with links to all documents referring back to the one you're currently viewing, if any has been
-defined. If you have created root folders and tags for various story elements like characters and
-plot points, these will appear as additional tabs in this panel.
+At the bottom of the view panel there is a **References** panel. (If it is hidden, click the button
+on the left side of the footer area to reveal it.) This panel contains a References tab with links
+to all documents referring back to the one you're currently viewing, if any has been defined. If
+you have created root folders and tags for various story elements like characters and plot points,
+these will appear as additional tabs in this panel.
.. note::
- The :guilabel:`References` panel relies on an up-to-date :term:`index` of the
- project. The index is maintained automatically. However, if anything is missing, or seems wrong,
- the index can always be rebuilt by selecting :guilabel:`Rebuild Index` from the
- :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
+ The **References** panel relies on an up-to-date :term:`index` of the project.
+ The index is maintained automatically. However, if anything is missing, or seems wrong, the
+ index can always be rebuilt by selecting **Rebuild Index** from the **Tools** menu, or by
+ pressing :kbd:`F9`.
.. versionadded:: 2.2
The reference panel was redesigned and the additional tabs added.
@@ -131,8 +133,7 @@ Auto-Replace as You Type
========================
A few auto-replace features are supported by the editor. You can control every aspect of the
-auto-replace feature from :guilabel:`Preferences`. You can also disable this feature entirely if
-you wish.
+auto-replace feature from **Preferences**. You can also disable this feature entirely if you wish.
.. tip::
If you don't like auto-replacement, all symbols inserted by this feature are also available in
diff --git a/i18n/nw_base.ts b/i18n/nw_base.ts
index e458fa8b..60b73d72 100644
--- a/i18n/nw_base.ts
+++ b/i18n/nw_base.ts
@@ -50,7 +50,7 @@
- Hard Scene Format
+ Alt. Scene Format
@@ -791,22 +791,22 @@
GuiDocEditFooter
-
+ Line: {0} ({1})
-
+ Words: {0} ({1})
-
+ Words: {0} selected
-
+ Status
@@ -814,27 +814,27 @@
GuiDocEditHeader
-
+ Toggle Tool Bar
-
+ Outline
-
+ Search
-
+ Toggle Focus Mode
-
+ Close
@@ -842,62 +842,62 @@
GuiDocEditSearch
-
+ Search for
-
+ Replace with
-
+ Search
-
+ Case Sensitive
-
+ Whole Words Only
-
+ RegEx Mode
-
+ Loop Search
-
+ Search Next File
-
+ Preserve Case
-
+ Close Search
-
+ Find in current document
-
+ Find and replace in current document
@@ -920,107 +920,107 @@
-
+ Saved Document: {0}
-
+ Spell checking requires the package PyEnchant. It does not appear to be installed.
-
+ Spell check complete
-
+ Document Details
-
+ Created: {0}
-
+ Updated: {0}
-
+ File Location: {0}
-
+ Set as Document Name
-
+ Follow Tag
-
+ Create Note for Tag
-
+ Cut
-
+ Copy
-
+ Paste
-
+ Select All
-
+ Select Word
-
+ Select Paragraph
-
+ Spelling Suggestion(s)
-
+ No Suggestions
-
+ Add Word to Dictionary
-
+ Please select some text before calling replace quotes.
-
+ Do you want to create a new project note for the tag '{0}'?
@@ -1104,52 +1104,52 @@
GuiDocToolBar
-
+ Markdown Bold
-
+ Markdown Italic
-
+ Markdown Strikethrough
-
+ Shortcode Bold
-
+ Shortcode Italic
-
+ Shortcode Strikethrough
-
+ Shortcode Underline
-
+ Shortcode Highlight
-
+ Shortcode Superscript
-
+ Shortcode Subscript
@@ -1898,7 +1898,7 @@
- Hard Scene
+ Alternative Scene
diff --git a/i18n/nw_en_US.ts b/i18n/nw_en_US.ts
index a15a9cc7..b1adeacf 100644
--- a/i18n/nw_en_US.ts
+++ b/i18n/nw_en_US.ts
@@ -4,232 +4,232 @@
Builds
-
+ Document FiltersDocument Filters
-
+ Novel DocumentsNovel Documents
-
+ Project NotesProject Notes
-
+ Inactive DocumentsInactive Documents
-
+ HeadingsHeadings
-
+ Partition FormatPartition Format
-
+ Chapter FormatChapter Format
-
+ Unnumbered FormatUnnumbered Format
-
+ Scene FormatScene Format
-
- Hard Scene Format
- Hard Scene Format
+
+ Alt. Scene Format
+ Alt. Scene Format
-
+ Section FormatSection Format
-
+ Text ContentText Content
-
+ Include SynopsisInclude Synopsis
-
+ Include CommentsInclude Comments
-
+ Include KeywordsInclude Keywords
-
+ Include Body TextInclude Body Text
-
+ Ignore These KeywordsIgnore These Keywords
-
+ Insert ContentInsert Content
-
+ Add Titles for NotesAdd Titles for Notes
-
+ Text FormatText Format
-
+ Font FamilyFont Family
-
+ Font SizeFont Size
-
+ Line HeightLine Height
-
+ Text OptionsText Options
-
+ Justify Text MarginsJustify Text Margins
-
+ Replace Unicode CharactersReplace Unicode Characters
-
+ Replace Tabs with SpacesReplace Tabs with Spaces
-
+ Page LayoutPage Layout
-
+ UnitUnit
-
+ Page SizePage Size
-
+ Page WidthPage Width
-
+ Page HeightPage Height
-
+ Top MarginTop Margin
-
+ Bottom MarginBottom Margin
-
+ Left MarginLeft Margin
-
+ Right MarginRight Margin
-
+ Open Document (.odt)Open Document (.odt)
-
+ Add Highlight ColoursAdd Highlight Colors
-
+ Page HeaderPage Header
-
+ Page Counter OffsetPage Counter Offset
-
+ First Line IndentFirst Line Indent
-
+ Markdown (.md)Markdown (.md)
-
+ Preserve Hard Line BreaksPreserve Hard Line Breaks
-
+ HTML (.html)HTML (.html)
-
+ Add CSS StylesAdd CSS Styles
-
+ Preserve Tab CharactersPreserve Tab Characters
@@ -237,72 +237,72 @@
Common
-
+ in the futurein the future
-
+ just nowjust now
-
+ a minute agoa minute ago
-
+ {0} minutes ago{0} minutes ago
-
+ an hour agoan hour ago
-
+ {0} hours ago{0} hours ago
-
+ a day agoa day ago
-
+ {0} days ago{0} days ago
-
+ a week agoa week ago
-
+ {0} weeks ago{0} weeks ago
-
+ a month agoa month ago
-
+ {0} months ago{0} months ago
-
+ a year agoa year ago
-
+ {0} years ago{0} years ago
@@ -310,375 +310,375 @@
Constant
-
-
-
+
+
+ NoneNone
-
+ NovelNovel
-
-
+
+ PlotPlot
-
-
+
+ CharactersCharacters
-
-
+
+ LocationsLocations
-
-
+
+ TimelineTimeline
-
-
+
+ ObjectsObjects
-
-
+
+ EntitiesEntities
-
-
-
+
+
+ CustomCustom
-
+ ArchiveArchive
-
+ TemplatesTemplates
-
+ TrashTrash
-
-
+
+ Novel DocumentNovel Document
-
-
+
+ Project NoteProject Note
-
+ Root FolderRoot Folder
-
+ FolderFolder
-
+ Novel Title PageNovel Title Page
-
+ Novel ChapterNovel Chapter
-
+ Novel SceneNovel Scene
-
+ Novel SectionNovel Section
-
+ TagTag
-
+ Point of ViewPoint of View
-
-
+
+ FocusFocus
-
+ TitleTitle
-
+ LevelLevel
-
+ DocumentDocument
-
+ LineLine
-
+ CharsChars
-
+ WordsWords
-
+ ParsPars
-
+ POVPOV
-
+ SynopsisSynopsis
-
+ Open Document (.odt)Open Document (.odt)
-
+ Flat Open Document (.fodt)Flat Open Document (.fodt)
-
+ novelWriter HTML (.html)novelWriter HTML (.html)
-
+ novelWriter Markup (.txt)novelWriter Markup (.txt)
-
+ Standard Markdown (.md)Standard Markdown (.md)
-
+ Extended Markdown (.md)Extended Markdown (.md)
-
+ JSON + novelWriter HTML (.json)JSON + novelWriter HTML (.json)
-
+ JSON + novelWriter Markup (.json)JSON + novelWriter Markup (.json)
-
+ Text filesText files
-
+ Markdown filesMarkdown files
-
+ novelWriter filesnovelWriter files
-
+ CSV filesCSV files
-
+ All filesAll files
-
+ MillimetresMillimeters
-
+ CentimetresCentimeters
-
+ InchesInches
-
+ A4A4
-
+ A5A5
-
+ A6A6
-
+ US LegalUS Legal
-
+ US LetterUS Letter
-
+ Straight single quotation markStraight single quotation mark
-
+ Straight double quotation markStraight double quotation mark
-
+ Left single quotation markLeft single quotation mark
-
+ Right single quotation markRight single quotation mark
-
+ Single low-9 quotation markSingle low-9 quotation mark
-
+ Single high-reversed-9 quotation markSingle high-reversed-9 quotation mark
-
+ Left double quotation markLeft double quotation mark
-
+ Right double quotation markRight double quotation mark
-
+ Double low-9 quotation markDouble low-9 quotation mark
-
+ Double high-reversed-9 quotation markDouble high-reversed-9 quotation mark
-
+ Double low-reversed-9 quotation markDouble low-reversed-9 quotation mark
-
+ Single left-pointing angle quotation markSingle left-pointing angle quotation mark
-
+ Single right-pointing angle quotation markSingle right-pointing angle quotation mark
-
+ Double left-pointing angle quotation markDouble left-pointing angle quotation mark
-
+ Double right-pointing angle quotation markDouble right-pointing angle quotation mark
-
+ Left corner bracketLeft corner bracket
-
+ Right corner bracketRight corner bracket
-
+ Left white corner bracketLeft white corner bracket
-
+ Right white corner bracketRight white corner bracket
@@ -686,17 +686,17 @@
GuiAbout
-
+ About novelWriterAbout novelWriter
-
+ This application is licenced under {0}This application is licensed under {0}
-
+ CreditsCredits
@@ -704,38 +704,38 @@
GuiBuildSettings
-
-
+
+ Manuscript Build SettingsManuscript Build Settings
-
+ NameName
-
+ SelectionSelection
-
+ HeadingsHeadings
-
+ ContentContent
-
+ FormatFormat
-
+ OutputOutput
@@ -743,47 +743,47 @@
GuiDictionaries
-
+ Add DictionariesAdd Dictionaries
-
+ Download a dictionary from one of the links, and add it below.Download a dictionary from one of the links, and add it below.
-
+ Add DictionaryAdd Dictionary
-
+ Dictionary install locationDictionary install location
-
+ Additional dictionaries found: {0}Additional dictionaries found: {0}
-
+ Free or Libre Office extensionFree or Libre Office extension
-
+ Browse FilesBrowse Files
-
+ Could not process dictionary fileCould not process dictionary file
-
+ Added: {0} [{1}B]Added: {0} [{1}B]
@@ -791,22 +791,22 @@
GuiDocEditFooter
-
+ Line: {0} ({1})Line: {0} ({1})
-
+ Words: {0} ({1})Words: {0} ({1})
-
+ Words: {0} selectedWords: {0} selected
-
+ StatusStatus
@@ -814,27 +814,27 @@
GuiDocEditHeader
-
+ Toggle Tool BarToggle Tool Bar
-
+ OutlineOutline
-
+ SearchSearch
-
+ Toggle Focus ModeToggle Focus Mode
-
+ CloseClose
@@ -842,62 +842,62 @@
GuiDocEditSearch
-
+ Search forSearch for
-
+ Replace withReplace with
-
+ SearchSearch
-
+ Case SensitiveCase Sensitive
-
+ Whole Words OnlyWhole Words Only
-
+ RegEx ModeRegEx Mode
-
+ Loop SearchLoop Search
-
+ Search Next FileSearch Next File
-
+ Preserve CasePreserve Case
-
+ Close SearchClose Search
-
+ Find in current documentFind in current document
-
+ Find and replace in current documentFind and replace in current document
@@ -905,122 +905,122 @@
GuiDocEditor
-
+ Opened Document: {0}Opened Document: {0}
-
+ This document has been changed outside of novelWriter while it was open. Overwrite the file on disk?This document has been changed outside of novelWriter while it was open. Overwrite the file on disk?
-
+ Could not save document.Could not save document.
-
+ Saved Document: {0}Saved Document: {0}
-
+ Spell checking requires the package PyEnchant. It does not appear to be installed.Spell checking requires the package PyEnchant. It does not appear to be installed.
-
+ Spell check completeSpell check complete
-
+ Document DetailsDocument Details
-
+ Created: {0}Created: {0}
-
+ Updated: {0}Updated: {0}
-
+ File Location: {0}File Location: {0}
-
+ Set as Document NameSet as Document Name
-
+ Follow TagFollow Tag
-
+ Create Note for TagCreate Note for Tag
-
+ CutCut
-
+ CopyCopy
-
+ PastePaste
-
+ Select AllSelect All
-
+ Select WordSelect Word
-
+ Select ParagraphSelect Paragraph
-
+ Spelling Suggestion(s)Spelling Suggestion(s)
-
+ No SuggestionsNo Suggestions
-
+ Add Word to DictionaryAdd Word to Dictionary
-
+ Please select some text before calling replace quotes.Please select some text before calling replace quotes.
-
+ Do you want to create a new project note for the tag '{0}'?Do you want to create a new project note for the tag '{0}'?
@@ -1028,22 +1028,22 @@
GuiDocMerge
-
+ Merge DocumentsMerge Documents
-
+ Documents to MergeDocuments to Merge
-
+ Drag and drop items to change the order, or uncheck to exclude.Drag and drop items to change the order, or uncheck to exclude.
-
+ Move merged items to TrashMove merged items to Trash
@@ -1051,52 +1051,52 @@
GuiDocSplit
-
+ Split DocumentSplit Document
-
+ Document HeadingsDocument Headings
-
+ Select the maximum level to split into files.Select the maximum level to split into files.
-
+ Split on Heading Level 1 (Partition)Split on Heading Level 1 (Partition)
-
+ Split up to Heading Level 2 (Chapter)Split up to Heading Level 2 (Chapter)
-
+ Split up to Heading Level 3 (Scene)Split up to Heading Level 3 (Scene)
-
+ Split up to Heading Level 4 (Section)Split up to Heading Level 4 (Section)
-
+ Split into a new folderSplit into a new folder
-
+ Create document hierarchyCreate document hierarchy
-
+ Move split document to TrashMove split document to Trash
@@ -1104,52 +1104,52 @@
GuiDocToolBar
-
+ Markdown BoldMarkdown Bold
-
+ Markdown ItalicMarkdown Italic
-
+ Markdown StrikethroughMarkdown Strikethrough
-
+ Shortcode BoldShortcode Bold
-
+ Shortcode ItalicShortcode Italic
-
+ Shortcode StrikethroughShortcode Strikethrough
-
+ Shortcode UnderlineShortcode Underline
-
+ Shortcode HighlightShortcode Highlight
-
+ Shortcode SuperscriptShortcode Superscript
-
+ Shortcode SubscriptShortcode Subscript
@@ -1157,27 +1157,27 @@
GuiDocViewFooter
-
+ Show/Hide Viewer PanelShow/Hide Viewer Panel
-
+ CommentsComments
-
+ Show CommentsShow Comments
-
+ SynopsisSynopsis
-
+ Show Synopsis CommentsShow Synopsis Comments
@@ -1185,27 +1185,27 @@
GuiDocViewHeader
-
+ OutlineOutline
-
+ Go BackwardGo Backward
-
+ Go ForwardGo Forward
-
+ ReloadReload
-
+ CloseClose
@@ -1213,27 +1213,27 @@
GuiDocViewer
-
+ An error occurred while generating the preview.An error occurred while generating the preview.
-
+ CopyCopy
-
+ Select AllSelect All
-
+ Select WordSelect Word
-
+ Select ParagraphSelect Paragraph
@@ -1241,12 +1241,12 @@
GuiDocViewerPanel
-
+ Hide Inactive TagsHide Inactive Tags
-
+ ReferencesReferences
@@ -1254,12 +1254,12 @@
GuiEditLabel
-
+ Item LabelItem Label
-
+ LabelLabel
@@ -1267,37 +1267,37 @@
GuiItemDetails
-
+ LabelLabel
-
+ StatusStatus
-
+ ClassClass
-
+ UsageUsage
-
+ CharactersCharacters
-
+ WordsWords
-
+ ParagraphsParagraphs
@@ -1305,27 +1305,27 @@
GuiLipsum
-
+ Insert Placeholder TextInsert Placeholder Text
-
+ Insert Lorem Ipsum TextInsert Lorem Ipsum Text
-
+ Number of paragraphsNumber of paragraphs
-
+ Randomise orderRandomize order
-
+ InsertInsert
@@ -1333,103 +1333,103 @@
GuiMain
-
+ novelWriter is ready ...novelWriter is ready ...
-
+ You are now running novelWriter version {0}.You are now running novelWriter version {0}.
-
+ Please check the {0}release notes{1} for further details.Please check the {0}release notes{1} for further details.
-
+ Close the current project?Close the current project?
-
-
+
+ Changes are saved automatically.Changes are saved automatically.
-
+ Backup the current project?Backup the current project?
-
+ The project is already open by another instance of novelWriter, and is therefore locked. Override lock and continue anyway?The project is already open by another instance of novelWriter, and is therefore locked. Override lock and continue anyway?
-
+ Note: If the program or the computer previously crashed, the lock can safely be overridden. However, overriding it is not recommended if the project is open in another instance of novelWriter. Doing so may corrupt the project.Note: If the program or the computer previously crashed, the lock can safely be overridden. However, overriding it is not recommended if the project is open in another instance of novelWriter. Doing so may corrupt the project.
-
+ The project was locked by the computer '{0}' ({1} {2}), last active on {3}.The project was locked by the computer '{0}' ({1} {2}), last active on {3}.
-
+ The project index is outdated or broken. Rebuilding index.The project index is outdated or broken. Rebuilding index.
-
+ Import FileImport File
-
+ Could not read file. The file must be an existing text file.Could not read file. The file must be an existing text file.
-
+ Please open a document to import the text file into.Please open a document to import the text file into.
-
+ Importing the file will overwrite the current content of the document. Do you want to proceed?Importing the file will overwrite the current content of the document. Do you want to proceed?
-
+ Indexing completed in {0} msIndexing completed in {0} ms
-
+ The project index has been successfully rebuilt.The project index has been successfully rebuilt.
-
+ Could not initialise the dialog.Could not initialize the dialog.
-
+ Do you want to exit novelWriter?Do you want to exit novelWriter?
-
+ Some changes will not be applied until novelWriter has been restarted.Some changes will not be applied until novelWriter has been restarted.
-
+ Could not find the reference for tag '{0}'. It either doesn't exist, or the index is out of date. The index can be updated from the Tools menu, or by pressing {1}.Could not find the reference for tag '{0}'. It either doesn't exist, or the index is out of date. The index can be updated from the Tools menu, or by pressing {1}.
@@ -1437,657 +1437,657 @@
GuiMainMenu
-
+ &Project&Project
-
+ Create or Open ProjectCreate or Open Project
-
+ Save ProjectSave Project
-
+ Close ProjectClose Project
-
+ Project SettingsProject Settings
-
+ Novel DetailsNovel Details
-
+ Rename ItemRename Item
-
+ Delete ItemDelete Item
-
+ Empty TrashEmpty Trash
-
+ ExitExit
-
+ &Document&Document
-
+ Open DocumentOpen Document
-
+ Save DocumentSave Document
-
+ Close DocumentClose Document
-
+ View DocumentView Document
-
+ Close Document ViewClose Document View
-
+ Show File DetailsShow File Details
-
+ Import Text from FileImport Text from File
-
+ &Edit&Edit
-
+ UndoUndo
-
+ RedoRedo
-
+ CutCut
-
+ CopyCopy
-
+ PastePaste
-
+ Select AllSelect All
-
+ Select ParagraphSelect Paragraph
-
+ &View&View
-
+ Go to Project TreeGo to Project Tree
-
+ Go to Document EditorGo to Document Editor
-
+ Go to OutlineGo to Outline
-
+ Navigate BackwardNavigate Backward
-
+ Navigate ForwardNavigate Forward
-
+ Focus ModeFocus Mode
-
+ Full Screen ModeFull Screen Mode
-
+ &Insert&Insert
-
+ DashesDashes
-
+ Short DashShort Dash
-
+ Long DashLong Dash
-
+ Horizontal BarHorizontal Bar
-
+ Figure DashFigure Dash
-
+ Quote MarksQuote Marks
-
+ Left Single QuoteLeft Single Quote
-
+ Right Single QuoteRight Single Quote
-
+ Left Double QuoteLeft Double Quote
-
+ Right Double QuoteRight Double Quote
-
+ Alternative ApostropheAlternative Apostrophe
-
+ General PunctuationGeneral Punctuation
-
+ EllipsisEllipsis
-
+ PrimePrime
-
+ Double PrimeDouble Prime
-
+ White SpacesWhite Spaces
-
+ Non-Breaking SpaceNon-Breaking Space
-
+ Thin SpaceThin Space
-
+ Thin Non-Breaking SpaceThin Non-Breaking Space
-
+ Other SymbolsOther Symbols
-
+ List BulletList Bullet
-
+ Hyphen BulletHyphen Bullet
-
+ Flower MarkFlower Mark
-
+ Per MillePer Mille
-
+ Degree SymbolDegree Symbol
-
+ Minus SignMinus Sign
-
+ Times SignTimes Sign
-
+ Division SignDivision Sign
-
+ Tags and ReferencesTags and References
-
+ Special CommentsSpecial Comments
-
+ Synopsis CommentSynopsis Comment
-
+ Short Description CommentShort Description Comment
-
+ Page Break and SpacePage Break and Space
-
+ Page BreakPage Break
-
+ Vertical Space (Single)Vertical Space (Single)
-
+ Vertical Space (Multi)Vertical Space (Multi)
-
+ Placeholder TextPlaceholder Text
-
+ &Format&Format
-
+ BoldBold
-
+ ItalicItalic
-
+ StrikethroughStrikethrough
-
+ Wrap Double QuotesWrap Double Quotes
-
+ Wrap Single QuotesWrap Single Quotes
-
+ More Formats ...More Formats ...
-
+ Bold (Shortcode)Bold (Shortcode)
-
+ Italics (Shortcode)Italics (Shortcode)
-
+ Strikethrough (Shortcode)Strikethrough (Shortcode)
-
+ UnderlineUnderline
-
+ HighlightHighlight
-
+ SuperscriptSuperscript
-
+ SubscriptSubscript
-
+ Heading 1 (Partition)Heading 1 (Partition)
-
+ Heading 2 (Chapter)Heading 2 (Chapter)
-
+ Heading 3 (Scene)Heading 3 (Scene)
-
+ Heading 4 (Section)Heading 4 (Section)
-
+ Novel TitleNovel Title
-
+ Unnumbered ChapterUnnumbered Chapter
-
- Hard Scene
- Hard Scene
+
+ Alternative Scene
+ Alternative Scene
-
+ Align LeftAlign Left
-
+ Align CentreAlign Center
-
+ Align RightAlign Right
-
+ Indent LeftIndent Left
-
+ Indent RightIndent Right
-
+ Toggle CommentToggle Comment
-
+ Toggle Ignore TextToggle Ignore Text
-
+ Remove Block FormatRemove Block Format
-
+ Replace Straight Single QuotesReplace Straight Single Quotes
-
+ Replace Straight Double QuotesReplace Straight Double Quotes
-
+ Remove In-Paragraph BreaksRemove In-Paragraph Breaks
-
+ &Search&Search
-
+ FindFind
-
+ ReplaceReplace
-
+ Find NextFind Next
-
+ Find PreviousFind Previous
-
+ Replace NextReplace Next
-
+ Find in ProjectFind in Project
-
+ &Tools&Tools
-
+ Check SpellingCheck Spelling
-
+ Spell Check LanguageSpell Check Language
-
+ DefaultDefault
-
+ Re-Run Spell CheckRe-Run Spell Check
-
+ Project Word ListProject Word List
-
+ Add DictionariesAdd Dictionaries
-
+ Rebuild IndexRebuild Index
-
+ Backup ProjectBackup Project
-
+ Build ManuscriptBuild Manuscript
-
+ Writing StatisticsWriting Statistics
-
+ PreferencesPreferences
-
+ &Help&Help
-
+ About novelWriterAbout novelWriter
-
+ About Qt5About Qt5
-
+ User Manual (Online)User Manual (Online)
-
+ User Manual (PDF)User Manual (PDF)
-
+ Report an Issue (GitHub)Report an Issue (GitHub)
-
+ Ask a Question (GitHub)Ask a Question (GitHub)
-
+ The novelWriter WebsiteThe novelWriter Website
@@ -2095,38 +2095,38 @@
GuiMainStatus
-
-
+
+ NoneNone
-
+ EditorEditor
-
+ ProjectProject
-
+ Session TimeSession Time
-
+ Words: {0} ({1})Words: {0} ({1})
-
+ Project word count (session change)Project word count (session change)
-
+ Novel word count (session change)Novel word count (session change)
@@ -2134,63 +2134,63 @@
GuiManuscript
-
+ Build ManuscriptBuild Manuscript
-
+ Add New BuildAdd New Build
-
+ Delete Selected BuildDelete Selected Build
-
+ Edit Selected BuildEdit Selected Build
-
+ BuildsBuilds
-
+ DetailsDetails
-
+ OutlineOutline
-
+ PreviewPreview
-
+ PrintPrint
-
+ BuildBuild
-
+ CloseClose
-
-
+
+ My ManuscriptMy Manuscript
@@ -2198,57 +2198,57 @@
GuiManuscriptBuild
-
+ Build ManuscriptBuild Manuscript
-
+ Output FormatOutput Format
-
+ Table of ContentsTable of Contents
-
+ PathPath
-
+ File NameFile Name
-
+ Reset file name to defaultReset file name to default
-
+ Open FolderOpen Folder
-
+ &Build&Build
-
+ Select FolderSelect Folder
-
+ Output folder does not exist.Output folder does not exist.
-
+ The file already exists. Do you want to overwrite it?The file already exists. Do you want to overwrite it?
@@ -2256,18 +2256,18 @@
GuiNovelDetails
-
-
+
+ Novel DetailsNovel Details
-
+ OverviewOverview
-
+ ContentsContents
@@ -2275,58 +2275,58 @@
GuiNovelToolBar
-
+ Outline of {0}Outline of {0}
-
+ Novel RootNovel Root
-
+ RefreshRefresh
-
+ Last ColumnLast Column
-
+ HiddenHidden
-
+ Point of View CharacterPoint of View Character
-
+ Focus CharacterFocus Character
-
+ Novel PlotNovel Plot
-
-
+
+ Column SizeColumn Size
-
+ More OptionsMore Options
-
+ Maximum column size in %Maximum column size in %
@@ -2334,7 +2334,7 @@
GuiNovelTree
-
+ No meta dataNo meta data
@@ -2342,64 +2342,64 @@
GuiOutlineDetails
-
-
-
+
+
+ TitleTitle
-
+ ChapterChapter
-
+ SceneScene
-
+ SectionSection
-
+ DocumentDocument
-
+ StatusStatus
-
+ CharactersCharacters
-
+ WordsWords
-
+ ParagraphsParagraphs
-
+ SynopsisSynopsis
-
+ Title DetailsTitle Details
-
+ Reference TagsReference Tags
@@ -2407,7 +2407,7 @@
GuiOutlineHeaderMenu
-
+ Select ColumnsSelect Columns
@@ -2415,17 +2415,17 @@
GuiOutlineToolBar
-
+ Outline ofOutline of
-
+ RefreshRefresh
-
+ Export CSVExport CSV
@@ -2433,7 +2433,7 @@
GuiOutlineTree
-
+ Save Outline AsSave Outline As
@@ -2441,553 +2441,553 @@
GuiPreferences
-
-
+
+ PreferencesPreferences
-
+ SearchSearch
-
+ GeneralGeneral
-
+ AppearanceAppearance
-
+ Display languageDisplay language
-
-
-
+
+
+ Requires restart to take effect.Requires restart to take effect.
-
+ Colour themeColor theme
-
+ General colour theme and icons.General color theme and icons.
-
+ Application font familyApplication font family
-
+ Application font sizeApplication font size
-
-
+
+ ptpt
-
+ Hide vertical scroll bars in main windowsHide vertical scroll bars in main windows
-
-
+
+ Scrolling available with mouse wheel and keys only.Scrolling available with mouse wheel and keys only.
-
+ Hide horizontal scroll bars in main windowsHide horizontal scroll bars in main windows
-
+ Document StyleDocument Style
-
+ Document colour themeDocument color theme
-
+ Colour theme for the editor and viewer.Color theme for the editor and viewer.
-
+ Document font familyDocument font family
-
-
-
-
+
+
+
+ Applies to both document editor and viewer.Applies to both document editor and viewer.
-
+ Document font sizeDocument font size
-
+ Emphasise partition and chapter labelsEmphasize partition and chapter labels
-
+ Makes them stand out in the project tree.Makes them stand out in the project tree.
-
+ Show full path in document headerShow full path in document header
-
+ Add the parent folder names to the header.Add the parent folder names to the header.
-
+ Include project notes in status bar word countInclude project notes in status bar word count
-
+ Auto SaveAuto Save
-
+ Save document intervalSave document interval
-
+ How often the document is automatically saved.How often the document is automatically saved.
-
-
+
+ secondsseconds
-
+ Save project intervalSave project interval
-
+ How often the project is automatically saved.How often the project is automatically saved.
-
+ Project BackupProject Backup
-
+ BrowseBrowse
-
+ Backup storage locationBackup storage location
-
-
+
+ Path: {0}Path: {0}
-
+ Run backup when the project is closedRun backup when the project is closed
-
+ Can be overridden for individual projects in Project Settings.Can be overridden for individual projects in Project Settings.
-
+ Ask before running backupAsk before running backup
-
+ If off, backups will run in the background.If off, backups will run in the background.
-
+ Session TimerSession Timer
-
+ Pause the session timer when not writingPause the session timer when not writing
-
+ Also pauses when the application window does not have focus.Also pauses when the application window does not have focus.
-
+ Editor inactive time before pausing timerEditor inactive time before pausing timer
-
+ User activity includes typing and changing the content.User activity includes typing and changing the content.
-
+ minutesminutes
-
+ WritingWriting
-
+ Text FlowText Flow
-
+ Maximum text width in "Normal Mode"Maximum text width in "Normal Mode"
-
+ Set to 0 to disable this feature.Set to 0 to disable this feature.
-
-
-
-
+
+
+
+ pxpx
-
+ Maximum text width in "Focus Mode"Maximum text width in "Focus Mode"
-
+ The maximum width cannot be disabled.The maximum width cannot be disabled.
-
+ Hide document footer in "Focus Mode"Hide document footer in "Focus Mode"
-
+ Hide the information bar in the document editor.Hide the information bar in the document editor.
-
+ Justify the text marginsJustify the text margins
-
+ Minimum text marginMinimum text margin
-
+ Tab widthTab width
-
+ The width of a tab key press in the editor and viewer.The width of a tab key press in the editor and viewer.
-
+ Text EditingText Editing
-
+ Spell check languageSpell check language
-
+ Available languages are determined by your system.Available languages are determined by your system.
-
+ Auto-select word under cursorAuto-select word under cursor
-
+ Apply formatting to word under cursor if no selection is made.Apply formatting to word under cursor if no selection is made.
-
+ Show tabs and spacesShow tabs and spaces
-
+ Show line endingsShow line endings
-
+ Editor ScrollingEditor Scrolling
-
+ Scroll past end of the documentScroll past end of the document
-
+ Also centres the cursor when scrolling.Also centers the cursor when scrolling.
-
+ Typewriter style scrolling when you typeTypewriter style scrolling when you type
-
+ Keeps the cursor at a fixed vertical position.Keeps the cursor at a fixed vertical position.
-
+ Minimum position for Typewriter scrollingMinimum position for Typewriter scrolling
-
+ Percentage of the editor height from the top.Percentage of the editor height from the top.
-
+ Text HighlightingText Highlighting
-
+ Highlight text wrapped in quotesHighlight text wrapped in quotes
-
-
-
+
+
+ Applies to the document editor only.Applies to the document editor only.
-
+ Allow open-ended single quotesAllow open-ended single quotes
-
+ Highlight single-quoted line with no closing quote.Highlight single-quoted line with no closing quote.
-
+ Allow open-ended double quotesAllow open-ended double quotes
-
+ Highlight double-quoted line with no closing quote.Highlight double-quoted line with no closing quote.
-
+ Add highlight colour to emphasised textAdd highlight color to emphasised text
-
+ Highlight multiple or trailing spacesHighlight multiple or trailing spaces
-
+ Text AutomationText Automation
-
+ Auto-replace text as you typeAuto-replace text as you type
-
+ Allow the editor to replace symbols as you type.Allow the editor to replace symbols as you type.
-
+ Auto-replace single quotesAuto-replace single quotes
-
-
+
+ Try to guess which is an opening or a closing quote.Try to guess which is an opening or a closing quote.
-
+ Auto-replace double quotesAuto-replace double quotes
-
+ Auto-replace dashesAuto-replace dashes
-
+ Double and triple hyphens become short and long dashes.Double and triple hyphens become short and long dashes.
-
+ Auto-replace dotsAuto-replace dots
-
+ Three consecutive dots become ellipsis.Three consecutive dots become ellipsis.
-
+ Insert non-breaking space beforeInsert non-breaking space before
-
+ Automatically add space before any of these symbols.Automatically add space before any of these symbols.
-
+ Insert non-breaking space afterInsert non-breaking space after
-
+ Automatically add space after any of these symbols.Automatically add space after any of these symbols.
-
+ Use thin space insteadUse thin space instead
-
+ Inserts a thin space instead of a regular space.Inserts a thin space instead of a regular space.
-
+ Quotation StyleQuotation Style
-
+ Single quote open styleSingle quote open style
-
+ The symbol to use for a leading single quote.The symbol to use for a leading single quote.
-
+ Single quote close styleSingle quote close style
-
+ The symbol to use for a trailing single quote.The symbol to use for a trailing single quote.
-
+ Double quote open styleDouble quote open style
-
+ The symbol to use for a leading double quote.The symbol to use for a leading double quote.
-
+ Double quote close styleDouble quote close style
-
+ The symbol to use for a trailing double quote.The symbol to use for a trailing double quote.
-
+ Backup DirectoryBackup Directory
@@ -2995,27 +2995,27 @@
GuiProjectSearch
-
+ Project SearchProject Search
-
+ Case SensitiveCase Sensitive
-
+ Whole Words OnlyWhole Words Only
-
+ RegEx ModeRegEx Mode
-
+ Search forSearch for
@@ -3023,28 +3023,28 @@
GuiProjectSettings
-
-
+
+ Project SettingsProject Settings
-
+ SettingsSettings
-
+ StatusStatus
-
+ ImportanceImportance
-
+ Auto-ReplaceAuto-Replace
@@ -3052,47 +3052,47 @@
GuiProjectToolBar
-
+ Project ContentProject Content
-
+ Quick LinksQuick Links
-
+ Move UpMove Up
-
+ Move DownMove Down
-
+ Add ItemAdd Item
-
+ Expand AllExpand All
-
+ Collapse AllCollapse All
-
+ Empty TrashEmpty Trash
-
+ More OptionsMore Options
@@ -3100,118 +3100,118 @@
GuiProjectTree
-
+ ActiveActive
-
+ InactiveInactive
-
+ Permanently delete {0} file(s) from Trash?Permanently delete {0} file(s) from Trash?
-
+ Did not find anywhere to add the file or folder!Did not find anywhere to add the file or folder!
-
+ Cannot add new files or folders to the Trash folder.Cannot add new files or folders to the Trash folder.
-
+ New NoteNew Note
-
+ New ChapterNew Chapter
-
+ New SceneNew Scene
-
+ New DocumentNew Document
-
+ New FolderNew Folder
-
+ There is currently no Trash folder in this project.There is currently no Trash folder in this project.
-
+ The Trash folder is already empty.The Trash folder is already empty.
-
+ Move '{0}' to Trash?Move '{0}' to Trash?
-
+ Root folders can only be deleted when they are empty.Root folders can only be deleted when they are empty.
-
+ Permanently delete '{0}'?Permanently delete '{0}'?
-
+ Drag and drop is only allowed for single items, non-root items, or multiple items with the same parent.Drag and drop is only allowed for single items, non-root items, or multiple items with the same parent.
-
+ No documents selected for merging.No documents selected for merging.
-
+ MergedMerged
-
-
+
+ Could not write document content.Could not write document content.
-
+ Do you want to duplicate this document?Do you want to duplicate this document?
-
+ Do you want to duplicate this item and all child items?Do you want to duplicate this item and all child items?
-
+ Could not duplicate all items.Could not duplicate all items.
-
+ There is nowhere to add item with name '{0}'.There is nowhere to add item with name '{0}'.
@@ -3219,42 +3219,42 @@
GuiSideBar
-
+ Project Tree ViewProject Tree View
-
+ Novel Tree ViewNovel Tree View
-
+ Project SearchProject Search
-
+ Novel Outline ViewNovel Outline View
-
+ Build ManuscriptBuild Manuscript
-
+ Novel DetailsNovel Details
-
+ Writing StatisticsWriting Statistics
-
+ SettingsSettings
@@ -3262,37 +3262,37 @@
GuiWelcome
-
+ WelcomeWelcome
-
+ ListList
-
+ NewNew
-
+ BrowseBrowse
-
+ CancelCancel
-
+ CreateCreate
-
+ OpenOpen
@@ -3300,33 +3300,33 @@
GuiWordList
-
-
+
+ Project Word ListProject Word List
-
+ Import words from text fileImport words from text file
-
+ Export words to text fileExport words to text file
-
+ Note: The import file must be a plain text file with UTF-8 or ASCII encoding.Note: The import file must be a plain text file with UTF-8 or ASCII encoding.
-
+ Import FileImport File
-
+ Export FileExport File
@@ -3334,147 +3334,147 @@
GuiWritingStats
-
+ Writing StatisticsWriting Statistics
-
+ Session StartSession Start
-
+ LengthLength
-
+ IdleIdle
-
+ WordsWords
-
+ HistogramHistogram
-
+ Sum TotalsSum Totals
-
+ Total Time:Total Time:
-
+ Idle Time:Idle Time:
-
+ Filtered Time:Filtered Time:
-
+ Novel Word Count:Novel Word Count:
-
+ Notes Word Count:Notes Word Count:
-
+ Total Word Count:Total Word Count:
-
+ FiltersFilters
-
+ Count novel filesCount novel files
-
+ Count note filesCount note files
-
+ Hide zero word countHide zero word count
-
+ Hide negative word countHide negative word count
-
+ Group entries by dayGroup entries by day
-
+ Show idle timeShow idle time
-
+ Word count cap for the histogramWord count cap for the histogram
-
+ Save AsSave As
-
+ JSON Data File (.json)JSON Data File (.json)
-
+ CSV Data File (.csv)CSV Data File (.csv)
-
+ JSON Data FileJSON Data File
-
+ CSV Data FileCSV Data File
-
+ Save Data AsSave Data As
-
+ {0} file successfully written to:{0} file successfully written to:
-
+ Failed to write {0} file.Failed to write {0} file.
@@ -3482,153 +3482,153 @@
NWProject
-
+ Could not delete document file.Could not delete document file.
-
+ Not a known project file format.Not a known project file format.
-
+ Project file not found.Project file not found.
-
+ Failed to open project.Failed to open project.
-
+ UnknownUnknown
-
+ Project file does not appear to be a novelWriterXML file.Project file does not appear to be a novelWriterXML file.
-
+ 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}.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}.
-
+ Failed to parse project xml.Failed to parse project xml.
-
+ The file format of your project is about to be updated. If you proceed, older versions of novelWriter will no longer be able to open this project. Continue?The file format of your project is about to be updated. If you proceed, older versions of novelWriter will no longer be able to open this project. Continue?
-
+ This project was saved by a newer version of novelWriter, version {0}. This is version {1}. If you continue to open the project, some attributes and settings may not be preserved, but the overall project should be fine. Continue opening the project?This project was saved by a newer version of novelWriter, version {0}. This is version {1}. If you continue to open the project, some attributes and settings may not be preserved, but the overall project should be fine. Continue opening the project?
-
+ RecoveredRecovered
-
+ Found {0} orphaned file(s) in the project. {1} file(s) were recovered.Found {0} orphaned file(s) in the project. {1} file(s) were recovered.
-
+ Opened Project: {0}Opened Project: {0}
-
+ There is no project open.There is no project open.
-
+ Failed to save project.Failed to save project.
-
+ Saved Project: {0}Saved Project: {0}
-
+ Backing up project ...Backing up project ...
-
+ Cannot backup project because no project name is set. Please set a Project Name in Project Settings.Cannot backup project because no project name is set. Please set a Project Name in Project Settings.
-
+ Could not create backup folder.Could not create backup folder.
-
+ Created a backup of your project of size {0}B.Created a backup of your project of size {0}B.
-
+ Path: {0}Path: {0}
-
+ Could not write backup archive.Could not write backup archive.
-
+ Project backed up to '{0}'Project backed up to '{0}'
-
-
+
+ NewNew
-
+ NoteNote
-
+ DraftDraft
-
+ FinishedFinished
-
+ MinorMinor
-
+ MajorMajor
-
+ MainMain
@@ -3636,7 +3636,7 @@
NovelSelector
-
+ All Novel FoldersAll Novel Folders
@@ -3644,89 +3644,89 @@
ProjectBuilder
-
+ The target folder is not empty. Please choose another folder.The target folder is not empty. Please choose another folder.
-
+ An error occurred while trying to create the project.An error occurred while trying to create the project.
-
+ New ProjectNew Project
-
+ Title PageTitle Page
-
+ ByBy
-
+ Summary of the chapter.Summary of the chapter.
-
+ Summary of the scene.Summary of the scene.
-
+ A short description.A short description.
-
+ Chapter {0}Chapter {0}
-
-
+
+ Scene {0}Scene {0}
-
+ Main PlotMain Plot
-
+ ProtagonistProtagonist
-
+ Main LocationMain Location
-
-
+
+ The target folder already exists. Please choose another folder.The target folder already exists. Please choose another folder.
-
+ Could not copy project files.Could not copy project files.
-
+ Failed to create a new example project.Failed to create a new example project.
-
+ Failed to create a new example project. Could not find the necessary files. They seem to be missing from this installation.Failed to create a new example project. Could not find the necessary files. They seem to be missing from this installation.
@@ -3734,7 +3734,7 @@
QDialogButtonBox
-
+ OKOK
@@ -3742,27 +3742,27 @@
QGnomeTheme
-
+ &OK&OK
-
+ &Save&Save
-
+ &Cancel&Cancel
-
+ &Close&Close
-
+ Close without SavingClose without Saving
@@ -3770,92 +3770,92 @@
QPlatformTheme
-
+ OKOK
-
+ SaveSave
-
+ Save AllSave All
-
+ OpenOpen
-
+ &Yes&Yes
-
+ Yes to &AllYes to &All
-
+ &No&No
-
+ N&o to AllN&o to All
-
+ AbortAbort
-
+ RetryRetry
-
+ IgnoreIgnore
-
+ CloseClose
-
+ CancelCancel
-
+ DiscardDiscard
-
+ HelpHelp
-
+ ApplyApply
-
+ ResetReset
-
+ Restore DefaultsRestore Defaults
@@ -3863,58 +3863,58 @@
QWizard
-
+ Go BackGo Back
-
+ < &Back< &Back
-
+ ContinueContinue
-
+ &Next&Next
-
+ &Next >&Next >
-
+ CommitCommit
-
+ DoneDone
-
+ &Finish&Finish
-
-
+
+ CancelCancel
-
+ HelpHelp
-
+ &Help&Help
@@ -3922,17 +3922,17 @@
SharedData
-
+ novelWriter Project File or Zip FilenovelWriter Project File or Zip File
-
+ novelWriter Project FilenovelWriter Project File
-
+ Open ProjectOpen Project
@@ -3940,42 +3940,42 @@
VersionInfoWidget
-
+ Latest Version: {0}Latest Version: {0}
-
+ Checking ...Checking ...
-
+ Download from {0}Download from {0}
-
+ VersionVersion
-
+ Released onReleased on
-
+ Release NotesRelease Notes
-
+ Check NowCheck Now
-
+ FailedFailed
@@ -3983,57 +3983,57 @@
_ContentsPage
-
+ Table of ContentsTable of Contents
-
+ TitleTitle
-
+ WordsWords
-
+ PagesPages
-
+ PagePage
-
+ ProgressProgress
-
+ Words per pageWords per page
-
+ First page offsetFirst page offset
-
+ Chapters on odd pagesChapters on odd pages
-
+ UntitledUntitled
-
+ ENDEND
@@ -4041,32 +4041,32 @@
_DetailsWidget
-
+ SettingSetting
-
+ ValueValue
-
+ NameName
-
+ SelectionSelection
-
+ TitleTitle
-
+ HiddenHidden
@@ -4074,37 +4074,37 @@
_FilterTab
-
+ Included in manuscriptIncluded in manuscript
-
+ Excluded from manuscriptExcluded from manuscript
-
+ Always includedAlways included
-
+ Always excludedAlways excluded
-
+ Reset to defaultReset to default
-
+ Mark selection asMark selection as
-
+ Select Root FoldersSelect Root Folders
@@ -4112,22 +4112,22 @@
_GuiAlert
-
+ InformationInformation
-
+ WarningWarning
-
+ ErrorError
-
+ QuestionQuestion
@@ -4135,93 +4135,93 @@
_HeadingsTab
-
+ HideHide
-
-
+
+ Editing: {0}Editing: {0}
-
-
+
+ NoneNone
-
+ TitleTitle
-
+ Chapter NumberChapter Number
-
+ Chapter Number (Word)Chapter Number (Word)
-
+ Chapter Number (Upper Case Roman)Chapter Number (Upper Case Roman)
-
+ Chapter Number (Lower Case Roman)Chapter Number (Lower Case Roman)
-
+ Scene Number (In Chapter)Scene Number (In Chapter)
-
+ Scene Number (Absolute)Scene Number (Absolute)
-
+ Point of View CharacterPoint of View Character
-
+ Focus CharacterFocus Character
-
+ InsertInsert
-
+ ApplyApply
-
+ Additional StylingAdditional Styling
-
-
-
+
+
+ CentreCenter
-
-
-
+
+
+ Page BreakPage Break
@@ -4229,117 +4229,117 @@
_NewProjectForm
-
+ RequiredRequired
-
+ OptionalOptional
-
+ Create a fresh projectCreate a fresh project
-
+ Create an example projectCreate an example project
-
+ Copy an existing projectCopy an existing project
-
+ Project NameProject Name
-
+ AuthorAuthor
-
+ Project PathProject Path
-
+ Prefill ProjectPrefill Project
-
+ Set to 0 to only add scenesSet to 0 to only add scenes
-
+ Add {0} chapter documentsAdd {0} chapter documents
-
+ Add {0} scene documents (to each chapter)Add {0} scene documents (to each chapter)
-
+ Add a folder for plot notesAdd a folder for plot notes
-
+ Add a folder for character notesAdd a folder for character notes
-
+ Add a folder for location notesAdd a folder for location notes
-
+ Add example notes to the aboveAdd example notes to the above
-
+ Chapters and ScenesChapters and Scenes
-
+ Project NotesProject Notes
-
+ Create New ProjectCreate New Project
-
+ Select Project FolderSelect Project Folder
-
+ Fresh ProjectFresh Project
-
+ Example ProjectExample Project
-
+ Template: {0}Template: {0}
@@ -4347,7 +4347,7 @@
_NewProjectPage
-
+ A project name is required.A project name is required.
@@ -4355,27 +4355,27 @@
_OpenProjectPage
-
+ The project path is not reachable.The project path is not reachable.
-
+ PathPath
-
+ Remove '{0}' from the recent projects list? The project files will not be deleted.Remove '{0}' from the recent projects list? The project files will not be deleted.
-
+ Open ProjectOpen Project
-
+ Remove ProjectRemove Project
@@ -4383,54 +4383,54 @@
_OverviewPage
-
+ ProjectProject
-
-
+
+ NameName
-
+ RevisionsRevisions
-
+ Editing TimeEditing Time
-
-
+
+ Word CountWord Count
-
+ In NovelsIn Novels
-
+ In NotesIn Notes
-
+ Selected NovelSelected Novel
-
+ ChaptersChapters
-
+ ScenesScenes
@@ -4438,27 +4438,27 @@
_PreviewWidget
-
+ Press the "Preview" button to generate ...Press the "Preview" button to generate ...
-
+ Processing ...Processing ...
-
+ DoneDone
-
+ UnknownUnknown
-
+ BuiltBuilt
@@ -4466,12 +4466,12 @@
_ProjectListModel
-
+ Word CountWord Count
-
+ Last OpenedLast Opened
@@ -4479,27 +4479,27 @@
_ReplacePage
-
+ Text Auto-Replace for Preview and BuildText Auto-Replace for Preview and Build
-
+ KeywordKeyword
-
+ Replace WithReplace With
-
+ Select item to editSelect item to edit
-
+ SaveSave
@@ -4507,49 +4507,49 @@
_SettingsPage
-
+ Project nameProject name
-
+ Changing this will affect the backup path.Changing this will affect the backup path.
-
+ Author(s)Author(s)
-
-
+
+ Only used when building the manuscript.Only used when building the manuscript.
-
+ Project languageProject language
-
+ DefaultDefault
-
+ Spell check languageSpell check language
-
-
+
+ Overrides main preferences.Overrides main preferences.
-
+ Disable backup on closeDisable backup on close
@@ -4557,59 +4557,59 @@
_StatsWidget
-
-
+
+ WordsWords
-
-
+
+ CharactersCharacters
-
+ Words in HeadingsWords in Headings
-
+ Words in TextWords in Text
-
+ HeadingsHeadings
-
+ ParagraphsParagraphs
-
+ Characters in HeadingsCharacters in Headings
-
+ Characters in TextCharacters in Text
-
+ Characters, No SpacesCharacters, No Spaces
-
+ Characters in Headings, No SpacesCharacters in Headings, No Spaces
-
+ Characters in Text, No SpacesCharacters in Text, No Spaces
@@ -4617,67 +4617,67 @@
_StatusPage
-
+ Novel Document Status LevelsNovel Document Status Levels
-
+ Project Note Importance LevelsProject Note Importance Levels
-
+ LabelLabel
-
+ UsageUsage
-
+ Select item to editSelect item to edit
-
+ ColourColor
-
+ SaveSave
-
+ Select ColourSelect Color
-
+ New ItemNew Item
-
+ Cannot delete a status item that is in use.Cannot delete a status item that is in use.
-
+ Not in useNot in use
-
+ Used onceUsed once
-
+ Used by {0} itemsUsed by {0} items
@@ -4685,128 +4685,128 @@
_TreeContextMenu
-
+ Empty TrashEmpty Trash
-
+ RenameRename
-
+ Open DocumentOpen Document
-
+ View DocumentView Document
-
+ Create New ...Create New ...
-
+ Rename to HeadingRename to Heading
-
+ Set Active to ...Set Active to ...
-
+ Toggle ActiveToggle Active
-
+ Set Status to ...Set Status to ...
-
-
+
+ Manage Labels ...Manage Labels ...
-
+ Set Importance to ...Set Importance to ...
-
+ Transform ...Transform ...
-
-
-
-
+
+
+
+ Convert to {0}Convert to {0}
-
+ Merge Child Items into SelfMerge Child Items into Self
-
+ Merge Child Items into NewMerge Child Items into New
-
+ Merge Documents in FolderMerge Documents in Folder
-
+ Split Document by HeadingsSplit Document by Headings
-
+ Expand AllExpand All
-
+ Collapse AllCollapse All
-
+ DuplicateDuplicate
-
-
+
+ Delete PermanentlyDelete Permanently
-
-
+
+ Move to TrashMove to Trash
-
+ Move {0} items to Trash?Move {0} items to Trash?
-
+ Do you want to convert the folder to a {0}? This action cannot be reversed.Do you want to convert the folder to a {0}? This action cannot be reversed.
@@ -4814,7 +4814,7 @@
_UpdatableMenu
-
+ From TemplateFrom Template
@@ -4822,12 +4822,12 @@
_ViewPanelBackRefs
-
+ DocumentDocument
-
+ First HeadingFirst Heading
@@ -4835,27 +4835,27 @@
_ViewPanelKeyWords
-
+ TagTag
-
+ ImportanceImportance
-
+ DocumentDocument
-
+ HeadingHeading
-
+ Short DescriptionShort Description
diff --git a/i18n/nw_es_419.ts b/i18n/nw_es_419.ts
index ba4c855b..58299e4e 100644
--- a/i18n/nw_es_419.ts
+++ b/i18n/nw_es_419.ts
@@ -31,32 +31,32 @@
Partition Format
-
+ Formato para ParticionesChapter Format
-
+ Formato para CapítulosUnnumbered Format
-
+ Formato Sin NumeraciónScene Format
-
+ Formato para Escenas
- Hard Scene Format
-
+ Alt. Scene Format
+ Formato Alternativo para EscenasSection Format
-
+ Formato para Secciones
@@ -86,7 +86,7 @@
Ignore These Keywords
-
+ Ignorar estas palabras clave
@@ -206,17 +206,17 @@
First Line Indent
-
+ Sangría en Línea InicialMarkdown (.md)
-
+ Markdown (.md)Preserve Hard Line Breaks
-
+ Conservar Saltos de Línea Forzados
@@ -231,7 +231,7 @@
Preserve Tab Characters
-
+ Preservar Caracteres de Tabulación
@@ -791,22 +791,22 @@
GuiDocEditFooter
-
+ Line: {0} ({1})Línea: {0} ({1})
-
+ Words: {0} ({1})Palabras: {0} ({1})
-
+ Words: {0} selectedPalabras: {0} seleccionadas
-
+ StatusEstado
@@ -814,27 +814,27 @@
GuiDocEditHeader
-
+ Toggle Tool BarAlternar Barra de Herramientas
-
+ Outline
-
+ Estructura
-
+ SearchBuscar
-
+ Toggle Focus ModeAlternar el Modo Enfocado
-
+ CloseCerrar
@@ -842,62 +842,62 @@
GuiDocEditSearch
-
+ Search for
-
+ Buscar
-
+ Replace with
-
+ Reemplazar con
-
+ SearchBuscar
-
+ Case SensitiveSensibilidad a Mayúsculas y Minúsculas
-
+ Whole Words OnlySólo Palabras Enteras
-
+ RegEx ModeModo ExReg
-
+ Loop SearchReiniciar la Búsqueda
-
+ Search Next FileBuscar en el Siguiente Archivo
-
+ Preserve CaseConservar Mayúsculas y Minúsculas
-
+ Close SearchCerrar la Búsqueda
-
+ Find in current documentBuscar en el documento actual
-
+ Find and replace in current documentBuscar y reemplazar en el documento actual
@@ -920,107 +920,107 @@
No se puedo guardar el documento.
-
+ Saved Document: {0}Documento Guardado: {0}
-
+ Spell checking requires the package PyEnchant. It does not appear to be installed.Para la corrección ortográfica se requiere del paquete PyEnchant. Parece que no se encuentra instalado.
-
+ Spell check completeSe completó la comprobación ortográfica
-
+ Document DetailsDetalles del Documento
-
+ Created: {0}Creación: {0}
-
+ Updated: {0}Actualizado en: {0}
-
+ File Location: {0}Ubicación del Archivo: {0}
-
+ Set as Document NameElegir como Nombre del Documento
-
+ Follow TagContinuar a Etiqueta
-
+ Create Note for TagCrear Nota para la Etiqueta
-
+ CutCortar
-
+ CopyCopiar
-
+ PastePegar
-
+ Select AllSeleccionar Todo
-
+ Select WordSeleccionar Palabra
-
+ Select ParagraphSeleccionar Párrafo
-
+ Spelling Suggestion(s)Sugerencia(s) de Ortografía
-
+ No SuggestionsNo Hay Sugerencias
-
+ Add Word to DictionaryAñadir Palabra al Diccionario
-
+ Please select some text before calling replace quotes.Por favor seleccione algo del texto antes de intentar reemplazar las comillas.
-
+ Do you want to create a new project note for the tag '{0}'?¿Desea crear una nueva nota del proyecto para la etiqueta '{0}'?
@@ -1058,7 +1058,7 @@
Document Headings
-
+ Títulación de Documentos
@@ -1068,22 +1068,22 @@
Split on Heading Level 1 (Partition)
-
+ Separar por el Nivel de Título 1 (Partición)Split up to Heading Level 2 (Chapter)
-
+ Separar hasta el Nivel de Título 2 (Capítulo)Split up to Heading Level 3 (Scene)
-
+ Separar hasta el Nivel de Título 3 (Escena)Split up to Heading Level 4 (Section)
-
+ Separar hasta el Nivel de Título 4 (Sección)
@@ -1104,52 +1104,52 @@
GuiDocToolBar
-
+ Markdown BoldNegrita (de Markdown)
-
+ Markdown ItalicCursiva (de Markdown)
-
+ Markdown StrikethroughTachado (de Markdown)
-
+ Shortcode BoldNegrita (en código)
-
+ Shortcode ItalicCursiva (en código)
-
+ Shortcode StrikethroughTachado (en código)
-
+ Shortcode UnderlineSubrayado (en código)
-
+ Shortcode Highlight
-
+ Resaltado (en código)
-
+ Shortcode SuperscriptSuperíndice (en código)
-
+ Shortcode SubscriptSubíndice (en código)
@@ -1187,7 +1187,7 @@
Outline
-
+ Estructura
@@ -1854,7 +1854,7 @@
Highlight
-
+ Resaltar
@@ -1869,22 +1869,22 @@
Heading 1 (Partition)
-
+ Título 1 (Partición)Heading 2 (Chapter)
-
+ Título 2 (Capítulo)Heading 3 (Scene)
-
+ Título 3 (Escena)Heading 4 (Section)
-
+ Título 4 (Sección)
@@ -1898,8 +1898,8 @@
- Hard Scene
-
+ Alternative Scene
+ Escena Alternativa
@@ -1944,12 +1944,12 @@
Replace Straight Single Quotes
-
+ Reemplazar ApóstrofosReplace Straight Double Quotes
-
+ Reemplazar Comillas ASCII
@@ -1989,7 +1989,7 @@
Find in Project
-
+ Buscar en el Proyecto
@@ -2161,12 +2161,12 @@
Details
-
+ DetallesOutline
-
+ Estructura
@@ -2997,27 +2997,27 @@
Project Search
-
+ Búsqueda en ProyectoCase Sensitive
- Sensibilidad a Mayúsculas y Minúsculas
+ Sensibilidad a Mayúsculas y MinúsculasWhole Words Only
- Sólo Palabras Enteras
+ Sólo Palabras EnterasRegEx Mode
- Modo ExReg
+ Modo ExRegSearch for
-
+ Buscar
@@ -3231,7 +3231,7 @@
Project Search
-
+ Búsqueda en Proyecto
@@ -4068,7 +4068,7 @@
Hidden
- Ocultar
+ Oculto
@@ -4209,21 +4209,21 @@
Additional Styling
-
+ Estilos AdicionalesCentre
-
+ CentradoPage Break
- Salto de Página
+ Salto de Página
@@ -4560,58 +4560,58 @@
Words
- Palabras
+ PalabrasCharacters
- Personajes
+ CaracteresWords in Headings
-
+ Palabras en TitulosWords in Text
-
+ Palabras en el TextoHeadings
- Títulación
+ TítulosParagraphs
- Párrafos
+ PárrafosCharacters in Headings
-
+ Caracteres en los TítulosCharacters in Text
-
+ Caracteres en el TextoCharacters, No Spaces
-
+ Caracteres, Sin EspaciosCharacters in Headings, No Spaces
-
+ Caracteres en los Títulos, Sin EspaciosCharacters in Text, No Spaces
-
+ Caracteres en el Texto, Sin Espacios
@@ -4771,7 +4771,7 @@
Split Document by Headings
-
+ Separar el Documento según Titulación
@@ -4786,7 +4786,7 @@
Duplicate
-
+ Duplicar
diff --git a/i18n/nw_it_IT.ts b/i18n/nw_it_IT.ts
index 7b543baa..15e7c409 100644
--- a/i18n/nw_it_IT.ts
+++ b/i18n/nw_it_IT.ts
@@ -31,32 +31,32 @@
Partition Format
-
+ Formato della PartizioneChapter Format
-
+ Formato del CapitoloUnnumbered Format
-
+ Formato non numeratoScene Format
-
+ Formato della Scena
- Hard Scene Format
-
+ Alt. Scene Format
+ Formato scena alt.Section Format
-
+ Formato della Sezione
@@ -86,7 +86,7 @@
Ignore These Keywords
-
+ Ignora queste parole chiave
@@ -206,17 +206,17 @@
First Line Indent
-
+ Rientro della prima lineaMarkdown (.md)
-
+ Markdown (.md)Preserve Hard Line Breaks
-
+ Conserva le interruzioni di linea
@@ -231,7 +231,7 @@
Preserve Tab Characters
-
+ Conserva gli spazi di tabulazione
@@ -791,22 +791,22 @@
GuiDocEditFooter
-
+ Line: {0} ({1})Riga: {0} ({1})
-
+ Words: {0} ({1})Parole: {0} ({1})
-
+ Words: {0} selectedParole: {0} selezionate
-
+ StatusStato
@@ -814,27 +814,27 @@
GuiDocEditHeader
-
+ Toggle Tool BarAttiva/disattiva la Barra degli strumenti
-
+ Outline
-
+ Struttura
-
+ SearchCerca
-
+ Toggle Focus ModeAttiva/Disattiva modalità Focus
-
+ CloseChiudi
@@ -842,62 +842,62 @@
GuiDocEditSearch
-
+ Search for
-
+ Ricerca
-
+ Replace with
-
+ Sostituisci con
-
+ SearchCerca
-
+ Case SensitiveConsidera maiuscole/minuscole
-
+ Whole Words OnlySolo parole intere
-
+ RegEx ModeModalità RegEx
-
+ Loop SearchRicerca a ciclo continuo
-
+ Search Next FileCerca nel file successivo
-
+ Preserve CaseNon considerare maiuscole/minuscole
-
+ Close SearchChiudi ricerca
-
+ Find in current documentTrova nel documento corrente
-
+ Find and replace in current documentTrova e sostituisci nel documento corrente
@@ -920,107 +920,107 @@
Impossibile salvare il documento.
-
+ Saved Document: {0}Documento salvato: {0}
-
+ Spell checking requires the package PyEnchant. It does not appear to be installed.Il controllo ortografico richiede il pacchetto PyEnchant. Non sembra essere installato.
-
+ Spell check completeControllo ortografico completo
-
+ Document DetailsDettagli del documento
-
+ Created: {0}Creato: {0}
-
+ Updated: {0}Aggiornato: {0}
-
+ File Location: {0}Posizione del file: {0}
-
+ Set as Document NameImposta come nome del documento
-
+ Follow TagSegui i Tag
-
+ Create Note for TagCrea una nota per il Tag
-
+ CutTaglia
-
+ CopyCopia
-
+ PasteIncolla
-
+ Select AllSeleziona tutto
-
+ Select WordSeleziona parola
-
+ Select ParagraphSeleziona paragrafo
-
+ Spelling Suggestion(s)Suggerimento(i) ortografico(i)
-
+ No SuggestionsNessun suggerimento
-
+ Add Word to DictionaryAggiungi parola al dizionario
-
+ Please select some text before calling replace quotes.Per favore seleziona del testo prima di chiedere il cambio di virgolette.
-
+ Do you want to create a new project note for the tag '{0}'?Vuoi creare una nuova nota di progetto per il tag '{0}'?
@@ -1058,7 +1058,7 @@
Document Headings
-
+ Intestazioni del documento
@@ -1068,22 +1068,22 @@
Split on Heading Level 1 (Partition)
-
+ Dividi sul livello d'intestazione 1 (Partizione)Split up to Heading Level 2 (Chapter)
-
+ Dividi sul livello d'intestazione 2 (Capitolo)Split up to Heading Level 3 (Scene)
-
+ Dividi sul livello d'intestazione 3 (Scena)Split up to Heading Level 4 (Section)
-
+ Dividi sul livello d'intestazione 4 (Sezione)
@@ -1104,52 +1104,52 @@
GuiDocToolBar
-
+ Markdown BoldMarkdown grassetto
-
+ Markdown ItalicMarkdown corsivo
-
+ Markdown StrikethroughMarkdown barrato
-
+ Shortcode BoldShortcode grassetto
-
+ Shortcode ItalicShortcode corsivo
-
+ Shortcode StrikethroughShortcode barrato
-
+ Shortcode UnderlineShortcode sottolineato
-
+ Shortcode Highlight
-
+ Evidenziazione
-
+ Shortcode SuperscriptShortcode apice
-
+ Shortcode SubscriptShortcode pendice
@@ -1187,7 +1187,7 @@
Outline
-
+ Struttura
@@ -1854,7 +1854,7 @@
Highlight
-
+ Evidenzia
@@ -1869,22 +1869,22 @@
Heading 1 (Partition)
-
+ Titolo 1 (Partizione)Heading 2 (Chapter)
-
+ Titolo 2 (Capitolo)Heading 3 (Scene)
-
+ Titolo 3 (Scena)Heading 4 (Section)
-
+ Titolo 4 (Sezione)
@@ -1898,8 +1898,8 @@
- Hard Scene
-
+ Alternative Scene
+ Scena alternativa
@@ -1944,12 +1944,12 @@
Replace Straight Single Quotes
-
+ Sostituisci le singole virgolette dritteReplace Straight Double Quotes
-
+ Sostituisci le doppie virgolette dritte
@@ -1989,7 +1989,7 @@
Find in Project
-
+ Trova nel progetto
@@ -2161,12 +2161,12 @@
Details
-
+ DettagliOutline
-
+ Struttura
@@ -2997,27 +2997,27 @@
Project Search
-
+ Ricerca nel progettoCase Sensitive
- Considera maiuscole/minuscole
+ Considera maiuscole/minuscoleWhole Words Only
- Solo parole intere
+ Solo parole intereRegEx Mode
- Modalità RegEx
+ Modalità RegExSearch for
-
+ Ricerca
@@ -3231,7 +3231,7 @@
Project Search
-
+ Ricerca nel progetto
@@ -4068,7 +4068,7 @@
Hidden
- Nascosto
+ Nascosto
@@ -4209,21 +4209,21 @@
Additional Styling
-
+ Elementi di stile supplementariCentre
-
+ CentroPage Break
- Interruzione di pagina
+ Interruzione di pagina
@@ -4560,58 +4560,58 @@
Words
- Parole
+ ParoleCharacters
- Caratteri
+ CaratteriWords in Headings
-
+ Parole nelle intestazioniWords in Text
-
+ Parole nel testoHeadings
- Intestazioni
+ IntestazioniParagraphs
- Paragrafi
+ ParagrafiCharacters in Headings
-
+ Caratteri nelle intestazioniCharacters in Text
-
+ Caratteri nel testoCharacters, No Spaces
-
+ Caratteri, esclusi gli spaziCharacters in Headings, No Spaces
-
+ Caratteri nelle intestazioni, esclusi gli spaziCharacters in Text, No Spaces
-
+ Caratteri nel testo, esclusi gli spazi
@@ -4771,7 +4771,7 @@
Split Document by Headings
-
+ Dividi il documento alle intestazioni
@@ -4786,7 +4786,7 @@
Duplicate
-
+ Duplica
diff --git a/i18n/nw_ja_JP.ts b/i18n/nw_ja_JP.ts
index 1018ff39..0abdc800 100644
--- a/i18n/nw_ja_JP.ts
+++ b/i18n/nw_ja_JP.ts
@@ -31,32 +31,32 @@
Partition Format
-
+ パーティション書式Chapter Format
-
+ チャプター書式Unnumbered Format
-
+ 番号なし書式Scene Format
-
+ シーン書式
- Hard Scene Format
-
+ Alt. Scene Format
+ 代替シーン書式Section Format
-
+ セクション書式
@@ -86,7 +86,7 @@
Ignore These Keywords
-
+ これらのキーワードを無視
@@ -196,7 +196,7 @@
Page Header
- ページ見出し
+ ページヘッダー
@@ -206,17 +206,17 @@
First Line Indent
-
+ 最初の行のインデントMarkdown (.md)
-
+ マークダウン (.md)Preserve Hard Line Breaks
-
+ ハードラインブレイクを保持
@@ -231,7 +231,7 @@
Preserve Tab Characters
-
+ タブ文字を保持
@@ -409,17 +409,17 @@
Novel Chapter
- 小説の章
+ 小説のチャプターNovel Scene
- 小説の場面
+ 小説のシーンNovel Section
- 小説の節
+ 小説のセクション
@@ -791,22 +791,22 @@
GuiDocEditFooter
-
+ Line: {0} ({1})行: {0} ({1})
-
+ Words: {0} ({1})単語: {0} ({1})
-
+ Words: {0} selected単語: {0} 選択済み
-
+ Statusステータス
@@ -814,27 +814,27 @@
GuiDocEditHeader
-
+ Toggle Tool Barツールバーの切り替え
-
+ Outline
-
+ アウトライン
-
+ Search検索
-
+ Toggle Focus Modeフォーカスモードの切り替え
-
+ Close閉じる
@@ -842,62 +842,62 @@
GuiDocEditSearch
-
+ Search for
-
+ 検索
-
+ Replace with
-
+ 置換候補
-
+ Search検索
-
+ Case Sensitive大文字と小文字を区別
-
+ Whole Words Only完全一致のみ
-
+ RegEx Mode正規表現モード
-
+ Loop Searchループ検索
-
+ Search Next File次のファイルを検索
-
+ Preserve Case大文字と小文字を保持
-
+ Close Search検索を閉じる
-
+ Find in current document現在のドキュメント内を検索
-
+ Find and replace in current document現在のドキュメント内を検索して置き換え
@@ -920,107 +920,107 @@
ドキュメントを保存できませんでした。
-
+ Saved Document: {0}保存済みドキュメント: {0}
-
+ Spell checking requires the package PyEnchant. It does not appear to be installed.スペルチェックにはPyEnchantパッケージが必要ですが、インストールされていないようです。
-
+ Spell check completeスペルチェック完了
-
+ Document Detailsドキュメントの詳細
-
+ Created: {0}作成済み: {0}
-
+ Updated: {0}更新: {0}
-
+ File Location: {0}ファイル場所: {0}
-
+ Set as Document Nameドキュメント名として設定
-
+ Follow Tagタグをフォロー
-
+ Create Note for Tagタグのメモを作成
-
+ Cut切り取り
-
+ Copyコピー
-
+ Paste貼り付け
-
+ Select Allすべて選択
-
+ Select Word単語を選択
-
+ Select Paragraph段落を選択
-
+ Spelling Suggestion(s)スペルの提案
-
+ No Suggestions候補なし
-
+ Add Word to Dictionary単語を辞書に追加
-
+ Please select some text before calling replace quotes.置き換え引用符を呼び出す前にテキストを選択してください。
-
+ Do you want to create a new project note for the tag '{0}'?タグ '{0}' の新しいプロジェクトノートを作成しますか?
@@ -1058,7 +1058,7 @@
Document Headings
-
+ ドキュメントの見出し
@@ -1068,22 +1068,22 @@
Split on Heading Level 1 (Partition)
-
+ 見出し階層1 (パーティション) で分割Split up to Heading Level 2 (Chapter)
-
+ 見出し階層2 (チャプター) で分割Split up to Heading Level 3 (Scene)
-
+ 見出し階層3 (シーン) で分割Split up to Heading Level 4 (Section)
-
+ 見出し階層4 (セクション) で分割
@@ -1104,52 +1104,52 @@
GuiDocToolBar
-
+ Markdown Boldマークダウン 太字
-
+ Markdown Italicマークダウン 斜体
-
+ Markdown Strikethroughマークダウン 取り消し線
-
+ Shortcode Boldショートコード 太字
-
+ Shortcode Italicショートコード 斜体
-
+ Shortcode Strikethroughショートコード 取り消し線
-
+ Shortcode Underlineショートコード 下線
-
+ Shortcode Highlight
-
+ ショートコードハイライト
-
+ Shortcode Superscriptショートコード 上付き文字
-
+ Shortcode Subscriptショートコード 下付き文字
@@ -1187,7 +1187,7 @@
Outline
-
+ アウトライン
@@ -1644,22 +1644,22 @@
Left Single Quote
- 左シングルクォーテーション
+ 左シングルクォートRight Single Quote
- 右シングルクォーテーション
+ 右シングルクォートLeft Double Quote
- 左ダブルクォーテーション
+ 左ダブルクォートRight Double Quote
- 右ダブルクォーテーション
+ 右ダブルクォート
@@ -1819,12 +1819,12 @@
Wrap Double Quotes
- ダブルクォーテーションで包む
+ ダブルクォートで包むWrap Single Quotes
- シングルクォーテーションで包む
+ シングルクォートで包む
@@ -1854,7 +1854,7 @@
Highlight
-
+ ハイライト
@@ -1869,22 +1869,22 @@
Heading 1 (Partition)
-
+ 見出し1 (パーティション)Heading 2 (Chapter)
-
+ 見出し2 (チャプター)Heading 3 (Scene)
-
+ 見出し3 (シーン)Heading 4 (Section)
-
+ 見出し4 (セクション)
@@ -1894,12 +1894,12 @@
Unnumbered Chapter
- 番号のない章
+ 番号なしチャプター
- Hard Scene
-
+ Alternative Scene
+ 代替シーン
@@ -1944,12 +1944,12 @@
Replace Straight Single Quotes
-
+ 直線シングルクォートを置き換えReplace Straight Double Quotes
-
+ 直線ダブルクォートを置換
@@ -1989,7 +1989,7 @@
Find in Project
-
+ プロジェクト内を検索
@@ -2161,12 +2161,12 @@
Details
-
+ 詳細Outline
-
+ アウトライン
@@ -2351,17 +2351,17 @@
Chapter
- 章
+ チャプターScene
- 場面
+ シーンSection
- 節
+ セクション
@@ -2551,7 +2551,7 @@
Emphasise partition and chapter labels
- パーティションと章ラベルを強調
+ パーティションとチャプターラベルを強調
@@ -2561,12 +2561,12 @@
Show full path in document header
- ドキュメント見出しにフルパスを表示
+ ドキュメントヘッダーにフルパスを表示Add the parent folder names to the header.
- 親フォルダ名を見出しに追加します。
+ 親フォルダ名をヘッダーに追加します。
@@ -2821,7 +2821,7 @@
Highlight text wrapped in quotes
- 引用符で囲まれたテキストを強調
+ 引用符で囲まれたテキストをハイライト
@@ -2833,22 +2833,22 @@
Allow open-ended single quotes
- オープンエンドのシングルクォートを許可
+ 閉じないシングルクォートを許可Highlight single-quoted line with no closing quote.
- 終了引用符がないシングルクォートの行を強調表示します。
+ 終了引用符がないシングルクォートの行をハイライト表示します。Allow open-ended double quotes
- オープンエンドのダブルクォートを許可
+ 閉じないダブルクォートを許可Highlight double-quoted line with no closing quote.
- 終了引用符がないダブルクォートの行を強調表示します。
+ 終了引用符がないダブルクォートの行をハイライト表示します。
@@ -2997,27 +2997,27 @@
Project Search
-
+ プロジェクト検索Case Sensitive
- 大文字と小文字を区別
+ 大文字と小文字を区別Whole Words Only
- 完全一致のみ
+ 完全一致のみRegEx Mode
- 正規表現モード
+ 正規表現モードSearch for
-
+ 検索
@@ -3132,12 +3132,12 @@
New Chapter
- 新規章
+ 新規チャプターNew Scene
- 新規場面
+ 新規シーン
@@ -3231,7 +3231,7 @@
Project Search
-
+ プロジェクト検索
@@ -3671,12 +3671,12 @@
Summary of the chapter.
- 章の概要。
+ チャプターの概要。Summary of the scene.
- 場面の概要。
+ シーンの概要。
@@ -3686,13 +3686,13 @@
Chapter {0}
- 章 {0}
+ チャプター {0}Scene {0}
- 場面 {0}
+ シーン {0}
@@ -4025,7 +4025,7 @@
Chapters on odd pages
- 奇数ページ上の章
+ 奇数ページ上のチャプター
@@ -4068,7 +4068,7 @@
Hidden
- 非表示
+ 非表示
@@ -4159,32 +4159,32 @@
Chapter Number
- 章番号
+ チャプター番号Chapter Number (Word)
- 章番号 (文章)
+ チャプター番号 (文章)Chapter Number (Upper Case Roman)
- 章番号 (大文字のローマ字)
+ チャプター番号 (大文字のローマ字)Chapter Number (Lower Case Roman)
- 章番号 (小文字のローマ字)
+ チャプター番号 (小文字のローマ字)Scene Number (In Chapter)
- 場面番号 (チャプター内)
+ シーン番号 (チャプター内)Scene Number (Absolute)
- 場面番号 (絶対)
+ シーン番号 (絶対)
@@ -4209,21 +4209,21 @@
Additional Styling
-
+ 追加のスタイルCentre
-
+ 中央Page Break
- 改ページ
+ 改ページ
@@ -4281,12 +4281,12 @@
Add {0} chapter documents
- {0} 章ドキュメントを追加
+ {0} チャプタードキュメントを追加Add {0} scene documents (to each chapter)
- {0} 場面ドキュメントを追加(各章に)
+ {0} シーンドキュメントを追加(各チャプターに)
@@ -4311,7 +4311,7 @@
Chapters and Scenes
- 章と場面
+ チャプターとシーン
@@ -4427,12 +4427,12 @@
Chapters
- 章
+ チャプターScenes
- 場面
+ シーン
@@ -4560,58 +4560,58 @@
Words
- 単語
+ 単語数Characters
- 文字
+ 文字数Words in Headings
-
+ 見出し内の単語数Words in Text
-
+ テキスト内の単語数Headings
- 見出し
+ 見出しParagraphs
- 段落
+ 段落Characters in Headings
-
+ 見出し内の文字数Characters in Text
-
+ テキスト内の文字数Characters, No Spaces
-
+ スペースなし文字数Characters in Headings, No Spaces
-
+ 見出し内のスペースなし文字数Characters in Text, No Spaces
-
+ テキスト内のスペースなし文字数
@@ -4771,7 +4771,7 @@
Split Document by Headings
-
+ 見出しでドキュメントを分割
@@ -4786,7 +4786,7 @@
Duplicate
-
+ 複製
diff --git a/i18n/nw_nb_NO.ts b/i18n/nw_nb_NO.ts
index 20e3d251..aaab9314 100644
--- a/i18n/nw_nb_NO.ts
+++ b/i18n/nw_nb_NO.ts
@@ -4,232 +4,232 @@
Builds
-
+ Document FiltersDokumentfiltre
-
+ Novel DocumentsRomandokumenter
-
+ Project NotesProsjektnotater
-
+ Inactive DocumentsInaktive dokumenter
-
+ HeadingsOverskrifter
-
+ Partition FormatTittelformat
-
+ Chapter FormatKapittelformat
-
+ Unnumbered FormatUnummerert format
-
+ Scene FormatSceneformat
-
- Hard Scene Format
+
+ Alt. Scene FormatAlt. sceneformat
-
+ Section FormatSeksjonformat
-
+ Text ContentTekstinnhold
-
+ Include SynopsisInkluder sammendrag
-
+ Include CommentsInkluder kommentarer
-
+ Include KeywordsInkluder kodeord
-
+ Include Body TextInkluder tekst
-
+ Ignore These KeywordsIgnorer disse nøkkelordene
-
+ Insert ContentLegg til innhold
-
+ Add Titles for NotesLegg til titler for notater
-
+ Text FormatTekstformat
-
+ Font FamilySkriftfamilie
-
+ Font SizeSkriftstørrelse
-
+ Line HeightLinjehøyde
-
+ Text OptionsSkriftvalg
-
+ Justify Text MarginsJuster tekstmarginer
-
+ Replace Unicode CharactersErstatt unicode-tegn
-
+ Replace Tabs with SpacesErstatt tabulator med mellomrom
-
+ Page LayoutSideoppsett
-
+ UnitEnhet
-
+ Page SizeSidestørrelse
-
+ Page WidthSidebredde
-
+ Page HeightSidehøyde
-
+ Top MarginToppmarg
-
+ Bottom MarginBunnmarg
-
+ Left MarginVenstremarg
-
+ Right MarginHøyremarg
-
+ Open Document (.odt)Open Document (.odt)
-
+ Add Highlight ColoursBruk farger på spesielle elementer
-
+ Page HeaderTopptekst
-
+ Page Counter OffsetFørste side for sideteller
-
+ First Line IndentInnrykk på første linje
-
+ Markdown (.md)Markdown (.md)
-
+ Preserve Hard Line BreaksBevar harde linjeskift
-
+ HTML (.html)HTML (.html)
-
+ Add CSS StylesLegg til CSS style
-
+ Preserve Tab CharactersBevar tabulatorer
@@ -237,72 +237,72 @@
Common
-
+ in the futurei fremtiden
-
+ just nownå nettopp
-
+ a minute agofor et minutt siden
-
+ {0} minutes agofor {0} minutter siden
-
+ an hour agofor en time siden
-
+ {0} hours agofor {0} timer siden
-
+ a day agofor en dag siden
-
+ {0} days agofor {0} dager siden
-
+ a week agofor en uke siden
-
+ {0} weeks agofor {0} uker siden
-
+ a month agofor en måned siden
-
+ {0} months agofor {0} måneder siden
-
+ a year agofor et år siden
-
+ {0} years agofor {0} år siden
@@ -310,375 +310,375 @@
Constant
-
-
-
+
+
+ NoneIngen
-
+ NovelRoman
-
-
+
+ PlotPlott
-
-
+
+ CharactersKarakterer
-
-
+
+ LocationsLokasjoner
-
-
+
+ TimelineTidslinje
-
-
+
+ ObjectsObjekter
-
-
+
+ EntitiesEnheter
-
-
-
+
+
+ CustomAnnet
-
+ ArchiveArkiv
-
+ TemplatesMaler
-
+ TrashSøppel
-
-
+
+ Novel DocumentRomandokument
-
-
+
+ Project NoteProsjektnotat
-
+ Root FolderHovedmappe
-
+ FolderMappe
-
+ Novel Title PageTittelside
-
+ Novel ChapterKapittel
-
+ Novel SceneScene
-
+ Novel SectionSeksjon
-
+ TagKnagg
-
+ Point of ViewPerspektiv
-
-
+
+ FocusFokus
-
+ TitleTittel
-
+ LevelNivå
-
+ DocumentDokument
-
+ LineLinje
-
+ CharsTegn
-
+ WordsOrd
-
+ ParsAvsnitt
-
+ POVPersp.
-
+ SynopsisSammendrag
-
+ Open Document (.odt)Open Document (.odt)
-
+ Flat Open Document (.fodt)Flat Open Document (.fodt)
-
+ novelWriter HTML (.html)novelWriter HTML (.htm)
-
+ novelWriter Markup (.txt)novelWriter Markup (.txt)
-
+ Standard Markdown (.md)Standard Markdown (.md)
-
+ Extended Markdown (.md)Utvidet Markdown (.md)
-
+ JSON + novelWriter HTML (.json)JSON + novelWriter HTML (.json)
-
+ JSON + novelWriter Markup (.json)JSON + novelWriter Markup (.json)
-
+ Text filesTekstfiler
-
+ Markdown filesMarkdown-filer
-
+ novelWriter filesnovelWriter-filer
-
+ CSV filesCSV-filer
-
+ All filesAlle filer
-
+ MillimetresMillimeter
-
+ CentimetresCentimeter
-
+ InchesTommer
-
+ A4A4
-
+ A5A5
-
+ A6A6
-
+ US LegalUS Legal
-
+ US LetterUS Letter
-
+ Straight single quotation markRett, enkelt sitattegn
-
+ Straight double quotation markRett, dobbelt sitattegn
-
+ Left single quotation markVenstre, enkelt sitattegn
-
+ Right single quotation markHøyre, enkelt sitattegn
-
+ Single low-9 quotation markEnkelt, lavt-9 sitattegn
-
+ Single high-reversed-9 quotation markEnkelt, høyt, reversert-9 sitattegn
-
+ Left double quotation markVenstre, dobbelt sitattegn
-
+ Right double quotation markHøyre, dobbelt sitattegn
-
+ Double low-9 quotation markDobbelt, lavt-9 sitattegn
-
+ Double high-reversed-9 quotation markDobbelt, høyt, reversert-9 sitattegn
-
+ Double low-reversed-9 quotation markDobbelt, lavt, reversert-9 sitattegn
-
+ Single left-pointing angle quotation markEnkelt, venstre, angulært sitattegn
-
+ Single right-pointing angle quotation markEnkelt, høyre, angulært sitattegn
-
+ Double left-pointing angle quotation markDobbelt, venstre, angulært sitattegn
-
+ Double right-pointing angle quotation markDobbelt, høyre, angulært sitattegn
-
+ Left corner bracketVenstre hjørnevinkel
-
+ Right corner bracketHøyre hjørnevinkel
-
+ Left white corner bracketVenstre, hvit hjørnevinkel
-
+ Right white corner bracketHøyre, hvit hjørnevinkel
@@ -686,17 +686,17 @@
GuiAbout
-
+ About novelWriterOm novelWriter
-
+ This application is licenced under {0}Denne applikasjonen er lisensiert under {0}
-
+ CreditsKrediteringer
@@ -704,38 +704,38 @@
GuiBuildSettings
-
-
+
+ Manuscript Build SettingsByggeinnstillinger for manuskript
-
+ NameNavn
-
+ SelectionUtvalg
-
+ HeadingsOverskrifter
-
+ ContentInnhold
-
+ FormatFormat
-
+ OutputUtdata
@@ -743,47 +743,47 @@
GuiDictionaries
-
+ Add DictionariesLegg til ordbøker
-
+ Download a dictionary from one of the links, and add it below.Last ned en ordbok fra en av lenkene, og legg den til nedenfor.
-
+ Add DictionaryLegg til ordbok
-
+ Dictionary install locationMappe hvor ordbøkene er installert
-
+ Additional dictionaries found: {0}Ordbøker funnet: {0}
-
+ Free or Libre Office extensionFree- eller Libre Office-utvidelse
-
+ Browse FilesBla gjennom
-
+ Could not process dictionary fileKunne ikke behandle ordbokfilen
-
+ Added: {0} [{1}B]Lagt til: {0} [{1}B]
@@ -791,22 +791,22 @@
GuiDocEditFooter
-
+ Line: {0} ({1})Linje: {0} ({1})
-
+ Words: {0} ({1})Ord: {0} ({1})
-
+ Words: {0} selectedOrd: {0} valgt
-
+ StatusStatus
@@ -814,27 +814,27 @@
GuiDocEditHeader
-
+ Toggle Tool BarVis/skjul verktøylinje
-
+ OutlineDisposisjon
-
+ SearchSøk
-
+ Toggle Focus Mode
- Slå av/på "Fokus-modus"
+ Slå av/på "Fokus-modus"
-
+ CloseLukk
@@ -842,62 +842,62 @@
GuiDocEditSearch
-
+ Search forSøketekst
-
+ Replace withErstatt med
-
+ SearchSøk
-
+ Case SensitiveSkill store/små bokstaver
-
+ Whole Words OnlyKun hele ord
-
+ RegEx ModeRegEx-modus
-
+ Loop SearchSøk rundt
-
+ Search Next FileSøk i neste file
-
+ Preserve CaseBehold store/små bokstaver
-
+ Close SearchLukk søk
-
+ Find in current documentSøk i det åpne dokumentet
-
+ Find and replace in current documentSøk og erstatt i det åpne dokumentet
@@ -905,122 +905,122 @@
GuiDocEditor
-
+ Opened Document: {0}Åpnet dokument: {0}
-
+ This document has been changed outside of novelWriter while it was open. Overwrite the file on disk?Dette dokumentet er endret utenfor novelWriter mens det var åpent. Overskrive filen på disken?
-
+ Could not save document.Kunne ikke lagre dokumentet.
-
+ Saved Document: {0}Lagret dokument: {0}
-
+ Spell checking requires the package PyEnchant. It does not appear to be installed.Stavekontroll krever at pakken PyEnchant er installert. Det ser det ikke ut til at den er.
-
+ Spell check completeStavekontrollen er ferdig
-
+ Document DetailsDokumentdetaljer
-
+ Created: {0}Opprettet: {0}
-
+ Updated: {0}Oppdatert: {0}
-
+ File Location: {0}Filplassering: {0}
-
+ Set as Document NameSett som dokumentnavn
-
+ Follow TagFølg knagg
-
+ Create Note for TagOpprett notat for knagg
-
+ CutKlipp
-
+ CopyKopier
-
+ PasteLim inn
-
+ Select AllVelg hele teksten
-
+ Select WordVelg hele ordet
-
+ Select ParagraphVelg hele avsnittet
-
+ Spelling Suggestion(s)Forslag fra stavekontrollen
-
+ No SuggestionsIngen forslag
-
+ Add Word to DictionaryLegg til ord i ordbok
-
+ Please select some text before calling replace quotes.Venligst velg en del av teksten før du velger å erstatte sitattegn.
-
+ Do you want to create a new project note for the tag '{0}'?Vil du opprette et nytt prosjektnotat for knaggen '{0}'?
@@ -1028,22 +1028,22 @@
GuiDocMerge
-
+ Merge DocumentsSlå sammen dokumenter
-
+ Documents to MergeDokumenter som skal slås sammen
-
+ Drag and drop items to change the order, or uncheck to exclude.Dra og slipp elementer for å endre rekkefølgen, eller fjern merking for å ekskludere.
-
+ Move merged items to TrashFlytt sammenslåtte elementer til papirkurven
@@ -1051,52 +1051,52 @@
GuiDocSplit
-
+ Split DocumentDel opp dokument
-
+ Document HeadingsDokumentets overskrifter
-
+ Select the maximum level to split into files.Velg hvilket nivå av overskrifter å dele opp til.
-
+ Split on Heading Level 1 (Partition)Splitt på overskrifter på nivå 1 (titler)
-
+ Split up to Heading Level 2 (Chapter)Splitt på overskrifter opp til nivå 2 (kapitler)
-
+ Split up to Heading Level 3 (Scene)Splitt på overskrifter opp til nivå 3 (scener)
-
+ Split up to Heading Level 4 (Section)Splitt på overskrifter opp til nivå 4 (seksjoner)
-
+ Split into a new folderDel inn i en ny mappe
-
+ Create document hierarchyOpprett dokumenthierarki
-
+ Move split document to TrashFlytt splittet element til papirkurven
@@ -1104,52 +1104,52 @@
GuiDocToolBar
-
+ Markdown BoldFet skrift med Markdown
-
+ Markdown ItalicKursiv med Markdown
-
+ Markdown StrikethroughGjennomstrek med Markdown
-
+ Shortcode BoldFet skrift med kortkode
-
+ Shortcode ItalicKursiv med kortkode
-
+ Shortcode StrikethroughGjennomstrek med kortkode
-
+ Shortcode UnderlineUnderstrek med kortkode
-
+ Shortcode HighlightTekstutheving med kortkode
-
+ Shortcode SuperscriptHevet skrift med kortkode
-
+ Shortcode SubscriptSenket skrift med kortkode
@@ -1157,27 +1157,27 @@
GuiDocViewFooter
-
+ Show/Hide Viewer PanelVis/skjul visningspanelet
-
+ CommentsKommentarer
-
+ Show CommentsVis kommentarer
-
+ SynopsisSammendrag
-
+ Show Synopsis CommentsVis sammendrag
@@ -1185,27 +1185,27 @@
GuiDocViewHeader
-
+ OutlineDisposisjon
-
+ Go BackwardGå bakover
-
+ Go ForwardGå fremover
-
+ ReloadOppdater
-
+ CloseLukk
@@ -1213,27 +1213,27 @@
GuiDocViewer
-
+ An error occurred while generating the preview.Det har oppstått en feil under genereringen av visningen.
-
+ CopyKopier
-
+ Select AllVelg hele teksten
-
+ Select WordVelg hele ordet
-
+ Select ParagraphVelg hele avsnittet
@@ -1241,12 +1241,12 @@
GuiDocViewerPanel
-
+ Hide Inactive TagsSkjul inaktive knagger
-
+ ReferencesReferanser
@@ -1254,12 +1254,12 @@
GuiEditLabel
-
+ Item LabelEnhetens navn
-
+ LabelNavn
@@ -1267,37 +1267,37 @@
GuiItemDetails
-
+ LabelNavn
-
+ StatusStatus
-
+ ClassKlasse
-
+ UsageFormål
-
+ CharactersTegn
-
+ WordsOrd
-
+ ParagraphsAvsnitt
@@ -1305,27 +1305,27 @@
GuiLipsum
-
+ Insert Placeholder TextSett inn midlertidig tekst
-
+ Insert Lorem Ipsum TextSett inn Lorem Ipsum-tekst
-
+ Number of paragraphsAntall avsnitt
-
+ Randomise orderTilfeldig rekkefølge
-
+ InsertSett inn
@@ -1333,103 +1333,103 @@
GuiMain
-
+ novelWriter is ready ...novelWriter er klar ...
-
+ You are now running novelWriter version {0}.Du kjører nå novelWriter versjon {0}.
-
+ Please check the {0}release notes{1} for further details.Sjekk {0}utgivelsesnotater{1} for mer informasjon.
-
+ Close the current project?Ønsker du å lukke dette prosjektet?
-
-
+
+ Changes are saved automatically.Endringer lagres automatisk.
-
+ Backup the current project?Ønsker du å ta sikkerhetskopi av dette prosjektet?
-
+ The project is already open by another instance of novelWriter, and is therefore locked. Override lock and continue anyway?Prosjektet er allerede åpent av en annen instans av novelWriter, og er derfor låst. Vil du overstyre denne låsen og fortsette likevel?
-
+ Note: If the program or the computer previously crashed, the lock can safely be overridden. However, overriding it is not recommended if the project is open in another instance of novelWriter. Doing so may corrupt the project.Merk: Hvis programmet eller datamaskinen tidligere krasjet, kan fil-låsen trygt overstyres. Det anbefales imidlertid ikke å overstyre den hvis prosjektet er åpent i en annen instans av novelWriter. Å gjøre det kan skape konflikter i prosjektets filer.
-
+ The project was locked by the computer '{0}' ({1} {2}), last active on {3}.Prosjektet er låst av datamaskinen {0} ({1} {2}), siste registrerte aktivitet var {3}.
-
+ The project index is outdated or broken. Rebuilding index.Prosjektets indeks er utdatert eller skadet. Bygger indeksen på nytt.
-
+ Import FileImporter fil
-
+ Could not read file. The file must be an existing text file.Kunne ikke lese filen. Filen må eksistere fra før av.
-
+ Please open a document to import the text file into.Vennligst åpne et dokument hvor teksten i filen kan importeres.
-
+ Importing the file will overwrite the current content of the document. Do you want to proceed?Å importere filen vil overskrive all eksisterende tekst i dokumentet. Ønsker du å fortsette?
-
+ Indexing completed in {0} msIndekseringen tok {0} ms
-
+ The project index has been successfully rebuilt.Prosjektets indeks har blitt bygget på nytt.
-
+ Could not initialise the dialog.Kunne ikke initialisere dialogen.
-
+ Do you want to exit novelWriter?Ønsker du å avslutte novelWriter?
-
+ Some changes will not be applied until novelWriter has been restarted.Noen endringer vil ikke tas i bruk før neste gang novelWriter startes.
-
+ Could not find the reference for tag '{0}'. It either doesn't exist, or the index is out of date. The index can be updated from the Tools menu, or by pressing {1}.Kunne ikke finne referansen til knagg {0}. Enten finnes den ikke, eller så er prosjektets indeks ikke oppdatert. Indeksen kan oppdateres fra Verktøy-menyen eller ved å trykke på {1}.
@@ -1437,657 +1437,657 @@
GuiMainMenu
-
+ &Project&Prosjekt
-
+ Create or Open ProjectOpprett eller åpne prosjekt
-
+ Save ProjectLagre prosjektet
-
+ Close ProjectLukk prosjektet
-
+ Project SettingsProsjektinnstillinger
-
+ Novel DetailsRoman-detaljer
-
+ Rename ItemEndre navn
-
+ Delete ItemSlett enhet
-
+ Empty TrashTøm søppel
-
+ ExitAvslutt
-
+ &Document&Dokument
-
+ Open DocumentÅpne dokument
-
+ Save DocumentLagre dokumentet
-
+ Close DocumentLukk dokumentet
-
+ View DocumentVis dokument
-
+ Close Document ViewLukk dokumentvisning
-
+ Show File DetailsVis filinformasjon
-
+ Import Text from FileImporter tekst fra fil
-
+ &Edit&Rediger
-
+ UndoAngre
-
+ RedoGjenopprett
-
+ CutKlipp
-
+ CopyKopier
-
+ PasteLim inn
-
+ Select AllVelg hele teksten
-
+ Select ParagraphVelg hele avsnittet
-
+ &View&Vis
-
+ Go to Project TreeGå til prosjekt-tre
-
+ Go to Document EditorGå til dokument-editor
-
+ Go to OutlineGå til disposisjon
-
+ Navigate BackwardNavigere bakover
-
+ Navigate ForwardNavigere fremover
-
+ Focus ModeFocus-modus
-
+ Full Screen ModeFullskjerm-modus
-
+ &InsertSett &inn
-
+ DashesBindestreker
-
+ Short DashKort bindestrek
-
+ Long DashLang bindestrek
-
+ Horizontal BarHorisontal strek
-
+ Figure DashTallstrek
-
+ Quote MarksSitattegn
-
+ Left Single QuoteVenstre, enkelt sitattegn
-
+ Right Single QuoteHøyre, enkelt sitattegn
-
+ Left Double QuoteVenstre, dobbelt sitattegn
-
+ Right Double QuoteHøyre, dobbelt sitattegn
-
+ Alternative ApostropheAlternativ apostrof
-
+ General PunctuationGenerell tegnsetting
-
+ EllipsisEllipsis
-
+ PrimePrimtegn
-
+ Double PrimeDobbelt primtegn
-
+ White SpacesMellomrom
-
+ Non-Breaking SpaceHardt mellomrom
-
+ Thin SpaceKort mellomrom
-
+ Thin Non-Breaking SpaceHardt, kort mellomrom
-
+ Other SymbolsAndre symboler
-
+ List BulletKulepunkt
-
+ Hyphen BulletBindestrekpunkt
-
+ Flower MarkBlomsterpunkt
-
+ Per MillePromille
-
+ Degree SymbolGradertegn
-
+ Minus SignMinustegn
-
+ Times SignGangetegn
-
+ Division SignDeletegn
-
+ Tags and ReferencesKnagger og referanser
-
+ Special CommentsAndre kommentartyper
-
+ Synopsis CommentKommentar med sammendrag
-
+ Short Description CommentKommentar for kort beskrivelse
-
+ Page Break and SpaceSideskift og avstand
-
+ Page BreakSideskift
-
+ Vertical Space (Single)Vertikal avstand (enkel)
-
+ Vertical Space (Multi)Vertikal avstand (flere)
-
+ Placeholder TextMidlertidig tekst
-
+ &Format&Formattering
-
+ BoldFet
-
+ ItalicKursiv
-
+ StrikethroughGjennomstrek
-
+ Wrap Double QuotesSett i doble sitattegn
-
+ Wrap Single QuotesSett i enkle sitattegn
-
+ More Formats ...Flere formater ...
-
+ Bold (Shortcode)Fet (kortkode)
-
+ Italics (Shortcode)Kursiv (kortkode)
-
+ Strikethrough (Shortcode)Gjennomstrek (Kortkode)
-
+ UnderlineUnderstrek
-
+ HighlightTekstutheving
-
+ SuperscriptHevet skrift
-
+ SubscriptSenket skrift
-
+ Heading 1 (Partition)Overskrift 1 (inndeling)
-
+ Heading 2 (Chapter)Overskrift 2 (kapittel)
-
+ Heading 3 (Scene)Overskrift 3 (scene)
-
+ Heading 4 (Section)Overskrift 4 (seksjon)
-
+ Novel TitleBoktittel
-
+ Unnumbered ChapterUnumrert kapittel
-
- Hard Scene
- Alt. scene
+
+ Alternative Scene
+ Alternativ scene
-
+ Align LeftVenstrejuster
-
+ Align CentreSentrer
-
+ Align RightHøyrejuster
-
+ Indent LeftInnrykk fra venstre
-
+ Indent RightInnrykk fra høyre
-
+ Toggle CommentVeksle kommentar
-
+ Toggle Ignore TextAktiver/deaktiver ignorert tekst
-
+ Remove Block FormatFjern formattering
-
+ Replace Straight Single QuotesErstatt enkle sitattegn
-
+ Replace Straight Double QuotesErstatt doble sitattegn
-
+ Remove In-Paragraph BreaksFjern linjeskift i avsnittet
-
+ &Search&Søk
-
+ FindSøk
-
+ ReplaceErstatt
-
+ Find NextFinn neste
-
+ Find PreviousFinn forrige
-
+ Replace NextErstatt neste
-
+ Find in ProjectSøk i prosjektet
-
+ &Tools&Verktøy
-
+ Check SpellingStavekontroll
-
+ Spell Check LanguageSpråk for stavekontroll
-
+ DefaultIngen valg
-
+ Re-Run Spell CheckKjør stavekontroll
-
+ Project Word ListProsjektets ordliste
-
+ Add DictionariesLegg til ordbøker
-
+ Rebuild IndexBygg indeks
-
+ Backup ProjectLag sikkerhetskopi av prosjektets mappe
-
+ Build ManuscriptBygg manuskript
-
+ Writing StatisticsStatistikk
-
+ PreferencesInnstillinger
-
+ &Help&Hjelp
-
+ About novelWriterOm novelWriter
-
+ About Qt5Om Qt5
-
+ User Manual (Online)Brukermanual (på nett)
-
+ User Manual (PDF)Brukermanual (PDF)
-
+ Report an Issue (GitHub)Rapporter en feil (GitHub)
-
+ Ask a Question (GitHub)Still et spørsmål (GitHub)
-
+ The novelWriter WebsitenovelWriters nettside
@@ -2095,38 +2095,38 @@
GuiMainStatus
-
-
+
+ NoneIngen
-
+ EditorEditor
-
+ ProjectProsjekt
-
+ Session TimeTid brukt i gjeldende sesjon
-
+ Words: {0} ({1})Ord: {0} ({1})
-
+ Project word count (session change)Antall ord i prosjektet (endring i denne sesjonen)
-
+ Novel word count (session change)Antall ord i roman-teksten (endring i denne sesjonen)
@@ -2134,63 +2134,63 @@
GuiManuscript
-
+ Build ManuscriptBygg manuskript
-
+ Add New BuildLegg til ny byggedefinisjon
-
+ Delete Selected BuildSlett valgte byggedefinisjon
-
+ Edit Selected BuildRediger valgte byggedefinisjon
-
+ BuildsByggedefinisjoner
-
+ DetailsDetaljer
-
+ OutlineDisposisjon
-
+ PreviewForhåndsvis
-
+ PrintSkriv ut
-
+ BuildBygg
-
+ CloseLukk
-
-
+
+ My ManuscriptMitt manuskript
@@ -2198,57 +2198,57 @@
GuiManuscriptBuild
-
+ Build ManuscriptBygg manuskript
-
+ Output FormatDokumentformat
-
+ Table of ContentsInnholdsfortegnelse
-
+ PathFilbane
-
+ File NameFilnavn
-
+ Reset file name to defaultTilbakestill filnavn til standard
-
+ Open FolderÅpne mappe
-
+ &Build&Bygg
-
+ Select FolderVelg mappe
-
+ Output folder does not exist.Mappen eksisterer ikke.
-
+ The file already exists. Do you want to overwrite it?Filen eksisterer allerede. Vil du overskrive den?
@@ -2256,18 +2256,18 @@
GuiNovelDetails
-
-
+
+ Novel DetailsRoman-detaljer
-
+ OverviewOversikt
-
+ ContentsInnhold
@@ -2275,58 +2275,58 @@
GuiNovelToolBar
-
+ Outline of {0}Innhold i {0}
-
+ Novel RootRoman-mappe
-
+ RefreshOppdatér
-
+ Last ColumnSiste kolonne
-
+ HiddenSkjult
-
+ Point of View CharacterSynsvinkel-karakter
-
+ Focus CharacterFokus-karakter
-
+ Novel PlotRoman-plott
-
-
+
+ Column SizeKolonnebredde
-
+ More OptionsFlere valg
-
+ Maximum column size in %Maksimal kolonnebredde i %
@@ -2334,7 +2334,7 @@
GuiNovelTree
-
+ No meta dataIngen meta-data
@@ -2342,64 +2342,64 @@
GuiOutlineDetails
-
-
-
+
+
+ TitleTittel
-
+ ChapterKapittel
-
+ SceneScene
-
+ SectionSeksjon
-
+ DocumentDokument
-
+ StatusStatus
-
+ CharactersTegn
-
+ WordsOrd
-
+ ParagraphsAvsnitt
-
+ SynopsisSammendrag
-
+ Title DetailsOversikt
-
+ Reference TagsReferanser
@@ -2407,7 +2407,7 @@
GuiOutlineHeaderMenu
-
+ Select ColumnsVelg kolonner
@@ -2415,17 +2415,17 @@
GuiOutlineToolBar
-
+ Outline ofDisposisjon for
-
+ RefreshOppdatér
-
+ Export CSVEksporter CSV
@@ -2433,7 +2433,7 @@
GuiOutlineTree
-
+ Save Outline AsLagre disposisjon som
@@ -2441,553 +2441,553 @@
GuiPreferences
-
-
+
+ PreferencesInnstillinger
-
+ SearchSøk
-
+ GeneralGenerelt
-
+ AppearanceUtseende
-
+ Display languageVisningspråk
-
-
-
+
+
+ Requires restart to take effect.Krever omstart for å tre i kraft.
-
+ Colour themeFargetema
-
+ General colour theme and icons.Generelt fargetema og ikoner.
-
+ Application font familySkriftfamilie for program
-
+ Application font sizeSkriftstørrelse for program
-
-
+
+ ptpt
-
+ Hide vertical scroll bars in main windowsSkjul vertikale rullefelt i hovedvinduer
-
-
+
+ Scrolling available with mouse wheel and keys only.Rulling kan bare gjøres med mus og tastatur.
-
+ Hide horizontal scroll bars in main windowsSkjul horisontale rullefelt i hovedvinduer
-
+ Document StyleDokumentets stil
-
+ Document colour themeDokumentets fargetema
-
+ Colour theme for the editor and viewer.Fargetema for redigering og visning.
-
+ Document font familyDokumentets skriftfamilie
-
-
-
-
+
+
+
+ Applies to both document editor and viewer.Gjelder både redigerings- og visningsvindu.
-
+ Document font sizeDokumentets skriftstørrelse
-
+ Emphasise partition and chapter labelsFremhev filnavn for inndeling og kapitler
-
+ Makes them stand out in the project tree.Får dem til å skille seg ut i prosjekttreet.
-
+ Show full path in document headerVis full prosjektbane i dokumenthoder
-
+ Add the parent folder names to the header.Legger til mappene foran dokumentets navn.
-
+ Include project notes in status bar word countInkluder prosjektnotater i antallet ord i statuslinjen
-
+ Auto SaveAutomatisk lagring
-
+ Save document intervalIntervall for lagring av dokument
-
+ How often the document is automatically saved.Hvor ofte dokumentet lagres automatisk.
-
-
+
+ secondssekunder
-
+ Save project intervalIntervall for lagring av prosjekt
-
+ How often the project is automatically saved.Hvor ofte prosjektet lagres automatisk.
-
+ Project BackupSikkerhetskopi
-
+ BrowseBla
-
+ Backup storage locationFilbane for sikkerhetskopi
-
-
+
+ Path: {0}Filbane: {0}
-
+ Run backup when the project is closedLag sikkerhetskopi når prosjektet lukkes
-
+ Can be overridden for individual projects in Project Settings.Kan overstyres fra individuelle prosjektinnstillinger.
-
+ Ask before running backupSpør før sikkerhetskopi tas
-
+ If off, backups will run in the background.Hvis avslått, tas sikkerhetskopi automatisk.
-
+ Session TimerSesjons-klokke
-
+ Pause the session timer when not writingSett klokka på pause når du er inaktiv
-
+ Also pauses when the application window does not have focus.Pauses også når du ikke jobber i applikasjonens vindu.
-
+ Editor inactive time before pausing timerTid uten skriving før klokka settes på pause
-
+ User activity includes typing and changing the content.Dette måler kun endringer i teksteditoren.
-
+ minutesminutter
-
+ WritingSkriving
-
+ Text FlowTekstflyt
-
+ Maximum text width in "Normal Mode"Maks tekstbredde i "Normal-modus"
-
+ Set to 0 to disable this feature.Sett til 0 for å deaktivere denne funksjonen.
-
-
-
-
+
+
+
+ pxpx
-
+ Maximum text width in "Focus Mode"Maks tekstbredde i "Fokus-modus"
-
+ The maximum width cannot be disabled.Denne maks-bredden kan ikke deaktiveres.
-
+ Hide document footer in "Focus Mode"Gjem dokumentets bunnlinje i "Fokus-modus"
-
+ Hide the information bar in the document editor.Skjul informasjonslinjen i dokumenteditoren.
-
+ Justify the text marginsJuster tekstmarginer
-
+ Minimum text marginMinimum tekstmargin
-
+ Tab widthTabulatorens bredde
-
+ The width of a tab key press in the editor and viewer.Hvor langt tabulatoren hopper i editor og visning.
-
+ Text EditingRedigering av tekst
-
+ Spell check languageSpråk for stavekontroll
-
+ Available languages are determined by your system.Tilgjengelige språk hentes fra operativystemet ditt.
-
+ Auto-select word under cursorAuto-velg ord under markør
-
+ Apply formatting to word under cursor if no selection is made.Hvis ingen tekst er valgt, formatter ordet hvor markøren står.
-
+ Show tabs and spacesSynlige tabulatorer og mellomrom
-
+ Show line endingsSynlige linjeender
-
+ Editor ScrollingTekstbehandler rulling
-
+ Scroll past end of the documentTillat å rulle forbi slutten av dokumentet
-
+ Also centres the cursor when scrolling.Sentrerer også markøren når man ruller.
-
+ Typewriter style scrolling when you typeSkrivemaskin-liknende rulling mens du skriver
-
+ Keeps the cursor at a fixed vertical position.Holder markøren på samme sted vertikalt.
-
+ Minimum position for Typewriter scrollingMinste avstand for skrivemaskin-rulling
-
+ Percentage of the editor height from the top.I prosent fra toppen av editor-vinduet.
-
+ Text HighlightingFremheving
-
+ Highlight text wrapped in quotesFremhev tekst mellom sitattegn
-
-
-
+
+
+ Applies to the document editor only.Gjelder bare for redigeringsvindu.
-
+ Allow open-ended single quotesTillat enkle sitattegn som ikke lukkes
-
+ Highlight single-quoted line with no closing quote.Fremhev sitater som ikke er lukket i samme avsnitt.
-
+ Allow open-ended double quotesTillat doble sitattegn som ikke lukkes
-
+ Highlight double-quoted line with no closing quote.Fremhev sitater som ikke er lukket i samme avsnitt.
-
+ Add highlight colour to emphasised textFremhev formattert tekst
-
+ Highlight multiple or trailing spacesFremhev flere eller etterfølgende mellomrom
-
+ Text AutomationTekstautomatisering
-
+ Auto-replace text as you typeErstatt mens du skriver
-
+ Allow the editor to replace symbols as you type.Erstatt symboler mens du skriver.
-
+ Auto-replace single quotesErstatt enkle sitattegn
-
-
+
+ Try to guess which is an opening or a closing quote.Prøv å gjette om det er et åpne- eller lukketegn.
-
+ Auto-replace double quotesErstatt doble sitattegn
-
+ Auto-replace dashesErstatt bindestreker
-
+ Double and triple hyphens become short and long dashes.To og tre bindestreker erstattes med kort og lang bindestrek.
-
+ Auto-replace dotsErstatt tre punktum
-
+ Three consecutive dots become ellipsis.Tre punktum på rad erstattes med ellipsis.
-
+ Insert non-breaking space beforeSett inn hardt mellomrom foran
-
+ Automatically add space before any of these symbols.Legg til mellomrom automatisk foran disse tegnene.
-
+ Insert non-breaking space afterSett inn hardt mellomrom etter
-
+ Automatically add space after any of these symbols.Legg til mellomrom automatisk etter disse tegnene.
-
+ Use thin space insteadBruk tynt mellomrom istedet
-
+ Inserts a thin space instead of a regular space.Sett inn et tynt mellomrom istedenfor et vanlig et.
-
+ Quotation StyleSitattegn
-
+ Single quote open styleEnkelt sitat, venstre side
-
+ The symbol to use for a leading single quote.Symbol for enkelt sitattegn før et sitat.
-
+ Single quote close styleEnkelt sitat, høyre side
-
+ The symbol to use for a trailing single quote.Symbol for enkelt sitattegn etter et sitat.
-
+ Double quote open styleDobbelt sitat, venstre side
-
+ The symbol to use for a leading double quote.Symbol for dobbelt sitattegn før et sitat.
-
+ Double quote close styleDobbelt sitat, høyre side
-
+ The symbol to use for a trailing double quote.Symbol for dobbelt sitattegn etter et sitat.
-
+ Backup DirectoryMappe for sikkerhetskopi
@@ -2995,27 +2995,27 @@
GuiProjectSearch
-
+ Project SearchSøk i prosjektet
-
+ Case SensitiveSkill store/små bokstaver
-
+ Whole Words OnlyKun hele ord
-
+ RegEx ModeRegEx-modus
-
+ Search forSøketekst
@@ -3023,28 +3023,28 @@
GuiProjectSettings
-
-
+
+ Project SettingsProsjektinnstillinger
-
+ SettingsInnstillinger
-
+ StatusStatus
-
+ ImportanceViktighet
-
+ Auto-ReplaceAutoerstatt
@@ -3052,47 +3052,47 @@
GuiProjectToolBar
-
+ Project ContentProsjektets innhold
-
+ Quick LinksHurtiglenker
-
+ Move UpFlytt opp
-
+ Move DownFlytt ned
-
+ Add ItemLegg til element
-
+ Expand AllUtvid alle
-
+ Collapse AllLukk alle
-
+ Empty TrashTøm papirkurven
-
+ More OptionsFlere valg
@@ -3100,118 +3100,118 @@
GuiProjectTree
-
+ ActiveAktiv
-
+ InactiveInaktiv
-
+ Permanently delete {0} file(s) from Trash?Vil du slette {0} filer i papirkurven for godt?
-
+ Did not find anywhere to add the file or folder!Fant ikke noe sted å legge til filen eller mappen!
-
+ Cannot add new files or folders to the Trash folder.Kan ikke legge til nye filer eller mapper i papirkurvmappen.
-
+ New NoteNytt notat
-
+ New ChapterNytt kapittel
-
+ New SceneNy scene
-
+ New DocumentNytt dokument
-
+ New FolderNy mappe
-
+ There is currently no Trash folder in this project.Det er for øyeblikket ingen papirkurv i dette prosjektet.
-
+ The Trash folder is already empty.Papirkurven er allerede tom.
-
+ Move '{0}' to Trash?Vil du flytte filen "{0}" til søpla?
-
+ Root folders can only be deleted when they are empty.Rotmapper kan bare slettes når de er tomme.
-
+ Permanently delete '{0}'?Slette filen "{0}" for godt?
-
+ Drag and drop is only allowed for single items, non-root items, or multiple items with the same parent.Dra og slipp er bare tillatt for enkeltelementer, ikke hovedmapper, eller elementer under samme mappe eller dokument.
-
+ No documents selected for merging.Ingen dokumenter er valgt for sammenslåing.
-
+ MergedSammenslått
-
-
+
+ Could not write document content.Kan ikke skrive til dokumentet.
-
+ Do you want to duplicate this document?Vil du duplisere dette dokumentet?
-
+ Do you want to duplicate this item and all child items?Vil du duplisere dette elementet og alle underelementer?
-
+ Could not duplicate all items.Kunne ikke duplisere alle elementer.
-
+ There is nowhere to add item with name '{0}'.Fant ikke noe sted å legge til enheten med navn {0}'.
@@ -3219,42 +3219,42 @@
GuiSideBar
-
+ Project Tree ViewProsjektoversikt
-
+ Novel Tree ViewRomanoversikt
-
+ Project SearchSøk i prosjektet
-
+ Novel Outline ViewDisposisjon
-
+ Build ManuscriptBygg manuskript
-
+ Novel DetailsRoman-detaljer
-
+ Writing StatisticsStatistikk
-
+ SettingsOppsett
@@ -3262,37 +3262,37 @@
GuiWelcome
-
+ WelcomeVelkommen
-
+ ListListe
-
+ NewNytt
-
+ BrowseBla gjennom
-
+ CancelAvbryt
-
+ CreateOpprett
-
+ OpenÅpne
@@ -3300,33 +3300,33 @@
GuiWordList
-
-
+
+ Project Word ListProsjektets ordliste
-
+ Import words from text fileImporter ord fra tekstfil
-
+ Export words to text fileEksporter ord til tekstfil
-
+ Note: The import file must be a plain text file with UTF-8 or ASCII encoding.Merk: Importfilen må være en standard tekstfil med UTF-8 eller ASCII-koding.
-
+ Import FileImporter fil
-
+ Export FileEksporter fil
@@ -3334,147 +3334,147 @@
GuiWritingStats
-
+ Writing StatisticsStatistikk
-
+ Session StartStarttid
-
+ LengthLengde
-
+ IdleInaktiv
-
+ WordsOrd
-
+ HistogramHistogram
-
+ Sum TotalsTotalsummer
-
+ Total Time:Totaltid:
-
+ Idle Time:Inaktiv tid:
-
+ Filtered Time:Filtrert tid:
-
+ Novel Word Count:Ord i roman:
-
+ Notes Word Count:Ord i notater:
-
+ Total Word Count:Ord totalt:
-
+ FiltersFiltre
-
+ Count novel filesTell i romanfiler
-
+ Count note filesTell i notatfiler
-
+ Hide zero word countSkjul null-verdier
-
+ Hide negative word countSkjul negative verdier
-
+ Group entries by daySamle rader per dag
-
+ Show idle timeVis inaktiv som tid
-
+ Word count cap for the histogramMaks antall ord for histogram
-
+ Save AsLagre som
-
+ JSON Data File (.json)JSON-format (.json)
-
+ CSV Data File (.csv)CSV-format (.csv)
-
+ JSON Data FileJSON-format
-
+ CSV Data FileCSV-format
-
+ Save Data AsLagre data som
-
+ {0} file successfully written to:{0}-filen ble skrevet til:
-
+ Failed to write {0} file.Kunne ikke skrive {0}-filen.
@@ -3482,153 +3482,153 @@
NWProject
-
+ Could not delete document file.Kunne ikke slette dokumentets fil.
-
+ Not a known project file format.Ikke et kjent prosjektfilformat.
-
+ Project file not found.Prosjektfilen finnes ikke.
-
+ Failed to open project.Kunne ikke åpne prosjektet.
-
+ UnknownUkjent
-
+ Project file does not appear to be a novelWriterXML file.Prosjektfilen later ikke til å være en novelWriterXML-fil.
-
+ 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}.Prosjektfilen har et ukjent eller ikke støttet format, og kan ikke åpnes med denne versjonen av novelWriter. Prosjektet ble lagret av novelWriter versjon {0}.
-
+ Failed to parse project xml.Kunne ikke lese prosjektets xml-data.
-
+ The file format of your project is about to be updated. If you proceed, older versions of novelWriter will no longer be able to open this project. Continue?Filformatet til prosjektet ditt er i ferd med å bli oppdatert. Hvis du fortsetter, vil ikke eldre versjoner av novelWriter lenger kunne åpne dette prosjektet. Fortsette?
-
+ This project was saved by a newer version of novelWriter, version {0}. This is version {1}. If you continue to open the project, some attributes and settings may not be preserved, but the overall project should be fine. Continue opening the project?Dette prosjektet ble lagret av en nyere versjon av novelWriter, versjon {0}. Dette er versjon {1}. Hvis du ønsker å fortsette med å åpne prosjektet, kan noen av innstillingene bli borte, men selve prosjektet vil være i orden. Vil du fortsatt åpne prosjektet?
-
+ RecoveredGjennopprettet
-
+ Found {0} orphaned file(s) in the project. {1} file(s) were recovered.{0} foreldreløse fil(er) ble funnet i prosjektet. {1} fil(er) ble gjenopprettet.
-
+ Opened Project: {0}Åpnet prosjekt: {0}
-
+ There is no project open.Det er ikke noe prosjekter åpent.
-
+ Failed to save project.Kunne ikke lagre prosjektet.
-
+ Saved Project: {0}Lagret prosjekt: {0}
-
+ Backing up project ...Lager sikkerhetskopi ...
-
+ Cannot backup project because no project name is set. Please set a Project Name in Project Settings.Kan ikke ta sikkerhetskopi av prosjektet da prosjektnavn ikke er satt. Du må først sette et prosjektnavn i Prosjektinnstillinger.
-
+ Could not create backup folder.Kunne ikke lage mappe til sikkerhetskopi.
-
+ Created a backup of your project of size {0}B.Opprettet en sikkerhetskopi av prosjektet med størrelse {0}B.
-
+ Path: {0}Filbane: {0}
-
+ Could not write backup archive.Kunne ikke lage sikkerhetskopi.
-
+ Project backed up to '{0}'Sikkerhetskopi skrevet til '{0}'
-
-
+
+ NewNy
-
+ NoteNotat
-
+ DraftUtkast
-
+ FinishedFerdig
-
+ MinorMindre
-
+ MajorStørre
-
+ MainHoved
@@ -3636,7 +3636,7 @@
NovelSelector
-
+ All Novel FoldersAlle roman-mapper
@@ -3644,89 +3644,89 @@
ProjectBuilder
-
+ The target folder is not empty. Please choose another folder.Den valgte mappen er ikke tom. Vennligst velg en annen mappe.
-
+ An error occurred while trying to create the project.Det oppsto en feil under forsøk på å opprette prosjektet.
-
+ New ProjectNytt prosjekt
-
+ Title PageTittelside
-
+ ByAv
-
+ Summary of the chapter.Sammendrag av kapittelet.
-
+ Summary of the scene.Sammendrag av scenen.
-
+ A short description.En kort beskrivelse.
-
+ Chapter {0}Kapittel {0}
-
-
+
+ Scene {0}Scene {0}
-
+ Main PlotHovedplott
-
+ ProtagonistProtagonist
-
+ Main LocationHovedlokasjon
-
-
+
+ The target folder already exists. Please choose another folder.Den valgte mappen finnes allerede. Vennligst velg en annen mappe.
-
+ Could not copy project files.Kunne ikke kopiere prosjektfiler.
-
+ Failed to create a new example project.Kunne ikke lage nytt eksempel-prosjekt.
-
+ Failed to create a new example project. Could not find the necessary files. They seem to be missing from this installation.Kunne ikke lage nytt eksempel-prosjekt. Kunne ikke finne de nødvendige filene. De ser ut til å mangle i denne installasjonen.
@@ -3734,7 +3734,7 @@
QDialogButtonBox
-
+ OKOK
@@ -3742,27 +3742,27 @@
QGnomeTheme
-
+ &OK&OK
-
+ &Save&Lagre
-
+ &Cancel&Avbryt
-
+ &Close&Lukk
-
+ Close without SavingLukk uten å lagre
@@ -3770,92 +3770,92 @@
QPlatformTheme
-
+ OKOK
-
+ SaveLagre
-
+ Save AllLagre alle
-
+ OpenÅpne
-
+ &Yes&Ja
-
+ Yes to &AllJa til &alle
-
+ &No&Nei
-
+ N&o to AllN&ei til alle
-
+ AbortAvbryt
-
+ RetryPrøv igjen
-
+ IgnoreIgnorer
-
+ CloseLukk
-
+ CancelAvbryt
-
+ DiscardForkast
-
+ HelpHjelp
-
+ ApplyAnvend
-
+ ResetNullstill
-
+ Restore DefaultsGjennopprett standard
@@ -3863,58 +3863,58 @@
QWizard
-
+ Go BackGå tilbake
-
+ < &Back< &Tilbake
-
+ ContinueFortsett
-
+ &Next&Neste
-
+ &Next >&Neste >
-
+ CommitGjennomfør
-
+ DoneFerdig
-
+ &Finish&Fullfør
-
-
+
+ CancelAvbryt
-
+ HelpHjelp
-
+ &Help&Hjelp
@@ -3922,17 +3922,17 @@
SharedData
-
+ novelWriter Project File or Zip FilenovelWriter prosjektfil eller Zip-fil
-
+ novelWriter Project FilenovelWriter prosjektfil
-
+ Open ProjectÅpne prosjekt
@@ -3940,42 +3940,42 @@
VersionInfoWidget
-
+ Latest Version: {0}Siste versjon: {0}
-
+ Checking ...Sjekker ...
-
+ Download from {0}Last ned fra {0}
-
+ VersionVersjon
-
+ Released onUtgitt den
-
+ Release NotesLanseringsnotater
-
+ Check NowSjekk nå
-
+ FailedMislyktes
@@ -3983,57 +3983,57 @@
_ContentsPage
-
+ Table of ContentsInnholdsfortegnelse
-
+ TitleTittel
-
+ WordsOrd
-
+ PagesSider
-
+ PageSide
-
+ ProgressFremdrift
-
+ Words per pageOrd per side
-
+ First page offsetFørste side forskjøvet
-
+ Chapters on odd pagesKapittel på oddetall-sider
-
+ UntitledUten tittel
-
+ ENDSLUTT
@@ -4041,32 +4041,32 @@
_DetailsWidget
-
+ SettingInnstilling
-
+ ValueVerdi
-
+ NameNavn
-
+ SelectionUtvalg
-
+ TitleTittel
-
+ HiddenSkjult
@@ -4074,37 +4074,37 @@
_FilterTab
-
+ Included in manuscriptInkludert i manuskript
-
+ Excluded from manuscriptEkskludert fra manuskript
-
+ Always includedAlltid inkludert
-
+ Always excludedAlltid ekskludert
-
+ Reset to defaultTilbakestill til standard
-
+ Mark selection asMerk utvalg som
-
+ Select Root FoldersVelg hovedmapper
@@ -4112,22 +4112,22 @@
_GuiAlert
-
+ InformationInformasjon
-
+ WarningAdvarsel
-
+ ErrorFeil
-
+ QuestionSpørsmål
@@ -4135,93 +4135,93 @@
_HeadingsTab
-
+ HideSkjul
-
-
+
+ Editing: {0}Redigerer: {0}
-
-
+
+ NoneIngen
-
+ TitleTittel
-
+ Chapter NumberKapittelnummer
-
+ Chapter Number (Word)Kapittelnummer (som ord)
-
+ Chapter Number (Upper Case Roman)Kapittelnummer (store romertall)
-
+ Chapter Number (Lower Case Roman)Kapittelnummer (små romertall)
-
+ Scene Number (In Chapter)Scenenummer (i kapittel)
-
+ Scene Number (Absolute)Scenenummer (absolutt)
-
+ Point of View CharacterSynsvinkel-karakter
-
+ Focus CharacterFokus-karakter
-
+ InsertSett inn
-
+ ApplyAnvend
-
+ Additional StylingAndre innstillinger
-
-
-
+
+
+ CentreSentrer
-
-
-
+
+
+ Page BreakSideskift
@@ -4229,117 +4229,117 @@
_NewProjectForm
-
+ RequiredPåkrevd
-
+ OptionalValgfritt
-
+ Create a fresh projectOpprett et nytt prosjekt
-
+ Create an example projectOpprett et eksempelprosjekt
-
+ Copy an existing projectKopier et eksisterende prosjekt
-
+ Project NameProsjektnavn
-
+ AuthorForfatter
-
+ Project PathFilbane
-
+ Prefill ProjectForhåndsfyll prosjektet
-
+ Set to 0 to only add scenesSatt til 0 for bare å legge til scener
-
+ Add {0} chapter documentsLegg til {0} kapitteldokumenter
-
+ Add {0} scene documents (to each chapter)Legg til {0} scenedokumenter (til hvert kapittel)
-
+ Add a folder for plot notesLegg til en mappe for plott-notater
-
+ Add a folder for character notesLegg til en mappe for karakterer
-
+ Add a folder for location notesLegg til en mappe for lokasjoner
-
+ Add example notes to the aboveLag eksempelfiler til ovennevnte
-
+ Chapters and ScenesKapittel og scener
-
+ Project NotesProsjektnotater
-
+ Create New ProjectOpprett nytt prosjekt
-
+ Select Project FolderVelg prosjektmappe
-
+ Fresh ProjectNytt prosjekt
-
+ Example ProjectEksempelprosjekt
-
+ Template: {0}Mal: {0}
@@ -4347,7 +4347,7 @@
_NewProjectPage
-
+ A project name is required.Vennligst oppgi et prosjektnavn.
@@ -4355,27 +4355,27 @@
_OpenProjectPage
-
+ The project path is not reachable.Prosjektets bane er ikke tilgjengelig.
-
+ PathFilbane
-
+ Remove '{0}' from the recent projects list? The project files will not be deleted.Vil du fjerne {0} fra listen over tidligere åpnede prosjekter? Selve prosjektfilene blir ikke slettet.
-
+ Open ProjectÅpne prosjekt
-
+ Remove ProjectFjern prosjekt
@@ -4383,54 +4383,54 @@
_OverviewPage
-
+ ProjectProsjekt
-
-
+
+ NameNavn
-
+ RevisionsRevisjoner
-
+ Editing TimeRedigeringstid
-
-
+
+ Word CountAntall ord
-
+ In NovelsI romaner
-
+ In NotesI notater
-
+ Selected NovelVelg roman
-
+ ChaptersKapitler
-
+ ScenesScener
@@ -4438,27 +4438,27 @@
_PreviewWidget
-
+ Press the "Preview" button to generate ...Trykk på "Forhåndsvisning"-knappen for å generere ...
-
+ Processing ...Behandler ...
-
+ DoneFerdig
-
+ UnknownUkjent
-
+ BuiltBygget
@@ -4466,12 +4466,12 @@
_ProjectListModel
-
+ Word CountAntall ord
-
+ Last OpenedSist åpnet
@@ -4479,27 +4479,27 @@
_ReplacePage
-
+ Text Auto-Replace for Preview and BuildErstatt automatisk for forhåndsvisning og manuskript
-
+ KeywordNøkkelord
-
+ Replace WithErstatt med
-
+ Select item to editVelg enhet å redigere
-
+ SaveLagre
@@ -4507,49 +4507,49 @@
_SettingsPage
-
+ Project nameProsjektnavn
-
+ Changing this will affect the backup path.Å endring denne vil påvirke banen til sikkerhetskopier.
-
+ Author(s)Forfatter(e)
-
-
+
+ Only used when building the manuscript.Brukes kun ved bygging av manuskript.
-
+ Project languageProsjektets språk
-
+ DefaultIngen valg
-
+ Spell check languageSpråk for stavekontroll
-
-
+
+ Overrides main preferences.Overstyrer valg i innstillinger.
-
+ Disable backup on closeIkke lag sikkerhetskopi når prosjektet lukkes
@@ -4557,59 +4557,59 @@
_StatsWidget
-
-
+
+ WordsOrd
-
-
+
+ CharactersTegn
-
+ Words in HeadingsOrd i overskrifter
-
+ Words in TextOrd i tekst
-
+ HeadingsOverskrifter
-
+ ParagraphsAvsnitt
-
+ Characters in HeadingsTegn i overskrifter
-
+ Characters in TextTegn i tekst
-
+ Characters, No SpacesTegn, utenom mellomrom
-
+ Characters in Headings, No SpacesTegn i overskrifter, utenom mellomrom
-
+ Characters in Text, No SpacesTegn i tekst, utenom mellomrom
@@ -4617,67 +4617,67 @@
_StatusPage
-
+ Novel Document Status LevelsStatusnivåer i roman-filer
-
+ Project Note Importance LevelsViktighetsnivåer i notatfiler
-
+ LabelNavn
-
+ UsageBruk
-
+ Select item to editVelg enhet å redigere
-
+ ColourFarge
-
+ SaveLagre
-
+ Select ColourVelg farge
-
+ New ItemLegg til
-
+ Cannot delete a status item that is in use.Kan ikke slette status som er i bruk.
-
+ Not in useIkke i bruk
-
+ Used onceBrukt ett sted
-
+ Used by {0} itemsBrukt {0} steder
@@ -4685,128 +4685,128 @@
_TreeContextMenu
-
+ Empty TrashTøm papirkurven
-
+ RenameEndre navn
-
+ Open DocumentÅpne dokument
-
+ View DocumentVis dokument
-
+ Create New ...Opprett ny ...
-
+ Rename to HeadingEndre navn til overskriften
-
+ Set Active to ...Sett aktiv til ...
-
+ Toggle ActiveAktiver/deaktiver
-
+ Set Status to ...Sett status til ...
-
-
+
+ Manage Labels ...Administrer etiketter ...
-
+ Set Importance to ...Sett viktighetsnivå til ...
-
+ Transform ...Endre ...
-
-
-
-
+
+
+
+ Convert to {0}Konverter til {0}
-
+ Merge Child Items into SelfLim inn underelementer i dette dokumentet
-
+ Merge Child Items into NewLim inn underelementer i nytt dokument
-
+ Merge Documents in FolderSlå sammen dokumenter i mappen
-
+ Split Document by HeadingsSplitt dokumentet etter overskrifter
-
+ Expand AllUtvid alle
-
+ Collapse AllLukk alle
-
+ DuplicateDupliser
-
-
+
+ Delete PermanentlySlett permanent
-
-
+
+ Move to TrashFlytt til papirkurv
-
+ Move {0} items to Trash?Vil du flytte {0} filer til papirkurven?
-
+ Do you want to convert the folder to a {0}? This action cannot be reversed.Vil du konvertere mappen til et {0}? Denne handlingen kan ikke angres.
@@ -4814,7 +4814,7 @@
_UpdatableMenu
-
+ From TemplateFra mal
@@ -4822,12 +4822,12 @@
_ViewPanelBackRefs
-
+ DocumentDokument
-
+ First HeadingFørste overskrift
@@ -4835,27 +4835,27 @@
_ViewPanelKeyWords
-
+ TagKnagg
-
+ ImportanceViktighet
-
+ DocumentDokument
-
+ HeadingOverskrift
-
+ Short DescriptionKort beskrivelse
diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py
index 5bd809a8..02d5a82e 100644
--- a/novelwriter/__init__.py
+++ b/novelwriter/__init__.py
@@ -23,16 +23,21 @@ along with this program. If not, see .
"""
from __future__ import annotations
-import sys
import getopt
import logging
+import sys
+
+from typing import TYPE_CHECKING
from PyQt5.QtWidgets import QApplication, QErrorMessage
-from novelwriter.error import exceptionHandler, logException
from novelwriter.config import Config
+from novelwriter.error import exceptionHandler, logException
from novelwriter.shared import SharedData
+if TYPE_CHECKING: # pragma: no cover
+ from novelwriter.guimain import GuiMain
+
# Package Meta
# ============
@@ -42,9 +47,9 @@ __license__ = "GPLv3"
__author__ = "Veronica Berglyd Olsen"
__maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net"
-__version__ = "2.4rc1"
-__hexversion__ = "0x020400c1"
-__date__ = "2024-04-06"
+__version__ = "2.5a1"
+__hexversion__ = "0x020500a1"
+__date__ = "2024-04-13"
__status__ = "Stable"
__domain__ = "novelwriter.io"
@@ -60,7 +65,7 @@ CONFIG = Config()
SHARED = SharedData()
-def main(sysArgs: list | None = None):
+def main(sysArgs: list | None = None) -> GuiMain | None:
"""Parse command line, set up logging, and launch main GUI."""
if sysArgs is None:
sysArgs = sys.argv[1:]
@@ -239,4 +244,6 @@ def main(sysArgs: list | None = None):
sys.exit(nwApp.exec())
+ return None
+
# END Function main
diff --git a/novelwriter/common.py b/novelwriter/common.py
index c148e75d..ff36bae2 100644
--- a/novelwriter/common.py
+++ b/novelwriter/common.py
@@ -449,7 +449,7 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
indentations = ["\n"]
- def indentChildren(elem, level):
+ def indentChildren(elem: ET.Element, level: int) -> None:
chLevel = level + 1
try:
chIndent = indentations[chLevel]
@@ -472,6 +472,8 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
if last is not None:
last.tail = indentations[level]
+ return
+
if len(tree):
indentChildren(tree, 0)
tree.tail = "\n"
@@ -534,8 +536,9 @@ class NWConfigParser(ConfigParser):
helper functions, and support for lists.
"""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
+ return
def rdStr(self, section: str, option: str, default: str) -> str:
"""Read string value."""
diff --git a/novelwriter/config.py b/novelwriter/config.py
index 1f817e0f..e9fab918 100644
--- a/novelwriter/config.py
+++ b/novelwriter/config.py
@@ -390,6 +390,10 @@ class Config:
"""Un-scale fixed gui sizes by the screen scale factor."""
return int(value/self.guiScale)
+ def homePath(self) -> Path:
+ """The user's home folder."""
+ return self._homePath
+
def dataPath(self, target: str | None = None) -> Path:
"""Return a path in the data folder."""
if isinstance(target, str):
diff --git a/novelwriter/constants.py b/novelwriter/constants.py
index a1e2b84a..48487d77 100644
--- a/novelwriter/constants.py
+++ b/novelwriter/constants.py
@@ -25,7 +25,7 @@ from __future__ import annotations
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
-from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline
+from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline, nwStatusShape
def trConst(text: str) -> str:
@@ -268,6 +268,34 @@ class nwLabels:
nwBuildFmt.J_HTML: ".json",
nwBuildFmt.J_NWD: ".json",
}
+ SHAPES_PLAIN = {
+ nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
+ nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
+ nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
+ nwStatusShape.DIAMOND: QT_TRANSLATE_NOOP("Constant", "Diamond"),
+ nwStatusShape.PENTAGON: QT_TRANSLATE_NOOP("Constant", "Pentagon"),
+ nwStatusShape.HEXAGON: QT_TRANSLATE_NOOP("Constant", "Hexagon"),
+ nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
+ nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
+ }
+ SHAPES_CIRCLE = {
+ nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
+ nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
+ nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
+ nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
+ }
+ SHAPES_BARS = {
+ nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
+ nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
+ nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
+ nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
+ }
+ SHAPES_BLOCKS = {
+ nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
+ nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
+ nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
+ nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
+ }
FILE_FILTERS = {
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py
index 1bd03485..b68dd97d 100644
--- a/novelwriter/core/buildsettings.py
+++ b/novelwriter/core/buildsettings.py
@@ -56,13 +56,13 @@ SETTINGS_TEMPLATE = {
"headings.fmtChapter": (str, nwHeadFmt.TITLE),
"headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
"headings.fmtScene": (str, "* * *"),
- "headings.fmtHardScene": (str, ""),
+ "headings.fmtAltScene": (str, ""),
"headings.fmtSection": (str, ""),
"headings.hideTitle": (bool, False),
"headings.hideChapter": (bool, False),
"headings.hideUnnumbered": (bool, False),
"headings.hideScene": (bool, False),
- "headings.hideHardScene": (bool, False),
+ "headings.hideAltScene": (bool, False),
"headings.hideSection": (bool, True),
"headings.centerTitle": (bool, True),
"headings.centerChapter": (bool, False),
@@ -110,7 +110,7 @@ SETTINGS_LABELS = {
"headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Format"),
"headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Format"),
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"),
- "headings.fmtHardScene": QT_TRANSLATE_NOOP("Builds", "Hard Scene Format"),
+ "headings.fmtAltScene": QT_TRANSLATE_NOOP("Builds", "Alt. Scene Format"),
"headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Format"),
"text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
@@ -178,7 +178,7 @@ class BuildSettings:
def __init__(self) -> None:
self._name = ""
self._uuid = str(uuid.uuid4())
- self._path = Path.home()
+ self._path = CONFIG.homePath()
self._build = ""
self._order = 0
self._format = nwBuildFmt.ODT
@@ -220,7 +220,7 @@ class BuildSettings:
"""The last used build path."""
if self._path.is_dir():
return self._path
- return Path.home()
+ return CONFIG.homePath()
@property
def lastBuildName(self) -> str:
@@ -297,7 +297,7 @@ class BuildSettings:
if isinstance(path, Path) and path.is_dir():
self._path = path
else:
- self._path = Path.home()
+ self._path = CONFIG.homePath()
self._changed = True
return
@@ -382,7 +382,7 @@ class BuildSettings:
postponed = []
- def allowRoot(rHandle):
+ def allowRoot(rHandle: str | None) -> None:
if rHandle in postponed and rHandle in result and rHandle is not None:
result[rHandle] = (True, FilterMode.ROOT)
postponed.remove(rHandle)
diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py
index b8fc5ba1..925db901 100644
--- a/novelwriter/core/coretools.py
+++ b/novelwriter/core/coretools.py
@@ -104,7 +104,7 @@ class DocMerger:
docText = self._project.storage.getDocumentText(srcHandle).rstrip("\n")
if addComment:
docInfo = srcItem.describeMe()
- docSt, _ = srcItem.getImportStatus(incIcon=False)
+ docSt, _ = srcItem.getImportStatus()
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
docText = cmtLine + docText
diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py
index 3c1d6951..0c2ab461 100644
--- a/novelwriter/core/docbuild.py
+++ b/novelwriter/core/docbuild.py
@@ -306,8 +306,8 @@ class NWBuildDocument:
self._build.getBool("headings.hideScene")
)
bldObj.setHardSceneFormat(
- self._build.getStr("headings.fmtHardScene"),
- self._build.getBool("headings.hideHardScene")
+ self._build.getStr("headings.fmtAltScene"),
+ self._build.getBool("headings.hideAltScene")
)
bldObj.setSectionFormat(
self._build.getStr("headings.fmtSection"),
diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py
index a52b8a36..35dba0fe 100644
--- a/novelwriter/core/item.py
+++ b/novelwriter/core/item.py
@@ -25,7 +25,7 @@ from __future__ import annotations
import logging
-from typing import TYPE_CHECKING, Any, Literal, overload
+from typing import TYPE_CHECKING, Any
from PyQt5.QtGui import QIcon
@@ -308,25 +308,15 @@ class NWItem:
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
- @overload # pragma: no cover
- def getImportStatus(self, incIcon: Literal[True] = True) -> tuple[str, QIcon]:
- pass
-
- @overload # pragma: no cover
- def getImportStatus(self, incIcon: Literal[False]) -> tuple[str, None]:
- pass
-
- def getImportStatus(self, incIcon=True):
+ def getImportStatus(self) -> tuple[str, QIcon]:
"""Return the relevant importance or status label and icon for
the current item based on its class.
"""
if self.isNovelLike():
- stName = self._project.data.itemStatus.name(self._status)
- stIcon = self._project.data.itemStatus.icon(self._status) if incIcon else None
+ entry = self._project.data.itemStatus[self._status]
else:
- stName = self._project.data.itemImport.name(self._import)
- stIcon = self._project.data.itemImport.icon(self._import) if incIcon else None
- return stName, stIcon
+ entry = self._project.data.itemImport[self._import]
+ return entry.name, entry.icon
##
# Checker Methods
diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py
index 4a399c24..08a3dd11 100644
--- a/novelwriter/core/project.py
+++ b/novelwriter/core/project.py
@@ -26,33 +26,32 @@ from __future__ import annotations
import json
import logging
+from collections.abc import Iterable
from enum import Enum
+from functools import partial
+from pathlib import Path
from time import time
from typing import TYPE_CHECKING
-from pathlib import Path
-from functools import partial
-from collections.abc import Iterable
from PyQt5.QtCore import QCoreApplication
from novelwriter import CONFIG, SHARED, __version__, __hexversion__
-from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
-from novelwriter.error import logException
-from novelwriter.constants import trConst, nwLabels
-from novelwriter.core.tree import NWTree
-from novelwriter.core.index import NWIndex
-from novelwriter.core.options import OptionState
-from novelwriter.core.storage import NWStorage, NWStorageOpen
-from novelwriter.core.sessions import NWSessionLog
-from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
-from novelwriter.core.projectdata import NWProjectData
from novelwriter.common import (
checkStringNone, formatInt, formatTimeStamp, getFileSize, hexToInt, makeFileNameSafe, minmax
)
+from novelwriter.constants import trConst, nwLabels
+from novelwriter.core.index import NWIndex
+from novelwriter.core.options import OptionState
+from novelwriter.core.projectdata import NWProjectData
+from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
+from novelwriter.core.sessions import NWSessionLog
+from novelwriter.core.storage import NWStorage, NWStorageOpen
+from novelwriter.core.tree import NWTree
+from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
+from novelwriter.error import logException
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.item import NWItem
- from novelwriter.core.status import NWStatus
logger = logging.getLogger(__name__)
@@ -461,14 +460,14 @@ class NWProject:
def setDefaultStatusImport(self) -> None:
"""Set the default status and importance values."""
- self._data.itemStatus.write(None, self.tr("New"), (100, 100, 100))
- self._data.itemStatus.write(None, self.tr("Note"), (200, 50, 0))
- self._data.itemStatus.write(None, self.tr("Draft"), (200, 150, 0))
- self._data.itemStatus.write(None, self.tr("Finished"), (50, 200, 0))
- self._data.itemImport.write(None, self.tr("New"), (100, 100, 100))
- self._data.itemImport.write(None, self.tr("Minor"), (200, 50, 0))
- self._data.itemImport.write(None, self.tr("Major"), (200, 150, 0))
- self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0))
+ self._data.itemStatus.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
+ self._data.itemStatus.add(None, self.tr("Note"), (200, 50, 0), "SQUARE", 0)
+ self._data.itemStatus.add(None, self.tr("Draft"), (200, 150, 0), "SQUARE", 0)
+ self._data.itemStatus.add(None, self.tr("Finished"), (50, 200, 0), "SQUARE", 0)
+ self._data.itemImport.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
+ self._data.itemImport.add(None, self.tr("Minor"), (200, 50, 0), "SQUARE", 0)
+ self._data.itemImport.add(None, self.tr("Major"), (200, 150, 0), "SQUARE", 0)
+ self._data.itemImport.add(None, self.tr("Main"), (50, 200, 0), "SQUARE", 0)
return
def setProjectLang(self, language: str | None) -> None:
@@ -491,14 +490,6 @@ class NWProject:
self.setProjectChanged(True)
return
- def setStatusColours(self, new: list[dict], deleted: list[str]) -> bool:
- """Update the list of novel file status flags."""
- return self._setStatusImport(new, deleted, self._data.itemStatus)
-
- def setImportColours(self, new: list[dict], deleted: list[str]) -> bool:
- """Update the list of note file importance flags."""
- return self._setStatusImport(new, deleted, self._data.itemImport)
-
def setProjectChanged(self, status: bool) -> bool:
"""Toggle the project changed flag, and propagate the
information to the GUI statusbar.
@@ -584,28 +575,6 @@ class NWProject:
# Internal Functions
##
- def _setStatusImport(self, new: list[dict], delete: list[str], target: NWStatus) -> bool:
- """Update the list of novel file status or importance flags, and
- delete those that have been requested deleted.
- """
- if not (new or delete):
- return False
-
- order = []
- for entry in new:
- key = entry.get("key", None)
- name = entry.get("name", "")
- cols = entry.get("cols", (100, 100, 100))
- if name:
- order.append(target.write(key, name, cols))
-
- for key in delete:
- target.remove(key)
-
- target.reorder(order)
-
- return True
-
def _loadProjectLocalisation(self) -> bool:
"""Load the language data for the current project language."""
if self._data.language is None or CONFIG._nwLangPath is None:
diff --git a/novelwriter/core/projectxml.py b/novelwriter/core/projectxml.py
index c99c8abf..ac28ed4a 100644
--- a/novelwriter/core/projectxml.py
+++ b/novelwriter/core/projectxml.py
@@ -46,7 +46,7 @@ if TYPE_CHECKING: # pragma: no cover
logger = logging.getLogger(__name__)
FILE_VERSION = "1.5" # The current project file format version
-FILE_REVISION = "3" # The current project file format revision
+FILE_REVISION = "4" # The current project file format revision
HEX_VERSION = 0x0105
NUM_VERSION = {
@@ -109,6 +109,8 @@ class ProjectXMLReader:
Rev 2: Drops the title node from project and adds the TEMPLATE
class for items. 2.3 Beta 1.
Rev 3: Added TEMPLATE class. 2.3.
+ Rev 4: Added shape attribute to status and importance entry
+ nodes. 2.5.
"""
def __init__(self, path: str | Path) -> None:
@@ -356,8 +358,8 @@ class ProjectXMLReader:
logger.debug("Parsing section (legacy format)")
# Create maps to look up name -> key for status and importance
- statusMap = {entry.get("name"): key for key, entry in data.itemStatus.items()}
- importMap = {entry.get("name"): key for key, entry in data.itemImport.items()}
+ sMap: dict[str | None, str] = {e.name: k for k, e in data.itemStatus.iterItems()}
+ iMap: dict[str | None, str] = {e.name: k for k, e in data.itemImport.iterItems()}
for xItem in xSection:
if xItem.tag != "item":
@@ -404,9 +406,9 @@ class ProjectXMLReader:
# Status was split into separate status/import with a key in 1.4
if item.get("class", "") in ("NOVEL", "ARCHIVE"):
- name["status"] = statusMap.get(tmpStatus, None)
+ name["status"] = sMap.get(tmpStatus, None)
else:
- name["import"] = importMap.get(tmpStatus, None)
+ name["import"] = iMap.get(tmpStatus, None)
# A number of layouts were removed in 1.3
if item.get("layout", "") in (
@@ -436,7 +438,8 @@ class ProjectXMLReader:
green = checkInt(xEntry.attrib.get("green", 0), 0)
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
count = checkInt(xEntry.attrib.get("count", 0), 0)
- sObject.write(key, xEntry.text or "", (red, green, blue), count)
+ shape = xEntry.attrib.get("shape", "")
+ sObject.add(key, xEntry.text or "", (red, green, blue), shape, count)
return
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
@@ -449,7 +452,7 @@ class ProjectXMLReader:
result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
return result
- def _parseDictTagText(self, xItem) -> dict:
+ def _parseDictTagText(self, xItem: ET.Element) -> dict:
"""Parse a dictionary stored with key as the tag and the value
as the text property.
"""
diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py
index 51804d83..6dc47785 100644
--- a/novelwriter/core/spellcheck.py
+++ b/novelwriter/core/spellcheck.py
@@ -74,7 +74,7 @@ class NWSpellEnchant:
# Setters
##
- def setLanguage(self, language: str | None):
+ def setLanguage(self, language: str | None) -> None:
"""Load a dictionary for the language specified in the config.
If that fails, we load a mock dictionary so that lookups don't
crash. Note that enchant will allow loading an empty string as
@@ -182,10 +182,10 @@ class FakeEnchant:
def check(self, word: str) -> bool:
return True
- def suggest(self, word) -> list[str]:
+ def suggest(self, word: str) -> list[str]:
return []
- def add_to_session(self, word: str):
+ def add_to_session(self, word: str) -> None:
return
# END Class FakeEnchant
diff --git a/novelwriter/core/status.py b/novelwriter/core/status.py
index 554bdeed..6b85ccec 100644
--- a/novelwriter/core/status.py
+++ b/novelwriter/core/status.py
@@ -24,17 +24,19 @@ along with this program. If not, see .
"""
from __future__ import annotations
-import random
+import dataclasses
import logging
+import random
-from typing import TYPE_CHECKING, Literal
-from collections.abc import ItemsView, Iterable, Iterator, KeysView, ValuesView
+from collections.abc import Iterable
+from typing import TYPE_CHECKING
-from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
-from PyQt5.QtCore import QRectF
+from PyQt5.QtCore import QPointF, Qt
+from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor, QPolygonF
-from novelwriter import CONFIG
-from novelwriter.common import minmax, simplified
+from novelwriter import SHARED
+from novelwriter.common import simplified
+from novelwriter.enum import nwStatusShape
from novelwriter.types import QtPaintAnitAlias, QtTransparent
if TYPE_CHECKING: # pragma: no cover
@@ -43,83 +45,94 @@ if TYPE_CHECKING: # pragma: no cover
logger = logging.getLogger(__name__)
+@dataclasses.dataclass
+class StatusEntry:
+
+ name: str
+ color: QColor
+ shape: nwStatusShape
+ icon: QIcon
+ count: int = 0
+
+ @classmethod
+ def duplicate(cls, source: StatusEntry) -> StatusEntry:
+ """Create a deep copy of the source object."""
+ cls = dataclasses.replace(source)
+ cls.color = QColor(source.color)
+ cls.icon = QIcon(source.icon)
+ return cls
+
+# END Class StatusEntry
+
+
+NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
+
+
class NWStatus:
- STATUS = 1
- IMPORT = 2
+ STATUS = "s"
+ IMPORT = "i"
- def __init__(self, kind: Literal[1, 2]) -> None:
+ __slots__ = ("_store", "_default", "_prefix", "_height")
- self._type = kind
- self._store = {}
+ def __init__(self, prefix: str) -> None:
+ self._store: dict[str, StatusEntry] = {}
self._default = None
-
- self._iPX = CONFIG.pxInt(24)
-
- pA = CONFIG.pxInt(2)
- pB = CONFIG.pxInt(20)
- pR = float(CONFIG.pxInt(4))
- self._iconPath = QPainterPath()
- self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR)
-
- self._defaultIcon = self._createIcon(100, 100, 100)
-
- if self._type == self.STATUS:
- self._prefix = "s"
- elif self._type == self.IMPORT:
- self._prefix = "i"
- else:
- raise Exception("This is a bug!")
-
+ self._prefix = prefix[:1]
+ self._height = SHARED.theme.baseIconHeight
return
- def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str:
+ def __len__(self) -> int:
+ return len(self._store)
+
+ def __getitem__(self, key: str | None) -> StatusEntry:
+ """Return the entry associated with a given key."""
+ if key and key in self._store:
+ return self._store[key]
+ elif self._default is not None:
+ return self._store[self._default]
+ return NO_ENTRY
+
+ ##
+ # Methods
+ ##
+
+ def add(self, key: str | None, name: str, color: tuple[int, int, int],
+ shape: str, count: int) -> str:
"""Add or update a status entry. If the key is invalid, a new
key is generated.
"""
- if not self._isKey(key):
- key = self._newKey()
- if not isinstance(col, tuple):
- col = (100, 100, 100)
- if len(col) != 3:
- col = (100, 100, 100)
+ if isinstance(color, tuple) and len(color) == 3:
+ qColor = QColor(*color)
+ else:
+ qColor = QColor(100, 100, 100)
- cR = minmax(col[0], 0, 255)
- cG = minmax(col[1], 0, 255)
- cB = minmax(col[2], 0, 255)
+ try:
+ iShape = nwStatusShape[shape]
+ except KeyError:
+ iShape = nwStatusShape.SQUARE
+
+ key = self._checkKey(key)
name = simplified(name)
- if count is None:
- count = self._store.get(key, {}).get("count", 0)
-
- self._store[key] = {
- "name": name,
- "icon": self._createIcon(cR, cG, cB),
- "cols": (cR, cG, cB),
- "count": count,
- }
+ icon = self.createIcon(self._height, qColor, iShape)
+ self._store[key] = StatusEntry(name, qColor, iShape, icon, count)
if self._default is None:
self._default = key
return key
- def remove(self, key: str) -> bool:
- """Remove an entry in the list, except if the count > 0."""
- if key not in self._store:
- return False
- if self._store[key]["count"] > 0:
- return False
+ def update(self, update: list[tuple[str | None, StatusEntry]]) -> None:
+ """Update the list of statuses, and from removed list."""
+ self._store.clear()
+ for key, entry in update:
+ self._store[self._checkKey(key)] = entry
- del self._store[key]
+ # Check if we need a new default
+ if self._default not in self._store:
+ self._default = next(iter(self._store)) if self._store else None
- keys = list(self._store.keys())
- if key == self._default:
- if len(keys) > 0:
- self._default = keys[0]
- else:
- self._default = None
-
- return True
+ return
def check(self, value: str) -> str:
"""Check the key against the stored status names."""
@@ -129,93 +142,51 @@ class NWStatus:
return self._default
return ""
- def name(self, key: str | None) -> str:
- """Return the name associated with a given key."""
- if key and key in self._store:
- return self._store[key]["name"]
- elif self._default is not None:
- return self._store[self._default]["name"]
- return ""
-
- def cols(self, key: str | None) -> tuple[int, int, int]:
- """Return the colours associated with a given key."""
- if key and key in self._store:
- return self._store[key]["cols"]
- elif self._default is not None:
- return self._store[self._default]["cols"]
- return 100, 100, 100
-
- def count(self, key: str | None) -> int:
- """Return the count associated with a given key."""
- if key and key in self._store:
- return self._store[key]["count"]
- elif self._default is not None:
- return self._store[self._default]["count"]
- return 0
-
- def icon(self, key: str | None) -> QIcon:
- """Return the icon associated with a given key."""
- if key and key in self._store:
- return self._store[key]["icon"]
- elif self._default is not None:
- return self._store[self._default]["icon"]
- return self._defaultIcon
-
- def reorder(self, order: list[str]) -> bool:
- """Reorder the items according to list."""
- if len(order) != len(self._store):
- logger.error("Length mismatch between new and old order")
- return False
-
- if order == list(self._store.keys()):
- return False
-
- store = {}
- for key in order:
- if key in self._store:
- store[key] = self._store[key]
- else:
- logger.error("Unknown key '%s' in order", key)
- return False
-
- self._store = store
-
- return True
-
def resetCounts(self) -> None:
"""Clear the counts of references to the status entries."""
- for key in self._store:
- self._store[key]["count"] = 0
+ for entry in self._store.values():
+ entry.count = 0
return
def increment(self, key: str | None) -> None:
"""Increment the counter for a given entry."""
if key and key in self._store:
- self._store[key]["count"] += 1
+ self._store[key].count += 1
return
def pack(self) -> Iterable[tuple[str, dict]]:
"""Pack the status entries into a dictionary."""
- for key, data in self._store.items():
- yield (data["name"], {
+ for key, entry in self._store.items():
+ yield (entry.name, {
"key": key,
- "count": str(data["count"]),
- "red": str(data["cols"][0]),
- "green": str(data["cols"][1]),
- "blue": str(data["cols"][2]),
+ "count": str(entry.count),
+ "red": str(entry.color.red()),
+ "green": str(entry.color.green()),
+ "blue": str(entry.color.blue()),
+ "shape": entry.shape.name,
})
return
- def unpack(self, data: dict) -> None:
- """Unpack a data dictionary and set the class values."""
- self._store = {}
- self._default = None
- for key, entry in data.items():
- label = entry.get("label", "")
- colour = entry.get("colour", (100, 100, 100))
- count = entry.get("count", 0)
- self.write(key, label, colour, count)
- return
+ def iterItems(self) -> Iterable[tuple[str, StatusEntry]]:
+ """Yield entries from the status icons."""
+ yield from self._store.items()
+
+ @staticmethod
+ def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
+ """Generate an icon for a status label."""
+ pixmap = QPixmap(48, 48)
+ pixmap.fill(QtTransparent)
+
+ painter = QPainter(pixmap)
+ painter.setRenderHint(QtPaintAnitAlias)
+ painter.fillPath(_SHAPES.getShape(shape), color)
+ painter.end()
+
+ return QIcon(pixmap.scaled(
+ height, height,
+ Qt.AspectRatioMode.IgnoreAspectRatio,
+ Qt.TransformationMode.SmoothTransformation
+ ))
##
# Internal Functions
@@ -246,38 +217,120 @@ class NWStatus:
return False
return True
- def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
- """Generate an icon for a status label."""
- pixmap = QPixmap(self._iPX, self._iPX)
- pixmap.fill(QtTransparent)
-
- painter = QPainter(pixmap)
- painter.setRenderHint(QtPaintAnitAlias)
- painter.fillPath(self._iconPath, QColor(red, green, blue))
- painter.end()
-
- return QIcon(pixmap)
-
- ##
- # Iterator Bits
- ##
-
- def __len__(self) -> int:
- return len(self._store)
-
- def __getitem__(self, key: str) -> dict:
- return self._store[key]
-
- def __iter__(self) -> Iterator[dict]:
- return iter(self._store)
-
- def keys(self) -> KeysView[str]:
- return self._store.keys()
-
- def items(self) -> ItemsView[str, dict]:
- return self._store.items()
-
- def values(self) -> ValuesView[dict]:
- return self._store.values()
+ def _checkKey(self, key: str | None) -> str:
+ """Check key is valid, and if not, generate one."""
+ return key if self._isKey(key) else self._newKey()
# END Class NWStatus
+
+
+class _ShapeCache:
+
+ def __init__(self) -> None:
+ self._cache: dict[nwStatusShape, QPainterPath] = {}
+ return
+
+ def getShape(self, shape: nwStatusShape) -> QPainterPath:
+ """Return a painter shape for an icon."""
+ if shape in self._cache:
+ return self._cache[shape]
+
+ path = QPainterPath()
+ if shape == nwStatusShape.SQUARE:
+ path.addRoundedRect(2.0, 2.0, 44.0, 44.0, 4.0, 4.0)
+ elif shape == nwStatusShape.TRIANGLE:
+ path.addPolygon(QPolygonF([
+ QPointF(24.00, 3.00),
+ QPointF(43.92, 37.50),
+ QPointF(4.08, 37.50),
+ ]))
+ elif shape == nwStatusShape.NABLA:
+ path.addPolygon(QPolygonF([
+ QPointF(24.00, 48.00),
+ QPointF(4.08, 14.50),
+ QPointF(43.92, 14.50),
+ ]))
+ elif shape == nwStatusShape.DIAMOND:
+ path.addPolygon(QPolygonF([
+ QPointF(24.00, 2.00),
+ QPointF(44.00, 24.00),
+ QPointF(24.00, 46.00),
+ QPointF(4.00, 24.00),
+ ]))
+ elif shape == nwStatusShape.PENTAGON:
+ path.addPolygon(QPolygonF([
+ QPointF(24.00, 1.50),
+ QPointF(45.87, 17.39),
+ QPointF(37.52, 43.11),
+ QPointF(10.48, 43.11),
+ QPointF(2.13, 17.39),
+ ]))
+ elif shape == nwStatusShape.HEXAGON:
+ path.addPolygon(QPolygonF([
+ QPointF(24.00, 1.50),
+ QPointF(43.92, 13.00),
+ QPointF(43.92, 36.00),
+ QPointF(24.00, 47.50),
+ QPointF(4.08, 36.00),
+ QPointF(4.08, 13.00),
+ ]))
+ elif shape == nwStatusShape.STAR:
+ path.addPolygon(QPolygonF([
+ QPointF(24.00, 0.50), QPointF(31.05, 14.79),
+ QPointF(46.83, 17.08), QPointF(35.41, 28.21),
+ QPointF(38.11, 43.92), QPointF(24.00, 36.50),
+ QPointF(9.89, 43.92), QPointF(12.59, 28.21),
+ QPointF(1.17, 17.08), QPointF(15.37, 16.16),
+ ]))
+ elif shape == nwStatusShape.PACMAN:
+ path.moveTo(24.0, 24.0)
+ path.arcTo(2.0, 2.0, 44.0, 44.0, 40.0, 280.0)
+ elif shape == nwStatusShape.CIRCLE_Q:
+ path.moveTo(24.0, 24.0)
+ path.arcTo(2.0, 2.0, 44.0, 44.0, 0.0, 90.0)
+ elif shape == nwStatusShape.CIRCLE_H:
+ path.moveTo(24.0, 24.0)
+ path.arcTo(2.0, 2.0, 44.0, 44.0, -90.0, 180.0)
+ elif shape == nwStatusShape.CIRCLE_T:
+ path.moveTo(24.0, 24.0)
+ path.arcTo(2.0, 2.0, 44.0, 44.0, -180.0, 270.0)
+ elif shape == nwStatusShape.CIRCLE:
+ path.addEllipse(2.0, 2.0, 44.0, 44.0)
+ elif shape == nwStatusShape.BARS_1:
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BARS_2:
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BARS_3:
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ path.addRoundedRect(26.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BARS_4:
+ path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ path.addRoundedRect(26.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ path.addRoundedRect(38.0, 2.0, 8.0, 44.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BLOCK_1:
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BLOCK_2:
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BLOCK_3:
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ path.addRoundedRect(2.0, 24.0, 20.0, 20.0, 4.0, 4.0)
+ path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ elif shape == nwStatusShape.BLOCK_4:
+ path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ path.addRoundedRect(2.0, 24.0, 20.0, 20.0, 4.0, 4.0)
+ path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
+ path.addRoundedRect(24.0, 24.0, 20.0, 20.0, 4.0, 4.0)
+
+ self._cache[shape] = path
+
+ return path
+
+# END Class _ShapeCache
+
+
+# Create Singleton
+_SHAPES = _ShapeCache()
diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py
index abb6fc28..d8c9861e 100644
--- a/novelwriter/core/tokenizer.py
+++ b/novelwriter/core/tokenizer.py
@@ -49,7 +49,7 @@ ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": ""
RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL)
-def stripEscape(text) -> str:
+def stripEscape(text: str) -> str:
"""Strip escaped Markdown characters from paragraph text."""
if "\\" in text:
return RX_ESC.sub(lambda x: ESCAPES[x.group(0)], text)
@@ -639,8 +639,8 @@ class Tokenizer(ABC):
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("### ", "###! ")):
- # (Hard) Scene Headings
- # =====================
+ # (Alternative) Scene Headings
+ # ============================
# Scene headings in novel documents are treated as centred
# separators if the formatting does not change the text. If the
# format is empty, the scene can be hidden or a blank paragraph
diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py
index 2801fc5b..22418f32 100644
--- a/novelwriter/core/toodt.py
+++ b/novelwriter/core/toodt.py
@@ -548,7 +548,7 @@ class ToOdt(Tokenizer):
oVers = _mkTag("office", "version")
xSett = ET.Element(oRoot, attrib={oVers: X_VERS})
- def putInZip(name, xObj, zipObj):
+ def putInZip(name: str, xObj: ET.Element, zipObj: ZipFile) -> None:
with zipObj.open(name, mode="w") as fObj:
xml = ET.ElementTree(xObj)
xml.write(fObj, encoding="utf-8", xml_declaration=True)
diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py
index 7d1e4671..a8900786 100644
--- a/novelwriter/dialogs/preferences.py
+++ b/novelwriter/dialogs/preferences.py
@@ -795,7 +795,7 @@ class GuiPreferences(QDialog):
return
@pyqtSlot()
- def _selectTextFont(self):
+ def _selectTextFont(self) -> None:
"""Open the QFontDialog and set a font for the font style."""
current = QFont()
current.setFamily(CONFIG.textFont)
diff --git a/novelwriter/dialogs/projectsettings.py b/novelwriter/dialogs/projectsettings.py
index 4e234f76..23bb43da 100644
--- a/novelwriter/dialogs/projectsettings.py
+++ b/novelwriter/dialogs/projectsettings.py
@@ -27,20 +27,26 @@ from __future__ import annotations
import logging
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
-from PyQt5.QtGui import QCloseEvent, QColor, QIcon, QPixmap
+from PyQt5.QtGui import QCloseEvent, QColor
from PyQt5.QtWidgets import (
- QApplication, QColorDialog, QDialog, QDialogButtonBox, QHBoxLayout,
- QLineEdit, QPushButton, QStackedWidget, QTreeWidget, QTreeWidgetItem,
- QVBoxLayout, QWidget
+ QAbstractItemView, QApplication, QColorDialog, QDialog, QDialogButtonBox, QHBoxLayout,
+ QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget,
+ QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import simplified
+from novelwriter.constants import nwLabels, trConst
+from novelwriter.core.status import NWStatus, StatusEntry
+from novelwriter.enum import nwStatusShape
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
from novelwriter.extensions.modified import NComboBox, NIconToolButton
from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch
-from novelwriter.types import QtDialogCancel, QtDialogSave, QtUserRole
+from novelwriter.types import (
+ QtDialogCancel, QtDialogSave, QtSizeMinimum, QtSizeMinimumExpanding,
+ QtUserRole
+)
logger = logging.getLogger(__name__)
@@ -179,19 +185,19 @@ class GuiProjectSettings(QDialog):
rebuildTrees = False
- if self.statusPage.wasChanged:
- newList, delList = self.statusPage.getNewList()
- project.setStatusColours(newList, delList)
+ if self.statusPage.changed:
+ logger.debug("Updating status labels")
+ project.data.itemStatus.update(self.statusPage.getNewList())
rebuildTrees = True
- if self.importPage.wasChanged:
- newList, delList = self.importPage.getNewList()
- project.setImportColours(newList, delList)
+ if self.importPage.changed:
+ logger.debug("Updating importance labels")
+ project.data.itemImport.update(self.importPage.getNewList())
rebuildTrees = True
- if self.replacePage.wasChanged:
- newList = self.replacePage.getNewList()
- project.data.setAutoReplace(newList)
+ if self.replacePage.changed:
+ logger.debug("Updating auto-replace settings")
+ project.data.setAutoReplace(self.replacePage.getNewList())
self.newProjectSettingsReady.emit(rebuildTrees)
QApplication.processEvents()
@@ -257,17 +263,17 @@ class _SettingsPage(NScrollableForm):
)
# Project Language
+ projLang = data.language or CONFIG.guiLocale
self.projLang = NComboBox(self)
self.projLang.setMinimumWidth(xW)
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
self.projLang.addItem(language, tag)
+ self.projLang.setCurrentData(projLang, projLang)
self.addRow(
self.tr("Project language"), self.projLang,
self.tr("Only used when building the manuscript."),
stretch=(3, 2)
)
- if (idx := self.projLang.findData(data.language)) != -1:
- self.projLang.setCurrentIndex(idx)
# Spell Check Language
self.spellLang = NComboBox(self)
@@ -301,12 +307,12 @@ class _SettingsPage(NScrollableForm):
class _StatusPage(NFixedPage):
- COL_LABEL = 0
- COL_USAGE = 1
+ C_DATA = 0
+ C_LABEL = 0
+ C_USAGE = 1
- KEY_ROLE = QtUserRole
- COL_ROLE = QtUserRole + 1
- NUM_ROLE = QtUserRole + 2
+ D_KEY = QtUserRole
+ D_ENTRY = QtUserRole + 1
def __init__(self, parent: QWidget, isStatus: bool) -> None:
super().__init__(parent=parent)
@@ -325,12 +331,21 @@ class _StatusPage(NFixedPage):
)
self._changed = False
- self._colDeleted = []
- self._selColour = QColor(100, 100, 100)
+ self._color = QColor(100, 100, 100)
+ self._shape = nwStatusShape.SQUARE
+ self._icons = {}
- self.iPx = SHARED.theme.baseIconHeight
+ self._iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
- bSz = SHARED.theme.buttonIconSize
+ bPd = CONFIG.pxInt(4)
+
+ iColor = self.palette().text().color()
+
+ # Labels
+ self.trCountNone = self.tr("Not in use")
+ self.trCountOne = self.tr("Used once")
+ self.trCountMore = self.tr("Used by {0} items")
+ self.trSelColor = self.tr("Select Colour")
# Title
self.pageTitle = NColourLabel(
@@ -341,12 +356,14 @@ class _StatusPage(NFixedPage):
# List Box
self.listBox = QTreeWidget(self)
self.listBox.setHeaderLabels([self.tr("Label"), self.tr("Usage")])
- self.listBox.itemSelectionChanged.connect(self._selectedItem)
- self.listBox.setColumnWidth(self.COL_LABEL, wCol0)
+ self.listBox.setColumnWidth(self.C_LABEL, wCol0)
self.listBox.setIndentation(0)
+ self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+ self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
+ self.listBox.itemSelectionChanged.connect(self._selectionChanged)
- for key, entry in status.items():
- self._addItem(key, entry["name"], entry["cols"], entry["count"])
+ for key, entry in status.iterItems():
+ self._addItem(key, StatusEntry.duplicate(entry))
# List Controls
self.addButton = NIconToolButton(self, iSz, "add")
@@ -367,16 +384,43 @@ class _StatusPage(NFixedPage):
self.editName.setPlaceholderText(self.tr("Select item to edit"))
self.editName.setEnabled(False)
- self.colPixmap = QPixmap(self.iPx, self.iPx)
- self.colPixmap.fill(QColor(100, 100, 100))
- self.colButton = QPushButton(QIcon(self.colPixmap), self.tr("Colour"), self)
- self.colButton.setIconSize(bSz)
- self.colButton.setEnabled(False)
- self.colButton.clicked.connect(self._selectColour)
+ buttonStyle = (
+ f"QToolButton {{padding: 0 {bPd}px;}} "
+ "QToolButton::menu-indicator {image: none;}"
+ )
- self.saveButton = QPushButton(self.tr("Save"), self)
- self.saveButton.setEnabled(False)
- self.saveButton.clicked.connect(self._saveItem)
+ self.colorButton = NIconToolButton(self, iSz)
+ self.colorButton.setToolTip(self.tr("Colour"))
+ self.colorButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
+ self.colorButton.setStyleSheet(buttonStyle)
+ self.colorButton.setEnabled(False)
+ self.colorButton.clicked.connect(self._selectColour)
+
+ def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
+ for shape, label in items.items():
+ icon = NWStatus.createIcon(self._iPx, iColor, shape)
+ action = menu.addAction(icon, trConst(label))
+ action.triggered.connect(lambda _, shape=shape: self._selectShape(shape))
+ self._icons[shape] = icon
+
+ self.shapeMenu = QMenu(self)
+ buildMenu(self.shapeMenu, nwLabels.SHAPES_PLAIN)
+ buildMenu(self.shapeMenu.addMenu(self.tr("Circles ...")), nwLabels.SHAPES_CIRCLE)
+ buildMenu(self.shapeMenu.addMenu(self.tr("Bars ...")), nwLabels.SHAPES_BARS)
+ buildMenu(self.shapeMenu.addMenu(self.tr("Blocks ...")), nwLabels.SHAPES_BLOCKS)
+
+ self.shapeButton = NIconToolButton(self, iSz)
+ self.shapeButton.setMenu(self.shapeMenu)
+ self.shapeButton.setToolTip(self.tr("Shape"))
+ self.shapeButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
+ self.shapeButton.setStyleSheet(buttonStyle)
+ self.shapeButton.setEnabled(False)
+
+ self.applyButton = QToolButton(self)
+ self.applyButton.setText(self.tr("Apply"))
+ self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
+ self.applyButton.setEnabled(False)
+ self.applyButton.clicked.connect(self._applyChanges)
# Assemble
self.listControls = QVBoxLayout()
@@ -387,28 +431,30 @@ class _StatusPage(NFixedPage):
self.listControls.addStretch(1)
self.editBox = QHBoxLayout()
- self.editBox.addWidget(self.editName)
- self.editBox.addWidget(self.colButton)
- self.editBox.addWidget(self.saveButton)
+ self.editBox.addWidget(self.editName, 1)
+ self.editBox.addWidget(self.colorButton, 0)
+ self.editBox.addWidget(self.shapeButton, 0)
+ self.editBox.addWidget(self.applyButton, 0)
self.mainBox = QVBoxLayout()
- self.mainBox.addWidget(self.listBox)
- self.mainBox.addLayout(self.editBox)
+ self.mainBox.addWidget(self.listBox, 1)
+ self.mainBox.addLayout(self.editBox, 0)
self.innerBox = QHBoxLayout()
- self.innerBox.addLayout(self.mainBox)
- self.innerBox.addLayout(self.listControls)
+ self.innerBox.addLayout(self.mainBox, 1)
+ self.innerBox.addLayout(self.listControls, 0)
self.outerBox = QVBoxLayout()
- self.outerBox.addWidget(self.pageTitle)
- self.outerBox.addLayout(self.innerBox)
+ self.outerBox.addWidget(self.pageTitle, 0)
+ self.outerBox.addLayout(self.innerBox, 1)
self.setCentralLayout(self.outerBox)
+ self._setButtonIcons()
return
@property
- def wasChanged(self) -> bool:
+ def changed(self) -> bool:
"""The user changed these settings."""
return self._changed
@@ -416,20 +462,17 @@ class _StatusPage(NFixedPage):
# Methods
##
- def getNewList(self) -> tuple[list, list]:
+ def getNewList(self) -> list[tuple[str | None, StatusEntry]]:
"""Return list of entries."""
if self._changed:
- newList = []
+ update = []
for n in range(self.listBox.topLevelItemCount()):
- item = self.listBox.topLevelItem(n)
- if item is not None:
- newList.append({
- "key": item.data(self.COL_LABEL, self.KEY_ROLE),
- "name": item.text(self.COL_LABEL),
- "cols": item.data(self.COL_LABEL, self.COL_ROLE),
- })
- return newList, self._colDeleted
- return [], []
+ if item := self.listBox.topLevelItem(n):
+ key = item.data(self.C_DATA, self.D_KEY)
+ entry = item.data(self.C_DATA, self.D_ENTRY)
+ update.append((key, entry))
+ return update
+ return []
def columnWidth(self) -> int:
"""Return the size of the header column."""
@@ -442,124 +485,119 @@ class _StatusPage(NFixedPage):
@pyqtSlot()
def _selectColour(self) -> None:
"""Open a dialog to select the status icon colour."""
- if self._selColour is not None:
- newCol = QColorDialog.getColor(
- self._selColour, self, self.tr("Select Colour")
- )
- if newCol.isValid():
- self._selColour = newCol
- pixmap = QPixmap(self.iPx, self.iPx)
- pixmap.fill(newCol)
- self.colButton.setIcon(QIcon(pixmap))
- self.colButton.setIconSize(pixmap.rect().size())
+ if (color := QColorDialog.getColor(self._color, self, self.trSelColor)).isValid():
+ self._color = color
+ self._setButtonIcons()
return
@pyqtSlot()
def _newItem(self) -> None:
"""Create a new status item."""
- self._addItem(None, self.tr("New Item"), (100, 100, 100), 0)
+ color = QColor(100, 100, 100)
+ shape = nwStatusShape.SQUARE
+ icon = NWStatus.createIcon(self._iPx, color, shape)
+ self._addItem(None, StatusEntry(self.tr("New Item"), color, shape, icon, 0))
self._changed = True
return
@pyqtSlot()
def _delItem(self) -> None:
"""Delete a status item."""
- selItem = self._getSelectedItem()
- if isinstance(selItem, QTreeWidgetItem):
- iRow = self.listBox.indexOfTopLevelItem(selItem)
- if selItem.data(self.COL_LABEL, self.NUM_ROLE) > 0:
+ if item := self._getSelectedItem():
+ iRow = self.listBox.indexOfTopLevelItem(item)
+ entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
+ if entry.count > 0:
SHARED.error(self.tr("Cannot delete a status item that is in use."))
else:
self.listBox.takeTopLevelItem(iRow)
- self._colDeleted.append(selItem.data(self.COL_LABEL, self.KEY_ROLE))
self._changed = True
return
@pyqtSlot()
- def _saveItem(self) -> None:
+ def _applyChanges(self) -> None:
"""Save changes made to a status item."""
- selItem = self._getSelectedItem()
- if isinstance(selItem, QTreeWidgetItem):
- selItem.setText(self.COL_LABEL, simplified(self.editName.text()))
- selItem.setIcon(self.COL_LABEL, self.colButton.icon())
- selItem.setData(self.COL_LABEL, self.COL_ROLE, (
- self._selColour.red(), self._selColour.green(), self._selColour.blue()
- ))
+ if item := self._getSelectedItem():
+ entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
+
+ name = simplified(self.editName.text())
+ icon = NWStatus.createIcon(self._iPx, self._color, self._shape)
+
+ entry.name = name
+ entry.color = self._color
+ entry.shape = self._shape
+ entry.icon = icon
+
+ item.setText(self.C_LABEL, name)
+ item.setIcon(self.C_LABEL, icon)
+
self._changed = True
+
return
@pyqtSlot()
- def _selectedItem(self) -> None:
+ def _selectionChanged(self) -> None:
"""Extract the info of a selected item and populate the settings
boxes and button. If no item is selected, clear the form.
"""
- selItem = self._getSelectedItem()
- if isinstance(selItem, QTreeWidgetItem):
- cols = selItem.data(self.COL_LABEL, self.COL_ROLE)
- name = selItem.text(self.COL_LABEL)
- pixmap = QPixmap(self.iPx, self.iPx)
- pixmap.fill(QColor(*cols))
- self._selColour = QColor(*cols)
- self.editName.setText(name)
- self.colButton.setIcon(QIcon(pixmap))
+ if item := self._getSelectedItem():
+ entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
+ self._color = entry.color
+ self._shape = entry.shape
+ self._setButtonIcons()
+
+ self.editName.setText(entry.name)
self.editName.selectAll()
self.editName.setFocus()
+
self.editName.setEnabled(True)
- self.colButton.setEnabled(True)
- self.saveButton.setEnabled(True)
+ self.colorButton.setEnabled(True)
+ self.shapeButton.setEnabled(True)
+ self.applyButton.setEnabled(True)
+
else:
- pixmap = QPixmap(self.iPx, self.iPx)
- pixmap.fill(QColor(100, 100, 100))
- self._selColour = QColor(100, 100, 100)
+ self._color = QColor(100, 100, 100)
+ self._shape = nwStatusShape.SQUARE
+ self._setButtonIcons()
self.editName.setText("")
- self.colButton.setIcon(QIcon(pixmap))
+
self.editName.setEnabled(False)
- self.colButton.setEnabled(False)
- self.saveButton.setEnabled(False)
+ self.colorButton.setEnabled(False)
+ self.shapeButton.setEnabled(False)
+ self.applyButton.setEnabled(False)
return
##
# Internal Functions
##
- def _addItem(self, key: str | None, name: str,
- colour: tuple[int, int, int], count: int) -> None:
+ def _selectShape(self, shape: nwStatusShape) -> None:
+ """Set the current shape."""
+ self._shape = shape
+ self._setButtonIcons()
+ return
+
+ def _addItem(self, key: str | None, entry: StatusEntry) -> None:
"""Add a status item to the list."""
- pixmap = QPixmap(self.iPx, self.iPx)
- pixmap.fill(QColor(*colour))
-
item = QTreeWidgetItem()
- item.setText(self.COL_LABEL, name)
- item.setIcon(self.COL_LABEL, QIcon(pixmap))
- item.setData(self.COL_LABEL, self.KEY_ROLE, key)
- item.setData(self.COL_LABEL, self.COL_ROLE, colour)
- item.setData(self.COL_LABEL, self.NUM_ROLE, count)
- item.setText(self.COL_USAGE, self._usageString(count))
-
+ item.setText(self.C_LABEL, entry.name)
+ item.setIcon(self.C_LABEL, entry.icon)
+ item.setText(self.C_USAGE, self._usageString(entry.count))
+ item.setData(self.C_DATA, self.D_KEY, key)
+ item.setData(self.C_DATA, self.D_ENTRY, entry)
self.listBox.addTopLevelItem(item)
-
return
def _moveItem(self, step: int) -> None:
"""Move and item up or down step."""
- selItem = self._getSelectedItem()
- if selItem is None:
- return
-
- tIndex = self.listBox.indexOfTopLevelItem(selItem)
- nChild = self.listBox.topLevelItemCount()
- nIndex = tIndex + step
- if nIndex < 0 or nIndex >= nChild:
- return
-
- cItem = self.listBox.takeTopLevelItem(tIndex)
- self.listBox.insertTopLevelItem(nIndex, cItem)
- self.listBox.clearSelection()
-
- if cItem is not None:
- cItem.setSelected(True)
- self._changed = True
-
+ if item := self._getSelectedItem():
+ tIdx = self.listBox.indexOfTopLevelItem(item)
+ nItm = self.listBox.topLevelItemCount()
+ nIdx = tIdx + step
+ if (0 <= nIdx < nItm) and (cItem := self.listBox.takeTopLevelItem(tIdx)):
+ self.listBox.insertTopLevelItem(nIdx, cItem)
+ self.listBox.clearSelection()
+ cItem.setSelected(True)
+ self._changed = True
return
def _getSelectedItem(self) -> QTreeWidgetItem | None:
@@ -571,19 +609,26 @@ class _StatusPage(NFixedPage):
def _usageString(self, count: int) -> str:
"""Generate usage string."""
if count == 0:
- return self.tr("Not in use")
+ return self.trCountNone
elif count == 1:
- return self.tr("Used once")
+ return self.trCountOne
else:
- return self.tr("Used by {0} items").format(count)
+ return self.trCountMore.format(count)
+
+ def _setButtonIcons(self) -> None:
+ """Set the colour of the colour button."""
+ icon = NWStatus.createIcon(self._iPx, self._color, nwStatusShape.SQUARE)
+ self.colorButton.setIcon(icon)
+ self.shapeButton.setIcon(self._icons[self._shape])
+ return
# END Class _StatusPage
class _ReplacePage(NFixedPage):
- COL_KEY = 0
- COL_REPL = 1
+ C_KEY = 0
+ C_REPL = 1
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
@@ -605,15 +650,17 @@ class _ReplacePage(NFixedPage):
# List Box
self.listBox = QTreeWidget(self)
self.listBox.setHeaderLabels([self.tr("Keyword"), self.tr("Replace With")])
- self.listBox.setColumnWidth(self.COL_KEY, wCol0)
+ self.listBox.setColumnWidth(self.C_KEY, wCol0)
self.listBox.setIndentation(0)
- self.listBox.itemSelectionChanged.connect(self._selectedItem)
+ self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+ self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
+ self.listBox.itemSelectionChanged.connect(self._selectionChanged)
for aKey, aVal in SHARED.project.data.autoReplace.items():
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
self.listBox.addTopLevelItem(newItem)
- self.listBox.sortByColumn(self.COL_KEY, Qt.SortOrder.AscendingOrder)
+ self.listBox.sortByColumn(self.C_KEY, Qt.SortOrder.AscendingOrder)
self.listBox.setSortingEnabled(True)
# List Controls
@@ -633,8 +680,10 @@ class _ReplacePage(NFixedPage):
self.editValue.setEnabled(False)
self.editValue.setMaxLength(80)
- self.saveButton = QPushButton(self.tr("Save"), self)
- self.saveButton.clicked.connect(self._saveEntry)
+ self.applyButton = QToolButton(self)
+ self.applyButton.setText(self.tr("Apply"))
+ self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
+ self.applyButton.clicked.connect(self._applyChanges)
# Assemble
self.listControls = QVBoxLayout()
@@ -645,11 +694,11 @@ class _ReplacePage(NFixedPage):
self.editBox = QHBoxLayout()
self.editBox.addWidget(self.editKey, 4)
self.editBox.addWidget(self.editValue, 5)
- self.editBox.addWidget(self.saveButton, 0)
+ self.editBox.addWidget(self.applyButton, 0)
self.mainBox = QVBoxLayout()
- self.mainBox.addWidget(self.listBox)
- self.mainBox.addLayout(self.editBox)
+ self.mainBox.addWidget(self.listBox, 1)
+ self.mainBox.addLayout(self.editBox, 0)
self.innerBox = QHBoxLayout()
self.innerBox.addLayout(self.mainBox)
@@ -664,7 +713,7 @@ class _ReplacePage(NFixedPage):
return
@property
- def wasChanged(self) -> bool:
+ def changed(self) -> bool:
"""The user changed these settings."""
return self._changed
@@ -672,15 +721,13 @@ class _ReplacePage(NFixedPage):
# Methods
##
- def getNewList(self) -> dict:
+ def getNewList(self) -> dict[str, str]:
"""Extract the list from the widget."""
new = {}
for n in range(self.listBox.topLevelItemCount()):
- if tItem := self.listBox.topLevelItem(n):
- aKey = self._stripNotAllowed(tItem.text(0))
- aVal = tItem.text(1)
- if len(aKey) > 0:
- new[aKey] = aVal
+ if item := self.listBox.topLevelItem(n):
+ if key := self._stripNotAllowed(item.text(self.C_KEY)):
+ new[key] = item.text(self.C_REPL)
return new
def columnWidth(self) -> int:
@@ -692,51 +739,48 @@ class _ReplacePage(NFixedPage):
##
@pyqtSlot()
- def _selectedItem(self) -> None:
+ def _selectionChanged(self) -> None:
"""Extract the details from the selected item and populate the
edit form.
"""
- if selItem := self._getSelectedItem():
- editKey = self._stripNotAllowed(selItem.text(0))
- editVal = selItem.text(1)
- self.editKey.setText(editKey)
- self.editValue.setText(editVal)
+ if item := self._getSelectedItem():
+ self.editKey.setText(self._stripNotAllowed(item.text(self.C_KEY)))
+ self.editValue.setText(item.text(self.C_REPL))
self.editKey.setEnabled(True)
self.editValue.setEnabled(True)
self.editKey.selectAll()
self.editKey.setFocus()
+ else:
+ self.editKey.setText("")
+ self.editValue.setText("")
+ self.editKey.setEnabled(False)
+ self.editValue.setEnabled(False)
return
@pyqtSlot()
- def _saveEntry(self) -> None:
+ def _applyChanges(self) -> None:
"""Save the form data into the list widget."""
- if selItem := self._getSelectedItem():
- newKey = self.editKey.text()
- newVal = self.editValue.text()
- saveKey = self._stripNotAllowed(newKey)
- if len(saveKey) > 0 and len(newVal) > 0:
- selItem.setText(self.COL_KEY, "<%s>" % saveKey)
- selItem.setText(self.COL_REPL, newVal)
- self.editKey.clear()
- self.editValue.clear()
- self.editKey.setEnabled(False)
- self.editValue.setEnabled(False)
- self.listBox.clearSelection()
+ if item := self._getSelectedItem():
+ key = self._stripNotAllowed(self.editKey.text())
+ value = self.editValue.text()
+ if key and value:
+ item.setText(self.C_KEY, f"<{key}>")
+ item.setText(self.C_REPL, value)
self._changed = True
return
@pyqtSlot()
def _addEntry(self) -> None:
"""Add a new list entry."""
- saveKey = "" % (self.listBox.topLevelItemCount() + 1)
- self.listBox.addTopLevelItem(QTreeWidgetItem([saveKey, ""]))
+ key = f""
+ self.listBox.addTopLevelItem(QTreeWidgetItem([key, ""]))
return
@pyqtSlot()
def _delEntry(self) -> None:
"""Delete the selected entry."""
- if selItem := self._getSelectedItem():
- self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
+ if item := self._getSelectedItem():
+ self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(item))
self._changed = True
return
diff --git a/novelwriter/dialogs/wordlist.py b/novelwriter/dialogs/wordlist.py
index c82ca3d6..54ec2f2f 100644
--- a/novelwriter/dialogs/wordlist.py
+++ b/novelwriter/dialogs/wordlist.py
@@ -190,7 +190,7 @@ class GuiWordList(QDialog):
))
ffilter = formatFileFilter(["*.txt", "*"])
path, _ = QFileDialog.getOpenFileName(
- self, self.tr("Import File"), str(Path.home()), filter=ffilter
+ self, self.tr("Import File"), str(CONFIG.homePath()), filter=ffilter
)
if path:
try:
@@ -207,7 +207,7 @@ class GuiWordList(QDialog):
def _exportWords(self) -> None:
"""Export words to file."""
path, _ = QFileDialog.getSaveFileName(
- self, self.tr("Export File"), str(Path.home())
+ self, self.tr("Export File"), str(CONFIG.homePath())
)
if path:
try:
diff --git a/novelwriter/enum.py b/novelwriter/enum.py
index 4750ef5d..1781eb2f 100644
--- a/novelwriter/enum.py
+++ b/novelwriter/enum.py
@@ -210,3 +210,29 @@ class nwBuildFmt(Enum):
J_NWD = 7
# END Enum nwBuildFormat
+
+
+class nwStatusShape(Enum):
+
+ SQUARE = 0
+ TRIANGLE = 1
+ NABLA = 2
+ DIAMOND = 3
+ PENTAGON = 4
+ HEXAGON = 5
+ STAR = 6
+ PACMAN = 7
+ CIRCLE_Q = 8
+ CIRCLE_H = 9
+ CIRCLE_T = 10
+ CIRCLE = 11
+ BARS_1 = 12
+ BARS_2 = 13
+ BARS_3 = 14
+ BARS_4 = 15
+ BLOCK_1 = 16
+ BLOCK_2 = 17
+ BLOCK_3 = 18
+ BLOCK_4 = 19
+
+# END Enum nwStatusShape
diff --git a/novelwriter/error.py b/novelwriter/error.py
index 0801d55d..86641011 100644
--- a/novelwriter/error.py
+++ b/novelwriter/error.py
@@ -54,7 +54,7 @@ def logException() -> None:
return
-def formatException(exc) -> str:
+def formatException(exc: BaseException) -> str:
"""Format an exception as a string the same way the default
exception handler does.
"""
diff --git a/novelwriter/extensions/circularprogress.py b/novelwriter/extensions/circularprogress.py
index bbc2f64a..23ca7d3d 100644
--- a/novelwriter/extensions/circularprogress.py
+++ b/novelwriter/extensions/circularprogress.py
@@ -27,10 +27,11 @@ from math import ceil
from PyQt5.QtCore import QRect
from PyQt5.QtGui import QBrush, QColor, QPaintEvent, QPainter, QPen
-from PyQt5.QtWidgets import QProgressBar, QSizePolicy, QWidget
+from PyQt5.QtWidgets import QProgressBar, QWidget
from novelwriter.types import (
- QtPaintAnitAlias, QtAlignCenter, QtRoundCap, QtSolidLine, QtTransparent
+ QtPaintAnitAlias, QtAlignCenter, QtRoundCap, QtSizeFixed, QtSolidLine,
+ QtTransparent
)
@@ -59,7 +60,7 @@ class NProgressCircle(QProgressBar):
bar=self.palette().highlight().color(),
text=self.palette().text().color()
)
- self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
+ self.setSizePolicy(QtSizeFixed, QtSizeFixed)
self.setFixedWidth(size)
self.setFixedHeight(size)
return
diff --git a/novelwriter/extensions/modified.py b/novelwriter/extensions/modified.py
index 4eb50cff..b83a2662 100644
--- a/novelwriter/extensions/modified.py
+++ b/novelwriter/extensions/modified.py
@@ -25,6 +25,8 @@ along with this program. If not, see .
"""
from __future__ import annotations
+from enum import Enum
+
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QWheelEvent
from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QToolButton, QWidget
@@ -46,7 +48,7 @@ class NComboBox(QComboBox):
event.ignore()
return
- def setCurrentData(self, data: str, default: str) -> None:
+ def setCurrentData(self, data: str | Enum, default: str | Enum) -> None:
"""Set the current index from data, with a fallback."""
idx = self.findData(data)
self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
diff --git a/novelwriter/extensions/pagedsidebar.py b/novelwriter/extensions/pagedsidebar.py
index a3c53455..7709509f 100644
--- a/novelwriter/extensions/pagedsidebar.py
+++ b/novelwriter/extensions/pagedsidebar.py
@@ -28,11 +28,14 @@ from __future__ import annotations
from PyQt5.QtGui import QColor, QPaintEvent, QPainter, QPolygon
from PyQt5.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
- QAbstractButton, QAction, QButtonGroup, QLabel, QSizePolicy, QStyle,
+ QAbstractButton, QAction, QButtonGroup, QLabel, QStyle,
QStyleOptionToolButton, QToolBar, QToolButton, QWidget
)
-from novelwriter.types import QtPaintAnitAlias, QtAlignLeft, QtMouseOver, QtNoBrush, QtNoPen
+from novelwriter.types import (
+ QtPaintAnitAlias, QtAlignLeft, QtMouseOver, QtNoBrush, QtNoPen,
+ QtSizeExpanding, QtSizeFixed
+)
class NPagedSideBar(QToolBar):
@@ -59,7 +62,7 @@ class NPagedSideBar(QToolBar):
self.setOrientation(Qt.Orientation.Vertical)
stretch = QWidget(self)
- stretch.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
self._stretchAction = self.addWidget(stretch)
return
@@ -119,7 +122,7 @@ class _NPagedToolButton(QToolButton):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
- self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
+ self.setSizePolicy(QtSizeExpanding, QtSizeFixed)
self.setCheckable(True)
fH = self.fontMetrics().height()
@@ -197,7 +200,7 @@ class _NPagedToolLabel(QLabel):
def __init__(self, parent: QWidget, textColor: QColor | None = None) -> None:
super().__init__(parent=parent)
- self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
+ self.setSizePolicy(QtSizeExpanding, QtSizeFixed)
fH = self.fontMetrics().height()
self._bH = round(fH * 1.7)
diff --git a/novelwriter/extensions/switch.py b/novelwriter/extensions/switch.py
index e35bd79a..1c04a158 100644
--- a/novelwriter/extensions/switch.py
+++ b/novelwriter/extensions/switch.py
@@ -25,10 +25,10 @@ from __future__ import annotations
from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
from PyQt5.QtCore import QEvent, QPropertyAnimation, Qt, pyqtProperty
-from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
+from PyQt5.QtWidgets import QAbstractButton, QWidget
from novelwriter import CONFIG, SHARED
-from novelwriter.types import QtPaintAnitAlias, QtMouseLeft, QtNoPen
+from novelwriter.types import QtPaintAnitAlias, QtMouseLeft, QtNoPen, QtSizeFixed
class NSwitch(QAbstractButton):
@@ -46,7 +46,7 @@ class NSwitch(QAbstractButton):
self._rR = self._xR - self._rB
self.setCheckable(True)
- self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
+ self.setSizePolicy(QtSizeFixed, QtSizeFixed)
self.setFixedWidth(self._xW)
self.setFixedHeight(self._xH)
self._offset = self._xR
diff --git a/novelwriter/extensions/switchbox.py b/novelwriter/extensions/switchbox.py
index d41587ba..1cff6a9c 100644
--- a/novelwriter/extensions/switchbox.py
+++ b/novelwriter/extensions/switchbox.py
@@ -25,10 +25,13 @@ from __future__ import annotations
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSignal
-from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
+from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QWidget
from novelwriter.extensions.switch import NSwitch
-from novelwriter.types import QtAlignLeft, QtAlignRight, QtAlignRightMiddle
+from novelwriter.types import (
+ QtAlignLeft, QtAlignRight, QtAlignRightMiddle, QtSizeMinimum,
+ QtSizeMinimumExpanding
+)
class NSwitchBox(QScrollArea):
@@ -59,7 +62,7 @@ class NSwitchBox(QScrollArea):
self._content.setColumnStretch(1, 1)
self._widget = QWidget(self)
- self._widget.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Minimum)
+ self._widget.setSizePolicy(QtSizeMinimumExpanding, QtSizeMinimum)
self._widget.setLayout(self._content)
self.setWidgetResizable(True)
diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py
index 13525ded..4d603289 100644
--- a/novelwriter/gui/doceditor.py
+++ b/novelwriter/gui/doceditor.py
@@ -381,7 +381,7 @@ class GuiDocEditor(QPlainTextEdit):
return
- def loadText(self, tHandle: str, tLine=None) -> bool:
+ def loadText(self, tHandle: str, tLine: int | None = None) -> bool:
"""Load text from a document into the editor. If we have an I/O
error, we must handle this and clear the editor so that we don't
risk overwriting the file if it exists. This can for instance
@@ -497,6 +497,7 @@ class GuiDocEditor(QPlainTextEdit):
return False
self.setDocumentChanged(False)
+ self.docTextChanged.emit(self._docHandle, self._lastEdit)
oldHeader = self._nwItem.mainHeading
oldCount = SHARED.project.index.getHandleHeaderCount(tHandle)
@@ -1080,7 +1081,7 @@ class GuiDocEditor(QPlainTextEdit):
return
@pyqtSlot()
- def _cursorMoved(self):
+ def _cursorMoved(self) -> None:
"""Triggered when the cursor moved in the editor."""
self.docFooter.updateLineCount(self.textCursor())
return
@@ -1470,7 +1471,7 @@ class GuiDocEditor(QPlainTextEdit):
# Internal Functions : Text Manipulation
##
- def _toggleFormat(self, fLen: int, fChar: str) -> bool:
+ def _toggleFormat(self, fLen: int, fChar: str) -> None:
"""Toggle the formatting of a specific type for a piece of text.
If more than one block is selected, the formatting is applied to
the first block.
@@ -1488,12 +1489,12 @@ class GuiDocEditor(QPlainTextEdit):
posS = cursor.selectionStart()
posE = cursor.selectionEnd()
- if self._qDocument.characterAt(posO - 1) == fChar:
+ if posS == posE and self._qDocument.characterAt(posO - 1) == fChar:
logger.warning("Format repetition, cancelling action")
cursor.clearSelection()
cursor.setPosition(posO)
self.setTextCursor(cursor)
- return False
+ return
blockS = self._qDocument.findBlock(posS)
blockE = self._qDocument.findBlock(posE)
@@ -1519,7 +1520,6 @@ class GuiDocEditor(QPlainTextEdit):
break
if fLen == min(numA, numB):
- cursor.clearSelection()
cursor.beginEditBlock()
cursor.setPosition(posS)
for i in range(fLen):
@@ -1528,17 +1528,19 @@ class GuiDocEditor(QPlainTextEdit):
for i in range(fLen):
cursor.deletePreviousChar()
cursor.endEditBlock()
- cursor.clearSelection()
- cursor.setPosition(posO - fLen)
- self.setTextCursor(cursor)
+
+ if select != _SelectAction.KEEP_SELECTION:
+ cursor.clearSelection()
+ cursor.setPosition(posO - fLen)
+ self.setTextCursor(cursor)
else:
self._wrapSelection(fChar*fLen, pos=posO, select=select)
- return True
+ return
def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
- select: _SelectAction = _SelectAction.NO_DECISION) -> bool:
+ select: _SelectAction = _SelectAction.NO_DECISION) -> None:
"""Wrap the selected text in whatever is in tBefore and tAfter.
If there is no selection, the autoSelect setting decides the
action. AutoSelect will select the word under the cursor before
@@ -1578,21 +1580,21 @@ class GuiDocEditor(QPlainTextEdit):
if select == _SelectAction.MOVE_AFTER:
cursor.setPosition(posE + len(before + after))
elif select == _SelectAction.KEEP_SELECTION:
- cursor.setPosition(posE + len(before), QtMoveAnchor)
- cursor.setPosition(posS + len(before), QtKeepAnchor)
+ cursor.setPosition(posS + len(before), QtMoveAnchor)
+ cursor.setPosition(posE + len(before), QtKeepAnchor)
elif select == _SelectAction.KEEP_POSITION:
cursor.setPosition(posO + len(before))
self.setTextCursor(cursor)
- return True
+ return
- def _replaceQuotes(self, sQuote: str, oQuote: str, cQuote: str) -> bool:
+ def _replaceQuotes(self, sQuote: str, oQuote: str, cQuote: str) -> None:
"""Replace all straight quotes in the selected text."""
cursor = self.textCursor()
if not cursor.hasSelection():
SHARED.error(self.tr("Please select some text before calling replace quotes."))
- return False
+ return
posS = cursor.selectionStart()
posE = cursor.selectionEnd()
@@ -1632,7 +1634,7 @@ class GuiDocEditor(QPlainTextEdit):
self._allowAutoReplace(True)
- return True
+ return
def _processBlockFormat(
self, action: nwDocAction, text: str, toggle: bool = True
@@ -2185,7 +2187,7 @@ class MetaCompleter(QMenu):
# Internal Functions
##
- def _emitComplete(self, pos: int, length: int, value: str):
+ def _emitComplete(self, pos: int, length: int, value: str) -> None:
"""Emit the signal to indicate a selection has been made."""
self.complete.emit(pos, length, value)
return
@@ -2966,7 +2968,7 @@ class GuiDocEditHeader(QWidget):
# Events
##
- def mousePressEvent(self, event: QMouseEvent):
+ def mousePressEvent(self, event: QMouseEvent) -> None:
"""Capture a click on the title and ensure that the item is
selected in the project tree.
"""
@@ -3134,7 +3136,7 @@ class GuiDocEditFooter(QWidget):
sText = ""
else:
iPx = round(0.9*SHARED.theme.baseIconHeight)
- status, icon = self._tItem.getImportStatus(incIcon=True)
+ status, icon = self._tItem.getImportStatus()
sIcon = icon.pixmap(iPx, iPx)
sText = f"{status} / {self._tItem.describeMe()}"
diff --git a/novelwriter/gui/dochighlight.py b/novelwriter/gui/dochighlight.py
index 62f88f61..24bacd58 100644
--- a/novelwriter/gui/dochighlight.py
+++ b/novelwriter/gui/dochighlight.py
@@ -328,7 +328,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.setFormat(0, 3, self._hStyles["head2h"])
self.setFormat(3, len(text), self._hStyles["header2"])
- elif text.startswith("###! "): # Hard Scene
+ elif text.startswith("###! "): # Alternative Scene
self.setFormat(0, 4, self._hStyles["head3h"])
self.setFormat(4, len(text), self._hStyles["header3"])
diff --git a/novelwriter/gui/docviewerpanel.py b/novelwriter/gui/docviewerpanel.py
index f96e44e6..ed4d9ba6 100644
--- a/novelwriter/gui/docviewerpanel.py
+++ b/novelwriter/gui/docviewerpanel.py
@@ -450,7 +450,7 @@ class _ViewPanelKeyWords(QTreeWidget):
nwItem.itemType, nwItem.itemClass,
nwItem.itemLayout, nwItem.mainHeading
)
- impLabel, impIcon = nwItem.getImportStatus(incIcon=True)
+ impLabel, impIcon = nwItem.getImportStatus()
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py
index 5e9ed84d..1a90d2a3 100644
--- a/novelwriter/gui/itemdetails.py
+++ b/novelwriter/gui/itemdetails.py
@@ -253,7 +253,7 @@ class GuiItemDetails(QWidget):
# Status
# ======
- status, icon = nwItem.getImportStatus(incIcon=True)
+ status, icon = nwItem.getImportStatus()
self.statusIcon.setPixmap(icon.pixmap(iPx, iPx))
self.statusData.setText(status)
diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py
index 96fe2b2c..7d6b05cf 100644
--- a/novelwriter/gui/mainmenu.py
+++ b/novelwriter/gui/mainmenu.py
@@ -742,8 +742,8 @@ class GuiMainMenu(QMenuBar):
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
)
- # Format > Hard Scene
- self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Hard Scene"))
+ # Format > Alternative Scene
+ self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Alternative Scene"))
self.aFmtHardSc.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_HSC)
)
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index ef52577d..6643df47 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -35,8 +35,8 @@ from PyQt5.QtCore import QModelIndex, QPoint, Qt, pyqtSlot, pyqtSignal
from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
from PyQt5.QtWidgets import (
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
- QInputDialog, QMenu, QSizePolicy, QToolTip, QTreeWidget, QTreeWidgetItem,
- QVBoxLayout, QWidget
+ QInputDialog, QMenu, QToolTip, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
+ QWidget
)
from novelwriter import CONFIG, SHARED
@@ -47,7 +47,10 @@ from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
from novelwriter.extensions.modified import NIconToolButton
from novelwriter.extensions.novelselector import NovelSelector
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
-from novelwriter.types import QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtUserRole
+from novelwriter.types import (
+ QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
+ QtUserRole
+)
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
@@ -215,7 +218,7 @@ class GuiNovelToolBar(QWidget):
self.novelValue.setFont(selFont)
self.novelValue.setListFormat(self.tr("Outline of {0}"))
self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
- self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ self.novelValue.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
self.tbNovel = NIconToolButton(self, iSz)
@@ -344,7 +347,7 @@ class GuiNovelToolBar(QWidget):
# Internal Functions
##
- def _addLastColAction(self, colType, actionLabel) -> None:
+ def _addLastColAction(self, colType: NovelTreeColumn, actionLabel: str) -> None:
"""Add a column selection entry to the last column menu."""
aLast = self.mLastCol.addAction(actionLabel)
aLast.setCheckable(True)
diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py
index 0d07e5ab..380455ce 100644
--- a/novelwriter/gui/outline.py
+++ b/novelwriter/gui/outline.py
@@ -36,20 +36,19 @@ from enum import Enum
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, QT_TRANSLATE_NOOP
from PyQt5.QtWidgets import (
QAbstractItemView, QAction, QFileDialog, QFrame, QGridLayout, QGroupBox,
- QHBoxLayout, QLabel, QMenu, QScrollArea, QSizePolicy, QSplitter, QToolBar,
- QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
+ QHBoxLayout, QLabel, QMenu, QScrollArea, QSplitter, QToolBar, QToolButton,
+ QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
-from novelwriter.enum import (
- nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
-)
+from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
from novelwriter.error import logException
from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
from novelwriter.extensions.novelselector import NovelSelector
from novelwriter.types import (
- QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration, QtUserRole
+ QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration,
+ QtSizeExpanding, QtUserRole
)
@@ -190,7 +189,7 @@ class GuiOutlineView(QWidget):
return
@pyqtSlot(str)
- def _rootItemChanged(self, tHandle) -> None:
+ def _rootItemChanged(self, tHandle: str) -> None:
"""Handle root novel changed or needs to be refreshed."""
self.outlineTree.refreshTree(rootHandle=(tHandle or None), overRide=True)
return
@@ -217,7 +216,7 @@ class GuiOutlineToolBar(QToolBar):
self.setContentsMargins(0, 0, 0, 0)
stretch = QWidget(self)
- stretch.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
# Novel Selector
self.novelLabel = QLabel(self.tr("Outline of"), self)
@@ -428,7 +427,7 @@ class GuiOutlineTree(QTreeWidget):
##
@property
- def hiddenColumns(self):
+ def hiddenColumns(self) -> dict[nwOutline, bool]:
return self._colHidden
##
@@ -586,7 +585,7 @@ class GuiOutlineTree(QTreeWidget):
# Internal Functions
##
- def _loadHeaderState(self):
+ def _loadHeaderState(self) -> None:
"""Load the state of the main tree header, that is, column order
and column width.
"""
@@ -1048,7 +1047,7 @@ class GuiOutlineDetails(QScrollArea):
self.titleLabel.setText(self.tr(self.LVL_MAP.get(novIdx.level, "H1")))
self.titleValue.setText(novIdx.title)
- itemStatus, _ = nwItem.getImportStatus(incIcon=False)
+ itemStatus, _ = nwItem.getImportStatus()
self.fileValue.setText(nwItem.itemName)
self.itemValue.setText(itemStatus)
diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py
index 2ade45d8..8074325b 100644
--- a/novelwriter/gui/projtree.py
+++ b/novelwriter/gui/projtree.py
@@ -38,8 +38,8 @@ from PyQt5.QtGui import (
)
from PyQt5.QtWidgets import (
QAbstractItemView, QAction, QDialog, QFrame, QHBoxLayout, QHeaderView,
- QLabel, QMenu, QShortcut, QSizePolicy, QTreeWidget, QTreeWidgetItem,
- QVBoxLayout, QWidget
+ QLabel, QMenu, QShortcut, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
+ QWidget
)
from novelwriter import CONFIG, SHARED
@@ -54,7 +54,10 @@ from novelwriter.dialogs.projectsettings import GuiProjectSettings
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout
from novelwriter.extensions.modified import NIconToolButton
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
-from novelwriter.types import QtAlignLeft, QtAlignRight, QtMouseLeft, QtMouseMiddle, QtUserRole
+from novelwriter.types import (
+ QtAlignLeft, QtAlignRight, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
+ QtUserRole,
+)
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
@@ -274,7 +277,7 @@ class GuiProjectToolBar(QWidget):
self.viewLabel = QLabel(self.tr("Project Content"), self)
self.viewLabel.setFont(SHARED.theme.guiFontB)
self.viewLabel.setContentsMargins(0, 0, 0, 0)
- self.viewLabel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ self.viewLabel.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
# Quick Links
self.mQuick = QMenu(self)
@@ -453,7 +456,7 @@ class GuiProjectToolBar(QWidget):
def _buildRootMenu(self) -> None:
"""Build the rood folder menu."""
- def addClass(itemClass):
+ def addClass(itemClass: nwItemClass) -> None:
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass]))
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
@@ -1033,7 +1036,7 @@ class GuiProjectTree(QTreeWidget):
if trItem is None or nwItem is None:
return
- itemStatus, statusIcon = nwItem.getImportStatus(incIcon=True)
+ itemStatus, statusIcon = nwItem.getImportStatus()
hLevel = nwItem.mainHeading
itemIcon = SHARED.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
@@ -1855,11 +1858,11 @@ class _TreeContextMenu(QMenu):
if self._item.isNovelLike():
menu = self.addMenu(self.tr("Set Status to ..."))
current = self._item.itemStatus
- for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.items()):
- name = entry["name"]
+ for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.iterItems()):
+ name = entry.name
if not multi and current == key:
name += f" ({nwUnicode.U_CHECK})"
- action = menu.addAction(entry["icon"], name)
+ action = menu.addAction(entry.icon, name)
if multi:
action.triggered.connect(lambda n, key=key: self._iterSetItemStatus(key))
else:
@@ -1872,11 +1875,11 @@ class _TreeContextMenu(QMenu):
else:
menu = self.addMenu(self.tr("Set Importance to ..."))
current = self._item.itemImport
- for n, (key, entry) in enumerate(SHARED.project.data.itemImport.items()):
- name = entry["name"]
+ for n, (key, entry) in enumerate(SHARED.project.data.itemImport.iterItems()):
+ name = entry.name
if not multi and current == key:
name += f" ({nwUnicode.U_CHECK})"
- action = menu.addAction(entry["icon"], name)
+ action = menu.addAction(entry.icon, name)
if multi:
action.triggered.connect(lambda n, key=key: self._iterSetItemImport(key))
else:
diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py
index c636f677..c13283f4 100644
--- a/novelwriter/gui/theme.py
+++ b/novelwriter/gui/theme.py
@@ -74,9 +74,9 @@ class GuiTheme:
self.isLightTheme = True
# GUI
- self.statNone = QColor(120, 120, 120)
- self.statUnsaved = QColor(200, 15, 39)
- self.statSaved = QColor(2, 133, 37)
+ self.statNone = QColor(0, 0, 0)
+ self.statUnsaved = QColor(0, 0, 0)
+ self.statSaved = QColor(0, 0, 0)
self.helpText = QColor(0, 0, 0)
# Loaded Syntax Settings
@@ -227,6 +227,10 @@ class GuiTheme:
logException()
return False
+ # Reset Palette
+ self._guiPalette = QApplication.style().standardPalette()
+ self._resetGuiColors()
+
# Main
sec = "Main"
if parser.has_section(sec):
@@ -256,8 +260,6 @@ class GuiTheme:
self._setPalette(parser, sec, "highlightedtext", QPalette.ColorRole.HighlightedText)
self._setPalette(parser, sec, "link", QPalette.ColorRole.Link)
self._setPalette(parser, sec, "linkvisited", QPalette.ColorRole.LinkVisited)
- else:
- self._guiPalette = QApplication.style().standardPalette()
# GUI
sec = "GUI"
@@ -393,6 +395,14 @@ class GuiTheme:
# Internal Functions
##
+ def _resetGuiColors(self) -> None:
+ """Reset GUI colours to default values."""
+ self.statNone = QColor(120, 120, 120)
+ self.statUnsaved = QColor(200, 15, 39)
+ self.statSaved = QColor(2, 133, 37)
+ self.helpText = QColor(0, 0, 0)
+ return
+
def _setGuiFont(self) -> None:
"""Update the GUI's font style from settings."""
font = QFont()
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index 47b288d0..105d2652 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -944,7 +944,7 @@ class GuiMain(QMainWindow):
# Events
##
- def closeEvent(self, event: QCloseEvent):
+ def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the closing event of the GUI and call the close
function to handle all the close process steps.
"""
@@ -1201,7 +1201,7 @@ class GuiMain(QMainWindow):
return
@pyqtSlot()
- def _toggleViewerPanelVisibility(self):
+ def _toggleViewerPanelVisibility(self) -> None:
"""Toggle the visibility of the document viewer panel."""
CONFIG.showViewerPanel = not CONFIG.showViewerPanel
self.docViewerPanel.setVisible(CONFIG.showViewerPanel)
diff --git a/novelwriter/text/counting.py b/novelwriter/text/counting.py
index 59974ee5..61804edb 100644
--- a/novelwriter/text/counting.py
+++ b/novelwriter/text/counting.py
@@ -57,6 +57,7 @@ def preProcessText(text: str, keepHeaders: bool = True) -> list[str]:
continue
if line[0] == ">":
line = line.lstrip(">").lstrip(" ")
+ if line: # Above block can return empty line (Issue #1816)
if line[-1] == "<":
line = line.rstrip("<").rstrip(" ")
if "[" in line:
diff --git a/novelwriter/tools/dictionaries.py b/novelwriter/tools/dictionaries.py
index a944c745..a2607e63 100644
--- a/novelwriter/tools/dictionaries.py
+++ b/novelwriter/tools/dictionaries.py
@@ -179,7 +179,7 @@ class GuiDictionaries(QDialog):
##
@pyqtSlot()
- def _doBrowseHunspell(self):
+ def _doBrowseHunspell(self) -> None:
"""Browse for a Free/Libre Office dictionary."""
ffilter = formatFileFilter([
(self.tr("Free or Libre Office extension"), "*.sox *.oxt"), "*"
@@ -193,7 +193,7 @@ class GuiDictionaries(QDialog):
return
@pyqtSlot()
- def _doImportHunspell(self):
+ def _doImportHunspell(self) -> None:
"""Import a hunspell dictionary from .sox or .oxt file."""
procErr = self.tr("Could not process dictionary file")
if self._installPath:
diff --git a/novelwriter/tools/manusbuild.py b/novelwriter/tools/manusbuild.py
index 301e4432..68b5e867 100644
--- a/novelwriter/tools/manusbuild.py
+++ b/novelwriter/tools/manusbuild.py
@@ -60,7 +60,7 @@ class GuiManuscriptBuild(QDialog):
D_KEY = QtUserRole
- def __init__(self, parent: QWidget, build: BuildSettings):
+ def __init__(self, parent: QWidget, build: BuildSettings) -> None:
super().__init__(parent=parent)
logger.debug("Create: GuiManuscriptBuild")
@@ -260,7 +260,7 @@ class GuiManuscriptBuild(QDialog):
##
@pyqtSlot("QAbstractButton*")
- def _dialogButtonClicked(self, button: QAbstractButton):
+ def _dialogButtonClicked(self, button: QAbstractButton) -> None:
"""Handle button clicks from the dialog button box."""
role = self.dlgButtons.buttonRole(button)
if role == QtRoleAction:
@@ -273,7 +273,7 @@ class GuiManuscriptBuild(QDialog):
return
@pyqtSlot()
- def _doSelectPath(self):
+ def _doSelectPath(self) -> None:
"""Select a folder for output."""
bPath = Path(self.buildPath.text())
bPath = bPath if bPath.is_dir() else self._build.lastPath
@@ -285,7 +285,7 @@ class GuiManuscriptBuild(QDialog):
return
@pyqtSlot()
- def _doResetBuildName(self):
+ def _doResetBuildName(self) -> None:
"""Generate a default build name."""
bName = f"{SHARED.project.data.name} - {self._build.name}"
self.buildName.setText(bName)
@@ -293,7 +293,7 @@ class GuiManuscriptBuild(QDialog):
return
@pyqtSlot()
- def _resetProgress(self):
+ def _resetProgress(self) -> None:
"""Set the progress bar back to 0."""
self.buildProgress.setValue(0)
return
@@ -350,7 +350,7 @@ class GuiManuscriptBuild(QDialog):
return items[0].data(self.D_KEY)
return None
- def _saveSettings(self):
+ def _saveSettings(self) -> None:
"""Save the user GUI settings."""
winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height())
@@ -369,7 +369,7 @@ class GuiManuscriptBuild(QDialog):
return
- def _populateContentList(self):
+ def _populateContentList(self) -> None:
"""Build the content list."""
rootMap = {}
filtered = self._build.buildItemFilter(SHARED.project)
@@ -398,7 +398,7 @@ class GuiManuscriptBuild(QDialog):
return
- def _openOutputFolder(self):
+ def _openOutputFolder(self) -> None:
"""Open the build folder in the system's file explorer."""
openExternalPath(Path(self.buildPath.text()))
return
diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py
index 3b81429e..782a7109 100644
--- a/novelwriter/tools/manuscript.py
+++ b/novelwriter/tools/manuscript.py
@@ -36,8 +36,8 @@ from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
from PyQt5.QtWidgets import (
QAbstractItemView, QApplication, QDialog, QFormLayout, QGridLayout,
QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton,
- QSizePolicy, QSplitter, QStackedWidget, QTabWidget, QTextBrowser,
- QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
+ QSplitter, QStackedWidget, QTabWidget, QTextBrowser, QTreeWidget,
+ QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
@@ -54,7 +54,7 @@ from novelwriter.tools.manusbuild import GuiManuscriptBuild
from novelwriter.tools.manussettings import GuiBuildSettings
from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignRight, QtAlignTop,
- QtUserRole
+ QtSizeExpanding, QtSizeIgnored, QtUserRole
)
if TYPE_CHECKING: # pragma: no cover
@@ -303,25 +303,21 @@ class GuiManuscript(QDialog):
@pyqtSlot()
def _editSelectedBuild(self) -> None:
"""Edit the currently selected build settings entry."""
- build = self._getSelectedBuild()
- if build is not None:
+ if build := self._getSelectedBuild():
self._openSettingsDialog(build)
return
@pyqtSlot("QListWidgetItem*", "QListWidgetItem*")
def _updateBuildDetails(self, current: QListWidgetItem, previous: QListWidgetItem) -> None:
"""Process change of build selection to update the details."""
- if isinstance(current, QListWidgetItem):
- build = self._builds.getBuild(current.data(self.D_KEY))
- if build is not None:
- self.buildDetails.updateInfo(build)
+ if current and (build := self._builds.getBuild(current.data(self.D_KEY))):
+ self.buildDetails.updateInfo(build)
return
@pyqtSlot()
def _deleteSelectedBuild(self) -> None:
"""Delete the currently selected build settings entry."""
- build = self._getSelectedBuild()
- if build is not None:
+ if build := self._getSelectedBuild():
if SHARED.question(self.tr("Delete build '{0}'?".format(build.name))):
self._builds.removeBuild(build.buildID)
self._updateBuildsList()
@@ -332,8 +328,7 @@ class GuiManuscript(QDialog):
"""Process new build settings from the settings dialog."""
self._builds.setBuild(build)
self._updateBuildItem(build)
- current = self.buildList.currentItem()
- if isinstance(current, QListWidgetItem) and current.data(self.D_KEY) == build.buildID:
+ if (current := self.buildList.currentItem()) and current.data(self.D_KEY) == build.buildID:
self._updateBuildDetails(current, current)
return
@@ -342,8 +337,7 @@ class GuiManuscript(QDialog):
"""Run the document builder on the current build settings for
the preview widget.
"""
- build = self._getSelectedBuild()
- if build is None:
+ if not (build := self._getSelectedBuild()):
return
docBuild = NWBuildDocument(SHARED.project, build)
@@ -383,8 +377,7 @@ class GuiManuscript(QDialog):
@pyqtSlot()
def _buildManuscript(self) -> None:
"""Open the build dialog and build the manuscript."""
- build = self._getSelectedBuild()
- if isinstance(build, BuildSettings):
+ if build := self._getSelectedBuild():
dlgBuild = GuiManuscriptBuild(self, build)
dlgBuild.exec()
@@ -627,7 +620,7 @@ class _DetailsWidget(QWidget):
("headings.fmtChapter", "headings.hideChapter"),
("headings.fmtUnnumbered", "headings.hideUnnumbered"),
("headings.fmtScene", "headings.hideScene"),
- ("headings.fmtHardScene", "headings.hideHardScene"),
+ ("headings.fmtAltScene", "headings.hideAltScene"),
("headings.fmtSection", "headings.hideSection"),
]:
sub = QTreeWidgetItem()
@@ -1030,16 +1023,14 @@ class _StatsWidget(QWidget):
@pyqtSlot(bool)
def _toggleView(self, state: bool) -> None:
"""Toggle minimal or maximal view."""
- ignored = QSizePolicy.Policy.Ignored
- expanded = QSizePolicy.Policy.Expanding
if state:
self.mainStack.setCurrentWidget(self.maxWidget)
- self.maxWidget.setSizePolicy(expanded, expanded)
- self.minWidget.setSizePolicy(ignored, ignored)
+ self.maxWidget.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
+ self.minWidget.setSizePolicy(QtSizeIgnored, QtSizeIgnored)
else:
self.mainStack.setCurrentWidget(self.minWidget)
- self.maxWidget.setSizePolicy(ignored, ignored)
- self.minWidget.setSizePolicy(expanded, expanded)
+ self.maxWidget.setSizePolicy(QtSizeIgnored, QtSizeIgnored)
+ self.minWidget.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
self.maxWidget.adjustSize()
self.minWidget.adjustSize()
self.mainStack.adjustSize()
diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py
index ebbcbb17..18786ffa 100644
--- a/novelwriter/tools/manussettings.py
+++ b/novelwriter/tools/manussettings.py
@@ -669,20 +669,20 @@ class _HeadingsTab(NScrollablePage):
self.formatBox.addWidget(self.swtScene, 3, 4)
# Alt Scene Heading
- self.lblHScene = QLabel(self._build.getLabel("headings.fmtHardScene"), self)
- self.fmtHScene = QLineEdit("", self)
- self.fmtHScene.setReadOnly(True)
- self.btnHScene = NIconToolButton(self, iSz, "edit")
- self.btnHScene.clicked.connect(lambda: self._editHeading(self.EDIT_HSCENE))
- self.hdeHScene = QLabel(trHide, self)
- self.hdeHScene.setIndent(bSp)
- self.swtHScene = NSwitch(self, height=iPx)
+ self.lblAScene = QLabel(self._build.getLabel("headings.fmtAltScene"), self)
+ self.fmtAScene = QLineEdit("", self)
+ self.fmtAScene.setReadOnly(True)
+ self.btnAScene = NIconToolButton(self, iSz, "edit")
+ self.btnAScene.clicked.connect(lambda: self._editHeading(self.EDIT_HSCENE))
+ self.hdeAScene = QLabel(trHide, self)
+ self.hdeAScene.setIndent(bSp)
+ self.swtAScene = NSwitch(self, height=iPx)
- self.formatBox.addWidget(self.lblHScene, 4, 0)
- self.formatBox.addWidget(self.fmtHScene, 4, 1)
- self.formatBox.addWidget(self.btnHScene, 4, 2)
- self.formatBox.addWidget(self.hdeHScene, 4, 3)
- self.formatBox.addWidget(self.swtHScene, 4, 4)
+ self.formatBox.addWidget(self.lblAScene, 4, 0)
+ self.formatBox.addWidget(self.fmtAScene, 4, 1)
+ self.formatBox.addWidget(self.btnAScene, 4, 2)
+ self.formatBox.addWidget(self.hdeAScene, 4, 3)
+ self.formatBox.addWidget(self.swtAScene, 4, 4)
# Section Heading
self.lblSection = QLabel(self._build.getLabel("headings.fmtSection"), self)
@@ -826,14 +826,14 @@ class _HeadingsTab(NScrollablePage):
self.fmtChapter.setText(self._build.getStr("headings.fmtChapter"))
self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered"))
self.fmtScene.setText(self._build.getStr("headings.fmtScene"))
- self.fmtHScene.setText(self._build.getStr("headings.fmtHardScene"))
+ self.fmtAScene.setText(self._build.getStr("headings.fmtAltScene"))
self.fmtSection.setText(self._build.getStr("headings.fmtSection"))
self.swtTitle.setChecked(self._build.getBool("headings.hideTitle"))
self.swtChapter.setChecked(self._build.getBool("headings.hideChapter"))
self.swtUnnumbered.setChecked(self._build.getBool("headings.hideUnnumbered"))
self.swtScene.setChecked(self._build.getBool("headings.hideScene"))
- self.swtHScene.setChecked(self._build.getBool("headings.hideHardScene"))
+ self.swtAScene.setChecked(self._build.getBool("headings.hideAltScene"))
self.swtSection.setChecked(self._build.getBool("headings.hideSection"))
self.centerTitle.setChecked(self._build.getBool("headings.centerTitle"))
@@ -850,7 +850,7 @@ class _HeadingsTab(NScrollablePage):
self._build.setValue("headings.hideChapter", self.swtChapter.isChecked())
self._build.setValue("headings.hideUnnumbered", self.swtUnnumbered.isChecked())
self._build.setValue("headings.hideScene", self.swtScene.isChecked())
- self._build.setValue("headings.hideHardScene", self.swtHScene.isChecked())
+ self._build.setValue("headings.hideAltScene", self.swtAScene.isChecked())
self._build.setValue("headings.hideSection", self.swtSection.isChecked())
self._build.setValue("headings.centerTitle", self.centerTitle.isChecked())
@@ -890,8 +890,8 @@ class _HeadingsTab(NScrollablePage):
text = self.fmtScene.text()
label = self._build.getLabel("headings.fmtScene")
elif heading == self.EDIT_HSCENE:
- text = self.fmtHScene.text()
- label = self._build.getLabel("headings.fmtHardScene")
+ text = self.fmtAScene.text()
+ label = self._build.getLabel("headings.fmtAltScene")
elif heading == self.EDIT_SECTION:
text = self.fmtSection.text()
label = self._build.getLabel("headings.fmtSection")
@@ -928,8 +928,8 @@ class _HeadingsTab(NScrollablePage):
self.fmtScene.setText(text)
self._build.setValue("headings.fmtScene", text)
elif heading == self.EDIT_HSCENE:
- self.fmtHScene.setText(text)
- self._build.setValue("headings.fmtHardScene", text)
+ self.fmtAScene.setText(text)
+ self._build.setValue("headings.fmtAltScene", text)
elif heading == self.EDIT_SECTION:
self.fmtSection.setText(text)
self._build.setValue("headings.fmtSection", text)
@@ -1312,7 +1312,7 @@ class _FormatTab(NScrollableForm):
return
@pyqtSlot()
- def _pageSizeValueChanged(self):
+ def _pageSizeValueChanged(self) -> None:
"""The user has changed the page size spin boxes, so we flip
the page size box to Custom.
"""
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index c8f26727..ce37539b 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -560,7 +560,7 @@ class _NewProjectForm(QWidget):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
- self._basePath = CONFIG.lastPath()
+ self._basePath = CONFIG.homePath()
self._fillMode = self.FILL_BLANK
self._copyPath = None
@@ -632,7 +632,7 @@ class _NewProjectForm(QWidget):
self.numChapters = NSpinBox(self)
self.numChapters.setRange(0, 200)
- self.numChapters.setValue(5)
+ self.numChapters.setValue(0)
self.numChapters.setToolTip(self.tr("Set to 0 to only add scenes"))
self.chapterBox = NWrappedWidgetBox(
@@ -642,7 +642,7 @@ class _NewProjectForm(QWidget):
self.numScenes = NSpinBox(self)
self.numScenes.setRange(0, 200)
- self.numScenes.setValue(5)
+ self.numScenes.setValue(0)
self.sceneBox = NWrappedWidgetBox(
self.tr("Add {0} scene documents (to each chapter)"), self.numScenes
@@ -742,7 +742,6 @@ class _NewProjectForm(QWidget):
):
self._basePath = Path(projDir)
self._updateProjPath()
- CONFIG.setLastPath(self._basePath)
return
@pyqtSlot()
@@ -753,7 +752,7 @@ class _NewProjectForm(QWidget):
return
@pyqtSlot()
- def _syncSwitches(self):
+ def _syncSwitches(self) -> None:
"""Check if the add notes option should also be switched off."""
addPlot = self.addPlot.isChecked()
addChar = self.addChar.isChecked()
diff --git a/novelwriter/types.py b/novelwriter/types.py
index 56e59604..49e670ce 100644
--- a/novelwriter/types.py
+++ b/novelwriter/types.py
@@ -25,7 +25,7 @@ from __future__ import annotations
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor, QPainter, QTextCursor
-from PyQt5.QtWidgets import QDialogButtonBox, QStyle
+from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle
# Qt Alignment Flags
@@ -88,3 +88,11 @@ QtKeepAnchor = QTextCursor.MoveMode.KeepAnchor
QtMoveAnchor = QTextCursor.MoveMode.MoveAnchor
QtMoveLeft = QTextCursor.MoveOperation.Left
QtMoveRight = QTextCursor.MoveOperation.Right
+
+# Size Policy
+
+QtSizeExpanding = QSizePolicy.Policy.Expanding
+QtSizeFixed = QSizePolicy.Policy.Fixed
+QtSizeIgnored = QSizePolicy.Policy.Ignored
+QtSizeMinimum = QSizePolicy.Policy.Minimum
+QtSizeMinimumExpanding = QSizePolicy.Policy.MinimumExpanding
diff --git a/pkgutils.py b/pkgutils.py
index 2144000a..9d51551d 100755
--- a/pkgutils.py
+++ b/pkgutils.py
@@ -47,7 +47,7 @@ def extractVersion(beQuiet: bool = False) -> tuple[str, str, str]:
"""Extract the novelWriter version number without having to import
anything else from the main package.
"""
- def getValue(text):
+ def getValue(text: str) -> str:
bits = text.partition("=")
return bits[2].strip().strip('"')
@@ -176,7 +176,7 @@ def cleanBuildDirs() -> None:
print("Cleaning up build environment ...")
print("")
- def removeFolder(rmDir):
+ def removeFolder(rmDir: str) -> None:
if os.path.isdir(rmDir):
try:
shutil.rmtree(rmDir)
@@ -320,7 +320,7 @@ def buildQtI18nTS(sysArgs: list[str]) -> None:
print("=============================")
try:
- from PyQt6.lupdate import lupdate
+ from PyQt6.lupdate.lupdate import lupdate
except ImportError:
print("ERROR: This command requires lupdate from PyQt6")
print("On Debian/Ubuntu, install: pyqt6-dev-tools")
@@ -911,7 +911,7 @@ def makeAppImage(sysArgs: list[str]) -> list[str]:
import argparse
try:
- import python_appimage # noqa F401
+ import python_appimage # noqa: F401 # type: ignore
except ImportError:
print(
"ERROR: Package 'python-appimage' is missing on this system.\n"
@@ -1252,12 +1252,12 @@ def makeWindowsEmbedded(sysArgs: list[str]) -> None:
# Clean Up Files
# ==============
- def unlinkIfFound(delFile):
+ def unlinkIfFound(delFile: str) -> None:
if os.path.isfile(delFile):
os.unlink(delFile)
print("Deleted: %s" % delFile)
- def deleteFolder(delPath):
+ def deleteFolder(delPath: str) -> None:
if os.path.isdir(delPath):
shutil.rmtree(delPath)
print("Deleted: %s" % delPath)
@@ -1561,6 +1561,9 @@ def xdgUninstall() -> None:
def winInstall() -> None:
"""Will attempt to install icons and make a launcher for Windows."""
+ if sys.platform != "win32":
+ raise Exception("This method only runs on Windows")
+
import winreg
try:
import win32com.client
@@ -1654,7 +1657,7 @@ def winInstall() -> None:
print("")
print("Creating registry keys ...")
- def setKey(kPath, kName, kVal):
+ def setKey(kPath: str, kName: str, kVal: str) -> None:
winreg.CreateKey(winreg.HKEY_CURRENT_USER, kPath)
regKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, kPath, 0, winreg.KEY_WRITE)
winreg.SetValueEx(regKey, kName, 0, winreg.REG_SZ, kVal)
@@ -1688,6 +1691,9 @@ def winInstall() -> None:
def winUninstall() -> None:
"""Will attempt to uninstall icons previously installed."""
+ if sys.platform != "win32":
+ raise Exception("This method only runs on Windows")
+
import winreg
try:
import win32com.client
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 65bec68e..baf54924 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,2 +1,3 @@
flake8
flake8-pep585
+flake8-annotations
diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx
index 24170a34..716b21e7 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -1,6 +1,6 @@
-
-
+
+ Sample ProjectJane Smith
@@ -20,19 +20,20 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Background
+ Minor
+ Major
+ Main
@@ -57,7 +58,7 @@
Chapter One
-
+
Making a Scene
@@ -78,7 +79,7 @@
- We Found John!
+ We Found John!
@@ -90,7 +91,7 @@
- Chapter One
+ Chapter One
@@ -102,11 +103,11 @@
- John Smith
+ John Smith
- Jane Smith
+ Jane Smith
@@ -114,15 +115,15 @@
- Earth
+ Earth
- Space
+ Space
- Mars
+ Mars
diff --git a/tests/files/nwProject-1.5.nwx b/tests/files/nwProject-1.5.nwx
index 7823c057..35eab583 100644
--- a/tests/files/nwProject-1.5.nwx
+++ b/tests/files/nwProject-1.5.nwx
@@ -1,5 +1,5 @@
-
+Sample ProjectJane Smith
@@ -20,19 +20,19 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Minor
+ Major
+ Main
diff --git a/tests/mocked.py b/tests/mocked.py
index 65b65d93..5f9fc6a3 100644
--- a/tests/mocked.py
+++ b/tests/mocked.py
@@ -75,7 +75,7 @@ class MockStatusBar:
class MockTheme:
def __init__(self):
- self.baseIconHeight = 10
+ self.baseIconHeight = 20
return
def getPixmap(self, *a):
diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf
index 4e218943..668c3fb9 100644
--- a/tests/reference/baseConfig_novelwriter.conf
+++ b/tests/reference/baseConfig_novelwriter.conf
@@ -1,5 +1,5 @@
[Meta]
-timestamp = 2024-03-25 11:57:35
+timestamp = 2024-04-13 18:57:38
[Main]
theme = default
diff --git a/tests/reference/coreProject_NewFileFolder_nwProject.nwx b/tests/reference/coreProject_NewFileFolder_nwProject.nwx
index 2422fc02..4aba7481 100644
--- a/tests/reference/coreProject_NewFileFolder_nwProject.nwx
+++ b/tests/reference/coreProject_NewFileFolder_nwProject.nwx
@@ -1,5 +1,5 @@
-
+New ProjectJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/coreProject_NewRoot_nwProject.nwx b/tests/reference/coreProject_NewRoot_nwProject.nwx
index 028da14c..4bfbb568 100644
--- a/tests/reference/coreProject_NewRoot_nwProject.nwx
+++ b/tests/reference/coreProject_NewRoot_nwProject.nwx
@@ -1,5 +1,5 @@
-
+New ProjectJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/coreTools_DocDuplicator_nwProject.nwx b/tests/reference/coreTools_DocDuplicator_nwProject.nwx
index bab9a82d..5f6685a6 100644
--- a/tests/reference/coreTools_DocDuplicator_nwProject.nwx
+++ b/tests/reference/coreTools_DocDuplicator_nwProject.nwx
@@ -1,5 +1,5 @@
-
+New ProjectJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx b/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx
index 60c0fce3..01d3a0ef 100644
--- a/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx
+++ b/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx
@@ -1,5 +1,5 @@
-
+Test Project AJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx b/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx
index 86980406..ed43d5cd 100644
--- a/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx
+++ b/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx
@@ -1,5 +1,5 @@
-
+Test Project BJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/guiEditor_Main_Final_nwProject.nwx b/tests/reference/guiEditor_Main_Final_nwProject.nwx
index c3ee941f..2c6603eb 100644
--- a/tests/reference/guiEditor_Main_Final_nwProject.nwx
+++ b/tests/reference/guiEditor_Main_Final_nwProject.nwx
@@ -1,5 +1,5 @@
-
+New ProjectJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/guiEditor_Main_Initial_nwProject.nwx b/tests/reference/guiEditor_Main_Initial_nwProject.nwx
index aa9c3a74..f5119a55 100644
--- a/tests/reference/guiEditor_Main_Initial_nwProject.nwx
+++ b/tests/reference/guiEditor_Main_Initial_nwProject.nwx
@@ -1,5 +1,5 @@
-
+New ProjectJane Doe
@@ -16,16 +16,16 @@
- New
- Note
- Draft
- Finished
+ New
+ Note
+ Draft
+ Finished
- New
- Minor
- Major
- Main
+ New
+ Minor
+ Major
+ Main
diff --git a/tests/reference/projectXML_ReadLegacy10.nwx b/tests/reference/projectXML_ReadLegacy10.nwx
index a8f9e945..70885cfd 100644
--- a/tests/reference/projectXML_ReadLegacy10.nwx
+++ b/tests/reference/projectXML_ReadLegacy10.nwx
@@ -1,5 +1,5 @@
-
+Sample ProjectJay Doh
@@ -20,19 +20,19 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Minor
+ Major
+ Main
diff --git a/tests/reference/projectXML_ReadLegacy11.nwx b/tests/reference/projectXML_ReadLegacy11.nwx
index c216f88a..1d93bd02 100644
--- a/tests/reference/projectXML_ReadLegacy11.nwx
+++ b/tests/reference/projectXML_ReadLegacy11.nwx
@@ -1,5 +1,5 @@
-
+Sample ProjectJay Doh
@@ -20,19 +20,19 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Minor
+ Major
+ Main
diff --git a/tests/reference/projectXML_ReadLegacy12.nwx b/tests/reference/projectXML_ReadLegacy12.nwx
index b3cad791..d344f378 100644
--- a/tests/reference/projectXML_ReadLegacy12.nwx
+++ b/tests/reference/projectXML_ReadLegacy12.nwx
@@ -1,5 +1,5 @@
-
+Sample ProjectJay Doh
@@ -20,19 +20,19 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Minor
+ Major
+ Main
diff --git a/tests/reference/projectXML_ReadLegacy13.nwx b/tests/reference/projectXML_ReadLegacy13.nwx
index 4bd25a9a..05a4dfa5 100644
--- a/tests/reference/projectXML_ReadLegacy13.nwx
+++ b/tests/reference/projectXML_ReadLegacy13.nwx
@@ -1,5 +1,5 @@
-
+Sample ProjectJay Doh
@@ -20,19 +20,19 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Minor
+ Major
+ Main
diff --git a/tests/reference/projectXML_ReadLegacy14.nwx b/tests/reference/projectXML_ReadLegacy14.nwx
index 1ca92061..2a7e24d0 100644
--- a/tests/reference/projectXML_ReadLegacy14.nwx
+++ b/tests/reference/projectXML_ReadLegacy14.nwx
@@ -1,5 +1,5 @@
-
+Sample ProjectJay Doh
@@ -20,19 +20,19 @@
D
- New
- Notes
- Started
- 1st Draft
- 2nd Draft
- 3rd Draft
- Finished
+ New
+ Notes
+ Started
+ 1st Draft
+ 2nd Draft
+ 3rd Draft
+ Finished
- None
- Minor
- Major
- Main
+ None
+ Minor
+ Major
+ Main
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index 5ec474fe..7c9cc88d 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -200,6 +200,9 @@ def testBaseConfig_Methods(fncPath):
tstConf = Config()
tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
+ # Home Path
+ assert tstConf.homePath() == Path.home().absolute()
+
# Data Path
assert tstConf.dataPath() == fncPath
assert tstConf.dataPath("stuff") == fncPath / "stuff"
diff --git a/tests/test_core/test_core_buildsettings.py b/tests/test_core/test_core_buildsettings.py
index bccccae1..10c63253 100644
--- a/tests/test_core/test_core_buildsettings.py
+++ b/tests/test_core/test_core_buildsettings.py
@@ -27,6 +27,7 @@ import shutil
from pathlib import Path
+from novelwriter import CONFIG
from tools import C, buildTestProject
from mocked import causeOSError
@@ -58,7 +59,7 @@ def testCoreBuildSettings_ClassAttributes(fncPath: Path):
build.setName("Test Build")
assert build.name == "Test Build"
- # Only valid UUIDs are accpeted, anything else generates a new UUID
+ # Only valid UUIDs are accepted, anything else generates a new UUID
build.setBuildID("5cf45d24-f496-42c9-8733-529a9e52a62b")
assert build.buildID == "5cf45d24-f496-42c9-8733-529a9e52a62b"
@@ -72,14 +73,14 @@ def testCoreBuildSettings_ClassAttributes(fncPath: Path):
# Last path must be valid, if not it defaults to $HOME
build.setLastPath("/path/to/nowhere")
- assert build.lastPath == Path.home()
+ assert build.lastPath == CONFIG.homePath()
build.setLastPath(None)
- assert build.lastPath == Path.home()
+ assert build.lastPath == CONFIG.homePath()
(fncPath / "test.txt").write_text("foobar")
build.setLastPath(fncPath / "test.txt") # Can't be a file
- assert build.lastPath == Path.home()
+ assert build.lastPath == CONFIG.homePath()
build.setLastPath(fncPath)
assert build.lastPath == fncPath
@@ -93,7 +94,7 @@ def testCoreBuildSettings_ClassAttributes(fncPath: Path):
build.setLastPath(testDir)
assert build.lastPath == testDir
testDir.rmdir()
- assert build.lastPath == Path.home()
+ assert build.lastPath == CONFIG.homePath()
# Last build name
build.setLastBuildName(None) # type: ignore
diff --git a/tests/test_core/test_core_item.py b/tests/test_core/test_core_item.py
index 151d89da..65cf7b5f 100644
--- a/tests/test_core/test_core_item.py
+++ b/tests/test_core/test_core_item.py
@@ -553,8 +553,8 @@ def testCoreItem_ClassDefaults(mockGUI):
def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
"""Test packing and unpacking entries for the NWItem class."""
project = NWProject()
- project.data.itemStatus.write(None, "New", (100, 100, 100))
- project.data.itemImport.write(None, "New", (100, 100, 100))
+ project.data.itemStatus.add(None, "New", (100, 100, 100), "SQUARE", 0)
+ project.data.itemImport.add(None, "New", (100, 100, 100), "SQUARE", 0)
# Invalid
item = NWItem(project, "0000000000000")
diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py
index 8a4c0b8f..3304fc14 100644
--- a/tests/test_core/test_core_project.py
+++ b/tests/test_core/test_core_project.py
@@ -400,115 +400,6 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
# END Test testCoreProject_AccessItems
-@pytest.mark.core
-def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
- """Test the status and importance flag handling."""
- project = NWProject()
- mockRnd.reset()
- buildTestProject(project, fncPath)
-
- statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
- importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
-
- # Change Status
- # =============
-
- project.tree[C.hNovelRoot].setStatus(statusKeys[3]) # type: ignore
- project.tree[C.hPlotRoot].setStatus(statusKeys[2]) # type: ignore
- project.tree[C.hCharRoot].setStatus(statusKeys[1]) # type: ignore
- project.tree[C.hWorldRoot].setStatus(statusKeys[3]) # type: ignore
-
- assert project.tree[C.hNovelRoot].itemStatus == statusKeys[3] # type: ignore
- assert project.tree[C.hPlotRoot].itemStatus == statusKeys[2] # type: ignore
- assert project.tree[C.hCharRoot].itemStatus == statusKeys[1] # type: ignore
- assert project.tree[C.hWorldRoot].itemStatus == statusKeys[3] # type: ignore
-
- newList = [
- {"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
- {"key": statusKeys[1], "name": "Draft", "cols": (2, 2, 2)}, # These are swapped
- {"key": statusKeys[2], "name": "Note", "cols": (3, 3, 3)}, # These are swapped
- {"key": statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed
- {"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name
- ]
- assert project.setStatusColours(None, None) is False # type: ignore
- assert project.setStatusColours([], []) is False
- assert project.setStatusColours(newList, []) is True
-
- assert project.data.itemStatus.name(statusKeys[0]) == "New"
- assert project.data.itemStatus.name(statusKeys[1]) == "Draft"
- assert project.data.itemStatus.name(statusKeys[2]) == "Note"
- assert project.data.itemStatus.name(statusKeys[3]) == "Edited"
- assert project.data.itemStatus.cols(statusKeys[0]) == (1, 1, 1)
- assert project.data.itemStatus.cols(statusKeys[1]) == (2, 2, 2)
- assert project.data.itemStatus.cols(statusKeys[2]) == (3, 3, 3)
- assert project.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4)
-
- # Check the new entry
- lastKey = project.data.itemStatus.check("s000010")
- assert lastKey == "s000010"
- assert project.data.itemStatus.name(lastKey) == "Finished"
- assert project.data.itemStatus.cols(lastKey) == (5, 5, 5)
-
- # Delete last entry
- assert project.setStatusColours([], [lastKey]) is True
- assert project.data.itemStatus.name(lastKey) == "New"
-
- # Change Importance
- # =================
-
- fHandle = project.newFile("Jane Doe", C.hCharRoot)
- project.tree[fHandle].setImport(importKeys[3]) # type: ignore
-
- assert project.tree[fHandle].itemImport == importKeys[3] # type: ignore
- newList = [
- {"key": importKeys[0], "name": "New", "cols": (1, 1, 1)},
- {"key": importKeys[1], "name": "Minor", "cols": (2, 2, 2)},
- {"key": importKeys[2], "name": "Major", "cols": (3, 3, 3)},
- {"key": importKeys[3], "name": "Min", "cols": (4, 4, 4)},
- {"key": None, "name": "Max", "cols": (5, 5, 5)},
- ]
- assert project.setImportColours(None, None) is False # type: ignore
- assert project.setImportColours([], []) is False
- assert project.setImportColours(newList, []) is True
-
- assert project.data.itemImport.name(importKeys[0]) == "New"
- assert project.data.itemImport.name(importKeys[1]) == "Minor"
- assert project.data.itemImport.name(importKeys[2]) == "Major"
- assert project.data.itemImport.name(importKeys[3]) == "Min"
- assert project.data.itemImport.cols(importKeys[0]) == (1, 1, 1)
- assert project.data.itemImport.cols(importKeys[1]) == (2, 2, 2)
- assert project.data.itemImport.cols(importKeys[2]) == (3, 3, 3)
- assert project.data.itemImport.cols(importKeys[3]) == (4, 4, 4)
-
- # Check the new entry
- lastKey = project.data.itemImport.check("i000012")
- assert lastKey == "i000012"
- assert project.data.itemImport.name(lastKey) == "Max"
- assert project.data.itemImport.cols(lastKey) == (5, 5, 5)
-
- # Delete last entry
- assert project.setImportColours([], [lastKey]) is True
- assert project.data.itemImport.name(lastKey) == "New"
-
- # Delete Status/Import
- # ====================
-
- project.data.itemStatus.resetCounts()
- for key in list(project.data.itemStatus.keys()):
- assert project.data.itemStatus.remove(key) is True
-
- project.data.itemImport.resetCounts()
- for key in list(project.data.itemImport.keys()):
- assert project.data.itemImport.remove(key) is True
-
- assert len(project.data.itemStatus) == 0
- assert len(project.data.itemImport) == 0
- assert project.saveProject() is True
- project.closeProject()
-
-# END Test testCoreProject_StatusImport
-
-
@pytest.mark.core
def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
"""Test other project class methods and functions."""
diff --git a/tests/test_core/test_core_projectxml.py b/tests/test_core/test_core_projectxml.py
index 534aaed2..394e29cf 100644
--- a/tests/test_core/test_core_projectxml.py
+++ b/tests/test_core/test_core_projectxml.py
@@ -27,9 +27,12 @@ from shutil import copyfile
from datetime import datetime
from novelwriter.constants import nwFiles
+from novelwriter.enum import nwStatusShape
from tools import cmpFiles, writeFile
from mocked import causeOSError
+from PyQt5.QtGui import QColor
+
from novelwriter.core.item import NWItem
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
from novelwriter.core.projectdata import NWProjectData
@@ -131,7 +134,7 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
assert xmlReader.state == XMLReadState.PARSED_OK
assert xmlReader.xmlRoot == "novelWriterXML"
assert xmlReader.xmlVersion == 0x0105
- assert xmlReader.xmlRevision == 3
+ assert xmlReader.xmlRevision == 4
assert xmlReader.appVersion == "2.0-rc1"
assert xmlReader.hexVersion == 0x020000c1
@@ -154,44 +157,57 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
assert data.getLastHandle("novelTree") == "7031beac91f75"
assert data.getLastHandle("outline") == "7031beac91f75"
- assert data.itemStatus.name("sf12341") == "New"
- assert data.itemStatus.name("sf24ce6") == "Notes"
- assert data.itemStatus.name("sc24b8f") == "Started"
- assert data.itemStatus.name("s90e6c9") == "1st Draft"
- assert data.itemStatus.name("sd51c5b") == "2nd Draft"
- assert data.itemStatus.name("s8ae72a") == "3rd Draft"
- assert data.itemStatus.name("s78ea90") == "Finished"
+ assert data.itemStatus["sf12341"].name == "New"
+ assert data.itemStatus["sf24ce6"].name == "Notes"
+ assert data.itemStatus["sc24b8f"].name == "Started"
+ assert data.itemStatus["s90e6c9"].name == "1st Draft"
+ assert data.itemStatus["sd51c5b"].name == "2nd Draft"
+ assert data.itemStatus["s8ae72a"].name == "3rd Draft"
+ assert data.itemStatus["s78ea90"].name == "Finished"
- assert data.itemImport.name("ia857f0") == "None"
- assert data.itemImport.name("icfb3a5") == "Minor"
- assert data.itemImport.name("i2d7a54") == "Major"
- assert data.itemImport.name("i56be10") == "Main"
+ assert data.itemImport["ia857f0"].name == "None"
+ assert data.itemImport["icfb3a5"].name == "Minor"
+ assert data.itemImport["i2d7a54"].name == "Major"
+ assert data.itemImport["i56be10"].name == "Main"
- assert data.itemStatus.cols("sf12341") == (100, 100, 100)
- assert data.itemStatus.cols("sf24ce6") == (200, 50, 0)
- assert data.itemStatus.cols("sc24b8f") == (182, 60, 0)
- assert data.itemStatus.cols("s90e6c9") == (193, 129, 0)
- assert data.itemStatus.cols("sd51c5b") == (193, 129, 0)
- assert data.itemStatus.cols("s8ae72a") == (193, 129, 0)
- assert data.itemStatus.cols("s78ea90") == (58, 180, 58)
+ assert data.itemStatus["sf12341"].color == QColor(100, 100, 100)
+ assert data.itemStatus["sf24ce6"].color == QColor(200, 50, 0)
+ assert data.itemStatus["sc24b8f"].color == QColor(182, 60, 0)
+ assert data.itemStatus["s90e6c9"].color == QColor(193, 129, 0)
+ assert data.itemStatus["sd51c5b"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s8ae72a"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s78ea90"].color == QColor(58, 180, 58)
- assert data.itemImport.cols("ia857f0") == (100, 100, 100)
- assert data.itemImport.cols("icfb3a5") == (0, 122, 188)
- assert data.itemImport.cols("i2d7a54") == (21, 0, 180)
- assert data.itemImport.cols("i56be10") == (117, 0, 175)
+ assert data.itemImport["ia857f0"].color == QColor(100, 100, 100)
+ assert data.itemImport["icfb3a5"].color == QColor(0, 122, 188)
+ assert data.itemImport["i2d7a54"].color == QColor(21, 0, 180)
+ assert data.itemImport["i56be10"].color == QColor(117, 0, 175)
- assert data.itemStatus.count("sf12341") == 4
- assert data.itemStatus.count("sf24ce6") == 2
- assert data.itemStatus.count("sc24b8f") == 3
- assert data.itemStatus.count("s90e6c9") == 7
- assert data.itemStatus.count("sd51c5b") == 0
- assert data.itemStatus.count("s8ae72a") == 0
- assert data.itemStatus.count("s78ea90") == 1
+ assert data.itemStatus["sf12341"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["sf24ce6"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["sc24b8f"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s90e6c9"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["sd51c5b"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s8ae72a"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s78ea90"].shape == nwStatusShape.SQUARE
- assert data.itemImport.count("ia857f0") == 5
- assert data.itemImport.count("icfb3a5") == 2
- assert data.itemImport.count("i2d7a54") == 2
- assert data.itemImport.count("i56be10") == 1
+ assert data.itemImport["ia857f0"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["icfb3a5"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i2d7a54"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i56be10"].shape == nwStatusShape.SQUARE
+
+ assert data.itemStatus["sf12341"].count == 4
+ assert data.itemStatus["sf24ce6"].count == 2
+ assert data.itemStatus["sc24b8f"].count == 3
+ assert data.itemStatus["s90e6c9"].count == 7
+ assert data.itemStatus["sd51c5b"].count == 0
+ assert data.itemStatus["s8ae72a"].count == 0
+ assert data.itemStatus["s78ea90"].count == 1
+
+ assert data.itemImport["ia857f0"].count == 5
+ assert data.itemImport["icfb3a5"].count == 2
+ assert data.itemImport["i2d7a54"].count == 2
+ assert data.itemImport["i56be10"].count == 1
# Compare content
dumpFile = tstPaths.outDir / "projectXML_ReadCurrent.json"
@@ -272,44 +288,57 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockRnd):
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.0
assert data.getLastHandle("outline") is None # Doesn't exist in 1.0
- assert data.itemStatus.name("s000000") == "New"
- assert data.itemStatus.name("s000001") == "Notes"
- assert data.itemStatus.name("s000002") == "Started"
- assert data.itemStatus.name("s000003") == "1st Draft"
- assert data.itemStatus.name("s000004") == "2nd Draft"
- assert data.itemStatus.name("s000005") == "3rd Draft"
- assert data.itemStatus.name("s000006") == "Finished"
+ assert data.itemStatus["s000000"].name == "New"
+ assert data.itemStatus["s000001"].name == "Notes"
+ assert data.itemStatus["s000002"].name == "Started"
+ assert data.itemStatus["s000003"].name == "1st Draft"
+ assert data.itemStatus["s000004"].name == "2nd Draft"
+ assert data.itemStatus["s000005"].name == "3rd Draft"
+ assert data.itemStatus["s000006"].name == "Finished"
- assert data.itemImport.name("i000007") == "None"
- assert data.itemImport.name("i000008") == "Minor"
- assert data.itemImport.name("i000009") == "Major"
- assert data.itemImport.name("i00000a") == "Main"
+ assert data.itemImport["i000007"].name == "None"
+ assert data.itemImport["i000008"].name == "Minor"
+ assert data.itemImport["i000009"].name == "Major"
+ assert data.itemImport["i00000a"].name == "Main"
- assert data.itemStatus.cols("s000000") == (100, 100, 100)
- assert data.itemStatus.cols("s000001") == (200, 50, 0)
- assert data.itemStatus.cols("s000002") == (182, 60, 0)
- assert data.itemStatus.cols("s000003") == (193, 129, 0)
- assert data.itemStatus.cols("s000004") == (193, 129, 0)
- assert data.itemStatus.cols("s000005") == (193, 129, 0)
- assert data.itemStatus.cols("s000006") == (58, 180, 58)
+ assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
+ assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
+ assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
+ assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
- assert data.itemImport.cols("i000007") == (100, 100, 100)
- assert data.itemImport.cols("i000008") == (0, 122, 188)
- assert data.itemImport.cols("i000009") == (21, 0, 180)
- assert data.itemImport.cols("i00000a") == (117, 0, 175)
+ assert data.itemImport["i000007"].color == QColor(100, 100, 100)
+ assert data.itemImport["i000008"].color == QColor(0, 122, 188)
+ assert data.itemImport["i000009"].color == QColor(21, 0, 180)
+ assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
- assert data.itemStatus.count("s000000") == 0
- assert data.itemStatus.count("s000001") == 0
- assert data.itemStatus.count("s000002") == 0
- assert data.itemStatus.count("s000003") == 0
- assert data.itemStatus.count("s000004") == 0
- assert data.itemStatus.count("s000005") == 0
- assert data.itemStatus.count("s000006") == 0
+ assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
- assert data.itemImport.count("i000007") == 0
- assert data.itemImport.count("i000008") == 0
- assert data.itemImport.count("i000009") == 0
- assert data.itemImport.count("i00000a") == 0
+ assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
+
+ assert data.itemStatus["s000000"].count == 0
+ assert data.itemStatus["s000001"].count == 0
+ assert data.itemStatus["s000002"].count == 0
+ assert data.itemStatus["s000003"].count == 0
+ assert data.itemStatus["s000004"].count == 0
+ assert data.itemStatus["s000005"].count == 0
+ assert data.itemStatus["s000006"].count == 0
+
+ assert data.itemImport["i000007"].count == 0
+ assert data.itemImport["i000008"].count == 0
+ assert data.itemImport["i000009"].count == 0
+ assert data.itemImport["i00000a"].count == 0
# Compare content
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy10.json"
@@ -325,7 +354,7 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockRnd):
for entry in content:
item = NWItem(mockProject, "0000000000000") # type: ignore
item.unpack(entry)
- status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
+ status[item.itemHandle] = item.getImportStatus()[0]
packedContent.append(item.pack())
assert status == {
@@ -406,44 +435,57 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockRnd):
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.1
assert data.getLastHandle("outline") is None # Doesn't exist in 1.1
- assert data.itemStatus.name("s000000") == "New"
- assert data.itemStatus.name("s000001") == "Notes"
- assert data.itemStatus.name("s000002") == "Started"
- assert data.itemStatus.name("s000003") == "1st Draft"
- assert data.itemStatus.name("s000004") == "2nd Draft"
- assert data.itemStatus.name("s000005") == "3rd Draft"
- assert data.itemStatus.name("s000006") == "Finished"
+ assert data.itemStatus["s000000"].name == "New"
+ assert data.itemStatus["s000001"].name == "Notes"
+ assert data.itemStatus["s000002"].name == "Started"
+ assert data.itemStatus["s000003"].name == "1st Draft"
+ assert data.itemStatus["s000004"].name == "2nd Draft"
+ assert data.itemStatus["s000005"].name == "3rd Draft"
+ assert data.itemStatus["s000006"].name == "Finished"
- assert data.itemImport.name("i000007") == "None"
- assert data.itemImport.name("i000008") == "Minor"
- assert data.itemImport.name("i000009") == "Major"
- assert data.itemImport.name("i00000a") == "Main"
+ assert data.itemImport["i000007"].name == "None"
+ assert data.itemImport["i000008"].name == "Minor"
+ assert data.itemImport["i000009"].name == "Major"
+ assert data.itemImport["i00000a"].name == "Main"
- assert data.itemStatus.cols("s000000") == (100, 100, 100)
- assert data.itemStatus.cols("s000001") == (200, 50, 0)
- assert data.itemStatus.cols("s000002") == (182, 60, 0)
- assert data.itemStatus.cols("s000003") == (193, 129, 0)
- assert data.itemStatus.cols("s000004") == (193, 129, 0)
- assert data.itemStatus.cols("s000005") == (193, 129, 0)
- assert data.itemStatus.cols("s000006") == (58, 180, 58)
+ assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
+ assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
+ assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
+ assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
- assert data.itemImport.cols("i000007") == (100, 100, 100)
- assert data.itemImport.cols("i000008") == (0, 122, 188)
- assert data.itemImport.cols("i000009") == (21, 0, 180)
- assert data.itemImport.cols("i00000a") == (117, 0, 175)
+ assert data.itemImport["i000007"].color == QColor(100, 100, 100)
+ assert data.itemImport["i000008"].color == QColor(0, 122, 188)
+ assert data.itemImport["i000009"].color == QColor(21, 0, 180)
+ assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
- assert data.itemStatus.count("s000000") == 0
- assert data.itemStatus.count("s000001") == 0
- assert data.itemStatus.count("s000002") == 0
- assert data.itemStatus.count("s000003") == 0
- assert data.itemStatus.count("s000004") == 0
- assert data.itemStatus.count("s000005") == 0
- assert data.itemStatus.count("s000006") == 0
+ assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
- assert data.itemImport.count("i000007") == 0
- assert data.itemImport.count("i000008") == 0
- assert data.itemImport.count("i000009") == 0
- assert data.itemImport.count("i00000a") == 0
+ assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
+
+ assert data.itemStatus["s000000"].count == 0
+ assert data.itemStatus["s000001"].count == 0
+ assert data.itemStatus["s000002"].count == 0
+ assert data.itemStatus["s000003"].count == 0
+ assert data.itemStatus["s000004"].count == 0
+ assert data.itemStatus["s000005"].count == 0
+ assert data.itemStatus["s000006"].count == 0
+
+ assert data.itemImport["i000007"].count == 0
+ assert data.itemImport["i000008"].count == 0
+ assert data.itemImport["i000009"].count == 0
+ assert data.itemImport["i00000a"].count == 0
# Compare content
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy11.json"
@@ -459,7 +501,7 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockRnd):
for entry in content:
item = NWItem(mockProject, "0000000000000") # type: ignore
item.unpack(entry)
- status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
+ status[item.itemHandle] = item.getImportStatus()[0]
packedContent.append(item.pack())
assert status == {
@@ -540,44 +582,57 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockRnd):
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.2
assert data.getLastHandle("outline") is None # Doesn't exist in 1.2
- assert data.itemStatus.name("s000000") == "New"
- assert data.itemStatus.name("s000001") == "Notes"
- assert data.itemStatus.name("s000002") == "Started"
- assert data.itemStatus.name("s000003") == "1st Draft"
- assert data.itemStatus.name("s000004") == "2nd Draft"
- assert data.itemStatus.name("s000005") == "3rd Draft"
- assert data.itemStatus.name("s000006") == "Finished"
+ assert data.itemStatus["s000000"].name == "New"
+ assert data.itemStatus["s000001"].name == "Notes"
+ assert data.itemStatus["s000002"].name == "Started"
+ assert data.itemStatus["s000003"].name == "1st Draft"
+ assert data.itemStatus["s000004"].name == "2nd Draft"
+ assert data.itemStatus["s000005"].name == "3rd Draft"
+ assert data.itemStatus["s000006"].name == "Finished"
- assert data.itemImport.name("i000007") == "None"
- assert data.itemImport.name("i000008") == "Minor"
- assert data.itemImport.name("i000009") == "Major"
- assert data.itemImport.name("i00000a") == "Main"
+ assert data.itemImport["i000007"].name == "None"
+ assert data.itemImport["i000008"].name == "Minor"
+ assert data.itemImport["i000009"].name == "Major"
+ assert data.itemImport["i00000a"].name == "Main"
- assert data.itemStatus.cols("s000000") == (100, 100, 100)
- assert data.itemStatus.cols("s000001") == (200, 50, 0)
- assert data.itemStatus.cols("s000002") == (182, 60, 0)
- assert data.itemStatus.cols("s000003") == (193, 129, 0)
- assert data.itemStatus.cols("s000004") == (193, 129, 0)
- assert data.itemStatus.cols("s000005") == (193, 129, 0)
- assert data.itemStatus.cols("s000006") == (58, 180, 58)
+ assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
+ assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
+ assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
+ assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
- assert data.itemImport.cols("i000007") == (100, 100, 100)
- assert data.itemImport.cols("i000008") == (0, 122, 188)
- assert data.itemImport.cols("i000009") == (21, 0, 180)
- assert data.itemImport.cols("i00000a") == (117, 0, 175)
+ assert data.itemImport["i000007"].color == QColor(100, 100, 100)
+ assert data.itemImport["i000008"].color == QColor(0, 122, 188)
+ assert data.itemImport["i000009"].color == QColor(21, 0, 180)
+ assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
- assert data.itemStatus.count("s000000") == 0
- assert data.itemStatus.count("s000001") == 0
- assert data.itemStatus.count("s000002") == 0
- assert data.itemStatus.count("s000003") == 0
- assert data.itemStatus.count("s000004") == 0
- assert data.itemStatus.count("s000005") == 0
- assert data.itemStatus.count("s000006") == 0
+ assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
- assert data.itemImport.count("i000007") == 0
- assert data.itemImport.count("i000008") == 0
- assert data.itemImport.count("i000009") == 0
- assert data.itemImport.count("i00000a") == 0
+ assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
+
+ assert data.itemStatus["s000000"].count == 0
+ assert data.itemStatus["s000001"].count == 0
+ assert data.itemStatus["s000002"].count == 0
+ assert data.itemStatus["s000003"].count == 0
+ assert data.itemStatus["s000004"].count == 0
+ assert data.itemStatus["s000005"].count == 0
+ assert data.itemStatus["s000006"].count == 0
+
+ assert data.itemImport["i000007"].count == 0
+ assert data.itemImport["i000008"].count == 0
+ assert data.itemImport["i000009"].count == 0
+ assert data.itemImport["i00000a"].count == 0
# Compare content
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy12.json"
@@ -593,7 +648,7 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockRnd):
for entry in content:
item = NWItem(mockProject, "0000000000000") # type: ignore
item.unpack(entry)
- status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
+ status[item.itemHandle] = item.getImportStatus()[0]
packedContent.append(item.pack())
assert status == {
@@ -677,44 +732,57 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockRnd):
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.3
assert data.getLastHandle("outline") is None # Doesn't exist in 1.3
- assert data.itemStatus.name("s000000") == "New"
- assert data.itemStatus.name("s000001") == "Notes"
- assert data.itemStatus.name("s000002") == "Started"
- assert data.itemStatus.name("s000003") == "1st Draft"
- assert data.itemStatus.name("s000004") == "2nd Draft"
- assert data.itemStatus.name("s000005") == "3rd Draft"
- assert data.itemStatus.name("s000006") == "Finished"
+ assert data.itemStatus["s000000"].name == "New"
+ assert data.itemStatus["s000001"].name == "Notes"
+ assert data.itemStatus["s000002"].name == "Started"
+ assert data.itemStatus["s000003"].name == "1st Draft"
+ assert data.itemStatus["s000004"].name == "2nd Draft"
+ assert data.itemStatus["s000005"].name == "3rd Draft"
+ assert data.itemStatus["s000006"].name == "Finished"
- assert data.itemImport.name("i000007") == "None"
- assert data.itemImport.name("i000008") == "Minor"
- assert data.itemImport.name("i000009") == "Major"
- assert data.itemImport.name("i00000a") == "Main"
+ assert data.itemImport["i000007"].name == "None"
+ assert data.itemImport["i000008"].name == "Minor"
+ assert data.itemImport["i000009"].name == "Major"
+ assert data.itemImport["i00000a"].name == "Main"
- assert data.itemStatus.cols("s000000") == (100, 100, 100)
- assert data.itemStatus.cols("s000001") == (200, 50, 0)
- assert data.itemStatus.cols("s000002") == (182, 60, 0)
- assert data.itemStatus.cols("s000003") == (193, 129, 0)
- assert data.itemStatus.cols("s000004") == (193, 129, 0)
- assert data.itemStatus.cols("s000005") == (193, 129, 0)
- assert data.itemStatus.cols("s000006") == (58, 180, 58)
+ assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
+ assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
+ assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
+ assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
- assert data.itemImport.cols("i000007") == (100, 100, 100)
- assert data.itemImport.cols("i000008") == (0, 122, 188)
- assert data.itemImport.cols("i000009") == (21, 0, 180)
- assert data.itemImport.cols("i00000a") == (117, 0, 175)
+ assert data.itemImport["i000007"].color == QColor(100, 100, 100)
+ assert data.itemImport["i000008"].color == QColor(0, 122, 188)
+ assert data.itemImport["i000009"].color == QColor(21, 0, 180)
+ assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
- assert data.itemStatus.count("s000000") == 0
- assert data.itemStatus.count("s000001") == 0
- assert data.itemStatus.count("s000002") == 0
- assert data.itemStatus.count("s000003") == 0
- assert data.itemStatus.count("s000004") == 0
- assert data.itemStatus.count("s000005") == 0
- assert data.itemStatus.count("s000006") == 0
+ assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
- assert data.itemImport.count("i000007") == 0
- assert data.itemImport.count("i000008") == 0
- assert data.itemImport.count("i000009") == 0
- assert data.itemImport.count("i00000a") == 0
+ assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
+
+ assert data.itemStatus["s000000"].count == 0
+ assert data.itemStatus["s000001"].count == 0
+ assert data.itemStatus["s000002"].count == 0
+ assert data.itemStatus["s000003"].count == 0
+ assert data.itemStatus["s000004"].count == 0
+ assert data.itemStatus["s000005"].count == 0
+ assert data.itemStatus["s000006"].count == 0
+
+ assert data.itemImport["i000007"].count == 0
+ assert data.itemImport["i000008"].count == 0
+ assert data.itemImport["i000009"].count == 0
+ assert data.itemImport["i00000a"].count == 0
# Compare content
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy13.json"
@@ -730,7 +798,7 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockRnd):
for entry in content:
item = NWItem(mockProject, "0000000000000") # type: ignore
item.unpack(entry)
- status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
+ status[item.itemHandle] = item.getImportStatus()[0]
packedContent.append(item.pack())
assert status == {
@@ -814,44 +882,56 @@ def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockRnd):
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.3
assert data.getLastHandle("outline") is None # Doesn't exist in 1.3
- assert data.itemStatus.name("sf12341") == "New"
- assert data.itemStatus.name("sf24ce6") == "Notes"
- assert data.itemStatus.name("sc24b8f") == "Started"
- assert data.itemStatus.name("s90e6c9") == "1st Draft"
- assert data.itemStatus.name("sd51c5b") == "2nd Draft"
- assert data.itemStatus.name("s8ae72a") == "3rd Draft"
- assert data.itemStatus.name("s78ea90") == "Finished"
+ assert data.itemStatus["sf12341"].name == "New"
+ assert data.itemStatus["sf24ce6"].name == "Notes"
+ assert data.itemStatus["sc24b8f"].name == "Started"
+ assert data.itemStatus["s90e6c9"].name == "1st Draft"
+ assert data.itemStatus["sd51c5b"].name == "2nd Draft"
+ assert data.itemStatus["s8ae72a"].name == "3rd Draft"
+ assert data.itemStatus["s78ea90"].name == "Finished"
- assert data.itemImport.name("ia857f0") == "None"
- assert data.itemImport.name("icfb3a5") == "Minor"
- assert data.itemImport.name("i2d7a54") == "Major"
- assert data.itemImport.name("i56be10") == "Main"
+ assert data.itemImport["ia857f0"].name == "None"
+ assert data.itemImport["icfb3a5"].name == "Minor"
+ assert data.itemImport["i2d7a54"].name == "Major"
+ assert data.itemImport["i56be10"].name == "Main"
- assert data.itemStatus.cols("sf12341") == (100, 100, 100)
- assert data.itemStatus.cols("sf24ce6") == (200, 50, 0)
- assert data.itemStatus.cols("sc24b8f") == (182, 60, 0)
- assert data.itemStatus.cols("s90e6c9") == (193, 129, 0)
- assert data.itemStatus.cols("sd51c5b") == (193, 129, 0)
- assert data.itemStatus.cols("s8ae72a") == (193, 129, 0)
- assert data.itemStatus.cols("s78ea90") == (58, 180, 58)
+ assert data.itemStatus["sf12341"].color == QColor(100, 100, 100)
+ assert data.itemStatus["sf24ce6"].color == QColor(200, 50, 0)
+ assert data.itemStatus["sc24b8f"].color == QColor(182, 60, 0)
+ assert data.itemStatus["s90e6c9"].color == QColor(193, 129, 0)
+ assert data.itemStatus["sd51c5b"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s8ae72a"].color == QColor(193, 129, 0)
+ assert data.itemStatus["s78ea90"].color == QColor(58, 180, 58)
- assert data.itemImport.cols("ia857f0") == (100, 100, 100)
- assert data.itemImport.cols("icfb3a5") == (0, 122, 188)
- assert data.itemImport.cols("i2d7a54") == (21, 0, 180)
- assert data.itemImport.cols("i56be10") == (117, 0, 175)
+ assert data.itemImport["ia857f0"].color == QColor(100, 100, 100)
+ assert data.itemImport["icfb3a5"].color == QColor(0, 122, 188)
+ assert data.itemImport["i2d7a54"].color == QColor(21, 0, 180)
+ assert data.itemImport["i56be10"].color == QColor(117, 0, 175)
- assert data.itemStatus.count("sf12341") == 4
- assert data.itemStatus.count("sf24ce6") == 2
- assert data.itemStatus.count("sc24b8f") == 3
- assert data.itemStatus.count("s90e6c9") == 7
- assert data.itemStatus.count("sd51c5b") == 0
- assert data.itemStatus.count("s8ae72a") == 0
- assert data.itemStatus.count("s78ea90") == 1
+ assert data.itemStatus["sf12341"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["sf24ce6"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["sc24b8f"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s90e6c9"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["sd51c5b"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s8ae72a"].shape == nwStatusShape.SQUARE
+ assert data.itemStatus["s78ea90"].shape == nwStatusShape.SQUARE
- assert data.itemImport.count("ia857f0") == 5
- assert data.itemImport.count("icfb3a5") == 2
- assert data.itemImport.count("i2d7a54") == 2
- assert data.itemImport.count("i56be10") == 1
+ assert data.itemImport["ia857f0"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["icfb3a5"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i2d7a54"].shape == nwStatusShape.SQUARE
+ assert data.itemImport["i56be10"].shape == nwStatusShape.SQUARE
+
+ assert data.itemStatus["sf12341"].count == 4
+ assert data.itemStatus["sf24ce6"].count == 2
+ assert data.itemStatus["sc24b8f"].count == 3
+ assert data.itemStatus["s90e6c9"].count == 7
+ assert data.itemStatus["sd51c5b"].count == 0
+ assert data.itemStatus["s8ae72a"].count == 0
+ assert data.itemStatus["s78ea90"].count == 1
+ assert data.itemImport["ia857f0"].count == 5
+ assert data.itemImport["icfb3a5"].count == 2
+ assert data.itemImport["i2d7a54"].count == 2
+ assert data.itemImport["i56be10"].count == 1
# Compare content
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy14.json"
@@ -867,7 +947,7 @@ def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockRnd):
for entry in content:
item = NWItem(mockProject, "0000000000000") # type: ignore
item.unpack(entry)
- status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
+ status[item.itemHandle] = item.getImportStatus()[0]
packedContent.append(item.pack())
assert status == {
diff --git a/tests/test_core/test_core_status.py b/tests/test_core/test_core_status.py
index 9e30d9c4..7c6b2fc4 100644
--- a/tests/test_core/test_core_status.py
+++ b/tests/test_core/test_core_status.py
@@ -24,24 +24,50 @@ import pytest
from tools import C
-from PyQt5.QtGui import QIcon
+from PyQt5.QtGui import QColor, QIcon
-from novelwriter.core.status import NWStatus
+from novelwriter.core.status import NWStatus, StatusEntry, _ShapeCache
+from novelwriter.enum import nwStatusShape
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
@pytest.mark.core
-def testCoreStatus_Internal(mockRnd):
- """Test all the internal functions of the NWStatus class.
- """
+def testCoreStatus_StatusEntry():
+ """Test the StatusEntry class."""
+ color = QColor(255, 0, 0)
+ icon = NWStatus.createIcon(24, color, nwStatusShape.CIRCLE)
+ entry = StatusEntry("Test", color, nwStatusShape.CIRCLE, icon, 42)
+
+ # Check values
+ assert entry.name == "Test"
+ assert entry.color is color
+ assert entry.shape == nwStatusShape.CIRCLE
+ assert entry.icon is icon
+ assert entry.count == 42
+
+ # Make a copy
+ other = StatusEntry.duplicate(entry)
+ assert other is not entry
+
+ # Check copy is not the same
+ assert other.name == "Test"
+ assert other.color is not color # Not the same object
+ assert other.color == color # But same colours
+ assert other.shape == nwStatusShape.CIRCLE
+ assert other.icon is not icon # Not the same icon, but a copy
+ assert other.count == 42
+
+# END Test testCoreStatus_StatusEntry
+
+
+@pytest.mark.core
+def testCoreStatus_Internal(mockGUI, mockRnd):
+ """Test all the internal functions of the NWStatus class."""
nStatus = NWStatus(NWStatus.STATUS)
nImport = NWStatus(NWStatus.IMPORT)
- with pytest.raises(Exception):
- NWStatus(999)
-
# Generate Key
# ============
@@ -49,14 +75,14 @@ def testCoreStatus_Internal(mockRnd):
assert nStatus._newKey() == statusKeys[1]
# Key collision, should move to key 3
- nStatus.write(statusKeys[2], "Crash", (0, 0, 0))
+ nStatus.add(statusKeys[2], "Crash", (0, 0, 0), "SQUARE", 0)
assert nStatus._newKey() == statusKeys[3]
assert nImport._newKey() == importKeys[0]
assert nImport._newKey() == importKeys[1]
# Key collision, should move to key 3
- nImport.write(importKeys[2], "Crash", (0, 0, 0))
+ nImport.add(importKeys[2], "Crash", (0, 0, 0), "SQUARE", 0)
assert nImport._newKey() == importKeys[3]
# Check Key
@@ -82,81 +108,94 @@ def testCoreStatus_Internal(mockRnd):
assert nImport._isKey("i12345F") is False # Not a lower case hex value
assert nImport._isKey("i12345f") is True # Valid hex value
+ assert nStatus._checkKey(None) == "s000008" # Creates next key
+ assert nStatus._checkKey("s654321") == "s654321" # Status key accepted
+ assert nStatus._checkKey("i123456") == "s000009" # Import key not accepted
+
+ assert nImport._checkKey(None) == "i00000a" # Creates next key
+ assert nImport._checkKey("s654321") == "i00000b" # Status key not accepted
+ assert nImport._checkKey("i123456") == "i123456" # Import key accepted
+
# END Test testCoreStatus_Internal
@pytest.mark.core
-def testCoreStatus_Iterator(mockRnd):
- """Test the iterator functions of the NWStatus class.
- """
+def testCoreStatus_Iterator(mockGUI, mockRnd):
+ """Test the iterator functions of the NWStatus class."""
nStatus = NWStatus(NWStatus.STATUS)
-
- nStatus.write(None, "New", (100, 100, 100))
- nStatus.write(None, "Note", (200, 50, 0))
- nStatus.write(None, "Draft", (200, 150, 0))
- nStatus.write(None, "Finished", (50, 200, 0))
+ nStatus.add(None, "New", (100, 100, 100), "SQUARE", 0)
+ nStatus.add(None, "Note", (200, 50, 0), "CIRCLE", 1)
+ nStatus.add(None, "Draft", (200, 150, 0), "SQUARE", 2)
+ nStatus.add(None, "Finished", (50, 200, 0), "CIRCLE", 3)
# Direct access
entry = nStatus[statusKeys[0]]
- assert entry["cols"] == (100, 100, 100)
- assert entry["name"] == "New"
- assert entry["count"] == 0
- assert isinstance(entry["icon"], QIcon)
+ assert entry.color == QColor(100, 100, 100)
+ assert entry.name == "New"
+ assert entry.count == 0
+ assert isinstance(entry.icon, QIcon)
- # Iterate
- entries = list(nStatus)
- assert len(entries) == 4
+ # Length
+ assert len(nStatus._store) == 4
assert len(nStatus) == 4
- # Keys
- assert list(nStatus.keys()) == statusKeys
+ # Content : Keys
+ assert [k for k, _ in nStatus.iterItems()] == [
+ "s000000", "s000001", "s000002", "s000003"
+ ]
- # Items
- for index, (key, entry) in enumerate(nStatus.items()):
- assert key == statusKeys[index]
- assert "cols" in entry
- assert "name" in entry
- assert "count" in entry
- assert "icon" in entry
+ # Content : Names
+ assert [e.name for _, e in nStatus.iterItems()] == [
+ "New", "Note", "Draft", "Finished"
+ ]
- # Valuse
- for entry in nStatus.values():
- assert "cols" in entry
- assert "name" in entry
- assert "count" in entry
- assert "icon" in entry
+ # Content : Colours
+ assert [e.color for _, e in nStatus.iterItems()] == [
+ QColor(100, 100, 100), QColor(200, 50, 0), QColor(200, 150, 0), QColor(50, 200, 0)
+ ]
+
+ # Content : Shape
+ assert [e.shape for _, e in nStatus.iterItems()] == [
+ nwStatusShape.SQUARE, nwStatusShape.CIRCLE, nwStatusShape.SQUARE, nwStatusShape.CIRCLE
+ ]
+
+ # Content : Count
+ assert [e.count for _, e in nStatus.iterItems()] == [0, 1, 2, 3]
# END Test testCoreStatus_Iterator
@pytest.mark.core
-def testCoreStatus_Entries(mockRnd):
- """Test all the simple setters for the NWStatus class.
- """
+def testCoreStatus_Entries(mockGUI, mockRnd):
+ """Test all the simple setters for the NWStatus class."""
nStatus = NWStatus(NWStatus.STATUS)
- # Write
- # =====
+ # Add
+ # ===
- # Have a key
- nStatus.write(statusKeys[0], "Entry 1", (200, 100, 50))
- assert nStatus[statusKeys[0]]["name"] == "Entry 1"
- assert nStatus[statusKeys[0]]["cols"] == (200, 100, 50)
+ # Has a key
+ nStatus.add(statusKeys[0], "Entry 1", (200, 100, 50), "SQUARE", 0)
+ assert nStatus[statusKeys[0]].name == "Entry 1"
+ assert nStatus[statusKeys[0]].color == QColor(200, 100, 50)
+ assert nStatus[statusKeys[0]].shape == nwStatusShape.SQUARE
- # Don't have a key
- nStatus.write(None, "Entry 2", (210, 110, 60))
- assert nStatus[statusKeys[1]]["name"] == "Entry 2"
- assert nStatus[statusKeys[1]]["cols"] == (210, 110, 60)
+ # Doesn't have a key
+ nStatus.add(None, "Entry 2", (210, 110, 60), "SQUARE", 0)
+ assert nStatus[statusKeys[1]].name == "Entry 2"
+ assert nStatus[statusKeys[1]].color == QColor(210, 110, 60)
+ assert nStatus[statusKeys[1]].shape == nwStatusShape.SQUARE
- # Wrong colour spec
- nStatus.write(None, "Entry 3", "what?")
- assert nStatus[statusKeys[2]]["name"] == "Entry 3"
- assert nStatus[statusKeys[2]]["cols"] == (100, 100, 100)
+ # Wrong colour spec, unknown shape
+ nStatus.add(None, "Entry 3", "what?", "", 0) # type: ignore
+ assert nStatus[statusKeys[2]].name == "Entry 3"
+ assert nStatus[statusKeys[2]].color == QColor(100, 100, 100)
+ assert nStatus[statusKeys[2]].shape == nwStatusShape.SQUARE
# Wrong colour count
- nStatus.write(None, "Entry 4", (10, 20))
- assert nStatus[statusKeys[3]]["name"] == "Entry 4"
- assert nStatus[statusKeys[3]]["cols"] == (100, 100, 100)
+ nStatus.add(None, "Entry 4", (10, 20), "CIRCLE", 0) # type: ignore
+ assert nStatus[statusKeys[3]].name == "Entry 4"
+ assert nStatus[statusKeys[3]].color == QColor(100, 100, 100)
+ assert nStatus[statusKeys[3]].shape == nwStatusShape.CIRCLE
# Check
# =====
@@ -171,29 +210,38 @@ def testCoreStatus_Entries(mockRnd):
# Name Access
# ===========
- assert nStatus.name(statusKeys[0]) == "Entry 1"
- assert nStatus.name(statusKeys[1]) == "Entry 2"
- assert nStatus.name(statusKeys[2]) == "Entry 3"
- assert nStatus.name(statusKeys[3]) == "Entry 4"
- assert nStatus.name("blablabla") == "Entry 1"
+ assert nStatus[statusKeys[0]].name == "Entry 1"
+ assert nStatus[statusKeys[1]].name == "Entry 2"
+ assert nStatus[statusKeys[2]].name == "Entry 3"
+ assert nStatus[statusKeys[3]].name == "Entry 4"
+ assert nStatus["blablabla"].name == "Entry 1"
# Colour Access
# =============
- assert nStatus.cols(statusKeys[0]) == (200, 100, 50)
- assert nStatus.cols(statusKeys[1]) == (210, 110, 60)
- assert nStatus.cols(statusKeys[2]) == (100, 100, 100)
- assert nStatus.cols(statusKeys[3]) == (100, 100, 100)
- assert nStatus.cols("blablabla") == (200, 100, 50)
+ assert nStatus[statusKeys[0]].color == QColor(200, 100, 50)
+ assert nStatus[statusKeys[1]].color == QColor(210, 110, 60)
+ assert nStatus[statusKeys[2]].color == QColor(100, 100, 100)
+ assert nStatus[statusKeys[3]].color == QColor(100, 100, 100)
+ assert nStatus["blablabla"].color == QColor(200, 100, 50)
# Icon Access
# ===========
- assert isinstance(nStatus.icon(statusKeys[0]), QIcon)
- assert isinstance(nStatus.icon(statusKeys[1]), QIcon)
- assert isinstance(nStatus.icon(statusKeys[2]), QIcon)
- assert isinstance(nStatus.icon(statusKeys[3]), QIcon)
- assert isinstance(nStatus.icon("blablabla"), QIcon)
+ assert isinstance(nStatus[statusKeys[0]].icon, QIcon)
+ assert isinstance(nStatus[statusKeys[1]].icon, QIcon)
+ assert isinstance(nStatus[statusKeys[2]].icon, QIcon)
+ assert isinstance(nStatus[statusKeys[3]].icon, QIcon)
+ assert isinstance(nStatus["blablabla"].icon, QIcon)
+
+ # Shape Access
+ # ============
+
+ assert nStatus[statusKeys[0]].shape == nwStatusShape.SQUARE
+ assert nStatus[statusKeys[1]].shape == nwStatusShape.SQUARE
+ assert nStatus[statusKeys[2]].shape == nwStatusShape.SQUARE
+ assert nStatus[statusKeys[3]].shape == nwStatusShape.CIRCLE
+ assert nStatus["blablabla"].shape == nwStatusShape.SQUARE
# Increment and Count Access
# ==========================
@@ -203,50 +251,32 @@ def testCoreStatus_Entries(mockRnd):
for _ in range(n):
nStatus.increment(statusKeys[i])
- assert nStatus.count(statusKeys[0]) == countTo[0]
- assert nStatus.count(statusKeys[1]) == countTo[1]
- assert nStatus.count(statusKeys[2]) == countTo[2]
- assert nStatus.count(statusKeys[3]) == countTo[3]
- assert nStatus.count("blablabla") == countTo[0]
+ assert nStatus[statusKeys[0]].count == countTo[0]
+ assert nStatus[statusKeys[1]].count == countTo[1]
+ assert nStatus[statusKeys[2]].count == countTo[2]
+ assert nStatus[statusKeys[3]].count == countTo[3]
+ assert nStatus["blablabla"].count == countTo[0]
nStatus.resetCounts()
- assert nStatus.count(statusKeys[0]) == 0
- assert nStatus.count(statusKeys[1]) == 0
- assert nStatus.count(statusKeys[2]) == 0
- assert nStatus.count(statusKeys[3]) == 0
+ assert nStatus[statusKeys[0]].count == 0
+ assert nStatus[statusKeys[1]].count == 0
+ assert nStatus[statusKeys[2]].count == 0
+ assert nStatus[statusKeys[3]].count == 0
- # Reorder
- # =======
+ # Update
+ # ======
- cOrder = list(nStatus.keys())
- assert cOrder == statusKeys
+ assert list(nStatus._store.keys()) == statusKeys
- # Wrong length
- assert nStatus.reorder([]) is False
+ # Reverse
+ order: list[tuple[str | None, StatusEntry]] = list(nStatus.iterItems())
+ nStatus.update(list(reversed(order)))
+ assert list(nStatus._store.keys()) == list(reversed(statusKeys))
- # No change
- assert nStatus.reorder(cOrder) is False
-
- # Actual reaorder
- nOrder = [
- statusKeys[0],
- statusKeys[2],
- statusKeys[1],
- statusKeys[3],
- ]
- assert nStatus.reorder(nOrder) is True
- assert list(nStatus.keys()) == nOrder
-
- # Add an unknown key
- wOrder = nOrder.copy()
- wOrder[3] = nStatus._newKey()
- assert nStatus.reorder(wOrder) is False
- assert list(nStatus.keys()) == nOrder
-
- # Put it back
- assert nStatus.reorder(cOrder) is True
- assert list(nStatus.keys()) == cOrder
+ # Restore
+ nStatus.update(order)
+ assert list(nStatus._store.keys()) == statusKeys
# Default
# =======
@@ -255,56 +285,41 @@ def testCoreStatus_Entries(mockRnd):
nStatus._default = None
assert nStatus.check("Entry 5") == ""
- assert nStatus.name("blablabla") == ""
- assert nStatus.cols("blablabla") == (100, 100, 100)
- assert nStatus.count("blablabla") == 0
- assert isinstance(nStatus.icon("blablabla"), QIcon)
+ assert nStatus["blablabla"].name == ""
+ assert nStatus["blablabla"].color == QColor(0, 0, 0)
+ assert nStatus["blablabla"].shape == nwStatusShape.SQUARE
+ assert nStatus["blablabla"].icon.isNull()
+ assert nStatus["blablabla"].count == 0
nStatus._default = default
# Remove
# ======
+ # This uses update with deleted items
- # Non-existing entry
- assert nStatus.remove("blablabla") is False
+ order: list[tuple[str | None, StatusEntry]] = list(nStatus.iterItems())
- # Non-zero entry
- nStatus.increment(statusKeys[3])
- assert nStatus.remove(statusKeys[3]) is False
+ # Remove Entry 0
+ nStatus.update([order[1], order[3], order[2]])
+ assert list(nStatus._store.keys()) == [statusKeys[1], statusKeys[3], statusKeys[2]]
+ assert nStatus._default == statusKeys[1]
- # Delete last entry
- nStatus.resetCounts()
- lastName = nStatus.name(statusKeys[3])
- assert lastName == "Entry 4"
- assert nStatus.remove(statusKeys[3]) is True
- assert nStatus.check(statusKeys[3]) == nStatus._default
- assert nStatus.check(lastName) == nStatus._default
-
- # Delete default entry, Entry 2 is new default
- firstName = nStatus.name(nStatus._default)
- assert firstName == "Entry 1"
- assert nStatus.remove(nStatus._default) is True
- assert nStatus.name(firstName) == "Entry 2"
-
- # Remove remaining entries
- assert nStatus.remove(statusKeys[1]) is True
- assert nStatus.remove(statusKeys[2]) is True
-
- assert len(nStatus) == 0
- assert nStatus._default is None
+ # Remove Entry 1
+ nStatus.update([order[3], order[2]])
+ assert list(nStatus._store.keys()) == [statusKeys[3], statusKeys[2]]
+ assert nStatus._default == statusKeys[3]
# END Test testCoreStatus_Entries
@pytest.mark.core
-def testCoreStatus_PackUnpack(mockRnd):
- """Test all the pack/unpack of the NWStatus class.
- """
+def testCoreStatus_Pack(mockGUI, mockRnd):
+ """Test data packing of the NWStatus class."""
nStatus = NWStatus(NWStatus.STATUS)
- nStatus.write(None, "New", (100, 100, 100))
- nStatus.write(None, "Note", (200, 50, 0))
- nStatus.write(None, "Draft", (200, 150, 0))
- nStatus.write(None, "Finished", (50, 200, 0))
+ nStatus.add(None, "New", (100, 100, 100), "SQUARE", 0)
+ nStatus.add(None, "Note", (200, 50, 0), "CIRCLE", 0)
+ nStatus.add(None, "Draft", (200, 150, 0), "SQUARE", 0)
+ nStatus.add(None, "Finished", (50, 200, 0), "SQUARE", 0)
countTo = [3, 5, 7, 9]
for i, n in enumerate(countTo):
@@ -318,52 +333,85 @@ def testCoreStatus_PackUnpack(mockRnd):
"count": "3",
"red": "100",
"green": "100",
- "blue": "100"
+ "blue": "100",
+ "shape": "SQUARE",
}),
("Note", {
"key": statusKeys[1],
"count": "5",
"red": "200",
"green": "50",
- "blue": "0"
+ "blue": "0",
+ "shape": "CIRCLE",
}),
("Draft", {
"key": statusKeys[2],
"count": "7",
"red": "200",
"green": "150",
- "blue": "0"
+ "blue": "0",
+ "shape": "SQUARE",
}),
("Finished", {
"key": statusKeys[3],
"count": "9",
"red": "50",
"green": "200",
- "blue": "0"
+ "blue": "0",
+ "shape": "SQUARE",
}),
]
- # Unpack
- nStatus = NWStatus(NWStatus.STATUS)
- nStatus.unpack({
- statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
- statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
- statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
- statusKeys[3]: {"label": "New3", "colour": (250, 250, 250), "count": countTo[3]},
- })
- assert len(nStatus._store) == 4
- assert list(nStatus._store.keys()) == statusKeys
- assert nStatus._store[statusKeys[0]]["name"] == "New0"
- assert nStatus._store[statusKeys[1]]["name"] == "New1"
- assert nStatus._store[statusKeys[2]]["name"] == "New2"
- assert nStatus._store[statusKeys[3]]["name"] == "New3"
- assert nStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
- assert nStatus._store[statusKeys[1]]["cols"] == (150, 150, 150)
- assert nStatus._store[statusKeys[2]]["cols"] == (200, 200, 200)
- assert nStatus._store[statusKeys[3]]["cols"] == (250, 250, 250)
- assert nStatus._store[statusKeys[0]]["count"] == countTo[0]
- assert nStatus._store[statusKeys[1]]["count"] == countTo[1]
- assert nStatus._store[statusKeys[2]]["count"] == countTo[2]
- assert nStatus._store[statusKeys[3]]["count"] == countTo[3]
+# END Test testCoreStatus_Pack
-# END Test testCoreStatus_PackUnpack
+
+@pytest.mark.core
+def testCoreStatus_ShapeCache():
+ """Test the _ShapeCache class."""
+ shapes = _ShapeCache()
+
+ # Generate all shapes
+ square = shapes.getShape(nwStatusShape.SQUARE)
+ circleQ = shapes.getShape(nwStatusShape.CIRCLE_Q)
+ circleH = shapes.getShape(nwStatusShape.CIRCLE_H)
+ circleT = shapes.getShape(nwStatusShape.CIRCLE_T)
+ circle = shapes.getShape(nwStatusShape.CIRCLE)
+ triangle = shapes.getShape(nwStatusShape.TRIANGLE)
+ nabla = shapes.getShape(nwStatusShape.NABLA)
+ diamond = shapes.getShape(nwStatusShape.DIAMOND)
+ pentagon = shapes.getShape(nwStatusShape.PENTAGON)
+ hexagon = shapes.getShape(nwStatusShape.HEXAGON)
+ star = shapes.getShape(nwStatusShape.STAR)
+ pacman = shapes.getShape(nwStatusShape.PACMAN)
+ bars1 = shapes.getShape(nwStatusShape.BARS_1)
+ bars2 = shapes.getShape(nwStatusShape.BARS_2)
+ bars3 = shapes.getShape(nwStatusShape.BARS_3)
+ bars4 = shapes.getShape(nwStatusShape.BARS_4)
+ block1 = shapes.getShape(nwStatusShape.BLOCK_1)
+ block2 = shapes.getShape(nwStatusShape.BLOCK_2)
+ block3 = shapes.getShape(nwStatusShape.BLOCK_3)
+ block4 = shapes.getShape(nwStatusShape.BLOCK_4)
+
+ # Request again should return from cache
+ assert shapes.getShape(nwStatusShape.SQUARE) is square
+ assert shapes.getShape(nwStatusShape.CIRCLE_Q) is circleQ
+ assert shapes.getShape(nwStatusShape.CIRCLE_H) is circleH
+ assert shapes.getShape(nwStatusShape.CIRCLE_T) is circleT
+ assert shapes.getShape(nwStatusShape.CIRCLE) is circle
+ assert shapes.getShape(nwStatusShape.TRIANGLE) is triangle
+ assert shapes.getShape(nwStatusShape.NABLA) is nabla
+ assert shapes.getShape(nwStatusShape.DIAMOND) is diamond
+ assert shapes.getShape(nwStatusShape.PENTAGON) is pentagon
+ assert shapes.getShape(nwStatusShape.HEXAGON) is hexagon
+ assert shapes.getShape(nwStatusShape.STAR) is star
+ assert shapes.getShape(nwStatusShape.PACMAN) is pacman
+ assert shapes.getShape(nwStatusShape.BARS_1) is bars1
+ assert shapes.getShape(nwStatusShape.BARS_2) is bars2
+ assert shapes.getShape(nwStatusShape.BARS_3) is bars3
+ assert shapes.getShape(nwStatusShape.BARS_4) is bars4
+ assert shapes.getShape(nwStatusShape.BLOCK_1) is block1
+ assert shapes.getShape(nwStatusShape.BLOCK_2) is block2
+ assert shapes.getShape(nwStatusShape.BLOCK_3) is block3
+ assert shapes.getShape(nwStatusShape.BLOCK_4) is block4
+
+# END Test testCoreStatus_ShapeCache
diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py
index beba03f0..86cc0d50 100644
--- a/tests/test_core/test_core_tokenizer.py
+++ b/tests/test_core/test_core_tokenizer.py
@@ -949,6 +949,15 @@ def testCoreToken_ExtractFormats(mockGUI):
(10, tokens.FMT_B_B), (31, tokens.FMT_I_B), (38, tokens.FMT_B_E), (38, tokens.FMT_I_E)
]
+ # So does this
+ text, fmt = tokens._extractFormats(
+ "Text with [b]bold and overlapping [i]italics[/b] in[/i] it."
+ )
+ assert text == "Text with bold and overlapping italics in it."
+ assert fmt == [
+ (10, tokens.FMT_B_B), (31, tokens.FMT_I_B), (38, tokens.FMT_B_E), (41, tokens.FMT_I_E)
+ ]
+
# END Test testCoreToken_ExtractFormats
diff --git a/tests/test_dialogs/test_dlg_projectsettings.py b/tests/test_dialogs/test_dlg_projectsettings.py
index 9831ea1f..498dfda5 100644
--- a/tests/test_dialogs/test_dlg_projectsettings.py
+++ b/tests/test_dialogs/test_dlg_projectsettings.py
@@ -24,14 +24,13 @@ import pytest
from tools import C, buildTestProject
-from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QDialog, QAction, QColorDialog
from novelwriter import CONFIG, SHARED
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projectsettings import GuiProjectSettings
-from novelwriter.enum import nwItemType
+from novelwriter.enum import nwItemType, nwStatusShape
from novelwriter.types import QtMouseLeft
KEY_DELAY = 1
@@ -169,8 +168,8 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
nwGUI.rebuildTrees()
project.countStatus()
- assert [e["count"] for _, e in project.data.itemStatus.items()] == [2, 0, 2, 1]
- assert [e["count"] for _, e in project.data.itemImport.items()] == [3, 0, 2, 1]
+ assert [e.count for _, e in project.data.itemStatus.iterItems()] == [2, 0, 2, 1]
+ assert [e.count for _, e in project.data.itemImport.iterItems()] == [3, 0, 2, 1]
# Create Dialog
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.PAGE_STATUS)
@@ -182,20 +181,20 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
status = projSettings.statusPage
- assert status.wasChanged is False
- assert status.getNewList() == ([], [])
+ assert status.changed is False
+ assert status.getNewList() == []
assert status.listBox.topLevelItemCount() == 4
# Can't delete the first item (it's in use)
status.listBox.clearSelection()
status.listBox.setCurrentItem(status.listBox.topLevelItem(0))
- qtbot.mouseClick(status.delButton, QtMouseLeft)
+ status.delButton.click()
assert status.listBox.topLevelItemCount() == 4
# Can delete the second item
status.listBox.clearSelection()
status.listBox.setCurrentItem(status.listBox.topLevelItem(1))
- qtbot.mouseClick(status.delButton, QtMouseLeft)
+ status.delButton.click()
assert status.listBox.topLevelItemCount() == 3
# Add a new item
@@ -204,39 +203,38 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
status.addButton.click()
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
status.editName.setText("Final")
- status.colButton.click()
- status.saveButton.click()
+ status.colorButton.click()
+ status._selectShape(nwStatusShape.CIRCLE)
+ status.applyButton.click()
assert status.listBox.topLevelItemCount() == 4
- assert status.wasChanged is True
- assert status.getNewList() == (
- [
- {
- "key": C.sNew,
- "name": "New",
- "cols": (100, 100, 100)
- }, {
- "key": C.sDraft,
- "name": "Draft",
- "cols": (200, 150, 0)
- }, {
- "key": C.sFinished,
- "name": "Finished",
- "cols": (50, 200, 0)
- }, {
- "key": None,
- "name": "Final",
- "cols": (20, 30, 40)
- }
- ], [
- C.sNote # Deleted item
- ]
- )
+ assert status.changed is True
+ update = status.getNewList()
+
+ assert update[0][0] == C.sNew
+ assert update[0][1].name == "New"
+ assert update[0][1].color == QColor(100, 100, 100)
+ assert update[0][1].shape == nwStatusShape.SQUARE
+
+ assert update[1][0] == C.sDraft
+ assert update[1][1].name == "Draft"
+ assert update[1][1].color == QColor(200, 150, 0)
+ assert update[1][1].shape == nwStatusShape.SQUARE
+
+ assert update[2][0] == C.sFinished
+ assert update[2][1].name == "Finished"
+ assert update[2][1].color == QColor(50, 200, 0)
+ assert update[2][1].shape == nwStatusShape.SQUARE
+
+ assert update[3][0] is None
+ assert update[3][1].name == "Final"
+ assert update[3][1].color == QColor(20, 30, 40)
+ assert update[3][1].shape == nwStatusShape.CIRCLE
# Move items, none selected -> no change
status.listBox.clearSelection()
status._moveItem(1)
- assert [x["key"] for x in status.getNewList()[0]] == [
+ assert [x[0] for x in status.getNewList()] == [
C.sNew, C.sDraft, C.sFinished, None
]
@@ -244,7 +242,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
status.listBox.clearSelection()
status.listBox.setCurrentItem(status.listBox.topLevelItem(0))
status._moveItem(-1)
- assert [x["key"] for x in status.getNewList()[0]] == [
+ assert [x[0] for x in status.getNewList()] == [
C.sNew, C.sDraft, C.sFinished, None
]
@@ -252,13 +250,13 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
status.listBox.clearSelection()
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
status._moveItem(-1)
- assert [x["key"] for x in status.getNewList()[0]] == [
+ assert [x[0] for x in status.getNewList()] == [
C.sNew, C.sDraft, None, C.sFinished
]
# Move items, same selected, move down -> allowed
status._moveItem(1)
- assert [x["key"] for x in status.getNewList()[0]] == [
+ assert [x[0] for x in status.getNewList()] == [
C.sNew, C.sDraft, C.sFinished, None
]
@@ -271,62 +269,58 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
# Delete unused entry
importance.listBox.clearSelection()
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(1))
- qtbot.mouseClick(importance.delButton, QtMouseLeft)
+ importance.delButton.click()
assert importance.listBox.topLevelItemCount() == 3
# Add a new entry
with monkeypatch.context() as mp:
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
- qtbot.mouseClick(importance.addButton, QtMouseLeft)
+ importance.addButton.click()
importance.listBox.clearSelection()
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(3))
- for _ in range(8):
- qtbot.keyClick(importance.editName, Qt.Key.Key_Backspace, delay=KEY_DELAY)
- for c in "Final":
- qtbot.keyClick(importance.editName, c, delay=KEY_DELAY)
- qtbot.mouseClick(importance.colButton, QtMouseLeft)
- qtbot.mouseClick(importance.saveButton, QtMouseLeft)
+ importance.editName.setText("Final")
+ importance.colorButton.click()
+ importance._selectShape(nwStatusShape.TRIANGLE)
+ importance.applyButton.click()
assert importance.listBox.topLevelItemCount() == 4
- assert importance.wasChanged is True
- assert importance.getNewList() == (
- [
- {
- "key": C.iNew,
- "name": "New",
- "cols": (100, 100, 100)
- }, {
- "key": C.iMajor,
- "name": "Major",
- "cols": (200, 150, 0)
- }, {
- "key": C.iMain,
- "name": "Main",
- "cols": (50, 200, 0)
- }, {
- "key": None,
- "name": "Final",
- "cols": (20, 30, 40)
- }
- ], [
- C.iMinor # Deleted item
- ]
- )
+ assert importance.changed is True
+ update = importance.getNewList()
+
+ assert update[0][0] == C.iNew
+ assert update[0][1].name == "New"
+ assert update[0][1].color == QColor(100, 100, 100)
+ assert update[0][1].shape == nwStatusShape.SQUARE
+
+ assert update[1][0] == C.iMajor
+ assert update[1][1].name == "Major"
+ assert update[1][1].color == QColor(200, 150, 0)
+ assert update[1][1].shape == nwStatusShape.SQUARE
+
+ assert update[2][0] == C.iMain
+ assert update[2][1].name == "Main"
+ assert update[2][1].color == QColor(50, 200, 0)
+ assert update[2][1].shape == nwStatusShape.SQUARE
+
+ assert update[3][0] is None
+ assert update[3][1].name == "Final"
+ assert update[3][1].color == QColor(20, 30, 40)
+ assert update[3][1].shape == nwStatusShape.TRIANGLE
# Check Project
projSettings._doSave()
- statusItems = dict(project.data.itemStatus.items())
- assert statusItems[C.sNew]["name"] == "New"
- assert statusItems[C.sDraft]["name"] == "Draft"
- assert statusItems[C.sFinished]["name"] == "Finished"
- assert statusItems["s000013"]["name"] == "Final"
+ statusItems = dict(project.data.itemStatus.iterItems())
+ assert statusItems[C.sNew].name == "New"
+ assert statusItems[C.sDraft].name == "Draft"
+ assert statusItems[C.sFinished].name == "Finished"
+ assert statusItems["s000013"].name == "Final"
- importItems = dict(project.data.itemImport.items())
- assert importItems[C.iNew]["name"] == "New"
- assert importItems[C.iMajor]["name"] == "Major"
- assert importItems[C.iMain]["name"] == "Main"
- assert importItems["i000014"]["name"] == "Final"
+ importItems = dict(project.data.itemImport.iterItems())
+ assert importItems[C.iNew].name == "New"
+ assert importItems[C.iMajor].name == "Major"
+ assert importItems[C.iMain].name == "Main"
+ assert importItems["i000014"].name == "Final"
# qtbot.stop()
@@ -363,7 +357,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# Nothing to save or delete
replace.listBox.clearSelection()
- replace._saveEntry()
+ replace._applyChanges()
replace._delEntry()
assert replace.listBox.topLevelItemCount() == 2
@@ -381,7 +375,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
replace.editValue.setText("")
for c in "With This Stuff ":
qtbot.keyClick(replace.editValue, c, delay=KEY_DELAY)
- qtbot.mouseClick(replace.saveButton, QtMouseLeft)
+ qtbot.mouseClick(replace.applyButton, QtMouseLeft)
assert replace.listBox.topLevelItem(2).text(0) == "" # type: ignore
assert replace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore
diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py
index cfcb8f68..be409df2 100644
--- a/tests/test_gui/test_gui_doceditor.py
+++ b/tests/test_gui/test_gui_doceditor.py
@@ -880,13 +880,13 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Wrap Equal
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._wrapSelection("=") is True
+ nwGUI.docEditor._wrapSelection("=")
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur=")
# Wrap Unequal
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._wrapSelection("=", "*") is True
+ nwGUI.docEditor._wrapSelection("=", "*")
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur*")
# Past Paragraph
@@ -895,7 +895,7 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
cursor.setPosition(13, QTextCursor.MoveAnchor)
cursor.setPosition(1000, QTextCursor.KeepAnchor)
nwGUI.docEditor.setTextCursor(cursor)
- assert nwGUI.docEditor._wrapSelection("=") is True
+ nwGUI.docEditor._wrapSelection("=")
newText = nwGUI.docEditor.getText()
newPara = list(filter(str.strip, newText.split("\n")))
@@ -910,14 +910,15 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Block format repetition
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(39)
- assert nwGUI.docEditor._toggleFormat(1, "=") is True
+ nwGUI.docEditor._toggleFormat(1, "=")
+ assert nwGUI.docEditor.getText() == text.replace("amet", "=amet=", 1)
+ nwGUI.docEditor._toggleFormat(1, "=")
assert nwGUI.docEditor.getText() == text.replace("amet", "=amet=", 1)
- assert nwGUI.docEditor._toggleFormat(1, "=") is False
# Wrap Single Equal
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._toggleFormat(1, "=") is True
+ nwGUI.docEditor._toggleFormat(1, "=")
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur=")
# Past Paragraph
@@ -926,7 +927,7 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
cursor.setPosition(13, QTextCursor.MoveAnchor)
cursor.setPosition(1000, QTextCursor.KeepAnchor)
nwGUI.docEditor.setTextCursor(cursor)
- assert nwGUI.docEditor._toggleFormat(1, "=") is True
+ nwGUI.docEditor._toggleFormat(1, "=")
newText = nwGUI.docEditor.getText()
newPara = list(filter(str.strip, newText.split("\n")))
@@ -936,30 +937,40 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Wrap Double Equal
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._toggleFormat(2, "=") is True
+ nwGUI.docEditor._toggleFormat(2, "=")
assert nwGUI.docEditor.getText() == text.replace("consectetur", "==consectetur==")
+ # Toggle Double Equal with Selection
+ nwGUI.docEditor.replaceText(text)
+ nwGUI.docEditor.setCursorSelection(41, 11)
+ nwGUI.docEditor._toggleFormat(2, "=")
+ assert nwGUI.docEditor.getText() == text.replace("consectetur", "==consectetur==")
+ assert nwGUI.docEditor.getSelectedText() == "consectetur"
+ nwGUI.docEditor._toggleFormat(2, "=")
+ assert nwGUI.docEditor.getText() == text
+ assert nwGUI.docEditor.getSelectedText() == "consectetur"
+
# Toggle Double Equal
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._toggleFormat(2, "=") is True
- assert nwGUI.docEditor._toggleFormat(2, "=") is True
+ nwGUI.docEditor._toggleFormat(2, "=")
+ nwGUI.docEditor._toggleFormat(2, "=")
assert nwGUI.docEditor.getText() == text
# Toggle Triple+Double Equal
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._toggleFormat(3, "=") is True
- assert nwGUI.docEditor._toggleFormat(2, "=") is True
+ nwGUI.docEditor._toggleFormat(3, "=")
+ nwGUI.docEditor._toggleFormat(2, "=")
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur=")
# Toggle Unequal
repText = text.replace("consectetur", "=consectetur==")
nwGUI.docEditor.replaceText(repText)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._toggleFormat(1, "=") is True
+ nwGUI.docEditor._toggleFormat(1, "=")
assert nwGUI.docEditor.getText() == text.replace("consectetur", "consectetur=")
- assert nwGUI.docEditor._toggleFormat(1, "=") is True
+ nwGUI.docEditor._toggleFormat(1, "=")
assert nwGUI.docEditor.getText() == repText
# Replace Quotes
@@ -969,7 +980,8 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
text = "### A Scene\n\n%s" % ipsumText[0].replace("consectetur", "=consectetur=")
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
- assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is False
+ nwGUI.docEditor._replaceQuotes("=", "<", ">")
+ assert nwGUI.docEditor.getText() == text
# First Paragraph Selected
# This should not replace anything in second paragraph
@@ -977,7 +989,7 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA)
- assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is True
+ nwGUI.docEditor._replaceQuotes("=", "<", ">")
newText = nwGUI.docEditor.getText()
newPara = list(filter(str.strip, newText.split("\n")))
@@ -989,7 +1001,7 @@ def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
nwGUI.docEditor.replaceText(text)
nwGUI.docEditor.setCursorPosition(45)
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
- assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is True
+ nwGUI.docEditor._replaceQuotes("=", "<", ">")
assert nwGUI.docEditor.getText() == text.replace("=Lorem=", "")
# Remove Line Breaks
diff --git a/tests/test_gui/test_gui_theme.py b/tests/test_gui/test_gui_theme.py
index ad2d7040..0490eabf 100644
--- a/tests/test_gui/test_gui_theme.py
+++ b/tests/test_gui/test_gui_theme.py
@@ -186,11 +186,11 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI):
# Check a few values
assert mainTheme._guiPalette.color(
- QPalette.ColorRole.Window).getRgb() == (239, 239, 239, 255)
+ QPalette.ColorRole.Window).getRgb() == (239, 239, 239, 255)
assert mainTheme._guiPalette.color(
- QPalette.ColorRole.WindowText).getRgb() == (0, 0, 0, 255)
+ QPalette.ColorRole.WindowText).getRgb() == (0, 0, 0, 255)
assert mainTheme._guiPalette.color(
- QPalette.ColorRole.Base).getRgb() == (255, 255, 255, 255)
+ QPalette.ColorRole.Base).getRgb() == (255, 255, 255, 255)
assert mainTheme._guiPalette.color(
QPalette.ColorRole.AlternateBase).getRgb() == (239, 239, 239, 255)
diff --git a/tests/test_text/test_core_counting.py b/tests/test_text/test_core_counting.py
index bab0596e..05780c49 100644
--- a/tests/test_text/test_core_counting.py
+++ b/tests/test_text/test_core_counting.py
@@ -29,7 +29,7 @@ from novelwriter.text.counting import bodyTextCounter, preProcessText, standardC
def testTextCounting_preProcessText():
"""Test the text preprocessor for counters."""
# Not Text
- assert preProcessText(None) == []
+ assert preProcessText(None) == [] # type: ignore
# No Text
assert preProcessText("") == []
@@ -81,6 +81,10 @@ def testTextCounting_standardCounter():
assert standardCounter(None) == (0, 0, 0) # type: ignore
assert standardCounter(1234) == (0, 0, 0) # type: ignore
+ # Test Corner Cases, Bug #1816
+ assert standardCounter("> ") == (0, 0, 0)
+ assert standardCounter(" <") == (0, 0, 0)
+
# General Text
cC, wC, pC = standardCounter((
"#! Title\n\n"
@@ -88,7 +92,8 @@ def testTextCounting_standardCounter():
"# Heading One\n"
"## Heading Two\n"
"### Heading Three\n"
- "#### Heading Four\n\n"
+ "###! Heading Four\n"
+ "#### Heading Five\n\n"
"@tag: value\n\n"
"% A comment that should not be counted.\n\n"
"The first paragraph.\n\n"
@@ -96,8 +101,8 @@ def testTextCounting_standardCounter():
"The third paragraph.\n\n"
"Dashes\u2013and even longer\u2014dashes."
))
- assert cC == 151
- assert wC == 24
+ assert cC == 163
+ assert wC == 26
assert pC == 4
# Text Alignment
@@ -182,7 +187,7 @@ def testTextCounting_standardCounter():
def testTextCounting_bodyTextCounter():
"""Test the body text counter."""
# Not Text
- assert bodyTextCounter(None) == (0, 0, 0)
+ assert bodyTextCounter(None) == (0, 0, 0) # type: ignore
# General Text
wC, cC, sC = bodyTextCounter((
diff --git a/tests/test_tools/test_tools_manuscript.py b/tests/test_tools/test_tools_manuscript.py
index 0d2e04ca..5e80767f 100644
--- a/tests/test_tools/test_tools_manuscript.py
+++ b/tests/test_tools/test_tools_manuscript.py
@@ -212,7 +212,7 @@ def testManuscript_Features(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
build = manus._builds.getBuild(first.data(GuiManuscript.D_KEY))
assert isinstance(build, BuildSettings)
build.setValue("headings.fmtScene", nwHeadFmt.TITLE)
- build.setValue("headings.fmtHardScene", nwHeadFmt.TITLE)
+ build.setValue("headings.fmtAltScene", nwHeadFmt.TITLE)
manus._builds.setBuild(build)
# Preview again, and allow cache file to be created
diff --git a/tests/test_tools/test_tools_manussettings.py b/tests/test_tools/test_tools_manussettings.py
index 179df303..3f51c9cd 100644
--- a/tests/test_tools/test_tools_manussettings.py
+++ b/tests/test_tools/test_tools_manussettings.py
@@ -335,7 +335,7 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
build.setValue("headings.fmtChapter", chTitle)
build.setValue("headings.fmtUnnumbered", unTitle)
build.setValue("headings.fmtScene", scTitle)
- build.setValue("headings.fmtHardScene", shTitle)
+ build.setValue("headings.fmtAltScene", shTitle)
build.setValue("headings.fmtSection", sxTitle)
build.setValue("headings.hideScene", False)
build.setValue("headings.hideSection", False)
@@ -354,7 +354,7 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
assert headTab.fmtChapter.text() == chTitle
assert headTab.fmtUnnumbered.text() == unTitle
assert headTab.fmtScene.text() == scTitle
- assert headTab.fmtHScene.text() == shTitle
+ assert headTab.fmtAScene.text() == shTitle
assert headTab.fmtSection.text() == sxTitle
assert headTab.swtScene.isChecked() is False
assert headTab.swtSection.isChecked() is False
@@ -401,7 +401,7 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
assert headTab.editTextBox.isEnabled() is False
# Hard Scene
- headTab.btnHScene.click()
+ headTab.btnAScene.click()
assert headTab._editing == headTab.EDIT_HSCENE
assert headTab.editTextBox.isEnabled() is True
assert headTab.editTextBox.toPlainText() == shTitle
@@ -467,10 +467,10 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
headTab.btnApply.click()
assert build.getStr("headings.fmtScene") == nwHeadFmt.TITLE
- headTab.btnHScene.click()
+ headTab.btnAScene.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click()
- assert build.getStr("headings.fmtHardScene") == nwHeadFmt.TITLE
+ assert build.getStr("headings.fmtAltScene") == nwHeadFmt.TITLE
headTab.btnSection.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)