Merge branch 'main' into xml_rw_class
@@ -1,7 +1,147 @@
|
|||||||
# novelWriter Changelog
|
# novelWriter Changelog
|
||||||
|
|
||||||
|
## Version 2.0 RC 1 [2022-10-17]
|
||||||
|
|
||||||
|
### Release Notes
|
||||||
|
|
||||||
|
This is a release candidate of the next release version, and is intended for testing purposes.
|
||||||
|
Please be careful when using this version on live writing projects, and make sure you take frequent
|
||||||
|
backups.
|
||||||
|
|
||||||
|
Please check the changelog for an overview of changes. The full release notes will be added to the
|
||||||
|
final release.
|
||||||
|
|
||||||
|
**Note:** As of the 2.0 release, novelWriter requires Qt 5.10 or higher and Python 3.7 or higher.
|
||||||
|
|
||||||
|
### Detailed Changelog
|
||||||
|
|
||||||
|
**Note:** This will no longer be release 1.7, but 2.0 instead due to the major changes to the User
|
||||||
|
Interface. PR #1146.
|
||||||
|
|
||||||
|
**Features**
|
||||||
|
|
||||||
|
* The add new documents feature has been made a little smarter and now tries to choose whether the
|
||||||
|
added document should be a sibling or a child to the selected document. Issue #1107. PR #1110.
|
||||||
|
* A folder in the Project Tree can now be converted to a Novel Document or Project Note from the
|
||||||
|
item's context menu. This makes sense now that documents can have child items. Issue #1071.
|
||||||
|
PR #1128.
|
||||||
|
* All child elements of an item in the Project Tree can now be collectively expanded or collapsed
|
||||||
|
from the context menu. The same action can be triggered on the entire tree from the menu button
|
||||||
|
at the top. Issue #1122. PR #1129.
|
||||||
|
* If using the auto-insert feature for spaces next to punctuation designed for for instance French,
|
||||||
|
any existing spaces are first stripped before the correct space character is added. Some users
|
||||||
|
find it hard to unlearn the reflex to type the space. Issue #1061. PR #1131.
|
||||||
|
* The Project Tree now has Quick Links for navigating directly between root folders. This is
|
||||||
|
convenient for very large projects. Issue #1137. PR #1165.
|
||||||
|
* It is now possible to filter out entire root folders on the Build Tool. Issue #1138. PR #1168.
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
* Fix a typo in the Lorem Ipsum tool and set a max width for the views bar. PR #1065.
|
||||||
|
* The language set in the Build Tool is now properly exported to Open Document files. Previously,
|
||||||
|
it would be set to English regardless of the user's selection. Issue #1073. PR #1077.
|
||||||
|
* Since the text editor cursor extends to the right of its position in the window, it would
|
||||||
|
disappear under the right-hand margin if it reached the edge. A minimum margin of the width of
|
||||||
|
the cursor has been added to the editor, and the same value subtracted from the margin of the
|
||||||
|
viewbox. Thus, the cursor no longer disappears. Issue #1112. PR #1113.
|
||||||
|
* The `Shift+Enter` key combination no longer inserts a Unicode line separator in the text editor.
|
||||||
|
Issue #1150. PR #1151.
|
||||||
|
* Fixed an issue where idle time would not be properly reset when a new project was created.
|
||||||
|
Issue #1149. PR #1153.
|
||||||
|
* Removed the context menu that would appear on the Views Bar, from which the bar could be hidden.
|
||||||
|
This is some feature imposed by the Qt library, and it is not wanted. Issue #1147. PR #1153.
|
||||||
|
|
||||||
|
**Known Issues**
|
||||||
|
|
||||||
|
* An attempt has been made to fix the missing mime icon for novelWriter projects on Linux. The
|
||||||
|
newer versions of Nautilus don't seem to display the correct icon due to some changes in the way
|
||||||
|
icons are extracted from the theme. The icon displays fine in other places. PR #1068.
|
||||||
|
|
||||||
|
**Internationalisation**
|
||||||
|
|
||||||
|
* Norwegian and US English translations have been updated. PR #1170.
|
||||||
|
|
||||||
|
**User Interface**
|
||||||
|
|
||||||
|
* The New Project Wizard has been updated based on user feedback. The "Working Title" setting is
|
||||||
|
now called "Project Name". The number of root folders one can create on the options page has been
|
||||||
|
reduced to the standard ones. An option to add sample notes has been added, and the option to
|
||||||
|
generate chapter folders has been removed. The "Minimal Project" option also no longer creates
|
||||||
|
folders. An Archive root folder is automatically added to all new projects, and a Trash folder is
|
||||||
|
added to custom projects. PR #1067.
|
||||||
|
* The border around a lot of Widgets on the main GUI have been removed. PR #1069.
|
||||||
|
* The Views Bar has been updated since the beta re;ease with better icons, tooltips, and an
|
||||||
|
expanding menu on the Settings button. The Build Tool can now also be accessed from the Views
|
||||||
|
Bar. PR #1069.
|
||||||
|
* The editor theme setting in Preferences has been moved back to the General tab where users seem
|
||||||
|
to expect to find it. PR #1069.
|
||||||
|
* A toolbar has been added to the Outline View where the user can select which Novel folder to
|
||||||
|
view. A refresh button for a forced reload of the selected Novel folder has been added, and the
|
||||||
|
menu to select which columns to show has been added to a new button. This makes it easier to find
|
||||||
|
for users instead of having to right-click the table header. Issue #1105. PRs #1063, #1094, and
|
||||||
|
#1111.
|
||||||
|
* The Rebuild Outline and Auto-Update Outline options have been removed from the Main Menu.
|
||||||
|
PR #1063.
|
||||||
|
* The Project Tree has been redesigned. The header is now hidden, and the columns resize
|
||||||
|
automatically. A toolbar has been added with buttons for moving items up and down in the tree, a
|
||||||
|
button for adding new files, folders and root folders, and a menu button for further options
|
||||||
|
affecting the whole tree. These features have mostly bee removed from the Main Menu. PR #1079.
|
||||||
|
* The context menu of the Project Tree has been rewritten completely. It is now possible to set
|
||||||
|
importance and status directly from this menu. Part of issue #973. PRs #1079 and #1105.
|
||||||
|
* The Edit Item dialog has been replaced with a simple Edit Label dialog. Most of the features
|
||||||
|
handled by the old dialog have been moved to the Project Tree context menu. PR #1082.
|
||||||
|
* The Novel View has been redesigned to match the new Outline View and Project Tree. The header is
|
||||||
|
now hidden, and the columns auto-size. The third column can be selected from a set of options, or
|
||||||
|
be hidden entirely. A button with a menu can be used to select which Novel folder to show.
|
||||||
|
Issue #1041. PR #1084.
|
||||||
|
* An arrow icon has been added behind each item in the Novel View. Clicking it, will pop up a
|
||||||
|
tooltip showing the collected meta data for the heading the item represents. PR #1088.
|
||||||
|
* The Project Details dialog now supports multiple novel folders. Issue #1078. PRs #1130 and #1167.
|
||||||
|
* The Split and Merge tools have been rewritten to work with the new feature of allowing documents
|
||||||
|
to have child documents. The tools have also had more options added to them. The feature is now
|
||||||
|
accessible through the Project Tree item's context menu, and is no longer available from the Main
|
||||||
|
Menu. Issues #1072 and #1032. PRs #1148 and #1154.
|
||||||
|
|
||||||
|
**Installation and Packaging**
|
||||||
|
|
||||||
|
* AppImage distribution has been added by @Ryex. Issue #1091. PR #1092.
|
||||||
|
|
||||||
|
**Code Improvements**
|
||||||
|
|
||||||
|
* A new test project generator function has been added to replace the dependency on the New Project
|
||||||
|
tools in the app itself. This ensures that changing the in-app feature doesn't affect the entire
|
||||||
|
test suite. Over time, this generator function should also replace the minimal sample project
|
||||||
|
saved in the test suite. PR #1067.
|
||||||
|
* The index class has been rewritten. The index data is now stored in a hierarchy of objects rather
|
||||||
|
than a set of nested dictionaries. The `itemIndex` holds all the data collected from the project,
|
||||||
|
and the `tagsIndex` is a reverse lookup index for linking tags back to where they are used. The
|
||||||
|
reading/writing of the index to disk between sessions is now handled by pack/unpack functions in
|
||||||
|
each object. The index will be regenerated when the user first opens a project as it has been
|
||||||
|
completely restructured. PR #1074.
|
||||||
|
* The index instance is now an instance of the project class, not the main GUI. PR #1074.
|
||||||
|
* The Outline View has been restructured into a single parent widget in the same manner as the
|
||||||
|
Novel View was in the previous pre-release. Related to #1041. PR #1063.
|
||||||
|
* A lot of main GUI objects have been given new names in the code to better represent what they do.
|
||||||
|
PR #1081.
|
||||||
|
* The converter for the old 1.0 project structure has been simplified. Mostly to reduce the amount
|
||||||
|
of code and number of translation labels needed for it. It now produces a single error if
|
||||||
|
something went wrong. PR #1083.
|
||||||
|
* A number of unused icons have been removed from the code base. PR #1164.
|
||||||
|
* All checks for Qt versions below 5.10 have been removed. PR #1174.
|
||||||
|
|
||||||
|
**Not Implemented**
|
||||||
|
|
||||||
|
* An idea to make the indexer run in the global thread pool was not implemented. Issue #1076.
|
||||||
|
* Feature #997 is now obsolete due to the changes made to the Edit Item dialog in #1082.
|
||||||
|
* Feature #1106 is not implemented. It proposes to hide the option to select Novel folder if there
|
||||||
|
is only one. We will see if this is really needed based on user feedback.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
## Version 1.7 Beta 1 [2022-05-17]
|
## Version 1.7 Beta 1 [2022-05-17]
|
||||||
|
|
||||||
|
**Note:** The 1.7 release cycle was renamed 2.0 on 2022-10-06. See #1144.
|
||||||
|
|
||||||
### Release Notes
|
### Release Notes
|
||||||
|
|
||||||
This is a beta release of the next release version, and is intended for testing purposes. Please be
|
This is a beta release of the next release version, and is intended for testing purposes. Please be
|
||||||
@@ -89,6 +229,39 @@ final release.
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
## Version 1.6.5 [2022-10-13]
|
||||||
|
|
||||||
|
### Release Notes
|
||||||
|
|
||||||
|
This is a bugfix release that fixes a a few minor issues. The idle time for new projects would be
|
||||||
|
artificially inflated as the clock was not reset when the project was first created. This only
|
||||||
|
affects the first entry in the writing statistics. A scaling issue for the Preferences dialog has
|
||||||
|
also been fixed. It only affected screens with UI scaling enabled. Lastly, typing `Shift+Enter` in
|
||||||
|
the text editor now creates a regular line break instead of a special line separator. The line
|
||||||
|
separator serves no purpose in plain text, and was producing inconsistencies in how text is
|
||||||
|
processed and displayed.
|
||||||
|
|
||||||
|
### Detailed Changelog
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
* Fixed a bug where the idle time was not properly zeroed when a new project was generated after
|
||||||
|
the wizard was closed. The idle time would be calculated from the time the previous project
|
||||||
|
closed, thus inflating the value. Issue #1149. PR #1159.
|
||||||
|
* Fixes an issue where the window size of the Preferences dialog would have the GUI scaling factor
|
||||||
|
applied twice when the dialog was closed, resulting in the dialog growing in size each time it is
|
||||||
|
opened. Issue #989. PR #1159.
|
||||||
|
|
||||||
|
**Other Changes**
|
||||||
|
|
||||||
|
* The text editor no longer creates a Unicode line separator (U+2028) when the user presses
|
||||||
|
`Shift+Enter`. The line separator serves no purpose in a plain text editor, and the code in
|
||||||
|
general treats them as regular line break. This caused the line separator to display differently
|
||||||
|
before and after saving. The line separator character is now automatically replaced by a
|
||||||
|
paragraph separator. Issue #1150. PR #1159.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
## Version 1.6.4 [2022-09-29]
|
## Version 1.6.4 [2022-09-29]
|
||||||
|
|
||||||
### Release Notes
|
### Release Notes
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ You can also follow novelWriter on Mastodon at [fosstodon.org/@novelwriter](http
|
|||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
The application is written with Python 3 (3.7+) using Qt5 and PyQt5 (5.3+). It is developed on
|
The application is written with Python 3 (3.7+) using Qt5 and PyQt5 (5.10+). It is developed on
|
||||||
Linux, but should in principle work fine on other operating systems as well as long as dependencies
|
Linux, but should in principle work fine on other operating systems as well as long as dependencies
|
||||||
are met. It is regularly tested on Debian and Ubuntu Linux, Windows, and macOS.
|
are met. It is regularly tested on Debian and Ubuntu Linux, Windows, and macOS.
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 423 KiB After Width: | Height: | Size: 534 KiB |
|
Before Width: | Height: | Size: 431 KiB After Width: | Height: | Size: 539 KiB |
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 379 KiB |
@@ -71,5 +71,5 @@ On Windows, file extensions may not be visible by default, so make sure you only
|
|||||||
extension, and don't end up with two.
|
extension, and don't end up with two.
|
||||||
|
|
||||||
The QSS files are Qt Style Sheet files. See Qt's
|
The QSS files are Qt Style Sheet files. See Qt's
|
||||||
`The Style Sheet Syntax <https://doc.qt.io/qt-5/stylesheet-syntax.html>`_` documentation for more
|
`The Style Sheet Syntax <https://doc.qt.io/qt-5/stylesheet-syntax.html>`_ documentation for more
|
||||||
details.
|
details.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ The following Python packages are needed to run novelWriter:
|
|||||||
* ``lxml`` – needed for full XML support.
|
* ``lxml`` – needed for full XML support.
|
||||||
* ``PyEnchant`` – needed for spell checking (optional).
|
* ``PyEnchant`` – needed for spell checking (optional).
|
||||||
|
|
||||||
PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work. For
|
PyQt/Qt should be at least 5.10, but ideally 5.13 or higher for nearly all features to work. For
|
||||||
instance, searching using regular expressions with full Unicode support requires 5.13. There is no
|
instance, searching using regular expressions with full Unicode support requires 5.13. There is no
|
||||||
known minimum version requirement for package ``lxml``, but the code was originally written with
|
known minimum version requirement for package ``lxml``, but the code was originally written with
|
||||||
4.2, which is therefore set as the minimum. It may work on lower versions. You have to test it.
|
4.2, which is therefore set as the minimum. It may work on lower versions. You have to test it.
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ Application Data
|
|||||||
|
|
||||||
novelWriter also stores a bit of data that is generated by the user's actions. This includes the
|
novelWriter also stores a bit of data that is generated by the user's actions. This includes the
|
||||||
list of recent projects form the :guilabel:`Open Project` dialog. Custom themes are also saved
|
list of recent projects form the :guilabel:`Open Project` dialog. Custom themes are also saved
|
||||||
here. The system paths are provided by the Qt QStandardPaths_ class and its AppDataLocation value
|
here. The system paths are provided by the Qt QStandardPaths_ class and its AppDataLocation.
|
||||||
on Qt 5.4 or greater, or DataLocation for earlier versions.
|
|
||||||
|
|
||||||
The standard paths are:
|
The standard paths are:
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ The main shorcuts are as follows:
|
|||||||
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
|
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
|
||||||
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
|
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
|
||||||
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
|
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
|
||||||
|
":kbd:`Ctrl`:kbd:`L`", "Open the :guilabel:`Quick Links` menu in the Project Tree."
|
||||||
":kbd:`Ctrl`:kbd:`N`", "Create new project item."
|
":kbd:`Ctrl`:kbd:`N`", "Create new project item."
|
||||||
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
||||||
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
|
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ __license__ = "GPLv3"
|
|||||||
__author__ = "Veronica Berglyd Olsen"
|
__author__ = "Veronica Berglyd Olsen"
|
||||||
__maintainer__ = "Veronica Berglyd Olsen"
|
__maintainer__ = "Veronica Berglyd Olsen"
|
||||||
__email__ = "code@vkbo.net"
|
__email__ = "code@vkbo.net"
|
||||||
__version__ = "1.7-beta1"
|
__version__ = "2.0-rc1"
|
||||||
__hexversion__ = "0x010700b1"
|
__hexversion__ = "0x020000c1"
|
||||||
__date__ = "2022-05-17"
|
__date__ = "2022-10-17"
|
||||||
__status__ = "Stable"
|
__status__ = "Stable"
|
||||||
__domain__ = "novelwriter.io"
|
__domain__ = "novelwriter.io"
|
||||||
__url__ = "https://novelwriter.io"
|
__url__ = "https://novelwriter.io"
|
||||||
@@ -72,32 +72,6 @@ __helpurl__ = "https://github.com/vkbo/novelWriter/discussions"
|
|||||||
__releaseurl__ = "https://github.com/vkbo/novelWriter/releases/latest"
|
__releaseurl__ = "https://github.com/vkbo/novelWriter/releases/latest"
|
||||||
__docurl__ = "https://novelwriter.readthedocs.io"
|
__docurl__ = "https://novelwriter.readthedocs.io"
|
||||||
|
|
||||||
##
|
|
||||||
# Logging
|
|
||||||
# =========
|
|
||||||
# Standard used for logging levels in novelWriter:
|
|
||||||
# CRITICAL Use for errors that result in termination of the program
|
|
||||||
# ERROR Use when an action fails, but execution continues
|
|
||||||
# WARNING When something unexpected, but non-critical happens
|
|
||||||
# INFO Any useful user information like open, save, exit initiated
|
|
||||||
# ----------- SPAM Threshold : Output above should be minimal -----------------
|
|
||||||
# DEBUG Use for descriptions of main program flow
|
|
||||||
# VERBOSE Use for outputting values and program flow details
|
|
||||||
##
|
|
||||||
|
|
||||||
# Add verbose logging level
|
|
||||||
VERBOSE = 5
|
|
||||||
logging.addLevelName(VERBOSE, "VERBOSE")
|
|
||||||
|
|
||||||
|
|
||||||
def logVerbose(self, message, *args, **kws):
|
|
||||||
if self.isEnabledFor(VERBOSE):
|
|
||||||
self._log(VERBOSE, message, args, **kws)
|
|
||||||
|
|
||||||
|
|
||||||
logging.Logger.verbose = logVerbose
|
|
||||||
|
|
||||||
# Initiating logging
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -122,7 +96,6 @@ def main(sysArgs=None):
|
|||||||
"version",
|
"version",
|
||||||
"info",
|
"info",
|
||||||
"debug",
|
"debug",
|
||||||
"verbose",
|
|
||||||
"style=",
|
"style=",
|
||||||
"config=",
|
"config=",
|
||||||
"data=",
|
"data=",
|
||||||
@@ -143,7 +116,6 @@ def main(sysArgs=None):
|
|||||||
" -v, --version Print program version and exit.\n"
|
" -v, --version Print program version and exit.\n"
|
||||||
" --info Print additional runtime information.\n"
|
" --info Print additional runtime information.\n"
|
||||||
" --debug Print debug output. Includes --info.\n"
|
" --debug Print debug output. Includes --info.\n"
|
||||||
" --verbose Increase verbosity of debug output. Includes --debug.\n"
|
|
||||||
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
|
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
|
||||||
" --config= Alternative config file.\n"
|
" --config= Alternative config file.\n"
|
||||||
" --data= Alternative user data path.\n"
|
" --data= Alternative user data path.\n"
|
||||||
@@ -181,9 +153,6 @@ def main(sysArgs=None):
|
|||||||
elif inOpt == "--debug":
|
elif inOpt == "--debug":
|
||||||
logLevel = logging.DEBUG
|
logLevel = logging.DEBUG
|
||||||
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
|
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
|
||||||
elif inOpt == "--verbose":
|
|
||||||
logLevel = VERBOSE
|
|
||||||
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
|
|
||||||
elif inOpt == "--style":
|
elif inOpt == "--style":
|
||||||
qtStyle = inArg
|
qtStyle = inArg
|
||||||
elif inOpt == "--config":
|
elif inOpt == "--config":
|
||||||
@@ -214,14 +183,14 @@ def main(sysArgs=None):
|
|||||||
"At least Python 3.7 is required, found %s" % CONFIG.verPyString
|
"At least Python 3.7 is required, found %s" % CONFIG.verPyString
|
||||||
)
|
)
|
||||||
errorCode |= 0x04
|
errorCode |= 0x04
|
||||||
if CONFIG.verQtValue < 50300:
|
if CONFIG.verQtValue < 51000:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least Qt5 version 5.3 is required, found %s" % CONFIG.verQtString
|
"At least Qt5 version 5.10 is required, found %s" % CONFIG.verQtString
|
||||||
)
|
)
|
||||||
errorCode |= 0x08
|
errorCode |= 0x08
|
||||||
if CONFIG.verPyQtValue < 50300:
|
if CONFIG.verPyQtValue < 51000:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least PyQt5 version 5.3 is required, found %s" % CONFIG.verPyQtString
|
"At least PyQt5 version 5.10 is required, found %s" % CONFIG.verPyQtString
|
||||||
)
|
)
|
||||||
errorCode |= 0x10
|
errorCode |= 0x10
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
|||||||
[Map]
|
[Map]
|
||||||
add = typ_plus.svg
|
add = typ_plus.svg
|
||||||
backward = typ_chevron-left.svg
|
backward = typ_chevron-left.svg
|
||||||
|
bookmark = typ_bookmark.svg
|
||||||
bullet-off = typ_media-record-outline.svg
|
bullet-off = typ_media-record-outline.svg
|
||||||
bullet-on = typ_media-record.svg
|
bullet-on = typ_media-record.svg
|
||||||
check = typ_tick.svg
|
check = typ_tick.svg
|
||||||
clear = typ_backspace.svg
|
|
||||||
close = typ_times.svg
|
close = typ_times.svg
|
||||||
cls_archive = typ_delete.svg
|
cls_archive = typ_delete.svg
|
||||||
cls_character = typ_user.svg
|
cls_character = typ_user.svg
|
||||||
@@ -35,17 +35,9 @@ cls_timeline = typ_calendar.svg
|
|||||||
cls_trash = typ_trash.svg
|
cls_trash = typ_trash.svg
|
||||||
cls_world = typ_location.svg
|
cls_world = typ_location.svg
|
||||||
cross = typ_times.svg
|
cross = typ_times.svg
|
||||||
delete = typ_delete.svg
|
|
||||||
doc_h0 = mixed_heading0.svg
|
|
||||||
doc_h1 = mixed_heading1.svg
|
|
||||||
doc_h2 = mixed_heading2.svg
|
|
||||||
doc_h3 = mixed_heading3.svg
|
|
||||||
doc_h4 = mixed_heading4.svg
|
|
||||||
done = typ_input-checked.svg
|
|
||||||
down = typ_chevron-down.svg
|
down = typ_chevron-down.svg
|
||||||
edit = typ_pencil.svg
|
edit = typ_pencil.svg
|
||||||
forward = typ_chevron-right.svg
|
forward = typ_chevron-right.svg
|
||||||
hash = typ_hash.svg
|
|
||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-menu.svg
|
||||||
minimise = typ_arrow-minimise.svg
|
minimise = typ_arrow-minimise.svg
|
||||||
@@ -54,13 +46,13 @@ proj_details = typ_th-list-grey.svg
|
|||||||
proj_document = typ_document-text.svg
|
proj_document = typ_document-text.svg
|
||||||
proj_folder = typ_folder.svg
|
proj_folder = typ_folder.svg
|
||||||
proj_note = mixed_document-note.svg
|
proj_note = mixed_document-note.svg
|
||||||
|
proj_section = mixed_document-section.svg
|
||||||
proj_scene = mixed_document-scene.svg
|
proj_scene = mixed_document-scene.svg
|
||||||
proj_stats = typ_chart-bar-grey.svg
|
proj_stats = typ_chart-bar-grey.svg
|
||||||
proj_title = mixed_document-title.svg
|
proj_title = mixed_document-title.svg
|
||||||
reference = typ_at.svg
|
reference = typ_at.svg
|
||||||
refresh = typ_refresh.svg
|
refresh = typ_refresh.svg
|
||||||
remove = typ_minus.svg
|
remove = typ_minus.svg
|
||||||
save = typ_download.svg
|
|
||||||
search = typ_search.svg
|
search = typ_search.svg
|
||||||
search_cancel = typ_cancel-grey.svg
|
search_cancel = typ_cancel-grey.svg
|
||||||
search_case = nw_search-case.svg
|
search_case = nw_search-case.svg
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.2"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg2161">
|
||||||
|
<metadata
|
||||||
|
id="metadata2167">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs2165" />
|
||||||
|
<path
|
||||||
|
id="rect12986"
|
||||||
|
style="display:inline;opacity:0.95;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.88635;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 5.8886719 3.4453125 C 5.2152275 3.4453125 4.6660156 3.9925712 4.6660156 4.6660156 L 4.6660156 19.333984 C 4.6660156 20.007428 5.2152275 20.554687 5.8886719 20.554688 L 18.111328 20.554688 C 18.784774 20.554688 19.333984 20.007428 19.333984 19.333984 L 19.333984 4.6660156 C 19.333984 3.9925712 18.784774 3.4453125 18.111328 3.4453125 L 5.8886719 3.4453125 z " />
|
||||||
|
<path
|
||||||
|
id="rect935"
|
||||||
|
style="fill:#999999;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
|
||||||
|
d="M 5.8886719 3.4453125 C 5.2152275 3.4453125 4.6660156 3.9925712 4.6660156 4.6660156 L 4.6660156 6 L 4.6660156 7.1894531 L 4.6660156 10.058594 L 19.333984 10.058594 L 19.333984 7.1894531 L 19.333984 6 L 19.333984 4.6660156 C 19.333984 3.9925712 18.784774 3.4453125 18.111328 3.4453125 L 5.8886719 3.4453125 z " />
|
||||||
|
<path
|
||||||
|
id="path2157"
|
||||||
|
style="display:inline;fill:#595959;fill-opacity:1;stroke-width:1.22222"
|
||||||
|
d="M 7.1113281,13.222656 C 6.7739949,13.222656 6.5,13.49665 6.5,13.833984 c 0,0.337332 0.2739949,0.611328 0.6113281,0.611328 h 9.7773439 c 0.337334,1e-6 0.611328,-0.273996 0.611328,-0.611328 0,-0.337334 -0.273994,-0.611328 -0.611328,-0.611328 z m 0,3.666016 C 6.7739949,16.888672 6.5,17.162666 6.5,17.5 c 0,0.337334 0.2739949,0.611328 0.6113281,0.611328 H 16.888672 C 17.226006,18.111328 17.5,17.837334 17.5,17.5 c 0,-0.337334 -0.273994,-0.611328 -0.611328,-0.611328 z" />
|
||||||
|
<path
|
||||||
|
id="path902"
|
||||||
|
style="display:inline;fill:#848484;fill-opacity:1;stroke-width:1.22222"
|
||||||
|
d="M 5.8886719,1 C 3.8671165,1 2.2226562,2.6444602 2.2226562,4.6660156 V 19.333984 C 2.2226563,21.355538 3.8671165,23 5.8886719,23 H 18.111328 c 2.021556,0 3.666016,-1.644462 3.666016,-3.666016 V 4.6660156 C 21.777344,2.6444602 20.132884,1 18.111328,1 Z m 0,2.4453125 H 18.111328 c 0.673446,0 1.222656,0.5472587 1.222656,1.2207031 V 19.333984 c 0,0.673444 -0.54921,1.220704 -1.222656,1.220704 H 5.8886719 c -0.6734444,-10e-7 -1.2226563,-0.54726 -1.2226563,-1.220704 V 4.6660156 c 0,-0.6734444 0.5492119,-1.2207031 1.2226563,-1.2207031 z" />
|
||||||
|
<path
|
||||||
|
id="path2157-0"
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke-width:1.22222"
|
||||||
|
d="M 7.1113281 5.8886719 C 6.7739949 5.8886719 6.5 6.1626667 6.5 6.5 C 6.5 6.837334 6.7739949 7.1113281 7.1113281 7.1113281 L 16.888672 7.1113281 C 17.226006 7.1113281 17.5 6.837334 17.5 6.5 C 17.5 6.1626667 17.226006 5.8886719 16.888672 5.8886719 L 7.1113281 5.8886719 z " />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2469"
|
|
||||||
cx="19.470242"
|
|
||||||
cy="9.4077778"
|
|
||||||
r="2.2222221" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2469"
|
|
||||||
cx="19.470242"
|
|
||||||
cy="9.4077778"
|
|
||||||
r="2.2222221" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="14.592222"
|
|
||||||
cx="19.470242"
|
|
||||||
id="circle2471"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<g
|
|
||||||
transform="matrix(1.1111111,0,0,1.1111111,-1.3333334,-1.3333332)"
|
|
||||||
id="g2616">
|
|
||||||
<path
|
|
||||||
d="m 4.029904,3.7531206 c -1.0041608,1.0041608 -1.0041608,2.6319041 0,3.6360649 L 8.6392724,11.999839 4.029904,16.610493 c -1.0041608,1.004161 -1.0041608,2.631904 0,3.636065 C 4.5313415,20.749281 5.189639,21 5.8479364,21 c 0.658298,0 1.316595,-0.250719 1.818033,-0.753442 L 15.913973,11.999839 7.6659694,3.7531206 c -1.002876,-1.0041608 -2.6331904,-1.0041608 -3.6360654,0 z"
|
|
||||||
id="path1895"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke-width:1.28573728" />
|
|
||||||
<circle
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="path2467"
|
|
||||||
cx="18.723217"
|
|
||||||
cy="5"
|
|
||||||
r="2" />
|
|
||||||
<circle
|
|
||||||
r="2"
|
|
||||||
cy="9.6669998"
|
|
||||||
cx="18.723217"
|
|
||||||
id="circle2469"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2471"
|
|
||||||
cx="18.723217"
|
|
||||||
cy="14.333"
|
|
||||||
r="2" />
|
|
||||||
<circle
|
|
||||||
r="2"
|
|
||||||
cy="19"
|
|
||||||
cx="18.723217"
|
|
||||||
id="circle2473"
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg13156">
|
|
||||||
<metadata
|
|
||||||
id="metadata13162">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs13160" />
|
|
||||||
<path
|
|
||||||
d="M 19.496214,4.4886395 H 9.4810665 c -1.2679176,0 -2.8382927,0.8082224 -3.5754075,1.8397825 L 3.2917056,9.9869555 2.0908895,11.668499 c -0.1211833,0.175265 -0.1221848,0.492744 0.003,0.665006 l 1.1897995,1.66652 2.622967,3.672554 c 0.7361133,1.030558 2.3054869,1.838782 3.574406,1.838782 H 19.496214 C 20.877302,19.511361 22,18.387661 22,17.007573 V 6.9924263 C 22,5.612339 20.877302,4.4886395 19.496214,4.4886395 Z M 17.19974,14.296473 c 0.391593,0.391592 0.391593,1.02455 0,1.416141 -0.195295,0.195296 -0.451683,0.293445 -0.70807,0.293445 -0.256388,0 -0.512776,-0.09815 -0.708071,-0.293445 l -2.296474,-2.296473 -2.296473,2.296473 c -0.195295,0.195296 -0.451683,0.293445 -0.708071,0.293445 -0.256388,0 -0.512775,-0.09815 -0.7080706,-0.293445 -0.3915923,-0.391591 -0.3915923,-1.024549 0,-1.416141 L 12.070984,12 9.7745104,9.7035265 c -0.3915923,-0.3915921 -0.3915923,-1.0245494 0,-1.4161417 0.3915926,-0.3915922 1.0245496,-0.3915922 1.4161416,0 l 2.296473,2.2964732 2.296474,-2.2964732 c 0.391592,-0.3915922 1.024549,-0.3915922 1.416141,0 0.391593,0.3915923 0.391593,1.0245496 0,1.4161417 L 14.903267,12 Z"
|
|
||||||
id="path13154"
|
|
||||||
style="stroke-width:1.00151;fill:#d64848;fill-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -9,9 +9,9 @@
|
|||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
id="svg2608">
|
id="svg901">
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata2614">
|
id="metadata907">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work
|
||||||
rdf:about="">
|
rdf:about="">
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<defs
|
<defs
|
||||||
id="defs2612" />
|
id="defs905" />
|
||||||
<path
|
<path
|
||||||
d="M 16.203226,2 H 7.796774 C 6.05874,2 4.6443545,3.4143855 4.6443545,5.1524195 V 19.86371 c 0,0.540115 0.1092839,0.994063 0.3236484,1.350287 0.5926549,0.982504 1.9072138,1.059213 2.9559187,0.0084 L 11.25708,17.889239 c 0.394052,-0.393002 1.091787,-0.393002 1.485841,0 l 3.333158,3.333158 C 16.592024,21.738349 17.128987,22 17.673304,22 c 0.837492,0 1.682342,-0.660957 1.682342,-2.13629 V 5.1524195 C 19.355646,3.4143855 17.941259,2 16.203226,2 Z M 7.796774,4.101613 h 8.406452 c 0.578994,0 1.050806,0.4718121 1.050806,1.0508065 V 15.560658 l -2.575527,-2.361162 c -1.477434,-1.35449 -3.880628,-1.353439 -5.3580615,0 l -2.574476,2.361162 V 5.1524195 c 0,-0.5789944 0.4718121,-1.0508065 1.0508065,-1.0508065 z m 6.431987,12.301792 C 13.635055,15.809699 12.843798,15.482898 12,15.482898 c -0.843797,0 -1.635054,0.327852 -2.2287605,0.920507 l -3.025272,3.025272 v -2.442075 l 3.2848215,-3.011611 c 1.085483,-0.995114 2.851889,-0.995114 3.937372,0 l 3.285871,3.011611 v 2.442075 z"
|
d="M 16.203226,2 H 7.796774 C 6.05874,2 4.6443545,3.4143855 4.6443545,5.1524195 V 19.86371 c 0,0.540115 0.1092839,0.994063 0.3236484,1.350287 0.5926549,0.982504 1.9072138,1.059213 2.9559187,0.0084 L 11.25708,17.889239 c 0.394052,-0.393002 1.091787,-0.393002 1.485841,0 l 3.333158,3.333158 C 16.592024,21.738349 17.128987,22 17.673304,22 c 0.837492,0 1.682342,-0.660957 1.682342,-2.13629 V 5.1524195 C 19.355646,3.4143855 17.941259,2 16.203226,2 Z M 7.796774,4.101613 h 8.406452 c 0.578994,0 1.050806,0.4718121 1.050806,1.0508065 V 15.560658 l -2.575527,-2.361162 c -1.477434,-1.35449 -3.880628,-1.353439 -5.3580615,0 l -2.574476,2.361162 V 5.1524195 c 0,-0.5789944 0.4718121,-1.0508065 1.0508065,-1.0508065 z m 6.431987,12.301792 C 13.635055,15.809699 12.843798,15.482898 12,15.482898 c -0.843797,0 -1.635054,0.327852 -2.2287605,0.920507 l -3.025272,3.025272 v -2.442075 l 3.2848215,-3.011611 c 1.085483,-0.995114 2.851889,-0.995114 3.937372,0 l 3.285871,3.011611 v 2.442075 z"
|
||||||
id="path2606"
|
id="path899"
|
||||||
style="stroke-width:1.05081;fill:#6699cc;fill-opacity:1" />
|
style="fill:#6699cc;fill-opacity:1;stroke-width:1.05081" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg13801">
|
|
||||||
<metadata
|
|
||||||
id="metadata13807">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs13805" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:#cda66f;fill-opacity:1"
|
|
||||||
id="path6288"
|
|
||||||
d="m 20.987,16 c 0,-0.105 -0.004,-0.211 -0.039,-0.316 l -2,-6 C 18.812,9.275 18.431,9 18,9 h -0.219 c -0.094,0.188 -0.21,0.368 -0.367,0.525 L 15.932,11 h 1.348 l 1.667,5 H 5.054 L 6.721,11 H 8.069 L 6.586,9.525 C 6.429,9.368 6.312,9.188 6.219,9 H 6 C 5.569,9 5.188,9.275 5.052,9.684 l -2,6 C 3.017,15.789 3.013,15.895 3.013,16 3,16 3,21 3,21 c 0,0.553 0.447,1 1,1 h 16 c 0.553,0 1,-0.447 1,-1 0,0 0,-5 -0.013,-5 z" />
|
|
||||||
<path
|
|
||||||
d="M 16.707,7.404 C 16.518,7.216 16.259,7.121 16,7.121 c -0.259,0 -0.518,0.095 -0.707,0.283 L 13,9.697 V 3 C 13,2.448 12.552,2 12,2 11.448,2 11,2.448 11,3 V 9.697 L 8.707,7.404 C 8.518,7.216 8.267,7.111 8,7.111 c -0.267,0 -0.518,0.105 -0.707,0.293 -0.39,0.39 -0.39,1.024 0,1.414 L 12,13.5 16.709,8.816 C 17.097,8.429 17.097,7.794 16.707,7.404 Z"
|
|
||||||
id="path13799"
|
|
||||||
style="display:inline;fill:#99cc99;fill-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg3369">
|
|
||||||
<metadata
|
|
||||||
id="metadata3375">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs3373" />
|
|
||||||
<path
|
|
||||||
d="m 10.828125,20 h -4 c -0.553,0 -1,0.447 -1,1 0,0.553 0.447,1 1,1 h 10 c 0.553,0 1,-0.447 1,-1 0,-0.553 -0.447,-1 -1,-1 h -4 v -1.23 c 1.64,-0.371 3.146,-1.188 4.363,-2.406 1.7,-1.7 2.637,-3.96 2.637,-6.364 0,-2.067 -0.692,-4.029 -1.968,-5.619 l 0.675,-0.673 c 0.391,-0.391 0.391,-1.023 10e-4,-1.415 -0.391,-0.391 -1.024,-0.39 -1.415,-0.001 l -2.052,2.049 0.708,0.708 c 1.322,1.322 2.051,3.081 2.051,4.951 0,1.87 -0.729,3.627 -2.051,4.949 -1.322,1.322 -3.079,2.051 -4.949,2.051 -1.87,0 -3.627,-0.729 -4.949,-2.051 -0.391,-0.391 -1.023,-0.391 -1.414,0 -0.391,0.39 -0.391,1.023 0,1.414 1.699,1.7 3.959,2.637 6.363,2.637 z m 0,-16 c 1.657,0 3.157,0.672 4.243,1.757 1.085,1.086 1.757,2.586 1.757,4.243 0,1.656 -0.672,3.156 -1.757,4.242 -1.086,1.086 -2.586,1.758 -4.243,1.758 -1.658,0 -3.157,-0.672 -4.242,-1.757 -1.085,-1.086 -1.756,-2.586 -1.756,-4.243 0,-1.657 0.671,-3.157 1.756,-4.243 1.085,-1.085 2.584,-1.757 4.242,-1.757 z"
|
|
||||||
id="path3367"
|
|
||||||
style="fill:#aeaeae;fill-opacity:1;stroke-width:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg5204">
|
|
||||||
<metadata
|
|
||||||
id="metadata5210">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs5208" />
|
|
||||||
<path
|
|
||||||
id="rect928"
|
|
||||||
d="M 7.75 2 C 6.7805 2 6 2.7805 6 3.75 L 6 5.5 L 4.1621094 5.5 C 3.2410844 5.5 2.5 6.2805 2.5 7.25 C 2.5 8.2195 3.2410844 9 4.1621094 9 L 6 9 L 6 15 L 4.1621094 15 C 3.2410844 15 2.5 15.7805 2.5 16.75 C 2.5 17.7195 3.2410844 18.5 4.1621094 18.5 L 6 18.5 L 6 20.25 C 6 21.2195 6.7805 22 7.75 22 C 8.7195 22 9.5 21.2195 9.5 20.25 L 9.5 18.5 L 14.5 18.5 L 14.5 20.25 C 14.5 21.2195 15.2805 22 16.25 22 C 17.2195 22 18 21.2195 18 20.25 L 18 18.5 L 19.837891 18.5 C 20.758916 18.5 21.5 17.7195 21.5 16.75 C 21.5 15.7805 20.758916 15 19.837891 15 L 18 15 L 18 9 L 19.837891 9 C 20.758916 9 21.5 8.2195 21.5 7.25 C 21.5 6.2805 20.758916 5.5 19.837891 5.5 L 18 5.5 L 18 3.75 C 18 2.7805 17.2195 2 16.25 2 C 15.2805 2 14.5 2.7805 14.5 3.75 L 14.5 5.5 L 9.5 5.5 L 9.5 3.75 C 9.5 2.7805 8.7195 2 7.75 2 z M 9.5 9 L 14.5 9 L 14.5 15 L 9.5 15 L 9.5 9 z "
|
|
||||||
style="fill:#6699cc;fill-opacity:1;stroke:none;stroke-width:1.7544229;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg11367"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata11373">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs11371" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:#99cc99;fill-opacity:1;stroke-width:1.28570998"
|
|
||||||
id="path11365"
|
|
||||||
d="m 13.499143,15.642429 c -0.45,0 -0.885857,-0.178715 -1.209857,-0.502715 L 8.859,11.710714 c -0.668571,-0.669857 -0.668571,-1.755 0,-2.423571 0.669857,-0.669857 1.755,-0.669857 2.426143,0 L 13.105714,11.109 17.573571,4.0954286 c 0.459,-0.828 1.504286,-1.1275715 2.332286,-0.6672857 0.826714,0.4602857 1.125,1.5042857 0.666,2.331 l -5.572286,9.0000001 c -0.261,0.470571 -0.727714,0.790714 -1.26,0.865286 z" />
|
|
||||||
<path
|
|
||||||
d="M 17.142857,21 H 6.8571429 C 4.7305714,21 3,19.269429 3,17.142857 V 6.8571429 C 3,4.7305714 4.7305714,3 6.8571429,3 h 6.4285711 c 0.711,0 1.285715,0.576 1.285715,1.2857143 0,0.7097143 -0.574715,1.2857143 -1.285715,1.2857143 H 6.8571429 c -0.7097143,0 -1.2857143,0.5772857 -1.2857143,1.2857143 V 17.142857 c 0,0.708429 0.576,1.285714 1.2857143,1.285714 H 17.142857 c 0.709714,0 1.285714,-0.577285 1.285714,-1.285714 V 13.285714 C 18.428571,12.576 19.003286,12 19.714286,12 20.425286,12 21,12.576 21,13.285714 v 3.857143 C 21,19.269429 19.269429,21 17.142857,21 Z"
|
|
||||||
id="path16196"
|
|
||||||
style="display:inline;fill:#aeaeae;fill-opacity:1;stroke-width:1.28570998" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg873">
|
|
||||||
<metadata
|
|
||||||
id="metadata879">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs877" />
|
|
||||||
<path
|
|
||||||
id="path871"
|
|
||||||
style="display:inline;fill:#6699cc;fill-opacity:1;stroke-width:1.19007"
|
|
||||||
d="m 10.330622,1.9999997 c -4.5924663,0 -8.3306193,3.738153 -8.3306193,8.3306193 0,1.391929 0.3467825,2.702587 0.9532665,3.857242 0.084308,-0.108411 0.1590865,-0.228838 0.2534294,-0.323181 L 4.7482003,12.323178 C 4.5237338,11.697056 4.378519,11.032991 4.3785189,10.330619 c 0,-3.2810129 2.6710902,-5.949778 5.9521031,-5.949778 0.702303,0 1.36649,0.1429324 1.992559,0.3673563 L 14.141363,2.9300158 C 12.997884,2.3384803 11.704084,1.9999997 10.330622,1.9999997 Z m 5.635898,3.4852355 -0.60451,0.6045104 2.545919,2.5482442 0.460357,-0.4626829 C 18.332278,8.0413114 18.299136,7.9066594 18.256684,7.7754 Z m 0.292955,4.4245521 c 0.0083,0.1189097 0.01733,0.2373427 0.01861,0.3580557 l 0.169728,-0.169728 z m 2.059986,2.6993717 -5.710299,5.710299 c 0.105352,-0.03032 0.217183,-0.04484 0.320855,-0.07906 l 0.892816,0.89049 1.799581,1.797261 0.07208,0.07208 0.07672,0.06743 C 16.486443,21.66984 17.375591,22 18.275281,22 c 2.054056,0 3.724716,-1.671795 3.724716,-3.72704 0,-0.999657 -0.390465,-1.936781 -1.099745,-2.638921 l -1.129969,-1.174145 -0.639387,-0.639386 -0.890489,-0.892815 c 0.03403,-0.103138 0.04885,-0.213757 0.07906,-0.318531 z m -11.7600542,2.28784 -0.4929085,0.492908 0.5417343,0.541734 0.57196,-0.571959 C 6.960254,15.221393 6.7588057,15.062002 6.5594068,14.896999 Z m 2.7179722,1.285747 -1.209021,1.209021 0.5417345,0.541734 1.6577535,-1.655429 c -0.3372847,-0.0036 -0.668706,-0.03764 -0.990467,-0.09533 z" />
|
|
||||||
<path
|
|
||||||
d="m 15.96652,2.4766329 c -0.396657,0 -0.792437,0.1507259 -1.095095,0.4533829 L 3.57173,14.229712 c -0.3026571,0.302657 -0.5759103,0.751428 -0.7742385,1.227621 -0.1983282,0.47826 -0.3208556,0.99063 -0.3208556,1.418274 v 4.647756 h 4.6477558 c 0.427645,0 0.9376894,-0.122526 1.4159495,-0.320855 0.4782601,-0.198328 0.9272894,-0.471583 1.2299463,-0.774239 L 21.069984,9.1285734 c 0.302656,-0.302657 0.453382,-0.7017958 0.453382,-1.0974191 0,-0.3966562 -0.150726,-0.7924369 -0.453382,-1.0950939 L 17.063939,2.9300158 C 16.761281,2.6273588 16.362143,2.4766329 15.96652,2.4766329 Z m 0,2.2785396 3.275981,3.2759818 -1.334572,1.3368983 -3.275982,-3.2759819 z M 13.901884,6.8221334 15.173682,8.0939305 6.6082326,16.661704 5.3364357,15.387582 Z m 2.004185,2.0041848 1.271796,1.2717968 -8.5654475,8.565448 -1.2741221,-1.271796 z M 4.7063497,16.250176 c 0.010333,-0.02065 3.0644031,3.034179 3.0644031,3.034179 C 7.43814,19.422771 7.2039295,19.456408 7.1243917,19.456408 H 5.5759147 L 4.5435968,18.424084 v -1.548477 c 0,-0.07953 0.033633,-0.315548 0.1627529,-0.625436 z"
|
|
||||||
style="display:inline;fill:#99cc99;fill-opacity:1;stroke-width:1.19007"
|
|
||||||
id="path15590" />
|
|
||||||
<path
|
|
||||||
id="rect19553"
|
|
||||||
d="m 2.4766359,18.937425 v 2.585938 h 2.5605469 z"
|
|
||||||
style="display:inline;opacity:0.95;fill:#0a0a0a;fill-opacity:1;stroke:none;stroke-width:1.88249397;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg7030"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata7036">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs7034" />
|
|
||||||
<g
|
|
||||||
style="fill:#aeaeae;fill-opacity:1"
|
|
||||||
transform="matrix(0.95238095,0,0,0.95238095,0.5714286,0.09523812)"
|
|
||||||
id="g7028">
|
|
||||||
<path
|
|
||||||
style="fill:#aeaeae;fill-opacity:1"
|
|
||||||
id="path7026"
|
|
||||||
d="M 19.414,8.902 C 19.518,8.854 19.62,8.794 19.707,8.707 l 0.5,-0.5 c 0.391,-0.391 0.391,-1.023 0,-1.414 -0.391,-0.391 -1.023,-0.391 -1.414,0 l -0.5,0.5 -0.115,0.173 C 16.791,6.154 14.99,5.276 12.989,5.056 L 13,5 V 4 h 1 C 14.55,4 15,3.55 15,3 15,2.45 14.55,2 14,2 H 10 C 9.45,2 9,2.45 9,3 9,3.55 9.45,4 10,4 h 1 v 1 l 0.012,0.057 C 6.506,5.549 3,9.364 3,14 c 0,4.971 4.029,9 9,9 4.971,0 9,-4.029 9,-9 0,-1.894 -0.588,-3.648 -1.586,-5.098 z M 12,21 C 8.141,21 5,17.86 5,14 5,10.14 8.141,7 12,7 c 3.859,0 7,3.14 7,7 0,3.86 -3.141,7 -7,7 z m 1,-8 v -2 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 v 3 c 0,0.55 0.45,1 1,1 h 3 c 0.55,0 1,-0.45 1,-1 0,-0.55 -0.45,-1 -1,-1 z M 12,8 c -3.312,0 -6,2.688 -6,6 0,3.312 2.688,6 6,6 3.312,0 6,-2.688 6,-6 0,-3.312 -2.688,-6 -6,-6 z m 0,11 C 9.243,19 7,16.757 7,14 7,11.243 9.243,9 12,9 c 2.757,0 5,2.243 5,5 0,2.757 -2.243,5 -5,5 z" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -18,10 +18,10 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
|||||||
[Map]
|
[Map]
|
||||||
add = typ_plus.svg
|
add = typ_plus.svg
|
||||||
backward = typ_chevron-left.svg
|
backward = typ_chevron-left.svg
|
||||||
|
bookmark = typ_bookmark.svg
|
||||||
bullet-off = typ_media-record-outline.svg
|
bullet-off = typ_media-record-outline.svg
|
||||||
bullet-on = typ_media-record.svg
|
bullet-on = typ_media-record.svg
|
||||||
check = typ_tick.svg
|
check = typ_tick.svg
|
||||||
clear = typ_backspace.svg
|
|
||||||
close = typ_times.svg
|
close = typ_times.svg
|
||||||
cls_archive = typ_delete.svg
|
cls_archive = typ_delete.svg
|
||||||
cls_character = typ_user.svg
|
cls_character = typ_user.svg
|
||||||
@@ -35,17 +35,9 @@ cls_timeline = typ_calendar.svg
|
|||||||
cls_trash = typ_trash.svg
|
cls_trash = typ_trash.svg
|
||||||
cls_world = typ_location.svg
|
cls_world = typ_location.svg
|
||||||
cross = typ_times.svg
|
cross = typ_times.svg
|
||||||
delete = typ_delete.svg
|
|
||||||
doc_h0 = mixed_heading0.svg
|
|
||||||
doc_h1 = mixed_heading1.svg
|
|
||||||
doc_h2 = mixed_heading2.svg
|
|
||||||
doc_h3 = mixed_heading3.svg
|
|
||||||
doc_h4 = mixed_heading4.svg
|
|
||||||
done = typ_input-checked.svg
|
|
||||||
down = typ_chevron-down.svg
|
down = typ_chevron-down.svg
|
||||||
edit = typ_pencil.svg
|
edit = typ_pencil.svg
|
||||||
forward = typ_chevron-right.svg
|
forward = typ_chevron-right.svg
|
||||||
hash = typ_hash.svg
|
|
||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-menu.svg
|
||||||
minimise = typ_arrow-minimise.svg
|
minimise = typ_arrow-minimise.svg
|
||||||
@@ -54,13 +46,13 @@ proj_details = typ_th-list-grey.svg
|
|||||||
proj_document = typ_document-text.svg
|
proj_document = typ_document-text.svg
|
||||||
proj_folder = typ_folder.svg
|
proj_folder = typ_folder.svg
|
||||||
proj_note = mixed_document-note.svg
|
proj_note = mixed_document-note.svg
|
||||||
|
proj_section = mixed_document-section.svg
|
||||||
proj_scene = mixed_document-scene.svg
|
proj_scene = mixed_document-scene.svg
|
||||||
proj_stats = typ_chart-bar-grey.svg
|
proj_stats = typ_chart-bar-grey.svg
|
||||||
proj_title = mixed_document-title.svg
|
proj_title = mixed_document-title.svg
|
||||||
reference = typ_at.svg
|
reference = typ_at.svg
|
||||||
refresh = typ_refresh.svg
|
refresh = typ_refresh.svg
|
||||||
remove = typ_minus.svg
|
remove = typ_minus.svg
|
||||||
save = typ_download.svg
|
|
||||||
search = typ_search.svg
|
search = typ_search.svg
|
||||||
search_cancel = typ_cancel-grey.svg
|
search_cancel = typ_cancel-grey.svg
|
||||||
search_case = nw_search-case.svg
|
search_case = nw_search-case.svg
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.2"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg2161">
|
||||||
|
<metadata
|
||||||
|
id="metadata2167">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs2165" />
|
||||||
|
<path
|
||||||
|
id="rect12986"
|
||||||
|
style="display:inline;opacity:0.95;fill:#ededed;fill-opacity:1;stroke:none;stroke-width:1.88635;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 5.8886719 3.4453125 C 5.2152275 3.4453125 4.6660156 3.9925712 4.6660156 4.6660156 L 4.6660156 19.333984 C 4.6660156 20.007428 5.2152275 20.554687 5.8886719 20.554688 L 18.111328 20.554688 C 18.784774 20.554688 19.333984 20.007428 19.333984 19.333984 L 19.333984 4.6660156 C 19.333984 3.9925712 18.784774 3.4453125 18.111328 3.4453125 L 5.8886719 3.4453125 z " />
|
||||||
|
<path
|
||||||
|
id="rect935"
|
||||||
|
style="fill:#787878;fill-opacity:1;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2"
|
||||||
|
d="M 5.8886719 3.4453125 C 5.2152275 3.4453125 4.6660156 3.9925712 4.6660156 4.6660156 L 4.6660156 6 L 4.6660156 7.1894531 L 4.6660156 10.058594 L 19.333984 10.058594 L 19.333984 7.1894531 L 19.333984 6 L 19.333984 4.6660156 C 19.333984 3.9925712 18.784774 3.4453125 18.111328 3.4453125 L 5.8886719 3.4453125 z " />
|
||||||
|
<path
|
||||||
|
id="path2157"
|
||||||
|
style="display:inline;fill:#333333;fill-opacity:1;stroke-width:1.22222"
|
||||||
|
d="M 7.1113281,13.222656 C 6.7739949,13.222656 6.5,13.49665 6.5,13.833984 c 0,0.337332 0.2739949,0.611328 0.6113281,0.611328 h 9.7773439 c 0.337334,1e-6 0.611328,-0.273996 0.611328,-0.611328 0,-0.337334 -0.273994,-0.611328 -0.611328,-0.611328 z m 0,3.666016 C 6.7739949,16.888672 6.5,17.162666 6.5,17.5 c 0,0.337334 0.2739949,0.611328 0.6113281,0.611328 H 16.888672 C 17.226006,18.111328 17.5,17.837334 17.5,17.5 c 0,-0.337334 -0.273994,-0.611328 -0.611328,-0.611328 z" />
|
||||||
|
<path
|
||||||
|
id="path902"
|
||||||
|
style="display:inline;fill:#333333;fill-opacity:1;stroke-width:1.22222"
|
||||||
|
d="M 5.8886719,1 C 3.8671165,1 2.2226562,2.6444602 2.2226562,4.6660156 V 19.333984 C 2.2226563,21.355538 3.8671165,23 5.8886719,23 H 18.111328 c 2.021556,0 3.666016,-1.644462 3.666016,-3.666016 V 4.6660156 C 21.777344,2.6444602 20.132884,1 18.111328,1 Z m 0,2.4453125 H 18.111328 c 0.673446,0 1.222656,0.5472587 1.222656,1.2207031 V 19.333984 c 0,0.673444 -0.54921,1.220704 -1.222656,1.220704 H 5.8886719 c -0.6734444,-10e-7 -1.2226563,-0.54726 -1.2226563,-1.220704 V 4.6660156 c 0,-0.6734444 0.5492119,-1.2207031 1.2226563,-1.2207031 z" />
|
||||||
|
<path
|
||||||
|
id="path2157-0"
|
||||||
|
style="display:inline;fill:#ededed;fill-opacity:1;stroke-width:1.22222"
|
||||||
|
d="M 7.1113281 5.8886719 C 6.7739949 5.8886719 6.5 6.1626667 6.5 6.5 C 6.5 6.837334 6.7739949 7.1113281 7.1113281 7.1113281 L 16.888672 7.1113281 C 17.226006 7.1113281 17.5 6.837334 17.5 6.5 C 17.5 6.1626667 17.226006 5.8886719 16.888672 5.8886719 L 7.1113281 5.8886719 z " />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2469"
|
|
||||||
cx="19.470242"
|
|
||||||
cy="9.4077778"
|
|
||||||
r="2.2222221" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2469"
|
|
||||||
cx="19.470242"
|
|
||||||
cy="9.4077778"
|
|
||||||
r="2.2222221" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="14.592222"
|
|
||||||
cx="19.470242"
|
|
||||||
id="circle2471"
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,55 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg1897"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata1903">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs1901" />
|
|
||||||
<path
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke-width:1.42859697"
|
|
||||||
id="path1895"
|
|
||||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="4.2222223"
|
|
||||||
cx="19.470242"
|
|
||||||
id="path2467"
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2469"
|
|
||||||
cx="19.470242"
|
|
||||||
cy="9.4077778"
|
|
||||||
r="2.2222221" />
|
|
||||||
<circle
|
|
||||||
r="2.2222221"
|
|
||||||
cy="14.592222"
|
|
||||||
cx="19.470242"
|
|
||||||
id="circle2471"
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle2473"
|
|
||||||
cx="19.470242"
|
|
||||||
cy="19.777779"
|
|
||||||
r="2.2222221" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg13156"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata13162">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs13160" />
|
|
||||||
<path
|
|
||||||
style="stroke-width:1.00151;fill:#c82829;fill-opacity:1"
|
|
||||||
id="path13154"
|
|
||||||
d="M 19.496214,4.4886395 H 9.4810665 c -1.2679176,0 -2.8382927,0.8082224 -3.5754075,1.8397825 L 3.2917056,9.9869555 2.0908895,11.668499 c -0.1211833,0.175265 -0.1221848,0.492744 0.003,0.665006 l 1.1897995,1.66652 2.622967,3.672554 c 0.7361133,1.030558 2.3054869,1.838782 3.574406,1.838782 H 19.496214 C 20.877302,19.511361 22,18.387661 22,17.007573 V 6.9924263 C 22,5.612339 20.877302,4.4886395 19.496214,4.4886395 Z M 17.19974,14.296473 c 0.391593,0.391592 0.391593,1.02455 0,1.416141 -0.195295,0.195296 -0.451683,0.293445 -0.70807,0.293445 -0.256388,0 -0.512776,-0.09815 -0.708071,-0.293445 l -2.296474,-2.296473 -2.296473,2.296473 c -0.195295,0.195296 -0.451683,0.293445 -0.708071,0.293445 -0.256388,0 -0.512775,-0.09815 -0.7080706,-0.293445 -0.3915923,-0.391591 -0.3915923,-1.024549 0,-1.416141 L 12.070984,12 9.7745104,9.7035265 c -0.3915923,-0.3915921 -0.3915923,-1.0245494 0,-1.4161417 0.3915926,-0.3915922 1.0245496,-0.3915922 1.4161416,0 l 2.296473,2.2964732 2.296474,-2.2964732 c 0.391592,-0.3915922 1.024549,-0.3915922 1.416141,0 0.391593,0.3915923 0.391593,1.0245496 0,1.4161417 L 14.903267,12 Z" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -5,13 +5,13 @@
|
|||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
id="svg2608"
|
version="1.2"
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
width="24"
|
||||||
version="1.2">
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg901">
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata2614">
|
id="metadata907">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work
|
||||||
rdf:about="">
|
rdf:about="">
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<defs
|
<defs
|
||||||
id="defs2612" />
|
id="defs905" />
|
||||||
<path
|
<path
|
||||||
style="stroke-width:1.05081;fill:#4271ae;fill-opacity:1"
|
d="M 16.203226,2 H 7.796774 C 6.05874,2 4.6443545,3.4143855 4.6443545,5.1524195 V 19.86371 c 0,0.540115 0.1092839,0.994063 0.3236484,1.350287 0.5926549,0.982504 1.9072138,1.059213 2.9559187,0.0084 L 11.25708,17.889239 c 0.394052,-0.393002 1.091787,-0.393002 1.485841,0 l 3.333158,3.333158 C 16.592024,21.738349 17.128987,22 17.673304,22 c 0.837492,0 1.682342,-0.660957 1.682342,-2.13629 V 5.1524195 C 19.355646,3.4143855 17.941259,2 16.203226,2 Z M 7.796774,4.101613 h 8.406452 c 0.578994,0 1.050806,0.4718121 1.050806,1.0508065 V 15.560658 l -2.575527,-2.361162 c -1.477434,-1.35449 -3.880628,-1.353439 -5.3580615,0 l -2.574476,2.361162 V 5.1524195 c 0,-0.5789944 0.4718121,-1.0508065 1.0508065,-1.0508065 z m 6.431987,12.301792 C 13.635055,15.809699 12.843798,15.482898 12,15.482898 c -0.843797,0 -1.635054,0.327852 -2.2287605,0.920507 l -3.025272,3.025272 v -2.442075 l 3.2848215,-3.011611 c 1.085483,-0.995114 2.851889,-0.995114 3.937372,0 l 3.285871,3.011611 v 2.442075 z"
|
||||||
id="path2606"
|
id="path899"
|
||||||
d="M 16.203226,2 H 7.796774 C 6.05874,2 4.6443545,3.4143855 4.6443545,5.1524195 V 19.86371 c 0,0.540115 0.1092839,0.994063 0.3236484,1.350287 0.5926549,0.982504 1.9072138,1.059213 2.9559187,0.0084 L 11.25708,17.889239 c 0.394052,-0.393002 1.091787,-0.393002 1.485841,0 l 3.333158,3.333158 C 16.592024,21.738349 17.128987,22 17.673304,22 c 0.837492,0 1.682342,-0.660957 1.682342,-2.13629 V 5.1524195 C 19.355646,3.4143855 17.941259,2 16.203226,2 Z M 7.796774,4.101613 h 8.406452 c 0.578994,0 1.050806,0.4718121 1.050806,1.0508065 V 15.560658 l -2.575527,-2.361162 c -1.477434,-1.35449 -3.880628,-1.353439 -5.3580615,0 l -2.574476,2.361162 V 5.1524195 c 0,-0.5789944 0.4718121,-1.0508065 1.0508065,-1.0508065 z m 6.431987,12.301792 C 13.635055,15.809699 12.843798,15.482898 12,15.482898 c -0.843797,0 -1.635054,0.327852 -2.2287605,0.920507 l -3.025272,3.025272 v -2.442075 l 3.2848215,-3.011611 c 1.085483,-0.995114 2.851889,-0.995114 3.937372,0 l 3.285871,3.011611 v 2.442075 z" />
|
style="fill:#4271ae;fill-opacity:1;stroke-width:1.05081" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg13801">
|
|
||||||
<metadata
|
|
||||||
id="metadata13807">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs13805" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:#a2694f;fill-opacity:1"
|
|
||||||
id="path6288"
|
|
||||||
d="m 20.987,16 c 0,-0.105 -0.004,-0.211 -0.039,-0.316 l -2,-6 C 18.812,9.275 18.431,9 18,9 h -0.219 c -0.094,0.188 -0.21,0.368 -0.367,0.525 L 15.932,11 h 1.348 l 1.667,5 H 5.054 L 6.721,11 H 8.069 L 6.586,9.525 C 6.429,9.368 6.312,9.188 6.219,9 H 6 C 5.569,9 5.188,9.275 5.052,9.684 l -2,6 C 3.017,15.789 3.013,15.895 3.013,16 3,16 3,21 3,21 c 0,0.553 0.447,1 1,1 h 16 c 0.553,0 1,-0.447 1,-1 0,0 0,-5 -0.013,-5 z" />
|
|
||||||
<path
|
|
||||||
d="M 16.707,7.404 C 16.518,7.216 16.259,7.121 16,7.121 c -0.259,0 -0.518,0.095 -0.707,0.283 L 13,9.697 V 3 C 13,2.448 12.552,2 12,2 11.448,2 11,2.448 11,3 V 9.697 L 8.707,7.404 C 8.518,7.216 8.267,7.111 8,7.111 c -0.267,0 -0.518,0.105 -0.707,0.293 -0.39,0.39 -0.39,1.024 0,1.414 L 12,13.5 16.709,8.816 C 17.097,8.429 17.097,7.794 16.707,7.404 Z"
|
|
||||||
id="path13799"
|
|
||||||
style="display:inline;fill:#718c00;fill-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
id="svg3369"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
version="1.2">
|
|
||||||
<metadata
|
|
||||||
id="metadata3375">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs3373" />
|
|
||||||
<path
|
|
||||||
style="fill:#333333;fill-opacity:1;stroke-width:1"
|
|
||||||
id="path3367"
|
|
||||||
d="m 10.828125,20 h -4 c -0.553,0 -1,0.447 -1,1 0,0.553 0.447,1 1,1 h 10 c 0.553,0 1,-0.447 1,-1 0,-0.553 -0.447,-1 -1,-1 h -4 v -1.23 c 1.64,-0.371 3.146,-1.188 4.363,-2.406 1.7,-1.7 2.637,-3.96 2.637,-6.364 0,-2.067 -0.692,-4.029 -1.968,-5.619 l 0.675,-0.673 c 0.391,-0.391 0.391,-1.023 10e-4,-1.415 -0.391,-0.391 -1.024,-0.39 -1.415,-0.001 l -2.052,2.049 0.708,0.708 c 1.322,1.322 2.051,3.081 2.051,4.951 0,1.87 -0.729,3.627 -2.051,4.949 -1.322,1.322 -3.079,2.051 -4.949,2.051 -1.87,0 -3.627,-0.729 -4.949,-2.051 -0.391,-0.391 -1.023,-0.391 -1.414,0 -0.391,0.39 -0.391,1.023 0,1.414 1.699,1.7 3.959,2.637 6.363,2.637 z m 0,-16 c 1.657,0 3.157,0.672 4.243,1.757 1.085,1.086 1.757,2.586 1.757,4.243 0,1.656 -0.672,3.156 -1.757,4.242 -1.086,1.086 -2.586,1.758 -4.243,1.758 -1.658,0 -3.157,-0.672 -4.242,-1.757 -1.085,-1.086 -1.756,-2.586 -1.756,-4.243 0,-1.657 0.671,-3.157 1.756,-4.243 1.085,-1.085 2.584,-1.757 4.242,-1.757 z" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg5204">
|
|
||||||
<metadata
|
|
||||||
id="metadata5210">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs5208" />
|
|
||||||
<path
|
|
||||||
id="rect928"
|
|
||||||
d="M 7.75 2 C 6.7805 2 6 2.7805 6 3.75 L 6 5.5 L 4.1621094 5.5 C 3.2410844 5.5 2.5 6.2805 2.5 7.25 C 2.5 8.2195 3.2410844 9 4.1621094 9 L 6 9 L 6 15 L 4.1621094 15 C 3.2410844 15 2.5 15.7805 2.5 16.75 C 2.5 17.7195 3.2410844 18.5 4.1621094 18.5 L 6 18.5 L 6 20.25 C 6 21.2195 6.7805 22 7.75 22 C 8.7195 22 9.5 21.2195 9.5 20.25 L 9.5 18.5 L 14.5 18.5 L 14.5 20.25 C 14.5 21.2195 15.2805 22 16.25 22 C 17.2195 22 18 21.2195 18 20.25 L 18 18.5 L 19.837891 18.5 C 20.758916 18.5 21.5 17.7195 21.5 16.75 C 21.5 15.7805 20.758916 15 19.837891 15 L 18 15 L 18 9 L 19.837891 9 C 20.758916 9 21.5 8.2195 21.5 7.25 C 21.5 6.2805 20.758916 5.5 19.837891 5.5 L 18 5.5 L 18 3.75 C 18 2.7805 17.2195 2 16.25 2 C 15.2805 2 14.5 2.7805 14.5 3.75 L 14.5 5.5 L 9.5 5.5 L 9.5 3.75 C 9.5 2.7805 8.7195 2 7.75 2 z M 9.5 9 L 14.5 9 L 14.5 15 L 9.5 15 L 9.5 9 z "
|
|
||||||
style="fill:#4271ae;fill-opacity:1;stroke:none;stroke-width:1.7544229;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg11367">
|
|
||||||
<metadata
|
|
||||||
id="metadata11373">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs11371" />
|
|
||||||
<path
|
|
||||||
d="m 13.499143,15.642429 c -0.45,0 -0.885857,-0.178715 -1.209857,-0.502715 L 8.859,11.710714 c -0.668571,-0.669857 -0.668571,-1.755 0,-2.423571 0.669857,-0.669857 1.755,-0.669857 2.426143,0 L 13.105714,11.109 17.573571,4.0954286 c 0.459,-0.828 1.504286,-1.1275715 2.332286,-0.6672857 0.826714,0.4602857 1.125,1.5042857 0.666,2.331 l -5.572286,9.0000001 c -0.261,0.470571 -0.727714,0.790714 -1.26,0.865286 z"
|
|
||||||
id="path11365"
|
|
||||||
style="display:inline;fill:#718c00;fill-opacity:1;stroke-width:1.28570998" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:#333333;fill-opacity:1;stroke-width:1.28570998"
|
|
||||||
id="path16196"
|
|
||||||
d="M 17.142857,21 H 6.8571429 C 4.7305714,21 3,19.269429 3,17.142857 V 6.8571429 C 3,4.7305714 4.7305714,3 6.8571429,3 h 6.4285711 c 0.711,0 1.285715,0.576 1.285715,1.2857143 0,0.7097143 -0.574715,1.2857143 -1.285715,1.2857143 H 6.8571429 c -0.7097143,0 -1.2857143,0.5772857 -1.2857143,1.2857143 V 17.142857 c 0,0.708429 0.576,1.285714 1.2857143,1.285714 H 17.142857 c 0.709714,0 1.285714,-0.577285 1.285714,-1.285714 V 13.285714 C 18.428571,12.576 19.003286,12 19.714286,12 20.425286,12 21,12.576 21,13.285714 v 3.857143 C 21,19.269429 19.269429,21 17.142857,21 Z" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg7030">
|
|
||||||
<metadata
|
|
||||||
id="metadata7036">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs7034" />
|
|
||||||
<g
|
|
||||||
id="g7028"
|
|
||||||
transform="matrix(0.95238095,0,0,0.95238095,0.5714286,0.09523812)"
|
|
||||||
style="fill:#333333;fill-opacity:1">
|
|
||||||
<path
|
|
||||||
d="M 19.414,8.902 C 19.518,8.854 19.62,8.794 19.707,8.707 l 0.5,-0.5 c 0.391,-0.391 0.391,-1.023 0,-1.414 -0.391,-0.391 -1.023,-0.391 -1.414,0 l -0.5,0.5 -0.115,0.173 C 16.791,6.154 14.99,5.276 12.989,5.056 L 13,5 V 4 h 1 C 14.55,4 15,3.55 15,3 15,2.45 14.55,2 14,2 H 10 C 9.45,2 9,2.45 9,3 9,3.55 9.45,4 10,4 h 1 v 1 l 0.012,0.057 C 6.506,5.549 3,9.364 3,14 c 0,4.971 4.029,9 9,9 4.971,0 9,-4.029 9,-9 0,-1.894 -0.588,-3.648 -1.586,-5.098 z M 12,21 C 8.141,21 5,17.86 5,14 5,10.14 8.141,7 12,7 c 3.859,0 7,3.14 7,7 0,3.86 -3.141,7 -7,7 z m 1,-8 v -2 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 v 3 c 0,0.55 0.45,1 1,1 h 3 c 0.55,0 1,-0.45 1,-1 0,-0.55 -0.45,-1 -1,-1 z M 12,8 c -3.312,0 -6,2.688 -6,6 0,3.312 2.688,6 6,6 3.312,0 6,-2.688 6,-6 0,-3.312 -2.688,-6 -6,-6 z m 0,11 C 9.243,19 7,16.757 7,14 7,11.243 9.243,9 12,9 c 2.757,0 5,2.243 5,5 0,2.757 -2.243,5 -5,5 z"
|
|
||||||
id="path7026"
|
|
||||||
style="fill:#333333;fill-opacity:1" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -2,11 +2,11 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2>Release Notes for 1.7 Beta 1</h2>
|
<h2>Release Notes for 2.0 RC 1</h2>
|
||||||
<p><i>Released on 17 May 2022</i></p>
|
<p><i>Released on 17 October 2022</i></p>
|
||||||
|
|
||||||
<p>This is a beta release of the next release version, and is intended for testing purposes. Please
|
<p>This is a release candidate of the next release version, and is intended for testing purposes.
|
||||||
be careful when using this version on live writing projects, and make sure you take frequent
|
Please be careful when using this version on live writing projects, and make sure you take frequent
|
||||||
backups.</p>
|
backups.</p>
|
||||||
<p>Please check the changelog for an overview of changes. The full release notes will be added to
|
<p>Please check the changelog for an overview of changes. The full release notes will be added to
|
||||||
the final release.</p>
|
the final release.</p>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from PyQt5.QtCore import QCoreApplication
|
|||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import formatException, logException
|
||||||
from novelwriter.constants import nwConst, nwUnicode
|
from novelwriter.constants import nwConst, nwUnicode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -45,52 +45,54 @@ logger = logging.getLogger(__name__)
|
|||||||
# Checker Functions
|
# Checker Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def checkString(value, default, allowNone=False):
|
def checkStringNone(value, default):
|
||||||
"""Check if a variable is a string or a None.
|
"""Check if a variable is a string or a None.
|
||||||
"""
|
"""
|
||||||
if allowNone and (value is None or value == "None"):
|
if value is None or value == "None":
|
||||||
return None
|
return None
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return str(value)
|
return str(value)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkInt(value, default, allowNone=False):
|
def checkString(value, default):
|
||||||
"""Check if a variable is an integer or a None.
|
"""Check if a variable is a string.
|
||||||
|
"""
|
||||||
|
if isinstance(value, str):
|
||||||
|
return str(value)
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def checkInt(value, default):
|
||||||
|
"""Check if a variable is an integer.
|
||||||
"""
|
"""
|
||||||
if allowNone and (value is None or value == "None"):
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except Exception:
|
except Exception:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkFloat(value, default, allowNone=False):
|
def checkFloat(value, default):
|
||||||
"""Check if a variable is a float or a None.
|
"""Check if a variable is a float.
|
||||||
"""
|
"""
|
||||||
if allowNone and (value is None or value == "None"):
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
return float(value)
|
return float(value)
|
||||||
except Exception:
|
except Exception:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkBool(value, default, allowNone=False):
|
def checkBool(value, default):
|
||||||
"""Check if a variable is a boolean or a None.
|
"""Check if a variable is a boolean.
|
||||||
"""
|
"""
|
||||||
if allowNone and (value is None or value == "None"):
|
if isinstance(value, bool):
|
||||||
return None
|
return value
|
||||||
|
elif isinstance(value, str):
|
||||||
if isinstance(value, str):
|
|
||||||
if value == "True":
|
if value == "True":
|
||||||
return True
|
return True
|
||||||
elif value == "False":
|
elif value == "False":
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
elif isinstance(value, int):
|
elif isinstance(value, int):
|
||||||
if value == 1:
|
if value == 1:
|
||||||
return True
|
return True
|
||||||
@@ -98,7 +100,6 @@ def checkBool(value, default, allowNone=False):
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
@@ -174,16 +175,6 @@ def hexToInt(value, default=0):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkIntRange(value, first, last, default):
|
|
||||||
"""Check that an int is in a given range. If it isn't, return the
|
|
||||||
default value.
|
|
||||||
"""
|
|
||||||
if isinstance(value, int):
|
|
||||||
if value >= first and value <= last:
|
|
||||||
return value
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def minmax(value, minVal, maxVal):
|
def minmax(value, minVal, maxVal):
|
||||||
"""Make sure an integer is between min and max value (inclusive).
|
"""Make sure an integer is between min and max value (inclusive).
|
||||||
"""
|
"""
|
||||||
@@ -225,25 +216,25 @@ def formatInt(value):
|
|||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
def formatTimeStamp(theTime, fileSafe=False):
|
def formatTimeStamp(value, fileSafe=False):
|
||||||
"""Take a number (on the format returned by time.time()) and convert
|
"""Take a number (on the format returned by time.time()) and convert
|
||||||
it to a timestamp string.
|
it to a timestamp string.
|
||||||
"""
|
"""
|
||||||
if fileSafe:
|
if fileSafe:
|
||||||
return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_FSTAMP)
|
return datetime.fromtimestamp(value).strftime(nwConst.FMT_FSTAMP)
|
||||||
else:
|
else:
|
||||||
return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_TSTAMP)
|
return datetime.fromtimestamp(value).strftime(nwConst.FMT_TSTAMP)
|
||||||
|
|
||||||
|
|
||||||
def formatTime(tS):
|
def formatTime(t):
|
||||||
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
|
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
|
||||||
if a full day or longer.
|
if a full day or longer.
|
||||||
"""
|
"""
|
||||||
if isinstance(tS, int):
|
if isinstance(t, int):
|
||||||
if tS >= 86400:
|
if t >= 86400:
|
||||||
return f"{tS//86400:d}-{tS%86400//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}"
|
return f"{t//86400:d}-{t%86400//3600:02d}:{t%3600//60:02d}:{t%60:02d}"
|
||||||
else:
|
else:
|
||||||
return f"{tS//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}"
|
return f"{t//3600:02d}:{t%3600//60:02d}:{t%60:02d}"
|
||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
|
||||||
|
|
||||||
@@ -263,7 +254,7 @@ def splitVersionNumber(value):
|
|||||||
and patch, and computes an integer value aabbcc.
|
and patch, and computes an integer value aabbcc.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
return [0, 0, 0, 0]
|
return 0, 0, 0, 0
|
||||||
|
|
||||||
vMajor = 0
|
vMajor = 0
|
||||||
vMinor = 0
|
vMinor = 0
|
||||||
@@ -282,114 +273,114 @@ def splitVersionNumber(value):
|
|||||||
|
|
||||||
vInt = vMajor*10000 + vMinor*100 + vPatch
|
vInt = vMajor*10000 + vMinor*100 + vPatch
|
||||||
|
|
||||||
return [vMajor, vMinor, vPatch, vInt]
|
return vMajor, vMinor, vPatch, vInt
|
||||||
|
|
||||||
|
|
||||||
def transferCase(theSource, theTarget):
|
def transferCase(source, target):
|
||||||
"""Transfers the case of the source word to the target word. This
|
"""Transfers the case of the source word to the target word. This
|
||||||
will consider all upper or lower, and first char capitalisation.
|
will consider all upper or lower, and first char capitalisation.
|
||||||
"""
|
"""
|
||||||
theResult = theTarget
|
theResult = target
|
||||||
|
|
||||||
if not isinstance(theSource, str) or not isinstance(theTarget, str):
|
if not isinstance(source, str) or not isinstance(target, str):
|
||||||
return theResult
|
return theResult
|
||||||
if len(theTarget) < 1 or len(theSource) < 1:
|
if len(target) < 1 or len(source) < 1:
|
||||||
return theResult
|
return theResult
|
||||||
|
|
||||||
if theSource.istitle():
|
if source.istitle():
|
||||||
theResult = theTarget.title()
|
theResult = target.title()
|
||||||
|
|
||||||
if theSource.isupper():
|
if source.isupper():
|
||||||
theResult = theTarget.upper()
|
theResult = target.upper()
|
||||||
elif theSource.islower():
|
elif source.islower():
|
||||||
theResult = theTarget.lower()
|
theResult = target.lower()
|
||||||
|
|
||||||
return theResult
|
return theResult
|
||||||
|
|
||||||
|
|
||||||
def fuzzyTime(secDiff):
|
def fuzzyTime(seconds):
|
||||||
"""Converts a time difference in seconds into a fuzzy time string.
|
"""Converts a time difference in seconds into a fuzzy time string.
|
||||||
"""
|
"""
|
||||||
if secDiff < 0:
|
if seconds < 0:
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "in the future"
|
"Common", "in the future"
|
||||||
)
|
)
|
||||||
elif secDiff < 30:
|
elif seconds < 30:
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "just now"
|
"Common", "just now"
|
||||||
)
|
)
|
||||||
elif secDiff < 90:
|
elif seconds < 90:
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a minute ago"
|
"Common", "a minute ago"
|
||||||
)
|
)
|
||||||
elif secDiff < 3300: # 55 minutes
|
elif seconds < 3300: # 55 minutes
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} minutes ago"
|
"Common", "{0} minutes ago"
|
||||||
).format(int(round(secDiff/60)))
|
).format(int(round(seconds/60)))
|
||||||
elif secDiff < 5400: # 90 minutes
|
elif seconds < 5400: # 90 minutes
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "an hour ago"
|
"Common", "an hour ago"
|
||||||
)
|
)
|
||||||
elif secDiff < 84600: # 23.5 hours
|
elif seconds < 84600: # 23.5 hours
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} hours ago"
|
"Common", "{0} hours ago"
|
||||||
).format(int(round(secDiff/3600)))
|
).format(int(round(seconds/3600)))
|
||||||
elif secDiff < 129600: # 1.5 days
|
elif seconds < 129600: # 1.5 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a day ago"
|
"Common", "a day ago"
|
||||||
)
|
)
|
||||||
elif secDiff < 561600: # 6.5 days
|
elif seconds < 561600: # 6.5 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} days ago"
|
"Common", "{0} days ago"
|
||||||
).format(int(round(secDiff/86400)))
|
).format(int(round(seconds/86400)))
|
||||||
elif secDiff < 907200: # 10.5 days
|
elif seconds < 907200: # 10.5 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a week ago"
|
"Common", "a week ago"
|
||||||
)
|
)
|
||||||
elif secDiff < 2419200: # 28 days
|
elif seconds < 2419200: # 28 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} weeks ago"
|
"Common", "{0} weeks ago"
|
||||||
).format(int(round(secDiff/604800)))
|
).format(int(round(seconds/604800)))
|
||||||
elif secDiff < 3888000: # 45 days
|
elif seconds < 3888000: # 45 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a month ago"
|
"Common", "a month ago"
|
||||||
)
|
)
|
||||||
elif secDiff < 29808000: # 345 days
|
elif seconds < 29808000: # 345 days
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} months ago"
|
"Common", "{0} months ago"
|
||||||
).format(int(round(secDiff/2592000)))
|
).format(int(round(seconds/2592000)))
|
||||||
elif secDiff < 47336400: # 1.5 years
|
elif seconds < 47336400: # 1.5 years
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "a year ago"
|
"Common", "a year ago"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "{0} years ago"
|
"Common", "{0} years ago"
|
||||||
).format(int(round(secDiff/31557600)))
|
).format(int(round(seconds/31557600)))
|
||||||
|
|
||||||
|
|
||||||
def numberToRoman(numVal, toLower=False):
|
def numberToRoman(value, toLower=False):
|
||||||
"""Convert an integer to a Roman number.
|
"""Convert an integer to a Roman number.
|
||||||
"""
|
"""
|
||||||
if not isinstance(numVal, int):
|
if not isinstance(value, int):
|
||||||
return "NAN"
|
return "NAN"
|
||||||
if numVal < 1 or numVal > 4999:
|
if value < 1 or value > 4999:
|
||||||
return "OOR"
|
return "OOR"
|
||||||
|
|
||||||
theValues = [
|
lookup = [
|
||||||
(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"),
|
(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"),
|
||||||
(50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"),
|
(50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"),
|
||||||
]
|
]
|
||||||
|
|
||||||
romNum = ""
|
roman = ""
|
||||||
for theDiv, theSym in theValues:
|
for divisor, symbol in lookup:
|
||||||
n = numVal//theDiv
|
n = value//divisor
|
||||||
romNum += n*theSym
|
roman += n*symbol
|
||||||
numVal -= n*theDiv
|
value -= n*divisor
|
||||||
if numVal <= 0:
|
if value <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
return romNum.lower() if toLower else romNum
|
return roman.lower() if toLower else roman
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
@@ -447,51 +438,70 @@ def jsonEncode(data, n=0, nmax=0):
|
|||||||
# File and File System Functions
|
# File and File System Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def readTextFile(filePath):
|
def readTextFile(path):
|
||||||
"""Read the content of a text file in a robust manner.
|
"""Read the content of a text file in a robust manner.
|
||||||
"""
|
"""
|
||||||
if not os.path.isfile(filePath):
|
if not os.path.isfile(path):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
fileText = ""
|
text = ""
|
||||||
try:
|
try:
|
||||||
with open(filePath, mode="r", encoding="utf-8") as inFile:
|
with open(path, mode="r", encoding="utf-8") as inFile:
|
||||||
fileText = inFile.read()
|
text = inFile.read()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not read file: %s", filePath)
|
logger.error("Could not read file: %s", path)
|
||||||
logException()
|
logException()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return fileText
|
return text
|
||||||
|
|
||||||
|
|
||||||
def makeFileNameSafe(value):
|
def makeFileNameSafe(value):
|
||||||
"""Returns a filename safe string of the value.
|
"""Returns a filename safe string of the value.
|
||||||
"""
|
"""
|
||||||
cleanName = ""
|
clean = ""
|
||||||
for c in str(value).strip():
|
for c in str(value).strip():
|
||||||
if c.isalpha() or c.isdigit() or c == " ":
|
if c.isalpha() or c.isdigit() or c == " ":
|
||||||
cleanName += c
|
clean += c
|
||||||
return cleanName
|
return clean
|
||||||
|
|
||||||
|
|
||||||
def sha256sum(filePath):
|
def ensureFolder(path, parent=None, errLog=None):
|
||||||
|
"""Make sure a folder exists, and if it doesn't, create it.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if parent:
|
||||||
|
path = os.path.join(parent, path)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
os.mkdir(path)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("Could not create folder: %s", path)
|
||||||
|
logException()
|
||||||
|
if isinstance(errLog, list):
|
||||||
|
errLog.append(f"Could not create folder: {path}")
|
||||||
|
errLog.append(formatException(exc))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def sha256sum(path):
|
||||||
"""Make a shasum of a file using a buffer.
|
"""Make a shasum of a file using a buffer.
|
||||||
Based on: https://stackoverflow.com/a/44873382/5825851
|
Based on: https://stackoverflow.com/a/44873382/5825851
|
||||||
"""
|
"""
|
||||||
hDigest = hashlib.sha256()
|
digest = hashlib.sha256()
|
||||||
bData = bytearray(65536)
|
bData = bytearray(65536)
|
||||||
mData = memoryview(bData)
|
mData = memoryview(bData)
|
||||||
try:
|
try:
|
||||||
with open(filePath, mode="rb", buffering=0) as inFile:
|
with open(path, mode="rb", buffering=0) as inFile:
|
||||||
for n in iter(lambda: inFile.readinto(mData), 0):
|
for n in iter(lambda: inFile.readinto(mData), 0):
|
||||||
hDigest.update(mData[:n])
|
digest.update(mData[:n])
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not create sha256sum of: %s", filePath)
|
logger.error("Could not create sha256sum of: %s", path)
|
||||||
logException()
|
logException()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return hDigest.hexdigest()
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
@@ -513,87 +523,59 @@ def getGuiItem(objName):
|
|||||||
|
|
||||||
class NWConfigParser(ConfigParser):
|
class NWConfigParser(ConfigParser):
|
||||||
|
|
||||||
CNF_STR = 0
|
|
||||||
CNF_INT = 1
|
|
||||||
CNF_FLOAT = 2
|
|
||||||
CNF_BOOL = 3
|
|
||||||
CNF_S_LST = 4
|
|
||||||
CNF_I_LST = 5
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def rdStr(self, section, option, default):
|
def rdStr(self, section, option, default):
|
||||||
"""Read string value.
|
"""Read string value.
|
||||||
"""
|
"""
|
||||||
return self._parseLine(section, option, default, self.CNF_STR)
|
return self.get(section, option, fallback=default)
|
||||||
|
|
||||||
def rdInt(self, section, option, default):
|
def rdInt(self, section, option, default):
|
||||||
"""Read integer value.
|
"""Read integer value.
|
||||||
"""
|
"""
|
||||||
return self._parseLine(section, option, default, self.CNF_INT)
|
try:
|
||||||
|
return self.getint(section, option, fallback=default)
|
||||||
|
except ValueError:
|
||||||
|
logger.error("Could not read '%s':'%s' from config", section, option)
|
||||||
|
return default
|
||||||
|
|
||||||
def rdFlt(self, section, option, default):
|
def rdFlt(self, section, option, default):
|
||||||
"""Read float value.
|
"""Read float value.
|
||||||
"""
|
"""
|
||||||
return self._parseLine(section, option, default, self.CNF_FLOAT)
|
try:
|
||||||
|
return self.getfloat(section, option, fallback=default)
|
||||||
|
except ValueError:
|
||||||
|
logger.error("Could not read '%s':'%s' from config", section, option)
|
||||||
|
return default
|
||||||
|
|
||||||
def rdBool(self, section, option, default):
|
def rdBool(self, section, option, default):
|
||||||
"""Read boolean value.
|
"""Read boolean value.
|
||||||
"""
|
"""
|
||||||
return self._parseLine(section, option, default, self.CNF_BOOL)
|
try:
|
||||||
|
return self.getboolean(section, option, fallback=default)
|
||||||
|
except ValueError:
|
||||||
|
logger.error("Could not read '%s':'%s' from config", section, option)
|
||||||
|
return default
|
||||||
|
|
||||||
def rdStrList(self, section, option, default):
|
def rdStrList(self, section, option, default):
|
||||||
"""Read string list.
|
"""Read string list.
|
||||||
"""
|
"""
|
||||||
return self._parseLine(section, option, default, self.CNF_S_LST)
|
result = default.copy() if isinstance(default, list) else []
|
||||||
|
if self.has_option(section, option):
|
||||||
|
data = self.get(section, option, fallback="").split(",")
|
||||||
|
for i in range(min(len(data), len(result))):
|
||||||
|
result[i] = data[i].strip()
|
||||||
|
return result
|
||||||
|
|
||||||
def rdIntList(self, section, option, default):
|
def rdIntList(self, section, option, default):
|
||||||
"""Read integer list.
|
"""Read integer list.
|
||||||
"""
|
"""
|
||||||
return self._parseLine(section, option, default, self.CNF_I_LST)
|
result = default.copy() if isinstance(default, list) else []
|
||||||
|
|
||||||
##
|
|
||||||
# Internal Functions
|
|
||||||
##
|
|
||||||
|
|
||||||
def _unpackList(self, value, default, type):
|
|
||||||
"""Unpack a comma-separated string of items into a list.
|
|
||||||
"""
|
|
||||||
inList = value.split(",")
|
|
||||||
outList = []
|
|
||||||
if isinstance(default, list):
|
|
||||||
outList = default.copy()
|
|
||||||
for i in range(min(len(inList), len(outList))):
|
|
||||||
try:
|
|
||||||
if type == self.CNF_S_LST:
|
|
||||||
outList[i] = inList[i].strip()
|
|
||||||
elif type == self.CNF_I_LST:
|
|
||||||
outList[i] = int(inList[i].strip())
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
return outList
|
|
||||||
|
|
||||||
def _parseLine(self, section, option, default, type):
|
|
||||||
"""Parse a line and return the correct datatype.
|
|
||||||
"""
|
|
||||||
if self.has_option(section, option):
|
if self.has_option(section, option):
|
||||||
try:
|
data = self.get(section, option, fallback="").split(",")
|
||||||
if type == self.CNF_STR:
|
for i in range(min(len(data), len(result))):
|
||||||
return self.get(section, option)
|
result[i] = checkInt(data[i].strip(), result[i])
|
||||||
elif type == self.CNF_INT:
|
return result
|
||||||
return self.getint(section, option)
|
|
||||||
elif type == self.CNF_FLOAT:
|
|
||||||
return self.getfloat(section, option)
|
|
||||||
elif type == self.CNF_BOOL:
|
|
||||||
return self.getboolean(section, option)
|
|
||||||
elif type in (self.CNF_I_LST, self.CNF_S_LST):
|
|
||||||
return self._unpackList(self.get(section, option), default, type)
|
|
||||||
except ValueError:
|
|
||||||
logger.error("Could not read '%s':'%s' from config", str(section), str(option))
|
|
||||||
logException()
|
|
||||||
return default
|
|
||||||
|
|
||||||
return default
|
|
||||||
|
|
||||||
# END Class NWConfigParser
|
# END Class NWConfigParser
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from PyQt5.QtCore import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.error import logException, formatException
|
from novelwriter.error import logException, formatException
|
||||||
from novelwriter.common import splitVersionNumber, formatTimeStamp, NWConfigParser
|
from novelwriter.common import ensureFolder, splitVersionNumber, formatTimeStamp, NWConfigParser
|
||||||
from novelwriter.constants import nwFiles, nwUnicode
|
from novelwriter.constants import nwFiles, nwUnicode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -52,25 +52,22 @@ class Config:
|
|||||||
|
|
||||||
# Set Application Variables
|
# Set Application Variables
|
||||||
self.appName = "novelWriter"
|
self.appName = "novelWriter"
|
||||||
self.appHandle = self.appName.lower()
|
self.appHandle = "novelwriter"
|
||||||
|
|
||||||
# Config Error Handling
|
|
||||||
self.hasError = False # True if the config class encountered an error
|
|
||||||
self.errData = [] # List of error messages
|
|
||||||
|
|
||||||
# Set Paths
|
# Set Paths
|
||||||
self.cmdOpen = None # Path from command line for project to be opened on launch
|
self.cmdOpen = None # Path from command line for project to be opened on launch
|
||||||
self.confPath = None # Folder where the config is saved
|
self.confPath = None # Folder where the config is saved
|
||||||
self.confFile = None # The config file name
|
self.dataPath = None # Folder where app data is stored
|
||||||
self.dataPath = None # Folder where app data is stored
|
self.lastPath = None # The last user-selected folder (browse dialogs)
|
||||||
self.lastPath = None # The last user-selected folder (browse dialogs)
|
self.appPath = None # The full path to the novelwriter package folder
|
||||||
self.appPath = None # The full path to the novelwriter package folder
|
self.appRoot = None # The full path to the novelwriter root folder
|
||||||
self.appRoot = None # The full path to the novelwriter root folder
|
self.appIcon = None # The full path to the novelwriter icon file
|
||||||
self.appIcon = None # The full path to the novelwriter icon file
|
self.assetPath = None # The full path to the novelwriter/assets folder
|
||||||
self.assetPath = None # The full path to the novelwriter/assets folder
|
self.pdfDocs = None # The location of the PDF manual, if it exists
|
||||||
self.pdfDocs = None # The location of the PDF manual, if it exists
|
|
||||||
|
|
||||||
# Runtime Settings and Variables
|
# Runtime Settings and Variables
|
||||||
|
self.hasError = False # True if the config class encountered an error
|
||||||
|
self.errData = [] # List of error messages
|
||||||
self.confChanged = False # True whenever the config has chenged, false after save
|
self.confChanged = False # True whenever the config has chenged, false after save
|
||||||
|
|
||||||
# General
|
# General
|
||||||
@@ -94,14 +91,14 @@ class Config:
|
|||||||
self.qtTrans = {}
|
self.qtTrans = {}
|
||||||
|
|
||||||
# Sizes
|
# Sizes
|
||||||
self.winGeometry = [1200, 650]
|
self.winGeometry = [1200, 650]
|
||||||
self.prefGeometry = [700, 615]
|
self.prefGeometry = [700, 615]
|
||||||
self.projColWidth = [200, 60, 140]
|
self.projColWidth = [200, 60, 140]
|
||||||
self.mainPanePos = [300, 800]
|
self.mainPanePos = [300, 800]
|
||||||
self.docPanePos = [400, 400]
|
self.docPanePos = [400, 400]
|
||||||
self.viewPanePos = [500, 150]
|
self.viewPanePos = [500, 150]
|
||||||
self.outlnPanePos = [500, 150]
|
self.outlnPanePos = [500, 150]
|
||||||
self.isFullScreen = False
|
self.isFullScreen = False
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
self.hideVScroll = False # Hide vertical scroll bars on main widgets
|
self.hideVScroll = False # Hide vertical scroll bars on main widgets
|
||||||
@@ -220,8 +217,8 @@ class Config:
|
|||||||
self.osUnknown = True
|
self.osUnknown = True
|
||||||
|
|
||||||
# Other System Info
|
# Other System Info
|
||||||
self.hostName = "Unknown"
|
self.hostName = QSysInfo.machineHostName()
|
||||||
self.kernelVer = "Unknown"
|
self.kernelVer = QSysInfo.kernelVersion()
|
||||||
|
|
||||||
# Packages
|
# Packages
|
||||||
self.hasEnchant = False # The pyenchant package
|
self.hasEnchant = False # The pyenchant package
|
||||||
@@ -264,31 +261,15 @@ class Config:
|
|||||||
self.confPath = confPath
|
self.confPath = confPath
|
||||||
|
|
||||||
if dataPath is None:
|
if dataPath is None:
|
||||||
if self.verQtValue >= 50400:
|
dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
|
||||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
|
|
||||||
else:
|
|
||||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
|
|
||||||
self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle)
|
self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle)
|
||||||
else:
|
else:
|
||||||
logger.info("Setting data path from alternative path: %s", dataPath)
|
logger.info("Setting data path from alternative path: %s", dataPath)
|
||||||
self.dataPath = dataPath
|
self.dataPath = dataPath
|
||||||
|
|
||||||
logger.verbose("Config path: %s", self.confPath)
|
logger.debug("Config path: %s", self.confPath)
|
||||||
logger.verbose("Data path: %s", self.dataPath)
|
logger.debug("Data path: %s", self.dataPath)
|
||||||
|
|
||||||
# Check Data Path Subdirs
|
|
||||||
dataDirs = ["syntax", "themes"]
|
|
||||||
for dataDir in dataDirs:
|
|
||||||
dirPath = os.path.join(self.dataPath, dataDir)
|
|
||||||
if not os.path.isdir(dirPath):
|
|
||||||
try:
|
|
||||||
os.mkdir(dirPath)
|
|
||||||
logger.info("Created folder: %s", dirPath)
|
|
||||||
except Exception:
|
|
||||||
logger.error("Could not create folder: %s", dirPath)
|
|
||||||
logException()
|
|
||||||
|
|
||||||
self.confFile = self.appHandle+".conf"
|
|
||||||
self.lastPath = os.path.expanduser("~")
|
self.lastPath = os.path.expanduser("~")
|
||||||
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
||||||
self.appRoot = os.path.abspath(os.path.join(self.appPath, os.path.pardir))
|
self.appRoot = os.path.abspath(os.path.join(self.appPath, os.path.pardir))
|
||||||
@@ -308,50 +289,33 @@ class Config:
|
|||||||
self.nwLangPath = os.path.join(self.assetPath, "i18n")
|
self.nwLangPath = os.path.join(self.assetPath, "i18n")
|
||||||
|
|
||||||
logger.debug("Assets: %s", self.assetPath)
|
logger.debug("Assets: %s", self.assetPath)
|
||||||
logger.verbose("App path: %s", self.appPath)
|
logger.debug("App path: %s", self.appPath)
|
||||||
logger.verbose("Last path: %s", self.lastPath)
|
logger.debug("Last path: %s", self.lastPath)
|
||||||
|
|
||||||
# If the config folder does not exist, create it.
|
# If the config and data folders don't not exist, create them
|
||||||
# This assumes that the os config folder itself exists.
|
# This assumes that the os config and data folders exist
|
||||||
if not os.path.isdir(self.confPath):
|
if not ensureFolder(self.confPath, errLog=self.errData):
|
||||||
try:
|
self.hasError = True
|
||||||
os.mkdir(self.confPath)
|
self.confPath = None
|
||||||
except Exception as exc:
|
|
||||||
logger.error("Could not create folder: %s", self.confPath)
|
if not ensureFolder(self.dataPath, errLog=self.errData):
|
||||||
logException()
|
self.hasError = True
|
||||||
self.hasError = True
|
self.dataPath = None
|
||||||
self.errData.append("Could not create folder: %s" % self.confPath)
|
|
||||||
self.errData.append(formatException(exc))
|
# We don't error on these failing since they are not essential
|
||||||
self.confPath = None
|
if self.dataPath is not None:
|
||||||
|
ensureFolder("syntax", parent=self.dataPath)
|
||||||
|
ensureFolder("themes", parent=self.dataPath)
|
||||||
|
|
||||||
# Check if config file exists
|
# Check if config file exists
|
||||||
if self.confPath is not None:
|
if self.confPath is not None:
|
||||||
if os.path.isfile(os.path.join(self.confPath, self.confFile)):
|
if os.path.isfile(os.path.join(self.confPath, nwFiles.CONF_FILE)):
|
||||||
# If it exists, load it
|
# If it exists, load it
|
||||||
self.loadConfig()
|
self.loadConfig()
|
||||||
else:
|
else:
|
||||||
# If it does not exist, save a copy of the default values
|
# If it does not exist, save a copy of the default values
|
||||||
self.saveConfig()
|
self.saveConfig()
|
||||||
|
|
||||||
# If the data folder does not exist, create it.
|
|
||||||
# This assumes that the os data folder itself exists.
|
|
||||||
if self.dataPath is not None:
|
|
||||||
if not os.path.isdir(self.dataPath):
|
|
||||||
try:
|
|
||||||
os.mkdir(self.dataPath)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error("Could not create folder: %s", self.dataPath)
|
|
||||||
logException()
|
|
||||||
self.hasError = True
|
|
||||||
self.errData.append("Could not create folder: %s" % self.dataPath)
|
|
||||||
self.errData.append(formatException(exc))
|
|
||||||
self.dataPath = None
|
|
||||||
|
|
||||||
# Host and Kernel
|
|
||||||
if self.verQtValue >= 50600:
|
|
||||||
self.hostName = QSysInfo.machineHostName()
|
|
||||||
self.kernelVer = QSysInfo.kernelVersion()
|
|
||||||
|
|
||||||
# Load recent projects cache
|
# Load recent projects cache
|
||||||
self.loadRecentCache()
|
self.loadRecentCache()
|
||||||
|
|
||||||
@@ -397,7 +361,7 @@ class Config:
|
|||||||
|
|
||||||
def listLanguages(self, lngSet):
|
def listLanguages(self, lngSet):
|
||||||
"""List localisation files in the i18n folder. The default GUI
|
"""List localisation files in the i18n folder. The default GUI
|
||||||
language 'en_GB' is British English.
|
language is British English (en_GB).
|
||||||
"""
|
"""
|
||||||
if lngSet == self.LANG_NW:
|
if lngSet == self.LANG_NW:
|
||||||
fPre = "nw_"
|
fPre = "nw_"
|
||||||
@@ -430,7 +394,7 @@ class Config:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
theConf = NWConfigParser()
|
theConf = NWConfigParser()
|
||||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
cnfPath = os.path.join(self.confPath, nwFiles.CONF_FILE)
|
||||||
try:
|
try:
|
||||||
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
||||||
theConf.read_file(inFile)
|
theConf.read_file(inFile)
|
||||||
@@ -653,7 +617,7 @@ class Config:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Write config file
|
# Write config file
|
||||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
cnfPath = os.path.join(self.confPath, nwFiles.CONF_FILE)
|
||||||
try:
|
try:
|
||||||
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
||||||
theConf.write(outFile)
|
theConf.write(outFile)
|
||||||
@@ -738,7 +702,7 @@ class Config:
|
|||||||
"""
|
"""
|
||||||
if thePath in self.recentProj:
|
if thePath in self.recentProj:
|
||||||
del self.recentProj[thePath]
|
del self.recentProj[thePath]
|
||||||
logger.verbose("Removed recent: %s", thePath)
|
logger.debug("Removed recent: %s", thePath)
|
||||||
self.saveRecentCache()
|
self.saveRecentCache()
|
||||||
else:
|
else:
|
||||||
logger.error("Unknown recent: %s", thePath)
|
logger.error("Unknown recent: %s", thePath)
|
||||||
@@ -749,29 +713,6 @@ class Config:
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setConfPath(self, newPath):
|
|
||||||
"""Set the path and filename to the config file.
|
|
||||||
"""
|
|
||||||
if newPath is None:
|
|
||||||
return True
|
|
||||||
if not os.path.isfile(newPath):
|
|
||||||
logger.error("File not found, using default config path instead")
|
|
||||||
return False
|
|
||||||
self.confPath = os.path.dirname(newPath)
|
|
||||||
self.confFile = os.path.basename(newPath)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setDataPath(self, newPath):
|
|
||||||
"""Set the data path.
|
|
||||||
"""
|
|
||||||
if newPath is None:
|
|
||||||
return True
|
|
||||||
if not os.path.isdir(newPath):
|
|
||||||
logger.error("Path not found, using default data path instead")
|
|
||||||
return False
|
|
||||||
self.dataPath = os.path.abspath(newPath)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setLastPath(self, lastPath):
|
def setLastPath(self, lastPath):
|
||||||
"""Set the last used path (by the user).
|
"""Set the last used path (by the user).
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class nwHeaders:
|
|||||||
|
|
||||||
class nwFiles:
|
class nwFiles:
|
||||||
|
|
||||||
|
CONF_FILE = "novelwriter.conf"
|
||||||
PROJ_FILE = "nwProject.nwx"
|
PROJ_FILE = "nwProject.nwx"
|
||||||
PROJ_DICT = "wordlist.txt"
|
PROJ_DICT = "wordlist.txt"
|
||||||
PROJ_LOCK = "nwProject.lock"
|
PROJ_LOCK = "nwProject.lock"
|
||||||
@@ -157,6 +158,7 @@ class nwLabels:
|
|||||||
"doc_h1": QT_TRANSLATE_NOOP("Constant", "Novel Title Page"),
|
"doc_h1": QT_TRANSLATE_NOOP("Constant", "Novel Title Page"),
|
||||||
"doc_h2": QT_TRANSLATE_NOOP("Constant", "Novel Chapter"),
|
"doc_h2": QT_TRANSLATE_NOOP("Constant", "Novel Chapter"),
|
||||||
"doc_h3": QT_TRANSLATE_NOOP("Constant", "Novel Scene"),
|
"doc_h3": QT_TRANSLATE_NOOP("Constant", "Novel Scene"),
|
||||||
|
"doc_h4": QT_TRANSLATE_NOOP("Constant", "Novel Section"),
|
||||||
"note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
"note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
|
||||||
}
|
}
|
||||||
KEY_NAME = {
|
KEY_NAME = {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from novelwriter.core.doctools import DocMerger, DocSplitter
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
from novelwriter.core.index import countWords
|
from novelwriter.core.index import countWords
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
@@ -28,6 +29,8 @@ from novelwriter.core.toodt import ToOdt
|
|||||||
from novelwriter.core.tomd import ToMarkdown
|
from novelwriter.core.tomd import ToMarkdown
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"DocMerger",
|
||||||
|
"DocSplitter",
|
||||||
"countWords",
|
"countWords",
|
||||||
"NWDoc",
|
"NWDoc",
|
||||||
"NWProject",
|
"NWProject",
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Project Document Tools
|
||||||
|
====================================
|
||||||
|
A collection of tools to create and manipulate documents
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2022-10-02 [2.0b1] DocMerger
|
||||||
|
Created: 2022-10-11 [2.0b1] DocSplitter
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2022, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from novelwriter.common import minmax
|
||||||
|
from novelwriter.core.document import NWDoc
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DocMerger:
|
||||||
|
|
||||||
|
def __init__(self, theProject):
|
||||||
|
|
||||||
|
self.theProject = theProject
|
||||||
|
|
||||||
|
self._error = ""
|
||||||
|
self._targetDoc = None
|
||||||
|
self._targetText = []
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def getError(self):
|
||||||
|
"""Return any collected errors.
|
||||||
|
"""
|
||||||
|
return self._error
|
||||||
|
|
||||||
|
def setTargetDoc(self, tHandle):
|
||||||
|
"""Set the target document for the merging. Calling this
|
||||||
|
function resets the class.
|
||||||
|
"""
|
||||||
|
self._targetDoc = tHandle
|
||||||
|
self._targetText = []
|
||||||
|
return
|
||||||
|
|
||||||
|
def newTargetDoc(self, srcHandle, docLabel):
|
||||||
|
"""Create a barnd new target document based on a source handle
|
||||||
|
and a new doc label. Calling this function resets the class.
|
||||||
|
"""
|
||||||
|
srcItem = self.theProject.tree[srcHandle]
|
||||||
|
if srcItem is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
newHandle = self.theProject.newFile(docLabel, srcItem.itemParent)
|
||||||
|
newItem = self.theProject.tree[newHandle]
|
||||||
|
newItem.setLayout(srcItem.itemLayout)
|
||||||
|
newItem.setStatus(srcItem.itemStatus)
|
||||||
|
newItem.setImport(srcItem.itemImport)
|
||||||
|
|
||||||
|
self._targetDoc = newHandle
|
||||||
|
self._targetText = []
|
||||||
|
|
||||||
|
return newHandle
|
||||||
|
|
||||||
|
def appendText(self, srcHandle, addComment, cmtPrefix):
|
||||||
|
"""Append text from an existing document to the text buffer.
|
||||||
|
"""
|
||||||
|
srcItem = self.theProject.tree[srcHandle]
|
||||||
|
if srcItem is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
inDoc = NWDoc(self.theProject, srcHandle)
|
||||||
|
docText = (inDoc.readDocument() or "").rstrip("\n")
|
||||||
|
|
||||||
|
if addComment:
|
||||||
|
docInfo = srcItem.describeMe()
|
||||||
|
docSt, _ = srcItem.getImportStatus(incIcon=False)
|
||||||
|
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
|
||||||
|
docText = cmtLine + docText
|
||||||
|
|
||||||
|
self._targetText.append(docText)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def writeTargetDoc(self):
|
||||||
|
"""Write the accumulated text into the designated target
|
||||||
|
document, appending any existing text.
|
||||||
|
"""
|
||||||
|
if self._targetDoc is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
outDoc = NWDoc(self.theProject, self._targetDoc)
|
||||||
|
docText = (outDoc.readDocument() or "").rstrip("\n")
|
||||||
|
if docText:
|
||||||
|
self._targetText.insert(0, docText)
|
||||||
|
|
||||||
|
status = outDoc.writeDocument("\n\n".join(self._targetText) + "\n\n")
|
||||||
|
if not status:
|
||||||
|
self._error = outDoc.getError()
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
# END Class DocMerger
|
||||||
|
|
||||||
|
|
||||||
|
class DocSplitter:
|
||||||
|
|
||||||
|
def __init__(self, theProject, sHandle):
|
||||||
|
|
||||||
|
self.theProject = theProject
|
||||||
|
|
||||||
|
self._error = ""
|
||||||
|
self._parHandle = None
|
||||||
|
self._srcHandle = None
|
||||||
|
self._srcItem = None
|
||||||
|
|
||||||
|
self._inFolder = False
|
||||||
|
self._rawData = []
|
||||||
|
|
||||||
|
srcItem = self.theProject.tree[sHandle]
|
||||||
|
if srcItem is not None and srcItem.isFileType():
|
||||||
|
self._srcHandle = sHandle
|
||||||
|
self._srcItem = srcItem
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def getError(self):
|
||||||
|
"""Return any collected errors.
|
||||||
|
"""
|
||||||
|
return self._error
|
||||||
|
|
||||||
|
def setParentItem(self, pHandle):
|
||||||
|
"""Set the item that will be the top level parent item for the
|
||||||
|
new documents.
|
||||||
|
"""
|
||||||
|
self._parHandle = pHandle
|
||||||
|
self._inFolder = False
|
||||||
|
return
|
||||||
|
|
||||||
|
def newParentFolder(self, pHandle, folderLabel):
|
||||||
|
"""Create a new folder that will be the top level parent item
|
||||||
|
for the new documents.
|
||||||
|
"""
|
||||||
|
if self._srcItem is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
newHandle = self.theProject.newFolder(folderLabel, pHandle)
|
||||||
|
newItem = self.theProject.tree[newHandle]
|
||||||
|
newItem.setStatus(self._srcItem.itemStatus)
|
||||||
|
newItem.setImport(self._srcItem.itemImport)
|
||||||
|
|
||||||
|
self._parHandle = newHandle
|
||||||
|
self._inFolder = True
|
||||||
|
|
||||||
|
return newHandle
|
||||||
|
|
||||||
|
def splitDocument(self, splitData, splitText):
|
||||||
|
"""Loop through the split data record and perform the split job.
|
||||||
|
"""
|
||||||
|
self._rawData = []
|
||||||
|
buffer = splitText.copy()
|
||||||
|
for lineNo, hLevel, hLabel in reversed(splitData):
|
||||||
|
chunk = buffer[lineNo:]
|
||||||
|
buffer = buffer[:lineNo]
|
||||||
|
self._rawData.insert(0, (chunk, hLevel, hLabel))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def writeDocuments(self, docHierarchy):
|
||||||
|
"""An iterator that will write each document in the buffer, and
|
||||||
|
return its new handle, parent handle, and sibling handle.
|
||||||
|
"""
|
||||||
|
if self._srcHandle is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
pHandle = self._parHandle
|
||||||
|
nHandle = self._parHandle if self._inFolder else self._srcHandle
|
||||||
|
hHandle = [self._parHandle, None, None, None, None]
|
||||||
|
|
||||||
|
pLevel = 0
|
||||||
|
for docText, hLevel, docLabel in self._rawData:
|
||||||
|
|
||||||
|
hLevel = minmax(hLevel, 1, 4)
|
||||||
|
if pLevel == 0:
|
||||||
|
pLevel = hLevel
|
||||||
|
|
||||||
|
if docHierarchy:
|
||||||
|
if hLevel == 1:
|
||||||
|
pHandle = self._parHandle
|
||||||
|
elif hLevel == 2:
|
||||||
|
pHandle = hHandle[1] or hHandle[0]
|
||||||
|
elif hLevel == 3:
|
||||||
|
pHandle = hHandle[2] or hHandle[1] or hHandle[0]
|
||||||
|
elif hLevel == 4:
|
||||||
|
pHandle = hHandle[3] or hHandle[2] or hHandle[1] or hHandle[0]
|
||||||
|
|
||||||
|
if hLevel < pLevel:
|
||||||
|
nHandle = hHandle[hLevel] or hHandle[0]
|
||||||
|
elif hLevel > pLevel:
|
||||||
|
nHandle = pHandle
|
||||||
|
|
||||||
|
dHandle = self.theProject.newFile(docLabel, pHandle)
|
||||||
|
hHandle[hLevel] = dHandle
|
||||||
|
|
||||||
|
outDoc = NWDoc(self.theProject, dHandle)
|
||||||
|
status = outDoc.writeDocument("\n".join(docText))
|
||||||
|
if not status:
|
||||||
|
self._error = outDoc.getError()
|
||||||
|
|
||||||
|
yield status, dHandle, nHandle
|
||||||
|
|
||||||
|
hHandle[hLevel] = dHandle
|
||||||
|
nHandle = dHandle
|
||||||
|
pLevel = hLevel
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class DocSplitter
|
||||||
@@ -33,7 +33,7 @@ from novelwriter.common import isHandle, sha256sum
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWDoc():
|
class NWDoc:
|
||||||
|
|
||||||
def __init__(self, theProject, theHandle):
|
def __init__(self, theProject, theHandle):
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class NWIndex:
|
|||||||
|
|
||||||
self._indexChange = round(time())
|
self._indexChange = round(time())
|
||||||
|
|
||||||
logger.verbose("Index loaded in %.3f ms", (time() - tStart)*1000)
|
logger.debug("Index loaded in %.3f ms", (time() - tStart)*1000)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ class NWIndex:
|
|||||||
logException()
|
logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.verbose("Index saved in %.3f ms", (time() - tStart)*1000)
|
logger.debug("Index saved in %.3f ms", (time() - tStart)*1000)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -243,20 +243,43 @@ class NWIndex:
|
|||||||
if theItem.itemParent is None:
|
if theItem.itemParent is None:
|
||||||
logger.info("Not indexing orphaned item '%s'", tHandle)
|
logger.info("Not indexing orphaned item '%s'", tHandle)
|
||||||
return False
|
return False
|
||||||
if theItem.isInactive():
|
|
||||||
logger.debug("Not indexing inactive item '%s'", tHandle)
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.debug("Indexing item with handle '%s'", tHandle)
|
logger.debug("Indexing item with handle '%s'", tHandle)
|
||||||
|
if theItem.isInactive():
|
||||||
|
self._scanInactive(theItem, theText)
|
||||||
|
else:
|
||||||
|
self._scanActive(tHandle, theItem, theText, itemTags)
|
||||||
|
|
||||||
# Scan the text content
|
# Update timestamps for index changes
|
||||||
|
nowTime = round(time())
|
||||||
|
self._indexChange = nowTime
|
||||||
|
self._rootChange[theItem.itemRoot] = nowTime
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Indexer Helpers
|
||||||
|
##
|
||||||
|
|
||||||
|
def _scanActive(self, tHandle, theItem, theText, itemTags):
|
||||||
|
"""Scan an active document for meta data.
|
||||||
|
"""
|
||||||
nTitle = 0
|
nTitle = 0
|
||||||
|
findHeader = True
|
||||||
theLines = theText.splitlines()
|
theLines = theText.splitlines()
|
||||||
|
|
||||||
for nLine, aLine in enumerate(theLines, start=1):
|
for nLine, aLine in enumerate(theLines, start=1):
|
||||||
|
|
||||||
if len(aLine.strip()) == 0:
|
if len(aLine.strip()) == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if aLine.startswith("#"):
|
if aLine.startswith("#"):
|
||||||
|
if findHeader:
|
||||||
|
hDepth, _ = self._splitHeading(aLine)
|
||||||
|
if hDepth != "H0":
|
||||||
|
theItem.setMainHeading(hDepth)
|
||||||
|
findHeader = False
|
||||||
|
|
||||||
isTitle = self._indexTitle(tHandle, aLine, nLine)
|
isTitle = self._indexTitle(tHandle, aLine, nLine)
|
||||||
if isTitle and nLine > 0:
|
if isTitle and nLine > 0:
|
||||||
if nTitle > 0:
|
if nTitle > 0:
|
||||||
@@ -289,48 +312,49 @@ class NWIndex:
|
|||||||
# Prune no longer used tags
|
# Prune no longer used tags
|
||||||
for tTag, isActive in itemTags.items():
|
for tTag, isActive in itemTags.items():
|
||||||
if not isActive:
|
if not isActive:
|
||||||
logger.verbose("Deleting removed tag '%s'", tTag)
|
logger.debug("Deleting removed tag '%s'", tTag)
|
||||||
del self._tagsIndex[tTag]
|
del self._tagsIndex[tTag]
|
||||||
|
|
||||||
# Update timestamps for index changes
|
return
|
||||||
nowTime = round(time())
|
|
||||||
self._indexChange = nowTime
|
|
||||||
self._rootChange[theItem.itemRoot] = nowTime
|
|
||||||
|
|
||||||
return True
|
def _scanInactive(self, theItem, theText):
|
||||||
|
"""Scan an inactive document for meta data.
|
||||||
|
"""
|
||||||
|
for aLine in theText.splitlines():
|
||||||
|
if aLine.startswith("#"):
|
||||||
|
hDepth, _ = self._splitHeading(aLine)
|
||||||
|
if hDepth != "H0":
|
||||||
|
theItem.setMainHeading(hDepth)
|
||||||
|
break
|
||||||
|
return
|
||||||
|
|
||||||
##
|
def _splitHeading(self, aLine):
|
||||||
# Internal Indexer Helpers
|
"""Split a heading into its header level and text value.
|
||||||
##
|
"""
|
||||||
|
if aLine.startswith("# "):
|
||||||
|
return "H1", aLine[2:].strip()
|
||||||
|
elif aLine.startswith("## "):
|
||||||
|
return "H2", aLine[3:].strip()
|
||||||
|
elif aLine.startswith("### "):
|
||||||
|
return "H3", aLine[4:].strip()
|
||||||
|
elif aLine.startswith("#### "):
|
||||||
|
return "H4", aLine[5:].strip()
|
||||||
|
elif aLine.startswith("#! "):
|
||||||
|
return "H1", aLine[3:].strip()
|
||||||
|
elif aLine.startswith("##! "):
|
||||||
|
return "H2", aLine[4:].strip()
|
||||||
|
return "H0", ""
|
||||||
|
|
||||||
def _indexTitle(self, tHandle, aLine, nTitle):
|
def _indexTitle(self, tHandle, aLine, nTitle):
|
||||||
"""Save information about the title and its location in the
|
"""Save information about the title and its location in the
|
||||||
file to the index.
|
file to the index.
|
||||||
"""
|
"""
|
||||||
if aLine.startswith("# "):
|
hDepth, hText = self._splitHeading(aLine)
|
||||||
hDepth = "H1"
|
if hDepth == "H0":
|
||||||
hText = aLine[2:].strip()
|
|
||||||
elif aLine.startswith("## "):
|
|
||||||
hDepth = "H2"
|
|
||||||
hText = aLine[3:].strip()
|
|
||||||
elif aLine.startswith("### "):
|
|
||||||
hDepth = "H3"
|
|
||||||
hText = aLine[4:].strip()
|
|
||||||
elif aLine.startswith("#### "):
|
|
||||||
hDepth = "H4"
|
|
||||||
hText = aLine[5:].strip()
|
|
||||||
elif aLine.startswith("#! "):
|
|
||||||
hDepth = "H1"
|
|
||||||
hText = aLine[3:].strip()
|
|
||||||
elif aLine.startswith("##! "):
|
|
||||||
hDepth = "H2"
|
|
||||||
hText = aLine[4:].strip()
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
sTitle = f"T{nTitle:06d}"
|
sTitle = f"T{nTitle:06d}"
|
||||||
self._itemIndex.addItemHeading(tHandle, sTitle, hDepth, hText)
|
self._itemIndex.addItemHeading(tHandle, sTitle, hDepth, hText)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _indexWordCounts(self, tHandle, theText, nTitle):
|
def _indexWordCounts(self, tHandle, theText, nTitle):
|
||||||
@@ -493,18 +517,15 @@ class NWIndex:
|
|||||||
for sTitle, hItem in self._itemIndex.iterItemHeaders(tHandle)
|
for sTitle, hItem in self._itemIndex.iterItemHeaders(tHandle)
|
||||||
]
|
]
|
||||||
|
|
||||||
def getHandleHeaderLevel(self, tHandle):
|
def getTableOfContents(self, rootHandle, maxDepth, skipExcl=True):
|
||||||
"""Get the header level of the first header of a handle.
|
|
||||||
"""
|
|
||||||
return self._itemIndex.mainItemHeader(tHandle)
|
|
||||||
|
|
||||||
def getTableOfContents(self, maxDepth, skipExcl=True):
|
|
||||||
"""Generate a table of contents up to a maximum depth.
|
"""Generate a table of contents up to a maximum depth.
|
||||||
"""
|
"""
|
||||||
tOrder = []
|
tOrder = []
|
||||||
tData = {}
|
tData = {}
|
||||||
pKey = None
|
pKey = None
|
||||||
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
|
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(
|
||||||
|
rootHandle=rootHandle, skipExcl=skipExcl
|
||||||
|
):
|
||||||
tKey = f"{tHandle}:{sTitle}"
|
tKey = f"{tHandle}:{sTitle}"
|
||||||
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
|
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0)
|
||||||
if iLevel > maxDepth:
|
if iLevel > maxDepth:
|
||||||
@@ -647,23 +668,17 @@ class TagsIndex:
|
|||||||
def tagHandle(self, tagKey):
|
def tagHandle(self, tagKey):
|
||||||
"""Get the handle of a given tag.
|
"""Get the handle of a given tag.
|
||||||
"""
|
"""
|
||||||
if tagKey in self._tags:
|
return self._tags.get(tagKey, {}).get("handle", None)
|
||||||
return self._tags.get(tagKey).get("handle")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def tagHeading(self, tagKey):
|
def tagHeading(self, tagKey):
|
||||||
"""Get the heading of a given tag.
|
"""Get the heading of a given tag.
|
||||||
"""
|
"""
|
||||||
if tagKey in self._tags:
|
return self._tags.get(tagKey, {}).get("heading", nwHeaders.TT_NONE)
|
||||||
return self._tags.get(tagKey).get("heading")
|
|
||||||
return nwHeaders.TT_NONE
|
|
||||||
|
|
||||||
def tagClass(self, tagKey):
|
def tagClass(self, tagKey):
|
||||||
"""Get the class of a given tag.
|
"""Get the class of a given tag.
|
||||||
"""
|
"""
|
||||||
if tagKey in self._tags:
|
return self._tags.get(tagKey, {}).get("class", None)
|
||||||
return self._tags.get(tagKey).get("class")
|
|
||||||
return None
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Pack/Unpack
|
# Pack/Unpack
|
||||||
@@ -755,13 +770,6 @@ class ItemIndex:
|
|||||||
self._items[tHandle] = IndexItem(tHandle, tItem)
|
self._items[tHandle] = IndexItem(tHandle, tItem)
|
||||||
return
|
return
|
||||||
|
|
||||||
def mainItemHeader(self, tHandle):
|
|
||||||
"""Return the primary item header for an item.
|
|
||||||
"""
|
|
||||||
if tHandle in self._items:
|
|
||||||
return self._items[tHandle].level
|
|
||||||
return "H0"
|
|
||||||
|
|
||||||
def allItemTags(self, tHandle):
|
def allItemTags(self, tHandle):
|
||||||
"""Get all tags set for headings of an item.
|
"""Get all tags set for headings of an item.
|
||||||
"""
|
"""
|
||||||
@@ -794,7 +802,7 @@ class ItemIndex:
|
|||||||
continue
|
continue
|
||||||
if tItem.isNoteLayout():
|
if tItem.isNoteLayout():
|
||||||
continue
|
continue
|
||||||
if skipExcl and not tItem.isExported:
|
if skipExcl and not tItem.isActive:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tHandle = tItem.itemHandle
|
tHandle = tItem.itemHandle
|
||||||
@@ -821,7 +829,6 @@ class ItemIndex:
|
|||||||
"""
|
"""
|
||||||
if tHandle in self._items:
|
if tHandle in self._items:
|
||||||
tItem = self._items[tHandle]
|
tItem = self._items[tHandle]
|
||||||
tItem.updateLevel(hDepth)
|
|
||||||
tItem.addHeading(IndexHeading(sTitle, hDepth, hText))
|
tItem.addHeading(IndexHeading(sTitle, hDepth, hText))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -897,7 +904,6 @@ class IndexItem:
|
|||||||
def __init__(self, tHandle, tItem):
|
def __init__(self, tHandle, tItem):
|
||||||
self._handle = tHandle
|
self._handle = tHandle
|
||||||
self._item = tItem
|
self._item = tItem
|
||||||
self._level = "H0"
|
|
||||||
self._headings = {}
|
self._headings = {}
|
||||||
self._index = 0
|
self._index = 0
|
||||||
|
|
||||||
@@ -917,21 +923,10 @@ class IndexItem:
|
|||||||
def item(self):
|
def item(self):
|
||||||
return self._item
|
return self._item
|
||||||
|
|
||||||
@property
|
|
||||||
def level(self):
|
|
||||||
return self._level
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateLevel(self, level):
|
|
||||||
"""Set the level only if it has not already been set.
|
|
||||||
"""
|
|
||||||
if self._level == "H0":
|
|
||||||
self._level = level
|
|
||||||
return
|
|
||||||
|
|
||||||
def addHeading(self, tHeading):
|
def addHeading(self, tHeading):
|
||||||
"""Add a heading to the item. Also remove the placeholder entry
|
"""Add a heading to the item. Also remove the placeholder entry
|
||||||
if it exists.
|
if it exists.
|
||||||
@@ -1011,7 +1006,7 @@ class IndexItem:
|
|||||||
if hRefs:
|
if hRefs:
|
||||||
refs[sTitle] = hRefs
|
refs[sTitle] = hRefs
|
||||||
|
|
||||||
data = {"level": self._level}
|
data = {}
|
||||||
data["headings"] = heads
|
data["headings"] = heads
|
||||||
if refs:
|
if refs:
|
||||||
data["references"] = refs
|
data["references"] = refs
|
||||||
@@ -1021,7 +1016,6 @@ class IndexItem:
|
|||||||
def unpackData(self, data):
|
def unpackData(self, data):
|
||||||
"""Unpack an item entry from the data.
|
"""Unpack an item entry from the data.
|
||||||
"""
|
"""
|
||||||
self._level = data.get("level", "H0")
|
|
||||||
references = data.get("references", {})
|
references = data.get("references", {})
|
||||||
for sTitle, hData in data.get("headings", {}).items():
|
for sTitle, hData in data.get("headings", {}).items():
|
||||||
if not isTitleTag(sTitle):
|
if not isTitleTag(sTitle):
|
||||||
@@ -1030,6 +1024,7 @@ class IndexItem:
|
|||||||
tHeading.unpackData(hData)
|
tHeading.unpackData(hData)
|
||||||
tHeading.unpackReferences(references.get(sTitle, {}))
|
tHeading.unpackReferences(references.get(sTitle, {}))
|
||||||
self.addHeading(tHeading)
|
self.addHeading(tHeading)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class IndexItem
|
# END Class IndexItem
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
|||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.constants import nwLabels, trConst
|
from novelwriter.constants import nwHeaders, nwLabels, trConst
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWItem():
|
class NWItem:
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
|
|
||||||
@@ -52,15 +52,16 @@ class NWItem():
|
|||||||
self._layout = nwItemLayout.NO_LAYOUT
|
self._layout = nwItemLayout.NO_LAYOUT
|
||||||
self._status = None
|
self._status = None
|
||||||
self._import = None
|
self._import = None
|
||||||
|
self._active = True
|
||||||
self._expanded = False
|
self._expanded = False
|
||||||
self._exported = True
|
|
||||||
|
|
||||||
# Document Meta Data
|
# Document Meta Data
|
||||||
self._charCount = 0 # Current character count
|
self._heading = "H0" # The main heading
|
||||||
self._wordCount = 0 # Current word count
|
self._charCount = 0 # Current character count
|
||||||
self._paraCount = 0 # Current paragraph count
|
self._wordCount = 0 # Current word count
|
||||||
self._cursorPos = 0 # Last cursor position
|
self._paraCount = 0 # Current paragraph count
|
||||||
self._initCount = 0 # Initial word count
|
self._cursorPos = 0 # Last cursor position
|
||||||
|
self._initCount = 0 # Initial word count
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -114,13 +115,17 @@ class NWItem():
|
|||||||
def itemImport(self):
|
def itemImport(self):
|
||||||
return self._import
|
return self._import
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isActive(self):
|
||||||
|
return self._active
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isExpanded(self):
|
def isExpanded(self):
|
||||||
return self._expanded
|
return self._expanded
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isExported(self):
|
def mainHeading(self):
|
||||||
return self._exported
|
return self._heading
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def charCount(self):
|
def charCount(self):
|
||||||
@@ -162,6 +167,7 @@ class NWItem():
|
|||||||
metaAttrib = {}
|
metaAttrib = {}
|
||||||
metaAttrib["expanded"] = str(self._expanded)
|
metaAttrib["expanded"] = str(self._expanded)
|
||||||
if self._type == nwItemType.FILE:
|
if self._type == nwItemType.FILE:
|
||||||
|
metaAttrib["mainHeading"] = str(self._heading)
|
||||||
metaAttrib["charCount"] = str(self._charCount)
|
metaAttrib["charCount"] = str(self._charCount)
|
||||||
metaAttrib["wordCount"] = str(self._wordCount)
|
metaAttrib["wordCount"] = str(self._wordCount)
|
||||||
metaAttrib["paraCount"] = str(self._paraCount)
|
metaAttrib["paraCount"] = str(self._paraCount)
|
||||||
@@ -171,7 +177,7 @@ class NWItem():
|
|||||||
nameAttrib["status"] = str(self._status)
|
nameAttrib["status"] = str(self._status)
|
||||||
nameAttrib["import"] = str(self._import)
|
nameAttrib["import"] = str(self._import)
|
||||||
if self._type == nwItemType.FILE:
|
if self._type == nwItemType.FILE:
|
||||||
nameAttrib["exported"] = str(self._exported)
|
nameAttrib["active"] = str(self._active)
|
||||||
|
|
||||||
xPack = etree.SubElement(xParent, "item", attrib=itemAttrib)
|
xPack = etree.SubElement(xParent, "item", attrib=itemAttrib)
|
||||||
self._subPack(xPack, "meta", attrib=metaAttrib)
|
self._subPack(xPack, "meta", attrib=metaAttrib)
|
||||||
@@ -202,6 +208,7 @@ class NWItem():
|
|||||||
for xValue in xItem:
|
for xValue in xItem:
|
||||||
if xValue.tag == "meta":
|
if xValue.tag == "meta":
|
||||||
self.setExpanded(xValue.attrib.get("expanded", False))
|
self.setExpanded(xValue.attrib.get("expanded", False))
|
||||||
|
self.setMainHeading(xValue.attrib.get("mainHeading", "H0"))
|
||||||
self.setCharCount(xValue.attrib.get("charCount", 0))
|
self.setCharCount(xValue.attrib.get("charCount", 0))
|
||||||
self.setWordCount(xValue.attrib.get("wordCount", 0))
|
self.setWordCount(xValue.attrib.get("wordCount", 0))
|
||||||
self.setParaCount(xValue.attrib.get("paraCount", 0))
|
self.setParaCount(xValue.attrib.get("paraCount", 0))
|
||||||
@@ -210,7 +217,11 @@ class NWItem():
|
|||||||
self.setName(xValue.text)
|
self.setName(xValue.text)
|
||||||
self.setStatus(xValue.attrib.get("status", None))
|
self.setStatus(xValue.attrib.get("status", None))
|
||||||
self.setImport(xValue.attrib.get("import", None))
|
self.setImport(xValue.attrib.get("import", None))
|
||||||
self.setExported(xValue.attrib.get("exported", True))
|
self.setActive(xValue.attrib.get("active", True))
|
||||||
|
|
||||||
|
# ToDo: Remove before 2.0 release. Only needed for 2.0 pre-releases.
|
||||||
|
if "exported" in xValue.attrib:
|
||||||
|
self.setActive(xValue.attrib.get("exported", True))
|
||||||
|
|
||||||
# Legacy Format (1.3 and earlier)
|
# Legacy Format (1.3 and earlier)
|
||||||
elif xValue.tag == "status":
|
elif xValue.tag == "status":
|
||||||
@@ -224,7 +235,7 @@ class NWItem():
|
|||||||
elif xValue.tag == "expanded":
|
elif xValue.tag == "expanded":
|
||||||
self.setExpanded(xValue.text)
|
self.setExpanded(xValue.text)
|
||||||
elif xValue.tag == "exported":
|
elif xValue.tag == "exported":
|
||||||
self.setExported(xValue.text)
|
self.setActive(xValue.text)
|
||||||
elif xValue.tag == "charCount":
|
elif xValue.tag == "charCount":
|
||||||
self.setCharCount(xValue.text)
|
self.setCharCount(xValue.text)
|
||||||
elif xValue.tag == "wordCount":
|
elif xValue.tag == "wordCount":
|
||||||
@@ -269,7 +280,7 @@ class NWItem():
|
|||||||
# Lookup Methods
|
# Lookup Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def describeMe(self, hLevel=None):
|
def describeMe(self):
|
||||||
"""Return a string description of the item.
|
"""Return a string description of the item.
|
||||||
"""
|
"""
|
||||||
descKey = "none"
|
descKey = "none"
|
||||||
@@ -279,12 +290,14 @@ class NWItem():
|
|||||||
descKey = "folder"
|
descKey = "folder"
|
||||||
elif self._type == nwItemType.FILE:
|
elif self._type == nwItemType.FILE:
|
||||||
if self._layout == nwItemLayout.DOCUMENT:
|
if self._layout == nwItemLayout.DOCUMENT:
|
||||||
if hLevel == "H1":
|
if self._heading == "H1":
|
||||||
descKey = "doc_h1"
|
descKey = "doc_h1"
|
||||||
elif hLevel == "H2":
|
elif self._heading == "H2":
|
||||||
descKey = "doc_h2"
|
descKey = "doc_h2"
|
||||||
elif hLevel == "H3":
|
elif self._heading == "H3":
|
||||||
descKey = "doc_h3"
|
descKey = "doc_h3"
|
||||||
|
elif self._heading == "H4":
|
||||||
|
descKey = "doc_h4"
|
||||||
else:
|
else:
|
||||||
descKey = "document"
|
descKey = "document"
|
||||||
elif self._layout == nwItemLayout.NOTE:
|
elif self._layout == nwItemLayout.NOTE:
|
||||||
@@ -292,16 +305,16 @@ class NWItem():
|
|||||||
|
|
||||||
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
|
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
|
||||||
|
|
||||||
def getImportStatus(self):
|
def getImportStatus(self, incIcon=True):
|
||||||
"""Return the relevant importance or status label and icon for
|
"""Return the relevant importance or status label and icon for
|
||||||
the current item based on its class.
|
the current item based on its class.
|
||||||
"""
|
"""
|
||||||
if self.isNovelLike():
|
if self.isNovelLike():
|
||||||
stName = self.theProject.statusItems.name(self._status)
|
stName = self.theProject.statusItems.name(self._status)
|
||||||
stIcon = self.theProject.statusItems.icon(self._status)
|
stIcon = self.theProject.statusItems.icon(self._status) if incIcon else None
|
||||||
else:
|
else:
|
||||||
stName = self.theProject.importItems.name(self._import)
|
stName = self.theProject.importItems.name(self._import)
|
||||||
stIcon = self.theProject.importItems.icon(self._import)
|
stIcon = self.theProject.importItems.icon(self._import) if incIcon else None
|
||||||
return stName, stIcon
|
return stName, stIcon
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -487,6 +500,15 @@ class NWItem():
|
|||||||
self._import = self.theProject.importItems.check(value)
|
self._import = self.theProject.importItems.check(value)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def setActive(self, state):
|
||||||
|
"""Set the export flag.
|
||||||
|
"""
|
||||||
|
if isinstance(state, str):
|
||||||
|
self._active = (state == str(True))
|
||||||
|
else:
|
||||||
|
self._active = (state is True)
|
||||||
|
return
|
||||||
|
|
||||||
def setExpanded(self, state):
|
def setExpanded(self, state):
|
||||||
"""Set the expanded status of an item in the project tree.
|
"""Set the expanded status of an item in the project tree.
|
||||||
"""
|
"""
|
||||||
@@ -496,19 +518,17 @@ class NWItem():
|
|||||||
self._expanded = (state is True)
|
self._expanded = (state is True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setExported(self, state):
|
|
||||||
"""Set the export flag.
|
|
||||||
"""
|
|
||||||
if isinstance(state, str):
|
|
||||||
self._exported = (state == str(True))
|
|
||||||
else:
|
|
||||||
self._exported = (state is True)
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Set Document Meta Data
|
# Set Document Meta Data
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def setMainHeading(self, value):
|
||||||
|
"""Set the main heading level.
|
||||||
|
"""
|
||||||
|
if value in nwHeaders.H_LEVEL:
|
||||||
|
self._heading = value
|
||||||
|
return
|
||||||
|
|
||||||
def setCharCount(self, count):
|
def setCharCount(self, count):
|
||||||
"""Set the character count, and ensure that it is an integer.
|
"""Set the character count, and ensure that it is an integer.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -40,30 +40,30 @@ VALID_MAP = {
|
|||||||
"GuiWritingStats": {
|
"GuiWritingStats": {
|
||||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||||
"widthCol3", "sortCol", "sortOrder", "incNovel", "incNotes",
|
"widthCol3", "sortCol", "sortOrder", "incNovel", "incNotes",
|
||||||
"hideZeros", "hideNegative", "groupByDay", "showIdleTime", "histMax"
|
"hideZeros", "hideNegative", "groupByDay", "showIdleTime", "histMax",
|
||||||
},
|
},
|
||||||
"GuiDocSplit": {"spLevel"},
|
"GuiDocSplit": {"spLevel", "intoFolder", "docHierarchy"},
|
||||||
"GuiBuildNovel": {
|
"GuiBuildNovel": {
|
||||||
"winWidth", "winHeight", "boxWidth", "docWidth", "hideScene",
|
"winWidth", "winHeight", "boxWidth", "docWidth", "hideScene",
|
||||||
"hideSection", "addNovel", "addNotes", "ignoreFlag", "justifyText",
|
"hideSection", "addNovel", "addNotes", "ignoreFlag", "justifyText",
|
||||||
"excludeBody", "textFont", "textSize", "lineHeight", "noStyling",
|
"excludeBody", "textFont", "textSize", "lineHeight", "noStyling",
|
||||||
"incSynopsis", "incComments", "incKeywords", "incBodyText",
|
"incSynopsis", "incComments", "incKeywords", "incBodyText",
|
||||||
"replaceTabs", "replaceUCode"
|
"replaceTabs", "replaceUCode", "rootFilter",
|
||||||
},
|
},
|
||||||
"GuiOutline": {"headerOrder", "columnWidth", "columnHidden"},
|
"GuiOutline": {"headerOrder", "columnWidth", "columnHidden"},
|
||||||
"GuiProjectSettings": {
|
"GuiProjectSettings": {
|
||||||
"winWidth", "winHeight", "replaceColW", "statusColW", "importColW"
|
"winWidth", "winHeight", "replaceColW", "statusColW", "importColW",
|
||||||
},
|
},
|
||||||
"GuiProjectDetails": {
|
"GuiProjectDetails": {
|
||||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||||
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble"
|
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
|
||||||
},
|
},
|
||||||
"GuiWordList": {"winWidth", "winHeight"},
|
"GuiWordList": {"winWidth", "winHeight"},
|
||||||
"GuiNovelView": {"lastCol"},
|
"GuiNovelView": {"lastCol"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class OptionState():
|
class OptionState:
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
@@ -188,8 +188,7 @@ class OptionState():
|
|||||||
the default value.
|
the default value.
|
||||||
"""
|
"""
|
||||||
if group in self._theState:
|
if group in self._theState:
|
||||||
if name in self._theState[group]:
|
return checkBool(self._theState[group].get(name, default), default)
|
||||||
return checkBool(self._theState[group].get(name, default), default)
|
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getEnum(self, group, name, lookup, default):
|
def getEnum(self, group, name, lookup, default):
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ from novelwriter.core.document import NWDoc
|
|||||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkString, checkBool, checkInt, isHandle, formatTimeStamp,
|
checkString, checkBool, checkInt, checkStringNone, isHandle, formatTimeStamp,
|
||||||
makeFileNameSafe, hexToInt, minmax, simplified
|
makeFileNameSafe, hexToInt, minmax, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.constants import trConst, nwFiles, nwLabels
|
from novelwriter.constants import trConst, nwFiles, nwLabels
|
||||||
@@ -52,7 +52,7 @@ from novelwriter.constants import trConst, nwFiles, nwLabels
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWProject():
|
class NWProject:
|
||||||
|
|
||||||
FILE_VERSION = "1.4" # The current project file format version
|
FILE_VERSION = "1.4" # The current project file format version
|
||||||
|
|
||||||
@@ -85,7 +85,6 @@ class NWProject():
|
|||||||
self.projDict = None # The spell check dictionary
|
self.projDict = None # The spell check dictionary
|
||||||
self.projSpell = None # The spell check language, if different than default
|
self.projSpell = None # The spell check language, if different than default
|
||||||
self.projLang = None # The project language, used for builds
|
self.projLang = None # The project language, used for builds
|
||||||
self.projFile = None # The file name of the project main XML file
|
|
||||||
self.projFiles = [] # A list of all files in the content folder on load
|
self.projFiles = [] # A list of all files in the content folder on load
|
||||||
|
|
||||||
# Project Meta
|
# Project Meta
|
||||||
@@ -176,7 +175,7 @@ class NWProject():
|
|||||||
self._projTree.updateItemData(newItem.itemHandle)
|
self._projTree.updateItemData(newItem.itemHandle)
|
||||||
return newItem.itemHandle
|
return newItem.itemHandle
|
||||||
|
|
||||||
def writeNewFile(self, tHandle, hLevel, isDocument):
|
def writeNewFile(self, tHandle, hLevel, isDocument, addText=""):
|
||||||
"""Write content to a new document after it is created. This
|
"""Write content to a new document after it is created. This
|
||||||
will not run if the file exists and is not empty.
|
will not run if the file exists and is not empty.
|
||||||
"""
|
"""
|
||||||
@@ -187,11 +186,11 @@ class NWProject():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
newDoc = NWDoc(self, tHandle)
|
newDoc = NWDoc(self, tHandle)
|
||||||
if newDoc.readDocument().strip():
|
if (newDoc.readDocument() or "").strip():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
hshText = "#"*minmax(hLevel, 1, 4)
|
hshText = "#"*minmax(hLevel, 1, 4)
|
||||||
newText = f"{hshText} {tItem.itemName}\n\n"
|
newText = f"{hshText} {tItem.itemName}\n\n{addText}"
|
||||||
if tItem.isNovelLike() and isDocument:
|
if tItem.isNovelLike() and isDocument:
|
||||||
tItem.setLayout(nwItemLayout.DOCUMENT)
|
tItem.setLayout(nwItemLayout.DOCUMENT)
|
||||||
else:
|
else:
|
||||||
@@ -202,6 +201,23 @@ class NWProject():
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def removeItem(self, tHandle):
|
||||||
|
"""Remove an item from the project. This will delete both the
|
||||||
|
project entry and a document file if it exists.
|
||||||
|
"""
|
||||||
|
if self._projTree.checkType(tHandle, nwItemType.FILE):
|
||||||
|
delDoc = NWDoc(self, tHandle)
|
||||||
|
if not delDoc.deleteDocument():
|
||||||
|
self.mainGui.makeAlert([
|
||||||
|
self.tr("Could not delete document file."), delDoc.getError()
|
||||||
|
], nwAlert.ERROR)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._projIndex.deleteHandle(tHandle)
|
||||||
|
del self._projTree[tHandle]
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def trashFolder(self):
|
def trashFolder(self):
|
||||||
"""Add the special trash root folder to the project.
|
"""Add the special trash root folder to the project.
|
||||||
"""
|
"""
|
||||||
@@ -243,7 +259,6 @@ class NWProject():
|
|||||||
self.projDict = None
|
self.projDict = None
|
||||||
self.projSpell = None
|
self.projSpell = None
|
||||||
self.projLang = None
|
self.projLang = None
|
||||||
self.projFile = nwFiles.PROJ_FILE
|
|
||||||
self.projFiles = []
|
self.projFiles = []
|
||||||
self.projName = ""
|
self.projName = ""
|
||||||
self.bookTitle = ""
|
self.bookTitle = ""
|
||||||
@@ -437,7 +452,7 @@ class NWProject():
|
|||||||
|
|
||||||
legacyList = [] # Cleanup is done later
|
legacyList = [] # Cleanup is done later
|
||||||
for projItem in os.listdir(self.projPath):
|
for projItem in os.listdir(self.projPath):
|
||||||
logger.verbose("Project contains: %s", projItem)
|
logger.debug("Project contains: %s", projItem)
|
||||||
if projItem.startswith("data_") and len(projItem) == 6:
|
if projItem.startswith("data_") and len(projItem) == 6:
|
||||||
legacyList.append(projItem)
|
legacyList.append(projItem)
|
||||||
|
|
||||||
@@ -457,7 +472,7 @@ class NWProject():
|
|||||||
self.clearProject()
|
self.clearProject()
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
logger.verbose("Project is not locked")
|
logger.debug("Project is not locked")
|
||||||
|
|
||||||
# Open The Project XML File
|
# Open The Project XML File
|
||||||
# =========================
|
# =========================
|
||||||
@@ -494,8 +509,8 @@ class NWProject():
|
|||||||
hexVersion = xRoot.attrib.get("hexVersion", "0x0")
|
hexVersion = xRoot.attrib.get("hexVersion", "0x0")
|
||||||
fileVersion = xRoot.attrib.get("fileVersion", self.tr("Unknown"))
|
fileVersion = xRoot.attrib.get("fileVersion", self.tr("Unknown"))
|
||||||
|
|
||||||
logger.verbose("XML root is '%s'", nwxRoot)
|
logger.debug("XML root is '%s'", nwxRoot)
|
||||||
logger.verbose("File version is '%s'", fileVersion)
|
logger.debug("File version is '%s'", fileVersion)
|
||||||
|
|
||||||
# Check File Type
|
# Check File Type
|
||||||
# ===============
|
# ===============
|
||||||
@@ -576,16 +591,16 @@ class NWProject():
|
|||||||
if xItem.text is None:
|
if xItem.text is None:
|
||||||
continue
|
continue
|
||||||
if xItem.tag == "name":
|
if xItem.tag == "name":
|
||||||
self.projName = checkString(simplified(xItem.text), "")
|
self.projName = simplified(checkString(xItem.text, ""))
|
||||||
logger.verbose("Working Title: '%s'", self.projName)
|
logger.info("Project Name: '%s'", self.projName)
|
||||||
elif xItem.tag == "title":
|
elif xItem.tag == "title":
|
||||||
self.bookTitle = checkString(simplified(xItem.text), "")
|
self.bookTitle = simplified(checkString(xItem.text, ""))
|
||||||
logger.verbose("Title is '%s'", self.bookTitle)
|
logger.info("Project Title: '%s'", self.bookTitle)
|
||||||
elif xItem.tag == "author":
|
elif xItem.tag == "author":
|
||||||
author = checkString(simplified(xItem.text), "")
|
author = simplified(checkString(xItem.text, ""))
|
||||||
if author:
|
if author:
|
||||||
self.bookAuthors.append(author)
|
self.bookAuthors.append(author)
|
||||||
logger.verbose("Author: '%s'", author)
|
logger.debug("Author: '%s'", author)
|
||||||
elif xItem.tag == "saveCount":
|
elif xItem.tag == "saveCount":
|
||||||
self.saveCount = checkInt(xItem.text, 0)
|
self.saveCount = checkInt(xItem.text, 0)
|
||||||
elif xItem.tag == "autoCount":
|
elif xItem.tag == "autoCount":
|
||||||
@@ -601,25 +616,25 @@ class NWProject():
|
|||||||
if xItem.tag == "doBackup":
|
if xItem.tag == "doBackup":
|
||||||
self.doBackup = checkBool(xItem.text, False)
|
self.doBackup = checkBool(xItem.text, False)
|
||||||
elif xItem.tag == "language":
|
elif xItem.tag == "language":
|
||||||
self.projLang = checkString(xItem.text, None, True)
|
self.projLang = checkStringNone(xItem.text, None)
|
||||||
elif xItem.tag == "spellCheck":
|
elif xItem.tag == "spellCheck":
|
||||||
self.spellCheck = checkBool(xItem.text, False)
|
self.spellCheck = checkBool(xItem.text, False)
|
||||||
elif xItem.tag == "spellLang":
|
elif xItem.tag == "spellLang":
|
||||||
self.projSpell = checkString(xItem.text, None, True)
|
self.projSpell = checkStringNone(xItem.text, None)
|
||||||
elif xItem.tag == "lastEdited":
|
elif xItem.tag == "lastEdited":
|
||||||
self.lastEdited = checkString(xItem.text, None, True)
|
self.lastEdited = checkStringNone(xItem.text, None)
|
||||||
elif xItem.tag == "lastViewed":
|
elif xItem.tag == "lastViewed":
|
||||||
self.lastViewed = checkString(xItem.text, None, True)
|
self.lastViewed = checkStringNone(xItem.text, None)
|
||||||
elif xItem.tag == "lastNovel":
|
elif xItem.tag == "lastNovel":
|
||||||
self.lastNovel = checkString(xItem.text, None, True)
|
self.lastNovel = checkStringNone(xItem.text, None)
|
||||||
elif xItem.tag == "lastOutline":
|
elif xItem.tag == "lastOutline":
|
||||||
self.lastOutline = checkString(xItem.text, None, True)
|
self.lastOutline = checkStringNone(xItem.text, None)
|
||||||
elif xItem.tag == "lastWordCount":
|
elif xItem.tag == "lastWordCount":
|
||||||
self.lastWCount = checkInt(xItem.text, 0, False)
|
self.lastWCount = checkInt(xItem.text, 0)
|
||||||
elif xItem.tag == "novelWordCount":
|
elif xItem.tag == "novelWordCount":
|
||||||
self.lastNovelWC = checkInt(xItem.text, 0, False)
|
self.lastNovelWC = checkInt(xItem.text, 0)
|
||||||
elif xItem.tag == "notesWordCount":
|
elif xItem.tag == "notesWordCount":
|
||||||
self.lastNotesWC = checkInt(xItem.text, 0, False)
|
self.lastNotesWC = checkInt(xItem.text, 0)
|
||||||
elif xItem.tag == "status":
|
elif xItem.tag == "status":
|
||||||
self.statusItems.unpackXML(xItem)
|
self.statusItems.unpackXML(xItem)
|
||||||
elif xItem.tag == "importance":
|
elif xItem.tag == "importance":
|
||||||
@@ -628,12 +643,12 @@ class NWProject():
|
|||||||
for xEntry in xItem:
|
for xEntry in xItem:
|
||||||
if xEntry.tag == "entry" and "key" in xEntry.attrib:
|
if xEntry.tag == "entry" and "key" in xEntry.attrib:
|
||||||
self.autoReplace[xEntry.attrib["key"]] = checkString(
|
self.autoReplace[xEntry.attrib["key"]] = checkString(
|
||||||
xEntry.text, None, False
|
xEntry.text, "ERROR"
|
||||||
)
|
)
|
||||||
elif xItem.tag == "titleFormat":
|
elif xItem.tag == "titleFormat":
|
||||||
titleFormat = self.titleFormat.copy()
|
titleFormat = self.titleFormat.copy()
|
||||||
for xEntry in xItem:
|
for xEntry in xItem:
|
||||||
titleFormat[xEntry.tag] = checkString(xEntry.text, "", False)
|
titleFormat[xEntry.tag] = checkString(xEntry.text, "")
|
||||||
self.setTitleFormat(titleFormat)
|
self.setTitleFormat(titleFormat)
|
||||||
|
|
||||||
elif xChild.tag == "content":
|
elif xChild.tag == "content":
|
||||||
@@ -663,7 +678,7 @@ class NWProject():
|
|||||||
# Check the project tree consistency
|
# Check the project tree consistency
|
||||||
for tItem in self._projTree:
|
for tItem in self._projTree:
|
||||||
tHandle = tItem.itemHandle
|
tHandle = tItem.itemHandle
|
||||||
logger.verbose("Checking item '%s'", tHandle)
|
logger.debug("Checking item '%s'", tHandle)
|
||||||
if not self._projTree.updateItemData(tHandle):
|
if not self._projTree.updateItemData(tHandle):
|
||||||
logger.error("There was a problem item '%s', and it has been removed", tHandle)
|
logger.error("There was a problem item '%s', and it has been removed", tHandle)
|
||||||
del self._projTree[tHandle] # The file will be re-added as orphaned
|
del self._projTree[tHandle] # The file will be re-added as orphaned
|
||||||
@@ -757,9 +772,9 @@ class NWProject():
|
|||||||
self._projTree.packXML(nwXML)
|
self._projTree.packXML(nwXML)
|
||||||
|
|
||||||
# Write the xml tree to file
|
# Write the xml tree to file
|
||||||
tempFile = os.path.join(self.projPath, self.projFile+"~")
|
tempFile = os.path.join(self.projPath, nwFiles.PROJ_FILE+"~")
|
||||||
saveFile = os.path.join(self.projPath, self.projFile)
|
saveFile = os.path.join(self.projPath, nwFiles.PROJ_FILE)
|
||||||
backFile = os.path.join(self.projPath, self.projFile[:-3]+"bak")
|
backFile = os.path.join(self.projPath, nwFiles.PROJ_FILE[:-3]+"bak")
|
||||||
try:
|
try:
|
||||||
with open(tempFile, mode="wb") as outFile:
|
with open(tempFile, mode="wb") as outFile:
|
||||||
outFile.write(etree.tostring(
|
outFile.write(etree.tostring(
|
||||||
@@ -799,7 +814,7 @@ class NWProject():
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def closeProject(self, idleTime=0):
|
def closeProject(self, idleTime=0.0):
|
||||||
"""Close the current project and clear all meta data.
|
"""Close the current project and clear all meta data.
|
||||||
"""
|
"""
|
||||||
logger.info("Closing project: %s", self.projPath)
|
logger.info("Closing project: %s", self.projPath)
|
||||||
@@ -1074,7 +1089,7 @@ class NWProject():
|
|||||||
def setSpellLang(self, theLang):
|
def setSpellLang(self, theLang):
|
||||||
"""Set the project-specific spell check language.
|
"""Set the project-specific spell check language.
|
||||||
"""
|
"""
|
||||||
theLang = checkString(theLang, None, True)
|
theLang = checkStringNone(theLang, None)
|
||||||
if self.projSpell != theLang:
|
if self.projSpell != theLang:
|
||||||
self.projSpell = theLang
|
self.projSpell = theLang
|
||||||
self.setProjectChanged(True)
|
self.setProjectChanged(True)
|
||||||
@@ -1084,7 +1099,7 @@ class NWProject():
|
|||||||
def setProjectLang(self, theLang):
|
def setProjectLang(self, theLang):
|
||||||
"""Set the project-specific language.
|
"""Set the project-specific language.
|
||||||
"""
|
"""
|
||||||
theLang = checkString(theLang, None, True)
|
theLang = checkStringNone(theLang, None)
|
||||||
if self.projLang != theLang:
|
if self.projLang != theLang:
|
||||||
self.projLang = theLang
|
self.projLang = theLang
|
||||||
self._loadProjectLocalisation()
|
self._loadProjectLocalisation()
|
||||||
@@ -1168,7 +1183,7 @@ class NWProject():
|
|||||||
information to the GUI statusbar.
|
information to the GUI statusbar.
|
||||||
"""
|
"""
|
||||||
self.projChanged = bValue
|
self.projChanged = bValue
|
||||||
self.mainGui.statusBar.doUpdateProjectStatus(bValue)
|
self.mainGui.mainStatus.doUpdateProjectStatus(bValue)
|
||||||
if bValue:
|
if bValue:
|
||||||
# If we've changed the project at all, this should be True
|
# If we've changed the project at all, this should be True
|
||||||
self.projAltered = True
|
self.projAltered = True
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from novelwriter.error import logException
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWSpellEnchant():
|
class NWSpellEnchant:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from novelwriter.common import checkInt, minmax, simplified
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWStatus():
|
class NWStatus:
|
||||||
|
|
||||||
STATUS = 1
|
STATUS = 1
|
||||||
IMPORT = 2
|
IMPORT = 2
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class ToHtml(Tokenizer):
|
|||||||
M_EBOOK = 2 # Tweak output for converting to epub
|
M_EBOOK = 2 # Tweak output for converting to epub
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
Tokenizer.__init__(self, theProject)
|
super().__init__(theProject)
|
||||||
|
|
||||||
self._genMode = self.M_EXPORT
|
self._genMode = self.M_EXPORT
|
||||||
self._cssStyles = True
|
self._cssStyles = True
|
||||||
@@ -107,7 +107,7 @@ class ToHtml(Tokenizer):
|
|||||||
"""Extend the auto-replace to also properly encode some unicode
|
"""Extend the auto-replace to also properly encode some unicode
|
||||||
characters into their respective HTML entities.
|
characters into their respective HTML entities.
|
||||||
"""
|
"""
|
||||||
Tokenizer.doPreProcessing(self)
|
super().doPreProcessing()
|
||||||
self._theText = self._theText.translate(self._trMap)
|
self._theText = self._theText.translate(self._trMap)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -304,16 +304,10 @@ class Tokenizer(ABC):
|
|||||||
if self._theItem is None:
|
if self._theItem is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._theText = ""
|
if theText is None:
|
||||||
if theText is not None:
|
theText = NWDoc(self.theProject, theHandle).readDocument() or ""
|
||||||
# If the text is set, just use that
|
|
||||||
self._theText = theText
|
self._theText = theText
|
||||||
else:
|
|
||||||
# Otherwise, load it from file
|
|
||||||
theDoc = NWDoc(self.theProject, theHandle)
|
|
||||||
theText = theDoc.readDocument()
|
|
||||||
if theText:
|
|
||||||
self._theText = theText
|
|
||||||
|
|
||||||
docSize = len(self._theText)
|
docSize = len(self._theText)
|
||||||
if docSize > nwConst.MAX_DOCSIZE:
|
if docSize > nwConst.MAX_DOCSIZE:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class ToMarkdown(Tokenizer):
|
|||||||
M_GH = 1 # GitHub Markdown
|
M_GH = 1 # GitHub Markdown
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
Tokenizer.__init__(self, theProject)
|
super().__init__(theProject)
|
||||||
|
|
||||||
self._genMode = self.M_STD
|
self._genMode = self.M_STD
|
||||||
self._fullMD = []
|
self._fullMD = []
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ M_DEL = ~X_DEL
|
|||||||
class ToOdt(Tokenizer):
|
class ToOdt(Tokenizer):
|
||||||
|
|
||||||
def __init__(self, theProject, isFlat):
|
def __init__(self, theProject, isFlat):
|
||||||
Tokenizer.__init__(self, theProject)
|
super().__init__(theProject)
|
||||||
|
|
||||||
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
||||||
|
|
||||||
@@ -994,7 +994,7 @@ class ToOdt(Tokenizer):
|
|||||||
# Auto-Style Classes
|
# Auto-Style Classes
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
class ODTParagraphStyle():
|
class ODTParagraphStyle:
|
||||||
"""Wrapper class for the paragraph style setting used by the
|
"""Wrapper class for the paragraph style setting used by the
|
||||||
exporter. Only the used settings are exposed here to keep the class
|
exporter. Only the used settings are exposed here to keep the class
|
||||||
minimal and fast.
|
minimal and fast.
|
||||||
@@ -1208,7 +1208,7 @@ class ODTParagraphStyle():
|
|||||||
# END Class ODTParagraphStyle
|
# END Class ODTParagraphStyle
|
||||||
|
|
||||||
|
|
||||||
class ODTTextStyle():
|
class ODTTextStyle:
|
||||||
"""Wrapper class for the text style setting used by the exporter.
|
"""Wrapper class for the text style setting used by the exporter.
|
||||||
Only the used settings are exposed here to keep the class minimal
|
Only the used settings are exposed here to keep the class minimal
|
||||||
and fast.
|
and fast.
|
||||||
@@ -1297,7 +1297,7 @@ X_SPAN_TEXT = 2
|
|||||||
X_SPAN_SING = 3
|
X_SPAN_SING = 3
|
||||||
|
|
||||||
|
|
||||||
class XMLParagraph():
|
class XMLParagraph:
|
||||||
"""This is a helper class to manage the text content of a single
|
"""This is a helper class to manage the text content of a single
|
||||||
XML element using mixed content tags.
|
XML element using mixed content tags.
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from novelwriter.core.item import NWItem
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWTree():
|
class NWTree:
|
||||||
|
|
||||||
MAX_DEPTH = 1000 # Cap of tree traversing for loops
|
MAX_DEPTH = 1000 # Cap of tree traversing for loops
|
||||||
|
|
||||||
@@ -89,20 +89,20 @@ class NWTree():
|
|||||||
logger.warning("Duplicate handle '%s' detected, skipping", tHandle)
|
logger.warning("Duplicate handle '%s' detected, skipping", tHandle)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.verbose("Adding item '%s' with parent '%s'", str(tHandle), str(pHandle))
|
logger.debug("Adding item '%s' with parent '%s'", str(tHandle), str(pHandle))
|
||||||
|
|
||||||
nwItem.setHandle(tHandle)
|
nwItem.setHandle(tHandle)
|
||||||
nwItem.setParent(pHandle)
|
nwItem.setParent(pHandle)
|
||||||
|
|
||||||
if nwItem.isRootType():
|
if nwItem.isRootType():
|
||||||
logger.verbose("Item '%s' is a root item", str(tHandle))
|
logger.debug("Item '%s' is a root item", str(tHandle))
|
||||||
self._treeRoots[tHandle] = nwItem
|
self._treeRoots[tHandle] = nwItem
|
||||||
if nwItem.itemClass == nwItemClass.ARCHIVE:
|
if nwItem.itemClass == nwItemClass.ARCHIVE:
|
||||||
logger.verbose("Item '%s' is the archive folder", str(tHandle))
|
logger.debug("Item '%s' is the archive folder", str(tHandle))
|
||||||
self._archRoot = tHandle
|
self._archRoot = tHandle
|
||||||
elif nwItem.itemClass == nwItemClass.TRASH:
|
elif nwItem.itemClass == nwItemClass.TRASH:
|
||||||
if self._trashRoot is None:
|
if self._trashRoot is None:
|
||||||
logger.verbose("Item '%s' is the trash folder", str(tHandle))
|
logger.debug("Item '%s' is the trash folder", str(tHandle))
|
||||||
self._trashRoot = tHandle
|
self._trashRoot = tHandle
|
||||||
else:
|
else:
|
||||||
logger.error("Only one trash folder allowed")
|
logger.error("Only one trash folder allowed")
|
||||||
@@ -274,11 +274,13 @@ class NWTree():
|
|||||||
return rootClasses
|
return rootClasses
|
||||||
|
|
||||||
def iterRoots(self, itemClass):
|
def iterRoots(self, itemClass):
|
||||||
"""Iterate over all items of a given class.
|
"""Iterate over all root items of a given class in order.
|
||||||
"""
|
"""
|
||||||
for tHandle, nwItem in self._treeRoots.items():
|
for tHandle in self._treeOrder:
|
||||||
if nwItem.itemClass == itemClass:
|
nwItem = self.__getitem__(tHandle)
|
||||||
yield tHandle, nwItem
|
if nwItem is not None and nwItem.isRootType():
|
||||||
|
if itemClass is None or nwItem.itemClass == itemClass:
|
||||||
|
yield tHandle, nwItem
|
||||||
return
|
return
|
||||||
|
|
||||||
def isRoot(self, tHandle):
|
def isRoot(self, tHandle):
|
||||||
@@ -347,7 +349,7 @@ class NWTree():
|
|||||||
# Save the temp list
|
# Save the temp list
|
||||||
self._treeOrder = tmpOrder
|
self._treeOrder = tmpOrder
|
||||||
self._setTreeChanged(True)
|
self._setTreeChanged(True)
|
||||||
logger.verbose("Project tree order updated")
|
logger.debug("Project tree order updated")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -457,7 +459,7 @@ class NWTree():
|
|||||||
"""Generate a unique item handle. In the event that the key
|
"""Generate a unique item handle. In the event that the key
|
||||||
already exists, generate a new one.
|
already exists, generate a new one.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Generating new handle")
|
logger.debug("Generating new handle")
|
||||||
handle = f"{random.getrandbits(52):013x}"
|
handle = f"{random.getrandbits(52):013x}"
|
||||||
if handle in self._projTree:
|
if handle in self._projTree:
|
||||||
logger.warning("Duplicate handle encountered! Retrying ...")
|
logger.warning("Duplicate handle encountered! Retrying ...")
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class QConfigLayout(QGridLayout):
|
|||||||
class QHelpLabel(QLabel):
|
class QHelpLabel(QLabel):
|
||||||
|
|
||||||
def __init__(self, theText, textCol, fontSize=0.9):
|
def __init__(self, theText, textCol, fontSize=0.9):
|
||||||
QLabel.__init__(self, theText)
|
super().__init__(theText)
|
||||||
|
|
||||||
if isinstance(textCol, QColor):
|
if isinstance(textCol, QColor):
|
||||||
qCol = textCol
|
qCol = textCol
|
||||||
@@ -267,8 +267,8 @@ class QSwitch(QAbstractButton):
|
|||||||
return self._offset
|
return self._offset
|
||||||
|
|
||||||
@offset.setter
|
@offset.setter
|
||||||
def offset(self, theOffset):
|
def offset(self, offset):
|
||||||
self._offset = theOffset
|
self._offset = offset
|
||||||
self.update()
|
self.update()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -276,11 +276,11 @@ class QSwitch(QAbstractButton):
|
|||||||
# Getters and Setters
|
# Getters and Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setChecked(self, isChecked):
|
def setChecked(self, checked):
|
||||||
"""Overload setChecked to also alter the offset.
|
"""Overload setChecked to also alter the offset.
|
||||||
"""
|
"""
|
||||||
super().setChecked(isChecked)
|
super().setChecked(checked)
|
||||||
if isChecked:
|
if checked:
|
||||||
self.offset = self._xW - self._xR
|
self.offset = self._xW - self._xR
|
||||||
else:
|
else:
|
||||||
self.offset = self._xR
|
self.offset = self._xR
|
||||||
@@ -290,10 +290,10 @@ class QSwitch(QAbstractButton):
|
|||||||
# Events
|
# Events
|
||||||
##
|
##
|
||||||
|
|
||||||
def resizeEvent(self, theEvent):
|
def resizeEvent(self, event):
|
||||||
"""Overload resize to ensure correct offset.
|
"""Overload resize to ensure correct offset.
|
||||||
"""
|
"""
|
||||||
super().resizeEvent(theEvent)
|
super().resizeEvent(event)
|
||||||
if self.isChecked():
|
if self.isChecked():
|
||||||
self.offset = self._xW - self._xR
|
self.offset = self._xW - self._xR
|
||||||
else:
|
else:
|
||||||
@@ -377,7 +377,7 @@ class QSwitch(QAbstractButton):
|
|||||||
class PagedDialog(QDialog):
|
class PagedDialog(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QDialog.__init__(self, parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self._tabBar = VerticalTabBar(self)
|
self._tabBar = VerticalTabBar(self)
|
||||||
self._tabBar.setExpanding(False)
|
self._tabBar.setExpanding(False)
|
||||||
@@ -410,13 +410,13 @@ class PagedDialog(QDialog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def addTab(self, widget, label):
|
def addTab(self, widget, label):
|
||||||
"""Forwards the adding of tabs to the QTabWidget.
|
"""Forward the adding of tabs to the QTabWidget.
|
||||||
"""
|
"""
|
||||||
self._tabBox.addTab(widget, label)
|
self._tabBox.addTab(widget, label)
|
||||||
return
|
return
|
||||||
|
|
||||||
def addControls(self, buttonBar):
|
def addControls(self, buttonBar):
|
||||||
"""Adds a button bar to the dialog.
|
"""Add a button bar to the dialog.
|
||||||
"""
|
"""
|
||||||
self._buttonBox.addWidget(buttonBar)
|
self._buttonBox.addWidget(buttonBar)
|
||||||
return
|
return
|
||||||
@@ -427,14 +427,14 @@ class PagedDialog(QDialog):
|
|||||||
class VerticalTabBar(QTabBar):
|
class VerticalTabBar(QTabBar):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QTabBar.__init__(self, parent=parent)
|
super().__init__(parent=parent)
|
||||||
self._mW = novelwriter.CONFIG.pxInt(150)
|
self._mW = novelwriter.CONFIG.pxInt(150)
|
||||||
return
|
return
|
||||||
|
|
||||||
def tabSizeHint(self, index):
|
def tabSizeHint(self, index):
|
||||||
"""Returns a transposed size hint for the rotated bar.
|
"""Return a transposed size hint for the rotated bar.
|
||||||
"""
|
"""
|
||||||
tSize = QTabBar.tabSizeHint(self, index)
|
tSize = super().tabSizeHint(index)
|
||||||
tSize.transpose()
|
tSize.transpose()
|
||||||
tSize.setWidth(min(tSize.width(), self._mW))
|
tSize.setWidth(min(tSize.width(), self._mW))
|
||||||
return tSize
|
return tSize
|
||||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiAbout(QDialog):
|
class GuiAbout(QDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiAbout ...")
|
logger.debug("Initialising GuiAbout ...")
|
||||||
self.setObjectName("GuiAbout")
|
self.setObjectName("GuiAbout")
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – GUI Doc Merge Tool
|
novelWriter – GUI Doc Merge Dialog
|
||||||
================================
|
==================================
|
||||||
GUI class for merging multiple documents to one document
|
Custom dialog class for merging documents.
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2020-01-23 [0.4.3]
|
Created: 2020-01-23 [0.4.3]
|
||||||
|
Rewritten: 2022-10-06 [2.0b1]
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2022, Veronica Berglyd Olsen
|
Copyright 2018–2022, Veronica Berglyd Olsen
|
||||||
@@ -26,169 +27,146 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import logging
|
import logging
|
||||||
import novelwriter
|
import novelwriter
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QVBoxLayout, QLabel, QListWidget, QAbstractItemView,
|
QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel,
|
||||||
QListWidgetItem, QDialogButtonBox
|
QListWidget, QListWidgetItem, QVBoxLayout,
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.core import NWDoc
|
from novelwriter.custom import QHelpLabel, QSwitch
|
||||||
from novelwriter.enum import nwAlert, nwItemType
|
|
||||||
from novelwriter.gui.custom import QHelpLabel
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiDocMerge(QDialog):
|
class GuiDocMerge(QDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui, sHandle, itemList):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocMerge ...")
|
logger.debug("Initialising GuiDocMerge ...")
|
||||||
self.setObjectName("GuiDocMerge")
|
self.setObjectName("GuiDocMerge")
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
|
self.mainTheme = mainGui.mainTheme
|
||||||
self.theProject = mainGui.theProject
|
self.theProject = mainGui.theProject
|
||||||
self.sourceItem = None
|
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self._data = {}
|
||||||
|
|
||||||
self.setWindowTitle(self.tr("Merge Documents"))
|
self.setWindowTitle(self.tr("Merge Documents"))
|
||||||
|
|
||||||
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
|
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
|
||||||
self.helpLabel = QHelpLabel(
|
self.helpLabel = QHelpLabel(self.tr(
|
||||||
self.tr("Drag and drop items to change the order."), self.mainGui.mainTheme.helpText
|
"Drag and drop items to change the order, or uncheck to exclude."
|
||||||
)
|
), self.mainTheme.helpText)
|
||||||
|
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
hSp = self.mainConf.pxInt(12)
|
||||||
|
vSp = self.mainConf.pxInt(8)
|
||||||
|
bSp = self.mainConf.pxInt(12)
|
||||||
|
|
||||||
self.listBox = QListWidget()
|
self.listBox = QListWidget()
|
||||||
self.listBox.setDragDropMode(QAbstractItemView.InternalMove)
|
self.listBox.setIconSize(QSize(iPx, iPx))
|
||||||
self.listBox.setMinimumWidth(self.mainConf.pxInt(400))
|
self.listBox.setMinimumWidth(self.mainConf.pxInt(400))
|
||||||
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
|
self.listBox.setMinimumHeight(self.mainConf.pxInt(180))
|
||||||
|
self.listBox.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||||
|
self.listBox.setDragDropMode(QAbstractItemView.InternalMove)
|
||||||
|
|
||||||
|
# Merge Options
|
||||||
|
self.trashLabel = QLabel(self.tr("Move merged items to Trash"))
|
||||||
|
self.trashSwitch = QSwitch(width=2*iPx, height=iPx)
|
||||||
|
|
||||||
|
self.optBox = QGridLayout()
|
||||||
|
self.optBox.addWidget(self.trashLabel, 0, 0)
|
||||||
|
self.optBox.addWidget(self.trashSwitch, 0, 1)
|
||||||
|
self.optBox.setHorizontalSpacing(hSp)
|
||||||
|
self.optBox.setColumnStretch(2, 1)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
self.buttonBox.accepted.connect(self._doMerge)
|
self.buttonBox.accepted.connect(self.accept)
|
||||||
self.buttonBox.rejected.connect(self._doClose)
|
self.buttonBox.rejected.connect(self.reject)
|
||||||
|
|
||||||
|
self.resetButton = self.buttonBox.addButton(QDialogButtonBox.Reset)
|
||||||
|
self.resetButton.clicked.connect(self._resetList)
|
||||||
|
|
||||||
|
# Assemble
|
||||||
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.setSpacing(0)
|
self.outerBox.setSpacing(0)
|
||||||
self.outerBox.addWidget(self.headLabel)
|
self.outerBox.addWidget(self.headLabel)
|
||||||
self.outerBox.addWidget(self.helpLabel)
|
self.outerBox.addWidget(self.helpLabel)
|
||||||
self.outerBox.addSpacing(self.mainConf.pxInt(8))
|
self.outerBox.addSpacing(vSp)
|
||||||
self.outerBox.addWidget(self.listBox)
|
self.outerBox.addWidget(self.listBox)
|
||||||
self.outerBox.addSpacing(self.mainConf.pxInt(12))
|
self.outerBox.addSpacing(vSp)
|
||||||
|
self.outerBox.addLayout(self.optBox)
|
||||||
|
self.outerBox.addSpacing(bSp)
|
||||||
self.outerBox.addWidget(self.buttonBox)
|
self.outerBox.addWidget(self.buttonBox)
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
self.rejected.connect(self._doClose)
|
# Load Content
|
||||||
|
self._loadContent(sHandle, itemList)
|
||||||
self._populateList()
|
|
||||||
|
|
||||||
logger.debug("GuiDocMerge initialisation complete")
|
logger.debug("GuiDocMerge initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
def getData(self):
|
||||||
# Buttons
|
"""Return the user's choices.
|
||||||
##
|
|
||||||
|
|
||||||
def _doMerge(self):
|
|
||||||
"""Perform the merge of the files in the selected folder, and
|
|
||||||
create a new file in the same parent folder. The old files are
|
|
||||||
not removed in the merge process, and must be deleted manually.
|
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiDocMerge merge button clicked")
|
finalItems = []
|
||||||
|
|
||||||
finalOrder = []
|
|
||||||
for i in range(self.listBox.count()):
|
for i in range(self.listBox.count()):
|
||||||
finalOrder.append(self.listBox.item(i).data(Qt.UserRole))
|
item = self.listBox.item(i)
|
||||||
|
if item is not None and item.checkState() == Qt.Checked:
|
||||||
|
finalItems.append(item.data(Qt.UserRole))
|
||||||
|
|
||||||
if len(finalOrder) == 0:
|
self._data["moveToTrash"] = self.trashSwitch.isChecked()
|
||||||
self.mainGui.makeAlert(self.tr(
|
self._data["finalItems"] = finalItems
|
||||||
"No source documents found. Nothing to do."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
theText = ""
|
return self._data
|
||||||
for tHandle in finalOrder:
|
|
||||||
inDoc = NWDoc(self.theProject, tHandle)
|
|
||||||
docText = inDoc.readDocument()
|
|
||||||
docErr = inDoc.getError()
|
|
||||||
if docText is None and docErr:
|
|
||||||
self.mainGui.makeAlert([
|
|
||||||
self.tr("Failed to open document file."), docErr
|
|
||||||
], nwAlert.ERROR)
|
|
||||||
if docText:
|
|
||||||
theText += docText.rstrip("\n")+"\n\n"
|
|
||||||
|
|
||||||
if self.sourceItem is None:
|
##
|
||||||
self.mainGui.makeAlert(self.tr(
|
# Slots
|
||||||
"No source folder selected. Nothing to do."
|
##
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
srcItem = self.theProject.tree[self.sourceItem]
|
def _resetList(self):
|
||||||
if srcItem is None:
|
"""Reset the content of the list box to its original state.
|
||||||
self.mainGui.makeAlert(self.tr("Internal error."), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemParent)
|
|
||||||
newItem = self.theProject.tree[nHandle]
|
|
||||||
newItem.setStatus(srcItem.itemStatus)
|
|
||||||
newItem.setImport(srcItem.itemImport)
|
|
||||||
|
|
||||||
outDoc = NWDoc(self.theProject, nHandle)
|
|
||||||
if not outDoc.writeDocument(theText):
|
|
||||||
self.mainGui.makeAlert([
|
|
||||||
self.tr("Could not save document."), outDoc.getError()
|
|
||||||
], nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.mainGui.projView.revealNewTreeItem(nHandle)
|
|
||||||
self.mainGui.openDocument(nHandle, doScroll=True)
|
|
||||||
|
|
||||||
self._doClose()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _doClose(self):
|
|
||||||
"""Close the dialog window without doing anything.
|
|
||||||
"""
|
"""
|
||||||
self.close()
|
logger.debug("Resetting list box content")
|
||||||
|
sHandle = self._data.get("sHandle", None)
|
||||||
|
itemList = self._data.get("origItems", [])
|
||||||
|
self._loadContent(sHandle, itemList)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _populateList(self):
|
def _loadContent(self, sHandle, itemList):
|
||||||
"""Get the item selected in the tree, check that it is a folder,
|
"""Load content from a given list of items.
|
||||||
and try to find all files associated with it. The valid files
|
|
||||||
are then added to the list view in order. The list itself can be
|
|
||||||
reordered by the user.
|
|
||||||
"""
|
"""
|
||||||
tHandle = self.mainGui.projView.getSelectedHandle()
|
self._data = {}
|
||||||
self.sourceItem = tHandle
|
self._data["sHandle"] = sHandle
|
||||||
if tHandle is None:
|
self._data["origItems"] = itemList
|
||||||
return False
|
|
||||||
|
|
||||||
nwItem = self.theProject.tree[tHandle]
|
self.listBox.clear()
|
||||||
if nwItem is None:
|
for tHandle in itemList:
|
||||||
return False
|
nwItem = self.theProject.tree[tHandle]
|
||||||
|
if nwItem is None or not nwItem.isFileType():
|
||||||
if nwItem.itemType is not nwItemType.FOLDER:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Element selected in the project tree must be a folder."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
for sHandle in self.mainGui.projView.getTreeFromHandle(tHandle):
|
|
||||||
newItem = QListWidgetItem()
|
|
||||||
nwItem = self.theProject.tree[sHandle]
|
|
||||||
if not nwItem.isFileType():
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
itemIcon = self.mainTheme.getItemIcon(
|
||||||
|
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
|
||||||
|
)
|
||||||
|
|
||||||
|
newItem = QListWidgetItem()
|
||||||
|
newItem.setIcon(itemIcon)
|
||||||
newItem.setText(nwItem.itemName)
|
newItem.setText(nwItem.itemName)
|
||||||
newItem.setData(Qt.UserRole, sHandle)
|
newItem.setData(Qt.UserRole, tHandle)
|
||||||
|
newItem.setCheckState(Qt.Checked)
|
||||||
|
|
||||||
self.listBox.addItem(newItem)
|
self.listBox.addItem(newItem)
|
||||||
|
|
||||||
return True
|
return
|
||||||
|
|
||||||
# END Class GuiDocMerge
|
# END Class GuiDocMerge
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – GUI Doc Split Tool
|
novelWriter – GUI Doc Split Dialog
|
||||||
================================
|
==================================
|
||||||
GUI class for splitting a single document into multiple documents
|
Custom dialog class for splitting documents.
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2020-02-01 [0.4.3]
|
Created: 2020-02-01 [0.4.3]
|
||||||
|
Rewritten: 2022-10-12 [2.0b1]
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2022, Veronica Berglyd Olsen
|
Copyright 2018–2022, Veronica Berglyd Olsen
|
||||||
@@ -29,32 +30,35 @@ import novelwriter
|
|||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QVBoxLayout, QComboBox, QListWidget, QAbstractItemView,
|
QDialog, QVBoxLayout, QComboBox, QListWidget, QAbstractItemView,
|
||||||
QListWidgetItem, QDialogButtonBox, QLabel
|
QListWidgetItem, QDialogButtonBox, QLabel, QGridLayout
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.core import NWDoc
|
from novelwriter.core import NWDoc
|
||||||
from novelwriter.enum import nwAlert
|
from novelwriter.custom import QHelpLabel, QSwitch
|
||||||
from novelwriter.gui.custom import QHelpLabel
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiDocSplit(QDialog):
|
class GuiDocSplit(QDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
LINE_ROLE = Qt.UserRole
|
||||||
QDialog.__init__(self, mainGui)
|
LEVEL_ROLE = Qt.UserRole + 1
|
||||||
|
LABEL_ROLE = Qt.UserRole + 2
|
||||||
|
|
||||||
|
def __init__(self, mainGui, sHandle):
|
||||||
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocSplit ...")
|
logger.debug("Initialising GuiDocSplit ...")
|
||||||
self.setObjectName("GuiDocSplit")
|
self.setObjectName("GuiDocSplit")
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
|
self.mainTheme = mainGui.mainTheme
|
||||||
self.theProject = mainGui.theProject
|
self.theProject = mainGui.theProject
|
||||||
|
|
||||||
self.sourceItem = None
|
self._data = {}
|
||||||
self.sourceText = []
|
self._text = []
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
|
||||||
self.setWindowTitle(self.tr("Split Document"))
|
self.setWindowTitle(self.tr("Split Document"))
|
||||||
|
|
||||||
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
|
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
|
||||||
@@ -63,6 +67,18 @@ class GuiDocSplit(QDialog):
|
|||||||
self.mainGui.mainTheme.helpText
|
self.mainGui.mainTheme.helpText
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Values
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
hSp = self.mainConf.pxInt(12)
|
||||||
|
vSp = self.mainConf.pxInt(8)
|
||||||
|
bSp = self.mainConf.pxInt(12)
|
||||||
|
|
||||||
|
pOptions = self.theProject.options
|
||||||
|
spLevel = pOptions.getInt("GuiDocSplit", "spLevel", 3)
|
||||||
|
intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
|
||||||
|
docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
|
||||||
|
|
||||||
|
# Header Selection
|
||||||
self.listBox = QListWidget()
|
self.listBox = QListWidget()
|
||||||
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
|
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
|
||||||
self.listBox.setMinimumWidth(self.mainConf.pxInt(400))
|
self.listBox.setMinimumWidth(self.mainConf.pxInt(400))
|
||||||
@@ -73,206 +89,155 @@ class GuiDocSplit(QDialog):
|
|||||||
self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
|
self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
|
||||||
self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3)
|
self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3)
|
||||||
self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4)
|
self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4)
|
||||||
spIndex = self.splitLevel.findData(
|
spIndex = self.splitLevel.findData(spLevel)
|
||||||
self.theProject.options.getInt("GuiDocSplit", "spLevel", 3)
|
|
||||||
)
|
|
||||||
if spIndex != -1:
|
if spIndex != -1:
|
||||||
self.splitLevel.setCurrentIndex(spIndex)
|
self.splitLevel.setCurrentIndex(spIndex)
|
||||||
self.splitLevel.currentIndexChanged.connect(self._populateList)
|
self.splitLevel.currentIndexChanged.connect(self._reloadList)
|
||||||
|
|
||||||
|
# Split Options
|
||||||
|
self.folderLabel = QLabel(self.tr("Split into a new folder"))
|
||||||
|
self.folderSwitch = QSwitch(width=2*iPx, height=iPx)
|
||||||
|
self.folderSwitch.setChecked(intoFolder)
|
||||||
|
|
||||||
|
self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"))
|
||||||
|
self.hierarchySwitch = QSwitch(width=2*iPx, height=iPx)
|
||||||
|
self.hierarchySwitch.setChecked(docHierarchy)
|
||||||
|
|
||||||
|
self.optBox = QGridLayout()
|
||||||
|
self.optBox.addWidget(self.folderLabel, 0, 0)
|
||||||
|
self.optBox.addWidget(self.folderSwitch, 0, 1)
|
||||||
|
self.optBox.addWidget(self.hierarchyLabel, 1, 0)
|
||||||
|
self.optBox.addWidget(self.hierarchySwitch, 1, 1)
|
||||||
|
self.optBox.setVerticalSpacing(vSp)
|
||||||
|
self.optBox.setHorizontalSpacing(hSp)
|
||||||
|
self.optBox.setColumnStretch(2, 1)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
self.buttonBox.accepted.connect(self._doSplit)
|
self.buttonBox.accepted.connect(self.accept)
|
||||||
self.buttonBox.rejected.connect(self._doClose)
|
self.buttonBox.rejected.connect(self.reject)
|
||||||
|
|
||||||
|
# Assemble
|
||||||
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.setSpacing(0)
|
self.outerBox.setSpacing(0)
|
||||||
self.outerBox.addWidget(self.headLabel)
|
self.outerBox.addWidget(self.headLabel)
|
||||||
self.outerBox.addWidget(self.helpLabel)
|
self.outerBox.addWidget(self.helpLabel)
|
||||||
self.outerBox.addSpacing(self.mainConf.pxInt(8))
|
self.outerBox.addSpacing(vSp)
|
||||||
self.outerBox.addWidget(self.listBox)
|
self.outerBox.addWidget(self.listBox)
|
||||||
self.outerBox.addWidget(self.splitLevel)
|
self.outerBox.addWidget(self.splitLevel)
|
||||||
self.outerBox.addSpacing(self.mainConf.pxInt(12))
|
self.outerBox.addSpacing(vSp)
|
||||||
|
self.outerBox.addLayout(self.optBox)
|
||||||
|
self.outerBox.addSpacing(bSp)
|
||||||
self.outerBox.addWidget(self.buttonBox)
|
self.outerBox.addWidget(self.buttonBox)
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
self.rejected.connect(self._doClose)
|
# Load Content
|
||||||
|
self._loadContent(sHandle)
|
||||||
self._populateList()
|
|
||||||
|
|
||||||
logger.debug("GuiDocSplit initialisation complete")
|
logger.debug("GuiDocSplit initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
def getData(self):
|
||||||
# Buttons
|
"""Return the user's choices. Also save the users options for
|
||||||
##
|
the next time the dialog is used.
|
||||||
|
|
||||||
def _doSplit(self):
|
|
||||||
"""Perform the split of the file, create a new folder in the
|
|
||||||
same parent folder, and multiple files depending on split level
|
|
||||||
settings. The old file is not removed in the split process, and
|
|
||||||
must be deleted manually.
|
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiDocSplit split button clicked")
|
headerList = []
|
||||||
|
|
||||||
if self.sourceItem is None:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"No source document selected. Nothing to do."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
srcItem = self.theProject.tree[self.sourceItem]
|
|
||||||
if srcItem is None:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Could not parse source document."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
inDoc = NWDoc(self.theProject, self.sourceItem)
|
|
||||||
theText = inDoc.readDocument()
|
|
||||||
|
|
||||||
docErr = inDoc.getError()
|
|
||||||
if theText is None and docErr:
|
|
||||||
self.mainGui.makeAlert([
|
|
||||||
self.tr("Failed to open document file."), docErr
|
|
||||||
], nwAlert.ERROR)
|
|
||||||
|
|
||||||
if theText is None:
|
|
||||||
theText = ""
|
|
||||||
|
|
||||||
nLines = len(self.sourceText)
|
|
||||||
logger.debug("Splitting document %s with %d lines", self.sourceItem, nLines)
|
|
||||||
|
|
||||||
finalOrder = []
|
|
||||||
for i in range(self.listBox.count()):
|
for i in range(self.listBox.count()):
|
||||||
listItem = self.listBox.item(i)
|
item = self.listBox.item(i)
|
||||||
wTitle = listItem.text()
|
if item is not None:
|
||||||
lineNo = listItem.data(Qt.UserRole)
|
headerList.append((
|
||||||
finalOrder.append([wTitle, lineNo, nLines])
|
item.data(self.LINE_ROLE),
|
||||||
if i > 0:
|
item.data(self.LEVEL_ROLE),
|
||||||
finalOrder[i-1][2] = lineNo
|
item.data(self.LABEL_ROLE),
|
||||||
|
))
|
||||||
|
|
||||||
nFiles = len(finalOrder)
|
spLevel = self.splitLevel.currentData()
|
||||||
if nFiles == 0:
|
intoFolder = self.folderSwitch.isChecked()
|
||||||
self.mainGui.makeAlert(self.tr(
|
docHierarchy = self.hierarchySwitch.isChecked()
|
||||||
"No headers found. Nothing to do."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
msgYes = self.mainGui.askQuestion(
|
self._data["spLevel"] = spLevel
|
||||||
self.tr("Split Document"),
|
self._data["headerList"] = headerList
|
||||||
"{0}<br><br>{1}".format(
|
self._data["intoFolder"] = intoFolder
|
||||||
self.tr(
|
self._data["docHierarchy"] = docHierarchy
|
||||||
"The document will be split into {0} file(s) in a new folder. "
|
|
||||||
"The original document will remain intact."
|
|
||||||
).format(nFiles),
|
|
||||||
self.tr(
|
|
||||||
"Continue with the splitting process?"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if not msgYes:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Create the folder
|
pOptions = self.theProject.options
|
||||||
fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemParent)
|
pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
|
||||||
self.mainGui.projView.revealNewTreeItem(fHandle)
|
pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder)
|
||||||
logger.verbose("Creating folder '%s'", fHandle)
|
pOptions.setValue("GuiDocSplit", "docHierarchy", docHierarchy)
|
||||||
|
|
||||||
# Loop through, and create the files
|
return self._data, self._text
|
||||||
for wTitle, iStart, iEnd in finalOrder:
|
|
||||||
|
|
||||||
wTitle = wTitle.lstrip("#").strip()
|
##
|
||||||
nHandle = self.theProject.newFile(wTitle, fHandle)
|
# Slots
|
||||||
newItem = self.theProject.tree[nHandle]
|
##
|
||||||
newItem.setStatus(srcItem.itemStatus)
|
|
||||||
newItem.setImport(srcItem.itemImport)
|
|
||||||
logger.verbose(
|
|
||||||
"Creating new document '%s' with text from line %d to %d",
|
|
||||||
nHandle, iStart+1, iEnd
|
|
||||||
)
|
|
||||||
|
|
||||||
theText = "\n".join(self.sourceText[iStart:iEnd])
|
def _reloadList(self):
|
||||||
theText = theText.rstrip("\n") + "\n\n"
|
"""Reload the content of the list box.
|
||||||
|
|
||||||
outDoc = NWDoc(self.theProject, nHandle)
|
|
||||||
if not outDoc.writeDocument(theText):
|
|
||||||
self.mainGui.makeAlert([
|
|
||||||
self.tr("Could not save document."), outDoc.getError()
|
|
||||||
], nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.mainGui.projView.revealNewTreeItem(nHandle)
|
|
||||||
|
|
||||||
self._doClose()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _doClose(self):
|
|
||||||
"""Close the dialog window without doing anything.
|
|
||||||
"""
|
"""
|
||||||
self.theProject.options.saveSettings()
|
sHandle = self._data.get("sHandle", None)
|
||||||
self.close()
|
self._loadContent(sHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _populateList(self):
|
def _loadContent(self, sHandle):
|
||||||
"""Get the item selected in the tree, check that it is a folder,
|
"""Load content from a given source item.
|
||||||
and try to find all files associated with it. The valid files
|
|
||||||
are then added to the list view in order. The list itself can be
|
|
||||||
reordered by the user.
|
|
||||||
"""
|
"""
|
||||||
|
self._data = {}
|
||||||
|
self._data["sHandle"] = sHandle
|
||||||
|
|
||||||
self.listBox.clear()
|
self.listBox.clear()
|
||||||
if self.sourceItem is None:
|
|
||||||
self.sourceItem = self.mainGui.projView.getSelectedHandle()
|
|
||||||
|
|
||||||
if self.sourceItem is None:
|
nwItem = self.theProject.tree[sHandle]
|
||||||
return False
|
if nwItem is None or not nwItem.isFileType():
|
||||||
|
return
|
||||||
nwItem = self.theProject.tree[self.sourceItem]
|
|
||||||
if nwItem is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not nwItem.isFileType():
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Element selected in the project tree must be a file."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
return False
|
|
||||||
|
|
||||||
inDoc = NWDoc(self.theProject, self.sourceItem)
|
|
||||||
theText = inDoc.readDocument()
|
|
||||||
if theText is None:
|
|
||||||
theText = ""
|
|
||||||
return False
|
|
||||||
|
|
||||||
spLevel = self.splitLevel.currentData()
|
spLevel = self.splitLevel.currentData()
|
||||||
self.theProject.options.setValue("GuiDocSplit", "spLevel", spLevel)
|
if not self._text:
|
||||||
logger.debug(
|
inDoc = NWDoc(self.theProject, sHandle)
|
||||||
"Scanning document '%s' for headings level <= %d",
|
self._text = (inDoc.readDocument() or "").splitlines()
|
||||||
self.sourceItem, spLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
self.sourceText = theText.splitlines()
|
for lineNo, aLine in enumerate(self._text):
|
||||||
for lineNo, aLine in enumerate(self.sourceText):
|
|
||||||
|
|
||||||
onLine = -1
|
onLine = -1
|
||||||
|
hLevel = 0
|
||||||
|
hLabel = aLine.strip()
|
||||||
if aLine.startswith("# ") and spLevel >= 1:
|
if aLine.startswith("# ") and spLevel >= 1:
|
||||||
onLine = lineNo
|
onLine = lineNo
|
||||||
|
hLevel = 1
|
||||||
|
hLabel = aLine[2:].strip()
|
||||||
elif aLine.startswith("## ") and spLevel >= 2:
|
elif aLine.startswith("## ") and spLevel >= 2:
|
||||||
onLine = lineNo
|
onLine = lineNo
|
||||||
|
hLevel = 2
|
||||||
|
hLabel = aLine[3:].strip()
|
||||||
elif aLine.startswith("### ") and spLevel >= 3:
|
elif aLine.startswith("### ") and spLevel >= 3:
|
||||||
onLine = lineNo
|
onLine = lineNo
|
||||||
|
hLevel = 3
|
||||||
|
hLabel = aLine[4:].strip()
|
||||||
elif aLine.startswith("#### ") and spLevel >= 4:
|
elif aLine.startswith("#### ") and spLevel >= 4:
|
||||||
onLine = lineNo
|
onLine = lineNo
|
||||||
|
hLevel = 4
|
||||||
|
hLabel = aLine[5:].strip()
|
||||||
|
elif aLine.startswith("#! ") and spLevel >= 1:
|
||||||
|
onLine = lineNo
|
||||||
|
hLevel = 1
|
||||||
|
hLabel = aLine[3:].strip()
|
||||||
|
elif aLine.startswith("##! ") and spLevel >= 2:
|
||||||
|
onLine = lineNo
|
||||||
|
hLevel = 2
|
||||||
|
hLabel = aLine[4:].strip()
|
||||||
|
|
||||||
if onLine >= 0:
|
if onLine >= 0 and hLevel > 0:
|
||||||
newItem = QListWidgetItem()
|
newItem = QListWidgetItem()
|
||||||
newItem.setText(aLine.strip())
|
newItem.setText(aLine.strip())
|
||||||
newItem.setData(Qt.UserRole, onLine)
|
newItem.setData(self.LINE_ROLE, onLine)
|
||||||
|
newItem.setData(self.LEVEL_ROLE, hLevel)
|
||||||
|
newItem.setData(self.LABEL_ROLE, hLabel)
|
||||||
self.listBox.addItem(newItem)
|
self.listBox.addItem(newItem)
|
||||||
|
|
||||||
return True
|
return
|
||||||
|
|
||||||
# END Class GuiDocSplit
|
# END Class GuiDocSplit
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiEditLabel(QDialog):
|
class GuiEditLabel(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent, text=""):
|
def __init__(self, parent, text=""):
|
||||||
QDialog.__init__(self, parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self.setObjectName("GuiEditLabel")
|
self.setObjectName("GuiEditLabel")
|
||||||
self.setWindowTitle(self.tr("Item Label"))
|
self.setWindowTitle(self.tr("Item Label"))
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.enum import nwAlert
|
from novelwriter.enum import nwAlert
|
||||||
from novelwriter.gui.custom import QSwitch, QConfigLayout, PagedDialog
|
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
|
||||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiPreferences(PagedDialog):
|
class GuiPreferences(PagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
PagedDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiPreferences ...")
|
logger.debug("Initialising GuiPreferences ...")
|
||||||
self.setObjectName("GuiPreferences")
|
self.setObjectName("GuiPreferences")
|
||||||
@@ -55,13 +55,13 @@ class GuiPreferences(PagedDialog):
|
|||||||
|
|
||||||
self.setWindowTitle(self.tr("Preferences"))
|
self.setWindowTitle(self.tr("Preferences"))
|
||||||
|
|
||||||
self.tabGeneral = GuiPreferencesGeneral(self.mainGui)
|
self.tabGeneral = GuiPreferencesGeneral(self)
|
||||||
self.tabProjects = GuiPreferencesProjects(self.mainGui)
|
self.tabProjects = GuiPreferencesProjects(self)
|
||||||
self.tabDocs = GuiPreferencesDocuments(self.mainGui)
|
self.tabDocs = GuiPreferencesDocuments(self)
|
||||||
self.tabEditor = GuiPreferencesEditor(self.mainGui)
|
self.tabEditor = GuiPreferencesEditor(self)
|
||||||
self.tabSyntax = GuiPreferencesSyntax(self.mainGui)
|
self.tabSyntax = GuiPreferencesSyntax(self)
|
||||||
self.tabAuto = GuiPreferencesAutomation(self.mainGui)
|
self.tabAuto = GuiPreferencesAutomation(self)
|
||||||
self.tabQuote = GuiPreferencesQuotes(self.mainGui)
|
self.tabQuote = GuiPreferencesQuotes(self)
|
||||||
|
|
||||||
self.addTab(self.tabGeneral, self.tr("General"))
|
self.addTab(self.tabGeneral, self.tr("General"))
|
||||||
self.addTab(self.tabProjects, self.tr("Projects"))
|
self.addTab(self.tabProjects, self.tr("Projects"))
|
||||||
@@ -128,9 +128,7 @@ class GuiPreferences(PagedDialog):
|
|||||||
def _saveWindowSize(self):
|
def _saveWindowSize(self):
|
||||||
"""Save the dialog window size.
|
"""Save the dialog window size.
|
||||||
"""
|
"""
|
||||||
winWidth = self.mainConf.rpxInt(self.width())
|
self.mainConf.setPreferencesSize(self.width(), self.height())
|
||||||
winHeight = self.mainConf.rpxInt(self.height())
|
|
||||||
self.mainConf.setPreferencesSize(winWidth, winHeight)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class GuiPreferences
|
# END Class GuiPreferences
|
||||||
@@ -138,12 +136,12 @@ class GuiPreferences(PagedDialog):
|
|||||||
|
|
||||||
class GuiPreferencesGeneral(QWidget):
|
class GuiPreferencesGeneral(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -344,12 +342,12 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
|
|
||||||
class GuiPreferencesProjects(QWidget):
|
class GuiPreferencesProjects(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -505,12 +503,12 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
|
|
||||||
class GuiPreferencesDocuments(QWidget):
|
class GuiPreferencesDocuments(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -666,12 +664,12 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
|
|
||||||
class GuiPreferencesEditor(QWidget):
|
class GuiPreferencesEditor(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -840,12 +838,12 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
|
|
||||||
class GuiPreferencesSyntax(QWidget):
|
class GuiPreferencesSyntax(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -943,12 +941,12 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
|
|
||||||
class GuiPreferencesAutomation(QWidget):
|
class GuiPreferencesAutomation(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -1100,12 +1098,12 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
|
|
||||||
class GuiPreferencesQuotes(QWidget):
|
class GuiPreferencesQuotes(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, prefsGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
|
|||||||
@@ -27,16 +27,18 @@ import math
|
|||||||
import logging
|
import logging
|
||||||
import novelwriter
|
import novelwriter
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QDialogButtonBox, QVBoxLayout, QTreeWidget, QTreeWidgetItem,
|
QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QHBoxLayout,
|
||||||
QLabel, QSpinBox, QGridLayout, QHBoxLayout, QLineEdit, QAbstractItemView
|
QLabel, QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||||
|
QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from novelwriter.enum import nwItemClass
|
||||||
from novelwriter.common import numberToRoman
|
from novelwriter.common import numberToRoman
|
||||||
from novelwriter.constants import nwUnicode
|
from novelwriter.custom import PagedDialog, QSwitch
|
||||||
from novelwriter.gui.custom import PagedDialog, QSwitch
|
from novelwriter.constants import nwLabels, nwUnicode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiProjectDetails(PagedDialog):
|
class GuiProjectDetails(PagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
PagedDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectDetails ...")
|
logger.debug("Initialising GuiProjectDetails ...")
|
||||||
self.setObjectName("GuiProjectDetails")
|
self.setObjectName("GuiProjectDetails")
|
||||||
@@ -140,7 +142,7 @@ class GuiProjectDetails(PagedDialog):
|
|||||||
class GuiProjectDetailsMain(QWidget):
|
class GuiProjectDetailsMain(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui, theProject):
|
def __init__(self, mainGui, theProject):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
@@ -272,7 +274,7 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
C_PROG = 4
|
C_PROG = 4
|
||||||
|
|
||||||
def __init__(self, mainGui, theProject):
|
def __init__(self, mainGui, theProject):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
@@ -281,12 +283,26 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
|
|
||||||
# Internal
|
# Internal
|
||||||
self._theToC = []
|
self._theToC = []
|
||||||
|
self._currentRoot = None
|
||||||
|
|
||||||
iPx = self.mainTheme.baseIconSize
|
iPx = self.mainTheme.baseIconSize
|
||||||
hPx = self.mainConf.pxInt(12)
|
hPx = self.mainConf.pxInt(12)
|
||||||
vPx = self.mainConf.pxInt(4)
|
vPx = self.mainConf.pxInt(4)
|
||||||
pOptions = self.theProject.options
|
pOptions = self.theProject.options
|
||||||
|
|
||||||
|
# Header
|
||||||
|
# ======
|
||||||
|
|
||||||
|
self.tocLabel = QLabel("<b>%s</b>" % self.tr("Table of Contents"))
|
||||||
|
|
||||||
|
self.novelValue = QComboBox(self)
|
||||||
|
self.novelValue.setMinimumWidth(self.mainConf.pxInt(200))
|
||||||
|
self.novelValue.currentIndexChanged.connect(self._novelValueChanged)
|
||||||
|
|
||||||
|
self.headBox = QHBoxLayout()
|
||||||
|
self.headBox.addWidget(self.tocLabel)
|
||||||
|
self.headBox.addWidget(self.novelValue)
|
||||||
|
|
||||||
# Contents Tree
|
# Contents Tree
|
||||||
# =============
|
# =============
|
||||||
|
|
||||||
@@ -389,7 +405,7 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
# ========
|
# ========
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.addWidget(QLabel("<b>%s</b>" % self.tr("Table of Contents")))
|
self.outerBox.addLayout(self.headBox)
|
||||||
self.outerBox.addWidget(self.tocTree)
|
self.outerBox.addWidget(self.tocTree)
|
||||||
self.outerBox.addLayout(self.optionsBox)
|
self.outerBox.addLayout(self.optionsBox)
|
||||||
|
|
||||||
@@ -412,19 +428,35 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
def updateValues(self):
|
def updateValues(self):
|
||||||
"""Populate the tree.
|
"""Populate the tree.
|
||||||
"""
|
"""
|
||||||
self._prepareData()
|
self._currentRoot = None
|
||||||
|
self._populateNovelList()
|
||||||
|
|
||||||
|
rootHandle = self.novelValue.currentData()
|
||||||
|
self._prepareData(rootHandle)
|
||||||
self._populateTree()
|
self._populateTree()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _prepareData(self):
|
def _populateNovelList(self):
|
||||||
"""Extract the data for the tree.
|
"""Fill the novel combo box with a list of all novel folders.
|
||||||
"""
|
"""
|
||||||
self._theToC = []
|
self.novelValue.clear()
|
||||||
self._theToC = self.theProject.index.getTableOfContents(2)
|
|
||||||
|
tIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
|
||||||
|
for tHandle, nwItem in self.theProject.tree.iterRoots(nwItemClass.NOVEL):
|
||||||
|
self.novelValue.addItem(tIcon, nwItem.itemName, tHandle)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _prepareData(self, rootHandle):
|
||||||
|
"""Extract the information from the project index.
|
||||||
|
"""
|
||||||
|
logger.debug("Populating ToC from handle '%s'", rootHandle)
|
||||||
|
self._theToC = self.theProject.index.getTableOfContents(rootHandle, 2)
|
||||||
self._theToC.append(("", 0, self.tr("END"), 0))
|
self._theToC.append(("", 0, self.tr("END"), 0))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -432,6 +464,18 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _novelValueChanged(self):
|
||||||
|
"""Refresh the tree with another root item.
|
||||||
|
"""
|
||||||
|
rootHandle = self.novelValue.currentData()
|
||||||
|
if rootHandle != self._currentRoot:
|
||||||
|
self._prepareData(rootHandle)
|
||||||
|
self._populateTree()
|
||||||
|
self._currentRoot = rootHandle
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _populateTree(self):
|
def _populateTree(self):
|
||||||
"""Set the content of the chapter/page tree.
|
"""Set the content of the chapter/page tree.
|
||||||
"""
|
"""
|
||||||
@@ -466,10 +510,11 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
progPage = f"{cPage:n}"
|
progPage = f"{cPage:n}"
|
||||||
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
|
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
|
||||||
|
|
||||||
|
hDec = self.mainTheme.getHeaderDecoration(tLevel)
|
||||||
if tTitle.strip() == "":
|
if tTitle.strip() == "":
|
||||||
tTitle = self.tr("Untitled")
|
tTitle = self.tr("Untitled")
|
||||||
|
|
||||||
newItem.setIcon(self.C_TITLE, self.mainTheme.getIcon("doc_h%d" % tLevel))
|
newItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
|
||||||
newItem.setText(self.C_TITLE, tTitle)
|
newItem.setText(self.C_TITLE, tTitle)
|
||||||
newItem.setText(self.C_WORDS, f"{wCount:n}")
|
newItem.setText(self.C_WORDS, f"{wCount:n}")
|
||||||
newItem.setText(self.C_PAGES, f"{pCount:n}")
|
newItem.setText(self.C_PAGES, f"{pCount:n}")
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
C_TIME = 2
|
C_TIME = 2
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectLoad ...")
|
logger.debug("Initialising GuiProjectLoad ...")
|
||||||
self.setObjectName("GuiProjectLoad")
|
self.setObjectName("GuiProjectLoad")
|
||||||
@@ -158,7 +158,6 @@ class GuiProjectLoad(QDialog):
|
|||||||
def _doOpenRecent(self):
|
def _doOpenRecent(self):
|
||||||
"""Close the dialog window with a recent project selected.
|
"""Close the dialog window with a recent project selected.
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiProjectLoad open button clicked")
|
|
||||||
self._saveSettings()
|
self._saveSettings()
|
||||||
|
|
||||||
self.openPath = None
|
self.openPath = None
|
||||||
@@ -183,7 +182,6 @@ class GuiProjectLoad(QDialog):
|
|||||||
def _doBrowse(self):
|
def _doBrowse(self):
|
||||||
"""Browse for a folder path.
|
"""Browse for a folder path.
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiProjectLoad browse button clicked")
|
|
||||||
extFilter = [
|
extFilter = [
|
||||||
self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
|
self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
|
||||||
self.tr("All files ({0})").format("*"),
|
self.tr("All files ({0})").format("*"),
|
||||||
@@ -203,7 +201,6 @@ class GuiProjectLoad(QDialog):
|
|||||||
def _doCancel(self):
|
def _doCancel(self):
|
||||||
"""Close the dialog window without doing anything.
|
"""Close the dialog window without doing anything.
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiProjectLoad close button clicked")
|
|
||||||
self.openPath = None
|
self.openPath = None
|
||||||
self.openState = self.NONE_STATE
|
self.openState = self.NONE_STATE
|
||||||
self.close()
|
self.close()
|
||||||
@@ -212,7 +209,6 @@ class GuiProjectLoad(QDialog):
|
|||||||
def _doNewProject(self):
|
def _doNewProject(self):
|
||||||
"""Create a new project.
|
"""Create a new project.
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiProjectLoad new project button clicked")
|
|
||||||
self._saveSettings()
|
self._saveSettings()
|
||||||
self.openPath = None
|
self.openPath = None
|
||||||
self.openState = self.NEW_STATE
|
self.openState = self.NEW_STATE
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter.enum import nwAlert
|
from novelwriter.enum import nwAlert
|
||||||
from novelwriter.common import simplified
|
from novelwriter.common import simplified
|
||||||
from novelwriter.gui.custom import QSwitch, PagedDialog, QConfigLayout
|
from novelwriter.custom import QSwitch, PagedDialog, QConfigLayout
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiProjectSettings(PagedDialog):
|
class GuiProjectSettings(PagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
PagedDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectSettings ...")
|
logger.debug("Initialising GuiProjectSettings ...")
|
||||||
self.setObjectName("GuiProjectSettings")
|
self.setObjectName("GuiProjectSettings")
|
||||||
@@ -67,10 +67,10 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
self.mainConf.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
|
self.mainConf.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.tabMain = GuiProjectEditMain(self.mainGui, self.theProject)
|
self.tabMain = GuiProjectEditMain(self)
|
||||||
self.tabStatus = GuiProjectEditStatus(self.mainGui, self.theProject, True)
|
self.tabStatus = GuiProjectEditStatus(self, True)
|
||||||
self.tabImport = GuiProjectEditStatus(self.mainGui, self.theProject, False)
|
self.tabImport = GuiProjectEditStatus(self, False)
|
||||||
self.tabReplace = GuiProjectEditReplace(self.mainGui, self.theProject)
|
self.tabReplace = GuiProjectEditReplace(self)
|
||||||
|
|
||||||
self.addTab(self.tabMain, self.tr("Settings"))
|
self.addTab(self.tabMain, self.tr("Settings"))
|
||||||
self.addTab(self.tabStatus, self.tr("Status"))
|
self.addTab(self.tabStatus, self.tr("Status"))
|
||||||
@@ -96,8 +96,6 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
def _doSave(self):
|
def _doSave(self):
|
||||||
"""Save settings and close dialog.
|
"""Save settings and close dialog.
|
||||||
"""
|
"""
|
||||||
logger.verbose("GuiProjectSettings save button clicked")
|
|
||||||
|
|
||||||
projName = self.tabMain.editName.text()
|
projName = self.tabMain.editName.text()
|
||||||
bookTitle = self.tabMain.editTitle.text()
|
bookTitle = self.tabMain.editTitle.text()
|
||||||
bookAuthors = self.tabMain.editAuthors.toPlainText()
|
bookAuthors = self.tabMain.editAuthors.toPlainText()
|
||||||
@@ -166,12 +164,12 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
|
|
||||||
class GuiProjectEditMain(QWidget):
|
class GuiProjectEditMain(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui, theProject):
|
def __init__(self, projGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=projGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = projGui.mainGui
|
||||||
self.theProject = theProject
|
self.theProject = projGui.theProject
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = QConfigLayout()
|
||||||
@@ -256,13 +254,13 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
COL_ROLE = Qt.UserRole + 1
|
COL_ROLE = Qt.UserRole + 1
|
||||||
NUM_ROLE = Qt.UserRole + 2
|
NUM_ROLE = Qt.UserRole + 2
|
||||||
|
|
||||||
def __init__(self, mainGui, theProject, isStatus):
|
def __init__(self, projGui, isStatus):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=projGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = projGui.mainGui
|
||||||
self.theProject = theProject
|
self.theProject = projGui.theProject
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = projGui.mainGui.mainTheme
|
||||||
|
|
||||||
if isStatus:
|
if isStatus:
|
||||||
self.theStatus = self.theProject.statusItems
|
self.theStatus = self.theProject.statusItems
|
||||||
@@ -367,11 +365,12 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
newList = []
|
newList = []
|
||||||
for n in range(self.listBox.topLevelItemCount()):
|
for n in range(self.listBox.topLevelItemCount()):
|
||||||
item = self.listBox.topLevelItem(n)
|
item = self.listBox.topLevelItem(n)
|
||||||
newList.append({
|
if item is not None:
|
||||||
"key": item.data(self.COL_LABEL, self.KEY_ROLE),
|
newList.append({
|
||||||
"name": item.text(self.COL_LABEL),
|
"key": item.data(self.COL_LABEL, self.KEY_ROLE),
|
||||||
"cols": item.data(self.COL_LABEL, self.COL_ROLE),
|
"name": item.text(self.COL_LABEL),
|
||||||
})
|
"cols": item.data(self.COL_LABEL, self.COL_ROLE),
|
||||||
|
})
|
||||||
return newList, self.colDeleted
|
return newList, self.colDeleted
|
||||||
|
|
||||||
return [], []
|
return [], []
|
||||||
@@ -470,7 +469,8 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
self.listBox.insertTopLevelItem(nIndex, cItem)
|
self.listBox.insertTopLevelItem(nIndex, cItem)
|
||||||
self.listBox.clearSelection()
|
self.listBox.clearSelection()
|
||||||
|
|
||||||
cItem.setSelected(True)
|
if cItem is not None:
|
||||||
|
cItem.setSelected(True)
|
||||||
self.colChanged = True
|
self.colChanged = True
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -527,13 +527,13 @@ class GuiProjectEditReplace(QWidget):
|
|||||||
COL_KEY = 0
|
COL_KEY = 0
|
||||||
COL_REPL = 1
|
COL_REPL = 1
|
||||||
|
|
||||||
def __init__(self, mainGui, theProject):
|
def __init__(self, projGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=projGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = projGui.mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = projGui.mainGui.mainTheme
|
||||||
self.theProject = theProject
|
self.theProject = projGui.theProject
|
||||||
self.arChanged = False
|
self.arChanged = False
|
||||||
|
|
||||||
wCol0 = self.mainConf.pxInt(
|
wCol0 = self.mainConf.pxInt(
|
||||||
@@ -619,10 +619,11 @@ class GuiProjectEditReplace(QWidget):
|
|||||||
newList = {}
|
newList = {}
|
||||||
for n in range(self.listBox.topLevelItemCount()):
|
for n in range(self.listBox.topLevelItemCount()):
|
||||||
tItem = self.listBox.topLevelItem(n)
|
tItem = self.listBox.topLevelItem(n)
|
||||||
aKey = self._stripNotAllowed(tItem.text(0))
|
if tItem is not None:
|
||||||
aVal = tItem.text(1)
|
aKey = self._stripNotAllowed(tItem.text(0))
|
||||||
if len(aKey) > 0:
|
aVal = tItem.text(1)
|
||||||
newList[aKey] = aVal
|
if len(aKey) > 0:
|
||||||
|
newList[aKey] = aVal
|
||||||
|
|
||||||
return newList
|
return newList
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class GuiQuoteSelect(QDialog):
|
|||||||
selectedQuote = ""
|
selectedQuote = ""
|
||||||
|
|
||||||
def __init__(self, parent=None, currentQuote='"'):
|
def __init__(self, parent=None, currentQuote='"'):
|
||||||
QDialog.__init__(self, parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiUpdates(QDialog):
|
class GuiUpdates(QDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiUpdates ...")
|
logger.debug("Initialising GuiUpdates ...")
|
||||||
self.setObjectName("GuiUpdates")
|
self.setObjectName("GuiUpdates")
|
||||||
@@ -135,7 +135,7 @@ class GuiUpdates(QDialog):
|
|||||||
logException()
|
logException()
|
||||||
|
|
||||||
relVersion = rawData.get("tag_name", "Unknown")
|
relVersion = rawData.get("tag_name", "Unknown")
|
||||||
relDate = rawData.get("created_at", None)
|
relDate = rawData.get("created_at", "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
relDate = datetime.strptime(relDate[:10], "%Y-%m-%d").strftime("%x")
|
relDate = datetime.strptime(relDate[:10], "%Y-%m-%d").strftime("%x")
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiWordList(QDialog):
|
class GuiWordList(QDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiWordList ...")
|
logger.debug("Initialising GuiWordList ...")
|
||||||
self.setObjectName("GuiWordList")
|
self.setObjectName("GuiWordList")
|
||||||
@@ -156,7 +156,9 @@ class GuiWordList(QDialog):
|
|||||||
try:
|
try:
|
||||||
with open(tmpFile, mode="w", encoding="utf-8") as outFile:
|
with open(tmpFile, mode="w", encoding="utf-8") as outFile:
|
||||||
for i in range(self.listBox.count()):
|
for i in range(self.listBox.count()):
|
||||||
outFile.write(self.listBox.item(i).text() + "\n")
|
item = self.listBox.item(i)
|
||||||
|
if item is not None:
|
||||||
|
outFile.write(item.text() + "\n")
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not save new word list")
|
logger.error("Could not save new word list")
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ def logException():
|
|||||||
"""Log the content of an exception message.
|
"""Log the content of an exception message.
|
||||||
"""
|
"""
|
||||||
exType, exValue, _ = sys.exc_info()
|
exType, exValue, _ = sys.exc_info()
|
||||||
logger.error("%s: %s", exType.__name__, str(exValue))
|
if exType is not None:
|
||||||
|
logger.error("%s: %s", exType.__name__, str(exValue))
|
||||||
|
|
||||||
|
|
||||||
def formatException(exc):
|
def formatException(exc):
|
||||||
@@ -61,7 +62,7 @@ def formatException(exc):
|
|||||||
class NWErrorMessage(QDialog):
|
class NWErrorMessage(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QDialog.__init__(self, parent=parent)
|
super().__init__(parent=parent)
|
||||||
self.setObjectName("NWErrorMessage")
|
self.setObjectName("NWErrorMessage")
|
||||||
|
|
||||||
# Widgets
|
# Widgets
|
||||||
@@ -131,7 +132,7 @@ class NWErrorMessage(QDialog):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import lxml
|
import lxml
|
||||||
lxmlVersion = lxml.__version__
|
lxmlVersion = lxml.__version__ # type: ignore
|
||||||
except Exception:
|
except Exception:
|
||||||
lxmlVersion = "Unknown"
|
lxmlVersion = "Unknown"
|
||||||
|
|
||||||
@@ -198,8 +199,8 @@ def exceptionHandler(exType, exValue, exTrace):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Try a controlled shutdown
|
# Try a controlled shutdown
|
||||||
nwGUI.closeProject(isYes=True)
|
nwGUI.closeProject(isYes=True) # type: ignore
|
||||||
nwGUI.closeMain()
|
nwGUI.closeMain() # type: ignore
|
||||||
logger.info("Emergency shutdown successful")
|
logger.info("Emergency shutdown successful")
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QTextEdit.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditor ...")
|
logger.debug("Initialising GuiDocEditor ...")
|
||||||
|
|
||||||
@@ -303,10 +303,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||||
|
|
||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else: # pragma: no cover
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
# Initialise the syntax highlighter
|
# Initialise the syntax highlighter
|
||||||
self.highLight.initHighlighter()
|
self.highLight.initHighlighter()
|
||||||
@@ -511,12 +508,12 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
|
|
||||||
oldHeader = self.theProject.index.getHandleHeaderLevel(tHandle)
|
oldHeader = self._nwItem.mainHeading
|
||||||
self.theProject.index.scanText(tHandle, docText)
|
self.theProject.index.scanText(tHandle, docText)
|
||||||
newHeader = self.theProject.index.getHandleHeaderLevel(tHandle)
|
newHeader = self._nwItem.mainHeading
|
||||||
|
|
||||||
# ToDo: This should be a signal
|
# ToDo: This should be a signal
|
||||||
if self._updateHeaders(checkLevel=True):
|
if self._updateHeaders():
|
||||||
self.mainGui.requestNovelTreeRefresh()
|
self.mainGui.requestNovelTreeRefresh()
|
||||||
else:
|
else:
|
||||||
self.mainGui.novelView.updateWordCounts(tHandle)
|
self.mainGui.novelView.updateWordCounts(tHandle)
|
||||||
@@ -604,19 +601,15 @@ class GuiDocEditor(QTextEdit):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def getText(self):
|
def getText(self):
|
||||||
"""Get the text content of the current document. This method
|
"""Get the text content of the current document. This method uses
|
||||||
uses QTextEdit->toPlainText for Qt versions lower than 5.9, and
|
QTextDocument->toRawText instead of toPlainText(). The former preserves
|
||||||
the QTextDocument->toRawText for higher version. The latter
|
non-breaking spaces, the latter does not. We still want to get rid of
|
||||||
preserves non-breaking spaces, which the former does not.
|
page and line separators though.
|
||||||
We still want to get rid of page and line separators though.
|
|
||||||
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
|
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
|
||||||
"""
|
"""
|
||||||
if self.mainConf.verQtValue >= 50900:
|
theText = self.document().toRawText()
|
||||||
theText = self.document().toRawText()
|
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
|
||||||
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
|
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
|
||||||
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
|
|
||||||
else:
|
|
||||||
theText = self.toPlainText()
|
|
||||||
return theText
|
return theText
|
||||||
|
|
||||||
def getCursorPosition(self):
|
def getCursorPosition(self):
|
||||||
@@ -671,7 +664,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if theBlock:
|
if theBlock:
|
||||||
self.setCursorPosition(theBlock.position())
|
self.setCursorPosition(theBlock.position())
|
||||||
self.docFooter.updateLineCount()
|
self.docFooter.updateLineCount()
|
||||||
logger.verbose("Cursor moved to line %d", theLine)
|
logger.debug("Cursor moved to line %d", theLine)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -725,7 +718,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if not self._bigDoc:
|
if not self._bigDoc:
|
||||||
self.spellCheckDocument()
|
self.spellCheckDocument()
|
||||||
|
|
||||||
logger.verbose("Spell check is set to '%s'", str(theMode))
|
logger.debug("Spell check is set to '%s'", str(theMode))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -735,7 +728,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
of Qt 5.13, is to clear the text and put it back. This clears
|
of Qt 5.13, is to clear the text and put it back. This clears
|
||||||
the undo stack, so we only do it for big documents.
|
the undo stack, so we only do it for big documents.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Running spell checker")
|
logger.debug("Running spell checker")
|
||||||
if self._spellCheck:
|
if self._spellCheck:
|
||||||
bfTime = time()
|
bfTime = time()
|
||||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
@@ -747,7 +740,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
qApp.restoreOverrideCursor()
|
qApp.restoreOverrideCursor()
|
||||||
afTime = time()
|
afTime = time()
|
||||||
logger.debug("Document highlighted in %.3f ms", 1000*(afTime-bfTime))
|
logger.debug("Document highlighted in %.3f ms", 1000*(afTime-bfTime))
|
||||||
self.mainGui.statusBar.setStatus(self.tr("Spell check complete"))
|
self.mainGui.mainStatus.setStatus(self.tr("Spell check complete"))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -770,7 +763,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
logger.error("Not a document action")
|
logger.error("Not a document action")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.verbose("Requesting action: %s", theAction.name)
|
logger.debug("Requesting action: %s", theAction.name)
|
||||||
|
|
||||||
self._allowAutoReplace(False)
|
self._allowAutoReplace(False)
|
||||||
if theAction == nwDocAction.UNDO:
|
if theAction == nwDocAction.UNDO:
|
||||||
@@ -951,7 +944,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
logger.error("Invalid keyword '%s'", keyWord)
|
logger.error("Invalid keyword '%s'", keyWord)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.verbose("Inserting keyword '%s'", keyWord)
|
logger.debug("Inserting keyword '%s'", keyWord)
|
||||||
theState = self.insertNewBlock("%s: " % keyWord)
|
theState = self.insertNewBlock("%s: " % keyWord)
|
||||||
|
|
||||||
return theState
|
return theState
|
||||||
@@ -1003,7 +996,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if self.mainConf.autoScroll:
|
if self.mainConf.autoScroll:
|
||||||
|
|
||||||
cOld = self.cursorRect().center().y()
|
cOld = self.cursorRect().center().y()
|
||||||
QTextEdit.keyPressEvent(self, keyEvent)
|
super().keyPressEvent(keyEvent)
|
||||||
|
|
||||||
kMod = keyEvent.modifiers()
|
kMod = keyEvent.modifiers()
|
||||||
okMod = kMod == Qt.NoModifier or kMod == Qt.ShiftModifier
|
okMod = kMod == Qt.NoModifier or kMod == Qt.ShiftModifier
|
||||||
@@ -1022,7 +1015,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
doAnim.start()
|
doAnim.start()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
QTextEdit.keyPressEvent(self, keyEvent)
|
super().keyPressEvent(keyEvent)
|
||||||
|
|
||||||
self.docFooter.updateLineCount()
|
self.docFooter.updateLineCount()
|
||||||
|
|
||||||
@@ -1049,7 +1042,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
theCursor = self.cursorForPosition(theEvent.pos())
|
theCursor = self.cursorForPosition(theEvent.pos())
|
||||||
self._followTag(theCursor)
|
self._followTag(theCursor)
|
||||||
|
|
||||||
QTextEdit.mouseReleaseEvent(self, theEvent)
|
super().mouseReleaseEvent(theEvent)
|
||||||
self.docFooter.updateLineCount()
|
self.docFooter.updateLineCount()
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -1059,7 +1052,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
has its margins adjusted according to user preferences.
|
has its margins adjusted according to user preferences.
|
||||||
"""
|
"""
|
||||||
self.updateDocMargins()
|
self.updateDocMargins()
|
||||||
QTextEdit.resizeEvent(self, theEvent)
|
super().resizeEvent(theEvent)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -1179,7 +1172,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
spellCheck &= theWord != ""
|
spellCheck &= theWord != ""
|
||||||
|
|
||||||
if spellCheck:
|
if spellCheck:
|
||||||
logger.verbose("Looking up '%s' in the dictionary", theWord)
|
logger.debug("Looking up '%s' in the dictionary", theWord)
|
||||||
spellCheck &= not self.spEnchant.checkWord(theWord)
|
spellCheck &= not self.spEnchant.checkWord(theWord)
|
||||||
|
|
||||||
if spellCheck:
|
if spellCheck:
|
||||||
@@ -1245,11 +1238,11 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.wCounterDoc.isRunning():
|
if self.wCounterDoc.isRunning():
|
||||||
logger.verbose("Word counter is busy")
|
logger.debug("Word counter is busy")
|
||||||
return
|
return
|
||||||
|
|
||||||
if time() - self._lastEdit < 5 * self.wcInterval:
|
if time() - self._lastEdit < 5 * self.wcInterval:
|
||||||
logger.verbose("Running word counter")
|
logger.debug("Running word counter")
|
||||||
self.mainGui.threadPool.start(self.wCounterDoc)
|
self.mainGui.threadPool.start(self.wCounterDoc)
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -1261,7 +1254,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if self._docHandle is None or self._nwItem is None:
|
if self._docHandle is None or self._nwItem is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.verbose("Updating word count")
|
logger.debug("Updating word count")
|
||||||
|
|
||||||
self._charCount = cCount
|
self._charCount = cCount
|
||||||
self._wordCount = wCount
|
self._wordCount = wCount
|
||||||
@@ -1304,7 +1297,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.wCounterSel.isRunning():
|
if self.wCounterSel.isRunning():
|
||||||
logger.verbose("Selection word counter is busy")
|
logger.debug("Selection word counter is busy")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.mainGui.threadPool.start(self.wCounterSel)
|
self.mainGui.threadPool.start(self.wCounterSel)
|
||||||
@@ -1318,7 +1311,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
if self._docHandle is None or self._nwItem is None:
|
if self._docHandle is None or self._nwItem is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.verbose("User selectee %d words", wCount)
|
logger.debug("User selectee %d words", wCount)
|
||||||
self.docFooter.updateCounts(wCount=wCount, cCount=cCount)
|
self.docFooter.updateCounts(wCount=wCount, cCount=cCount)
|
||||||
self.wcTimerSel.stop()
|
self.wcTimerSel.stop()
|
||||||
|
|
||||||
@@ -1336,11 +1329,11 @@ class GuiDocEditor(QTextEdit):
|
|||||||
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
|
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
|
||||||
)
|
)
|
||||||
if self._queuePos <= thePos:
|
if self._queuePos <= thePos:
|
||||||
logger.verbose("Allowed cursor move to %d <= %d", self._queuePos, thePos)
|
logger.debug("Allowed cursor move to %d <= %d", self._queuePos, thePos)
|
||||||
self.setCursorPosition(self._queuePos)
|
self.setCursorPosition(self._queuePos)
|
||||||
self._queuePos = None
|
self._queuePos = None
|
||||||
else:
|
else:
|
||||||
logger.verbose("Denied cursor move to %d > %d", self._queuePos, thePos)
|
logger.debug("Denied cursor move to %d > %d", self._queuePos, thePos)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1527,7 +1520,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
theCursor.endEditBlock()
|
theCursor.endEditBlock()
|
||||||
theCursor.setPosition(theCursor.selectionEnd())
|
theCursor.setPosition(theCursor.selectionEnd())
|
||||||
self.setTextCursor(theCursor)
|
self.setTextCursor(theCursor)
|
||||||
logger.verbose(
|
logger.debug(
|
||||||
"Replaced occurrence of '%s' with '%s' on line %d",
|
"Replaced occurrence of '%s' with '%s' on line %d",
|
||||||
searchFor, replWith, theCursor.blockNumber()
|
searchFor, replWith, theCursor.blockNumber()
|
||||||
)
|
)
|
||||||
@@ -1905,10 +1898,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if loadTag:
|
if loadTag:
|
||||||
logger.verbose("Attempting to follow tag '%s'", theWord)
|
logger.debug("Attempting to follow tag '%s'", theWord)
|
||||||
self.loadDocumentTagRequest.emit(theWord, nwDocMode.VIEW)
|
self.loadDocumentTagRequest.emit(theWord, nwDocMode.VIEW)
|
||||||
else:
|
else:
|
||||||
logger.verbose("Potential tag '%s'", theWord)
|
logger.debug("Potential tag '%s'", theWord)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1992,13 +1985,22 @@ class GuiDocEditor(QTextEdit):
|
|||||||
nDelete = 3
|
nDelete = 3
|
||||||
tInsert = nwUnicode.U_HELLIP
|
tInsert = nwUnicode.U_HELLIP
|
||||||
|
|
||||||
|
elif theOne == nwUnicode.U_LSEP:
|
||||||
|
# This resolves issue #1150
|
||||||
|
nDelete = 1
|
||||||
|
tInsert = nwUnicode.U_PSEP
|
||||||
|
|
||||||
tCheck = tInsert
|
tCheck = tInsert
|
||||||
if tCheck in self.mainConf.fmtPadBefore:
|
if self.mainConf.fmtPadBefore and tCheck in self.mainConf.fmtPadBefore:
|
||||||
if self._allowSpaceBeforeColon(theText, tCheck):
|
if self._allowSpaceBeforeColon(theText, tCheck):
|
||||||
nDelete = max(nDelete, 1)
|
nDelete = max(nDelete, 1)
|
||||||
|
chkPos = thePos - nDelete - 1
|
||||||
|
if chkPos >= 0 and theText[chkPos].isspace():
|
||||||
|
# Strip existing space before inserting a new (#1061)
|
||||||
|
nDelete += 1
|
||||||
tInsert = self._typPadChar + tInsert
|
tInsert = self._typPadChar + tInsert
|
||||||
|
|
||||||
if tCheck in self.mainConf.fmtPadAfter:
|
if self.mainConf.fmtPadAfter and tCheck in self.mainConf.fmtPadAfter:
|
||||||
if self._allowSpaceBeforeColon(theText, tCheck):
|
if self._allowSpaceBeforeColon(theText, tCheck):
|
||||||
nDelete = max(nDelete, 1)
|
nDelete = max(nDelete, 1)
|
||||||
tInsert = tInsert + self._typPadChar
|
tInsert = tInsert + self._typPadChar
|
||||||
@@ -2023,7 +2025,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _updateHeaders(self, checkPos=False, checkLevel=False):
|
def _updateHeaders(self):
|
||||||
"""Update the headers record and return True if anything
|
"""Update the headers record and return True if anything
|
||||||
changed, if a check flag was provided.
|
changed, if a check flag was provided.
|
||||||
"""
|
"""
|
||||||
@@ -2031,21 +2033,12 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
newHeaders = self.theProject.index.getHandleHeaders(self._docHandle)
|
newHeaders = self.theProject.index.getHandleHeaders(self._docHandle)
|
||||||
if checkPos:
|
newLev = [x[1] for x in newHeaders]
|
||||||
newPos = [x[0] for x in newHeaders]
|
oldLev = [x[1] for x in self._docHeaders]
|
||||||
oldPos = [x[0] for x in self._docHeaders]
|
|
||||||
if checkLevel:
|
|
||||||
newLev = [x[1] for x in newHeaders]
|
|
||||||
oldLev = [x[1] for x in self._docHeaders]
|
|
||||||
|
|
||||||
self._docHeaders = newHeaders
|
self._docHeaders = newHeaders
|
||||||
|
|
||||||
if checkPos:
|
return newLev != oldLev
|
||||||
return newPos != oldPos
|
|
||||||
if checkLevel:
|
|
||||||
return newLev != oldLev
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _checkDocSize(self, theSize):
|
def _checkDocSize(self, theSize):
|
||||||
"""Check if document size crosses the big document limit set in
|
"""Check if document size crosses the big document limit set in
|
||||||
@@ -2152,7 +2145,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
class BackgroundWordCounter(QRunnable):
|
class BackgroundWordCounter(QRunnable):
|
||||||
|
|
||||||
def __init__(self, docEditor, forSelection=False):
|
def __init__(self, docEditor, forSelection=False):
|
||||||
QRunnable.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
self._docEditor = docEditor
|
self._docEditor = docEditor
|
||||||
self._forSelection = forSelection
|
self._forSelection = forSelection
|
||||||
@@ -2202,7 +2195,7 @@ class BackgroundWordCounterSignals(QObject):
|
|||||||
class GuiDocEditSearch(QFrame):
|
class GuiDocEditSearch(QFrame):
|
||||||
|
|
||||||
def __init__(self, docEditor):
|
def __init__(self, docEditor):
|
||||||
QFrame.__init__(self, docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditSearch ...")
|
logger.debug("Initialising GuiDocEditSearch ...")
|
||||||
|
|
||||||
@@ -2429,7 +2422,6 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.searchBox.selectAll()
|
self.searchBox.selectAll()
|
||||||
if self.isRegEx:
|
if self.isRegEx:
|
||||||
self._alertSearchValid(True)
|
self._alertSearchValid(True)
|
||||||
logger.verbose("Setting search text to '%s'", theText)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setReplaceText(self, theText):
|
def setReplaceText(self, theText):
|
||||||
@@ -2469,7 +2461,8 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self._alertSearchValid(theRegEx.isValid())
|
self._alertSearchValid(theRegEx.isValid())
|
||||||
return theRegEx
|
return theRegEx
|
||||||
|
|
||||||
else: # >= 50300 to < 51300
|
else: # pragma: no cover
|
||||||
|
# >= 50300 to < 51300
|
||||||
if self.isCaseSense:
|
if self.isCaseSense:
|
||||||
rxOpt = Qt.CaseSensitive
|
rxOpt = Qt.CaseSensitive
|
||||||
else:
|
else:
|
||||||
@@ -2590,7 +2583,7 @@ class GuiDocEditSearch(QFrame):
|
|||||||
class GuiDocEditHeader(QWidget):
|
class GuiDocEditHeader(QWidget):
|
||||||
|
|
||||||
def __init__(self, docEditor):
|
def __init__(self, docEditor):
|
||||||
QWidget.__init__(self, docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditHeader ...")
|
logger.debug("Initialising GuiDocEditHeader ...")
|
||||||
|
|
||||||
@@ -2636,7 +2629,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
self.editButton.setStyleSheet(buttonStyle)
|
self.editButton.setStyleSheet(buttonStyle)
|
||||||
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.editButton.setVisible(False)
|
self.editButton.setVisible(False)
|
||||||
self.editButton.setToolTip(self.tr("Edit document meta"))
|
self.editButton.setToolTip(self.tr("Edit document label"))
|
||||||
self.editButton.clicked.connect(self._editDocument)
|
self.editButton.clicked.connect(self._editDocument)
|
||||||
|
|
||||||
self.searchButton = QToolButton(self)
|
self.searchButton = QToolButton(self)
|
||||||
@@ -2814,7 +2807,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
class GuiDocEditFooter(QWidget):
|
class GuiDocEditFooter(QWidget):
|
||||||
|
|
||||||
def __init__(self, docEditor):
|
def __init__(self, docEditor):
|
||||||
QWidget.__init__(self, docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditFooter ...")
|
logger.debug("Initialising GuiDocEditFooter ...")
|
||||||
|
|
||||||
@@ -2942,7 +2935,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
"""
|
"""
|
||||||
self._docHandle = tHandle
|
self._docHandle = tHandle
|
||||||
if self._docHandle is None:
|
if self._docHandle is None:
|
||||||
logger.verbose("No handle set, so clearing the editor footer")
|
logger.debug("No handle set, so clearing the editor footer")
|
||||||
self._theItem = None
|
self._theItem = None
|
||||||
else:
|
else:
|
||||||
self._theItem = self.theProject.tree[self._docHandle]
|
self._theItem = self.theProject.tree[self._docHandle]
|
||||||
@@ -2969,8 +2962,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
else:
|
else:
|
||||||
theStatus, theIcon = self._theItem.getImportStatus()
|
theStatus, theIcon = self._theItem.getImportStatus()
|
||||||
sIcon = theIcon.pixmap(self.sPx, self.sPx)
|
sIcon = theIcon.pixmap(self.sPx, self.sPx)
|
||||||
hLevel = self.theProject.index.getHandleHeaderLevel(self._docHandle)
|
sText = f"{theStatus} / {self._theItem.describeMe()}"
|
||||||
sText = f"{theStatus} / {self._theItem.describeMe(hLevel)}"
|
|
||||||
|
|
||||||
self.statusIcon.setPixmap(sIcon)
|
self.statusIcon.setPixmap(sIcon)
|
||||||
self.statusText.setText(sText)
|
self.statusText.setText(sText)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|||||||
BLOCK_TITLE = 4
|
BLOCK_TITLE = 4
|
||||||
|
|
||||||
def __init__(self, theDoc, mainGui, spEnchant):
|
def __init__(self, theDoc, mainGui, spEnchant):
|
||||||
QSyntaxHighlighter.__init__(self, theDoc)
|
super().__init__(theDoc)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocHighlighter ...")
|
logger.debug("Initialising GuiDocHighlighter ...")
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QTextBrowser.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewer ...")
|
logger.debug("Initialising GuiDocViewer ...")
|
||||||
|
|
||||||
@@ -150,10 +150,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||||
|
|
||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else:
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
# If we have a document open, we should reload it in case the font changed
|
# If we have a document open, we should reload it in case the font changed
|
||||||
if self._docHandle is not None:
|
if self._docHandle is not None:
|
||||||
@@ -193,10 +190,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else:
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
# Must be before setHtml
|
# Must be before setHtml
|
||||||
if updateHistory:
|
if updateHistory:
|
||||||
@@ -247,7 +241,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
"""Wrapper function for various document actions on the current
|
"""Wrapper function for various document actions on the current
|
||||||
document.
|
document.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Requesting action: '%s'", theAction.name)
|
logger.debug("Requesting action: '%s'", theAction.name)
|
||||||
if self._docHandle is None:
|
if self._docHandle is None:
|
||||||
logger.error("No document open")
|
logger.error("No document open")
|
||||||
return False
|
return False
|
||||||
@@ -270,7 +264,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
if not isinstance(tAnchor, str):
|
if not isinstance(tAnchor, str):
|
||||||
return False
|
return False
|
||||||
if tAnchor.startswith("#"):
|
if tAnchor.startswith("#"):
|
||||||
logger.verbose("Moving to anchor '%s'", tAnchor)
|
logger.debug("Moving to anchor '%s'", tAnchor)
|
||||||
self.setSource(QUrl(tAnchor))
|
self.setSource(QUrl(tAnchor))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -356,7 +350,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
theBlock = self.document().findBlockByLineNumber(theLine)
|
theBlock = self.document().findBlockByLineNumber(theLine)
|
||||||
if theBlock:
|
if theBlock:
|
||||||
self.setCursorPosition(theBlock.position())
|
self.setCursorPosition(theBlock.position())
|
||||||
logger.verbose("Cursor moved to line %d", theLine)
|
logger.debug("Cursor moved to line %d", theLine)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setScrollPosition(self, thePos):
|
def setScrollPosition(self, thePos):
|
||||||
@@ -402,7 +396,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
"""Process a clicked link internally in the document.
|
"""Process a clicked link internally in the document.
|
||||||
"""
|
"""
|
||||||
theLink = theURL.url()
|
theLink = theURL.url()
|
||||||
logger.verbose("Clicked link: '%s'", theLink)
|
logger.debug("Clicked link: '%s'", theLink)
|
||||||
if len(theLink) > 0:
|
if len(theLink) > 0:
|
||||||
theBits = theLink.split("=")
|
theBits = theLink.split("=")
|
||||||
if len(theBits) == 2:
|
if len(theBits) == 2:
|
||||||
@@ -461,7 +455,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
has its margins adjusted according to user preferences.
|
has its margins adjusted according to user preferences.
|
||||||
"""
|
"""
|
||||||
self.updateDocMargins()
|
self.updateDocMargins()
|
||||||
QTextBrowser.resizeEvent(self, theEvent)
|
super().resizeEvent(theEvent)
|
||||||
return
|
return
|
||||||
|
|
||||||
def mouseReleaseEvent(self, theEvent):
|
def mouseReleaseEvent(self, theEvent):
|
||||||
@@ -472,7 +466,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
elif theEvent.button() == Qt.ForwardButton:
|
elif theEvent.button() == Qt.ForwardButton:
|
||||||
self.navForward()
|
self.navForward()
|
||||||
else:
|
else:
|
||||||
QTextBrowser.mouseReleaseEvent(self, theEvent)
|
super().mouseReleaseEvent(theEvent)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -568,7 +562,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
# END Class GuiDocViewer
|
# END Class GuiDocViewer
|
||||||
|
|
||||||
|
|
||||||
class GuiDocViewHistory():
|
class GuiDocViewHistory:
|
||||||
|
|
||||||
def __init__(self, docViewer):
|
def __init__(self, docViewer):
|
||||||
|
|
||||||
@@ -584,7 +578,7 @@ class GuiDocViewHistory():
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
"""Clear the view history.
|
"""Clear the view history.
|
||||||
"""
|
"""
|
||||||
logger.verbose("View history cleared")
|
logger.debug("View history cleared")
|
||||||
self._navHistory = []
|
self._navHistory = []
|
||||||
self._posHistory = []
|
self._posHistory = []
|
||||||
self._currPos = -1
|
self._currPos = -1
|
||||||
@@ -598,7 +592,7 @@ class GuiDocViewHistory():
|
|||||||
"""
|
"""
|
||||||
if self._currPos >= 0 and self._currPos < len(self._navHistory):
|
if self._currPos >= 0 and self._currPos < len(self._navHistory):
|
||||||
if tHandle == self._navHistory[self._currPos]:
|
if tHandle == self._navHistory[self._currPos]:
|
||||||
logger.verbose("Not updating view hsitory")
|
logger.debug("Not updating view hsitory")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._truncateHistory(self._currPos)
|
self._truncateHistory(self._currPos)
|
||||||
@@ -613,7 +607,7 @@ class GuiDocViewHistory():
|
|||||||
|
|
||||||
self._dumpHistory()
|
self._dumpHistory()
|
||||||
|
|
||||||
logger.verbose("Added '%s' to view history", tHandle)
|
logger.debug("Added '%s' to view history", tHandle)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -622,7 +616,7 @@ class GuiDocViewHistory():
|
|||||||
"""
|
"""
|
||||||
newPos = self._currPos + 1
|
newPos = self._currPos + 1
|
||||||
if newPos < len(self._navHistory):
|
if newPos < len(self._navHistory):
|
||||||
logger.verbose("Move forward in view history")
|
logger.debug("Move forward in view history")
|
||||||
self._prevPos = self._currPos
|
self._prevPos = self._currPos
|
||||||
self._updateScrollBar()
|
self._updateScrollBar()
|
||||||
|
|
||||||
@@ -640,7 +634,7 @@ class GuiDocViewHistory():
|
|||||||
"""
|
"""
|
||||||
newPos = self._currPos - 1
|
newPos = self._currPos - 1
|
||||||
if newPos >= 0:
|
if newPos >= 0:
|
||||||
logger.verbose("Move backward in view history")
|
logger.debug("Move backward in view history")
|
||||||
self._prevPos = self._currPos
|
self._prevPos = self._currPos
|
||||||
self._updateScrollBar()
|
self._updateScrollBar()
|
||||||
|
|
||||||
@@ -686,11 +680,11 @@ class GuiDocViewHistory():
|
|||||||
|
|
||||||
def _dumpHistory(self):
|
def _dumpHistory(self):
|
||||||
"""Debug function to dump history to the logger. Since it is a
|
"""Debug function to dump history to the logger. Since it is a
|
||||||
for loop, it is skipped entirely if log level isn't VERBOSE.
|
for loop, it is skipped entirely if log level isn't DEBUG.
|
||||||
"""
|
"""
|
||||||
if logger.getEffectiveLevel() < logging.DEBUG:
|
if logger.getEffectiveLevel() == logging.DEBUG:
|
||||||
for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory)):
|
for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory)):
|
||||||
logger.verbose(
|
logger.debug(
|
||||||
"History %02d: %s %13s [x:%d]" % (
|
"History %02d: %s %13s [x:%d]" % (
|
||||||
i + 1, ">" if i == self._currPos else " ", h, p
|
i + 1, ">" if i == self._currPos else " ", h, p
|
||||||
)
|
)
|
||||||
@@ -708,7 +702,7 @@ class GuiDocViewHistory():
|
|||||||
class GuiDocViewHeader(QWidget):
|
class GuiDocViewHeader(QWidget):
|
||||||
|
|
||||||
def __init__(self, docViewer):
|
def __init__(self, docViewer):
|
||||||
QWidget.__init__(self, docViewer)
|
super().__init__(parent=docViewer)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewHeader ...")
|
logger.debug("Initialising GuiDocViewHeader ...")
|
||||||
|
|
||||||
@@ -915,7 +909,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
class GuiDocViewFooter(QWidget):
|
class GuiDocViewFooter(QWidget):
|
||||||
|
|
||||||
def __init__(self, docViewer):
|
def __init__(self, docViewer):
|
||||||
QWidget.__init__(self, docViewer)
|
super().__init__(parent=docViewer)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewFooter ...")
|
logger.debug("Initialising GuiDocViewFooter ...")
|
||||||
|
|
||||||
@@ -1110,7 +1104,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
def _doToggleSticky(self, theState):
|
def _doToggleSticky(self, theState):
|
||||||
"""Toggle the sticky flag for the reference panel.
|
"""Toggle the sticky flag for the reference panel.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Reference sticky is %s", str(theState))
|
logger.debug("Reference sticky is %s", str(theState))
|
||||||
self.docViewer.stickyRef = theState
|
self.docViewer.stickyRef = theState
|
||||||
if not theState and self.docViewer.docHandle() is not None:
|
if not theState and self.docViewer.docHandle() is not None:
|
||||||
self.viewMeta.refreshReferences(self.docViewer.docHandle())
|
self.viewMeta.refreshReferences(self.docViewer.docHandle())
|
||||||
@@ -1141,7 +1135,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
class GuiDocViewDetails(QScrollArea):
|
class GuiDocViewDetails(QScrollArea):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QScrollArea.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewDetails ...")
|
logger.debug("Initialising GuiDocViewDetails ...")
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
@@ -1205,7 +1199,7 @@ class GuiDocViewDetails(QScrollArea):
|
|||||||
"""Capture the link-click and forward it to the document viewer
|
"""Capture the link-click and forward it to the document viewer
|
||||||
class for handling.
|
class for handling.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Clicked link: '%s'", theLink)
|
logger.debug("Clicked link: '%s'", theLink)
|
||||||
if len(theLink) == 21:
|
if len(theLink) == 21:
|
||||||
tHandle = theLink[:13]
|
tHandle = theLink[:13]
|
||||||
tAnchor = theLink[13:]
|
tAnchor = theLink[13:]
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiItemDetails(QWidget):
|
class GuiItemDetails(QWidget):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiItemDetails ...")
|
logger.debug("Initialising GuiItemDetails ...")
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
@@ -247,7 +247,7 @@ class GuiItemDetails(QWidget):
|
|||||||
theLabel = theLabel[:96].rstrip()+" ..."
|
theLabel = theLabel[:96].rstrip()+" ..."
|
||||||
|
|
||||||
if nwItem.isFileType():
|
if nwItem.isFileType():
|
||||||
if nwItem.isExported:
|
if nwItem.isActive:
|
||||||
self.labelIcon.setPixmap(self._expCheck)
|
self.labelIcon.setPixmap(self._expCheck)
|
||||||
else:
|
else:
|
||||||
self.labelIcon.setPixmap(self._expCross)
|
self.labelIcon.setPixmap(self._expCross)
|
||||||
@@ -273,12 +273,11 @@ class GuiItemDetails(QWidget):
|
|||||||
# Layout
|
# Layout
|
||||||
# ======
|
# ======
|
||||||
|
|
||||||
hLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
|
|
||||||
usageIcon = self.mainTheme.getItemIcon(
|
usageIcon = self.mainTheme.getItemIcon(
|
||||||
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
|
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
|
||||||
)
|
)
|
||||||
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
|
self.usageIcon.setPixmap(usageIcon.pixmap(iPx, iPx))
|
||||||
self.usageData.setText(nwItem.describeMe(hLevel))
|
self.usageData.setText(nwItem.describeMe())
|
||||||
|
|
||||||
# Counts
|
# Counts
|
||||||
# ======
|
# ======
|
||||||
|
|||||||
@@ -40,9 +40,13 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class GuiMainMenu(QMenuBar):
|
class GuiMainMenu(QMenuBar):
|
||||||
|
"""The GUI main menu. All menu actions are defined here with the
|
||||||
|
main menu as the owner. Each widget that need them elsewhere need to
|
||||||
|
add them from this class.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QMenuBar.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiMainMenu ...")
|
logger.debug("Initialising GuiMainMenu ...")
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
@@ -79,12 +83,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.aSpellCheck.setChecked(theMode)
|
self.aSpellCheck.setChecked(theMode)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFocusMode(self, theMode):
|
|
||||||
"""Forward focus mode check state to its action.
|
|
||||||
"""
|
|
||||||
self.aFocusMode.setChecked(theMode)
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
@@ -164,14 +162,14 @@ class GuiMainMenu(QMenuBar):
|
|||||||
|
|
||||||
# Project > Edit
|
# Project > Edit
|
||||||
self.aEditItem = QAction(self.tr("Rename Item"), self)
|
self.aEditItem = QAction(self.tr("Rename Item"), self)
|
||||||
self.aEditItem.setShortcuts(["F2"])
|
self.aEditItem.setShortcut("F2")
|
||||||
self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None))
|
self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None))
|
||||||
self.projMenu.addAction(self.aEditItem)
|
self.projMenu.addAction(self.aEditItem)
|
||||||
|
|
||||||
# Project > Delete
|
# Project > Delete
|
||||||
self.aDeleteItem = QAction(self.tr("Delete Item"), self)
|
self.aDeleteItem = QAction(self.tr("Delete Item"), self)
|
||||||
self.aDeleteItem.setShortcut("Ctrl+Shift+Del")
|
self.aDeleteItem.setShortcut("Ctrl+Shift+Del")
|
||||||
self.aDeleteItem.triggered.connect(lambda: self.mainGui.projView.deleteItem(None))
|
self.aDeleteItem.triggered.connect(lambda: self.mainGui.projView.requestDeleteItem(None))
|
||||||
self.projMenu.addAction(self.aDeleteItem)
|
self.projMenu.addAction(self.aDeleteItem)
|
||||||
|
|
||||||
# Project > Empty Trash
|
# Project > Empty Trash
|
||||||
@@ -244,16 +242,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.aImportFile.triggered.connect(lambda: self.mainGui.importDocument())
|
self.aImportFile.triggered.connect(lambda: self.mainGui.importDocument())
|
||||||
self.docuMenu.addAction(self.aImportFile)
|
self.docuMenu.addAction(self.aImportFile)
|
||||||
|
|
||||||
# Document > Merge Documents
|
|
||||||
self.aMergeDocs = QAction(self.tr("Merge Folder to Document"), self)
|
|
||||||
self.aMergeDocs.triggered.connect(lambda: self.mainGui.mergeDocuments())
|
|
||||||
self.docuMenu.addAction(self.aMergeDocs)
|
|
||||||
|
|
||||||
# Document > Split Document
|
|
||||||
self.aSplitDoc = QAction(self.tr("Split Document to Folder"), self)
|
|
||||||
self.aSplitDoc.triggered.connect(lambda: self.mainGui.splitDocument())
|
|
||||||
self.docuMenu.addAction(self.aSplitDoc)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _buildEditMenu(self):
|
def _buildEditMenu(self):
|
||||||
@@ -375,8 +363,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# View > Focus Mode
|
# View > Focus Mode
|
||||||
self.aFocusMode = QAction(self.tr("Focus Mode"), self)
|
self.aFocusMode = QAction(self.tr("Focus Mode"), self)
|
||||||
self.aFocusMode.setShortcut("F8")
|
self.aFocusMode.setShortcut("F8")
|
||||||
self.aFocusMode.setCheckable(True)
|
|
||||||
self.aFocusMode.setChecked(self.mainGui.isFocusMode)
|
|
||||||
self.aFocusMode.triggered.connect(lambda: self.mainGui.toggleFocusMode())
|
self.aFocusMode.triggered.connect(lambda: self.mainGui.toggleFocusMode())
|
||||||
self.viewMenu.addAction(self.aFocusMode)
|
self.viewMenu.addAction(self.aFocusMode)
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class GuiNovelView(QWidget):
|
|||||||
openDocumentRequest = pyqtSignal(str, Enum, int, str)
|
openDocumentRequest = pyqtSignal(str, Enum, int, str)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
self.theProject = mainGui.theProject
|
self.theProject = mainGui.theProject
|
||||||
@@ -166,7 +166,7 @@ class GuiNovelView(QWidget):
|
|||||||
class GuiNovelToolBar(QWidget):
|
class GuiNovelToolBar(QWidget):
|
||||||
|
|
||||||
def __init__(self, novelView):
|
def __init__(self, novelView):
|
||||||
QTreeWidget.__init__(self, novelView)
|
super().__init__(parent=novelView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiNovelToolBar ...")
|
logger.debug("Initialising GuiNovelToolBar ...")
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
D_KEY = Qt.UserRole + 2
|
D_KEY = Qt.UserRole + 2
|
||||||
|
|
||||||
def __init__(self, novelView):
|
def __init__(self, novelView):
|
||||||
QTreeWidget.__init__(self, novelView)
|
super().__init__(parent=novelView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiNovelTree ...")
|
logger.debug("Initialising GuiNovelTree ...")
|
||||||
|
|
||||||
@@ -390,13 +390,6 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
fH2.setBold(True)
|
fH2.setBold(True)
|
||||||
|
|
||||||
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
||||||
self._pIndent = [
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h0", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h1", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h2", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h3", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h4", pxH=iPx),
|
|
||||||
]
|
|
||||||
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
|
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
|
||||||
|
|
||||||
# Connect signals
|
# Connect signals
|
||||||
@@ -450,14 +443,14 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
def refreshTree(self, rootHandle=None, overRide=False):
|
def refreshTree(self, rootHandle=None, overRide=False):
|
||||||
"""Called whenever the Novel tab is activated.
|
"""Called whenever the Novel tab is activated.
|
||||||
"""
|
"""
|
||||||
logger.verbose("Requesting refresh of the novel tree")
|
logger.debug("Requesting refresh of the novel tree")
|
||||||
if rootHandle is None:
|
if rootHandle is None:
|
||||||
rootHandle = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
rootHandle = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
||||||
|
|
||||||
treeChanged = self.mainGui.projView.changedSince(self._lastBuild)
|
treeChanged = self.mainGui.projView.changedSince(self._lastBuild)
|
||||||
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
|
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
|
||||||
if not (treeChanged or indexChanged or overRide):
|
if not (treeChanged or indexChanged or overRide):
|
||||||
logger.verbose("No changes have been made to the novel index")
|
logger.debug("No changes have been made to the novel index")
|
||||||
return
|
return
|
||||||
|
|
||||||
selItem = self.selectedItems()
|
selItem = self.selectedItems()
|
||||||
@@ -515,18 +508,19 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
self._actHandle = tHandle
|
self._actHandle = tHandle
|
||||||
for i in range(self.topLevelItemCount()):
|
for i in range(self.topLevelItemCount()):
|
||||||
tItem = self.topLevelItem(i)
|
tItem = self.topLevelItem(i)
|
||||||
if tItem.data(self.C_TITLE, self.D_HANDLE) == tHandle:
|
if tItem is not None:
|
||||||
tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
|
if tItem.data(self.C_TITLE, self.D_HANDLE) == tHandle:
|
||||||
tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
|
tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
|
||||||
tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
|
tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
|
||||||
tItem.setBackground(self.C_MORE, self.palette().alternateBase())
|
tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
|
||||||
else:
|
tItem.setBackground(self.C_MORE, self.palette().alternateBase())
|
||||||
tItem.setBackground(self.C_TITLE, self.palette().base())
|
else:
|
||||||
tItem.setBackground(self.C_WORDS, self.palette().base())
|
tItem.setBackground(self.C_TITLE, self.palette().base())
|
||||||
tItem.setBackground(self.C_EXTRA, self.palette().base())
|
tItem.setBackground(self.C_WORDS, self.palette().base())
|
||||||
tItem.setBackground(self.C_MORE, self.palette().base())
|
tItem.setBackground(self.C_EXTRA, self.palette().base())
|
||||||
|
tItem.setBackground(self.C_MORE, self.palette().base())
|
||||||
|
|
||||||
logger.verbose("Highlighted Novel Tree in %.3f ms", (time() - tStart)*1000)
|
logger.debug("Highlighted Novel Tree in %.3f ms", (time() - tStart)*1000)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -539,7 +533,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
mouse in a blank area of the tree view, and to load a document
|
mouse in a blank area of the tree view, and to load a document
|
||||||
for viewing if the user middle-clicked.
|
for viewing if the user middle-clicked.
|
||||||
"""
|
"""
|
||||||
QTreeWidget.mousePressEvent(self, theEvent)
|
super().mousePressEvent(theEvent)
|
||||||
|
|
||||||
if theEvent.button() == Qt.LeftButton:
|
if theEvent.button() == Qt.LeftButton:
|
||||||
selItem = self.indexAt(theEvent.pos())
|
selItem = self.indexAt(theEvent.pos())
|
||||||
@@ -562,7 +556,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
def focusOutEvent(self, theEvent):
|
def focusOutEvent(self, theEvent):
|
||||||
"""Clear the selection when the tree no longer has focus.
|
"""Clear the selection when the tree no longer has focus.
|
||||||
"""
|
"""
|
||||||
QTreeWidget.focusOutEvent(self, theEvent)
|
super().focusOutEvent(theEvent)
|
||||||
self.clearSelection()
|
self.clearSelection()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -610,7 +604,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
"""
|
"""
|
||||||
self.clearContent()
|
self.clearContent()
|
||||||
tStart = time()
|
tStart = time()
|
||||||
logger.verbose("Building novel tree for root item '%s'", rootHandle)
|
logger.debug("Building novel tree for root item '%s'", rootHandle)
|
||||||
|
|
||||||
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
|
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
|
||||||
for tKey, tHandle, sTitle, novIdx in novStruct:
|
for tKey, tHandle, sTitle, novIdx in novStruct:
|
||||||
@@ -619,8 +613,10 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
if iLevel == 0:
|
if iLevel == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
hDec = self.mainTheme.getHeaderDecoration(iLevel)
|
||||||
|
|
||||||
newItem = QTreeWidgetItem()
|
newItem = QTreeWidgetItem()
|
||||||
newItem.setData(self.C_TITLE, Qt.DecorationRole, self._pIndent[iLevel])
|
newItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
|
||||||
newItem.setText(self.C_TITLE, novIdx.title)
|
newItem.setText(self.C_TITLE, novIdx.title)
|
||||||
newItem.setData(self.C_TITLE, self.D_HANDLE, tHandle)
|
newItem.setData(self.C_TITLE, self.D_HANDLE, tHandle)
|
||||||
newItem.setData(self.C_TITLE, self.D_TITLE, sTitle)
|
newItem.setData(self.C_TITLE, self.D_TITLE, sTitle)
|
||||||
@@ -641,7 +637,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
self.setActiveHandle(self._actHandle)
|
self.setActiveHandle(self._actHandle)
|
||||||
|
|
||||||
logger.verbose("Novel Tree built in %.3f ms", (time() - tStart)*1000)
|
logger.debug("Novel Tree built in %.3f ms", (time() - tStart)*1000)
|
||||||
self._lastBuild = time()
|
self._lastBuild = time()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class GuiOutlineView(QWidget):
|
|||||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QWidget.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -198,7 +198,7 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
viewColumnToggled = pyqtSignal(bool, Enum)
|
viewColumnToggled = pyqtSignal(bool, Enum)
|
||||||
|
|
||||||
def __init__(self, theOutline):
|
def __init__(self, theOutline):
|
||||||
QTreeWidget.__init__(self, theOutline)
|
super().__init__(parent=theOutline)
|
||||||
|
|
||||||
logger.debug("Initialising GuiOutlineToolBar ...")
|
logger.debug("Initialising GuiOutlineToolBar ...")
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
activeItemChanged = pyqtSignal(str, str)
|
activeItemChanged = pyqtSignal(str, str)
|
||||||
|
|
||||||
def __init__(self, theOutline):
|
def __init__(self, theOutline):
|
||||||
QTreeWidget.__init__(self, theOutline)
|
super().__init__(parent=theOutline)
|
||||||
|
|
||||||
logger.debug("Initialising GuiOutlineTree ...")
|
logger.debug("Initialising GuiOutlineTree ...")
|
||||||
|
|
||||||
@@ -391,13 +391,6 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
fH2.setBold(True)
|
fH2.setBold(True)
|
||||||
|
|
||||||
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
||||||
self._pIndent = [
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h0", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h1", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h2", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h3", pxH=iPx),
|
|
||||||
self.mainTheme.loadDecoration("deco_doc_h4", pxH=iPx),
|
|
||||||
]
|
|
||||||
self._dIcon = {
|
self._dIcon = {
|
||||||
"H0": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"),
|
"H0": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H0"),
|
||||||
"H1": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"),
|
"H1": self.mainTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, "H1"),
|
||||||
@@ -491,7 +484,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
# was last built, we rebuild the tree from the updated index.
|
# was last built, we rebuild the tree from the updated index.
|
||||||
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
|
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
|
||||||
if not (novelChanged or indexChanged or overRide):
|
if not (novelChanged or indexChanged or overRide):
|
||||||
logger.verbose("No changes have been made to the novel index")
|
logger.debug("No changes have been made to the novel index")
|
||||||
return
|
return
|
||||||
|
|
||||||
self._populateTree(rootHandle)
|
self._populateTree(rootHandle)
|
||||||
@@ -561,11 +554,9 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
"""Receive the changes to column visibility forwarded by the
|
"""Receive the changes to column visibility forwarded by the
|
||||||
column selection menu.
|
column selection menu.
|
||||||
"""
|
"""
|
||||||
logger.verbose("User toggled Outline column '%s'", theItem.name)
|
|
||||||
if theItem in self._colIdx:
|
if theItem in self._colIdx:
|
||||||
self.setColumnHidden(self._colIdx[theItem], not isChecked)
|
self.setColumnHidden(self._colIdx[theItem], not isChecked)
|
||||||
self._saveHeaderState()
|
self._saveHeaderState()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -684,28 +675,29 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
self.setColumnHidden(self._colIdx[nwOutline.TITLE], False)
|
self.setColumnHidden(self._colIdx[nwOutline.TITLE], False)
|
||||||
|
|
||||||
headItem = self.headerItem()
|
headItem = self.headerItem()
|
||||||
headItem.setTextAlignment(self._colIdx[nwOutline.CCOUNT], Qt.AlignRight)
|
if headItem is not None:
|
||||||
headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
|
headItem.setTextAlignment(self._colIdx[nwOutline.CCOUNT], Qt.AlignRight)
|
||||||
headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
|
headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
|
||||||
|
headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
|
||||||
|
|
||||||
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
|
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
|
||||||
for _, tHandle, sTitle, novIdx in novStruct:
|
for _, tHandle, sTitle, novIdx in novStruct:
|
||||||
|
|
||||||
iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
|
iLevel = nwHeaders.H_LEVEL.get(novIdx.level, 0)
|
||||||
dLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
|
|
||||||
if iLevel == 0:
|
if iLevel == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
trItem = QTreeWidgetItem()
|
trItem = QTreeWidgetItem()
|
||||||
nwItem = self.theProject.tree[tHandle]
|
nwItem = self.theProject.tree[tHandle]
|
||||||
|
hDec = self.mainTheme.getHeaderDecoration(iLevel)
|
||||||
|
|
||||||
trItem.setData(self._colIdx[nwOutline.TITLE], Qt.DecorationRole, self._pIndent[iLevel])
|
trItem.setData(self._colIdx[nwOutline.TITLE], Qt.DecorationRole, hDec)
|
||||||
trItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title)
|
trItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title)
|
||||||
trItem.setData(self._colIdx[nwOutline.TITLE], self.D_HANDLE, tHandle)
|
trItem.setData(self._colIdx[nwOutline.TITLE], self.D_HANDLE, tHandle)
|
||||||
trItem.setData(self._colIdx[nwOutline.TITLE], self.D_TITLE, sTitle)
|
trItem.setData(self._colIdx[nwOutline.TITLE], self.D_TITLE, sTitle)
|
||||||
trItem.setFont(self._colIdx[nwOutline.TITLE], self._hFonts[iLevel])
|
trItem.setFont(self._colIdx[nwOutline.TITLE], self._hFonts[iLevel])
|
||||||
trItem.setText(self._colIdx[nwOutline.LEVEL], novIdx.level)
|
trItem.setText(self._colIdx[nwOutline.LEVEL], novIdx.level)
|
||||||
trItem.setIcon(self._colIdx[nwOutline.LABEL], self._dIcon[dLevel])
|
trItem.setIcon(self._colIdx[nwOutline.LABEL], self._dIcon[nwItem.mainHeading])
|
||||||
trItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
|
trItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
|
||||||
trItem.setText(self._colIdx[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
trItem.setText(self._colIdx[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
||||||
trItem.setText(self._colIdx[nwOutline.SYNOP], novIdx.synopsis)
|
trItem.setText(self._colIdx[nwOutline.SYNOP], novIdx.synopsis)
|
||||||
@@ -741,7 +733,7 @@ class GuiOutlineHeaderMenu(QMenu):
|
|||||||
columnToggled = pyqtSignal(bool, Enum)
|
columnToggled = pyqtSignal(bool, Enum)
|
||||||
|
|
||||||
def __init__(self, theOutline):
|
def __init__(self, theOutline):
|
||||||
QMenu.__init__(self, theOutline)
|
super().__init__(parent=theOutline)
|
||||||
|
|
||||||
self.acceptToggle = True
|
self.acceptToggle = True
|
||||||
|
|
||||||
@@ -792,7 +784,7 @@ class GuiOutlineDetails(QScrollArea):
|
|||||||
itemTagClicked = pyqtSignal(str)
|
itemTagClicked = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, theOutline):
|
def __init__(self, theOutline):
|
||||||
QScrollArea.__init__(self, theOutline)
|
super().__init__(parent=theOutline)
|
||||||
|
|
||||||
logger.debug("Initialising GuiOutlineDetails ...")
|
logger.debug("Initialising GuiOutlineDetails ...")
|
||||||
|
|
||||||
@@ -1048,7 +1040,7 @@ class GuiOutlineDetails(QScrollArea):
|
|||||||
self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
|
self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
|
||||||
self.titleValue.setText(novIdx.title)
|
self.titleValue.setText(novIdx.title)
|
||||||
|
|
||||||
itemStatus, _ = nwItem.getImportStatus()
|
itemStatus, _ = nwItem.getImportStatus(incIcon=False)
|
||||||
|
|
||||||
self.fileValue.setText(nwItem.itemName)
|
self.fileValue.setText(nwItem.itemName)
|
||||||
self.itemValue.setText(itemStatus)
|
self.itemValue.setText(itemStatus)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiMainStatus(QStatusBar):
|
class GuiMainStatus(QStatusBar):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QStatusBar.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiMainStatus ...")
|
logger.debug("Initialising GuiMainStatus ...")
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from PyQt5.QtGui import (
|
|||||||
|
|
||||||
from novelwriter.enum import nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemLayout, nwItemType
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import NWConfigParser, readTextFile
|
from novelwriter.common import NWConfigParser, minmax, readTextFile
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -120,11 +120,13 @@ class GuiTheme:
|
|||||||
self._availThemes = {}
|
self._availThemes = {}
|
||||||
self._availSyntax = {}
|
self._availSyntax = {}
|
||||||
|
|
||||||
self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax"))
|
|
||||||
self._listConf(self._availSyntax, os.path.join(self.mainConf.assetPath, "syntax"))
|
self._listConf(self._availSyntax, os.path.join(self.mainConf.assetPath, "syntax"))
|
||||||
self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes"))
|
|
||||||
self._listConf(self._availThemes, os.path.join(self.mainConf.assetPath, "themes"))
|
self._listConf(self._availThemes, os.path.join(self.mainConf.assetPath, "themes"))
|
||||||
|
|
||||||
|
if self.mainConf.dataPath: # Not guaranteed to be set
|
||||||
|
self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax"))
|
||||||
|
self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes"))
|
||||||
|
|
||||||
self.updateFont()
|
self.updateFont()
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
self.iconCache.updateTheme()
|
self.iconCache.updateTheme()
|
||||||
@@ -134,13 +136,14 @@ class GuiTheme:
|
|||||||
self.getPixmap = self.iconCache.getPixmap
|
self.getPixmap = self.iconCache.getPixmap
|
||||||
self.getItemIcon = self.iconCache.getItemIcon
|
self.getItemIcon = self.iconCache.getItemIcon
|
||||||
self.loadDecoration = self.iconCache.loadDecoration
|
self.loadDecoration = self.iconCache.loadDecoration
|
||||||
|
self.getHeaderDecoration = self.iconCache.getHeaderDecoration
|
||||||
|
|
||||||
# Extract Other Info
|
# Extract Other Info
|
||||||
self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
|
self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
|
||||||
self.guiScale = qApp.primaryScreen().logicalDotsPerInchX()/96.0
|
self.guiScale = qApp.primaryScreen().logicalDotsPerInchX()/96.0
|
||||||
self.mainConf.guiScale = self.guiScale
|
self.mainConf.guiScale = self.guiScale
|
||||||
logger.verbose("GUI DPI: %.1f", self.guiDPI)
|
logger.debug("GUI DPI: %.1f", self.guiDPI)
|
||||||
logger.verbose("GUI Scale: %.2f", self.guiScale)
|
logger.debug("GUI Scale: %.2f", self.guiScale)
|
||||||
|
|
||||||
# Fonts
|
# Fonts
|
||||||
self.guiFont = qApp.font()
|
self.guiFont = qApp.font()
|
||||||
@@ -157,12 +160,12 @@ class GuiTheme:
|
|||||||
self.guiFontFixed.setPointSizeF(0.95*self.fontPointSize)
|
self.guiFontFixed.setPointSizeF(0.95*self.fontPointSize)
|
||||||
self.guiFontFixed.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
|
self.guiFontFixed.setFamily(QFontDatabase.systemFont(QFontDatabase.FixedFont).family())
|
||||||
|
|
||||||
logger.verbose("GUI Font Family: %s", self.guiFont.family())
|
logger.debug("GUI Font Family: %s", self.guiFont.family())
|
||||||
logger.verbose("GUI Font Point Size: %.2f", self.fontPointSize)
|
logger.debug("GUI Font Point Size: %.2f", self.fontPointSize)
|
||||||
logger.verbose("GUI Font Pixel Size: %d", self.fontPixelSize)
|
logger.debug("GUI Font Pixel Size: %d", self.fontPixelSize)
|
||||||
logger.verbose("GUI Base Icon Size: %d", self.baseIconSize)
|
logger.debug("GUI Base Icon Size: %d", self.baseIconSize)
|
||||||
logger.verbose("Text 'N' Height: %d", self.textNHeight)
|
logger.debug("Text 'N' Height: %d", self.textNHeight)
|
||||||
logger.verbose("Text 'N' Width: %d", self.textNWidth)
|
logger.debug("Text 'N' Width: %d", self.textNWidth)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -355,7 +358,7 @@ class GuiTheme:
|
|||||||
|
|
||||||
confParser = NWConfigParser()
|
confParser = NWConfigParser()
|
||||||
for themeKey, themePath in self._availThemes.items():
|
for themeKey, themePath in self._availThemes.items():
|
||||||
logger.verbose("Checking theme config for '%s'", themeKey)
|
logger.debug("Checking theme config for '%s'", themeKey)
|
||||||
themeName = _loadInternalName(confParser, themePath)
|
themeName = _loadInternalName(confParser, themePath)
|
||||||
if themeName:
|
if themeName:
|
||||||
self._themeList.append((themeKey, themeName))
|
self._themeList.append((themeKey, themeName))
|
||||||
@@ -372,7 +375,7 @@ class GuiTheme:
|
|||||||
|
|
||||||
confParser = NWConfigParser()
|
confParser = NWConfigParser()
|
||||||
for syntaxKey, syntaxPath in self._availSyntax.items():
|
for syntaxKey, syntaxPath in self._availSyntax.items():
|
||||||
logger.verbose("Checking theme syntax for '%s'", syntaxKey)
|
logger.debug("Checking theme syntax for '%s'", syntaxKey)
|
||||||
syntaxName = _loadInternalName(confParser, syntaxPath)
|
syntaxName = _loadInternalName(confParser, syntaxPath)
|
||||||
if syntaxName:
|
if syntaxName:
|
||||||
self._syntaxList.append((syntaxKey, syntaxName))
|
self._syntaxList.append((syntaxKey, syntaxName))
|
||||||
@@ -457,17 +460,17 @@ class GuiIcons:
|
|||||||
ICON_KEYS = {
|
ICON_KEYS = {
|
||||||
# Project and GUI icons
|
# Project and GUI icons
|
||||||
"novelwriter", "cls_archive", "cls_character", "cls_custom", "cls_entity", "cls_none",
|
"novelwriter", "cls_archive", "cls_character", "cls_custom", "cls_entity", "cls_none",
|
||||||
"cls_novel", "cls_object", "cls_plot", "cls_timeline", "cls_trash", "cls_world", "doc_h0",
|
"cls_novel", "cls_object", "cls_plot", "cls_timeline", "cls_trash", "cls_world",
|
||||||
"doc_h1", "doc_h2", "doc_h3", "doc_h4", "proj_chapter", "proj_details", "proj_document",
|
"proj_chapter", "proj_details", "proj_document", "proj_folder", "proj_note", "proj_nwx",
|
||||||
"proj_folder", "proj_note", "proj_nwx", "proj_scene", "proj_stats", "proj_title",
|
"proj_section", "proj_scene", "proj_stats", "proj_title", "search_cancel", "search_case",
|
||||||
"search_cancel", "search_case", "search_loop", "search_preserve", "search_project",
|
"search_loop", "search_preserve", "search_project", "search_regex", "search_word",
|
||||||
"search_regex", "search_word", "status_idle", "status_lang", "status_lines",
|
"status_idle", "status_lang", "status_lines", "status_stats", "status_time", "view_build",
|
||||||
"status_stats", "status_time", "view_build", "view_editor", "view_novel", "view_outline",
|
"view_editor", "view_novel", "view_outline",
|
||||||
|
|
||||||
# General Button Icons
|
# General Button Icons
|
||||||
"add", "backward", "check", "clear", "close", "cross", "delete", "done", "down", "edit",
|
"add", "backward", "bookmark", "check", "close", "cross", "down", "edit", "forward",
|
||||||
"forward", "hash", "maximise", "menu", "minimise", "reference", "refresh", "remove",
|
"maximise", "menu", "minimise", "reference", "refresh", "remove", "search_replace",
|
||||||
"save", "search_replace", "search", "settings", "up",
|
"search", "settings", "up",
|
||||||
|
|
||||||
# Switches
|
# Switches
|
||||||
"sticky-on", "sticky-off",
|
"sticky-on", "sticky-off",
|
||||||
@@ -490,6 +493,7 @@ class GuiIcons:
|
|||||||
self._qIcons = {}
|
self._qIcons = {}
|
||||||
self._themeMap = {}
|
self._themeMap = {}
|
||||||
self._themeList = []
|
self._themeList = []
|
||||||
|
self._headerDec = []
|
||||||
self._confName = "icons.conf"
|
self._confName = "icons.conf"
|
||||||
|
|
||||||
# Icon Theme Path
|
# Icon Theme Path
|
||||||
@@ -557,7 +561,7 @@ class GuiIcons:
|
|||||||
iconPath = os.path.join(self._themePath, iconFile)
|
iconPath = os.path.join(self._themePath, iconFile)
|
||||||
if os.path.isfile(iconPath):
|
if os.path.isfile(iconPath):
|
||||||
self._themeMap[iconName] = iconPath
|
self._themeMap[iconName] = iconPath
|
||||||
logger.verbose("Icon slot '%s' using file '%s'", iconName, iconFile)
|
logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile)
|
||||||
else:
|
else:
|
||||||
logger.error("Icon file '%s' not in theme folder", iconFile)
|
logger.error("Icon file '%s' not in theme folder", iconFile)
|
||||||
|
|
||||||
@@ -641,6 +645,8 @@ class GuiIcons:
|
|||||||
iconName = "proj_chapter"
|
iconName = "proj_chapter"
|
||||||
elif hLevel == "H3":
|
elif hLevel == "H3":
|
||||||
iconName = "proj_scene"
|
iconName = "proj_scene"
|
||||||
|
elif hLevel == "H4":
|
||||||
|
iconName = "proj_section"
|
||||||
elif tLayout == nwItemLayout.NOTE:
|
elif tLayout == nwItemLayout.NOTE:
|
||||||
iconName = "proj_note"
|
iconName = "proj_note"
|
||||||
if iconName is None:
|
if iconName is None:
|
||||||
@@ -648,6 +654,20 @@ class GuiIcons:
|
|||||||
|
|
||||||
return self.getIcon(iconName)
|
return self.getIcon(iconName)
|
||||||
|
|
||||||
|
def getHeaderDecoration(self, hLevel):
|
||||||
|
"""Get the decoration for a specific header level.
|
||||||
|
"""
|
||||||
|
if not self._headerDec:
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
self._headerDec = [
|
||||||
|
self.loadDecoration("deco_doc_h0", pxH=iPx),
|
||||||
|
self.loadDecoration("deco_doc_h1", pxH=iPx),
|
||||||
|
self.loadDecoration("deco_doc_h2", pxH=iPx),
|
||||||
|
self.loadDecoration("deco_doc_h3", pxH=iPx),
|
||||||
|
self.loadDecoration("deco_doc_h4", pxH=iPx),
|
||||||
|
]
|
||||||
|
return self._headerDec[minmax(hLevel, 0, 4)]
|
||||||
|
|
||||||
def listThemes(self):
|
def listThemes(self):
|
||||||
"""Scan the icons themes folder and list all themes.
|
"""Scan the icons themes folder and list all themes.
|
||||||
"""
|
"""
|
||||||
@@ -660,7 +680,7 @@ class GuiIcons:
|
|||||||
if not os.path.isdir(themePath):
|
if not os.path.isdir(themePath):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.verbose("Checking icon theme config for '%s'", themeDir)
|
logger.debug("Checking icon theme config for '%s'", themeDir)
|
||||||
themeConf = os.path.join(themePath, self._confName)
|
themeConf = os.path.join(themePath, self._confName)
|
||||||
themeName = _loadInternalName(confParser, themeConf)
|
themeName = _loadInternalName(confParser, themeConf)
|
||||||
if themeName:
|
if themeName:
|
||||||
@@ -708,7 +728,7 @@ class GuiIcons:
|
|||||||
# Otherwise, we load from the theme folder
|
# Otherwise, we load from the theme folder
|
||||||
if iconKey in self._themeMap:
|
if iconKey in self._themeMap:
|
||||||
relPath = os.path.relpath(self._themeMap[iconKey], self._iconPath)
|
relPath = os.path.relpath(self._themeMap[iconKey], self._iconPath)
|
||||||
logger.verbose("Loading: %s", relPath)
|
logger.debug("Loading: %s", relPath)
|
||||||
return QIcon(self._themeMap[iconKey])
|
return QIcon(self._themeMap[iconKey])
|
||||||
|
|
||||||
# If we didn't find one, give up and return an empty icon
|
# If we didn't find one, give up and return an empty icon
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class GuiViewsBar(QToolBar):
|
|||||||
viewChangeRequested = pyqtSignal(nwView)
|
viewChangeRequested = pyqtSignal(nwView)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QToolBar.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiViewsBar ...")
|
logger.debug("Initialising GuiViewsBar ...")
|
||||||
|
|
||||||
@@ -67,52 +67,49 @@ class GuiViewsBar(QToolBar):
|
|||||||
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
self.aProject = QAction(self.tr("Project"))
|
self.aProject = QAction(self.tr("Project"), self)
|
||||||
self.aProject.setFont(lblFont)
|
self.aProject.setFont(lblFont)
|
||||||
self.aProject.setToolTip(self.tr("Show project tree and editor"))
|
self.aProject.setToolTip(self.tr("Project Tree View"))
|
||||||
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
|
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
|
||||||
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
|
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
|
||||||
|
|
||||||
self.aNovel = QAction(self.tr("Novel"))
|
self.aNovel = QAction(self.tr("Novel"), self)
|
||||||
self.aNovel.setFont(lblFont)
|
self.aNovel.setFont(lblFont)
|
||||||
self.aNovel.setToolTip(self.tr("Show novel tree and editor"))
|
self.aNovel.setToolTip(self.tr("Novel Tree View"))
|
||||||
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
|
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
|
||||||
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
|
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
|
||||||
|
|
||||||
self.aOutline = QAction(self.tr("Outline"))
|
self.aOutline = QAction(self.tr("Outline"), self)
|
||||||
self.aOutline.setFont(lblFont)
|
self.aOutline.setFont(lblFont)
|
||||||
self.aOutline.setToolTip(self.tr("Show novel outline"))
|
self.aOutline.setToolTip(self.tr("Novel Outline View"))
|
||||||
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
|
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
|
||||||
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
|
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
|
||||||
|
|
||||||
self.aBuild = QAction(self.tr("Build"))
|
self.aBuild = QAction(self.tr("Build"), self)
|
||||||
self.aBuild.setFont(lblFont)
|
self.aBuild.setFont(lblFont)
|
||||||
self.aBuild.setToolTip(self.tr("Build novel project"))
|
self.aBuild.setToolTip(self.tr("Build Novel Project"))
|
||||||
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
|
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
|
||||||
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
||||||
|
|
||||||
self.aDetails = QAction(self.tr("Details"))
|
self.aDetails = QAction(self.tr("Details"), self)
|
||||||
self.aDetails.setFont(lblFont)
|
self.aDetails.setFont(lblFont)
|
||||||
self.aDetails.setToolTip(self.tr("Show project details"))
|
self.aDetails.setToolTip(self.tr("Project Details"))
|
||||||
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
|
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
|
||||||
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
|
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
|
||||||
|
|
||||||
self.aStats = QAction(self.tr("Stats"))
|
self.aStats = QAction(self.tr("Stats"), self)
|
||||||
self.aStats.setFont(lblFont)
|
self.aStats.setFont(lblFont)
|
||||||
self.aStats.setToolTip(self.tr("Show project statistics"))
|
self.aStats.setToolTip(self.tr("Writing Statistics"))
|
||||||
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
|
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
|
||||||
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
||||||
|
|
||||||
# Settings Menu
|
# Settings Menu
|
||||||
self.mSettings = QMenu()
|
self.mSettings = QMenu()
|
||||||
|
|
||||||
self.aPrjSettings = QAction(self.tr("Project Settings"))
|
self.mSettings.addAction(self.mainGui.mainMenu.aEditWordList)
|
||||||
self.aPrjSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog())
|
self.mSettings.addAction(self.mainGui.mainMenu.aProjectSettings)
|
||||||
self.mSettings.addAction(self.aPrjSettings)
|
self.mSettings.addSeparator()
|
||||||
|
self.mSettings.addAction(self.mainGui.mainMenu.aPreferences)
|
||||||
self.aPreferences = QAction(self.tr("Preferences"))
|
|
||||||
self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog())
|
|
||||||
self.mSettings.addAction(self.aPreferences)
|
|
||||||
|
|
||||||
self.tbSettings = QToolButton(self)
|
self.tbSettings = QToolButton(self)
|
||||||
self.tbSettings.setFont(lblFont)
|
self.tbSettings.setFont(lblFont)
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ from novelwriter.gui import (
|
|||||||
GuiViewsBar
|
GuiViewsBar
|
||||||
)
|
)
|
||||||
from novelwriter.dialogs import (
|
from novelwriter.dialogs import (
|
||||||
GuiAbout, GuiDocMerge, GuiDocSplit, GuiPreferences, GuiProjectDetails,
|
GuiAbout, GuiPreferences, GuiProjectDetails, GuiProjectLoad,
|
||||||
GuiProjectLoad, GuiProjectSettings, GuiUpdates, GuiWordList
|
GuiProjectSettings, GuiUpdates, GuiWordList
|
||||||
)
|
)
|
||||||
from novelwriter.tools import (
|
from novelwriter.tools import (
|
||||||
GuiBuildNovel, GuiLipsum, GuiProjectWizard, GuiWritingStats
|
GuiBuildNovel, GuiLipsum, GuiProjectWizard, GuiWritingStats
|
||||||
@@ -55,6 +55,7 @@ from novelwriter.enum import (
|
|||||||
nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwState, nwView
|
nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwState, nwView
|
||||||
)
|
)
|
||||||
from novelwriter.common import getGuiItem, hexToInt
|
from novelwriter.common import getGuiItem, hexToInt
|
||||||
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiMain(QMainWindow):
|
class GuiMain(QMainWindow):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QMainWindow.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
logger.debug("Initialising GUI ...")
|
logger.debug("Initialising GUI ...")
|
||||||
self.setObjectName("GuiMain")
|
self.setObjectName("GuiMain")
|
||||||
@@ -104,7 +105,7 @@ class GuiMain(QMainWindow):
|
|||||||
hWd = self.mainConf.pxInt(4)
|
hWd = self.mainConf.pxInt(4)
|
||||||
|
|
||||||
# Main GUI Elements
|
# Main GUI Elements
|
||||||
self.statusBar = GuiMainStatus(self)
|
self.mainStatus = GuiMainStatus(self)
|
||||||
self.projView = GuiProjectView(self)
|
self.projView = GuiProjectView(self)
|
||||||
self.novelView = GuiNovelView(self)
|
self.novelView = GuiNovelView(self)
|
||||||
self.docEditor = GuiDocEditor(self)
|
self.docEditor = GuiDocEditor(self)
|
||||||
@@ -189,8 +190,9 @@ class GuiMain(QMainWindow):
|
|||||||
# Set Main Window Elements
|
# Set Main Window Elements
|
||||||
self.setMenuBar(self.mainMenu)
|
self.setMenuBar(self.mainMenu)
|
||||||
self.setCentralWidget(self.mainStack)
|
self.setCentralWidget(self.mainStack)
|
||||||
self.setStatusBar(self.statusBar)
|
self.setStatusBar(self.mainStatus)
|
||||||
self.addToolBar(Qt.LeftToolBarArea, self.viewsBar)
|
self.addToolBar(Qt.LeftToolBarArea, self.viewsBar)
|
||||||
|
self.setContextMenuPolicy(Qt.NoContextMenu) # Issue #1147
|
||||||
|
|
||||||
# Connect Signals
|
# Connect Signals
|
||||||
# ===============
|
# ===============
|
||||||
@@ -205,12 +207,13 @@ class GuiMain(QMainWindow):
|
|||||||
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
|
||||||
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
||||||
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
||||||
|
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
||||||
|
|
||||||
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||||
self.novelView.openDocumentRequest.connect(self._openDocument)
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
||||||
|
|
||||||
self.docEditor.spellDictionaryChanged.connect(self.statusBar.setLanguage)
|
self.docEditor.spellDictionaryChanged.connect(self.mainStatus.setLanguage)
|
||||||
self.docEditor.docEditedStatusChanged.connect(self.statusBar.doUpdateDocumentStatus)
|
self.docEditor.docEditedStatusChanged.connect(self.mainStatus.doUpdateDocumentStatus)
|
||||||
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
|
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
|
||||||
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
|
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
|
||||||
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
||||||
@@ -252,7 +255,7 @@ class GuiMain(QMainWindow):
|
|||||||
keyEscape.activated.connect(self._keyPressEscape)
|
keyEscape.activated.connect(self._keyPressEscape)
|
||||||
|
|
||||||
# Forward Functions
|
# Forward Functions
|
||||||
self.setStatus = self.statusBar.setStatus
|
self.setStatus = self.mainStatus.setStatus
|
||||||
|
|
||||||
# Force a show of the GUI
|
# Force a show of the GUI
|
||||||
self.show()
|
self.show()
|
||||||
@@ -264,7 +267,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.initMain()
|
self.initMain()
|
||||||
self.asProjTimer.start()
|
self.asProjTimer.start()
|
||||||
self.asDocTimer.start()
|
self.asDocTimer.start()
|
||||||
self.statusBar.clearStatus()
|
self.mainStatus.clearStatus()
|
||||||
|
|
||||||
# Handle Windows Mode
|
# Handle Windows Mode
|
||||||
self.showNormal()
|
self.showNormal()
|
||||||
@@ -305,7 +308,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.outlineView.clearProject()
|
self.outlineView.clearProject()
|
||||||
|
|
||||||
# General
|
# General
|
||||||
self.statusBar.clearStatus()
|
self.mainStatus.clearStatus()
|
||||||
self._updateWindowTitle()
|
self._updateWindowTitle()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -351,7 +354,7 @@ class GuiMain(QMainWindow):
|
|||||||
logger.error("No projData or projPath set")
|
logger.error("No projData or projPath set")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if os.path.isfile(os.path.join(projPath, self.theProject.projFile)):
|
if os.path.isfile(os.path.join(projPath, nwFiles.PROJ_FILE)):
|
||||||
self.makeAlert(self.tr(
|
self.makeAlert(self.tr(
|
||||||
"A project already exists in that location. "
|
"A project already exists in that location. "
|
||||||
"Please choose another folder."
|
"Please choose another folder."
|
||||||
@@ -360,18 +363,27 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
logger.info("Creating new project")
|
logger.info("Creating new project")
|
||||||
if self.theProject.newProject(projData):
|
if self.theProject.newProject(projData):
|
||||||
|
|
||||||
self.hasProject = True
|
self.hasProject = True
|
||||||
|
self.idleRefTime = time()
|
||||||
|
self.idleTime = 0.0
|
||||||
|
|
||||||
self.rebuildTrees()
|
self.rebuildTrees()
|
||||||
self.saveProject()
|
self.saveProject()
|
||||||
|
|
||||||
self.docEditor.setDictionaries()
|
self.docEditor.setDictionaries()
|
||||||
|
self.projView.openProjectTasks()
|
||||||
self.novelView.openProjectTasks()
|
self.novelView.openProjectTasks()
|
||||||
self.outlineView.openProjectTasks()
|
self.outlineView.openProjectTasks()
|
||||||
self.rebuildIndex(beQuiet=True)
|
self.rebuildIndex(beQuiet=True)
|
||||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
|
||||||
self.statusBar.setProjectStatus(nwState.GOOD)
|
self.mainStatus.setRefTime(self.theProject.projOpened)
|
||||||
self.statusBar.setDocumentStatus(nwState.NONE)
|
self.mainStatus.setProjectStatus(nwState.GOOD)
|
||||||
self.statusBar.setStatus(self.tr("New project created ..."))
|
self.mainStatus.setDocumentStatus(nwState.NONE)
|
||||||
|
self.mainStatus.setStatus(self.tr("New project created ..."))
|
||||||
|
|
||||||
self._updateWindowTitle(self.theProject.projName)
|
self._updateWindowTitle(self.theProject.projName)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.theProject.clearProject()
|
self.theProject.clearProject()
|
||||||
return False
|
return False
|
||||||
@@ -512,7 +524,8 @@ class GuiMain(QMainWindow):
|
|||||||
self.rebuildTrees()
|
self.rebuildTrees()
|
||||||
self.docEditor.setDictionaries()
|
self.docEditor.setDictionaries()
|
||||||
self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
|
self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
|
||||||
self.statusBar.setRefTime(self.theProject.projOpened)
|
self.mainStatus.setRefTime(self.theProject.projOpened)
|
||||||
|
self.projView.openProjectTasks()
|
||||||
self.novelView.openProjectTasks()
|
self.novelView.openProjectTasks()
|
||||||
self.outlineView.openProjectTasks()
|
self.outlineView.openProjectTasks()
|
||||||
self._updateStatusWordCount()
|
self._updateStatusWordCount()
|
||||||
@@ -547,7 +560,7 @@ class GuiMain(QMainWindow):
|
|||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.projView.saveProjectTree()
|
self.projView.saveProjectTasks()
|
||||||
if self.theProject.saveProject(autoSave=autoSave):
|
if self.theProject.saveProject(autoSave=autoSave):
|
||||||
self.theProject.index.saveIndex()
|
self.theProject.index.saveIndex()
|
||||||
|
|
||||||
@@ -613,7 +626,7 @@ class GuiMain(QMainWindow):
|
|||||||
fHandle = None # The first file handle we encounter
|
fHandle = None # The first file handle we encounter
|
||||||
foundIt = False # We've found tHandle, pick the next we see
|
foundIt = False # We've found tHandle, pick the next we see
|
||||||
for tItem in self.theProject.tree:
|
for tItem in self.theProject.tree:
|
||||||
if not self.theProject.tree.checkType(tItem.itemHandle, nwItemType.FILE):
|
if tItem is None or not tItem.isFileType():
|
||||||
continue
|
continue
|
||||||
if fHandle is None:
|
if fHandle is None:
|
||||||
fHandle = tItem.itemHandle
|
fHandle = tItem.itemHandle
|
||||||
@@ -654,21 +667,18 @@ class GuiMain(QMainWindow):
|
|||||||
logger.debug("Viewing document, but no handle provided")
|
logger.debug("Viewing document, but no handle provided")
|
||||||
|
|
||||||
if self.docEditor.hasFocus():
|
if self.docEditor.hasFocus():
|
||||||
logger.verbose("Trying editor document")
|
|
||||||
tHandle = self.docEditor.docHandle()
|
tHandle = self.docEditor.docHandle()
|
||||||
|
|
||||||
if tHandle is not None:
|
if tHandle is not None:
|
||||||
self.saveDocument()
|
self.saveDocument()
|
||||||
else:
|
else:
|
||||||
logger.verbose("Trying selected document")
|
|
||||||
tHandle = self.projView.getSelectedHandle()
|
tHandle = self.projView.getSelectedHandle()
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
logger.verbose("Trying last viewed document")
|
|
||||||
tHandle = self.theProject.lastViewed
|
tHandle = self.theProject.lastViewed
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
logger.verbose("No document to view, giving up")
|
logger.debug("No document to view, giving up")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Make sure main tab is in Editor view
|
# Make sure main tab is in Editor view
|
||||||
@@ -745,30 +755,6 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def mergeDocuments(self):
|
|
||||||
"""Merge multiple documents to one single new document.
|
|
||||||
"""
|
|
||||||
if not self.hasProject:
|
|
||||||
logger.error("No project open")
|
|
||||||
return False
|
|
||||||
|
|
||||||
dlgMerge = GuiDocMerge(self)
|
|
||||||
dlgMerge.exec_()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def splitDocument(self):
|
|
||||||
"""Split a single document into multiple documents.
|
|
||||||
"""
|
|
||||||
if not self.hasProject:
|
|
||||||
logger.error("No project open")
|
|
||||||
return False
|
|
||||||
|
|
||||||
dlgSplit = GuiDocSplit(self)
|
|
||||||
dlgSplit.exec_()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def passDocumentAction(self, theAction):
|
def passDocumentAction(self, theAction):
|
||||||
"""Pass on document action to the document viewer if it has
|
"""Pass on document action to the document viewer if it has
|
||||||
focus, or pass it to the document editor if it or any of
|
focus, or pass it to the document editor if it or any of
|
||||||
@@ -856,14 +842,14 @@ class GuiMain(QMainWindow):
|
|||||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
tStart = time()
|
tStart = time()
|
||||||
|
|
||||||
self.projView.saveProjectTree()
|
self.projView.saveProjectTasks()
|
||||||
self.theProject.index.clearIndex()
|
self.theProject.index.clearIndex()
|
||||||
|
|
||||||
for tItem in self.theProject.tree:
|
for tItem in self.theProject.tree:
|
||||||
if tItem is None: # pragma: no cover
|
if tItem is None: # pragma: no cover
|
||||||
continue # This is a bug trap
|
continue # This is a bug trap
|
||||||
|
|
||||||
logger.verbose("Indexing '%s'", tItem.itemName)
|
logger.debug("Indexing '%s'", tItem.itemName)
|
||||||
if self.theProject.index.reIndexHandle(tItem.itemHandle):
|
if self.theProject.index.reIndexHandle(tItem.itemHandle):
|
||||||
# Update Word Counts
|
# Update Word Counts
|
||||||
self.projView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True)
|
self.projView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True)
|
||||||
@@ -964,6 +950,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgDetails = getGuiItem("GuiProjectDetails")
|
dlgDetails = getGuiItem("GuiProjectDetails")
|
||||||
if dlgDetails is None:
|
if dlgDetails is None:
|
||||||
dlgDetails = GuiProjectDetails(self)
|
dlgDetails = GuiProjectDetails(self)
|
||||||
|
assert isinstance(dlgDetails, GuiProjectDetails)
|
||||||
|
|
||||||
dlgDetails.setModal(False)
|
dlgDetails.setModal(False)
|
||||||
dlgDetails.show()
|
dlgDetails.show()
|
||||||
@@ -982,6 +969,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgBuild = getGuiItem("GuiBuildNovel")
|
dlgBuild = getGuiItem("GuiBuildNovel")
|
||||||
if dlgBuild is None:
|
if dlgBuild is None:
|
||||||
dlgBuild = GuiBuildNovel(self)
|
dlgBuild = GuiBuildNovel(self)
|
||||||
|
assert isinstance(dlgBuild, GuiBuildNovel)
|
||||||
|
|
||||||
dlgBuild.setModal(False)
|
dlgBuild.setModal(False)
|
||||||
dlgBuild.show()
|
dlgBuild.show()
|
||||||
@@ -1001,6 +989,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgLipsum = getGuiItem("GuiLipsum")
|
dlgLipsum = getGuiItem("GuiLipsum")
|
||||||
if dlgLipsum is None:
|
if dlgLipsum is None:
|
||||||
dlgLipsum = GuiLipsum(self)
|
dlgLipsum = GuiLipsum(self)
|
||||||
|
assert isinstance(dlgLipsum, GuiLipsum)
|
||||||
|
|
||||||
dlgLipsum.setModal(False)
|
dlgLipsum.setModal(False)
|
||||||
dlgLipsum.show()
|
dlgLipsum.show()
|
||||||
@@ -1035,6 +1024,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgStats = getGuiItem("GuiWritingStats")
|
dlgStats = getGuiItem("GuiWritingStats")
|
||||||
if dlgStats is None:
|
if dlgStats is None:
|
||||||
dlgStats = GuiWritingStats(self)
|
dlgStats = GuiWritingStats(self)
|
||||||
|
assert isinstance(dlgStats, GuiWritingStats)
|
||||||
|
|
||||||
dlgStats.setModal(False)
|
dlgStats.setModal(False)
|
||||||
dlgStats.show()
|
dlgStats.show()
|
||||||
@@ -1050,6 +1040,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgAbout = getGuiItem("GuiAbout")
|
dlgAbout = getGuiItem("GuiAbout")
|
||||||
if dlgAbout is None:
|
if dlgAbout is None:
|
||||||
dlgAbout = GuiAbout(self)
|
dlgAbout = GuiAbout(self)
|
||||||
|
assert isinstance(dlgAbout, GuiAbout)
|
||||||
|
|
||||||
dlgAbout.setModal(True)
|
dlgAbout.setModal(True)
|
||||||
dlgAbout.show()
|
dlgAbout.show()
|
||||||
@@ -1075,6 +1066,7 @@ class GuiMain(QMainWindow):
|
|||||||
dlgUpdate = getGuiItem("GuiUpdates")
|
dlgUpdate = getGuiItem("GuiUpdates")
|
||||||
if dlgUpdate is None:
|
if dlgUpdate is None:
|
||||||
dlgUpdate = GuiUpdates(self)
|
dlgUpdate = GuiUpdates(self)
|
||||||
|
assert isinstance(dlgUpdate, GuiUpdates)
|
||||||
|
|
||||||
dlgUpdate.setModal(True)
|
dlgUpdate.setModal(True)
|
||||||
dlgUpdate.show()
|
dlgUpdate.show()
|
||||||
@@ -1225,11 +1217,9 @@ class GuiMain(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
if self.docEditor.docHandle() is None:
|
if self.docEditor.docHandle() is None:
|
||||||
logger.error("No document open, so not activating Focus Mode")
|
logger.error("No document open, so not activating Focus Mode")
|
||||||
self.mainMenu.setFocusMode(self.isFocusMode)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.isFocusMode = not self.isFocusMode
|
self.isFocusMode = not self.isFocusMode
|
||||||
self.mainMenu.setFocusMode(self.isFocusMode)
|
|
||||||
if self.isFocusMode:
|
if self.isFocusMode:
|
||||||
logger.debug("Activating Focus Mode")
|
logger.debug("Activating Focus Mode")
|
||||||
self.switchFocus(nwWidget.EDITOR)
|
self.switchFocus(nwWidget.EDITOR)
|
||||||
@@ -1238,7 +1228,7 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
isVisible = not self.isFocusMode
|
isVisible = not self.isFocusMode
|
||||||
self.treePane.setVisible(isVisible)
|
self.treePane.setVisible(isVisible)
|
||||||
self.statusBar.setVisible(isVisible)
|
self.mainStatus.setVisible(isVisible)
|
||||||
self.mainMenu.setVisible(isVisible)
|
self.mainMenu.setVisible(isVisible)
|
||||||
self.viewsBar.setVisible(isVisible)
|
self.viewsBar.setVisible(isVisible)
|
||||||
|
|
||||||
@@ -1522,12 +1512,12 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
if editIdle or userIdle:
|
if editIdle or userIdle:
|
||||||
self.idleTime += currTime - self.idleRefTime
|
self.idleTime += currTime - self.idleRefTime
|
||||||
self.statusBar.setUserIdle(True)
|
self.mainStatus.setUserIdle(True)
|
||||||
else:
|
else:
|
||||||
self.statusBar.setUserIdle(False)
|
self.mainStatus.setUserIdle(False)
|
||||||
|
|
||||||
self.idleRefTime = currTime
|
self.idleRefTime = currTime
|
||||||
self.statusBar.updateTime(idleTime=self.idleTime)
|
self.mainStatus.updateTime(idleTime=self.idleTime)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1536,9 +1526,8 @@ class GuiMain(QMainWindow):
|
|||||||
"""Update the word count on the status bar.
|
"""Update the word count on the status bar.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
self.statusBar.setProjectStats(0, 0)
|
self.mainStatus.setProjectStats(0, 0)
|
||||||
|
|
||||||
logger.verbose("Updating total word count")
|
|
||||||
self.theProject.updateWordCounts()
|
self.theProject.updateWordCounts()
|
||||||
if self.mainConf.incNotesWCount:
|
if self.mainConf.incNotesWCount:
|
||||||
currWords = self.theProject.currWCount
|
currWords = self.theProject.currWCount
|
||||||
@@ -1547,7 +1536,7 @@ class GuiMain(QMainWindow):
|
|||||||
currWords = self.theProject.currNovelWC
|
currWords = self.theProject.currNovelWC
|
||||||
diffWords = currWords - self.theProject.lastNovelWC
|
diffWords = currWords - self.theProject.lastNovelWC
|
||||||
|
|
||||||
self.statusBar.setProjectStats(currWords, diffWords)
|
self.mainStatus.setProjectStats(currWords, diffWords)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1575,13 +1564,9 @@ class GuiMain(QMainWindow):
|
|||||||
def _mainStackChanged(self, stIndex):
|
def _mainStackChanged(self, stIndex):
|
||||||
"""Activated when the main window tab is changed.
|
"""Activated when the main window tab is changed.
|
||||||
"""
|
"""
|
||||||
if stIndex == self.idxEditorView:
|
if stIndex == self.idxOutlineView:
|
||||||
logger.verbose("Editor View activated")
|
|
||||||
elif stIndex == self.idxOutlineView:
|
|
||||||
logger.verbose("Outline View activated")
|
|
||||||
if self.hasProject:
|
if self.hasProject:
|
||||||
self.outlineView.refreshTree()
|
self.outlineView.refreshTree()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
@@ -1591,11 +1576,9 @@ class GuiMain(QMainWindow):
|
|||||||
sHandle = None
|
sHandle = None
|
||||||
|
|
||||||
if stIndex == self.idxProjView:
|
if stIndex == self.idxProjView:
|
||||||
logger.verbose("Project Tree View activated")
|
|
||||||
sHandle = self.projView.getSelectedHandle()
|
sHandle = self.projView.getSelectedHandle()
|
||||||
|
|
||||||
elif stIndex == self.idxNovelView:
|
elif stIndex == self.idxNovelView:
|
||||||
logger.verbose("Novel Tree View activated")
|
|
||||||
if self.hasProject:
|
if self.hasProject:
|
||||||
self.novelView.refreshTree()
|
self.novelView.refreshTree()
|
||||||
sHandle, _ = self.novelView.getSelectedHandle()
|
sHandle, _ = self.novelView.getSelectedHandle()
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ from novelwriter.core import ToHtml, ToOdt, ToMarkdown
|
|||||||
from novelwriter.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
from novelwriter.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
||||||
from novelwriter.error import formatException, logException
|
from novelwriter.error import formatException, logException
|
||||||
from novelwriter.common import fuzzyTime, makeFileNameSafe
|
from novelwriter.common import fuzzyTime, makeFileNameSafe
|
||||||
|
from novelwriter.custom import QSwitch
|
||||||
from novelwriter.constants import nwConst, nwFiles
|
from novelwriter.constants import nwConst, nwFiles
|
||||||
from novelwriter.gui.custom import QSwitch
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
|
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiBuildNovel ...")
|
logger.debug("Initialising GuiBuildNovel ...")
|
||||||
self.setObjectName("GuiBuildNovel")
|
self.setObjectName("GuiBuildNovel")
|
||||||
@@ -351,6 +351,36 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.textForm.setColumnStretch(0, 1)
|
self.textForm.setColumnStretch(0, 1)
|
||||||
self.textForm.setColumnStretch(1, 0)
|
self.textForm.setColumnStretch(1, 0)
|
||||||
|
|
||||||
|
# Root Filter Options
|
||||||
|
# ===================
|
||||||
|
|
||||||
|
self.rootGroup = QGroupBox(self.tr("Root Filter Options"), self)
|
||||||
|
self.rootForm = QGridLayout(self)
|
||||||
|
self.rootGroup.setLayout(self.rootForm)
|
||||||
|
|
||||||
|
rootFilter = pOptions.getValue("GuiBuildNovel", "rootFilter", [])
|
||||||
|
if not isinstance(rootFilter, list):
|
||||||
|
rootFilter = []
|
||||||
|
|
||||||
|
iRow = 0
|
||||||
|
self.rootSelection = {}
|
||||||
|
for tHandle, nwItem in self.theProject.tree.iterRoots(None):
|
||||||
|
if not nwItem.isInactive():
|
||||||
|
rootLabel = QLabel(nwItem.itemName)
|
||||||
|
rootLabel.setWordWrap(True)
|
||||||
|
|
||||||
|
rootValue = QSwitch(width=wS, height=hS)
|
||||||
|
rootValue.setChecked(tHandle not in rootFilter)
|
||||||
|
|
||||||
|
self.rootSelection[tHandle] = rootValue
|
||||||
|
self.rootForm.addWidget(rootLabel, iRow, 0, 1, 1, Qt.AlignLeft)
|
||||||
|
self.rootForm.addWidget(rootValue, iRow, 1, 1, 1, Qt.AlignRight)
|
||||||
|
|
||||||
|
iRow += 1
|
||||||
|
|
||||||
|
self.rootForm.setColumnStretch(0, 1)
|
||||||
|
self.rootForm.setColumnStretch(1, 0)
|
||||||
|
|
||||||
# File Filter Options
|
# File Filter Options
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
@@ -375,13 +405,13 @@ class GuiBuildNovel(QDialog):
|
|||||||
|
|
||||||
novelLabel = QLabel(self.tr("Include novel files"))
|
novelLabel = QLabel(self.tr("Include novel files"))
|
||||||
notesLabel = QLabel(self.tr("Include note files"))
|
notesLabel = QLabel(self.tr("Include note files"))
|
||||||
exportLabel = QLabel(self.tr("Ignore export flag"))
|
activeLabel = QLabel(self.tr("Include inactive files"))
|
||||||
|
|
||||||
self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft)
|
self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft)
|
||||||
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
|
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
|
||||||
self.fileForm.addWidget(notesLabel, 1, 0, 1, 1, Qt.AlignLeft)
|
self.fileForm.addWidget(notesLabel, 1, 0, 1, 1, Qt.AlignLeft)
|
||||||
self.fileForm.addWidget(self.noteFiles, 1, 1, 1, 1, Qt.AlignRight)
|
self.fileForm.addWidget(self.noteFiles, 1, 1, 1, 1, Qt.AlignRight)
|
||||||
self.fileForm.addWidget(exportLabel, 2, 0, 1, 1, Qt.AlignLeft)
|
self.fileForm.addWidget(activeLabel, 2, 0, 1, 1, Qt.AlignLeft)
|
||||||
self.fileForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)
|
self.fileForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)
|
||||||
|
|
||||||
self.fileForm.setColumnStretch(0, 1)
|
self.fileForm.setColumnStretch(0, 1)
|
||||||
@@ -503,6 +533,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.toolsBox.addWidget(self.fontGroup)
|
self.toolsBox.addWidget(self.fontGroup)
|
||||||
self.toolsBox.addWidget(self.styleGroup)
|
self.toolsBox.addWidget(self.styleGroup)
|
||||||
self.toolsBox.addWidget(self.textGroup)
|
self.toolsBox.addWidget(self.textGroup)
|
||||||
|
self.toolsBox.addWidget(self.rootGroup)
|
||||||
self.toolsBox.addWidget(self.fileGroup)
|
self.toolsBox.addWidget(self.fileGroup)
|
||||||
self.toolsBox.addWidget(self.exportGroup)
|
self.toolsBox.addWidget(self.exportGroup)
|
||||||
self.toolsBox.addStretch(1)
|
self.toolsBox.addStretch(1)
|
||||||
@@ -716,6 +747,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.buildProgress.setMaximum(len(self.theProject.tree))
|
self.buildProgress.setMaximum(len(self.theProject.tree))
|
||||||
self.buildProgress.setValue(0)
|
self.buildProgress.setValue(0)
|
||||||
|
|
||||||
|
rootFilter = set(self._generateRootFilter())
|
||||||
for nItt, tItem in enumerate(self.theProject.tree):
|
for nItt, tItem in enumerate(self.theProject.tree):
|
||||||
|
|
||||||
noteRoot = noteFiles
|
noteRoot = noteFiles
|
||||||
@@ -730,7 +762,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
if doConvert:
|
if doConvert:
|
||||||
bldObj.doConvert()
|
bldObj.doConvert()
|
||||||
|
|
||||||
elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag):
|
elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag, rootFilter):
|
||||||
bldObj.setText(tItem.itemHandle)
|
bldObj.setText(tItem.itemHandle)
|
||||||
bldObj.doPreProcessing()
|
bldObj.doPreProcessing()
|
||||||
bldObj.tokenizeText()
|
bldObj.tokenizeText()
|
||||||
@@ -766,7 +798,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _checkInclude(self, theItem, noteFiles, novelFiles, ignoreFlag):
|
def _checkInclude(self, theItem, noteFiles, novelFiles, ignoreFlag, rootFilter):
|
||||||
"""This function checks whether a file should be included in the
|
"""This function checks whether a file should be included in the
|
||||||
export or not. For standard note and novel files, this is
|
export or not. For standard note and novel files, this is
|
||||||
controlled by the options selected by the user. For other files
|
controlled by the options selected by the user. For other files
|
||||||
@@ -781,7 +813,10 @@ class GuiBuildNovel(QDialog):
|
|||||||
if theItem is None:
|
if theItem is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not (theItem.isExported or ignoreFlag):
|
if not (theItem.isActive or ignoreFlag):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if theItem.itemRoot in rootFilter:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
isNone = not theItem.isFileType()
|
isNone = not theItem.isFileType()
|
||||||
@@ -1045,6 +1080,11 @@ class GuiBuildNovel(QDialog):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _generateRootFilter(self):
|
||||||
|
"""Return a list of all root folders that are filtered out.
|
||||||
|
"""
|
||||||
|
return [h for h, s in self.rootSelection.items() if not s.isChecked()]
|
||||||
|
|
||||||
def _loadCache(self):
|
def _loadCache(self):
|
||||||
"""Save the current data to cache.
|
"""Save the current data to cache.
|
||||||
"""
|
"""
|
||||||
@@ -1146,6 +1186,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
incBodyText = self.includeBody.isChecked()
|
incBodyText = self.includeBody.isChecked()
|
||||||
replaceTabs = self.replaceTabs.isChecked()
|
replaceTabs = self.replaceTabs.isChecked()
|
||||||
replaceUCode = self.replaceUCode.isChecked()
|
replaceUCode = self.replaceUCode.isChecked()
|
||||||
|
rootFilter = self._generateRootFilter()
|
||||||
|
|
||||||
mainSplit = self.mainSplit.sizes()
|
mainSplit = self.mainSplit.sizes()
|
||||||
boxWidth = self.mainConf.rpxInt(mainSplit[0])
|
boxWidth = self.mainConf.rpxInt(mainSplit[0])
|
||||||
@@ -1175,6 +1216,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
pOptions.setValue("GuiBuildNovel", "incBodyText", incBodyText)
|
pOptions.setValue("GuiBuildNovel", "incBodyText", incBodyText)
|
||||||
pOptions.setValue("GuiBuildNovel", "replaceTabs", replaceTabs)
|
pOptions.setValue("GuiBuildNovel", "replaceTabs", replaceTabs)
|
||||||
pOptions.setValue("GuiBuildNovel", "replaceUCode", replaceUCode)
|
pOptions.setValue("GuiBuildNovel", "replaceUCode", replaceUCode)
|
||||||
|
pOptions.setValue("GuiBuildNovel", "rootFilter", rootFilter)
|
||||||
pOptions.saveSettings()
|
pOptions.saveSettings()
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -1194,7 +1236,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
class GuiBuildNovelDocView(QTextBrowser):
|
class GuiBuildNovelDocView(QTextBrowser):
|
||||||
|
|
||||||
def __init__(self, mainGui, theProject):
|
def __init__(self, mainGui, theProject):
|
||||||
QTextBrowser.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiBuildNovelDocView ...")
|
logger.debug("Initialising GuiBuildNovelDocView ...")
|
||||||
|
|
||||||
@@ -1223,10 +1265,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
self.setFont(theFont)
|
self.setFont(theFont)
|
||||||
|
|
||||||
# Set the tab stops
|
# Set the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else:
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
docPalette = self.palette()
|
docPalette = self.palette()
|
||||||
docPalette.setColor(QPalette.Base, QColor(255, 255, 255))
|
docPalette.setColor(QPalette.Base, QColor(255, 255, 255))
|
||||||
@@ -1343,7 +1382,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
def resizeEvent(self, theEvent):
|
def resizeEvent(self, theEvent):
|
||||||
"""Make sure the document title is the same width as the window.
|
"""Make sure the document title is the same width as the window.
|
||||||
"""
|
"""
|
||||||
QTextBrowser.resizeEvent(self, theEvent)
|
super().resizeEvent(theEvent)
|
||||||
self._updateDocMargins()
|
self._updateDocMargins()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ from PyQt5.QtWidgets import (
|
|||||||
QSpinBox
|
QSpinBox
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.gui.custom import QSwitch
|
|
||||||
from novelwriter.common import readTextFile
|
from novelwriter.common import readTextFile
|
||||||
|
from novelwriter.custom import QSwitch
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiLipsum(QDialog):
|
class GuiLipsum(QDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiLipsum ...")
|
logger.debug("Initialising GuiLipsum ...")
|
||||||
self.setObjectName("GuiLipsum")
|
self.setObjectName("GuiLipsum")
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.common import makeFileNameSafe
|
from novelwriter.common import makeFileNameSafe
|
||||||
from novelwriter.gui.custom import QSwitch
|
from novelwriter.custom import QSwitch
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ PAGE_FINAL = 4
|
|||||||
class GuiProjectWizard(QWizard):
|
class GuiProjectWizard(QWizard):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QWizard.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectWizard ...")
|
logger.debug("Initialising GuiProjectWizard ...")
|
||||||
self.setObjectName("GuiProjectWizard")
|
self.setObjectName("GuiProjectWizard")
|
||||||
@@ -88,7 +88,7 @@ class GuiProjectWizard(QWizard):
|
|||||||
class ProjWizardIntroPage(QWizardPage):
|
class ProjWizardIntroPage(QWizardPage):
|
||||||
|
|
||||||
def __init__(self, theWizard):
|
def __init__(self, theWizard):
|
||||||
QWizardPage.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWizard = theWizard
|
self.theWizard = theWizard
|
||||||
@@ -158,7 +158,7 @@ class ProjWizardIntroPage(QWizardPage):
|
|||||||
class ProjWizardFolderPage(QWizardPage):
|
class ProjWizardFolderPage(QWizardPage):
|
||||||
|
|
||||||
def __init__(self, theWizard):
|
def __init__(self, theWizard):
|
||||||
QWizardPage.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWizard = theWizard
|
self.theWizard = theWizard
|
||||||
@@ -209,12 +209,12 @@ class ProjWizardFolderPage(QWizardPage):
|
|||||||
"""Check that the selected path isn't already being used.
|
"""Check that the selected path isn't already being used.
|
||||||
"""
|
"""
|
||||||
self.errLabel.setText("")
|
self.errLabel.setText("")
|
||||||
if not QWizardPage.isComplete(self):
|
if not super().isComplete():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
setPath = os.path.abspath(os.path.expanduser(self.projPath.text()))
|
setPath = os.path.abspath(os.path.expanduser(self.projPath.text()))
|
||||||
parPath = os.path.dirname(setPath)
|
parPath = os.path.dirname(setPath)
|
||||||
logger.verbose("Path is: %s", setPath)
|
logger.debug("Path is: %s", setPath)
|
||||||
if parPath and not os.path.isdir(parPath):
|
if parPath and not os.path.isdir(parPath):
|
||||||
self.errLabel.setText(self.tr(
|
self.errLabel.setText(self.tr(
|
||||||
"Error: A project folder cannot be created using this path."
|
"Error: A project folder cannot be created using this path."
|
||||||
@@ -259,7 +259,7 @@ class ProjWizardFolderPage(QWizardPage):
|
|||||||
class ProjWizardPopulatePage(QWizardPage):
|
class ProjWizardPopulatePage(QWizardPage):
|
||||||
|
|
||||||
def __init__(self, theWizard):
|
def __init__(self, theWizard):
|
||||||
QWizardPage.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWizard = theWizard
|
self.theWizard = theWizard
|
||||||
@@ -315,7 +315,7 @@ class ProjWizardPopulatePage(QWizardPage):
|
|||||||
class ProjWizardCustomPage(QWizardPage):
|
class ProjWizardCustomPage(QWizardPage):
|
||||||
|
|
||||||
def __init__(self, theWizard):
|
def __init__(self, theWizard):
|
||||||
QWizardPage.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWizard = theWizard
|
self.theWizard = theWizard
|
||||||
@@ -334,13 +334,18 @@ class ProjWizardCustomPage(QWizardPage):
|
|||||||
|
|
||||||
# Root Folders
|
# Root Folders
|
||||||
self.addPlot = QSwitch()
|
self.addPlot = QSwitch()
|
||||||
self.addChar = QSwitch()
|
|
||||||
self.addWorld = QSwitch()
|
|
||||||
self.addNotes = QSwitch()
|
|
||||||
|
|
||||||
self.addPlot.setChecked(True)
|
self.addPlot.setChecked(True)
|
||||||
|
self.addPlot.clicked.connect(self._syncSwitches)
|
||||||
|
|
||||||
|
self.addChar = QSwitch()
|
||||||
self.addChar.setChecked(True)
|
self.addChar.setChecked(True)
|
||||||
|
self.addChar.clicked.connect(self._syncSwitches)
|
||||||
|
|
||||||
|
self.addWorld = QSwitch()
|
||||||
self.addWorld.setChecked(False)
|
self.addWorld.setChecked(False)
|
||||||
|
self.addWorld.clicked.connect(self._syncSwitches)
|
||||||
|
|
||||||
|
self.addNotes = QSwitch()
|
||||||
self.addNotes.setChecked(False)
|
self.addNotes.setChecked(False)
|
||||||
|
|
||||||
# Generate Content
|
# Generate Content
|
||||||
@@ -391,13 +396,27 @@ class ProjWizardCustomPage(QWizardPage):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _syncSwitches(self):
|
||||||
|
"""Check if the add notes option should also be switched off.
|
||||||
|
"""
|
||||||
|
addPlot = self.addPlot.isChecked()
|
||||||
|
addChar = self.addChar.isChecked()
|
||||||
|
addWorld = self.addWorld.isChecked()
|
||||||
|
if not (addPlot or addChar or addWorld):
|
||||||
|
self.addNotes.setChecked(False)
|
||||||
|
return
|
||||||
|
|
||||||
# END Class ProjWizardCustomPage
|
# END Class ProjWizardCustomPage
|
||||||
|
|
||||||
|
|
||||||
class ProjWizardFinalPage(QWizardPage):
|
class ProjWizardFinalPage(QWizardPage):
|
||||||
|
|
||||||
def __init__(self, theWizard):
|
def __init__(self, theWizard):
|
||||||
QWizardPage.__init__(self)
|
super().__init__()
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWizard = theWizard
|
self.theWizard = theWizard
|
||||||
@@ -418,7 +437,7 @@ class ProjWizardFinalPage(QWizardPage):
|
|||||||
def initializePage(self):
|
def initializePage(self):
|
||||||
"""Update the summary information on the final page.
|
"""Update the summary information on the final page.
|
||||||
"""
|
"""
|
||||||
QWizardPage.initializePage(self)
|
super().initializePage()
|
||||||
|
|
||||||
sumList = []
|
sumList = []
|
||||||
sumList.append(self.tr("Project Name: {0}").format(self.field("projName")))
|
sumList.append(self.tr("Project Name: {0}").format(self.field("projName")))
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter.enum import nwAlert
|
from novelwriter.enum import nwAlert
|
||||||
from novelwriter.error import formatException
|
from novelwriter.error import formatException
|
||||||
from novelwriter.common import formatTime, checkInt, checkIntRange, checkIntTuple
|
from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
|
||||||
|
from novelwriter.custom import QSwitch
|
||||||
from novelwriter.constants import nwConst, nwFiles
|
from novelwriter.constants import nwConst, nwFiles
|
||||||
from novelwriter.gui.custom import QSwitch
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class GuiWritingStats(QDialog):
|
|||||||
FMT_CSV = 1
|
FMT_CSV = 1
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
QDialog.__init__(self, mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiWritingStats ...")
|
logger.debug("Initialising GuiWritingStats ...")
|
||||||
self.setObjectName("GuiWritingStats")
|
self.setObjectName("GuiWritingStats")
|
||||||
@@ -112,11 +112,12 @@ class GuiWritingStats(QDialog):
|
|||||||
self.listBox.setColumnWidth(self.C_COUNT, wCol3)
|
self.listBox.setColumnWidth(self.C_COUNT, wCol3)
|
||||||
|
|
||||||
hHeader = self.listBox.headerItem()
|
hHeader = self.listBox.headerItem()
|
||||||
hHeader.setTextAlignment(self.C_LENGTH, Qt.AlignRight)
|
if hHeader is not None:
|
||||||
hHeader.setTextAlignment(self.C_IDLE, Qt.AlignRight)
|
hHeader.setTextAlignment(self.C_LENGTH, Qt.AlignRight)
|
||||||
hHeader.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
hHeader.setTextAlignment(self.C_IDLE, Qt.AlignRight)
|
||||||
|
hHeader.setTextAlignment(self.C_COUNT, Qt.AlignRight)
|
||||||
|
|
||||||
sortCol = checkIntRange(pOptions.getInt("GuiWritingStats", "sortCol", 0), 0, 2, 0)
|
sortCol = minmax(pOptions.getInt("GuiWritingStats", "sortCol", 0), 0, 2)
|
||||||
sortOrder = checkIntTuple(
|
sortOrder = checkIntTuple(
|
||||||
pOptions.getInt("GuiWritingStats", "sortOrder", Qt.DescendingOrder),
|
pOptions.getInt("GuiWritingStats", "sortOrder", Qt.DescendingOrder),
|
||||||
(Qt.AscendingOrder, Qt.DescendingOrder), Qt.DescendingOrder
|
(Qt.AscendingOrder, Qt.DescendingOrder), Qt.DescendingOrder
|
||||||
@@ -449,7 +450,7 @@ class GuiWritingStats(QDialog):
|
|||||||
if inLine.startswith("#"):
|
if inLine.startswith("#"):
|
||||||
if inLine.startswith("# Offset"):
|
if inLine.startswith("# Offset"):
|
||||||
self.wordOffset = checkInt(inLine[9:].strip(), 0)
|
self.wordOffset = checkInt(inLine[9:].strip(), 0)
|
||||||
logger.verbose(
|
logger.debug(
|
||||||
"Initial word count when log was started is %d" % self.wordOffset
|
"Initial word count when log was started is %d" % self.wordOffset
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-07-31 18:38:08">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:17:33">
|
||||||
<project>
|
<project>
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
<saveCount>1378</saveCount>
|
<saveCount>1385</saveCount>
|
||||||
<autoCount>236</autoCount>
|
<autoCount>236</autoCount>
|
||||||
<editTime>69273</editTime>
|
<editTime>69348</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>False</doBackup>
|
<doBackup>False</doBackup>
|
||||||
@@ -55,56 +55,56 @@
|
|||||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
<meta expanded="False" mainHeading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||||
<name status="sc24b8f" import="ia857f0" exported="True">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
<meta expanded="False" mainHeading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||||
<name status="sf12341" import="ia857f0" exported="True">Page</name>
|
<name status="sf12341" import="ia857f0" active="True">Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
<meta expanded="False" mainHeading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">Part One</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Part One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
<meta expanded="True" mainHeading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||||
<name status="sf24ce6" import="ia857f0" exported="True">Chapter One</name>
|
<name status="sf24ce6" import="ia857f0" active="True">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="2687" wordCount="479" paraCount="14" cursorPos="2183"/>
|
<meta expanded="False" mainHeading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">Making a Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Making a Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
<meta expanded="False" mainHeading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">Another Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Another Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
<meta expanded="False" mainHeading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||||
<name status="s78ea90" import="ia857f0" exported="True">Interlude</name>
|
<name status="s78ea90" import="ia857f0" active="True">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
<meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||||
<name status="sf24ce6" import="ia857f0" exported="False">A Note on Structure</name>
|
<name status="sf24ce6" import="ia857f0" active="False">A Note on Structure</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
<meta expanded="True" mainHeading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">Chapter Two</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">We Found John!</name>
|
<name status="s90e6c9" import="ia857f0" active="True">We Found John!</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="sf12341" import="ia857f0">Sequel</name>
|
<name status="sf12341" import="ia857f0">Sequel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
<meta expanded="False" mainHeading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||||
<name status="sc24b8f" import="ia857f0" exported="True">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
<meta expanded="False" mainHeading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">Chapter One</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
@@ -115,28 +115,28 @@
|
|||||||
<name status="sf12341" import="ia857f0">Main Characters</name>
|
<name status="sf12341" import="ia857f0">Main Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
<meta expanded="False" mainHeading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
<name status="sf12341" import="icfb3a5" exported="True">John Smith</name>
|
<name status="sf12341" import="icfb3a5" active="True">John Smith</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
<meta expanded="False" mainHeading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
<name status="sf12341" import="i2d7a54" exported="True">Jane Smith</name>
|
<name status="sf12341" import="i2d7a54" active="True">Jane Smith</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="sf12341" import="ia857f0">Locations</name>
|
<name status="sf12341" import="ia857f0">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
<meta expanded="False" mainHeading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
<name status="sf12341" import="i56be10" exported="True">Earth</name>
|
<name status="sf12341" import="i56be10" active="True">Earth</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
<meta expanded="False" mainHeading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
<name status="sf12341" import="icfb3a5" exported="True">Space</name>
|
<name status="sf12341" import="icfb3a5" active="True">Space</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
<meta expanded="False" mainHeading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
<name status="sf12341" import="i2d7a54" exported="True">Mars</name>
|
<name status="sf12341" import="i2d7a54" active="True">Mars</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
@@ -147,16 +147,16 @@
|
|||||||
<name status="sf12341" import="ia857f0">Scenes</name>
|
<name status="sf12341" import="ia857f0">Scenes</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="6827118336ac1" order="0" type="FILE" class="ARCHIVE" layout="DOCUMENT">
|
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="6827118336ac1" order="0" type="FILE" class="ARCHIVE" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
<meta expanded="False" mainHeading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||||
<name status="s90e6c9" import="ia857f0" exported="True">Old File</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Old File</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="sf12341" import="ia857f0">Trash</name>
|
<name status="sf12341" import="ia857f0">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="98acd8c76c93a" order="0" type="FILE" class="TRASH" layout="DOCUMENT">
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="98acd8c76c93a" order="0" type="FILE" class="TRASH" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
<meta expanded="False" mainHeading="H3" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
<name status="sf12341" import="ia857f0" exported="True">Delete Me!</name>
|
<name status="sf12341" import="ia857f0" active="True">Delete Me!</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
|
|||||||
@@ -190,14 +190,20 @@ def mockRnd(monkeypatch):
|
|||||||
from 0. This one will generate status/importance flags and handles
|
from 0. This one will generate status/importance flags and handles
|
||||||
in a predictable sequence.
|
in a predictable sequence.
|
||||||
"""
|
"""
|
||||||
def rnd(n):
|
class MockRnd:
|
||||||
for x in range(n):
|
|
||||||
yield x
|
|
||||||
|
|
||||||
gen = rnd(1000)
|
def __init__(self):
|
||||||
monkeypatch.setattr("random.getrandbits", lambda *a: next(gen))
|
self.reset()
|
||||||
|
|
||||||
return
|
def _rnd(self, n):
|
||||||
|
for x in range(n):
|
||||||
|
yield x
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
gen = self._rnd(1000)
|
||||||
|
monkeypatch.setattr("random.getrandbits", lambda *a: next(gen))
|
||||||
|
|
||||||
|
return MockRnd()
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:13">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-17 21:17:31">
|
||||||
<project>
|
<project>
|
||||||
<name>Lorem Ipsum</name>
|
<name>Lorem Ipsum</name>
|
||||||
<title>Lorem Ipsum</title>
|
<title>Lorem Ipsum</title>
|
||||||
<author>lipsum.com</author>
|
<author>lipsum.com</author>
|
||||||
<saveCount>26</saveCount>
|
<saveCount>28</saveCount>
|
||||||
<autoCount>24</autoCount>
|
<autoCount>24</autoCount>
|
||||||
<editTime>1863</editTime>
|
<editTime>1874</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>False</doBackup>
|
<doBackup>False</doBackup>
|
||||||
@@ -50,84 +50,84 @@
|
|||||||
<name status="sbaa94f" import="i613591">Novel</name>
|
<name status="sbaa94f" import="i613591">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="7a992350f3eb6" parent="b3643d0f92e32" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="7a992350f3eb6" parent="b3643d0f92e32" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="230" wordCount="40" paraCount="3" cursorPos="148"/>
|
<meta expanded="False" mainHeading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="148"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Lorem Ipsum</name>
|
<name status="sedd043" import="i613591" active="True">Lorem Ipsum</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="1058" wordCount="176" paraCount="2" cursorPos="43"/>
|
<meta expanded="False" mainHeading="H0" charCount="1058" wordCount="176" paraCount="2" cursorPos="43"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Front Matter</name>
|
<name status="sedd043" import="i613591" active="True">Front Matter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88d59a277361b" parent="b3643d0f92e32" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88d59a277361b" parent="b3643d0f92e32" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="584" wordCount="92" paraCount="1" cursorPos="4"/>
|
<meta expanded="False" mainHeading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="4"/>
|
||||||
<name status="s92a87b" import="i613591" exported="True">Prologue</name>
|
<name status="s92a87b" import="i613591" active="True">Prologue</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="db7e733775d4d" parent="b3643d0f92e32" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="db7e733775d4d" parent="b3643d0f92e32" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="35" wordCount="6" paraCount="1" cursorPos="42"/>
|
<meta expanded="False" mainHeading="H1" charCount="35" wordCount="6" paraCount="1" cursorPos="42"/>
|
||||||
<name status="sbaa94f" import="i613591" exported="True">Act One</name>
|
<name status="sbaa94f" import="i613591" active="True">Act One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="45e6b01ca35c1" parent="b3643d0f92e32" root="b3643d0f92e32" order="4" type="FOLDER" class="NOVEL">
|
<item handle="45e6b01ca35c1" parent="b3643d0f92e32" root="b3643d0f92e32" order="4" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="s92a87b" import="i613591">Chapter One</name>
|
<name status="s92a87b" import="i613591">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="fb609cd8319dc" parent="45e6b01ca35c1" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="fb609cd8319dc" parent="45e6b01ca35c1" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="419" wordCount="67" paraCount="1" cursorPos="56"/>
|
<meta expanded="False" mainHeading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="56"/>
|
||||||
<name status="s92a87b" import="i613591" exported="True">Chapter One</name>
|
<name status="s92a87b" import="i613591" active="True">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88243afbe5ed8" parent="45e6b01ca35c1" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88243afbe5ed8" parent="45e6b01ca35c1" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528"/>
|
<meta expanded="False" mainHeading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Scene One</name>
|
<name status="sedd043" import="i613591" active="True">Scene One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f96ec11c6a3da" parent="45e6b01ca35c1" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="f96ec11c6a3da" parent="45e6b01ca35c1" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="4043" wordCount="600" paraCount="6" cursorPos="2335"/>
|
<meta expanded="False" mainHeading="H3" charCount="4043" wordCount="600" paraCount="6" cursorPos="2335"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Scene Two</name>
|
<name status="sedd043" import="i613591" active="True">Scene Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="846352075de7d" parent="b3643d0f92e32" root="b3643d0f92e32" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="846352075de7d" parent="b3643d0f92e32" root="b3643d0f92e32" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="631" wordCount="109" paraCount="3" cursorPos="376"/>
|
<meta expanded="False" mainHeading="H2" charCount="631" wordCount="109" paraCount="3" cursorPos="376"/>
|
||||||
<name status="sbaa94f" import="i613591" exported="False">Interlude</name>
|
<name status="sbaa94f" import="i613591" active="False">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6bd935d2490cd" parent="b3643d0f92e32" root="b3643d0f92e32" order="6" type="FOLDER" class="NOVEL">
|
<item handle="6bd935d2490cd" parent="b3643d0f92e32" root="b3643d0f92e32" order="6" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="s92a87b" import="i613591">Chapter Two</name>
|
<name status="s92a87b" import="i613591">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="441420a886d82" parent="6bd935d2490cd" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="441420a886d82" parent="6bd935d2490cd" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="477" wordCount="70" paraCount="1" cursorPos="56"/>
|
<meta expanded="False" mainHeading="H2" charCount="477" wordCount="70" paraCount="1" cursorPos="56"/>
|
||||||
<name status="s92a87b" import="i613591" exported="True">Chapter Two</name>
|
<name status="s92a87b" import="i613591" active="True">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="eb103bc70c90c" parent="6bd935d2490cd" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="eb103bc70c90c" parent="6bd935d2490cd" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="3006" wordCount="439" paraCount="4" cursorPos="57"/>
|
<meta expanded="False" mainHeading="H3" charCount="3006" wordCount="439" paraCount="4" cursorPos="57"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Scene Three</name>
|
<name status="sedd043" import="i613591" active="True">Scene Three</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f8c0562e50f1b" parent="6bd935d2490cd" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="f8c0562e50f1b" parent="6bd935d2490cd" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="3839" wordCount="563" paraCount="6" cursorPos="56"/>
|
<meta expanded="False" mainHeading="H3" charCount="3839" wordCount="563" paraCount="6" cursorPos="56"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Scene Four</name>
|
<name status="sedd043" import="i613591" active="True">Scene Four</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="47666c91c7ccf" parent="6bd935d2490cd" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="47666c91c7ccf" parent="6bd935d2490cd" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="3644" wordCount="543" paraCount="5" cursorPos="351"/>
|
<meta expanded="False" mainHeading="H3" charCount="3644" wordCount="543" paraCount="5" cursorPos="351"/>
|
||||||
<name status="sedd043" import="i613591" exported="True">Scene Five</name>
|
<name status="sedd043" import="i613591" active="True">Scene Five</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="67a8707f2f249" parent="None" root="67a8707f2f249" order="1" type="ROOT" class="CHARACTER">
|
<item handle="67a8707f2f249" parent="None" root="67a8707f2f249" order="1" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="sbaa94f" import="i613591">Characters</name>
|
<name status="sbaa94f" import="i613591">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="4c4f28287af27" parent="67a8707f2f249" root="67a8707f2f249" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="4c4f28287af27" parent="67a8707f2f249" root="67a8707f2f249" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883"/>
|
<meta expanded="False" mainHeading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883"/>
|
||||||
<name status="sbaa94f" import="i613591" exported="True">Mr. Nobody</name>
|
<name status="sbaa94f" import="i613591" active="True">Mr. Nobody</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="sbaa94f" import="i613591">Plot</name>
|
<name status="sbaa94f" import="i613591">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="2426c6f0ca922" parent="6c6afb1247750" root="6c6afb1247750" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="2426c6f0ca922" parent="6c6afb1247750" root="6c6afb1247750" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" charCount="1369" wordCount="195" paraCount="2" cursorPos="1387"/>
|
<meta expanded="False" mainHeading="H1" charCount="1369" wordCount="195" paraCount="2" cursorPos="1387"/>
|
||||||
<name status="sbaa94f" import="i613591" exported="True">Main</name>
|
<name status="sbaa94f" import="i613591" active="True">Main</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="60bdf227455cc" parent="None" root="60bdf227455cc" order="3" type="ROOT" class="WORLD">
|
<item handle="60bdf227455cc" parent="None" root="60bdf227455cc" order="3" type="ROOT" class="WORLD">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="sbaa94f" import="i613591">World</name>
|
<name status="sbaa94f" import="i613591">World</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="04468803b92e1" parent="60bdf227455cc" root="60bdf227455cc" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="04468803b92e1" parent="60bdf227455cc" root="60bdf227455cc" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792"/>
|
<meta expanded="False" mainHeading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792"/>
|
||||||
<name status="sbaa94f" import="i613591" exported="True">Ancient Europe</name>
|
<name status="sbaa94f" import="i613591" active="True">Ancient Europe</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:21">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-17 20:32:06">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Minimal</name>
|
<name>Test Minimal</name>
|
||||||
<title>Minimal</title>
|
<title>Minimal</title>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
<author>John Doh</author>
|
<author>John Doh</author>
|
||||||
<saveCount>17</saveCount>
|
<saveCount>19</saveCount>
|
||||||
<autoCount>2</autoCount>
|
<autoCount>2</autoCount>
|
||||||
<editTime>150</editTime>
|
<editTime>167</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>True</doBackup>
|
<doBackup>True</doBackup>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
<lastEdited>None</lastEdited>
|
||||||
<lastViewed>None</lastViewed>
|
<lastViewed>None</lastViewed>
|
||||||
<lastNovel>None</lastNovel>
|
<lastNovel>a508bb932959c</lastNovel>
|
||||||
<lastOutline>None</lastOutline>
|
<lastOutline>None</lastOutline>
|
||||||
<lastWordCount>10</lastWordCount>
|
<lastWordCount>10</lastWordCount>
|
||||||
<novelWordCount>10</novelWordCount>
|
<novelWordCount>10</novelWordCount>
|
||||||
@@ -48,20 +48,20 @@
|
|||||||
<name status="s72322d" import="iffcacb">Novel</name>
|
<name status="s72322d" import="iffcacb">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="a35baf2e93843" parent="a508bb932959c" root="a508bb932959c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="a35baf2e93843" parent="a508bb932959c" root="a508bb932959c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="28" wordCount="6" paraCount="1" cursorPos="33"/>
|
<meta expanded="False" mainHeading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="33"/>
|
||||||
<name status="s72322d" import="iffcacb" exported="True">Title Page</name>
|
<name status="s72322d" import="iffcacb" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="a6d311a93600a" parent="a508bb932959c" root="a508bb932959c" order="1" type="FOLDER" class="NOVEL">
|
<item handle="a6d311a93600a" parent="a508bb932959c" root="a508bb932959c" order="1" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="s72322d" import="iffcacb">New Chapter</name>
|
<name status="s72322d" import="iffcacb">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f5ab3e30151e1" parent="a6d311a93600a" root="a508bb932959c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="f5ab3e30151e1" parent="a6d311a93600a" root="a508bb932959c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="11" wordCount="2" paraCount="0" cursorPos="16"/>
|
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="16"/>
|
||||||
<name status="s72322d" import="iffcacb" exported="True">New Chapter</name>
|
<name status="s72322d" import="iffcacb" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="8c659a11cd429" parent="a6d311a93600a" root="a508bb932959c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="8c659a11cd429" parent="a6d311a93600a" root="a508bb932959c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="9" wordCount="2" paraCount="0" cursorPos="15"/>
|
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="15"/>
|
||||||
<name status="s72322d" import="iffcacb" exported="True">New Scene</name>
|
<name status="s72322d" import="iffcacb" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="7695ce551d265" parent="None" root="7695ce551d265" order="1" type="ROOT" class="PLOT">
|
<item handle="7695ce551d265" parent="None" root="7695ce551d265" order="1" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
# Mock GUI
|
# Mock GUI
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
|
|
||||||
class MockGuiMain():
|
class MockGuiMain:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mainConf = None
|
self.mainConf = None
|
||||||
self.hasProject = True
|
self.hasProject = True
|
||||||
self.theProject = None
|
self.theProject = None
|
||||||
self.statusBar = MockStatusBar()
|
self.mainStatus = MockStatusBar()
|
||||||
|
|
||||||
# Test Variables
|
# Test Variables
|
||||||
self.askResponse = True
|
self.askResponse = True
|
||||||
@@ -81,7 +81,7 @@ class MockGuiMain():
|
|||||||
# END Class MockGuiMain
|
# END Class MockGuiMain
|
||||||
|
|
||||||
|
|
||||||
class MockStatusBar():
|
class MockStatusBar:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
%%~name: Chapter 1
|
||||||
|
%%~path: 0000000000008/0000000000010
|
||||||
|
%%~kind: NOVEL/DOCUMENT
|
||||||
|
## Chapter 1
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum.
|
||||||
|
|
||||||
|
Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.
|
||||||
|
|
||||||
|
% Merge Novel Scene: Scene 1.1 [New]
|
||||||
|
|
||||||
|
### Scene 1.1
|
||||||
|
|
||||||
|
Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.
|
||||||
|
|
||||||
|
Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl.
|
||||||
|
|
||||||
|
% Merge Novel Scene: Scene 1.2 [New]
|
||||||
|
|
||||||
|
### Scene 1.2
|
||||||
|
|
||||||
|
Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl.
|
||||||
|
|
||||||
|
Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis.
|
||||||
|
|
||||||
|
% Merge Novel Scene: Scene 1.3 [New]
|
||||||
|
|
||||||
|
### Scene 1.3
|
||||||
|
|
||||||
|
Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis.
|
||||||
|
|
||||||
|
Integer ac gravida quam. Quisque eleifend nisl nec pretium tincidunt. Quisque sollicitudin nisi in hendrerit scelerisque. Sed ornare nisl lacus, sit amet consectetur lectus egestas et. Vivamus nec arcu lorem. Donec rhoncus, purus a porta accumsan, nunc lectus iaculis libero, et fringilla tellus augue et velit. Integer varius felis scelerisque, vulputate tellus eu, laoreet justo. Suspendisse sit amet sem vehicula, auctor odio sed, aliquet enim. In ac tortor sed tortor fringilla elementum. Nulla non odio at magna vulputate scelerisque. Nam elementum diam eu rutrum scelerisque. Sed fermentum, felis quis vulputate fermentum, libero metus sollicitudin est, in faucibus purus nulla non dolor. Ut vitae felis porta, feugiat nunc et, bibendum neque. Nullam nec lorem nec metus ullamcorper malesuada ut a nisl. Etiam eget tristique dui. Nulla sed mi finibus, venenatis tellus non, maximus enim.
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
%%~name: All of Chapter 1
|
||||||
|
%%~path: 0000000000008/0000000000014
|
||||||
|
%%~kind: NOVEL/DOCUMENT
|
||||||
|
% Merge Novel Chapter: Chapter 1 [New]
|
||||||
|
|
||||||
|
## Chapter 1
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum.
|
||||||
|
|
||||||
|
Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.
|
||||||
|
|
||||||
|
% Merge Novel Scene: Scene 1.1 [New]
|
||||||
|
|
||||||
|
### Scene 1.1
|
||||||
|
|
||||||
|
Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.
|
||||||
|
|
||||||
|
Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl.
|
||||||
|
|
||||||
|
% Merge Novel Scene: Scene 1.2 [New]
|
||||||
|
|
||||||
|
### Scene 1.2
|
||||||
|
|
||||||
|
Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl.
|
||||||
|
|
||||||
|
Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis.
|
||||||
|
|
||||||
|
% Merge Novel Scene: Scene 1.3 [New]
|
||||||
|
|
||||||
|
### Scene 1.3
|
||||||
|
|
||||||
|
Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis.
|
||||||
|
|
||||||
|
Integer ac gravida quam. Quisque eleifend nisl nec pretium tincidunt. Quisque sollicitudin nisi in hendrerit scelerisque. Sed ornare nisl lacus, sit amet consectetur lectus egestas et. Vivamus nec arcu lorem. Donec rhoncus, purus a porta accumsan, nunc lectus iaculis libero, et fringilla tellus augue et velit. Integer varius felis scelerisque, vulputate tellus eu, laoreet justo. Suspendisse sit amet sem vehicula, auctor odio sed, aliquet enim. In ac tortor sed tortor fringilla elementum. Nulla non odio at magna vulputate scelerisque. Nam elementum diam eu rutrum scelerisque. Sed fermentum, felis quis vulputate fermentum, libero metus sollicitudin est, in faucibus purus nulla non dolor. Ut vitae felis porta, feugiat nunc et, bibendum neque. Nullam nec lorem nec metus ullamcorper malesuada ut a nisl. Etiam eget tristique dui. Nulla sed mi finibus, venenatis tellus non, maximus enim.
|
||||||
|
|
||||||
@@ -6,31 +6,26 @@
|
|||||||
},
|
},
|
||||||
"itemIndex": {
|
"itemIndex": {
|
||||||
"7a992350f3eb6": {
|
"7a992350f3eb6": {
|
||||||
"level": "H1",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H1", "title": "Lorem Ipsum", "tag": "", "cCount": 230, "wCount": 40, "pCount": 3, "synopsis": ""}
|
"T000001": {"level": "H1", "title": "Lorem Ipsum", "tag": "", "cCount": 230, "wCount": 40, "pCount": 3, "synopsis": ""}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8c58a65414c23": {
|
"8c58a65414c23": {
|
||||||
"level": "H0",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000000": {"level": "H0", "title": "", "tag": "", "cCount": 1058, "wCount": 176, "pCount": 2, "synopsis": ""}
|
"T000000": {"level": "H0", "title": "", "tag": "", "cCount": 1058, "wCount": 176, "pCount": 2, "synopsis": ""}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"88d59a277361b": {
|
"88d59a277361b": {
|
||||||
"level": "H2",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H2", "title": "Prologue", "tag": "", "cCount": 584, "wCount": 92, "pCount": 1, "synopsis": "Explanation from the lipsum.com website."}
|
"T000001": {"level": "H2", "title": "Prologue", "tag": "", "cCount": 584, "wCount": 92, "pCount": 1, "synopsis": "Explanation from the lipsum.com website."}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"db7e733775d4d": {
|
"db7e733775d4d": {
|
||||||
"level": "H1",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H1", "title": "Act One", "tag": "", "cCount": 35, "wCount": 6, "pCount": 1, "synopsis": ""}
|
"T000001": {"level": "H1", "title": "Act One", "tag": "", "cCount": 35, "wCount": 6, "pCount": 1, "synopsis": ""}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fb609cd8319dc": {
|
"fb609cd8319dc": {
|
||||||
"level": "H2",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H2", "title": "Chapter One", "tag": "", "cCount": 419, "wCount": 67, "pCount": 1, "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."}
|
"T000001": {"level": "H2", "title": "Chapter One", "tag": "", "cCount": 419, "wCount": 67, "pCount": 1, "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."}
|
||||||
},
|
},
|
||||||
@@ -39,7 +34,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"88243afbe5ed8": {
|
"88243afbe5ed8": {
|
||||||
"level": "H3",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H3", "title": "Scene One", "tag": "", "cCount": 1197, "wCount": 174, "pCount": 2, "synopsis": "Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur."},
|
"T000001": {"level": "H3", "title": "Scene One", "tag": "", "cCount": 1197, "wCount": 174, "pCount": 2, "synopsis": "Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur."},
|
||||||
"T000013": {"level": "H4", "title": "Scene One, Section Two", "tag": "", "cCount": 1561, "wCount": 230, "pCount": 2, "synopsis": ""}
|
"T000013": {"level": "H4", "title": "Scene One, Section Two", "tag": "", "cCount": 1561, "wCount": 230, "pCount": 2, "synopsis": ""}
|
||||||
@@ -49,7 +43,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"f96ec11c6a3da": {
|
"f96ec11c6a3da": {
|
||||||
"level": "H3",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H3", "title": "Scene Two", "tag": "", "cCount": 2034, "wCount": 299, "pCount": 3, "synopsis": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci."},
|
"T000001": {"level": "H3", "title": "Scene Two", "tag": "", "cCount": 2034, "wCount": 299, "pCount": 3, "synopsis": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci."},
|
||||||
"T000015": {"level": "H4", "title": "Scene Two, Section Two", "tag": "", "cCount": 2009, "wCount": 301, "pCount": 3, "synopsis": ""}
|
"T000015": {"level": "H4", "title": "Scene Two, Section Two", "tag": "", "cCount": 2009, "wCount": 301, "pCount": 3, "synopsis": ""}
|
||||||
@@ -59,13 +52,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"846352075de7d": {
|
"846352075de7d": {
|
||||||
"level": "H2",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H2", "title": "Why do we use it?", "tag": "", "cCount": 631, "wCount": 109, "pCount": 3, "synopsis": ""}
|
"T000001": {"level": "H2", "title": "Why do we use it?", "tag": "", "cCount": 631, "wCount": 109, "pCount": 3, "synopsis": ""}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"441420a886d82": {
|
"441420a886d82": {
|
||||||
"level": "H2",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H2", "title": "Chapter Two", "tag": "", "cCount": 477, "wCount": 70, "pCount": 1, "synopsis": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."}
|
"T000001": {"level": "H2", "title": "Chapter Two", "tag": "", "cCount": 477, "wCount": 70, "pCount": 1, "synopsis": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."}
|
||||||
},
|
},
|
||||||
@@ -74,7 +65,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eb103bc70c90c": {
|
"eb103bc70c90c": {
|
||||||
"level": "H3",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H3", "title": "Scene Three", "tag": "", "cCount": 3006, "wCount": 439, "pCount": 4, "synopsis": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."}
|
"T000001": {"level": "H3", "title": "Scene Three", "tag": "", "cCount": 3006, "wCount": 439, "pCount": 4, "synopsis": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."}
|
||||||
},
|
},
|
||||||
@@ -83,7 +73,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"f8c0562e50f1b": {
|
"f8c0562e50f1b": {
|
||||||
"level": "H3",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H3", "title": "Scene Four", "tag": "", "cCount": 3839, "wCount": 563, "pCount": 6, "synopsis": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."}
|
"T000001": {"level": "H3", "title": "Scene Four", "tag": "", "cCount": 3839, "wCount": 563, "pCount": 6, "synopsis": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."}
|
||||||
},
|
},
|
||||||
@@ -92,7 +81,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"47666c91c7ccf": {
|
"47666c91c7ccf": {
|
||||||
"level": "H3",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H3", "title": "Scene Five", "tag": "", "cCount": 3644, "wCount": 543, "pCount": 5, "synopsis": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."}
|
"T000001": {"level": "H3", "title": "Scene Five", "tag": "", "cCount": 3644, "wCount": 543, "pCount": 5, "synopsis": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."}
|
||||||
},
|
},
|
||||||
@@ -101,7 +89,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"4c4f28287af27": {
|
"4c4f28287af27": {
|
||||||
"level": "H1",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H1", "title": "Nobody Owens", "tag": "Bod", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""}
|
"T000001": {"level": "H1", "title": "Nobody Owens", "tag": "Bod", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""}
|
||||||
},
|
},
|
||||||
@@ -110,13 +97,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2426c6f0ca922": {
|
"2426c6f0ca922": {
|
||||||
"level": "H1",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H1", "title": "Main Plot", "tag": "Main", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""}
|
"T000001": {"level": "H1", "title": "Main Plot", "tag": "Main", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"04468803b92e1": {
|
"04468803b92e1": {
|
||||||
"level": "H1",
|
|
||||||
"headings": {
|
"headings": {
|
||||||
"T000001": {"level": "H1", "title": "Ancient Europe", "tag": "Europe", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""}
|
"T000001": {"level": "H1", "title": "Ancient Europe", "tag": "Europe", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-07-31 18:40:28">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -48,80 +48,80 @@
|
|||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000008" import="i00000c">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Title Page</name>
|
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Chapter 1</name>
|
<name status="s000008" import="i00000c" active="True">Chapter 1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 1.1</name>
|
<name status="s000008" import="i00000c" active="True">Scene 1.1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000014" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000014" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 1.2</name>
|
<name status="s000008" import="i00000c" active="True">Scene 1.2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000015" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000015" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 1.3</name>
|
<name status="s000008" import="i00000c" active="True">Scene 1.3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Chapter 2</name>
|
<name status="s000008" import="i00000c" active="True">Chapter 2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000017" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000017" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 2.1</name>
|
<name status="s000008" import="i00000c" active="True">Scene 2.1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000018" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000018" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 2.2</name>
|
<name status="s000008" import="i00000c" active="True">Scene 2.2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000019" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000019" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 2.3</name>
|
<name status="s000008" import="i00000c" active="True">Scene 2.3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Chapter 3</name>
|
<name status="s000008" import="i00000c" active="True">Chapter 3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001b" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000001b" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 3.1</name>
|
<name status="s000008" import="i00000c" active="True">Scene 3.1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001c" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000001c" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 3.2</name>
|
<name status="s000008" import="i00000c" active="True">Scene 3.2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001d" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000001d" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 3.3</name>
|
<name status="s000008" import="i00000c" active="True">Scene 3.3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="PLOT">
|
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Plot</name>
|
<name status="s000008" import="i00000c">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001f" parent="000000000001e" root="000000000001e" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="000000000001f" parent="000000000001e" root="000000000001e" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Main Plot</name>
|
<name status="s000008" import="i00000c" active="True">Main Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000020" parent="None" root="0000000000020" order="0" type="ROOT" class="CHARACTER">
|
<item handle="0000000000020" parent="None" root="0000000000020" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Characters</name>
|
<name status="s000008" import="i00000c">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000021" parent="0000000000020" root="0000000000020" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="0000000000021" parent="0000000000020" root="0000000000020" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Protagonist</name>
|
<name status="s000008" import="i00000c" active="True">Protagonist</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000022" parent="None" root="0000000000022" order="0" type="ROOT" class="WORLD">
|
<item handle="0000000000022" parent="None" root="0000000000022" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Locations</name>
|
<name status="s000008" import="i00000c">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000023" parent="0000000000022" root="0000000000022" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="0000000000023" parent="0000000000022" root="0000000000022" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Main Location</name>
|
<name status="s000008" import="i00000c" active="True">Main Location</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000024" parent="None" root="0000000000024" order="0" type="ROOT" class="ARCHIVE">
|
<item handle="0000000000024" parent="None" root="0000000000024" order="0" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-12 17:45:51">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -48,56 +48,56 @@
|
|||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000008" import="i00000c">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Title Page</name>
|
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 1</name>
|
<name status="s000008" import="i00000c" active="True">Scene 1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000013" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000013" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 2</name>
|
<name status="s000008" import="i00000c" active="True">Scene 2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 3</name>
|
<name status="s000008" import="i00000c" active="True">Scene 3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 4</name>
|
<name status="s000008" import="i00000c" active="True">Scene 4</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 5</name>
|
<name status="s000008" import="i00000c" active="True">Scene 5</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Scene 6</name>
|
<name status="s000008" import="i00000c" active="True">Scene 6</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="PLOT">
|
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Plot</name>
|
<name status="s000008" import="i00000c">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Main Plot</name>
|
<name status="s000008" import="i00000c" active="True">Main Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="CHARACTER">
|
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Characters</name>
|
<name status="s000008" import="i00000c">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Protagonist</name>
|
<name status="s000008" import="i00000c" active="True">Protagonist</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="WORLD">
|
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Locations</name>
|
<name status="s000008" import="i00000c">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001d" parent="000000000001c" root="000000000001c" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="000000000001d" parent="000000000001c" root="000000000001c" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Main Location</name>
|
<name status="s000008" import="i00000c" active="True">Main Location</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="ARCHIVE">
|
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-07-31 18:40:28">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
<lastViewed>None</lastViewed>
|
<lastViewed>None</lastViewed>
|
||||||
<lastNovel>None</lastNovel>
|
<lastNovel>None</lastNovel>
|
||||||
<lastOutline>None</lastOutline>
|
<lastOutline>None</lastOutline>
|
||||||
<lastWordCount>2</lastWordCount>
|
<lastWordCount>13</lastWordCount>
|
||||||
<novelWordCount>1</novelWordCount>
|
<novelWordCount>10</novelWordCount>
|
||||||
<notesWordCount>1</notesWordCount>
|
<notesWordCount>3</notesWordCount>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<title>%title%</title>
|
||||||
@@ -29,62 +29,62 @@
|
|||||||
<section></section>
|
<section></section>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000008" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
|
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="i00000c" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="i000004" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
|
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||||
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
|
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content count="11">
|
<content count="11">
|
||||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
|
<item handle="0000000000009" parent="None" root="0000000000009" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Plot</name>
|
<name status="s000000" import="i000004">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
|
<item handle="000000000000a" parent="None" root="000000000000a" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Characters</name>
|
<name status="s000000" import="i000004">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
|
<item handle="000000000000b" parent="None" root="000000000000b" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">World</name>
|
<name status="s000000" import="i000004">World</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
|
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">New Chapter</name>
|
<name status="s000000" import="i000004">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000016" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">New Chapter</name>
|
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000017" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">New Scene</name>
|
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000028" parent="0000000000015" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
|
<item handle="0000000000020" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Stuff</name>
|
<name status="s000000" import="i000004">Stuff</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000029" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000021" parent="0000000000020" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H2" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Hello</name>
|
<name status="s000000" import="i000004" active="True">Hello</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000002a" parent="0000000000012" root="0000000000012" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="0000000000022" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" charCount="4" wordCount="1" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" mainHeading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" exported="True">Jane</name>
|
<name status="s000000" import="i000004" active="True">Jane</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
|
|||||||