diff --git a/.gitignore b/.gitignore index 54fe2f2e..2905634a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ deploy/ # Documentation docs/build/ +docs/source/_* # Python Temp __pycache__ diff --git a/CHANGELOG.md b/CHANGELOG.md index 462dc91d..dfd9e8cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,16 @@ ## Version 0.7 [2020-xx-xx] +**Project Structure** + +* The project folder structure has been simplified and cleaned up. We also now pin the main entry values in the main XML file. the XML file is now given version 1.1, and locking it to only be opened by version 0.7 or later. The project is converted on first open, if the user approves. PR #253. + **Other Changes** * Dropped the usage of .bak copies of document files. This was the old method to ensure the document data was written successfully, but it uses twice the storage space. Instead, writing via a temp file is the safe way to save files. PR #248. -## Not Yet Released + +## Version 0.6.2 [2020-xx-xx] **Bugfixes** @@ -17,11 +22,14 @@ * Renamed the "Generate Preview" button on the "Build Novel Project" tool to "Build Novel Project". You must actually click this to be able to export or print. Issue #237, PR #238. * Added font family and font size selectors to the "Build Novel Project" tool. You may want a different print font than used in the editor itself. Issue #230, PR #238. +* Removed the "Help" feature in "Build Novel Project" and instead written detailed tooltips. Issue #250, PR #249. +* Changed the title formnatting codes for "Build Novel Project" to something less verbose. The old codes are translated automatically. Issue #247, PR #249. * A margin of the viewport (outside the document) has been added to the document editor and viewer to make room for the document title bar. Previously, the title bar would sit on top of the document top margin, which would sometimes hide text that would otherwise be visible. PR #236. * Fixed some alignment issue for the status icon on the project tree details panel. Mentioned in #235, PR #239. * Removed the `Xo` icon for NO_LAYOUT in the project tree details panel. Mentioned in #235, PR #239. * Added a Details tab to the Project Settings dialog, which also lists the project path. Issue #242, PR #239. + ## Version 0.6.1 [2020-05-25] **Bugfixes** diff --git a/docs/source/conf.py b/docs/source/conf.py index 26c6cdd6..06500213 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,7 +48,7 @@ templates_path = ["_templates"] # You can specify multiple suffix as a list of string: # # source_suffix = [".rst", ".md"] -source_suffix = ".txt" +source_suffix = ".rst" # The master toctree document. master_doc = "index" diff --git a/docs/source/export.txt b/docs/source/export.rst similarity index 67% rename from docs/source/export.txt rename to docs/source/export.rst index 0cb9d05b..5cf24cff 100644 --- a/docs/source/export.txt +++ b/docs/source/export.rst @@ -9,20 +9,20 @@ Header Formatting ***************** The titles for the four levels of story structure can be formatted collectively in the export tool. -This is done through a series of keyword-replace steps. +This is done through a series of keyword–replace steps. The keyword ``%title%`` will always be replaced by the text you put after the ``#`` characters in your document. -The keywords ``%chnum%`` and ``%chnumword%`` is replaced by a number, or a number word, respectively. +The keywords ``%ch%`` and ``%chw%`` is replaced by a number, or a number word, respectively. The number is incremented by one each time the build tool sees a new heading of level two in a file with layout "Chapter". -If the file has layout "Unnumbered", the counter is not incremented. -the latter is useful for for instance Prologue and Epilogue chapters. +If the file has layout "Unnumbered", the counter is *not* incremented. +The latter is useful for for instance Prologue and Epilogue chapters. -Likewise, the keywords ``%scnum%`` and ``%scabsnum%`` are number counters for scene files. +Likewise, the keywords ``%sc%`` and ``%sca%`` are number counters for scene files. These are incremented each time a heading of level three is encountered. -The former keyword is reset to one for each new chapter, while the latter is not reset but counts from first scene encountered. +The former keyword is reset to one for each new chapter, while the latter is not reset but counts from first scene encountered in the project. -If you want to insert a line break in you=r title format, add two backslashes ``\\``. +If you want to insert a line break in your title format, add two backslashes ``\\``. .. note:: Header formatting only applies to novel files. @@ -42,22 +42,28 @@ File Selection Which files are selected for export can be controlled from the options on the left side of the dialog window. The switch for "Include novel files" will select any file that isn't classified as a note. +That is, files with layout "Book", "Page", "Partition", "Chapet", "Unnumbered", or "Scene". The switch for "Include note files" will select any file that is a note. +That is, files with layout "Note". This is allows for exporting just the novel, just your notes, or both, as you see fit. In addition, you can select to export the synopsis comments, regular comments, keywords, and even exclude the body text itself. -If you for instance want to export a document with an outline of the novel, you can enable keywords and synopsis export and disable body text, thus getting a document with each heading followed by the tags and references and the synopsis. +If you for instance want to export a document with an outline of the novel, you can enable keywords and synopsis export and disable body text, thus getting a document with each heading followed by the tags and references and the synopsis. + +If you need to exclude specific files from your exports, like draft files or files you want to take out of your build, but don't want to delete, you can uncheck the "Include when building project" option for each file in the project tree. +An included file has a checkmark after the status icon in the "Flags" column. +The "Build Novel Project" tool has a switch to ignore this flag if you need to collectively override these settings. ************** Export Formats ************** -Currently, five formats are supported for exporting. +Currently, six formats are supported for exporting. OpenDocument Format =================== -This is produces an open document odt file. +This is produces an open document ``.odt`` file. The document produced has very little formatting, and may require further editing afterwards. For a better formatted office document, you may get a better result with exporting to HTML and the import that HTML document in your office word processor. @@ -79,6 +85,12 @@ This is simply a concatenation of the files selected by the filters. The files in the project are stacked together in the order they appear in the tree view, with comments, tags, etc. included if they are selected. This is a useful format for exporting the project for later import back into novelWriter. +Standard Markdown +================= + +If you have Qt 5.14 or higher, the option to export to plain Markdown is available. +This feature uses Qt's own Markdown export feature. + Plain Text ========== diff --git a/docs/source/index.txt b/docs/source/index.rst similarity index 100% rename from docs/source/index.txt rename to docs/source/index.rst diff --git a/docs/source/interface.txt b/docs/source/interface.rst similarity index 81% rename from docs/source/interface.txt rename to docs/source/interface.rst index c6e1cdd6..8a2c140b 100644 --- a/docs/source/interface.txt +++ b/docs/source/interface.rst @@ -5,6 +5,9 @@ User Interface The user interface is kept as simple as possible to avoid distractions when writing. The main window contains a tree view pane with the entire structure of the project, and a small details panel below it to display additional information about the currently selected item. +Edit View +========= + Editing a document can be done by either double-clicking on it, or hitting the return key when a file is selected. This will open the document editor, which uses a simplified markdown format, described in the section below. @@ -20,6 +23,12 @@ In the view panel, the references become clickable links, and the "Referenced By The "Referenced By" panel relies on an up-to-date index of the project. If anything is missing, or seems wrong, the index can always be rebuilt from :menuselection:`Tools --> Rebuild Index` or by pressing :kbd:`F9`. +Both the document editor and the viewer will show the label of the document as set in the Project Tree. +Optionally, the full project path to the file can be shown. +This can be set the Preferences. + +Clicking on the document title bar will select and reveal the file in the Project Tree, making it easier to find the project location of the file in a large project. + Markdown Format =============== @@ -28,21 +37,23 @@ That is, it supports basic formatting like bold, italics and underline, as well The formats are listed below. In addition to these standard markdown features, the editor also allows for comments, that is text that is ignored by the word counter and not exported or, optionally, hidden in the document viewer. +If the first word of a comment is "Synopsis:" (with the colon), the comment is treated specially, and will show up in the Outline View. The editor also has a minimal set of keywords used for setting tags and references between files. .. csv-table:: Formatting Syntax :header: "Format", "Description" :widths: 15, 50 - "``# Title``", "Heading level one. The space after the # is mandatory." - "``## Title``", "Heading level two. The space after the # is mandatory." - "``### Title``", "Heading level three. The space after the # is mandatory." - "``#### Title``", "Heading level four. The space after the # is mandatory." - "``**text**``", "The text is rendered as bold text." - "``_text_``", "The text is rendered as italicized text." - "``__text__``", "The text is rendered as underlined text." - "``% text...``", "A comment. The text is not exported, seen in viewer, or counted towards word counts." - "``@keyword: value``", "A keyword argument followed by a value, or a comma separated list of values." + "``# Title``", "Heading level one. The space after the # is mandatory." + "``## Title``", "Heading level two. The space after the # is mandatory." + "``### Title``", "Heading level three. The space after the # is mandatory." + "``#### Title``", "Heading level four. The space after the # is mandatory." + "``**text**``", "The text is rendered as bold text." + "``_text_``", "The text is rendered as italicized text." + "``__text__``", "The text is rendered as underlined text." + "``% text...``", "A comment. The text is not exported by default, seen in viewer, or counted towards word counts." + "``% Synopsis: text...``", "A synopsis comment. Shows up in the Synopsis column of the Outline View, but is otherwise treated as a comment." + "``@keyword: value``", "A keyword argument followed by a value, or a comma separated list of values." The editor and viewer also supports markdown standard hard line breaks, and preserves non-breaking spaces. A hard line break is achieved by leaving two or more spaces at the end of the line. @@ -97,6 +108,7 @@ These are as following: ":kbd:`Alt-2`", "Switch focus to document editor pane." ":kbd:`Alt-3`", "Switch focus to document viewer pane." ":kbd:`Ctrl-.`", "Correct word under cursor." + ":kbd:`Ctrl-,`", "Open the Preferences dialog." ":kbd:`Ctrl-/`", "Change block format to comment." ":kbd:`Ctrl-0`", "Remove block formatting for block under cursor." ":kbd:`Ctrl-1`", "Change block format to header level 1." @@ -115,9 +127,8 @@ These are as following: ":kbd:`Ctrl-N`", "Create new document." ":kbd:`Ctrl-O`", "Open selected document." ":kbd:`Ctrl-Q`", "Exit novelWriter." - ":kbd:`Ctrl-R`", "If in tree view, open a document for viewing. If in editor pane, open current document for viewing." + ":kbd:`Ctrl-R`", "If in tree view, open a document for viewing. If editor pane has focus, open current document for viewing." ":kbd:`Ctrl-S`", "Save the current document in the editor." - ":kbd:`Ctrl-T`", "Show project timeline." ":kbd:`Ctrl-U`", "Format selected text, or word under cursor, as underline." ":kbd:`Ctrl-V`", "Paste text from clipboard to cursor position." ":kbd:`Ctrl-W`", "Close the current document in the editor." @@ -128,12 +139,12 @@ These are as following: ":kbd:`Ctrl-F10`", "Toggle automatic updating of project outline." ":kbd:`Ctrl-Del`", "If in tree view, move a document to trash, or delete a folder." ":kbd:`Ctrl-Enter`", "Open the tag or reference under the cursor in the view panel." - ":kbd:`Ctrl-Shift-,`", "Change project settings." + ":kbd:`Ctrl-Shift-,`", "Open the Project Settings dialog." ":kbd:`Ctrl-Shift-/`", "Remove block formatting for block under cursor." ":kbd:`Ctrl-Shift-1`", "Replace occurrence of word in current document, and search for next occurrence." ":kbd:`Ctrl-Shift-A`", "Select all text in current paragraph." ":kbd:`Ctrl-Shift-D`", "Wrap selected text, or word under cursor, in single quotes." - ":kbd:`Ctrl-Shift+G`", "Find previous occurrence of word in current document. (Same as :kbd:`Shift-F3`" + ":kbd:`Ctrl-Shift-G`", "Find previous occurrence of word in current document. (Same as :kbd:`Shift-F3`" ":kbd:`Ctrl-Shift-I`", "Import text to the current document from a text file." ":kbd:`Ctrl-Shift-N`", "Create new folder." ":kbd:`Ctrl-Shift-O`", "Open a project." @@ -142,13 +153,13 @@ These are as following: ":kbd:`Ctrl-Shift-W`", "Close the current project." ":kbd:`Ctrl-Shift-Up`", "Move item one step up in the tree view." ":kbd:`Ctrl-Shift-Down`", "Move item one step down in the tree view." - ":kbd:`F1`", "Open documentation." + ":kbd:`F1`", "Open documentation. This just tries to send the documentation URL ti your browser." ":kbd:`F2`", "If in tree view, edit a document or folder settings. (Same as :kbd:`Ctrl-E`)" ":kbd:`F3`", "Find next occurrence of word in current document. (Same as :kbd:`Ctrl-G`)" - ":kbd:`F5`", "Export project dialog." + ":kbd:`F5`", "Open the Build Novel Project dialog." ":kbd:`F7`", "Re-run spell checker." ":kbd:`F8`", "Activate Zen Mode, hiding project tree and view panel." - ":kbd:`F9`", "Re-build project indices." + ":kbd:`F9`", "Re-build project index." ":kbd:`F10`", "Re-build project outline." ":kbd:`F11`", "Activate full screen mode." ":kbd:`Shift-Enter`", "Insert a hard line break at the cursor position." diff --git a/docs/source/introduction.txt b/docs/source/introduction.rst similarity index 80% rename from docs/source/introduction.txt rename to docs/source/introduction.rst index ad7117f1..13a83323 100644 --- a/docs/source/introduction.txt +++ b/docs/source/introduction.rst @@ -37,10 +37,9 @@ Project Export -------------- The project can at any time be exported to a range of different formats. -Natively, novelWriter supports export to plain text file, html5 document, LaTeX source file, novelWriter flavoured markdown, and standard markdown. -Additional export options are available through `Pandoc `_. -Currently, novelWriter will assist in converting the project to Open Office documents, Word documents, and epub. -However, to be able to fully take advantage of the many features of Pandoc, you may want to run it yourself and use the novelWriter html or markdown export as input. +Natively, novelWriter supports export to plain text file, HTML document, novelWriter flavoured markdown, standard markdown (requires Qt 5.14), and to a basic Open Document. +In addition, printing and printing to PDF is also possible. +The best supported export format is HTML, which can be imported or converted by a number of other tools like Pandoc, or simply imported into Libre Office and similar. Screenshot ---------- diff --git a/docs/source/notes.txt b/docs/source/notes.rst similarity index 84% rename from docs/source/notes.txt rename to docs/source/notes.rst index 4e31b2a9..5947ea3f 100644 --- a/docs/source/notes.txt +++ b/docs/source/notes.rst @@ -9,9 +9,9 @@ These are not required, but making at least minimal files for each such element, File Tags ========= -Each note file can have a tag associated with it, +Each new heading in a note file can have a tag associated with it. The format of a tag is ``@tag: tagname``, where tagname is a unique identifier. -Tags can then be referenced in the novel files and will show up in the Outline View. +Tags can then be referenced in the novel files, or other note files, and will show up in the Outline View and in the back-reference panel when a document is being viewed. The syntax highlighter will alert the user that the keyword is correctly used and that the tag is allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up to date. diff --git a/docs/source/projects.txt b/docs/source/projects.rst similarity index 85% rename from docs/source/projects.txt rename to docs/source/projects.rst index 44600b56..f712891f 100644 --- a/docs/source/projects.txt +++ b/docs/source/projects.rst @@ -7,25 +7,28 @@ See the Technical Information section for further details. A new project can be created from the Project menu by selecting :menuselection:`Project --> New Project`. A list of recently opened projects is maintained, and displayed in the "Open Project" dialog. +A project can be removed from this list by selecting it and pressing the :kbd:`Del` key. The project specific settings are available in :menuselection:`Project --> Project Settings`. See further details below. -Project Structure -================= +Project Roots +============= Projects are structured into a set of root folders, visible in the left side tree view panel. The core novel files go into a root folder of type "Novel". -Other supporting files go into root folders of types "Plot", "Characters", "Locations", "Timeline", "Objects", "Entities" or "Custom". +Other supporting files go into root folders of types "Plot", "Characters", "Locations", "Timeline", "Objects", "Entities", or "Custom". These other root folder types are intended for your notes on the various elements of your story. Using these is of course entirely optional. A new project will not have all of the root folders present, but you can add the ones you want from :menuselection:`Project --> Create Root Folder`. -The root folders are intended for the following use, but aside from the Novel folder, no restrictions apply. +The root folders are intended for the following use, but aside from the Novel folder, no restrictions are enforced by the application. You can use them however you want. -The root folders correspond to the categories of tags that can be used. -See the "Project Structure" section for further details. + +.. note:: + The root folders correspond to the categories of tags that can be used. + See the "Project Structure" section for further details. * **Novel:** The root folder of all text that goes into the final novel. This class of files have other rules and features than other files in the project. @@ -47,18 +50,20 @@ Orphaned Documents ------------------ In the event the editor crashes or otherwise exits without saving the project state, files that have been added to the project tree and are saved to disk will appear in a special "Orphaned Items" root folder next time the application is started. -These orphaned files will not have any meta data associated with them, although novelWriter will try to restore the file label it had in the project tree. Other information will have to be set again, and the files moved back to the correct location in the project. +These orphaned files will not have any meta data associated with them, although novelWriter will try to restore the file label it had in the project tree. +Other information will have to be set again, and the files moved back to the correct location in the project. Project Lockfile ---------------- To prevent orphaned files caused by file conflicts when novelWriter projects are synced with file synchronisation tools, a project lockfile is written to the project folder. -If you try to open a project which has such a file, you will be presented with a warning, and some information about where the project is potentially open. +If you try to open a project which has such a file, you will be presented with a warning, and some information about where novelWriter thinks the project is open. You will be give the option to ignore this warning, and continue opening the project. -However, if multiple instances are in fact editing the same project, you are likely to cause inconsistencies and create diverging project files. +However, if multiple instances are in fact editing the same project, you are likely to cause inconsistencies and create diverging project files, potentially resulting in loss of data. .. note:: - If, for some reason, novelWriter crashes, the lock file may remain. If so, it is safe to ignore the lock file warning when re-opening the project. + If, for some reason, novelWriter crashes, the lock file may remain. + In such a case it is safe to ignore the lock file warning when re-opening the project. Using Folders in the Project Tree --------------------------------- @@ -84,6 +89,12 @@ The Book Title is currently not ues for anything, so setting it is just for the The Book Authors text box takes one author per line. +Details Tab +----------- + +This tab presents an overview of meta data about the project. +It states where on your file system the project is saved, how may times it has been saved, how many folders and files it contains, and how many words exist in the entire project. + Status Tab ---------- @@ -126,6 +137,8 @@ This dialog can also be opened again later from either the menu, :menuselection: The layout of the file is also defined here. For Novel files, the full list of layout options are available. For non-Novel files, only "Note" is available. +You can also select whether the file is by default included when building the project. +This setting can be overridden in the export tool if you wish to include them anyway. See the Project Structure section for more details. @@ -133,7 +146,7 @@ Backup ====== An automatic backup system is built into novelWriter. -In order to use it, a backup path to where the backups are to be stored needs to be provided in :menuselection:`Tools --> Preferences`. +In order to use it, a backup path to where the backups are to be stored must to be provided in :menuselection:`Tools --> Preferences`. Backups can be run automatically when a project is closed, which also implies it is run when the application is closed. Backups are date stamped zip files of the entire project folder, and are stored in a subfolder of the backup path with the same name as the project working title set in Project Settings. diff --git a/docs/source/started.txt b/docs/source/started.rst similarity index 87% rename from docs/source/started.txt rename to docs/source/started.rst index 59a7f550..bd6684be 100644 --- a/docs/source/started.txt +++ b/docs/source/started.rst @@ -4,12 +4,7 @@ Getting Started You can download novelWriter from https://github.com/vkbo/novelWriter/releases -Latest version is |version|: - -* ZIP file: https://github.com/vkbo/novelWriter/archive/v0.6.1.zip -* TAR file: https://github.com/vkbo/novelWriter/archive/v0.6.1.tar.gz - -Extract the archive to a location of your choice. +Latest version is |version|. Installing Dependencies ======================= @@ -36,8 +31,8 @@ The following are optional, but recommended: * ``pyenchant`` for spell checking PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work. -Exporting to markdown requires PyQt/Qt 5.14. -There are no known minimum for lxml, but the code was originally written with 4.2. +Exporting to standard Markdown requires PyQt/Qt 5.14. +There are no known minimum for package lxml, but the code was originally written with 4.2. The optional spell check library must be at least 3.0.0 to work with Windows. On Linux, 2.0.0 also works fine. @@ -59,6 +54,8 @@ To list all options, run: python novelWriter.py --help +There are also a couple of install scripts in the assets folder which will assist in setting up launch icon and the novelWriter project file mimetype for Gnome desktops on Linux. +Currently, there's one script for Debian and one for Ubuntu. Building a Standalone Executable ================================ diff --git a/docs/source/structure.txt b/docs/source/structure.rst similarity index 55% rename from docs/source/structure.txt rename to docs/source/structure.rst index ddc28def..197a18e1 100644 --- a/docs/source/structure.txt +++ b/docs/source/structure.rst @@ -33,10 +33,12 @@ Each partition, indicated by a heading, can contain references to tags set in th The references are gathered by the indexer and used to generate the Outline View of how the different parts of the novel are connected. References and tags are also clickable in the view panel, and makes it easy to navigate reference notes while writing. -For setting the targets of references, see the "Supporting Files" section. +The targets of references can also be set per header. +This is covered in the "Supporting Files" section. References are set as keyword and a list of corresponding tags. -The valid keywords are listed below. The format of such a line is ``@keyword: value1, [value2] ... [valueN]``. +The valid keywords are listed below. +The format of a meta line is ``@keyword: value1, [value2] ... [valueN]``. All keywords allow multiple values. * ``@pov``: The point-of-view character for the current section. @@ -64,29 +66,53 @@ In general, the index for a file is regenerated when a file is saved, so this sh Novel File Layout ================= -Files that exist under the Novel type root folder can have a number of layouts set. -See overview below. +Files in a novelWriter project can have a layout format set. These layouts are important when the project is exported, as they indicate how to treat the content in terms of formatting, headings and page breaks. -the layout selected also shows up as flags in the tree view, making it easier to track what kind of files they are. +The layout for each file is indicated as the last set of characters in the Flags column of the project tree. +They also help to indicate what each file is for in your project. + +Some of these layout types are different, some are just cosmetic. +The "Book" layout is a generic novel file layout that in formatting is identical to "Chapter" and "Scene", but may help to indicate what files do in your project. +You can lay out your project using Book files for each act, and then later split those into chapter or scene files by using the "Split Document" tool. +Scenes can also be contained within chapter files, but you lose the drag and drop feature that comes with having them in separate files. + +Some layouts have implications on how the project is exported. +Files with layout "Title" and "Partition" have all headings and text centred, while the "Unnumbered" layout disables the automatic chapter numbering feature for everything contained within it. + +All of the above layout formats are only usable in the Novel root folder. +Files that are not a part of the novel itself should have the Note layout. +These files are not getting any special formatting, and it is possible to collectively filter them out during export. +Note files can be used anywhere in the project. + +Below is an overview of all available layout formats. * **Title Page**: The title page layout. The title should be formatted as a heading level one. -* **Book**: In principle, the entire novel can be contained in a single file. - In that case, use the Book layout on this file. - The internal structure is then controlled by the heading levels. -* **Plain Page**: A plain page is just that, - It is not included into content and the heading levels are ignored. -* **Partition**: A partition can be used to split a the novel into parts. - Use a level one heading for this. + All text is automatically centred on exports. +* **Plain Page**: A plain page layout useful for instance for front matter pages. + Heading levels are ignored for this layout format, and so are formatting options like Justify Text. + The page is exported with a page break before it. +* **Book**: This is the generic novel file format that in principle can be used for all novel files. + Since the internal structure of the novel is controlled by the heading levels, this file will produce the same result as a collection of Partition, Chapter and Scene type files. + However, it does not provide the functionality of the Unnumbered layout format. +* **Partition**: A partition can be used to split the novel into parts. + Partition titles are indicated with a level one heading. + You can also add text and meta data to the page. + The Partition file layout will in addition force a page break before the heading, and centre all content on the page. * **Chapter**: Signifies the start of a new chapter. - If the text itself is contained in scene files, these files should only contain the title and tag references for characters, plot, etc. + If the text itself is contained in scene files, these files should only contain the title, comments, synopsis, and tag references for characters, plot, etc. The heading for chapters should be level two. + If you need an opening text, like a quote or other leading text before the first scene, this is also where you'd want to add this text. * **Unnumbered**: Same as Chapter, but when exporting the files and automatic chapter numbering is enabled, this file will not receive a number. + This makes the layout suitable for Prologue and Epilogue type chapters. * **Scene**: A scene file. This file should have a header of level three. - Further sections can have headers of level four. - These will not impact the overall structure, but will allow for setting new characters and plot references in parts of a scene if such granularity is needed. + Further sections can have headers of level four, but there are no file layout specifically for sections. * **Note**: A generic file that is optionally ignored when the novel is exported. + Use these files for descriptions of content in the supporting root folders. + Note files can also be added to the Novel root folder if you need to insert notes there. + Note file headers receive no formatting when building the project. + They are always exported as-is. .. note:: The layout granularity is entirely optional. diff --git a/docs/source/technical.txt b/docs/source/technical.rst similarity index 64% rename from docs/source/technical.txt rename to docs/source/technical.rst index 9e358a38..027d2aaf 100644 --- a/docs/source/technical.txt +++ b/docs/source/technical.rst @@ -9,30 +9,30 @@ How Data is Stored All novelWriter files are written with utf-8 encoding. Since Python automatically converts Unix line endings to Windows line endings on Windows systems, novelWriter does not make any adaptations to the formatting on Windows systems. +This is handled entirely by the Python standard library. Main Project File ----------------- -The project itself requires a dedicated folder for storing its files. -The main project file is stored as an XML file with the name ``nwProject.nwx``. -This file contains all the meta data required for the project. -That includes project-specific settings. +The project itself requires a dedicated folder for storing its files, where novelWriter will create its own "file system" where the folder and file hierarchy is described in a project XML file. +This is the main project file in the project's root folder with the name ``nwProject.nwx``. +This file also contains all the meta data required for the project, and a number of related project settings. If this file is lost or corrupted, the structure of the project is lost. It is important to keep this file backed up, either through the built-in backup tool, or your own backup solution. .. note:: The novelWriter project folder is structured so that it can easily be added to a version control system like git. - If so, you may want to add a `.gitignore` file to exclude files with the extensions `.bak` and `.json`. + If so, you may want to add a `.gitignore` file to exclude files with the extensions `.json` as JSON files are used to cache the index and various run-time settings. -The project XML file is suitable for diff tools and version control, although a timesetamp is set in the meta section on line 2 each time the file is saved. +The project XML file is indent-formatted, suitable for diff tools and version control, although a timesetamp is set in the meta section on line 2 each time the file is saved. Project Documents ----------------- -The project documents are saved in folders starting with ``data_``. +The project documents are saved in a folder in the main project folder named ``content``. Each document has a file handle taken from the first 13 characters of a SHA256 hash of the system time when the file was first created. -The documents are saved with a folder and filename derived from this hash. +The documents are saved with a filename assembled from this hash and the file extension ``.nwd``. If you wish to find the physical location of a file in the project, you can either look it up in the project XML file, or select :menuselection:`Document --> Show File Details` in the menu when having the document open. The reason for this cryptic file naming is to avoid issues with file naming conventions and restrictions on different operating systems, and also to have a file name that does not depend on what the user names the files, or changes it to. @@ -40,16 +40,16 @@ The file meta data in the tree view, except the file label, is only saved in the Each document file contains a plain text version of the text from the editor. The file can in principle be edited in any text editor, and is suitable for diffing and version control if so desired. -Just make sure the file remains in utf-8 encoding, otherwise unicode chatracters may become mangled. +Just make sure the file remains in utf-8 encoding, otherwise unicode chatracters may become mangled when opened in novelWriter again. -The first line contains some meta data starting with the characters "%%~". +The first line of the file contains some meta data starting with the characters "%%~". This line is mainly there to restore some information if it is lost from the project file, and the information may be helpful if you do open the file in an external editor as it contains the file label as the last entry. -The line can be deleted without any consequences to the rest of the content of the file. +The line can be deleted without any consequences to the rest of the content of the file, and will be added back next time the file is saved in novelWriter. The File Saving Process ----------------------- When saving the project file, or any of the documents, the data is first saved to a temporary file. -If successful, the old data file is renamed with a `.bak` extension, and the temporary file becomes the new file. +If successful, the old data file is removed, and the temporary file becomes the new file. This ensures that the previously saved data is only replaced when the new data has been successfully saved. -The `.bak` file will always contain the previous version of the file, although when auto-save is enabled, they may have the same content. +For the project XML file, a `.bak` file is kept which will always contain the previous version of the file, although when auto-save is enabled, they may have the same content. diff --git a/nw/assets/text/exportHelp_en.htm b/nw/assets/text/exportHelp_en.htm deleted file mode 100644 index e921f05d..00000000 --- a/nw/assets/text/exportHelp_en.htm +++ /dev/null @@ -1,49 +0,0 @@ -

