Merge branch 'main' into outline_view

This commit is contained in:
Veronica Berglyd Olsen
2022-05-22 16:19:53 +02:00
47 changed files with 2613 additions and 2381 deletions
+89
View File
@@ -1,5 +1,94 @@
# novelWriter Changelog # novelWriter Changelog
## Version 1.7 Beta 1 [2022-05-17]
### Release Notes
This is a beta release of the next release version, and is intended for testing purposes. Please be
careful when using this version on live writing projects, and make sure you take frequent backups.
Please check the changelog for an overview of changes. The full release notes will be added to the
final release.
### Detailed Changelog
**Features**
* A simple tool to add Lorem Ipsum placeholder text has been added to the Insert menu. PR #1028.
* Status and Importance flags can now be reorganised in Project Settings. Issue #1035. PR #1040.
* It is now possible to create multiple Root Folders of the same kind. This makes it possible to
add multiple Novel root folders in a project, for instance. Issue #967. PR #1031.
* All documents can now be dragged and dropped anywhere in the project tree. The document layout
may be converted in the process. PR #1031.
* Documents in the project tree can now have other documents as child documents. Issue #1002.
PR #1047.
* Folders in the project tree that are not empty, can now be moved to trash. PR #1048.
* Empty folders are deleted on request, and not moved to trash. Issue #1052. PR #1055.
**User Interface**
* The tabs under the project tree and to the right of the main window have been replaced with a
toolbar on the left hand side. The toolbar has a set of buttons to change view between Project,
Novel and Outline. The three buttons that were available under the project tree have been moved
to the bottom of the new toolbar. Issue #1056. PR #1057.
* When a document changes from a project document to a note, and back again, the Status flag
setting is preserved. Previously, the Importance setting would overwrite it during the
conversion. PR #1030.
* Item labels, Status labels, and other labels on the GUI are now run through a "simplify" function
before being accepted. This functions strips out all whitespaces and consecutive whitespaces and
replace them with single plain whitespaces. This is a safer format to store in XML, and also
makes sure there aren't invisible characters floating around in the labels. PR #1038.
* Due to the changes to how drag and drop works, there are no longer any restrictions on folders
and documents. Only root folders remain restricted in terms of moving. Root folders can only be
reordered with the Move Up and Move Down commands. PR #1047.
* The label for the highlighting of redundant spaces in the Preferences dialog has been updated to
better reflect what it does. Issue #1043. PR #1046.
* The New Project Wizard will now try to check if the path selected for the new project can
actually be used before letting the user proceed to the next page. Issue #1058. PR #1062.
**Internationalisation**
* Dutch translations have been added by Martijn van der Kleijn (@mvdkleijn). PR #1027.
**Functionality**
* Documents that are missing in the project index when a project is opened are automatically
re-indexed. This also handles cases where the cached index is missing. PR #1039.
**Installation and Packaging**
* Python 3.6 is no longer supported. PR #1004.
* Ubuntu 18.04 packages will no longer be released, due to dropping Python 3.6. Issue #1005.
PR #1014.
**Project File Format**
* The item nodes in the content section of the main project XML file have been compacted. It now
consists of a main item node and meta and a name node. All settings have been made attributes of
one of these three nodes, except the item label which is the text value of the name node. The
file format version has been bumped to 1.4. Issue #995. PR #993.
* Both Importance and Status flag values are now saved to the project file. This means if a
document changes layout, the value is no longer lost. PR #1030.
**Code Improvements**
* The linting settings have been updated to select between mutually exclusive options in
pycodestyle. PR #1014.
* The Tokenizer class has been converted to an abstract base class. PR #1026.
* The class handling Status and Importance flags has been completely rewritten. The flags are now
handled using a unique random key as reference rather than relying on the text of the label
itself. This makes it a lot easier to rename them as there is no need to update project items.
PR #1034.
* Many of the decisions regarding where items are allowed to belong has been delegated to the
NWItem class that holds the item. Some is also handled by the NWTree class that holds the project
tree. A new maintenance function in the NWTree class will also ensure that the meta data of an
item is correct and up to date. This is especially important after an item has been moved, but is
also checked when items are initially loaded. PRs #1031 and #1054.
* Item handles are now generated using the standard library random number generator. The new
handles have the same format as the old algorithm, so they are compatible. PR #1044.
----
## Version 1.6.2 [2022-03-20] ## Version 1.6.2 [2022-03-20]
### Release Notes ### Release Notes
+5 -3
View File
@@ -11,12 +11,14 @@
## Translations ## Translations
The default language is English (UK) with English (US) as an option.
* Dutch: Martijn van der Kleijn (@mvdkleijn)
* French: Jan Lüdke (@jyhelle) * French: Jan Lüdke (@jyhelle)
* Latin American Spanish: Tommy Marplatt (@tmarplatt)
* Norwegian: Veronica Berglyd Olsen (@vkbo) * Norwegian: Veronica Berglyd Olsen (@vkbo)
* Portuguese: Bruno Meneguello (@bkmeneguello) * Portuguese: Bruno Meneguello (@bkmeneguello)
* Simplified Chinese: Qianzhi Long (@longqzh) * Simplified Chinese: Qianzhi Long (@longqzh)
* Latin American Spanish: Tommy Marplatt (@tmarplatt)
* Dutch: Martijn van der Kleijn (@mvdkleijn)
## Libraries ## Libraries
@@ -32,7 +34,7 @@ The following libraries are dependencies of novelWriter:
Some of the assets bundled with novelWriter were adapted from the following sources: Some of the assets bundled with novelWriter were adapted from the following sources:
* [Typicons](https://github.com/stephenhutchings/typicons.font) icons by Stephen Hutchings (CC BY-SA 4.0) * Typicons icons by [Stephen Hutchings](https://github.com/stephenhutchings/typicons.font) (CC BY-SA 4.0)
* Tomorrow syntax themes by Chris Kempson (MIT License) * Tomorrow syntax themes by Chris Kempson (MIT License)
* Owl syntax themes by Sarah Drasner (MIT License) * Owl syntax themes by Sarah Drasner (MIT License)
* Solarized themes by Ethan Schoonover, added by @nullbasis (MIT License) * Solarized themes by Ethan Schoonover, added by @nullbasis (MIT License)
+17
View File
@@ -2,6 +2,7 @@
The maintenance of translations has been moved to the Crowdin service. The translation strings can The maintenance of translations has been moved to the Crowdin service. The translation strings can
be edited there at the [novelWriter project page](https://crowdin.com/project/novelwriter). be edited there at the [novelWriter project page](https://crowdin.com/project/novelwriter).
However, please read the Translation Guidelines section below.
You can still use the manual approach listed below, and then upload the file through the website's You can still use the manual approach listed below, and then upload the file through the website's
interface. The translation strings for that language will then be updated and queued for approval. interface. The translation strings for that language will then be updated and queued for approval.
@@ -11,6 +12,22 @@ To verify a language file translated through the Crowdin tool, download and extr
[Generate an Updated Translation File](#generate-an-updated-translation-file) below. [Generate an Updated Translation File](#generate-an-updated-translation-file) below.
# Translation Guidelines
When contributing translations, keep the following things in mind.
* For descriptive labels and dialog boxes, make sure you do _not_ change the meaning of the text
when you translate it from English. The user must receive the same instructions or information
regardless of language. This is improtant, otherwise the documentation will be inconsistent with
the user interface and it will become a lot more difficult to handle user issues and questions.
* If you think a label or description is misleading or incomplete, please file an issue report. The
correct way to handle such changes is to change the text in the code first, which will then be
forwarded to _all_ translators such that the GUI is consistent across all languages.
* For very short labels, like button labels. it may be fine to replace the word with a similar
word, but only as long as the user understands what it is supposed to do. Some buttons and tabs
have limited space. If necessary, it is OK to use abbreviations.
# Direct Approach Using Qt Linguist # Direct Approach Using Qt Linguist
Here you will find instructions for translating novelWriter to a new language directly using Qt Here you will find instructions for translating novelWriter to a new language directly using Qt
+611 -602
View File
File diff suppressed because it is too large Load Diff
+615 -606
View File
File diff suppressed because it is too large Load Diff
+615 -606
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -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-alpha0" __version__ = "1.7-beta1"
__hexversion__ = "0x010700a0" __hexversion__ = "0x010700b1"
__date__ = "2022-02-20" __date__ = "2022-05-17"
__status__ = "Stable" __status__ = "Stable"
__domain__ = "novelwriter.io" __domain__ = "novelwriter.io"
__url__ = "https://novelwriter.io" __url__ = "https://novelwriter.io"
+100 -100
View File
@@ -2,104 +2,104 @@
"Synopsis": "Synopsis", "Synopsis": "Synopsis",
"Comment": "Opmerking", "Comment": "Opmerking",
"Notes": "Notities", "Notes": "Notities",
"0": "Nul", "0": "nul",
"1": "Één", "1": "één",
"2": "Twee", "2": "twee",
"3": "Drie", "3": "drie",
"4": "Vier", "4": "vier",
"5": "Vijf", "5": "vijf",
"6": "Zes", "6": "zes",
"7": "Zeven", "7": "zeven",
"8": "Acht", "8": "acht",
"9": "Negen", "9": "negen",
"10": "Tien", "10": "tien",
"11": "Elf", "11": "elf",
"12": "Twaalf", "12": "twaalf",
"13": "Dertien", "13": "dertien",
"14": "Veertien", "14": "veertien",
"15": "Vijftien", "15": "vijftien",
"16": "Zestien", "16": "zestien",
"17": "Zeventien", "17": "zeventien",
"18": "Achttien", "18": "achttien",
"19": "Negentien", "19": "negentien",
"20": "Twintig", "20": "twintig",
"21": "Eenentwintig", "21": "eenentwintig",
"22": "Tweeentwintig", "22": "tweeëntwintig",
"23": "Drieentwintig", "23": "drieëntwintig",
"24": "Vierentwintig", "24": "vierentwintig",
"25": "Vijfentwintig", "25": "vijfentwintig",
"26": "Zesentwintig", "26": "zesentwintig",
"27": "Zevenentwintig", "27": "zevenentwintig",
"28": "Achtentwintig", "28": "achtentwintig",
"29": "Negenentwintig", "29": "negenentwintig",
"30": "Dertig", "30": "dertig",
"31": "Eenendertig", "31": "eenendertig",
"32": "Tweeendertig", "32": "tweeëndertig",
"33": "Drieendertig", "33": "drieëndertig",
"34": "Vierendertig", "34": "vierendertig",
"35": "Vijfendertig", "35": "vijfendertig",
"36": "Zesendertig", "36": "zesendertig",
"37": "Zevenendertig", "37": "zevenendertig",
"38": "Achtendertig", "38": "achtendertig",
"39": "Negenendertig", "39": "negenendertig",
"40": "Veertig", "40": "veertig",
"41": "Eenenveertig", "41": "eenenveertig",
"42": "Tweeënveertig", "42": "tweeënveertig",
"43": "Drieenveertig", "43": "drieënveertig",
"44": "Vierenveertig", "44": "vierenveertig",
"45": "Vijfenveertig", "45": "vijfenveertig",
"46": "Zesenveertig", "46": "zesenveertig",
"47": "Zevenenveertig", "47": "zevenenveertig",
"48": "Achtenveertig", "48": "achtenveertig",
"49": "Negenenveertig", "49": "negenenveertig",
"50": "Vijftig", "50": "vijftig",
"51": "Eenenvijftig", "51": "eenenvijftig",
"52": "Tweeenvijftig", "52": "tweeënvijftig",
"53": "Drieenvijftig", "53": "drieënvijftig",
"54": "Vierenvijftig", "54": "vierenvijftig",
"55": "Vijfenvijftig", "55": "vijfenvijftig",
"56": "Zesenvijftig", "56": "zesenvijftig",
"57": "Zevenenvijftig", "57": "zevenenvijftig",
"58": "Achtenvijftig", "58": "achtenvijftig",
"59": "Negenenvijftig", "59": "negenenvijftig",
"60": "Zestig", "60": "zestig",
"61": "Eenenzestig", "61": "eenenzestig",
"62": "Tweeenzestig", "62": "tweeënzestig",
"63": "Drieenzestig", "63": "drieënzestig",
"64": "Vierenzestig", "64": "vierenzestig",
"65": "Vijfenzestig", "65": "vijfenzestig",
"66": "Zesenzestig", "66": "zesenzestig",
"67": "Zevenenzestig", "67": "zevenenzestig",
"68": "Achtenzestig", "68": "achtenzestig",
"69": "Negenenzestig", "69": "negenenzestig",
"70": "Zeventig", "70": "zeventig",
"71": "Eenenzeventig", "71": "eenenzeventig",
"72": "Tweeenzeventig", "72": "tweeënzeventig",
"73": "Drieenzeventig", "73": "drieënzeventig",
"74": "Vierenzeventig", "74": "vierenzeventig",
"75": "Vijfenzeventig", "75": "vijfenzeventig",
"76": "Zesenzeventig", "76": "zesenzeventig",
"77": "Zevenenzeventig", "77": "zevenenzeventig",
"78": "Achtenzeventig", "78": "achtenzeventig",
"79": "Negenenzeventig", "79": "negenenzeventig",
"80": "Tachtig", "80": "tachtig",
"81": "Eenentachtig", "81": "eenentachtig",
"82": "Tweeentachtig", "82": "tweeëntachtig",
"83": "Drieentachtig", "83": "drieëntachtig",
"84": "Vierentachtig", "84": "vierentachtig",
"85": "Vijfentachtig", "85": "vijfentachtig",
"86": "Zesentachtig", "86": "zesentachtig",
"87": "Zevenentachtig", "87": "zevenentachtig",
"88": "Achtentachtig", "88": "achtentachtig",
"89": "Negenentachtig", "89": "negenentachtig",
"90": "Negentig", "90": "negentig",
"91": "Eenennegentig", "91": "eenennegentig",
"92": "Tweeennegentig", "92": "tweeënnegentig",
"93": "Drieennegentig", "93": "drieënnegentig",
"94": "Vierennegentig", "94": "vierennegentig",
"95": "Vijfennegentig", "95": "vijfennegentig",
"96": "Zesennegentig", "96": "zesennegentig",
"97": "Zevenennegentig", "97": "zevenennegentig",
"98": "Achtennegentig", "98": "achtennegentig",
"99": "Negenennegentig" "99": "negenennegentig"
} }
+7 -47
View File
@@ -2,56 +2,16 @@
<html> <html>
<body> <body>
<h2>Release Notes for 1.6</h2> <h2>Release Notes for 1.7 Beta 1</h2>
<p><i>Released on 20 February 2022</i></p> <p><i>Released on 17 May 2022</i></p>
<p>This release does not introduce any major new features, but is instead a collection of minor <p>This is a beta release of the next release version, and is intended for testing purposes. Please
improvements and tweaks based on user requests. There are also a number of changes under the hood be careful when using this version on live writing projects, and make sure you take frequent
to improve the structure and performance of novelWriter.</p> backups.</p>
<p><u>Some key improvements to the user interface are:</u></p> <p>Please check the changelog for an overview of changes. The full release notes will be added to
<p>&#10003; The max text width setting in Preferences now also applies to the document viewer, and the final release.</p>
the setting itself on the Preference dialog has been simplified a bit.</p>
<p>&#10003; When text is selected in the document editor, the number of words selected is displayed
in the editor's footer area.</p>
<p>&#10003; The search tool in the document editor now shows the number of results in the
document.</p>
<p>&#10003; The Enter and Ctrl+O keyboard shortcuts should now work the same way in all tree
views.</p>
<p>&#10003; It is now possible to set a blank section title format on the Build Novel Project tool
and get empty paragraphs in the output. Previously, a blank format would just remove the section
break entirely. This change allows the user to define hard and soft scene breaks using level three
and four headings. The scene and section titles can be hidden completely with two new switches
added to the user interface.</p>
<p><u>Other feature changes include:</u></p>
<p>&#10003; The project index is now automatically rebuilt in the event it is empty or incomplete
when the project is opened.</p>
<p>&#10003; The user can now add their own syntax and GUI theme files in the app folder in their
user area on the host operating system. Where the custom files must be added is described in the
documentation.</p>
<p>&#10003; A Windows installer is yet again provided for novelWriter. If you have novelWriter
installed using another method, make sure you uninstall it properly first as the two methods are
not compatible.</p>
<p>&#10003; Release versions for Ubuntu 21.04 have been dropped, and added for the upcoming Ubuntu
22.04.</p>
<p>&#10003; Most translations have been updated. A Dutch translation is in the works.</p>
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p> <p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
<h2>Patch Notes</h2>
<h3>Patch 1.6.1 &ndash; 16 March 2022</h3>
<p>This is a bugfix and patch release that fixes two recursion/loop issues. One would potentially
cause a crash if the window was resized rapidly, and one would cause a hang with certain search
parameters in the editor's search box. The Latin American Spanish translation has also been
updated.</p>
<h3>Patch 1.6.2 &ndash; 20 March 2022</h3>
<p>This is a bugfix release that fixes a couple of minor issues. Projects containing one or more
empty documents would trigger a rebuild of the index each time the project was opened. This has now
been fixed. Another fix resolves an error message being written to the console logging output when
a new document was created. Both errors were harmless.</p>
</body> </body>
</html> </html>
+58 -52
View File
@@ -120,32 +120,34 @@ class NWProject():
# Item Methods # Item Methods
## ##
def newRoot(self, rootName, rootClass): def newRoot(self, itemClass, label=None):
"""Add a new root item. """Add a new root item. If label is None, use the class label.
""" """
if label is None:
label = trConst(nwLabels.CLASS_NAME[itemClass])
newItem = NWItem(self) newItem = NWItem(self)
newItem.setName(rootName) newItem.setName(label)
newItem.setType(nwItemType.ROOT) newItem.setType(nwItemType.ROOT)
newItem.setClass(rootClass) newItem.setClass(itemClass)
self.projTree.append(None, None, newItem) self.projTree.append(None, None, newItem)
self.projTree.updateItemData(newItem.itemHandle) self.projTree.updateItemData(newItem.itemHandle)
return newItem.itemHandle return newItem.itemHandle
def newFolder(self, folderName, pHandle): def newFolder(self, label, pHandle):
"""Add a new folder with a given name and parent item. """Add a new folder with a given label and parent item.
""" """
newItem = NWItem(self) newItem = NWItem(self)
newItem.setName(folderName) newItem.setName(label)
newItem.setType(nwItemType.FOLDER) newItem.setType(nwItemType.FOLDER)
self.projTree.append(None, pHandle, newItem) self.projTree.append(None, pHandle, newItem)
self.projTree.updateItemData(newItem.itemHandle) self.projTree.updateItemData(newItem.itemHandle)
return newItem.itemHandle return newItem.itemHandle
def newFile(self, fileName, pHandle): def newFile(self, label, pHandle):
"""Add a new file with a given name and parent item. """Add a new file with a given label and parent item.
""" """
newItem = NWItem(self) newItem = NWItem(self)
newItem.setName(fileName) newItem.setName(label)
newItem.setType(nwItemType.FILE) newItem.setType(nwItemType.FILE)
self.projTree.append(None, pHandle, newItem) self.projTree.append(None, pHandle, newItem)
self.projTree.updateItemData(newItem.itemHandle) self.projTree.updateItemData(newItem.itemHandle)
@@ -264,84 +266,88 @@ class NWProject():
self.setBookTitle(projTitle) self.setBookTitle(projTitle)
self.setBookAuthors(projAuthors) self.setBookAuthors(projAuthors)
hNovelRoot = self.newRoot(nwItemClass.NOVEL)
hTitlePage = self.newFile(self.tr("Title Page"), hNovelRoot)
titlePage = "#! %s\n\n" % (self.bookTitle if self.bookTitle else self.projName) titlePage = "#! %s\n\n" % (self.bookTitle if self.bookTitle else self.projName)
if self.bookAuthors: if self.bookAuthors:
titlePage = "%s>> %s %s <<\n" % (titlePage, self.tr("By"), self.getAuthors()) titlePage = "%s>> %s %s <<\n" % (titlePage, self.tr("By"), self.getAuthors())
aDoc = NWDoc(self, hTitlePage)
aDoc.writeDocument(titlePage)
if popMinimal: if popMinimal:
# Creating a minimal project with a few root folders and a # Creating a minimal project with a few root folders and a
# single chapter folder with a single file. # single chapter with a single scene.
xHandle = {} hChapter = self.newFile(self.tr("New Chapter"), hNovelRoot)
xHandle[1] = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL) aDoc = NWDoc(self, hChapter)
xHandle[2] = self.newRoot(self.tr("Plot"), nwItemClass.PLOT)
xHandle[3] = self.newRoot(self.tr("Characters"), nwItemClass.CHARACTER)
xHandle[4] = self.newRoot(self.tr("World"), nwItemClass.WORLD)
xHandle[5] = self.newFile(self.tr("Title Page"), xHandle[1])
xHandle[6] = self.newFolder(self.tr("New Chapter"), xHandle[1])
xHandle[7] = self.newFile(self.tr("New Chapter"), xHandle[6])
xHandle[8] = self.newFile(self.tr("New Scene"), xHandle[6])
aDoc = NWDoc(self, xHandle[5])
aDoc.writeDocument(titlePage)
aDoc = NWDoc(self, xHandle[7])
aDoc.writeDocument("## %s\n\n" % self.tr("New Chapter")) aDoc.writeDocument("## %s\n\n" % self.tr("New Chapter"))
aDoc = NWDoc(self, xHandle[8]) hScene = self.newFile(self.tr("New Scene"), hChapter)
aDoc = NWDoc(self, hScene)
aDoc.writeDocument("### %s\n\n" % self.tr("New Scene")) aDoc.writeDocument("### %s\n\n" % self.tr("New Scene"))
self.newRoot(nwItemClass.PLOT)
self.newRoot(nwItemClass.CHARACTER)
self.newRoot(nwItemClass.WORLD)
self.newRoot(nwItemClass.ARCHIVE)
elif popCustom: elif popCustom:
# Create a project structure based on selected root folders # Create a project structure based on selected root folders
# and a number of chapters and scenes selected in the # and a number of chapters and scenes selected in the
# wizard's custom page. # wizard's custom page.
# Create root folders
nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass:
self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot)
# Create a title page
tHandle = self.newFile(self.tr("Title Page"), nHandle)
aDoc = NWDoc(self, tHandle)
aDoc.writeDocument(titlePage)
# Create chapters and scenes # Create chapters and scenes
numChapters = projData.get("numChapters", 0) numChapters = projData.get("numChapters", 0)
numScenes = projData.get("numScenes", 0) numScenes = projData.get("numScenes", 0)
chFolders = projData.get("chFolders", False)
chSynop = self.tr("Summary of the chapter.")
scSynop = self.tr("Summary of the scene.")
# Create chapters # Create chapters
if numChapters > 0: if numChapters > 0:
for ch in range(numChapters): for ch in range(numChapters):
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}") chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
pHandle = nHandle cHandle = self.newFile(chTitle, hNovelRoot)
if chFolders:
pHandle = self.newFolder(chTitle, nHandle)
cHandle = self.newFile(chTitle, pHandle)
aDoc = NWDoc(self, cHandle) aDoc = NWDoc(self, cHandle)
aDoc.writeDocument("## %s\n\n" % chTitle) aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
# Create chapter scenes # Create chapter scenes
if numScenes > 0: if numScenes > 0:
for sc in range(numScenes): for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}") scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = self.newFile(scTitle, pHandle) sHandle = self.newFile(scTitle, cHandle)
aDoc = NWDoc(self, sHandle) aDoc = NWDoc(self, sHandle)
aDoc.writeDocument("### %s\n\n" % scTitle) aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
# Create scenes (no chapters) # Create scenes (no chapters)
elif numScenes > 0: elif numScenes > 0:
for sc in range(numScenes): for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}") scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = self.newFile(scTitle, nHandle) sHandle = self.newFile(scTitle, hNovelRoot)
aDoc = NWDoc(self, sHandle) aDoc = NWDoc(self, sHandle)
aDoc.writeDocument("### %s\n\n" % scTitle) aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
# Create notes folders
noteTitles = {
nwItemClass.PLOT: self.tr("Main Plot"),
nwItemClass.CHARACTER: self.tr("Protagonist"),
nwItemClass.WORLD: self.tr("Main Location"),
}
addNotes = projData.get("addNotes", False)
for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass:
rHandle = self.newRoot(newRoot)
if addNotes:
aHandle = self.newFile(noteTitles[newRoot], rHandle)
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
aDoc = NWDoc(self, aHandle)
aDoc.writeDocument(f"# {noteTitles[newRoot]}\n\n@tag: {ntTag}\n\n")
# Also add the archive and trash folders
self.newRoot(nwItemClass.ARCHIVE)
self.trashFolder()
# Finalise # Finalise
if popCustom or popMinimal: if popCustom or popMinimal:
+2 -2
View File
@@ -182,12 +182,12 @@ class GuiAbout(QDialog):
self.tr("Translations"), self.tr("Translations"),
self._wrapTable([ self._wrapTable([
("English", "Veronica Berglyd Olsen"), ("English", "Veronica Berglyd Olsen"),
("Español Latinoamericano", "Tommy Marplatt"),
("Français", "Jan Lüdke (jyhelle)"), ("Français", "Jan Lüdke (jyhelle)"),
("Nederlands", "Martijn van der Kleijn"),
("Norsk Bokmål", "Veronica Berglyd Olsen"), ("Norsk Bokmål", "Veronica Berglyd Olsen"),
("Português", "Bruno Meneguello"), ("Português", "Bruno Meneguello"),
("简体中文", "Qianzhi Long"), ("简体中文", "Qianzhi Long"),
("Español Latinoamericano", "Tommy Marplatt"),
("Nederlands", "Martijn van der Kleijn"),
]) ])
) )
+1 -4
View File
@@ -37,7 +37,6 @@ from PyQt5.QtWidgets import (
from novelwriter.core import NWDoc from novelwriter.core import NWDoc
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
from novelwriter.constants import trConst, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -176,9 +175,7 @@ class GuiProjectTree(QTreeWidget):
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass): if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
tHandle = self.theProject.newRoot( tHandle = self.theProject.newRoot(itemClass)
trConst(nwLabels.CLASS_NAME[itemClass]), itemClass
)
elif itemType in (nwItemType.FILE, nwItemType.FOLDER): elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
+2
View File
@@ -49,6 +49,7 @@ class GuiViewsBar(QToolBar):
# Style # Style
iPx = self.mainConf.pxInt(22) iPx = self.mainConf.pxInt(22)
mPx = self.mainConf.pxInt(58)
lblFont = self.theTheme.guiFont lblFont = self.theTheme.guiFont
lblFont.setPointSizeF(0.65*self.theTheme.fontPointSize) lblFont.setPointSizeF(0.65*self.theTheme.fontPointSize)
@@ -57,6 +58,7 @@ class GuiViewsBar(QToolBar):
self.setMovable(False) self.setMovable(False)
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setMaximumWidth(mPx)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
stretch = QWidget(self) stretch = QWidget(self)
+2 -8
View File
@@ -1432,9 +1432,9 @@ class GuiMain(QMainWindow):
"popMinimal": newProj.field("popMinimal"), "popMinimal": newProj.field("popMinimal"),
"popCustom": newProj.field("popCustom"), "popCustom": newProj.field("popCustom"),
"addRoots": [], "addRoots": [],
"addNotes": False,
"numChapters": 0, "numChapters": 0,
"numScenes": 0, "numScenes": 0,
"chFolders": False,
} }
if newProj.field("popCustom"): if newProj.field("popCustom"):
addRoots = [] addRoots = []
@@ -1444,16 +1444,10 @@ class GuiMain(QMainWindow):
addRoots.append(nwItemClass.CHARACTER) addRoots.append(nwItemClass.CHARACTER)
if newProj.field("addWorld"): if newProj.field("addWorld"):
addRoots.append(nwItemClass.WORLD) addRoots.append(nwItemClass.WORLD)
if newProj.field("addTime"):
addRoots.append(nwItemClass.TIMELINE)
if newProj.field("addObject"):
addRoots.append(nwItemClass.OBJECT)
if newProj.field("addEntity"):
addRoots.append(nwItemClass.ENTITY)
projData["addRoots"] = addRoots projData["addRoots"] = addRoots
projData["addNotes"] = newProj.field("addNotes")
projData["numChapters"] = newProj.field("numChapters") projData["numChapters"] = newProj.field("numChapters")
projData["numScenes"] = newProj.field("numScenes") projData["numScenes"] = newProj.field("numScenes")
projData["chFolders"] = newProj.field("chFolders")
return projData return projData
+1 -1
View File
@@ -72,7 +72,7 @@ class GuiLipsum(QDialog):
# Form # Form
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Insert Lorem Ipsum Text"))) self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Insert Lorem Ipsum Text")))
self.paraLabel = QLabel(self.tr("Number of pragraphs")) self.paraLabel = QLabel(self.tr("Number of paragraphs"))
self.paraCount = QSpinBox() self.paraCount = QSpinBox()
self.paraCount.setMinimum(1) self.paraCount.setMinimum(1)
self.paraCount.setMaximum(100) self.paraCount.setMaximum(100)
+91 -90
View File
@@ -31,12 +31,10 @@ from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QWizard, QWizardPage, QLabel, QVBoxLayout, QLineEdit, QPlainTextEdit, QWizard, QWizardPage, QLabel, QVBoxLayout, QLineEdit, QPlainTextEdit,
QPushButton, QFileDialog, QHBoxLayout, QRadioButton, QFormLayout, QPushButton, QFileDialog, QHBoxLayout, QRadioButton, QFormLayout,
QGroupBox, QGridLayout, QSpinBox QGridLayout, QSpinBox
) )
from novelwriter.enum import nwItemClass
from novelwriter.common import makeFileNameSafe from novelwriter.common import makeFileNameSafe
from novelwriter.constants import trConst, nwLabels
from novelwriter.gui.custom import QSwitch from novelwriter.gui.custom import QSwitch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -98,10 +96,10 @@ class ProjWizardIntroPage(QWizardPage):
self.setTitle(self.tr("Create New Project")) self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
"Provide at least a working title. The working title should not " "Provide at least a project name. The project name should not "
"be change beyond this point as it is used by the application for " "be changed beyond this point as it is used for generating file "
"generating file names for for instance backups. The other fields " "names for for instance backups. The other fields are optional "
"are optional and can be changed at any time in Project Settings." "and can be changed at any time in Project Settings."
)) ))
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
@@ -134,7 +132,7 @@ class ProjWizardIntroPage(QWizardPage):
self.projAuthors.setPlaceholderText(self.tr("Optional. One name per line.")) self.projAuthors.setPlaceholderText(self.tr("Optional. One name per line."))
self.mainForm = QFormLayout() self.mainForm = QFormLayout()
self.mainForm.addRow(self.tr("Working Title"), self.projName) self.mainForm.addRow(self.tr("Project Name"), self.projName)
self.mainForm.addRow(self.tr("Novel Title"), self.projTitle) self.mainForm.addRow(self.tr("Novel Title"), self.projTitle)
self.mainForm.addRow(self.tr("Author(s)"), self.projAuthors) self.mainForm.addRow(self.tr("Author(s)"), self.projAuthors)
self.mainForm.setVerticalSpacing(fS) self.mainForm.setVerticalSpacing(fS)
@@ -324,68 +322,28 @@ class ProjWizardCustomPage(QWizardPage):
self.setTitle(self.tr("Custom Project Options")) self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel(self.tr( self.theText = QLabel(self.tr(
"Select which additional root folders to make, and how to populate " "Select which additional elements to populate the project with. "
"the Novel folder. If you don't want to add chapters or scenes, set " "You can skip making chapters and add only scenes by setting the "
"the values to 0. You can add scenes without chapters." "number of chapters to 0."
)) ))
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12) cM = self.mainConf.pxInt(12)
mH = self.mainConf.pxInt(26)
fS = self.mainConf.pxInt(4)
# Root Folders # Root Folders
self.rootGroup = QGroupBox(self.tr("Additional Root Folders")) self.addPlot = QSwitch()
self.rootForm = QGridLayout() self.addChar = QSwitch()
self.rootGroup.setLayout(self.rootForm) self.addWorld = QSwitch()
self.addNotes = QSwitch()
self.lblPlot = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.PLOT]))
)
self.lblChar = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.CHARACTER]))
)
self.lblWorld = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.WORLD]))
)
self.lblTime = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.TIMELINE]))
)
self.lblObject = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.OBJECT]))
)
self.lblEntity = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.ENTITY]))
)
self.addPlot = QSwitch()
self.addChar = QSwitch()
self.addWorld = QSwitch()
self.addTime = QSwitch()
self.addObject = QSwitch()
self.addEntity = QSwitch()
self.addPlot.setChecked(True) self.addPlot.setChecked(True)
self.addChar.setChecked(True) self.addChar.setChecked(True)
self.addWorld.setChecked(True) self.addWorld.setChecked(False)
self.addNotes.setChecked(False)
self.rootForm.addWidget(self.lblPlot, 0, 0)
self.rootForm.addWidget(self.lblChar, 1, 0)
self.rootForm.addWidget(self.lblWorld, 2, 0)
self.rootForm.addWidget(self.lblTime, 3, 0)
self.rootForm.addWidget(self.lblObject, 4, 0)
self.rootForm.addWidget(self.lblEntity, 5, 0)
self.rootForm.addWidget(self.addPlot, 0, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addChar, 1, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addWorld, 2, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addTime, 3, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addObject, 4, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addEntity, 5, 1, 1, 1, Qt.AlignRight)
self.rootForm.setRowStretch(6, 1)
# Novel Options
self.novelGroup = QGroupBox(self.tr("Populate Novel Folder"))
self.novelForm = QGridLayout()
self.novelGroup.setLayout(self.novelForm)
# Generate Content
self.numChapters = QSpinBox() self.numChapters = QSpinBox()
self.numChapters.setRange(0, 100) self.numChapters.setRange(0, 100)
self.numChapters.setValue(5) self.numChapters.setValue(5)
@@ -394,37 +352,40 @@ class ProjWizardCustomPage(QWizardPage):
self.numScenes.setRange(0, 200) self.numScenes.setRange(0, 200)
self.numScenes.setValue(5) self.numScenes.setValue(5)
self.chFolders = QSwitch() # Grid Form
self.chFolders.setChecked(True) self.addBox = QGridLayout()
self.addBox.addWidget(QLabel(self.tr("Add a folder for plot notes")), 0, 0)
self.novelForm.addWidget(QLabel(self.tr("Add chapters")), 0, 0) self.addBox.addWidget(QLabel(self.tr("Add a folder for character notes")), 1, 0)
self.novelForm.addWidget(QLabel(self.tr("Scenes (per chapter)")), 1, 0) self.addBox.addWidget(QLabel(self.tr("Add a folder for location notes")), 2, 0)
self.novelForm.addWidget(QLabel(self.tr("Add chapter folders")), 2, 0) self.addBox.addWidget(QLabel(self.tr("Add example notes to the above")), 3, 0)
self.novelForm.addWidget(self.numChapters, 0, 1, 1, 1, Qt.AlignRight) self.addBox.addWidget(QLabel(self.tr("Add chapters to the novel folder")), 4, 0)
self.novelForm.addWidget(self.numScenes, 1, 1, 1, 1, Qt.AlignRight) self.addBox.addWidget(QLabel(self.tr("Add scenes to each chapter")), 5, 0)
self.novelForm.addWidget(self.chFolders, 2, 1, 1, 1, Qt.AlignRight) self.addBox.addWidget(self.addPlot, 0, 1, 1, 1, Qt.AlignRight)
self.novelForm.setRowStretch(3, 1) self.addBox.addWidget(self.addChar, 1, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.addWorld, 2, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.addNotes, 3, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.numChapters, 4, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.numScenes, 5, 1, 1, 1, Qt.AlignRight)
self.addBox.setVerticalSpacing(fS)
self.addBox.setHorizontalSpacing(cM)
self.addBox.setContentsMargins(cM, 0, cM, 0)
self.addBox.setColumnStretch(2, 1)
for i in range(6):
self.addBox.setRowMinimumHeight(i, mH)
# Wizard Fields # Wizard Fields
self.registerField("addPlot", self.addPlot) self.registerField("addPlot", self.addPlot)
self.registerField("addChar", self.addChar) self.registerField("addChar", self.addChar)
self.registerField("addWorld", self.addWorld) self.registerField("addWorld", self.addWorld)
self.registerField("addTime", self.addTime) self.registerField("addNotes", self.addNotes)
self.registerField("addObject", self.addObject)
self.registerField("addEntity", self.addEntity)
self.registerField("numChapters", self.numChapters) self.registerField("numChapters", self.numChapters)
self.registerField("numScenes", self.numScenes) self.registerField("numScenes", self.numScenes)
self.registerField("chFolders", self.chFolders)
# Assemble # Assemble
self.innerBox = QHBoxLayout()
self.innerBox.addWidget(self.rootGroup)
self.innerBox.addWidget(self.novelGroup)
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(vS) self.outerBox.setSpacing(cM)
self.outerBox.addWidget(self.theText) self.outerBox.addWidget(self.theText)
self.outerBox.addLayout(self.innerBox) self.outerBox.addLayout(self.addBox)
self.outerBox.addStretch(1) self.outerBox.addStretch(1)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
@@ -441,15 +402,8 @@ class ProjWizardFinalPage(QWizardPage):
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.theWizard = theWizard self.theWizard = theWizard
self.setTitle(self.tr("Finished")) self.setTitle(self.tr("Summary"))
self.theText = QLabel( self.theText = QLabel("")
"<p>%s</p><p>%s</p>" % (
self.tr("All done."),
self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)
)
)
self.theText.setWordWrap(True) self.theText.setWordWrap(True)
# Assemble # Assemble
@@ -461,4 +415,51 @@ class ProjWizardFinalPage(QWizardPage):
return return
def initializePage(self):
"""Update the summary information on the final page.
"""
QWizardPage.initializePage(self)
sumList = []
sumList.append(self.tr("Project Name: {0}").format(self.field("projName")))
sumList.append(self.tr("Project Path: {0}").format(self.field("projPath")))
if self.field("popMinimal"):
sumList.append(self.tr("Fill the project with a minimal set of items"))
elif self.field("popSample"):
sumList.append(self.tr("Fill the project with example files"))
elif self.field("popCustom"):
if self.field("addPlot"):
sumList.append(self.tr("Add a folder for plot notes"))
if self.field("addChar"):
sumList.append(self.tr("Add a folder for character notes"))
if self.field("addWorld"):
sumList.append(self.tr("Add a folder for location notes"))
if self.field("addNotes"):
sumList.append(self.tr("Add example notes to the above"))
if self.field("numChapters") > 0:
sumList.append(self.tr("Add {0} chapters to the novel folder").format(
self.field("numChapters")
))
if self.field("numScenes") > 0:
sumList.append(self.tr("Add {0} scenes to each chapter").format(
self.field("numScenes")
))
else:
if self.field("numScenes") > 0:
sumList.append(self.tr("Add {0} scenes").format(
self.field("numScenes")
))
self.theText.setText(
"<p>%s</p><p>&nbsp;&bull;&nbsp;%s</p><p>%s</p>" % (
self.tr("You have selected the following:"),
"<br>&nbsp;&bull;&nbsp;".join(sumList),
self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)
)
)
return
# END Class ProjWizardFinalPage # END Class ProjWizardFinalPage
+4 -6
View File
@@ -7,13 +7,13 @@
@char: John @char: John
@location: Earth @location: Earth
A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree. A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree. The scene document can be sorted after the chapter document, or as a child of the chapter. Both result in the same output in the end, so it is a matter of preference.
Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, _italic_ and **_bold italic_**. You can also ~~strike through~~ text. There is **some support for _nested_ emphasis**, but there are some known limitations. If the syntax highlighter doesnt show it correctly, the export tool will not either. Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, _italic_ and **_bold italic_**. You can also ~~strike through~~ text. There is **some support for _nested_ emphasis**, but there are some known limitations. If the syntax highlighter doesnt show it correctly, the export tool will not either.
In addition, the editor supports automatic formatting of “quotes”, both double and single. Depending on the syntax highlighter, these can be in different colours. “You can of course use **bold** and _italic_ text inside of quotes too.” In addition, the editor supports automatic formatting of “quotes”, both double and single. Depending on the syntax highlighter settings and colour theme, these can be in different colours. “You can of course use **bold** and _italic_ text inside of quotes too.”
If you have the need for it, you can also add text that can be automatically replaced by other text when you generate a preview or export the project. Now, lets auto-replace this A with <A>, and this C with <C>. While <E> is just <E>. Press Ctrl+R to see what this looks like in the view pane. If you have the need for it, you can also add text that can be automatically replaced by other text when you generate a preview or export the project. Now, lets auto-replace this A with <A>, and this C with <C>. While <E> is just <E>. Press Ctrl+R to see what this looks like in the view pane. The list of auto-replaced text is sett in Project Settings.
The editor also supports non breaking spaces, and the spell checker accepts long dashes—like this—as valid word separators. Regular dashes are also supported and can be automatically inserted when typing two hyphens. The editor also supports non breaking spaces, and the spell checker accepts long dashes—like this—as valid word separators. Regular dashes are also supported and can be automatically inserted when typing two hyphens.
@@ -25,7 +25,7 @@ If you need to split a scene file up into further pieces, you can do so with the
Both scene and section titles can be left out of the final exported document. The formatting of titles can be selected from the Build Novel Project dialog. You can also have them replaced with scene separators like “* * *”. Both scene and section titles can be left out of the final exported document. The formatting of titles can be selected from the Build Novel Project dialog. You can also have them replaced with scene separators like “* * *”.
#### Text Alignment #### Text Alignment and Indentation
The text by default will have the left or justified alignment in the main text files in your project. You can also specify alignment for a specific paragraph by “pushing” it away from an edge with a set of >> or << symbols, like so: The text by default will have the left or justified alignment in the main text files in your project. You can also specify alignment for a specific paragraph by “pushing” it away from an edge with a set of >> or << symbols, like so:
@@ -35,8 +35,6 @@ This text is left-aligned. <<
>> This text is centred. << >> This text is centred. <<
#### Text Indent
You can indent a paragraph from both the left and right margin with > and < symbols. You can indent a paragraph from both the left and right margin with > and < symbols.
> This paragraph is indented from both the left margin and the right margin. This is useful for when you want to quote a large chunk of text for instance. < > This paragraph is indented from both the left margin and the right margin. This is useful for when you want to quote a large chunk of text for instance. <
+3 -3
View File
@@ -1,11 +1,11 @@
%%~name: Chapter One %%~name: Chapter One
%%~path: e7ded148d6e4a/6a2d6d5f4f401 %%~path: 7031beac91f75/6a2d6d5f4f401
%%~kind: NOVEL/DOCUMENT %%~kind: NOVEL/DOCUMENT
## So it Begins ## So it Begins
@pov: Jane @pov: Jane
@location: Earth @location: Earth
% Synopsis: We can add a chapter file, but keep the scene files separate. In the chapter file we can set the meta data that applies to the whole chapter if we wish to. % Synopsis: We can add a chapter document, but keep the scene files separate. In the chapter document we can set the meta data that applies to the whole chapter if we wish to. You can add the scenes as child documents directly under the chapter.
A chapter can also contain leading text before the first scene. A chapter can contain leading text before the first scene, like this piece of text.
+2 -2
View File
@@ -1,6 +1,6 @@
%%~name: Old File %%~name: Old File
%%~path: ae9bf3c3ea159/8a5deb88c0e97 %%~path: ae9bf3c3ea159/8a5deb88c0e97
%%~kind: NOVEL/DOCUMENT %%~kind: ARCHIVE/DOCUMENT
### Discarded Scene ### Discarded Scene
If you have files you no longer want in your main project, you can move them to the “Archive” folder. This is equivalent to turning off the “Include when building project” switch, just that you also put the file away, although the switch can be ignored when building the project, this folder cannot. If you have files you no longer want in your main project, you can move them to the “Archive” folder. This is equivalent to turning off the “Include when building project” switch, just that you also put the file away.
+5 -3
View File
@@ -1,11 +1,11 @@
%%~name: A Note on Structure %%~name: A Note on Structure
%%~path: e7ded148d6e4a/96b68994dfa3d %%~path: 7031beac91f75/96b68994dfa3d
%%~kind: NOVEL/NOTE %%~kind: NOVEL/NOTE
# A Note on Structure # A Note on Structure
This file is just a note. You can save notes anywhere you like in the project tree. Notes can be filtered out when you export the project. This file is just a note. You can save notes anywhere you like in the project tree. Notes can be filtered out when you export the project.
In root folders that isnt the Novel root folder, you can _only_ add notes. In the Novel root folder, you can choose between a number of layouts. Some of them are just to let yourself know what each file is for. In root folders that arent the Novel or Archive root folders, you can _only_ add notes. In the Novel and Archive folder you can also add Project Documents, which are the documents that make up your actual story.
## Headers in Notes ## Headers in Notes
@@ -15,8 +15,10 @@ Unlike in novel files, headers in notes have no particular meaning other than vi
The folders in the tree view have no structural meaning other than theyre a way for you to organise your files into groups in whatever way suits you. They are not intended to represent chapters, but you can of course use them for that. If you do, know that you still need to define chapter headers in your structure so novelWriter knows where you want them. The folders in the tree view have no structural meaning other than theyre a way for you to organise your files into groups in whatever way suits you. They are not intended to represent chapters, but you can of course use them for that. If you do, know that you still need to define chapter headers in your structure so novelWriter knows where you want them.
If you do have separate chapter documents, you can always add scene documents as child document of the chapter instead of using folders.
## Linking Files and Notes ## Linking Files and Notes
You can link files and notes together by assigning tags to them, and then reference them from other files. The file class of a file determines which reference keywords apply to each file. For instance a file in the Characters root folder can be referenced using either the @char keyword or the @pov keyword. You can link project documents and notes together by assigning tags to the notes with the @tag keyword, and then reference them from other files using one of the many reference keywords. The file class of a file determines which reference keywords apply to each file. For instance a file in the Characters root folder can be referenced using either the @char keyword or the @pov keyword.
If you want to see the content of the file the reference points to, you can click Ctrl+Enter with the cursor on top of the reference, and the view pane will show you the file. In the view pane, all references are clickable, so you can navigate further. At the bottom of the view pane, a list of files referencing the one your viewing will appear. This panel updates when you navigate, unless you make it sticky by clicking the sticky checkbox. If you want to see the content of the file the reference points to, you can click Ctrl+Enter with the cursor on top of the reference, and the view pane will show you the file. In the view pane, all references are clickable, so you can navigate further. At the bottom of the view pane, a list of files referencing the one your viewing will appear. This panel updates when you navigate, unless you make it sticky by clicking the sticky checkbox.
+1 -1
View File
@@ -6,4 +6,4 @@
This is a plain page with some text on it. This is a plain page with some text on it.
If you want the text to start on a fresh page, add the [NEW PAGE] code above the text. You can also add empty paragraphs with the [VSPACE] code. If you want the text to start on a fresh page, add the [NEW PAGE] code above the text. You can also add empty paragraphs with the [VSPACE] code. The above code adds two empty paragraphs before the text starts.
+2 -2
View File
@@ -1,9 +1,9 @@
%%~name: Interlude %%~name: Interlude
%%~path: e7ded148d6e4a/ba8a28a246524 %%~path: 7031beac91f75/ba8a28a246524
%%~kind: NOVEL/DOCUMENT %%~kind: NOVEL/DOCUMENT
##! Interlude ##! Interlude
% Notice that this is a file with the flag N.Un. The N means its a novel file, and the Un means its an unnumbered chapter. Unnumbered chapters can be treated separately from numbered chapters during export. Perfect for when you want to add an interlude, or for a prologue or epilogue. % Notice that this document has a title with a ! in it in addition to the two hash symbols. This means it is an unnumbered chapter. Unnumbered chapters can be treated separately from numbered chapters during export. Perfect for when you want to add an interlude, or for a prologue or epilogue.
I am the very model of a modern Major-General I am the very model of a modern Major-General
I've information vegetable, animal, and mineral I've information vegetable, animal, and mineral
+3 -3
View File
@@ -7,9 +7,9 @@
@focus: Jane @focus: Jane
@location: Earth @location: Earth
Adding more scenes to a chapter is as easy as adding more scene files, with a level three heading, or just adding another level three heading in the same file if that works for the way you want to structure your files. Adding more scenes to a chapter is as easy as adding more scene files with a level three heading. You can of course also just add another level three heading in the same file if that works for the way you want to structure your files.
In fact, if you wish, you can add all the scenes in the chapter file too. All novelWriter cares about is the level of the headings. In fact, if you wish, you can add all the scenes in the chapter file too. All novelWriter cares about is the level of the headings and the order in which they appear.
### More Scenes ### More Scenes
@@ -17,4 +17,4 @@ In fact, if you wish, you can add all the scenes in the chapter file too. All no
@focus: John @focus: John
@location: Earth @location: Earth
This is a second scene in the same file as the previous scene. You can always split the files up later. This is a second scene in the same file as the previous scene. You can always split the files up later using the split tool.
+21 -25
View File
@@ -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 19:31:12"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-17 18:36:56">
<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>1327</saveCount> <saveCount>1329</saveCount>
<autoCount>207</autoCount> <autoCount>220</autoCount>
<editTime>66285</editTime> <editTime>67104</editTime>
</project> </project>
<settings> <settings>
<doBackup>False</doBackup> <doBackup>False</doBackup>
@@ -17,9 +17,9 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>636b6aa9b697b</lastEdited> <lastEdited>636b6aa9b697b</lastEdited>
<lastViewed>636b6aa9b697b</lastViewed> <lastViewed>636b6aa9b697b</lastViewed>
<lastWordCount>1206</lastWordCount> <lastWordCount>1303</lastWordCount>
<novelWordCount>830</novelWordCount> <novelWordCount>894</novelWordCount>
<notesWordCount>376</notesWordCount> <notesWordCount>409</notesWordCount>
<autoReplace> <autoReplace>
<entry key="A">B</entry> <entry key="A">B</entry>
<entry key="B">E</entry> <entry key="B">E</entry>
@@ -36,7 +36,7 @@
<entry key="sf12341" count="5" red="100" green="100" blue="100">New</entry> <entry key="sf12341" count="5" red="100" green="100" blue="100">New</entry>
<entry key="sf24ce6" count="1" red="200" green="50" blue="0">Notes</entry> <entry key="sf24ce6" count="1" red="200" green="50" blue="0">Notes</entry>
<entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry> <entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry>
<entry key="s90e6c9" count="6" red="193" green="129" blue="0">1st Draft</entry> <entry key="s90e6c9" count="5" red="193" green="129" blue="0">1st Draft</entry>
<entry key="sd51c5b" count="1" red="193" green="129" blue="0">2nd Draft</entry> <entry key="sd51c5b" count="1" red="193" green="129" blue="0">2nd Draft</entry>
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry> <entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
<entry key="s78ea90" count="0" red="58" green="180" blue="58">Finished</entry> <entry key="s78ea90" count="0" red="58" green="180" blue="58">Finished</entry>
@@ -48,7 +48,7 @@
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry> <entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
</importance> </importance>
</settings> </settings>
<content count="25"> <content count="24">
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL"> <item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
<meta expanded="True"/> <meta expanded="True"/>
<name status="sc24b8f" import="ia857f0">Novel</name> <name status="sc24b8f" import="ia857f0">Novel</name>
@@ -58,38 +58,34 @@
<name status="sc24b8f" import="ia857f0" exported="True">Title Page</name> <name status="sc24b8f" import="ia857f0" exported="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="186" wordCount="39" paraCount="2" cursorPos="212"/> <meta expanded="False" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
<name status="sf12341" import="ia857f0" exported="True">Page</name> <name status="sf12341" import="ia857f0" exported="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="33"/> <meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
<name status="sf12341" import="ia857f0" exported="True">Part One</name> <name status="sf12341" import="ia857f0" exported="True">Part One</name>
</item> </item>
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="7031beac91f75" order="3" type="FOLDER" class="NOVEL"> <item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="True"/> <meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
<name status="s90e6c9" import="ia857f0">A Folder</name>
</item>
<item handle="6a2d6d5f4f401" parent="e7ded148d6e4a" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="True" charCount="75" wordCount="14" paraCount="1" cursorPos="279"/>
<name status="sf24ce6" import="ia857f0" exported="True">Chapter One</name> <name status="sf24ce6" import="ia857f0" exported="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="2429" wordCount="432" paraCount="14" cursorPos="1479"/> <meta expanded="False" charCount="2687" wordCount="479" paraCount="14" cursorPos="61"/>
<name status="s90e6c9" import="ia857f0" exported="True">Making a Scene</name> <name status="s90e6c9" import="ia857f0" exported="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="476" wordCount="93" paraCount="3" cursorPos="577"/> <meta expanded="False" charCount="548" wordCount="108" paraCount="3" cursorPos="649"/>
<name status="s90e6c9" import="ia857f0" exported="True">Another Scene</name> <name status="s90e6c9" import="ia857f0" exported="True">Another Scene</name>
</item> </item>
<item handle="ba8a28a246524" parent="e7ded148d6e4a" root="7031beac91f75" order="1" 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="4"/> <meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
<name status="sf12341" import="ia857f0" exported="True">Interlude</name> <name status="sf12341" import="ia857f0" exported="True">Interlude</name>
</item> </item>
<item handle="96b68994dfa3d" parent="e7ded148d6e4a" root="7031beac91f75" order="2" 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="1692" wordCount="313" paraCount="6" cursorPos="1110"/> <meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
<name status="sd51c5b" import="ia857f0" exported="False">A Note on Structure</name> <name status="sd51c5b" import="ia857f0" exported="False">A Note on Structure</name>
</item> </item>
<item handle="88706ddc78b1b" parent="e7ded148d6e4a" root="7031beac91f75" order="3" 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="343"/> <meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
<name status="s90e6c9" import="ia857f0" exported="True">Chapter Two</name> <name status="s90e6c9" import="ia857f0" exported="True">Chapter Two</name>
</item> </item>
@@ -138,7 +134,7 @@
<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="314" wordCount="55" paraCount="1" cursorPos="322"/> <meta expanded="False" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
<name status="s90e6c9" import="ia857f0" exported="True">Old File</name> <name status="s90e6c9" import="ia857f0" exported="True">Old File</name>
</item> </item>
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH"> <item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH">
+1
View File
@@ -4,5 +4,6 @@
<comment>novelWriter Project</comment> <comment>novelWriter Project</comment>
<sub-class-of type="application/xml"/> <sub-class-of type="application/xml"/>
<glob pattern="*.nwx"/> <glob pattern="*.nwx"/>
<icon name="application-x-novelwriter-project"/>
</mime-type> </mime-type>
</mime-info> </mime-info>
@@ -1,5 +1,5 @@
<?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:25"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:49:23">
<project> <project>
<name>Test Custom</name> <name>Test Custom</name>
<title>Test Novel</title> <title>Test Novel</title>
@@ -29,110 +29,106 @@
<section></section> <section></section>
</titleFormat> </titleFormat>
<status> <status>
<entry key="s000008" count="17" red="100" green="100" blue="100">New</entry> <entry key="s000008" count="15" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry> <entry key="s000009" 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="s00000a" 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="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status> </status>
<importance> <importance>
<entry key="i00000c" count="6" red="100" green="100" blue="100">New</entry> <entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry> <entry key="i00000d" 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="i00000e" 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="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance> </importance>
</settings> </settings>
<content count="23"> <content count="22">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL"> <item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name> <name status="s000008" import="i00000c">Novel</name>
</item> </item>
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT"> <item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ENTITY">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Entities</name>
</item>
<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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Title Page</name> <name status="s000008" import="i00000c" exported="True">Title Page</name>
</item> </item>
<item handle="0000000000018" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL"> <item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Chapter 1</name>
</item>
<item handle="0000000000019" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Chapter 1</name> <name status="s000008" import="i00000c" exported="True">Chapter 1</name>
</item> </item>
<item handle="000000000001a" parent="0000000000018" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1.1</name> <name status="s000008" import="i00000c" exported="True">Scene 1.1</name>
</item> </item>
<item handle="000000000001b" parent="0000000000018" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1.2</name> <name status="s000008" import="i00000c" exported="True">Scene 1.2</name>
</item> </item>
<item handle="000000000001c" parent="0000000000018" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1.3</name> <name status="s000008" import="i00000c" exported="True">Scene 1.3</name>
</item> </item>
<item handle="000000000001d" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL"> <item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Chapter 2</name>
</item>
<item handle="000000000001e" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Chapter 2</name> <name status="s000008" import="i00000c" exported="True">Chapter 2</name>
</item> </item>
<item handle="000000000001f" parent="000000000001d" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2.1</name> <name status="s000008" import="i00000c" exported="True">Scene 2.1</name>
</item> </item>
<item handle="0000000000020" parent="000000000001d" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2.2</name> <name status="s000008" import="i00000c" exported="True">Scene 2.2</name>
</item> </item>
<item handle="0000000000021" parent="000000000001d" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2.3</name> <name status="s000008" import="i00000c" exported="True">Scene 2.3</name>
</item> </item>
<item handle="0000000000022" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL"> <item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Chapter 3</name>
</item>
<item handle="0000000000023" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Chapter 3</name> <name status="s000008" import="i00000c" exported="True">Chapter 3</name>
</item> </item>
<item handle="0000000000024" parent="0000000000022" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3.1</name> <name status="s000008" import="i00000c" exported="True">Scene 3.1</name>
</item> </item>
<item handle="0000000000025" parent="0000000000022" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3.2</name> <name status="s000008" import="i00000c" exported="True">Scene 3.2</name>
</item> </item>
<item handle="0000000000026" parent="0000000000022" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3.3</name> <name status="s000008" import="i00000c" exported="True">Scene 3.3</name>
</item> </item>
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<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"/>
<name status="s000008" import="i00000c" exported="True">Main Plot</name>
</item>
<item handle="0000000000020" parent="None" root="0000000000020" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<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"/>
<name status="s000008" import="i00000c" exported="True">Protagonist</name>
</item>
<item handle="0000000000022" parent="None" root="0000000000022" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<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"/>
<name status="s000008" import="i00000c" exported="True">Main Location</name>
</item>
<item handle="0000000000024" parent="None" root="0000000000024" order="0" type="ROOT" class="ARCHIVE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Archive</name>
</item>
<item handle="0000000000025" parent="None" root="0000000000025" order="0" type="ROOT" class="TRASH">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Trash</name>
</item>
</content> </content>
</novelWriterXML> </novelWriterXML>
@@ -1,5 +1,5 @@
<?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:25"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:48:40">
<project> <project>
<name>Test Custom</name> <name>Test Custom</name>
<title>Test Novel</title> <title>Test Novel</title>
@@ -29,74 +29,82 @@
<section></section> <section></section>
</titleFormat> </titleFormat>
<status> <status>
<entry key="s000008" count="8" red="100" green="100" blue="100">New</entry> <entry key="s000008" count="9" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry> <entry key="s000009" 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="s00000a" 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="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status> </status>
<importance> <importance>
<entry key="i00000c" count="6" red="100" green="100" blue="100">New</entry> <entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry> <entry key="i00000d" 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="i00000e" 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="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance> </importance>
</settings> </settings>
<content count="14"> <content count="16">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL"> <item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name> <name status="s000008" import="i00000c">Novel</name>
</item> </item>
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT"> <item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ENTITY">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Entities</name>
</item>
<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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Title Page</name> <name status="s000008" import="i00000c" exported="True">Title Page</name>
</item> </item>
<item handle="0000000000018" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1</name> <name status="s000008" import="i00000c" exported="True">Scene 1</name>
</item> </item>
<item handle="0000000000019" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2</name> <name status="s000008" import="i00000c" exported="True">Scene 2</name>
</item> </item>
<item handle="000000000001a" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3</name> <name status="s000008" import="i00000c" exported="True">Scene 3</name>
</item> </item>
<item handle="000000000001b" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 4</name> <name status="s000008" import="i00000c" exported="True">Scene 4</name>
</item> </item>
<item handle="000000000001c" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 5</name> <name status="s000008" import="i00000c" exported="True">Scene 5</name>
</item> </item>
<item handle="000000000001d" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 6</name> <name status="s000008" import="i00000c" exported="True">Scene 6</name>
</item> </item>
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<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"/>
<name status="s000008" import="i00000c" exported="True">Main Plot</name>
</item>
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<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"/>
<name status="s000008" import="i00000c" exported="True">Protagonist</name>
</item>
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<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"/>
<name status="s000008" import="i00000c" exported="True">Main Location</name>
</item>
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="ARCHIVE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Archive</name>
</item>
<item handle="000000000001f" parent="None" root="000000000001f" order="0" type="ROOT" class="TRASH">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Trash</name>
</item>
</content> </content>
</novelWriterXML> </novelWriterXML>
@@ -1,8 +1,9 @@
<?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:25"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:08:21">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title></title> <title>New Novel</title>
<author>Jane Doe</author>
<saveCount>2</saveCount> <saveCount>2</saveCount>
<autoCount>1</autoCount> <autoCount>1</autoCount>
<editTime>0</editTime> <editTime>0</editTime>
@@ -1,5 +1,5 @@
<?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:25"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:50:26">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title></title> <title></title>
@@ -44,33 +44,33 @@
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name> <name status="s000008" import="i00000c">Novel</name>
</item> </item>
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT"> <item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">World</name>
</item>
<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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Title Page</name> <name status="s000008" import="i00000c" exported="True">Title Page</name>
</item> </item>
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL"> <item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">New Chapter</name>
</item>
<item handle="0000000000016" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">New Chapter</name> <name status="s000008" import="i00000c" exported="True">New Chapter</name>
</item> </item>
<item handle="0000000000017" parent="0000000000015" 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" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">New Scene</name> <name status="s000008" import="i00000c" exported="True">New Scene</name>
</item> </item>
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="ARCHIVE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Archive</name>
</item>
</content> </content>
</novelWriterXML> </novelWriterXML>
@@ -1,8 +1,9 @@
<?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:25"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 22:07:28">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title></title> <title>New Novel</title>
<author>Jane Doe</author>
<saveCount>2</saveCount> <saveCount>2</saveCount>
<autoCount>1</autoCount> <autoCount>1</autoCount>
<editTime>0</editTime> <editTime>0</editTime>
@@ -82,11 +83,11 @@
</item> </item>
<item handle="000000000002a" parent="None" root="000000000002a" order="0" type="ROOT" class="CHARACTER"> <item handle="000000000002a" parent="None" root="000000000002a" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Character</name> <name status="s000008" import="i00000c">Characters</name>
</item> </item>
<item handle="000000000002b" parent="None" root="000000000002b" order="0" type="ROOT" class="WORLD"> <item handle="000000000002b" parent="None" root="000000000002b" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">World</name> <name status="s000008" import="i00000c">Locations</name>
</item> </item>
<item handle="000000000002c" parent="None" root="000000000002c" order="0" type="ROOT" class="TIMELINE"> <item handle="000000000002c" parent="None" root="000000000002c" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/> <meta expanded="False"/>
@@ -94,15 +95,15 @@
</item> </item>
<item handle="000000000002d" parent="None" root="000000000002d" order="0" type="ROOT" class="OBJECT"> <item handle="000000000002d" parent="None" root="000000000002d" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Object</name> <name status="s000008" import="i00000c">Objects</name>
</item> </item>
<item handle="000000000002e" parent="None" root="000000000002e" order="0" type="ROOT" class="CUSTOM"> <item handle="000000000002e" parent="None" root="000000000002e" order="0" type="ROOT" class="CUSTOM">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Custom1</name> <name status="s000008" import="i00000c">Custom</name>
</item> </item>
<item handle="000000000002f" parent="None" root="000000000002f" order="0" type="ROOT" class="CUSTOM"> <item handle="000000000002f" parent="None" root="000000000002f" order="0" type="ROOT" class="CUSTOM">
<meta expanded="False"/> <meta expanded="False"/>
<name status="s000008" import="i00000c">Custom2</name> <name status="s000008" import="i00000c">Custom</name>
</item> </item>
</content> </content>
</novelWriterXML> </novelWriterXML>
@@ -1,9 +1,10 @@
<?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 16:55:50"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:27:16">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title></title> <title>New Novel</title>
<saveCount>5</saveCount> <author>Jane Doe</author>
<saveCount>4</saveCount>
<autoCount>2</autoCount> <autoCount>2</autoCount>
<editTime>3</editTime> <editTime>3</editTime>
</project> </project>
@@ -15,8 +16,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>000000000000f</lastEdited> <lastEdited>000000000000f</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastWordCount>126</lastWordCount> <lastWordCount>129</lastWordCount>
<novelWordCount>99</novelWordCount> <novelWordCount>102</novelWordCount>
<notesWordCount>27</notesWordCount> <notesWordCount>27</notesWordCount>
<autoReplace/> <autoReplace/>
<titleFormat> <titleFormat>
@@ -45,7 +46,7 @@
<name status="s000000" import="i000004">Novel</name> <name status="s000000" import="i000004">Novel</name>
</item> </item>
<item handle="000000000000c" parent="0000000000008" root="0000000000008" 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="11" wordCount="2" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
<name status="s000000" import="i000004" exported="True">Title Page</name> <name status="s000000" import="i000004" exported="True">Title Page</name>
</item> </item>
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL"> <item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
@@ -1,9 +1,10 @@
<?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:28:44"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:25:41">
<project> <project>
<name>New Project</name> <name>New Project</name>
<title></title> <title>New Novel</title>
<saveCount>3</saveCount> <author>Jane Doe</author>
<saveCount>2</saveCount>
<autoCount>1</autoCount> <autoCount>1</autoCount>
<editTime>0</editTime> <editTime>0</editTime>
</project> </project>
@@ -15,8 +16,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastWordCount>6</lastWordCount> <lastWordCount>9</lastWordCount>
<novelWordCount>6</novelWordCount> <novelWordCount>9</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
<autoReplace/> <autoReplace/>
<titleFormat> <titleFormat>
@@ -45,7 +46,7 @@
<name status="s000000" import="i000004">Novel</name> <name status="s000000" import="i000004">Novel</name>
</item> </item>
<item handle="000000000000c" parent="0000000000008" root="0000000000008" 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="11" wordCount="2" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
<name status="s000000" import="i000004" exported="True">Title Page</name> <name status="s000000" import="i000004" exported="True">Title Page</name>
</item> </item>
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL"> <item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
@@ -1,11 +1,11 @@
<?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:28:41"> <novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:31:17">
<project> <project>
<name>Project Name</name> <name>Project Name</name>
<title>Project Title</title> <title>Project Title</title>
<author>Jane Doe</author> <author>Jane Doe</author>
<author>John Doh</author> <author>John Doh</author>
<saveCount>2</saveCount> <saveCount>1</saveCount>
<autoCount>1</autoCount> <autoCount>1</autoCount>
<editTime>0</editTime> <editTime>0</editTime>
</project> </project>
@@ -17,8 +17,8 @@
<autoOutline>True</autoOutline> <autoOutline>True</autoOutline>
<lastEdited>None</lastEdited> <lastEdited>None</lastEdited>
<lastViewed>None</lastViewed> <lastViewed>None</lastViewed>
<lastWordCount>6</lastWordCount> <lastWordCount>9</lastWordCount>
<novelWordCount>6</novelWordCount> <novelWordCount>9</novelWordCount>
<notesWordCount>0</notesWordCount> <notesWordCount>0</notesWordCount>
<autoReplace> <autoReplace>
<entry key="A">B</entry> <entry key="A">B</entry>
@@ -51,7 +51,7 @@
<name status="s000000" import="i000004">Novel</name> <name status="s000000" import="i000004">Novel</name>
</item> </item>
<item handle="000000000000c" parent="0000000000008" root="0000000000008" 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="11" wordCount="2" paraCount="0" cursorPos="0"/> <meta expanded="False" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
<name status="s000000" import="i000004" exported="True">Title Page</name> <name status="s000000" import="i000004" exported="True">Title Page</name>
</item> </item>
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL"> <item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
+1 -1
View File
@@ -280,7 +280,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex.scanText(xHandle, "Hello World!") is False assert theIndex.scanText(xHandle, "Hello World!") is False
# Create the archive root # Create the archive root
aHandle = theProject.newRoot("Archive", nwItemClass.ARCHIVE) aHandle = theProject.newRoot(nwItemClass.ARCHIVE)
assert theProject.projTree[aHandle] is not None assert theProject.projTree[aHandle] is not None
xItem.setParent(aHandle) xItem.setParent(aHandle)
theProject.projTree.updateItemData(xItem.itemHandle) theProject.projTree.updateItemData(xItem.itemHandle)
+23 -33
View File
@@ -26,7 +26,7 @@ from shutil import copyfile
from zipfile import ZipFile from zipfile import ZipFile
from lxml import etree from lxml import etree
from tools import cmpFiles, writeFile, readFile from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
from mock import causeOSError from mock import causeOSError
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
@@ -67,7 +67,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.closeProject() is True assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False assert theProject.projChanged is False
# Open a second time # Open a second time
@@ -77,7 +77,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.closeProject() is True assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# END Test testCoreProject_NewMinimal # END Test testCoreProject_NewMinimal
@@ -103,13 +103,10 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
nwItemClass.PLOT, nwItemClass.PLOT,
nwItemClass.CHARACTER, nwItemClass.CHARACTER,
nwItemClass.WORLD, nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
], ],
"addNotes": True,
"numChapters": 3, "numChapters": 3,
"numScenes": 3, "numScenes": 3,
"chFolders": True,
} }
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
@@ -118,7 +115,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# END Test testCoreProject_NewCustomA # END Test testCoreProject_NewCustomA
@@ -144,13 +141,10 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
nwItemClass.PLOT, nwItemClass.PLOT,
nwItemClass.CHARACTER, nwItemClass.CHARACTER,
nwItemClass.WORLD, nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
], ],
"addNotes": True,
"numChapters": 0, "numChapters": 0,
"numScenes": 6, "numScenes": 6,
"chFolders": True,
} }
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
@@ -159,7 +153,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# END Test testCoreProject_NewCustomB # END Test testCoreProject_NewCustomB
@@ -258,28 +252,28 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx") compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
buildTestProject(theProject, fncDir)
assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) is True assert theProject.setProjectPath(fncDir) is True
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.closeProject() is True assert theProject.closeProject() is True
assert theProject.openProject(projFile) is True assert theProject.openProject(projFile) is True
assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), str) assert isinstance(theProject.newRoot(nwItemClass.NOVEL), str)
assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), str) assert isinstance(theProject.newRoot(nwItemClass.PLOT), str)
assert isinstance(theProject.newRoot("Character", nwItemClass.CHARACTER), str) assert isinstance(theProject.newRoot(nwItemClass.CHARACTER), str)
assert isinstance(theProject.newRoot("World", nwItemClass.WORLD), str) assert isinstance(theProject.newRoot(nwItemClass.WORLD), str)
assert isinstance(theProject.newRoot("Timeline", nwItemClass.TIMELINE), str) assert isinstance(theProject.newRoot(nwItemClass.TIMELINE), str)
assert isinstance(theProject.newRoot("Object", nwItemClass.OBJECT), str) assert isinstance(theProject.newRoot(nwItemClass.OBJECT), str)
assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str) assert isinstance(theProject.newRoot(nwItemClass.CUSTOM), str)
assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str) assert isinstance(theProject.newRoot(nwItemClass.CUSTOM), str)
assert theProject.projChanged is True assert theProject.projChanged is True
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.closeProject() is True assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False assert theProject.projChanged is False
# END Test testCoreProject_NewRoot # END Test testCoreProject_NewRoot
@@ -294,8 +288,8 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx") compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
buildTestProject(theProject, fncDir)
assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) is True assert theProject.setProjectPath(fncDir) is True
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.closeProject() is True assert theProject.closeProject() is True
@@ -308,7 +302,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False assert theProject.projChanged is False
# END Test testCoreProject_NewFile # END Test testCoreProject_NewFile
@@ -489,7 +483,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
assert theProject.saveProject() is True assert theProject.saveProject() is True
assert theProject.saveCount == saveCount + 1 assert theProject.saveCount == saveCount + 1
assert theProject.autoCount == autoCount assert theProject.autoCount == autoCount
assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# Check that a second save creates a .bak file # Check that a second save creates a .bak file
assert os.path.isfile(backFile) is True assert os.path.isfile(backFile) is True
@@ -500,7 +494,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
assert theProject.saveProject(autoSave=True) is True assert theProject.saveProject(autoSave=True) is True
assert theProject.saveCount == saveCount assert theProject.saveCount == saveCount
assert theProject.autoCount == autoCount + 1 assert theProject.autoCount == autoCount + 1
assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# Close test project # Close test project
assert theProject.closeProject() assert theProject.closeProject()
@@ -677,7 +671,7 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
"""Test the status and importance flag handling. """Test the status and importance flag handling.
""" """
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
assert theProject.newProject({"projPath": fncDir}) is True buildTestProject(theProject, fncDir)
statusKeys = ["s000008", "s000009", "s00000a", "s00000b"] statusKeys = ["s000008", "s000009", "s00000a", "s00000b"]
importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"] importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"]
@@ -798,7 +792,7 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
"""Test other project class methods and functions. """Test other project class methods and functions.
""" """
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
assert theProject.newProject({"projPath": fncDir}) is True buildTestProject(theProject, fncDir)
# Setting project path # Setting project path
assert theProject.setProjectPath(None) assert theProject.setProjectPath(None)
@@ -1178,10 +1172,6 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theProject.setProjectPath(fncDir) theProject.setProjectPath(fncDir)
# assert theProject.newProject({"projPath": fncDir})
# assert theProject.saveProject()
# assert theProject.closeProject()
# Check behaviour of deprecated files function on OSError # Check behaviour of deprecated files function on OSError
tstFile = os.path.join(fncDir, "ToC.json") tstFile = os.path.join(fncDir, "ToC.json")
writeFile(tstFile, "stuff") writeFile(tstFile, "stuff")
+2 -2
View File
@@ -23,7 +23,7 @@ import os
import pytest import pytest
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, readFile, writeFile from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
@@ -41,7 +41,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project # Create a new project
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
# Handles for new objects # Handles for new objects
hNovelRoot = "0000000000008" hNovelRoot = "0000000000008"
+2 -2
View File
@@ -23,7 +23,7 @@ import os
import pytest import pytest
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, readFile, writeFile from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
@@ -42,7 +42,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project # Create a new project
assert nwGUI.newProject({"projPath": fncProj}) is True buildTestProject(nwGUI, fncProj)
# Handles for new objects # Handles for new objects
hNovelRoot = "0000000000008" hNovelRoot = "0000000000008"
+5 -5
View File
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest import pytest
from tools import getGuiItem from tools import getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QDialog, QMessageBox from PyQt5.QtWidgets import QAction, QDialog, QMessageBox
@@ -48,7 +48,7 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
assert nwGUI.editItem() is False assert nwGUI.editItem() is False
# Create and Open Project # Create and Open Project
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f" tHandle = "000000000000f"
# No Selection # No Selection
@@ -99,7 +99,7 @@ def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document # Create Project and Open Document
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f" tHandle = "000000000000f"
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New" assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
@@ -156,7 +156,7 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document # Create Project and Open Document
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New" assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note" assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.theProject.importItems.name(importKeys[0]) == "New" assert nwGUI.theProject.importItems.name(importKeys[0]) == "New"
@@ -209,7 +209,7 @@ def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document # Create Project and Open Document
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
# Edit a Folder # Edit a Folder
itemEdit = GuiItemEditor(nwGUI, "000000000000d") itemEdit = GuiItemEditor(nwGUI, "000000000000d")
+4 -3
View File
@@ -23,7 +23,7 @@ import os
import pytest import pytest
from shutil import copyfile from shutil import copyfile
from tools import cmpFiles, getGuiItem from tools import cmpFiles, getGuiItem, buildTestProject
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
@@ -57,7 +57,7 @@ def testDlgProjSettings_Dialog(
assert getGuiItem("GuiProjectSettings") is None assert getGuiItem("GuiProjectSettings") is None
# Create new project # Create new project
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
nwGUI.mainConf.backupPath = fncDir nwGUI.mainConf.backupPath = fncDir
nwGUI.theProject.setSpellLang("en") nwGUI.theProject.setSpellLang("en")
@@ -81,7 +81,7 @@ def testDlgProjSettings_Dialog(
# ============ # ============
assert projEdit.tabMain.editName.text() == "New Project" assert projEdit.tabMain.editName.text() == "New Project"
assert projEdit.tabMain.editTitle.text() == "" assert projEdit.tabMain.editTitle.text() == "New Novel"
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith" assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
assert projEdit.tabMain.spellLang.currentData() == "en" assert projEdit.tabMain.spellLang.currentData() == "en"
assert projEdit.tabMain.doBackup.isChecked() is False assert projEdit.tabMain.doBackup.isChecked() is False
@@ -90,6 +90,7 @@ def testDlgProjSettings_Dialog(
projEdit.tabMain.editName.setText("") projEdit.tabMain.editName.setText("")
for c in "Project Name": for c in "Project Name":
qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay) qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay)
projEdit.tabMain.editTitle.setText("")
for c in "Project Title": for c in "Project Title":
qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay) qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay)
+97 -10
View File
@@ -23,7 +23,7 @@ import os
import pytest import pytest
from shutil import copyfile from shutil import copyfile
from tools import cmpFiles from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QDialog from PyQt5.QtWidgets import QMessageBox, QDialog
@@ -32,6 +32,7 @@ from novelwriter.gui import (
GuiDocEditor, GuiProjectTree, GuiNovelTree, GuiOutline GuiDocEditor, GuiProjectTree, GuiNovelTree, GuiOutline
) )
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.tools import GuiProjectWizard
from novelwriter.dialogs.itemeditor import GuiItemEditor from novelwriter.dialogs.itemeditor import GuiItemEditor
keyDelay = 2 keyDelay = 2
@@ -70,15 +71,52 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
# END Test testGuiMain_NoProject # END Test testGuiMain_NoProject
@pytest.mark.gui
def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
"""Test creating a new project.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# No data
with monkeypatch.context() as mp:
mp.setattr(GuiProjectWizard, "exec_", lambda *a: None)
assert nwGUI.newProject(projData=None) is False
# Close project
with monkeypatch.context() as mp:
nwGUI.hasProject = True
mp.setattr(QMessageBox, "question", lambda *a: QMessageBox.No)
assert nwGUI.newProject(projData={"projPath": fncProj}) is False
# No project path
assert nwGUI.newProject(projData={}) is False
# Project file already exists
projFile = os.path.join(fncProj, nwGUI.theProject.projFile)
writeFile(projFile, "Stuff")
assert nwGUI.newProject(projData={"projPath": fncProj}) is False
os.unlink(projFile)
# An unreachable path should also fail
projPath = os.path.join(fncProj, "stuff", "stuff", "stuff")
assert nwGUI.newProject(projData={"projPath": projPath}) is False
# This one should work just fine
assert nwGUI.newProject(projData={"projPath": fncProj}) is True
assert os.path.isfile(os.path.join(fncProj, nwGUI.theProject.projFile))
assert os.path.isdir(os.path.join(fncProj, "content"))
# END Test testGuiMain_NewProject
@pytest.mark.gui @pytest.mark.gui
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test handling of project tree items based on GUI focus states. """Test handling of project tree items based on GUI focus states.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
assert nwGUI.newProject({"projPath": fncProj}) is True buildTestProject(nwGUI, fncProj)
assert nwGUI.saveProject() is True
# assert False
sHandle = "000000000000f" sHandle = "000000000000f"
assert nwGUI.openSelectedItem() is False assert nwGUI.openSelectedItem() is False
@@ -139,7 +177,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
# Create new, save, close project # Create new, save, close project
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
assert nwGUI.saveProject() assert nwGUI.saveProject()
assert nwGUI.closeProject() assert nwGUI.closeProject()
@@ -160,7 +198,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
testFile = os.path.join(outDir, "guiEditor_Main_Initial_nwProject.nwx") testFile = os.path.join(outDir, "guiEditor_Main_Initial_nwProject.nwx")
compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx") compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx")
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
@@ -178,9 +216,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta") assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta")
assert nwGUI.theProject.projFile == "nwProject.nwx" assert nwGUI.theProject.projFile == "nwProject.nwx"
assert nwGUI.theProject.projName == "New Project" assert nwGUI.theProject.projName == "New Project"
assert nwGUI.theProject.bookTitle == "" assert nwGUI.theProject.bookTitle == "New Novel"
assert len(nwGUI.theProject.bookAuthors) == 0 assert len(nwGUI.theProject.bookAuthors) == 1
assert not nwGUI.theProject.spellCheck assert nwGUI.theProject.spellCheck is False
# Check that tree items have been created # Check that tree items have been created
assert nwGUI.treeView._getTreeItem("0000000000008") is not None assert nwGUI.treeView._getTreeItem("0000000000008") is not None
@@ -438,7 +476,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
testFile = os.path.join(outDir, "guiEditor_Main_Final_nwProject.nwx") testFile = os.path.join(outDir, "guiEditor_Main_Final_nwProject.nwx")
compFile = os.path.join(refDir, "guiEditor_Main_Final_nwProject.nwx") compFile = os.path.join(refDir, "guiEditor_Main_Final_nwProject.nwx")
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
projFile = os.path.join(fncProj, "content", "000000000000f.nwd") projFile = os.path.join(fncProj, "content", "000000000000f.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_000000000000f.nwd") testFile = os.path.join(outDir, "guiEditor_Main_Final_000000000000f.nwd")
@@ -467,3 +505,52 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
# END Test testGuiMain_Editing # END Test testGuiMain_Editing
@pytest.mark.gui
def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test toggling focus mode in main window.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
buildTestProject(nwGUI, fncProj)
assert nwGUI.isFocusMode is False
# Focus Mode
# ==========
# No document open, so not allowing focus mode
assert nwGUI.toggleFocusMode() is False
# Open a file in editor and viewer
assert nwGUI.openDocument("000000000000f")
assert nwGUI.viewDocument("000000000000f")
# Enable focus mode
assert nwGUI.toggleFocusMode() is True
assert nwGUI.treePane.isVisible() is False
assert nwGUI.statusBar.isVisible() is False
assert nwGUI.mainMenu.isVisible() is False
assert nwGUI.viewsBar.isVisible() is False
assert nwGUI.splitView.isVisible() is False
# Disable focus mode
assert nwGUI.toggleFocusMode() is True
assert nwGUI.treePane.isVisible() is True
assert nwGUI.statusBar.isVisible() is True
assert nwGUI.mainMenu.isVisible() is True
assert nwGUI.viewsBar.isVisible() is True
assert nwGUI.splitView.isVisible() is True
# Full Screen Mode
# ================
assert nwGUI.mainConf.isFullScreen is False
nwGUI.toggleFullScreenMode()
assert nwGUI.mainConf.isFullScreen is True
nwGUI.toggleFullScreenMode()
assert nwGUI.mainConf.isFullScreen is False
# qtbot.stopForInteraction()
# END Test testGuiMain_FocusFullMode
+2 -2
View File
@@ -26,7 +26,7 @@ from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextCursor, QTextBlock from PyQt5.QtGui import QTextCursor, QTextBlock
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
from tools import writeFile from tools import writeFile, buildTestProject
from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.enum import nwDocAction, nwDocInsert from novelwriter.enum import nwDocAction, nwDocInsert
@@ -465,7 +465,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
assert nwGUI.treeView._getTreeItem("000000000000f") is not None assert nwGUI.treeView._getTreeItem("000000000000f") is not None
assert nwGUI.openDocument("000000000000f") is True assert nwGUI.openDocument("000000000000f") is True
+5 -3
View File
@@ -22,6 +22,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest import pytest
import os import os
from tools import buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtWidgets import QAction, QMessageBox
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
@@ -47,7 +49,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True buildTestProject(nwGUI, prjDir)
# No itemType set # No itemType set
nwTree.clearSelection() nwTree.clearSelection()
@@ -155,7 +157,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True buildTestProject(nwGUI, prjDir)
# Move Documents # Move Documents
# ============== # ==============
@@ -283,7 +285,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True buildTestProject(nwGUI, prjDir)
# Try emptying the trash already now, when there is no trash folder # Try emptying the trash already now, when there is no trash folder
assert nwTree.emptyTrash() is False assert nwTree.emptyTrash() is False
+5 -3
View File
@@ -22,6 +22,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import time import time
import pytest import pytest
from tools import buildTestProject
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from novelwriter.core import NWDoc from novelwriter.core import NWDoc
@@ -34,7 +36,7 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
assert nwGUI.newProject({"projPath": fncProj}) is True buildTestProject(nwGUI, fncProj)
cHandle = nwGUI.theProject.newFile("A Note", "000000000000a") cHandle = nwGUI.theProject.newFile("A Note", "000000000000a")
newDoc = NWDoc(nwGUI.theProject, cHandle) newDoc = NWDoc(nwGUI.theProject, cHandle)
newDoc.writeDocument("# A Note\n\n") newDoc.writeDocument("# A Note\n\n")
@@ -89,10 +91,10 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Project Stats # Project Stats
nwGUI.statusBar.mainConf.incNotesWCount = False nwGUI.statusBar.mainConf.incNotesWCount = False
nwGUI._updateStatusWordCount() nwGUI._updateStatusWordCount()
assert nwGUI.statusBar.statsText.text() == "Words: 6 (+6)" assert nwGUI.statusBar.statsText.text() == "Words: 9 (+9)"
nwGUI.statusBar.mainConf.incNotesWCount = True nwGUI.statusBar.mainConf.incNotesWCount = True
nwGUI._updateStatusWordCount() nwGUI._updateStatusWordCount()
assert nwGUI.statusBar.statsText.text() == "Words: 8 (+8)" assert nwGUI.statusBar.statsText.text() == "Words: 11 (+11)"
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
+2 -2
View File
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest import pytest
from tools import getGuiItem from tools import getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtWidgets import QAction, QMessageBox
@@ -40,7 +40,7 @@ def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
assert getGuiItem("GuiLipsum") is None assert getGuiItem("GuiLipsum") is None
# Create a new project # Create a new project
assert nwGUI.newProject({"projPath": fncProj}) is True buildTestProject(nwGUI, fncProj)
assert nwGUI.openDocument("000000000000f") is True assert nwGUI.openDocument("000000000000f") is True
assert len(nwGUI.docEditor.getText()) == 15 assert len(nwGUI.docEditor.getText()) == 15
+4 -10
View File
@@ -205,14 +205,11 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
customPage.addPlot.setChecked(True) customPage.addPlot.setChecked(True)
customPage.addChar.setChecked(True) customPage.addChar.setChecked(True)
customPage.addWorld.setChecked(True) customPage.addWorld.setChecked(True)
customPage.addTime.setChecked(True) customPage.addNotes.setChecked(True)
customPage.addObject.setChecked(True)
customPage.addEntity.setChecked(True)
if prjType == "custom2": if prjType == "custom2":
customPage.numChapters.setValue(0) customPage.numChapters.setValue(0)
customPage.numScenes.setValue(10) customPage.numScenes.setValue(10)
customPage.chFolders.setChecked(False)
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
@@ -240,23 +237,20 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
nwItemClass.PLOT, nwItemClass.PLOT,
nwItemClass.CHARACTER, nwItemClass.CHARACTER,
nwItemClass.WORLD, nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
] ]
if prjType == "custom1": if prjType == "custom1":
assert projData["numChapters"] == 5 assert projData["numChapters"] == 5
assert projData["numScenes"] == 5 assert projData["numScenes"] == 5
assert projData["chFolders"] assert projData["addNotes"] is True
else: else:
assert projData["numChapters"] == 0 assert projData["numChapters"] == 0
assert projData["numScenes"] == 10 assert projData["numScenes"] == 10
assert not projData["chFolders"] assert projData["addNotes"] is True
else: else:
assert projData["addRoots"] == [] assert projData["addRoots"] == []
assert projData["numChapters"] == 0 assert projData["numChapters"] == 0
assert projData["numScenes"] == 0 assert projData["numScenes"] == 0
assert not projData["chFolders"] assert projData["addNotes"] is False
# Cleanup # Cleanup
nwWiz.reject() nwWiz.reject()
+2 -2
View File
@@ -23,8 +23,8 @@ import pytest
import json import json
import os import os
from tools import getGuiItem, writeFile
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, writeFile, buildTestProject
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
@@ -48,7 +48,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Create a project to work on # Create a project to work on
assert nwGUI.newProject({"projPath": fncProj}) buildTestProject(nwGUI, fncProj)
qtbot.wait(100) qtbot.wait(100)
assert nwGUI.saveProject() assert nwGUI.saveProject()
sessFile = os.path.join(fncProj, "meta", nwFiles.SESS_STATS) sessFile = os.path.join(fncProj, "meta", nwFiles.SESS_STATS)
+56
View File
@@ -20,10 +20,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import os import os
import time
import shutil import shutil
from PyQt5.QtWidgets import qApp from PyQt5.QtWidgets import qApp
XML_IGNORE = ("<novelWriterXML", "<saveCount", "<autoCount", "<editTime")
def cmpFiles(fileOne, fileTwo, ignoreLines=None, ignoreStart=None): def cmpFiles(fileOne, fileTwo, ignoreLines=None, ignoreStart=None):
"""Compare two files, but optionally ignore lines given by a list. """Compare two files, but optionally ignore lines given by a list.
@@ -119,3 +122,56 @@ def cleanProject(projPath):
os.unlink(tocFile) os.unlink(tocFile)
return return
def buildTestProject(theObject, projPath):
"""Build a standard test project in projPath using theProject
object as the parent.
"""
from novelwriter.enum import nwItemClass
from novelwriter.core import NWProject, NWDoc
if isinstance(theObject, NWProject):
theGUI = None
theProject = theObject
else:
theGUI = theObject
theProject = theObject.theProject
theProject.clearProject()
theProject.setProjectPath(projPath, newProject=True)
theProject.setProjectName("New Project")
theProject.setBookTitle("New Novel")
theProject.setBookAuthors("Jane Doe")
# Creating a minimal project with a few root folders and a
# single chapter folder with a single file.
xHandle = {}
xHandle[1] = theProject.newRoot(nwItemClass.NOVEL, "Novel")
xHandle[2] = theProject.newRoot(nwItemClass.PLOT, "Plot")
xHandle[3] = theProject.newRoot(nwItemClass.CHARACTER, "Characters")
xHandle[4] = theProject.newRoot(nwItemClass.WORLD, "World")
xHandle[5] = theProject.newFile("Title Page", xHandle[1])
xHandle[6] = theProject.newFolder("New Chapter", xHandle[1])
xHandle[7] = theProject.newFile("New Chapter", xHandle[6])
xHandle[8] = theProject.newFile("New Scene", xHandle[6])
aDoc = NWDoc(theProject, xHandle[5])
aDoc.writeDocument("#! New Novel\n\n>> By Jane DOe <<\n")
aDoc = NWDoc(theProject, xHandle[7])
aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter"))
aDoc = NWDoc(theProject, xHandle[8])
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
theProject.projOpened = time.time()
theProject.setProjectChanged(True)
theProject.saveProject(autoSave=True)
if theGUI is not None:
theGUI.hasProject = True
theGUI.rebuildTrees()
theGUI.rebuildIndex(beQuiet=True)
return