Merge branch 'dev' into new_ref_panel
@@ -1,4 +1,4 @@
|
||||
name: flake8
|
||||
name: Flake8
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -1,5 +1,82 @@
|
||||
# novelWriter Changelog
|
||||
|
||||
## Version 2.2 Beta 1 [2023-11-11]
|
||||
|
||||
### 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**
|
||||
|
||||
* novelWriter has a new logo and icon. PR #1593.
|
||||
* The Document Editor is now a true plain text editor. This has a number of benefits and a couple
|
||||
of drawbacks. The most important benefits is that the editor responds a lot faster, and can hold
|
||||
much larger text documents. The big document limit has therefore been removed. It mostly affected
|
||||
automatic spell checking. The syntax highlighter and spell checker are also more efficient, which
|
||||
allows for needed improvements to these. The drawbacks are mainly that the editor now scrolls one
|
||||
line at a time, instead of scrolling pixel by pixel like before. PRs #1521 and #1525.
|
||||
* Tags and References are now case insensitive. Their display name on the user interface remains
|
||||
the same as the value set for the `@tag` entry. Issue #1313. PRs #1522 and #1578.
|
||||
* Keywords for Tags and References, and the References themselves, now have an auto-complete menu
|
||||
that pops up in the editor on lines starting with the `@` character. It will first suggest what
|
||||
keyword you want to use, and when it has been added, use that keyword to look up suggestions for
|
||||
references to add. The suggestions improve as you type by looking for the characters you've
|
||||
already typed in the tags you've previously defined. Issue #823. PR #1581.
|
||||
* You can now right-click an undefined tag, and a context menu option to create a Project Note for
|
||||
that tag will appear in the menu. On selection, it will create a note in the first root folder of
|
||||
the correct kind, and set the title and tag to match the undefined reference, making it instantly
|
||||
defined. Issues #1580 and #823. PR #1582.
|
||||
* Shortcodes have been added to the Document Editor. Shorcodes are HTML-like syntax, but uses
|
||||
square brackets instead of angular brackets. So `[b]text[/b]` will make the word "text" appear as
|
||||
bold. Shortcodes currently support bold, italic, striketrough, underline, superscript and
|
||||
subscript text. The first three are complimentary to the Markdown-like syntax that. The benefit
|
||||
of the shortcode emphasis syntax, however, is that it does not care about word boundaries, and
|
||||
can therefore be used any place in the text. Including in the middle of words. Issues #1337 and
|
||||
#1444. PRs #1540 and #1583.
|
||||
* A show/hide toolbar has been added to the editor where toolbuttons for formatting options are
|
||||
available. The toolbar is hidden by default, but can be activated from a three dots icon in the
|
||||
top left corner of the editor. Issue #1585. PR #1584.
|
||||
* Build Definitions in the Manuscript Build tool can now be re-ordered, and the order is preserved
|
||||
when the tool is closed and re-opened. Issue #1542. PR #1591.
|
||||
|
||||
**Usability**
|
||||
|
||||
* The Settings menu in the sidebar now always pops out to the right and upwards from the bottom of
|
||||
the icon. The previous behaviour was not guaranteed to stay in the visible area of the screen.
|
||||
PR #1520.
|
||||
* The right click action on a misspelled word now uses the actual spell checker data for lookup.
|
||||
Previously, the spell checker would underline a word that was misspelled, but the right click
|
||||
action actually had no way of reading where the error line was, so it had to guess again what
|
||||
word the user was clicking. Since these two parts of the code used different logic, they
|
||||
sometimes produced different results. The spell checker now saves the location of each spell
|
||||
check error, and the right click action retrieves this data when generating suggestions, which
|
||||
should eliminate the problem of picking the correct word boundaries. Issue #1532. PR #1525.
|
||||
* The language of a project is not set in the New Project Wizard and in Project Settings. It is no
|
||||
longer defined in the Build Settings panel. Issue #1588. PR #1589.
|
||||
* The way switching focus and view in the main GUI has changed. Pressing `Ctrl+T` will now switch
|
||||
focus to the Project or Novel Tree if focus is elsewhere, or if either have focus already, it
|
||||
will switch view to the other tree. Pressing `Ctrl+E` will switch focus and view to the Document
|
||||
Editor. Pressing `Ctrl+Shift+T` will do the same for the Outline View. The old Alt-based
|
||||
shortcuts have been removed. Issues #1310 and #1291. PR #1590.
|
||||
|
||||
**User Interface**
|
||||
|
||||
* The labels under the sidebar buttons have been removed. The tool tips have the necessary
|
||||
information. PR #1520.
|
||||
|
||||
**Other Improvements**
|
||||
|
||||
* Also the Tags and References keywords are now translated into the project language when these are
|
||||
included in Manuscript builds. As long as the phrases have been translated. PR #1586.
|
||||
|
||||
----
|
||||
|
||||
## Version 2.1.1 [2023-11-05]
|
||||
|
||||
### Release Notes
|
||||
|
||||
@@ -52,6 +52,12 @@ Some of the assets bundled with novelWriter were adapted from the following sour
|
||||
* [Owl](https://github.com/sdras/night-owl-vscode-theme) syntax themes by Sarah Drasner (MIT License)
|
||||
* [Solarized](https://github.com/altercation/solarized) themes by Ethan Schoonover (MIT License)
|
||||
|
||||
## Fonts
|
||||
|
||||
The font used for the main novelWriter logo, mimetype and text banners is:
|
||||
|
||||
* Pridi by Cadson Demak (Open Font License, Version 1.1)
|
||||
|
||||
## Special Mentions
|
||||
|
||||
Additional thanks to @johnblommers who was an early user and who has provided a lot of very useful
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# novelWriter
|
||||
|
||||
[](https://github.com/vkbo/novelWriter/releases)
|
||||
[](https://github.com/vkbo/novelWriter/actions/workflows/test_linux.yml)
|
||||
[](https://github.com/vkbo/novelWriter/actions/workflows/test_win.yml)
|
||||
[](https://github.com/vkbo/novelWriter/actions/workflows/test_mac.yml)
|
||||
[](https://github.com/vkbo/novelWriter/actions)
|
||||
[](https://codecov.io/gh/vkbo/novelWriter)
|
||||
[](https://github.com/vkbo/novelWriter/actions/workflows/test_mac.yml)
|
||||
[](https://github.com/vkbo/novelWriter/actions)
|
||||
[](https://codecov.io/gh/vkbo/novelWriter)
|
||||
|
||||
<img align="left" style="margin: 0 16px 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/setup/novelwriter.png">
|
||||
<img align="left" style="margin: 0 0 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/setup/novelwriter_readme.png">
|
||||
|
||||
novelWriter is a plain text editor designed for writing novels assembled from many smaller text
|
||||
documents. It uses a minimal formatting syntax inspired by Markdown, and adds a meta data syntax
|
||||
|
||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
@@ -25,19 +25,19 @@ Main Window Shortcuts
|
||||
":kbd:`F8`", "Toggle :guilabel:`Focus Mode`"
|
||||
":kbd:`F9`", "Re-build the project index"
|
||||
":kbd:`F11`", "Toggle full screen mode"
|
||||
":kbd:`Alt+1`", "Switch focus to the project tree (Windows :kbd:`Ctrl+Alt+1`)"
|
||||
":kbd:`Alt+2`", "Switch focus to document editor (Windows :kbd:`Ctrl+Alt+2`)"
|
||||
":kbd:`Alt+3`", "Switch focus to document viewer (Windows :kbd:`Ctrl+Alt+3`)"
|
||||
":kbd:`Alt+4`", "Switch focus to outline view (Windows :kbd:`Ctrl+Alt+4`)"
|
||||
":kbd:`Ctrl+,`", "Open the :guilabel:`Preferences` dialog"
|
||||
":kbd:`Ctrl+E`", "Switch focus to the document editor"
|
||||
":kbd:`Ctrl+T`", "Switch focus to the project/novel tree"
|
||||
":kbd:`Ctrl+Q`", "Exit novelWriter"
|
||||
":kbd:`Ctrl+Shift+,`", "Open the :guilabel:`Project Settings` dialog"
|
||||
":kbd:`Ctrl+Shift+O`", "Open a project"
|
||||
":kbd:`Ctrl+Shift+S`", "Save the current project"
|
||||
":kbd:`Ctrl+Shift+T`", "Switch focus to the outline view"
|
||||
":kbd:`Ctrl+Shift+W`", "Close the current project"
|
||||
":kbd:`Shift+F1`", "Open the local user manual (PDF) if it is available"
|
||||
":kbd:`Shift+F6`", "Open the :guilabel:`Project Details` dialog"
|
||||
|
||||
|
||||
.. _a_kb_tree:
|
||||
|
||||
Project Tree Shortcuts
|
||||
@@ -46,21 +46,21 @@ Project Tree Shortcuts
|
||||
.. csv-table::
|
||||
:header: "Shortcut", "Description"
|
||||
|
||||
":kbd:`F2`", "Edit the label of the selected item"
|
||||
":kbd:`Return`", "Open the selected document in the editor"
|
||||
":kbd:`Alt+Up`", "Jump or go to the previous item at same level in the tree"
|
||||
":kbd:`Alt+Down`", "Jump or go to the next item at same level in the tree"
|
||||
":kbd:`Alt+Left`", "Jump to the parent item in the tree"
|
||||
":kbd:`Alt+Right`", "Jump to the first child item in the project tree"
|
||||
":kbd:`Ctrl+.`", "Open the context menu on the selected item"
|
||||
":kbd:`Ctrl+L`", "Open the :guilabel:`Quick Links` menu"
|
||||
":kbd:`Ctrl+N`", "Open the :guilabel:`Create New Item` menu"
|
||||
":kbd:`Ctrl+O`", "Open selected document"
|
||||
":kbd:`Ctrl+R`", "Open the selected document in the viewer"
|
||||
":kbd:`Ctrl+Up`", "Move selected item one step up in the tree"
|
||||
":kbd:`Ctrl+Down`", "Move selected item one step down in the tree"
|
||||
":kbd:`Ctrl+Shift+Z`", "Undo the last move of a project item, if possible"
|
||||
":kbd:`Ctrl+Shift+Del`", "Move the selected item to Trash"
|
||||
":kbd:`F2`", "Edit the label of the selected item"
|
||||
":kbd:`Return`", "Open the selected document in the editor"
|
||||
":kbd:`Alt+Up`", "Jump or go to the previous item at same level in the tree"
|
||||
":kbd:`Alt+Down`", "Jump or go to the next item at same level in the tree"
|
||||
":kbd:`Alt+Left`", "Jump to the parent item in the tree"
|
||||
":kbd:`Alt+Right`", "Jump to the first child item in the project tree"
|
||||
":kbd:`Ctrl+.`", "Open the context menu on the selected item"
|
||||
":kbd:`Ctrl+L`", "Open the :guilabel:`Quick Links` menu"
|
||||
":kbd:`Ctrl+N`", "Open the :guilabel:`Create New Item` menu"
|
||||
":kbd:`Ctrl+O`", "Open selected document"
|
||||
":kbd:`Ctrl+R`", "Open the selected document in the viewer"
|
||||
":kbd:`Ctrl+Up`", "Move selected item one step up in the tree"
|
||||
":kbd:`Ctrl+Down`", "Move selected item one step down in the tree"
|
||||
":kbd:`Ctrl+Del`", "Move the selected item to Trash"
|
||||
":kbd:`Ctrl+Shift+Z`", "Undo the last move of a project item, if possible"
|
||||
|
||||
|
||||
.. _a_kb_editor:
|
||||
@@ -134,6 +134,7 @@ Other Editor Shortcuts
|
||||
":kbd:`Ctrl+Shift+A`", "Select all text in the current paragraph"
|
||||
":kbd:`Ctrl+Shift+I`", "Import text to the current document from a text file"
|
||||
|
||||
|
||||
.. _a_kb_ins:
|
||||
|
||||
Insert Shortcuts
|
||||
|
||||
@@ -33,26 +33,8 @@ from novelwriter.error import exceptionHandler, logException
|
||||
from novelwriter.config import Config
|
||||
from novelwriter.shared import SharedData
|
||||
|
||||
##
|
||||
# Version Scheme
|
||||
# ================
|
||||
# Generally follows PEP 440
|
||||
# Hex Version:
|
||||
# - Digit 1,2 : Major Version (01-ff)
|
||||
# - Digit 3,4 : Minor Version (01-ff)
|
||||
# - Digit 5,6 : Patch Version (01-ff)
|
||||
# - Digit 7 : Release Type (a: alpha, b: beta, c: candidate, f: final)
|
||||
# - Digit 8 : Release Number (0-f)
|
||||
#
|
||||
# Example : Full Short Description
|
||||
# --------------------------------------------------------------------
|
||||
# 0x010200a0 : 1.2-alpha0 1.2a0 Use while developing next release
|
||||
# 0x010200a1 : 1.2-alpha1 1.2a1 First alpha release
|
||||
# 0x010200b1 : 1.2-beta1 1.2b1 First beta release
|
||||
# 0x010200c1 : 1.2-rc1 1.2rc1 First release candidate
|
||||
# 0x010200f0 : 1.2.0 1.2.0 Final release
|
||||
# 0x010201f0 : 1.2.1 1.2.1 Patch release
|
||||
##
|
||||
# Package Meta
|
||||
# ============
|
||||
|
||||
__package__ = "novelwriter"
|
||||
__copyright__ = "Copyright 2018–2023, Veronica Berglyd Olsen"
|
||||
@@ -60,9 +42,9 @@ __license__ = "GPLv3"
|
||||
__author__ = "Veronica Berglyd Olsen"
|
||||
__maintainer__ = "Veronica Berglyd Olsen"
|
||||
__email__ = "code@vkbo.net"
|
||||
__version__ = "2.2-alpha1"
|
||||
__hexversion__ = "0x020200a1"
|
||||
__date__ = "2023-09-01"
|
||||
__version__ = "2.2-beta1"
|
||||
__hexversion__ = "0x020200b1"
|
||||
__date__ = "2023-11-12"
|
||||
__status__ = "Stable"
|
||||
__domain__ = "novelwriter.io"
|
||||
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
"Synopsis": "Samandrag",
|
||||
"Comment": "Kommentar",
|
||||
"Notes": "Notat",
|
||||
"Tag": "Knagg",
|
||||
"Point of View": "Perspektiv",
|
||||
"Focus": "Fokus",
|
||||
"Characters": "Karakterar",
|
||||
"Plot": "Plott",
|
||||
"Timeline": "Tidslinje",
|
||||
"Locations": "Lokasjonar",
|
||||
"Objects": "Objekt",
|
||||
"Entities": "Eining",
|
||||
"Custom": "Anna",
|
||||
"0": "null",
|
||||
"1": "ein",
|
||||
"2": "to",
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 167 KiB |
@@ -1,207 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 270.93333 270.93334"
|
||||
height="1024"
|
||||
width="1024">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient2515">
|
||||
<stop
|
||||
style="stop-color:#f7f7f7;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2511" />
|
||||
<stop
|
||||
style="stop-color:#e5e5e5;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2513" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
x="60.484347"
|
||||
y="205.79962"
|
||||
width="130.57787"
|
||||
height="65.785888"
|
||||
id="rect852" />
|
||||
<rect
|
||||
id="rect1474"
|
||||
height="28.631245"
|
||||
width="177.70083"
|
||||
y="18.019813"
|
||||
x="13.463703" />
|
||||
<rect
|
||||
id="rect852-7"
|
||||
height="65.785889"
|
||||
width="130.57787"
|
||||
y="205.79962"
|
||||
x="60.484348" />
|
||||
<rect
|
||||
id="rect1544"
|
||||
height="65.785889"
|
||||
width="130.57787"
|
||||
y="205.79962"
|
||||
x="60.484348" />
|
||||
<rect
|
||||
x="60.484348"
|
||||
y="205.79962"
|
||||
width="130.57787"
|
||||
height="65.785889"
|
||||
id="rect1570" />
|
||||
<rect
|
||||
x="13.463703"
|
||||
y="18.019813"
|
||||
width="177.70083"
|
||||
height="28.631245"
|
||||
id="rect1587" />
|
||||
<filter
|
||||
id="filter1444"
|
||||
style="color-interpolation-filters:sRGB">
|
||||
<feFlood
|
||||
id="feFlood1434"
|
||||
result="flood"
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="0.498039" />
|
||||
<feComposite
|
||||
id="feComposite1436"
|
||||
result="composite1"
|
||||
operator="in"
|
||||
in2="SourceGraphic"
|
||||
in="flood" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1438"
|
||||
result="blur"
|
||||
stdDeviation="3"
|
||||
in="composite1" />
|
||||
<feOffset
|
||||
id="feOffset1440"
|
||||
result="offset"
|
||||
dy="6"
|
||||
dx="6" />
|
||||
<feComposite
|
||||
id="feComposite1442"
|
||||
result="composite2"
|
||||
operator="over"
|
||||
in2="offset"
|
||||
in="SourceGraphic" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1456"
|
||||
style="color-interpolation-filters:sRGB">
|
||||
<feFlood
|
||||
id="feFlood1446"
|
||||
result="flood"
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="0.498039" />
|
||||
<feComposite
|
||||
id="feComposite1448"
|
||||
result="composite1"
|
||||
operator="in"
|
||||
in2="SourceGraphic"
|
||||
in="flood" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1450"
|
||||
result="blur"
|
||||
stdDeviation="3"
|
||||
in="composite1" />
|
||||
<feOffset
|
||||
id="feOffset1452"
|
||||
result="offset"
|
||||
dy="6"
|
||||
dx="6" />
|
||||
<feComposite
|
||||
id="feComposite1454"
|
||||
result="composite2"
|
||||
operator="over"
|
||||
in2="offset"
|
||||
in="SourceGraphic" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient2515"
|
||||
id="radialGradient2517"
|
||||
cx="605.42725"
|
||||
cy="623.97186"
|
||||
fx="605.42725"
|
||||
fy="623.97186"
|
||||
r="410"
|
||||
gradientTransform="matrix(2.4110274,1.7617596,-0.719517,0.98463462,-776.23441,-1372.7537)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer3"
|
||||
style="display:inline">
|
||||
<path
|
||||
clip-path="none"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 192,22 c -44.32,0 -80,35.680002 -80,80 v 820 c 0,44.32002 35.68,80 80,80 h 640 c 44.31999,0 80,-35.67998 80,-80 V 213.44727 L 720.55469,22 Z"
|
||||
style="fill:url(#radialGradient2517);fill-opacity:1;stroke:#999999;stroke-width:20;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect1481" />
|
||||
<path
|
||||
id="path1490"
|
||||
style="display:inline;fill:#e0e0e0;fill-opacity:1;stroke:#999999;stroke-width:5.29167;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 241.30037,56.474215 190.64713,5.8204593 V 35.307549 c 0,11.726333 9.44034,21.166666 21.16667,21.166666 z" />
|
||||
<g
|
||||
aria-label=".nwx"
|
||||
transform="matrix(1.7893481,0,0,1.782571,-45.41919,-191.90571)"
|
||||
id="text850"
|
||||
style="font-size:5.64444px;line-height:1.25;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans, Normal';letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect852);display:inline;fill:#666666;fill-opacity:1">
|
||||
<path
|
||||
d="m 63.088865,241.76928 q 0,-1.36426 0.909505,-2.27376 0.930175,-0.93018 2.480467,-0.93018 1.550291,0 2.459796,0.93018 0.909504,0.9095 0.909504,2.27376 0,1.34359 -0.909504,2.27376 -0.909505,0.9095 -2.459796,0.9095 -1.550292,0 -2.480467,-0.9095 -0.909505,-0.93017 -0.909505,-2.27376 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path856" />
|
||||
<path
|
||||
d="m 84.75161,226.78312 q -2.542479,0 -3.782712,2.14974 v 15.79231 h -5.973791 v -22.36554 h 5.601721 l 0.206705,2.56314 q 2.397785,-2.97656 6.428544,-2.97656 3.162595,0 5.105627,1.88103 1.943033,1.88102 1.963703,6.55256 v 14.34537 h -5.994461 v -14.26269 q 0,-2.14973 -0.930175,-2.91454 -0.909505,-0.76482 -2.625161,-0.76482 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path858" />
|
||||
<path
|
||||
d="m 126.96089,222.35963 -5.68441,22.36554 h -5.00227 l -4.25814,-14.07665 -4.23746,14.07665 h -4.9816 l -5.705078,-22.36554 h 5.767088 l 2.95589,14.22134 4.05143,-14.22134 h 4.32014 l 4.09277,14.20067 2.91455,-14.20067 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path860" />
|
||||
<path
|
||||
d="m 134.77436,222.35963 3.65869,6.65592 3.76204,-6.65592 h 6.3872 l -6.36653,10.93472 6.63525,11.43082 h -6.3872 l -4.01009,-7.04866 -3.94808,7.04866 h -6.42854 l 6.63525,-11.43082 -6.36654,-10.93472 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path862" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g2509"
|
||||
transform="matrix(1.2371037,0,0,1.2371037,206.3182,37.341857)">
|
||||
<path
|
||||
transform="matrix(0.16895991,0,0,0.18329422,-134.6942,-19.491243)"
|
||||
id="rect1406-1"
|
||||
style="display:inline;fill:#a11212;fill-opacity:1;stroke:#7e1b1b;stroke-width:9.99999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1444)"
|
||||
d="m 145.18984,56.414257 c -44.32,0 -79.999998,35.68 -79.999998,80.000003 v 199.99805 a 279.99999,279.99999 0 0 0 0,0.002 279.99999,279.99999 0 0 0 0,0.0195 v 199.98242 c 0,44.31999 35.679998,80 79.999998,80 44.32,0 80,-35.68001 80,-80 v -200 a 120,120 0 0 1 120,-120 120,120 0 0 1 120,120 v 282.83592 h 160 V 336.41621 a 279.99999,279.99999 0 0 0 -280,-280.000003 279.99999,279.99999 0 0 0 -0.81055,0 279.99999,279.99999 0 0 0 -133.46484,34.29688 c -14.4245,-20.75262 -38.42829,-34.29883 -65.72461,-34.29883 z M 376.65078,619.64082 c -0.62174,0.0972 -1.2172,0.22317 -1.7793,0.37696 0.5621,-0.15379 1.15756,-0.27973 1.7793,-0.37696 z m -1.7793,0.37696 c -0.5621,0.15378 -1.09071,0.33534 -1.58008,0.53906 0.48937,-0.20372 1.01798,-0.38528 1.58008,-0.53906 z m 338.85742,-0.37696 c 0.62174,0.0972 1.2172,0.22317 1.7793,0.37696 -0.5621,-0.15379 -1.15756,-0.27973 -1.7793,-0.37696 z m 1.7793,0.37696 c 0.5621,0.15378 1.09071,0.33534 1.58008,0.53906 -0.48938,-0.20372 -1.01798,-0.38528 -1.58008,-0.53906 z" />
|
||||
<path
|
||||
transform="matrix(0.16895991,0,0,0.18329422,-134.6942,-19.491243)"
|
||||
id="path897"
|
||||
style="display:inline;fill:#730d0d;fill-opacity:1;stroke:#590d0d;stroke-width:9.99999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1456)"
|
||||
d="m 465.18984,619.25215 v 0.16406 h -85.58398 c -5.49302,0 -9.91602,2.2329 -9.91602,5.00586 0,0.52294 0.15913,1.02696 0.45117,1.5 h -0.44531 c 0.30916,2.03607 1.24102,3.99791 2.81641,5.57227 l 165.59961,165.49414 c 3.92061,3.91808 10.23371,3.91812 14.15429,0 L 717.86757,631.49434 c 1.57569,-1.57463 2.50725,-3.53578 2.81641,-5.57227 h -0.44531 c 0.29216,-0.47304 0.45117,-0.97706 0.45117,-1.5 0,-2.77296 -4.42304,-5.00586 -9.91602,-5.00586 h -85.58398 v -0.16406 z" />
|
||||
<rect
|
||||
style="display:inline;fill:#c32222;fill-opacity:1;stroke:none;stroke-width:1.75982;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="rect914"
|
||||
width="6.7583966"
|
||||
height="82.482399"
|
||||
x="-118.27303"
|
||||
y="1.2969339"
|
||||
rx="3.3791983"
|
||||
ry="3.6658845" />
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="256" height="256" version="1.1" viewBox="0 0 67.733 67.733" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m12.869 1.7145c-2.9081 0-5.2493 2.3412-5.2493 5.2493v53.806c0 2.9081 2.3412 5.2493 5.2493 5.2493h41.995c2.9081 0 5.2493-2.3412 5.2493-5.2493v-46.493l-12.562-12.562z" fill="#ececec" stroke="#999" stroke-linejoin="round" stroke-width="1.3123"/>
|
||||
<path d="m60.113 14.277-12.562-12.562v7.3128c0 2.9081 2.3412 5.2493 5.2493 5.2493z" fill="#e0e0e0" stroke="#999" stroke-linejoin="round" stroke-width="1.3123"/>
|
||||
<path d="m34.617 50.796c-0.31888 0-0.57319 0.09267-0.7628 0.27797-0.1853 0.18099-0.27797 0.41156-0.27797 0.69167 0 0.2758 0.09268 0.50636 0.27797 0.69166 0.18961 0.18099 0.44392 0.27144 0.7628 0.27144s0.57306-0.09044 0.76266-0.27144c0.18961-0.1853 0.28438-0.41587 0.28438-0.69166 0-0.28011-0.09477-0.51067-0.28438-0.69167-0.18961-0.1853-0.44378-0.27797-0.76266-0.27797zm-21.512 0.36204v9.4116h1.9456v-3.3226h1.7323c1.1118 0 1.978-0.26924 2.5985-0.8079 0.62484-0.53867 0.93722-1.2627 0.93722-2.172 0-0.90065-0.31238-1.644-0.93722-2.2301-0.62053-0.58607-1.4867-0.87903-2.5985-0.87903zm38.395 0.69154v1.7259h-1.0278v1.364h1.0278v3.6909c0 0.74121 0.1809 1.2713 0.54287 1.5902 0.36198 0.31889 0.84896 0.47828 1.4609 0.47828 0.40076 0 0.77566-0.0582 1.1247-0.17455v-1.4156c-0.17237 0.03448-0.3534 0.05178-0.543 0.05178-0.23701 0-0.41581-0.04745-0.53647-0.14226-0.12066-0.09481-0.18096-0.28867-0.18096-0.58171v-3.497h1.1893v-1.364h-1.1893v-1.7259zm-36.45 0.87916h1.7323c0.55589 0 0.95662 0.15729 1.2022 0.47188 0.24562 0.31027 0.36845 0.67008 0.36845 1.0795 0 0.40939-0.12283 0.7456-0.36845 1.0085-0.24562 0.26287-0.64636 0.39421-1.2022 0.39421h-1.7323zm9.8574 0.71755c-0.67224 0-1.1915 0.32101-1.5578 0.96311l-0.05818-0.83392h-1.7646v6.9942h1.8744v-4.583c0.21977-0.47834 0.68093-0.71756 1.3833-0.71756l0.67872 0.05178 0.01934-1.7906c-0.14651-0.05602-0.33829-0.08395-0.5753-0.08395zm4.2402 0c-1.0514 0-1.8659 0.34041-2.4433 1.0213-0.57743 0.67657-0.86609 1.5233-0.86609 2.5403v0.21979c0.01724 0.99546 0.31244 1.8228 0.88557 2.4822 0.57743 0.65933 1.3897 0.98899 2.4368 0.98899 1.0558 0 1.8702-0.33831 2.4433-1.0149 0.57312-0.67657 0.85968-1.5233 0.85968-2.5403v-0.13572c0-1.017-0.28655-1.8638-0.85968-2.5403-0.57313-0.68088-1.3919-1.0213-2.4563-1.0213zm10.93 0c-0.99974 0-1.7948 0.33819-2.3851 1.0148-0.58605 0.67657-0.87903 1.5429-0.87903 2.5987v0.27144c0.0043 0.93082 0.31236 1.7259 0.92427 2.3853 0.61191 0.65502 1.4608 0.98246 2.5467 0.98246 0.68085 0 1.2498-0.13134 1.7065-0.39421 0.45678-0.26287 0.79721-0.55386 1.0213-0.87275l-0.9244-1.0213c-0.41368 0.52143-0.97605 0.78214-1.6871 0.78214-0.47832 0-0.86398-0.13789-1.157-0.41369-0.29303-0.28011-0.47183-0.64634-0.53647-1.0988h4.4536v-0.79509c0-1.0342-0.26296-1.8659-0.78868-2.4951-0.52572-0.62917-1.2906-0.94376-2.2947-0.94376zm7.0715 0c-1.0816 0-1.8896 0.34251-2.424 1.0277-0.53434 0.68519-0.8015 1.5212-0.8015 2.508v0.18108c0 0.99115 0.26716 1.8293 0.8015 2.5145 0.53865 0.68088 1.3488 1.0213 2.4304 1.0213 0.81013 0 1.4932-0.23909 2.0491-0.71743 0.5602-0.47834 0.84453-1.0881 0.85314-1.8293h-1.7581c-0.0043 0.31889-0.11639 0.57306-0.33616 0.76267-0.21977 0.1853-0.4999 0.27797-0.84033 0.27797-0.55158 0-0.91141-0.20251-1.0795-0.6076-0.16375-0.40939-0.24555-0.88337-0.24555-1.422 0-0.65933 0.08403-1.1916 0.25208-1.5967 0.16806-0.40939 0.52357-0.614 1.0665-0.614 0.37921 0 0.67009 0.12059 0.87262 0.36192 0.20253 0.23702 0.30596 0.52578 0.31027 0.86622h1.7581c-0.0086-0.84032-0.2822-1.5061-0.82085-1.9973-0.53434-0.49127-1.2303-0.73691-2.0878-0.73691zm-13.432 0.12918v7.382c0 0.60331-0.28223 0.90492-0.84674 0.90492-0.19822 0-0.39643-0.01939-0.59465-0.05818v1.4803c0.29734 0.07326 0.60972 0.10983 0.93722 0.10983 0.75411 0 1.338-0.20894 1.7517-0.62695 0.41799-0.41801 0.62707-1.0214 0.62707-1.81v-7.382zm-4.57 1.3769c0.53003 0 0.90284 0.20462 1.1183 0.614 0.21977 0.40508 0.32962 0.88561 0.32962 1.4415 0 0.66364-0.10985 1.1937-0.32962 1.5902-0.21546 0.39646-0.58395 0.59465-1.1054 0.59465-0.53004 0-0.90494-0.19819-1.1247-0.59465-0.21546-0.40077-0.32321-0.88562-0.32321-1.4545 0-0.65072 0.10775-1.1786 0.32321-1.5836 0.21546-0.40508 0.58604-0.6076 1.1118-0.6076zm10.917 0c0.43092 0 0.7454 0.12492 0.94362 0.37486 0.20254 0.24563 0.30819 0.5559 0.3168 0.93081v0.14226h-2.5985c0.06464-0.43094 0.20477-0.78 0.42023-1.0472 0.21546-0.26718 0.52142-0.40075 0.91786-0.40075z" fill="#666" stroke-width=".1773" style="shape-inside:url(#rect2964);white-space:pre"/>
|
||||
<path d="m16.933 38.766h1.5478q1.2383 0 1.7336-0.49531 0.55722-0.55722 0.55722-1.7955v-15.912q0-1.2383-0.49531-1.7336-0.49531-0.55722-1.7336-0.55722h-1.3002v-4.6435h7.9249l0.37148 4.1482q2.1051-2.6623 4.7673-3.8386 2.7242-1.2383 6.5628-1.2383 9.9062 0 9.9062 10.278v13.497q0 1.2383 0.49531 1.7955 0.55722 0.49531 1.7955 0.49531h1.7336v4.5816h-10.649v-20.308q0-3.0957-1.3002-4.4578-1.3002-1.424-4.2101-1.424-3.1576 0-5.2007 1.7955-2.0431 1.7955-2.0431 5.015v12.507q0 1.2383 0.43339 1.7955 0.49531 0.49531 1.6717 0.49531h1.6098v4.5816h-14.178z" fill="#346598" stroke-linecap="round" stroke-width=".16123"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -61,6 +61,13 @@ more contributions are listed on the project's <a href="https://crowdin.com/proj
|
||||
<a href="https://github.com/altercation/solarized">Solarized</a> themes by Ethan Schoonover (MIT License)
|
||||
</p>
|
||||
|
||||
<h3>Fonts</h3>
|
||||
|
||||
<p>The font used for the main novelWriter logo, mimetype and text banners is:</p>
|
||||
<p>
|
||||
Pridi by Cadson Demak (Open Font License, Version 1.1)
|
||||
</p>
|
||||
|
||||
<h3>Special Mentions</h3>
|
||||
|
||||
<p>Additional thanks to <a href="https://github.com/johnblommers">@johnblommers</a> who was an early user and who has provided a lot
|
||||
|
||||
@@ -2,46 +2,16 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Release Notes for 2.1</h2>
|
||||
<p><i>Released on 17 October 2023</i></p>
|
||||
<h2>Release Notes for 2.2 Beta 1</h2>
|
||||
<p><i>Released on 11 November 2023</i></p>
|
||||
|
||||
<p>Scroll down for <a href="#patch">Patch Notes</a></p>
|
||||
|
||||
<p>The primary focus of this release has been a complete redesign of the Build Tool, that is, the
|
||||
tool that assembles your project into a manuscript document. The new tool, called the "Manuscript
|
||||
Build Tool" allows you to define multiple build definitions for your project. The build definitions
|
||||
are edited in a new Manuscript Build Settings dialog, with a lot more options than the old tool.</p>
|
||||
<p>The reason for this redesign is a long list of feature requests that could not easily be
|
||||
accommodated in the old, much simpler tool. Far from all the features have been added yet, but now
|
||||
that the new tool is in place, they will be gradually added in the coming releases.</p>
|
||||
<p>The key feature added in this release is the extended control you now have for selecting exactly
|
||||
what part of your project is included in a given build definition. You have the same filters for
|
||||
selecting documents and notes, and turning on or off root folders as before, but you can now easily
|
||||
override on a per-document basis what is included or excluded in addition to the filter.</p>
|
||||
<p>A second major improvement is a better tool to format your manuscript headings. You no longer
|
||||
have to look up formatting codes and add them manually. Instead, there is now a heading format
|
||||
editor in the Build Settings dialog for creating the header format, with syntax highlighting
|
||||
included.</p>
|
||||
|
||||
<h3>Other Changes</h3>
|
||||
<p>Among other features is a new option to duplicate documents and folders in the project tree. The
|
||||
duplicate feature is available from the right-click menu. A proper light colour theme has also been
|
||||
added. In most cases it will be the same as the default theme, depending on your platform.</p>
|
||||
<p>There are other, minor improvements as well, and a lot of code improvements under the hood. For
|
||||
a full list of changes, see the detailed changelogs.</p>
|
||||
<p>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.</p>
|
||||
<p>Please check the changelog for an overview of changes. The full release notes will be added to
|
||||
the final release.</p>
|
||||
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
<a name="patch"></a><h2>Patch Notes</h2>
|
||||
|
||||
<h3>Patch 2.2.1 – 5 November 2023</h3>
|
||||
|
||||
<p>This is a patch release that fixes a layout issue and internationalisation issues with the new
|
||||
Manuscript Build tool. It also fixes a number of issues related to bugs in the underlying Qt
|
||||
framework that affects drag and drop functionality in the project tree. These issues were mostly
|
||||
only affecting Debian Linux package releases.</p>
|
||||
<p>Other, minor issues related to updating the editor on colour theme change and project word list
|
||||
changes have been fixed as well. See the full changelog for more details.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -148,6 +148,7 @@ class Config:
|
||||
|
||||
self.autoScroll = False # Typewriter-like scrolling
|
||||
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
||||
self.scrollPastEnd = True # Scroll past end of document, and centre cursor
|
||||
|
||||
self.wordCountTimer = 5.0 # Interval for word count update in seconds
|
||||
self.incNotesWCount = True # The status bar word count includes notes
|
||||
@@ -577,6 +578,7 @@ class Config:
|
||||
self.doReplaceDots = conf.rdBool(sec, "repdots", self.doReplaceDots)
|
||||
self.autoScroll = conf.rdBool(sec, "autoscroll", self.autoScroll)
|
||||
self.autoScrollPos = conf.rdInt(sec, "autoscrollpos", self.autoScrollPos)
|
||||
self.scrollPastEnd = conf.rdBool(sec, "scrollpastend", self.scrollPastEnd)
|
||||
self.fmtSQuoteOpen = conf.rdStr(sec, "fmtsquoteopen", self.fmtSQuoteOpen)
|
||||
self.fmtSQuoteClose = conf.rdStr(sec, "fmtsquoteclose", self.fmtSQuoteClose)
|
||||
self.fmtDQuoteOpen = conf.rdStr(sec, "fmtdquoteopen", self.fmtDQuoteOpen)
|
||||
@@ -701,6 +703,7 @@ class Config:
|
||||
"repdots": str(self.doReplaceDots),
|
||||
"autoscroll": str(self.autoScroll),
|
||||
"autoscrollpos": str(self.autoScrollPos),
|
||||
"scrollpastend": str(self.scrollPastEnd),
|
||||
"fmtsquoteopen": str(self.fmtSQuoteOpen),
|
||||
"fmtsquoteclose": str(self.fmtSQuoteClose),
|
||||
"fmtdquoteopen": str(self.fmtDQuoteOpen),
|
||||
|
||||
@@ -327,8 +327,7 @@ class nwQuotes:
|
||||
|
||||
|
||||
class nwUnicode:
|
||||
"""Supported unicode character constants and their HTML equivalents.
|
||||
"""
|
||||
"""Supported unicode character constants and their HTML equivalents."""
|
||||
# Unicode Constants
|
||||
# =================
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ SETTINGS_TEMPLATE = {
|
||||
"text.includeKeywords": (bool, False),
|
||||
"text.includeBodyText": (bool, True),
|
||||
"text.addNoteHeadings": (bool, True),
|
||||
"format.buildLang": (str, "en_GB"),
|
||||
"format.textFont": (str, CONFIG.textFont),
|
||||
"format.textSize": (int, 12),
|
||||
"format.lineHeight": (float, 1.15, 0.75, 3.0),
|
||||
@@ -107,7 +106,6 @@ SETTINGS_LABELS = {
|
||||
"text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"),
|
||||
|
||||
"format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"),
|
||||
"format.buildLang": QT_TRANSLATE_NOOP("Builds", "Language"),
|
||||
"format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"),
|
||||
"format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"),
|
||||
"format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"),
|
||||
@@ -158,6 +156,7 @@ class BuildSettings:
|
||||
self._uuid = str(uuid.uuid4())
|
||||
self._path = Path.home()
|
||||
self._build = ""
|
||||
self._order = 0
|
||||
self._format = nwBuildFmt.ODT
|
||||
self._skipRoot = set()
|
||||
self._excluded = set()
|
||||
@@ -166,20 +165,32 @@ class BuildSettings:
|
||||
self._changed = False
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def fromDict(cls, data: dict) -> BuildSettings:
|
||||
"""Create a build settings object from a dict."""
|
||||
cls = BuildSettings()
|
||||
cls.unpack(data)
|
||||
return cls
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""The build name."""
|
||||
"""Return the build name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def buildID(self) -> str:
|
||||
"""The build ID as a UUID."""
|
||||
"""Return the build ID as a UUID."""
|
||||
return self._uuid
|
||||
|
||||
@property
|
||||
def order(self) -> int:
|
||||
"""Return the build order."""
|
||||
return self._order
|
||||
|
||||
@property
|
||||
def lastPath(self) -> Path:
|
||||
"""The last used build path."""
|
||||
@@ -253,6 +264,12 @@ class BuildSettings:
|
||||
self._uuid = value
|
||||
return
|
||||
|
||||
def setOrder(self, value: int) -> None:
|
||||
"""Set the build order."""
|
||||
if isinstance(value, int):
|
||||
self._order = value
|
||||
return
|
||||
|
||||
def setLastPath(self, path: Path | str | None) -> None:
|
||||
"""Set the last used build path."""
|
||||
if isinstance(path, str):
|
||||
@@ -400,6 +417,7 @@ class BuildSettings:
|
||||
"uuid": self._uuid,
|
||||
"path": str(self._path),
|
||||
"build": self._build,
|
||||
"order": self._order,
|
||||
"format": self._format.name,
|
||||
"settings": self._settings.copy(),
|
||||
"content": {
|
||||
@@ -411,14 +429,15 @@ class BuildSettings:
|
||||
|
||||
def unpack(self, data: dict) -> None:
|
||||
"""Unpack a dictionary and populate the class."""
|
||||
content = data.get("content", {})
|
||||
settings = data.get("settings", {})
|
||||
content = data.get("content", {})
|
||||
included = content.get("included", [])
|
||||
excluded = content.get("excluded", [])
|
||||
skipRoot = content.get("skipRoot", [])
|
||||
|
||||
self.setName(data.get("name", ""))
|
||||
self.setBuildID(data.get("uuid", ""))
|
||||
self.setOrder(data.get("order", 0))
|
||||
self.setLastPath(data.get("path", None))
|
||||
self.setLastBuildName(data.get("build", ""))
|
||||
|
||||
@@ -455,9 +474,9 @@ class BuildCollection:
|
||||
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
self._builds = {}
|
||||
self._lastBuild = ""
|
||||
self._defaultBuild = ""
|
||||
self._builds: dict[str, BuildSettings] = {}
|
||||
self._loadCollection()
|
||||
return
|
||||
|
||||
@@ -485,21 +504,19 @@ class BuildCollection:
|
||||
|
||||
def getBuild(self, buildID: str) -> BuildSettings | None:
|
||||
"""Get a specific build settings object."""
|
||||
if buildID not in self._builds:
|
||||
return None
|
||||
build = BuildSettings()
|
||||
build.unpack(self._builds[buildID])
|
||||
return build
|
||||
return self._builds.get(buildID, None)
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setLastBuild(self, buildID: str) -> None:
|
||||
def setBuildsState(self, lastBuild: str, order: list[str]) -> None:
|
||||
"""Set the last active build id."""
|
||||
if buildID != self._lastBuild:
|
||||
self._lastBuild = buildID
|
||||
self._saveCollection()
|
||||
for i, key in enumerate(order):
|
||||
if build := self._builds.get(key):
|
||||
build.setOrder(i)
|
||||
self._lastBuild = lastBuild
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
def setDefaultBuild(self, buildID: str) -> None:
|
||||
@@ -512,8 +529,7 @@ class BuildCollection:
|
||||
def setBuild(self, build: BuildSettings) -> None:
|
||||
"""Set build settings data in the collection."""
|
||||
if isinstance(build, BuildSettings):
|
||||
buildID = build.buildID
|
||||
self._builds[buildID] = build.pack()
|
||||
self._builds[build.buildID] = build
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
@@ -522,15 +538,15 @@ class BuildCollection:
|
||||
##
|
||||
|
||||
def removeBuild(self, buildID: str) -> None:
|
||||
"""Remove the a build from the collection."""
|
||||
"""Remove a build from the collection."""
|
||||
self._builds.pop(buildID, None)
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
def builds(self) -> Iterable[tuple[str, str]]:
|
||||
"""Iterate over all available builds."""
|
||||
for buildID in self._builds:
|
||||
yield buildID, self._builds[buildID].get("name", "")
|
||||
for buildID, build in sorted(self._builds.items(), key=lambda x: x[1].order):
|
||||
yield buildID, build.name
|
||||
return
|
||||
|
||||
##
|
||||
@@ -569,7 +585,7 @@ class BuildCollection:
|
||||
elif key == "defaultBuild":
|
||||
self._defaultBuild = str(entry)
|
||||
elif isinstance(entry, dict):
|
||||
self._builds[key] = entry
|
||||
self._builds[key] = BuildSettings.fromDict(entry)
|
||||
|
||||
return True
|
||||
|
||||
@@ -581,11 +597,11 @@ class BuildCollection:
|
||||
|
||||
logger.debug("Saving builds file")
|
||||
try:
|
||||
data = {
|
||||
data: dict[str, str | dict] = {
|
||||
"lastBuild": self._lastBuild,
|
||||
"defaultBuild": self._defaultBuild,
|
||||
}
|
||||
data.update(self._builds)
|
||||
data.update({k: b.pack() for k, b in self._builds.items()})
|
||||
with open(buildsFile, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write(jsonEncode({"novelWriter.builds": data}, nmax=4))
|
||||
except Exception:
|
||||
|
||||
@@ -353,11 +353,13 @@ class ProjectBuilder:
|
||||
projName = data.get("projName", lblNewProject)
|
||||
projTitle = data.get("projTitle", lblNewProject)
|
||||
projAuthor = data.get("projAuthor", "")
|
||||
projLang = data.get("projLang", "en_GB")
|
||||
|
||||
project.data.setUuid(None)
|
||||
project.data.setName(projName)
|
||||
project.data.setTitle(projTitle)
|
||||
project.data.setAuthor(projAuthor)
|
||||
project.data.setLanguage(projLang)
|
||||
project.setDefaultStatusImport()
|
||||
project.session.startSession()
|
||||
|
||||
|
||||
@@ -248,13 +248,9 @@ class NWBuildDocument:
|
||||
def _setupBuild(self, bldObj: Tokenizer) -> dict:
|
||||
"""Configure the build object."""
|
||||
# Get Settings
|
||||
buildLang = self._build.getStr("format.buildLang")
|
||||
textFont = self._build.getStr("format.textFont")
|
||||
textSize = self._build.getInt("format.textSize")
|
||||
|
||||
# The language lookup dict is reloaded if needed
|
||||
self._project.setProjectLang(buildLang)
|
||||
|
||||
fontFamily = textFont or CONFIG.textFont
|
||||
bldFont = QFont(fontFamily, textSize)
|
||||
fontInfo = QFontInfo(bldFont)
|
||||
@@ -287,7 +283,7 @@ class NWBuildDocument:
|
||||
|
||||
if isinstance(bldObj, ToOdt):
|
||||
bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
|
||||
bldObj.setLanguage(buildLang)
|
||||
bldObj.setLanguage(self._project.data.language)
|
||||
|
||||
scale = nwLabels.UNIT_SCALE.get(self._build.getStr("format.pageUnit"), 1.0)
|
||||
pW, pH = nwLabels.PAPER_SIZE.get(self._build.getStr("format.pageSize"), (-1.0, -1.0))
|
||||
|
||||
@@ -152,14 +152,14 @@ class NWProjectData:
|
||||
"""Return the initial count of words for novel and note
|
||||
documents.
|
||||
"""
|
||||
return tuple(self._initCounts)
|
||||
return self._initCounts[0], self._initCounts[1]
|
||||
|
||||
@property
|
||||
def currCounts(self) -> tuple[int, int]:
|
||||
"""Return the current count of words for novel and note
|
||||
documents.
|
||||
"""
|
||||
return tuple(self._currCounts)
|
||||
return self._currCounts[0], self._currCounts[1]
|
||||
|
||||
@property
|
||||
def lastHandle(self) -> dict[str, str | None]:
|
||||
|
||||
@@ -456,11 +456,10 @@ class ToHtml(Tokenizer):
|
||||
|
||||
def _formatSynopsis(self, text: str) -> str:
|
||||
"""Apply HTML formatting to synopsis."""
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
sSynop = self._trSynopsis
|
||||
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {text}</p>\n"
|
||||
else:
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
return f"<p class='synopsis'><strong>{sSynop}:</strong> {text}</p>\n"
|
||||
|
||||
def _formatComments(self, text: str) -> str:
|
||||
|
||||
@@ -167,9 +167,6 @@ class Tokenizer(ABC):
|
||||
self._localLookup = self._project.localLookup
|
||||
self.tr = partial(QCoreApplication.translate, "Tokenizer")
|
||||
|
||||
# Cached Translations
|
||||
self._trSynopsis = self.tr("Synopsis")
|
||||
|
||||
# Format RegEx
|
||||
self._rxMarkdown = [
|
||||
(QRegularExpression(nwRegEx.FMT_EI), [0, self.FMT_I_B, 0, self.FMT_I_E]),
|
||||
|
||||
@@ -26,7 +26,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
||||
@@ -43,12 +43,11 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiPreferences(NPagedDialog):
|
||||
|
||||
def __init__(self, mainGui):
|
||||
super().__init__(parent=mainGui)
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiPreferences")
|
||||
self.setObjectName("GuiPreferences")
|
||||
|
||||
self.setWindowTitle(self.tr("Preferences"))
|
||||
|
||||
self.tabGeneral = GuiPreferencesGeneral(self)
|
||||
@@ -93,26 +92,27 @@ class GuiPreferences(NPagedDialog):
|
||||
##
|
||||
|
||||
@property
|
||||
def updateTheme(self):
|
||||
def updateTheme(self) -> bool:
|
||||
return self._updateTheme
|
||||
|
||||
@property
|
||||
def updateSyntax(self):
|
||||
def updateSyntax(self) -> bool:
|
||||
return self._updateSyntax
|
||||
|
||||
@property
|
||||
def needsRestart(self):
|
||||
def needsRestart(self) -> bool:
|
||||
return self._needsRestart
|
||||
|
||||
@property
|
||||
def refreshTree(self):
|
||||
def refreshTree(self) -> bool:
|
||||
return self._refreshTree
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _doSave(self):
|
||||
@pyqtSlot()
|
||||
def _doSave(self) -> None:
|
||||
"""Trigger all the save functions in the tabs, and collect the
|
||||
status of the saves.
|
||||
"""
|
||||
@@ -132,9 +132,9 @@ class GuiPreferences(NPagedDialog):
|
||||
|
||||
return
|
||||
|
||||
def _doClose(self):
|
||||
"""Close the preferences without saving the changes.
|
||||
"""
|
||||
@pyqtSlot()
|
||||
def _doClose(self) -> None:
|
||||
"""Close the preferences without saving the changes."""
|
||||
self._saveWindowSize()
|
||||
self.reject()
|
||||
return
|
||||
@@ -143,9 +143,8 @@ class GuiPreferences(NPagedDialog):
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _saveWindowSize(self):
|
||||
"""Save the dialog window size.
|
||||
"""
|
||||
def _saveWindowSize(self) -> None:
|
||||
"""Save the dialog window size."""
|
||||
CONFIG.setPreferencesWinSize(self.width(), self.height())
|
||||
return
|
||||
|
||||
@@ -154,7 +153,7 @@ class GuiPreferences(NPagedDialog):
|
||||
|
||||
class GuiPreferencesGeneral(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
self.prefsGui = prefsGui
|
||||
@@ -285,9 +284,8 @@ class GuiPreferencesGeneral(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
guiLocale = self.guiLocale.currentData()
|
||||
guiTheme = self.guiTheme.currentData()
|
||||
guiSyntax = self.guiSyntax.currentData()
|
||||
@@ -316,12 +314,12 @@ class GuiPreferencesGeneral(QWidget):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _selectFont(self):
|
||||
"""Open the QFontDialog and set a font for the font style.
|
||||
"""
|
||||
@pyqtSlot()
|
||||
def _selectFont(self) -> None:
|
||||
"""Open the QFontDialog and set a font for the font style."""
|
||||
currFont = QFont()
|
||||
currFont.setFamily(CONFIG.guiFont)
|
||||
currFont.setPointSize(CONFIG.guiFontSize)
|
||||
@@ -336,7 +334,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
|
||||
class GuiPreferencesProjects(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
# The Form
|
||||
@@ -438,9 +436,8 @@ class GuiPreferencesProjects(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
# Automatic Save
|
||||
CONFIG.autoSaveDoc = self.autoSaveDoc.value()
|
||||
CONFIG.autoSaveProj = self.autoSaveProj.value()
|
||||
@@ -457,12 +454,12 @@ class GuiPreferencesProjects(QWidget):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _backupFolder(self):
|
||||
"""Open a dialog to select the backup folder.
|
||||
"""
|
||||
@pyqtSlot()
|
||||
def _backupFolder(self) -> None:
|
||||
"""Open a dialog to select the backup folder."""
|
||||
currDir = self.backupPath or ""
|
||||
newDir = QFileDialog.getExistingDirectory(
|
||||
self, self.tr("Backup Directory"), str(currDir), options=QFileDialog.ShowDirsOnly
|
||||
@@ -472,15 +469,13 @@ class GuiPreferencesProjects(QWidget):
|
||||
self.mainForm.setHelpText(
|
||||
self.backupPathRow, self.tr("Path: {0}").format(self.backupPath)
|
||||
)
|
||||
return True
|
||||
return
|
||||
return
|
||||
|
||||
return False
|
||||
|
||||
def _toggledBackupOnClose(self, theState):
|
||||
"""Enable or disable switch that depends on the backup on close
|
||||
switch.
|
||||
"""
|
||||
self.askBeforeBackup.setEnabled(theState)
|
||||
@pyqtSlot(bool)
|
||||
def _toggledBackupOnClose(self, state: bool) -> None:
|
||||
"""Toggle switch that depends on the backup on close switch."""
|
||||
self.askBeforeBackup.setEnabled(state)
|
||||
return
|
||||
|
||||
# END Class GuiPreferencesProjects
|
||||
@@ -488,7 +483,7 @@ class GuiPreferencesProjects(QWidget):
|
||||
|
||||
class GuiPreferencesDocuments(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
# The Form
|
||||
@@ -604,9 +599,8 @@ class GuiPreferencesDocuments(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
# Text Style
|
||||
CONFIG.setTextFont(self.textFont.text(), self.textSize.value())
|
||||
|
||||
@@ -621,12 +615,12 @@ class GuiPreferencesDocuments(QWidget):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _selectFont(self):
|
||||
"""Open the QFontDialog and set a font for the font style.
|
||||
"""
|
||||
"""Open the QFontDialog and set a font for the font style."""
|
||||
currFont = QFont()
|
||||
currFont.setFamily(CONFIG.textFont)
|
||||
currFont.setPointSize(CONFIG.textSize)
|
||||
@@ -642,7 +636,7 @@ class GuiPreferencesDocuments(QWidget):
|
||||
|
||||
class GuiPreferencesEditor(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
# The Form
|
||||
@@ -726,6 +720,15 @@ class GuiPreferencesEditor(QWidget):
|
||||
# ================
|
||||
self.mainForm.addGroupLabel(self.tr("Scroll Behaviour"))
|
||||
|
||||
# Scroll Past End
|
||||
self.scrollPastEnd = NSwitch()
|
||||
self.scrollPastEnd.setChecked(CONFIG.scrollPastEnd)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Scroll past end of the document"),
|
||||
self.scrollPastEnd,
|
||||
self.tr("Also centres the cursor when scrolling.")
|
||||
)
|
||||
|
||||
# Typewriter Scrolling
|
||||
self.autoScroll = NSwitch()
|
||||
self.autoScroll.setChecked(CONFIG.autoScroll)
|
||||
@@ -750,7 +753,7 @@ class GuiPreferencesEditor(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
# Spell Checking
|
||||
CONFIG.spellLanguage = self.spellLanguage.currentData()
|
||||
@@ -766,6 +769,7 @@ class GuiPreferencesEditor(QWidget):
|
||||
# Scroll Behaviour
|
||||
CONFIG.autoScroll = self.autoScroll.isChecked()
|
||||
CONFIG.autoScrollPos = self.autoScrollPos.value()
|
||||
CONFIG.scrollPastEnd = self.scrollPastEnd.isChecked()
|
||||
|
||||
return
|
||||
|
||||
@@ -774,7 +778,7 @@ class GuiPreferencesEditor(QWidget):
|
||||
|
||||
class GuiPreferencesSyntax(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
self.prefsGui = prefsGui
|
||||
@@ -840,9 +844,8 @@ class GuiPreferencesSyntax(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
highlightQuotes = self.highlightQuotes.isChecked()
|
||||
allowOpenSQuote = self.allowOpenSQuote.isChecked()
|
||||
allowOpenDQuote = self.allowOpenDQuote.isChecked()
|
||||
@@ -862,15 +865,14 @@ class GuiPreferencesSyntax(QWidget):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _toggleHighlightQuotes(self, theState):
|
||||
"""Enables or disables switches controlled by the highlight
|
||||
quotes switch.
|
||||
"""
|
||||
self.allowOpenSQuote.setEnabled(theState)
|
||||
self.allowOpenDQuote.setEnabled(theState)
|
||||
@pyqtSlot(bool)
|
||||
def _toggleHighlightQuotes(self, state: bool) -> None:
|
||||
"""Toggle switches controlled by the highlight quotes switch."""
|
||||
self.allowOpenSQuote.setEnabled(state)
|
||||
self.allowOpenDQuote.setEnabled(state)
|
||||
return
|
||||
|
||||
# END Class GuiPreferencesSyntax
|
||||
@@ -878,7 +880,7 @@ class GuiPreferencesSyntax(QWidget):
|
||||
|
||||
class GuiPreferencesAutomation(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
# The Form
|
||||
@@ -989,9 +991,8 @@ class GuiPreferencesAutomation(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
# Automatic Features
|
||||
CONFIG.autoSelect = self.autoSelect.isChecked()
|
||||
CONFIG.doReplace = self.doReplace.isChecked()
|
||||
@@ -1010,18 +1011,17 @@ class GuiPreferencesAutomation(QWidget):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _toggleAutoReplaceMain(self, theState):
|
||||
"""Enables or disables switches controlled by the main auto
|
||||
replace switch.
|
||||
"""
|
||||
self.doReplaceSQuote.setEnabled(theState)
|
||||
self.doReplaceDQuote.setEnabled(theState)
|
||||
self.doReplaceDash.setEnabled(theState)
|
||||
self.doReplaceDots.setEnabled(theState)
|
||||
self.fmtPadThin.setEnabled(theState)
|
||||
@pyqtSlot(bool)
|
||||
def _toggleAutoReplaceMain(self, state: bool) -> None:
|
||||
"""Toggle switches controlled by the auto replace switch."""
|
||||
self.doReplaceSQuote.setEnabled(state)
|
||||
self.doReplaceDQuote.setEnabled(state)
|
||||
self.doReplaceDash.setEnabled(state)
|
||||
self.doReplaceDots.setEnabled(state)
|
||||
self.fmtPadThin.setEnabled(state)
|
||||
return
|
||||
|
||||
# END Class GuiPreferencesAutomation
|
||||
@@ -1029,7 +1029,7 @@ class GuiPreferencesAutomation(QWidget):
|
||||
|
||||
class GuiPreferencesQuotes(QWidget):
|
||||
|
||||
def __init__(self, prefsGui):
|
||||
def __init__(self, prefsGui: GuiPreferences) -> None:
|
||||
super().__init__(parent=prefsGui)
|
||||
|
||||
# The Form
|
||||
@@ -1113,9 +1113,8 @@ class GuiPreferencesQuotes(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
def saveValues(self) -> None:
|
||||
"""Save the values set for this tab."""
|
||||
# Quotation Style
|
||||
CONFIG.fmtSQuoteOpen = self.quoteSym["SO"].text()
|
||||
CONFIG.fmtSQuoteClose = self.quoteSym["SC"].text()
|
||||
@@ -1124,12 +1123,11 @@ class GuiPreferencesQuotes(QWidget):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _getQuote(self, qType):
|
||||
"""Dialog for single quote open.
|
||||
"""
|
||||
def _getQuote(self, qType: str) -> None:
|
||||
"""Dialog for single quote open."""
|
||||
qtBox = GuiQuoteSelect(self, currentQuote=self.quoteSym[qType].text())
|
||||
if qtBox.exec_() == QDialog.Accepted:
|
||||
self.quoteSym[qType].setText(qtBox.selectedQuote)
|
||||
|
||||
@@ -96,21 +96,22 @@ class GuiProjectSettings(NPagedDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiProjectSettings")
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _doSave(self):
|
||||
"""Save settings and close dialog.
|
||||
"""
|
||||
project = SHARED.project
|
||||
@pyqtSlot()
|
||||
def _doSave(self) -> None:
|
||||
"""Save settings and close dialog."""
|
||||
project = SHARED.project
|
||||
projName = self.tabMain.editName.text()
|
||||
bookTitle = self.tabMain.editTitle.text()
|
||||
bookAuthor = self.tabMain.editAuthor.text()
|
||||
projLang = self.tabMain.projLang.currentData()
|
||||
spellLang = self.tabMain.spellLang.currentData()
|
||||
doBackup = not self.tabMain.doBackup.isChecked()
|
||||
|
||||
@@ -119,6 +120,7 @@ class GuiProjectSettings(NPagedDialog):
|
||||
project.data.setAuthor(bookAuthor)
|
||||
project.data.setDoBackup(doBackup)
|
||||
project.data.setSpellLang(spellLang)
|
||||
project.setProjectLang(projLang)
|
||||
|
||||
if self.tabStatus.colChanged:
|
||||
newList, delList = self.tabStatus.getNewList()
|
||||
@@ -140,9 +142,9 @@ class GuiProjectSettings(NPagedDialog):
|
||||
|
||||
return
|
||||
|
||||
def _doClose(self):
|
||||
"""Save settings and close the dialog.
|
||||
"""
|
||||
@pyqtSlot()
|
||||
def _doClose(self) -> None:
|
||||
"""Save settings and close the dialog."""
|
||||
self._saveGuiSettings()
|
||||
self.reject()
|
||||
return
|
||||
@@ -151,9 +153,8 @@ class GuiProjectSettings(NPagedDialog):
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _focusTab(self, tab):
|
||||
"""Change which is the focused tab.
|
||||
"""
|
||||
def _focusTab(self, tab: int) -> None:
|
||||
"""Change which is the focused tab."""
|
||||
if tab == self.TAB_MAIN:
|
||||
self.setCurrentWidget(self.tabMain)
|
||||
elif tab == self.TAB_STATUS:
|
||||
@@ -164,9 +165,8 @@ class GuiProjectSettings(NPagedDialog):
|
||||
self.setCurrentWidget(self.tabReplace)
|
||||
return
|
||||
|
||||
def _saveGuiSettings(self):
|
||||
"""Save GUI settings.
|
||||
"""
|
||||
def _saveGuiSettings(self) -> None:
|
||||
"""Save GUI settings."""
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
replaceColW = CONFIG.rpxInt(self.tabReplace.listBox.columnWidth(0))
|
||||
@@ -187,8 +187,8 @@ class GuiProjectSettings(NPagedDialog):
|
||||
|
||||
class GuiProjectEditMain(QWidget):
|
||||
|
||||
def __init__(self, projGui):
|
||||
super().__init__(parent=projGui)
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
# The Form
|
||||
self.mainForm = NConfigLayout()
|
||||
@@ -230,24 +230,41 @@ class GuiProjectEditMain(QWidget):
|
||||
self.tr("Change whenever you want!")
|
||||
)
|
||||
|
||||
self.projLang = QComboBox(self)
|
||||
self.projLang.setMaximumWidth(xW)
|
||||
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
|
||||
self.projLang.addItem(language, tag)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Project language"),
|
||||
self.projLang,
|
||||
self.tr("Used when building the manuscript.")
|
||||
)
|
||||
|
||||
langIdx = 0
|
||||
if pData.language is not None:
|
||||
langIdx = self.projLang.findData(pData.language)
|
||||
if langIdx == -1:
|
||||
langIdx = self.projLang.findData("en_GB")
|
||||
if langIdx != -1:
|
||||
self.projLang.setCurrentIndex(langIdx)
|
||||
|
||||
self.spellLang = QComboBox(self)
|
||||
self.spellLang.setMaximumWidth(xW)
|
||||
self.spellLang.addItem(self.tr("Default"), "None")
|
||||
if CONFIG.hasEnchant:
|
||||
for tag, language in SHARED.spelling.listDictionaries():
|
||||
self.spellLang.addItem(language, tag)
|
||||
|
||||
self.mainForm.addRow(
|
||||
self.tr("Spell check language"),
|
||||
self.spellLang,
|
||||
self.tr("Overrides main preferences.")
|
||||
)
|
||||
|
||||
spellIdx = 0
|
||||
langIdx = 0
|
||||
if pData.spellLang is not None:
|
||||
spellIdx = self.spellLang.findData(pData.spellLang)
|
||||
if spellIdx != -1:
|
||||
self.spellLang.setCurrentIndex(spellIdx)
|
||||
langIdx = self.spellLang.findData(pData.spellLang)
|
||||
if langIdx != -1:
|
||||
self.spellLang.setCurrentIndex(langIdx)
|
||||
|
||||
self.doBackup = NSwitch(self)
|
||||
self.doBackup.setChecked(not pData.doBackup)
|
||||
@@ -271,8 +288,8 @@ class GuiProjectEditStatus(QWidget):
|
||||
COL_ROLE = Qt.ItemDataRole.UserRole + 1
|
||||
NUM_ROLE = Qt.ItemDataRole.UserRole + 2
|
||||
|
||||
def __init__(self, projGui, isStatus):
|
||||
super().__init__(parent=projGui)
|
||||
def __init__(self, parent: QWidget, isStatus: bool) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
if isStatus:
|
||||
self.theStatus = SHARED.project.data.itemStatus
|
||||
@@ -372,9 +389,8 @@ class GuiProjectEditStatus(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def getNewList(self):
|
||||
"""Return list of entries.
|
||||
"""
|
||||
def getNewList(self) -> tuple[list, list]:
|
||||
"""Return list of entries."""
|
||||
if self.colChanged:
|
||||
newList = []
|
||||
for n in range(self.listBox.topLevelItemCount()):
|
||||
@@ -394,9 +410,8 @@ class GuiProjectEditStatus(QWidget):
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _selectColour(self):
|
||||
"""Open a dialog to select the status icon colour.
|
||||
"""
|
||||
def _selectColour(self) -> None:
|
||||
"""Open a dialog to select the status icon colour."""
|
||||
if self.selColour is not None:
|
||||
newCol = QColorDialog.getColor(
|
||||
self.selColour, self, self.tr("Select Colour")
|
||||
@@ -410,17 +425,15 @@ class GuiProjectEditStatus(QWidget):
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _newItem(self):
|
||||
"""Create a new status item.
|
||||
"""
|
||||
def _newItem(self) -> None:
|
||||
"""Create a new status item."""
|
||||
self._addItem(None, self.tr("New Item"), (100, 100, 100), 0)
|
||||
self.colChanged = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _delItem(self):
|
||||
"""Delete a status item.
|
||||
"""
|
||||
def _delItem(self) -> None:
|
||||
"""Delete a status item."""
|
||||
selItem = self._getSelectedItem()
|
||||
if isinstance(selItem, QTreeWidgetItem):
|
||||
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
||||
@@ -433,9 +446,8 @@ class GuiProjectEditStatus(QWidget):
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _saveItem(self):
|
||||
"""Save changes made to a status item.
|
||||
"""
|
||||
def _saveItem(self) -> None:
|
||||
"""Save changes made to a status item."""
|
||||
selItem = self._getSelectedItem()
|
||||
if isinstance(selItem, QTreeWidgetItem):
|
||||
selItem.setText(self.COL_LABEL, simplified(self.editName.text()))
|
||||
@@ -444,11 +456,10 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.selColour.red(), self.selColour.green(), self.selColour.blue()
|
||||
))
|
||||
self.colChanged = True
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _selectedItem(self):
|
||||
def _selectedItem(self) -> None:
|
||||
"""Extract the info of a selected item and populate the settings
|
||||
boxes and button. If no item is selected, clear the form.
|
||||
"""
|
||||
@@ -456,7 +467,6 @@ class GuiProjectEditStatus(QWidget):
|
||||
if isinstance(selItem, QTreeWidgetItem):
|
||||
cols = selItem.data(self.COL_LABEL, self.COL_ROLE)
|
||||
name = selItem.text(self.COL_LABEL)
|
||||
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(*cols))
|
||||
self.selColour = QColor(*cols)
|
||||
@@ -464,31 +474,27 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
self.editName.selectAll()
|
||||
self.editName.setFocus()
|
||||
|
||||
self.editName.setEnabled(True)
|
||||
self.colButton.setEnabled(True)
|
||||
self.saveButton.setEnabled(True)
|
||||
|
||||
else:
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(100, 100, 100))
|
||||
self.selColour = QColor(100, 100, 100)
|
||||
self.editName.setText("")
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
|
||||
self.editName.setEnabled(False)
|
||||
self.colButton.setEnabled(False)
|
||||
self.saveButton.setEnabled(False)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _addItem(self, key, name, cols, count):
|
||||
"""Add a status item to the list.
|
||||
"""
|
||||
def _addItem(self, key: str | None, name: str,
|
||||
cols: tuple[int, int, int], count: int) -> None:
|
||||
"""Add a status item to the list."""
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(*cols))
|
||||
|
||||
@@ -504,9 +510,8 @@ class GuiProjectEditStatus(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def _moveItem(self, step):
|
||||
"""Move and item up or down step.
|
||||
"""
|
||||
def _moveItem(self, step: int) -> None:
|
||||
"""Move and item up or down step."""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem is None:
|
||||
return
|
||||
@@ -527,17 +532,15 @@ class GuiProjectEditStatus(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def _getSelectedItem(self):
|
||||
"""Get the currently selected item.
|
||||
"""
|
||||
def _getSelectedItem(self) -> QTreeWidgetItem | None:
|
||||
"""Get the currently selected item."""
|
||||
selItem = self.listBox.selectedItems()
|
||||
if len(selItem) > 0:
|
||||
return selItem[0]
|
||||
return None
|
||||
|
||||
def _usageString(self, nUse):
|
||||
"""Generate usage string.
|
||||
"""
|
||||
def _usageString(self, nUse: int) -> str:
|
||||
"""Generate usage string."""
|
||||
if nUse == 0:
|
||||
return self.tr("Not in use")
|
||||
elif nUse == 1:
|
||||
@@ -553,8 +556,8 @@ class GuiProjectEditReplace(QWidget):
|
||||
COL_KEY = 0
|
||||
COL_REPL = 1
|
||||
|
||||
def __init__(self, projGui):
|
||||
super().__init__(parent=projGui)
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.arChanged = False
|
||||
|
||||
@@ -635,25 +638,23 @@ class GuiProjectEditReplace(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def getNewList(self):
|
||||
"""Extract the list from the widget.
|
||||
"""
|
||||
newList = {}
|
||||
def getNewList(self) -> dict:
|
||||
"""Extract the list from the widget."""
|
||||
new = {}
|
||||
for n in range(self.listBox.topLevelItemCount()):
|
||||
tItem = self.listBox.topLevelItem(n)
|
||||
if tItem is not None:
|
||||
aKey = self._stripNotAllowed(tItem.text(0))
|
||||
aVal = tItem.text(1)
|
||||
if len(aKey) > 0:
|
||||
newList[aKey] = aVal
|
||||
|
||||
return newList
|
||||
new[aKey] = aVal
|
||||
return new
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _selectedItem(self):
|
||||
def _selectedItem(self) -> bool:
|
||||
"""Extract the details from the selected item and populate the
|
||||
edit form.
|
||||
"""
|
||||
@@ -670,63 +671,53 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.editKey.setFocus()
|
||||
return True
|
||||
|
||||
def _saveEntry(self):
|
||||
"""Save the form data into the list widget.
|
||||
"""
|
||||
def _saveEntry(self) -> None:
|
||||
"""Save the form data into the list widget."""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem is None:
|
||||
return False
|
||||
|
||||
newKey = self.editKey.text()
|
||||
newVal = self.editValue.text()
|
||||
saveKey = self._stripNotAllowed(newKey)
|
||||
|
||||
if len(saveKey) > 0 and len(newVal) > 0:
|
||||
selItem.setText(self.COL_KEY, "<%s>" % saveKey)
|
||||
selItem.setText(self.COL_REPL, newVal)
|
||||
self.editKey.clear()
|
||||
self.editValue.clear()
|
||||
self.editKey.setEnabled(False)
|
||||
self.editValue.setEnabled(False)
|
||||
self.listBox.clearSelection()
|
||||
self.arChanged = True
|
||||
|
||||
if selItem:
|
||||
newKey = self.editKey.text()
|
||||
newVal = self.editValue.text()
|
||||
saveKey = self._stripNotAllowed(newKey)
|
||||
if len(saveKey) > 0 and len(newVal) > 0:
|
||||
selItem.setText(self.COL_KEY, "<%s>" % saveKey)
|
||||
selItem.setText(self.COL_REPL, newVal)
|
||||
self.editKey.clear()
|
||||
self.editValue.clear()
|
||||
self.editKey.setEnabled(False)
|
||||
self.editValue.setEnabled(False)
|
||||
self.listBox.clearSelection()
|
||||
self.arChanged = True
|
||||
return
|
||||
|
||||
def _addEntry(self):
|
||||
"""Add a new list entry.
|
||||
"""
|
||||
def _addEntry(self) -> None:
|
||||
"""Add a new list entry."""
|
||||
saveKey = "<keyword%d>" % (self.listBox.topLevelItemCount() + 1)
|
||||
newVal = ""
|
||||
newItem = QTreeWidgetItem([saveKey, newVal])
|
||||
self.listBox.addTopLevelItem(newItem)
|
||||
return True
|
||||
return
|
||||
|
||||
def _delEntry(self):
|
||||
"""Delete the selected entry.
|
||||
"""
|
||||
def _delEntry(self) -> None:
|
||||
"""Delete the selected entry."""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem is None:
|
||||
return False
|
||||
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
|
||||
self.arChanged = True
|
||||
return True
|
||||
if selItem:
|
||||
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
|
||||
self.arChanged = True
|
||||
return
|
||||
|
||||
def _getSelectedItem(self):
|
||||
"""Extract the currently selected item.
|
||||
"""
|
||||
def _getSelectedItem(self) -> QTreeWidgetItem | None:
|
||||
"""Extract the currently selected item."""
|
||||
selItem = self.listBox.selectedItems()
|
||||
if len(selItem) == 0:
|
||||
return None
|
||||
return selItem[0]
|
||||
|
||||
def _stripNotAllowed(self, theKey):
|
||||
"""Clean up the replace key string.
|
||||
"""
|
||||
retKey = ""
|
||||
for c in theKey:
|
||||
def _stripNotAllowed(self, key: str) -> str:
|
||||
"""Clean up the replace key string."""
|
||||
result = ""
|
||||
for c in key:
|
||||
if c.isalnum():
|
||||
retKey += c
|
||||
return retKey
|
||||
result += c
|
||||
return result
|
||||
|
||||
# END Class GuiProjectEditReplace
|
||||
|
||||
@@ -10,6 +10,8 @@ Created: 2020-04-25 [0.4.5] GuiDocEditHeader
|
||||
Rewritten: 2020-06-15 [0.9] GuiDocEditSearch
|
||||
Created: 2020-06-27 [0.10] GuiDocEditFooter
|
||||
Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter
|
||||
Created: 2023-11-06 [2.2b1] MetaCompleter
|
||||
Created: 2023-11-07 [2.2b1] GuiDocToolBar
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||
@@ -67,6 +69,16 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _SelectAction(Enum):
|
||||
|
||||
NO_DECISION = 0
|
||||
KEEP_SELECTION = 1
|
||||
KEEP_POSITION = 2
|
||||
MOVE_AFTER = 3
|
||||
|
||||
# END Class _SelectAction
|
||||
|
||||
|
||||
class GuiDocEditor(QPlainTextEdit):
|
||||
"""Gui Widget: Main Document Editor"""
|
||||
|
||||
@@ -152,7 +164,6 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
self.setMinimumWidth(CONFIG.pxInt(300))
|
||||
self.setAutoFillBackground(True)
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
self.setCenterOnScroll(True)
|
||||
|
||||
# Custom Shortcuts
|
||||
self.keyContext = QShortcut(self)
|
||||
@@ -330,7 +341,8 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
|
||||
self._qDocument.setDefaultTextOption(options)
|
||||
|
||||
# Scroll bars
|
||||
# Scrolling
|
||||
self.setCenterOnScroll(CONFIG.scrollPastEnd)
|
||||
if CONFIG.hideVScroll:
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
else:
|
||||
@@ -996,7 +1008,9 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
return
|
||||
|
||||
text = block.text()
|
||||
if text.startswith("@"):
|
||||
if text.startswith("@") and added + removed == 1:
|
||||
# Only run on single keypresses, otherwise it will trigger
|
||||
# at unwanted times when other changes are made to the document
|
||||
cursor = self.textCursor()
|
||||
bPos = cursor.positionInBlock()
|
||||
if bPos > 0:
|
||||
@@ -1004,8 +1018,10 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
point = self.cursorRect().bottomRight()
|
||||
self._completer.move(self.viewport().mapToGlobal(point))
|
||||
self._completer.setVisible(show)
|
||||
else:
|
||||
self._completer.setVisible(False)
|
||||
|
||||
elif self._doReplace and added == 1:
|
||||
if self._doReplace and added == 1:
|
||||
self._docAutoReplace(text)
|
||||
|
||||
return
|
||||
@@ -1410,17 +1426,28 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
If more than one block is selected, the formatting is applied to
|
||||
the first block.
|
||||
"""
|
||||
cursor = self._autoSelect()
|
||||
if not cursor.hasSelection():
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return False
|
||||
cursor = self.textCursor()
|
||||
posO = cursor.position()
|
||||
if cursor.hasSelection():
|
||||
select = _SelectAction.KEEP_SELECTION
|
||||
else:
|
||||
cursor = self._autoSelect()
|
||||
if cursor.hasSelection() and posO == cursor.selectionEnd():
|
||||
select = _SelectAction.MOVE_AFTER
|
||||
else:
|
||||
select = _SelectAction.KEEP_POSITION
|
||||
|
||||
posS = cursor.selectionStart()
|
||||
posE = cursor.selectionEnd()
|
||||
if self._qDocument.characterAt(posO - 1) == fChar:
|
||||
logger.warning("Format repetition, cancelling action")
|
||||
cursor.clearSelection()
|
||||
cursor.setPosition(posO)
|
||||
self.setTextCursor(cursor)
|
||||
return False
|
||||
|
||||
blockS = self._qDocument.findBlock(posS)
|
||||
blockE = self._qDocument.findBlock(posE)
|
||||
|
||||
if blockS != blockE:
|
||||
posE = blockS.position() + blockS.length() - 1
|
||||
cursor.clearSelection()
|
||||
@@ -1443,34 +1470,26 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
break
|
||||
|
||||
if fLen == min(numA, numB):
|
||||
self._clearSurrounding(cursor, fLen)
|
||||
cursor.clearSelection()
|
||||
cursor.beginEditBlock()
|
||||
cursor.setPosition(posS)
|
||||
for i in range(fLen):
|
||||
cursor.deletePreviousChar()
|
||||
cursor.setPosition(posE)
|
||||
for i in range(fLen):
|
||||
cursor.deletePreviousChar()
|
||||
cursor.endEditBlock()
|
||||
cursor.clearSelection()
|
||||
cursor.setPosition(posO - fLen)
|
||||
self.setTextCursor(cursor)
|
||||
|
||||
else:
|
||||
self._wrapSelection(fChar*fLen)
|
||||
self._wrapSelection(fChar*fLen, pos=posO, select=select)
|
||||
|
||||
return True
|
||||
|
||||
def _clearSurrounding(self, cursor: QTextCursor, nChars: int) -> bool:
|
||||
"""Clear n characters before and after the cursor."""
|
||||
if not cursor.hasSelection():
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return False
|
||||
|
||||
posS = cursor.selectionStart()
|
||||
posE = cursor.selectionEnd()
|
||||
cursor.clearSelection()
|
||||
cursor.beginEditBlock()
|
||||
cursor.setPosition(posS)
|
||||
for i in range(nChars):
|
||||
cursor.deletePreviousChar()
|
||||
cursor.setPosition(posE)
|
||||
for i in range(nChars):
|
||||
cursor.deletePreviousChar()
|
||||
cursor.endEditBlock()
|
||||
cursor.clearSelection()
|
||||
|
||||
return True
|
||||
|
||||
def _wrapSelection(self, before: str, after: str | None = None) -> bool:
|
||||
def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
|
||||
select: _SelectAction = _SelectAction.NO_DECISION) -> bool:
|
||||
"""Wrap the selected text in whatever is in tBefore and tAfter.
|
||||
If there is no selection, the autoSelect setting decides the
|
||||
action. AutoSelect will select the word under the cursor before
|
||||
@@ -1479,10 +1498,17 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
if after is None:
|
||||
after = before
|
||||
|
||||
cursor = self._autoSelect()
|
||||
if not cursor.hasSelection():
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return False
|
||||
cursor = self.textCursor()
|
||||
posO = pos if isinstance(pos, int) else cursor.position()
|
||||
if select == _SelectAction.NO_DECISION:
|
||||
if cursor.hasSelection():
|
||||
select = _SelectAction.KEEP_SELECTION
|
||||
else:
|
||||
cursor = self._autoSelect()
|
||||
if cursor.hasSelection() and posO == cursor.selectionEnd():
|
||||
select = _SelectAction.MOVE_AFTER
|
||||
else:
|
||||
select = _SelectAction.KEEP_POSITION
|
||||
|
||||
posS = cursor.selectionStart()
|
||||
posE = cursor.selectionEnd()
|
||||
@@ -1500,8 +1526,14 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
cursor.insertText(before)
|
||||
cursor.endEditBlock()
|
||||
|
||||
cursor.setPosition(posE + len(before), QTextCursor.MoveAnchor)
|
||||
cursor.setPosition(posS + len(before), QTextCursor.KeepAnchor)
|
||||
if select == _SelectAction.MOVE_AFTER:
|
||||
cursor.setPosition(posE + len(before + after))
|
||||
elif select == _SelectAction.KEEP_SELECTION:
|
||||
cursor.setPosition(posE + len(before), QTextCursor.MoveMode.MoveAnchor)
|
||||
cursor.setPosition(posS + len(before), QTextCursor.MoveMode.KeepAnchor)
|
||||
elif select == _SelectAction.KEEP_POSITION:
|
||||
cursor.setPosition(posO + len(before))
|
||||
|
||||
self.setTextCursor(cursor)
|
||||
|
||||
return True
|
||||
@@ -1908,27 +1940,38 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
|
||||
def _autoSelect(self) -> QTextCursor:
|
||||
"""Return a cursor which may or may not have a selection based
|
||||
on user settings and document action.
|
||||
on user settings and document action. The selection will be the
|
||||
word closest to the cursor consisting of alphanumerical unicode
|
||||
characters.
|
||||
"""
|
||||
cursor = self.textCursor()
|
||||
if CONFIG.autoSelect and not cursor.hasSelection():
|
||||
cursor.select(QTextCursor.WordUnderCursor)
|
||||
posS = cursor.selectionStart()
|
||||
posE = cursor.selectionEnd()
|
||||
cPos = cursor.position()
|
||||
bPos = cursor.block().position()
|
||||
bLen = cursor.block().length()
|
||||
|
||||
# Underscore counts as a part of the word, so check that the
|
||||
# selection isn't wrapped in italics markers.
|
||||
reSelect = False
|
||||
if self._qDocument.characterAt(posS) == "_":
|
||||
posS += 1
|
||||
reSelect = True
|
||||
if self._qDocument.characterAt(posE) == "_":
|
||||
posE -= 1
|
||||
reSelect = True
|
||||
if reSelect:
|
||||
cursor.clearSelection()
|
||||
cursor.setPosition(posS, QTextCursor.MoveAnchor)
|
||||
cursor.setPosition(posE-1, QTextCursor.KeepAnchor)
|
||||
# Scan backwards
|
||||
sPos = cPos
|
||||
for i in range(cPos - bPos):
|
||||
sPos = cPos - i - 1
|
||||
if not self._qDocument.characterAt(sPos).isalnum():
|
||||
sPos += 1
|
||||
break
|
||||
|
||||
# Scan forwards
|
||||
ePos = cPos
|
||||
for i in range(bPos + bLen - cPos):
|
||||
ePos = cPos + i
|
||||
if not self._qDocument.characterAt(ePos).isalnum():
|
||||
break
|
||||
|
||||
if ePos - sPos <= 0:
|
||||
# No selection possible
|
||||
return cursor
|
||||
|
||||
cursor.clearSelection()
|
||||
cursor.setPosition(sPos, QTextCursor.MoveAnchor)
|
||||
cursor.setPosition(ePos, QTextCursor.KeepAnchor)
|
||||
|
||||
self.setTextCursor(cursor)
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ class GuiMainMenu(QMenuBar):
|
||||
requestDocInsert = pyqtSignal(nwDocInsert)
|
||||
requestDocInsertText = pyqtSignal(str)
|
||||
requestDocKeyWordInsert = pyqtSignal(str)
|
||||
requestFocusChange = pyqtSignal(nwWidget)
|
||||
|
||||
def __init__(self, mainGui: GuiMain) -> None:
|
||||
super().__init__(parent=mainGui)
|
||||
@@ -173,7 +174,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Project > Delete
|
||||
self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item"))
|
||||
self.aDeleteItem.setShortcut("Ctrl+Shift+Del")
|
||||
self.aDeleteItem.setShortcuts(["Ctrl+Del", "Ctrl+Shift+Del"]) # Latter is deprecated
|
||||
self.aDeleteItem.triggered.connect(lambda: self.mainGui.projView.requestDeleteItem(None))
|
||||
|
||||
# Project > Empty Trash
|
||||
@@ -246,12 +247,16 @@ class GuiMainMenu(QMenuBar):
|
||||
# Edit > Undo
|
||||
self.aEditUndo = self.editMenu.addAction(self.tr("Undo"))
|
||||
self.aEditUndo.setShortcut("Ctrl+Z")
|
||||
self.aEditUndo.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.UNDO))
|
||||
self.aEditUndo.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.UNDO)
|
||||
)
|
||||
|
||||
# Edit > Redo
|
||||
self.aEditRedo = self.editMenu.addAction(self.tr("Redo"))
|
||||
self.aEditRedo.setShortcut("Ctrl+Y")
|
||||
self.aEditRedo.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.REDO))
|
||||
self.aEditRedo.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.REDO)
|
||||
)
|
||||
|
||||
# Edit > Separator
|
||||
self.editMenu.addSeparator()
|
||||
@@ -259,17 +264,23 @@ class GuiMainMenu(QMenuBar):
|
||||
# Edit > Cut
|
||||
self.aEditCut = self.editMenu.addAction(self.tr("Cut"))
|
||||
self.aEditCut.setShortcut("Ctrl+X")
|
||||
self.aEditCut.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.CUT))
|
||||
self.aEditCut.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.CUT)
|
||||
)
|
||||
|
||||
# Edit > Copy
|
||||
self.aEditCopy = self.editMenu.addAction(self.tr("Copy"))
|
||||
self.aEditCopy.setShortcut("Ctrl+C")
|
||||
self.aEditCopy.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.COPY))
|
||||
self.aEditCopy.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.COPY)
|
||||
)
|
||||
|
||||
# Edit > Paste
|
||||
self.aEditPaste = self.editMenu.addAction(self.tr("Paste"))
|
||||
self.aEditPaste.setShortcut("Ctrl+V")
|
||||
self.aEditPaste.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.PASTE))
|
||||
self.aEditPaste.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.PASTE)
|
||||
)
|
||||
|
||||
# Edit > Separator
|
||||
self.editMenu.addSeparator()
|
||||
@@ -277,12 +288,16 @@ class GuiMainMenu(QMenuBar):
|
||||
# Edit > Select All
|
||||
self.aSelectAll = self.editMenu.addAction(self.tr("Select All"))
|
||||
self.aSelectAll.setShortcut("Ctrl+A")
|
||||
self.aSelectAll.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.SEL_ALL))
|
||||
self.aSelectAll.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.SEL_ALL)
|
||||
)
|
||||
|
||||
# Edit > Select Paragraph
|
||||
self.aSelectPar = self.editMenu.addAction(self.tr("Select Paragraph"))
|
||||
self.aSelectPar.setShortcut("Ctrl+Shift+A")
|
||||
self.aSelectPar.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.SEL_PARA))
|
||||
self.aSelectPar.triggered.connect(
|
||||
lambda: self.requestDocAction.emit(nwDocAction.SEL_PARA)
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -293,23 +308,24 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# View > TreeView
|
||||
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Project Tree"))
|
||||
self.aFocusTree.setShortcut("Ctrl+Alt+1" if CONFIG.osWindows else "Alt+1")
|
||||
self.aFocusTree.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.TREE))
|
||||
self.aFocusTree.setShortcut("Ctrl+T")
|
||||
self.aFocusTree.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwWidget.TREE)
|
||||
)
|
||||
|
||||
# View > Document Pane 1
|
||||
# View > Document Editor
|
||||
self.aFocusEditor = self.viewMenu.addAction(self.tr("Go to Document Editor"))
|
||||
self.aFocusEditor.setShortcut("Ctrl+Alt+2" if CONFIG.osWindows else "Alt+2")
|
||||
self.aFocusEditor.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.EDITOR))
|
||||
|
||||
# View > Document Pane 2
|
||||
self.aFocusView = self.viewMenu.addAction(self.tr("Go to Document Viewer"))
|
||||
self.aFocusView.setShortcut("Ctrl+Alt+3" if CONFIG.osWindows else "Alt+3")
|
||||
self.aFocusView.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.VIEWER))
|
||||
self.aFocusEditor.setShortcut("Ctrl+E")
|
||||
self.aFocusEditor.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwWidget.EDITOR)
|
||||
)
|
||||
|
||||
# View > Outline
|
||||
self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline"))
|
||||
self.aFocusOutline.setShortcut("Ctrl+Alt+4" if CONFIG.osWindows else "Alt+4")
|
||||
self.aFocusOutline.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.OUTLINE))
|
||||
self.aFocusOutline.setShortcut("Ctrl+Shift+T")
|
||||
self.aFocusOutline.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwWidget.OUTLINE)
|
||||
)
|
||||
|
||||
# View > Separator
|
||||
self.viewMenu.addSeparator()
|
||||
|
||||
@@ -189,7 +189,7 @@ class GuiProjectView(QWidget):
|
||||
self.projTree.buildTree()
|
||||
return
|
||||
|
||||
def setFocus(self) -> None:
|
||||
def setTreeFocus(self) -> None:
|
||||
"""Forward the set focus call to the tree widget."""
|
||||
self.projTree.setFocus()
|
||||
return
|
||||
|
||||
@@ -57,32 +57,32 @@ class GuiSideBar(QWidget):
|
||||
|
||||
# Buttons
|
||||
self.tbProject = QToolButton(self)
|
||||
self.tbProject.setToolTip(self.tr("Project Tree View"))
|
||||
self.tbProject.setToolTip("{0} [Ctrl+T]".format(self.tr("Project Tree View")))
|
||||
self.tbProject.setIconSize(iconSize)
|
||||
self.tbProject.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
|
||||
|
||||
self.tbNovel = QToolButton(self)
|
||||
self.tbNovel.setToolTip(self.tr("Novel Tree View"))
|
||||
self.tbNovel.setToolTip("{0} [Ctrl+T]".format(self.tr("Novel Tree View")))
|
||||
self.tbNovel.setIconSize(iconSize)
|
||||
self.tbNovel.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
|
||||
|
||||
self.tbOutline = QToolButton(self)
|
||||
self.tbOutline.setToolTip(self.tr("Novel Outline View"))
|
||||
self.tbOutline.setToolTip("{0} [Ctrl+Shift+T]".format(self.tr("Novel Outline View")))
|
||||
self.tbOutline.setIconSize(iconSize)
|
||||
self.tbOutline.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
|
||||
|
||||
self.tbBuild = QToolButton(self)
|
||||
self.tbBuild.setToolTip(self.tr("Build Manuscript"))
|
||||
self.tbBuild.setToolTip("{0} [F5]".format(self.tr("Build Manuscript")))
|
||||
self.tbBuild.setIconSize(iconSize)
|
||||
self.tbBuild.clicked.connect(self.mainGui.showBuildManuscriptDialog)
|
||||
|
||||
self.tbDetails = QToolButton(self)
|
||||
self.tbDetails.setToolTip(self.tr("Project Details"))
|
||||
self.tbDetails.setToolTip("{0} [Shift+F6]".format(self.tr("Project Details")))
|
||||
self.tbDetails.setIconSize(iconSize)
|
||||
self.tbDetails.clicked.connect(self.mainGui.showProjectDetailsDialog)
|
||||
|
||||
self.tbStats = QToolButton(self)
|
||||
self.tbStats.setToolTip(self.tr("Writing Statistics"))
|
||||
self.tbStats.setToolTip("{0} [F6]".format(self.tr("Writing Statistics")))
|
||||
self.tbStats.setIconSize(iconSize)
|
||||
self.tbStats.clicked.connect(self.mainGui.showWritingStatsDialog)
|
||||
|
||||
@@ -111,7 +111,7 @@ class GuiSideBar(QWidget):
|
||||
self.outerBox.addWidget(self.tbDetails)
|
||||
self.outerBox.addWidget(self.tbStats)
|
||||
self.outerBox.addWidget(self.tbSettings)
|
||||
self.outerBox.setContentsMargins(0, 0, CONFIG.pxInt(2), 0)
|
||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||
self.outerBox.setSpacing(CONFIG.pxInt(4))
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
@@ -131,7 +131,7 @@ class GuiSideBar(QWidget):
|
||||
buttonStyle = (
|
||||
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
||||
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
||||
).format(CONFIG.pxInt(4), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||
).format(CONFIG.pxInt(6), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||
buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}"
|
||||
|
||||
self.tbProject.setIcon(SHARED.theme.getIcon("view_editor"))
|
||||
|
||||
@@ -31,7 +31,7 @@ from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
|
||||
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence
|
||||
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut,
|
||||
QSplitter, QStackedWidget, QVBoxLayout, QWidget, qApp
|
||||
@@ -247,6 +247,7 @@ class GuiMain(QMainWindow):
|
||||
self.mainMenu.requestDocInsert.connect(self._passDocumentInsert)
|
||||
self.mainMenu.requestDocInsertText.connect(self._passDocumentInsert)
|
||||
self.mainMenu.requestDocKeyWordInsert.connect(self.docEditor.insertKeyWord)
|
||||
self.mainMenu.requestFocusChange.connect(self.switchFocus)
|
||||
|
||||
self.sideBar.viewChangeRequested.connect(self._changeView)
|
||||
|
||||
@@ -300,17 +301,17 @@ class GuiMain(QMainWindow):
|
||||
# Shortcuts and Actions
|
||||
self._connectMenuActions()
|
||||
|
||||
keyReturn = QShortcut(self)
|
||||
keyReturn.setKey(QKeySequence(Qt.Key_Return))
|
||||
keyReturn.activated.connect(self._keyPressReturn)
|
||||
self.keyReturn = QShortcut(self)
|
||||
self.keyReturn.setKey(Qt.Key.Key_Return)
|
||||
self.keyReturn.activated.connect(self._keyPressReturn)
|
||||
|
||||
keyEnter = QShortcut(self)
|
||||
keyEnter.setKey(QKeySequence(Qt.Key_Enter))
|
||||
keyEnter.activated.connect(self._keyPressReturn)
|
||||
self.keyEnter = QShortcut(self)
|
||||
self.keyEnter.setKey(Qt.Key.Key_Enter)
|
||||
self.keyEnter.activated.connect(self._keyPressReturn)
|
||||
|
||||
keyEscape = QShortcut(self)
|
||||
keyEscape.setKey(QKeySequence(Qt.Key_Escape))
|
||||
keyEscape.activated.connect(self._keyPressEscape)
|
||||
self.keyEscape = QShortcut(self)
|
||||
self.keyEscape.setKey(Qt.Key.Key_Escape)
|
||||
self.keyEscape.activated.connect(self._keyPressEscape)
|
||||
|
||||
# Check that config loaded fine
|
||||
self.reportConfErr()
|
||||
@@ -1093,25 +1094,6 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return True
|
||||
|
||||
def switchFocus(self, paneNo: nwWidget) -> None:
|
||||
"""Switch focus between main GUI views."""
|
||||
if paneNo == nwWidget.TREE:
|
||||
tabIdx = self.projStack.currentIndex()
|
||||
if tabIdx == self.idxProjView:
|
||||
self.projView.setFocus()
|
||||
elif tabIdx == self.idxNovelView:
|
||||
self.novelView.setTreeFocus()
|
||||
elif paneNo == nwWidget.EDITOR:
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docEditor.setFocus()
|
||||
elif paneNo == nwWidget.VIEWER:
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docViewer.setFocus()
|
||||
elif paneNo == nwWidget.OUTLINE:
|
||||
self._changeView(nwView.OUTLINE)
|
||||
self.outlineView.setTreeFocus()
|
||||
return
|
||||
|
||||
def closeDocViewer(self, byUser: bool = True) -> bool:
|
||||
"""Close the document view panel."""
|
||||
self.docViewer.clearViewer()
|
||||
@@ -1189,6 +1171,33 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot(nwWidget)
|
||||
def switchFocus(self, paneNo: nwWidget) -> None:
|
||||
"""Switch focus between main GUI views."""
|
||||
if paneNo == nwWidget.TREE:
|
||||
if self.projStack.currentWidget() is self.projView:
|
||||
if self.projView.treeHasFocus():
|
||||
self._changeView(nwView.NOVEL)
|
||||
self.novelView.setTreeFocus()
|
||||
else:
|
||||
self.projView.setTreeFocus()
|
||||
else:
|
||||
if self.novelView.treeHasFocus():
|
||||
self._changeView(nwView.PROJECT)
|
||||
self.projView.setTreeFocus()
|
||||
else:
|
||||
self.novelView.setTreeFocus()
|
||||
elif paneNo == nwWidget.EDITOR:
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docEditor.setFocus()
|
||||
elif paneNo == nwWidget.VIEWER:
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docViewer.setFocus()
|
||||
elif paneNo == nwWidget.OUTLINE:
|
||||
self._changeView(nwView.OUTLINE)
|
||||
self.outlineView.setTreeFocus()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
@@ -1224,18 +1233,14 @@ class GuiMain(QMainWindow):
|
||||
if view == nwView.EDITOR:
|
||||
# Only change the main stack, but not the project stack
|
||||
self.mainStack.setCurrentWidget(self.splitMain)
|
||||
|
||||
elif view == nwView.PROJECT:
|
||||
self.mainStack.setCurrentWidget(self.splitMain)
|
||||
self.projStack.setCurrentWidget(self.projView)
|
||||
|
||||
elif view == nwView.NOVEL:
|
||||
self.mainStack.setCurrentWidget(self.splitMain)
|
||||
self.projStack.setCurrentWidget(self.novelView)
|
||||
|
||||
elif view == nwView.OUTLINE:
|
||||
self.mainStack.setCurrentWidget(self.outlineView)
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot(nwDocAction)
|
||||
@@ -1482,6 +1487,12 @@ class GuiMain(QMainWindow):
|
||||
projData["numChapters"] = newProj.field("numChapters")
|
||||
projData["numScenes"] = newProj.field("numScenes")
|
||||
|
||||
try:
|
||||
langIdx = newProj.field("projLang")
|
||||
projData["projLang"] = CONFIG.listLanguages(CONFIG.LANG_PROJ)[langIdx][0]
|
||||
except Exception:
|
||||
projData["projLang"] = "en_GB"
|
||||
|
||||
return projData
|
||||
|
||||
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
|
||||
|
||||
@@ -144,6 +144,8 @@ class GuiManuscript(QDialog):
|
||||
self.buildList.setIconSize(QSize(iPx, iPx))
|
||||
self.buildList.doubleClicked.connect(self._editSelectedBuild)
|
||||
self.buildList.currentItemChanged.connect(self._updateBuildDetails)
|
||||
self.buildList.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.buildList.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
|
||||
self.buildDetails = _DetailsWidget(self)
|
||||
self.buildDetails.setColumnWidth(
|
||||
@@ -417,9 +419,15 @@ class GuiManuscript(QDialog):
|
||||
"""Save the user GUI settings."""
|
||||
logger.debug("Saving GuiManuscript settings")
|
||||
|
||||
buildOrder = []
|
||||
for i in range(self.buildList.count()):
|
||||
if item := self.buildList.item(i):
|
||||
buildOrder.append(item.data(self.D_KEY))
|
||||
|
||||
current = self.buildList.currentItem()
|
||||
if isinstance(current, QListWidgetItem):
|
||||
self._builds.setLastBuild(current.data(self.D_KEY))
|
||||
lastBuild = current.data(self.D_KEY) if isinstance(current, QListWidgetItem) else ""
|
||||
|
||||
self._builds.setBuildsState(lastBuild, buildOrder)
|
||||
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
|
||||
@@ -974,15 +974,6 @@ class _FormatTab(QWidget):
|
||||
self.formFormat = NConfigLayout()
|
||||
self.formFormat.addGroupLabel(self._build.getLabel("format.grpFormat"))
|
||||
|
||||
# Build Language
|
||||
self.buildLang = QComboBox()
|
||||
langauges = CONFIG.listLanguages(CONFIG.LANG_PROJ)
|
||||
self.buildLang.addItem("[%s]" % self.tr("Not Set"), "None")
|
||||
for langID, langName in langauges:
|
||||
self.buildLang.addItem(langName, langID)
|
||||
|
||||
self.formFormat.addRow(self._build.getLabel("format.buildLang"), self.buildLang)
|
||||
|
||||
# Font Family
|
||||
self.textFont = QLineEdit()
|
||||
self.textFont.setReadOnly(True)
|
||||
@@ -1103,10 +1094,6 @@ class _FormatTab(QWidget):
|
||||
|
||||
def loadContent(self) -> None:
|
||||
"""Populate the widgets."""
|
||||
langIdx = self.buildLang.findData(self._build.getStr("format.buildLang"))
|
||||
if langIdx != -1:
|
||||
self.buildLang.setCurrentIndex(langIdx)
|
||||
|
||||
textFont = self._build.getStr("format.textFont")
|
||||
if not textFont:
|
||||
textFont = str(CONFIG.textFont)
|
||||
@@ -1146,7 +1133,6 @@ class _FormatTab(QWidget):
|
||||
|
||||
def saveContent(self) -> None:
|
||||
"""Save choices back into build object."""
|
||||
self._build.setValue("format.buildLang", str(self.buildLang.currentData()))
|
||||
self._build.setValue("format.textFont", self.textFont.text())
|
||||
self._build.setValue("format.textSize", self.textSize.value())
|
||||
self._build.setValue("format.lineHeight", self.lineHeight.value())
|
||||
|
||||
@@ -28,8 +28,9 @@ import logging
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QFileDialog, QFormLayout, QGridLayout, QHBoxLayout, QLabel, QLineEdit,
|
||||
QPushButton, QRadioButton, QSpinBox, QVBoxLayout, QWizard, QWizardPage
|
||||
QComboBox, QFileDialog, QFormLayout, QGridLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QPushButton, QRadioButton, QSpinBox, QVBoxLayout, QWizard,
|
||||
QWizardPage
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -127,15 +128,28 @@ class ProjWizardIntroPage(QWizardPage):
|
||||
self.projAuthor.setFixedWidth(xW)
|
||||
self.projAuthor.setPlaceholderText(self.tr("Optional"))
|
||||
|
||||
self.projLang = QComboBox(self)
|
||||
self.projLang.setMaximumWidth(xW)
|
||||
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
|
||||
self.projLang.addItem(language, tag)
|
||||
|
||||
langIdx = self.projLang.findData(CONFIG.guiLocale)
|
||||
if langIdx == -1:
|
||||
langIdx = self.projLang.findData("en_GB")
|
||||
if langIdx != -1:
|
||||
self.projLang.setCurrentIndex(langIdx)
|
||||
|
||||
self.mainForm = QFormLayout()
|
||||
self.mainForm.addRow(self.tr("Project Name"), self.projName)
|
||||
self.mainForm.addRow(self.tr("Novel Title"), self.projTitle)
|
||||
self.mainForm.addRow(self.tr("Author(s)"), self.projAuthor)
|
||||
self.mainForm.addRow(self.tr("Language"), self.projLang)
|
||||
self.mainForm.setVerticalSpacing(fS)
|
||||
|
||||
self.registerField("projName*", self.projName)
|
||||
self.registerField("projTitle", self.projTitle)
|
||||
self.registerField("projAuthor", self.projAuthor)
|
||||
self.registerField("projLang", self.projLang)
|
||||
|
||||
# Assemble
|
||||
self.outerBox = QVBoxLayout()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-11-06 11:44:21">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1609" autoCount="255" editTime="81165">
|
||||
<novelWriterXML appVersion="2.2-beta1" hexVersion="0x020200b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-11-11 22:48:30">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1611" autoCount="255" editTime="81193">
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
@@ -36,7 +36,7 @@
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="27" novelWords="989" notesWords="409">
|
||||
<content items="27" novelWords="987" notesWords="409">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes" />
|
||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||
@@ -46,7 +46,7 @@
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="269" wordCount="51" paraCount="3" cursorPos="275" />
|
||||
<meta expanded="no" heading="H0" charCount="250" wordCount="49" paraCount="2" cursorPos="275" />
|
||||
<name status="sf12341" import="ia857f0" active="yes">Page</name>
|
||||
</item>
|
||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -58,7 +58,7 @@
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="2901" wordCount="513" paraCount="15" cursorPos="749" />
|
||||
<meta expanded="no" heading="H3" charCount="2901" wordCount="513" paraCount="15" cursorPos="48" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 705 B After Width: | Height: | Size: 608 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 779 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 915 B |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -1,207 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 270.93333 270.93334"
|
||||
height="1024"
|
||||
width="1024">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient2515">
|
||||
<stop
|
||||
style="stop-color:#f7f7f7;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2511" />
|
||||
<stop
|
||||
style="stop-color:#e5e5e5;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2513" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
x="60.484347"
|
||||
y="205.79962"
|
||||
width="130.57787"
|
||||
height="65.785888"
|
||||
id="rect852" />
|
||||
<rect
|
||||
id="rect1474"
|
||||
height="28.631245"
|
||||
width="177.70083"
|
||||
y="18.019813"
|
||||
x="13.463703" />
|
||||
<rect
|
||||
id="rect852-7"
|
||||
height="65.785889"
|
||||
width="130.57787"
|
||||
y="205.79962"
|
||||
x="60.484348" />
|
||||
<rect
|
||||
id="rect1544"
|
||||
height="65.785889"
|
||||
width="130.57787"
|
||||
y="205.79962"
|
||||
x="60.484348" />
|
||||
<rect
|
||||
x="60.484348"
|
||||
y="205.79962"
|
||||
width="130.57787"
|
||||
height="65.785889"
|
||||
id="rect1570" />
|
||||
<rect
|
||||
x="13.463703"
|
||||
y="18.019813"
|
||||
width="177.70083"
|
||||
height="28.631245"
|
||||
id="rect1587" />
|
||||
<filter
|
||||
id="filter1444"
|
||||
style="color-interpolation-filters:sRGB">
|
||||
<feFlood
|
||||
id="feFlood1434"
|
||||
result="flood"
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="0.498039" />
|
||||
<feComposite
|
||||
id="feComposite1436"
|
||||
result="composite1"
|
||||
operator="in"
|
||||
in2="SourceGraphic"
|
||||
in="flood" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1438"
|
||||
result="blur"
|
||||
stdDeviation="3"
|
||||
in="composite1" />
|
||||
<feOffset
|
||||
id="feOffset1440"
|
||||
result="offset"
|
||||
dy="6"
|
||||
dx="6" />
|
||||
<feComposite
|
||||
id="feComposite1442"
|
||||
result="composite2"
|
||||
operator="over"
|
||||
in2="offset"
|
||||
in="SourceGraphic" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1456"
|
||||
style="color-interpolation-filters:sRGB">
|
||||
<feFlood
|
||||
id="feFlood1446"
|
||||
result="flood"
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="0.498039" />
|
||||
<feComposite
|
||||
id="feComposite1448"
|
||||
result="composite1"
|
||||
operator="in"
|
||||
in2="SourceGraphic"
|
||||
in="flood" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1450"
|
||||
result="blur"
|
||||
stdDeviation="3"
|
||||
in="composite1" />
|
||||
<feOffset
|
||||
id="feOffset1452"
|
||||
result="offset"
|
||||
dy="6"
|
||||
dx="6" />
|
||||
<feComposite
|
||||
id="feComposite1454"
|
||||
result="composite2"
|
||||
operator="over"
|
||||
in2="offset"
|
||||
in="SourceGraphic" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient2515"
|
||||
id="radialGradient2517"
|
||||
cx="605.42725"
|
||||
cy="623.97186"
|
||||
fx="605.42725"
|
||||
fy="623.97186"
|
||||
r="410"
|
||||
gradientTransform="matrix(2.4110274,1.7617596,-0.719517,0.98463462,-776.23441,-1372.7537)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer3"
|
||||
style="display:inline">
|
||||
<path
|
||||
clip-path="none"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 192,22 c -44.32,0 -80,35.680002 -80,80 v 820 c 0,44.32002 35.68,80 80,80 h 640 c 44.31999,0 80,-35.67998 80,-80 V 213.44727 L 720.55469,22 Z"
|
||||
style="fill:url(#radialGradient2517);fill-opacity:1;stroke:#999999;stroke-width:20;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect1481" />
|
||||
<path
|
||||
id="path1490"
|
||||
style="display:inline;fill:#e0e0e0;fill-opacity:1;stroke:#999999;stroke-width:5.29167;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 241.30037,56.474215 190.64713,5.8204593 V 35.307549 c 0,11.726333 9.44034,21.166666 21.16667,21.166666 z" />
|
||||
<g
|
||||
aria-label=".nwx"
|
||||
transform="matrix(1.7893481,0,0,1.782571,-45.41919,-191.90571)"
|
||||
id="text850"
|
||||
style="font-size:5.64444px;line-height:1.25;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans, Normal';letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect852);display:inline;fill:#666666;fill-opacity:1">
|
||||
<path
|
||||
d="m 63.088865,241.76928 q 0,-1.36426 0.909505,-2.27376 0.930175,-0.93018 2.480467,-0.93018 1.550291,0 2.459796,0.93018 0.909504,0.9095 0.909504,2.27376 0,1.34359 -0.909504,2.27376 -0.909505,0.9095 -2.459796,0.9095 -1.550292,0 -2.480467,-0.9095 -0.909505,-0.93017 -0.909505,-2.27376 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path856" />
|
||||
<path
|
||||
d="m 84.75161,226.78312 q -2.542479,0 -3.782712,2.14974 v 15.79231 h -5.973791 v -22.36554 h 5.601721 l 0.206705,2.56314 q 2.397785,-2.97656 6.428544,-2.97656 3.162595,0 5.105627,1.88103 1.943033,1.88102 1.963703,6.55256 v 14.34537 h -5.994461 v -14.26269 q 0,-2.14973 -0.930175,-2.91454 -0.909505,-0.76482 -2.625161,-0.76482 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path858" />
|
||||
<path
|
||||
d="m 126.96089,222.35963 -5.68441,22.36554 h -5.00227 l -4.25814,-14.07665 -4.23746,14.07665 h -4.9816 l -5.705078,-22.36554 h 5.767088 l 2.95589,14.22134 4.05143,-14.22134 h 4.32014 l 4.09277,14.20067 2.91455,-14.20067 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path860" />
|
||||
<path
|
||||
d="m 134.77436,222.35963 3.65869,6.65592 3.76204,-6.65592 h 6.3872 l -6.36653,10.93472 6.63525,11.43082 h -6.3872 l -4.01009,-7.04866 -3.94808,7.04866 h -6.42854 l 6.63525,-11.43082 -6.36654,-10.93472 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path862" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g2509"
|
||||
transform="matrix(1.2371037,0,0,1.2371037,206.3182,37.341857)">
|
||||
<path
|
||||
transform="matrix(0.16895991,0,0,0.18329422,-134.6942,-19.491243)"
|
||||
id="rect1406-1"
|
||||
style="display:inline;fill:#a11212;fill-opacity:1;stroke:#7e1b1b;stroke-width:9.99999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1444)"
|
||||
d="m 145.18984,56.414257 c -44.32,0 -79.999998,35.68 -79.999998,80.000003 v 199.99805 a 279.99999,279.99999 0 0 0 0,0.002 279.99999,279.99999 0 0 0 0,0.0195 v 199.98242 c 0,44.31999 35.679998,80 79.999998,80 44.32,0 80,-35.68001 80,-80 v -200 a 120,120 0 0 1 120,-120 120,120 0 0 1 120,120 v 282.83592 h 160 V 336.41621 a 279.99999,279.99999 0 0 0 -280,-280.000003 279.99999,279.99999 0 0 0 -0.81055,0 279.99999,279.99999 0 0 0 -133.46484,34.29688 c -14.4245,-20.75262 -38.42829,-34.29883 -65.72461,-34.29883 z M 376.65078,619.64082 c -0.62174,0.0972 -1.2172,0.22317 -1.7793,0.37696 0.5621,-0.15379 1.15756,-0.27973 1.7793,-0.37696 z m -1.7793,0.37696 c -0.5621,0.15378 -1.09071,0.33534 -1.58008,0.53906 0.48937,-0.20372 1.01798,-0.38528 1.58008,-0.53906 z m 338.85742,-0.37696 c 0.62174,0.0972 1.2172,0.22317 1.7793,0.37696 -0.5621,-0.15379 -1.15756,-0.27973 -1.7793,-0.37696 z m 1.7793,0.37696 c 0.5621,0.15378 1.09071,0.33534 1.58008,0.53906 -0.48938,-0.20372 -1.01798,-0.38528 -1.58008,-0.53906 z" />
|
||||
<path
|
||||
transform="matrix(0.16895991,0,0,0.18329422,-134.6942,-19.491243)"
|
||||
id="path897"
|
||||
style="display:inline;fill:#730d0d;fill-opacity:1;stroke:#590d0d;stroke-width:9.99999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1456)"
|
||||
d="m 465.18984,619.25215 v 0.16406 h -85.58398 c -5.49302,0 -9.91602,2.2329 -9.91602,5.00586 0,0.52294 0.15913,1.02696 0.45117,1.5 h -0.44531 c 0.30916,2.03607 1.24102,3.99791 2.81641,5.57227 l 165.59961,165.49414 c 3.92061,3.91808 10.23371,3.91812 14.15429,0 L 717.86757,631.49434 c 1.57569,-1.57463 2.50725,-3.53578 2.81641,-5.57227 h -0.44531 c 0.29216,-0.47304 0.45117,-0.97706 0.45117,-1.5 0,-2.77296 -4.42304,-5.00586 -9.91602,-5.00586 h -85.58398 v -0.16406 z" />
|
||||
<rect
|
||||
style="display:inline;fill:#c32222;fill-opacity:1;stroke:none;stroke-width:1.75982;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="rect914"
|
||||
width="6.7583966"
|
||||
height="82.482399"
|
||||
x="-118.27303"
|
||||
y="1.2969339"
|
||||
rx="3.3791983"
|
||||
ry="3.6658845" />
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="256" height="256" version="1.1" viewBox="0 0 67.733 67.733" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m12.869 1.7145c-2.9081 0-5.2493 2.3412-5.2493 5.2493v53.806c0 2.9081 2.3412 5.2493 5.2493 5.2493h41.995c2.9081 0 5.2493-2.3412 5.2493-5.2493v-46.493l-12.562-12.562z" fill="#ececec" stroke="#999" stroke-linejoin="round" stroke-width="1.3123"/>
|
||||
<path d="m60.113 14.277-12.562-12.562v7.3128c0 2.9081 2.3412 5.2493 5.2493 5.2493z" fill="#e0e0e0" stroke="#999" stroke-linejoin="round" stroke-width="1.3123"/>
|
||||
<path d="m34.617 50.796c-0.31888 0-0.57319 0.09267-0.7628 0.27797-0.1853 0.18099-0.27797 0.41156-0.27797 0.69167 0 0.2758 0.09268 0.50636 0.27797 0.69166 0.18961 0.18099 0.44392 0.27144 0.7628 0.27144s0.57306-0.09044 0.76266-0.27144c0.18961-0.1853 0.28438-0.41587 0.28438-0.69166 0-0.28011-0.09477-0.51067-0.28438-0.69167-0.18961-0.1853-0.44378-0.27797-0.76266-0.27797zm-21.512 0.36204v9.4116h1.9456v-3.3226h1.7323c1.1118 0 1.978-0.26924 2.5985-0.8079 0.62484-0.53867 0.93722-1.2627 0.93722-2.172 0-0.90065-0.31238-1.644-0.93722-2.2301-0.62053-0.58607-1.4867-0.87903-2.5985-0.87903zm38.395 0.69154v1.7259h-1.0278v1.364h1.0278v3.6909c0 0.74121 0.1809 1.2713 0.54287 1.5902 0.36198 0.31889 0.84896 0.47828 1.4609 0.47828 0.40076 0 0.77566-0.0582 1.1247-0.17455v-1.4156c-0.17237 0.03448-0.3534 0.05178-0.543 0.05178-0.23701 0-0.41581-0.04745-0.53647-0.14226-0.12066-0.09481-0.18096-0.28867-0.18096-0.58171v-3.497h1.1893v-1.364h-1.1893v-1.7259zm-36.45 0.87916h1.7323c0.55589 0 0.95662 0.15729 1.2022 0.47188 0.24562 0.31027 0.36845 0.67008 0.36845 1.0795 0 0.40939-0.12283 0.7456-0.36845 1.0085-0.24562 0.26287-0.64636 0.39421-1.2022 0.39421h-1.7323zm9.8574 0.71755c-0.67224 0-1.1915 0.32101-1.5578 0.96311l-0.05818-0.83392h-1.7646v6.9942h1.8744v-4.583c0.21977-0.47834 0.68093-0.71756 1.3833-0.71756l0.67872 0.05178 0.01934-1.7906c-0.14651-0.05602-0.33829-0.08395-0.5753-0.08395zm4.2402 0c-1.0514 0-1.8659 0.34041-2.4433 1.0213-0.57743 0.67657-0.86609 1.5233-0.86609 2.5403v0.21979c0.01724 0.99546 0.31244 1.8228 0.88557 2.4822 0.57743 0.65933 1.3897 0.98899 2.4368 0.98899 1.0558 0 1.8702-0.33831 2.4433-1.0149 0.57312-0.67657 0.85968-1.5233 0.85968-2.5403v-0.13572c0-1.017-0.28655-1.8638-0.85968-2.5403-0.57313-0.68088-1.3919-1.0213-2.4563-1.0213zm10.93 0c-0.99974 0-1.7948 0.33819-2.3851 1.0148-0.58605 0.67657-0.87903 1.5429-0.87903 2.5987v0.27144c0.0043 0.93082 0.31236 1.7259 0.92427 2.3853 0.61191 0.65502 1.4608 0.98246 2.5467 0.98246 0.68085 0 1.2498-0.13134 1.7065-0.39421 0.45678-0.26287 0.79721-0.55386 1.0213-0.87275l-0.9244-1.0213c-0.41368 0.52143-0.97605 0.78214-1.6871 0.78214-0.47832 0-0.86398-0.13789-1.157-0.41369-0.29303-0.28011-0.47183-0.64634-0.53647-1.0988h4.4536v-0.79509c0-1.0342-0.26296-1.8659-0.78868-2.4951-0.52572-0.62917-1.2906-0.94376-2.2947-0.94376zm7.0715 0c-1.0816 0-1.8896 0.34251-2.424 1.0277-0.53434 0.68519-0.8015 1.5212-0.8015 2.508v0.18108c0 0.99115 0.26716 1.8293 0.8015 2.5145 0.53865 0.68088 1.3488 1.0213 2.4304 1.0213 0.81013 0 1.4932-0.23909 2.0491-0.71743 0.5602-0.47834 0.84453-1.0881 0.85314-1.8293h-1.7581c-0.0043 0.31889-0.11639 0.57306-0.33616 0.76267-0.21977 0.1853-0.4999 0.27797-0.84033 0.27797-0.55158 0-0.91141-0.20251-1.0795-0.6076-0.16375-0.40939-0.24555-0.88337-0.24555-1.422 0-0.65933 0.08403-1.1916 0.25208-1.5967 0.16806-0.40939 0.52357-0.614 1.0665-0.614 0.37921 0 0.67009 0.12059 0.87262 0.36192 0.20253 0.23702 0.30596 0.52578 0.31027 0.86622h1.7581c-0.0086-0.84032-0.2822-1.5061-0.82085-1.9973-0.53434-0.49127-1.2303-0.73691-2.0878-0.73691zm-13.432 0.12918v7.382c0 0.60331-0.28223 0.90492-0.84674 0.90492-0.19822 0-0.39643-0.01939-0.59465-0.05818v1.4803c0.29734 0.07326 0.60972 0.10983 0.93722 0.10983 0.75411 0 1.338-0.20894 1.7517-0.62695 0.41799-0.41801 0.62707-1.0214 0.62707-1.81v-7.382zm-4.57 1.3769c0.53003 0 0.90284 0.20462 1.1183 0.614 0.21977 0.40508 0.32962 0.88561 0.32962 1.4415 0 0.66364-0.10985 1.1937-0.32962 1.5902-0.21546 0.39646-0.58395 0.59465-1.1054 0.59465-0.53004 0-0.90494-0.19819-1.1247-0.59465-0.21546-0.40077-0.32321-0.88562-0.32321-1.4545 0-0.65072 0.10775-1.1786 0.32321-1.5836 0.21546-0.40508 0.58604-0.6076 1.1118-0.6076zm10.917 0c0.43092 0 0.7454 0.12492 0.94362 0.37486 0.20254 0.24563 0.30819 0.5559 0.3168 0.93081v0.14226h-2.5985c0.06464-0.43094 0.20477-0.78 0.42023-1.0472 0.21546-0.26718 0.52142-0.40075 0.91786-0.40075z" fill="#666" stroke-width=".1773" style="shape-inside:url(#rect2964);white-space:pre"/>
|
||||
<path d="m16.933 38.766h1.5478q1.2383 0 1.7336-0.49531 0.55722-0.55722 0.55722-1.7955v-15.912q0-1.2383-0.49531-1.7336-0.49531-0.55722-1.7336-0.55722h-1.3002v-4.6435h7.9249l0.37148 4.1482q2.1051-2.6623 4.7673-3.8386 2.7242-1.2383 6.5628-1.2383 9.9062 0 9.9062 10.278v13.497q0 1.2383 0.49531 1.7955 0.55722 0.49531 1.7955 0.49531h1.7336v4.5816h-10.649v-20.308q0-3.0957-1.3002-4.4578-1.3002-1.424-4.2101-1.424-3.1576 0-5.2007 1.7955-2.0431 1.7955-2.0431 5.015v12.507q0 1.2383 0.43339 1.7955 0.49531 0.49531 1.6717 0.49531h1.6098v4.5816h-14.178z" fill="#346598" stroke-linecap="round" stroke-width=".16123"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -10,9 +10,9 @@ synchronisation tools. All text is saved as plain text files with a meta data he
|
||||
project structure is stored in a single project XML file, and other meta data is primarily saved as
|
||||
JSON files.
|
||||
|
||||
The application is written with Python 3 (3.8+) using Qt5 and PyQt5 (5.3+). It is developed on
|
||||
The application is written with Python 3 (3.8+) using Qt5 and PyQt5 (5.10+). It is developed on
|
||||
Linux, but should in principle work fine on other operating systems as well as long as dependencies
|
||||
are met. It is regularly tested on Debian and Ubuntu Linux, Windows, and macOS.
|
||||
are met. It is regularly tested on Debian and Ubuntu Linux, Windows, and MacOS.
|
||||
|
||||
novelWriter is developed and maintained by [Veronica Berglyd Olsen](https://github.com/vkbo).
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 167 KiB |
@@ -1,207 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 270.93333 270.93334"
|
||||
height="1024"
|
||||
width="1024">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient2515">
|
||||
<stop
|
||||
style="stop-color:#f7f7f7;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2511" />
|
||||
<stop
|
||||
style="stop-color:#e5e5e5;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2513" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
x="60.484347"
|
||||
y="205.79962"
|
||||
width="130.57787"
|
||||
height="65.785888"
|
||||
id="rect852" />
|
||||
<rect
|
||||
id="rect1474"
|
||||
height="28.631245"
|
||||
width="177.70083"
|
||||
y="18.019813"
|
||||
x="13.463703" />
|
||||
<rect
|
||||
id="rect852-7"
|
||||
height="65.785889"
|
||||
width="130.57787"
|
||||
y="205.79962"
|
||||
x="60.484348" />
|
||||
<rect
|
||||
id="rect1544"
|
||||
height="65.785889"
|
||||
width="130.57787"
|
||||
y="205.79962"
|
||||
x="60.484348" />
|
||||
<rect
|
||||
x="60.484348"
|
||||
y="205.79962"
|
||||
width="130.57787"
|
||||
height="65.785889"
|
||||
id="rect1570" />
|
||||
<rect
|
||||
x="13.463703"
|
||||
y="18.019813"
|
||||
width="177.70083"
|
||||
height="28.631245"
|
||||
id="rect1587" />
|
||||
<filter
|
||||
id="filter1444"
|
||||
style="color-interpolation-filters:sRGB">
|
||||
<feFlood
|
||||
id="feFlood1434"
|
||||
result="flood"
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="0.498039" />
|
||||
<feComposite
|
||||
id="feComposite1436"
|
||||
result="composite1"
|
||||
operator="in"
|
||||
in2="SourceGraphic"
|
||||
in="flood" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1438"
|
||||
result="blur"
|
||||
stdDeviation="3"
|
||||
in="composite1" />
|
||||
<feOffset
|
||||
id="feOffset1440"
|
||||
result="offset"
|
||||
dy="6"
|
||||
dx="6" />
|
||||
<feComposite
|
||||
id="feComposite1442"
|
||||
result="composite2"
|
||||
operator="over"
|
||||
in2="offset"
|
||||
in="SourceGraphic" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1456"
|
||||
style="color-interpolation-filters:sRGB">
|
||||
<feFlood
|
||||
id="feFlood1446"
|
||||
result="flood"
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="0.498039" />
|
||||
<feComposite
|
||||
id="feComposite1448"
|
||||
result="composite1"
|
||||
operator="in"
|
||||
in2="SourceGraphic"
|
||||
in="flood" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur1450"
|
||||
result="blur"
|
||||
stdDeviation="3"
|
||||
in="composite1" />
|
||||
<feOffset
|
||||
id="feOffset1452"
|
||||
result="offset"
|
||||
dy="6"
|
||||
dx="6" />
|
||||
<feComposite
|
||||
id="feComposite1454"
|
||||
result="composite2"
|
||||
operator="over"
|
||||
in2="offset"
|
||||
in="SourceGraphic" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient2515"
|
||||
id="radialGradient2517"
|
||||
cx="605.42725"
|
||||
cy="623.97186"
|
||||
fx="605.42725"
|
||||
fy="623.97186"
|
||||
r="410"
|
||||
gradientTransform="matrix(2.4110274,1.7617596,-0.719517,0.98463462,-776.23441,-1372.7537)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer3"
|
||||
style="display:inline">
|
||||
<path
|
||||
clip-path="none"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 192,22 c -44.32,0 -80,35.680002 -80,80 v 820 c 0,44.32002 35.68,80 80,80 h 640 c 44.31999,0 80,-35.67998 80,-80 V 213.44727 L 720.55469,22 Z"
|
||||
style="fill:url(#radialGradient2517);fill-opacity:1;stroke:#999999;stroke-width:20;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect1481" />
|
||||
<path
|
||||
id="path1490"
|
||||
style="display:inline;fill:#e0e0e0;fill-opacity:1;stroke:#999999;stroke-width:5.29167;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 241.30037,56.474215 190.64713,5.8204593 V 35.307549 c 0,11.726333 9.44034,21.166666 21.16667,21.166666 z" />
|
||||
<g
|
||||
aria-label=".nwx"
|
||||
transform="matrix(1.7893481,0,0,1.782571,-45.41919,-191.90571)"
|
||||
id="text850"
|
||||
style="font-size:5.64444px;line-height:1.25;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans, Normal';letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect852);display:inline;fill:#666666;fill-opacity:1">
|
||||
<path
|
||||
d="m 63.088865,241.76928 q 0,-1.36426 0.909505,-2.27376 0.930175,-0.93018 2.480467,-0.93018 1.550291,0 2.459796,0.93018 0.909504,0.9095 0.909504,2.27376 0,1.34359 -0.909504,2.27376 -0.909505,0.9095 -2.459796,0.9095 -1.550292,0 -2.480467,-0.9095 -0.909505,-0.93017 -0.909505,-2.27376 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path856" />
|
||||
<path
|
||||
d="m 84.75161,226.78312 q -2.542479,0 -3.782712,2.14974 v 15.79231 h -5.973791 v -22.36554 h 5.601721 l 0.206705,2.56314 q 2.397785,-2.97656 6.428544,-2.97656 3.162595,0 5.105627,1.88103 1.943033,1.88102 1.963703,6.55256 v 14.34537 h -5.994461 v -14.26269 q 0,-2.14973 -0.930175,-2.91454 -0.909505,-0.76482 -2.625161,-0.76482 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path858" />
|
||||
<path
|
||||
d="m 126.96089,222.35963 -5.68441,22.36554 h -5.00227 l -4.25814,-14.07665 -4.23746,14.07665 h -4.9816 l -5.705078,-22.36554 h 5.767088 l 2.95589,14.22134 4.05143,-14.22134 h 4.32014 l 4.09277,14.20067 2.91455,-14.20067 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path860" />
|
||||
<path
|
||||
d="m 134.77436,222.35963 3.65869,6.65592 3.76204,-6.65592 h 6.3872 l -6.36653,10.93472 6.63525,11.43082 h -6.3872 l -4.01009,-7.04866 -3.94808,7.04866 h -6.42854 l 6.63525,-11.43082 -6.36654,-10.93472 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;fill-opacity:1"
|
||||
id="path862" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g2509"
|
||||
transform="matrix(1.2371037,0,0,1.2371037,206.3182,37.341857)">
|
||||
<path
|
||||
transform="matrix(0.16895991,0,0,0.18329422,-134.6942,-19.491243)"
|
||||
id="rect1406-1"
|
||||
style="display:inline;fill:#a11212;fill-opacity:1;stroke:#7e1b1b;stroke-width:9.99999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1444)"
|
||||
d="m 145.18984,56.414257 c -44.32,0 -79.999998,35.68 -79.999998,80.000003 v 199.99805 a 279.99999,279.99999 0 0 0 0,0.002 279.99999,279.99999 0 0 0 0,0.0195 v 199.98242 c 0,44.31999 35.679998,80 79.999998,80 44.32,0 80,-35.68001 80,-80 v -200 a 120,120 0 0 1 120,-120 120,120 0 0 1 120,120 v 282.83592 h 160 V 336.41621 a 279.99999,279.99999 0 0 0 -280,-280.000003 279.99999,279.99999 0 0 0 -0.81055,0 279.99999,279.99999 0 0 0 -133.46484,34.29688 c -14.4245,-20.75262 -38.42829,-34.29883 -65.72461,-34.29883 z M 376.65078,619.64082 c -0.62174,0.0972 -1.2172,0.22317 -1.7793,0.37696 0.5621,-0.15379 1.15756,-0.27973 1.7793,-0.37696 z m -1.7793,0.37696 c -0.5621,0.15378 -1.09071,0.33534 -1.58008,0.53906 0.48937,-0.20372 1.01798,-0.38528 1.58008,-0.53906 z m 338.85742,-0.37696 c 0.62174,0.0972 1.2172,0.22317 1.7793,0.37696 -0.5621,-0.15379 -1.15756,-0.27973 -1.7793,-0.37696 z m 1.7793,0.37696 c 0.5621,0.15378 1.09071,0.33534 1.58008,0.53906 -0.48938,-0.20372 -1.01798,-0.38528 -1.58008,-0.53906 z" />
|
||||
<path
|
||||
transform="matrix(0.16895991,0,0,0.18329422,-134.6942,-19.491243)"
|
||||
id="path897"
|
||||
style="display:inline;fill:#730d0d;fill-opacity:1;stroke:#590d0d;stroke-width:9.99999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1456)"
|
||||
d="m 465.18984,619.25215 v 0.16406 h -85.58398 c -5.49302,0 -9.91602,2.2329 -9.91602,5.00586 0,0.52294 0.15913,1.02696 0.45117,1.5 h -0.44531 c 0.30916,2.03607 1.24102,3.99791 2.81641,5.57227 l 165.59961,165.49414 c 3.92061,3.91808 10.23371,3.91812 14.15429,0 L 717.86757,631.49434 c 1.57569,-1.57463 2.50725,-3.53578 2.81641,-5.57227 h -0.44531 c 0.29216,-0.47304 0.45117,-0.97706 0.45117,-1.5 0,-2.77296 -4.42304,-5.00586 -9.91602,-5.00586 h -85.58398 v -0.16406 z" />
|
||||
<rect
|
||||
style="display:inline;fill:#c32222;fill-opacity:1;stroke:none;stroke-width:1.75982;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="rect914"
|
||||
width="6.7583966"
|
||||
height="82.482399"
|
||||
x="-118.27303"
|
||||
y="1.2969339"
|
||||
rx="3.3791983"
|
||||
ry="3.6658845" />
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="256" height="256" version="1.1" viewBox="0 0 67.733 67.733" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m12.869 1.7145c-2.9081 0-5.2493 2.3412-5.2493 5.2493v53.806c0 2.9081 2.3412 5.2493 5.2493 5.2493h41.995c2.9081 0 5.2493-2.3412 5.2493-5.2493v-46.493l-12.562-12.562z" fill="#ececec" stroke="#999" stroke-linejoin="round" stroke-width="1.3123"/>
|
||||
<path d="m60.113 14.277-12.562-12.562v7.3128c0 2.9081 2.3412 5.2493 5.2493 5.2493z" fill="#e0e0e0" stroke="#999" stroke-linejoin="round" stroke-width="1.3123"/>
|
||||
<path d="m34.617 50.796c-0.31888 0-0.57319 0.09267-0.7628 0.27797-0.1853 0.18099-0.27797 0.41156-0.27797 0.69167 0 0.2758 0.09268 0.50636 0.27797 0.69166 0.18961 0.18099 0.44392 0.27144 0.7628 0.27144s0.57306-0.09044 0.76266-0.27144c0.18961-0.1853 0.28438-0.41587 0.28438-0.69166 0-0.28011-0.09477-0.51067-0.28438-0.69167-0.18961-0.1853-0.44378-0.27797-0.76266-0.27797zm-21.512 0.36204v9.4116h1.9456v-3.3226h1.7323c1.1118 0 1.978-0.26924 2.5985-0.8079 0.62484-0.53867 0.93722-1.2627 0.93722-2.172 0-0.90065-0.31238-1.644-0.93722-2.2301-0.62053-0.58607-1.4867-0.87903-2.5985-0.87903zm38.395 0.69154v1.7259h-1.0278v1.364h1.0278v3.6909c0 0.74121 0.1809 1.2713 0.54287 1.5902 0.36198 0.31889 0.84896 0.47828 1.4609 0.47828 0.40076 0 0.77566-0.0582 1.1247-0.17455v-1.4156c-0.17237 0.03448-0.3534 0.05178-0.543 0.05178-0.23701 0-0.41581-0.04745-0.53647-0.14226-0.12066-0.09481-0.18096-0.28867-0.18096-0.58171v-3.497h1.1893v-1.364h-1.1893v-1.7259zm-36.45 0.87916h1.7323c0.55589 0 0.95662 0.15729 1.2022 0.47188 0.24562 0.31027 0.36845 0.67008 0.36845 1.0795 0 0.40939-0.12283 0.7456-0.36845 1.0085-0.24562 0.26287-0.64636 0.39421-1.2022 0.39421h-1.7323zm9.8574 0.71755c-0.67224 0-1.1915 0.32101-1.5578 0.96311l-0.05818-0.83392h-1.7646v6.9942h1.8744v-4.583c0.21977-0.47834 0.68093-0.71756 1.3833-0.71756l0.67872 0.05178 0.01934-1.7906c-0.14651-0.05602-0.33829-0.08395-0.5753-0.08395zm4.2402 0c-1.0514 0-1.8659 0.34041-2.4433 1.0213-0.57743 0.67657-0.86609 1.5233-0.86609 2.5403v0.21979c0.01724 0.99546 0.31244 1.8228 0.88557 2.4822 0.57743 0.65933 1.3897 0.98899 2.4368 0.98899 1.0558 0 1.8702-0.33831 2.4433-1.0149 0.57312-0.67657 0.85968-1.5233 0.85968-2.5403v-0.13572c0-1.017-0.28655-1.8638-0.85968-2.5403-0.57313-0.68088-1.3919-1.0213-2.4563-1.0213zm10.93 0c-0.99974 0-1.7948 0.33819-2.3851 1.0148-0.58605 0.67657-0.87903 1.5429-0.87903 2.5987v0.27144c0.0043 0.93082 0.31236 1.7259 0.92427 2.3853 0.61191 0.65502 1.4608 0.98246 2.5467 0.98246 0.68085 0 1.2498-0.13134 1.7065-0.39421 0.45678-0.26287 0.79721-0.55386 1.0213-0.87275l-0.9244-1.0213c-0.41368 0.52143-0.97605 0.78214-1.6871 0.78214-0.47832 0-0.86398-0.13789-1.157-0.41369-0.29303-0.28011-0.47183-0.64634-0.53647-1.0988h4.4536v-0.79509c0-1.0342-0.26296-1.8659-0.78868-2.4951-0.52572-0.62917-1.2906-0.94376-2.2947-0.94376zm7.0715 0c-1.0816 0-1.8896 0.34251-2.424 1.0277-0.53434 0.68519-0.8015 1.5212-0.8015 2.508v0.18108c0 0.99115 0.26716 1.8293 0.8015 2.5145 0.53865 0.68088 1.3488 1.0213 2.4304 1.0213 0.81013 0 1.4932-0.23909 2.0491-0.71743 0.5602-0.47834 0.84453-1.0881 0.85314-1.8293h-1.7581c-0.0043 0.31889-0.11639 0.57306-0.33616 0.76267-0.21977 0.1853-0.4999 0.27797-0.84033 0.27797-0.55158 0-0.91141-0.20251-1.0795-0.6076-0.16375-0.40939-0.24555-0.88337-0.24555-1.422 0-0.65933 0.08403-1.1916 0.25208-1.5967 0.16806-0.40939 0.52357-0.614 1.0665-0.614 0.37921 0 0.67009 0.12059 0.87262 0.36192 0.20253 0.23702 0.30596 0.52578 0.31027 0.86622h1.7581c-0.0086-0.84032-0.2822-1.5061-0.82085-1.9973-0.53434-0.49127-1.2303-0.73691-2.0878-0.73691zm-13.432 0.12918v7.382c0 0.60331-0.28223 0.90492-0.84674 0.90492-0.19822 0-0.39643-0.01939-0.59465-0.05818v1.4803c0.29734 0.07326 0.60972 0.10983 0.93722 0.10983 0.75411 0 1.338-0.20894 1.7517-0.62695 0.41799-0.41801 0.62707-1.0214 0.62707-1.81v-7.382zm-4.57 1.3769c0.53003 0 0.90284 0.20462 1.1183 0.614 0.21977 0.40508 0.32962 0.88561 0.32962 1.4415 0 0.66364-0.10985 1.1937-0.32962 1.5902-0.21546 0.39646-0.58395 0.59465-1.1054 0.59465-0.53004 0-0.90494-0.19819-1.1247-0.59465-0.21546-0.40077-0.32321-0.88562-0.32321-1.4545 0-0.65072 0.10775-1.1786 0.32321-1.5836 0.21546-0.40508 0.58604-0.6076 1.1118-0.6076zm10.917 0c0.43092 0 0.7454 0.12492 0.94362 0.37486 0.20254 0.24563 0.30819 0.5559 0.3168 0.93081v0.14226h-2.5985c0.06464-0.43094 0.20477-0.78 0.42023-1.0472 0.21546-0.26718 0.52142-0.40075 0.91786-0.40075z" fill="#666" stroke-width=".1773" style="shape-inside:url(#rect2964);white-space:pre"/>
|
||||
<path d="m16.933 38.766h1.5478q1.2383 0 1.7336-0.49531 0.55722-0.55722 0.55722-1.7955v-15.912q0-1.2383-0.49531-1.7336-0.49531-0.55722-1.7336-0.55722h-1.3002v-4.6435h7.9249l0.37148 4.1482q2.1051-2.6623 4.7673-3.8386 2.7242-1.2383 6.5628-1.2383 9.9062 0 9.9062 10.278v13.497q0 1.2383 0.49531 1.7955 0.55722 0.49531 1.7955 0.49531h1.7336v4.5816h-10.649v-20.308q0-3.0957-1.3002-4.4578-1.3002-1.424-4.2101-1.424-3.1576 0-5.2007 1.7955-2.0431 1.7955-2.0431 5.015v12.507q0 1.2383 0.43339 1.7955 0.49531 0.49531 1.6717 0.49531h1.6098v4.5816h-14.178z" fill="#346598" stroke-linecap="round" stroke-width=".16123"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -62,6 +62,14 @@ fi
|
||||
echo "Content of current dir:"
|
||||
ls -lah .
|
||||
|
||||
# Create icon
|
||||
echo "Creating icon ..."
|
||||
pushd "$SRC_DIR/setup/macos" || exit 1
|
||||
iconutil -c icns $SRC_DIR/setup/macos/novelwriter.iconset
|
||||
echo "Content of current dir:"
|
||||
ls -lah .
|
||||
|
||||
popd || exit 1
|
||||
popd || exit 1
|
||||
pushd "$BUILD_DIR"/ || exit 1
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
function generate_linux () {
|
||||
echo "Generating on Linux ..."
|
||||
if ! command -v png2icns &> /dev/null
|
||||
then
|
||||
echo "png2icns cound not be found, it is required. Please install a package like icnsutils or libicns."
|
||||
exit
|
||||
fi
|
||||
|
||||
if ! command -v convert $> /dev/null
|
||||
then
|
||||
echo "convert could not be found, it is required. please install a imagemagick package."
|
||||
exit
|
||||
fi
|
||||
|
||||
mkdir -p $SCRIPT_DIR/icons
|
||||
|
||||
sizes=( 16 32 64 128 256 )
|
||||
for base in "${sizes[@]}"; do
|
||||
let basex2=${base}*2
|
||||
size="${base}x${base}"
|
||||
echo "Copying files for $size"
|
||||
double=${basex2}x${basex2}
|
||||
cp $SCRIPT_DIR/../data/hicolor/${size}/apps/novelwriter.png $SCRIPT_DIR/icons/icon_${size}.png
|
||||
echo "Resizing ${size}@2x to $double from $size"
|
||||
convert $SCRIPT_DIR/icons/icon_${size}.png -resize $double $SCRIPT_DIR/icons/icon_${size}@2x.png
|
||||
done
|
||||
|
||||
png2icns $SCRIPT_DIR/novelwriter.icns $SCRIPT_DIR/icons/icon_*.png
|
||||
|
||||
rm -r $SCRIPT_DIR/icons
|
||||
|
||||
echo "Done"
|
||||
}
|
||||
|
||||
function generate_macos () {
|
||||
echo "Generating on MacOs ..."
|
||||
mkdir -p $SCRIPT_DIR/icons
|
||||
|
||||
echo "Building Iconset ..."
|
||||
sizes=( 16 32 64 128 256 )
|
||||
for base in "${sizes[@]}"; do
|
||||
let basex2=${base}*2
|
||||
size="${base}x${base}"
|
||||
echo "Copying files for $size"
|
||||
double=${basex2}x${basex2}
|
||||
cp $SCRIPT_DIR/../data/hicolor/${size}/apps/novelwriter.png $SCRIPT_DIR/icons/icon_${size}.png
|
||||
cp $SCRIPT_DIR/../data/hicolor/${size}/apps/novelwriter.png $SCRIPT_DIR/icons/icon_${size}@2x.png
|
||||
echo "Resizing ${size}@2x to $double from $size"
|
||||
sips -Z $basex2 $SCRIPT_DIR/icons/icon_${size}@2x.png
|
||||
done
|
||||
|
||||
rm -rf $SCRIPT_DIR/novelwriter.iconset
|
||||
mv $SCRIPT_DIR/icons $SCRIPT_DIR/novelwriter.iconset
|
||||
|
||||
echo "Generating icns ..."
|
||||
iconutil -c icns $SCRIPT_DIR/novelwriter.iconset
|
||||
|
||||
echo "Done"
|
||||
}
|
||||
|
||||
unameOut="$(uname -s)"
|
||||
case "$unameOut" in
|
||||
Linux*) generate_linux;;
|
||||
Darwin*) generate_macos;;
|
||||
*) echo "Unsupported OS"
|
||||
esac
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 563 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 38 KiB |
@@ -26,8 +26,8 @@ echo " Building Minimal Packages"
|
||||
echo "================================================================================"
|
||||
echo ""
|
||||
python3 pkgutils.py minimal-zip --target-win
|
||||
python3 pkgutils.py minimal-zip --target-linux
|
||||
python3 pkgutils.py minimal-zip --target-darwin
|
||||
# python3 pkgutils.py minimal-zip --target-linux
|
||||
# python3 pkgutils.py minimal-zip --target-darwin
|
||||
|
||||
echo ""
|
||||
echo " Building Linux Packages"
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 20 KiB |
@@ -45,6 +45,7 @@ repdash = True
|
||||
repdots = True
|
||||
autoscroll = False
|
||||
autoscrollpos = 30
|
||||
scrollpastend = True
|
||||
fmtsquoteopen = ‘
|
||||
fmtsquoteclose = ’
|
||||
fmtdquoteopen = “
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<language>en_GB</language>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
|
||||
@@ -45,6 +45,7 @@ repdash = True
|
||||
repdots = True
|
||||
autoscroll = True
|
||||
autoscrollpos = 30
|
||||
scrollpastend = True
|
||||
fmtsquoteopen = ‘
|
||||
fmtsquoteclose = ’
|
||||
fmtdquoteopen = “
|
||||
|
||||
@@ -435,7 +435,7 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
|
||||
(buildIDTwo, "Build Two"),
|
||||
(buildIDOne, "Build One"),
|
||||
]
|
||||
builds.setLastBuild(buildIDOne)
|
||||
builds.setBuildsState(buildIDOne, [buildIDTwo, buildIDOne])
|
||||
builds.setDefaultBuild(buildIDTwo)
|
||||
|
||||
# Check errors: No valid path
|
||||
|
||||
@@ -446,6 +446,7 @@ def testCoreTools_NewCustomA(monkeypatch, fncPath, tstPaths, mockRnd):
|
||||
"projName": "Test Custom",
|
||||
"projTitle": "Test Novel",
|
||||
"projAuthor": "Jane Doe",
|
||||
"projLang": -1,
|
||||
"projPath": fncPath,
|
||||
"popSample": False,
|
||||
"popMinimal": False,
|
||||
@@ -484,6 +485,7 @@ def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockRnd):
|
||||
"projName": "Test Custom",
|
||||
"projTitle": "Test Novel",
|
||||
"projAuthor": "Jane Doe",
|
||||
"projLang": -1,
|
||||
"projPath": fncPath,
|
||||
"popSample": False,
|
||||
"popMinimal": False,
|
||||
|
||||
@@ -155,9 +155,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
|
||||
# Set some values
|
||||
theProject = SHARED.project
|
||||
theProject.tree[C.hTitlePage].setStatus(C.sFinished)
|
||||
theProject.tree[C.hChapterDoc].setStatus(C.sDraft)
|
||||
theProject.tree[C.hSceneDoc].setStatus(C.sDraft)
|
||||
theProject.tree[C.hTitlePage].setStatus(C.sFinished) # type: ignore
|
||||
theProject.tree[C.hChapterDoc].setStatus(C.sDraft) # type: ignore
|
||||
theProject.tree[C.hSceneDoc].setStatus(C.sDraft) # type: ignore
|
||||
|
||||
nwGUI.projView.projTree.setSelectedHandle(C.hPlotRoot)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||
@@ -170,9 +170,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
hCharNote = "0000000000011"
|
||||
hWorldNote = "0000000000012"
|
||||
|
||||
theProject.tree[hPlotNote].setImport(C.iMajor)
|
||||
theProject.tree[hCharNote].setImport(C.iMajor)
|
||||
theProject.tree[hWorldNote].setImport(C.iMain)
|
||||
theProject.tree[hPlotNote].setImport(C.iMajor) # type: ignore
|
||||
theProject.tree[hCharNote].setImport(C.iMajor) # type: ignore
|
||||
theProject.tree[hWorldNote].setImport(C.iMain) # type: ignore
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_STATUS)
|
||||
@@ -367,23 +367,23 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo
|
||||
|
||||
tabReplace = projSettings.tabReplace
|
||||
|
||||
assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
|
||||
assert tabReplace.listBox.topLevelItem(0).text(1) == "B"
|
||||
assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
|
||||
assert tabReplace.listBox.topLevelItem(1).text(1) == "D"
|
||||
assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(0).text(1) == "B" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(1).text(1) == "D" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||
|
||||
# Nothing to save or delete
|
||||
tabReplace.listBox.clearSelection()
|
||||
assert tabReplace._saveEntry() is False
|
||||
assert tabReplace._delEntry() is False
|
||||
tabReplace._saveEntry()
|
||||
tabReplace._delEntry()
|
||||
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||
|
||||
# Create a new entry
|
||||
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||
assert tabReplace.listBox.topLevelItem(2).text(0) == "<keyword3>"
|
||||
assert tabReplace.listBox.topLevelItem(2).text(1) == ""
|
||||
assert tabReplace.listBox.topLevelItem(2).text(0) == "<keyword3>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(2).text(1) == "" # type: ignore
|
||||
|
||||
# Edit the entry
|
||||
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
|
||||
@@ -394,8 +394,8 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo
|
||||
for c in "With This Stuff ":
|
||||
qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
|
||||
assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>"
|
||||
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff "
|
||||
assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore
|
||||
|
||||
# Create a new entry again
|
||||
tabReplace.listBox.clearSelection()
|
||||
@@ -405,7 +405,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo
|
||||
# The list is sorted, so we must find it
|
||||
newIdx = -1
|
||||
for i in range(tabReplace.listBox.topLevelItemCount()):
|
||||
if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
|
||||
if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>": # type: ignore
|
||||
newIdx = i
|
||||
break
|
||||
assert newIdx >= 0
|
||||
|
||||
@@ -672,7 +672,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd):
|
||||
def testGuiEditor_TextManipulation(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
||||
"""Test the text manipulation functions."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
@@ -680,37 +680,6 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
||||
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
|
||||
# Clear Surrounding
|
||||
# =================
|
||||
|
||||
# No Selection
|
||||
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
nwGUI.docEditor.setCursorPosition(45)
|
||||
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
assert nwGUI.docEditor._clearSurrounding(cursor, 1) is False
|
||||
|
||||
# Clear Characters, 1 Layer
|
||||
repText = text.replace("consectetur", "=consectetur=")
|
||||
nwGUI.docEditor.replaceText(repText)
|
||||
nwGUI.docEditor.setCursorPosition(45)
|
||||
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
cursor.select(QTextCursor.WordUnderCursor)
|
||||
assert nwGUI.docEditor._clearSurrounding(cursor, 1) is True
|
||||
assert nwGUI.docEditor.getText() == text
|
||||
|
||||
# Clear Characters, 2 Layers
|
||||
repText = text.replace("consectetur", "==consectetur==")
|
||||
nwGUI.docEditor.replaceText(repText)
|
||||
nwGUI.docEditor.setCursorPosition(45)
|
||||
|
||||
cursor = nwGUI.docEditor.textCursor()
|
||||
cursor.select(QTextCursor.WordUnderCursor)
|
||||
assert nwGUI.docEditor._clearSurrounding(cursor, 2) is True
|
||||
assert nwGUI.docEditor.getText() == text
|
||||
|
||||
# Wrap Selection
|
||||
# ==============
|
||||
|
||||
@@ -718,11 +687,6 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
nwGUI.docEditor.setCursorPosition(45)
|
||||
|
||||
# No Selection
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "_autoSelect", lambda: QTextCursor())
|
||||
assert nwGUI.docEditor._wrapSelection("=", "=") is False
|
||||
|
||||
# Wrap Equal
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
nwGUI.docEditor.setCursorPosition(45)
|
||||
@@ -752,13 +716,13 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
||||
# =============
|
||||
|
||||
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2])
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
nwGUI.docEditor.setCursorPosition(45)
|
||||
|
||||
# No Selection
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "_autoSelect", lambda: QTextCursor())
|
||||
assert nwGUI.docEditor._toggleFormat(2, "=") is False
|
||||
# Block format repetition
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
nwGUI.docEditor.setCursorPosition(39)
|
||||
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
||||
assert nwGUI.docEditor.getText() == text.replace("amet", "=amet=", 1)
|
||||
assert nwGUI.docEditor._toggleFormat(1, "=") is False
|
||||
|
||||
# Wrap Single Equal
|
||||
nwGUI.docEditor.replaceText(text)
|
||||
|
||||
@@ -562,7 +562,6 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
|
||||
assert bSettings.toolStack.currentWidget() is fmtTab
|
||||
|
||||
# Check initial values
|
||||
assert fmtTab.buildLang.currentData() == "en_US"
|
||||
assert fmtTab.textFont.text() == textFont
|
||||
assert fmtTab.textSize.value() == 12
|
||||
assert fmtTab.lineHeight.value() == 1.2
|
||||
@@ -581,7 +580,6 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
|
||||
assert fmtTab.rightMargin.value() == 15.0
|
||||
|
||||
# Change values
|
||||
fmtTab.buildLang.setCurrentIndex(fmtTab.buildLang.findData("en_GB"))
|
||||
fmtTab.textFont.setText("Arial")
|
||||
fmtTab.textSize.setValue(11)
|
||||
fmtTab.lineHeight.setValue(1.15)
|
||||
@@ -596,7 +594,6 @@ def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
|
||||
# Save values
|
||||
fmtTab.saveContent()
|
||||
|
||||
assert build.getStr("format.buildLang") == "en_GB"
|
||||
assert build.getStr("format.textFont") == "Arial"
|
||||
assert build.getInt("format.textSize") == 11
|
||||
assert build.getFloat("format.lineHeight") == 1.15
|
||||
|
||||