Help!

-

A brief guide to make the most out of the Build Novel Project tool.

- -

Novel Title Formats

-

The format of the various title levels in the files under the Novel folder can be customised in - these settings. The actual title given in the headings of your files will for instance replace - all occurrences of the keyword %title%. Any static text will be left as-is in the - final title. An empty field means the title isn't written out at all.

-

The available formatting keywords are:

-

%title% – This is replaced with the text you put in your headings in your - documents

-

%chnum% – This is replaced with the chapter number of your chapter type - headings. These are generated automaticall starting from 1, but ignoring chapter headings in - files with "Unnumbered" layout.

-

%chnumword% – This is replaced with the chapter number, but instead of an - arabic number, the word for it is used, e.g. One, Two, Fifteen, Twenty-Five, etc.

-

%scnum% – This is replaced with the scene number. The number is reset to one - for each new chapter, so it is the scene number within the current chapter.

-

%scabsnum% – This is replaced with the absolute scene number. That is, the - number is counted from the first scene in the novel, and not reset for each chapter.

-

\\ – Two backslashes are replaced by a line break.

-

Note: The Scene and Section formats are treated slightly differently than the other title - formats. If the format is a constant text, that is, contains no %keyword% tags, it - will be treated as a separator instead. Scene and Section separators are centred, and for - scenes, not shown if placed directly after the chapter heading. For instance, it you want the - classic three asterisk * * * separator between scenes, just put that into the - scene format box, and nothing else.

