Compare commits
276 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5064d6237 | ||
|
|
483a9aedfd | ||
|
|
98f94efbdd | ||
|
|
3335c08a42 | ||
|
|
013e94afc9 | ||
|
|
7829c894ff | ||
|
|
924bf67bfb | ||
|
|
7aea468103 | ||
|
|
9336410180 | ||
|
|
22162d3915 | ||
|
|
edf4256648 | ||
|
|
29f9ea2a55 | ||
|
|
8d614c2aeb | ||
|
|
604df85e62 | ||
|
|
075a9bf850 | ||
|
|
02f20b7a4f | ||
|
|
5df369a831 | ||
|
|
ead1740f83 | ||
|
|
791eebe8a6 | ||
|
|
5178c41e54 | ||
|
|
4c55bfe49f | ||
|
|
7137494740 | ||
|
|
a3bd0d38a4 | ||
|
|
925d9fd5ad | ||
|
|
d12e626bc4 | ||
|
|
c182cc4383 | ||
|
|
750ba36d32 | ||
|
|
72b14ff08c | ||
|
|
4ac050fe75 | ||
|
|
311b611afe | ||
|
|
37e2392237 | ||
|
|
a1e6398116 | ||
|
|
b0b1c0d386 | ||
|
|
56ed4b88f8 | ||
|
|
5f79186d10 | ||
|
|
87a1bb050e | ||
|
|
de16d2d6a1 | ||
|
|
4fa21107ee | ||
|
|
97371533bc | ||
|
|
c3cf69cbe6 | ||
|
|
1353eaa460 | ||
|
|
4273ba5158 | ||
|
|
368cbdc62c | ||
|
|
b0614e8c25 | ||
|
|
6ef646acb2 | ||
|
|
6db7f049bc | ||
|
|
404b102a54 | ||
|
|
ef84588145 | ||
|
|
eb105951ac | ||
|
|
9f6a1b4bc0 | ||
|
|
dfd4df46ce | ||
|
|
3c8e3b3653 | ||
|
|
6500975cc3 | ||
|
|
2fb87c415e | ||
|
|
996f48a85f | ||
|
|
1c91ba6fd9 | ||
|
|
10764a2041 | ||
|
|
576c13d045 | ||
|
|
f0e86dad10 | ||
|
|
4bf82e4df7 | ||
|
|
eab8bb2351 | ||
|
|
02d805122f | ||
|
|
595fd01863 | ||
|
|
7b8b303886 | ||
|
|
8b701f8651 | ||
|
|
e6071e613b | ||
|
|
75de1a1888 | ||
|
|
f25c172705 | ||
|
|
b741b21db8 | ||
|
|
a350d38d4e | ||
|
|
2e55d28a10 | ||
|
|
76864f2a2f | ||
|
|
25b6d1c001 | ||
|
|
1a635f60a9 | ||
|
|
c6524d02ea | ||
|
|
ed7dde2aaa | ||
|
|
5917c9da08 | ||
|
|
3a1f77b8f8 | ||
|
|
bdf100e608 | ||
|
|
f8d57539d4 | ||
|
|
453a228182 | ||
|
|
ea14f06245 | ||
|
|
5e9d96788e | ||
|
|
b793842f6d | ||
|
|
827702159d | ||
|
|
17aae16345 | ||
|
|
16d2f1cc2a | ||
|
|
e778d05ee3 | ||
|
|
fb92c6c333 | ||
|
|
701fe29de9 | ||
|
|
642b5e3681 | ||
|
|
81a9d51986 | ||
|
|
e6efd15b9a | ||
|
|
f883de6e03 | ||
|
|
dc661b7a70 | ||
|
|
edfb4e60c6 | ||
|
|
e185f87875 | ||
|
|
ce3a8f56c8 | ||
|
|
23b7baf62c | ||
|
|
56cb545fab | ||
|
|
71c4d7aa94 | ||
|
|
c8b1a10f26 | ||
|
|
e9e4788602 | ||
|
|
2f20266602 | ||
|
|
1c3b252b6b | ||
|
|
bc2f91e54f | ||
|
|
8a41748173 | ||
|
|
c3e6b4e10a | ||
|
|
9c1bdef9ad | ||
|
|
fe22949cb4 | ||
|
|
a9bda1deed | ||
|
|
492dd58068 | ||
|
|
186cead603 | ||
|
|
467772c489 | ||
|
|
8ff292fec8 | ||
|
|
4113be7a4c | ||
|
|
0aaf42de21 | ||
|
|
d9a5db5884 | ||
|
|
fe8a112328 | ||
|
|
bbb23b4bc9 | ||
|
|
c420d8ae38 | ||
|
|
e78545fcdb | ||
|
|
0a1c5b20ed | ||
|
|
321e57a3b7 | ||
|
|
4d016c3b75 | ||
|
|
d5064a8333 | ||
|
|
a11657c145 | ||
|
|
64889ac5d8 | ||
|
|
8af720cbd6 | ||
|
|
68a6390868 | ||
|
|
a8f61f2b98 | ||
|
|
c866491910 | ||
|
|
c1a14e7aa5 | ||
|
|
27fa356846 | ||
|
|
54f36d4e8e | ||
|
|
3b9ef47e0f | ||
|
|
d504c0a7ec | ||
|
|
58fa3e64ec | ||
|
|
b0f1db0aea | ||
|
|
90dda43205 | ||
|
|
d20af1283c | ||
|
|
2cd6cc299e | ||
|
|
a227c58d55 | ||
|
|
d23fdf28ad | ||
|
|
9803dc16fd | ||
|
|
7fcc9a3928 | ||
|
|
ec7f50b566 | ||
|
|
76ab9c3908 | ||
|
|
e6193ed858 | ||
|
|
5b4ccad19b | ||
|
|
e1531009d3 | ||
|
|
ca188fee08 | ||
|
|
c5edbbfaf6 | ||
|
|
f15563cf6e | ||
|
|
e5c6dae080 | ||
|
|
6952211607 | ||
|
|
b396cff9a7 | ||
|
|
ca7ae6b666 | ||
|
|
cbf5f1ead3 | ||
|
|
a48a4e5674 | ||
|
|
1ec0bff159 | ||
|
|
116409bb8d | ||
|
|
0e41a48ad1 | ||
|
|
f732d13807 | ||
|
|
8e4e6903b1 | ||
|
|
97b791511f | ||
|
|
100989030d | ||
|
|
6c75bdecdc | ||
|
|
51314804bf | ||
|
|
6b106599dd | ||
|
|
82d5899f2e | ||
|
|
11c4428a8a | ||
|
|
c0a5d257ac | ||
|
|
6a30ce5fe3 | ||
|
|
3ba345acc8 | ||
|
|
b27589d3d6 | ||
|
|
855c525670 | ||
|
|
d113b3feb3 | ||
|
|
7a2d239aaa | ||
|
|
a76268743f | ||
|
|
7d9ca66fd6 | ||
|
|
e752a97ea7 | ||
|
|
ed3e513544 | ||
|
|
d6eafccde6 | ||
|
|
119d572050 | ||
|
|
7a1fab0c0a | ||
|
|
de6fd65664 | ||
|
|
838a2a8062 | ||
|
|
e03305d601 | ||
|
|
4c5f68242a | ||
|
|
40a8d728bb | ||
|
|
a7a2eaff55 | ||
|
|
3761b1729f | ||
|
|
0dc4719400 | ||
|
|
c6ee6746a2 | ||
|
|
ee21045ee2 | ||
|
|
df4993c38e | ||
|
|
d73763b1f7 | ||
|
|
10012054be | ||
|
|
b6c9f1160f | ||
|
|
483528b3b2 | ||
|
|
4ef40f2c4d | ||
|
|
23713bb791 | ||
|
|
6622a7e8e0 | ||
|
|
cc4d92f0f7 | ||
|
|
a14e544e78 | ||
|
|
269fa700b0 | ||
|
|
bd45619e2f | ||
|
|
97384ee4bd | ||
|
|
415f1a1d1a | ||
|
|
b33a3b480d | ||
|
|
9f2c5379b7 | ||
|
|
f35722ab35 | ||
|
|
8a8a218e6b | ||
|
|
fe0098ebbc | ||
|
|
ca02bbf20d | ||
|
|
e10140b1cd | ||
|
|
b92112e933 | ||
|
|
82c63723a4 | ||
|
|
ce2ac9b1a1 | ||
|
|
97ea3ebc16 | ||
|
|
ae7c19f923 | ||
|
|
3135bee47d | ||
|
|
c15ffb0813 | ||
|
|
a2a62f8b26 | ||
|
|
ca4aa75880 | ||
|
|
427a910ec0 | ||
|
|
beb01d572f | ||
|
|
16461279cf | ||
|
|
f40722d196 | ||
|
|
34eb39e10d | ||
|
|
2cb772847e | ||
|
|
7b5b82136f | ||
|
|
fa9d0f3503 | ||
|
|
4afbd98d6f | ||
|
|
944e2455d5 | ||
|
|
2504965f8d | ||
|
|
34fcbdd350 | ||
|
|
671d7130e1 | ||
|
|
1fbd50334d | ||
|
|
1ed3cea414 | ||
|
|
978a8145a8 | ||
|
|
1482451e87 | ||
|
|
5c99e2186a | ||
|
|
10435945db | ||
|
|
df95cd85e8 | ||
|
|
05e9729c22 | ||
|
|
bc17305c4f | ||
|
|
7feb3ec2f9 | ||
|
|
25686e46a8 | ||
|
|
53228faaa3 | ||
|
|
bbabc41ac2 | ||
|
|
18b3edbf87 | ||
|
|
63fd7bf06a | ||
|
|
ba538920aa | ||
|
|
8b6691c056 | ||
|
|
41b7495a06 | ||
|
|
a71d0d8cfe | ||
|
|
956abf3e8e | ||
|
|
054d4f965e | ||
|
|
f26b405f42 | ||
|
|
8a61d57ce4 | ||
|
|
818fa2bf11 | ||
|
|
856c14ce76 | ||
|
|
f9089fd506 | ||
|
|
904f954d4d | ||
|
|
356ecde88a | ||
|
|
ded91bc3b0 | ||
|
|
6d769cb512 | ||
|
|
ecaaffb029 | ||
|
|
8952bbab1d | ||
|
|
3fe2ee3058 | ||
|
|
f2b35d798b | ||
|
|
d11a8ee789 | ||
|
|
5278eb7895 | ||
|
|
4f42407c23 |
32
.github/workflows/build.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
sudo apt install qttools5-dev-tools latexmk texlive texlive-latex-extra
|
||||
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Packages (pip)
|
||||
run: pip install -r docs/source/requirements.txt
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
run: python pkgutils.py qtlrelease sample manual
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nw-assets
|
||||
path: |
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
linux-tag: ["manylinux_2_24_x86_64", "manylinux_2_28_x86_64"]
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
@@ -55,10 +55,10 @@ jobs:
|
||||
run: pip install python-appimage
|
||||
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: nw-assets
|
||||
path: novelwriter/assets
|
||||
@@ -67,12 +67,10 @@ jobs:
|
||||
run: python pkgutils.py build-appimage --linux-tag ${{ matrix.linux-tag }} --python-version ${{ matrix.python-version }}
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: novelWriter-Packages
|
||||
path: |
|
||||
dist_appimage/novelWriter*.AppImage
|
||||
dist_appimage/novelWriter*.AppImage.sha256
|
||||
name: AppImage-${{ matrix.python-version }}-${{ matrix.linux-tag }}
|
||||
path: dist_appimage
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
@@ -81,10 +79,10 @@ jobs:
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: nw-assets
|
||||
path: novelwriter/assets
|
||||
@@ -93,11 +91,9 @@ jobs:
|
||||
run: ./setup/macos/build.sh
|
||||
|
||||
- name: Upload DMG
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: novelWriter-Packages
|
||||
path: |
|
||||
dist_macos/novelWriter*.dmg
|
||||
dist_macos/novelWriter*.dmg.sha256
|
||||
name: MacOS-DMG
|
||||
path: dist_macos
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
6
.github/workflows/syntax.yml
vendored
@@ -15,14 +15,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3
|
||||
architecture: x64
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install flake8
|
||||
run: pip install flake8
|
||||
run: pip install flake8 flake8-pep585
|
||||
- name: Syntax Check
|
||||
run: |
|
||||
flake8 --version
|
||||
|
||||
4
.github/workflows/test_linux.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt install libenchant-2-dev qttools5-dev-tools aspell-en
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Dependencies (pip)
|
||||
run: |
|
||||
pip install -U -r requirements.txt -r tests/requirements.txt
|
||||
|
||||
4
.github/workflows/test_mac.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
run: |
|
||||
brew install enchant
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Dependencies (pip)
|
||||
run: |
|
||||
pip install -U pyobjc -r requirements.txt -r tests/requirements.txt
|
||||
|
||||
4
.github/workflows/test_win.yml
vendored
@@ -15,12 +15,12 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Dependencies (pip)
|
||||
run: |
|
||||
pip install -U -r requirements.txt -r tests/requirements.txt
|
||||
|
||||
181
CHANGELOG.md
@@ -1,5 +1,186 @@
|
||||
# novelWriter Changelog
|
||||
|
||||
## Version 2.3 [2024-03-10]
|
||||
|
||||
### Release Notes
|
||||
|
||||
This release introduces a new Welcome dialog that replaces both the previous Open Project dialog
|
||||
and the New Project Wizard. The Welcome dialog has received a friendly custom art design created by
|
||||
Louis Durrant.
|
||||
|
||||
The Preferences, Project Settings, Project Details, and About dialogs have received completely new
|
||||
designs and layouts, and the Manuscript Build Settings dialog has been updated to match.
|
||||
|
||||
Among new features is a new Templates root folder where the writer can store template documents to
|
||||
be used when creating new project documents and notes. It is now also possible to include Point of
|
||||
View and Focus character names in chapter titles in Manuscripts. A new feature to ignore text has
|
||||
also been added. It behaves similarly to comments, but is never included in a manuscript even if
|
||||
comments in the manuscript are enabled.
|
||||
|
||||
See the website for complete [Release Notes](https://novelwriter.io/releases/release_2_3.html).
|
||||
|
||||
### Detailed Changelog
|
||||
|
||||
**Bugfixes**
|
||||
|
||||
* Fix an issue where the Tags and References panel below the document viewer was not cleared if a
|
||||
project was opened, closed, and a new project with no tags defined were opened again. PR #1720.
|
||||
Issue #1718.
|
||||
* Fixed an issue where multi-selecting documents in Trash would give the option to again move items
|
||||
to Trash. The menu now properly offers the option to permanently delete the documents. PR #1728.
|
||||
* Fixed an issue where multi-select for deletion would only process the item right-clicked, and not
|
||||
all the selected items. PR #1728.
|
||||
* Changed the error message that pops up when trying to open a project from the Welcome dialog that
|
||||
no longer exists. The error message should no indicate that the project was not found, as opposed
|
||||
to unreadable. PR #1740. Issue #1737.
|
||||
|
||||
**Improvements**
|
||||
|
||||
* The build tool no longer inserts a scene separator immediately after a partition heading, and
|
||||
should now behave the same way as for chapters. If there is text before the first scene break,
|
||||
the text flows continuously without a separator. PR #1716. Issue #1704.
|
||||
* Added one new GUI theme named "Cyberpunk Night" by @alemvigh. Also added a matching "Cyberpunk
|
||||
Night" document editor theme by @alemvigh, and a new Tango theme by @vkbo. PR #1738. Issue #1730.
|
||||
* When the Welcome dialog fails to open a project, and there is not already a project open in the
|
||||
app, the Welcome dialog appears again. PR #1740. Issue #1737.
|
||||
|
||||
**Internationalisation**
|
||||
|
||||
* Norwegian and US English translations updated by @vkbo. PR #1714.
|
||||
* Japanese translation updated by @hebekeg. PR #1714.
|
||||
* Italian translation updated by Lou Cyper (loucyper1). PR #1714.
|
||||
* Dutch translation updated by Annelotte and Martijn van der Kleijn (mvdkleijn). PR #1714.
|
||||
* Latin American Spanish translation updated by @tmarplatt. PR #1741.
|
||||
* German translation updated by Myian (@HeyMyian). PR #1741.
|
||||
|
||||
**Other Changes**
|
||||
|
||||
* Removed the novelwriter-cli entry point that was in any case broken. PR #1734. Issue #1732.
|
||||
|
||||
----
|
||||
|
||||
## Version 2.3 RC 1 [2024-02-24]
|
||||
|
||||
### Release Notes
|
||||
|
||||
This is a release candidate of the next release version, and is intended for testing purposes.
|
||||
Please be careful when using this version on live writing projects, and make sure you take frequent
|
||||
backups.
|
||||
|
||||
Please check the changelog for an overview of changes. The full release notes will be added to the
|
||||
final release.
|
||||
|
||||
### Detailed Changelog
|
||||
|
||||
**Improvements**
|
||||
|
||||
* Redesign the buttons on the new Welcome dialog so that they only show buttons related to the
|
||||
visible page. Drop the additional buttons on the New Project page. Issue #1706.
|
||||
PRs #1707 and #1709.
|
||||
* Drop the Language setting on the Welcome dialog's New Project page. PR #1707.
|
||||
* Hide the additional settings for Fresh Projects on the New Project page of the new Welcome
|
||||
dialog. Issue #1705. PR #1707.
|
||||
* Update the Templates root folder icon. PR #1709.
|
||||
* Scene separators are now hidden after a new title in manuscript builds, also when there are no
|
||||
chapters. Issue #1704. PR #1711.
|
||||
* Scene and chapter counters are now reset when a novel title is encountered. PR #1711.
|
||||
|
||||
----
|
||||
|
||||
## Version 2.3 Beta 1 [2024-02-16]
|
||||
|
||||
### Release Notes
|
||||
|
||||
This is a beta release of the next release version, and is intended for testing purposes. Please be
|
||||
careful when using this version on live writing projects, and make sure you take frequent backups.
|
||||
|
||||
Please check the changelog for an overview of changes. The full release notes will be added to the
|
||||
final release.
|
||||
|
||||
### Detailed Changelog
|
||||
|
||||
**Major Features**
|
||||
|
||||
* A new Welcome dialog has been added. The dialog replaces the Open Project dialog and the New
|
||||
Project Wizard. The Welcome dialog features artwork created by Louis Durrant, and a custom design
|
||||
for the project list. New projects can be created by a form available from the same dialog, and
|
||||
features a simplified set of options. Issue #1506. PRs #1647, #1681 and #1689.
|
||||
* It is now possible to create a new project by copying the content of another project, or a Zip
|
||||
file of a project, including a backup. This option is available from the New Project feature of
|
||||
the Welcome dialog. Issue #841. PRs #1680 and #1684.
|
||||
* The Preferences dialog has been completely redesigned. All options are now available in a single,
|
||||
scrollable list with appropriate section headers. All sections are available as navigation
|
||||
buttons along the side, and it is also possible to search for settings in a search box at the
|
||||
top. The design matches that created for the Manuscript Build Settings dialog added in 2.1.
|
||||
Issues #1603 and #1604. PR #1652.
|
||||
* The Project Details dialog has been redesigned to match the other new dialogs. It has also been
|
||||
modified to properly handle multiple novel folders. The novel selector is placed at the top of
|
||||
the dialog, and affects all data in the tabs. The Novel Title info has been removed. PR #1665.
|
||||
* The Manuscript Build Settings dialog has been updated to use the new config layout classes, which
|
||||
are more flexible in terms of content flow. PR #1674.
|
||||
* A new root folder type called "Templates" has been added. Any document added here will show up in
|
||||
the Add Item menu in the Project Tree view under a "From Template" submenu. Selecting such an
|
||||
entry will create a new document at the selected location in the project, and populate it with
|
||||
the content of the template file. Issues #996 and #1125. PR #1688.
|
||||
* The About novelWriter dialog has been simplified to only show some key information and the
|
||||
credits text. A link to the releases page is available for checking release notes. Keeping the
|
||||
release notes online means it is easier to update them, and make them more visually interesting
|
||||
as the formatting of the dialog box is limited. PR #1695.
|
||||
* The old Check for Updates dialog has been removed. Checking for new releases of novelWriter can
|
||||
be done directly in the Welcome dialog or the About dialog by clicking "Check Now" next to the
|
||||
"Latest Version" label. PR #1696.
|
||||
|
||||
**Minor Features**
|
||||
|
||||
* A new drop down menu in the References panel below the Document Viewer has an option to filter
|
||||
out inactive notes in the various tag lists in the tabs on the panel. Issue #1653. PR #1654.
|
||||
* The Novel Title fields, which no longer makes much sense after it was possible to add multiple
|
||||
novel folders to a project, has been fully dropped. Issue #1655. PR #1669.
|
||||
* The document header for ODT manuscript files can now be customised. Issue #1505. PR #1675.
|
||||
* The Manuscript Build tool can now insert characters (Point of View or Focus) into chapter headers
|
||||
and other headers. By default, it inserts the tag value, but the display name for a tag can be
|
||||
set with a `|` character in the `@tag` definition if a different text is desired. Issue #1468.
|
||||
PR #1677.
|
||||
* A new modified comment format for the editor has been added. Instead of the regular comment using
|
||||
`%`, this one uses `%~`. The only difference is that the latter will never be exported to a
|
||||
manuscript at all, while regular comments can be exported when a setting is enabled. Issue #1075.
|
||||
PR #1690.
|
||||
* It is now possible to change a document's label from the first heading in the document by
|
||||
right-clicking on it and selecting "Rename to heading". Issue #1443. PR #1692.
|
||||
* The content of the Project Word List can now be exported and imported using plain text files.
|
||||
Issue #1560. PR #1691.
|
||||
* The content of the Outline View can now be exported to a CSV file to be opened in any spreadsheet
|
||||
application. Issue #1507. PR #1697.
|
||||
|
||||
**Usability**
|
||||
|
||||
* A Create New submenu in the Project Tree context menu has been added, which give quick access to
|
||||
the items at the top level of the Add Item menu. Issue #1519. PR #1679.
|
||||
* When multiple paragraphs are selected in the editor, and any of the comment features are toggled,
|
||||
or formatting is reset, the action is applied to all selected paragraphs, not just the first.
|
||||
Issue #1042 and #1687. PR #1690.
|
||||
|
||||
**Packaging**
|
||||
|
||||
* The in-app version format is now identical to the version tag, and the format for Ubuntu
|
||||
pre-release packages has been updated so that they are compatible with release packages. That is,
|
||||
if you add both the release and pre-release repos from Launchpad, release packages will now
|
||||
properly replace pre-release packages when running apt upgrade. PR #1659.
|
||||
|
||||
**Code Improvements**
|
||||
|
||||
* The Storage class has been refactored. It is the class that handles the project storage folder
|
||||
for a novelWriter project. The refactoring is a step towards allowing single file storage for
|
||||
projects as an alternative to project folders. Issue #977. PR #1635.
|
||||
* All theme colours are now proper QColor objects from the start, which avoids the need to create
|
||||
a large number of these where they are used. PR #1656.
|
||||
* A nwProject.bak file is no longer kept in the project folder. It never really served any purpose.
|
||||
The project file is still written to a temp file before the old file is replaced, which prevents
|
||||
partial overwrites. PR #1670.
|
||||
* Other minor code improvements in PRs #1693 and 1694.
|
||||
|
||||
----
|
||||
|
||||
## Version 2.2.1 [2024-01-27]
|
||||
|
||||
### Release Notes
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
For other contributions, see the project's [Contributors](https://github.com/vkbo/novelWriter/graphs/contributors) page.
|
||||
|
||||
## Artwork
|
||||
|
||||
The artwork on the Welcome dialog was created by [Louis Durrant](https://louisdurrant.art).
|
||||
|
||||
## Translations
|
||||
|
||||
The default language is English (UK) with English (US) as an option. These are the original
|
||||
|
||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
BIN
docs/source/images/fig_welcome.jpg
Normal file
|
After Width: | Height: | Size: 150 KiB |
@@ -5,9 +5,9 @@ novelWriter User Guide
|
||||
| **Release Version:** |release|
|
||||
| **Updated:** |today|
|
||||
|
||||
novelWriter is an open source plain text editor designed for writing novels assembled from many
|
||||
smaller text documents. It uses a minimal formatting syntax inspired by Markdown, and adds a meta
|
||||
data syntax for comments, synopsis, and cross-referencing. It is designed to be a simple text
|
||||
novelWriter is an open source plain text editor designed for writing novels assembled from
|
||||
individual text documents. It uses a minimal formatting syntax inspired by Markdown, and adds a
|
||||
meta data syntax for comments, synopsis, and cross-referencing. It is designed to be a simple text
|
||||
editor that allows for easy organisation of text and notes, using human readable text files as
|
||||
storage for robustness.
|
||||
|
||||
@@ -23,13 +23,14 @@ See the :ref:`a_breakdown_storage` section 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 also be run directly from the Python source, or installed from packages or
|
||||
with pip. See :ref:`a_started` for more details.
|
||||
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.
|
||||
|
||||
**Useful Links**
|
||||
|
||||
* Website: https://novelwriter.io
|
||||
* Documentation: https://docs.novelwriter.io
|
||||
* Public Releases: https://releases.novelwriter.io
|
||||
* Internationalisation: https://crowdin.com/project/novelwriter
|
||||
* Source Code: https://github.com/vkbo/novelWriter
|
||||
* Source Releases: https://github.com/vkbo/novelWriter/releases
|
||||
@@ -66,7 +67,6 @@ with pip. See :ref:`a_started` for more details.
|
||||
usage_format
|
||||
usage_shortcuts
|
||||
usage_typography
|
||||
usage_projectformat
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
@@ -80,7 +80,15 @@ with pip. See :ref:`a_started` for more details.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Additional Topics
|
||||
:caption: Additional Details
|
||||
:hidden:
|
||||
|
||||
more_projectformat
|
||||
more_counting
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Technical Topics
|
||||
:hidden:
|
||||
|
||||
tech_locations
|
||||
|
||||
@@ -36,7 +36,8 @@ and add dictionaries yourself.
|
||||
|
||||
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.
|
||||
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**
|
||||
|
||||
@@ -139,10 +140,10 @@ In the Main section you must at least define the ``name`` and ``icontheme`` sett
|
||||
``typicons_light`` or ``typicons_dark``, or to an icon theme in your custom icons directory. The
|
||||
setting must match the icon theme's folder name.
|
||||
|
||||
The Palette values correspond to the Qt enum values for QPalette::ColorRole, see the
|
||||
The Palette values correspond to the Qt enum values for ``QPalette::ColorRole``, see the
|
||||
`Qt documentation <https://doc.qt.io/qt-5.15/qpalette.html#ColorRole-enum>`_ for more details. The
|
||||
colour values are RGB numbers on the format ``r, g, b`` where each is an integer from to 255.
|
||||
Omitted values are not loaded and will use default values.
|
||||
colour values are RGB numbers on the format ``r, g, b`` where each is an integer from ``0`` to
|
||||
``255``. Omitted values are not loaded and will use default values.
|
||||
|
||||
|
||||
Custom Syntax Theme
|
||||
@@ -174,15 +175,20 @@ A syntax theme ``.conf`` file consists of the following settings:
|
||||
shortcode = 0, 0, 0
|
||||
keyword = 0, 0, 0
|
||||
value = 0, 0, 0
|
||||
optional = 0, 0, 0
|
||||
spellcheckline = 0, 0, 0
|
||||
errorline = 0, 0, 0
|
||||
replacetag = 0, 0, 0
|
||||
modifier = 0, 0, 0
|
||||
|
||||
In the Main section, you must define at least the ``name`` setting. The Syntax colour values are
|
||||
RGB numbers of the format ``r, g, b`` where each is an integer from to 255. Omitted values default
|
||||
to black, except ``background`` which defaults to white,
|
||||
RGB(A) numbers of the format ``r, g, b, a`` where each is an integer ``0`` from to ``255``. The
|
||||
fourth value is the alpha channel (transparency), which can be omitted.
|
||||
|
||||
Omitted syntax colours default to black, except ``background`` which defaults to white.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
The `shortcode` syntax colour entry was added, so you need to update your custom themes if you
|
||||
made any before version 2.2.
|
||||
The `shortcode` syntax colour entry was added.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
The `optional` syntax colour entry was added.
|
||||
|
||||
@@ -50,7 +50,7 @@ Glossary
|
||||
Headings
|
||||
Each level of headings in :term:`Novel Documents` have a specific meaning in terms of the
|
||||
structure of the story. That is, they determine what novelWriter considers a partition, a
|
||||
chapter, a scene or a text section. For :term:`Project Notes`, the header levels don't
|
||||
chapter, a scene or a text section. For :term:`Project Notes`, the heading levels don't
|
||||
matter. For more details on headings in novel documents, see :ref:`a_struct_heads`.
|
||||
|
||||
Keyword
|
||||
|
||||
@@ -25,7 +25,7 @@ Managing the Project
|
||||
: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
|
||||
can also rearrange them here of you wish. See :ref:`a_ui_tree_split_merge` for more details.
|
||||
can rearrange them here of you wish. See :ref:`a_ui_tree_split_merge` for more details.
|
||||
|
||||
|
||||
Layout Tricks
|
||||
@@ -42,7 +42,7 @@ Layout Tricks
|
||||
in columns using the tab key, and it should look the same when viewed next to the editor.
|
||||
|
||||
This is most suitable for your notes, as the result in exported documents cannot be guaranteed
|
||||
to match.
|
||||
to match. Especially if you don't use the same font in your manuscript as in the editor.
|
||||
|
||||
|
||||
Organising Your Text
|
||||
@@ -56,7 +56,9 @@ Organising Your Text
|
||||
|
||||
If you add separate files for chapters and scenes, the chapter file is the perfect place to add
|
||||
such text. Separating chapter and scene files also allows you to make scene files child
|
||||
documents of the chapter (added in novelWriter 2.0).
|
||||
documents of the chapter.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. dropdown:: Distinguishing Soft and Hard Scene Breaks
|
||||
:animate: fade-in-slide-down
|
||||
@@ -64,12 +66,15 @@ 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 the scene heading for hard scene breaks and section headings
|
||||
for soft scene breaks. the :guilabel:`Build Manuscript` tool will let you add separate
|
||||
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.
|
||||
|
||||
|
||||
Other Tools
|
||||
===========
|
||||
|
||||
@@ -8,9 +8,18 @@ Key Features
|
||||
.. _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 is designed for writing fiction, so the
|
||||
formatting features available are limited to those relevant for this purpose. It is *not* suitable
|
||||
for technical writing, and it is *not* a full-featured Markdown editor.
|
||||
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.
|
||||
|
||||
.. admonition:: Limitations
|
||||
|
||||
novelWriter is designed for writing fiction, so the formatting features available are limited to
|
||||
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
|
||||
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
|
||||
@@ -20,10 +29,10 @@ There are two kinds of documents in your project: :term:`Novel Documents` are do
|
||||
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 headers up into multiple documents, or
|
||||
merge multiple documents into single documents. This makes it easier to use variations of the
|
||||
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
|
||||
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 scenes by their headers.
|
||||
one per act for instance, and later effortlessly split these up into chapters or scenes.
|
||||
|
||||
Below are some key features of novelWriter.
|
||||
|
||||
@@ -45,15 +54,15 @@ Below are some key features of novelWriter.
|
||||
build the project into a manuscript, they are all glued together in the top-to-bottom order in
|
||||
which they appear in the project tree. You can use as few text documents as you like, but
|
||||
splitting the project up into chapters and scenes means you can easily reorder them using the
|
||||
drag-and-drop feature of the project tree. You can also start out with a few documents and then
|
||||
later split them into multiple documents based on their headers.
|
||||
drag-and-drop feature of the project tree. You can also start out with fewer documents and then
|
||||
later split them into multiple documents based on chapter and scene headings.
|
||||
|
||||
**Multi-novel project support**
|
||||
As of novelWriter 2.0, 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.
|
||||
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.
|
||||
|
||||
**Keep track of your plot elements**
|
||||
**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.
|
||||
@@ -69,10 +78,10 @@ Below are some key features of novelWriter.
|
||||
**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 when you don't need it
|
||||
to free up space.
|
||||
on which categories you are using in your story. This panel can be hidden to free up space when
|
||||
you don't need it.
|
||||
|
||||
**Building your manuscript**
|
||||
**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
|
||||
do so. The tool lets you select what information you want to include in the generated document,
|
||||
|
||||
@@ -11,9 +11,9 @@ Overview
|
||||
:width: 220
|
||||
|
||||
novelWriter is built as a cross-platform application using `Python 3 <https://www.python.org/>`_ as
|
||||
the programming language, and `Qt 5 <https://www.qt.io/>`_ for the user interface.
|
||||
the programming language, and `Qt 5 <https://www.qt.io/>`_ framework for the user interface.
|
||||
|
||||
novelWriter is built for Linux first, and this is where it works best. However, it also runs fine
|
||||
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.
|
||||
@@ -22,7 +22,7 @@ Spell checking in novelWriter is provided by a third party library called
|
||||
`Enchant <https://abiword.github.io/enchant/>`_. Please see the section on :ref:`a_custom_dict` for
|
||||
how to install spell checking languages.
|
||||
|
||||
For install instructions, see :ref:`a_started`.
|
||||
For install instructions for novelWriter, see :ref:`a_started`.
|
||||
|
||||
|
||||
Using novelWriter
|
||||
@@ -58,12 +58,6 @@ read on.
|
||||
your language, and other special characters. If you use any symbols aside from these, their
|
||||
intended use is explained here.
|
||||
|
||||
:ref:`a_prjfmt` – Optional
|
||||
This chapter is more technical and has an overview of changes made to the way your project data
|
||||
is stored. The format has changed a bit from time to time, and sometimes the changes require
|
||||
that you make small modifications to your project. Everything you need to know is listed in this
|
||||
chapter.
|
||||
|
||||
|
||||
Organising Your Projects
|
||||
========================
|
||||
@@ -90,3 +84,11 @@ meta data for it to extract.
|
||||
: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.
|
||||
|
||||
|
||||
Additional Details & Technical Topics
|
||||
=====================================
|
||||
|
||||
The Additional Details and the Technical Topics sections contain more in-depth information about
|
||||
how various bits of novelWriter works. This information is not essential to getting started using
|
||||
novelWriter.
|
||||
|
||||
@@ -6,7 +6,7 @@ Getting Started
|
||||
|
||||
.. _Enchant: https://abiword.github.io/enchant/
|
||||
.. _GitHub: https://github.com/vkbo/novelWriter
|
||||
.. _main website: https://novelwriter.io
|
||||
.. _Downloads page: https://download.novelwriter.io
|
||||
.. _PPA: https://launchpad.net/~vkbo/+archive/ubuntu/novelwriter
|
||||
.. _Pre-Release PPA: https://launchpad.net/~vkbo/+archive/ubuntu/novelwriter-pre
|
||||
.. _PyPi: https://pypi.org/project/novelWriter/
|
||||
@@ -15,9 +15,10 @@ Getting Started
|
||||
.. _AppImage: https://appimage.org/
|
||||
|
||||
Ready-made packages and installers for novelWriter are available for all major platforms, including
|
||||
Linux, Windows and MacOS. See below for install instructions for each platform.
|
||||
Linux, Windows and MacOS, from the `Downloads page`_. See below for install instructions for each
|
||||
platform.
|
||||
|
||||
You can also install novelWriter from the Python Package Index (PyPi). See :ref:`a_started_pip`.
|
||||
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,
|
||||
or start novelWriter from the command line.
|
||||
|
||||
@@ -32,7 +33,7 @@ Installing on Windows
|
||||
=====================
|
||||
|
||||
You can install novelWriter with both Python and library dependencies embedded using the Windows
|
||||
Installer (setup.exe) file from the `main website`_, or from the Releases_ page on GitHub_.
|
||||
Installer (setup.exe) file from the `Downloads page`_, or from the Releases_ page on GitHub_.
|
||||
Installing it should be straightforward.
|
||||
|
||||
If you have any issues, try uninstalling the previous version and making a fresh install. If you
|
||||
@@ -51,8 +52,8 @@ multiple installations has been known to cause problems.
|
||||
Installing on Linux
|
||||
===================
|
||||
|
||||
A Debian package can be downloaded from the `main website`_, or from the Releases_ page on GitHub_.
|
||||
This package should work on both Debian, Ubuntu and Linux Mint, at least.
|
||||
A Debian package can be downloaded from the `Downloads page`_, or from the Releases_ page on
|
||||
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.
|
||||
|
||||
@@ -82,7 +83,7 @@ Instead, run the following commands to add the repository and key:
|
||||
.. code-block:: bash
|
||||
|
||||
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/novelwriter-ppa-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys F19F1FCE50043114
|
||||
echo "deb [signed-by=/usr/share/keyrings/novelwriter-ppa-keyring.gpg] http://ppa.launchpad.net/vkbo/novelwriter/ubuntu jammy main" | sudo tee /etc/apt/sources.list.d/novelwriter.list
|
||||
echo "deb [signed-by=/usr/share/keyrings/novelwriter-ppa-keyring.gpg] http://ppa.launchpad.net/vkbo/novelwriter/ubuntu noble main" | sudo tee /etc/apt/sources.list.d/novelwriter.list
|
||||
|
||||
Then run the update and install commands as for Ubuntu:
|
||||
|
||||
@@ -112,8 +113,8 @@ completely standalone images for the app that include the necessary environment
|
||||
They can of course be run on any Linux distro, if you prefer this to native packages.
|
||||
|
||||
.. note::
|
||||
novelWriter generally don't support Python versions that have reached end of life. If your Linux
|
||||
distro still uses older Python versions and novelWriter won't run, you may want to try the
|
||||
novelWriter generally doesn't support Python versions that have reached end of life. If your
|
||||
Linux distro still uses older Python versions and novelWriter won't run, you may want to try the
|
||||
AppImage instead.
|
||||
|
||||
|
||||
@@ -123,14 +124,14 @@ Installing on MacOS
|
||||
===================
|
||||
|
||||
You can install novelWriter with both its Python and library dependencies embedded using the DMG
|
||||
application image file from the `main website`_, or from the Releases_ page on GitHub_. Installing
|
||||
it should be straightforward.
|
||||
application image file from the `Downloads page`_, or from the Releases_ page on GitHub_.
|
||||
Installing it should be straightforward.
|
||||
|
||||
* Download the DMG file and open it. Then drag the novelWriter icon to the :guilabel:`Applications`
|
||||
folder on the right. This will install it into your :guilabel:`Applications`.
|
||||
* The first time you try to launch it, it will say that the bundle cannot be verified, simply press
|
||||
the :guilabel:`Open` button to add an exception.
|
||||
* If you are not presented with an :guilabel:`Open` button in the dialog launch the application
|
||||
* If you are not presented with an :guilabel:`Open` button in the dialog, launch the application
|
||||
again by right clicking on the application in Finder and selecting :guilabel:`Open` from the
|
||||
context menu.
|
||||
|
||||
@@ -149,7 +150,7 @@ Installing from PyPi
|
||||
====================
|
||||
|
||||
novelWriter is also available on the Python Package Index, or PyPi_. This install method works on
|
||||
all supported operating systems.
|
||||
all supported operating systems with a suitable Python environment.
|
||||
|
||||
To install from PyPi you must first have the ``python`` and ``pip`` commands available on your
|
||||
system. You can download Python from `python.org`_. It is recommended that you install the latest
|
||||
|
||||
39
docs/source/more_counting.rst
Normal file
@@ -0,0 +1,39 @@
|
||||
.. _a_counting:
|
||||
|
||||
********************
|
||||
Word and Text Counts
|
||||
********************
|
||||
|
||||
This is an overview of how words and other counts of your text are performed. The counting rules
|
||||
should be relatively standard, and are compared to LibreOffice Writer rules. The counts provided in
|
||||
the app on the raw text are only meant to be approximate.
|
||||
|
||||
|
||||
Text Word Counts and Stats
|
||||
==========================
|
||||
|
||||
These are the rules for the main counts available for for each document in a project.
|
||||
|
||||
For all counts, the following rules apply.
|
||||
|
||||
#. Short (–) and long (—) dashes are considered word separators.
|
||||
#. Any line starting with ``%`` or ``@`` is ignored.
|
||||
#. Trailing white spaces are ignored, including line breaks.
|
||||
#. Leading ``>`` and trailing ``<`` are ignored with any spaces next to them.
|
||||
#. Valid shortcodes and other commands wrapped in brackets ``[]`` are ignored.
|
||||
#. In-line Markdown syntax in text paragraphs is treated as part of the text.
|
||||
|
||||
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.
|
||||
|
||||
**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.
|
||||
|
||||
**Paragraph Count**
|
||||
The paragraph count is the number of text blocks separated by one or more empty line. A line
|
||||
consisting only of white spaces is considered empty.
|
||||
@@ -10,7 +10,7 @@ You can activate it from the sidebar, the :guilabel:`Tools` menu, or by pressing
|
||||
|
||||
.. versionadded:: 2.1
|
||||
This tool is new for version 2.1. A simpler tool was used for earlier versions. The simpler tool
|
||||
only allows you to define a single set of options for the build, but otherwise has much the same
|
||||
only allows you to define a single set of options for the build, but otherwise had much the same
|
||||
functionality.
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ Formatting Headings
|
||||
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 like shown int he figure above.
|
||||
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
|
||||
@@ -152,7 +152,7 @@ novelWriter Markup
|
||||
|
||||
Standard/Extended Markdown
|
||||
The Markdown format comes in both Standard and Extended flavour. The *only* difference in terms
|
||||
of novelWriter functionality is the support for strikethrough text, which is not supported by
|
||||
of novelWriter functionality is the support for strike through text, which is not supported by
|
||||
the Standard flavour.
|
||||
|
||||
|
||||
|
||||
@@ -4,21 +4,25 @@
|
||||
Novel Projects
|
||||
**************
|
||||
|
||||
New projects can be created from the :guilabel:`Project` menu by selecting :guilabel:`New Project`.
|
||||
This will open the :guilabel:`New Project Wizard` that will assist you in creating a bare bone
|
||||
project suited to your needs.
|
||||
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.
|
||||
|
||||
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:`Open Project`
|
||||
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 clicking the :guilabel:`Remove` button.
|
||||
by right-clicking it and selecting the :guilabel:`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,
|
||||
including word counts, and a table of contents with word and page counts, is available through the
|
||||
:guilabel:`Project Details` dialog.
|
||||
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.
|
||||
|
||||
|
||||
.. _a_proj_roots:
|
||||
@@ -58,8 +62,9 @@ details, see the :ref:`a_references` chapter.
|
||||
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. 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
|
||||
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`
|
||||
@@ -105,8 +110,8 @@ trash folder can then be deleted permanently, either individually, or by emptyin
|
||||
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 deleted from the :guilabel:`Project` menu, or by pressing
|
||||
:kbd:`Ctrl+Shift+Del`. Root folders can only be deleted when they are empty.
|
||||
A document or a folder can be moved to trash from the :guilabel:`Project` menu, or by pressing
|
||||
:kbd:`Ctrl+Shift+Del`. Root folders can only be removed when they are empty.
|
||||
|
||||
|
||||
.. _a_proj_roots_out:
|
||||
@@ -129,9 +134,9 @@ Recovered Documents
|
||||
-------------------
|
||||
|
||||
If novelWriter crashes or otherwise exits without saving the project state, or if you're using a
|
||||
file synchronisation tool that runs out of sync, there may be files in the project folder that
|
||||
aren't tracked in the core project file. These files, when discovered, are recovered and added back
|
||||
into the project.
|
||||
file synchronisation tool that runs out of sync, there may be files in the project storage folder
|
||||
that aren't tracked in the core project file. These files, when discovered, are recovered and added
|
||||
back into the project.
|
||||
|
||||
The discovered files are scanned for metadata that give clues as to where the document may
|
||||
previously have been located in the project. The project loading routine will try to put them back
|
||||
@@ -152,10 +157,10 @@ 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 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. 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.
|
||||
|
||||
.. note::
|
||||
If, for some reason, novelWriter crashes, the lock file may remain even if there are no other
|
||||
@@ -208,6 +213,18 @@ converting, splitting, or merging items. See :ref:`a_ui_tree_split_merge` for mo
|
||||
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.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
|
||||
.. _a_proj_files_counts:
|
||||
|
||||
Word Counts
|
||||
@@ -227,6 +244,8 @@ the values in the project tree, which again depend on an up to date :term:`proje
|
||||
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`.
|
||||
|
||||
The rules for how the counts are made is covered in more detail in :ref:`a_counting`.
|
||||
|
||||
|
||||
.. _a_proj_settings:
|
||||
|
||||
@@ -242,25 +261,23 @@ Settings Tab
|
||||
|
||||
The :guilabel:`Settings` tab holds the project name, title, and author settings.
|
||||
|
||||
The :guilabel:`Project Name` can be set to a different value than the :guilabel:`Novel Title`. The
|
||||
difference between them is simply that the :guilabel:`Project Name` is used for the GUI (main
|
||||
window title) and for generating backup files. The intention is that the :guilabel:`Project Name`
|
||||
should remain unchanged throughout the project's lifetime, otherwise the name of exported files and
|
||||
backup files may change too.
|
||||
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 :guilabel:`Novel Title` and :guilabel:`Authors` settings are used when building the manuscript,
|
||||
for some formats.
|
||||
You can also change the :guilabel:`Authors` and :guilabel:`Project Language` setting. These are
|
||||
only used when building the manuscript, for some formats.
|
||||
|
||||
If your project is in a different language than your main spell checking language is set to, you
|
||||
can override the default setting here. You can also override the automatic backup setting. The
|
||||
project language can also be changed from the :guilabel:`Tools` menu.
|
||||
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.
|
||||
|
||||
|
||||
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*
|
||||
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.
|
||||
|
||||
These labels are there purely for your convenience, and you are not required to use them for any
|
||||
|
||||
@@ -4,21 +4,21 @@
|
||||
Tags and References
|
||||
*******************
|
||||
|
||||
In novelWriter there are no forms or tables to fill in to define the characters, locations and
|
||||
other elements of your story. Instead, you can mark your :term:`project notes` as representing
|
||||
these story elements by creating a :term:`tag`. Whenever you want to link a piece of your story to
|
||||
a note defining a story element, like a character, you create a :term:`reference` back to that tag.
|
||||
You can also cross-link your project notes in the same way.
|
||||
In novelWriter there are no forms or tables to fill in to define characters, locations or other
|
||||
elements of your story. Instead, you create :term:`project notes` which you can mark as
|
||||
representing these story elements by creating a :term:`tag`. Whenever you want to link a piece of
|
||||
your story to a note defining a story element, like a character, you create a :term:`reference`
|
||||
back to that tag. You can also cross-link your project notes in the same way.
|
||||
|
||||
This is perhaps one of the features that makes novelWriter different from other, similar
|
||||
applications. It is therefore not always obvious to new users how this is supposed to work, so
|
||||
this chapter hopes to explains in more detail how to use the tags and references system.
|
||||
this chapter hopes to explain in more detail how to use this tags and references system.
|
||||
|
||||
.. tip::
|
||||
If you find the Tags and Reference system difficult to follow just from reading this chapter,
|
||||
you can create a new project in novelWriter and select to "Fill the project with example files"
|
||||
in the :guilabel:`New Project Wizard`. The example project contains several examples of tags and
|
||||
references.
|
||||
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.
|
||||
|
||||
|
||||
.. _a_references_metadata:
|
||||
@@ -31,14 +31,15 @@ documents, not the documents themselves. See :ref:`a_struct_heads` for more deta
|
||||
metadata is also associated with headings, and not the documents directly.
|
||||
|
||||
If you split your project into separate documents for each scene, this distinction may not matter.
|
||||
However, there are several benefits to using documents at a larger structural scale when starting
|
||||
your project. For instance, it may make more sense to define all your scenes, and even chapters, in
|
||||
a single document at first, or perhaps a document per act. You can later split these documents up
|
||||
using the document split feature. See :ref:`a_ui_tree_split_merge` for more details.
|
||||
However, there are several benefits to using documents at a larger structural scale when first
|
||||
starting your project. For instance, it may make more sense to define all your scenes, and even
|
||||
chapters, in a single document at first, or perhaps a document per act. You can later split these
|
||||
documents up using the document split feature. See :ref:`a_ui_tree_split_merge` for more details.
|
||||
|
||||
The implication here is that you can treat each heading as an independent element of your notes
|
||||
that can be referenced somewhere else. In order to make it possible to reference a header section,
|
||||
you need to assign it a tag.
|
||||
You can do the same with your notes. You can treat each heading as an independent element of your
|
||||
notes that can be referenced somewhere else. That way you can collect all your minor or background
|
||||
characters in a single note file, and still be able to reference them individually by separating
|
||||
them with headings and assigning each a tag.
|
||||
|
||||
|
||||
.. _a_references_tags:
|
||||
@@ -47,43 +48,63 @@ How to Use Tags
|
||||
===============
|
||||
|
||||
A "tag" in novelWriter is a word or phrase that you define as belonging to a heading. Tags are set
|
||||
by using the ``@tags`` :term:`keyword`. The full format of a tag is ``@tag: tagname``, where
|
||||
``tagname`` is an identifier of your choosing. You can only set *one* tag per heading, and the tag
|
||||
has to be unique across all documents in the project.
|
||||
by using the ``@tag`` :term:`keyword`.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
Tags are now case insensitive.
|
||||
The basic format of a tag is ``@tag: tagName``.
|
||||
|
||||
After the tags have been defined, they can then be referenced in the novel documents, or
|
||||
cross-referenced in other notes. they will also show up in the :guilabel:`Outline View` and in the
|
||||
back-reference panel when a document is opened in the viewer.
|
||||
An alternative format of a tag is ``@tag: tagName | displayName``.
|
||||
|
||||
``tagName`` (Required)
|
||||
This is a unique identifier of your choosing. It is the value you use later for making
|
||||
references back to this document, or section of the document.
|
||||
|
||||
``displayName`` (Optional)
|
||||
This is an optional display name used for the tag. When you build your manuscript, you can for
|
||||
instance insert the point of view character name directly into chapter headings. By default, the
|
||||
``tagName`` value is used in such headings, but if you use a shortened format internally in your
|
||||
project, you can use this to specify a more suitable format for your manuscript headings.
|
||||
|
||||
You can only set **one** tag per heading, and the tag has to be unique across **all** documents in
|
||||
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
|
||||
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
|
||||
allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up
|
||||
to date. An invalid tag should have a green wiggly line under it, and will not receive the syntax
|
||||
colour that valid tags do.
|
||||
to date. An invalid tag should have a green wiggly line under it, and will not receive the colour
|
||||
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.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
Tags are no longer case sensitive. The tags are by default displayed with the capitalisation you
|
||||
use when defining the tag, but you don't have to use the same capitalisation when referencing
|
||||
it later.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
Tags can have an optional display name for manuscript builds.
|
||||
|
||||
Example of a heading with a tag for a character of the story:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: md
|
||||
|
||||
# Jane Doe
|
||||
# Character: Jane Doe
|
||||
|
||||
@tag: Jane
|
||||
@tag: Jane | Jane Doe
|
||||
|
||||
Some information about the character Jane Doe.
|
||||
|
||||
When this is done in a document in a :term:`Root Folder` of type "Characters", the tag is
|
||||
automatically treated as an available character in your project, and you will be able to reference
|
||||
it in any of your other documents using the reference keywords for characters. It will also show up
|
||||
in the Character tab in the Reference panel below the document viewer, and in the reference
|
||||
auto-completer menu in the editor when you fill in references. See :ref:`a_ui_view` and
|
||||
:ref:`a_references_completer`.
|
||||
automatically treated as an available character in your project with the value "Jane", and you will
|
||||
be able to reference it in any of your other documents using the reference keywords for characters.
|
||||
It will also show up in the Character tab in the Reference panel below the document viewer, and in
|
||||
the reference auto-completer menu in the editor when you fill in references. See :ref:`a_ui_view`
|
||||
and :ref:`a_references_completer`.
|
||||
|
||||
It is the root folder type that defines what category of story elements the tag is indexed under.
|
||||
See the :ref:`a_proj_roots` section for an overview of available root folder types. They are also
|
||||
@@ -172,7 +193,7 @@ highlight that two notes are related.
|
||||
|
||||
Example of a novel document with references to characters and plots:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: md
|
||||
|
||||
## Chapter 1
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ 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`. 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.
|
||||
: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.
|
||||
|
||||
The :guilabel:`Project Tree` can distinguish between the different header levels of the novel
|
||||
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`.
|
||||
|
||||
@@ -27,47 +28,47 @@ Four levels of headings are supported, signified by the number of hashes (``#``)
|
||||
title. See also the :ref:`a_fmt` section for more details about the markup syntax.
|
||||
|
||||
.. note::
|
||||
The header levels are not only important when generating the manuscript, they are also used by
|
||||
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 syntax for the four basic header types, and the two special header types, is listed in section
|
||||
The syntax for the four basic heading types, and the two 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:
|
||||
|
||||
**Header Level 1: Partition**
|
||||
This header level signifies that the text refers to a top level partition. This is useful when
|
||||
**Heading Level 1: Partition**
|
||||
This heading level signifies that the text refers to a top level partition. 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 header level ``#!`` covered in :ref:`a_fmt_head`.
|
||||
The novel title itself should use the special heading level ``#!`` covered in :ref:`a_fmt_head`.
|
||||
|
||||
**Header Level 2: Chapter**
|
||||
This header level signifies a chapter level partition. Each time you want to start a new
|
||||
**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.
|
||||
|
||||
**Header Level 3: Scene**
|
||||
This header level signifies a scene level partition. You must provide a title text, but the
|
||||
**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.
|
||||
|
||||
**Header Level 4: Section**
|
||||
This header level signifies a sub-scene level partition, usually called a "section" in the
|
||||
**Heading Level 4: Section**
|
||||
This heading level signifies a sub-scene level partition, 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.
|
||||
|
||||
Page breaks are automatically added before level 1 and 2 headers when you build your project to a
|
||||
format that supports page breaks, or when you print the document directly from the
|
||||
:guilabel:`Manuscript Build` tool. If you want page breaks in other places, you have to specify
|
||||
them manually. See :ref:`a_fmt_break`.
|
||||
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`.
|
||||
|
||||
.. tip::
|
||||
There are multiple options of how to process novel titles when building the manuscript. For
|
||||
There are multiple options of how to process novel headings when building the manuscript. For
|
||||
instance, chapter numbers can be applied automatically, and so can scene numbers if you want
|
||||
them in a draft manuscript. See the :ref:`a_manuscript` page for more details.
|
||||
them in a draft manuscript. You can also insert point-of-view character names in chapter titles.
|
||||
See the :ref:`a_manuscript` page for more details.
|
||||
|
||||
|
||||
.. _a_struct_heads_title:
|
||||
@@ -76,17 +77,21 @@ 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 header 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:
|
||||
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:
|
||||
|
||||
``#! My Novel``
|
||||
.. code-block:: md
|
||||
|
||||
#! My Novel
|
||||
|
||||
>> _by Jane Doe_ <<
|
||||
|
||||
The title is by default centred on the page. You can add more text to the page as you wish, like
|
||||
for instance the author's name and details.
|
||||
|
||||
If you want an additional page of text after the title page, starting on a fresh page, you can add
|
||||
``[NEW PAGE]`` on a line by itself, and continue the text after it. This will insert a page break
|
||||
``[new page]`` on a line by itself, and continue the text after it. This will insert a page break
|
||||
before the text. See also :ref:`a_fmt_break`.
|
||||
|
||||
|
||||
@@ -96,15 +101,19 @@ 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 cam add an ``!`` to the level 2 header formatting code to tell the
|
||||
build tool to skip these chapters.
|
||||
chapters separate from this, you can add an ``!`` to the level 2 heading formatting code to tell
|
||||
the build tool to skip these chapters.
|
||||
|
||||
``##! Unnumbered Chapter Title``
|
||||
.. code-block:: md
|
||||
|
||||
There is a separate formatting feature for such chapters 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.
|
||||
##! Unnumbered Chapter Title
|
||||
|
||||
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.
|
||||
|
||||
.. Note::
|
||||
Previously, you could also disable the automatic numbering of a chapter by adding an ``*`` as
|
||||
|
||||
@@ -33,7 +33,7 @@ itself. It is important to keep this file backed up, either through the built-in
|
||||
your own backup solution.
|
||||
|
||||
The project XML file is indent-formatted, and is suitable for diff tools and version control since
|
||||
most of the file will stay static, although a timesetamp is set in the meta section on line 2, and
|
||||
most of the file will stay static, although a timestamp is set in the meta section on line 2, and
|
||||
various meta data entries incremented, on each save.
|
||||
|
||||
.. only:: not html
|
||||
@@ -91,11 +91,6 @@ If successful, the old data file is then removed, and the temporary file replace
|
||||
that the previously saved data is only replaced when the new data has been successfully saved to
|
||||
the storage medium.
|
||||
|
||||
For the project XML file, a ``.bak`` file is in addition kept, which will always contain the
|
||||
previous version of the file, although when auto-save is enabled, they may have the same content.
|
||||
If the opening of a project file fails, novelWriter will automatically try to open the ``.bak``
|
||||
file instead.
|
||||
|
||||
|
||||
Project Meta Data
|
||||
=================
|
||||
|
||||
@@ -10,7 +10,7 @@ How it Works
|
||||
|
||||
The main features of novelWriter are listed in the :ref:`a_intro` chapter. In this chapter, we go
|
||||
into some more details on how they are implemented. This is intended as an overview. Later on in
|
||||
this documentation, these features will be covered in more detail.
|
||||
this documentation these features will be covered in more detail.
|
||||
|
||||
|
||||
.. _a_breakdown_design:
|
||||
@@ -23,6 +23,8 @@ 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.
|
||||
|
||||
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
|
||||
@@ -31,7 +33,8 @@ them. For reference, a list of all shortcuts can be found in the :ref:`a_kb` cha
|
||||
.. note::
|
||||
novelWriter is not intended to be a full office type word processor. It doesn't support images,
|
||||
links, tables, and other complex structures and objects often needed for such documents.
|
||||
Formatting is limited to headers, emphasis, text alignment, and a few other simple features.
|
||||
Formatting is limited to headings, in-line basic text formats, text alignment, and a few other
|
||||
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
|
||||
@@ -54,18 +57,18 @@ When in :guilabel:`Project Tree View` mode, the main work area of the main windo
|
||||
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 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 as it will apply the
|
||||
formatting tags you have specified. The main purpose of the viewer is for viewing your notes next
|
||||
to your editor while you're writing.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Novel Tree and Editor View
|
||||
Novel View and Editor View
|
||||
--------------------------
|
||||
|
||||
.. figure:: images/fig_novel_tree_view.png
|
||||
@@ -81,17 +84,17 @@ Each heading is indented according to the heading level. You can open and edit y
|
||||
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 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.
|
||||
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 at the top of the side panel.
|
||||
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
|
||||
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
|
||||
@@ -123,12 +126,12 @@ neutral colours from :guilabel:`Preferences`. Other colour themes are also avail
|
||||
can be contributed to novelWriter on GitHub.
|
||||
|
||||
Switching the GUI colour theme does not affect the colours of the editor and viewer. They have
|
||||
separate colour themes called :guilabel:`Editor Themes`. 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 :guilabel:`Preferences`.
|
||||
They are separated because there are a lot more options to choose from for the editor and viewer.
|
||||
|
||||
.. note::
|
||||
If you switch to 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.
|
||||
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.
|
||||
|
||||
|
||||
.. _a_breakdown_project:
|
||||
@@ -147,7 +150,7 @@ within those documents.
|
||||
|
||||
.. figure:: images/fig_header_levels.png
|
||||
|
||||
An illustration of how header levels correspond to the novel structure.
|
||||
An illustration of how heading levels correspond to the novel structure.
|
||||
|
||||
The four heading levels (**H1** to **H4**) are treated as follows:
|
||||
|
||||
@@ -158,8 +161,8 @@ The four heading levels (**H1** to **H4**) are treated as follows:
|
||||
|
||||
The project tree will select an icon for the document based on the first heading in it.
|
||||
|
||||
This header level structure is only taken into account for :term:`novel documents`. For
|
||||
:term:`project notes`, the header levels have no structural meaning, and you are free to use them
|
||||
This heading level structure is only taken into account for :term:`novel documents`. For
|
||||
:term:`project notes`, the heading levels have no structural meaning, and you are free to use them
|
||||
however you want. See :ref:`a_struct` and :ref:`a_references` for more details.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
@@ -171,8 +174,8 @@ however you want. See :ref:`a_struct` and :ref:`a_references` for more details.
|
||||
|
||||
.. _a_breakdown_export:
|
||||
|
||||
Building the Manuscript
|
||||
=======================
|
||||
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
|
||||
@@ -217,8 +220,8 @@ Secondly, having multiple small files means it is very easy to synchronise them
|
||||
with standard file synchronisation tools.
|
||||
|
||||
Thirdly, if you use version control software to track the changes to your project, the file formats
|
||||
used for the files are well suited. Also the JSON documents have line breaks and indents, which
|
||||
makes it easier to track them with version control software.
|
||||
used for the files are well suited. All the JSON documents have line breaks and indents as well,
|
||||
which makes it easier to track them with version control software.
|
||||
|
||||
.. note::
|
||||
Since novelWriter has to keep track of a bunch of files and folders when a project is open, it
|
||||
|
||||
@@ -6,8 +6,8 @@ Formatting Your Text
|
||||
|
||||
The novelWriter text editor is a plain text editor that uses formatting codes for setting meta data
|
||||
values and allowing for some text formatting. The syntax is based on Markdown, but novelWriter is
|
||||
*not* a Markdown editor. It supports basic formatting like emphasis (italic), strong importance
|
||||
(bold) and strike through text, as well as four levels of headings. Form some further complex
|
||||
**not** a Markdown editor. It supports basic formatting like emphasis (italic), strong importance
|
||||
(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
|
||||
@@ -30,7 +30,7 @@ dialogue in your text.
|
||||
An example of the colour highlighting of references. "Bob" is not defined, and "@blabla" is not
|
||||
a valid reference type.
|
||||
|
||||
When you use the commands to set tags and references, these also change colour. Correct commands
|
||||
When you use the keywords to set tags and references, these also change colour. Correct keywords
|
||||
have a distinct colour, and the references themselves will get a colour if they are valid. Invalid
|
||||
references will get a squiggly error line underneath. The same applies to duplicate tags.
|
||||
|
||||
@@ -45,7 +45,7 @@ Headings
|
||||
|
||||
.. figure:: images/fig_header_levels.png
|
||||
|
||||
An illustration of how header levels correspond to the novel structure.
|
||||
An illustration of how heading levels correspond to the novel structure.
|
||||
|
||||
Four levels of headings are allowed. For :term:`project notes`, they are free to be used as you see
|
||||
fit. That is, novelWriter doesn't assign the different headings any particular meaning. However,
|
||||
@@ -53,28 +53,28 @@ for :term:`novel documents` they indicate the structural level of the novel and
|
||||
correctly to produce the intended result. See :ref:`a_struct_heads` for more details.
|
||||
|
||||
``# Title Text``
|
||||
Heading level one. For novel documents, the header level indicates the start of a new partition.
|
||||
Heading level one. For novel documents, the level indicates the start of a new partition.
|
||||
|
||||
``## Title Text``
|
||||
Heading level two. For novel documents, the header level indicates the start of a new chapter.
|
||||
Chapter numbers can be inserted automatically when building the manuscript.
|
||||
Heading level two. For novel documents, the level indicates the start of a new chapter. Chapter
|
||||
numbers can be inserted automatically when building the manuscript.
|
||||
|
||||
``### Title Text``
|
||||
Heading level three. For novel documents, the header 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.
|
||||
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.
|
||||
|
||||
``#### Title Text``
|
||||
Heading level four. For novel documents, the header level indicates the start of a new section.
|
||||
Section titles can be replaced by separators or ignored completely when building the manuscript.
|
||||
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 headers level one and two, adding a ``!`` modifies the behaviour of the heading:
|
||||
For headings level one and two, adding a ``!`` modifies its meaning:
|
||||
|
||||
``#! Title Text``
|
||||
This tells the build tool that the level one heading is intended to be used for the novel'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.
|
||||
This tells the build tool that the level one heading is intended to be used for the novel's 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.
|
||||
|
||||
``##! Title Text``
|
||||
This tells the build tool to not assign a chapter number to this chapter title if automatic
|
||||
@@ -99,10 +99,10 @@ In addition, the editor supports a few additional types of white spaces:
|
||||
|
||||
* A non-breaking space can be inserted with :kbd:`Ctrl+K`, :kbd:`Space`.
|
||||
* 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`.
|
||||
* 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. They are also listed
|
||||
in :ref:`a_kb_ins`.
|
||||
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`.
|
||||
|
||||
Non-breaking spaces are highlighted by the syntax highlighter with an alternate coloured
|
||||
background, depending on the selected theme.
|
||||
@@ -118,7 +118,7 @@ background, depending on the selected theme.
|
||||
Text Emphasis
|
||||
=============
|
||||
|
||||
A minimal set of text emphasis styles are supported for text paragraphs.
|
||||
A minimal set of Markdown text emphasis styles are supported for text paragraphs.
|
||||
|
||||
``_text_``
|
||||
The text is rendered as emphasised text (italicised).
|
||||
@@ -141,7 +141,7 @@ In addition, the following rules apply:
|
||||
tag itself. That is, ``**text**`` is valid, ``**text **`` is not.
|
||||
2. More generally, the delimiters must be on the outer edge of words. That is, ``some **text in
|
||||
bold** here`` is valid, ``some** text in bold** here`` is not.
|
||||
3. If using both ``**`` and ``_`` to wrap the same text, the underscore must be the *inner*
|
||||
3. If using both ``**`` and ``_`` to wrap the same text, the underscore must be the **inner**
|
||||
wrapper. This is due to the underscore also being a valid word character, so if they are on the
|
||||
outside, they violate rule 2.
|
||||
4. Text emphasis does not span past line breaks. If you need to add emphasis to multiple lines or
|
||||
@@ -214,9 +214,10 @@ indicate this by altering the colour of the word.
|
||||
document of the whole project.
|
||||
|
||||
``%Short: text ...``
|
||||
This is a short description comment. It is identical to the synopsis comment, 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`.
|
||||
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`.
|
||||
|
||||
.. note::
|
||||
Only one comment can be flagged as a synopsis or short comment for each heading. If multiple
|
||||
@@ -232,11 +233,11 @@ Tags and References
|
||||
The document editor supports a set of keywords used for setting tags, and making references between
|
||||
documents.
|
||||
|
||||
Tags use the command ``@tag:`` to define a tag. The tag can be set once per section defined by a
|
||||
Tags use the keyword ``@tag:`` to define a tag. The tag can be set once per section defined by a
|
||||
heading. Setting it multiple times under the same heading will just override the previous setting.
|
||||
|
||||
``@tag: value``
|
||||
A tag command followed by the tag value, like for instance the name of a character.
|
||||
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:
|
||||
@@ -282,7 +283,7 @@ Examples:
|
||||
.. note::
|
||||
The text editor will not show the alignment and indentation live. But the viewer will show them
|
||||
when you open the document there. It will of course also be reflected in the document generated
|
||||
from the build tool as long as the format supports paragraph alignment.
|
||||
from the manuscript build tool as long as the format supports paragraph alignment.
|
||||
|
||||
|
||||
.. _a_fmt_break:
|
||||
@@ -290,37 +291,37 @@ Examples:
|
||||
Vertical Space and Page Breaks
|
||||
==============================
|
||||
|
||||
Adding more than one line break between paragraphs will *not* increase the space between those
|
||||
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.
|
||||
``[vspace]`` on a line of its own, and the build tool will insert a blank paragraph in its place.
|
||||
|
||||
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.
|
||||
instance, writing ``[vspace:3]`` will insert three blank paragraphs.
|
||||
|
||||
Normally, the build tool will insert a page break before all headers of level one and for all
|
||||
headers of level two for novel documents, i.e. chapters, but not for project notes.
|
||||
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
|
||||
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.
|
||||
|
||||
.. note::
|
||||
The page break code is applied to the text that follows it. It adds a "page break before" mark
|
||||
to the text when exporting to HTML or Open Document. This means that a ``[NEW PAGE]`` which has
|
||||
to the text when exporting to HTML or Open Document. This means that a ``[new page]`` which has
|
||||
no text following it, it will not result in a page break.
|
||||
|
||||
**Example:**
|
||||
|
||||
.. code-block:: markdown
|
||||
.. code-block:: md
|
||||
|
||||
This is a text paragraph.
|
||||
|
||||
[VSPACE:2]
|
||||
[vspace:2]
|
||||
|
||||
This is another text paragraph, but there will be two empty paragraphs
|
||||
in-between them.
|
||||
|
||||
[NEWPAGE]
|
||||
[new page]
|
||||
|
||||
This text will always start on a new page if the build format has pages.
|
||||
|
||||
@@ -86,7 +86,7 @@ Splitting Documents
|
||||
|
||||
The :guilabel:`Split Document` dialog.
|
||||
|
||||
The :guilabel:`Split Document by Header` option will open a dialog that allows you to split the
|
||||
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.
|
||||
@@ -94,7 +94,7 @@ 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.
|
||||
|
||||
The source document *is not* deleted in the process, but you have the option to let the tool move
|
||||
The source document **is not** deleted in the process, but you have the option to let the tool move
|
||||
the source document to the :guilabel:`Trash` folder.
|
||||
|
||||
|
||||
@@ -123,16 +123,16 @@ 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. novelWriter doesn't do anything with them at all.
|
||||
To modify the labels, go to their respective tabs in :guilabel:`Project Settings`.
|
||||
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`.
|
||||
|
||||
The "Status" flag is intended to tag a :term:`novel document<novel documents>` 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 :guilabel:`Novel` folder, it uses the "Status" flag, otherwise it uses an
|
||||
"Importance" flag. Some folders, like :guilabel:`Trash` and :guilabel:`Archive` allow both.
|
||||
lives in. If it's in a Novel type folder, it uses the "Status" flag, otherwise it uses an
|
||||
"Importance" flag.
|
||||
|
||||
|
||||
.. _a_ui_tree_dnd:
|
||||
@@ -155,32 +155,31 @@ 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 :guilabel:`Novel` 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.
|
||||
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
|
||||
project tree, or by pressing :kbd:`Ctrl+Shift+Up` or :kbd:`Ctrl+Shift+Down` when they are selected.
|
||||
project tree, or by pressing :kbd:`Ctrl+Up` or :kbd:`Ctrl+Down` when they are selected.
|
||||
|
||||
|
||||
.. _a_ui_tree_novel:
|
||||
|
||||
The Novel Tree
|
||||
==============
|
||||
The Novel Tree View
|
||||
===================
|
||||
|
||||
.. figure:: images/fig_novel_tree_view.png
|
||||
|
||||
A screenshot of the Novel Tree View.
|
||||
|
||||
An alternative way to view the project structure is the novel tree. You can switch to this view by
|
||||
selecting the :guilabel:`Novel Tree 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
|
||||
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.
|
||||
|
||||
.. note::
|
||||
You cannot reorganise the entries in the novel tree, or add any new documents, as that would
|
||||
You cannot reorganise the entries in the novel view, or add any new documents, as that would
|
||||
imply restructuring the content of the document files themselves. Any such editing must be done
|
||||
in the project tree. However, you can add new headings to existing documents, or change
|
||||
references, which will be updated in this view when the document is saved.
|
||||
@@ -188,17 +187,17 @@ of the story itself rather than the document files.
|
||||
|
||||
.. _a_ui_outline:
|
||||
|
||||
Project Outline View
|
||||
====================
|
||||
The Novel Outline View
|
||||
======================
|
||||
|
||||
.. figure:: images/fig_outline_view.png
|
||||
|
||||
A screenshot of the Novel Outline View.
|
||||
|
||||
The project's :guilabel:`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 :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 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
|
||||
|
||||
@@ -23,14 +23,14 @@ Main Window Shortcuts
|
||||
":kbd:`F5`", "Open the :guilabel:`Build Manuscript` tool"
|
||||
":kbd:`F6`", "Open the :guilabel:`Writing Statistics` tool"
|
||||
":kbd:`F8`", "Toggle :guilabel:`Focus Mode`"
|
||||
":kbd:`F9`", "Re-build the project index"
|
||||
":kbd:`F9`", "Re-build the project's index"
|
||||
":kbd:`F11`", "Toggle full screen mode"
|
||||
":kbd:`Ctrl+,`", "Open the :guilabel:`Preferences` dialog"
|
||||
":kbd:`Ctrl+E`", "Switch focus to the document editor"
|
||||
":kbd:`Ctrl+T`", "Switch focus to the project/novel tree"
|
||||
":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+O`", "Open a project"
|
||||
":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"
|
||||
@@ -55,12 +55,11 @@ Project Tree Shortcuts
|
||||
":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+O`", "Open selected document"
|
||||
":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"
|
||||
":kbd:`Ctrl+Down`", "Move selected item one step down in the tree"
|
||||
":kbd:`Ctrl+Shift+Del`", "Move the selected item to Trash"
|
||||
":kbd:`Ctrl+Shift+Z`", "Undo the last move of a project item, if possible"
|
||||
|
||||
|
||||
.. _a_kb_editor:
|
||||
@@ -92,21 +91,22 @@ 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"
|
||||
":kbd:`Ctrl+0`", "Remove block formatting for block under cursor"
|
||||
":kbd:`Ctrl+1`", "Change block format to header level 1"
|
||||
":kbd:`Ctrl+2`", "Change block format to header level 2"
|
||||
":kbd:`Ctrl+3`", "Change block format to header level 3"
|
||||
":kbd:`Ctrl+4`", "Change block format to header level 4"
|
||||
":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+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"
|
||||
":kbd:`Ctrl+4`", "Change block format to heading level 4"
|
||||
":kbd:`Ctrl+5`", "Change block alignment to left-aligned"
|
||||
":kbd:`Ctrl+6`", "Change block alignment to centred"
|
||||
":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+D`", "Strike through selected text, or word under cursor"
|
||||
":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"
|
||||
":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"
|
||||
|
||||
|
||||
Other Editor Shortcuts
|
||||
@@ -115,7 +115,7 @@ Other Editor Shortcuts
|
||||
.. csv-table::
|
||||
:header: "Shortcut", "Description"
|
||||
|
||||
":kbd:`F7`", "Re-run the spell checker"
|
||||
":kbd:`F7`", "Re-run the spell checker on the document"
|
||||
":kbd:`Ctrl+.`", "Open the context menu at the current cursor location"
|
||||
":kbd:`Ctrl+A`", "Select all text in the document"
|
||||
":kbd:`Ctrl+C`", "Copy selected text to clipboard"
|
||||
@@ -132,7 +132,6 @@ Other Editor Shortcuts
|
||||
":kbd:`Ctrl+F7`", "Toggle spell checking"
|
||||
":kbd:`Ctrl+Return`", "Open the tag or reference under the cursor in the viewer"
|
||||
":kbd:`Ctrl+Shift+A`", "Select all text in the current paragraph"
|
||||
":kbd:`Ctrl+Shift+I`", "Import text to the current document from a text file"
|
||||
|
||||
|
||||
.. _a_kb_ins:
|
||||
|
||||
@@ -42,13 +42,12 @@ 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 context 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 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.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
The auto-completer feature was added.
|
||||
|
||||
|
||||
.. _a_ui_view:
|
||||
|
||||
3248
i18n/nw_base.ts
3538
i18n/nw_de_DE.ts
3518
i18n/nw_en_US.ts
3576
i18n/nw_es_419.ts
3518
i18n/nw_it_IT.ts
3516
i18n/nw_ja_JP.ts
3516
i18n/nw_nb_NO.ts
4400
i18n/nw_nl_NL.ts
@@ -42,9 +42,9 @@ __license__ = "GPLv3"
|
||||
__author__ = "Veronica Berglyd Olsen"
|
||||
__maintainer__ = "Veronica Berglyd Olsen"
|
||||
__email__ = "code@vkbo.net"
|
||||
__version__ = "2.2.1"
|
||||
__hexversion__ = "0x020201f0"
|
||||
__date__ = "2024-01-27"
|
||||
__version__ = "2.3"
|
||||
__hexversion__ = "0x020300f0"
|
||||
__date__ = "2024-03-10"
|
||||
__status__ = "Stable"
|
||||
__domain__ = "novelwriter.io"
|
||||
|
||||
@@ -131,7 +131,7 @@ def main(sysArgs: list | None = None):
|
||||
elif inOpt == "--debug":
|
||||
CONFIG.isDebug = True
|
||||
logLevel = logging.DEBUG
|
||||
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
|
||||
logFormat = "[{asctime:}] {filename:>18}:{lineno:<4d} {levelname:8} {message:}"
|
||||
elif inOpt == "--style":
|
||||
qtStyle = inArg
|
||||
elif inOpt == "--config":
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
{
|
||||
"Synopsis": "Synopsis",
|
||||
"Short Description": "Korte beschrijving",
|
||||
"Comment": "Opmerking",
|
||||
"Notes": "Notities",
|
||||
"Tag": "Label",
|
||||
"Point of View": "Perspectief",
|
||||
"Focus": "Focus",
|
||||
"Characters": "Personages",
|
||||
"Plot": "Plot",
|
||||
"Timeline": "Tijdslijn",
|
||||
"Locations": "Locaties",
|
||||
"Objects": "Objecten",
|
||||
"Entities": "Entiteiten",
|
||||
"Custom": "Aangepast",
|
||||
"0": "nul",
|
||||
"1": "één",
|
||||
"2": "twee",
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
{
|
||||
"Synopsis": "Sinopse",
|
||||
"Short Description": "Descrição breve",
|
||||
"Comment": "Comentários",
|
||||
"Notes": "Notas",
|
||||
"Tag": "Etiqueta",
|
||||
"Point of View": "Ponto de vista",
|
||||
"Focus": "Foco",
|
||||
"Characters": "Personagens",
|
||||
"Plot": "Trama",
|
||||
"Timeline": "Linha do tempo",
|
||||
"Locations": "Lugares",
|
||||
"Objects": "Objetos",
|
||||
"Entities": "Entidades",
|
||||
"Custom": "Outros",
|
||||
"0": "Zero",
|
||||
"1": "Um",
|
||||
"2": "Dois",
|
||||
|
||||
@@ -17,6 +17,7 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
[Map]
|
||||
add = typ_plus.svg
|
||||
add_document = typ_document-add.svg
|
||||
alert_error = typ_delete-full.svg
|
||||
alert_info = typ_lightbulb-full.svg
|
||||
alert_question = typ_directions-full.svg
|
||||
@@ -39,10 +40,12 @@ cls_none = typ_cancel.svg
|
||||
cls_novel = typ_book.svg
|
||||
cls_object = typ_key.svg
|
||||
cls_plot = typ_puzzle.svg
|
||||
cls_template = mixed_document-new.svg
|
||||
cls_timeline = typ_calendar.svg
|
||||
cls_trash = typ_trash.svg
|
||||
cls_world = typ_location.svg
|
||||
cross = typ_times.svg
|
||||
document = typ_document.svg
|
||||
down = typ_chevron-down.svg
|
||||
edit = typ_pencil.svg
|
||||
export = typ_export.svg
|
||||
@@ -56,10 +59,14 @@ fmt_subscript = nw_tb-subscript.svg
|
||||
fmt_superscript = nw_tb-superscript.svg
|
||||
fmt_underline = nw_tb-underline.svg
|
||||
forward = typ_chevron-right.svg
|
||||
import = mixed_import.svg
|
||||
list = typ_th-list.svg
|
||||
maximise = typ_arrow-maximise.svg
|
||||
menu = typ_th-dot-menu.svg
|
||||
minimise = typ_arrow-minimise.svg
|
||||
more = typ_th-dot-more.svg
|
||||
noncheckable = mixed_input-none.svg
|
||||
open = typ_folder.svg
|
||||
panel = nw_panel.svg
|
||||
proj_chapter = mixed_document-chapter.svg
|
||||
proj_details = typ_th-list-grey.svg
|
||||
@@ -83,6 +90,7 @@ search_regex = nw_search-regex.svg
|
||||
search_replace = mixed_search-replace.svg
|
||||
search_word = nw_search-word.svg
|
||||
settings = typ_cog.svg
|
||||
star = typ_star.svg
|
||||
status_idle = typ_media-pause-grey.svg
|
||||
status_lang = typ_globe-grey.svg
|
||||
status_lines = typ_th-list-grey.svg
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m5.8898 3.4449c-0.67467 1e-7 -1.2225 0.5478-1.2225 1.2225v14.665c0 0.67466 0.5478 1.2225 1.2225 1.2225h12.22c0.67466 0 1.2225-0.5478 1.2225-1.2225v-11h-1.8326c-1.6854 0-3.0551-1.3696-3.0551-3.0551v-1.8326zm9.7775 0.50488v1.3277c0 1.0108 0.82184 1.8326 1.8326 1.8326h1.3277z" fill="#e5e5e5" fill-opacity=".5" stroke-linecap="round" stroke-width=".55"/>
|
||||
<path d="m5.8898 1c-2.0216 0-3.6674 1.6458-3.6674 3.6674v14.665c0 2.0216 1.6458 3.6674 3.6674 3.6674h12.22c2.0216 0 3.6674-1.6458 3.6674-3.6674v-12.222c0-0.32511-0.12901-0.63512-0.35879-0.86367l-4.8877-4.8877c-0.22856-0.22978-0.53856-0.35879-0.86367-0.35879zm0 2.4449h8.5551v1.8326c0 1.6854 1.3696 3.0551 3.0551 3.0551h1.8326v11c0 0.67466-0.5478 1.2225-1.2225 1.2225h-12.22c-0.67467 0-1.2225-0.5478-1.2225-1.2225v-14.665c0-0.67467 0.5478-1.2225 1.2225-1.2225zm9.7775 0.50488 3.1604 3.1604h-1.3277c-1.0108 0-1.8326-0.82184-1.8326-1.8326z" fill="#aeaeae" stroke-width="1.2222"/>
|
||||
<path d="m10.105 9.9584-3.785 0.4196c-0.13651 0.01653-0.25722 0.10777-0.30249 0.24644-0.045266 0.13867 0 0.28308 0.10059 0.37434 1.1252 1.0267 2.8158 2.5643 2.8158 2.5643-0.00143 0-0.46415 2.2367-0.77166 3.729-0.025865 0.13508 0.022992 0.27806 0.14083 0.36356 0.11711 0.0855 0.268 0.08694 0.38655 0.02012 1.3249-0.753 3.3094-1.8853 3.3094-1.8853l3.308 1.886c0.12071 0.0661 0.27159 0.06467 0.38871-0.02084 0.11783-0.0855 0.16669-0.22848 0.1401-0.36284l-0.7695-3.7297 2.8158-2.5622c0.10059-0.09412 0.14514-0.23854 0.10059-0.37649-0.04454-0.13794-0.16525-0.22919-0.30177-0.245-1.5146-0.16956-3.7857-0.42104-3.7857-0.42104l-1.5685-3.4703c-0.05892-0.1243-0.1825-0.21124-0.32764-0.21124-0.14514 0-0.26944 0.087654-0.32548 0.21124z" fill="#ffff84" stroke-width=".7185"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
5
novelwriter/assets/icons/typicons_dark/mixed_import.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m3.1113 3.666c-0.61332 0-1.1094 0.49689-1.1094 1.1113v15.557h-0.0019531c0 0.61443 0.498 1.1094 1.1113 1.1094h15.557c0.61332 0 1.1113-0.49494 1.1113-1.1094v-8.2578c-0.61899 0.57364-1.3634 1.0131-2.252 1.2578-0.004058 2.0524-0.007162 4.0735 0.005859 5.8887h-13.311v-13.318c1.6116-0.00848 2.8992 0 4.4648 0 0.75712-0.75269 1.5907-1.5005 2.3418-2.2383h-7.918z" fill="#9c9"/>
|
||||
<path d="m21.996 3.1106v-0.55556c-1.8789 2.8644-4 4.3922-6.6667 4.4444v-3.3333c0-0.61222-0.56778-1.1111-1.27-1.1111-0.40444 0-0.75 0.17556-0.98111 0.43444-2.1478 2.2544-6.6378 6.7878-6.6378 6.7878s4.49 4.5356 6.6389 6.8189c0.23111 0.22778 0.57556 0.40333 0.98 0.40333 0.70222 0 1.27-0.49667 1.27-1.1111v-3.3333c5.1778 0 6.6667-5.4122 6.6667-9.4444z" fill="#9c9" stroke-width="1.1111"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 912 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m15.667 12h-2.4444v-2.4444c0-0.67589-0.54633-1.2222-1.2222-1.2222s-1.2222 0.54633-1.2222 1.2222v2.4444h-2.4444c-0.67589 0-1.2222 0.54633-1.2222 1.2222s0.54633 1.2222 1.2222 1.2222h2.4444v2.4444c0 0.67589 0.54633 1.2222 1.2222 1.2222s1.2222-0.54633 1.2222-1.2222v-2.4444h2.4444c0.67589 0 1.2222-0.54633 1.2222-1.2222s-0.54633-1.2222-1.2222-1.2222zm5.753-5.753-4.8889-4.8889c-0.22856-0.22978-0.539-0.35811-0.86411-0.35811h-9.7778c-2.0216 0-3.6667 1.6451-3.6667 3.6667v14.667c0 2.0216 1.6451 3.6667 3.6667 3.6667h12.222c2.0216 0 3.6667-1.6451 3.6667-3.6667v-12.222c0-0.32511-0.12833-0.63556-0.35811-0.86411zm-2.5923 0.86411h-1.3273c-1.0108 0-1.8333-0.82256-1.8333-1.8333v-1.3273zm-0.71622 13.444h-12.222c-0.67467 0-1.2222-0.54756-1.2222-1.2222v-14.667c0-0.67467 0.54756-1.2222 1.2222-1.2222h8.5556v1.8333c0 1.6854 1.3701 3.0556 3.0556 3.0556h1.8333v11c0 0.67467-0.54756 1.2222-1.2222 1.2222z" fill="#aeaeae" stroke-width="1.2222"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
4
novelwriter/assets/icons/typicons_dark/typ_document.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m21.42 6.247-4.8889-4.8889c-0.22856-0.22978-0.539-0.35811-0.86411-0.35811h-9.7778c-2.0216 0-3.6667 1.6451-3.6667 3.6667v14.667c0 2.0216 1.6451 3.6667 3.6667 3.6667h12.222c2.0216 0 3.6667-1.6451 3.6667-3.6667v-12.222c0-0.32511-0.12833-0.63556-0.35811-0.86411zm-2.5923 0.86411h-1.3273c-1.0108 0-1.8333-0.82256-1.8333-1.8333v-1.3273zm-0.71622 13.444h-12.222c-0.67467 0-1.2222-0.54756-1.2222-1.2222v-14.667c0-0.67467 0.54756-1.2222 1.2222-1.2222h8.5556v1.8333c0 1.6854 1.3701 3.0556 3.0556 3.0556h1.8333v11c0 0.67467-0.54756 1.2222-1.2222 1.2222z" fill="#aeaeae" stroke-width="1.2222"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 737 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 22,12 A 2.6315,2.6315 0 0 0 19.3686,9.3686 2.6315,2.6315 0 0 0 16.7372,12 2.6315,2.6315 0 0 0 19.3686,14.6314 2.6315,2.6315 0 0 0 22,12 Z m -7.3686,0 A 2.6315,2.6315 0 0 0 12,9.3686 2.6315,2.6315 0 0 0 9.3686,12 2.6315,2.6315 0 0 0 12,14.6314 2.6315,2.6315 0 0 0 14.6314,12 Z M 7.2628,12 A 2.6315,2.6315 0 0 0 4.6314,9.3686 2.6315,2.6315 0 0 0 2,12 2.6315,2.6315 0 0 0 4.6314,14.6314 2.6315,2.6315 0 0 0 7.2628,12 Z" fill="#aeaeae" stroke-width=".90909"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 612 B |
9
novelwriter/assets/icons/typicons_dark/typ_th-list.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#69c" stroke-width="1.0526">
|
||||
<path d="m19.632 17.263h-7.3684c-1.1611 0-2.1053 0.94421-2.1053 2.1053s0.94421 2.1053 2.1053 2.1053h7.3684c1.1611 0 2.1053-0.94421 2.1053-2.1053s-0.94421-2.1053-2.1053-2.1053zm0-7.3684h-7.3684c-1.1611 0-2.1053 0.94421-2.1053 2.1053s0.94421 2.1053 2.1053 2.1053h7.3684c1.1611 0 2.1053-0.94421 2.1053-2.1053s-0.94421-2.1053-2.1053-2.1053zm0-7.3684h-7.3684c-1.1611 0-2.1053 0.94421-2.1053 2.1053 0 1.1611 0.94421 2.1053 2.1053 2.1053h7.3684c1.1611 0 2.1053-0.94421 2.1053-2.1053 0-1.1611-0.94421-2.1053-2.1053-2.1053z"/>
|
||||
<circle cx="4.8947" cy="19.368" r="2.6316"/>
|
||||
<circle cx="4.8947" cy="12" r="2.6316"/>
|
||||
<circle cx="4.8947" cy="4.6316" r="2.6316"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 846 B |
@@ -17,6 +17,7 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
[Map]
|
||||
add = typ_plus.svg
|
||||
add_document = typ_document-add.svg
|
||||
alert_error = typ_delete-full.svg
|
||||
alert_info = typ_lightbulb-full.svg
|
||||
alert_question = typ_directions-full.svg
|
||||
@@ -39,10 +40,12 @@ cls_none = typ_cancel.svg
|
||||
cls_novel = typ_book.svg
|
||||
cls_object = typ_key.svg
|
||||
cls_plot = typ_puzzle.svg
|
||||
cls_template = mixed_document-new.svg
|
||||
cls_timeline = typ_calendar.svg
|
||||
cls_trash = typ_trash.svg
|
||||
cls_world = typ_location.svg
|
||||
cross = typ_times.svg
|
||||
document = typ_document.svg
|
||||
down = typ_chevron-down.svg
|
||||
edit = typ_pencil.svg
|
||||
export = typ_export.svg
|
||||
@@ -56,10 +59,14 @@ fmt_subscript = nw_tb-subscript.svg
|
||||
fmt_superscript = nw_tb-superscript.svg
|
||||
fmt_underline = nw_tb-underline.svg
|
||||
forward = typ_chevron-right.svg
|
||||
import = mixed_import.svg
|
||||
list = typ_th-list.svg
|
||||
maximise = typ_arrow-maximise.svg
|
||||
menu = typ_th-dot-menu.svg
|
||||
minimise = typ_arrow-minimise.svg
|
||||
more = typ_th-dot-more.svg
|
||||
noncheckable = mixed_input-none.svg
|
||||
open = typ_folder.svg
|
||||
panel = nw_panel.svg
|
||||
proj_chapter = mixed_document-chapter.svg
|
||||
proj_details = typ_th-list-grey.svg
|
||||
@@ -83,6 +90,7 @@ search_regex = nw_search-regex.svg
|
||||
search_replace = mixed_search-replace.svg
|
||||
search_word = nw_search-word.svg
|
||||
settings = typ_cog.svg
|
||||
star = typ_star.svg
|
||||
status_idle = typ_media-pause-grey.svg
|
||||
status_lang = typ_globe-grey.svg
|
||||
status_lines = typ_th-list-grey.svg
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m5.8898 3.4449c-0.67467 1e-7 -1.2225 0.5478-1.2225 1.2225v14.665c0 0.67466 0.5478 1.2225 1.2225 1.2225h12.22c0.67466 0 1.2225-0.5478 1.2225-1.2225v-11h-1.8326c-1.6854 0-3.0551-1.3696-3.0551-3.0551v-1.8326zm9.7775 0.50488v1.3277c0 1.0108 0.82184 1.8326 1.8326 1.8326h1.3277z" fill="#e5e5e5" stroke-linecap="round" stroke-width=".55"/>
|
||||
<path d="m5.8898 1c-2.0216 0-3.6674 1.6458-3.6674 3.6674v14.665c0 2.0216 1.6458 3.6674 3.6674 3.6674h12.22c2.0216 0 3.6674-1.6458 3.6674-3.6674v-12.222c0-0.32511-0.12901-0.63512-0.35879-0.86367l-4.8877-4.8877c-0.22856-0.22978-0.53856-0.35879-0.86367-0.35879zm0 2.4449h8.5551v1.8326c0 1.6854 1.3696 3.0551 3.0551 3.0551h1.8326v11c0 0.67466-0.5478 1.2225-1.2225 1.2225h-12.22c-0.67467 0-1.2225-0.5478-1.2225-1.2225v-14.665c0-0.67467 0.5478-1.2225 1.2225-1.2225zm9.7775 0.50488 3.1604 3.1604h-1.3277c-1.0108 0-1.8326-0.82184-1.8326-1.8326z" fill="#333" stroke-width="1.2222"/>
|
||||
<path d="m10.105 9.9584-3.785 0.4196c-0.13651 0.01653-0.25722 0.10777-0.30249 0.24644-0.04526 0.13867 0 0.28308 0.10059 0.37434 1.1252 1.0267 2.8158 2.5643 2.8158 2.5643-0.0014 0-0.46415 2.2367-0.77166 3.729-0.02587 0.13508 0.02299 0.27806 0.14082 0.36356 0.11711 0.0855 0.268 0.08694 0.38655 0.02012 1.3249-0.753 3.3094-1.8853 3.3094-1.8853l3.308 1.886c0.12071 0.0661 0.27159 0.06467 0.38871-0.02084 0.11783-0.0855 0.16669-0.22848 0.1401-0.36284l-0.7695-3.7297 2.8158-2.5622c0.10059-0.09412 0.14514-0.23854 0.10059-0.37649-0.04454-0.13795-0.16525-0.2292-0.30177-0.24501-1.5146-0.16956-3.7857-0.42103-3.7857-0.42103l-1.5685-3.4703c-0.05892-0.1243-0.1825-0.21124-0.32764-0.21124-0.14514 0-0.26944 0.087654-0.32548 0.21124z" fill="#eab700" stroke-width=".7185"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
5
novelwriter/assets/icons/typicons_light/mixed_import.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m3.1113 3.666c-0.61332 0-1.1094 0.49689-1.1094 1.1113v15.557h-0.0019531c0 0.61443 0.498 1.1094 1.1113 1.1094h15.557c0.61332 0 1.1113-0.49494 1.1113-1.1094v-8.2578c-0.61899 0.57364-1.3634 1.0131-2.252 1.2578-0.004058 2.0524-0.007162 4.0735 0.005859 5.8887h-13.311v-13.318c1.6116-0.00848 2.8992 0 4.4648 0 0.75712-0.75269 1.5907-1.5005 2.3418-2.2383h-7.918z" fill="#718c00"/>
|
||||
<path d="m21.996 3.1106v-0.55556c-1.8789 2.8644-4 4.3922-6.6667 4.4444v-3.3333c0-0.61222-0.56778-1.1111-1.27-1.1111-0.40444 0-0.75 0.17556-0.98111 0.43444-2.1478 2.2544-6.6378 6.7878-6.6378 6.7878s4.49 4.5356 6.6389 6.8189c0.23111 0.22778 0.57556 0.40333 0.98 0.40333 0.70222 0 1.27-0.49667 1.27-1.1111v-3.3333c5.1778 0 6.6667-5.4122 6.6667-9.4444z" fill="#718c00" stroke-width="1.1111"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 918 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m15.667 12h-2.4444v-2.4444c0-0.67589-0.54633-1.2222-1.2222-1.2222s-1.2222 0.54633-1.2222 1.2222v2.4444h-2.4444c-0.67589 0-1.2222 0.54633-1.2222 1.2222s0.54633 1.2222 1.2222 1.2222h2.4444v2.4444c0 0.67589 0.54633 1.2222 1.2222 1.2222s1.2222-0.54633 1.2222-1.2222v-2.4444h2.4444c0.67589 0 1.2222-0.54633 1.2222-1.2222s-0.54633-1.2222-1.2222-1.2222zm5.753-5.753-4.8889-4.8889c-0.22856-0.22978-0.539-0.35811-0.86411-0.35811h-9.7778c-2.0216 0-3.6667 1.6451-3.6667 3.6667v14.667c0 2.0216 1.6451 3.6667 3.6667 3.6667h12.222c2.0216 0 3.6667-1.6451 3.6667-3.6667v-12.222c0-0.32511-0.12833-0.63556-0.35811-0.86411zm-2.5923 0.86411h-1.3273c-1.0108 0-1.8333-0.82256-1.8333-1.8333v-1.3273zm-0.71622 13.444h-12.222c-0.67467 0-1.2222-0.54756-1.2222-1.2222v-14.667c0-0.67467 0.54756-1.2222 1.2222-1.2222h8.5556v1.8333c0 1.6854 1.3701 3.0556 3.0556 3.0556h1.8333v11c0 0.67467-0.54756 1.2222-1.2222 1.2222z" fill-opacity=".78039" stroke-width="1.2222"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
4
novelwriter/assets/icons/typicons_light/typ_document.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m21.42 6.247-4.8889-4.8889c-0.22856-0.22978-0.539-0.35811-0.86411-0.35811h-9.7778c-2.0216 0-3.6667 1.6451-3.6667 3.6667v14.667c0 2.0216 1.6451 3.6667 3.6667 3.6667h12.222c2.0216 0 3.6667-1.6451 3.6667-3.6667v-12.222c0-0.32511-0.12833-0.63556-0.35811-0.86411zm-2.5923 0.86411h-1.3273c-1.0108 0-1.8333-0.82256-1.8333-1.8333v-1.3273zm-0.71622 13.444h-12.222c-0.67467 0-1.2222-0.54756-1.2222-1.2222v-14.667c0-0.67467 0.54756-1.2222 1.2222-1.2222h8.5556v1.8333c0 1.6854 1.3701 3.0556 3.0556 3.0556h1.8333v11c0 0.67467-0.54756 1.2222-1.2222 1.2222z" fill-opacity=".78039" stroke-width="1.2222"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 744 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 22,12 A 2.6315,2.6315 0 0 0 19.3686,9.3686 2.6315,2.6315 0 0 0 16.7372,12 2.6315,2.6315 0 0 0 19.3686,14.6314 2.6315,2.6315 0 0 0 22,12 Z m -7.3686,0 A 2.6315,2.6315 0 0 0 12,9.3686 2.6315,2.6315 0 0 0 9.3686,12 2.6315,2.6315 0 0 0 12,14.6314 2.6315,2.6315 0 0 0 14.6314,12 Z M 7.2628,12 A 2.6315,2.6315 0 0 0 4.6314,9.3686 2.6315,2.6315 0 0 0 2,12 2.6315,2.6315 0 0 0 4.6314,14.6314 2.6315,2.6315 0 0 0 7.2628,12 Z" fill-opacity=".78039" stroke-width=".90909"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 619 B |
9
novelwriter/assets/icons/typicons_light/typ_th-list.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#4271ae" stroke-width="1.0526">
|
||||
<path d="m19.632 17.263h-7.3684c-1.1611 0-2.1053 0.94421-2.1053 2.1053s0.94421 2.1053 2.1053 2.1053h7.3684c1.1611 0 2.1053-0.94421 2.1053-2.1053s-0.94421-2.1053-2.1053-2.1053zm0-7.3684h-7.3684c-1.1611 0-2.1053 0.94421-2.1053 2.1053s0.94421 2.1053 2.1053 2.1053h7.3684c1.1611 0 2.1053-0.94421 2.1053-2.1053s-0.94421-2.1053-2.1053-2.1053zm0-7.3684h-7.3684c-1.1611 0-2.1053 0.94421-2.1053 2.1053 0 1.1611 0.94421 2.1053 2.1053 2.1053h7.3684c1.1611 0 2.1053-0.94421 2.1053-2.1053 0-1.1611-0.94421-2.1053-2.1053-2.1053z"/>
|
||||
<circle cx="4.8947" cy="19.368" r="2.6316"/>
|
||||
<circle cx="4.8947" cy="12" r="2.6316"/>
|
||||
<circle cx="4.8947" cy="4.6316" r="2.6316"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 849 B |
4
novelwriter/assets/images/novelwriter-text-dark.svg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
4
novelwriter/assets/images/novelwriter-text-light.svg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
novelwriter/assets/images/welcome-dark.jpg
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
novelwriter/assets/images/welcome-light.jpg
Normal file
|
After Width: | Height: | Size: 273 KiB |
|
Before Width: | Height: | Size: 116 KiB |
26
novelwriter/assets/syntax/cyberpunk_night.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
[Main]
|
||||
name = Cyberpunk Night
|
||||
author = Anders Lemvigh
|
||||
url = https://github.com/alemvigh
|
||||
license = CC BY-SA 4.0
|
||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
[Syntax]
|
||||
background = 0, 0, 0
|
||||
text = 150, 150, 150
|
||||
link = 77, 077, 255
|
||||
headertext = 255, 255, 255
|
||||
headertag = 50, 0, 180
|
||||
emphasis = 0, 255, 255
|
||||
straightquotes = 7, 27, 219
|
||||
doublequotes = 0, 255, 0
|
||||
singlequotes = 0, 140, 255
|
||||
hidden = 77, 77, 100
|
||||
shortcode = 255, 255, 0
|
||||
keyword = 255, 100, 255
|
||||
value = 255, 150, 10
|
||||
spellcheckline = 242, 72, 23
|
||||
errorline = 186, 218, 4
|
||||
replacetag = 0, 0, 180
|
||||
modifier = 144, 142, 176
|
||||
optional = 180, 180, 180
|
||||
@@ -20,6 +20,7 @@ hidden = 150, 150, 150
|
||||
shortcode = 0, 155, 200
|
||||
keyword = 200, 46, 0
|
||||
value = 184, 200, 0
|
||||
optional = 0, 155, 200
|
||||
spellcheckline = 200, 46, 0
|
||||
errorline = 46, 200, 0
|
||||
replacetag = 0, 184, 46
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 100, 100, 100
|
||||
shortcode = 0, 100, 0
|
||||
keyword = 200, 50, 50
|
||||
value = 50, 150, 50
|
||||
optional = 0, 100, 0
|
||||
spellcheckline = 200, 0, 0
|
||||
errorline = 0, 150, 0
|
||||
replacetag = 0, 150, 0
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 150, 150, 150
|
||||
shortcode = 225, 225, 225
|
||||
keyword = 225, 225, 225
|
||||
value = 200, 200, 200
|
||||
optional = 225, 225, 225
|
||||
spellcheckline = 200, 46, 0
|
||||
errorline = 46, 200, 0
|
||||
replacetag = 225, 225, 225
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 100, 100, 100
|
||||
shortcode = 0, 0, 0
|
||||
keyword = 0, 0, 0
|
||||
value = 20, 20, 20
|
||||
optional = 0, 0, 0
|
||||
spellcheckline = 200, 0, 0
|
||||
errorline = 0, 150, 0
|
||||
replacetag = 0, 0, 0
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 152, 159, 177
|
||||
shortcode = 40, 142, 215
|
||||
keyword = 222, 61, 58
|
||||
value = 150, 74, 193
|
||||
optional = 40, 142, 215
|
||||
spellcheckline = 222, 61, 58
|
||||
errorline = 8, 145, 106
|
||||
replacetag = 42, 162, 152
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 99, 119, 119
|
||||
shortcode = 130, 170, 255
|
||||
keyword = 247, 140, 108
|
||||
value = 199, 146, 234
|
||||
optional = 130, 170, 255
|
||||
spellcheckline = 247, 140, 108
|
||||
errorline = 173, 219, 103
|
||||
replacetag = 127, 219, 202
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 147, 161, 161
|
||||
shortcode = 147, 161, 161
|
||||
keyword = 133, 153, 0
|
||||
value = 203, 75, 22
|
||||
optional = 147, 161, 161
|
||||
spellcheckline = 203, 75, 22
|
||||
errorline = 220, 50, 47
|
||||
replacetag = 133, 153, 0
|
||||
|
||||
@@ -20,6 +20,7 @@ hidden = 88, 110, 117
|
||||
shortcode = 88, 110, 117
|
||||
keyword = 133, 153, 0
|
||||
value = 203, 75, 22
|
||||
optional = 88, 110, 117
|
||||
spellcheckline = 203, 75, 22
|
||||
errorline = 220, 50, 47
|
||||
replacetag = 133, 153, 0
|
||||
|
||||
23
novelwriter/assets/syntax/tango.conf
Normal file
@@ -0,0 +1,23 @@
|
||||
[Main]
|
||||
name = Tango
|
||||
author = Veronica Berglyd Olsen (adaptation)
|
||||
|
||||
[Syntax]
|
||||
background = 48, 48, 47
|
||||
text = 238, 238, 236
|
||||
link = 115, 159, 207
|
||||
headertext = 115, 159, 207
|
||||
headertag = 52, 101, 164
|
||||
emphasis = 196, 160, 0
|
||||
straightquotes = 239, 41, 41
|
||||
doublequotes = 138, 226, 52
|
||||
singlequotes = 252, 233, 79
|
||||
hidden = 143, 143, 141
|
||||
shortcode = 115, 159, 207
|
||||
keyword = 239, 41, 41
|
||||
value = 173, 127, 168
|
||||
optional = 115, 159, 207
|
||||
spellcheckline = 239, 41, 41
|
||||
errorline = 138, 226, 52
|
||||
replacetag = 51, 226, 226
|
||||
modifier = 196, 160, 0
|
||||
@@ -40,6 +40,7 @@ hidden = 142, 144, 140
|
||||
shortcode = 66, 113, 174
|
||||
keyword = 240, 40, 41
|
||||
value = 137, 89, 168
|
||||
optional = 66, 113, 174
|
||||
spellcheckline = 240, 40, 41
|
||||
errorline = 113, 140, 0
|
||||
replacetag = 62, 153, 159
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 150, 152, 150
|
||||
shortcode = 129, 162, 190
|
||||
keyword = 204, 102, 102
|
||||
value = 178, 148, 187
|
||||
optional = 129, 162, 190
|
||||
spellcheckline = 204, 102, 102
|
||||
errorline = 181, 189, 104
|
||||
replacetag = 138, 190, 183
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 114, 133, 183
|
||||
shortcode = 187, 218, 255
|
||||
keyword = 255, 157, 164
|
||||
value = 235, 187, 255
|
||||
optional = 187, 218, 255
|
||||
spellcheckline = 255, 157, 164
|
||||
errorline = 209, 241, 169
|
||||
replacetag = 153, 255, 255
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 150, 152, 150
|
||||
shortcode = 122, 166, 218
|
||||
keyword = 213, 78, 83
|
||||
value = 195, 151, 216
|
||||
optional = 122, 166, 218
|
||||
spellcheckline = 213, 78, 83
|
||||
errorline = 185, 202, 74
|
||||
replacetag = 112, 192, 177
|
||||
|
||||
@@ -40,6 +40,7 @@ hidden = 153, 153, 153
|
||||
shortcode = 102, 153, 204
|
||||
keyword = 242, 119, 122
|
||||
value = 204, 153, 204
|
||||
optional = 102, 153, 204
|
||||
spellcheckline = 242, 119, 122
|
||||
errorline = 153, 204, 153
|
||||
replacetag = 102, 204, 204
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Credits</h2>
|
||||
|
||||
<h3>Main Developer</h3>
|
||||
<p>Veronica Berglyd Olsen (<a href="https://github.com/vkbo">@vkbo</a>)</p>
|
||||
|
||||
@@ -17,6 +15,10 @@
|
||||
<p>For other contributions, see the project's
|
||||
<a href="https://github.com/vkbo/novelWriter/graphs/contributors">Contributors</a> page.</p>
|
||||
|
||||
<h3>Artwork</h3>
|
||||
|
||||
<p>The artwork on the Welcome dialog was created by <a href="https://louisdurrant.art">Louis Durrant</a>.</p>
|
||||
|
||||
<h3>Translations</h3>
|
||||
|
||||
<p>The default language is English (UK) with English (US) as an option. These are the original
|
||||
|
||||
@@ -1,641 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>GNU General Public License v3.0 - GNU Project - Free Software Foundation (FSF)</title>
|
||||
<link rel="alternate" type="application/rdf+xml"
|
||||
href="http://www.gnu.org/licenses/gpl-3.0.rdf" />
|
||||
</head>
|
||||
<body>
|
||||
<h3 style="text-align: center;">GNU GENERAL PUBLIC LICENSE</h3>
|
||||
<p style="text-align: center;">Version 3, 29 June 2007</p>
|
||||
|
||||
<p>Copyright © 2007 Free Software Foundation, Inc.
|
||||
<<a href="https://fsf.org/">https://fsf.org/</a>></p><p>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.</p>
|
||||
|
||||
<h3><a name="preamble"></a>Preamble</h3>
|
||||
|
||||
<p>The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.</p>
|
||||
|
||||
<p>The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.</p>
|
||||
|
||||
<p>When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.</p>
|
||||
|
||||
<p>To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.</p>
|
||||
|
||||
<p>For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.</p>
|
||||
|
||||
<p>Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.</p>
|
||||
|
||||
<p>For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.</p>
|
||||
|
||||
<p>Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.</p>
|
||||
|
||||
<p>Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.</p>
|
||||
|
||||
<p>The precise terms and conditions for copying, distribution and
|
||||
modification follow.</p>
|
||||
|
||||
<h3><a name="terms"></a>TERMS AND CONDITIONS</h3>
|
||||
|
||||
<h4><a name="section0"></a>0. Definitions.</h4>
|
||||
|
||||
<p>“This License” refers to version 3 of the GNU General Public License.</p>
|
||||
|
||||
<p>“Copyright” also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.</p>
|
||||
|
||||
<p>“The Program” refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as “you”. “Licensees” and
|
||||
“recipients” may be individuals or organizations.</p>
|
||||
|
||||
<p>To “modify” a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a “modified version” of the
|
||||
earlier work or a work “based on” the earlier work.</p>
|
||||
|
||||
<p>A “covered work” means either the unmodified Program or a work based
|
||||
on the Program.</p>
|
||||
|
||||
<p>To “propagate” a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.</p>
|
||||
|
||||
<p>To “convey” a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.</p>
|
||||
|
||||
<p>An interactive user interface displays “Appropriate Legal Notices”
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.</p>
|
||||
|
||||
<h4><a name="section1"></a>1. Source Code.</h4>
|
||||
|
||||
<p>The “source code” for a work means the preferred form of the work
|
||||
for making modifications to it. “Object code” means any non-source
|
||||
form of a work.</p>
|
||||
|
||||
<p>A “Standard Interface” means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.</p>
|
||||
|
||||
<p>The “System Libraries” of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
“Major Component”, in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.</p>
|
||||
|
||||
<p>The “Corresponding Source” for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.</p>
|
||||
|
||||
<p>The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.</p>
|
||||
|
||||
<p>The Corresponding Source for a work in source code form is that
|
||||
same work.</p>
|
||||
|
||||
<h4><a name="section2"></a>2. Basic Permissions.</h4>
|
||||
|
||||
<p>All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.</p>
|
||||
|
||||
<p>You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.</p>
|
||||
|
||||
<p>Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.</p>
|
||||
|
||||
<h4><a name="section3"></a>3. Protecting Users' Legal Rights From Anti-Circumvention Law.</h4>
|
||||
|
||||
<p>No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.</p>
|
||||
|
||||
<p>When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.</p>
|
||||
|
||||
<h4><a name="section4"></a>4. Conveying Verbatim Copies.</h4>
|
||||
|
||||
<p>You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.</p>
|
||||
|
||||
<p>You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.</p>
|
||||
|
||||
<h4><a name="section5"></a>5. Conveying Modified Source Versions.</h4>
|
||||
|
||||
<p>You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.</li>
|
||||
|
||||
<li>b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
“keep intact all notices”.</li>
|
||||
|
||||
<li>c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.</li>
|
||||
|
||||
<li>d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.</li>
|
||||
</ul>
|
||||
|
||||
<p>A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
“aggregate” if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.</p>
|
||||
|
||||
<h4><a name="section6"></a>6. Conveying Non-Source Forms.</h4>
|
||||
|
||||
<p>You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.</li>
|
||||
|
||||
<li>b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.</li>
|
||||
|
||||
<li>c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.</li>
|
||||
|
||||
<li>d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.</li>
|
||||
|
||||
<li>e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.</li>
|
||||
</ul>
|
||||
|
||||
<p>A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.</p>
|
||||
|
||||
<p>A “User Product” is either (1) a “consumer product”, which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, “normally used” refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.</p>
|
||||
|
||||
<p>“Installation Information” for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.</p>
|
||||
|
||||
<p>If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).</p>
|
||||
|
||||
<p>The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.</p>
|
||||
|
||||
<p>Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.</p>
|
||||
|
||||
<h4><a name="section7"></a>7. Additional Terms.</h4>
|
||||
|
||||
<p>“Additional permissions” are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.</p>
|
||||
|
||||
<p>When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.</p>
|
||||
|
||||
<p>Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or</li>
|
||||
|
||||
<li>b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or</li>
|
||||
|
||||
<li>c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or</li>
|
||||
|
||||
<li>d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or</li>
|
||||
|
||||
<li>e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or</li>
|
||||
|
||||
<li>f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.</li>
|
||||
</ul>
|
||||
|
||||
<p>All other non-permissive additional terms are considered “further
|
||||
restrictions” within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.</p>
|
||||
|
||||
<p>If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.</p>
|
||||
|
||||
<p>Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.</p>
|
||||
|
||||
<h4><a name="section8"></a>8. Termination.</h4>
|
||||
|
||||
<p>You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).</p>
|
||||
|
||||
<p>However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.</p>
|
||||
|
||||
<p>Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.</p>
|
||||
|
||||
<p>Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.</p>
|
||||
|
||||
<h4><a name="section9"></a>9. Acceptance Not Required for Having Copies.</h4>
|
||||
|
||||
<p>You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.</p>
|
||||
|
||||
<h4><a name="section10"></a>10. Automatic Licensing of Downstream Recipients.</h4>
|
||||
|
||||
<p>Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.</p>
|
||||
|
||||
<p>An “entity transaction” is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.</p>
|
||||
|
||||
<p>You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.</p>
|
||||
|
||||
<h4><a name="section11"></a>11. Patents.</h4>
|
||||
|
||||
<p>A “contributor” is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's “contributor version”.</p>
|
||||
|
||||
<p>A contributor's “essential patent claims” are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, “control” includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.</p>
|
||||
|
||||
<p>Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.</p>
|
||||
|
||||
<p>In the following three paragraphs, a “patent license” is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To “grant” such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.</p>
|
||||
|
||||
<p>If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. “Knowingly relying” means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.</p>
|
||||
|
||||
<p>If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.</p>
|
||||
|
||||
<p>A patent license is “discriminatory” if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.</p>
|
||||
|
||||
<p>Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.</p>
|
||||
|
||||
<h4><a name="section12"></a>12. No Surrender of Others' Freedom.</h4>
|
||||
|
||||
<p>If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.</p>
|
||||
|
||||
<h4><a name="section13"></a>13. Use with the GNU Affero General Public License.</h4>
|
||||
|
||||
<p>Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.</p>
|
||||
|
||||
<h4><a name="section14"></a>14. Revised Versions of this License.</h4>
|
||||
|
||||
<p>The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.</p>
|
||||
|
||||
<p>Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License “or any later version” applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.</p>
|
||||
|
||||
<p>If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.</p>
|
||||
|
||||
<p>Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.</p>
|
||||
|
||||
<h4><a name="section15"></a>15. Disclaimer of Warranty.</h4>
|
||||
|
||||
<p>THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.</p>
|
||||
|
||||
<h4><a name="section16"></a>16. Limitation of Liability.</h4>
|
||||
|
||||
<p>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.</p>
|
||||
|
||||
<h4><a name="section17"></a>17. Interpretation of Sections 15 and 16.</h4>
|
||||
|
||||
<p>If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.</p>
|
||||
|
||||
<p>END OF TERMS AND CONDITIONS</p>
|
||||
|
||||
</body></html>
|
||||
@@ -1,60 +0,0 @@
|
||||
<!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Release Notes for 2.2</h2>
|
||||
<p><i>Released on 17 December 2023</i></p>
|
||||
|
||||
<p>Scroll down for <a href="#patch">Patch Notes</a></p>
|
||||
|
||||
<p>This release comes with a number of new features. These are some highlights.</p>
|
||||
<p>In addition to the common Markdown style formatting for bold, italic and strike through, a set
|
||||
of new shortcodes have been added. The shortcodes are far more flexible than the Markdown style
|
||||
syntax, and can be used for more complex formatting cases. Like when you need to add multiple,
|
||||
overlapping formats, or add emphasis to just a part of a word. The shortcodes also allow for
|
||||
underline, subscript and superscript, which the Markdown syntax does not. The new formats are
|
||||
available in the "Format" menu, and in a new toolbar in the editor that can be enabled by clicking
|
||||
the three dots in the top–left corner. The shortcode format was chosen because it can later be
|
||||
extended to include other requested features as well. Please have a look at the documentation for
|
||||
more details about the new shortcodes.</p>
|
||||
<p>The Tags and References system has been improved. The tags themselves are no longer case
|
||||
sensitive when you use them in references, but they are still displayed as you typed them in the
|
||||
tag definition when they are displayed in the user interface. Starting to type the '@' symbol in
|
||||
the text editor, on a new line, will now open an auto-completer menu which will display available
|
||||
options. It may not display all of your tags if you have a lot of them, but starting to type more
|
||||
characters will filter the list down further.</p>
|
||||
<p>You can now automatically create a note file for a new tag that you have added to a reference
|
||||
list in a document, but is not yet defined in a project note. So, for instance, if you come up with
|
||||
a new character while writing, and add a new tag to your '@char' references, you can right-click
|
||||
the new tag and create a new note for that entry directly. In addition, it is now also possible to
|
||||
right-click a heading in an open document and set the item label in the project tree to match the
|
||||
heading.</p>
|
||||
<p>In addition to the changes in the editor, the "References" panel below the document viewer has
|
||||
also been completely redesigned. It now shows all the references to the document you are viewing as
|
||||
a list, with a lot more details than before. In addition, tabs in the panel will appear to show all
|
||||
the tags you have defined in your notes, sorted as one tab per category. Like for instance
|
||||
Characters, Locations, Objects, etc. You can also give each note a short description comment on the
|
||||
same format as the summary comments for chapters and scenes. The short description comment can be
|
||||
added from the "Insert" menu under "Special Comments".</p>
|
||||
<p>The last major change in this release is the new multi-select feature in the project tree. You
|
||||
can now select multiple documents and folders using the mouse while pressing 'Ctrl' or 'Shift'. By
|
||||
right-clicking the selected items, you can perform a limited set of operations on all of them, like
|
||||
changing active status, and the status or importance labels. You can also drag and drop multiple
|
||||
items under the condition that all the selected items are in the same folder, at the same level.
|
||||
This restriction is in place due to limitations in the framework novelWriter is based on. But this
|
||||
should help in cases where multiple documents need to be moved in and out of folders or between
|
||||
folders. Note that adding the multi-select feature meant that the undo feature of the project tree
|
||||
had to be removed. It may be added back later.</p>
|
||||
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
<a name="patch"></a><h2>Patch Notes</h2>
|
||||
|
||||
<h3>Patch 2.2.1 – 27 January 2024</h3>
|
||||
|
||||
<p>This is a patch release that fixes an issue where the Project View would sometimes switch to the
|
||||
Novel View when a new item was created. This patch also includes updated translations for German
|
||||
and Chinese.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
29
novelwriter/assets/themes/cyberpunk_night.conf
Normal file
@@ -0,0 +1,29 @@
|
||||
[Main]
|
||||
name = Cyberpunk Night
|
||||
description = A taste of the future 80s
|
||||
author = Anders Lemvigh
|
||||
url = https://github.com/alemvigh
|
||||
license = CC BY-SA 4.0
|
||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
icontheme = typicons_dark
|
||||
|
||||
[Palette]
|
||||
window = 0, 0, 0
|
||||
windowtext = 150, 150, 150
|
||||
base = 0, 0, 0
|
||||
alternatebase = 30, 20, 45
|
||||
text = 150, 150, 150
|
||||
tooltipbase = 40, 20, 70
|
||||
tooltiptext = 255, 255, 255
|
||||
button = 5, 0, 10
|
||||
buttontext = 150, 150, 150
|
||||
brighttext = 255, 255, 255
|
||||
highlight = 50, 30, 80
|
||||
highlightedtext = 255, 255, 255
|
||||
link = 77, 77, 255
|
||||
linkvisited = 50, 0, 80
|
||||
|
||||
[GUI]
|
||||
statusnone = 50, 50, 50
|
||||
statussaved = 77, 255, 77
|
||||
statusunsaved = 255, 77, 77
|
||||
@@ -21,8 +21,8 @@ buttontext = 204, 204, 204
|
||||
brighttext = 62, 62, 62
|
||||
highlight = 44, 152, 247
|
||||
highlightedtext = 255, 255, 255
|
||||
link = 44, 152, 247
|
||||
linkvisited = 44, 152, 247
|
||||
link = 102, 153, 204
|
||||
linkvisited = 102, 153, 204
|
||||
|
||||
[GUI]
|
||||
helptext = 164, 164, 164
|
||||
|
||||
@@ -21,8 +21,8 @@ buttontext = 0, 0, 0
|
||||
brighttext = 255, 255, 255
|
||||
highlight = 48, 135, 198
|
||||
highlightedtext = 255, 255, 255
|
||||
link = 0, 84, 255
|
||||
linkvisited = 0, 84, 255
|
||||
link = 66, 113, 174
|
||||
linkvisited = 66, 113, 174
|
||||
|
||||
[GUI]
|
||||
helptext = 92, 92, 92
|
||||
|
||||
@@ -29,7 +29,7 @@ import logging
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from typing import Any, Literal
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from configparser import ConfigParser
|
||||
@@ -41,7 +41,10 @@ from PyQt5.QtCore import QCoreApplication, QUrl
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.constants import nwConst, nwUnicode
|
||||
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import TypeGuard # Requires Python 3.10
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -104,15 +107,6 @@ def checkBool(value: Any, default: bool) -> bool:
|
||||
return default
|
||||
|
||||
|
||||
def checkHandle(value, default, allowNone=False):
|
||||
"""Check if a value is a handle."""
|
||||
if allowNone and (value is None or value == "None"):
|
||||
return None
|
||||
if isHandle(value):
|
||||
return str(value)
|
||||
return default
|
||||
|
||||
|
||||
def checkUuid(value: Any, default: str) -> str:
|
||||
"""Try to process a value as an UUID, or return a default."""
|
||||
try:
|
||||
@@ -135,7 +129,7 @@ def checkPath(value: Any, default: Path) -> Path:
|
||||
# Validator Functions
|
||||
##
|
||||
|
||||
def isHandle(value: Any) -> bool:
|
||||
def isHandle(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid novelWriter handle.
|
||||
Note: This is case sensitive. Must be lower case!
|
||||
"""
|
||||
@@ -149,7 +143,7 @@ def isHandle(value: Any) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def isTitleTag(value: Any) -> bool:
|
||||
def isTitleTag(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid title tag string."""
|
||||
if not isinstance(value, str):
|
||||
return False
|
||||
@@ -163,19 +157,19 @@ def isTitleTag(value: Any) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def isItemClass(value: str) -> bool:
|
||||
def isItemClass(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid nwItemClass identifier."""
|
||||
return value in nwItemClass.__members__
|
||||
return isinstance(value, str) and value in nwItemClass.__members__
|
||||
|
||||
|
||||
def isItemType(value: str) -> bool:
|
||||
def isItemType(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid nwItemType identifier."""
|
||||
return value in nwItemType.__members__
|
||||
return isinstance(value, str) and value in nwItemType.__members__
|
||||
|
||||
|
||||
def isItemLayout(value: str) -> bool:
|
||||
def isItemLayout(value: Any) -> TypeGuard[str]:
|
||||
"""Check if a string is a valid nwItemLayout identifier."""
|
||||
return value in nwItemLayout.__members__
|
||||
return isinstance(value, str) and value in nwItemLayout.__members__
|
||||
|
||||
|
||||
def hexToInt(value: Any, default: int = 0) -> int:
|
||||
@@ -189,8 +183,7 @@ def hexToInt(value: Any, default: int = 0) -> int:
|
||||
|
||||
|
||||
def minmax(value: int, minVal: int, maxVal: int) -> int:
|
||||
"""Make sure an integer is between min and max value (inclusive).
|
||||
"""
|
||||
"""Check that an value is between min and max value (inclusive)."""
|
||||
return min(maxVal, max(minVal, value))
|
||||
|
||||
|
||||
@@ -213,17 +206,17 @@ def formatInt(value: int) -> str:
|
||||
if not isinstance(value, int):
|
||||
return "ERR"
|
||||
|
||||
theVal = float(value)
|
||||
if theVal > 1000.0:
|
||||
fVal = float(value)
|
||||
if fVal > 1000.0:
|
||||
for pF in ["k", "M", "G", "T", "P", "E"]:
|
||||
theVal /= 1000.0
|
||||
if theVal < 1000.0:
|
||||
if theVal < 10.0:
|
||||
return f"{theVal:4.2f}{nwUnicode.U_THSP}{pF}"
|
||||
elif theVal < 100.0:
|
||||
return f"{theVal:4.1f}{nwUnicode.U_THSP}{pF}"
|
||||
fVal /= 1000.0
|
||||
if fVal < 1000.0:
|
||||
if fVal < 10.0:
|
||||
return f"{fVal:4.2f}{nwUnicode.U_THSP}{pF}"
|
||||
elif fVal < 100.0:
|
||||
return f"{fVal:4.1f}{nwUnicode.U_THSP}{pF}"
|
||||
else:
|
||||
return f"{theVal:3.0f}{nwUnicode.U_THSP}{pF}"
|
||||
return f"{fVal:3.0f}{nwUnicode.U_THSP}{pF}"
|
||||
|
||||
return str(value)
|
||||
|
||||
@@ -250,6 +243,24 @@ def formatTime(t: int) -> str:
|
||||
return "ERROR"
|
||||
|
||||
|
||||
def formatVersion(value: str) -> str:
|
||||
"""Format a version number into a more human readable form."""
|
||||
return value.lower().replace("a", " Alpha ").replace("b", " Beta ").replace("rc", " RC ")
|
||||
|
||||
|
||||
def formatFileFilter(extensions: list[str | tuple[str, str]]) -> str:
|
||||
"""Format a list of extensions, or extension + label pairs into a
|
||||
QFileDialog extensions filter.
|
||||
"""
|
||||
result = []
|
||||
for ext in extensions:
|
||||
if isinstance(ext, str):
|
||||
result.append(f"{trConst(nwLabels.FILE_FILTERS.get(ext))} ({ext})")
|
||||
elif isinstance(ext, tuple) and len(ext) == 2:
|
||||
result.append(f"{ext[0]} ({ext[1]})")
|
||||
return ";;".join(result)
|
||||
|
||||
|
||||
##
|
||||
# String Functions
|
||||
##
|
||||
@@ -270,22 +281,22 @@ def transferCase(source: str, target: str) -> str:
|
||||
"""Transfers the case of the source word to the target word. This
|
||||
will consider all upper or lower, and first char capitalisation.
|
||||
"""
|
||||
theResult = target
|
||||
result = target
|
||||
|
||||
if not isinstance(source, str) or not isinstance(target, str):
|
||||
return theResult
|
||||
return result
|
||||
if len(target) < 1 or len(source) < 1:
|
||||
return theResult
|
||||
return result
|
||||
|
||||
if source.istitle():
|
||||
theResult = target.title()
|
||||
result = target.title()
|
||||
|
||||
if source.isupper():
|
||||
theResult = target.upper()
|
||||
result = target.upper()
|
||||
elif source.islower():
|
||||
theResult = target.lower()
|
||||
result = target.lower()
|
||||
|
||||
return theResult
|
||||
return result
|
||||
|
||||
|
||||
def fuzzyTime(seconds: int) -> str:
|
||||
|
||||
@@ -110,8 +110,8 @@ class Config:
|
||||
|
||||
# Size Settings
|
||||
self._mainWinSize = [1200, 650] # Last size of the main GUI window
|
||||
self._welcomeSize = [800, 550] # Last size of the welcome window
|
||||
self._prefsWinSize = [700, 615] # Last size of the Preferences dialog
|
||||
self._projLoadCols = [280, 60, 160] # Last columns widths of the Project Load dialog
|
||||
self._mainPanePos = [300, 800] # Last position of the main window splitter
|
||||
self._viewPanePos = [500, 150] # Last position of the document viewer splitter
|
||||
self._outlnPanePos = [500, 150] # Last position of the outline panel splitter
|
||||
@@ -150,9 +150,6 @@ class Config:
|
||||
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
||||
self.scrollPastEnd = True # Scroll past end of document, and centre cursor
|
||||
|
||||
self.wordCountTimer = 5.0 # Interval for word count update in seconds
|
||||
self.incNotesWCount = True # The status bar word count includes notes
|
||||
|
||||
self.highlightQuotes = True # Highlight text in quotes
|
||||
self.allowOpenSQuote = False # Allow open-ended single quotes
|
||||
self.allowOpenDQuote = True # Allow open-ended double quotes
|
||||
@@ -160,6 +157,7 @@ class Config:
|
||||
|
||||
self.stopWhenIdle = True # Stop the status bar clock when the user is idle
|
||||
self.userIdleTime = 300 # Time of inactivity to consider user idle
|
||||
self.incNotesWCount = True # The status bar word count includes notes
|
||||
|
||||
# User-Selected Symbol Settings
|
||||
self.fmtApostrophe = nwUnicode.U_RSQUO
|
||||
@@ -250,12 +248,12 @@ class Config:
|
||||
return [int(x*self.guiScale) for x in self._mainWinSize]
|
||||
|
||||
@property
|
||||
def preferencesWinSize(self) -> list[int]:
|
||||
return [int(x*self.guiScale) for x in self._prefsWinSize]
|
||||
def welcomeWinSize(self) -> list[int]:
|
||||
return [int(x*self.guiScale) for x in self._welcomeSize]
|
||||
|
||||
@property
|
||||
def projLoadColWidths(self) -> list[int]:
|
||||
return [int(x*self.guiScale) for x in self._projLoadCols]
|
||||
def preferencesWinSize(self) -> list[int]:
|
||||
return [int(x*self.guiScale) for x in self._prefsWinSize]
|
||||
|
||||
@property
|
||||
def mainPanePos(self) -> list[int]:
|
||||
@@ -306,17 +304,18 @@ class Config:
|
||||
self._mainWinSize[1] = height
|
||||
return
|
||||
|
||||
def setWelcomeWinSize(self, width: int, height: int) -> None:
|
||||
"""Set the size of the Preferences dialog window."""
|
||||
self._welcomeSize[0] = int(width/self.guiScale)
|
||||
self._welcomeSize[1] = int(height/self.guiScale)
|
||||
return
|
||||
|
||||
def setPreferencesWinSize(self, width: int, height: int) -> None:
|
||||
"""Set the size of the Preferences dialog window."""
|
||||
self._prefsWinSize[0] = int(width/self.guiScale)
|
||||
self._prefsWinSize[1] = int(height/self.guiScale)
|
||||
return
|
||||
|
||||
def setProjLoadColWidths(self, widths: list[int]) -> None:
|
||||
"""Set the column widths of the Load Project dialog."""
|
||||
self._projLoadCols = [int(x/self.guiScale) for x in widths]
|
||||
return
|
||||
|
||||
def setMainPanePos(self, pos: list[int]) -> None:
|
||||
"""Set the position of the main GUI splitter."""
|
||||
self._mainPanePos = [int(x/self.guiScale) for x in pos]
|
||||
@@ -410,10 +409,10 @@ class Config:
|
||||
"""Compile and return error messages from the initialisation of
|
||||
the Config class, and clear the error buffer.
|
||||
"""
|
||||
errMessage = "<br>".join(self._errData)
|
||||
message = "<br>".join(self._errData)
|
||||
self._hasError = False
|
||||
self._errData = []
|
||||
return errMessage
|
||||
return message
|
||||
|
||||
def listLanguages(self, lngSet: int) -> list[tuple[str, str]]:
|
||||
"""List localisation files in the i18n folder. The default GUI
|
||||
@@ -545,8 +544,8 @@ class Config:
|
||||
# Sizes
|
||||
sec = "Sizes"
|
||||
self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
|
||||
self._welcomeSize = conf.rdIntList(sec, "welcome", self._welcomeSize)
|
||||
self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
|
||||
self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols)
|
||||
self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
|
||||
self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
|
||||
self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
|
||||
@@ -590,7 +589,6 @@ class Config:
|
||||
self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces)
|
||||
self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings)
|
||||
self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
|
||||
self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer)
|
||||
self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
|
||||
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
|
||||
self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes)
|
||||
@@ -635,7 +633,7 @@ class Config:
|
||||
conf = NWConfigParser()
|
||||
|
||||
conf["Meta"] = {
|
||||
"timestamp": formatTimeStamp(time()),
|
||||
"timestamp": formatTimeStamp(time()),
|
||||
}
|
||||
|
||||
conf["Main"] = {
|
||||
@@ -651,12 +649,12 @@ class Config:
|
||||
}
|
||||
|
||||
conf["Sizes"] = {
|
||||
"mainwindow": self._packList(self._mainWinSize),
|
||||
"preferences": self._packList(self._prefsWinSize),
|
||||
"projloadcols": self._packList(self._projLoadCols),
|
||||
"mainpane": self._packList(self._mainPanePos),
|
||||
"viewpane": self._packList(self._viewPanePos),
|
||||
"outlinepane": self._packList(self._outlnPanePos),
|
||||
"mainwindow": self._packList(self._mainWinSize),
|
||||
"welcome": self._packList(self._welcomeSize),
|
||||
"preferences": self._packList(self._prefsWinSize),
|
||||
"mainpane": self._packList(self._mainPanePos),
|
||||
"viewpane": self._packList(self._viewPanePos),
|
||||
"outlinepane": self._packList(self._outlnPanePos),
|
||||
}
|
||||
|
||||
conf["Project"] = {
|
||||
@@ -697,7 +695,6 @@ class Config:
|
||||
"showtabsnspaces": str(self.showTabsNSpaces),
|
||||
"showlineendings": str(self.showLineEndings),
|
||||
"showmultispaces": str(self.showMultiSpaces),
|
||||
"wordcounttimer": str(self.wordCountTimer),
|
||||
"incnoteswcount": str(self.incNotesWCount),
|
||||
"showfullpath": str(self.showFullPath),
|
||||
"highlightquotes": str(self.highlightQuotes),
|
||||
@@ -774,22 +771,20 @@ class RecentProjects:
|
||||
self._data = {}
|
||||
|
||||
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
||||
if not cacheFile.is_file():
|
||||
return True
|
||||
|
||||
try:
|
||||
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = json.load(inFile)
|
||||
for projPath, theEntry in theData.items():
|
||||
self._data[projPath] = {
|
||||
"title": theEntry.get("title", ""),
|
||||
"words": theEntry.get("words", 0),
|
||||
"time": theEntry.get("time", 0),
|
||||
}
|
||||
except Exception:
|
||||
logger.error("Could not load recent project cache")
|
||||
logException()
|
||||
return False
|
||||
if cacheFile.is_file():
|
||||
try:
|
||||
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||
data = json.load(inFile)
|
||||
for path, entry in data.items():
|
||||
self._data[path] = {
|
||||
"title": entry.get("title", ""),
|
||||
"words": entry.get("words", 0),
|
||||
"time": entry.get("time", 0),
|
||||
}
|
||||
except Exception:
|
||||
logger.error("Could not load recent project cache")
|
||||
logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@ class nwConst:
|
||||
FMT_DSTAMP = "%Y-%m-%d" # Date only format
|
||||
|
||||
# URLs
|
||||
URL_WEB = "https://novelwriter.io"
|
||||
URL_DOCS = "https://docs.novelwriter.io"
|
||||
URL_CODE = "https://github.com/vkbo/novelWriter"
|
||||
URL_REPORT = "https://github.com/vkbo/novelWriter/issues"
|
||||
URL_HELP = "https://github.com/vkbo/novelWriter/discussions"
|
||||
URL_RELEASE = "https://github.com/vkbo/novelWriter/releases/latest"
|
||||
URL_WEB = "https://novelwriter.io"
|
||||
URL_DOCS = "https://docs.novelwriter.io"
|
||||
URL_RELEASES = "https://releases.novelwriter.io"
|
||||
URL_CODE = "https://github.com/vkbo/novelWriter"
|
||||
URL_REPORT = "https://github.com/vkbo/novelWriter/issues"
|
||||
URL_HELP = "https://github.com/vkbo/novelWriter/discussions"
|
||||
|
||||
# Requests
|
||||
USER_AGENT = "Mozilla/5.0 (compatible; novelWriter (Python))"
|
||||
@@ -56,6 +56,9 @@ class nwConst:
|
||||
# Gui Settings
|
||||
STATUS_MSG_TIMEOUT = 15000 # milliseconds
|
||||
|
||||
# Dialogs
|
||||
DLG_FINISHED = 2
|
||||
|
||||
# END Class nwConst
|
||||
|
||||
|
||||
@@ -107,7 +110,6 @@ class nwFiles:
|
||||
|
||||
# Project Root Files
|
||||
PROJ_FILE = "nwProject.nwx"
|
||||
PROJ_BACKUP = "nwProject.bak"
|
||||
PROJ_LOCK = "nwProject.lock"
|
||||
TOC_TXT = "ToC.txt"
|
||||
|
||||
@@ -184,6 +186,7 @@ class nwLabels:
|
||||
nwItemClass.ENTITY: QT_TRANSLATE_NOOP("Constant", "Entities"),
|
||||
nwItemClass.CUSTOM: QT_TRANSLATE_NOOP("Constant", "Custom"),
|
||||
nwItemClass.ARCHIVE: QT_TRANSLATE_NOOP("Constant", "Archive"),
|
||||
nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
|
||||
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
|
||||
}
|
||||
CLASS_ICON = {
|
||||
@@ -197,6 +200,7 @@ class nwLabels:
|
||||
nwItemClass.ENTITY: "cls_entity",
|
||||
nwItemClass.CUSTOM: "cls_custom",
|
||||
nwItemClass.ARCHIVE: "cls_archive",
|
||||
nwItemClass.TEMPLATE: "cls_template",
|
||||
nwItemClass.TRASH: "cls_trash",
|
||||
}
|
||||
LAYOUT_NAME = {
|
||||
@@ -266,6 +270,13 @@ class nwLabels:
|
||||
nwBuildFmt.J_HTML: ".json",
|
||||
nwBuildFmt.J_NWD: ".json",
|
||||
}
|
||||
FILE_FILTERS = {
|
||||
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
|
||||
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
|
||||
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
|
||||
"*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
|
||||
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
|
||||
}
|
||||
UNIT_NAME = {
|
||||
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
|
||||
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
|
||||
@@ -298,16 +309,27 @@ class nwLabels:
|
||||
|
||||
class nwHeadFmt:
|
||||
|
||||
BR = "{BR}"
|
||||
TITLE = "{Title}"
|
||||
CH_NUM = "{Chapter}"
|
||||
CH_WORD = "{Chapter:Word}"
|
||||
CH_ROMU = "{Chapter:URoman}"
|
||||
CH_ROML = "{Chapter:LRoman}"
|
||||
SC_NUM = "{Scene}"
|
||||
SC_ABS = "{Scene:Abs}"
|
||||
BR = "{BR}"
|
||||
TITLE = "{Title}"
|
||||
CH_NUM = "{Chapter}"
|
||||
CH_WORD = "{Chapter:Word}"
|
||||
CH_ROMU = "{Chapter:URoman}"
|
||||
CH_ROML = "{Chapter:LRoman}"
|
||||
SC_NUM = "{Scene}"
|
||||
SC_ABS = "{Scene:Abs}"
|
||||
CHAR_POV = "{Char:POV}"
|
||||
CHAR_FOCUS = "{Char:Focus}"
|
||||
|
||||
ALL = [TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS]
|
||||
PAGE_HEADERS = [
|
||||
TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
|
||||
CHAR_POV, CHAR_FOCUS
|
||||
]
|
||||
|
||||
# ODT Document Page Header
|
||||
ODT_PROJECT = "{Project}"
|
||||
ODT_AUTHOR = "{Author}"
|
||||
ODT_PAGE = "{Page}"
|
||||
ODT_AUTO = "{Project} / {Author} / {Page}"
|
||||
|
||||
# END Class nwHeadFmt
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ import uuid
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
from typing import Iterable
|
||||
from pathlib import Path
|
||||
from collections.abc import Iterable
|
||||
|
||||
from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
||||
|
||||
@@ -79,6 +79,8 @@ SETTINGS_TEMPLATE = {
|
||||
"format.leftMargin": (float, 2.0),
|
||||
"format.rightMargin": (float, 2.0),
|
||||
"odt.addColours": (bool, True),
|
||||
"odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
|
||||
"odt.pageCountOffset": (int, 0),
|
||||
"html.addStyles": (bool, True),
|
||||
}
|
||||
|
||||
@@ -125,6 +127,8 @@ SETTINGS_LABELS = {
|
||||
|
||||
"odt": QT_TRANSLATE_NOOP("Builds", "Open Document (.odt)"),
|
||||
"odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"),
|
||||
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
|
||||
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
|
||||
|
||||
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"),
|
||||
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
|
||||
@@ -235,16 +239,12 @@ class BuildSettings:
|
||||
def getInt(self, key: str) -> int:
|
||||
"""Type safe value access for integers."""
|
||||
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None))[1])
|
||||
if isinstance(value, (int, float)):
|
||||
return int(value)
|
||||
return 0
|
||||
return int(value) if isinstance(value, (int, float)) else 0
|
||||
|
||||
def getFloat(self, key: str) -> float:
|
||||
"""Type safe value access for floats."""
|
||||
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None))[1])
|
||||
if isinstance(value, (int, float)):
|
||||
return float(value)
|
||||
return 0.0
|
||||
return float(value) if isinstance(value, (int, float)) else 0.0
|
||||
|
||||
##
|
||||
# Setters
|
||||
|
||||
@@ -29,16 +29,19 @@ from __future__ import annotations
|
||||
import shutil
|
||||
import logging
|
||||
|
||||
from typing import Iterable
|
||||
from pathlib import Path
|
||||
from functools import partial
|
||||
from zipfile import ZipFile, is_zipfile
|
||||
from collections.abc import Iterable
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import minmax, simplified
|
||||
from novelwriter.constants import nwItemClass
|
||||
from novelwriter.common import isHandle, minmax, simplified
|
||||
from novelwriter.constants import nwFiles, nwItemClass
|
||||
from novelwriter.core.item import NWItem
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.storage import NWStorageCreate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -277,28 +280,25 @@ class DocDuplicator:
|
||||
"""Run through a list of items, duplicate them, and copy the
|
||||
text content if they are documents.
|
||||
"""
|
||||
if not items:
|
||||
return
|
||||
|
||||
nHandle = items[0]
|
||||
hMap: dict[str, str | None] = {t: None for t in items}
|
||||
for tHandle in items:
|
||||
newItem = self._project.tree.duplicate(tHandle)
|
||||
if newItem is None:
|
||||
return
|
||||
hMap[tHandle] = newItem.itemHandle
|
||||
if newItem.itemParent in hMap:
|
||||
newItem.setParent(hMap[newItem.itemParent])
|
||||
self._project.tree.updateItemData(newItem.itemHandle)
|
||||
if newItem.isFileType():
|
||||
oldDoc = self._project.storage.getDocument(tHandle)
|
||||
newDoc = self._project.storage.getDocument(newItem.itemHandle)
|
||||
if newDoc.fileExists():
|
||||
if items:
|
||||
nHandle = items[0]
|
||||
hMap: dict[str, str | None] = {t: None for t in items}
|
||||
for tHandle in items:
|
||||
newItem = self._project.tree.duplicate(tHandle)
|
||||
if newItem is None:
|
||||
return
|
||||
newDoc.writeDocument(oldDoc.readDocument() or "")
|
||||
yield newItem.itemHandle, nHandle
|
||||
nHandle = None
|
||||
|
||||
hMap[tHandle] = newItem.itemHandle
|
||||
if newItem.itemParent in hMap:
|
||||
newItem.setParent(hMap[newItem.itemParent])
|
||||
self._project.tree.updateItemData(newItem.itemHandle)
|
||||
if newItem.isFileType():
|
||||
oldDoc = self._project.storage.getDocument(tHandle)
|
||||
newDoc = self._project.storage.getDocument(newItem.itemHandle)
|
||||
if newDoc.fileExists():
|
||||
return
|
||||
newDoc.writeDocument(oldDoc.readDocument() or "")
|
||||
yield newItem.itemHandle, nHandle
|
||||
nHandle = None
|
||||
return
|
||||
|
||||
# END Class DocDuplicator
|
||||
@@ -310,64 +310,72 @@ class ProjectBuilder:
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||
self._path = None
|
||||
self.tr = partial(QCoreApplication.translate, "ProjectBuilder")
|
||||
return
|
||||
|
||||
@property
|
||||
def projPath(self) -> Path | None:
|
||||
"""The path of the newly created project."""
|
||||
return self._path
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def buildProject(self, data: dict) -> bool:
|
||||
"""Build a project from a data dictionary of specifications
|
||||
provided by the wizard.
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
logger.error("Invalid call to newProject function")
|
||||
return False
|
||||
"""Build or copy a project from a data dictionary."""
|
||||
if isinstance(data, dict):
|
||||
path = data.get("path", None) or None
|
||||
if isinstance(path, (str, Path)):
|
||||
self._path = Path(path).resolve()
|
||||
if data.get("sample", False):
|
||||
return self._extractSampleProject(self._path)
|
||||
elif data.get("template"):
|
||||
return self._copyProject(self._path, data)
|
||||
else:
|
||||
return self._buildAndPopulate(self._path, data)
|
||||
SHARED.error("A project path is required.")
|
||||
return False
|
||||
|
||||
popMinimal = data.get("popMinimal", True)
|
||||
popCustom = data.get("popCustom", False)
|
||||
popSample = data.get("popSample", False)
|
||||
|
||||
# Check if we're extracting the sample project. This is handled
|
||||
# differently as it isn't actually a new project, so we forward
|
||||
# this to another function and return here.
|
||||
if popSample:
|
||||
return self._extractSampleProject(data)
|
||||
|
||||
projPath = data.get("projPath", None)
|
||||
if projPath is None:
|
||||
logger.error("No project path set for the new project")
|
||||
return False
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _buildAndPopulate(self, path: Path, data: dict) -> bool:
|
||||
"""Build a blank project from a data dictionary."""
|
||||
project = NWProject()
|
||||
if not project.storage.openProjectInPlace(projPath, newProject=True):
|
||||
status = project.storage.createNewProject(path)
|
||||
if status == NWStorageCreate.NOT_EMPTY:
|
||||
SHARED.error(self.tr(
|
||||
"The target folder is not empty. "
|
||||
"Please choose another folder."
|
||||
))
|
||||
return False
|
||||
elif status == NWStorageCreate.OS_ERROR:
|
||||
SHARED.error(self.tr(
|
||||
"An error occurred while trying to create the project."
|
||||
), exc=project.storage.exc)
|
||||
return False
|
||||
|
||||
self._path = project.storage.storagePath
|
||||
|
||||
lblNewProject = self.tr("New Project")
|
||||
lblNewChapter = self.tr("New Chapter")
|
||||
lblNewScene = self.tr("New Scene")
|
||||
lblTitlePage = self.tr("Title Page")
|
||||
lblByAuthors = self.tr("By")
|
||||
|
||||
# Settings
|
||||
projName = data.get("projName", lblNewProject)
|
||||
projTitle = data.get("projTitle", lblNewProject)
|
||||
projAuthor = data.get("projAuthor", "")
|
||||
projLang = data.get("projLang", "en_GB")
|
||||
|
||||
project.data.setUuid(None)
|
||||
project.data.setName(projName)
|
||||
project.data.setTitle(projTitle)
|
||||
project.data.setAuthor(projAuthor)
|
||||
project.data.setLanguage(projLang)
|
||||
project.data.setName(data.get("name", lblNewProject))
|
||||
project.data.setAuthor(data.get("author", ""))
|
||||
project.data.setLanguage(CONFIG.guiLocale)
|
||||
project.setDefaultStatusImport()
|
||||
project.session.startSession()
|
||||
|
||||
# Add Root Folders
|
||||
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
||||
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
|
||||
novelTitle = project.data.title if project.data.title else project.data.name
|
||||
novelTitle = project.data.name
|
||||
|
||||
titlePage = f"#! {novelTitle}\n\n"
|
||||
if project.data.author:
|
||||
@@ -376,116 +384,148 @@ class ProjectBuilder:
|
||||
aDoc = project.storage.getDocument(hTitlePage)
|
||||
aDoc.writeDocument(titlePage)
|
||||
|
||||
if popMinimal:
|
||||
# Creating a minimal project with a few root folders and a
|
||||
# single chapter with a single scene.
|
||||
hChapter = project.newFile(lblNewChapter, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(hChapter)
|
||||
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
|
||||
# Create a project structure based on selected root folders
|
||||
# and a number of chapters and scenes selected in the
|
||||
# wizard's custom page.
|
||||
|
||||
if hChapter:
|
||||
hScene = project.newFile(lblNewScene, hChapter)
|
||||
aDoc = project.storage.getDocument(hScene)
|
||||
aDoc.writeDocument(f"### {lblNewScene}\n\n")
|
||||
# Create chapters and scenes
|
||||
numChapters = data.get("chapters", 0)
|
||||
numScenes = data.get("scenes", 0)
|
||||
|
||||
project.newRoot(nwItemClass.PLOT)
|
||||
project.newRoot(nwItemClass.CHARACTER)
|
||||
project.newRoot(nwItemClass.WORLD)
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
chSynop = self.tr("Summary of the chapter.")
|
||||
scSynop = self.tr("Summary of the scene.")
|
||||
bfNote = self.tr("A short description.")
|
||||
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
# Create chapters
|
||||
if numChapters > 0:
|
||||
for ch in range(numChapters):
|
||||
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
||||
cHandle = project.newFile(chTitle, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(cHandle)
|
||||
aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {chSynop}\n\n")
|
||||
|
||||
elif popCustom:
|
||||
# Create a project structure based on selected root folders
|
||||
# and a number of chapters and scenes selected in the
|
||||
# wizard's custom page.
|
||||
# Create chapter scenes
|
||||
if numScenes > 0 and cHandle:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, cHandle)
|
||||
aDoc = project.storage.getDocument(sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
|
||||
|
||||
# Create chapters and scenes
|
||||
numChapters = data.get("numChapters", 0)
|
||||
numScenes = data.get("numScenes", 0)
|
||||
# Create scenes (no chapters)
|
||||
elif numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
|
||||
|
||||
chSynop = self.tr("Summary of the chapter.")
|
||||
scSynop = self.tr("Summary of the scene.")
|
||||
bfNote = self.tr("A short description.")
|
||||
# Create notes folders
|
||||
noteTitles = {
|
||||
nwItemClass.PLOT: self.tr("Main Plot"),
|
||||
nwItemClass.CHARACTER: self.tr("Protagonist"),
|
||||
nwItemClass.WORLD: self.tr("Main Location"),
|
||||
}
|
||||
|
||||
# Create chapters
|
||||
if numChapters > 0:
|
||||
for ch in range(numChapters):
|
||||
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
||||
cHandle = project.newFile(chTitle, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(cHandle)
|
||||
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
||||
addNotes = data.get("notes", False)
|
||||
for newRoot in data.get("roots", []):
|
||||
if newRoot in nwItemClass:
|
||||
rHandle = project.newRoot(newRoot)
|
||||
if addNotes:
|
||||
aHandle = project.newFile(noteTitles[newRoot], rHandle)
|
||||
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
||||
aDoc = project.storage.getDocument(aHandle)
|
||||
aDoc.writeDocument(
|
||||
f"# {noteTitles[newRoot]}\n\n"
|
||||
f"@tag: {ntTag}\n\n"
|
||||
f"%Short: {bfNote}\n\n"
|
||||
)
|
||||
|
||||
# Create chapter scenes
|
||||
if numScenes > 0 and cHandle:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, cHandle)
|
||||
aDoc = project.storage.getDocument(sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||
# Also add the archive and trash folders
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
project.trashFolder()
|
||||
|
||||
# Create scenes (no chapters)
|
||||
elif numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||
|
||||
# Create notes folders
|
||||
noteTitles = {
|
||||
nwItemClass.PLOT: self.tr("Main Plot"),
|
||||
nwItemClass.CHARACTER: self.tr("Protagonist"),
|
||||
nwItemClass.WORLD: self.tr("Main Location"),
|
||||
}
|
||||
|
||||
addNotes = data.get("addNotes", False)
|
||||
for newRoot in data.get("addRoots", []):
|
||||
if newRoot in nwItemClass:
|
||||
rHandle = project.newRoot(newRoot)
|
||||
if addNotes:
|
||||
aHandle = project.newFile(noteTitles[newRoot], rHandle)
|
||||
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
||||
aDoc = project.storage.getDocument(aHandle)
|
||||
aDoc.writeDocument(
|
||||
f"# {noteTitles[newRoot]}\n\n"
|
||||
f"@tag: {ntTag}\n\n"
|
||||
f"% Short: {bfNote}\n\n"
|
||||
)
|
||||
|
||||
# Also add the archive and trash folders
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
project.trashFolder()
|
||||
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
def _copyProject(self, path: Path, data: dict) -> bool:
|
||||
"""Copy an existing project content, but not the meta data, and
|
||||
update new settings.
|
||||
"""
|
||||
source = data.get("template")
|
||||
if not (isinstance(source, Path) and source.is_file()
|
||||
and (source.name == nwFiles.PROJ_FILE or is_zipfile(source))):
|
||||
logger.error("Could not access source project: %s", source)
|
||||
return False
|
||||
|
||||
def _extractSampleProject(self, data: dict) -> bool:
|
||||
logger.info("Copying project: %s", source)
|
||||
if path.exists():
|
||||
SHARED.error(self.tr(
|
||||
"The target folder already exists. "
|
||||
"Please choose another folder."
|
||||
))
|
||||
return False
|
||||
|
||||
# Begin copying
|
||||
srcPath = source.parent
|
||||
dstPath = path.resolve()
|
||||
srcCont = srcPath / "content"
|
||||
dstCont = dstPath / "content"
|
||||
try:
|
||||
dstPath.mkdir(exist_ok=True)
|
||||
dstCont.mkdir(exist_ok=True)
|
||||
if is_zipfile(source):
|
||||
with ZipFile(source) as zipObj:
|
||||
for member in zipObj.namelist():
|
||||
if member == nwFiles.PROJ_FILE:
|
||||
zipObj.extract(member, dstPath)
|
||||
elif member.startswith("content") and member.endswith(".nwd"):
|
||||
zipObj.extract(member, dstPath)
|
||||
else:
|
||||
shutil.copy2(srcPath / nwFiles.PROJ_FILE, dstPath)
|
||||
for item in srcCont.iterdir():
|
||||
if item.is_file() and item.suffix == ".nwd" and isHandle(item.stem):
|
||||
shutil.copy2(item, dstCont)
|
||||
except Exception as exc:
|
||||
SHARED.error(self.tr("Could not copy project files."), exc=exc)
|
||||
return False
|
||||
|
||||
# Open the copied project and update settings
|
||||
project = NWProject()
|
||||
project.openProject(dstPath)
|
||||
project.data.setUuid("") # Creates a fresh uuid
|
||||
project.data.setName(data.get("name", "None"))
|
||||
project.data.setAuthor(data.get("author", ""))
|
||||
project.data.setSpellCheck(True)
|
||||
project.data.setSpellLang(None)
|
||||
project.data.setDoBackup(True)
|
||||
project.data.setSaveCount(0)
|
||||
project.data.setAutoCount(0)
|
||||
project.data.setEditTime(0)
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
|
||||
return True
|
||||
|
||||
def _extractSampleProject(self, path: Path) -> bool:
|
||||
"""Make a copy of the sample project by extracting the
|
||||
sample.zip file to the new path.
|
||||
"""
|
||||
projPath = data.get("projPath", None)
|
||||
if projPath is None:
|
||||
logger.error("No project path set for the example project")
|
||||
if path.exists():
|
||||
SHARED.error(self.tr(
|
||||
"The target folder already exists. "
|
||||
"Please choose another folder."
|
||||
))
|
||||
return False
|
||||
|
||||
pkgSample = CONFIG.assetPath("sample.zip")
|
||||
if pkgSample.is_file():
|
||||
if (sample := CONFIG.assetPath("sample.zip")).is_file():
|
||||
try:
|
||||
shutil.unpack_archive(pkgSample, projPath)
|
||||
shutil.unpack_archive(sample, path)
|
||||
except Exception as exc:
|
||||
SHARED.error(self.tr(
|
||||
"Failed to create a new example project."
|
||||
), exc=exc)
|
||||
SHARED.error(self.tr("Failed to create a new example project."), exc=exc)
|
||||
return False
|
||||
|
||||
else:
|
||||
SHARED.error(self.tr(
|
||||
"Failed to create a new example project. "
|
||||
|
||||
@@ -25,8 +25,8 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Iterable
|
||||
from pathlib import Path
|
||||
from collections.abc import Iterable
|
||||
|
||||
from PyQt5.QtGui import QFont, QFontInfo
|
||||
|
||||
@@ -248,8 +248,8 @@ class NWBuildDocument:
|
||||
def _setupBuild(self, bldObj: Tokenizer) -> dict:
|
||||
"""Configure the build object."""
|
||||
# Get Settings
|
||||
textFont = self._build.getStr("format.textFont")
|
||||
textSize = self._build.getInt("format.textSize")
|
||||
textFont = self._build.getStr("format.textFont")
|
||||
textSize = self._build.getInt("format.textSize")
|
||||
|
||||
fontFamily = textFont or CONFIG.textFont
|
||||
bldFont = QFont(fontFamily, textSize)
|
||||
@@ -284,6 +284,9 @@ class NWBuildDocument:
|
||||
if isinstance(bldObj, ToOdt):
|
||||
bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
|
||||
bldObj.setLanguage(self._project.data.language)
|
||||
bldObj.setHeaderFormat(
|
||||
self._build.getStr("odt.pageHeader"), self._build.getInt("odt.pageCountOffset")
|
||||
)
|
||||
|
||||
scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0)
|
||||
pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0))
|
||||
|
||||
@@ -52,7 +52,7 @@ class NWDocument:
|
||||
|
||||
def __init__(self, project: NWProject, tHandle: str | None) -> None:
|
||||
|
||||
self._project = project
|
||||
self._project = project
|
||||
|
||||
self._item = None # The currently open item
|
||||
self._handle = None # The handle of the currently open item
|
||||
@@ -284,12 +284,12 @@ class NWDocument:
|
||||
"""Parse the document meta tag and return the name, parent,
|
||||
class and layout meta values.
|
||||
"""
|
||||
theName = self._docMeta.get("name", "")
|
||||
theParent = self._docMeta.get("parent", None)
|
||||
theClass = self._docMeta.get("class", None)
|
||||
theLayout = self._docMeta.get("layout", None)
|
||||
name = self._docMeta.get("name", "")
|
||||
parent = self._docMeta.get("parent", None)
|
||||
itemClass = self._docMeta.get("class", None)
|
||||
itemLayout = self._docMeta.get("layout", None)
|
||||
|
||||
return theName, theParent, theClass, theLayout
|
||||
return name, parent, itemClass, itemLayout
|
||||
|
||||
def getError(self) -> str:
|
||||
"""Return the last recorded exception."""
|
||||
|
||||
@@ -32,8 +32,9 @@ import json
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING, ItemsView, Iterable, Iterator
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
from collections.abc import ItemsView, Iterable, Iterator
|
||||
|
||||
from novelwriter import SHARED
|
||||
from novelwriter.enum import nwComment, nwItemClass, nwItemType, nwItemLayout
|
||||
@@ -122,8 +123,8 @@ class NWIndex:
|
||||
for nwItem in self._project.tree:
|
||||
if nwItem.isFileType():
|
||||
tHandle = nwItem.itemHandle
|
||||
theDoc = self._project.storage.getDocument(tHandle)
|
||||
self.scanText(tHandle, theDoc.readDocument() or "", blockSignal=True)
|
||||
doc = self._project.storage.getDocument(tHandle)
|
||||
self.scanText(tHandle, doc.readDocument() or "", blockSignal=True)
|
||||
self._indexBroken = False
|
||||
SHARED.indexSignalProxy({"event": "buildIndex"})
|
||||
return
|
||||
@@ -148,8 +149,8 @@ class NWIndex:
|
||||
"""
|
||||
if tHandle and self._project.tree.checkType(tHandle, nwItemType.FILE):
|
||||
logger.debug("Re-indexing item '%s'", tHandle)
|
||||
theDoc = self._project.storage.getDocument(tHandle)
|
||||
self.scanText(tHandle, theDoc.readDocument() or "")
|
||||
doc = self._project.storage.getDocument(tHandle)
|
||||
self.scanText(tHandle, doc.readDocument() or "")
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -420,10 +421,10 @@ class NWIndex:
|
||||
return
|
||||
|
||||
if tBits[0] == nwKeyWords.TAG_KEY:
|
||||
tagName = tBits[1]
|
||||
self._tagsIndex.add(tagName, tHandle, sTitle, itemClass)
|
||||
self._itemIndex.setHeadingTag(tHandle, sTitle, tagName)
|
||||
tags[tagName.lower()] = True
|
||||
tagKey, displayName = self.parseValue(tBits[1])
|
||||
self._tagsIndex.add(tagKey, displayName, tHandle, sTitle, itemClass.name)
|
||||
self._itemIndex.setHeadingTag(tHandle, sTitle, tagKey)
|
||||
tags[tagKey.lower()] = True
|
||||
else:
|
||||
self._itemIndex.addHeadingRef(tHandle, sTitle, tBits[1:], tBits[0])
|
||||
|
||||
@@ -471,10 +472,8 @@ class NWIndex:
|
||||
|
||||
return True, tBits, tPos
|
||||
|
||||
def checkThese(self, tBits: list[str], nwItem: NWItem) -> list[bool]:
|
||||
"""Check the tags against the index to see if they are valid
|
||||
tags. This is needed for syntax highlighting.
|
||||
"""
|
||||
def checkThese(self, tBits: list[str], tHandle: str) -> list[bool]:
|
||||
"""Check tags against the index to see if they are valid."""
|
||||
nBits = len(tBits)
|
||||
isGood = [False]*nBits
|
||||
if nBits == 0:
|
||||
@@ -487,20 +486,27 @@ class NWIndex:
|
||||
|
||||
# For a tag, only the first value is accepted, the rest are ignored
|
||||
if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
|
||||
if tBits[1] in self._tagsIndex:
|
||||
isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == nwItem.itemHandle
|
||||
check, _ = self.parseValue(tBits[1])
|
||||
if check in self._tagsIndex:
|
||||
isGood[1] = self._tagsIndex.tagHandle(check) == tHandle
|
||||
else:
|
||||
isGood[1] = True
|
||||
return isGood
|
||||
|
||||
# If we're still here, we check that the references exist
|
||||
# Class references cannot have the | symbol in them
|
||||
refKey = nwKeyWords.KEY_CLASS[tBits[0]].name
|
||||
for n in range(1, nBits):
|
||||
if tBits[n] in self._tagsIndex:
|
||||
isGood[n] = self._tagsIndex.tagClass(tBits[n]) == refKey
|
||||
if (aBit := tBits[n]) in self._tagsIndex:
|
||||
isGood[n] = self._tagsIndex.tagClass(aBit) == refKey and "|" not in aBit
|
||||
|
||||
return isGood
|
||||
|
||||
def parseValue(self, text: str) -> tuple[str, str]:
|
||||
"""Parse a single value into a name and display part."""
|
||||
name, _, display = text.partition("|")
|
||||
return name.rstrip(), display.lstrip()
|
||||
|
||||
##
|
||||
# Extract Data
|
||||
##
|
||||
@@ -517,28 +523,31 @@ class NWIndex:
|
||||
return None
|
||||
|
||||
def novelStructure(
|
||||
self, rootHandle: str | None = None, skipExcl: bool = True
|
||||
self, rootHandle: str | None = None, activeOnly: bool = True
|
||||
) -> Iterator[tuple[str, str, str, IndexHeading]]:
|
||||
"""Iterate over all titles in the novel, in the correct order as
|
||||
they appear in the tree view and in the respective document
|
||||
files, but skipping all note files.
|
||||
"""
|
||||
structure = self._itemIndex.iterNovelStructure(rHandle=rootHandle, skipExcl=skipExcl)
|
||||
structure = self._itemIndex.iterNovelStructure(rHandle=rootHandle, activeOnly=activeOnly)
|
||||
for tHandle, sTitle, hItem in structure:
|
||||
yield f"{tHandle}:{sTitle}", tHandle, sTitle, hItem
|
||||
return
|
||||
|
||||
def getNovelWordCount(self, skipExcl: bool = True) -> int:
|
||||
"""Count the number of words in the novel project."""
|
||||
wCount = 0
|
||||
for _, _, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
|
||||
wCount += hItem.wordCount
|
||||
return wCount
|
||||
def getNovelWordCount(self, rootHandle: str | None = None, activeOnly: bool = True) -> int:
|
||||
"""Count the number of words in one or all novel roots."""
|
||||
return sum(hItem.wordCount for _, _, hItem in self._itemIndex.iterNovelStructure(
|
||||
rHandle=rootHandle, activeOnly=activeOnly
|
||||
))
|
||||
|
||||
def getNovelTitleCounts(self, skipExcl: bool = True) -> list[int]:
|
||||
"""Count the number of titles in the novel project."""
|
||||
def getNovelTitleCounts(
|
||||
self, rootHandle: str | None = None, activeOnly: bool = True
|
||||
) -> list[int]:
|
||||
"""Count the number of titles in one or all novel roots."""
|
||||
hCount = [0, 0, 0, 0, 0]
|
||||
for _, _, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
|
||||
for _, _, hItem in self._itemIndex.iterNovelStructure(
|
||||
rHandle=rootHandle, activeOnly=activeOnly
|
||||
):
|
||||
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
|
||||
hCount[iLevel] += 1
|
||||
return hCount
|
||||
@@ -551,14 +560,14 @@ class NWIndex:
|
||||
return 0
|
||||
|
||||
def getTableOfContents(
|
||||
self, rHandle: str | None, maxDepth: int, skipExcl: bool = True
|
||||
self, rHandle: str | None, maxDepth: int, activeOnly: bool = True
|
||||
) -> list[tuple[str, int, str, int]]:
|
||||
"""Generate a table of contents up to a maximum depth."""
|
||||
tOrder = []
|
||||
tData = {}
|
||||
pKey = None
|
||||
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(
|
||||
rHandle=rHandle, skipExcl=skipExcl
|
||||
rHandle=rHandle, activeOnly=activeOnly
|
||||
):
|
||||
tKey = f"{tHandle}:{sTitle}"
|
||||
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
|
||||
@@ -612,9 +621,18 @@ class NWIndex:
|
||||
for refType in refTypes:
|
||||
if refType in tRefs:
|
||||
tRefs[refType].append(self._tagsIndex.tagName(aTag))
|
||||
|
||||
return tRefs
|
||||
|
||||
def getReferenceForHeader(self, tHandle: str, nHead: int, keyClass: str) -> list[str]:
|
||||
"""Get the display names for a tags class for insertion into a
|
||||
heading by one of the build classes.
|
||||
"""
|
||||
if iItem := self._itemIndex[tHandle]:
|
||||
if hItem := iItem[f"T{nHead:04d}"]:
|
||||
hRefs = [k for k, v in hItem.references.items() if keyClass in v]
|
||||
return [self._tagsIndex.tagDisplay(k) for k in hRefs]
|
||||
return []
|
||||
|
||||
def getBackReferenceList(self, tHandle: str) -> dict[str, tuple[str, IndexHeading]]:
|
||||
"""Build a dict of files referring back to our file."""
|
||||
if tHandle is None or tHandle not in self._itemIndex:
|
||||
@@ -646,12 +664,15 @@ class NWIndex:
|
||||
"""Return all tags based on itemClass."""
|
||||
return self._tagsIndex.filterTagNames(itemClass.name)
|
||||
|
||||
def getTagsData(self) -> Iterator[tuple[str, str, str, IndexItem | None, IndexHeading | None]]:
|
||||
def getTagsData(
|
||||
self, activeOnly: bool = True
|
||||
) -> Iterator[tuple[str, str, str, IndexItem | None, IndexHeading | None]]:
|
||||
"""Return all known tags."""
|
||||
for tag, data in self._tagsIndex.items():
|
||||
iItem = self._itemIndex[data.get("handle")]
|
||||
hItem = None if iItem is None else iItem[data.get("heading")]
|
||||
yield tag, data.get("name", ""), data.get("class", ""), iItem, hItem
|
||||
if not activeOnly or (iItem and iItem.item.isActive):
|
||||
yield tag, data.get("name", ""), data.get("class", ""), iItem, hItem
|
||||
return
|
||||
|
||||
def getSingleTag(self, tagKey: str) -> tuple[str, str, IndexItem | None, IndexHeading | None]:
|
||||
@@ -709,17 +730,26 @@ class TagsIndex:
|
||||
"""Return a dictionary view of all tags."""
|
||||
return self._tags.items()
|
||||
|
||||
def add(self, tagKey: str, tHandle: str, sTitle: str, itemClass: nwItemClass) -> None:
|
||||
def add(self, tagKey: str, displayName: str, tHandle: str,
|
||||
sTitle: str, className: str) -> None:
|
||||
"""Add a key to the index and set all values."""
|
||||
self._tags[tagKey.lower()] = {
|
||||
"name": tagKey, "handle": tHandle, "heading": sTitle, "class": itemClass.name
|
||||
"name": tagKey,
|
||||
"display": displayName or tagKey,
|
||||
"handle": tHandle,
|
||||
"heading": sTitle,
|
||||
"class": className,
|
||||
}
|
||||
return
|
||||
|
||||
def tagName(self, tagKey: str) -> str:
|
||||
"""Get the display name of a given tag."""
|
||||
"""Get the name of a given tag."""
|
||||
return self._tags.get(tagKey.lower(), {}).get("name", "")
|
||||
|
||||
def tagDisplay(self, tagKey: str) -> str:
|
||||
"""Get the display name of a given tag."""
|
||||
return self._tags.get(tagKey.lower(), {}).get("display", "")
|
||||
|
||||
def tagHandle(self, tagKey: str) -> str | None:
|
||||
"""Get the handle of a given tag."""
|
||||
return self._tags.get(tagKey.lower(), {}).get("handle", None)
|
||||
@@ -754,27 +784,30 @@ class TagsIndex:
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("tagsIndex is not a dict")
|
||||
|
||||
for tagKey, tagData in data.items():
|
||||
if not isinstance(tagKey, str):
|
||||
raise ValueError("tagsIndex keys must be a strings")
|
||||
if "name" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a name entry")
|
||||
if "handle" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a handle entry")
|
||||
if "heading" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a heading entry")
|
||||
if "class" not in tagData:
|
||||
raise KeyError("A tagIndex item is missing a class entry")
|
||||
if tagData["name"].lower() != tagKey:
|
||||
raise ValueError("tagsIndex name must match key")
|
||||
if not isHandle(tagData["handle"]):
|
||||
for key, entry in data.items():
|
||||
if not isinstance(key, str):
|
||||
raise ValueError("tagsIndex keys must be a string")
|
||||
if not isinstance(entry, dict):
|
||||
raise ValueError("tagsIndex entry is not a dict")
|
||||
|
||||
name = entry.get("name")
|
||||
display = entry.get("display")
|
||||
handle = entry.get("handle")
|
||||
heading = entry.get("heading")
|
||||
className = entry.get("class")
|
||||
|
||||
if not isinstance(name, str):
|
||||
raise ValueError("tagsIndex name is not a string")
|
||||
if not isinstance(display, str):
|
||||
raise ValueError("tagsIndex display is not a string")
|
||||
if not isHandle(handle):
|
||||
raise ValueError("tagsIndex handle must be a handle")
|
||||
if not isTitleTag(tagData["heading"]):
|
||||
if not isTitleTag(heading):
|
||||
raise ValueError("tagsIndex heading must be a title tag")
|
||||
if not isItemClass(tagData["class"]):
|
||||
if not isItemClass(className):
|
||||
raise ValueError("tagsIndex handle must be an nwItemClass")
|
||||
|
||||
self._tags = data
|
||||
self.add(name, display, handle, heading, className)
|
||||
|
||||
return
|
||||
|
||||
@@ -848,7 +881,7 @@ class ItemIndex:
|
||||
return
|
||||
|
||||
def iterNovelStructure(
|
||||
self, rHandle: str | None = None, skipExcl: bool = False
|
||||
self, rHandle: str | None = None, activeOnly: bool = False
|
||||
) -> Iterable[tuple[str, str, IndexHeading]]:
|
||||
"""Iterate over all items and headers in the novel structure for
|
||||
a given root handle, or for all if root handle is None.
|
||||
@@ -856,7 +889,7 @@ class ItemIndex:
|
||||
for tItem in self._project.tree:
|
||||
if tItem.isNoteLayout():
|
||||
continue
|
||||
if skipExcl and not tItem.isActive:
|
||||
if activeOnly and not tItem.isActive:
|
||||
continue
|
||||
|
||||
tHandle = tItem.itemHandle
|
||||
@@ -1159,7 +1192,7 @@ class IndexHeading:
|
||||
return self._tag
|
||||
|
||||
@property
|
||||
def references(self) -> dict:
|
||||
def references(self) -> dict[str, set[str]]:
|
||||
return self._refs
|
||||
|
||||
##
|
||||
|
||||
@@ -334,15 +334,33 @@ class NWItem:
|
||||
|
||||
def isNovelLike(self) -> bool:
|
||||
"""Check if the item is of a novel-like class."""
|
||||
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE)
|
||||
return self._class in (
|
||||
nwItemClass.NOVEL,
|
||||
nwItemClass.ARCHIVE,
|
||||
nwItemClass.TEMPLATE,
|
||||
)
|
||||
|
||||
def isTemplateFile(self) -> bool:
|
||||
"""Check if the item is a template file."""
|
||||
return self._type == nwItemType.FILE and self._class == nwItemClass.TEMPLATE
|
||||
|
||||
def documentAllowed(self) -> bool:
|
||||
"""Check if the item is allowed to be of document layout."""
|
||||
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH)
|
||||
return self._class in (
|
||||
nwItemClass.NOVEL,
|
||||
nwItemClass.ARCHIVE,
|
||||
nwItemClass.TEMPLATE,
|
||||
nwItemClass.TRASH,
|
||||
)
|
||||
|
||||
def isInactiveClass(self) -> bool:
|
||||
"""Check if the item is in an inactive class."""
|
||||
return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH)
|
||||
return self._class in (
|
||||
nwItemClass.NO_CLASS,
|
||||
nwItemClass.ARCHIVE,
|
||||
nwItemClass.TEMPLATE,
|
||||
nwItemClass.TRASH,
|
||||
)
|
||||
|
||||
def isRootType(self) -> bool:
|
||||
"""Check if item is a root item."""
|
||||
|
||||
@@ -42,7 +42,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
NWEnum = TypeVar("NWEnum", bound=Enum)
|
||||
|
||||
VALID_MAP = {
|
||||
VALID_MAP: dict[str, set[str]] = {
|
||||
"GuiWritingStats": {
|
||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||
"widthCol3", "sortCol", "sortOrder", "incNovel", "incNotes",
|
||||
@@ -53,10 +53,6 @@ VALID_MAP = {
|
||||
"GuiProjectSettings": {
|
||||
"winWidth", "winHeight", "replaceColW", "statusColW", "importColW",
|
||||
},
|
||||
"GuiProjectDetails": {
|
||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
|
||||
},
|
||||
"GuiWordList": {"winWidth", "winHeight"},
|
||||
"GuiNovelView": {"lastCol", "lastColSize"},
|
||||
"GuiBuildSettings": {
|
||||
@@ -70,8 +66,13 @@ VALID_MAP = {
|
||||
"winWidth", "winHeight", "fmtWidth", "sumWidth",
|
||||
},
|
||||
"GuiDocViewerPanel": {
|
||||
"colWidths",
|
||||
}
|
||||
"colWidths", "hideInactive",
|
||||
},
|
||||
"GuiNovelDetails": {
|
||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
|
||||
"novelRoot",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,10 +26,12 @@ from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING, Iterator
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
from functools import partial
|
||||
from collections.abc import Iterator
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
@@ -40,7 +42,7 @@ 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
|
||||
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
|
||||
@@ -55,6 +57,16 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWProjectState(Enum):
|
||||
|
||||
UNKNOWN = 0
|
||||
LOCKED = 1
|
||||
RECOVERY = 2
|
||||
READY = 3
|
||||
|
||||
# END Enum NWProjectState
|
||||
|
||||
|
||||
class NWProject:
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -69,9 +81,9 @@ class NWProject:
|
||||
|
||||
# Project Status
|
||||
self._langData = {} # Localisation data
|
||||
self._lockedBy = None # Data on which computer has the project open
|
||||
self._changed = False # The project has unsaved changes
|
||||
self._valid = False # The project was successfully loaded
|
||||
self._state = NWProjectState.UNKNOWN
|
||||
|
||||
# Internal Mapping
|
||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||
@@ -125,12 +137,15 @@ class NWProject:
|
||||
"""Return True if a project is loaded."""
|
||||
return self._valid
|
||||
|
||||
@property
|
||||
def state(self) -> NWProjectState:
|
||||
"""Return the current project state."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def lockStatus(self) -> list | None:
|
||||
"""Return the project lock information."""
|
||||
if isinstance(self._lockedBy, list) and len(self._lockedBy) == 4:
|
||||
return self._lockedBy
|
||||
return None
|
||||
return self._storage.lockStatus
|
||||
|
||||
@property
|
||||
def currentEditTime(self) -> int:
|
||||
@@ -161,24 +176,47 @@ class NWProject:
|
||||
will not run if the file exists and is not empty.
|
||||
"""
|
||||
tItem = self._tree[tHandle]
|
||||
if tItem is None:
|
||||
return False
|
||||
if not tItem.isFileType():
|
||||
if not (tItem and tItem.isFileType()):
|
||||
return False
|
||||
|
||||
newDoc = self._storage.getDocument(tHandle)
|
||||
if (newDoc.readDocument() or "").strip():
|
||||
return False
|
||||
|
||||
hshText = "#"*minmax(hLevel, 1, 4)
|
||||
newText = f"{hshText} {tItem.itemName}\n\n{text}"
|
||||
indent = "#"*minmax(hLevel, 1, 4)
|
||||
text = f"{indent} {tItem.itemName}\n\n{text}"
|
||||
|
||||
if tItem.isNovelLike() and isDocument:
|
||||
tItem.setLayout(nwItemLayout.DOCUMENT)
|
||||
else:
|
||||
tItem.setLayout(nwItemLayout.NOTE)
|
||||
|
||||
newDoc.writeDocument(newText)
|
||||
self._index.scanText(tHandle, newText)
|
||||
newDoc.writeDocument(text)
|
||||
self._index.scanText(tHandle, text)
|
||||
|
||||
return True
|
||||
|
||||
def copyFileContent(self, tHandle: str, sHandle: str) -> bool:
|
||||
"""Copy content to a new document after it is created. This
|
||||
will not run if the file exists and is not empty.
|
||||
"""
|
||||
tItem = self._tree[tHandle]
|
||||
if not (tItem and tItem.isFileType()):
|
||||
return False
|
||||
|
||||
sItem = self._tree[sHandle]
|
||||
if not (sItem and sItem.isFileType()):
|
||||
return False
|
||||
|
||||
newDoc = self._storage.getDocument(tHandle)
|
||||
if (newDoc.readDocument() or "").strip():
|
||||
return False
|
||||
|
||||
logger.debug("Populating '%s' with text from '%s'", tHandle, sHandle)
|
||||
text = self._storage.getDocument(sHandle).readDocument() or ""
|
||||
newDoc.writeDocument(text)
|
||||
sItem.setLayout(tItem.itemLayout)
|
||||
self._index.scanText(tHandle, text)
|
||||
|
||||
return True
|
||||
|
||||
@@ -219,29 +257,21 @@ class NWProject:
|
||||
build the tree of project items.
|
||||
"""
|
||||
logger.info("Opening project: %s", projPath)
|
||||
if not self._storage.openProjectInPlace(projPath):
|
||||
SHARED.error(self.tr("Could not open project with path: {0}").format(projPath))
|
||||
|
||||
status = self._storage.initProjectStorage(projPath, clearLock)
|
||||
if status != NWStorageOpen.READY:
|
||||
if status == NWStorageOpen.UNKOWN:
|
||||
SHARED.error(self.tr("Not a known project file format."))
|
||||
elif status == NWStorageOpen.NOT_FOUND:
|
||||
SHARED.error(self.tr("Project file not found."))
|
||||
elif status == NWStorageOpen.LOCKED:
|
||||
self._state = NWProjectState.LOCKED
|
||||
elif status == NWStorageOpen.FAILED:
|
||||
SHARED.error(self.tr("Failed to open project."), exc=self._storage.exc)
|
||||
return False
|
||||
|
||||
# Project Lock
|
||||
# ============
|
||||
|
||||
if clearLock:
|
||||
self._storage.clearLockFile()
|
||||
|
||||
lockStatus = self._storage.readLockFile()
|
||||
if len(lockStatus) > 0:
|
||||
if lockStatus[0] == "ERROR":
|
||||
logger.warning("Failed to check lock file")
|
||||
else:
|
||||
logger.error("Project is locked, so not opening")
|
||||
self._lockedBy = lockStatus
|
||||
return False
|
||||
else:
|
||||
logger.debug("Project is not locked")
|
||||
|
||||
# Open The Project XML File
|
||||
# =========================
|
||||
# Read Project XML
|
||||
# ================
|
||||
|
||||
xmlReader = self._storage.getXmlReader()
|
||||
if not isinstance(xmlReader, ProjectXMLReader):
|
||||
@@ -250,9 +280,7 @@ class NWProject:
|
||||
self._data = NWProjectData(self)
|
||||
projContent = []
|
||||
xmlParsed = xmlReader.read(self._data, projContent)
|
||||
|
||||
appVersion = xmlReader.appVersion or self.tr("Unknown")
|
||||
|
||||
if not xmlParsed:
|
||||
if xmlReader.state == XMLReadState.NOT_NWX_FILE:
|
||||
SHARED.error(self.tr(
|
||||
@@ -302,8 +330,7 @@ class NWProject:
|
||||
self._loadProjectLocalisation()
|
||||
|
||||
# Update recent projects
|
||||
storePath = self._storage.storagePath
|
||||
if storePath:
|
||||
if storePath := self._storage.storagePath:
|
||||
CONFIG.recentProjects.update(
|
||||
storePath, self._data.name, sum(self._data.initCounts), time()
|
||||
)
|
||||
@@ -323,9 +350,9 @@ class NWProject:
|
||||
|
||||
self.updateWordCounts()
|
||||
self._session.startSession()
|
||||
self._storage.writeLockFile()
|
||||
self.setProjectChanged(False)
|
||||
self._valid = True
|
||||
self._state = NWProjectState.READY
|
||||
|
||||
SHARED.newStatusMessage(self.tr("Opened Project: {0}").format(self._data.name))
|
||||
|
||||
@@ -370,13 +397,11 @@ class NWProject:
|
||||
self._storage.runPostSaveTasks(autoSave=autoSave)
|
||||
|
||||
# Update recent projects
|
||||
storePath = self._storage.storagePath
|
||||
if storePath:
|
||||
if storagePath := self._storage.storagePath:
|
||||
CONFIG.recentProjects.update(
|
||||
storePath, self._data.name, sum(self._data.currCounts), saveTime
|
||||
storagePath, self._data.name, sum(self._data.currCounts), saveTime
|
||||
)
|
||||
|
||||
self._storage.writeLockFile()
|
||||
SHARED.newStatusMessage(self.tr("Saved Project: {0}").format(self._data.name))
|
||||
self.setProjectChanged(False)
|
||||
|
||||
@@ -385,6 +410,7 @@ class NWProject:
|
||||
def closeProject(self, idleTime: float = 0.0) -> None:
|
||||
"""Close the project."""
|
||||
logger.info("Closing project")
|
||||
self._index.clearIndex() # Triggers clear signal, see #1718
|
||||
self._options.saveSettings()
|
||||
self._tree.writeToCFile()
|
||||
self._session.appendSession(idleTime)
|
||||
@@ -420,8 +446,8 @@ class NWProject:
|
||||
timeStamp = formatTimeStamp(time(), fileSafe=True)
|
||||
archName = baseDir / f"{cleanName} {timeStamp}.zip"
|
||||
if self._storage.zipIt(archName, compression=2):
|
||||
size = formatInt(getFileSize(archName))
|
||||
if doNotify:
|
||||
size = formatInt(getFileSize(archName))
|
||||
SHARED.info(
|
||||
self.tr("Created a backup of your project of size {0}B.").format(size),
|
||||
info=self.tr("Path: {0}").format(str(backupPath))
|
||||
|
||||
@@ -53,7 +53,6 @@ class NWProjectData:
|
||||
# Project Meta
|
||||
self._uuid = ""
|
||||
self._name = ""
|
||||
self._title = ""
|
||||
self._author = ""
|
||||
self._saveCount = 0
|
||||
self._autoCount = 0
|
||||
@@ -102,11 +101,6 @@ class NWProjectData:
|
||||
"""Return the project name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Return the project title."""
|
||||
return self._title
|
||||
|
||||
@property
|
||||
def author(self) -> str:
|
||||
"""Return the project author."""
|
||||
@@ -228,16 +222,9 @@ class NWProjectData:
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setTitle(self, value: str | None) -> None:
|
||||
"""Set a new novel title."""
|
||||
if value != self._title:
|
||||
self._title = simplified(str(value or ""))
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setAuthor(self, value: str | None) -> None:
|
||||
"""Set the author value."""
|
||||
if value != self._title:
|
||||
if value != self._author:
|
||||
self._author = simplified(str(value or ""))
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
@@ -38,7 +38,6 @@ from novelwriter.common import (
|
||||
checkBool, checkInt, checkString, checkStringNone, formatTimeStamp,
|
||||
hexToInt, simplified, xmlIndent, yesNo
|
||||
)
|
||||
from novelwriter.constants import nwFiles
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.core.status import NWStatus
|
||||
@@ -47,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 = "1" # The current project file format revision
|
||||
FILE_REVISION = "2" # The current project file format revision
|
||||
HEX_VERSION = 0x0105
|
||||
|
||||
NUM_VERSION = {
|
||||
@@ -65,12 +64,11 @@ class XMLReadState(Enum):
|
||||
|
||||
NO_ACTION = 0
|
||||
NO_ERROR = 1
|
||||
PARSED_BACKUP = 2
|
||||
CANNOT_PARSE = 3
|
||||
NOT_NWX_FILE = 4
|
||||
UNKNOWN_VERSION = 5
|
||||
PARSED_OK = 6
|
||||
WAS_LEGACY = 7
|
||||
CANNOT_PARSE = 2
|
||||
NOT_NWX_FILE = 3
|
||||
UNKNOWN_VERSION = 4
|
||||
PARSED_OK = 5
|
||||
WAS_LEGACY = 6
|
||||
|
||||
# END Class XMLReadState
|
||||
|
||||
@@ -107,7 +105,9 @@ class ProjectXMLReader:
|
||||
the project or the content into their respective section nodes
|
||||
as attributes. The id attribute was also added to the project.
|
||||
|
||||
Rev 1: Drops the titleFormat section of settings.
|
||||
Rev 1: Drops the titleFormat node from settings. 2.1 Beta 1.
|
||||
Rev 2: Drops the title node from project and adds the TEMPLATE
|
||||
class for items. 2.3 Beta 1.
|
||||
"""
|
||||
|
||||
def __init__(self, path: str | Path) -> None:
|
||||
@@ -172,23 +172,9 @@ class ProjectXMLReader:
|
||||
xml = ET.parse(str(self._path))
|
||||
self._state = XMLReadState.NO_ERROR
|
||||
except Exception as exc:
|
||||
# Trying to open backup file instead
|
||||
logger.error("Failed to parse project XML", exc_info=exc)
|
||||
self._state = XMLReadState.CANNOT_PARSE
|
||||
|
||||
backFile = self._path.with_suffix(".bak")
|
||||
if backFile.is_file():
|
||||
try:
|
||||
xml = ET.parse(str(backFile))
|
||||
self._state = XMLReadState.PARSED_BACKUP
|
||||
logger.info("Backup project file parsed")
|
||||
except Exception as exc:
|
||||
logger.error("Failed to parse backup project XML", exc_info=exc)
|
||||
self._state = XMLReadState.CANNOT_PARSE
|
||||
return False
|
||||
else:
|
||||
self._state = XMLReadState.CANNOT_PARSE
|
||||
return False
|
||||
return False
|
||||
|
||||
xRoot = xml.getroot()
|
||||
self._root = str(xRoot.tag)
|
||||
@@ -248,8 +234,6 @@ class ProjectXMLReader:
|
||||
for xItem in xSection:
|
||||
if xItem.tag == "name":
|
||||
data.setName(xItem.text)
|
||||
elif xItem.tag == "title":
|
||||
data.setTitle(xItem.text)
|
||||
elif xItem.tag == "author":
|
||||
data.setAuthor(xItem.text)
|
||||
else:
|
||||
@@ -521,7 +505,6 @@ class ProjectXMLWriter:
|
||||
|
||||
xProject = ET.SubElement(xRoot, "project", attrib=projAttr)
|
||||
self._packSingleValue(xProject, "name", data.name)
|
||||
self._packSingleValue(xProject, "title", data.title)
|
||||
self._packSingleValue(xProject, "author", data.author)
|
||||
|
||||
# Save Project Settings
|
||||
@@ -558,23 +541,12 @@ class ProjectXMLWriter:
|
||||
xName.text = item["name"]
|
||||
|
||||
# Write the XML tree to file
|
||||
saveFile = self._path / nwFiles.PROJ_FILE
|
||||
tempFile = saveFile.with_suffix(".tmp")
|
||||
backFile = saveFile.with_suffix(".bak")
|
||||
tmp = self._path.with_suffix(".tmp")
|
||||
try:
|
||||
xml = ET.ElementTree(xRoot)
|
||||
xmlIndent(xml)
|
||||
xml.write(tempFile, encoding="utf-8", xml_declaration=True)
|
||||
except Exception as exc:
|
||||
self._error = exc
|
||||
return False
|
||||
|
||||
# If we're here, the file was successfully saved,
|
||||
# so let's sort out the temps and backups
|
||||
try:
|
||||
if saveFile.exists():
|
||||
saveFile.replace(backFile)
|
||||
tempFile.replace(saveFile)
|
||||
xml.write(tmp, encoding="utf-8", xml_declaration=True)
|
||||
tmp.replace(self._path)
|
||||
except Exception as exc:
|
||||
self._error = exc
|
||||
return False
|
||||
|
||||
@@ -27,8 +27,9 @@ import json
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING, Iterator
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
from collections.abc import Iterator
|
||||
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import formatTimeStamp
|
||||
|
||||