Merge branch 'main' into dev
This commit is contained in:
@@ -9,7 +9,7 @@ jobs:
|
|||||||
- name: Python Setup
|
- name: Python Setup
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3
|
python-version: "3.11"
|
||||||
architecture: x64
|
architecture: x64
|
||||||
|
|
||||||
- name: Install Packages (apt)
|
- name: Install Packages (apt)
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
- name: Python Setup
|
- name: Python Setup
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3
|
python-version: "3.11"
|
||||||
architecture: x64
|
architecture: x64
|
||||||
|
|
||||||
- name: Install Packages (pip)
|
- name: Install Packages (pip)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
# Read the Docs configuration file
|
|
||||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
||||||
version: 2
|
|
||||||
sphinx:
|
|
||||||
configuration: docs/source/conf.py
|
|
||||||
formats:
|
|
||||||
- htmlzip
|
|
||||||
- pdf
|
|
||||||
python:
|
|
||||||
install:
|
|
||||||
- requirements: docs/source/requirements.txt
|
|
||||||
@@ -1,5 +1,62 @@
|
|||||||
# novelWriter Changelog
|
# novelWriter Changelog
|
||||||
|
|
||||||
|
## Version 2.1.1 [2023-11-05]
|
||||||
|
|
||||||
|
### Release Notes
|
||||||
|
|
||||||
|
This is a patch release that fixes a layout issue and internationalisation issues with the new
|
||||||
|
Manuscript Build tool. It also fixes a number of issues related to bugs in the underlying Qt
|
||||||
|
framework that affects drag and drop functionality in the project tree. These issues were mostly
|
||||||
|
only affecting Debian Linux package releases.
|
||||||
|
|
||||||
|
Other, minor issues related to updating the editor on colour theme change and project word list
|
||||||
|
changes have been fixed as well. See the full changelog for more details.
|
||||||
|
|
||||||
|
### Detailed Changelog
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
* Fix an issue with width of the last two columns on Selection page of the Build Settings dialog on
|
||||||
|
Windows. They were far too wide by default. Issue #1551. PR #1553.
|
||||||
|
* Fix an issue where a lot of string were not translated to the UI language in the new Manuscript
|
||||||
|
Build tool. Issue #1563. PR #1565.
|
||||||
|
* Fix an issue in the Document Viewer where it wouldn't scroll to a heading further down the page
|
||||||
|
when following a reference pointing to it. Issue #1566. PR #1568.
|
||||||
|
* Add back in checks for illegal drag and drop moves in the project tree. In 2.0, the logic here
|
||||||
|
was changed to set certain restrictions on the elements of the project tree itself, but there are
|
||||||
|
numerous bugs in the Qt framework related to drag and drop, so the checks are ignored on at least
|
||||||
|
Qt 5.15.8. In particular, it is possible to drop items on the root level, and it's possible to
|
||||||
|
move root items to other locations. Neither should be possible and will severely mess up the
|
||||||
|
project if done. Issue #1569. PR #1570.
|
||||||
|
* Add a custom autoscroll feature when dragging an item in the project tree to near the top or
|
||||||
|
bottom. This is actually a default feature of the tree widget in the Qt library, but this too is
|
||||||
|
broken in some versions of Qt 5.15.x. The default feature has been permanently disabled and
|
||||||
|
replaced by a custom written feature that behaves similarly. Issue #1561. PR #1571.
|
||||||
|
* Fix an issue where the editor document wasn't re-highlighted when the Syntax Theme for it was
|
||||||
|
changed. Issue #1535. PR #1573.
|
||||||
|
* Fix an issue where editing the Project Word List would not refresh the spell checking of the
|
||||||
|
editor. Issue #1559. PR #1573.
|
||||||
|
|
||||||
|
**Usability**
|
||||||
|
|
||||||
|
* Changed how the default UI language is selected. It used to default to the system locale, but
|
||||||
|
that is now changed to British English if the system local is not available in novelWriter. The
|
||||||
|
only real effects of this is that the dropdown box in Preferences now selects British English if
|
||||||
|
the system locale is not available rather than the first in the list (currently Deutch). The
|
||||||
|
second effect is that the language on buttons and other Qt components will match the rest of the
|
||||||
|
UI. Issue #1564. PR #1565.
|
||||||
|
* There is a bug in Qt on Wayland desktops where menus don't open in the correct location.
|
||||||
|
According to one Qt ticket, QTBUG-68636, this can be mitigated by ensuring all QMenu instances
|
||||||
|
have a parent set. This does not fix all issues, but it should help. The menus without a parent
|
||||||
|
set have now been updated. Issue #1536. PR #1572.
|
||||||
|
|
||||||
|
**Documentation**
|
||||||
|
|
||||||
|
* Fixed a number of spelling errors and typing mistakes in the documentation for 2.1. Contributed
|
||||||
|
by @nisemono-neko. PR #1567.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
## Version 2.1 [2023-10-17]
|
## Version 2.1 [2023-10-17]
|
||||||
|
|
||||||
### Release Notes
|
### Release Notes
|
||||||
|
|||||||
+2
-12
@@ -27,7 +27,7 @@ with open(initFile) as inFile:
|
|||||||
else:
|
else:
|
||||||
release = "unknown"
|
release = "unknown"
|
||||||
|
|
||||||
version = release.split("-")[0]
|
version = release.partition("-")[0]
|
||||||
|
|
||||||
# -- General Configuration ---------------------------------------------------
|
# -- General Configuration ---------------------------------------------------
|
||||||
|
|
||||||
@@ -67,11 +67,6 @@ html_sidebars = {
|
|||||||
"**": ["navbar-logo", "sidebar-title", "sbt-sidebar-nav"],
|
"**": ["navbar-logo", "sidebar-title", "sbt-sidebar-nav"],
|
||||||
}
|
}
|
||||||
|
|
||||||
# -- Options for HTMLHelp Output ---------------------------------------------
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = "novelWriterDoc"
|
|
||||||
|
|
||||||
# -- Options for LaTeX Output ------------------------------------------------
|
# -- Options for LaTeX Output ------------------------------------------------
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
@@ -84,11 +79,6 @@ latex_elements = {
|
|||||||
"figure_align": "htbp",
|
"figure_align": "htbp",
|
||||||
}
|
}
|
||||||
latex_logo = "_static/novelwriter-pdf.png"
|
latex_logo = "_static/novelwriter-pdf.png"
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title,
|
|
||||||
# author, documentclass [howto, manual, or own class]).
|
|
||||||
latex_documents = [(
|
latex_documents = [(
|
||||||
master_doc, "manual.tex", "User Guide",
|
master_doc, "manual.tex", "User Guide", author, "manual"
|
||||||
author, "manual"
|
|
||||||
)]
|
)]
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ set as ``name`` inside the file.
|
|||||||
Gustom GUI and Icons Theme
|
Gustom GUI and Icons Theme
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
A GUI theme ``.conf`` file consists of the follwing settings:
|
A GUI theme ``.conf`` file consists of the following settings:
|
||||||
|
|
||||||
.. code-block:: cfg
|
.. code-block:: cfg
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ In the Main section you must at least define the ``name`` and ``icontheme`` sett
|
|||||||
``typicons_light`` or ``typicons_dark``, or to an icon theme in your custom icons directory. The
|
``typicons_light`` or ``typicons_dark``, or to an icon theme in your custom icons directory. The
|
||||||
setting must match the icon theme's folder name.
|
setting must match the icon theme's folder name.
|
||||||
|
|
||||||
The Palette values correspond the the Qt enum values for QPalette::ColorRole, see the
|
The Palette values correspond to the Qt enum values for QPalette::ColorRole, see the
|
||||||
`Qt documentation <https://doc.qt.io/qt-5.15/qpalette.html#ColorRole-enum>`_ for more details. The
|
`Qt documentation <https://doc.qt.io/qt-5.15/qpalette.html#ColorRole-enum>`_ for more details. The
|
||||||
colour values are RGB numbers on the format ``r, g, b`` where each is an integer from to 255.
|
colour values are RGB numbers on the format ``r, g, b`` where each is an integer from to 255.
|
||||||
Omitted values are not loaded and will use default values.
|
Omitted values are not loaded and will use default values.
|
||||||
@@ -167,5 +167,5 @@ A syntax theme ``.conf`` file consists of the follwing settings:
|
|||||||
modifier = 0, 0, 0
|
modifier = 0, 0, 0
|
||||||
|
|
||||||
In the Main section, you must define at least the ``name`` setting. The Syntax colour values are
|
In the Main section, you must define at least the ``name`` setting. The Syntax colour values are
|
||||||
RGB numbers on the format ``r, g, b`` where each is an integer from to 255. Omitted values default
|
RGB numbers of the format ``r, g, b`` where each is an integer from to 255. Omitted values default
|
||||||
to black, except ``background`` which defaults to white,
|
to black, except ``background`` which defaults to white,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Glossary
|
|||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
Reference
|
Reference
|
||||||
A references is one of a set of :term:`keywords<keyword>` that can be used to link to a
|
A reference is one of a set of :term:`keywords<keyword>` that can be used to link to a
|
||||||
:term:`tag` in another document. The reference keywords are specific to the different
|
:term:`tag` in another document. The reference keywords are specific to the different
|
||||||
:term:`root folder` types. A full overview is available in the :ref:`a_references` chapter.
|
:term:`root folder` types. A full overview is available in the :ref:`a_references` chapter.
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ Glossary
|
|||||||
|
|
||||||
Keyword
|
Keyword
|
||||||
A keyword in novelWriter is a special command you put in the text of your documents. They are
|
A keyword in novelWriter is a special command you put in the text of your documents. They are
|
||||||
not standard Markdown, but is used in novelWriter to add information that is interpreted by
|
not standard Markdown, but are used in novelWriter to add information that is interpreted by
|
||||||
the application. For instance, keywords are used for :term:`tags<tag>` and
|
the application. For instance, keywords are used for :term:`tags<tag>` and
|
||||||
:term:`references<reference>`.
|
:term:`references<reference>`.
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Key Features
|
|||||||
At its core, novelWriter is a multi-document plain text editor. It uses a markup syntax inspired by
|
At its core, novelWriter is a multi-document plain text editor. It uses a markup syntax inspired by
|
||||||
Markdown_ to apply simple formatting to the text. It is designed for writing fiction, so the
|
Markdown_ to apply simple formatting to the text. It is designed for writing fiction, so the
|
||||||
formatting features available are limited to those relevant for this purpose. It is *not* suitable
|
formatting features available are limited to those relevant for this purpose. It is *not* suitable
|
||||||
for technical writing, and it is *not* a full-feature Markdown editor.
|
for technical writing, and it is *not* a full-featured Markdown editor.
|
||||||
|
|
||||||
Your novel project is organised as a collection of separate plain text documents instead of a
|
Your novel project is organised as a collection of separate plain text documents instead of a
|
||||||
single, large document. The idea here is to make it easier to reorganise your project structure
|
single, large document. The idea here is to make it easier to reorganise your project structure
|
||||||
@@ -32,11 +32,11 @@ Below are some key features of novelWriter.
|
|||||||
formatting text. Formatting is therefore limited to a small set of formatting tags for simple
|
formatting text. Formatting is therefore limited to a small set of formatting tags for simple
|
||||||
things like text emphasis and paragraph alignment. When you really want to focus on just
|
things like text emphasis and paragraph alignment. When you really want to focus on just
|
||||||
writing, you can switch the editor into :guilabel:`Focus Mode` where only the text editor panel
|
writing, you can switch the editor into :guilabel:`Focus Mode` where only the text editor panel
|
||||||
itself is vissible, and the project structure view is hidden away.
|
itself is visible, and the project structure view is hidden away.
|
||||||
|
|
||||||
**Keep an eye on your notes**
|
**Keep an eye on your notes**
|
||||||
The main window can optionally show a document viewer to the right of the editor. This view
|
The main window can optionally show a document viewer to the right of the editor. This view
|
||||||
panel is intended for displaying another scene document, you character notes, plot notes, or any
|
panel is intended for displaying another scene document, your character notes, plot notes, or any
|
||||||
other document you may need to reference while writing. It is not intended as a preview panel
|
other document you may need to reference while writing. It is not intended as a preview panel
|
||||||
for the document you're editing, but if you wish, you can also use it for this purpose.
|
for the document you're editing, but if you wish, you can also use it for this purpose.
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ novelWriter is built as a cross-platform application using `Python 3 <https://ww
|
|||||||
the programming language, and `Qt 5 <https://www.qt.io/>`_ for the user interface.
|
the programming language, and `Qt 5 <https://www.qt.io/>`_ for the user interface.
|
||||||
|
|
||||||
novelWriter is built for Linux first, and this is where it works best. However, it also runs fine
|
novelWriter is built for Linux first, and this is where it works best. However, it also runs fine
|
||||||
on Windows and MacOS due to the cross-platform framework it's build on. The author of the
|
on Windows and MacOS due to the cross-platform framework it's built on. The author of the
|
||||||
application doesn't own a Mac, so on-going Mac support is dependant on user feedback and user
|
application doesn't own a Mac, so on-going Mac support is dependent on user feedback and user
|
||||||
contributions.
|
contributions.
|
||||||
|
|
||||||
Spell checking in novelWriter is provided by a third party library called
|
Spell checking in novelWriter is provided by a third party library called
|
||||||
@@ -36,7 +36,7 @@ read on.
|
|||||||
This chapter explains the basics of how the application works and what it can and cannot do.
|
This chapter explains the basics of how the application works and what it can and cannot do.
|
||||||
|
|
||||||
:ref:`a_ui_project` – Recommended Reading
|
:ref:`a_ui_project` – Recommended Reading
|
||||||
This chapter will give you a more detailed explanation of how you the user interface components
|
This chapter will give you a more detailed explanation of how you can use the user interface components
|
||||||
to organise and view your project work.
|
to organise and view your project work.
|
||||||
|
|
||||||
:ref:`a_ui_writing` – Recommended Reading
|
:ref:`a_ui_writing` – Recommended Reading
|
||||||
@@ -68,7 +68,7 @@ read on.
|
|||||||
Organising Your Projects
|
Organising Your Projects
|
||||||
========================
|
========================
|
||||||
|
|
||||||
In addition to manage a collection of plain text files, novelWriter can interpret and map the
|
In addition to managing a collection of plain text files, novelWriter can interpret and map the
|
||||||
structure of your novel and show you additional information about its flow and content. In order
|
structure of your novel and show you additional information about its flow and content. In order
|
||||||
to take advantage of these features, you must structure your text in a specific way and add some
|
to take advantage of these features, you must structure your text in a specific way and add some
|
||||||
meta data for it to extract.
|
meta data for it to extract.
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ much all collected meta data is available here in different columns.
|
|||||||
|
|
||||||
You can select which novel root folder to display from the dropdown box, and you can select which
|
You can select which novel root folder to display from the dropdown box, and you can select which
|
||||||
columns to show or hide from the menu button. You can also rearrange the columns by drag and drop.
|
columns to show or hide from the menu button. You can also rearrange the columns by drag and drop.
|
||||||
The app will remember you column order and size between sessions, and for each individual project.
|
The app will remember your column order and size between sessions, and for each individual project.
|
||||||
|
|
||||||
|
|
||||||
Colour Themes
|
Colour Themes
|
||||||
@@ -220,7 +220,7 @@ makes it easier to track them with version control software.
|
|||||||
Drive is mapped on Linux Gnome desktops using gvfs/gio.
|
Drive is mapped on Linux Gnome desktops using gvfs/gio.
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
You should not add additional files to the project folder yourself. Nor should you,a s a rule,
|
You should not add additional files to the project folder yourself. Nor should you, as a rule,
|
||||||
manually edit files within it. If you really must manually edit the text files, e.g. with some
|
manually edit files within it. If you really must manually edit the text files, e.g. with some
|
||||||
automated task you want to perform, you need to rebuild the :term:`Project Index` when you open
|
automated task you want to perform, you need to rebuild the :term:`Project Index` when you open
|
||||||
the project again.
|
the project again.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ have a distinct colour, and the references themselves will get a colour if they
|
|||||||
references will get a squiggly error line underneath. The same applies to duplicate tags.
|
references will get a squiggly error line underneath. The same applies to duplicate tags.
|
||||||
|
|
||||||
There are a number of syntax highlighter colour themes available, both for light and dark GUIs. You
|
There are a number of syntax highlighter colour themes available, both for light and dark GUIs. You
|
||||||
can select them for :guilabel:`Preferences`.
|
can select them from :guilabel:`Preferences`.
|
||||||
|
|
||||||
|
|
||||||
.. _a_fmt_head:
|
.. _a_fmt_head:
|
||||||
@@ -72,7 +72,7 @@ For headers level one and two, adding a ``!`` modifies the behaviour of the head
|
|||||||
``#! Title Text``
|
``#! Title Text``
|
||||||
This tells the build tool that the level one heading is intended to be used for the novel's
|
This tells the build tool that the level one heading is intended to be used for the novel's
|
||||||
main title, like for instance on the front page. When building the manuscript, this will use a
|
main title, like for instance on the front page. When building the manuscript, this will use a
|
||||||
different styling and will exclude the title from for instance a Table of Contents in Libre
|
different styling and will exclude the title from, for instance, a Table of Contents in Libre
|
||||||
Office.
|
Office.
|
||||||
|
|
||||||
``##! Title Text``
|
``##! Title Text``
|
||||||
@@ -170,7 +170,7 @@ in a special manner and will show up in the :ref:`a_ui_outline` in a dedicated c
|
|||||||
indicate this by altering the colour of the word.
|
indicate this by altering the colour of the word.
|
||||||
|
|
||||||
``% text...``
|
``% text...``
|
||||||
This is a comment. The text is not renderred by default (this can be overridden), seen in the
|
This is a comment. The text is not rendered by default (this can be overridden), seen in the
|
||||||
document viewer, or counted towards word counts.
|
document viewer, or counted towards word counts.
|
||||||
|
|
||||||
``% Synopsis: text...``
|
``% Synopsis: text...``
|
||||||
@@ -196,10 +196,10 @@ Tags use the command ``@tag:`` to define a tag. The tag can be set once per sect
|
|||||||
heading. Setting it multiple times under the same heading will just override the previous setting.
|
heading. Setting it multiple times under the same heading will just override the previous setting.
|
||||||
|
|
||||||
``@tag: value``
|
``@tag: value``
|
||||||
A tag commad followed by the tag value, like for instance the name of a character.
|
A tag command followed by the tag value, like for instance the name of a character.
|
||||||
|
|
||||||
References can be set anywhere within a section, and are collected according to their category.
|
References can be set anywhere within a section, and are collected according to their category.
|
||||||
References are on the form:
|
References are in the form:
|
||||||
|
|
||||||
``@keyword: value``
|
``@keyword: value``
|
||||||
A reference keyword followed by a value, or a comma separated list of values.
|
A reference keyword followed by a value, or a comma separated list of values.
|
||||||
@@ -236,7 +236,7 @@ Examples:
|
|||||||
"``>> Centred text <<``", "The text paragraph is centred."
|
"``>> Centred text <<``", "The text paragraph is centred."
|
||||||
"``> Left indented text``", "The text has an increased left margin."
|
"``> Left indented text``", "The text has an increased left margin."
|
||||||
"``Right indented text <``", "The text has an increased right margin."
|
"``Right indented text <``", "The text has an increased right margin."
|
||||||
"``> Left/right indented text <``", "The text has an both margins increased."
|
"``> Left/right indented text <``", "The text has both margins increased."
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The text editor will not show the alignment and indentation live. But the viewer will show them
|
The text editor will not show the alignment and indentation live. But the viewer will show them
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Keyboard Shortcuts
|
Keyboard Shortcuts
|
||||||
******************
|
******************
|
||||||
|
|
||||||
Most features in novelWriter are available as keyboard shortcuts. This is a reference list if these
|
Most features in novelWriter are available as keyboard shortcuts. This is a reference list of those
|
||||||
shortcuts. Most of them are also listed in the application's user interface.
|
shortcuts. Most of them are also listed in the application's user interface.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ instance `en_GB` for British English.
|
|||||||
You will need the translation tool Qt 5 Linguist on your system.
|
You will need the translation tool Qt 5 Linguist on your system.
|
||||||
|
|
||||||
For Ubuntu/Debian, run:
|
For Ubuntu/Debian, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install qttools5-dev-tools
|
sudo apt install qttools5-dev-tools
|
||||||
```
|
```
|
||||||
@@ -75,6 +76,7 @@ The `i18n` folder at the root of the repository contains the `nw_XX.ts` translat
|
|||||||
|
|
||||||
Whether to add a new language to the translation framework, or to update the file against the
|
Whether to add a new language to the translation framework, or to update the file against the
|
||||||
current source code, you must first run the `qtlupdate` command:
|
current source code, you must first run the `qtlupdate` command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 pkgutils.py qtlupdate i18n/nw_XX.ts
|
python3 pkgutils.py qtlupdate i18n/nw_XX.ts
|
||||||
```
|
```
|
||||||
@@ -86,6 +88,10 @@ be a valid ISO language code, otherwise novelWriter will not accept the file.
|
|||||||
For instance, the French translation uses the language code `fr_FR`, so its translation file will
|
For instance, the French translation uses the language code `fr_FR`, so its translation file will
|
||||||
be `nw_fr_FR.ts`
|
be `nw_fr_FR.ts`
|
||||||
|
|
||||||
|
Note: The `qtlupdate` command needs the `lupdate` tool provided by PyQt6, which uses the latest
|
||||||
|
TS file format. The tool in PyQt5 generates an older file format. On Debian/Ubuntu it is provided
|
||||||
|
by the package `pyqt6-dev-tools`.
|
||||||
|
|
||||||
|
|
||||||
### Edit the Translation File in Qt Linguist
|
### Edit the Translation File in Qt Linguist
|
||||||
|
|
||||||
@@ -100,6 +106,7 @@ Please select "English" and "United Kingdom" as the _source_ language if prompte
|
|||||||
The application does not use `.ts` files directly. The `novelwriter/assets/i18n/nw_XX.qm` files are
|
The application does not use `.ts` files directly. The `novelwriter/assets/i18n/nw_XX.qm` files are
|
||||||
the actual files used to translate the GUI into another language other than the default British
|
the actual files used to translate the GUI into another language other than the default British
|
||||||
English. These files are not generated by default, but they can be built with:
|
English. These files are not generated by default, but they can be built with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 pkgutils.py qtlrelease
|
python3 pkgutils.py qtlrelease
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
<h2>Release Notes for 2.1</h2>
|
<h2>Release Notes for 2.1</h2>
|
||||||
<p><i>Released on 17 October 2023</i></p>
|
<p><i>Released on 17 October 2023</i></p>
|
||||||
|
|
||||||
|
<p>Scroll down for <a href="#patch">Patch Notes</a></p>
|
||||||
|
|
||||||
<p>The primary focus of this release has been a complete redesign of the Build Tool, that is, the
|
<p>The primary focus of this release has been a complete redesign of the Build Tool, that is, the
|
||||||
tool that assembles your project into a manuscript document. The new tool, called the "Manuscript
|
tool that assembles your project into a manuscript document. The new tool, called the "Manuscript
|
||||||
Build Tool" allows you to define multiple build definitions for your project. The build definitions
|
Build Tool" allows you to define multiple build definitions for your project. The build definitions
|
||||||
@@ -30,5 +32,16 @@ a full list of changes, see the detailed changelogs.</p>
|
|||||||
|
|
||||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||||
|
|
||||||
|
<a name="patch"></a><h2>Patch Notes</h2>
|
||||||
|
|
||||||
|
<h3>Patch 2.2.1 – 5 November 2023</h3>
|
||||||
|
|
||||||
|
<p>This is a patch release that fixes a layout issue and internationalisation issues with the new
|
||||||
|
Manuscript Build tool. It also fixes a number of issues related to bugs in the underlying Qt
|
||||||
|
framework that affects drag and drop functionality in the project tree. These issues were mostly
|
||||||
|
only affecting Debian Linux package releases.</p>
|
||||||
|
<p>Other, minor issues related to updating the editor on colour theme change and project word list
|
||||||
|
changes have been fixed as well. See the full changelog for more details.</p>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -81,10 +81,12 @@ class Config:
|
|||||||
|
|
||||||
# Localisation
|
# Localisation
|
||||||
# Note that these paths must be strings
|
# Note that these paths must be strings
|
||||||
self._qLocale = QLocale.system()
|
self._nwLangPath = self._appPath / "assets" / "i18n"
|
||||||
self._qtTrans = {}
|
|
||||||
self._qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
|
self._qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
|
||||||
self._nwLangPath = str(self._appPath / "assets" / "i18n")
|
|
||||||
|
wantedLocale = self._nwLangPath / f"nw_{QLocale.system().name()}.qm"
|
||||||
|
self._qLocale = QLocale.system() if wantedLocale.exists() else QLocale("en_GB")
|
||||||
|
self._qtTrans = {}
|
||||||
|
|
||||||
# PDF Manual
|
# PDF Manual
|
||||||
pdfDocs = self._appPath / "assets" / "manual.pdf"
|
pdfDocs = self._appPath / "assets" / "manual.pdf"
|
||||||
@@ -425,7 +427,7 @@ class Config:
|
|||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
for qmFile in Path(self._nwLangPath).iterdir():
|
for qmFile in self._nwLangPath.iterdir():
|
||||||
qmName = qmFile.name
|
qmName = qmFile.name
|
||||||
if not (qmFile.is_file() and qmName.startswith(fPre) and qmName.endswith(fExt)):
|
if not (qmFile.is_file() and qmName.startswith(fPre) and qmName.endswith(fExt)):
|
||||||
continue
|
continue
|
||||||
@@ -493,8 +495,8 @@ class Config:
|
|||||||
self._qtTrans = {}
|
self._qtTrans = {}
|
||||||
|
|
||||||
langList = [
|
langList = [
|
||||||
(self._qtLangPath, "qtbase"), # Qt 5.x
|
(self._qtLangPath, "qtbase"), # Qt 5.x
|
||||||
(self._nwLangPath, "nw"), # novelWriter
|
(str(self._nwLangPath), "nw"), # novelWriter
|
||||||
]
|
]
|
||||||
for lngPath, lngBase in langList:
|
for lngPath, lngBase in langList:
|
||||||
for lngCode in self._qLocale.uiLanguages():
|
for lngCode in self._qLocale.uiLanguages():
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from enum import Enum
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtCore import QT_TRANSLATE_NOOP
|
from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.enum import nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
@@ -209,7 +209,7 @@ class BuildSettings:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def getLabel(key: str) -> str:
|
def getLabel(key: str) -> str:
|
||||||
"""Extract the GUI label for a specific setting."""
|
"""Extract the GUI label for a specific setting."""
|
||||||
return SETTINGS_LABELS.get(key, "ERROR")
|
return QCoreApplication.translate("Builds", SETTINGS_LABELS.get(key, "ERROR"))
|
||||||
|
|
||||||
def getStr(self, key: str) -> str:
|
def getStr(self, key: str) -> str:
|
||||||
"""Type safe value access for strings."""
|
"""Type safe value access for strings."""
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class ToHtml(Tokenizer):
|
|||||||
pStyle = None
|
pStyle = None
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
for tType, tLine, tText, tFormat, tStyle in self._tokens:
|
for tType, nHead, tText, tFormat, tStyle in self._tokens:
|
||||||
|
|
||||||
# Replace < and > with HTML entities
|
# Replace < and > with HTML entities
|
||||||
if tFormat:
|
if tFormat:
|
||||||
@@ -223,7 +223,7 @@ class ToHtml(Tokenizer):
|
|||||||
hStyle = ""
|
hStyle = ""
|
||||||
|
|
||||||
if self._linkHeaders:
|
if self._linkHeaders:
|
||||||
aNm = f"<a name='T{tLine:06d}'></a>"
|
aNm = f"<a name='T{nHead:04d}'></a>"
|
||||||
else:
|
else:
|
||||||
aNm = ""
|
aNm = ""
|
||||||
|
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ class Tokenizer(ABC):
|
|||||||
The format of the token list is an entry with a five-tuple for
|
The format of the token list is an entry with a five-tuple for
|
||||||
each line in the file. The tuple is as follows:
|
each line in the file. The tuple is as follows:
|
||||||
1: The type of the block, self.T_*
|
1: The type of the block, self.T_*
|
||||||
2: The line in the file where this block occurred
|
2: The header number under which the text is placed
|
||||||
3: The text content of the block, without leading tags
|
3: The text content of the block, without leading tags
|
||||||
4: The internal formatting map of the text, self.FMT_*
|
4: The internal formatting map of the text, self.FMT_*
|
||||||
5: The style of the block, self.A_*
|
5: The style of the block, self.A_*
|
||||||
@@ -396,16 +396,15 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
self._tokens = []
|
self._tokens = []
|
||||||
tmpMarkdown = []
|
tmpMarkdown = []
|
||||||
nLine = 0
|
nHead = 0
|
||||||
breakNext = False
|
breakNext = False
|
||||||
for aLine in self._text.splitlines():
|
for aLine in self._text.splitlines():
|
||||||
nLine += 1
|
|
||||||
sLine = aLine.strip()
|
sLine = aLine.strip()
|
||||||
|
|
||||||
# Check for blank lines
|
# Check for blank lines
|
||||||
if len(sLine) == 0:
|
if len(sLine) == 0:
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_EMPTY, nLine, "", None, self.A_NONE
|
self.T_EMPTY, nHead, "", None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("\n")
|
tmpMarkdown.append("\n")
|
||||||
@@ -430,7 +429,7 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
elif sLine == "[VSPACE]":
|
elif sLine == "[VSPACE]":
|
||||||
self._tokens.append(
|
self._tokens.append(
|
||||||
(self.T_SKIP, nLine, "", None, sAlign)
|
(self.T_SKIP, nHead, "", None, sAlign)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -438,11 +437,11 @@ class Tokenizer(ABC):
|
|||||||
nSkip = checkInt(sLine[8:-1], 0)
|
nSkip = checkInt(sLine[8:-1], 0)
|
||||||
if nSkip >= 1:
|
if nSkip >= 1:
|
||||||
self._tokens.append(
|
self._tokens.append(
|
||||||
(self.T_SKIP, nLine, "", None, sAlign)
|
(self.T_SKIP, nHead, "", None, sAlign)
|
||||||
)
|
)
|
||||||
if nSkip > 1:
|
if nSkip > 1:
|
||||||
self._tokens += (nSkip - 1) * [
|
self._tokens += (nSkip - 1) * [
|
||||||
(self.T_SKIP, nLine, "", None, self.A_NONE)
|
(self.T_SKIP, nHead, "", None, self.A_NONE)
|
||||||
]
|
]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -451,20 +450,20 @@ class Tokenizer(ABC):
|
|||||||
synTag = cLine[:9].lower()
|
synTag = cLine[:9].lower()
|
||||||
if synTag == "synopsis:":
|
if synTag == "synopsis:":
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign
|
self.T_SYNOPSIS, nHead, cLine[9:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._doSynopsis and self._keepMarkdown:
|
if self._doSynopsis and self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
else:
|
else:
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign
|
self.T_COMMENT, nHead, aLine[1:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._doComments and self._keepMarkdown:
|
if self._doComments and self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[0] == "@":
|
elif aLine[0] == "@":
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign
|
self.T_KEYWORD, nHead, aLine[1:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._doKeywords and self._keepMarkdown:
|
if self._doKeywords and self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
@@ -474,8 +473,9 @@ class Tokenizer(ABC):
|
|||||||
sAlign |= self.A_CENTRE
|
sAlign |= self.A_CENTRE
|
||||||
sAlign |= self.A_PBB
|
sAlign |= self.A_PBB
|
||||||
|
|
||||||
|
nHead += 1
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign
|
self.T_HEAD1, nHead, aLine[2:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
@@ -484,39 +484,44 @@ class Tokenizer(ABC):
|
|||||||
if self._isNovel:
|
if self._isNovel:
|
||||||
sAlign |= self.A_PBB
|
sAlign |= self.A_PBB
|
||||||
|
|
||||||
|
nHead += 1
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign
|
self.T_HEAD2, nHead, aLine[3:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:4] == "### ":
|
elif aLine[:4] == "### ":
|
||||||
|
nHead += 1
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign
|
self.T_HEAD3, nHead, aLine[4:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:5] == "#### ":
|
elif aLine[:5] == "#### ":
|
||||||
|
nHead += 1
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign
|
self.T_HEAD4, nHead, aLine[5:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:3] == "#! ":
|
elif aLine[:3] == "#! ":
|
||||||
|
nHead += 1
|
||||||
if self._isNovel:
|
if self._isNovel:
|
||||||
tStyle = self.T_TITLE
|
tStyle = self.T_TITLE
|
||||||
else:
|
else:
|
||||||
tStyle = self.T_HEAD1
|
tStyle = self.T_HEAD1
|
||||||
|
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
tStyle, nHead, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:4] == "##! ":
|
elif aLine[:4] == "##! ":
|
||||||
|
nHead += 1
|
||||||
if self._isNovel:
|
if self._isNovel:
|
||||||
tStyle = self.T_UNNUM
|
tStyle = self.T_UNNUM
|
||||||
sAlign |= self.A_PBB
|
sAlign |= self.A_PBB
|
||||||
@@ -524,7 +529,7 @@ class Tokenizer(ABC):
|
|||||||
tStyle = self.T_HEAD2
|
tStyle = self.T_HEAD2
|
||||||
|
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
tStyle, nLine, aLine[4:].strip(), None, sAlign
|
tStyle, nHead, aLine[4:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
@@ -581,7 +586,7 @@ class Tokenizer(ABC):
|
|||||||
# sorted by position
|
# sorted by position
|
||||||
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_TEXT, nLine, aLine, fmtPos, sAlign
|
self.T_TEXT, nHead, aLine, fmtPos, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
@@ -600,7 +605,7 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
# Always add an empty line at the end of the file
|
# Always add an empty line at the end of the file
|
||||||
self._tokens.append((
|
self._tokens.append((
|
||||||
self.T_EMPTY, nLine, "", None, self.A_NONE
|
self.T_EMPTY, nHead, "", None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("\n")
|
tmpMarkdown.append("\n")
|
||||||
|
|||||||
@@ -176,6 +176,8 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
for lang, langName in theLangs:
|
for lang, langName in theLangs:
|
||||||
self.guiLocale.addItem(langName, lang)
|
self.guiLocale.addItem(langName, lang)
|
||||||
langIdx = self.guiLocale.findData(CONFIG.guiLocale)
|
langIdx = self.guiLocale.findData(CONFIG.guiLocale)
|
||||||
|
if langIdx < 0:
|
||||||
|
langIdx = self.guiLocale.findData("en_GB")
|
||||||
if langIdx != -1:
|
if langIdx != -1:
|
||||||
self.guiLocale.setCurrentIndex(langIdx)
|
self.guiLocale.setCurrentIndex(langIdx)
|
||||||
|
|
||||||
|
|||||||
@@ -339,6 +339,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self.clearEditor()
|
self.clearEditor()
|
||||||
else:
|
else:
|
||||||
self.redrawText()
|
self.redrawText()
|
||||||
|
if not self._bigDoc:
|
||||||
|
self.highLight.rehighlight()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -980,7 +982,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
uCursor = self.textCursor()
|
uCursor = self.textCursor()
|
||||||
pCursor = self.cursorForPosition(pos)
|
pCursor = self.cursorForPosition(pos)
|
||||||
|
|
||||||
ctxMenu = QMenu()
|
ctxMenu = QMenu(self)
|
||||||
|
|
||||||
# Follow
|
# Follow
|
||||||
if self._followTag(cursor=pCursor, loadTag=False):
|
if self._followTag(cursor=pCursor, loadTag=False):
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
userCursor = self.textCursor()
|
userCursor = self.textCursor()
|
||||||
userSelection = userCursor.hasSelection()
|
userSelection = userCursor.hasSelection()
|
||||||
|
|
||||||
mnuContext = QMenu()
|
mnuContext = QMenu(self)
|
||||||
|
|
||||||
# Cut, Copy and Paste
|
# Cut, Copy and Paste
|
||||||
# ===================
|
# ===================
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
self.tbRefresh.clicked.connect(self._refreshNovelTree)
|
self.tbRefresh.clicked.connect(self._refreshNovelTree)
|
||||||
|
|
||||||
# More Options Menu
|
# More Options Menu
|
||||||
self.mMore = QMenu()
|
self.mMore = QMenu(self)
|
||||||
|
|
||||||
self.mLastCol = self.mMore.addMenu(self.tr("Last Column"))
|
self.mLastCol = self.mMore.addMenu(self.tr("Last Column"))
|
||||||
self.gLastCol = QActionGroup(self.mMore)
|
self.gLastCol = QActionGroup(self.mMore)
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ from enum import Enum
|
|||||||
from time import time
|
from time import time
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtGui import QDropEvent, QMouseEvent, QPalette
|
from PyQt5.QtGui import QDragMoveEvent, QDropEvent, QMouseEvent, QPalette
|
||||||
from PyQt5.QtCore import QPoint, Qt, QSize, pyqtSignal, pyqtSlot
|
from PyQt5.QtCore import QPoint, QTimer, Qt, QSize, pyqtSignal, pyqtSlot
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAbstractItemView, QDialog, QFrame, QHBoxLayout, QHeaderView, QLabel,
|
QAbstractItemView, QDialog, QFrame, QHBoxLayout, QHeaderView, QLabel,
|
||||||
QMenu, QShortcut, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem,
|
QMenu, QShortcut, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem,
|
||||||
@@ -248,7 +248,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.viewLabel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
self.viewLabel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||||
|
|
||||||
# Quick Links
|
# Quick Links
|
||||||
self.mQuick = QMenu()
|
self.mQuick = QMenu(self)
|
||||||
|
|
||||||
self.tbQuick = QToolButton(self)
|
self.tbQuick = QToolButton(self)
|
||||||
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
|
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
|
||||||
@@ -269,7 +269,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
|
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
|
||||||
|
|
||||||
# Add Item Menu
|
# Add Item Menu
|
||||||
self.mAdd = QMenu()
|
self.mAdd = QMenu(self)
|
||||||
|
|
||||||
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
|
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
|
||||||
self.aAddEmpty.triggered.connect(
|
self.aAddEmpty.triggered.connect(
|
||||||
@@ -307,7 +307,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.tbAdd.setPopupMode(QToolButton.InstantPopup)
|
self.tbAdd.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
# More Options Menu
|
# More Options Menu
|
||||||
self.mMore = QMenu()
|
self.mMore = QMenu(self)
|
||||||
|
|
||||||
self.aExpand = self.mMore.addAction(self.tr("Expand All"))
|
self.aExpand = self.mMore.addAction(self.tr("Expand All"))
|
||||||
self.aExpand.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, True))
|
self.aExpand.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, True))
|
||||||
@@ -507,8 +507,15 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
# Allow Move by Drag & Drop
|
# Allow Move by Drag & Drop
|
||||||
self.setDragEnabled(True)
|
self.setDragEnabled(True)
|
||||||
self.setDragDropMode(QAbstractItemView.InternalMove)
|
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||||
|
self.setDropIndicatorShown(True)
|
||||||
|
|
||||||
|
# Disable built-in autoscroll as it isn't working in some Qt
|
||||||
|
# releases (see #1561) and instead use our own implementation
|
||||||
|
self.setAutoScroll(False)
|
||||||
|
|
||||||
# But don't allow drop on root level
|
# But don't allow drop on root level
|
||||||
|
# Due to a bug, this stops working somewhere between Qt 5.15.3
|
||||||
|
# and 5.15.8, so this is also blocked in dropEvent (see #1569)
|
||||||
trRoot = self.invisibleRootItem()
|
trRoot = self.invisibleRootItem()
|
||||||
trRoot.setFlags(trRoot.flags() ^ Qt.ItemIsDropEnabled)
|
trRoot.setFlags(trRoot.flags() ^ Qt.ItemIsDropEnabled)
|
||||||
|
|
||||||
@@ -524,6 +531,13 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||||
self.itemSelectionChanged.connect(self._treeSelectionChange)
|
self.itemSelectionChanged.connect(self._treeSelectionChange)
|
||||||
|
|
||||||
|
# Autoscroll
|
||||||
|
self._scrollMargin = SHARED.theme.baseIconSize
|
||||||
|
self._scrollDirection = 0
|
||||||
|
self._scrollTimer = QTimer()
|
||||||
|
self._scrollTimer.timeout.connect(self._doAutoScroll)
|
||||||
|
self._scrollTimer.setInterval(250)
|
||||||
|
|
||||||
# Set custom settings
|
# Set custom settings
|
||||||
self.initSettings()
|
self.initSettings()
|
||||||
|
|
||||||
@@ -1196,7 +1210,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
logger.debug("No item found")
|
logger.debug("No item found")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ctxMenu = QMenu()
|
ctxMenu = QMenu(self)
|
||||||
|
|
||||||
# Trash Folder
|
# Trash Folder
|
||||||
# ============
|
# ============
|
||||||
@@ -1343,6 +1357,17 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _doAutoScroll(self) -> None:
|
||||||
|
"""Scroll one item up or down based on direction value."""
|
||||||
|
if self._scrollDirection == -1:
|
||||||
|
self.scrollToItem(self.itemAbove(self.itemAt(1, 1)))
|
||||||
|
elif self._scrollDirection == 1:
|
||||||
|
self.scrollToItem(self.itemBelow(self.itemAt(1, self.height() - 1)))
|
||||||
|
self._scrollDirection = 0
|
||||||
|
self._scrollTimer.stop()
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Events
|
# Events
|
||||||
##
|
##
|
||||||
@@ -1374,14 +1399,36 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def dragMoveEvent(self, event: QDragMoveEvent) -> None:
|
||||||
|
"""Capture the drag move event to enable edge autoscroll."""
|
||||||
|
y = event.pos().y()
|
||||||
|
if y < self._scrollMargin:
|
||||||
|
if not self._scrollTimer.isActive():
|
||||||
|
self._scrollDirection = -1
|
||||||
|
self._scrollTimer.start()
|
||||||
|
elif y > self.height() - self._scrollMargin:
|
||||||
|
if not self._scrollTimer.isActive():
|
||||||
|
self._scrollDirection = 1
|
||||||
|
self._scrollTimer.start()
|
||||||
|
super().dragMoveEvent(event)
|
||||||
|
return
|
||||||
|
|
||||||
def dropEvent(self, event: QDropEvent) -> None:
|
def dropEvent(self, event: QDropEvent) -> None:
|
||||||
"""Overload the drop item event to ensure relevant data has been
|
"""Overload the drop item event to ensure relevant data has been
|
||||||
updated.
|
updated.
|
||||||
"""
|
"""
|
||||||
sHandle = self.getSelectedHandle()
|
sHandle = self.getSelectedHandle()
|
||||||
sItem = self._getTreeItem(sHandle) if sHandle else None
|
sItem = self._getTreeItem(sHandle) if sHandle else None
|
||||||
if sHandle is None or sItem is None:
|
if sHandle is None or sItem is None or sItem.parent() is None:
|
||||||
logger.error("Invalid drag and drop event")
|
logger.error("Invalid drag and drop event")
|
||||||
|
event.ignore()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.indexAt(event.pos()).isValid():
|
||||||
|
# Needed due to a bug somewhere around Qt 5.15.8 that
|
||||||
|
# ignores the invisible root item flags
|
||||||
|
logger.error("Invalid drop location")
|
||||||
|
event.ignore()
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug("Drag'n'drop of item '%s' accepted", sHandle)
|
logger.debug("Drag'n'drop of item '%s' accepted", sHandle)
|
||||||
|
|||||||
@@ -991,6 +991,7 @@ class GuiMain(QMainWindow):
|
|||||||
if dlgWords.result() == QDialog.Accepted:
|
if dlgWords.result() == QDialog.Accepted:
|
||||||
logger.debug("Reloading word list")
|
logger.debug("Reloading word list")
|
||||||
SHARED.updateSpellCheckLanguage(reload=True)
|
SHARED.updateSpellCheckLanguage(reload=True)
|
||||||
|
self.docEditor.spellCheckDocument()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -332,14 +332,16 @@ class _FilterTab(QWidget):
|
|||||||
|
|
||||||
treeHeader = self.optTree.header()
|
treeHeader = self.optTree.header()
|
||||||
treeHeader.setStretchLastSection(False)
|
treeHeader.setStretchLastSection(False)
|
||||||
|
treeHeader.setMinimumSectionSize(iPx + cMg) # See Issue #1551
|
||||||
treeHeader.setSectionResizeMode(self.C_NAME, QHeaderView.Stretch)
|
treeHeader.setSectionResizeMode(self.C_NAME, QHeaderView.Stretch)
|
||||||
treeHeader.setSectionResizeMode(self.C_ACTIVE, QHeaderView.Fixed)
|
treeHeader.setSectionResizeMode(self.C_ACTIVE, QHeaderView.Fixed)
|
||||||
treeHeader.setSectionResizeMode(self.C_STATUS, QHeaderView.Fixed)
|
treeHeader.setSectionResizeMode(self.C_STATUS, QHeaderView.Fixed)
|
||||||
treeHeader.resizeSection(self.C_ACTIVE, iPx + cMg)
|
treeHeader.resizeSection(self.C_ACTIVE, iPx + cMg)
|
||||||
treeHeader.resizeSection(self.C_STATUS, iPx + cMg)
|
treeHeader.resizeSection(self.C_STATUS, iPx + cMg)
|
||||||
|
|
||||||
self.optTree.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
|
||||||
self.optTree.setDragDropMode(QAbstractItemView.NoDragDrop)
|
self.optTree.setDragDropMode(QAbstractItemView.NoDragDrop)
|
||||||
|
self.optTree.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||||
|
self.optTree.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
|
||||||
# Filters
|
# Filters
|
||||||
# =======
|
# =======
|
||||||
@@ -390,11 +392,11 @@ class _FilterTab(QWidget):
|
|||||||
self.mainSplit.addWidget(self.filterOpt)
|
self.mainSplit.addWidget(self.filterOpt)
|
||||||
self.mainSplit.setCollapsible(0, False)
|
self.mainSplit.setCollapsible(0, False)
|
||||||
self.mainSplit.setCollapsible(1, False)
|
self.mainSplit.setCollapsible(1, False)
|
||||||
self.mainSplit.setStretchFactor(0, 0)
|
self.mainSplit.setStretchFactor(0, 1)
|
||||||
self.mainSplit.setStretchFactor(1, 1)
|
self.mainSplit.setStretchFactor(1, 0)
|
||||||
self.mainSplit.setSizes([
|
self.mainSplit.setSizes([
|
||||||
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "treeWidth", 1)),
|
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "treeWidth", 300)),
|
||||||
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "filterWidth", 1))
|
CONFIG.pxInt(pOptions.getInt("GuiBuildSettings", "filterWidth", 300))
|
||||||
])
|
])
|
||||||
|
|
||||||
self.outerBox = QHBoxLayout()
|
self.outerBox = QHBoxLayout()
|
||||||
@@ -718,7 +720,7 @@ class _HeadingsTab(QWidget):
|
|||||||
|
|
||||||
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document())
|
self.formSyntax = _HeadingSyntaxHighlighter(self.editTextBox.document())
|
||||||
|
|
||||||
self.menuInsert = QMenu()
|
self.menuInsert = QMenu(self)
|
||||||
self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
|
self.aInsTitle = self.menuInsert.addAction(self.tr("Title"))
|
||||||
self.aInsChNum = self.menuInsert.addAction(self.tr("Chapter Number"))
|
self.aInsChNum = self.menuInsert.addAction(self.tr("Chapter Number"))
|
||||||
self.aInsChWord = self.menuInsert.addAction(self.tr("Chapter Number (Word)"))
|
self.aInsChWord = self.menuInsert.addAction(self.tr("Chapter Number (Word)"))
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ def buildQtI18nTS(sysArgs):
|
|||||||
from PyQt6.lupdate import lupdate
|
from PyQt6.lupdate import lupdate
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("ERROR: This command requires lupdate from PyQt6")
|
print("ERROR: This command requires lupdate from PyQt6")
|
||||||
|
print("On Debian/Ubuntu, install: pyqt6-dev-tools")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-09-07 19:14:50">
|
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-11-05 18:54:42">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1531" autoCount="238" editTime="75619">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1532" autoCount="238" editTime="75633">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
|
|||||||
@@ -166,21 +166,21 @@ def testBaseConfig_Localisation(fncPath, tstPaths):
|
|||||||
|
|
||||||
i18nDir = fncPath / "i18n"
|
i18nDir = fncPath / "i18n"
|
||||||
i18nDir.mkdir()
|
i18nDir.mkdir()
|
||||||
tstConf._nwLangPath = str(i18nDir)
|
tstConf._nwLangPath = i18nDir
|
||||||
|
|
||||||
copyfile(tstPaths.filesDir / "nw_en_GB.qm", i18nDir / "nw_en_GB.qm")
|
copyfile(tstPaths.filesDir / "nw_en_GB.qm", i18nDir / "nw_en_GB.qm")
|
||||||
writeFile(i18nDir / "nw_en_GB.ts", "")
|
writeFile(i18nDir / "nw_en_GB.ts", "")
|
||||||
writeFile(i18nDir / "nw_abcd.qm", "")
|
writeFile(i18nDir / "nw_abcd.qm", "")
|
||||||
|
|
||||||
tstApp = MockApp()
|
tstApp = MockApp()
|
||||||
tstConf.initLocalisation(tstApp)
|
tstConf.initLocalisation(tstApp) # type: ignore
|
||||||
|
|
||||||
# Check Lists
|
# Check Lists
|
||||||
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
||||||
assert theList == [("en_GB", "British English")]
|
assert theList == [("en_GB", "British English")]
|
||||||
theList = tstConf.listLanguages(tstConf.LANG_PROJ)
|
theList = tstConf.listLanguages(tstConf.LANG_PROJ)
|
||||||
assert theList == [("en_GB", "British English")]
|
assert theList == [("en_GB", "British English")]
|
||||||
theList = tstConf.listLanguages(None)
|
theList = tstConf.listLanguages(None) # type: ignore
|
||||||
assert theList == []
|
assert theList == []
|
||||||
|
|
||||||
# Add Language
|
# Add Language
|
||||||
|
|||||||
+261
-267
@@ -29,177 +29,176 @@ from novelwriter.core.project import NWProject
|
|||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_ConvertFormat(mockGUI):
|
def testCoreToHtml_ConvertFormat(mockGUI):
|
||||||
"""Test the tokenizer and converter chain using the ToHtml class.
|
"""Test the tokenizer and converter chain using the ToHtml class."""
|
||||||
"""
|
project = NWProject()
|
||||||
theProject = NWProject()
|
html = ToHtml(project)
|
||||||
theHtml = ToHtml(theProject)
|
|
||||||
|
|
||||||
# Novel Files Headers
|
# Novel Files Headers
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
theHtml._isNovel = True
|
html._isNovel = True
|
||||||
theHtml._isNote = False
|
html._isNote = False
|
||||||
theHtml._isFirst = True
|
html._isFirst = True
|
||||||
|
|
||||||
# Header 1
|
# Header 1
|
||||||
theHtml._text = "# Partition\n"
|
html._text = "# Partition\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: center;'>Partition</h1>\n"
|
"<h1 class='title' style='text-align: center;'>Partition</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 2
|
# Header 2
|
||||||
theHtml._text = "## Chapter Title\n"
|
html._text = "## Chapter Title\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 style='page-break-before: always;'>Chapter Title</h1>\n"
|
"<h1 style='page-break-before: always;'>Chapter Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 3
|
# Header 3
|
||||||
theHtml._text = "### Scene Title\n"
|
html._text = "### Scene Title\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h2>Scene Title</h2>\n"
|
assert html.theResult == "<h2>Scene Title</h2>\n"
|
||||||
|
|
||||||
# Header 4
|
# Header 4
|
||||||
theHtml._text = "#### Section Title\n"
|
html._text = "#### Section Title\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h3>Section Title</h3>\n"
|
assert html.theResult == "<h3>Section Title</h3>\n"
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
theHtml._text = "#! Title\n"
|
html._text = "#! Title\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: center;'>Title</h1>\n"
|
"<h1 class='title' style='text-align: center;'>Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unnumbered
|
# Unnumbered
|
||||||
theHtml._text = "##! Prologue\n"
|
html._text = "##! Prologue\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
|
assert html.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
|
||||||
|
|
||||||
# Note Files Headers
|
# Note Files Headers
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
theHtml._isNovel = False
|
html._isNovel = False
|
||||||
theHtml._isNote = True
|
html._isNote = True
|
||||||
theHtml._isFirst = True
|
html._isFirst = True
|
||||||
theHtml.setLinkHeaders(True)
|
html.setLinkHeaders(True)
|
||||||
|
|
||||||
# Header 1
|
# Header 1
|
||||||
theHtml._text = "# Heading One\n"
|
html._text = "# Heading One\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h1><a name='T000001'></a>Heading One</h1>\n"
|
assert html.theResult == "<h1><a name='T0001'></a>Heading One</h1>\n"
|
||||||
|
|
||||||
# Header 2
|
# Header 2
|
||||||
theHtml._text = "## Heading Two\n"
|
html._text = "## Heading Two\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
|
assert html.theResult == "<h2><a name='T0001'></a>Heading Two</h2>\n"
|
||||||
|
|
||||||
# Header 3
|
# Header 3
|
||||||
theHtml._text = "### Heading Three\n"
|
html._text = "### Heading Three\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h3><a name='T000001'></a>Heading Three</h3>\n"
|
assert html.theResult == "<h3><a name='T0001'></a>Heading Three</h3>\n"
|
||||||
|
|
||||||
# Header 4
|
# Header 4
|
||||||
theHtml._text = "#### Heading Four\n"
|
html._text = "#### Heading Four\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h4><a name='T000001'></a>Heading Four</h4>\n"
|
assert html.theResult == "<h4><a name='T0001'></a>Heading Four</h4>\n"
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
theHtml._text = "#! Heading One\n"
|
html._text = "#! Heading One\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 style='text-align: center;'><a name='T000001'></a>Heading One</h1>\n"
|
"<h1 style='text-align: center;'><a name='T0001'></a>Heading One</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unnumbered
|
# Unnumbered
|
||||||
theHtml._text = "##! Heading Two\n"
|
html._text = "##! Heading Two\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
|
assert html.theResult == "<h2><a name='T0001'></a>Heading Two</h2>\n"
|
||||||
|
|
||||||
# Paragraphs
|
# Paragraphs
|
||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
# Text
|
# Text
|
||||||
theHtml._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Some <strong>nested bold and <em>italic</em> and "
|
"<p>Some <strong>nested bold and <em>italic</em> and "
|
||||||
"<del>strikethrough</del> text</strong> here</p>\n"
|
"<del>strikethrough</del> text</strong> here</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Text w/Hard Break
|
# Text w/Hard Break
|
||||||
theHtml._text = "Line one \nLine two \nLine three\n"
|
html._text = "Line one \nLine two \nLine three\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p class='break'>Line one<br/>Line two<br/>Line three</p>\n"
|
"<p class='break'>Line one<br/>Line two<br/>Line three</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Synopsis
|
# Synopsis
|
||||||
theHtml._text = "%synopsis: The synopsis ...\n"
|
html._text = "%synopsis: The synopsis ...\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == ""
|
assert html.theResult == ""
|
||||||
|
|
||||||
theHtml.setSynopsis(True)
|
html.setSynopsis(True)
|
||||||
theHtml._text = "%synopsis: The synopsis ...\n"
|
html._text = "%synopsis: The synopsis ...\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p class='synopsis'><strong>Synopsis:</strong> The synopsis ...</p>\n"
|
"<p class='synopsis'><strong>Synopsis:</strong> The synopsis ...</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Comment
|
# Comment
|
||||||
theHtml._text = "% A comment ...\n"
|
html._text = "% A comment ...\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == ""
|
assert html.theResult == ""
|
||||||
|
|
||||||
theHtml.setComments(True)
|
html.setComments(True)
|
||||||
theHtml._text = "% A comment ...\n"
|
html._text = "% A comment ...\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p class='comment'><strong>Comment:</strong> A comment ...</p>\n"
|
"<p class='comment'><strong>Comment:</strong> A comment ...</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Keywords
|
# Keywords
|
||||||
theHtml._text = "@char: Bod, Jane\n"
|
html._text = "@char: Bod, Jane\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == ""
|
assert html.theResult == ""
|
||||||
|
|
||||||
theHtml.setKeywords(True)
|
html.setKeywords(True)
|
||||||
theHtml._text = "@char: Bod, Jane\n"
|
html._text = "@char: Bod, Jane\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p><span class='tags'>Characters:</span> "
|
"<p><span class='tags'>Characters:</span> "
|
||||||
"<a href='#tag_Bod'>Bod</a>, <a href='#tag_Jane'>Jane</a></p>\n"
|
"<a href='#tag_Bod'>Bod</a>, <a href='#tag_Jane'>Jane</a></p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Multiple Keywords
|
# Multiple Keywords
|
||||||
theHtml.setKeywords(True)
|
html.setKeywords(True)
|
||||||
theHtml._text = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
html._text = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h2>"
|
"<h2>"
|
||||||
"<a name='T000001'></a>Chapter</h2>\n"
|
"<a name='T0001'></a>Chapter</h2>\n"
|
||||||
"<p style='margin-bottom: 0;'>"
|
"<p style='margin-bottom: 0;'>"
|
||||||
"<span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a>"
|
"<span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a>"
|
||||||
"</p>\n"
|
"</p>\n"
|
||||||
@@ -214,13 +213,13 @@ def testCoreToHtml_ConvertFormat(mockGUI):
|
|||||||
# Preview Mode
|
# Preview Mode
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
theHtml.setPreview(True, True)
|
html.setPreview(True, True)
|
||||||
|
|
||||||
# Text (HTML4)
|
# Text (HTML4)
|
||||||
theHtml._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Some <b>nested bold and <i>italic</i> and "
|
"<p>Some <b>nested bold and <i>italic</i> and "
|
||||||
"<span style='text-decoration: line-through;'>strikethrough</span> "
|
"<span style='text-decoration: line-through;'>strikethrough</span> "
|
||||||
"text</b> here</p>\n"
|
"text</b> here</p>\n"
|
||||||
@@ -231,109 +230,108 @@ def testCoreToHtml_ConvertFormat(mockGUI):
|
|||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_ConvertDirect(mockGUI):
|
def testCoreToHtml_ConvertDirect(mockGUI):
|
||||||
"""Test the converter directly using the ToHtml class.
|
"""Test the converter directly using the ToHtml class."""
|
||||||
"""
|
project = NWProject()
|
||||||
theProject = NWProject()
|
html = ToHtml(project)
|
||||||
theHtml = ToHtml(theProject)
|
|
||||||
|
|
||||||
theHtml._isNovel = True
|
html._isNovel = True
|
||||||
theHtml._isNote = False
|
html._isNote = False
|
||||||
theHtml.setLinkHeaders(True)
|
html.setLinkHeaders(True)
|
||||||
|
|
||||||
# Special Titles
|
# Special Titles
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB | theHtml.A_CENTRE),
|
(html.T_TITLE, 1, "A Title", None, html.A_PBB | html.A_CENTRE),
|
||||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: center; page-break-before: always;'>"
|
"<h1 class='title' style='text-align: center; page-break-before: always;'>"
|
||||||
"<a name='T000001'></a>A Title</h1>\n"
|
"<a name='T0001'></a>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unnumbered
|
# Unnumbered
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_UNNUM, 1, "Prologue", None, theHtml.A_PBB),
|
(html.T_UNNUM, 1, "Prologue", None, html.A_PBB),
|
||||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 style='page-break-before: always;'>"
|
"<h1 style='page-break-before: always;'>"
|
||||||
"<a name='T000001'></a>Prologue</h1>\n"
|
"<a name='T0001'></a>Prologue</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Separators
|
# Separators
|
||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
# Separator
|
# Separator
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE),
|
(html.T_SEP, 1, "* * *", None, html.A_CENTRE),
|
||||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
assert html.theResult == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
||||||
|
|
||||||
# Skip
|
# Skip
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_SKIP, 1, "", None, theHtml.A_NONE),
|
(html.T_SKIP, 1, "", None, html.A_NONE),
|
||||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
(html.T_EMPTY, 1, "", None, html.A_NONE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == "<p class='skip'> </p>\n"
|
assert html.theResult == "<p class='skip'> </p>\n"
|
||||||
|
|
||||||
# Alignment
|
# Alignment
|
||||||
# =========
|
# =========
|
||||||
|
|
||||||
theHtml.setLinkHeaders(False)
|
html.setLinkHeaders(False)
|
||||||
|
|
||||||
# Align Left
|
# Align Left
|
||||||
theHtml.setStyles(False)
|
html.setStyles(False)
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
|
(html.T_HEAD1, 1, "A Title", None, html.A_LEFT),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title'>A Title</h1>\n"
|
"<h1 class='title'>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
theHtml.setStyles(True)
|
html.setStyles(True)
|
||||||
|
|
||||||
# Align Left
|
# Align Left
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
|
(html.T_HEAD1, 1, "A Title", None, html.A_LEFT),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: left;'>A Title</h1>\n"
|
"<h1 class='title' style='text-align: left;'>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Align Right
|
# Align Right
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_RIGHT),
|
(html.T_HEAD1, 1, "A Title", None, html.A_RIGHT),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: right;'>A Title</h1>\n"
|
"<h1 class='title' style='text-align: right;'>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Align Centre
|
# Align Centre
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_CENTRE),
|
(html.T_HEAD1, 1, "A Title", None, html.A_CENTRE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: center;'>A Title</h1>\n"
|
"<h1 class='title' style='text-align: center;'>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Align Justify
|
# Align Justify
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_JUSTIFY),
|
(html.T_HEAD1, 1, "A Title", None, html.A_JUSTIFY),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' style='text-align: justify;'>A Title</h1>\n"
|
"<h1 class='title' style='text-align: justify;'>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -341,11 +339,11 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
# Page Break Always
|
# Page Break Always
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA),
|
(html.T_HEAD1, 1, "A Title", None, html.A_PBB | html.A_PBA),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 class='title' "
|
"<h1 class='title' "
|
||||||
"style='page-break-before: always; page-break-after: always;'>A Title</h1>\n"
|
"style='page-break-before: always; page-break-after: always;'>A Title</h1>\n"
|
||||||
)
|
)
|
||||||
@@ -354,22 +352,22 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
# ======
|
# ======
|
||||||
|
|
||||||
# Indent Left
|
# Indent Left
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_L),
|
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_L),
|
||||||
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
|
(html.T_EMPTY, 2, "", None, html.A_NONE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p style='margin-left: 40px;'>Some text ...</p>\n"
|
"<p style='margin-left: 40px;'>Some text ...</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Indent Right
|
# Indent Right
|
||||||
theHtml._tokens = [
|
html._tokens = [
|
||||||
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_R),
|
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_R),
|
||||||
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
|
(html.T_EMPTY, 2, "", None, html.A_NONE),
|
||||||
]
|
]
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p style='margin-right: 40px;'>Some text ...</p>\n"
|
"<p style='margin-right: 40px;'>Some text ...</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -378,40 +376,39 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_SpecialCases(mockGUI):
|
def testCoreToHtml_SpecialCases(mockGUI):
|
||||||
"""Test some special cases that have caused errors in the past.
|
"""Test some special cases that have caused errors in the past."""
|
||||||
"""
|
project = NWProject()
|
||||||
theProject = NWProject()
|
html = ToHtml(project)
|
||||||
theHtml = ToHtml(theProject)
|
html._isNovel = True
|
||||||
theHtml._isNovel = True
|
|
||||||
|
|
||||||
# Greater/Lesser than symbols
|
# Greater/Lesser than symbols
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
theHtml._text = "Text with > and < with some **bold text** in it.\n"
|
html._text = "Text with > and < with some **bold text** in it.\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Text with > and < with some <strong>bold text</strong> in it.</p>\n"
|
"<p>Text with > and < with some <strong>bold text</strong> in it.</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
theHtml._text = "Text with some <**bold text**> in it.\n"
|
html._text = "Text with some <**bold text**> in it.\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Text with some <<strong>bold text</strong>> in it.</p>\n"
|
"<p>Text with some <<strong>bold text</strong>> in it.</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
theHtml._text = "Let's > be > _difficult **shall** > we_?\n"
|
html._text = "Let's > be > _difficult **shall** > we_?\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Let's > be > <em>difficult <strong>shall</strong> > we</em>?</p>\n"
|
"<p>Let's > be > <em>difficult <strong>shall</strong> > we</em>?</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
theHtml._text = "Test > text _<**bold**>_ and more.\n"
|
html._text = "Test > text _<**bold**>_ and more.\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Test > text <em><<strong>bold</strong>></em> and more.</p>\n"
|
"<p>Test > text <em><<strong>bold</strong>></em> and more.</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -419,20 +416,20 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
|||||||
# ===================
|
# ===================
|
||||||
# See: https://github.com/vkbo/novelWriter/issues/950
|
# See: https://github.com/vkbo/novelWriter/issues/950
|
||||||
|
|
||||||
theHtml.setComments(True)
|
html.setComments(True)
|
||||||
theHtml._text = "% Test > text _<**bold**>_ and more.\n"
|
html._text = "% Test > text _<**bold**>_ and more.\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p class='comment'>"
|
"<p class='comment'>"
|
||||||
"<strong>Comment:</strong> Test > text _<**bold**>_ and more."
|
"<strong>Comment:</strong> Test > text _<**bold**>_ and more."
|
||||||
"</p>\n"
|
"</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
theHtml._text = "## Heading <1>\n"
|
html._text = "## Heading <1>\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<h1 style='page-break-before: always;'>Heading <1></h1>\n"
|
"<h1 style='page-break-before: always;'>Heading <1></h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -440,10 +437,10 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
|||||||
# ====================
|
# ====================
|
||||||
# See: https://github.com/vkbo/novelWriter/issues/1412
|
# See: https://github.com/vkbo/novelWriter/issues/1412
|
||||||
|
|
||||||
theHtml._text = "Test text \\**_bold_** and more.\n"
|
html._text = "Test text \\**_bold_** and more.\n"
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Test text **<em>bold</em>** and more.</p>\n"
|
"<p>Test text **<em>bold</em>** and more.</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -452,11 +449,10 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
|||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Complex(mockGUI, fncPath):
|
def testCoreToHtml_Complex(mockGUI, fncPath):
|
||||||
"""Test the save method of the ToHtml class.
|
"""Test the save method of the ToHtml class."""
|
||||||
"""
|
project = NWProject()
|
||||||
theProject = NWProject()
|
html = ToHtml(project)
|
||||||
theHtml = ToHtml(theProject)
|
html._isNovel = True
|
||||||
theHtml._isNovel = True
|
|
||||||
|
|
||||||
# Build Project
|
# Build Project
|
||||||
# =============
|
# =============
|
||||||
@@ -502,21 +498,21 @@ def testCoreToHtml_Complex(mockGUI, fncPath):
|
|||||||
]
|
]
|
||||||
|
|
||||||
for i in range(len(docText)):
|
for i in range(len(docText)):
|
||||||
theHtml._text = docText[i]
|
html._text = docText[i]
|
||||||
theHtml.doPreProcessing()
|
html.doPreProcessing()
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == resText[i]
|
assert html.theResult == resText[i]
|
||||||
|
|
||||||
assert theHtml.fullHTML == resText
|
assert html.fullHTML == resText
|
||||||
|
|
||||||
theHtml.replaceTabs(nSpaces=2, spaceChar=" ")
|
html.replaceTabs(nSpaces=2, spaceChar=" ")
|
||||||
resText[6] = "<h3>A Section</h3>\n<p> More text in scene two.</p>\n"
|
resText[6] = "<h3>A Section</h3>\n<p> More text in scene two.</p>\n"
|
||||||
|
|
||||||
# Check File
|
# Check File
|
||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
theStyle = theHtml.getStyleSheet()
|
theStyle = html.getStyleSheet()
|
||||||
htmlDoc = (
|
htmlDoc = (
|
||||||
"<!DOCTYPE html>\n"
|
"<!DOCTYPE html>\n"
|
||||||
"<html>\n"
|
"<html>\n"
|
||||||
@@ -539,7 +535,7 @@ def testCoreToHtml_Complex(mockGUI, fncPath):
|
|||||||
)
|
)
|
||||||
|
|
||||||
saveFile = fncPath / "outFile.htm"
|
saveFile = fncPath / "outFile.htm"
|
||||||
theHtml.saveHtml5(saveFile)
|
html.saveHtml5(saveFile)
|
||||||
assert readFile(saveFile) == htmlDoc
|
assert readFile(saveFile) == htmlDoc
|
||||||
|
|
||||||
# END Test testCoreToHtml_Complex
|
# END Test testCoreToHtml_Complex
|
||||||
@@ -547,86 +543,84 @@ def testCoreToHtml_Complex(mockGUI, fncPath):
|
|||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Methods(mockGUI):
|
def testCoreToHtml_Methods(mockGUI):
|
||||||
"""Test all the other methods of the ToHtml class.
|
"""Test all the other methods of the ToHtml class."""
|
||||||
"""
|
project = NWProject()
|
||||||
theProject = NWProject()
|
html = ToHtml(project)
|
||||||
theHtml = ToHtml(theProject)
|
html.setKeepMarkdown(True)
|
||||||
theHtml.setKeepMarkdown(True)
|
|
||||||
|
|
||||||
# Auto-Replace, keep Unicode
|
# Auto-Replace, keep Unicode
|
||||||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||||||
theHtml._text = docText
|
html._text = docText
|
||||||
theHtml.setReplaceUnicode(False)
|
html.setReplaceUnicode(False)
|
||||||
theHtml.doPreProcessing()
|
html.doPreProcessing()
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Auto-Replace, replace Unicode
|
# Auto-Replace, replace Unicode
|
||||||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||||||
theHtml._text = docText
|
html._text = docText
|
||||||
theHtml.setReplaceUnicode(True)
|
html.setReplaceUnicode(True)
|
||||||
theHtml.doPreProcessing()
|
html.doPreProcessing()
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theResult == (
|
assert html.theResult == (
|
||||||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# With Preview
|
# With Preview
|
||||||
theHtml.setPreview(True, True)
|
html.setPreview(True, True)
|
||||||
theHtml._text = docText
|
html._text = docText
|
||||||
theHtml.doPreProcessing()
|
html.doPreProcessing()
|
||||||
theHtml.tokenizeText()
|
html.tokenizeText()
|
||||||
theHtml.doConvert()
|
html.doConvert()
|
||||||
assert theHtml.theMarkdown[-1] == (
|
assert html.theMarkdown[-1] == (
|
||||||
"Text with <brackets> & short–dash, long—dash …\n\n"
|
"Text with <brackets> & short–dash, long—dash …\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Result Size
|
# Result Size
|
||||||
assert theHtml.getFullResultSize() == 147
|
assert html.getFullResultSize() == 147
|
||||||
|
|
||||||
# CSS
|
# CSS
|
||||||
# ===
|
# ===
|
||||||
|
|
||||||
assert len(theHtml.getStyleSheet()) > 1
|
assert len(html.getStyleSheet()) > 1
|
||||||
assert "p {text-align: left;" in " ".join(theHtml.getStyleSheet())
|
assert "p {text-align: left;" in " ".join(html.getStyleSheet())
|
||||||
assert "p {text-align: justify;" not in " ".join(theHtml.getStyleSheet())
|
assert "p {text-align: justify;" not in " ".join(html.getStyleSheet())
|
||||||
|
|
||||||
theHtml.setJustify(True)
|
html.setJustify(True)
|
||||||
assert "p {text-align: left;" not in " ".join(theHtml.getStyleSheet())
|
assert "p {text-align: left;" not in " ".join(html.getStyleSheet())
|
||||||
assert "p {text-align: justify;" in " ".join(theHtml.getStyleSheet())
|
assert "p {text-align: justify;" in " ".join(html.getStyleSheet())
|
||||||
|
|
||||||
theHtml.setStyles(False)
|
html.setStyles(False)
|
||||||
assert theHtml.getStyleSheet() == []
|
assert html.getStyleSheet() == []
|
||||||
|
|
||||||
# END Test testCoreToHtml_Methods
|
# END Test testCoreToHtml_Methods
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Format(mockGUI):
|
def testCoreToHtml_Format(mockGUI):
|
||||||
"""Test all the formatters for the ToHtml class.
|
"""Test all the formatters for the ToHtml class."""
|
||||||
"""
|
project = NWProject()
|
||||||
theProject = NWProject()
|
html = ToHtml(project)
|
||||||
theHtml = ToHtml(theProject)
|
|
||||||
|
|
||||||
# Export Mode
|
# Export Mode
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
assert theHtml._formatSynopsis("synopsis text") == (
|
assert html._formatSynopsis("synopsis text") == (
|
||||||
"<p class='synopsis'><strong>Synopsis:</strong> synopsis text</p>\n"
|
"<p class='synopsis'><strong>Synopsis:</strong> synopsis text</p>\n"
|
||||||
)
|
)
|
||||||
assert theHtml._formatComments("comment text") == (
|
assert html._formatComments("comment text") == (
|
||||||
"<p class='comment'><strong>Comment:</strong> comment text</p>\n"
|
"<p class='comment'><strong>Comment:</strong> comment text</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert theHtml._formatKeywords("") == ""
|
assert html._formatKeywords("") == ""
|
||||||
assert theHtml._formatKeywords("tag: Jane") == (
|
assert html._formatKeywords("tag: Jane") == (
|
||||||
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
|
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
|
||||||
)
|
)
|
||||||
assert theHtml._formatKeywords("char: Bod, Jane") == (
|
assert html._formatKeywords("char: Bod, Jane") == (
|
||||||
"<span class='tags'>Characters:</span> "
|
"<span class='tags'>Characters:</span> "
|
||||||
"<a href='#tag_Bod'>Bod</a>, "
|
"<a href='#tag_Bod'>Bod</a>, "
|
||||||
"<a href='#tag_Jane'>Jane</a>"
|
"<a href='#tag_Jane'>Jane</a>"
|
||||||
@@ -635,20 +629,20 @@ def testCoreToHtml_Format(mockGUI):
|
|||||||
# Preview Mode
|
# Preview Mode
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
theHtml.setPreview(True, True)
|
html.setPreview(True, True)
|
||||||
|
|
||||||
assert theHtml._formatSynopsis("synopsis text") == (
|
assert html._formatSynopsis("synopsis text") == (
|
||||||
"<p class='comment'><span class='synopsis'>Synopsis:</span> synopsis text</p>\n"
|
"<p class='comment'><span class='synopsis'>Synopsis:</span> synopsis text</p>\n"
|
||||||
)
|
)
|
||||||
assert theHtml._formatComments("comment text") == (
|
assert html._formatComments("comment text") == (
|
||||||
"<p class='comment'>comment text</p>\n"
|
"<p class='comment'>comment text</p>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert theHtml._formatKeywords("") == ""
|
assert html._formatKeywords("") == ""
|
||||||
assert theHtml._formatKeywords("tag: Jane") == (
|
assert html._formatKeywords("tag: Jane") == (
|
||||||
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
|
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
|
||||||
)
|
)
|
||||||
assert theHtml._formatKeywords("char: Bod, Jane") == (
|
assert html._formatKeywords("char: Bod, Jane") == (
|
||||||
"<span class='tags'>Characters:</span> "
|
"<span class='tags'>Characters:</span> "
|
||||||
"<a href='#char=Bod'>Bod</a>, "
|
"<a href='#char=Bod'>Bod</a>, "
|
||||||
"<a href='#char=Jane'>Jane</a>"
|
"<a href='#char=Jane'>Jane</a>"
|
||||||
|
|||||||
@@ -434,8 +434,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
|||||||
theToken._text = "% A comment\n"
|
theToken._text = "% A comment\n"
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_COMMENT, 1, "A comment", None, Tokenizer.A_NONE),
|
(Tokenizer.T_COMMENT, 0, "A comment", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "\n"
|
assert theToken.theMarkdown[-1] == "\n"
|
||||||
|
|
||||||
@@ -447,14 +447,14 @@ def testCoreToken_MetaFormat(mockGUI):
|
|||||||
theToken._text = "%synopsis: The synopsis\n"
|
theToken._text = "%synopsis: The synopsis\n"
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
theToken._text = "% synopsis: The synopsis\n"
|
theToken._text = "% synopsis: The synopsis\n"
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SYNOPSIS, 0, "The synopsis", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "\n"
|
assert theToken.theMarkdown[-1] == "\n"
|
||||||
|
|
||||||
@@ -466,8 +466,8 @@ def testCoreToken_MetaFormat(mockGUI):
|
|||||||
theToken._text = "@char: Bod\n"
|
theToken._text = "@char: Bod\n"
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_KEYWORD, 1, "char: Bod", None, Tokenizer.A_NONE),
|
(Tokenizer.T_KEYWORD, 0, "char: Bod", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "\n"
|
assert theToken.theMarkdown[-1] == "\n"
|
||||||
|
|
||||||
@@ -481,10 +481,10 @@ def testCoreToken_MetaFormat(mockGUI):
|
|||||||
styMid = Tokenizer.A_NONE | Tokenizer.A_Z_BTMMRG | Tokenizer.A_Z_TOPMRG
|
styMid = Tokenizer.A_NONE | Tokenizer.A_Z_BTMMRG | Tokenizer.A_Z_TOPMRG
|
||||||
styBtm = Tokenizer.A_NONE | Tokenizer.A_Z_TOPMRG
|
styBtm = Tokenizer.A_NONE | Tokenizer.A_Z_TOPMRG
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_KEYWORD, 1, "pov: Bod", None, styTop),
|
(Tokenizer.T_KEYWORD, 0, "pov: Bod", None, styTop),
|
||||||
(Tokenizer.T_KEYWORD, 2, "plot: Main", None, styMid),
|
(Tokenizer.T_KEYWORD, 0, "plot: Main", None, styMid),
|
||||||
(Tokenizer.T_KEYWORD, 3, "location: Europe", None, styBtm),
|
(Tokenizer.T_KEYWORD, 0, "location: Europe", None, styBtm),
|
||||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
assert theToken.theMarkdown[-1] == "@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||||||
|
|
||||||
@@ -513,23 +513,23 @@ def testCoreToken_MarginFormat(mockGUI):
|
|||||||
)
|
)
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 0, "Some regular text", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT),
|
(Tokenizer.T_TEXT, 0, "Some left-aligned text", [], Tokenizer.A_LEFT),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some right-aligned text", [], Tokenizer.A_RIGHT),
|
(Tokenizer.T_TEXT, 0, "Some right-aligned text", [], Tokenizer.A_RIGHT),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE),
|
(Tokenizer.T_TEXT, 0, "Some centered text", [], Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 9, "Left-indented block", [], Tokenizer.A_IND_L),
|
(Tokenizer.T_TEXT, 0, "Left-indented block", [], Tokenizer.A_IND_L),
|
||||||
(Tokenizer.T_EMPTY, 10, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 11, "Right-indented block", [], Tokenizer.A_IND_R),
|
(Tokenizer.T_TEXT, 0, "Right-indented block", [], Tokenizer.A_IND_R),
|
||||||
(Tokenizer.T_EMPTY, 12, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 13, "Double-indented block", [], dblIndent),
|
(Tokenizer.T_TEXT, 0, "Double-indented block", [], dblIndent),
|
||||||
(Tokenizer.T_EMPTY, 14, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 15, "Right-indent, right-aligned", [], rIndAlign),
|
(Tokenizer.T_TEXT, 0, "Right-indent, right-aligned", [], rIndAlign),
|
||||||
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == (
|
assert theToken.theMarkdown[-1] == (
|
||||||
"Some regular text\n\n"
|
"Some regular text\n\n"
|
||||||
@@ -556,20 +556,20 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
theToken._text = "Some plain text\non two lines\n\n\n"
|
theToken._text = "Some plain text\non two lines\n\n\n"
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_TEXT, 1, "Some plain text", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 0, "Some plain text", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 2, "on two lines", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 0, "on two lines", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "Some plain text\non two lines\n\n\n\n"
|
assert theToken.theMarkdown[-1] == "Some plain text\non two lines\n\n\n\n"
|
||||||
|
|
||||||
theToken.setBodyText(False)
|
theToken.setBodyText(False)
|
||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "\n\n\n"
|
assert theToken.theMarkdown[-1] == "\n\n\n"
|
||||||
theToken.setBodyText(True)
|
theToken.setBodyText(True)
|
||||||
@@ -579,7 +579,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(
|
(
|
||||||
Tokenizer.T_TEXT, 1,
|
Tokenizer.T_TEXT, 0,
|
||||||
"Some **bolded text** on this lines",
|
"Some **bolded text** on this lines",
|
||||||
[
|
[
|
||||||
[5, 2, Tokenizer.FMT_B_B],
|
[5, 2, Tokenizer.FMT_B_B],
|
||||||
@@ -587,7 +587,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
],
|
],
|
||||||
Tokenizer.A_NONE
|
Tokenizer.A_NONE
|
||||||
),
|
),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "Some **bolded text** on this lines\n\n"
|
assert theToken.theMarkdown[-1] == "Some **bolded text** on this lines\n\n"
|
||||||
|
|
||||||
@@ -595,7 +595,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(
|
(
|
||||||
Tokenizer.T_TEXT, 1,
|
Tokenizer.T_TEXT, 0,
|
||||||
"Some _italic text_ on this lines",
|
"Some _italic text_ on this lines",
|
||||||
[
|
[
|
||||||
[5, 1, Tokenizer.FMT_I_B],
|
[5, 1, Tokenizer.FMT_I_B],
|
||||||
@@ -603,7 +603,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
],
|
],
|
||||||
Tokenizer.A_NONE
|
Tokenizer.A_NONE
|
||||||
),
|
),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "Some _italic text_ on this lines\n\n"
|
assert theToken.theMarkdown[-1] == "Some _italic text_ on this lines\n\n"
|
||||||
|
|
||||||
@@ -611,7 +611,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(
|
(
|
||||||
Tokenizer.T_TEXT, 1,
|
Tokenizer.T_TEXT, 0,
|
||||||
"Some **_bold italic text_** on this lines",
|
"Some **_bold italic text_** on this lines",
|
||||||
[
|
[
|
||||||
[5, 2, Tokenizer.FMT_B_B],
|
[5, 2, Tokenizer.FMT_B_B],
|
||||||
@@ -621,7 +621,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
],
|
],
|
||||||
Tokenizer.A_NONE
|
Tokenizer.A_NONE
|
||||||
),
|
),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "Some **_bold italic text_** on this lines\n\n"
|
assert theToken.theMarkdown[-1] == "Some **_bold italic text_** on this lines\n\n"
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(
|
(
|
||||||
Tokenizer.T_TEXT, 1,
|
Tokenizer.T_TEXT, 0,
|
||||||
"Some ~~strikethrough text~~ on this lines",
|
"Some ~~strikethrough text~~ on this lines",
|
||||||
[
|
[
|
||||||
[5, 2, Tokenizer.FMT_D_B],
|
[5, 2, Tokenizer.FMT_D_B],
|
||||||
@@ -637,7 +637,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
],
|
],
|
||||||
Tokenizer.A_NONE
|
Tokenizer.A_NONE
|
||||||
),
|
),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == "Some ~~strikethrough text~~ on this lines\n\n"
|
assert theToken.theMarkdown[-1] == "Some ~~strikethrough text~~ on this lines\n\n"
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(
|
(
|
||||||
Tokenizer.T_TEXT, 1,
|
Tokenizer.T_TEXT, 0,
|
||||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here",
|
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here",
|
||||||
[
|
[
|
||||||
[5, 2, Tokenizer.FMT_B_B],
|
[5, 2, Tokenizer.FMT_B_B],
|
||||||
@@ -657,7 +657,7 @@ def testCoreToken_TextFormat(mockGUI):
|
|||||||
],
|
],
|
||||||
Tokenizer.A_NONE
|
Tokenizer.A_NONE
|
||||||
),
|
),
|
||||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 0, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
assert theToken.theMarkdown[-1] == (
|
assert theToken.theMarkdown[-1] == (
|
||||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
||||||
@@ -679,11 +679,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
|
|
||||||
correctResp = [
|
correctResp = [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_CENTRE),
|
||||||
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
|
(Tokenizer.T_HEAD1, 2, "Title Two", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
|
||||||
|
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
|
||||||
(Tokenizer.T_HEAD1, 5, "Title Two", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
|
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Command wo/Space
|
# Command wo/Space
|
||||||
@@ -727,12 +727,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Multiple Empty Paragraphs
|
# Multiple Empty Paragraphs
|
||||||
@@ -747,12 +747,12 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Three Skips
|
# Three Skips
|
||||||
@@ -764,14 +764,14 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Malformed Command, Case 1
|
# Malformed Command, Case 1
|
||||||
@@ -783,11 +783,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Malformed Command, Case 2
|
# Malformed Command, Case 2
|
||||||
@@ -799,11 +799,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Malformed Command, Case 3
|
# Malformed Command, Case 3
|
||||||
@@ -815,11 +815,11 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Empty Paragraph and Page Break
|
# Empty Paragraph and Page Break
|
||||||
@@ -835,13 +835,13 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_PBB),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_PBB),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 7, "Some text to go here ...", [], 0),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], 0),
|
||||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Multiple Skip
|
# Multiple Skip
|
||||||
@@ -854,15 +854,15 @@ def testCoreToken_SpecialFormat(mockGUI):
|
|||||||
theToken.tokenizeText()
|
theToken.tokenizeText()
|
||||||
assert theToken._tokens == [
|
assert theToken._tokens == [
|
||||||
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
|
||||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_PBB),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_PBB),
|
||||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_TEXT, 7, "Some text to go here ...", [], 0),
|
(Tokenizer.T_TEXT, 1, "Some text to go here ...", [], 0),
|
||||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
|
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||||
]
|
]
|
||||||
|
|
||||||
# END Test testCoreToken_SpecialFormat
|
# END Test testCoreToken_SpecialFormat
|
||||||
|
|||||||
Reference in New Issue
Block a user