- -

Build Overrides

-

Novel Outline Mode: This option will build an outline version of the novel rather than the - full thing. It overrides the title format settings without changing them. Each title will be - written out, and the synopsis text will appear instead of the body text of the files. Some of - the other options are still available in Outline Mode.

- -

Include Non-Text Elements

-

Include Synopsis: This will add the synopsis comment as the first paragraph after each - heading.

-

Include Comments: This will include any comments as additional paragraphs in the text.

-

Include Keywords: This will include any keywords and tags as clickable links after each - heading.

- -

Additional Options

-

Include Novel Files: This means all files that don't have a layout of type "Note" will be - included. This is the normal mode when exporting the novel itself without the notes.

-

Include Note Files: This means all files with a layout of type "Note" will be - included. Titles in note files are always left as they appear.

-

Ignore Export Flag: Each file in the project tree has an "Include when building project" - option set, which is indicated by a little check mark in the "Flags" column. Files without This - tick will normally be skipped during build, but can be included if this option is enabled.

diff --git a/nw/core/document.py b/nw/core/document.py index 4cfd09a3..7ce4de4e 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -37,8 +37,6 @@ logger = logging.getLogger(__name__) class NWDoc(): - FILE_MN = "main.nwd" - def __init__(self, theProject, theParent): self.mainConf = nw.CONFIG @@ -93,8 +91,9 @@ class NWDoc(): if self.theItem.parHandle == self.theProject.projTree.trashRoot(): self.docEditable = False - docDir, docFile = self._assemblePath(self.docHandle, self.FILE_MN) - self.fileLoc = path.join(docDir,docFile) + docDir = "content" + docFile = self.docHandle+".nwd" + self.fileLoc = path.join(docDir, docFile) logger.debug("Opening document %s" % self.fileLoc) dataDir = path.join(self.theProject.projPath, docDir) docPath = path.join(dataDir, docFile) @@ -139,8 +138,9 @@ class NWDoc(): if self.docHandle is None or not self.docEditable: return False - docDir, docFile = self._assemblePath(self.docHandle, self.FILE_MN) - logger.debug("Saving document %s" % path.join(docDir,docFile)) + docDir = "content" + docFile = self.docHandle+".nwd" + logger.debug("Saving document %s" % path.join(docDir, docFile)) dataPath = path.join(self.theProject.projPath, docDir) docPath = path.join(dataPath, docFile) if not path.isdir(dataPath): @@ -159,12 +159,6 @@ class NWDoc(): self.makeAlert(["Could not save document.",str(e)], nwAlert.ERROR) return False - # Remove bak files from old file save method, if one exists - # This part can eventually be removed - docBack = path.join(dataPath, docFile[:-3]+"bak") - if path.isfile(docBack): - unlink(docBack) - # If we're here, the file was successfully saved, so we can # replace the temp file with the actual file if path.isfile(docPath): @@ -179,7 +173,8 @@ class NWDoc(): """Permanently delete a document source file and its backups from the project data folder. """ - docDir, docFile = self._assemblePath(tHandle, self.FILE_MN) + docDir = "content" + docFile = self.docHandle+".nwd" dataPath = path.join(self.theProject.projPath, docDir) chkList = [] chkList.append(path.join(dataPath, docFile)) @@ -226,18 +221,4 @@ class NWDoc(): return theMeta, thePath - ## - # Internal Functions - ## - - @staticmethod - def _assemblePath(tHandle, docExt): - """Assemble the file path for a given handle. - """ - if tHandle is None: - return None, None - docDir = "data_"+tHandle[0] - docFile = tHandle[1:13]+"_"+docExt - return docDir, docFile - # END Class NWDoc diff --git a/nw/core/project.py b/nw/core/project.py index 0c303f74..96e22103 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -33,7 +33,7 @@ import logging import nw -from os import path, mkdir, listdir, unlink, rename +from os import path, mkdir, listdir, unlink, rename, rmdir from lxml import etree from hashlib import sha256 from time import time @@ -74,6 +74,7 @@ class NWProject(): # Class Settings self.projPath = None # The full path to where the currently open project is saved self.projMeta = None # The full path to the project's meta data folder + self.projData = None # The full path to the project's data folder self.projDict = None # The spell check dictionary self.projFile = None # The file name of the project main XML file @@ -195,6 +196,7 @@ class NWProject(): # Project Settings self.projPath = None self.projMeta = None + self.projData = None self.projDict = None self.projFile = nwFiles.PROJ_FILE self.projName = "" @@ -247,10 +249,13 @@ class NWProject(): logger.debug("Opening project: %s" % self.projPath) self.projMeta = path.join(self.projPath,"meta") + self.projData = path.join(self.projPath,"content") self.projDict = path.join(self.projMeta, nwFiles.PROJ_DICT) if not self._checkFolder(self.projMeta): return False + if not self._checkFolder(self.projData): + return False if overrideLock: self._clearLockFile() @@ -291,7 +296,7 @@ class NWProject(): self.clearProject() return False - xRoot = nwXML.getroot() + xRoot = nwXML.getroot() nwxRoot = xRoot.tag appVersion = "Unknown" @@ -314,13 +319,40 @@ class NWProject(): logger.verbose("XML root is %s" % nwxRoot) logger.verbose("File version is %s" % fileVersion) - if not nwxRoot == "novelWriterXML" or not fileVersion == "1.0": + # Check File Type + # =============== + if not nwxRoot == "novelWriterXML": self.makeAlert( - "Project file does not appear to be a novelWriterXML file version 1.0", + "Project file does not appear to be a novelWriterXML file.", nwAlert.ERROR ) return False + # Check Project Storage Version + # ============================= + if fileVersion == "1.0": + msgBox = QMessageBox() + msgRes = msgBox.question(self.theParent, "Old Project Version", ( + "The project file and data is created by a %s version lower than 0.7. " + "Do you want to upgrade the project to the most recent format?

