Merge branch 'main' into new_project_wizard

This commit is contained in:
Veronica K. B. Olsen
2020-08-08 15:26:27 +02:00
41 changed files with 2006 additions and 806 deletions
+2 -1
View File
@@ -8,7 +8,8 @@
# Documentation # Documentation
/docs/build/ /docs/build/
/docs/source/_* novelWriter.qch
novelWriter.qhc
# Python Temp # Python Temp
__pycache__ __pycache__
+37 -1
View File
@@ -1,5 +1,41 @@
# novelWriter ChangeLog # novelWriter ChangeLog
## Version 0.11 [2020-08-08]
Note: The source code has now switched to a default branch named `main` ahead of the changes planned by GitHub.
See their [notes](https://github.com/github/renaming) for more information.
**Bugfixes**
* The `pytest` config file now sets the local source path as the first search path for the main novelWriter package. This ensures that the tests can always find the correct version of the code when running tests. PR #381.
* The `install.py` script was expecting an older file layout for assets files. This has now been updated to the curren file layout. PR #380.
**User Interface**
* A set of new exception handling functions have been added. Recoverable errors will now pop an error dialog with the error message and a traceback for the user. The application will not generally exit on such errors, unless it causes Python itself to abort. It is possible to copy and paste the error message so it can be used for a ticket in the issue tracker. PRs #376 and #378.
**Documentation**
* The full documentation for novelWriter, available at [novelwriter.readthedocs.io](https://novelwriter.readthedocs.io/) has been rewritten. It was drifting out of sync with the development of the code. In addition, many improvements have been made to the reStructuredText formatting of the documentation source by providing better cross-reference linking and highlightings. The main repository README file has been updated to match. PRs #375, #382, and #384.
* The main `setup.py` script has been updated to also build documentation for the Qt Assistant when given a `qthelp` flag. The compiled help files are copied into the `nw/assets/help` folder, and bundled with the source when pushed to PyPi. The GUI has been altered to open the local help files instead of redirecting to the online documentation if the local files are both present and the Qt Assistant is installed. PR #375 and #379.
**Other Changes**
* Some minor changes to the source code has been made to more correctly use the Python `__package__` variable. PR #376.
## Version 0.10.2 [2020-07-29]
**Bugfixes**
* Fixed a crash when using the replace part of search/replace when using regular expressions for the search. The replace code assumed the search field was a string, which isn't the case when using RegEx, rather itb is a QRegularExpression or QRegExp object. This has now been resolved. In addition, the replace feature has been improved to make sure that it only replaces text selected by an actual search, not any user selected text. Issue #371, PRs #372 and #373.
* The Tokenizer class used for converting novelWriter markdown to other formats produced some invalid escape sequence warnings. The warnings did not seem to affect the results, but have nevertheless been fixed. PR #370.
**Features**
* Insert menu entries to insert single and double open and close quote symbols have been added. These are the symbols selected as the quote symbols in Preferences. They also have keyboard shortcuts associated with them. PR #367.
## Version 0.10.1 [2020-07-11] ## Version 0.10.1 [2020-07-11]
**Bugfixes** **Bugfixes**
@@ -17,7 +53,7 @@
* The search/replace Regular Expression option now uses the newest QRegularExpression tool instead of the older QRegExp tool if the Qt version is 5.13 or above. Otherwise, it still uses the old. The main benefit of the newer tool in this context is better Unicode support. PR #360. * The search/replace Regular Expression option now uses the newest QRegularExpression tool instead of the older QRegExp tool if the Qt version is 5.13 or above. Otherwise, it still uses the old. The main benefit of the newer tool in this context is better Unicode support. PR #360.
* The Build Novel Project tool can now generate Roman numbers for chapter markers. Both upper and lower case is supported. PRs #362 and #363. * The Build Novel Project tool can now generate Roman numbers for chapter markers. Both upper and lower case is supported. PRs #362 and #363.
**Other CHanges** **Other Changes**
* The install scripts now try to create folders before copying icons. PR #364. * The install scripts now try to create folders before copying icons. PR #364.
* The manifest file now lists the root assets folder, so that it is included in the pypi build. PR #364. * The manifest file now lists the root assets folder, so that it is included in the pypi build. PR #364.
+139 -102
View File
@@ -1,86 +1,113 @@
# novelWriter # novelWriter
[![Build Status](https://travis-ci.com/vkbo/novelWriter.svg?branch=master)](https://travis-ci.com/vkbo/novelWriter) [![Build Status](https://travis-ci.com/vkbo/novelWriter.svg?branch=main)](https://travis-ci.com/vkbo/novelWriter)
[![codecov](https://codecov.io/gh/vkbo/novelWriter/branch/master/graph/badge.svg)](https://codecov.io/gh/vkbo/novelWriter) [![codecov](https://codecov.io/gh/vkbo/novelWriter/branch/main/graph/badge.svg)](https://codecov.io/gh/vkbo/novelWriter)
[![Documentation Status](https://readthedocs.org/projects/novelwriter/badge/?version=latest)](https://novelwriter.readthedocs.io/en/latest/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/novelwriter/badge/?version=latest)](https://novelwriter.readthedocs.io/en/latest/?badge=latest)
[![PyPI](https://img.shields.io/pypi/v/novelwriter)](https://pypi.org/project/novelWriter/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/novelwriter)](https://pypi.org/project/novelWriter/)
<img align="left" style="margin: 0 16px 4px 0;" src="assets/icons/96x96/novelwriter.png"> <img align="left" style="margin: 0 16px 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/assets/icons/96x96/novelwriter.png">
novelWriter is a markdown-like text editor designed for writing novels and larger projects of many smaller plain text documents.
It uses its own flavour of markdown that supports a meta data syntax for comments, synopsis and cross-referencing between files. novelWriter is a markdown-like text editor designed for writing novels and larger projects of many
The idea is to have a simple text editor which allows for easy organisation of text files and notes, built on a plain text file project repository for robustness. smaller plain text documents. It uses its own flavour of markdown that supports a meta data syntax
The plain text storage is suitable for version control software, and also well suited for file synchronisation tools. for comments, synopsis and cross-referencing between files. It's designed to be a simple text editor
The core project structure is stored in a project XML file. which allows for easy organisation of text files and notes, built on plain text files for
Other meta data is primarily saved in JSON files. robustness.
The plain text storage is suitable for version control software, and also well suited for file
synchronisation tools. The core project structure is stored in a project XML file. Other meta data
is primarily saved in JSON files.
The full documentation is available at [novelwriter.readthedocs.io](https://novelwriter.readthedocs.io/). The full documentation is available at [novelwriter.readthedocs.io](https://novelwriter.readthedocs.io/).
### Note
The application is still under initial development, but all core features have now been added. ### Note on the Default Branch
The core functionality has been in place for a while, and novelWriter is being used for writing projects by the author and collaborators.
No new major features will be added at this time, until the application is stable. The default branch on this repository switched to `main` on 6. August 2020. If you are running
Until then, novelWriter is in a beta state. novelWriter from a git clone, you need to clone the repository again.
Please report any issues you may encounter in the repository issue tracker.
You should be able to use novelWriter for real projects, but as with all software, please make regular backups. Alternatively, you can run the following to get back on the main branch:
There is a built in backup feature that can pack the entire project into a zip file on close.
Please check the documentation for further details. ```bash
git remote update
git checkout -t origin/main
```
### Development Status
The application is still under initial development, but all core features have now been added. The
core functionality has been in place for a while, and novelWriter is being used for writing projects
by the author and collaborators.
No new major features will be added at this time, until the application is stable. Until then,
novelWriter is in a _beta_ state. Please report any issues you may encounter in the repository issue
tracker.
You should be able to use novelWriter for real projects, but as with all software, please make
regular backups. There is a built in backup feature that can pack the entire project into a zip file
on close. Please check the documentation for further details.
## License ## License
This is Open Source software, and novelWriter is licensed under GPLv3. This is Open Source software, and novelWriter is licensed under GPLv3. See the
See the [GNU General Public License website](https://www.gnu.org/licenses/gpl-3.0.en.html) for more details, or consult the [LICENSE](LICENSE.md) file. [GNU General Public License website](https://www.gnu.org/licenses/gpl-3.0.en.html) for more details,
or consult the [LICENSE](LICENSE.md) file.
Bundled assets have the following licenses: Bundled assets have the following licenses:
* The Typicon-based icon themes by Stephen Hutchings are licensed under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/). The icons have been altered in size and colour for use with novelWriter, and some additional icons added. The original icon set is available at [stephenhutchings/typicons.font](https://github.com/stephenhutchings/typicons.font). * The Typicon-based icon themes by Stephen Hutchings are licensed under
* The Cantarell font by Dave Crossland is licensed under [OPEN FONT LICENSE Version 1.1](http://scripts.sil.org/OFL). It is available at [Google Fonts](https://fonts.google.com/specimen/Cantarell). [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/). The icons have been altered in
* The Tomorrow syntax themes use colour schemes taken from Chris Kempson's collection of code editor themes, licensed with the [MIT License](https://github.com/chriskempson/tomorrow-theme/blob/master/LICENSE.md), and the main repo is available at [chriskempson/tomorrow-theme](https://github.com/chriskempson/tomorrow-theme). size and colour for use with novelWriter, and some additional icons added. The original icon set
* Likewise, the Owl syntax themes use colours from Sarah Drasner's code editor themes, licensed with the [MIT License](https://github.com/sdras/night-owl-vscode-theme/blob/master/LICENSE), and the main repo is available at [sdras/night-owl-vscode-theme](https://github.com/sdras/night-owl-vscode-theme). is available at [stephenhutchings/typicons.font](https://github.com/stephenhutchings/typicons.font).
* The Cantarell font by Dave Crossland is licensed under [OPEN FONT LICENSE Version 1.1](http://scripts.sil.org/OFL).
It is available at [Google Fonts](https://fonts.google.com/specimen/Cantarell).
* The Tomorrow syntax themes use colour schemes taken from Chris Kempson's collection of code editor
themes, licensed with the [MIT License](https://github.com/chriskempson/tomorrow-theme/blob/master/LICENSE.md),
and the main repo is available at [chriskempson/tomorrow-theme](https://github.com/chriskempson/tomorrow-theme).
* Likewise, the Owl syntax themes use colours from Sarah Drasner's code editor themes, licensed with
the [MIT License](https://github.com/sdras/night-owl-vscode-theme/blob/master/LICENSE), and the
main repo is available at [sdras/night-owl-vscode-theme](https://github.com/sdras/night-owl-vscode-theme).
## Markdown Flavour ## Markdown Flavour
novelWriter is **not** a full-feature Markdown editor. novelWriter is _not_ a full-feature Markdown editor. It allows for a minimal set of formatting
It allows for a minimal set of formatting needed for writing text documents for novels. needed for writing text documents for novels. These are currently limited to:
These are currently limited to:
* Headings level 1 to 4 using the `#` syntax only. * Headings level 1 to 4 using the `#` syntax only.
* Emphasised, strongly emphasised, and very strongly emphasised text. These are rendered as italicised, bold, and bold italicised text, respectively. * Emphasised, strong text. These are rendered as italicised and bold.
* Strikethrough text. * Strikethrough text.
* Hard line breaks using two or more spaces at the end of a line. * Hard line breaks using two or more spaces at the end of a line.
That is it. That is it. Features not supported in the editor are also not exported when using the export tool.
Features not supported in the editor are also not exported when using the export tool.
In addition, novelWriter adds the following, which is otherwise not supported by Markdown: In addition, novelWriter adds the following, which is otherwise not supported by Markdown:
* A line starting with `%` is treated as a comment and not rendered on exports unless requested. * A line starting with `%` is treated as a comment and not rendered on exports unless requested.
Comments do not count towards the word count. Comments do not count towards the word count. If the first word of the comment is `synopsis:`, the
* A set of meta data keyword/value sets starting with the character `@`. comment is indexed and treated as the synopsis for the following section of text. These synopsis
This is used for tagging and inter-linking documents. comments can be used to build an outline and exported to external documents.
* Non-breaking spaces are supported as long as your system is using at least Qt 5.9. * A set of meta data keyword/value sets starting with the character `@`. This is used for tagging
For earlier version, non-breaking spaces are converted to normal spaces when saving the document. and inter-linking documents.
This is done by the Qt library. * Non-breaking spaces are supported as long as your system is using at least Qt 5.9. For earlier
From Qt 5.9 and on, it is possible to extract the raw text from a document, which preserves non-breaking spaces. version, non-breaking spaces are converted to normal spaces when saving the document. This is done
* Tabs may be rendered, depending on export format. by the Qt library.
With Qt 5.10 or higher, the width of a tab in pixels can be changed in Preferences. * Thin spaces are also supported, as well as non-breaking thin spaces.
* Tabs may be rendered, depending on export format. With Qt 5.10 or higher, the width of a tab in
pixels can be changed in Preferences.
The core export format of novelWriter is HTML5. The core export format of novelWriter is HTML5. You can also export the entire project as a single
You can also export the entire project as a single novelWriter flavour document. novelWriter flavour document. In addition, other exports to Open Document, PDF, and plain text is
In addition, other exports to Open Document, PDF, and plain text is offered through the Qt library, although with limitations to formatting. offered through the Qt library, although with limitations to formatting.
Even though novelWriter can export to Open Document, the result is actually better when using the HTML output and then importing the HTML document into for instance Libre Office.
The HTML output is also suitable for conversion with tools like Pandoc.
## Implementation ## Implementation
The application is written in Python3 using Qt5 via PyQt5. The application is written in Python3 using Qt5 via PyQt5. It is developed on Linux, but it should
It is developed on Linux, but it should in principle work fine on other operating systems as well as long as dependencies are met. in principle work fine on other operating systems as well as long as dependencies are met. It is
It is regularly tested on Windows 10. regularly tested on Windows 10.
The application can be started from the source folder with one of the commands: The application can be started from the source folder with one of the commands:
``` ```
@@ -91,16 +118,17 @@ python3 novelWriter.py
It also takes a few parameters for debugging and such, which can be listed with the switch `--help`. It also takes a few parameters for debugging and such, which can be listed with the switch `--help`.
In the root assets folder there are icons and scripts and a template for setting up a launcher on Gnome desktops. In the root assets folder there are icons and scripts and a template for setting up a launcher on
You may need to modify those scripts slightly, but as they are, they work on Debian and Ubuntu. Gnome desktops. You may need to modify those scripts slightly, but as they are, they work on Debian
For other operating systems, please consult your operating system documentation for how to make those. and Ubuntu. For other operating systems, please consult your operating system documentation for how
Feel free to submit more if you are able to make them. to make those. Feel free to submit more if you are able to make them.
## Package Dependencies ## Package Dependencies
It is recommended that novelWriter runs with Qt 5.10 or later, and Python 3.6 or later. It is recommended that novelWriter runs with Qt 5.10 or later, and Python 3.6 or later. Running with
Running with Qt as low as 5.2.1 and Python 3.4.3 has been tested, and worked in the past, but there are no guarantees that this will keep working as these are not a part of the test builds. Qt as low as 5.2.1 and Python 3.4.3 has been tested, and worked in the past, but there are no
guarantees that this will keep working as these are not a part of the test builds.
For the apt package manager on Debian systems, the following Python3 packages are needed: For the apt package manager on Debian systems, the following Python3 packages are needed:
@@ -113,31 +141,33 @@ These are optional, but recommended:
* `python3-enchant` for better spell checking * `python3-enchant` for better spell checking
Alternatively, the packages can be installed with `pip` by running Alternatively, the packages can be installed with `pip` by running
``` ```bash
pip install -r requirements.txt pip install -r requirements.txt
``` ```
in the application folder. in the application folder.
You can also do them one at a time, skipping the ones you don't need: You can also do them one at a time, skipping the ones you don't need:
``` ```bash
pip install pyqt5 pip install pyqt5
pip install lxml pip install lxml
pip install pyenchant pip install pyenchant
``` ```
PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work. PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work.
Exporting to markdown requires PyQt/Qt 5.14. Exporting to markdown requires PyQt/Qt 5.14. There are no known minimum for `lxml`, but the code
There are no known minimum for lxml, but the code was originally written with 4.2. was originally written with 4.2. The optional spell check library must be at least 3.0.0 to work
The optional spell check library must be at least 3.0.0 to work with Windows. with Windows 64 bit systems. On Linux, 2.0.0 also works fine.
On Linux, 2.0.0 also works fine.
If no external spell checking tool is installed, novelWriter will use a basic spell checker based on standard Python package `difflib`. If no external spell checking tool is installed, novelWriter will use a basic spell checker based on
Currently, only English dictionaries are available for this spell checker, but more can be added to the `nw/assets/dict` folder. standard Python package `difflib`. Currently, only English dictionaries are available for this spell
See the [nw/assets/dict/README.md](README.md) file in that folder for how to generate more dictionaries. checker, but more can be added to the `nw/assets/dict` folder. See the [README](nw/assets/dict/README.md)
Note that the difflib-based option is both slow and limited. file in that folder for how to generate more dictionaries. Note that the difflib-based option is
both slow and limited.
Note: On Windows, make sure Python3 is in your PATH if you want to launch novelWriter from command line. Note: On Windows, make sure Python3 is in your PATH if you want to launch novelWriter from command
You can also right click the `novelWriter.py` file, create a shortcut, then right click again, select "Properties" and change the target to your python executable and `novelWriter.py`. line. You can also right click the `novelWriter.py` file, create a shortcut, then right click again,
select "Properties" and change the target to your python executable and `novelWriter.py`.
It should look something like this: It should look something like this:
``` ```
@@ -146,68 +176,75 @@ C:\...\AppData\Local\Programs\Python\Python38\python.exe novelWriter.py
## Key Features ## Key Features
The text documents of novelWriter use a format similar to markdown, but with a few extensions and a few omissions. Some features of novelWriter are listed below. Consult the documentation for more information.
Project meta data is stored as XML.
### Colour Themes ### Colour Themes
The editor has syntax highlighting for the features it supports, and includes a set of different syntax highlighting themes. The editor has syntax highlighting for the features it supports, and includes a set of different
The GUI also has an optional dark theme in addition to the default system theme. syntax highlighting themes. The GUI also has an optional dark theme in addition to the default
system theme.
Note that the dark theme may not render all elements of the GUI as dark colours if you are running an early version of Qt5. New themes can easily be added to the `nw/assets/themes` folder. Have a look in the existing folders
This is not due to a bug in novelWriter, but due to the fact that the the styling options in the Qt API in some versions were incomplete. for examples of how to define the colours.
New themes can easily be added to the `nw/assets/themes` folder.
Have a look in the existing folders for examples of how to define the colours.
### Auto-Saving and Document Stats ### Auto-Saving and Document Stats
Open documents and the project file itself is saved regularly on a timer. Open documents and the project file itself is saved regularly on a timer. The status of this is
The status of this is indicated by two indicators on the right hand side of the status bar. indicated by two indicators on the right hand side of the status bar. Latest project word count is
Unsaved changes are in yellow, and saved is indicated by green in the default theme. shown next to these indicators in the status bar. The counts are updated regularly, but not
Latest word count for the document and project is shown next to these indicators in the status bar. as-you-type.
The counts are updated regularly, but not as-you-type.
The word count for documents is presented in a footer in the document editor itself. Both project
and document word counters will also show how many words you've added in the current writing
session.
### Easy Organising of Project Files ### Easy Organising of Project Files
The structure of the project is shown on the left hand side of the main GUI. The structure of the project is shown on the left hand side of the main GUI. Project files are
Project files are organised into root folders, indicating what class of file they are. organised into root folders, indicating what class of file they are. The most important root folder
The most important root folder is the Novel folder, which contains all of the files that makes up the finished novel. is the Novel folder, which contains all of the files that makes up the finished novel. Each root
Each root folder can have subfolders. folder can have subfolders. Folders have no impact on the project structure, they are purely tools
Folders have no impact on the project structure, they are purely tools for organising the files in whatever way the user needs. for organising the files in whatever way the user needs.
The editor supports four levels of headings, which determines what level the following text belongs to. The editor supports four levels of headings, which determines what level the following text belongs
Headings of level one signify a book or partition title. to. Headings of level one signify a book or partition title. Headings of level two signify the start
Headings of level two signify the start of a new chapter. of a new chapter. Headings of level three signify the start of a new scene. Headings of level four
Headings of level three signify the start of a new scene. can be used internally in each scene to separate sections.
Headings of level four can be used internally in each scene to separate sections.
Each novel file can be assigned a layout format, which shows up as a flag next to the item in the
project tree. These are mostly to help the user see what they contain, but they also have some
impact on the format of the exported document. See the documentation for further details.
Each novel file can be assigned a layout format, which shows up as a flag next to the item in the project tree.
These are mostly to help the user see what they contain, but they also have some impact on the format of the exported document.
See the documentation for further details.
#### Project Notes #### Project Notes
Supporting note files can be added for the story plot, characters, locations, story timeline, etc. Supporting note files can be added for the story plot, characters, locations, story timeline, etc.
These have their separate root folders. These have their separate root folders. These are optional files.
These are optional files.
### Visualisation of Story Elements ### Visualisation of Story Elements
The different notes can be assigned tags, which other files can refer back to using special meta keywords. The different notes can be assigned tags, which other files can refer back to using special meta
This information can be used to display an outline of the story, showing where each scene connects to the plot, and which characters, etc. occur in them. keywords. This information can be used to display an outline of the story, showing where each scene
In addition, the tags themselves are clickable in the document view pane, and control-clickable in the editor. connects to the plot, and which characters, etc. occur in them. In addition, the tags themselves are
They make it possible to quickly navigate between the documents while editing. clickable in the document view pane, and control-clickable in the editor. They make it possible to
quickly navigate between the documents while editing.
## Contribution ## Contribution
If you want to contribute to novelWriter, please follow the coding convention laid out in the [Style Guide](docs/markdown/style.md). If you want to contribute to novelWriter, please follow the coding convention laid out in the
They broadly follow Python PEP8, but there are a few modifications. [Style Guide](markdown/style.md). They broadly follow Python PEP8, but there are a few
modifications.
## Screenshot ## Screenshot
**novelWriter with default system theme:** **novelWriter with default system theme:**
![Screenshot 1](docs/source/images/screenshot_default.png) ![Screenshot 1](https://raw.githubusercontent.com/vkbo/novelWriter/main/docs/source/images/screenshot_default.png)
**novelWriter with dark theme:** **novelWriter with dark theme:**
![Screenshot 2](docs/source/images/screenshot_dark.png) ![Screenshot 2](https://raw.githubusercontent.com/vkbo/novelWriter/main/docs/source/images/screenshot_dark.png)
+23
View File
@@ -0,0 +1,23 @@
/*
* Custom CSS Rules for Sphinx RTD Theme
*/
.kbd {
background-color: #eeeeee;
border: 1px solid #b4b4b4;
border-radius: 3px;
color: #333333;
display: inline-block;
font-size: 0.85em;
font-weight: 400;
line-height: 1;
padding: 2px 4px;
margin-left: 1px;
margin-right: 1px;
white-space: nowrap;
}
.tight-table td {
white-space: normal !important;
vertical-align: text-top;
}
+29 -36
View File
@@ -15,7 +15,7 @@
# import os # import os
# import sys # import sys
# sys.path.insert(0, os.path.abspath(".")) # sys.path.insert(0, os.path.abspath("."))
import sphinx_rtd_theme
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
@@ -24,21 +24,21 @@ copyright = "2018-2020, Veronica Berglyd Olsen"
author = "Veronica Berglyd Olsen" author = "Veronica Berglyd Olsen"
# The short X.Y version # The short X.Y version
version = "0.10.1" version = "0.11.0"
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = "0.10.1" release = "0.11.0"
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = "1.0" # needs_sphinx = "1.0"
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named "sphinx.ext.*") or your custom # extensions coming with Sphinx (named "sphinx.ext.*") or your custom
# ones. # ones.
extensions = [ extensions = [
"sphinx_rtd_theme",
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
@@ -46,7 +46,6 @@ templates_path = ["_templates"]
# The suffix(es) of source filenames. # The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string: # You can specify multiple suffix as a list of string:
#
# source_suffix = [".rst", ".md"] # source_suffix = [".rst", ".md"]
source_suffix = ".rst" source_suffix = ".rst"
@@ -55,7 +54,6 @@ master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language = None language = None
@@ -71,30 +69,29 @@ pygments_style = None
# -- Options for HTML output ------------------------------------------------- # -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for html_theme = "sphinx_rtd_theme"
# a list of builtin themes. html_logo = "images/novelwriter.png"
# html_theme_options = {
html_theme = "default" # Toc options
"collapse_navigation": True,
"sticky_navigation": True,
"navigation_depth": 3,
"includehidden": True,
"titles_only": False,
"logo_only": True,
}
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"] html_static_path = ["_static"]
html_css_files = [
"css/custom.css",
]
# Custom sidebar templates, must be a dictionary that maps document names # Custom sidebar templates, must be a dictionary that maps document names
# to template names. # to template names.
#
# The default sidebars (for documents that don"t match any pattern) are # The default sidebars (for documents that don"t match any pattern) are
# defined by theme itself. Builtin themes are using these templates by # defined by theme itself. Builtin themes are using these templates by
# default: ``["localtoc.html", "relations.html", "sourcelink.html", # default: ``["localtoc.html", "relations.html", "sourcelink.html",
# "searchbox.html"]``. # "searchbox.html"]``.
#
# html_sidebars = {} # html_sidebars = {}
@@ -127,20 +124,19 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [(
(master_doc, "novelWriter.tex", "novelWriter Documentation", master_doc, "novelWriter.tex", "novelWriter Documentation",
"Veronica Berglyd Olsen", "manual"), author, "manual"
] )]
# -- Options for manual page output ------------------------------------------ # -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [(
(master_doc, "novelwriter", "novelWriter Documentation", master_doc, "novelwriter", "novelWriter Documentation", [author], 1
[author], 1) )]
]
# -- Options for Texinfo output ---------------------------------------------- # -- Options for Texinfo output ----------------------------------------------
@@ -148,11 +144,10 @@ man_pages = [
# Grouping the document tree into Texinfo files. List of tuples # Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [(
(master_doc, "novelWriter", "novelWriter Documentation", master_doc, "novelWriter", "novelWriter Documentation", author,
author, "novelWriter", "Markdown-like editor for novels.", "novelWriter", "Markdown-like editor for novels.", "Miscellaneous"
"Miscellaneous"), )]
]
# -- Options for Epub output ------------------------------------------------- # -- Options for Epub output -------------------------------------------------
@@ -162,11 +157,9 @@ epub_title = project
# The unique identifier of the text. This can be a ISBN number # The unique identifier of the text. This can be a ISBN number
# or the project homepage. # or the project homepage.
#
# epub_identifier = "" # epub_identifier = ""
# A unique identification for the text. # A unique identification for the text.
#
# epub_uid = "" # epub_uid = ""
# A list of files that should not be packed into the epub file. # A list of files that should not be packed into the epub file.
+112 -72
View File
@@ -1,113 +1,153 @@
################## .. _a_export:
******************
Exporting Projects Exporting Projects
################## ******************
The novelWriter project can be exported in various formats using the build tool available from :menuselection:`Project --> Build Project` or by pressing :kbd:`F5`. The novelWriter project can be exported in various formats using the build tool available from
:guilabel:`Build Novel Project` in the :guilabel:`Tools` menu, or by pressing :kbd:`F5`.
.. _a_export_headers:
*****************
Header Formatting Header Formatting
***************** =================
The titles for the four levels of story structure can be formatted collectively in the export tool. The titles for the five types of titles (the chapter headings come in a numbered and unnumbered
This is done through a series of keywordreplace steps. version) of story structure can be formatted collectively in the export tool. This is done through
a series of keywordreplace steps. They are all on the format ``%keyword%``.
The keyword ``%title%`` will always be replaced by the text you put after the ``#`` characters in your document. ``%title%``
This keyword will always be replaced with the title text you put after the ``#`` characters in
your document.
The keywords ``%ch%`` and ``%chw%`` is replaced by a number, or a number word, respectively. ``%ch%``
You can also use ``%chi%`` or ``%chI%`` for lower and upper case Roman numbers. This is replaced by a chapter number. The number is incremented by one each time the build tool
The number is incremented by one each time the build tool sees a new heading of level two in a file with layout "Chapter". sees a new heading of level two in a file with layout :guilabel:`Chapter`. If the file has layout
If the file has layout "Unnumbered", the counter is *not* incremented. :guilabel:`Unnumbered`, the counter is *not* incremented. The latter is useful for for instance
The latter is useful for for instance Prologue and Epilogue chapters. Prologue and Epilogue chapters.
Likewise, the keywords ``%sc%`` and ``%sca%`` are number counters for scene files. ``%chw%``
These are incremented each time a heading of level three is encountered. This is like ``%ch%``, but the number is expressed as a word like for instance "One", "Two", etc.
The former keyword is reset to one for each new chapter, while the latter is not reset but counts from first scene encountered in the project.
If you want to insert a line break in your title format, add two backslashes ``\\``. ``%chi%``
This is also like ``%ch%``, but the number is represented as a lower case Roman number.
``%chI%``
This is also like ``%ch%``, but the number is represented as an upper case Roman number.
``%sc%``
This is the number counter equivalent for scenes. These are incremented each time a heading of
level three is encountered, but reset to 1 each time a chapter is encountered. They can thus be
used for counting scenes within a chapter.
``%sca%``
This is like ``%sc%``, but the number is *not* reset to 1 for each chapter. Instead it runs from
1 from the beginning of the novel.
``\\``
This inserts a line break within the title.
.. note:: .. note::
Header formatting only applies to novel files. Header formatting only applies to novel files. Headings in note files will will be left as-is on
Headings in note files will will be left as-is, but heading levels 1 through 4 are converted to the correct heading level in the respective output formats. export. However, heading levels 1 through 4 are converted to the correct heading level in the
respective output formats.
**Example**
* The format ``%title%`` just reproduces the title you set in the document file.
* The format ``Chapter %ch%: %title%`` produces something like "Chapter 1: My Chapter Title".
* The format ``Scene %ch%.%sc%`` produces something like "Scene 1.2" for scene 2 in chapter 1.
.. _a_export_scenes:
****************
Scene Separators Scene Separators
**************** ================
If you don't want any titles for your scenes (and for your sections if you have them), you can leave the boxes empty, and an empty paragraph will be inserted between the scenes or sections instead. If you don't want any titles for your scenes (and for your sections if you have them), you can leave
Alternatively, if you want a separator between them, like the common "\*\*\*", you can also enter that in the box. the boxes empty, and an empty paragraph will be inserted between the scenes or sections instead.
In fact, if the format is a piece of static text, it will always be treated as a separator.
Alternatively, if you want a separator between them, like the common ``* * *``, you can also enter
that in the box. In fact, if the format is a piece of static text, it will always be treated as a
separator.
.. _a_export_files:
**************
File Selection File Selection
************** ==============
Which files are selected for export can be controlled from the options on the left side of the dialog window. Which files are selected for export can also be controlled from the options on the left side of the
The switch for "Include novel files" will select any file that isn't classified as a note. dialog window. The switch for :guilabel:`Include novel files` will select any file that isn't
That is, files with layout "Book", "Page", "Partition", "Chapet", "Unnumbered", or "Scene". classified as a note. The switch for :guilabel:`Include note files` will select any file that *is*
The switch for "Include note files" will select any file that is a note. a note. This is allows for exporting just the novel, just your notes, or both, as you see fit.
That is, files with layout "Note".
This is allows for exporting just the novel, just your notes, or both, as you see fit.
In addition, you can select to export the synopsis comments, regular comments, keywords, and even exclude the body text itself. In addition, you can select to export the synopsis comments, regular comments, keywords, and even
If you for instance want to export a document with an outline of the novel, you can enable keywords and synopsis export and disable body text, thus getting a document with each heading followed by the tags and references and the synopsis. exclude the body text itself.
If you need to exclude specific files from your exports, like draft files or files you want to take out of your build, but don't want to delete, you can uncheck the "Include when building project" option for each file in the project tree. .. tip::
An included file has a checkmark after the status icon in the "Flags" column. If you for instance want to export a document with an outline of the novel, you can enable
The "Build Novel Project" tool has a switch to ignore this flag if you need to collectively override these settings. keywords and synopsis export and disable body text, thus getting a document with each heading
followed by the tags and references and the synopsis.
If you need to exclude specific files from your exports, like draft files or files you want to take
out of your manuscript, but don't want to delete, you can un-check the :guilabel:`Include when
building project` option for each file in the project tree. An included file has a checkmark after
the status icon in the :guilabel:`Flags` column. The :guilabel:`Build Novel Project` tool has a
switch to ignore this flag if you need to collectively override these settings.
.. _a_export_formats:
**************
Export Formats Export Formats
************** ==============
Currently, six formats are supported for exporting. Currently, six formats are supported for exporting.
OpenDocument Format OpenDocument Format
=================== This produces an open document ``.odt`` file. The document produced has very little formatting,
and may require further editing afterwards. For a better formatted office document, you may get a
This is produces an open document ``.odt`` file. better result with exporting to HTML and the import that HTML document into your office word
The document produced has very little formatting, and may require further editing afterwards. processor. They are generally very good at importing HTML files.
For a better formatted office document, you may get a better result with exporting to HTML and the import that HTML document in your office word processor.
PDF Format PDF Format
========== The PDF export is just a shortcut for print to file. For a better PDF result, you may instead
want to export HTML, and use a word processor to convert the HTML document to PDF.
The PDF export is just a shortcut for print to file.
novelWriter HTML novelWriter HTML
================ The HTML export format writes a single ``.htm`` file with minimal style formatting. The exported
HTML file is suitable for further processing by document conversion tools like Pandoc, for
The HTML export format writes a single ``.htm`` file with minimal style formatting. importing in word processors, or for printing from browser. It is generally the best formatted
The exported HTML file is suitable for further processing by document conversion tools like Pandoc, for importing in word processors, or for printing from browser. export option and supports all features of novelWriter since it is entirely geenrated by the
application and doesn't depend on Qt library features.
novelWriter Markdown novelWriter Markdown
==================== This is simply a concatenation of the files selected by the filters. The files in the project are
stacked together in the order they appear in the tree view, with comments, tags, etc. included if
This is simply a concatenation of the files selected by the filters. they are selected. This is a useful format for exporting the project for later import back into
The files in the project are stacked together in the order they appear in the tree view, with comments, tags, etc. included if they are selected. novelWriter.
This is a useful format for exporting the project for later import back into novelWriter.
Standard Markdown Standard Markdown
================= If you have Qt 5.14 or higher, the option to export to plain markdown is available. This feature
uses Qt's own markdown export feature.
If you have Qt 5.14 or higher, the option to export to plain Markdown is available.
This feature uses Qt's own Markdown export feature.
Plain Text Plain Text
========== The plain text export format writes a simple ``.txt`` file without any formatting at all.
The plain text export format writes a simple ``.txt`` file without any formatting at all.
************************* .. _a_export_options:
Additional Export Options Additional Export Options
************************* =========================
In addition to the above document formats, the novelWriter HTML and Markdown formats can also be wrapped in a JSON file. In addition to the above document formats, the novelWriter HTML and Markdown formats can also be
The files will have a meta data entry and a body entry. wrapped in a JSON file. The files will have a meta data entry and a body entry. For HTML, also the
For HTML, also the accompanying css styles are exported. accompanying css styles are exported.
The text body is saved in a two-level list. The text body is saved in a two-level list. The outer list contains one entry per exported file, in
The outer list contains one entry per exported file, in the order they appear in the project tree. the order they appear in the project tree. Each file is then split up into a list as well, with one
Each file is then split up into a lst as well, with one entry per line. entry per paragraph in the document.
These files are mainly intended for scripted post-processing for those who want that option. These files are mainly intended for scripted post-processing for those who want that option. A JSON
A JSON file can be imported directly into a Python dict object or a PHP array, to mentions a few options. file can be imported directly into a Python dict object or a PHP array, to mentions a few options.
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 431 KiB

+66 -6
View File
@@ -1,24 +1,84 @@
#################################### #####################
Welcome to novelWriter Documentation novelWriter |release|
#################################### #####################
This is the documentation for novelWriter |version|. .. image:: https://travis-ci.com/vkbo/novelWriter.svg?branch=master
:target: https://travis-ci.com/vkbo/novelWriter
:alt: Build Status
.. image:: https://codecov.io/gh/vkbo/novelWriter/branch/master/graph/badge.svg
:target: https://codecov.io/gh/vkbo/novelWriter
:alt: Code Coverage
.. image:: https://readthedocs.org/projects/novelwriter/badge/?version=latest
:target: https://novelwriter.readthedocs.io/en/latest/?badge=latest
:alt: Documentation
.. image:: https://img.shields.io/github/v/release/vkbo/novelwriter
:target: https://github.com/vkbo/novelWriter/releases
:alt: GitHub Release
.. image:: https://img.shields.io/pypi/v/novelwriter
:target: https://pypi.org/project/novelWriter/
:alt: PyPI
.. image:: https://img.shields.io/pypi/pyversions/novelwriter
:target: https://pypi.org/project/novelWriter/
:alt: Python Version
novelWriter is a markdown-like text editor designed for writing novels and larger projects of many
smaller plain text documents. It uses its own flavour of markdown that supports a meta data syntax
for comments, synopsis and cross-referencing between files. The idea is to have a simple text editor
which allows for easy organisation of text files and notes, built on a plain text file project
repository for robustness.
The plain text storage is suitable for version control software, and also well suited for file
synchronisation tools. The core project structure is stored in a project XML file. Other meta data
is primarily saved as JSON files.
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 already, and users have tested it on other
platforms too. Since novelWriter is still under development, it is easier to run it if you are
already familiar with how to run Python applications on your platform.
**Useful Links**
* Website: https://novelwriter.io
* Documentation: https://novelwriter.readthedocs.io
* Source Code: https://github.com/vkbo/novelWriter
* Source Releases: https://github.com/vkbo/novelWriter/releases
* Issue Tracker: https://github.com/vkbo/novelWriter/issues
* PyPi Project: https://pypi.org/project/novelWriter
**Contents**
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
:caption: First Steps
introduction introduction
started started
interface interface
.. toctree::
:maxdepth: 2
:caption: Writing Novels
projects projects
structure structure
notes notes
export export
.. toctree::
:maxdepth: 2
:caption: Under the Hood
technical technical
**Indices and Tables**
Indices and Tables
==================
* :ref:`genindex` * :ref:`genindex`
* :ref:`modindex` * :ref:`modindex`
+346 -146
View File
@@ -1,197 +1,397 @@
.. _a_ui:
*************** ***************
User Interface User Interface
*************** ***************
The user interface is kept as simple as possible to avoid distractions when writing. The user interface is kept as simple as possible to avoid distractions when writing. This page lists
The main window contains a tree view pane with the entire structure of the project, and a small details panel below it to display additional information about the currently selected item. all the main GUI elements, and explains what they do.
Edit View .. _a_ui_tree:
=========
Editing a document can be done by either double-clicking on it, or hitting the return key when a file is selected. The Project Tree
This will open the document editor, which uses a simplified markdown format, described in the section below. ================
The document currently being edited can also be viewed in parallel in a right hand side view pane. The main window contains a project tree in the left-most panel. It shows the entire structure of the
To view a document, simply press :kbd:`Ctrl-R`, or select a file and go to :menuselection:`Document --> View Document` in the menu. project. It has four columns:
The document viewed does not need to be the same document currently being edited.
If you are viewing the same document as the one you're editing, pressing :kbd:`Ctrl-R` again will update the document with your last changes.
References to tags can be opened in the view pane from the document editor by moving the cursor to a reference to a tag and hitting :kbd:`Ctrl-Enter`. :guilabel:`Label`
In the view panel, the references become clickable links, and the "Referenced By" panel at the bottom will show links to all documents referring back to it. The first column shows the item icon and its label. The labels can be edited from the menu, or by
pressing :kbd:`F2` or :kbd:`Ctrl`:kbd:`E`. The label is not the same as the title you set inside
the document, but it will appear in the header above the document text itself.
:guilabel:`Words`
The second column shows the word count of the file, or the sum of words in the child items if it
is a folder. If the counts seem incorrect, they can be updated by rebuilding the project index
from the :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
:guilabel:`Inc`
The third column indicates whether the file is included in the final project build or not. You
may want to filter out files that you no longer want to keep in the final manuscript, but want to
keep in the project for reference.
:guilabel:`Flags`
The fourth column shows various meta data flags for the item. The first is an icon indicating the
importance or status of the file. These are colour coded status levels that you control and
define yourself. They can be changed in :guilabel:`Project Settings` from the :guilabel:`Project`
menu. The first character after the icon indicates the class of the item, that is ``N`` for
**Novel**, ``C`` for **Character**, etc (see :ref:`a_struct_tags`. The second character indicates
the file layout type (see :ref:`a_proj_roots`).
Below the project tree you will find a small details panel showing the full information of the
currently selected item. This panel also includes the latest paragraph and character counts in
addition to the word count.
.. _a_ui_edit:
Editing and Viewing Documents
=============================
To edit a document, double-click the file in the project tree, or press the :kbd:`Return` key while
having it selected. This will open the file in the document editor. The editor uses a simplified
markdown format. The format is described in the :ref:`a_ui_md` section below. The editor has a
maximise button (activates :guilabel:`Focus Mode`) and a close button in the top-right corner.
Any document in the project tree can also be viewed in parallel in a right hand side document viewer
To view a document, press :kbd:`Ctrl`:kbd:`R`, or select :guilabel:`View Document` in the menu. The
document viewed does not have to be the same document currently being edited. However, If you *are*
viewing the same document, pressing :kbd:`Ctrl`:kbd:`R` again will update the document with your
latest changes. You can also press the little reload button in the top-right corner of the view
panel next to the close button to achieve the same thing.
Both the document editor and viewer will show the label of the document in the header at the top of
the edit or view panel. Optionally, the full project path to the file can be shown. This can be set
in the :guilabel:`Preferences` dialog from the :guilabel:`Tools` menu. Clicking on the document
title bar will select and reveal the file in the project tree, making it easier to find the project
location of the file in a large project.
Any reference to a tag in the editor can be opened in the viewer by moving the cursor to the label
and pressing :kbd:`Ctrl`:kbd:`Return`. In the viewer, the references become clickable links.
Clicking them will replace the content of the viewer with the content of the document the reference
points to.
At the bottom of the view panel there is a :guilabel:`References` panel. (If it is hidden, click the
icon to reveal it.) This panel will show links to all documents referring back to it, if any has
been defined. The :guilabel:`Sticky` button will freeze the content of the panel to the current
document, even if you navigate to another document. This is convenient if you want to quickly look
through all documents in the list in the :guilabel:`References` panel.
.. note:: .. note::
The "Referenced By" panel relies on an up-to-date index of the project. The :guilabel:`References` panel relies on an up-to-date index of the project. If anything is
If anything is missing, or seems wrong, the index can always be rebuilt from :menuselection:`Tools --> Rebuild Index` or by pressing :kbd:`F9`. missing, or seems wrong, the index can always be rebuilt by selecting :guilabel:`Rebuild Index`
from the :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
Both the document editor and the viewer will show the label of the document as set in the Project Tree.
Optionally, the full project path to the file can be shown.
This can be set the Preferences.
Clicking on the document title bar will select and reveal the file in the Project Tree, making it easier to find the project location of the file in a large project. .. _a_ui_edit_auto:
Auto-Replace as You Type
========================
A few auto-replace features are supported by the editor. You can control every aspect of the
auto-replace feature from :guilabel:`Preferences`.
.. tip::
If you don't like auto-replacement, all symbols inserted by this feature are also available in
the :guilabel:`Insert` menu, and via convenient :ref:`a_ui_shortcuts_ins`.
The editor is able to replace two and three hyphens with short and long dashes, triple points with
ellipsis, and replace straight single and double quotes with user-defined quote symbols. It will
also try to determine whether to use the opening or closing symbol, but this feature isn't always
accurate.
.. tip::
If the editor changes a symbol when you did not want it to change, pressing :kbd:`Ctrl`:kbd:`Z`
immediately after the auto-replacement will undo it without undoing the character you typed.
.. _a_ui_md:
Markdown Format Markdown Format
=============== ===============
The document editor uses a simplified markdown format. The document editor uses a simplified markdown format. That is, it supports basic formatting like
That is, it supports basic formatting like emphasis (italic), strong emphasis (bold) and strikethrough text, as well as four levels of headings. emphasis (italic), strong importance (bold) and strikethrough text, as well as four levels of
It is commonly recommended style to differentiate between strong emphasis and emphasis by using ``**`` for strong emphasis and ``_`` for emphasis, although Markdown generally supports also ``__`` for strong emphasis and ``*`` fdr emphasis. headings.
However, since the differentiation makes the highlighting and conversion significantly simpler and faster, in novelWriter this is a rule, not just a recommendation.
In addition to these standard markdown features, the editor also allows for comments, that is text that is ignored by the word counter and not exported or, optionally, hidden in the document viewer. Some non-standard markdown features have been added. For instance, novelWriter allows for comments,
If the first word of a comment is "Synopsis:" (with the colon), the comment is treated specially, and will show up in the Outline View. a synopsis tag, and a set of keyword and value sets used for tags and references.
The editor also has a minimal set of keywords used for setting tags and references between files.
.. csv-table:: Formatting Syntax
:header: "Format", "Description"
:widths: 15, 50
"``# Title``", "Heading level one. The space after the # is mandatory." .. _a_ui_md_head:
"``## Title``", "Heading level two. The space after the # is mandatory."
"``### Title``", "Heading level three. The space after the # is mandatory."
"``#### Title``", "Heading level four. The space after the # is mandatory."
"``_text_``", "The text is rendered as emphasised text (italicised)."
"``**text**``", "The text is rendered as strongly emphasised text (bold)."
"``~~text~~``", "Strikethrough text."
"``% text...``", "A comment. The text is not exported by default, seen in viewer, or counted towards word counts."
"``% Synopsis: text...``", "A synopsis comment. Shows up in the Synopsis column of the Outline View, but is otherwise treated as a comment."
"``@keyword: value``", "A keyword argument followed by a value, or a comma separated list of values."
Some additional rules: Headings
--------
1. The emphasis and strikethrough formatting tags do not allow spaces between the words and the tag itself. Four levels of headings are allowed. For files of type "Note", they are free to be used as you see
That is, ``**text**`` is valid, ``**text **`` is not. fit, but for all other file layouts used for the novel text itself, they indicate the structural
2. More generally, the delimiters must be on the outer edge of words. level of the novel. See :ref:`a_struct_heads` for more details.
That is, ``some **text in bold** here`` is valid, ``some** text in bold** here`` is not.
``# Title``
Heading level one. If the file is a novel file, the header level indicates the start of a new
partition. This heading level can also be used for the title page novel title.
``## Title``
Heading level two. If the file is a novel file, the header level indicates the start of a new
chapter.
``### Title``
Heading level three. If the file is a novel file, the header level indicates the start of a new
scene.
``#### Title``
Heading level four. If the file is a novel file, the header level indicates the start of a new
section.
.. note::
The space after the ``#`` characters is mandatory. The syntaxhighlighter will change colour and
font size when the heading is correctly formatted.
.. _a_ui_md_emph:
Text Emphasis
-------------
A minimal set of text emphasis styles are supported.
``_text_``
The text is rendered as emphasised text (italicised).
``**text**``
The text is rendered as strongly important text (bold).
``~~text~~``
Strikethrough text.
In markdown guides it is often recommended to differentiate between strong importance and emphasis
by using ``**`` for strong and ``_`` for emphasis, although markdown generally supports also ``__``
for strong and ``*`` fdr emphasis. However, since the differentiation makes the highlighting and
conversion significantly simpler and faster, in novelWriter this is a rule, not just a
recommendation. The following is therefore the only supported formatting syntax:
There are also some additional rules:
1. The emphasis and strikethrough formatting tags do not allow spaces between the words and the 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 wrapper. 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. This is due to the underscore also being a valid word character, so if they are on the outside,
they violate rule 2.
The editor and viewer also supports markdown standard hard line breaks, and preserves non-breaking spaces.
A hard line break is achieved by leaving two or more spaces at the end of the line.
Alternatively, the user can press :kbd:`Ctrl-K, Return` to insert this.
A non-breaking space is inserted with :kbd:`Ctrl-K, Space`.
Thin spaces are also supported, and can be inserted with :kbd:`Ctrl-K, Shift-Space`, and the non-breaking version of it with :kbd:`Ctrl-K, Ctrl-Space`. .. _a_ui_md_comm:
Both hard line breaks and non-breaking spaces are highlighted by the syntax highlighter as an alternate coloured background, depending on the selected theme. Comments and Synopsis
---------------------
In addition to these standard markdown features, novelWriter also allows for comments in the text
files. The text of the comment is ignored by the word counter and not exported or, optionally,
hidden when viewing the document. If the first word of a comment is ``Synopsis:`` (with the colon),
the comment is treated specially, and will show up in the :ref:`a_ui_outline` in a dedicated column.
``% text...``
A comment. The text is not exported by default (this can be overridden), seen in the Viewer, or
counted towards word counts.
``% Synopsis: text...``
A synopsis comment. It is generally treated in the same way as regular comments, except that it
is captured by the indexing algorithm and displayed in the :ref:`a_ui_outline`. It can also be
filtered separately when exporting the project to for instance generate an outline document of
the whole project.
.. _a_ui_md_tags:
Tags and References
-------------------
The document editor supports a minimal set of keywords used for setting tags, and making references
between files. The tags and references can be set once per section defined by a heading. Using them
multiple times under the same heading will just override the previous setting.
``@keyword: value``
A keyword argument followed by a value, or a comma separated list of values.
The available tag and reference keywords are listed in the :ref:`a_struct_tags` section.
.. _a_ui_md_add:
Additional Markdown and Non-Standard Features
---------------------------------------------
The editor and viewer also supports markdown standard hard line breaks, and preserves non-breaking
spaces if running with Qt 5.9 or higher. For older versions, the non-breaking spaces are lost when
the file is saved. This is unfortunately hard-coded in the Qt text editor.
* A hard line break is achieved by leaving two or more spaces at the end of the line. Alternatively,
the user can press :kbd:`Ctrl`:kbd:`K`, :kbd:`Return` to insert this.
* A non-breaking space is inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Space`.
* Thin spaces are also supported, and can be inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`.
* Non-breaking thin space can be inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`.
These are all insert features, and the :guilabel:`Insert` menu has more. They are also listed
in :ref:`a_ui_shortcuts_ins`.
Both hard line breaks and non-breaking spaces are highlighted by the syntax highlighter as an
alternate coloured background, depending on the selected theme.
.. _a_ui_outline:
Project Outline View Project Outline View
==================== ====================
The Project Outline View is available as the second tab on the right hand side of the main window marked "Outline". The project's Outline view is available as the second tab on the right hand side of the main window
The Outline View 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. labelled :guilabel:`Outline`. 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, not the files.
Various meta data and information extracted from tags can be displayed in columns in the Outline View. The document file containing the heading can also be displayed as a separate column, as well as the
To turn on or off specific columns, right click the header and select the columns you want to show. line number where it occurs. Double-clicking an entry will open the corresponding file in the
The order of the columns can be rearranged by dragging them to a different position. editor.
.. note:: .. note::
The "Title" columns cannot be disabled or moved. Since the internal structure of the novel does not depend on the file structure of the project
tree, these will not necessarily look the same, depending how you chose to organise your files.
See the :ref:`a_struct` page for more details.
The information viewed in teh Outline View is based on the Project Index. Various meta data and information extracted from tags can be displayed in columns in the outline.
While novelWriter does its best to keep the index up-to-date when content changes, you can always rebuild it manually by pressing :kbd:`F9`. A default set of such columns is visible, but you can turn on or off more columns by right clicking
the header and selecting the columns you want to show. The order of the columns can also be
The Outline View itself can be regenerated by pressing :kbd:`F10`. rearranged by dragging them to a different position.
You can also enable automatic updating in the :menuselection:`Tools` menu, which will trigger an update whenever the index is updated.
You may want to disable this feature if your project is very large,
Synopsis Feature
================
The "Synopsis" column of the Outline View takes its information from a specially formatted comment.
In order to flag a comment as a Synopsis, add the word "Synopsis:" as the first word of the comment.
The ":" is required, and "synopsis" is not case sensitive.
If it is correctly formatted, the syntax highlighter will indicate this by altering the colour of the word.
.. note:: .. note::
Only one comment can be flagged as a synopsis comment for each heading. The :guilabel:`Title` column cannot be disabled or moved.
If multiple comments are flagged as a synopsis, the last one will be used.
The information viewed in the outline is based on the project's main index. While novelWriter does
its best to keep the index up to date when content changes, you can always rebuild it manually by
pressing :kbd:`F9` if something isn't right.
The outline view itself can be regenerated by pressing :kbd:`F10`. You can also enable automatic
updating in the :guilabel:`Tools` menu, which will trigger an update whenever the index is updated
and the :guilabel:`Outline` tab is active. You may want to disable this feature if your project is
very large,
.. _a_ui_outline_synopsis:
Synopsis Column
---------------
The :guilabel:`Synopsis` column of the outline view takes its information from a specially formatted
comment. See :ref:`a_ui_md_comm`. In order to flag a comment as a synopsis, add the word
``Synopsis:`` as the first word of the comment. The ``:`` is required, and the word ``synopsis`` is
not case sensitive. If it is correctly formatted, the syntax highlighter will indicate this by
altering the colour of the word.
.. note::
Only one comment can be flagged as a synopsis comment for each heading. If multiple comments are
flagged as a synopsis comment, the last one will be used.
.. _a_ui_shortcuts:
Keyboard Shortcuts Keyboard Shortcuts
================== ==================
Most features are available as keyboard shortcuts. Most features are available as keyboard shortcuts. These are as follows:
These are as following:
.. csv-table:: Keyboard Shortcuts .. csv-table:: Keyboard Shortcuts
:header: "Shortcut", "Description" :header: "Shortcut", "Description"
:widths: 15, 50 :widths: 30, 70
:class: "tight-table"
":kbd:`Alt-1`", "Switch focus to tree view pane." ":kbd:`Alt`:kbd:`1`", "Switch focus to the project tree."
":kbd:`Alt-2`", "Switch focus to document editor pane." ":kbd:`Alt`:kbd:`2`", "Switch focus to document editor."
":kbd:`Alt-3`", "Switch focus to document viewer pane." ":kbd:`Alt`:kbd:`3`", "Switch focus to document viewer."
":kbd:`Ctrl-.`", "Correct word under cursor." ":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor."
":kbd:`Ctrl-,`", "Open the Preferences dialog." ":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog."
":kbd:`Ctrl-/`", "Change block format to comment." ":kbd:`Ctrl`:kbd:`/`", "Change block format to comment."
":kbd:`Ctrl--`", "Strikethrough selected text, or word under cursor." ":kbd:`Ctrl`:kbd:`-`", "Strikethrough selected text, or word under cursor."
":kbd:`Ctrl-0`", "Remove block formatting for block under cursor." ":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor."
":kbd:`Ctrl-1`", "Change block format to header level 1." ":kbd:`Ctrl`:kbd:`1`", "Change block format to header level 1."
":kbd:`Ctrl-2`", "Change block format to header level 2." ":kbd:`Ctrl`:kbd:`2`", "Change block format to header level 2."
":kbd:`Ctrl-3`", "Change block format to header level 3." ":kbd:`Ctrl`:kbd:`3`", "Change block format to header level 3."
":kbd:`Ctrl-4`", "Change block format to header level 4." ":kbd:`Ctrl`:kbd:`4`", "Change block format to header level 4."
":kbd:`Ctrl-A`", "Select all text in document." ":kbd:`Ctrl`:kbd:`A`", "Select all text in the document."
":kbd:`Ctrl-B`", "Format selected text, or word under cursor, with strong emphasis (bold)." ":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)."
":kbd:`Ctrl-C`", "Copy selected text to clipboard." ":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard."
":kbd:`Ctrl-D`", "Wrap selected text, or word under cursor, in double quotes." ":kbd:`Ctrl`:kbd:`D`", "Wrap selected text, or word under cursor, in double quotes."
":kbd:`Ctrl-E`", "If in tree view, edit a document or folder settings. (Same as :kbd:`F2`)" ":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`)"
":kbd:`Ctrl-F`", "Open the search bar and search for selected word, if any is selected." ":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected."
":kbd:`Ctrl-G`", "Find next occurrence of word in current document. (Same as :kbd:`F3`)" ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`)"
":kbd:`Ctrl-H`", "Open the search and replace bar and search for selected word, if any is selected. (On Mac, this is :kbd:`Cmd-=`)" ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`)"
":kbd:`Ctrl-I`", "Format selected text, or word under cursor, with emphasis (italic)." ":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
":kbd:`Ctrl-N`", "Create new document." ":kbd:`Ctrl`:kbd:`N`", "Create new document."
":kbd:`Ctrl-O`", "Open selected document." ":kbd:`Ctrl`:kbd:`O`", "Open selected document."
":kbd:`Ctrl-Q`", "Exit novelWriter." ":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
":kbd:`Ctrl-R`", "If in tree view, open a document for viewing. If editor pane has focus, open current document for viewing." ":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
":kbd:`Ctrl-S`", "Save the current document in the editor." ":kbd:`Ctrl`:kbd:`S`", "Save the current document in the document editor."
":kbd:`Ctrl-V`", "Paste text from clipboard to cursor position." ":kbd:`Ctrl`:kbd:`V`", "Paste text from clipboard to cursor position."
":kbd:`Ctrl-W`", "Close the current document in the editor." ":kbd:`Ctrl`:kbd:`W`", "Close the current document in the document editor."
":kbd:`Ctrl-X`", "Cut selected text to clipboard." ":kbd:`Ctrl`:kbd:`X`", "Cut selected text to clipboard."
":kbd:`Ctrl-Y`", "Redo latest undo." ":kbd:`Ctrl`:kbd:`Y`", "Redo latest undo."
":kbd:`Ctrl-Z`", "Undo latest changes." ":kbd:`Ctrl`:kbd:`Z`", "Undo latest changes."
":kbd:`Ctrl-F7`", "Toggle spell checking." ":kbd:`Ctrl`:kbd:`F7`", "Toggle spell checking."
":kbd:`Ctrl-F10`", "Toggle automatic updating of project outline." ":kbd:`Ctrl`:kbd:`F10`", "Toggle automatic updating of project outline."
":kbd:`Ctrl-Del`", "If in tree view, move a document to trash, or delete a folder." ":kbd:`Ctrl`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder."
":kbd:`Ctrl-Enter`", "Open the tag or reference under the cursor in the view panel." ":kbd:`Ctrl`:kbd:`Enter`", "Open the tag or reference under the cursor in the Viewer."
":kbd:`Ctrl-Shift-,`", "Open the Project Settings dialog." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`,`", "Open the :guilabel:`Project Settings` dialog."
":kbd:`Ctrl-Shift-/`", "Remove block formatting for block under cursor." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`/`", "Remove block formatting for block under cursor."
":kbd:`Ctrl-Shift-1`", "Replace occurrence of word in current document, and search for next occurrence." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence."
":kbd:`Ctrl-Shift-A`", "Select all text in current paragraph." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
":kbd:`Ctrl-Shift-B`", "Format selected text, or word under cursor, with very strong emphasis (bold and italic)." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`D`", "Wrap selected text, or word under cursor, in single quotes."
":kbd:`Ctrl-Shift-D`", "Wrap selected text, or word under cursor, in single quotes." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`)"
":kbd:`Ctrl-Shift-G`", "Find previous occurrence of word in current document. (Same as :kbd:`Shift-F3`" ":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
":kbd:`Ctrl-Shift-I`", "Import text to the current document from a text file." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
":kbd:`Ctrl-Shift-N`", "Create new folder." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
":kbd:`Ctrl-Shift-O`", "Open a project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
":kbd:`Ctrl-Shift-R`", "Close the document view pane." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
":kbd:`Ctrl-Shift-S`", "Save the current project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project."
":kbd:`Ctrl-Shift-W`", "Close the current project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`Up`", "Move item one step up in the project tree."
":kbd:`Ctrl-Shift-Up`", "Move item one step up in the tree view." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`Down`", "Move item one step down in the project tree."
":kbd:`Ctrl-Shift-Down`", "Move item one step down in the tree view." ":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website."
":kbd:`F1`", "Open documentation. This just tries to send the documentation URL ti your browser." ":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)"
":kbd:`F2`", "If in tree view, edit a document or folder settings. (Same as :kbd:`Ctrl-E`)" ":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)"
":kbd:`F3`", "Find next occurrence of word in current document. (Same as :kbd:`Ctrl-G`)" ":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog."
":kbd:`F5`", "Open the Build Novel Project dialog." ":kbd:`F6`", "Open the :guilabel:`Writing Statistics` dialog."
":kbd:`F6`", "Open the Writing Statistics dialog." ":kbd:`F7`", "Re-run spell checker."
":kbd:`F7`", "Re-run spell checker." ":kbd:`F8`", "Activate :guilabel:`Focus Mode`, hiding the project tree and document viewer."
":kbd:`F8`", "Activate Focus Mode, hiding project tree and view panel." ":kbd:`F9`", "Re-build the project index."
":kbd:`F9`", "Re-build project index." ":kbd:`F10`", "Re-build the project outline."
":kbd:`F10`", "Re-build project outline." ":kbd:`F11`", "Activate full screen mode."
":kbd:`F11`", "Activate full screen mode." ":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser."
":kbd:`Shift-F3`", "Find previous occurrence of word in current document. (Same as :kbd:`Ctrl-Shift-G`" ":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`)"
":kbd:`Enter`", "If in tree view, open a document for editing." ":kbd:`Return`", "If in the project tree, open a document for editing."
.. note:: .. note::
On macOS, replace :kbd:`Ctrl` with :kbd:`Cmd`. On macOS, replace :kbd:`Ctrl` with :kbd:`Cmd`.
A set of insert features are also available through shortcuts, but they require a double combination of shortcuts.
.. _a_ui_shortcuts_ins:
Insert Shortcuts
----------------
A set of insert features are also available through shortcuts, but they require a double combination
of key sequences. The insert feature is activated with :kbd:`Ctrl-K`, followed by a key or
combination for the inserted character or punctuation.
.. csv-table:: Keyboard Shortcuts .. csv-table:: Keyboard Shortcuts
:header: "Shortcut", "Description" :header: "Shortcut", "Description"
:widths: 30, 50 :widths: 40, 60
:class: "tight-table"
":kbd:`Ctrl-K, -`", "Insert a short dash (en dash)." ":kbd:`Ctrl`:kbd:`K`, :kbd:`-`", "Insert a short dash (en dash)."
":kbd:`Ctrl-K, _`", "Insert a long dash (em dash)." ":kbd:`Ctrl`:kbd:`K`, :kbd:`_`", "Insert a long dash (em dash)."
":kbd:`Ctrl-K, .`", "Insert ellipsis." ":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert ellipsis."
":kbd:`Ctrl-K, Return`", "Insert a hard line break." ":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert left single quote."
":kbd:`Ctrl-K, Space`", "Insert a non-breaking space." ":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert right single quote."
":kbd:`Ctrl-K, Shift-Space`", "Insert a thin space." ":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert left double quote."
":kbd:`Ctrl-K, Ctrl-Space`", "Insert a thin non-breaking space." ":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert right double quote."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Return`", "Insert a hard line break."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`", "Insert a thin non-breaking space."
+92 -25
View File
@@ -1,48 +1,115 @@
.. _a_intro:
************ ************
Introduction Introduction
************ ************
novelWriter is a simple, multi-document plain text editor using a modified markdown syntax to apply simple formatting. novelWriter is a simple, multi-document plain text editor using a modified markdown syntax to apply
Additional features that are not standard markdown are available through special meta data keywords. simple formatting. It is designed for writing novels, and allows for the component documents to be
These keywords make it possible to inter-link documents, and generate an overview of the entire novel project and how the various files are interconnected. ordered freely to create the desired structure of the novel. More details about how projects are
structured is covered on the :ref:`a_struct` page.
In addition, the project can contain notes on the various plot elements, characters, locations, etc,
that make up the story. These notes are organised in a set of category-specific top-level folders,
and each entry can be tagged and cross-referenced from within the novel files and other notes. These
tags make it possible to inter-link documents, and generate an overview of the entire novel project
and how the various files and plot elements are interconnected. This is covered on the :ref:`a_proj`
and :ref:`a_notes` pages.
These additional features are not standard in markdown, but are available through special meta
keywords. Syntax highlighting is provided to make it easier to verify that the markdown tags are
used correctly. The syntax is covered on the :ref:`a_ui` page.
.. _a_intro_design:
Design Philosophy Design Philosophy
----------------- =================
The user interface is intended to be as minimalistic as practically possible, while at the same time provide a complete set of features needed for writing a novel. The user interface of novelWriter is intended to be as minimalistic as practically possible, while
at the same time provide a complete set of features needed for writing a novel.
.. note:: .. note::
novelWriter is not intended to be a full office type word processor. novelWriter is not intended to be a full office type word processor. It doesn't support images,
It doesn't support images, links, tables, and its formatting is limited to headers, and bold, italicised and underlined text. links, tables, and other complex structure and objects often needed for such document. Formatting
is limited to headers, and bold, italicised and strikethrough text.
Most features are accessible through the menu and through keyboard shortcuts. The main window does not have a toolbar like most other applications do. This reduces clutter, and
The colour scheme of the user interface can be modified with various themes, and new themes are fairly straight forward to add. since the documents are formatted with markdown tags, is more or less redundant. However, all
formatting features supported are available through convenient keyboard shortcuts. They are also
available in the main menu. A full list of shortcuts can be found in the :ref:`a_ui_shortcuts`
section.
The project itself is laid out in a tree view on the left hand side of the main window. In addition, novelWriter offers a :guilabel:`Focus Mode` where all the user interface elements other
It has various sections, called *root folders*, for the various types of supporting files that the user may want to add to the project. than the document editor itself are hidden away.
The novel itself lives under its own root folder.
The colour scheme of the user interface defaults to that of the host operating system. In addition,
a dark theme is provided, and can be enabled in :guilabel:`Preferences` from the :guilabel:`Tools`
menu. A number of syntax highlighting themes are also available in :guilabel:`Preferences`. A set of
icon themes in colour and greyscale are also offered. The icons are based on the Typicon_ icon set
designed by Stephen Hutchings.
The main window is split in two, or optionally three, panels. The left-most contains the project
tree and all the files in your project. The second panel is the document editor, and the optional
third panel is a document viewer which can view any document in your project.
A second tab is also available on the main window. This is the :guilabel:`Outline` tab where the
entire novel structure can be displayed, with all the tags and references listed. Depending on how
you structure your novel project files, this outline can be quite different than your project tree.
Your project tree lists files, your Outline tree lists the structure of the novel itself.
.. _Typicon: https://github.com/stephenhutchings/typicons.font
.. _a_intro_project:
Project Layout Project Layout
-------------- ==============
The layout of the novel itself is managed through the four supported heading levels, H1 through H4. You are free to structure your project files as you wish in subfolders, and split the text between
H1 is used for the book title, and for partitions. files in whatever way suits you. All that matters to novelWriter is the linear order the files
H2 is used for chapter tiles. appear at in the project tree (top to bottom). The chapters, scenes and sections of the novel are
H3 is reserved for scene titles. determined by the headings within those files.
H4 is for section titles within scenes, if such granularity is necessary.
For the files designated as project notes, the usage of headers imply no structural meaning, and the user is free to do whatever they want. The four heading levels (**H1** to **H4**) are treated as follows:
* **H1** is used for the book title, and for partitions.
* **H2** is used for chapter tiles.
* **H3** is used for scene titles optionally replaced by separators.
* **H4** is for section titles within scenes, if such granularity is needed.
This header level structure is only taken into account for novel files. For the files designated as
project notes, the header levels imply no structural meaning, and the user is free to do whatever
they want. See the :ref:`a_struct` page for more details.
.. _a_intro_export:
Project Export Project Export
-------------- ==============
The project can at any time be exported to a range of different formats. The project can at any time be exported to a range of different formats. Natively, novelWriter
Natively, novelWriter supports export to plain text file, HTML document, novelWriter flavoured markdown, standard markdown (requires Qt 5.14), and to a basic Open Document. supports export to plain text file, HTML document, novelWriter flavoured markdown, standard
In addition, printing and printing to PDF is also possible. markdown (requires Qt 5.14), and to a basic Open Document.
The best supported export format is HTML, which can be imported or converted by a number of other tools like Pandoc, or simply imported into Libre Office and similar.
In addition, printing and printing to PDF is also possible. The best supported export format is
HTML, which can be imported or converted by a number of other tools like Pandoc, or simply imported
into Libre Office Writer and similar word processors.
It is also possible to export the content of the project to a JSON file. This is useful if you want
to write your own processing script in for instance Python as the entire novel can be read into a
Python dictionary with a couple of lines of code.
A number of filter options can be applied to the produced document, allowing you to export a draft
manuscript, a reference document of notes, an outline based on chapter and scene titles with a
synopsis each, and so on. See the :ref:`a_export` page for more details on export features and
formats.
.. _a_intro_screenshots:
Screenshot Screenshot
---------- ==========
**novelWriter with default system theme:** **novelWriter with default system theme:**
+35 -14
View File
@@ -1,23 +1,44 @@
.. _a_notes:
************************ ************************
Supporting Files (Notes) Supporting Files (Notes)
************************ ************************
Supporting files, or notes, are any files stored in root folders that are not the Novel root folder. novelWriter doesn't have a database and compicated forms to fill in all details about plot elements,
These files are intended for summaries and outlines of the various plot elements, characters, locations, and so on, of the novel. characters, and all sorts of additional information that isn't a part of the novel text itself.
These are not required, but making at least minimal files for each such element, and add a tag to them, makes it possible to use the Outline View feature to see how each element intersects with each section of the novel itself, and add clickable cross-references between document in the editor and viewer. Instead, all such information is saved in notes. The relation between all these additional elements
is extracted from these files by the project indexer based on the tags and references you set.
File Tags These files are not required, but making at least minimal files for each such plot element, and add
========= a tag to them, makes it possible to use the :guilabel:`Outline` feature to see how each element
intersects with each section of the novel itself, and add clickable cross-references between
documents in the editor and viewer.
Each new heading in a note file can have a tag associated with it.
The format of a tag is ``@tag: tagname``, where tagname is a unique identifier.
Tags can then be referenced in the novel files, or other note files, and will show up in the Outline View and in the back-reference panel when a document is being viewed.
The syntax highlighter will alert the user that the keyword is correctly used and that the tag is allowed, that is, the tag is unique. .. _a_notes_tags:
Duplicate tags should be detected as long as the index is up to date.
The tag is the only part of these files that the application uses. Tags in Notes
The rest of the file is there for the writer to use in whatever way they wish. =============
A note file can also reference other note files in the same way novel files do. Each new heading in a note file can have a tag associated with it. The format of a tag is
When the note file is opened in the view pane, these become clickable links, making it easier to follow connections in the plot. ``@tag: tagname``, where tagname is a unique identifier. Tags can then be referenced in the novel
files, or cross-referenced in other note files, and will show up in the outline view and in the
back-reference panel when a document is being viewed.
The syntax highlighter will alert the user 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.
The tag is the only part of these files that the application uses. The rest of the file content is
there for the writer to use in whatever way they wish. Of course, the content of the files can be
exported if you want to compile a single document of all your notes, or include them in an outline.
A note file can also reference other note files in the same way novel files do. When the note file
is opened in the view panel, these become clickable links, making it easier to follow connections in
the plot. Note files don't show up in the outline view though, so referencing between notes is only
meaningful if you want to be able to click-navigate between them.
.. tip::
If you cross-reference between notes as well, and export your project as an HTML file using the
export tool, the cross-references become clickable in the exported document.
+217 -96
View File
@@ -1,158 +1,279 @@
.. _a_proj:
************** **************
Novel Projects Novel Projects
************** **************
A novelWriter project requires a dedicated folder for storing its files on the local file system. A novelWriter project requires a dedicated folder for storing its files on the local file system.
See the Technical Information section for further details. See the :ref:`a_tech` page for further details on how files are organised.
A new project can be created from the Project menu by selecting :menuselection:`Project --> New Project`. A new project can be created from the :guilabel:`Project` menu by selecting :guilabel:`New Project`.
A list of recently opened projects is maintained, and displayed in the "Open Project" dialog. A list of recently opened projects is maintained, and displayed in the :guilabel:`Open Project`
A project can be removed from this list by selecting it and pressing the :kbd:`Del` key. dialog. A project can be removed from this list by selecting it and pressing the :kbd:`Del` key.
The project specific settings are available in :menuselection:`Project --> Project Settings`. The project specific settings are available in :guilabel:`Project Settings` in the
See further details below. :guilabel:`Project` menu. See further details below in the :ref:`a_proj_settings` section.
.. _a_proj_roots:
Project Roots Project Roots
============= =============
Projects are structured into a set of root folders, visible in the left side tree view panel. Projects are structured into a set of top level folders called *root folders*. They are visible in
the project tree at the left side of the main window.
The core novel files go into a root folder of type "Novel". The core novel files go into a root folder of type :guilabel:`Novel`. Other supporting files go into
Other supporting files go into root folders of types "Plot", "Characters", "Locations", "Timeline", "Objects", "Entities", or "Custom". the other root folders. These other root folder types are intended for your notes on the various
These other root folder types are intended for your notes on the various elements of your story. elements of your story. Using these is of course entirely optional.
Using these is of course entirely optional.
A new project will not have all of the root folders present, but you can add the ones you want from :menuselection:`Project --> Create Root Folder`.
The root folders are intended for the following use, but aside from the Novel folder, no restrictions are enforced by the application. A new project will not have all of the root folders present, but you can add the ones you want from
You can use them however you want. :guilabel:`Create Root Folder` in the :guilabel:`Project` menu.
.. note:: The root folders are intended for the following use, but aside from the :guilabel:`Novel` folder, no
The root folders correspond to the categories of tags that can be used. restrictions are enforced by the application. You can use them however you want.
See the "Project Structure" section for further details.
* **Novel:** The root folder of all text that goes into the final novel. :guilabel:`Novel`
This class of files have other rules and features than other files in the project. This is the root folder of all text that goes into the final novel. This class of files have
See the Novel Structure section for more details. other rules and features than other files in the project. See the :ref:`a_struct` page for more
* **Plot:** This is the root folder where main plots can be outlined. details.
It is optional, but adding at least dummy files can be useful in order to tag plot elements for the Outline View.
* **Characters:** Character files go in this root folder.
These are especially important if one wants to use the Outline View to see which character appears where, and which part of the story is told from a specific character's point-of-view.
* **Locations:** Location is for various scene locations that one wants to track.
* **Timeline:** If the story jumps in time within the same plot, this class of files can be used to track this.
* **Objects:** Important objects in the story can be tracked here.
* **Entities:** Entities, like organisations or companies, that are part of the plot, can be organised here.
* **Custom:** The custom root folder can be used for tracking anything else not covered by the above options.
Deleted files will be moved into a special "Trash" root folder. :guilabel:`Plot`
Files in the Trash folder can be deleted permanently. This is the root folder where main plots can be outlined. It is optional, but adding at least
dummy files can be useful in order to tag plot elements for the Outline view. Tags in this folder
can be references using the ``@plot`` keyword.
:guilabel:`Characters`
Character files go in this root folder. These are especially important if one wants to use the
Outline view to see which character appears where, and which part of the story is told from a
specific character's point-of-view. Tags in this folder can be references using the ``@pov``
keyword for point-of-view characters, or the ``@char`` keyword for other characters.
:guilabel:`Locations`
The locations folder is for various scene locations that you want to track. Tags in this folder
can be references using the ``@location`` keyword.
:guilabel:`Timeline`
If the story has multiple plot timelines or jumps in time within the same plot, this class of
files can be used to track this. Tags in this folder can be references using the ``@time``
keyword.
:guilabel:`Objects`
Important objects in the story, for instance important objects that change hands often, can be
tracked here. Tags in this folder can be references using the ``@object`` keyword.
:guilabel:`Entities`
Does your plot have many powerful organisations or companies? Or other entities that are part of
the plot? They can be organised here. Tags in this folder can be references using the ``@entity``
keyword.
:guilabel:`Custom`
The custom root folder can be used for tracking anything else not covered by the above options.
Tags in this folder can be references using the ``@custom`` keyword.
The root folders correspond to the categories of tags that can be used to reference them. For more
information about the tags listed, see :ref:`a_struct_tags`.
.. tip::
You can rename root folders to whatever you want. The first character in the :guilabel:`Flags`
column will still indicate what type they are, and so will the icon if you are using one of the
Typicons icon sets.
.. _a_proj_roots_del:
Deleted Documents
-----------------
Deleted document files will be moved into a special :guilabel:`Trash` root folder. Files in the
trash folder can then be deleted permanently, either individually, or by emptying the trash from the
menu.
Folders and root folders can only be deleted when they are empty. Recursive deletion is not
supported.
A document file or a folder can be deleted from the :guilabel:`project` menu, or by pressing
:kbd:`Ctrl`:kbd:`Del`.
.. _a_proj_roots_orph:
Orphaned Documents Orphaned Documents
------------------ ------------------
In the event the editor crashes or otherwise exits without saving the project state, files that have been added to the project tree and are saved to disk will appear in a special "Orphaned Items" root folder next time the application is started. If novelWriter crashes or otherwise exits without saving the project state, or if you're using a
These orphaned files will not have any meta data associated with them, although novelWriter will try to restore the file label it had in the project tree. file synchronisation tool that runs out of sync, there may be files in the project folder that isn't
Other information will have to be set again, and the files moved back to the correct location in the project. tracked in the core project file. These files, when discovered, are handled by the Orphaned
Documents routine.
Files that are discovered in the project folder, but not in the project, will be re-added to the
project tree in a special :guilabel:`Orphaned Items` root folder next time the application is
started. These orphaned files will not have most of the meta data preserved, although novelWriter
will try to restore the file label it had in the project tree. Other information will have to be set
again, and the files moved back to the correct location in the project tree.
.. _a_proj_roots_lock:
Project Lockfile Project Lockfile
---------------- ----------------
To prevent orphaned files caused by file conflicts when novelWriter projects are synced with file synchronisation tools, a project lockfile is written to the project folder. To prevent orphaned files caused by file conflicts when novelWriter projects are synced with file
If you try to open a project which has such a file, you will be presented with a warning, and some information about where novelWriter thinks the project is open. synchronisation tools, a project lockfile is written to the project folder. If you try to open a
You will be give the option to ignore this warning, and continue opening the project. project which has such a file present, you will be presented with a warning, and some information
However, if multiple instances are in fact editing the same project, you are likely to cause inconsistencies and create diverging project files, potentially resulting in loss of data. about where else novelWriter thinks the project is also open. You will be give the option to ignore
this warning, and continue opening the project.
.. note:: .. note::
If, for some reason, novelWriter crashes, the lock file may remain. If, for some reason, novelWriter crashes, the lock file may remain even if there are no other
In such a case it is safe to ignore the lock file warning when re-opening the project. instances keeping the project open. In such a case it is safe to ignore the lock file warning
when re-opening the project.
.. warning::
If you choose to ignore the warning and continue opening the project, and multiple instances of
the project are in fact open, you are likely to cause inconsistencies and create diverging
project files, potentially resulting in loss of data and orphaned files.
.. _a_proj_roots_dirs:
Using Folders in the Project Tree Using Folders in the Project Tree
--------------------------------- ---------------------------------
Folders, aside from root folders, have no structural significance to the project. Folders, aside from root folders, have no structural significance to the project. When novelWriter
They are there purely as a way for the user to organise the files in meaningful sections and to be able to close them in the tree view. is processing the files in the novel, like for instance during export, these folders are ignored.
When processing the files in the novel, like for instance during export, the folders are ignored. Only the order of the document files themselves matter.
The folders are there purely as a way for the user to organise the files in meaningful sections and
to be able to collapse and hide them in the project tree when you're not working on those files.
.. tip::
You can use folders to sort your scene files into chapters. You will then need to add a chapter
file as the first file of your folder, and the scene files as the following files.
.. _a_proj_files:
Project Files
=============
New document files can be created from the :guilabel:`Document` menu, or by pressing
:kbd:`Ctrl`:kbd:`N` while in the Project Tree. This will create a new, empty file, and open the
:guilabel:`:Item Settings` dialog where the filename and various other settings can be changed.
This dialog can also be opened again later from either the :guilabel:`Project` menu, selecting
:guilabel:`Edit Item`, or by pressing :kbd:`Ctrl`:kbd:`E` or :kbd:`F2` with the item selected.
The layout of the file is also defined here. For Novel files, the full list of layout options are
available. For non-Novel files, only "Note" is available. See :ref:`a_struct_layout` for more
details.
You can also select whether the file is by default included when building the project. This setting
can be overridden in the :guilabel:`Build Novel Project` tool if you wish to include them anyway.
This is covered in the :ref:`a_export_files` section.
.. _a_proj_files_counts:
Word Counts
-----------
A character, word and paragraph count is maintained for each file, as well as dor each section of a
file defined by a header. The word count, and change of words in the current session, is displayed
in the footer of any document open in the editor, and all stats are shown in the details panel below
the project tree for any file selected.
The word counts are not updated in real time, but runs in the background every five seconds for as
long as the document is being actively edited.
A total project word count is displayed in the status bar. The total count depends on the sum of the
values in the project tree, which again depend on an up to date index. If the counts seem wrong, a
full project word recount can be initiated by rebuilding the project's index. Either form the
:guilabel:`Tools` menu, or by pressing :kbd:`F9`.
.. _a_proj_settings:
Project Settings Project Settings
================ ================
The project settings can be accessed from the :menuselection:`Project --> Project Settings` menu entry. The :guilabel:`Project Settings` can be accessed from the :guilabel:`Project` menu, or by pressing
This will open a dialog box, with a set of tabs. :kbd:`Ctrl`:kbd:`Shift`:kbd:`,`. This will open a dialog box, with a set of tabs.
Settings Tab Settings Tab
------------ ------------
The Settings tab holds the project title and author settings. The :guilabel:`Settings` tab holds the project title and author settings.
Working Title can be set to a different title than the Book Title.
The difference between them is simply that the Working Title is used for the GUI (main window title) and for generating the backup files. The :guilabel:`Working Title` can be set to a different title than the :guilabel:`Book Title`. The
The intention is that the working title should remain unchanged, while changing the final title has no effect on features relying on the project name. difference between them is simply that the :guilabel:`Working Title` is used for the GUI (main
The Book Title is currently not ues for anything, so setting it is just for the benefit of the author. window title) and for generating the backup files. The intention is that the :guilabel:`Working
Title` should remain unchanged throughput the project, otherwise the name of exported files and
backup files may change too.
The :guilabel:`Book Title` and :guilabel:`Book Authors` settings are currently not used for
anything, so setting then is just for the benefit of the author. Future, planned features will be
using them, and they are exported on some export formats in the :guilabel:`Build Novel Project`
tool.
The Book Authors text box takes one author per line.
Details Tab Details Tab
----------- -----------
This tab presents an overview of meta data about the project. This tab presents an overview of meta data for the project. It states where on your file system the
It states where on your file system the project is saved, how may times it has been saved, how many folders and files it contains, and how many words exist in the entire project. project is saved, how may times it has been saved, how many folders and files it contains, and how
many words exist in the entire project.
Status Tab
----------
Each file of type "Novel" can be given a status level, signified by a coloured icon. Status and Importance Tabs
These are purely there for the user's convenience, and you are not required to use them for any other feature to work. --------------------------
The intention is to use this list to set what stage of writing you are on, although you can in principle make them whatever you want.
Each file of type "Novel" can be given a status level, signified by a coloured icon and each file of
the remaining types can be given an importance level. These are colour coded icons and labels that
can be applied to each file.
These are purely there for the user's convenience, and you are not required to use them for any
other feature to work. No other part of novelWriter accesses this information. The intention is to
use these to indicate at what stage of completeion each novel file is, or how important the content
of a note file is to the plot. You don't have to use them this way, that's just what they were
intended for, but you can make them whatever you want.
.. note:: .. note::
The status levels currently in use by a file cannot be deleted. The status or importance level currently in use by one or more files cannot be deleted, but they
can be edited.
Importance Tab
--------------
Each file of types "Plot", "Character", "World", "Timeline", "Object", "Entity", or "Custom", can be given an importance level, signified by a coloured icon like for status level.
These are also purely there for the user's convenience, and you are not required to use them for any other feature to work.
The intention is to use this list to set how important the character, plot element, or otherwise, is for the story.
Again, these can in principle be used for whatever you want.
.. note::
The importance levels currently in use by a file cannot be deleted.
Auto-Replace Tab Auto-Replace Tab
---------------- ----------------
A set of automatically replaced keywords can be added in this tab. A set of automatically replaced keywords can be added in this tab. The keywords in the left column
The keywords in the left column wile be replaced by the text in the right column when documents are opened in the viewer. will be replaced by the text in the right column when documents are opened in the viewer. They will
This will also be applied to exports when the feature is added. also be applied to exports.
Note that a keyword cannot contain any spaces. .. note::
The angle brackets are added by default, and when used in the text are a part of the keyword to be replaced. A keyword cannot contain any spaces. The angle brackets are added by default, and when used in
This is to ensure that parts of the text isn't unintentionally replaced by the content of the list. the text are a part of the keyword to be replaced. This is to ensure that parts of the text isn't
unintentionally replaced by the content of the list.
Writing Files
=============
New document files can be created from the Document menu, or by pressing :kbd:`Ctrl-N` while in the tree view pane. .. _a_proj_backup:
This will create a new, empty file, and open the item settings dialog where the filename and various other settings can be set.
This dialog can also be opened again later from either the menu, :menuselection:`Project -> Edit` item, or by pressing :kbd:`Ctrl-E` or :kbd:`F2` with the item selected.
The layout of the file is also defined here.
For Novel files, the full list of layout options are available.
For non-Novel files, only "Note" is available.
You can also select whether the file is by default included when building the project.
This setting can be overridden in the export tool if you wish to include them anyway.
See the Project Structure section for more details.
Backup Backup
====== ======
An automatic backup system is built into novelWriter. An automatic backup system is built into novelWriter. In order to use it, a backup path to where the
In order to use it, a backup path to where the backups are to be stored must to be provided in :menuselection:`Tools --> Preferences`. backup files are to be stored must to be provided in :guilabel:`Preferences`.
Backups can be run automatically when a project is closed, which also implies it is run when the application is closed.
Backups are date stamped zip files of the entire project folder, and are stored in a subfolder of the backup path with the same name as the project working title set in Project Settings.
The backup feature, when configured, can also be run manually from the :menuselection:`Tools` menu. Backups can be run automatically when a project is closed, which also implies it is run when the
It is also possible to dissable automated backup for a given project in Project Settings. application is closed. Backups are date stamped zip files of the entire project folder, and are
stored in a subfolder of the backup path with the same name as the project :guilabel:`Working Title`
set in :ref:`a_proj_settings`.
The backup feature, when configured, can also be run manually from the :guilabel:`Tools` menu.
It is also possible to dissable automated backup for a given project in :guilabel:`Project
Settings`.
.. note:: .. note::
For the backup to be able to run, the Working Title must be set in Project Settings. For the backup to be able to run, the :guilabel:`Working Title` must be set in :guilabel:`Project
This value is used to generate the folder name for the zip files. Settings`. This value is used to generate the folder name for the zip files. Without it, the
backup will not run at all, but produce a warning message.
+130 -32
View File
@@ -1,45 +1,135 @@
.. _a_started:
*************** ***************
Getting Started Getting Started
*************** ***************
You can download novelWriter from https://github.com/vkbo/novelWriter/releases This is a brief guide to how you can get novelWriter running on your computer. These are the methods
currently supported by the developer. Packages may also be available in other package managers, but
those are not managed by me.
Latest version is |version|. As novelWriter matures, more options for how to install it and get it running will be added. At the
present time, the process is best suited for people used to work with Python projects from command
line.
Installing Dependencies
=======================
If you already have Python installed, all you need to do is install the dependencies. .. _a_started_install:
To do this, your need to open your command line tool, find the folder where you extracted novelWriter, and run:
Installation
============
You can download the latest version of novelWriter from the source repository on GitHub_. You can
also install it directly from PyPi with ``pip install novelwriter``, or download the packages
directly from the PyPi_ project page.
Latest version of novelWriter is |release|.
.. _GitHub: https://github.com/vkbo/novelWriter/releases
.. _PyPi: https://pypi.org/project/novelWriter/
.. _a_started_depend:
Dependencies
============
novelWriter has been designed to rely on as few dependencies as possible. Aside from the package(s)
needed to communicate with the Qt GUI libraries, only one package is required for handling the XML
format of the main project file. Everything else is handled with standard Python libraries.
Optionally, a package can be installed to interface with the Enchant spell checking libaries, but
this isn't strictly required. If no external spell checking library is available, novelWriter falls
back to using the internal ``difflib`` of Python to check spelling. This is a much slower approach,
and it is less sophisticated than full spell checking libaries, but if you only work with small
files, the performance loss is not noticeable.
.. _a_started_depend_packages:
Package Installation
--------------------
If you already have Python installed, all you need to do is install the dependencies. To do this,
you need to open your command line tool, find the folder where you extracted novelWriter, and run
the following command:
.. code-block:: console .. code-block:: console
python -m pip install -r requirements.txt pip install -r requirements.txt
On some operating systems you need to use ``python3`` instead of ``python``. This will install all the dependencies and recommended packages.
The following Python packages are required to run novelWriter: The following Python packages are required to run novelWriter:
* ``pyqt5`` for the GUI * ``pyqt5``, needed for connecting with the Qt5 libraries.
* ``lxml`` for writing project files * ``lxml``, needed full XML support.
You can of course also install these packages from your operating system's package repository.
.. note:: .. note::
Sometimes the SVG graphics package for pyqt5 must be installed separately. Sometimes the SVG graphics package for PyQt5 must be installed separately. It is usually called
something like ``python3-pyqt5.qtsvg``.
The following are optional, but recommended:
* ``pyenchant`` for spell checking
PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work. PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work.
Exporting to standard Markdown requires PyQt/Qt 5.14. Exporting to standard Markdown, for instance, requires PyQt/Qt 5.14. Searching using regular
There are no known minimum for package lxml, but the code was originally written with 4.2. expressions requires 5.3, and for full Unicode support, 5.13.
The optional spell check library must be at least 3.0.0 to work with Windows.
On Linux, 2.0.0 also works fine. There are no known minimum version requirement for package ``lxml``, but the code was originally
written with 4.2, which is therefore set as the minimum. It may work on lower versions. You have to
test it.
The spell checking extension is optional, but recommended:
* ``pyenchant``, needed for efficient spell checking.
The optional spell check library must be at least 3.0.0 to work with Windows. On Linux, 2.0.0 also
works fine.
.. _a_started_depend_docs:
Building the Documentation
--------------------------
If you installed novelWriter from a package, the documentation should be included. If you're running
novelWriter from the source code, a local copy of this documentation can be generated. It requires
the following Python packages on Debian and Ubuntu.
* ``python3-sphinx``
* ``python3-sphinxcontrib.qthelp``
Or from PyPi:
.. code-block:: console
pip install sphinx sphinxcontrib-qthelp
To build the help packages from the documentation source, run
.. code-block:: console
./setup.py qthelp
from the root source folder.
The setup script will copy the generated files into the ``nw/assets/help`` folder, and novelWriter
will detect the presence of the files and redirect the menu help entry to open help locally instead
of sending the user to the website. Pressing the :kbd:`F1` key will in any case try to open help
locally first, then send you to the website as a fallback.
.. note::
In order for the local version of help to work, the Qt Assistant must be installed on the local
computer. If it isn't available, or novelWriter cannot find it, the help feature will fall back
to redirecting you to the documentation website.
.. _a_started_running:
Running novelWriter Running novelWriter
=================== ===================
If all the required dependencies are met, you can run novelWriter from the command line in one of the following ways: If all the required dependencies are met, you can run novelWriter from the command line in one of
the following ways:
.. code-block:: console .. code-block:: console
@@ -47,22 +137,27 @@ If all the required dependencies are met, you can run novelWriter from the comma
python3 novelWriter.py python3 novelWriter.py
./novelWriter.py ./novelWriter.py
A few switches are supported from the command line, mostly to assist in debugging if an error is encountered. A few switches are supported from the command line, mostly to assist in debugging if an error is
To list all options, run: encountered. To list all options, run:
.. code-block:: console .. code-block:: console
python novelWriter.py --help python novelWriter.py --help
There are also a couple of install scripts in the assets folder which will assist in setting up launch icon and the novelWriter project file mimetype for Gnome desktops on Linux. There are also a couple of install scripts in the assets folder which will assist in setting up a
Currently, there's one script for Debian and one for Ubuntu. launch icon and the novelWriter project file mimetype for Gnome desktops on Linux. Currently,
there's one script for Debian and one for Ubuntu.
.. _a_started_standalone:
Building a Standalone Executable Building a Standalone Executable
================================ ================================
A standalone executable can be built with pyinstaller, using the provided python script "install.py" in the source folder. A standalone executable can be built with ``pyinstaller``, using the provided python script
This script will automatically try to install all dependencies and build the standalone executable of novelWriter. ``install.py`` in the source folder. This script will automatically try to install all dependencies
You can run the script by typing the following into your command prompt: and build the standalone executable of novelWriter. You can run the script by typing the following
into your command prompt:
.. code-block:: console .. code-block:: console
@@ -71,18 +166,21 @@ You can run the script by typing the following into your command prompt:
If successful, the executable will be in the "dist" folder. If successful, the executable will be in the "dist" folder.
.. _a_started_standalone_win:
Additional Instructions for Windows Additional Instructions for Windows
----------------------------------- -----------------------------------
If you don't have Python installed, you can download it from the python.org website. If you don't have Python installed, you can download it from the python.org website. The installers
The installers for Windows are available at https://www.python.org/downloads/windows/ for Windows are available at https://www.python.org/downloads/windows/
novelWriter should work with Python 3.5 or higher, and the executable installer is the easiest to install. novelWriter should work with Python 3.6 or higher, and the executable installer is the easiest to
Please note that the `pyenchant` package for spell checking does not currently work with the x86-64 version, so if you want spell checking, you must install the x86 version. install.
Also, make sure you select the "Add Python to PATH" option. Also, make sure you select the "Add Python to PATH" option.
.. image:: images/python_win_install.png .. image:: images/python_win_install.png
:width: 600 :width: 600
Once Python is set up and running, you can either run novelWriter from the folder where you extracted it, or you can build an executable and run that from a desktop icon instead. Once Python is set up and running, you can either run novelWriter from the folder where you
extracted it, or you can build an executable and run that from a desktop icon instead.
+168 -90
View File
@@ -1,120 +1,198 @@
***************** .. _a_struct:
Project Structure
***************** ***************
Novel Structure
***************
This section covers the structure of a novel project. This section covers the structure of a novel project.
.. note:: This section concerns files under the Novel type root folder only. There are some restrictions
This section concerns files under the Novel type root folder only. and features that only applies to these type of files.
There are some restrictions and features that only applies to these type of files.
.. _a_struct_heads:
Importance of Headings Importance of Headings
====================== ======================
Subfolders under root folders have no impact on the structure of the novel itself. Subfolders under root folders have no impact on the structure of the novel itself. The structure is
The structure is instead dictated by the heading level. instead dictated by the heading level of the headers within the document files.
Four levels of headings are supported, signified by the number of hashes preceding the title.
See the Markdown section.
The header levels are not only important when generating the exported novel file, but they are also used by the indexer and Outline View. Four levels of headings are supported, signified by the number of hashes preceding the title. See
Each heading starts a new region where new references to tags can be set. also the :ref:`a_ui_md` section for more details about the markdown syntax.
The different header levels are interpreted as specific section types of the novel. .. note::
The header levels are not only important when generating the exported novel file, they are also
used by the indexer when building the outline tree in the :guilabel:`Outline` tab. Each heading
also starts a new region where new references to tags can be set.
* ``# Header1``: Header level 1 signifies that the text refers to either the novel title or the name of a top level partition. The different header levels are interpreted as specific section types of the novel in the following
* ``## Header2``: Header level 2 signifies a chapter level partition. way:
* ``### Header3``: Header level 3 signifies a scene level partition.
* ``#### Header4``: Header level 4 signifies a sub-scene level partition (section). ``# Header1``
Header level one signifies that the text refers to either the novel title or the name of a top
level partition when you want to split the manuscript up into books, parts, or acts.
``## Header2``
Header level two 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 file per scene,
you need a single chapeter file with just the heading. You can of course also add a synopsis and
reference keywords to the chapter file. If you want to open the chaper with a quote, this is
also where you'd put the text for that.
``### Header3``
Header level three signifies a scene level partition. The title itself can be replaced with a
scene separator or just skipped entirely when you export your manuscript.
``#### Header4``
Header level four signifies a sub-scene level partition, usually called just a section in the
documentation und user interface. These can be useful if you want to change tag references
mid-scene, like if you change the point-of-view character. You are free to use sections as you
wish also in novel files, and can filter the titles out of the final manuscript just like with
scene titles.
There are multiple options of how to process novel titles when exporting 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_export` page for more details.
.. _a_struct_tags:
Tag References Tag References
============== ==============
Each partition, indicated by a heading, can contain references to tags set in the supporting files of the project. Each text section indicated by a heading of any level, can contain references to tags set in the
supporting files of the project. The references are gathered by the indexer and used to generate the
outline view on the :guilabel:`Outline` tab of how the different parts of the novel are connected.
The references are gathered by the indexer and used to generate the Outline View of how the different parts of the novel are connected. References and tags are also clickable in the document editor and viewer, making it easy to navigate
References and tags are also clickable in the view panel, and makes it easy to navigate reference notes while writing. between reference notes while writing. Clicked links are always opened in the view panel.
The targets of references can also be set per header.
This is covered in the "Supporting Files" section.
References are set as keyword and a list of corresponding tags. References are set as a keyword and a list of corresponding tags. The valid keywords are listed
The valid keywords are listed below. below. The format of a reference line is ``@keyword: value1, [value2] ... [valueN]``. All keywords
The format of a meta line is ``@keyword: value1, [value2] ... [valueN]``. allow multiple values.
All keywords allow multiple values.
* ``@pov``: The point-of-view character for the current section. ``@pov``
The target must be a note tag in the character root folder. The point-of-view character for the current section. The target must be a note tag in the
* ``@char``: Other characters in the current section. :guilabel:`Character` type root folder.
The target must be a note tag in the character root folder.
This should not include the point-of-view character.
* ``@plot``: The plot timelines touched by the current section.
The target must be a note tag in the plot root folder.
* ``@time``: The timelines touched by the current section.
The target must be a note tag in the timeline root folder.
* ``@location``: The location the current section takes place in.
The target must be a note tag in the locations root folder.
* ``@object``: Objects present in the current section.
The target must be a note tag in the object root folder.
* ``@entity``: Entities present in the current section.
The target must be a note tag in the entities root folder.
* ``@custom``: Custom references in the current section.
The target must be a note tag in the custom root folder.
The syntax highlighter will alert the user that only the correct keywords are used, and that the tags referenced exist. ``@char``
If the index of defined tags is out of date, press :kbd:`F9` to regenerate it, or select :menuselection:`Tools --> Rebuild Index` from the menu. Other characters in the current section. The target must be a note tag in a :guilabel:`Character`
In general, the index for a file is regenerated when a file is saved, so this shouldn't normally be necessary. type root folder. This should not include the point-of-view character(s).
``@plot``
The plot or subplot advanced in the current section. The target must be a note tag in a
:guilabel:`Plot` type root folder.
``@time``
The timelines touched by the current section. The target must be a note tag in a
:guilabel:`Timeline` type root folder.
``@location``
The location the current section takes place in. The target must be a note tag in a
:guilabel:`Locations` type root folder.
``@object``
Objects present in the current section. The target must be a note tag in an :guilabel:`Object`
type root folder.
``@entity``
Entities present in the current section. The target must be a note tag in an :guilabel:`Entities`
type root folder.
``@custom``
Custom references in the current section. The target must be a note tag in a :guilabel:`Custom`
type root folder.
The syntax highlighter will alert the user that the tags and references are used correctly, and that
the tags referenced exist.
The highlighter may be mistaken if the index of defined tags is out of date. If so, press :kbd:`F9`
to regenerate it, or select :guilabel:`Rebuild Index` from the :guilabel:`Tools` menu. In general,
the index for a file is regenerated when a file is saved, so this shouldn't normally be necessary.
.. _a_struct_layout:
Novel File Layout Novel File Layout
================= =================
Files in a novelWriter project can have a layout format set. All files in a novelWriter project can have a layout format set. These layouts are important when
These layouts are important when the project is exported, as they indicate how to treat the content in terms of formatting, headings and page breaks. the project is exported as they indicate how to treat the content in terms of formatting, headings,
The layout for each file is indicated as the last set of characters in the Flags column of the project tree. and page breaks. The layout for each file is indicated as the last set of characters in the
They also help to indicate what each file is for in your project. :guilabel:`Flags` column of the project tree.
Some of these layout types are different, some are just cosmetic. Not all layout types are actually treated differently, but they also help to indicate what each file
The "Book" layout is a generic novel file layout that in formatting is identical to "Chapter" and "Scene", but may help to indicate what files do in your project. is for in your project. The :guilabel:`Book` layout is a generic novel file layout that is formatted
You can lay out your project using Book files for each act, and then later split those into chapter or scene files by using the "Split Document" tool. identically to :guilabel:`Chapter` and :guilabel:`Scene` layout files, but may help to indicate what
Scenes can also be contained within chapter files, but you lose the drag and drop feature that comes with having them in separate files. files do in your project.
Some layouts have implications on how the project is exported. You can for instance lay out your project using :guilabel:`Book` files for each act, and then later
Files with layout "Title" and "Partition" have all headings and text centred, while the "Unnumbered" layout disables the automatic chapter numbering feature for everything contained within it. split those into chapter or scene files by using the :guilabel:`Split Document` tool. Scenes can
also be contained within :guilabel:`Chapter` type files, but you lose the drag and drop feature that
comes with having them in separate files if you organise them this way.
All of the above layout formats are only usable in the Novel root folder. Some layouts *do* have implications on how the project is exported. Files with layout
Files that are not a part of the novel itself should have the Note layout. :guilabel:`Title Page` and :guilabel:`Partition` have all headings and text centred, while the
These files are not getting any special formatting, and it is possible to collectively filter them out during export. :guilabel:`Unnumbered` layout disables the automatic chapter numbering feature for everything
Note files can be used anywhere in the project. contained within it. The latter is convenient for Prologue and Epilogue type chapters.
All of the above layout formats are only usable in the Novel root folder. Files that are not a part
of the novel itself should have the Note layout. These files are not getting any special formatting,
and it is possible to collectively filter them out during export. Note files can be used anywhere
in the project, also in the Novel root folder.
Below is an overview of all available layout formats. Below is an overview of all available layout formats.
* **Title Page**: The title page layout. :guilabel:`Title Page`
The title should be formatted as a heading level one. The title page layout. The title should be formatted as a heading level one. All text is
All text is automatically centred on exports. automatically centred on exports.
* **Plain Page**: A plain page layout useful for instance for front matter pages.
Heading levels are ignored for this layout format, and so are formatting options like Justify Text. :guilabel:`Plain Page`
The page is exported with a page break before it. A plain page layout useful for instance for front matter pages. Heading levels are ignored for
* **Book**: This is the generic novel file format that in principle can be used for all novel files. this layout format, and so are formatting options like :guilabel:`Justify Text`. The page is
Since the internal structure of the novel is controlled by the heading levels, this file will produce the same result as a collection of Partition, Chapter and Scene type files. exported with a page break before it.
However, it does not provide the functionality of the Unnumbered layout format.
* **Partition**: A partition can be used to split the novel into parts. :guilabel:`Book`
Partition titles are indicated with a level one heading. This is the generic novel file format that in principle can be used for all novel files. Since
You can also add text and meta data to the page. the internal structure of the novel is controlled by the heading levels, this file will produce
The Partition file layout will in addition force a page break before the heading, and centre all content on the page. the same result as a collection of :guilabel:`Partition`, :guilabel:`Chapter` and
* **Chapter**: Signifies the start of a new chapter. :guilabel:`Scene` type files. However, it does not provide the functionality of the
If the text itself is contained in scene files, these files should only contain the title, comments, synopsis, and tag references for characters, plot, etc. :guilabel:`Unnumbered` layout format.
The heading for chapters should be level two.
If you need an opening text, like a quote or other leading text before the first scene, this is also where you'd want to add this text. :guilabel:`Partition`
* **Unnumbered**: Same as Chapter, but when exporting the files and automatic chapter numbering is enabled, this file will not receive a number. A partition can be used to split the novel into parts. Partition titles are indicated with a
This makes the layout suitable for Prologue and Epilogue type chapters. level one heading. You can also add text and meta data to the page. The :guilabel:`Partition`
* **Scene**: A scene file. file layout will in addition force a page break before the heading, and centre all content on the
This file should have a header of level three. page.
Further sections can have headers of level four, but there are no file layout specifically for sections.
* **Note**: A generic file that is optionally ignored when the novel is exported. :guilabel:`Chapter`
Use these files for descriptions of content in the supporting root folders. Signifies the start of a new chapter. If the text itself is contained in scene files, these files
Note files can also be added to the Novel root folder if you need to insert notes there. should only contain the title, comments, synopsis, and tag references for characters, plot, etc.
Note file headers receive no formatting when building the project. The heading for chapters should be level two. If you need an opening text, like a quote or other
They are always exported as-is. leading text before the first scene, this is also where you'd want to add this text.
:guilabel:`Unnumbered`
Same as :guilabel:`Chapter`, but when exporting the files and automatic chapter numbering is
enabled, this file will not increment the chapeter number. It also has a separate title
formatting setting. This makes the layout suitable for Prologue and Epilogue type chapters.
:guilabel:`Scene`
A scene file. This file should have a header of level three. Further sections can have headers
of level four, but there are no file layout specifically for sections.
:guilabel:`Note`
A generic file that is optionally ignored when the novel is exported. Use these files for
descriptions of content in the supporting root folders. Note files can also be added to the Novel
root folder if you need to insert notes there. Note file headers receive no special formatting
when building the project. They are always exported as-is.
.. note:: .. note::
The layout granularity is entirely optional. The layout granularity is entirely optional. In principle, you can write the entire novel in a
In principle, you can write the entire novel in a single file with layout "Book". single file with layout :guilabel:`Book`. You can also have a single file per chapter if that
You can also have a single file per chapter. suits you better. The :guilabel:`Outline` will show your structure of chapters and scenes
regardless of how your files are organised.
.. tip::
You can always start writing with a coarse file layout with one or a few files, and then later
use the split tool to automatically split the files into chapter and scene files.
+54 -27
View File
@@ -1,55 +1,82 @@
.. _a_tech:
********************* *********************
Technical Information Technical Information
********************* *********************
This section contains details of how novelWriter stores and handles the project data. This section contains details of how novelWriter stores and handles the project data.
How Data is Stored How Data is Stored
================== ==================
All novelWriter files are written with utf-8 encoding. All novelWriter files are written with utf-8 encoding. Since Python automatically converts Unix line
Since Python automatically converts Unix line endings to Windows line endings on Windows systems, novelWriter does not make any adaptations to the formatting on Windows systems. endings to Windows line endings on Windows systems, novelWriter does not make any adaptations to the
This is handled entirely by the Python standard library. formatting on Windows systems. This is handled entirely by the Python standard library. Python also
handles this fairly well when working on the same files on both Windows and Unix-based operating
systems.
Main Project File Main Project File
----------------- -----------------
The project itself requires a dedicated folder for storing its files, where novelWriter will create its own "file system" where the folder and file hierarchy is described in a project XML file. The project itself requires a dedicated folder for storing its files, where novelWriter will create
This is the main project file in the project's root folder with the name ``nwProject.nwx``. its own "file system" where the folder and file hierarchy is described in a project XML file. This
This file also contains all the meta data required for the project, and a number of related project settings. is the main project file in the project's root folder with the name ``nwProject.nwx``. This file
also contains all the meta data required for the project, and a number of related project settings.
If this file is lost or corrupted, the structure of the project is lost. If this file is lost or corrupted, the structure of the project is lost. It is important to keep
It is important to keep this file backed up, either through the built-in backup tool, or your own backup solution. this file backed up, either through the built-in backup tool, or your own backup solution.
.. note:: .. tip::
The novelWriter project folder is structured so that it can easily be added to a version control system like git. The novelWriter project folder is structured so that it can easily be added to a version control
If so, you may want to add a `.gitignore` file to exclude files with the extensions `.json` as JSON files are used to cache the index and various run-time settings. system like git. If so, you may want to add a `.gitignore` file to exclude files with the
extensions `.json` as JSON files are used to cache the index and various run-time settings and
are generally large files that change often. You'd also want to exclude the ``cache`` folder.
The project XML file is indent-formatted, 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 each time the
file is saved.
The project XML file is indent-formatted, suitable for diff tools and version control, although a timesetamp is set in the meta section on line 2 each time the file is saved.
Project Documents Project Documents
----------------- -----------------
The project documents are saved in a folder in the main project folder named ``content``. The project documents are saved in a folder in the main project folder named ``content``. Each
Each document has a file handle taken from the first 13 characters of a SHA256 hash of the system time when the file was first created. document has a file handle taken from the first 13 characters of a SHA256 hash of the system time
The documents are saved with a filename assembled from this hash and the file extension ``.nwd``. when the file was first created. The documents are saved with a filename assembled from this hash
If you wish to find the physical location of a file in the project, you can either look it up in the project XML file, or select :menuselection:`Document --> Show File Details` in the menu when having the document open. and the file extension ``.nwd``.
The reason for this cryptic file naming is to avoid issues with file naming conventions and restrictions on different operating systems, and also to have a file name that does not depend on what the user names the files, or changes it to. If you wish to find the physical location of a file in the project, you can either look it up in the
The file meta data in the tree view, except the file label, is only saved in the project XML file. project XML file, select :guilabel:`Show File Details` from the :guilabel:`Document` menu when
having the document open, or look in one of the ``ToC`` files in the root of the project folder.
The ``ToC`` files have a list of all document files in the project and where they are saved.
Each document file contains a plain text version of the text from the editor. The reason for this cryptic file naming is to avoid issues with file naming conventions and
The file can in principle be edited in any text editor, and is suitable for diffing and version control if so desired. restrictions on different operating systems, and also to have a file name that does not depend on
Just make sure the file remains in utf-8 encoding, otherwise unicode chatracters may become mangled when opened in novelWriter again. what the user names the files, or changes it to. The file meta data in the tree view, except the
file label, is only saved in the project XML file.
Each document file contains a plain text version of the text from the editor. The file can in
principle be edited in any text editor, and is suitable for diffing and version control if so
desired. Just make sure the file remains in utf-8 encoding, otherwise unicode chatracters may become
mangled when the file is opened in novelWriter again.
The first line of the file contains some meta data starting with the characters ``%%~``. This line
is mainly there to restore some information if it is lost from the project file, and the information
may be helpful if you do open the file in an external editor as it contains the file label as the
last entry. The line can be deleted without any consequences to the rest of the content of the file,
and will be added back the next time the file is saved in novelWriter.
The first line of the file contains some meta data starting with the characters "%%~".
This line is mainly there to restore some information if it is lost from the project file, and the information may be helpful if you do open the file in an external editor as it contains the file label as the last entry.
The line can be deleted without any consequences to the rest of the content of the file, and will be added back next time the file is saved in novelWriter.
The File Saving Process The File Saving Process
----------------------- -----------------------
When saving the project file, or any of the documents, the data is first saved to a temporary file. When saving the project file, or any of the documents, the data is first saved to a temporary file.
If successful, the old data file is removed, and the temporary file becomes the new file. If successful, the old data file is removed, and the temporary file becomes the new file. This
This ensures that the previously saved data is only replaced when the new data has been successfully saved. ensures that the previously saved data is only replaced when the new data has been successfully
For the project XML file, a `.bak` file is kept which will always contain the previous version of the file, although when auto-save is enabled, they may have the same content. saved.
For the project XML file, a ``.bak`` file is 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.
+3 -3
View File
@@ -62,10 +62,10 @@ else:
instOpt = [ instOpt = [
"--name=novelWriter", "--name=novelWriter",
"--clean",
"--onefile", "--onefile",
"--add-data=%s%s%s" % (os.path.join("nw", "assets", "themes"), dotDot,"themes"), "--add-data=%s%s%s" % (os.path.join("nw", "assets"), dotDot, "assets"),
"--add-data=%s%s%s" % (os.path.join("nw", "assets", "graphics"), dotDot,"graphics"), "--icon=%s" % os.path.join("nw", "assets", "icons", "novelwriter.ico"),
"--icon=%s" % os.path.join("nw", "assets", "icons", "novelWriter.ico"),
] ]
if buildWindowed: if buildWindowed:
instOpt.append("--windowed") instOpt.append("--windowed")
+66 -23
View File
@@ -34,19 +34,21 @@ from os import path, remove, rename
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QErrorMessage from PyQt5.QtWidgets import QApplication, QErrorMessage
from nw.error import exceptionHandler
from nw.config import Config from nw.config import Config
__package__ = "novelWriter" __package__ = "nw"
__author__ = "Veronica Berglyd Olsen" __author__ = "Veronica Berglyd Olsen"
__copyright__ = "Copyright 20182020, Veronica Berglyd Olsen" __copyright__ = "Copyright 20182020, Veronica Berglyd Olsen"
__license__ = "GPLv3" __license__ = "GPLv3"
__version__ = "0.10.1" __version__ = "0.11.0"
__hexversion__ = "0x001001f0" __hexversion__ = "0x001100f0"
__date__ = "2020-07-11" __date__ = "2020-08-08"
__maintainer__ = "Veronica Berglyd Olsen" __maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net" __email__ = "code@vkbo.net"
__status__ = "Pre-Release" __status__ = "Beta"
__url__ = "https://github.com/vkbo/novelWriter" __url__ = "https://novelwriter.io"
__sourceurl__ = "https://github.com/vkbo/novelWriter"
__issuesurl__ = "https://github.com/vkbo/novelWriter/issues" __issuesurl__ = "https://github.com/vkbo/novelWriter/issues"
__domain__ = "novelwriter.io" __domain__ = "novelwriter.io"
__docurl__ = "https://novelwriter.readthedocs.io" __docurl__ = "https://novelwriter.readthedocs.io"
@@ -90,7 +92,6 @@ CONFIG = Config()
def main(sysArgs=None): def main(sysArgs=None):
"""Parses command line, sets up logging, and launches main GUI. """Parses command line, sets up logging, and launches main GUI.
""" """
if sysArgs is None: if sysArgs is None:
sysArgs = sys.argv[1:] sysArgs = sys.argv[1:]
@@ -111,7 +112,7 @@ def main(sysArgs=None):
] ]
helpMsg = ( helpMsg = (
"{appname} {version} ({status} {date})\n" "novelWriter {version} ({status} {date})\n"
"{copyright}\n" "{copyright}\n"
"\n" "\n"
"This program is distributed in the hope that it will be useful,\n" "This program is distributed in the hope that it will be useful,\n"
@@ -132,7 +133,6 @@ def main(sysArgs=None):
" --data= Alternative user data path.\n" " --data= Alternative user data path.\n"
" --testmode Do not display GUI. Used by the test suite.\n" " --testmode Do not display GUI. Used by the test suite.\n"
).format( ).format(
appname = __package__,
version = __version__, version = __version__,
status = __status__, status = __status__,
copyright = __copyright__, copyright = __copyright__,
@@ -167,7 +167,9 @@ def main(sysArgs=None):
print(helpMsg) print(helpMsg)
sys.exit() sys.exit()
elif inOpt in ("-v", "--version"): elif inOpt in ("-v", "--version"):
print("%s %s Version %s [%s]" % (__package__,__status__,__version__,__date__)) print("novelWriter %s Version %s [%s]" % (
__status__, __version__, __date__)
)
sys.exit() sys.exit()
elif inOpt == "--info": elif inOpt == "--info":
debugLevel = logging.INFO debugLevel = logging.INFO
@@ -197,7 +199,7 @@ def main(sysArgs=None):
CONFIG.cmdOpen = cmdOpen CONFIG.cmdOpen = cmdOpen
# Set Logging # Set Logging
logFmt = logging.Formatter(fmt=logFormat, datefmt="%Y-%m-%d %H:%M:%S", style="{") logFmt = logging.Formatter(fmt=logFormat, style="{")
if not logFile == "" and toFile: if not logFile == "" and toFile:
if path.isfile(logFile+".bak"): if path.isfile(logFile+".bak"):
@@ -217,8 +219,8 @@ def main(sysArgs=None):
logger.addHandler(cHandle) logger.addHandler(cHandle)
logger.setLevel(debugLevel) logger.setLevel(debugLevel)
logger.info("Starting %s %s (%s) %s" % ( logger.info("Starting novelWriter %s (%s) %s" % (
__package__, __version__, __hexversion__, __date__ __version__, __hexversion__, __date__
)) ))
# Check Packages and Versions # Check Packages and Versions
@@ -249,13 +251,14 @@ def main(sysArgs=None):
if errorData: if errorData:
errApp = QApplication([]) errApp = QApplication([])
errMsg = QErrorMessage() errMsg = QErrorMessage()
errMsg.setMinimumWidth(500) errMsg.resize(500, 300)
errMsg.setMinimumHeight(300)
errMsg.showMessage(( errMsg.showMessage((
"ERROR: %s cannot start due to the following issues:<br><br>" "<h3>A critical error has been encountered</h3>"
"&nbsp;-&nbsp;%s<br><br>Exiting." "<p>novelWriter cannot start due to the following issues:<p>"
"<p>&nbsp;-&nbsp;%s</p>"
"<p>Shutting down ...</p>"
) % ( ) % (
__package__, "<br>&nbsp;-&nbsp;".join(errorData) "<br>&nbsp;-&nbsp;".join(errorData)
)) ))
errApp.exec_() errApp.exec_()
sys.exit(1) sys.exit(1)
@@ -268,13 +271,53 @@ def main(sysArgs=None):
if testMode: if testMode:
nwGUI = GuiMain() nwGUI = GuiMain()
return nwGUI return nwGUI
else: else:
nwApp = QApplication([__package__,("-style=%s" % qtStyle)]) nwApp = QApplication([CONFIG.appName, ("-style=%s" % qtStyle)])
nwApp.setApplicationName(__package__) nwApp.setApplicationName(CONFIG.appName)
nwApp.setApplicationVersion(__version__) nwApp.setApplicationVersion(__version__)
nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) nwApp.setWindowIcon(QIcon(CONFIG.appIcon))
nwApp.setOrganizationDomain("novelwriter.io") nwApp.setOrganizationDomain(__domain__)
nwGUI = GuiMain()
sys.exit(nwApp.exec_()) # We try to catch critical errors while setting up the main GUI
# by wrapping the main GUI in a try/except structure. This will
# not catch all exceptions for other parts of the application.
# For all other unhandled exceptions, we use a custom exception
# handler that pops a dialog box with the error message.
sys.excepthook = exceptionHandler
try:
nwGUI = GuiMain()
sys.exit(nwApp.exec_())
except Exception:
from traceback import print_tb
from nw.error import formatHtmlErrMsg
exType, exValue, exTrace = sys.exc_info()
logger.critical("%s: %s" % (exType.__name__, str(exValue)))
print_tb(exTrace)
try:
del nwApp
errApp = QApplication([])
errMsg = QErrorMessage()
errMsg.setWindowTitle("Critical Error")
errMsg.resize(800, 400)
errMsg.showMessage((
"<h3>A critical error has been encountered</h3>"
"%s"
"<p>Shutting down ...</p>"
) % formatHtmlErrMsg(exType, exValue, exTrace))
errApp.exec_()
except Exception as e:
logger.critical("Could not create error message dialog.")
logger.critical(str(e))
sys.exit(1)
return return
@@ -9,8 +9,8 @@
[Main] [Main]
name = Typicons Colour Dark name = Typicons Colour Dark
description = Coulorised icons for dark GUI theme based on Typicons. description = Coulorised icons for dark GUI theme based on Typicons.
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) author = Veronica Berglyd Olsen (adaptation)
credit = Stephen Hutchings credit = Stephen Hutchings (icon design)
url = https://github.com/stephenhutchings/typicons.font url = https://github.com/stephenhutchings/typicons.font
license = CC BY-SA 4.0 license = CC BY-SA 4.0
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
@@ -9,8 +9,8 @@
[Main] [Main]
name = Typicons Colour Light name = Typicons Colour Light
description = Coulorised icons for light GUI theme based on Typicons. description = Coulorised icons for light GUI theme based on Typicons.
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) author = Veronica Berglyd Olsen (adaptation)
credit = Stephen Hutchings credit = Stephen Hutchings (icon design)
url = https://github.com/stephenhutchings/typicons.font url = https://github.com/stephenhutchings/typicons.font
license = CC BY-SA 4.0 license = CC BY-SA 4.0
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
@@ -9,8 +9,8 @@
[Main] [Main]
name = Typicons Grey Dark name = Typicons Grey Dark
description = Greyscaled icons for dark GUI theme based on Typicons. description = Greyscaled icons for dark GUI theme based on Typicons.
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) author = Veronica Berglyd Olsen (adaptation)
credit = Stephen Hutchings credit = Stephen Hutchings (icon design)
url = https://github.com/stephenhutchings/typicons.font url = https://github.com/stephenhutchings/typicons.font
license = CC BY-SA 4.0 license = CC BY-SA 4.0
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
@@ -9,8 +9,8 @@
[Main] [Main]
name = Typicons Grey Light name = Typicons Grey Light
description = Greyscaled icons for light GUI theme based on Typicons. description = Greyscaled icons for light GUI theme based on Typicons.
author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) author = Veronica Berglyd Olsen (adaptation)
credit = Stephen Hutchings credit = Stephen Hutchings (icon design)
url = https://github.com/stephenhutchings/typicons.font url = https://github.com/stephenhutchings/typicons.font
license = CC BY-SA 4.0 license = CC BY-SA 4.0
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
+24 -7
View File
@@ -33,6 +33,7 @@ import nw
from os import path, mkdir, unlink, rename from os import path, mkdir, unlink, rename
from time import time from time import time
from shutil import which
from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
@@ -52,8 +53,8 @@ class Config:
def __init__(self): def __init__(self):
# Set Application Variables # Set Application Variables
self.appName = nw.__package__ self.appName = "novelWriter"
self.appHandle = nw.__package__.lower() self.appHandle = self.appName.lower()
self.showGUI = True self.showGUI = True
self.debugInfo = False self.debugInfo = False
self.cmdOpen = None self.cmdOpen = None
@@ -76,9 +77,11 @@ class Config:
self.graphPath = None self.graphPath = None
self.dictPath = None self.dictPath = None
self.iconPath = None self.iconPath = None
self.helpPath = None
# Set default values # Runtime Settings and Variables
self.confChanged = False self.confChanged = False
self.hasHelp = False
## General ## General
self.guiTheme = "default" self.guiTheme = "default"
@@ -200,8 +203,8 @@ class Config:
self.kernelVer = "Unknown" self.kernelVer = "Unknown"
# Packages # Packages
self.hasEnchant = False self.hasEnchant = False # The pyenchant package
self.hasSymSpell = False self.hasAssistant = False # The Qt Assistant executable
# Recent Cache # Recent Cache
self.recentProj = {} self.recentProj = {}
@@ -315,6 +318,11 @@ class Config:
if self.spellLanguage is None: if self.spellLanguage is None:
self.spellLanguage = "en" self.spellLanguage = "en"
# Check if local help files exist
self.helpPath = path.join(self.assetPath, "help", "novelWriter.qhc")
self.hasHelp = path.isfile(self.helpPath)
self.hasHelp &= path.isfile(path.join(self.assetPath, "help", "novelWriter.qch"))
logger.debug("Config initialisation complete") logger.debug("Config initialisation complete")
return True return True
@@ -888,10 +896,19 @@ class Config:
try: try:
import enchant import enchant
self.hasEnchant = True self.hasEnchant = True
logger.debug("Checking package pyenchant: Ok") logger.debug("Checking package 'pyenchant': Ok")
except: except:
self.hasEnchant = False self.hasEnchant = False
logger.debug("Checking package pyenchant: Missing") logger.debug("Checking package 'pyenchant': Missing")
try:
self.hasAssistant = which("assistant")
except:
self.hasAssistant = False
if self.hasAssistant:
logger.debug("Checking executable 'assistant': Ok")
else:
logger.debug("Checking executable 'assistant': Missing")
return return
+1 -3
View File
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from nw.constants.iso import isoLanguage, isoCountry from nw.constants.iso import isoLanguage, isoCountry
from nw.constants.constants import ( from nw.constants.constants import (
nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode, nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode
nwInsertSymbols
) )
from nw.constants.enum import ( from nw.constants.enum import (
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline, nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
@@ -19,7 +18,6 @@ __all__ = [
"nwLabels", "nwLabels",
"nwQuotes", "nwQuotes",
"nwUnicode", "nwUnicode",
"nwInsertSymbols",
"nwAlert", "nwAlert",
"nwDocAction", "nwDocAction",
"nwItemClass", "nwItemClass",
-15
View File
@@ -299,18 +299,3 @@ class nwUnicode:
H_LTRIS = "&#9666;" H_LTRIS = "&#9666;"
# END Class nwUnicode # END Class nwUnicode
class nwInsertSymbols():
SYMBOLS = {
nwDocInsert.NO_INSERT : "",
nwDocInsert.HARD_BREAK : " \n",
nwDocInsert.NB_SPACE : nwUnicode.U_NBSP,
nwDocInsert.THIN_SPACE : nwUnicode.U_THNSP,
nwDocInsert.THIN_NB_SPACE : nwUnicode.U_THNBSP,
nwDocInsert.SHORT_DASH : nwUnicode.U_ENDASH,
nwDocInsert.LONG_DASH : nwUnicode.U_EMDASH,
nwDocInsert.ELLIPSIS : nwUnicode.U_HELLIP,
}
# END Enum nwDocInsert
+4
View File
@@ -107,6 +107,10 @@ class nwDocInsert(Enum):
SHORT_DASH = 5 SHORT_DASH = 5
LONG_DASH = 6 LONG_DASH = 6
ELLIPSIS = 7 ELLIPSIS = 7
QUOTE_LS = 8
QUOTE_RS = 9
QUOTE_LD = 10
QUOTE_RD = 11
# END Enum nwDocInsert # END Enum nwDocInsert
+12 -14
View File
@@ -370,23 +370,21 @@ class NWProject():
if fileVersion == "1.0": if fileVersion == "1.0":
msgBox = QMessageBox() msgBox = QMessageBox()
msgRes = msgBox.question(self.theParent, "Old Project Version", ( msgRes = msgBox.question(self.theParent, "Old Project Version", (
"The project file and data is created by a %s version lower than 0.7. " "The project file and data is created by a novelWriter version "
"Do you want to upgrade the project to the most recent format?<br><br>" "lower than 0.7. Do you want to upgrade the project to the "
"Note that after the upgrade, you cannot open the project with an older " "most recent format?<br><br>Note that after the upgrade, you "
"version of %s any more, so make sure you have a recent backup." "cannot open the project with an older version of novelWriter "
) % ( "any more, so make sure you have a recent backup."
nw.__package__, nw.__package__
)) ))
if msgRes != QMessageBox.Yes: if msgRes != QMessageBox.Yes:
return False return False
elif fileVersion != "1.1" and fileVersion != "1.2": elif fileVersion != "1.1" and fileVersion != "1.2":
self.makeAlert(( self.makeAlert((
"Unknown or unsupported {nw:s} project file format. " "Unknown or unsupported novelWriter project file format. "
"The project cannot be opened by this version of {nw:s}. " "The project cannot be opened by this version of novelWriter. "
"The file was saved with {nw:s} version {vers:s}." "The file was saved with novelWriter version {vers:s}."
).format( ).format(
nw = nw.__package__,
vers = appVersion, vers = appVersion,
), nwAlert.ERROR) ), nwAlert.ERROR)
return False return False
@@ -397,11 +395,11 @@ class NWProject():
if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI: if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI:
msgBox = QMessageBox() msgBox = QMessageBox()
msgRes = msgBox.question(self.theParent, "Version Conflict", ( msgRes = msgBox.question(self.theParent, "Version Conflict", (
"This project was saved by a newer version of %s, version %s. This is version %s. " "This project was saved by a newer version of novelWriter, version %s. "
"If you continue to open the project, some attributes and settings may not be " "This is version %s. If you continue to open the project, some attributes "
"preserved. Continue opening the project?" "and settings may not be preserved. Continue opening the project?"
) % ( ) % (
nw.__package__, appVersion, nw.__version__ appVersion, nw.__version__
)) ))
if msgRes != QMessageBox.Yes: if msgRes != QMessageBox.Yes:
return False return False
-1
View File
@@ -43,7 +43,6 @@ class NWSpellCheck():
SP_INTERNAL = "internal" SP_INTERNAL = "internal"
SP_ENCHANT = "enchant" SP_ENCHANT = "enchant"
SP_SYMSPELL = "symspell"
theDict = None theDict = None
PROJW = [] PROJW = []
+3 -3
View File
@@ -281,9 +281,9 @@ class Tokenizer():
does the standard escaped characters. does the standard escaped characters.
""" """
escapeDict = { escapeDict = {
"\*" : "*", r"\*" : "*",
"\~" : "~", r"\~" : "~",
"\_" : "_", r"\_" : "_",
} }
escReplace = re.compile( escReplace = re.compile(
"|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL "|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL
+111
View File
@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
"""novelWriter Init
novelWriter Exception Handling
==================================
Error handling functions
File History:
Created: 2020-08-02 [0.10.2]
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
def formatHtmlErrMsg(exType, exValue, exTrace):
"""Generates a HTML version of an exception.
"""
try:
import sys
from traceback import format_tb
from nw import __issuesurl__, __version__
from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QSysInfo
fmtTrace = ""
for trEntry in format_tb(exTrace):
for trLine in trEntry.split("\n"):
stripLine = trLine.lstrip(" ")
nIndent = len(trLine) - len(stripLine)
fmtTrace += "&nbsp;"*nIndent + stripLine + "<br>"
theMessage = (
"<p>Please report this error by submitting an issue report on "
"GitHub, providing a description and this error message. "
"URL: &lt;{issueUrl}&gt;.</p>"
"<p><b>Environment</b><br>Version: {nwVersion}, OS: {osType} ({osKernel}),"
"Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}</p>"
"<p><b>Error Type</b><br>{exType}: {exMessage}</p>"
"<p><b>Traceback</b><br>{exTrace}</p>"
).format(
nwVersion = __version__,
osType = sys.platform,
osKernel = QSysInfo.kernelVersion(),
pyVersion = sys.version.split()[0],
pyHexVer = sys.hexversion,
qtVers = QT_VERSION_STR,
pyqtVers = PYQT_VERSION_STR,
issueUrl = __issuesurl__,
exType = exType.__name__,
exMessage = str(exValue),
exTrace = fmtTrace
)
return theMessage
except Exception as e:
return "Could not generate error message.<br>%s" % str(e)
return "Could not generate error message."
def exceptionHandler(exType, exValue, exTrace):
"""Function to catch unhandled global exceptions.
"""
import logging
from traceback import print_tb, format_tb
from nw import CONFIG
from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox
logger = logging.getLogger(__name__)
logger.error("%s: %s" % (exType.__name__, str(exValue)))
print_tb(exTrace)
if not CONFIG.showGUI:
return
try:
nwGUI = None
for qWin in qApp.topLevelWidgets():
if qWin.objectName() == "GuiMain":
nwGUI = qWin
break
if nwGUI is None:
logger.warning("Could not find main GUI window so cannot open error dialog")
return
errMsg = QErrorMessage(nwGUI)
errMsg.setWindowTitle("Unhandled Error")
errMsg.resize(800, 400)
errMsg.showMessage((
"<h3>An unhandled error has been encountered</h3>%s"
) % formatHtmlErrMsg(exType, exValue, exTrace))
except Exception as e:
logger.error(str(e))
return
+3 -3
View File
@@ -54,14 +54,14 @@ class GuiAbout(QDialog):
self.innerBox = QHBoxLayout() self.innerBox = QHBoxLayout()
self.innerBox.setSpacing(self.mainConf.pxInt(16)) self.innerBox.setSpacing(self.mainConf.pxInt(16))
self.setWindowTitle("About %s" % nw.__package__) self.setWindowTitle("About %s" % self.mainConf.appName)
self.setMinimumWidth(self.mainConf.pxInt(650)) self.setMinimumWidth(self.mainConf.pxInt(650))
self.setMinimumHeight(self.mainConf.pxInt(600)) self.setMinimumHeight(self.mainConf.pxInt(600))
nPx = self.mainConf.pxInt(96) nPx = self.mainConf.pxInt(96)
self.nwIcon = QLabel() self.nwIcon = QLabel()
self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx))) self.nwIcon.setPixmap(self.theParent.theTheme.getPixmap("novelwriter", (nPx, nPx)))
self.lblName = QLabel("<b>%s</b>" % nw.__package__) self.lblName = QLabel("<b>%s</b>" % self.mainConf.appName)
self.lblVers = QLabel("v%s" % nw.__version__) self.lblVers = QLabel("v%s" % nw.__version__)
self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x")) self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x"))
@@ -133,7 +133,7 @@ class GuiAbout(QDialog):
"<h3>Credits</h3>" "<h3>Credits</h3>"
"<p>{credits:s}</p>" "<p>{credits:s}</p>"
).format( ).format(
name = nw.__package__, name = self.mainConf.appName,
copyright = nw.__copyright__, copyright = nw.__copyright__,
website = nw.__url__, website = nw.__url__,
domain = nw.__domain__, domain = nw.__domain__,
-1
View File
@@ -430,7 +430,6 @@ class GuiBuildNovel(QDialog):
def _buildPreview(self): def _buildPreview(self):
"""Build a preview of the project in the document viewer. """Build a preview of the project in the document viewer.
""" """
# Get Settings # Get Settings
fmtTitle = self.fmtTitle.text().strip() fmtTitle = self.fmtTitle.text().strip()
fmtChapter = self.fmtChapter.text().strip() fmtChapter = self.fmtChapter.text().strip()
+85 -23
View File
@@ -52,7 +52,7 @@ from nw.core import NWDoc, NWSpellSimple, countWords
from nw.gui.dochighlight import GuiDocHighlighter from nw.gui.dochighlight import GuiDocHighlighter
from nw.common import transferCase from nw.common import transferCase
from nw.constants import ( from nw.constants import (
nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwInsertSymbols, nwItemClass nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwItemClass
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -79,6 +79,7 @@ class GuiDocEditor(QTextEdit):
self.wordCount = 0 self.wordCount = 0
self.paraCount = 0 self.paraCount = 0
self.lastEdit = 0 self.lastEdit = 0
self.lastFind = None
self.bigDoc = False self.bigDoc = False
self.doReplace = False self.doReplace = False
self.nonWord = "\"'" self.nonWord = "\"'"
@@ -89,7 +90,7 @@ class GuiDocEditor(QTextEdit):
self.typSQOpen = self.mainConf.fmtSingleQuotes[0] self.typSQOpen = self.mainConf.fmtSingleQuotes[0]
self.typSQClose = self.mainConf.fmtSingleQuotes[1] self.typSQClose = self.mainConf.fmtSingleQuotes[1]
# Core Elements # Core Elements and Signals
self.qDocument = self.document() self.qDocument = self.document()
self.qDocument.contentsChange.connect(self._docChange) self.qDocument.contentsChange.connect(self._docChange)
@@ -590,8 +591,33 @@ class GuiDocEditor(QTextEdit):
""" """
if isinstance(theInsert, str): if isinstance(theInsert, str):
theText = theInsert theText = theInsert
elif theInsert in nwInsertSymbols.SYMBOLS: elif theInsert in nwDocInsert:
theText = nwInsertSymbols.SYMBOLS[theInsert] if theInsert == nwDocInsert.NO_INSERT:
theText = "",
elif theInsert == nwDocInsert.HARD_BREAK:
theText = " \n",
elif theInsert == nwDocInsert.NB_SPACE:
theText = nwUnicode.U_NBSP,
elif theInsert == nwDocInsert.THIN_SPACE:
theText = nwUnicode.U_THNSP,
elif theInsert == nwDocInsert.THIN_NB_SPACE:
theText = nwUnicode.U_THNBSP,
elif theInsert == nwDocInsert.SHORT_DASH:
theText = nwUnicode.U_ENDASH,
elif theInsert == nwDocInsert.LONG_DASH:
theText = nwUnicode.U_EMDASH,
elif theInsert == nwDocInsert.ELLIPSIS:
theText = nwUnicode.U_HELLIP,
elif theInsert == nwDocInsert.QUOTE_LS:
theText = self.typSQOpen
elif theInsert == nwDocInsert.QUOTE_RS:
theText = self.typSQClose
elif theInsert == nwDocInsert.QUOTE_LD:
theText = self.typDQOpen
elif theInsert == nwDocInsert.QUOTE_RD:
theText = self.typDQClose
else:
return False
else: else:
return False return False
theCursor = self.textCursor() theCursor = self.textCursor()
@@ -660,6 +686,7 @@ class GuiDocEditor(QTextEdit):
triggers the syntax highlighter. triggers the syntax highlighter.
""" """
self.lastEdit = time() self.lastEdit = time()
self.lastFind = None
if not self.docChanged: if not self.docChanged:
self.setDocumentChanged(True) self.setDocumentChanged(True)
if not self.wcTimer.isActive(): if not self.wcTimer.isActive():
@@ -1145,19 +1172,21 @@ class GuiDocEditor(QTextEdit):
""" """
theCursor = self.textCursor() theCursor = self.textCursor()
if theCursor.hasSelection(): if theCursor.hasSelection():
selText = theCursor.selectedText() self.docSearch.setSearchText(theCursor.selectedText())
else: else:
selText = "" self.docSearch.setSearchText(None)
self.docSearch.setSearchText(selText)
self.updateDocMargins() self.updateDocMargins()
return return
def _beginReplace(self): def _beginReplace(self):
"""Opens the replace line of the search bar and sets the replace """Opens the replace line of the search bar and sets the find
text. text if a selection has been made, and resets the replace text.
""" """
self._beginSearch() theCursor = self.textCursor()
if theCursor.hasSelection():
self.docSearch.setSearchText(theCursor.selectedText())
self.docSearch.setReplaceText("") self.docSearch.setReplaceText("")
self.updateDocMargins()
return return
def _findNext(self, isBackward=False): def _findNext(self, isBackward=False):
@@ -1177,7 +1206,7 @@ class GuiDocEditor(QTextEdit):
if self.docSearch.isWholeWord: if self.docSearch.isWholeWord:
findOpt |= QTextDocument.FindWholeWords findOpt |= QTextDocument.FindWholeWords
searchFor = self.docSearch.getSearchText() searchFor = self.docSearch.getSearchObject()
wasFound = self.find(searchFor, findOpt) wasFound = self.find(searchFor, findOpt)
if not wasFound: if not wasFound:
if self.docSearch.doNextFile and not isBackward: if self.docSearch.doNextFile and not isBackward:
@@ -1190,7 +1219,11 @@ class GuiDocEditor(QTextEdit):
QTextCursor.End if isBackward else QTextCursor.Start QTextCursor.End if isBackward else QTextCursor.Start
) )
self.setTextCursor(theCursor) self.setTextCursor(theCursor)
self.find(searchFor, findOpt) wasFound = self.find(searchFor, findOpt)
if wasFound:
theCursor = self.textCursor()
self.lastFind = (theCursor.selectionStart(), theCursor.selectionEnd())
return return
@@ -1200,26 +1233,48 @@ class GuiDocEditor(QTextEdit):
next automatically when done. next automatically when done.
""" """
if not self.docSearch.isVisible(): if not self.docSearch.isVisible():
# The search tool is not active, so we activate it.
self._beginSearch() self._beginSearch()
return return
theCursor = self.textCursor() theCursor = self.textCursor()
if not theCursor.hasSelection(): if not theCursor.hasSelection():
# We have no text selected at all, so just make this a
# regular find next call.
self._findNext()
return
if self.lastFind is None and theCursor.hasSelection():
# If we have a selection but no search, it may have been the
# text we triggered the search with, in which case we search
# again from the beginning of that selection to make sure we
# have a valid result.
sPos = theCursor.selectionStart()
theCursor.clearSelection()
theCursor.setPosition(sPos)
self.setTextCursor(theCursor)
self._findNext()
theCursor = self.textCursor()
if self.lastFind is None:
# In case the above didn't find a result, we give up here.
return return
searchFor = self.docSearch.getSearchText() searchFor = self.docSearch.getSearchText()
replWith = self.docSearch.getReplaceText() replWith = self.docSearch.getReplaceText()
selText = theCursor.selectedText()
if self.docSearch.doMatchCap: if self.docSearch.doMatchCap:
replWith = transferCase(selText, replWith) replWith = transferCase(theCursor.selectedText(), replWith)
if not self.docSearch.isCaseSense: # Make sure the selected text was selected by an actual find
isMatch = searchFor.lower() == selText.lower() # call, and not the user.
else: try:
isMatch = searchFor == selText isFind = self.lastFind[0] == theCursor.selectionStart()
isFind &= self.lastFind[1] == theCursor.selectionEnd()
except:
isFind = False
if isMatch: if isFind:
theCursor.beginEditBlock() theCursor.beginEditBlock()
theCursor.removeSelectedText() theCursor.removeSelectedText()
theCursor.insertText(replWith) theCursor.insertText(replWith)
@@ -1229,9 +1284,10 @@ class GuiDocEditor(QTextEdit):
logger.verbose("Replaced occurrence of '%s' with '%s' on line %d" % ( logger.verbose("Replaced occurrence of '%s' with '%s' on line %d" % (
searchFor, replWith, theCursor.blockNumber() searchFor, replWith, theCursor.blockNumber()
)) ))
else:
logger.error("The selected text is not a search result, skipping replace")
if searchFor: self._findNext()
self._findNext()
return return
@@ -1500,7 +1556,8 @@ class GuiDocEditSearch(QFrame):
""" """
if not self.isVisible(): if not self.isVisible():
self.setVisible(True) self.setVisible(True)
self.searchBox.setText(theText) if theText is not None:
self.searchBox.setText(theText)
self.searchBox.setFocus() self.searchBox.setFocus()
if self.isRegEx: if self.isRegEx:
self._alertSearchValid(True) self._alertSearchValid(True)
@@ -1515,7 +1572,7 @@ class GuiDocEditSearch(QFrame):
self.replaceBox.setText(theText) self.replaceBox.setText(theText)
return True return True
def getSearchText(self): def getSearchObject(self):
"""Return the current search text either as text or as a regular """Return the current search text either as text or as a regular
expression object. expression object.
""" """
@@ -1543,6 +1600,11 @@ class GuiDocEditSearch(QFrame):
return theText return theText
def getSearchText(self):
"""Return the current search text.
"""
return self.searchBox.text()
def getReplaceText(self): def getReplaceText(self):
"""Return the current replace text. """Return the current replace text.
""" """
+111 -17
View File
@@ -28,7 +28,9 @@
import logging import logging
import nw import nw
from PyQt5.QtCore import QUrl from os import path
from PyQt5.QtCore import QUrl, QProcess
from PyQt5.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox
@@ -47,6 +49,10 @@ class GuiMainMenu(QMenuBar):
self.theParent = theParent self.theParent = theParent
self.theProject = theParent.theProject self.theProject = theParent.theProject
# Internals
self.assistProc = None
# Build Menu
self._buildProjectMenu() self._buildProjectMenu()
self._buildDocumentMenu() self._buildDocumentMenu()
self._buildEditMenu() self._buildEditMenu()
@@ -67,15 +73,40 @@ class GuiMainMenu(QMenuBar):
return return
##
# Methods
##
def setAvailableRoot(self): def setAvailableRoot(self):
for itemClass in nwItemClass: for itemClass in nwItemClass:
if itemClass == nwItemClass.NO_CLASS: continue if itemClass == nwItemClass.NO_CLASS:
if itemClass == nwItemClass.TRASH: continue continue
if itemClass == nwItemClass.TRASH:
continue
self.rootItems[itemClass].setEnabled( self.rootItems[itemClass].setEnabled(
self.theProject.projTree.checkRootUnique(itemClass) self.theProject.projTree.checkRootUnique(itemClass)
) )
return return
def closeHelp(self):
"""Close the process used for the Qt Assistant, if it is open.
"""
if self.assistProc is None:
return
if self.assistProc.state() == QProcess.Starting:
if self.assistProc.waitForStarted(10000):
self.assistProc.terminate()
else:
self.assistProc.kill()
elif self.assistProc.state() == QProcess.Running:
self.assistProc.terminate()
if not self.assistProc.waitForFinished(10000):
self.assistProc.kill()
return
## ##
# Update Menu on Settings Changed # Update Menu on Settings Changed
## ##
@@ -134,10 +165,26 @@ class GuiMainMenu(QMenuBar):
msgBox.aboutQt(self.theParent,"About Qt") msgBox.aboutQt(self.theParent,"About Qt")
return True return True
def _openHelp(self): def _openAssistant(self):
"""Open the documentation URL in the system's default browser. """Open the documentation in Qt Assistant.
""" """
QDesktopServices.openUrl(QUrl(nw.__docurl__)) if not self.mainConf.hasHelp:
self._openWebsite(nw.__docurl__)
return False
self.assistProc = QProcess(self)
self.assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
if not self.assistProc.waitForStarted(10000):
self._openWebsite(nw.__docurl__)
return False
return True
def _openWebsite(self, theUrl):
"""Open an URL in the system's default browser.
"""
QDesktopServices.openUrl(QUrl(theUrl))
return True return True
def _openIssue(self): def _openIssue(self):
@@ -258,7 +305,7 @@ class GuiMainMenu(QMenuBar):
# Project > Exit # Project > Exit
self.aExitNW = QAction("Exit", self) self.aExitNW = QAction("Exit", self)
self.aExitNW.setStatusTip("Exit %s" % nw.__package__) self.aExitNW.setStatusTip("Exit %s" % self.mainConf.appName)
self.aExitNW.setShortcut("Ctrl+Q") self.aExitNW.setShortcut("Ctrl+Q")
self.aExitNW.triggered.connect(self._menuExit) self.aExitNW.triggered.connect(self._menuExit)
self.projMenu.addAction(self.aExitNW) self.projMenu.addAction(self.aExitNW)
@@ -485,6 +532,37 @@ class GuiMainMenu(QMenuBar):
# Insert > Separator # Insert > Separator
self.insertMenu.addSeparator() self.insertMenu.addSeparator()
# Insert > Left Single Quote
self.aInsQuoteLS = QAction("Left Single Quote", self)
self.aInsQuoteLS.setStatusTip("Insert left single quote")
self.aInsQuoteLS.setShortcut("Ctrl+K, 1")
self.aInsQuoteLS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LS))
self.insertMenu.addAction(self.aInsQuoteLS)
# Insert > Right Single Quote
self.aInsQuoteRS = QAction("Right Single Quote", self)
self.aInsQuoteRS.setStatusTip("Insert right single quote")
self.aInsQuoteRS.setShortcut("Ctrl+K, 2")
self.aInsQuoteRS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RS))
self.insertMenu.addAction(self.aInsQuoteRS)
# Insert > Left Double Quote
self.aInsQuoteLD = QAction("Left Double Quote", self)
self.aInsQuoteLD.setStatusTip("Insert left double quote")
self.aInsQuoteLD.setShortcut("Ctrl+K, 3")
self.aInsQuoteLD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LD))
self.insertMenu.addAction(self.aInsQuoteLD)
# Insert > Right Double Quote
self.aInsQuoteRD = QAction("Right Double Quote", self)
self.aInsQuoteRD.setStatusTip("Insert right double quote")
self.aInsQuoteRD.setShortcut("Ctrl+K, 4")
self.aInsQuoteRD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RD))
self.insertMenu.addAction(self.aInsQuoteRD)
# Insert > Separator
self.insertMenu.addSeparator()
# Insert > Hard Line Break # Insert > Hard Line Break
self.aInsHardBreak = QAction("Hard Line Break", self) self.aInsHardBreak = QAction("Hard Line Break", self)
self.aInsHardBreak.setStatusTip("Insert a hard line break") self.aInsHardBreak.setStatusTip("Insert a hard line break")
@@ -773,8 +851,8 @@ class GuiMainMenu(QMenuBar):
self.helpMenu = self.addMenu("&Help") self.helpMenu = self.addMenu("&Help")
# Help > About # Help > About
self.aAboutNW = QAction("About %s" % nw.__package__, self) self.aAboutNW = QAction("About %s" % self.mainConf.appName, self)
self.aAboutNW.setStatusTip("About %s" % nw.__package__) self.aAboutNW.setStatusTip("About %s" % self.mainConf.appName)
self.aAboutNW.triggered.connect(self._showAbout) self.aAboutNW.triggered.connect(self._showAbout)
self.helpMenu.addAction(self.aAboutNW) self.helpMenu.addAction(self.aAboutNW)
@@ -787,17 +865,33 @@ class GuiMainMenu(QMenuBar):
# Help > Separator # Help > Separator
self.helpMenu.addSeparator() self.helpMenu.addSeparator()
# Document > Preview # Document > Documentation
self.aHelp = QAction("Online Documentation", self) if self.mainConf.hasHelp and self.mainConf.hasAssistant:
self.aHelp.setStatusTip("View online documentation") self.aHelpLoc = QAction("Documentation (Local)", self)
self.aHelp.setShortcut("F1") self.aHelpLoc.setStatusTip("View local documentation with Qt Assistant")
self.aHelp.triggered.connect(self._openHelp) self.aHelpLoc.triggered.connect(self._openAssistant)
self.helpMenu.addAction(self.aHelp) self.aHelpLoc.setShortcut("F1")
self.helpMenu.addAction(self.aHelpLoc)
self.aHelpWeb = QAction("Documentation (Online)", self)
self.aHelpWeb.setStatusTip("View online documentation")
self.aHelpWeb.triggered.connect(lambda: self._openWebsite(nw.__docurl__))
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
self.aHelpWeb.setShortcut("Shift+F1")
else:
self.aHelpWeb.setShortcuts(["F1","Shift+F1"])
self.helpMenu.addAction(self.aHelpWeb)
# Document > Go to Website
self.aWebsite = QAction("Open the novelWriter Website", self)
self.aWebsite.setStatusTip("View the main website")
self.aWebsite.triggered.connect(lambda: self._openWebsite(nw.__url__))
self.helpMenu.addAction(self.aWebsite)
# Document > Report Issue # Document > Report Issue
self.aIssue = QAction("Report an Issue", self) self.aIssue = QAction("Report an Issue", self)
self.aIssue.setStatusTip("View online documentation") self.aIssue.setStatusTip("Report a bug or issue on GitHub")
self.aIssue.triggered.connect(self._openIssue) self.aIssue.triggered.connect(lambda: self._openWebsite(nw.__issuesurl__))
self.helpMenu.addAction(self.aIssue) self.helpMenu.addAction(self.aIssue)
return return
+7 -10
View File
@@ -108,7 +108,7 @@ class GuiPreferences(PagedDialog):
msgBox = QMessageBox() msgBox = QMessageBox()
msgBox.information( msgBox.information(
self, "Preferences", self, "Preferences",
"Some changes will not be applied until %s has been restarted." % nw.__package__ "Some changes will not be applied until novelWriter has been restarted."
) )
if validEntries: if validEntries:
@@ -154,7 +154,7 @@ class GuiConfigEditGeneralTab(QWidget):
self.mainForm.addRow( self.mainForm.addRow(
"Main GUI theme", "Main GUI theme",
self.selectTheme, self.selectTheme,
"Changing this requires restarting %s." % nw.__package__ "Changing this requires restarting novelWriter."
) )
## Select Icon Theme ## Select Icon Theme
@@ -170,7 +170,7 @@ class GuiConfigEditGeneralTab(QWidget):
self.mainForm.addRow( self.mainForm.addRow(
"Main icon theme", "Main icon theme",
self.selectIcons, self.selectIcons,
"Changing this requires restarting %s." % nw.__package__ "Changing this requires restarting novelWriter."
) )
## Dark Icons ## Dark Icons
@@ -193,7 +193,7 @@ class GuiConfigEditGeneralTab(QWidget):
self.mainForm.addRow( self.mainForm.addRow(
"Font family", "Font family",
self.guiFont, self.guiFont,
"Changing this requires restarting %s." % nw.__package__, "Changing this requires restarting novelWriter.",
theButton = self.fontButton theButton = self.fontButton
) )
@@ -206,7 +206,7 @@ class GuiConfigEditGeneralTab(QWidget):
self.mainForm.addRow( self.mainForm.addRow(
"Font size", "Font size",
self.guiFontSize, self.guiFontSize,
"Changing this requires restarting %s." % nw.__package__, "Changing this requires restarting novelWriter.",
theUnit = "pt" theUnit = "pt"
) )
@@ -625,13 +625,10 @@ class GuiConfigEditEditingTab(QWidget):
self.spellToolList = QComboBox(self) self.spellToolList = QComboBox(self)
self.spellToolList.addItem("Internal (difflib)", NWSpellCheck.SP_INTERNAL) self.spellToolList.addItem("Internal (difflib)", NWSpellCheck.SP_INTERNAL)
self.spellToolList.addItem("Spell Enchant (pyenchant)", NWSpellCheck.SP_ENCHANT) self.spellToolList.addItem("Spell Enchant (pyenchant)", NWSpellCheck.SP_ENCHANT)
# self.spellToolList.addItem("SymSpell (symspellpy)", NWSpellCheck.SP_SYMSPELL)
theModel = self.spellToolList.model() theModel = self.spellToolList.model()
idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT) idEnchant = self.spellToolList.findData(NWSpellCheck.SP_ENCHANT)
# idSymSpell = self.spellToolList.findData(NWSpellCheck.SP_SYMSPELL)
theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant) theModel.item(idEnchant).setEnabled(self.mainConf.hasEnchant)
# theModel.item(idSymSpell).setEnabled(self.mainConf.hasSymSpell)
self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool) self.spellToolList.currentIndexChanged.connect(self._doUpdateSpellTool)
toolIdx = self.spellToolList.findData(self.mainConf.spellTool) toolIdx = self.spellToolList.findData(self.mainConf.spellTool)
+18 -10
View File
@@ -56,6 +56,7 @@ class GuiMain(QMainWindow):
QMainWindow.__init__(self) QMainWindow.__init__(self)
logger.debug("Initialising GUI ...") logger.debug("Initialising GUI ...")
self.setObjectName("GuiMain")
self.mainConf = nw.CONFIG self.mainConf = nw.CONFIG
# Some runtime info useful for debugging # Some runtime info useful for debugging
@@ -220,7 +221,7 @@ class GuiMain(QMainWindow):
else: else:
self.manageProjects() self.manageProjects()
logger.debug("%s is ready ..." % nw.__package__) logger.debug("novelWriter is ready ...")
return return
@@ -384,13 +385,13 @@ class GuiMain(QMainWindow):
msgBox = QMessageBox() msgBox = QMessageBox()
msgRes = msgBox.warning( msgRes = msgBox.warning(
self, "Project Locked", ( self, "Project Locked", (
"The project is already open by another instance of %s, and is " "The project is already open by another instance of novelWriter, and "
"therefore locked. Override lock and continue anyway?<br><br>" "is therefore locked. Override lock and continue anyway?<br><br>"
"Note: If the program or the computer previously crashed, the lock " "Note: If the program or the computer previously crashed, the lock "
"can safely be overridden. If, however, another instance of %s has " "can safely be overridden. If, however, another instance of "
"the project open, overriding the lock may corrupt the project, and " "novelWriter has the project open, overriding the lock may corrupt "
"is not recommended.%s" "the project, and is not recommended.%s"
) % (nw.__package__, nw.__package__, lockDetails), ) % lockDetails,
QMessageBox.Yes | QMessageBox.No, QMessageBox.No QMessageBox.Yes | QMessageBox.No, QMessageBox.No
) )
if msgRes == QMessageBox.Yes: if msgRes == QMessageBox.Yes:
@@ -874,7 +875,7 @@ class GuiMain(QMainWindow):
if msgRes != QMessageBox.Yes: if msgRes != QMessageBox.Yes:
return False return False
logger.info("Exiting %s" % nw.__package__) logger.info("Exiting novelWriter")
if not self.isFocusMode: if not self.isFocusMode:
self.mainConf.setMainPanePos(self.splitMain.sizes()) self.mainConf.setMainPanePos(self.splitMain.sizes())
@@ -893,6 +894,7 @@ class GuiMain(QMainWindow):
self.mainConf.saveConfig() self.mainConf.saveConfig()
self.reportConfErr() self.reportConfErr()
self.mainMenu.closeHelp()
qApp.quit() qApp.quit()
@@ -1006,6 +1008,10 @@ class GuiMain(QMainWindow):
self.addAction(self.mainMenu.aInsENDash) self.addAction(self.mainMenu.aInsENDash)
self.addAction(self.mainMenu.aInsEMDash) self.addAction(self.mainMenu.aInsEMDash)
self.addAction(self.mainMenu.aInsEllipsis) self.addAction(self.mainMenu.aInsEllipsis)
self.addAction(self.mainMenu.aInsQuoteLS)
self.addAction(self.mainMenu.aInsQuoteRS)
self.addAction(self.mainMenu.aInsQuoteLD)
self.addAction(self.mainMenu.aInsQuoteRD)
self.addAction(self.mainMenu.aInsHardBreak) self.addAction(self.mainMenu.aInsHardBreak)
self.addAction(self.mainMenu.aInsNBSpace) self.addAction(self.mainMenu.aInsNBSpace)
self.addAction(self.mainMenu.aInsThinSpace) self.addAction(self.mainMenu.aInsThinSpace)
@@ -1030,12 +1036,14 @@ class GuiMain(QMainWindow):
self.addAction(self.mainMenu.aPreferences) self.addAction(self.mainMenu.aPreferences)
# Help # Help
self.addAction(self.mainMenu.aHelp) if self.mainConf.hasHelp and self.mainConf.hasAssistant:
self.addAction(self.mainMenu.aHelpLoc)
self.addAction(self.mainMenu.aHelpWeb)
return True return True
def _setWindowTitle(self, projName=None): def _setWindowTitle(self, projName=None):
winTitle = "%s" % nw.__package__ winTitle = self.mainConf.appName
if projName is not None: if projName is not None:
winTitle += " - %s" % projName winTitle += " - %s" % projName
self.setWindowTitle(winTitle) self.setWindowTitle(winTitle)
+3 -3
View File
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.10.0rc1" hexVersion="0x001000c1" fileVersion="1.2" timeStamp="2020-06-29 13:27:47"> <novelWriterXML appVersion="0.11.0" hexVersion="0x001100f0" fileVersion="1.2" timeStamp="2020-08-08 14:25:39">
<project> <project>
<name>Sample Project</name> <name>Sample Project</name>
<title>Sample Project</title> <title>Sample Project</title>
<author>Jane Smith</author> <author>Jane Smith</author>
<author>Jay Doh</author> <author>Jay Doh</author>
<saveCount>652</saveCount> <saveCount>663</saveCount>
<autoCount>122</autoCount> <autoCount>122</autoCount>
<editTime>32716</editTime> <editTime>32747</editTime>
</project> </project>
<settings> <settings>
<doBackup>False</doBackup> <doBackup>False</doBackup>
+94 -13
View File
@@ -1,19 +1,102 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import sys
import shutil
import subprocess
import setuptools import setuptools
from nw import __version__, __url__, __docurl__, __issuesurl__, __sourceurl__
##
# Build the Package
##
buildDocs = False
if "qthelp" in sys.argv:
buildDocs = True
sys.argv.remove("qthelp")
if buildDocs:
buildDir = os.path.join("docs", "build", "qthelp")
helpDir = os.path.join("nw", "assets", "help")
inFile = "novelWriter.qhcp"
outFile = "novelWriter.qhc"
datFile = "novelWriter.qch"
print("")
print("Building Documentation")
print("======================")
print("")
buildFail = False
try:
subprocess.call(["make","-C", "docs", "qthelp"])
except Exception as e:
print("Failed with error:")
print(str(e))
buildFail = True
try:
subprocess.call(["qhelpgenerator", os.path.join(buildDir, inFile)])
except Exception as e:
print("Failed with error:")
print(str(e))
buildFail = True
if not os.path.isdir(helpDir):
try:
os.mkdir(helpDir)
except Exception as e:
print("Failed with error:")
print(str(e))
buildFail = True
try:
if os.path.isfile(os.path.join(helpDir, outFile)):
os.unlink(os.path.join(helpDir, outFile))
if os.path.isfile(os.path.join(helpDir, datFile)):
os.unlink(os.path.join(helpDir, datFile))
os.rename(os.path.join(buildDir, outFile), os.path.join(helpDir, outFile))
os.rename(os.path.join(buildDir, datFile), os.path.join(helpDir, datFile))
except Exception as e:
print("Failed with error:")
print(str(e))
buildFail = True
print("")
if buildFail:
print("Documentation build: FAILED")
else:
print("Documentation build: OK")
print("")
if len(sys.argv) == 1:
# Nothing more to do
sys.exit(0)
##
# Build the Package
##
# Read content from files
with open("README.md", "r") as inFile: with open("README.md", "r") as inFile:
long_description = inFile.read() longDescription = inFile.read()
with open("requirements.txt", "r") as inFile:
pkgRequirements = inFile.read().strip().splitlines()
setuptools.setup( setuptools.setup(
name = "novelWriter", name = "novelWriter",
version = "0.10.1", version = __version__,
author = "Veronica Berglyd Olsen", author = "Veronica Berglyd Olsen",
author_email = "code@vkbo.net", author_email = "code@vkbo.net",
description = "A markdown-like document editor for writing novels", description = "A markdown-like document editor for writing novels",
long_description = long_description, long_description = longDescription,
long_description_content_type = "text/markdown", long_description_content_type = "text/markdown",
license = "GNU General Public License v3", license = "GNU General Public License v3",
url = "https://github.com/vkbo/novelWriter", url = __url__,
entry_points = { entry_points = {
"console_scripts" : ["novelWriter-cli=nw:main"], "console_scripts" : ["novelWriter-cli=nw:main"],
"gui_scripts" : ["novelWriter=nw:main"], "gui_scripts" : ["novelWriter=nw:main"],
@@ -22,26 +105,24 @@ setuptools.setup(
include_package_data = True, include_package_data = True,
package_data = {"": ["*.conf"]}, package_data = {"": ["*.conf"]},
project_urls = { project_urls = {
"Bug Tracker": "https://github.com/vkbo/novelWriter/issues", "Bug Tracker": __issuesurl__,
"Documentation": "https://novelwriter.readthedocs.io/", "Documentation": __docurl__,
"Source Code": "https://github.com/vkbo/novelWriter", "Source Code": __sourceurl__,
}, },
classifiers = [ classifiers = [
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Development Status :: 3 - Alpha", "Development Status :: 4 - Beta",
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Intended Audience :: End Users/Desktop", "Intended Audience :: End Users/Desktop",
"Natural Language :: English", "Natural Language :: English",
"Topic :: Text Editors", "Topic :: Text Editors",
], ],
python_requires = ">=3.6", python_requires = ">=3.6",
install_requires = [ install_requires = pkgRequirements,
"pyqt5>=5.2.1",
"lxml>=4.2.0",
"pyenchant>=3.0.0",
],
) )
+3 -1
View File
@@ -2,9 +2,11 @@
"""novelWriter Test Config """novelWriter Test Config
""" """
import pytest, shutil import sys, pytest, shutil
from os import path, mkdir from os import path, mkdir
sys.path.insert(1, path.abspath(path.join(path.dirname(__file__), path.pardir)))
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def nwTemp(): def nwTemp():
testDir = path.dirname(__file__) testDir = path.dirname(__file__)