" + "Note that after the upgrade, you cannot open the project with an older " + "version of novelWriter any more, so make sure you have a recent backup." + ) % nw.__package__) + if msgRes == QMessageBox.Yes: + self._updateStorage() + else: + return False + elif fileVersion != "1.1": + self.makeAlert(( + "Unknown or unsupported %s project format. " + "The project cannot be opened by this version of %s." + ) % ( + nw.__package__, nw.__package__ + ), nwAlert.ERROR) + return False + + # Check novelWriter Version + # ========================= if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI: msgBox = QMessageBox() msgRes = msgBox.question(self.theParent, "Version Conflict", ( @@ -333,6 +365,8 @@ class NWProject(): if msgRes != QMessageBox.Yes: return False + # Start Parsing XML + # ================= for xChild in xRoot: if xChild.tag == "project": logger.debug("Found project meta") @@ -404,16 +438,21 @@ class NWProject(): file. """ if self.projPath is None: - self.makeAlert("Project path not set, cannot save.", nwAlert.ERROR) + self.makeAlert( + "Project path not set, cannot save project.", nwAlert.ERROR + ) return False - self.projMeta = path.join(self.projPath,"meta") + self.projMeta = path.join(self.projPath, "meta") + self.projData = path.join(self.projPath, "content") saveTime = time() if not self._checkFolder(self.projPath): return False if not self._checkFolder(self.projMeta): return False + if not self._checkFolder(self.projData): + return False logger.debug("Saving project: %s" % self.projPath) @@ -427,7 +466,7 @@ class NWProject(): nwXML = etree.Element("novelWriterXML",attrib={ "appVersion" : str(nw.__version__), "hexVersion" : str(nw.__hexversion__), - "fileVersion" : "1.0", + "fileVersion" : "1.1", "saveCount" : str(self.saveCount), "autoCount" : str(self.autoCount), "timeStamp" : formatTimeStamp(saveTime), @@ -937,29 +976,20 @@ class NWProject(): if self.projPath is None: return - # First, scan the project data folders - itemList = [] - for subItem in listdir(self.projPath): - if subItem[:5] != "data_": - continue - dataDir = path.join(self.projPath,subItem) - for subFile in listdir(dataDir): - if subFile[-4:] == ".nwd": - newItem = path.join(subItem,subFile) - itemList.append(newItem) - - # Then check the valid files + # Then check the files in the data folder orphanFiles = [] - for fileItem in itemList: - if len(fileItem) != 28: - # Just to be safe, shouldn't happen + for fileItem in listdir(self.projData): + if not fileItem.endswith(".nwd"): logger.warning("Skipping file %s" % fileItem) continue - fHandle = fileItem[5]+fileItem[7:19] + if len(fileItem) != 17: + logger.warning("Skipping file %s" % fileItem) + continue + fHandle = fileItem[:13] if fHandle in self.projTree: - logger.debug("Checking file %s, handle %s: OK" % (fileItem,fHandle)) + logger.debug("Checking file %s, handle %s: OK" % (fileItem, fHandle)) else: - logger.debug("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle)) + logger.debug("Checking file %s, handle %s: Orphaned" % (fileItem, fHandle)) orphanFiles.append(fHandle) # Report status @@ -1016,6 +1046,55 @@ class NWProject(): return True + def _updateStorage(self): + """Updates the project storage folder from 1.0 to 1.1. + """ + contDir = path.join(self.projPath, "content") + self._checkFolder(contDir) + errList = [] + + for projItem in listdir(self.projPath): + itemPath = path.join(self.projPath, projItem) + if not path.isdir(itemPath) or not projItem.startswith("data_"): + continue + for dataFile in listdir(itemPath): + dataPath = path.join(itemPath, dataFile) + if dataFile.endswith(".bak"): + try: + unlink(dataPath) + logger.info("Deleted file: %s" % dataPath) + except: + errList.append("Failed to delete: %s" % dataPath) + + elif dataFile.endswith(".nwd") and len(dataFile) == 21: + tHandle = projItem[-1]+dataFile[:12] + newPath = path.join(contDir, tHandle+".nwd") + try: + rename(dataPath, newPath) + logger.info("Moved file: %s" % dataPath) + logger.info("New location: %s" % newPath) + except: + errList.append("Failed to move: %s" % dataPath) + + else: + newPath = path.join(self.projPath, "unknown_"+dataFile) + try: + rename(dataPath, newPath) + logger.info("Moved file: %s" % dataPath) + logger.info("New location: %s" % newPath) + except: + errList.append("Failed to move: %s" % dataPath) + try: + rmdir(itemPath) + logger.info("Removed folder: %s" % itemPath) + except: + errList.append("Failed to delete: %s" % itemPath) + + if errList: + self.makeAlert(errList, nwAlert.ERROR) + + return + # END Class NWProject # ================================================================================================ # diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index fba25fa3..fb632140 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -577,10 +577,10 @@ class Tokenizer(): """Replaces the %keyword% strings. """ theTitle = theTitle.replace(r"%title%", theText) - theTitle = theTitle.replace(r"%chnum%", str(self.numChapter)) - theTitle = theTitle.replace(r"%scnum%", str(self.numChScene)) - theTitle = theTitle.replace(r"%scabsnum%", str(self.numAbsScene)) - theTitle = theTitle.replace(r"%chnumword%", numberToWord(self.numChapter,"en")) + theTitle = theTitle.replace(r"%ch%", str(self.numChapter)) + theTitle = theTitle.replace(r"%sc%", str(self.numChScene)) + theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene)) + theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter,"en")) return theTitle # END Class Tokenizer diff --git a/nw/core/tools.py b/nw/core/tools.py index a6eb244b..8aa81d7b 100644 --- a/nw/core/tools.py +++ b/nw/core/tools.py @@ -89,7 +89,7 @@ def projectMaintenance(theProject): if path.isdir(theProject.projPath): cacheDir = path.join(theProject.projPath, "cache") if path.isdir(cacheDir): - logger.info("Deprecated cache folder found") + logger.info("Deprecated cache folder content found") rmList = [] for i in range(10): rmList.append(path.join(cacheDir, "nwProject.nwx.%d" % i)) @@ -101,11 +101,6 @@ def projectMaintenance(theProject): unlink(rmFile) except Exception as e: logger.error(str(e)) - logger.info("Deleting: %s" % cacheDir) - try: - rmdir(cacheDir) - except Exception as e: - logger.error(str(e)) # Remove no longer used meta files rmList = [] diff --git a/nw/gui/build.py b/nw/gui/build.py index 7e5c00c8..6a7c99cc 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -39,7 +39,7 @@ from PyQt5.QtGui import ( from PyQt5.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel, QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction, - QFileDialog, QFontComboBox, QSpinBox + QFileDialog, QFontComboBox, QSpinBox, QDialogButtonBox ) from nw.gui.additions import QSwitch @@ -75,11 +75,11 @@ class GuiBuildNovel(QDialog): self.nwdText = [] # List of markdown documents self.setWindowTitle("Build Novel Project") - self.setMinimumWidth(800) + self.setMinimumWidth(900) self.setMinimumHeight(800) self.resize( - self.optState.getInt("GuiBuildNovel", "winWidth", 800), + self.optState.getInt("GuiBuildNovel", "winWidth", 900), self.optState.getInt("GuiBuildNovel", "winHeight", 800) ) @@ -95,30 +95,61 @@ class GuiBuildNovel(QDialog): self.titleForm = QGridLayout(self) self.titleGroup.setLayout(self.titleForm) + fmtHelp = ( + r"Formatting Codes:
" + r"%title% for the title as set in the document
" + r"%ch% for chapter number (1, 2, 3)
" + r"%chw% for chapter number as a word (one, two)
" + r"%sc% for scene number within chapter
" + r"%sca% for scene number within novel" + ) + fmtScHelp = ( + r"

" + r"Leave blank to skip this heading, or set to a static text, like " + r"for instance '* * *', to make a separator. The separator will " + r"be centred automatically and only appear between sections of " + r"the same type." + ) + self.fmtTitle = QLineEdit() self.fmtTitle.setMaxLength(200) - self.fmtTitle.setFixedWidth(200) - self.fmtTitle.setText(self.theProject.titleFormat["title"]) + self.fmtTitle.setFixedWidth(220) + self.fmtTitle.setToolTip(fmtHelp) + self.fmtTitle.setText( + self._reFmtCodes(self.theProject.titleFormat["title"]) + ) self.fmtChapter = QLineEdit() self.fmtChapter.setMaxLength(200) - self.fmtChapter.setFixedWidth(200) - self.fmtChapter.setText(self.theProject.titleFormat["chapter"]) + self.fmtChapter.setFixedWidth(220) + self.fmtChapter.setToolTip(fmtHelp) + self.fmtChapter.setText( + self._reFmtCodes(self.theProject.titleFormat["chapter"]) + ) self.fmtUnnumbered = QLineEdit() self.fmtUnnumbered.setMaxLength(200) - self.fmtUnnumbered.setFixedWidth(200) - self.fmtUnnumbered.setText(self.theProject.titleFormat["unnumbered"]) + self.fmtUnnumbered.setFixedWidth(220) + self.fmtUnnumbered.setToolTip(fmtHelp) + self.fmtUnnumbered.setText( + self._reFmtCodes(self.theProject.titleFormat["unnumbered"]) + ) self.fmtScene = QLineEdit() self.fmtScene.setMaxLength(200) - self.fmtScene.setFixedWidth(200) - self.fmtScene.setText(self.theProject.titleFormat["scene"]) + self.fmtScene.setFixedWidth(220) + self.fmtScene.setToolTip(fmtHelp + fmtScHelp) + self.fmtScene.setText( + self._reFmtCodes(self.theProject.titleFormat["scene"]) + ) self.fmtSection = QLineEdit() self.fmtSection.setMaxLength(200) - self.fmtSection.setFixedWidth(200) - self.fmtSection.setText(self.theProject.titleFormat["section"]) + self.fmtSection.setFixedWidth(220) + self.fmtSection.setToolTip(fmtHelp + fmtScHelp) + self.fmtSection.setText( + self._reFmtCodes(self.theProject.titleFormat["section"]) + ) self.titleForm.addWidget(QLabel("Title"), 0, 0, 1, 1, Qt.AlignLeft) self.titleForm.addWidget(self.fmtTitle, 0, 1, 1, 1, Qt.AlignRight) @@ -141,7 +172,8 @@ class GuiBuildNovel(QDialog): self.textGroup.setLayout(self.textForm) self.textFont = QFontComboBox() - self.textFont.setFixedWidth(200) + self.textFont.setFixedWidth(220) + self.textFont.setToolTip("The font is used for PDF and printing. Other formats have no font set.") self.textFont.setCurrentFont( QFont(self.optState.getString("GuiBuildNovel", "textFont", self.mainConf.textFont)) ) @@ -151,11 +183,13 @@ class GuiBuildNovel(QDialog): self.textSize.setMinimum(5) self.textSize.setMaximum(48) self.textSize.setSingleStep(1) + self.textSize.setToolTip("The size is used for PDF and printing. Other formats have no size set.") self.textSize.setValue( self.optState.getInt("GuiBuildNovel", "textSize", self.mainConf.textSize) ) self.justifyText = QSwitch() + self.justifyText.setToolTip("Applies to PDF, printing, HTML, and Open Document exports.") self.justifyText.setChecked( self.optState.getBool("GuiBuildNovel", "justifyText", False) ) @@ -177,12 +211,15 @@ class GuiBuildNovel(QDialog): self.includeGroup.setLayout(self.includeForm) self.includeSynopsis = QSwitch() + self.includeSynopsis.setToolTip("Include synopsis type comments in the output.") self.includeSynopsis.setChecked(self.theProject.titleFormat["withSynopsis"]) self.includeComments = QSwitch() + self.includeComments.setToolTip("Include plain comments in the output.") self.includeComments.setChecked(self.theProject.titleFormat["withComments"]) self.includeKeywords = QSwitch() + self.includeKeywords.setToolTip("Include meta keywords (tags, references) in the output.") self.includeKeywords.setChecked(self.theProject.titleFormat["withKeywords"]) self.includeForm.addWidget(QLabel("Include synopsis"), 0, 0, 1, 1, Qt.AlignLeft) @@ -202,21 +239,34 @@ class GuiBuildNovel(QDialog): self.addsGroup.setLayout(self.addsForm) self.novelFiles = QSwitch() + self.novelFiles.setToolTip( + "Include files with layouts 'Book', 'Page', 'Partition', " + "'Chapter', 'Unnumbered', and 'Scene'." + ) self.novelFiles.setChecked( self.optState.getBool("GuiBuildNovel", "addNovel", True) ) self.noteFiles = QSwitch() + self.noteFiles.setToolTip("Include files with layout 'Note'.") self.noteFiles.setChecked( self.optState.getBool("GuiBuildNovel", "addNotes", False) ) self.ignoreFlag = QSwitch() + self.ignoreFlag.setToolTip( + "Ignore the 'Include when building project' setting and include " + "all files in the output." + ) self.ignoreFlag.setChecked( self.optState.getBool("GuiBuildNovel", "ignoreFlag", False) ) self.excludeBody = QSwitch() + self.excludeBody.setToolTip( + "Exclude body text in the output. Combine with 'Include synopsis' " + "for making outline." + ) self.excludeBody.setChecked( self.optState.getBool("GuiBuildNovel", "excludeBody", False) ) @@ -279,13 +329,12 @@ class GuiBuildNovel(QDialog): self.saveTXT.triggered.connect(lambda: self._saveDocument(self.FMT_TXT)) self.saveMenu.addAction(self.saveTXT) - self.btnClose = QPushButton("Close") - self.btnClose.clicked.connect(self._doClose) - - self.buttonForm.addWidget(self.btnHelp, 0, 0) + self.buttonForm.addWidget(self.btnSave, 0, 0) self.buttonForm.addWidget(self.btnPrint, 0, 1) - self.buttonForm.addWidget(self.btnSave, 1, 0) - self.buttonForm.addWidget(self.btnClose, 1, 1) + + # Buttons + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close) + self.buttonBox.rejected.connect(self._doClose) # Assemble GUI # ============ @@ -303,6 +352,7 @@ class GuiBuildNovel(QDialog): self.innerBox.addWidget(self.docView) self.outerBox.addLayout(self.innerBox) + self.outerBox.addWidget(self.buttonBox) self.setLayout(self.outerBox) self.innerBox.setStretch(0, 0) @@ -661,21 +711,14 @@ class GuiBuildNovel(QDialog): return - def _showHelp(self): - """Generate a help text and show it in the document window. + def _reFmtCodes(self, theFormat): + """Translates old formatting codes to new ones. """ - docName = "exportHelp_%s.htm" % self.mainConf.guiLang - docPath = path.join(self.mainConf.assetPath, "text", docName) - if path.isfile(docPath): - with open(docPath, mode="r", encoding="utf8") as inFile: - helpText = inFile.read() - self.docView.setStyleSheet() - self.docView.setContent(helpText) - else: - self.theParent.makeAlert( - "Could not open help text file for Build Project.", nwAlert.ERROR - ) - return + theFormat = theFormat.replace(r"%chnum%", r"%ch%") + theFormat = theFormat.replace(r"%scnum%", r"%sc%") + theFormat = theFormat.replace(r"%scabsnum%", r"%sca%") + theFormat = theFormat.replace(r"%chnumword%", r"%chw%") + return theFormat # END Class GuiBuildNovel diff --git a/nw/gui/dialogs/projectload.py b/nw/gui/dialogs/projectload.py index 3b64848b..f5513ea0 100644 --- a/nw/gui/dialogs/projectload.py +++ b/nw/gui/dialogs/projectload.py @@ -93,7 +93,7 @@ class GuiProjectLoad(QDialog): self.lblRecent = QLabel("Recently Opened Projects") self.lblPath = QLabel("Path") self.selPath = QLineEdit("") - self.selPath.setEnabled(False) + self.selPath.setReadOnly(True) self.browseButton = QPushButton("...") self.browseButton.setMaximumWidth(30) diff --git a/sample/data_1/4298de4d9524_main.nwd b/sample/content/14298de4d9524.nwd similarity index 100% rename from sample/data_1/4298de4d9524_main.nwd rename to sample/content/14298de4d9524.nwd diff --git a/sample/data_5/3b69b83cdafc_main.nwd b/sample/content/53b69b83cdafc.nwd similarity index 100% rename from sample/data_5/3b69b83cdafc_main.nwd rename to sample/content/53b69b83cdafc.nwd diff --git a/sample/data_5/eaea4e8cdee8_main.nwd b/sample/content/5eaea4e8cdee8.nwd similarity index 100% rename from sample/data_5/eaea4e8cdee8_main.nwd rename to sample/content/5eaea4e8cdee8.nwd diff --git a/sample/data_6/36b6aa9b697b_main.nwd b/sample/content/636b6aa9b697b.nwd similarity index 100% rename from sample/data_6/36b6aa9b697b_main.nwd rename to sample/content/636b6aa9b697b.nwd diff --git a/sample/data_6/a2d6d5f4f401_main.nwd b/sample/content/6a2d6d5f4f401.nwd similarity index 100% rename from sample/data_6/a2d6d5f4f401_main.nwd rename to sample/content/6a2d6d5f4f401.nwd diff --git a/sample/data_8/8706ddc78b1b_main.nwd b/sample/content/88706ddc78b1b.nwd similarity index 100% rename from sample/data_8/8706ddc78b1b_main.nwd rename to sample/content/88706ddc78b1b.nwd diff --git a/sample/data_9/6b68994dfa3d_main.nwd b/sample/content/96b68994dfa3d.nwd similarity index 100% rename from sample/data_9/6b68994dfa3d_main.nwd rename to sample/content/96b68994dfa3d.nwd diff --git a/sample/data_9/74e400180a99_main.nwd b/sample/content/974e400180a99.nwd similarity index 100% rename from sample/data_9/74e400180a99_main.nwd rename to sample/content/974e400180a99.nwd diff --git a/sample/data_a/e7339df26ded_main.nwd b/sample/content/ae7339df26ded.nwd similarity index 100% rename from sample/data_a/e7339df26ded_main.nwd rename to sample/content/ae7339df26ded.nwd diff --git a/sample/data_b/3e74dbc1f584_main.nwd b/sample/content/b3e74dbc1f584.nwd similarity index 100% rename from sample/data_b/3e74dbc1f584_main.nwd rename to sample/content/b3e74dbc1f584.nwd diff --git a/sample/data_b/8136a5a774a0_main.nwd b/sample/content/b8136a5a774a0.nwd similarity index 100% rename from sample/data_b/8136a5a774a0_main.nwd rename to sample/content/b8136a5a774a0.nwd diff --git a/sample/data_b/a8a28a246524_main.nwd b/sample/content/ba8a28a246524.nwd similarity index 100% rename from sample/data_b/a8a28a246524_main.nwd rename to sample/content/ba8a28a246524.nwd diff --git a/sample/data_b/b2c23b3c42cc_main.nwd b/sample/content/bb2c23b3c42cc.nwd similarity index 100% rename from sample/data_b/b2c23b3c42cc_main.nwd rename to sample/content/bb2c23b3c42cc.nwd diff --git a/sample/data_b/c0cbd2a407f3_main.nwd b/sample/content/bc0cbd2a407f3.nwd similarity index 100% rename from sample/data_b/c0cbd2a407f3_main.nwd rename to sample/content/bc0cbd2a407f3.nwd diff --git a/sample/data_e/dca4be2fcaf8_main.nwd b/sample/content/edca4be2fcaf8.nwd similarity index 100% rename from sample/data_e/dca4be2fcaf8_main.nwd rename to sample/content/edca4be2fcaf8.nwd diff --git a/sample/data_f/1471bef9f2ae_main.nwd b/sample/content/f1471bef9f2ae.nwd similarity index 100% rename from sample/data_f/1471bef9f2ae_main.nwd rename to sample/content/f1471bef9f2ae.nwd diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 24ebc1a0..abe767ea 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,5 +1,5 @@ - + Sample Project Sample Project @@ -12,7 +12,7 @@ True 636b6aa9b697b ba8a28a246524 - 920 + 914 B E @@ -20,9 +20,9 @@ %title% - Chapter %chnum%: %title% + Chapter %ch%: %title% %title% - * * * + Scene %ch%.%sc%: %title%
True True @@ -301,9 +301,9 @@ False True SCENE - 30 - 6 - 1 + 0 + 0 + 0 36 diff --git a/tests/reference/gui/1_e17daca5f3e1_main.nwd b/tests/reference/gui/1_0e17daca5f3e1.nwd similarity index 100% rename from tests/reference/gui/1_e17daca5f3e1_main.nwd rename to tests/reference/gui/1_0e17daca5f3e1.nwd diff --git a/tests/reference/gui/1_a6562590ef19_main.nwd b/tests/reference/gui/1_1a6562590ef19.nwd similarity index 100% rename from tests/reference/gui/1_a6562590ef19_main.nwd rename to tests/reference/gui/1_1a6562590ef19.nwd diff --git a/tests/reference/gui/1_1489056e0916_main.nwd b/tests/reference/gui/1_31489056e0916.nwd similarity index 100% rename from tests/reference/gui/1_1489056e0916_main.nwd rename to tests/reference/gui/1_31489056e0916.nwd diff --git a/tests/reference/gui/1_8010bd9270f9_main.nwd b/tests/reference/gui/1_98010bd9270f9.nwd similarity index 100% rename from tests/reference/gui/1_8010bd9270f9_main.nwd rename to tests/reference/gui/1_98010bd9270f9.nwd diff --git a/tests/test_gui.py b/tests/test_gui.py index 47432f51..3d202b18 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -237,14 +237,14 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp): # Check the files refFile = path.join(nwTempGUI,"nwProject.nwx") assert cmpFiles(refFile, path.join(nwRef,"gui","1_nwProject.nwx"), [2]) - refFile = path.join(nwTempGUI,"data_0","e17daca5f3e1_main.nwd") - assert cmpFiles(refFile, path.join(nwRef,"gui","1_e17daca5f3e1_main.nwd")) - refFile = path.join(nwTempGUI,"data_9","8010bd9270f9_main.nwd") - assert cmpFiles(refFile, path.join(nwRef,"gui","1_8010bd9270f9_main.nwd")) - refFile = path.join(nwTempGUI,"data_3","1489056e0916_main.nwd") - assert cmpFiles(refFile, path.join(nwRef,"gui","1_1489056e0916_main.nwd")) - refFile = path.join(nwTempGUI,"data_1","a6562590ef19_main.nwd") - assert cmpFiles(refFile, path.join(nwRef,"gui","1_a6562590ef19_main.nwd")) + refFile = path.join(nwTempGUI,"content","0e17daca5f3e1.nwd") + assert cmpFiles(refFile, path.join(nwRef,"gui","1_0e17daca5f3e1.nwd")) + refFile = path.join(nwTempGUI,"content","98010bd9270f9.nwd") + assert cmpFiles(refFile, path.join(nwRef,"gui","1_98010bd9270f9.nwd")) + refFile = path.join(nwTempGUI,"content","31489056e0916.nwd") + assert cmpFiles(refFile, path.join(nwRef,"gui","1_31489056e0916.nwd")) + refFile = path.join(nwTempGUI,"content","1a6562590ef19.nwd") + assert cmpFiles(refFile, path.join(nwRef,"gui","1_1a6562590ef19.nwd")) nwGUI.closeMain() # qtbot.stopForInteraction()