Compare commits
876 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83e92994d1 | ||
|
|
33146f0a50 | ||
|
|
732450e163 | ||
|
|
df8e3dd82b | ||
|
|
75ddafd31d | ||
|
|
4f38a8d16c | ||
|
|
35746103c7 | ||
|
|
fb5347b8e4 | ||
|
|
6890b278ea | ||
|
|
9f667a0b35 | ||
|
|
36be7b0b33 | ||
|
|
1d16cc2a03 | ||
|
|
72fd2990e7 | ||
|
|
6d7ca2bb86 | ||
|
|
f10a8f9589 | ||
|
|
1881b3d32c | ||
|
|
1b89d0a4f0 | ||
|
|
671888b6ea | ||
|
|
50498a7462 | ||
|
|
ecd190987e | ||
|
|
2c000f6808 | ||
|
|
c472e40b2b | ||
|
|
0575f89ae9 | ||
|
|
e1dcfaa1e3 | ||
|
|
486af84107 | ||
|
|
8acbd8b978 | ||
|
|
7f9180e356 | ||
|
|
26912f226f | ||
|
|
0c286231cc | ||
|
|
991a61f2f6 | ||
|
|
edfd7c9770 | ||
|
|
ad62faaeea | ||
|
|
468903bbeb | ||
|
|
0723f44dc3 | ||
|
|
831bdabfc6 | ||
|
|
93a3eed530 | ||
|
|
307a52e7f4 | ||
|
|
3246f4bfcc | ||
|
|
fa4f4a8db4 | ||
|
|
cb797fe903 | ||
|
|
618461b1ff | ||
|
|
e2f08d3c76 | ||
|
|
f39cda729d | ||
|
|
b28d826d76 | ||
|
|
57ae559723 | ||
|
|
1d1be18689 | ||
|
|
141b5eb776 | ||
|
|
3f941bf9fd | ||
|
|
a48f302531 | ||
|
|
12d8872973 | ||
|
|
40ff865989 | ||
|
|
32e9c10702 | ||
|
|
804dd3bb7c | ||
|
|
9f2e3a66cd | ||
|
|
960d1967a3 | ||
|
|
ad025c3fe4 | ||
|
|
81212a108e | ||
|
|
8637a4eb12 | ||
|
|
0e4489d627 | ||
|
|
0bfb9d0700 | ||
|
|
efe759b94e | ||
|
|
9f89c1f7a4 | ||
|
|
530d92cc3f | ||
|
|
b0225515d2 | ||
|
|
d511c69ee7 | ||
|
|
6e1e40f13e | ||
|
|
70d3ee07bf | ||
|
|
e5477f314e | ||
|
|
80c4826253 | ||
|
|
e0de3ff191 | ||
|
|
239cff3dee | ||
|
|
6f9e3168b0 | ||
|
|
7d067f807d | ||
|
|
a7b43699ed | ||
|
|
7d11917d1c | ||
|
|
904ba9b1ca | ||
|
|
616fe35ed5 | ||
|
|
577a3c6c6a | ||
|
|
e937267eb0 | ||
|
|
2c1e1b94e7 | ||
|
|
60736ce04c | ||
|
|
03daa350f8 | ||
|
|
095cc38122 | ||
|
|
142d659b70 | ||
|
|
0826a2f8a3 | ||
|
|
a3819a7785 | ||
|
|
2314777b4b | ||
|
|
729e97a95d | ||
|
|
5ab60ca4a3 | ||
|
|
f1adff82a8 | ||
|
|
96b2d139c9 | ||
|
|
e1bc1222c4 | ||
|
|
0bff4a3d21 | ||
|
|
dfc775c925 | ||
|
|
dea304d1eb | ||
|
|
cfd80645e2 | ||
|
|
4b7f7887c8 | ||
|
|
9fc640aa59 | ||
|
|
d812aab173 | ||
|
|
39b58711c7 | ||
|
|
c6120fcf7c | ||
|
|
45b9215c87 | ||
|
|
394f6adf82 | ||
|
|
1a5076438d | ||
|
|
872b8e7902 | ||
|
|
0cad96fdca | ||
|
|
e9ef4fd091 | ||
|
|
4ce8b38acd | ||
|
|
24ab6f2d3b | ||
|
|
ba5e5f4bb5 | ||
|
|
6ef8611a0a | ||
|
|
486676baa5 | ||
|
|
542cbf13ff | ||
|
|
62630eb519 | ||
|
|
b1d597e63f | ||
|
|
887eee01fc | ||
|
|
f605f1fc75 | ||
|
|
cc8fc38734 | ||
|
|
5797b8655d | ||
|
|
fd4d663a1c | ||
|
|
88263d55f2 | ||
|
|
5c4398f948 | ||
|
|
9ff397dfbc | ||
|
|
1c9ebfd11c | ||
|
|
22e8d7ee71 | ||
|
|
35458a7e37 | ||
|
|
c1544ab374 | ||
|
|
d795eb6f5f | ||
|
|
ce8264b8f2 | ||
|
|
c6cbc8a8b1 | ||
|
|
6c79a1daba | ||
|
|
7867965989 | ||
|
|
348513a248 | ||
|
|
eb62253137 | ||
|
|
c4e87435dd | ||
|
|
9980d225f5 | ||
|
|
9c0f596c0d | ||
|
|
ff8cca6d8b | ||
|
|
0e5e263dea | ||
|
|
166732946e | ||
|
|
cccdab45dc | ||
|
|
190ca7cf2d | ||
|
|
009160b6bf | ||
|
|
851f67d86f | ||
|
|
b8be2277c4 | ||
|
|
14e266b938 | ||
|
|
94f070f096 | ||
|
|
0c28664ced | ||
|
|
754863e42c | ||
|
|
fb5e4a5f17 | ||
|
|
03e9fb93c7 | ||
|
|
3e77e24b1f | ||
|
|
d97809e4b0 | ||
|
|
551fd0aaf9 | ||
|
|
2fc36f8355 | ||
|
|
13cc2feb3f | ||
|
|
e2b62fc9f6 | ||
|
|
6b119cf4b8 | ||
|
|
88d198403e | ||
|
|
5ee39d7ecc | ||
|
|
e7102725bd | ||
|
|
bd28867804 | ||
|
|
0eb09a2136 | ||
|
|
470daf0036 | ||
|
|
fd28cbca4e | ||
|
|
6ecf0ecd88 | ||
|
|
c940d9d8a4 | ||
|
|
efef172658 | ||
|
|
e865b2c054 | ||
|
|
f8243f6934 | ||
|
|
746268c3ef | ||
|
|
48695651e1 | ||
|
|
45f726669e | ||
|
|
d473615ccf | ||
|
|
70425c77ea | ||
|
|
8520409459 | ||
|
|
1e48c6dcfe | ||
|
|
67a81400ff | ||
|
|
51431169dd | ||
|
|
2a0183c838 | ||
|
|
e222915a37 | ||
|
|
b698ccbc1d | ||
|
|
03a80beebe | ||
|
|
1c072e9f32 | ||
|
|
01576f941f | ||
|
|
4af80286d5 | ||
|
|
c9785bc57b | ||
|
|
81e72826ce | ||
|
|
71f9f3d230 | ||
|
|
500a01b46d | ||
|
|
8190c189e5 | ||
|
|
dea2ac22e1 | ||
|
|
1cae46a936 | ||
|
|
ac22838ded | ||
|
|
93875d667c | ||
|
|
41043e4dc1 | ||
|
|
2e8425d4b1 | ||
|
|
fcac28c69f | ||
|
|
a14b319ada | ||
|
|
6763ebdd66 | ||
|
|
421f2dd688 | ||
|
|
19b21a3e8a | ||
|
|
06dcaff806 | ||
|
|
f02bba5630 | ||
|
|
12fa295506 | ||
|
|
1054bbdf8b | ||
|
|
b47436ec59 | ||
|
|
856534cb81 | ||
|
|
3ee07c7da7 | ||
|
|
96fb892465 | ||
|
|
3a48a124e2 | ||
|
|
b391d47a40 | ||
|
|
a3c4d7c7b6 | ||
|
|
4ee5890d90 | ||
|
|
2ce1134b8b | ||
|
|
641ff441b5 | ||
|
|
7687259381 | ||
|
|
bd888451ec | ||
|
|
7484542af2 | ||
|
|
1e64a3f94c | ||
|
|
fd7319b295 | ||
|
|
923e00ad19 | ||
|
|
518be4e630 | ||
|
|
06a13557e3 | ||
|
|
72aa82c8ed | ||
|
|
b8a136e0ad | ||
|
|
38ba214327 | ||
|
|
c23ec06ce6 | ||
|
|
c495098582 | ||
|
|
499d095f81 | ||
|
|
2ed9367dc8 | ||
|
|
820e6fcfa3 | ||
|
|
f92ef05ee2 | ||
|
|
4b3795671d | ||
|
|
bff5ff79bd | ||
|
|
21056c5cb8 | ||
|
|
a4567939d6 | ||
|
|
e92ac2b30d | ||
|
|
90871d5859 | ||
|
|
cb299d145b | ||
|
|
6afc34e47e | ||
|
|
f6bcbbd962 | ||
|
|
55f1129a24 | ||
|
|
a2c48492ee | ||
|
|
cb0591fe5f | ||
|
|
2f269c3e17 | ||
|
|
d1a15f07d5 | ||
|
|
3cb0c95278 | ||
|
|
74910a6b56 | ||
|
|
5ab32326c1 | ||
|
|
7dc184f05f | ||
|
|
dbdb3931f4 | ||
|
|
56a5559024 | ||
|
|
8798afc41c | ||
|
|
e67c911c97 | ||
|
|
88d69f6fd4 | ||
|
|
99555f7dd0 | ||
|
|
b3ec77c631 | ||
|
|
d4c61e7ec9 | ||
|
|
48bbe288b7 | ||
|
|
3664266eed | ||
|
|
a1f7ab1562 | ||
|
|
5f95c745e2 | ||
|
|
1f735d8739 | ||
|
|
551fa28b27 | ||
|
|
909114e318 | ||
|
|
a4e55a0664 | ||
|
|
86e196808e | ||
|
|
0a4e15de31 | ||
|
|
cd1f52c6b5 | ||
|
|
b63449bffd | ||
|
|
93965138da | ||
|
|
fae1bd791f | ||
|
|
35855d2d71 | ||
|
|
c374534d9e | ||
|
|
f6c92dab24 | ||
|
|
03beae30ca | ||
|
|
4ee81501a9 | ||
|
|
3b92c3d75c | ||
|
|
814876f8df | ||
|
|
567d1ae104 | ||
|
|
7b7f028d74 | ||
|
|
0e53524f2e | ||
|
|
0f0e0951ec | ||
|
|
a172c4b725 | ||
|
|
9f979c02f8 | ||
|
|
1d59a67d6e | ||
|
|
5837a4602a | ||
|
|
e07a7ac496 | ||
|
|
de57d0b027 | ||
|
|
32bcb52f14 | ||
|
|
612ec06262 | ||
|
|
93dea90970 | ||
|
|
47ece1f285 | ||
|
|
64378419d0 | ||
|
|
00a201181d | ||
|
|
b3f9a14c85 | ||
|
|
065e3828b1 | ||
|
|
63e91f0ee5 | ||
|
|
182ef5c8f7 | ||
|
|
2f790140e0 | ||
|
|
a8bf247d01 | ||
|
|
e2b3e06077 | ||
|
|
43332d8752 | ||
|
|
b410ef5d08 | ||
|
|
1e97a9a872 | ||
|
|
26c49e7c22 | ||
|
|
b0f000acfd | ||
|
|
89269b848d | ||
|
|
7a55af405b | ||
|
|
a3343bc738 | ||
|
|
dd10486f27 | ||
|
|
a324ad2dd8 | ||
|
|
38b4653cb3 | ||
|
|
fdd9186ae9 | ||
|
|
81302e3ca5 | ||
|
|
cf99b4f8b9 | ||
|
|
12d39550a8 | ||
|
|
9c3e063635 | ||
|
|
bc0f3e343b | ||
|
|
d3eabd8d8f | ||
|
|
bfa8f97e7e | ||
|
|
4136328a36 | ||
|
|
2fd2c18dfc | ||
|
|
0a32f9405a | ||
|
|
7125e9ecd5 | ||
|
|
869e4a0b9d | ||
|
|
dc0e5c218f | ||
|
|
7fe3238339 | ||
|
|
f491f5bf49 | ||
|
|
17070b88d3 | ||
|
|
36fd23b264 | ||
|
|
fcd1d400f8 | ||
|
|
dbbec86a13 | ||
|
|
67bd23a027 | ||
|
|
f22a350938 | ||
|
|
2f0388d692 | ||
|
|
d7922c6330 | ||
|
|
b383142c14 | ||
|
|
c42b0caf0f | ||
|
|
6ba286087d | ||
|
|
c2e3fef7fc | ||
|
|
b9d344c7d9 | ||
|
|
320643d2da | ||
|
|
fe04c1af25 | ||
|
|
21d3ed61ef | ||
|
|
b7f27a2af0 | ||
|
|
2560b4d336 | ||
|
|
66282d4ed0 | ||
|
|
2152f54066 | ||
|
|
c89cb32871 | ||
|
|
59eaf9177e | ||
|
|
36aa58b38e | ||
|
|
2f4fccfb2c | ||
|
|
d251ebb995 | ||
|
|
0cc1573503 | ||
|
|
2ad3f2c1ab | ||
|
|
6cca26b914 | ||
|
|
0c946516e8 | ||
|
|
7b417929e6 | ||
|
|
c77ef4e4ce | ||
|
|
a9d90da5c8 | ||
|
|
4071c2ca43 | ||
|
|
58eaddf753 | ||
|
|
0a571502ce | ||
|
|
a850e5b74e | ||
|
|
71aa1fa113 | ||
|
|
24eef2dc0b | ||
|
|
5977c8c8e2 | ||
|
|
dcd4c19b21 | ||
|
|
464c7dae0a | ||
|
|
ad92e2e5eb | ||
|
|
254845d71f | ||
|
|
89ab97076c | ||
|
|
dd444ccc8a | ||
|
|
c9cab47ce0 | ||
|
|
b868477406 | ||
|
|
71ad3fc51d | ||
|
|
837ad7e39c | ||
|
|
898c589e2b | ||
|
|
49c90a4455 | ||
|
|
d315f37c88 | ||
|
|
799c1dc804 | ||
|
|
3d457e757d | ||
|
|
f74d575d6a | ||
|
|
d4e0bacde9 | ||
|
|
844c3ab26f | ||
|
|
97c884de42 | ||
|
|
6cc974a8e9 | ||
|
|
5f3822ac1a | ||
|
|
e9763f4ec6 | ||
|
|
e4d03f9581 | ||
|
|
8b0ac6e81e | ||
|
|
a73eba8916 | ||
|
|
c74d761a4b | ||
|
|
e60cc7b544 | ||
|
|
515e53b220 | ||
|
|
8948c3f435 | ||
|
|
3a1367a1d8 | ||
|
|
e114873e4c | ||
|
|
be8f98f6c7 | ||
|
|
5a685474ff | ||
|
|
185974a3ea | ||
|
|
6c87711595 | ||
|
|
35b8e389a3 | ||
|
|
51564d1adc | ||
|
|
88df06ee47 | ||
|
|
770c746444 | ||
|
|
47215ddb13 | ||
|
|
fb09066679 | ||
|
|
7591095f2a | ||
|
|
48575c09ca | ||
|
|
5fc01fe685 | ||
|
|
df94dd2cf5 | ||
|
|
ae53ce5544 | ||
|
|
1f0e6c0426 | ||
|
|
7f84c3c71c | ||
|
|
8a050810c1 | ||
|
|
4033f7a47d | ||
|
|
62cc64d912 | ||
|
|
290ca2178d | ||
|
|
b935f8a3f3 | ||
|
|
65e31115a1 | ||
|
|
19d536ea8b | ||
|
|
8aa2401134 | ||
|
|
4699d9404b | ||
|
|
b50132e255 | ||
|
|
8f1c4af9fa | ||
|
|
d5bd816676 | ||
|
|
8093528694 | ||
|
|
7b7bf5d390 | ||
|
|
8db140fe89 | ||
|
|
2e4148a9eb | ||
|
|
ed356ba85d | ||
|
|
cf79fef0bc | ||
|
|
d4b4451a48 | ||
|
|
2f57a4f86f | ||
|
|
31b665d344 | ||
|
|
6d56bc7c0c | ||
|
|
89cf28fade | ||
|
|
8087e8ad75 | ||
|
|
df19ced50c | ||
|
|
dba8865d75 | ||
|
|
d506cf2d2a | ||
|
|
5e30e1a2ee | ||
|
|
af6d96843d | ||
|
|
c0acc1d087 | ||
|
|
98e3343808 | ||
|
|
3c72f195ba | ||
|
|
f3f46858c9 | ||
|
|
861a847692 | ||
|
|
d4631229af | ||
|
|
a78f7d5789 | ||
|
|
fca5c77089 | ||
|
|
fe828a1c49 | ||
|
|
11e3573ebd | ||
|
|
ff1d625dc8 | ||
|
|
bd9691532f | ||
|
|
133b597093 | ||
|
|
d4d540bc33 | ||
|
|
d08322c1a3 | ||
|
|
ec1c664ed6 | ||
|
|
a3b46581b0 | ||
|
|
b115d4f02d | ||
|
|
6e64ede90b | ||
|
|
1917c2ce38 | ||
|
|
c06c2422a9 | ||
|
|
14d205f845 | ||
|
|
a57ce557e8 | ||
|
|
23c200f333 | ||
|
|
bf6a3adc06 | ||
|
|
60a8c8212c | ||
|
|
525c9c8fd8 | ||
|
|
08651685d4 | ||
|
|
3a60abaeee | ||
|
|
1e5d980b85 | ||
|
|
f9e97735e6 | ||
|
|
df36aceddb | ||
|
|
600e1cdbfb | ||
|
|
d0f11db700 | ||
|
|
27de8138f5 | ||
|
|
bc0cd2332c | ||
|
|
c1b5287ba0 | ||
|
|
a2a43b3996 | ||
|
|
6c07ecb700 | ||
|
|
2d3f848ab3 | ||
|
|
fdd188516b | ||
|
|
4c95b1d7ad | ||
|
|
19b7b51eea | ||
|
|
3c355e2d75 | ||
|
|
290c2f7b62 | ||
|
|
be7bcd03e1 | ||
|
|
0965f4213e | ||
|
|
bb9ca6e40f | ||
|
|
672ea6d6f2 | ||
|
|
ec189a4867 | ||
|
|
f67a50c064 | ||
|
|
a0fba34ff6 | ||
|
|
7ea45ac3d2 | ||
|
|
78a58617c1 | ||
|
|
f098aeb622 | ||
|
|
6904afc5e7 | ||
|
|
b079b30694 | ||
|
|
3e8a425684 | ||
|
|
b47c4c8f71 | ||
|
|
6d3097ae76 | ||
|
|
09e521c2c2 | ||
|
|
2439cf4633 | ||
|
|
f1b31f26d0 | ||
|
|
b1eca89a05 | ||
|
|
09bf2a0fe0 | ||
|
|
2d754641b5 | ||
|
|
871a0dfc6c | ||
|
|
3521d4a4f7 | ||
|
|
407f12760c | ||
|
|
479975fdff | ||
|
|
98fae8a6de | ||
|
|
2e94104ff8 | ||
|
|
039c13fc0a | ||
|
|
de266e8c88 | ||
|
|
ada99077a4 | ||
|
|
f5588f94db | ||
|
|
2a325ae4cc | ||
|
|
ac9345419e | ||
|
|
b3fb020dfa | ||
|
|
4b885a6e30 | ||
|
|
fa814d64de | ||
|
|
8aa376de25 | ||
|
|
14e7f2d986 | ||
|
|
f816f3d29b | ||
|
|
3db9987966 | ||
|
|
5e40bd5deb | ||
|
|
74a9ddbe47 | ||
|
|
188c2ae7ea | ||
|
|
e645c19761 | ||
|
|
baf9cab66c | ||
|
|
2fd940bcde | ||
|
|
f4988c5d86 | ||
|
|
b59b3a3a74 | ||
|
|
c677db70b4 | ||
|
|
9861f294b7 | ||
|
|
c7a42a1641 | ||
|
|
e2a721b209 | ||
|
|
66090e3926 | ||
|
|
60395fb16d | ||
|
|
54881facf8 | ||
|
|
9f2f131df9 | ||
|
|
70ab4122a3 | ||
|
|
7302fb951e | ||
|
|
dee8ccd8b3 | ||
|
|
40b2efb339 | ||
|
|
190e74b4d2 | ||
|
|
8bec08799e | ||
|
|
003b056301 | ||
|
|
97fd58f793 | ||
|
|
aa70d410f8 | ||
|
|
ab21a9238d | ||
|
|
be080b1463 | ||
|
|
f8ef3e6754 | ||
|
|
e4e8b939e2 | ||
|
|
020f122647 | ||
|
|
ab0626720a | ||
|
|
637c05466a | ||
|
|
08b98739f7 | ||
|
|
920a835344 | ||
|
|
88e27e315f | ||
|
|
7d3427474d | ||
|
|
d77183fd48 | ||
|
|
caa601c9aa | ||
|
|
b9a0152000 | ||
|
|
5c5e4e3464 | ||
|
|
044f987ecd | ||
|
|
295351d3e9 | ||
|
|
ab7ca411a4 | ||
|
|
c0dee497cd | ||
|
|
247e51478b | ||
|
|
a0540e0452 | ||
|
|
515d421dc0 | ||
|
|
6032f52798 | ||
|
|
05989fdc58 | ||
|
|
b62fafe520 | ||
|
|
4562a0130a | ||
|
|
6949481198 | ||
|
|
5654170278 | ||
|
|
735a324f5a | ||
|
|
2cb51f5b58 | ||
|
|
3baeef4f44 | ||
|
|
cada78ca4d | ||
|
|
a2929ef441 | ||
|
|
6c7c8b43b9 | ||
|
|
d0940708b0 | ||
|
|
af21f3c5d8 | ||
|
|
dd18040232 | ||
|
|
c72e3a1585 | ||
|
|
888a47e7a9 | ||
|
|
0df51d2d7c | ||
|
|
022945b0d1 | ||
|
|
0e62284671 | ||
|
|
704103f9f1 | ||
|
|
1f2ffe7e76 | ||
|
|
b763abdc76 | ||
|
|
af2fcbc059 | ||
|
|
0c4d278de2 | ||
|
|
9596d24cca | ||
|
|
a52b71ef43 | ||
|
|
216289455e | ||
|
|
019d8cf756 | ||
|
|
3a1c09f89c | ||
|
|
e9ed4a3f93 | ||
|
|
164d6b1b6c | ||
|
|
2307dfaaac | ||
|
|
3cecc1229f | ||
|
|
cca895e418 | ||
|
|
ddda934eed | ||
|
|
1d8c9ecfc4 | ||
|
|
1f20574520 | ||
|
|
da81bc3ba5 | ||
|
|
013fb58267 | ||
|
|
f4f2feae3c | ||
|
|
ffad6e47ec | ||
|
|
2b8f884c12 | ||
|
|
2df0d81d29 | ||
|
|
b02c4904a9 | ||
|
|
f73cdac9e2 | ||
|
|
de848511af | ||
|
|
9e294529cf | ||
|
|
96d6accbde | ||
|
|
86e276ea94 | ||
|
|
8566d62aee | ||
|
|
0ccc78e90a | ||
|
|
3c86207d63 | ||
|
|
c2a37c814d | ||
|
|
4727866b15 | ||
|
|
7a3c33fde3 | ||
|
|
087735a08f | ||
|
|
a3077d91ec | ||
|
|
649763f2b1 | ||
|
|
f9b988a232 | ||
|
|
ab45bcb121 | ||
|
|
e9190d01dc | ||
|
|
ba0e98fd4b | ||
|
|
40cc5b379b | ||
|
|
b1cb48aa9b | ||
|
|
82d9165d92 | ||
|
|
39d4ee9ad8 | ||
|
|
2cdd444e29 | ||
|
|
d492f0f41f | ||
|
|
943e085d37 | ||
|
|
0624f9103f | ||
|
|
cab1530afc | ||
|
|
90e63f1898 | ||
|
|
b2fdd92f9c | ||
|
|
91701b484a | ||
|
|
9fdae1ac45 | ||
|
|
b7766946d8 | ||
|
|
9486749345 | ||
|
|
a3bec4d5fc | ||
|
|
d8cc091f94 | ||
|
|
89ba4aeeaf | ||
|
|
a2f223a992 | ||
|
|
286efade59 | ||
|
|
4e7888259f | ||
|
|
8e3fa892e1 | ||
|
|
8de0c55b68 | ||
|
|
4c27d2600a | ||
|
|
59cfb14a19 | ||
|
|
6f7cf4f186 | ||
|
|
52454c50b7 | ||
|
|
c56219f5b4 | ||
|
|
99b6e1f93c | ||
|
|
6ff1549d2e | ||
|
|
1b05e8ddc9 | ||
|
|
4745877a63 | ||
|
|
c81c7f5a33 | ||
|
|
a23beddc66 | ||
|
|
045929681d | ||
|
|
cf0cf52298 | ||
|
|
1ff7c8bdd5 | ||
|
|
7254f766d5 | ||
|
|
83e07ec76c | ||
|
|
c3a1f25894 | ||
|
|
750d8b0a25 | ||
|
|
2e4558a5d3 | ||
|
|
3e5813323b | ||
|
|
ecd858251f | ||
|
|
ac0847ffe3 | ||
|
|
d1bf49790e | ||
|
|
815f24fbf5 | ||
|
|
d83352e16f | ||
|
|
8715ab9322 | ||
|
|
4df294f0a2 | ||
|
|
cbbe1b4f47 | ||
|
|
8ccd6a8ed3 | ||
|
|
cbd222fe30 | ||
|
|
1683567dda | ||
|
|
da2e40db4d | ||
|
|
e3c37f003b | ||
|
|
c9eebafb2d | ||
|
|
751da877ee | ||
|
|
14d19b5117 | ||
|
|
41d4c883a3 | ||
|
|
09504508d9 | ||
|
|
94c755a0e6 | ||
|
|
f21cff335b | ||
|
|
6947996c9a | ||
|
|
0756090081 | ||
|
|
95e03d2684 | ||
|
|
fab5833cdf | ||
|
|
a011c10d4d | ||
|
|
14584bef55 | ||
|
|
907a371bd4 | ||
|
|
a420c1749a | ||
|
|
c29e9bb68d | ||
|
|
fd965e6022 | ||
|
|
2e8d7a20d0 | ||
|
|
8084ed191e | ||
|
|
0b51e50c19 | ||
|
|
659e056eeb | ||
|
|
c70023c066 | ||
|
|
ba2bdf8ea4 | ||
|
|
57330bb2cd | ||
|
|
16fdef2d55 | ||
|
|
31928d5ebb | ||
|
|
8370b99302 | ||
|
|
13f98e1675 | ||
|
|
615804333e | ||
|
|
e1b9e113b1 | ||
|
|
dfbde3e41c | ||
|
|
034b4feff4 | ||
|
|
949439361e | ||
|
|
163b6b3725 | ||
|
|
bdc6addf81 | ||
|
|
fd7c1e24a3 | ||
|
|
a16ecbcebd | ||
|
|
0a1f6be4a5 | ||
|
|
98bf462e02 | ||
|
|
86fa59e5e1 | ||
|
|
1ddadeba5d | ||
|
|
2e1d5d4d13 | ||
|
|
31b29abf5f | ||
|
|
16c428dbd5 | ||
|
|
54bc6550ee | ||
|
|
a698cb6ef7 | ||
|
|
5f86ab27bb | ||
|
|
f0b2c08a9e | ||
|
|
3f8c705989 | ||
|
|
6f84574511 | ||
|
|
6a131977d3 | ||
|
|
4df0075b42 | ||
|
|
e3972c6205 | ||
|
|
4e949f542c | ||
|
|
af38fd3ba3 | ||
|
|
d00ac278c6 | ||
|
|
0e01303921 | ||
|
|
39eeaf06e5 | ||
|
|
7c2c4b26b3 | ||
|
|
2ea0703701 | ||
|
|
d425cdc27d | ||
|
|
799c833a66 | ||
|
|
96bc88d045 | ||
|
|
da7bbc2b20 | ||
|
|
7b4f6c3222 | ||
|
|
e3bc528647 | ||
|
|
29665203c0 | ||
|
|
7beb9afc36 | ||
|
|
2fcb3a0131 | ||
|
|
0a9749726f | ||
|
|
c1e6d9c633 | ||
|
|
4767d49e4d | ||
|
|
2bf7e8506c | ||
|
|
447966e3b8 | ||
|
|
adc17272dc | ||
|
|
6e54f34419 | ||
|
|
4b4051a823 | ||
|
|
5c4e230f69 | ||
|
|
2c191a9bd3 | ||
|
|
ed9313759b | ||
|
|
70a7a72baa | ||
|
|
4d69025250 | ||
|
|
9f9028b7ce | ||
|
|
e9be3242b2 | ||
|
|
6868b683d8 | ||
|
|
4934ec2ea1 | ||
|
|
211a06e9bc | ||
|
|
debca0a82d | ||
|
|
92da954229 | ||
|
|
91d96b162e | ||
|
|
0907bf195e | ||
|
|
f30b14a3da | ||
|
|
2030793a02 | ||
|
|
d09f561ef6 | ||
|
|
2d1bede57e | ||
|
|
a947ec10e4 | ||
|
|
fd04f94b76 | ||
|
|
6881446e67 | ||
|
|
bd0a9952ff | ||
|
|
c35255b82b | ||
|
|
5e71e7d04d | ||
|
|
cdf3c62d17 | ||
|
|
e7e2176e64 | ||
|
|
ecb9e92c48 | ||
|
|
053c2eb379 | ||
|
|
bde2810c08 | ||
|
|
692159219e | ||
|
|
71b194cbcb | ||
|
|
fb81c7e8e0 | ||
|
|
84878315d5 | ||
|
|
1bd675cce9 | ||
|
|
e77a0a9385 | ||
|
|
d63da05e87 | ||
|
|
dece58faf6 | ||
|
|
0bfc166899 | ||
|
|
3bc8006028 | ||
|
|
f6b7a35c81 | ||
|
|
5f17b28e05 | ||
|
|
138099f11d | ||
|
|
beb425bbd0 | ||
|
|
b5631aa1df | ||
|
|
2958881343 | ||
|
|
981e26e1b6 | ||
|
|
b5e11f2303 | ||
|
|
6a159aa631 | ||
|
|
4b14a4bc50 | ||
|
|
2ca4b15154 | ||
|
|
e02d602341 | ||
|
|
8d218db35f | ||
|
|
f96d78e9b8 | ||
|
|
28dd7e4ca7 | ||
|
|
14a0e6bd6e | ||
|
|
f2b2952909 | ||
|
|
adc82bea85 | ||
|
|
778adc2f39 | ||
|
|
a92895d09d | ||
|
|
dfb66cc41f | ||
|
|
065180e86c | ||
|
|
73cd7f7513 | ||
|
|
e2d5534959 | ||
|
|
a6cbdfc559 | ||
|
|
7c643ea698 | ||
|
|
39be3d0958 | ||
|
|
e693141f50 | ||
|
|
927dad4ff5 | ||
|
|
885caa52db | ||
|
|
88776b4e00 | ||
|
|
8b963f3fe4 | ||
|
|
4a4ac1d6c8 | ||
|
|
b5eef748b4 | ||
|
|
6ebe235dc3 | ||
|
|
bbde3159bb | ||
|
|
7e65aea6d9 | ||
|
|
e377675897 | ||
|
|
c3d40c9fb9 | ||
|
|
5efc61b155 | ||
|
|
323ab5498b | ||
|
|
b6e18e70bc | ||
|
|
1496d9d931 | ||
|
|
e0e35f5abf | ||
|
|
0b7e4d594e | ||
|
|
1bdf0b5500 | ||
|
|
acb6f1fc87 | ||
|
|
a57e8f3a5d | ||
|
|
4cb86b76e8 | ||
|
|
24f9511726 | ||
|
|
e4d160ee0b | ||
|
|
21d91193a2 | ||
|
|
4d7f925c5a | ||
|
|
ad5f3f2450 | ||
|
|
a84fc734ff | ||
|
|
3d5a5e4d86 | ||
|
|
2d14a74ec2 | ||
|
|
942a604b5b | ||
|
|
e5ea8074d2 | ||
|
|
2779c8e69f | ||
|
|
1d12014399 | ||
|
|
d4b26025ed | ||
|
|
597f177d5b |
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
# patreon: # Replace with a single Patreon username
|
||||
# open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: jadzia626
|
||||
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
# liberapay: # Replace with a single Liberapay username
|
||||
# issuehunt: # Replace with a single IssueHunt username
|
||||
# otechie: # Replace with a single Otechie username
|
||||
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
24
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Please check the following before you make a pull request:
|
||||
|
||||
* The branch you are making the pull request from (in your own fork) has a unique and descriptive
|
||||
name. Do not make a pull request directly from your copy of `main`.
|
||||
* If you are submitting translation files, only submit the files that you have added translations
|
||||
to, not the other .ts files that may have been updated in the process.
|
||||
* Make sure your contribution follows the style guide and other requirements mentioned in the
|
||||
CONTRIBUTING.md file in the repository.
|
||||
* Fill in the Summary section below, and if relevant, mention the issue numbers related to the PR.
|
||||
-->
|
||||
|
||||
**Summary:**
|
||||
|
||||
**Related Issue(s):**
|
||||
|
||||
**Reviewer's Checklist:**
|
||||
|
||||
* [ ] The header of all files contain a reference to the repository license
|
||||
* [ ] The overall test coverage is increased or remains the same as before
|
||||
* [ ] All tests are passing
|
||||
* [ ] All flake8 checks are passing and the style guide is followed
|
||||
* [ ] Documentation (as docstrings) is complete and understandable
|
||||
* [ ] Only files that have been actively changed are committed
|
||||
12
.github/workflows/syntax.yml
vendored
@@ -2,16 +2,18 @@ name: flake8
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
checkSyntax:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3
|
||||
architecture: x64
|
||||
@@ -25,5 +27,5 @@ jobs:
|
||||
flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
- name: Coding Style Violations
|
||||
run: |
|
||||
flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --show-source --statistics
|
||||
flake8 tests --count --max-line-length=99 --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --show-source --statistics
|
||||
flake8 nw --count --max-line-length=99 --ignore E221,E226,E228,E241 --show-source --statistics
|
||||
flake8 tests --count --max-line-length=99 --ignore E221,E226,E228,E241 --show-source --statistics
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
name: Linux (3.8)
|
||||
name: Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
testLinux38:
|
||||
testLinux:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
- name: Install Packages
|
||||
- name: Install Packages (apt)
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libenchant-dev qt5-default
|
||||
sudo apt install libenchant-dev qttools5-dev-tools
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
- name: Install Dependencies (pip)
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest-timeout
|
||||
pip install pytest-cov
|
||||
pip install pytest-qt
|
||||
pip install codecov
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Run Build Commands
|
||||
run: python setup.py qtlrelease sample
|
||||
- name: Run Tests
|
||||
run: |
|
||||
export QT_QPA_PLATFORM=offscreen
|
||||
33
.github/workflows/test_linux_3.6.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Linux (3.6)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
jobs:
|
||||
testLinux36:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.6
|
||||
architecture: x64
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libenchant-dev qt5-default
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest-timeout
|
||||
pip install pytest-qt
|
||||
- name: Run Tests
|
||||
run: |
|
||||
export QT_QPA_PLATFORM=offscreen
|
||||
pytest -v --timeout=60
|
||||
33
.github/workflows/test_linux_3.7.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Linux (3.7)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
jobs:
|
||||
testLinux37:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
architecture: x64
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libenchant-dev qt5-default
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest-timeout
|
||||
pip install pytest-qt
|
||||
- name: Run Tests
|
||||
run: |
|
||||
export QT_QPA_PLATFORM=offscreen
|
||||
pytest -v --timeout=60
|
||||
37
.github/workflows/test_linux_3.9.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: Linux (3.9)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
jobs:
|
||||
testLinux39:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
architecture: x64
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libenchant-dev qt5-default
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest-timeout
|
||||
pip install pytest-cov
|
||||
pip install pytest-qt
|
||||
pip install codecov
|
||||
- name: Run Tests
|
||||
run: |
|
||||
export QT_QPA_PLATFORM=offscreen
|
||||
pytest -v --cov=nw --timeout=60
|
||||
- name: Upload to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
18
.github/workflows/test_mac.yml
vendored
@@ -1,19 +1,21 @@
|
||||
name: macOS (3.8)
|
||||
name: macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
testMac38:
|
||||
testMac:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
architecture: x64
|
||||
- name: Install Packages
|
||||
run: |
|
||||
@@ -24,10 +26,8 @@ jobs:
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest-timeout
|
||||
pip install pytest-cov
|
||||
pip install pytest-qt
|
||||
pip install codecov
|
||||
pip install -r requirements-dev.txt
|
||||
pip install pyobjc
|
||||
- name: Run Tests
|
||||
run: |
|
||||
export QT_QPA_PLATFORM=offscreen
|
||||
|
||||
17
.github/workflows/test_win.yml
vendored
@@ -1,19 +1,21 @@
|
||||
name: Windows (3.8)
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
testWin38:
|
||||
testWin:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
architecture: x64
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
@@ -21,10 +23,7 @@ jobs:
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest-timeout
|
||||
pip install pytest-cov
|
||||
pip install pytest-qt
|
||||
pip install codecov
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Run Tests
|
||||
run: |
|
||||
pytest -v --cov=nw --timeout=60
|
||||
|
||||
10
.gitignore
vendored
@@ -7,6 +7,13 @@
|
||||
*.egg-info
|
||||
setup.iss
|
||||
novelwriter.desktop
|
||||
novelWriter.pyw
|
||||
|
||||
# Translations
|
||||
/nw/assets/i18n/*.qm
|
||||
.qmake.stash
|
||||
i18n/*.qm
|
||||
i18n/*.qph
|
||||
|
||||
# Documentation
|
||||
/docs/build/
|
||||
@@ -21,8 +28,10 @@ __pycache__
|
||||
/nw/assets/sample.zip
|
||||
/sample/cache
|
||||
/sample/meta
|
||||
/sample/versions
|
||||
*.bak
|
||||
*.lock
|
||||
ToC.txt
|
||||
|
||||
# PyTest
|
||||
/prof/
|
||||
@@ -38,3 +47,4 @@ __pycache__
|
||||
|
||||
# Coverage
|
||||
/.coverage
|
||||
/coverage.*
|
||||
|
||||
1032
CHANGELOG.md
165
CONTRIBUTING.md
@@ -1,106 +1,123 @@
|
||||
# Contributing
|
||||
# Contributing Guide
|
||||
|
||||
When contributing to this repository, please first discuss the change you wish to make via the
|
||||
issue tracker with the owner of this repository before making a change. If you just want to make a
|
||||
minor correction, like fix a typo or similar, feel free to just make a pull request directly.
|
||||
issue tracker or the discussions page with the owner of this repository. Especially if it is
|
||||
regarding new features. If you just want to make a minor correction, like fix a typo or similar,
|
||||
feel free to just make a pull request directly.
|
||||
|
||||
There is a code of conduct. Please follow it in all your interactions with the project.
|
||||
The following contributions are welcome:
|
||||
|
||||
## Pull Request Process
|
||||
* Bugfixes for new or existing bugs. Please also report new bugs in the issue tracker even if you
|
||||
also provide a fix. It makes it easier to keep track of what has been fixed and when.
|
||||
* New translations. You can read more about contributing translations
|
||||
[here](https://github.com/vkbo/novelWriter/blob/main/i18n/README.md).
|
||||
* Improvements to the documentation. Particularly if the documentation is unclear. Please don't
|
||||
make any larger changes to the documentation without discussing if with the maintainer first.
|
||||
* Adaptations, installation or packaging features targeting specific operating systems.
|
||||
|
||||
1. Make sure your code passes all tests and conforms to the style guide. You can check that the code
|
||||
conforms by running `flake8` from the root of the project folder.
|
||||
2. Please provide a complete description of the changes in the pull request, and a summary that can
|
||||
be copied into the [CHANGELOG](CHANGELOG.md). Remember to reference any issue related by
|
||||
providing the issue number.
|
||||
3. Do not change the version number unless asked to do so. Version numbers are bumped in separate
|
||||
release pull requests by the maintainer.
|
||||
Please do not:
|
||||
|
||||
* Make a pull request that restructures or reformats existing code. If you think some parts of the
|
||||
code could be improved, please make an issue thread or start a discussion. The same applies to
|
||||
any text document in the repository.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
This project follows the [OneFlow](https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow)
|
||||
model. The `main` branch is the default branch. For general changes, please make a new branch in
|
||||
your own fork from the current `main` branch. Do not make pull requests from your own `main`
|
||||
branch.
|
||||
|
||||
**Note:** If your contribution is a bugfix for a specific version, please make a bugfix branch from
|
||||
the tag you want the fix to be applied to, not directly from the `main` branch. This is important.
|
||||
|
||||
Also check the following:
|
||||
|
||||
* Make sure your code passes all tests and conforms to the style guide. You can check that the
|
||||
code generally conforms by running the Python linting tool `flake8` from the root of the project
|
||||
folder, although it doesn't check everything. The same check is also run on pull requests by the
|
||||
maintainer.
|
||||
* Please provide a description of the changes in the pull request under the summary section of the
|
||||
pull request template, and reference any related issues by providing the issue number.
|
||||
* Do not change the version number.
|
||||
* Do not submit files that were not actively changed but have otherwise been modifed. This is
|
||||
mostly an issue with translation files. The language tool may update all files in the `i18n`
|
||||
folder.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
There is a code of conduct. Please follow it in all your interactions with the project.
|
||||
|
||||
### Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers
|
||||
pledge to making participation in our project and our community a harassment-free experience for
|
||||
everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level
|
||||
of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
everyone, regardless of age, body size, disability, ethnicity, gender identity and expression,
|
||||
level of experience, nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
Please see the [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) file for the full text.
|
||||
|
||||
## Code Style Guide
|
||||
|
||||
The source code of novelWriter broadly follows the [PEP8](https://www.python.org/dev/peps/pep-0008/)
|
||||
style guide , but with a few modifications and exceptions.
|
||||
The source code of novelWriter broadly follows the [PEP8](https://www.python.org/dev/peps/pep-0008)
|
||||
style guide, but with a few exceptions. Some key points are listed below.
|
||||
|
||||
### Linting with flake8
|
||||
**Line Length:**
|
||||
|
||||
An excellent tool for checking Python code for errors and coding style is `flake8`.
|
||||
The documentation is available [here](https://flake8.pycqa.org/en/latest/).
|
||||
* Source code lines can extend to the upper limit of 99 characters allowed by PEP8. 79 characters
|
||||
is the recommended line length in PEP8, but this is often too restrictive. 99 characters are
|
||||
acceptable when that is more practical. Readability has priority. Generally, if a code statement
|
||||
requires multiple lines, the lines should wrap at 79 characters if possible. If wrapping can be
|
||||
avoided by going to 99, then that is generally preferrable.
|
||||
* For text files, the text should be wrapped at 99 character. The exception is markdown image tags
|
||||
and urls which can run past that limit.
|
||||
|
||||
The `setup.cfg` file in the root of this project has the following settings:
|
||||
**Variable and Function Names**
|
||||
|
||||
* PEP8 allows for camelCase for consistency with existing code. The Qt library uses camelCase, so
|
||||
the Python the source code does too.
|
||||
* The exception to the above is for constants. They should always be in upper snake case, like PEP8
|
||||
states.
|
||||
|
||||
**Spaces, Indentation and Alignment**
|
||||
|
||||
* Only indentation by 4 spaces is allowed.
|
||||
* No trailing spaces should occur on any line in the source code, including empty lines.
|
||||
* Ideally, a function should end on the same indention level as it started. Exceptions are allowed
|
||||
if it makes the code easier to follow.
|
||||
* Aligning operators and attributes in columns with multiple spaces is not allowed by PEP8. The
|
||||
rule is relaxed a bit here. Alignment is allowed when populating large dictionaries or setting
|
||||
many class attributes. It does improve readability in such cases, but should not be overused.
|
||||
|
||||
### Linting with `flake8`
|
||||
|
||||
A good tool for checking Python code for errors and code style is `flake8`. The documentation is
|
||||
available [here](https://flake8.pycqa.org/en/latest/).
|
||||
|
||||
The `setup.cfg` file in the root of this project has the following settings for `flake8` that
|
||||
matches the above code style:
|
||||
```conf
|
||||
[flake8]
|
||||
ignore = E203,E221,E226,E228,E241,E251,E261,E266,E302,E305
|
||||
ignore = E221,E226,E228,E241
|
||||
max-line-length = 99
|
||||
exclude = docs/*
|
||||
```
|
||||
|
||||
The command line equivalent, with reporting, is:
|
||||
```bash
|
||||
flake8 . --count --ignore E203,E221,E226,E228,E241,E251,E261,E266,E302,E305 --max-line-length=99 --show-source --statistics
|
||||
flake8 . --count --ignore E221,E226,E228,E241 --max-line-length=99 --show-source --statistics
|
||||
```
|
||||
|
||||
Passing this check is required before contributions are merged into the `main` branch. This is
|
||||
checked automatically when you make a pull request. You can run the `flake8` command locally to
|
||||
check beforehand. The full command will give you a detailed description of the code lines that do
|
||||
not conform to the standard.
|
||||
Passing this check is required before contributions are merged. This is checked automatically when
|
||||
you make a pull request. You can run the `flake8` command locally to check beforehand. The full
|
||||
command will give you a detailed description of the code lines that do not conform to the standard.
|
||||
|
||||
## Ignored Errors
|
||||
Two of the ignored errors are due to the relaxed restriction on column alignment, these are the
|
||||
E221 and E241 error codes.
|
||||
|
||||
Some errors are ignored in novelWriter, for various reasons. In addition, novelWriter uses camelCase
|
||||
function and variable names due to this being the standard for the Qt libraries, and also because of
|
||||
the author's personal preferences.
|
||||
|
||||
The reason behind the other ignored error codes are listed below. Many of them are due to PEP8 not
|
||||
permitting column alignment as opposed to many other coding styles. I find them useful in regions of
|
||||
bulk value assignments. There's a reason why tables are more readable than lists. They should be
|
||||
used sparingly though.
|
||||
|
||||
The ignored errors are all `pycodestyle` errors, and they are documented
|
||||
[here](https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes).
|
||||
|
||||
**E203:** whitespace before ‘:’
|
||||
**Reason:** Column alignment. It is natural to align dictionary columns along the `:` character.
|
||||
|
||||
**E221:** multiple spaces before operator
|
||||
**Reason:** Column alignment.
|
||||
|
||||
**E226:** missing whitespace around arithmetic operator
|
||||
**Reason:** This doesn't actually follow the PEP8 recommendation of grouping longer equations by
|
||||
operator precedence like `2*a + 3*b` instead of `a * a + 3 * b`. Generally, don't use spaces around
|
||||
`*`, `/` and `**`, but do use spaces around `+` and `-`. For appending strings, the spaces can be
|
||||
dropped. Don't use the `+` operator for appending multiple strings. Use formatting instead.
|
||||
|
||||
**E228** missing whitespace around modulo operator
|
||||
**Reason:** See reason for E226. Formatting `%` like `/` and `*` should be possible.
|
||||
|
||||
**E241:** multiple spaces after ‘,’
|
||||
**Reason:** Column alignment.
|
||||
|
||||
**E251:** unexpected spaces around keyword / parameter equals
|
||||
**Reason:** Column alignment.
|
||||
|
||||
**E261:** at least two spaces before inline comment
|
||||
**Reason:** With syntax highlighting, this one doesn't make much sense.
|
||||
|
||||
**E266:** too many leading ‘#’ for block comment
|
||||
**Reason:** In the source multiple `#`s is sometimes used to indicate importance or heading level,
|
||||
like markdown headers.
|
||||
|
||||
**E302:** expected 2 blank lines, found 0
|
||||
**Reason:** Applies to classes. Instead, end classes with a comment like `# END Class ClassName` to
|
||||
make it easier to see which class just ended. The double line break is then redundant.
|
||||
|
||||
**E305:** expected 2 blank lines after end of function or class
|
||||
**Reason:** Instead, _always_ end a function with a `return`, preferably indented at function
|
||||
level. The end of the function is then clear.
|
||||
The code E226 is ignored becuse it doesn't actually follow the
|
||||
[PEP8 recommendation](https://www.python.org/dev/peps/pep-0008/#other-recommendations)
|
||||
of grouping longer equations by operator precedence like `2*a + 3*b` instead of `a * a + 3 * b`.
|
||||
Generally, don't use spaces around `*`, `/` and `**`, but _do_ use spaces around `+` and `-`.
|
||||
The same applies to the modulo operator `%`, hence E228 is also ignored.
|
||||
|
||||
42
CREDITS.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Credits
|
||||
|
||||
## Main Developer
|
||||
|
||||
* Veronica Berglyd Olsen (@vkbo)
|
||||
|
||||
## Main Contributors
|
||||
|
||||
* Concept: Marian Lückhof (@Number042)
|
||||
* Internationalisation: Bruno Meneguello (@bkmeneguello)
|
||||
|
||||
## Translations
|
||||
|
||||
* French: Jan Lüdke (@jyhelle)
|
||||
* Norwegian: Veronica Berglyd Olsen (@vkbo)
|
||||
* Portuguese: Bruno Meneguello (@bkmeneguello)
|
||||
* Simplified Chinese: Qianzhi Long (@longqzh)
|
||||
|
||||
## Libraries
|
||||
|
||||
The following libraries are dependencies of novelWriter:
|
||||
|
||||
* Qt5 by [Qt Company](https://www.qt.io/)
|
||||
* PyQt5 by [Riverbank Computing](https://www.riverbankcomputing.com/software/pyqt/)
|
||||
* lxml by [Martijn Faassen](https://lxml.de/)
|
||||
* Enchant by [Dom Lachowicz](https://abiword.github.io/enchant/)
|
||||
* PyEnchant by [Dimitri Merejkowsky](https://pyenchant.github.io/pyenchant/)
|
||||
|
||||
## Assets
|
||||
|
||||
Some of the assets bundled with novelWriter were adapted from the following sources:
|
||||
|
||||
* Typicons icons by Stephen Hutchings (CC BY-SA 4.0)
|
||||
* Cantarell font by Dave Crossland (OPEN FONT LICENSE Version 1.1)
|
||||
* Tomorrow syntax themes by Chris Kempson (MIT License)
|
||||
* Owl syntax themes by Sarah Drasner (MIT License)
|
||||
* Solarized themes by Ethan Schoonover, added by @nullbasis (MIT License)
|
||||
|
||||
## Special Mentions
|
||||
|
||||
Additional thanks to @johnblommers who was an early user and who has provided a lot of very useful
|
||||
feedback over the years.
|
||||
@@ -1,4 +1,6 @@
|
||||
include setup.py
|
||||
include LICENSE.md
|
||||
include CHANGELOG.md
|
||||
recursive-include setup *
|
||||
recursive-include nw/assets *
|
||||
recursive-include sample *.nwx *.nwd
|
||||
|
||||
389
README.md
@@ -1,293 +1,220 @@
|
||||
# novelWriter
|
||||
|
||||
[/badge.svg?branch=main)](https://github.com/vkbo/novelWriter/actions)
|
||||
[/badge.svg?branch=main)](https://github.com/vkbo/novelWriter/actions)
|
||||
[/badge.svg?branch=main)](https://github.com/vkbo/novelWriter/actions)
|
||||
[/badge.svg?branch=main)](https://github.com/vkbo/novelWriter/actions)
|
||||
[/badge.svg?branch=main)](https://github.com/vkbo/novelWriter/actions)
|
||||
[/badge.svg?branch=main)](https://github.com/vkbo/novelWriter/actions)
|
||||
[](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://novelwriter.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://novelwriter.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://github.com/vkbo/novelWriter/releases)
|
||||
[](https://pypi.org/project/novelWriter)
|
||||
[](https://pypi.org/project/novelWriter)
|
||||
|
||||
<img align="left" style="margin: 0 16px 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/setup/icons/scaled/icon-novelwriter-96.png">
|
||||
|
||||
novelWriter is a Markdown-like text editor designed for writing novels and larger projects of many
|
||||
smaller plain text documents. It uses its own flavour of Markdown that supports a meta data syntax
|
||||
for comments, synopsis, and cross-referencing between files. It's designed to be a simple text
|
||||
editor that allows for easy organisation of text files and notes, built on plain text files for
|
||||
robustness.
|
||||
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
|
||||
for comments, synopsis, and cross-referencing. It's designed to be a simple text editor that allows
|
||||
for easy organisation of text and notes, using human readable text files as storage for robustness.
|
||||
|
||||
The plain text storage is suitable for version control software, and also well suited for file
|
||||
synchronisation tools. The core project structure is stored in a project XML file. Other meta data
|
||||
is primarily saved in JSON files.
|
||||
The project storage is suitable for version control software, and also well suited for file
|
||||
synchronisation tools. All text is saved as plain text files with a meta data header. The core
|
||||
project structure is stored in a single project XML file. Other meta data is primarily saved as
|
||||
JSON files.
|
||||
|
||||
The full documentation is available at
|
||||
[novelwriter.readthedocs.io](https://novelwriter.readthedocs.io/).
|
||||
|
||||
The contributing guide is available in [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
|
||||
### Development Status
|
||||
|
||||
The application is still under initial development, but all core features have now been added.
|
||||
The core functionality has been in place for a while, and novelWriter is being used for writing
|
||||
projects by the author and collaborators.
|
||||
|
||||
No new major features will be added at this time, until the application is stable. Until then,
|
||||
novelWriter is in a _beta_ state. Please report any issues you encounter via the repository's issue
|
||||
tracker.
|
||||
|
||||
You should be able to use novelWriter for real projects, but as with all software, please make
|
||||
regular backups of your data. There is a built in backup feature that can pack the entire project
|
||||
into a zip file each time the main window or the project is closed. Please check the documentation
|
||||
for further details.
|
||||
The full documentation is available on [readthedocs.io](https://novelwriter.readthedocs.io).
|
||||
|
||||
The full credits are listed in [CREDITS.md](https://github.com/vkbo/novelWriter/blob/main/CREDITS.md).
|
||||
|
||||
## Implementation
|
||||
|
||||
The application is written in Python 3 using Qt5 via PyQt5. It is developed on Linux, but it should
|
||||
in principle work fine on other operating systems as well as long as dependencies are met. The unit
|
||||
tests are run on the latest versions of Ubuntu Linux, Windows Server and macOS.
|
||||
The application is written in Python 3 (3.6+) using Qt5 and PyQt5 (5.3+). 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.
|
||||
|
||||
## Project Contributions
|
||||
|
||||
# Installing and Running
|
||||
Please don't make feature pull requests without first having discussed them with the maintainer.
|
||||
You can make a feature request in the issue tracker, or if the idea isn't fully formed, start a
|
||||
discussion on the discussion page. Please also don't make pull requests to reformat or rewrite
|
||||
existing code unless there is a very good reason for doing so.
|
||||
|
||||
novelWriter is available on [pypi.org](https://pypi.org/project/novelWriter/), and can be installed
|
||||
with:
|
||||
```bash
|
||||
pip install novelwriter
|
||||
```
|
||||
Fixes and patches are welcome. Contributions related to packaging and installing novelWriter will
|
||||
also be appreciated, but please make an issue or a discussion topic first. Before contributing any
|
||||
code, please read the full [Contributing Guide](https://github.com/vkbo/novelWriter/blob/main/CONTRIBUTING.md).
|
||||
|
||||
To upgrade an existing installation, use:
|
||||
```bash
|
||||
pip install --upgrade novelwriter
|
||||
```
|
||||
New translations are always welcome. Please read the additional
|
||||
[instructions](https://github.com/vkbo/novelWriter/blob/main/i18n/README.md) for further details.
|
||||
|
||||
Dependencies are installed automatically, but can generally be installed with:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
## Key Features
|
||||
|
||||
Below are some brief instructions on how to get started on different operating systems.
|
||||
Some key features of novelWriter are listed below. Consult the
|
||||
[documentation](https://novelwriter.readthedocs.io) for more information.
|
||||
|
||||
### Formatting Codes
|
||||
|
||||
## Linux
|
||||
Although novelWriter is a plain text editor, it uses a Markdown-like syntax to allow for a minimal
|
||||
set of formatting that is useful for the specific task of writing novels.
|
||||
|
||||
Either download the source, or install with pip.
|
||||
| Code | Usage | Description |
|
||||
|------------|----------|-------------|
|
||||
| `#` | Prefix | Headings level 1 to 4. |
|
||||
| `_` | Wrapped | Emphasised (italicised) text. |
|
||||
| `**` | Wrapped | Strongly emphasised (bold) text. |
|
||||
| `~~` | Wrapped | Strikethrough text. |
|
||||
| `%` | Prefix | A comment; does not count towards the word count.<sup>1</sup> |
|
||||
| `@` | Prefix | The following text is parsed as a keyword/value command for meta data. |
|
||||
| `>` | Prefix | The paragraph is indented one tab width from the left.<sup>2</sup> |
|
||||
| `<` | Suffix | The paragraph is indented one tab width from the right.<sup>2</sup> |
|
||||
| `>>` | Prefix | The paragraph is right-aligned.<sup>2</sup> |
|
||||
| `<<` | Suffix | The paragraph is left-aligned.<sup>2</sup> |
|
||||
| `>>`, `<<` | Wrapped | The paragraph is centred.<sup>2</sup> |
|
||||
|
||||
If you run from source, install the dependencies via pip, or directly from the OS repo. There are
|
||||
very few dependencies, and they should be available in the standard repo. The Python packages
|
||||
needed are `pyqt5`, `lxml` and `pyenchant`.
|
||||
<sup>1</sup> If the first word of the comment is `synopsis:`, the comment is indexed and treated as the
|
||||
synopsis for the section of text where it occurs. These synopsis comments can be used to build an
|
||||
outline and exported to external documents.
|
||||
|
||||
### Installing from Source
|
||||
<sup>2</sup> The indent and alignment codes are available from version 1.4.
|
||||
|
||||
You can also install novelWriter from source with:
|
||||
```bash
|
||||
python3 setup.py sample
|
||||
sudo python3 setup.py install
|
||||
sudo python3 setup.py launcher
|
||||
```
|
||||
In additions:
|
||||
|
||||
The last line will install the application icons and set up a launcher for novelWriter. The method
|
||||
uses hardcoded paths, so it may or may not work for your Linux distro. If you have any issues,
|
||||
please submit a ticket so the script can be tuned.
|
||||
* A variety of thin and non-breaking spaces are supported. Some of them depend on the system
|
||||
running at least Qt 5.9. Earlier versions of Qt will unfortunately strip them out when saving.
|
||||
* Tabs can be used in the text, and should be properly aligned in both editor and viewer. This can
|
||||
be used to make simple tables and lists. Note that for HTML exports, most browsers will treat a
|
||||
tab as a space, so it may not show up like expected. Open Document exports should produce the
|
||||
expected result.
|
||||
|
||||
The script may prompt you to choose which executable to configure if it finds more than one.
|
||||
### Export Formats
|
||||
|
||||
### Running from Source
|
||||
|
||||
If you want to run directly from the source, the application can be started with:
|
||||
```bash
|
||||
./novelWriter.py
|
||||
```
|
||||
|
||||
You can also create a launcher for running directly from source with:
|
||||
```bash
|
||||
python3 setup.py xdg-install
|
||||
```
|
||||
This will install the launcher and icons for the current user. To install them system-wide, run the
|
||||
above command with `sudo` or as root.
|
||||
|
||||
For more install options, see [Build and Install novelWriter](setup/README.md).
|
||||
|
||||
|
||||
## macOS
|
||||
|
||||
These instructions assume you're using brew, and have Python and pip set up. If not, see the
|
||||
[brew docs](https://docs.brew.sh/Homebrew-and-Python) for help.
|
||||
|
||||
Main requirements are installed via the requirements file. You also need to install the `pyobjc`
|
||||
package on macOS, so you must run:
|
||||
```bash
|
||||
pip3 install --user -r requirements.txt
|
||||
pip3 install --user pyobjc
|
||||
```
|
||||
|
||||
For spell checking you may also need to install the enchant package. It comes with a lot of default
|
||||
dictionaries.
|
||||
```bash
|
||||
brew install enchant
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
On Windows, you may first need to install Python. See the [python.org](https://www.python.org/)
|
||||
website for download packages. It is recommended that you install the latest version of Python 3.8.
|
||||
|
||||
To install dependencies, run:
|
||||
```bash
|
||||
pip install --user -r requirements.txt
|
||||
```
|
||||
|
||||
**Note:** On Windows, make sure Python3 is in your PATH if you want to launch novelWriter from
|
||||
command line. You can also right click the `novelWriter.py` file, create a shortcut, then right
|
||||
click again, select "Properties" and change the target to your python executable and
|
||||
`novelWriter.py`.
|
||||
|
||||
It should look something like this:
|
||||
```
|
||||
C:\...\AppData\Local\Programs\Python\Python38\python.exe novelWriter.py
|
||||
```
|
||||
|
||||
You can also run the `make.py` script to generate a single executable, or an installer.
|
||||
See [Build and Install novelWriter](setup/README.md) for more details.
|
||||
|
||||
|
||||
## Package Versions
|
||||
|
||||
Exporting to Markdown requires PyQt/Qt 5.14. There are no known minimum for `lxml`, but the code
|
||||
was originally written with 4.2. The optional spell check library must be at least 3.0.0 to work
|
||||
with Windows 64 bit systems. On Linux, 2.0.0 also works fine.
|
||||
|
||||
If no external spell checking tool is installed, novelWriter will use a basic spell checker based
|
||||
on standard Python package `difflib`. Currently, only English dictionaries are available for this
|
||||
spell checker, but more can be added to the `nw/assets/dict` folder. See the
|
||||
[README](nw/assets/dict/README.md) file in that folder for how to generate more dictionaries. Note
|
||||
that the difflib-based option is both slow and limited.
|
||||
|
||||
## Debugging
|
||||
|
||||
If you need to debug novelWriter, you must run it from command line. It takes a few parameters,
|
||||
which can be listed with the switch `--help`. The `--info`, `--debug` or `--verbose` flags are
|
||||
particularly useful for increasing logging output for debugging.
|
||||
|
||||
|
||||
# Key Features
|
||||
|
||||
Some features of novelWriter are listed below. Consult the documentation for more information.
|
||||
|
||||
### Markdown Flavour
|
||||
|
||||
novelWriter is _not_ a full-feature Markdown editor. It allows for a minimal set of formatting
|
||||
needed for writing text documents for novels. These are currently limited to:
|
||||
|
||||
* Headings level 1 to 4 using the `#` syntax only.
|
||||
* Emphasised and strong text. These are rendered as italicised and bold.
|
||||
* Strikethrough text.
|
||||
* Hard line breaks using two or more spaces at the end of a line.
|
||||
|
||||
That is it. Features not supported in the editor are also not exported when using the export tool.
|
||||
|
||||
In addition, novelWriter adds the following, which is otherwise not supported by Markdown:
|
||||
|
||||
* A line starting with `%` is treated as a comment and not rendered on exports unless requested.
|
||||
Comments do not count towards the word count. If the first word of the comment is `synopsis:`,
|
||||
the comment is indexed and treated as the synopsis for the section of text under the same header.
|
||||
These synopsis comments can be used to build an outline and exported to external documents.
|
||||
* A set of meta data keyword/values starting with the character `@`. This is used for tagging
|
||||
and inter-linking documents, and can be used to generate a project outline.
|
||||
* Non-breaking spaces are supported as long as your system is using at least Qt 5.9. For earlier
|
||||
version, non-breaking spaces are converted to normal spaces when saving the document. This is
|
||||
done by the Qt library.
|
||||
* Thin spaces are also supported, as well as non-breaking thin spaces, with the same library
|
||||
version restriction as above.
|
||||
* Tabs can be used in the text, and should be properly aligned. The width of a tab in pixels can be
|
||||
changed in Preferences. Note that for the HTML format, most browsers will treat a tab as a space,
|
||||
so it may not show up like expected. If you import the HTML file to Libre Office, for instance,
|
||||
they should appear as expected.
|
||||
|
||||
The core export format of novelWriter is HTML5. You can also export the entire project as a single
|
||||
novelWriter Markdown-flavour document. In addition, other exports to Open Document, PDF, and plain
|
||||
text is offered through the Qt library, although with limitations to formatting.
|
||||
|
||||
The HTML format is well suited for file conversion tools and import into other text editors.
|
||||
The core export formats of novelWriter are Open Document and HTML5. Open Document is an open
|
||||
standard for office type documents that is supported by most office applications. See
|
||||
[Open Document > Application Support](https://en.wikipedia.org/wiki/OpenDocument#Application_support)
|
||||
for more details.
|
||||
|
||||
You can also export the entire project as a single novelWriter-flavour document. These can later be
|
||||
imported again into novelWriter. In addition, printing and export to PDF is offered through the Qt
|
||||
library, although with limitations to formatting.
|
||||
|
||||
### Colour Themes
|
||||
|
||||
The editor has syntax highlighting for the features it supports, and includes a set of different
|
||||
syntax highlighting themes. The GUI also has an optional dark theme in addition to the default
|
||||
system theme.
|
||||
|
||||
New themes can easily be added to the `nw/assets/themes` folder. Have a look in the existing
|
||||
folders for examples of how to define the colours.
|
||||
|
||||
syntax highlighting themes. Optional GUI themes are also available, including dark themes.
|
||||
|
||||
### Easy Organising of Project Files
|
||||
|
||||
The structure of the project is shown on the left hand side of the main GUI. Project files are
|
||||
The structure of the project is shown on the left hand side of the main window. Project files are
|
||||
organised into root folders, indicating what class of file they are. The most important root folder
|
||||
is the Novel folder, which contains all of the files that makes up the finished novel. Each root
|
||||
folder can have subfolders. Folders have no impact on the final project structure, they are purely
|
||||
tools for organising the files in whatever way the user needs.
|
||||
is the `Novel` folder, which contains all of the files that make up the novel itself. Each root
|
||||
folder can have subfolders. Subfolders have no impact on the final project structure, they are
|
||||
there for you to organise your files in whatever way you want.
|
||||
|
||||
The editor supports four levels of headings, which determines what level the following text belongs
|
||||
The editor supports four levels of headings, which determine what level the following text belongs
|
||||
to. Headings of level one signify a book or partition title. Headings of level two signify the
|
||||
start of a new chapter. Headings of level three signify the start of a new scene. Headings of level
|
||||
four can be used internally in each scene to separate sections.
|
||||
four can be used internally in each scene to create separate sections.
|
||||
|
||||
Each novel file can be assigned a layout format, which shows up as a flag next to the item in the
|
||||
project tree. These are mostly to help the user track what they contain, but they also have some
|
||||
impact on the format of the exported document. See the documentation for further details.
|
||||
impact on the layout of the exported document. See the
|
||||
[documentation](https://novelwriter.readthedocs.io) for further details.
|
||||
|
||||
### Project Notes
|
||||
|
||||
#### Project Notes
|
||||
|
||||
Supporting note files can be added for the story plot, characters, locations, story timeline, etc.
|
||||
These have their separate root folders. These are optional files.
|
||||
|
||||
Supporting notes can be added for the story plot, characters, locations, story timeline, etc. These
|
||||
have their separate root folders and are optional to use.
|
||||
|
||||
### Visualisation of Story Elements
|
||||
|
||||
The different notes can be assigned tags, which other files can refer back to using the `@` meta
|
||||
keywords. This information can be used to display an outline of the story, showing where each scene
|
||||
connects to the plot, and which characters, etc. occur in them. In addition, the tags themselves
|
||||
are clickable in the document view pane, and control-clickable in the editor. They make it possible
|
||||
to quickly navigate between the documents while editing.
|
||||
The different notes can be assigned tags, which other files can refer back to using `@`-prefixed
|
||||
meta keywords. This information can be used to display an outline of the story, showing where each
|
||||
scene connects to the plot, and which characters, etc. occur in them. In addition, the tags
|
||||
themselves are clickable in the document view pane, and control-clickable in the editor. They make
|
||||
it possible to quickly navigate between the documents while writing.
|
||||
|
||||
## Standard Installation
|
||||
|
||||
For a regular installation, it is recommended that you download one of the minimal zip files from
|
||||
the [Releases](https://github.com/vkbo/novelWriter/releases) page or the
|
||||
[novelwriter.io](https://novelwriter.io/) website.
|
||||
The [documentation](https://novelwriter.readthedocs.io/) has detailed install instructions for
|
||||
[Linux](https://novelwriter.readthedocs.io/en/latest/setup_linux.html),
|
||||
[Windows](https://novelwriter.readthedocs.io/en/latest/setup_windows.html), and
|
||||
[macOS](https://novelwriter.readthedocs.io/en/latest/setup_mac.html).
|
||||
They are pretty straightforward.
|
||||
|
||||
## Running from Source
|
||||
|
||||
If you want to run novelWriter directly from the source code, you must run the `novelWriter.py`
|
||||
file from command line.
|
||||
|
||||
**Note:** You may need to replace `python` with `python3` and `pip` with `pip3` in the instructions
|
||||
below on some systems. You may also want to add the `--user` flag for `pip` to install in your user
|
||||
space only.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Dependencies can generally be installed from PyPi with:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Additional Steps for Linux
|
||||
|
||||
On Linux, you can most likely find the dependencies in your distribution's repository. On Ubuntu
|
||||
and Debian, run:
|
||||
```bash
|
||||
sudo apt install python3-pyqt5 python3-lxml python3-enchant
|
||||
```
|
||||
|
||||
If you want to set up a launcher and icons on Linux, you can run:
|
||||
```bash
|
||||
python setup.py xdg-install
|
||||
```
|
||||
|
||||
### Additional Steps for macOS
|
||||
|
||||
First, make sure you have properly set up Python3 with Homebrew. If not, check their
|
||||
[documentation](https://docs.brew.sh/Homebrew-and-Python).
|
||||
In addition, the following steps are necessary to install all dependencies:
|
||||
```bash
|
||||
brew install enchant
|
||||
pip3 install --user -r requirements.txt
|
||||
pip3 install --user pyobjc
|
||||
```
|
||||
|
||||
### Additional Steps for Windows
|
||||
|
||||
Windows does not by default come with Python installed. If you haven't installed it already, get it
|
||||
from [python.org/downloads](https://www.python.org/downloads/). Remember to select "Add Python to
|
||||
PATH" during the installation.
|
||||
|
||||
The script `windows_install.bat` in the `setup` folder can be used to create desktop and start menu
|
||||
icons for novelWriter. The script will also install dependencies for you from PyPi.
|
||||
|
||||
### Internationalisation
|
||||
|
||||
If you install from source, you must build the translation files yourself if you want to switch to
|
||||
a different GUI language than British English. This requires that you have the Qt translation
|
||||
framework installed. Check the specific instruction in the README in the `i18n` source folder for
|
||||
how to build the translation files.
|
||||
|
||||
## Debugging
|
||||
|
||||
If you need to debug novelWriter, you must run it from the command line. It takes a few parameters,
|
||||
which can be listed with the switch `--help`. The `--info`, `--debug` or `--verbose` flags are
|
||||
particularly useful for increasing logging output for debugging.
|
||||
|
||||
## Licenses
|
||||
|
||||
This is Open Source software, and novelWriter is licensed under GPLv3. See the
|
||||
[GNU General Public License website](https://www.gnu.org/licenses/gpl-3.0.en.html) for more
|
||||
details, or consult the [LICENSE](LICENSE.md) file.
|
||||
details, or consult the [LICENSE](https://github.com/vkbo/novelWriter/blob/main/LICENSE.md) file.
|
||||
|
||||
Bundled assets have the following licenses:
|
||||
Bundled assets and their licenses are listed in
|
||||
[CREDITS](https://github.com/vkbo/novelWriter/blob/main/CREDITS.md).
|
||||
|
||||
* The Typicon-based icon themes by Stephen Hutchings are licensed under
|
||||
[CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/). The icons have been altered in
|
||||
size and colour for use with novelWriter, and some additional icons added. The original icon set
|
||||
is available at
|
||||
[stephenhutchings/typicons.font](https://github.com/stephenhutchings/typicons.font).
|
||||
* The Cantarell font by Dave Crossland is licensed under
|
||||
[OPEN FONT LICENSE Version 1.1](http://scripts.sil.org/OFL).
|
||||
It is available at [Google Fonts](https://fonts.google.com/specimen/Cantarell).
|
||||
* The Tomorrow syntax themes use colour schemes taken from Chris Kempson's collection of code
|
||||
editor themes, licensed with the
|
||||
[MIT License](https://github.com/chriskempson/tomorrow-theme/blob/master/LICENSE.md),
|
||||
and the main repo is available at
|
||||
[chriskempson/tomorrow-theme](https://github.com/chriskempson/tomorrow-theme).
|
||||
* Likewise, the Owl syntax themes use colours from Sarah Drasner's code editor themes, licensed
|
||||
with the [MIT License](https://github.com/sdras/night-owl-vscode-theme/blob/master/LICENSE), and
|
||||
the main repo is available at
|
||||
[sdras/night-owl-vscode-theme](https://github.com/sdras/night-owl-vscode-theme).
|
||||
|
||||
|
||||
## Screenshot
|
||||
## Screenshots
|
||||
|
||||
**novelWriter with default system theme:**
|
||||

|
||||
|
||||
@@ -21,3 +21,7 @@
|
||||
white-space: normal !important;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
pre span.go {
|
||||
color: #333333 !important;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
@@ -25,9 +24,9 @@ copyright = "2018–2021, Veronica Berglyd Olsen"
|
||||
author = "Veronica Berglyd Olsen"
|
||||
|
||||
# The short X.Y version
|
||||
version = "1.0"
|
||||
version = "1.4"
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = "1.0"
|
||||
release = "1.4"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@@ -47,7 +46,6 @@ language = None
|
||||
exclude_patterns = []
|
||||
pygments_style = None
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
@@ -75,13 +73,11 @@ html_css_files = [
|
||||
# "searchbox.html"]``.
|
||||
# html_sidebars = {}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "novelWriterDoc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
@@ -110,7 +106,6 @@ latex_documents = [(
|
||||
author, "manual"
|
||||
)]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
@@ -119,7 +114,6 @@ man_pages = [(
|
||||
master_doc, "novelwriter", "novelWriter Documentation", [author], 1
|
||||
)]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
@@ -130,7 +124,6 @@ texinfo_documents = [(
|
||||
"novelWriter", "Markdown-like editor for novels.", "Miscellaneous"
|
||||
)]
|
||||
|
||||
|
||||
# -- Options for Epub output -------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
|
||||
@@ -41,8 +41,8 @@ meta data is primarily saved as JSON files.
|
||||
Any operating system that can run Python 3 and has the Qt 5 libraries should be able to run
|
||||
novelWriter. It runs fine on Linux, Windows and macOS, and users have tested it on other platforms
|
||||
too. novelWriter can be run directly from the Python source, or installed from the pip tool or via
|
||||
the provided setup script. A setup.exe is provided for Windows. See :ref:`a_started` for further
|
||||
details.
|
||||
the provided setup script. See :ref:`a_started`, or one or the setup instructions for your
|
||||
operating system for further details.
|
||||
|
||||
**Useful Links**
|
||||
|
||||
@@ -57,22 +57,26 @@ details.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: First Steps
|
||||
:caption: Introduction
|
||||
|
||||
int_introduction
|
||||
int_started
|
||||
int_interface
|
||||
int_typography
|
||||
setup_linux
|
||||
setup_mac
|
||||
setup_windows
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Writing Novels
|
||||
:caption: Usage
|
||||
|
||||
write_projects
|
||||
write_structure
|
||||
write_notes
|
||||
write_export
|
||||
usage_interface
|
||||
usage_shortcuts
|
||||
usage_projects
|
||||
usage_structure
|
||||
usage_notes
|
||||
usage_export
|
||||
usage_typography
|
||||
|
||||
|
||||
.. toctree::
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
.. _a_ui:
|
||||
|
||||
**************
|
||||
User Interface
|
||||
**************
|
||||
|
||||
The user interface is kept as simple as possible to avoid distractions when writing. This page
|
||||
lists all the main GUI elements, and explains what they do.
|
||||
|
||||
.. _a_ui_tree:
|
||||
|
||||
The Project Tree
|
||||
================
|
||||
|
||||
The main window contains a project tree in the left-most panel. It shows the entire structure of
|
||||
the project. It has four columns:
|
||||
|
||||
:guilabel:`Label`
|
||||
The first column shows the item icon and its label. The labels can be edited from the
|
||||
:guilabel:`Project` menu, or by pressing :kbd:`F2` or :kbd:`Ctrl`:kbd:`E`. The label is not the
|
||||
same as the title you set inside the document, but it will appear in the header above the
|
||||
document text itself.
|
||||
|
||||
:guilabel:`Words`
|
||||
The second column shows the word count of the document, or the sum of words in the child items
|
||||
if it is a folder. If the counts seem incorrect, they can be updated by rebuilding the project
|
||||
index from the :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
|
||||
|
||||
:guilabel:`Inc`
|
||||
The third column indicates whether the document is included in the final project build or not.
|
||||
You may want to filter out documents that you no longer want to keep in the final manuscript,
|
||||
but want to keep in the project for reference.
|
||||
|
||||
:guilabel:`Flags`
|
||||
The fourth column shows various meta data flags for the item. The first is an icon indicating
|
||||
the importance or status of the document. These are colour coded status levels that you control
|
||||
and define yourself. They can be changed in :guilabel:`Project Settings` from the
|
||||
:guilabel:`Project` menu. The first character after the icon indicates the class of the item,
|
||||
that is ``N`` for **Novel**, ``C`` for **Character**, etc (see :ref:`a_struct_tags`). The second
|
||||
character indicates the document layout type (see :ref:`a_proj_roots`).
|
||||
|
||||
Below the project tree you will find a small details panel showing the full information of the
|
||||
currently selected item. This panel also includes the latest paragraph and character counts in
|
||||
addition to the word count.
|
||||
|
||||
Right-clicking an item in the project tree will open a context menu under the cursor, displaying
|
||||
a selection of actions that can be performed on the selected item.
|
||||
|
||||
|
||||
.. _a_ui_tree_dnd:
|
||||
|
||||
Project Tree Drag and Drop
|
||||
--------------------------
|
||||
|
||||
The project tree allows drag and drop to a certain extent. This feature is primarily intended for
|
||||
rearranging the order of your documents within each root folder, and has only limited support for
|
||||
moving documents elsewhere in the project tree. In general, bulk actions are not allowed. This is
|
||||
deliberate to avoid accidentally messing up your project. The project tree has no undo function.
|
||||
|
||||
Documents and their folders can be rearranged freely within their root folders. Novel documents
|
||||
cannot be moved out of the :guilabel:`Novel` folder, except to :guilabel:`Trash` and the
|
||||
:guilabel:`Outtakes` folder. Notes can be moved freely between root folders.
|
||||
|
||||
Folders cannot be moved at all outside their root tree. Neither can a folder containing documents
|
||||
be deleted. You must first delete the containing documents.
|
||||
|
||||
Root folders in the project tree cannot be dragged and dropped at all. If you want to reorder them,
|
||||
you can move them up or down with respect to eachother from the :guilabel:`Tools` menu or the
|
||||
right-click context menu, or by pressing :kbd:`Ctrl`:kbd:`Shift` and the :kbd:`Up` or :kbd:`Down`
|
||||
key.
|
||||
|
||||
|
||||
.. _a_ui_edit:
|
||||
|
||||
Editing and Viewing Documents
|
||||
=============================
|
||||
|
||||
To edit a document, double-click it in the project tree, or press the :kbd:`Return` key while
|
||||
having it selected. This will open the document in the document editor. The editor uses a
|
||||
simplified markdown format. The format is described in the :ref:`a_ui_md` section below. The editor
|
||||
has a maximise button (activates :guilabel:`Distraction Free Mode`) and a close button in the
|
||||
top-right corner.
|
||||
|
||||
Any document in the project tree can also be viewed in parallel in a right hand side document
|
||||
viewer. To view a document, press :kbd:`Ctrl`:kbd:`R`, or select :guilabel:`View Document` in the
|
||||
menu. If you have a middle mouse button, middle-clicking on the document will also open it in the
|
||||
viewer. The document viewed does not have to be the same document currently being edited. However,
|
||||
If you *are* viewing the same document, pressing :kbd:`Ctrl`:kbd:`R` again will update the document
|
||||
with your latest changes. You can also press the reload button in the top-right corner of the view
|
||||
panel next to the close button to achieve the same thing.
|
||||
|
||||
Both the document editor and viewer will show the label of the document in the header at the top of
|
||||
the edit or view panel. Optionally, the full project path to the document can be shown. This can be
|
||||
set in the :guilabel:`Preferences` dialog from the :guilabel:`Tools` menu. Clicking on the document
|
||||
title bar will select and reveal its location in the project tree, making it easier to find in a
|
||||
large project.
|
||||
|
||||
Any reference to a tag in the editor can be opened in the viewer by moving the cursor to the label
|
||||
and pressing :kbd:`Ctrl`:kbd:`Return`. In the viewer, the references become clickable links.
|
||||
Clicking them will replace the content of the viewer with the content of the document the reference
|
||||
points to. The document viewer keeps a history of viewed documents that you can navigate with the
|
||||
arrow buttons in the top-left corner of the viewer. If your mouse has navigation buttons, these can
|
||||
be used as well.
|
||||
|
||||
At the bottom of the view panel there is a :guilabel:`References` panel. (If it is hidden, click
|
||||
the icon to reveal it.) This panel will show links to all documents referring back to it, if any
|
||||
has been defined. The :guilabel:`Sticky` button will freeze the content of the panel to the current
|
||||
document, even if you navigate to another document. This is convenient if you want to quickly look
|
||||
through all documents in the list in the :guilabel:`References` panel.
|
||||
|
||||
.. note::
|
||||
The :guilabel:`References` panel relies on an up-to-date index of the project. If anything is
|
||||
missing, or seems wrong, the index can always be rebuilt by selecting :guilabel:`Rebuild Index`
|
||||
from the :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
|
||||
|
||||
|
||||
.. _a_ui_edit_auto:
|
||||
|
||||
Auto-Replace as You Type
|
||||
========================
|
||||
|
||||
A few auto-replace features are supported by the editor. You can control every aspect of the
|
||||
auto-replace feature from :guilabel:`Preferences`.
|
||||
|
||||
.. tip::
|
||||
If you don't like auto-replacement, all symbols inserted by this feature are also available in
|
||||
the :guilabel:`Insert` menu, and via convenient :ref:`a_ui_shortcuts_ins`.
|
||||
|
||||
The editor is able to replace two and three hyphens with short and long dashes, triple points with
|
||||
ellipsis, and replace straight single and double quotes with user-defined quote symbols. It will
|
||||
also try to determine whether to use the opening or closing symbol, but this feature isn't always
|
||||
accurate.
|
||||
|
||||
.. tip::
|
||||
If the editor changes a symbol when you did not want it to change, pressing :kbd:`Ctrl`:kbd:`Z`
|
||||
immediately after the auto-replacement will undo it without undoing the character you typed.
|
||||
|
||||
|
||||
.. _a_ui_md:
|
||||
|
||||
Markdown Format
|
||||
===============
|
||||
|
||||
The document editor uses a simplified markdown format. That is, it supports basic formatting like
|
||||
emphasis (italic), strong importance (bold) and strikethrough text, as well as four levels of
|
||||
headings.
|
||||
|
||||
Some non-standard markdown features have been added. For instance, novelWriter allows for comments,
|
||||
a synopsis tag, and a set of keyword and value sets used for tags and references.
|
||||
|
||||
|
||||
.. _a_ui_md_head:
|
||||
|
||||
Headings
|
||||
--------
|
||||
|
||||
Four levels of headings are allowed. For documents of layout "Note", they are free to be used as
|
||||
you see fit, but for all other layouts used for the novel text itself, they indicate the structural
|
||||
level of the novel. See :ref:`a_struct_heads` for more details.
|
||||
|
||||
``# Title``
|
||||
Heading level one. If the document is a novel file, the header level indicates the start of a
|
||||
new partition. This heading level can also be used for the title page novel title.
|
||||
|
||||
``## Title``
|
||||
Heading level two. If the document is a novel file, the header level indicates the start of a
|
||||
new chapter.
|
||||
|
||||
``### Title``
|
||||
Heading level three. If the document is a novel file, the header level indicates the start of a
|
||||
new scene.
|
||||
|
||||
``#### Title``
|
||||
Heading level four. If the document is a novel file, the header level indicates the start of a
|
||||
new section.
|
||||
|
||||
.. note::
|
||||
The space after the ``#`` characters is mandatory. The syntax highlighter will change colour and
|
||||
font size when the heading is correctly formatted.
|
||||
|
||||
|
||||
.. _a_ui_md_emph:
|
||||
|
||||
Text Emphasis
|
||||
-------------
|
||||
|
||||
A minimal set of text emphasis styles are supported.
|
||||
|
||||
``_text_``
|
||||
The text is rendered as emphasised text (italicised).
|
||||
|
||||
``**text**``
|
||||
The text is rendered as strongly important text (bold).
|
||||
|
||||
``~~text~~``
|
||||
Strikethrough text.
|
||||
|
||||
In markdown guides it is often recommended to differentiate between strong importance and emphasis
|
||||
by using ``**`` for strong and ``_`` for emphasis, although markdown generally also supports ``__``
|
||||
for strong and ``*`` for emphasis. However, since the differentiation makes the highlighting and
|
||||
conversion significantly simpler and faster, in novelWriter this is a rule, not just a
|
||||
recommendation.
|
||||
|
||||
In addition, the following rules apply:
|
||||
|
||||
1. The emphasis and strikethrough formatting tags do not allow spaces between the words and the tag
|
||||
itself. That is, ``**text**`` is valid, ``**text **`` is not.
|
||||
2. More generally, the delimiters must be on the outer edge of words. That is, ``some **text in
|
||||
bold** here`` is valid, ``some** text in bold** here`` is not.
|
||||
3. If using both ``**`` and ``_`` to wrap the same text, the underscore must be the inner wrapper.
|
||||
This is due to the underscore also being a valid word character, so if they are on the outside,
|
||||
they violate rule 2.
|
||||
4. Text emphasis does not span past line breaks. If you need to add emphasis to multiple lines or
|
||||
paragraphs, you must apply it to each of them in turn.
|
||||
|
||||
|
||||
.. _a_ui_md_comm:
|
||||
|
||||
Comments and Synopsis
|
||||
---------------------
|
||||
|
||||
In addition to these standard markdown features, novelWriter also allows for comments in document.
|
||||
The text of the comment is ignored by the word counter and not exported or, optionally, hidden when
|
||||
viewing the document. If the first word of a comment is ``Synopsis:`` (with the colon), the comment
|
||||
is treated specially and will show up in the :ref:`a_ui_outline` in a dedicated column. The word
|
||||
``synopsis`` is not case sensitive. If it is correctly formatted, the syntax highlighter will
|
||||
indicate this by altering the colour of the word.
|
||||
|
||||
``% text...``
|
||||
A comment. The text is not exported by default (this can be overridden), seen in the Viewer, or
|
||||
counted towards word counts.
|
||||
|
||||
``% Synopsis: text...``
|
||||
A synopsis comment. It is generally treated in the same way as a regular comment, except that it
|
||||
is also captured by the indexing algorithm and displayed in the :ref:`a_ui_outline`. It can also
|
||||
be filtered separately when exporting the project to for instance generate an outline document
|
||||
of the whole project.
|
||||
|
||||
.. note::
|
||||
Only one comment can be flagged as a synopsis comment for each heading. If multiple comments are
|
||||
flagged as synopsis comments, the last one will be used.
|
||||
|
||||
|
||||
.. _a_ui_md_tags:
|
||||
|
||||
Tags and References
|
||||
-------------------
|
||||
|
||||
The document editor supports a minimal set of keywords used for setting tags, and making references
|
||||
between documents. The tags and references can be set once per section defined by a heading. Using
|
||||
them multiple times under the same heading will just override the previous setting.
|
||||
|
||||
``@keyword: value``
|
||||
A keyword argument followed by a value, or a comma separated list of values.
|
||||
|
||||
The available tag and reference keywords are listed in the :ref:`a_struct_tags` section. They can
|
||||
also be inserted at the cursor position in the editor via the :guilabel:`Insert` menu.
|
||||
|
||||
|
||||
.. _a_ui_md_add:
|
||||
|
||||
Additional Markdown and Non-Standard Features
|
||||
---------------------------------------------
|
||||
|
||||
The editor and viewer also supports markdown standard hard line breaks, and preserves non-breaking
|
||||
spaces if running with Qt 5.9 or higher. For older versions, the non-breaking spaces are lost when
|
||||
the document is saved. This is unfortunately hard-coded in the Qt text editor.
|
||||
|
||||
* A hard line break is achieved by leaving two or more spaces at the end of the line.
|
||||
Alternatively, the user can press :kbd:`Ctrl`:kbd:`K`, :kbd:`Return` to insert this.
|
||||
* A non-breaking space is inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Space`.
|
||||
* Thin spaces are also supported, and can be inserted with :kbd:`Ctrl`:kbd:`K`,
|
||||
:kbd:`Shift`:kbd:`Space`.
|
||||
* Non-breaking thin space can be inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`.
|
||||
|
||||
These are all insert features, and the :guilabel:`Insert` menu has more. They are also listed
|
||||
in :ref:`a_ui_shortcuts_ins`.
|
||||
|
||||
Both hard line breaks and non-breaking spaces are highlighted by the syntax highlighter as an
|
||||
alternate coloured background, depending on the selected theme.
|
||||
|
||||
|
||||
.. _a_ui_outline:
|
||||
|
||||
Project Outline View
|
||||
====================
|
||||
|
||||
The project's Outline view is available as the second tab on the right hand side of the main window
|
||||
labelled :guilabel:`Outline`. The outline provides an overview of the novel structure, displaying a
|
||||
tree hierarchy of the elements of the novel, that is, the level 1 to 4 headings.
|
||||
|
||||
The document containing the heading can also be displayed as a separate column, as well as the line
|
||||
number where it occurs. Double-clicking an entry will open the corresponding document in the
|
||||
editor.
|
||||
|
||||
.. note::
|
||||
Since the internal structure of the novel does not depend directly on the folder and document
|
||||
structure of the project tree, these will not necessarily look the same, depending how you
|
||||
choose to organise your documents. See the :ref:`a_struct` page for more details.
|
||||
|
||||
Various meta data and information extracted from tags can be displayed in columns in the outline.
|
||||
A default set of such columns is visible, but you can turn on or off more columns by right clicking
|
||||
the header and selecting the columns you want to show. The order of the columns can also be
|
||||
rearranged by dragging them to a different position.
|
||||
|
||||
.. note::
|
||||
The :guilabel:`Title` column cannot be disabled or moved.
|
||||
|
||||
The information viewed in the outline is based on the project's main index. While novelWriter does
|
||||
its best to keep the index up to date when content changes, you can always rebuild it manually by
|
||||
pressing :kbd:`F9` if something isn't right.
|
||||
|
||||
The outline view itself can be regenerated by pressing :kbd:`F10`. You can also enable automatic
|
||||
updating in the :guilabel:`Tools` menu, which will trigger an update whenever the index is updated
|
||||
and the :guilabel:`Outline` tab is active. You may want to disable this feature if your project is
|
||||
very large,
|
||||
|
||||
The :guilabel:`Synopsis` column of the outline view takes its information from a specially
|
||||
formatted comment. See :ref:`a_ui_md_comm`.
|
||||
|
||||
|
||||
.. _a_ui_shortcuts:
|
||||
|
||||
Keyboard Shortcuts
|
||||
==================
|
||||
|
||||
Most features are available as keyboard shortcuts. These are as follows:
|
||||
|
||||
.. csv-table:: Keyboard Shortcuts
|
||||
:header: "Shortcut", "Description"
|
||||
:widths: 30, 70
|
||||
:class: "tight-table"
|
||||
|
||||
":kbd:`Alt`:kbd:`1`", "Switch focus to the project tree."
|
||||
":kbd:`Alt`:kbd:`2`", "Switch focus to document editor."
|
||||
":kbd:`Alt`:kbd:`3`", "Switch focus to document viewer."
|
||||
":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer."
|
||||
":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer."
|
||||
":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor."
|
||||
":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog."
|
||||
":kbd:`Ctrl`:kbd:`/`", "Change block format to comment."
|
||||
":kbd:`Ctrl`:kbd:`-`", "Strikethrough selected text, or word under cursor."
|
||||
":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor."
|
||||
":kbd:`Ctrl`:kbd:`1`", "Change block format to header level 1."
|
||||
":kbd:`Ctrl`:kbd:`2`", "Change block format to header level 2."
|
||||
":kbd:`Ctrl`:kbd:`3`", "Change block format to header level 3."
|
||||
":kbd:`Ctrl`:kbd:`4`", "Change block format to header level 4."
|
||||
":kbd:`Ctrl`:kbd:`A`", "Select all text in the document."
|
||||
":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)."
|
||||
":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard."
|
||||
":kbd:`Ctrl`:kbd:`D`", "Wrap selected text, or word under cursor, in double quotes."
|
||||
":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`.)"
|
||||
":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected."
|
||||
":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`.)"
|
||||
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
|
||||
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
|
||||
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_ui_shortcuts_ins`."
|
||||
":kbd:`Ctrl`:kbd:`N`", "Create new document."
|
||||
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
||||
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
|
||||
":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
|
||||
":kbd:`Ctrl`:kbd:`S`", "Save the current document in the document editor."
|
||||
":kbd:`Ctrl`:kbd:`V`", "Paste text from clipboard to cursor position."
|
||||
":kbd:`Ctrl`:kbd:`W`", "Close the current document in the document editor."
|
||||
":kbd:`Ctrl`:kbd:`X`", "Cut selected text to clipboard."
|
||||
":kbd:`Ctrl`:kbd:`Y`", "Redo latest undo."
|
||||
":kbd:`Ctrl`:kbd:`Z`", "Undo latest changes."
|
||||
":kbd:`Ctrl`:kbd:`F7`", "Toggle spell checking."
|
||||
":kbd:`Ctrl`:kbd:`F10`", "Toggle automatic updating of project outline."
|
||||
":kbd:`Ctrl`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder."
|
||||
":kbd:`Ctrl`:kbd:`Enter`", "Open the tag or reference under the cursor in the Viewer."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`,`", "Open the :guilabel:`Project Settings` dialog."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`/`", "Remove block formatting for block under cursor."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`D`", "Wrap selected text, or word under cursor, in single quotes."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`.)"
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Alternative sequence for redo last undo."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Up`", "Move item one step up in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Down`", "Move item one step down in the project tree."
|
||||
":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website."
|
||||
":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)"
|
||||
":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)"
|
||||
":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog."
|
||||
":kbd:`F6`", "Open the :guilabel:`Writing Statistics` dialog."
|
||||
":kbd:`F7`", "Re-run spell checker."
|
||||
":kbd:`F8`", "Activate :guilabel:`Focus Mode`, hiding the project tree and document viewer."
|
||||
":kbd:`F9`", "Re-build the project index."
|
||||
":kbd:`F10`", "Re-build the project outline."
|
||||
":kbd:`F11`", "Activate full screen mode."
|
||||
":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser."
|
||||
":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)"
|
||||
":kbd:`Return`", "If in the project tree, open a document for editing."
|
||||
|
||||
.. note::
|
||||
On macOS, replace :kbd:`Ctrl` with :kbd:`Cmd`.
|
||||
|
||||
|
||||
.. _a_ui_shortcuts_ins:
|
||||
|
||||
Insert Shortcuts
|
||||
----------------
|
||||
|
||||
A set of insert features are also available through shortcuts, but they require a double
|
||||
combination of key sequences. The insert feature is activated with :kbd:`Ctrl`:kbd:`K`, followed by
|
||||
a key or combination for the inserted character or punctuation.
|
||||
|
||||
.. csv-table:: Keyboard Shortcuts
|
||||
:header: "Shortcut", "Description"
|
||||
:widths: 40, 60
|
||||
:class: "tight-table"
|
||||
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`-`", "Insert a short dash (en dash)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`_`", "Insert a long dash (em dash)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert an ellipsis."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert a left single quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert a right single quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert a left double quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert a right double quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier apostrophe."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Return`", "Insert a hard line break."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`", "Insert a thin non-breaking space."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`G`", "Insert a @tag keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`V`", "Insert a @pov keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`C`", "Insert a @char keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`P`", "Insert a @plot keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`T`", "Insert a @time keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`L`", "Insert a @location keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`O`", "Insert a @object keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`E`", "Insert a @entity keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`X`", "Insert a @custom keyword."
|
||||
@@ -7,20 +7,20 @@ Introduction
|
||||
novelWriter is a simple, multi-document plain text editor using a modified markdown syntax to apply
|
||||
simple formatting to the text. It is designed for writing novels, and allows for the component
|
||||
documents to be ordered freely to create the desired structure of the novel. More details about how
|
||||
projects are structured is covered on the :ref:`a_struct` page.
|
||||
projects are structured is covered in :ref:`a_struct`.
|
||||
|
||||
In addition, the project can contain notes on the various plot elements, characters, locations,
|
||||
etc, that make up the story. These notes are organised in a set of category-specific top-level
|
||||
folders (root folders), and each entry can be tagged and cross-referenced from within the novel
|
||||
documents and notes. These tags make it possible to inter-link documents, and generate an overview
|
||||
of the entire novel project and how the various documents and plot elements are interconnected.
|
||||
This is covered on the :ref:`a_proj` and :ref:`a_notes` pages.
|
||||
folders referred to as "root" folders, and each entry can be tagged and cross-referenced from
|
||||
within the novel documents and notes. These tags make it possible to inter-link documents, and
|
||||
generate an overview of the entire novel project and how the various documents and plot elements
|
||||
are interconnected. The tag and reference syntax is covered in :ref:`a_proj` and :ref:`a_notes`.
|
||||
|
||||
These additional features are not standard in markdown, but are available through special meta
|
||||
keywords described in :ref:`a_struct_tags`. Syntax highlighting is provided to make it easier to
|
||||
verify that the markdown tags are used correctly.
|
||||
|
||||
An overview of the supported markdown syntax is covered on the :ref:`a_ui` page.
|
||||
An overview of the supported markdown syntax is covered in :ref:`a_ui`.
|
||||
|
||||
|
||||
.. _a_intro_design:
|
||||
@@ -36,30 +36,36 @@ at the same time provide a complete set of features needed for writing a novel.
|
||||
links, tables, and other complex structures and objects often needed for such documents.
|
||||
Formatting is limited to headers, and bold, italicised and strikethrough text.
|
||||
|
||||
The main window does not have a toolbar like most other applications do. This reduces clutter, and
|
||||
.. tip::
|
||||
If you do need to align information in rows and columns in your notes, you can achieve this with
|
||||
tabs and line breaks. The tab stop width can be specified in :guilabel:`Preferences`.
|
||||
|
||||
The main window does not have a toolbar like many other applications do. This reduces clutter, and
|
||||
since the documents are formatted with markdown tags, is more or less redundant. However, all
|
||||
formatting features supported are available through convenient keyboard shortcuts. They are also
|
||||
available in the main menu. A full list of shortcuts can be found in the :ref:`a_ui_shortcuts`
|
||||
section.
|
||||
available in the main menu so you don't have to look up the syntax every time until you've
|
||||
memorised it. A full list of shortcuts can be found in the :ref:`a_kb` section.
|
||||
|
||||
In addition, novelWriter offers a :guilabel:`Distraction Free Mode` where all the user interface
|
||||
elements other than the document editor itself are hidden away.
|
||||
In addition, novelWriter has a :guilabel:`Focus Mode` where all the user interface elements other
|
||||
than the document editor itself are hidden away.
|
||||
|
||||
The colour scheme of the user interface defaults to that of the host operating system. In addition,
|
||||
a dark theme is provided, and can be enabled in :guilabel:`Preferences` from the :guilabel:`Tools`
|
||||
menu. A number of syntax highlighting themes are also available in :guilabel:`Preferences`. A set
|
||||
of icon themes in colour and greyscale are also offered. The icons are based on the Typicons_ icon
|
||||
set designed by Stephen Hutchings.
|
||||
other light and dark themes are provided, and can be enabled in :guilabel:`Preferences` from the
|
||||
:guilabel:`Tools` menu. A number of syntax highlighting themes are also available in
|
||||
:guilabel:`Preferences`. A set of icon themes in colour and greyscale are also offered. The icons
|
||||
are based on the Typicons_ icon set designed by Stephen Hutchings.
|
||||
|
||||
The main window is split in two, or optionally three, panels. The left-most contains the project
|
||||
tree and all the documents in your project. The second panel is the document editor, and the
|
||||
optional third panel is a document viewer which can view any document in your project.
|
||||
The main window is split in two, or optionally three, panels. The left-most panel contains the
|
||||
project tree and all the documents in your project. The second panel is the document editor, and
|
||||
the optional third panel is a document viewer which can view any document in your project
|
||||
independently of the document editor. It is not intended as a preview window, although you can use
|
||||
it for this, but for viewing your notes next to your editor while you're writing.
|
||||
|
||||
A second tab is also available on the main window. This is the :guilabel:`Outline` tab where the
|
||||
entire novel structure can be displayed, with all the tags and references listed. Depending on how
|
||||
you structure your novel documents, this outline can be quite different from your project tree.
|
||||
Your project tree lists individual documents, your Outline tree lists the structure of the novel
|
||||
itself as it appears in the text of the documents.
|
||||
itself in terms of partitions, chapters and scenes as it appears in the text of the documents.
|
||||
|
||||
.. _Typicons: https://github.com/stephenhutchings/typicons.font
|
||||
|
||||
@@ -69,7 +75,7 @@ itself as it appears in the text of the documents.
|
||||
Project Layout
|
||||
==============
|
||||
|
||||
You are free to structure your project documents as you wish in subfolders, and split the text
|
||||
You are free to organise your project documents as you wish into subfolders, and split the text
|
||||
between documents in whatever way suits you. All that matters to novelWriter is the linear order
|
||||
the documents appear at in the project tree (top to bottom). The chapters, scenes and sections of
|
||||
the novel are determined by the headings within those documents.
|
||||
@@ -83,7 +89,7 @@ The four heading levels (**H1** to **H4**) are treated as follows:
|
||||
|
||||
This header level structure is only taken into account for novel documents. For the project notes,
|
||||
the header levels have no structural meaning, and the user is free to do whatever they want. See
|
||||
the :ref:`a_struct` and :ref:`a_notes` pages for more details.
|
||||
:ref:`a_struct` and :ref:`a_notes` for more details.
|
||||
|
||||
|
||||
.. _a_intro_export:
|
||||
@@ -92,13 +98,12 @@ Project Export
|
||||
==============
|
||||
|
||||
The project can at any time be exported to a range of different formats through the
|
||||
:guilabel:`Build Novel Project` tool. Natively, novelWriter supports export to plain text file,
|
||||
HTML document, novelWriter flavoured markdown, standard markdown (requires Qt 5.14), and to a basic
|
||||
Open Document format.
|
||||
:guilabel:`Build Novel Project` tool. Natively, novelWriter supports export to Open Document,
|
||||
HTML5, and various flavours of Markdown.
|
||||
|
||||
In addition, printing and printing to PDF is also possible. The best supported export format is
|
||||
HTML, which can be imported or converted by a number of other tools like Pandoc, or simply imported
|
||||
into Libre Office Writer and similar word processors.
|
||||
The HTML5 export format is suitable for conversion by a number of other tools like Pandoc, or for
|
||||
importing into word processors if the Open Document format isn't suitable. In addition, printing
|
||||
and printing to PDF is also possible.
|
||||
|
||||
It is also possible to export the content of the project to a JSON file. This is useful if you want
|
||||
to write your own processing script in for instance Python as the entire novel can be read into a
|
||||
@@ -106,8 +111,7 @@ Python dictionary with a couple of lines of code.
|
||||
|
||||
A number of filter options can be applied to the produced document, allowing you to export a draft
|
||||
manuscript, a reference document of notes, an outline based on chapter and scene titles with a
|
||||
synopsis each, and so on. See the :ref:`a_export` page for more details on export features and
|
||||
formats.
|
||||
synopsis each, and so on. See :ref:`a_export` for more details on export features and formats.
|
||||
|
||||
|
||||
.. _a_intro_screenshots:
|
||||
|
||||
@@ -4,69 +4,28 @@
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
This is a brief guide to how you can get novelWriter running on your computer. These are the
|
||||
methods currently supported by the developer. Packages may also be available in other package
|
||||
managers, but those are not managed by the developer. A Windows installer file is also provided on
|
||||
the GitHub page and main website.
|
||||
|
||||
As novelWriter matures, more options for how to install it and get it running will be added. For
|
||||
non-Windows users the install process is at the present time best suited for people used to working
|
||||
with the command line.
|
||||
|
||||
.. note::
|
||||
The text below assumes the command ``python`` corresponds to a Python 3 executable. For
|
||||
operating systems with both Python 2 and 3, the command ``python3`` may be needed instead. On
|
||||
Linux, the scripts can also be made executable and run without the ``python`` command.
|
||||
|
||||
|
||||
.. _a_started_install:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
You can download the latest version of novelWriter from the source repository on GitHub_. You can
|
||||
also install it directly from PyPi with ``pip install novelwriter``, or download the packages
|
||||
directly from the PyPi_ project page.
|
||||
|
||||
Latest version of novelWriter is |release|.
|
||||
|
||||
.. _main website: https://novelwriter.io
|
||||
.. _GitHub: https://github.com/vkbo/novelWriter/releases
|
||||
.. _PyPi: https://pypi.org/project/novelWriter/
|
||||
|
||||
This section contains brief guides to how you can get novelWriter running on your computer. These
|
||||
are the methods currently supported by the developer. Packages may also be available in other
|
||||
package managers, but those are not managed by the developer. No installers are provided at this
|
||||
time, but it is fairly straightforward to set up novelWriter with the provided install scripts.
|
||||
|
||||
.. _a_started_install_win:
|
||||
As novelWriter matures, more options for how to install it and get it running will be added. For
|
||||
non-Windows users the install process is at the present time best suited for people used to working
|
||||
with the command line. But even if you're not, the install process is fairly straightforward.
|
||||
|
||||
Windows Installer
|
||||
-----------------
|
||||
The next pages have specific install instructions for the various operating systems novelWriter can
|
||||
run on. The instructions below are supplementary information, instructions for alterbative methods,
|
||||
and additional build options.
|
||||
|
||||
You can run novelWriter directly from source on Windows, but a Windows installer is also provided
|
||||
for 64-bit Windows on the `main website`_ and GitHub_ page. This installer bundles all that is
|
||||
needed for novelWriter to run, including Python and the xml and Qt libraries.
|
||||
|
||||
.. _main website: https://novelwriter.io
|
||||
|
||||
|
||||
.. _a_started_install_source:
|
||||
|
||||
Install from Source on Linux
|
||||
----------------------------
|
||||
|
||||
For Linux systems, novelWriter can be installed from source with the provided ``setup.py`` script.
|
||||
To install novelWriter into the system's default Python install locations, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python setup.py install
|
||||
|
||||
To set up the novelWriter desktop launcher, the icons and the project file association, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python setup.py xdg-install
|
||||
|
||||
By default, these commands installs novelWriter and its icons for the current user only. To install
|
||||
for all users, run the script with the ``sudo`` command. Other options are also available. Run
|
||||
``python setup.py help`` for a full list of install options.
|
||||
.. note::
|
||||
The text below assumes the command ``python`` corresponds to a Python 3 executable. Python 2 is
|
||||
now deprecated, but many systems still have both Python 2 and 3. For such systems, the command
|
||||
``python3`` may be needed instead. On Linux, the scripts can also be made executable and run
|
||||
without the ``python`` command. Likewise, ``pip`` may need to be replaced with ``pip3``.
|
||||
|
||||
|
||||
.. _a_started_depend:
|
||||
@@ -74,70 +33,115 @@ for all users, run the script with the ``sudo`` command. Other options are also
|
||||
Dependencies
|
||||
============
|
||||
|
||||
novelWriter has been designed to rely on as few dependencies as possible. Aside from the package(s)
|
||||
novelWriter has been designed to rely on as few dependencies as possible. Aside from the packages
|
||||
needed to communicate with the Qt GUI libraries, only one package is required for handling the XML
|
||||
format of the main project file. Everything else is handled with standard Python libraries.
|
||||
|
||||
The following Python packages are needed to run novelWriter:
|
||||
|
||||
* ``pyqt5`` – needed for connecting with the Qt5 libraries.
|
||||
* ``lxml`` – needed for full XML support.
|
||||
* ``pyenchant`` – needed for efficient spell checking (optional).
|
||||
|
||||
PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work. For
|
||||
instance, searching using regular expressions with full Unicode support requires 5.13. There is no
|
||||
known minimum version requirement for package ``lxml``, but the code was originally written with
|
||||
4.2, which is therefore set as the minimum. It may work on lower versions. You have to test it.
|
||||
|
||||
Optionally, a package can be installed to interface with the Enchant spell checking libaries, but
|
||||
this isn't strictly required. If no external spell checking library is available, novelWriter falls
|
||||
back to using the internal ``difflib`` of Python to check spelling. This is a much slower approach,
|
||||
and it is less sophisticated than full spell checking libaries, but if you only work with small
|
||||
files, the performance loss is not noticeable.
|
||||
back to using the internal ``difflib`` of Python to check spelling. This is a slower and less
|
||||
sophisticated spell checker than the full spell checking libaries. The spell check library must be
|
||||
at least 3.0 to work with Windows. On Linux, 2.0 also works fine.
|
||||
|
||||
|
||||
.. _a_started_depend_packages:
|
||||
|
||||
Package Installation
|
||||
--------------------
|
||||
|
||||
If you already have Python installed, all you need to do is install the dependencies. To do this,
|
||||
you need to open your command line tool, find the folder where you extracted novelWriter, and run
|
||||
the following command:
|
||||
If you install from PyPi, these dependencies should be installed automatically. If you install from
|
||||
source, dependencies can still be installed from PyPi with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
This will install all the dependencies and recommended packages.
|
||||
|
||||
The following Python packages are required to run novelWriter:
|
||||
.. _a_started_install:
|
||||
|
||||
* ``pyqt5``, needed for connecting with the Qt5 libraries.
|
||||
* ``lxml``, needed full XML support.
|
||||
Installing via PyPi
|
||||
===================
|
||||
|
||||
You can of course also install these packages from your operating system's package repository.
|
||||
The application is written in Python 3 using Qt5 via PyQt5. It is developed on Linux, but it should
|
||||
in principle work fine on other operating systems as long as dependencies are met.
|
||||
|
||||
PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work.
|
||||
Exporting to standard Markdown, for instance, requires PyQt/Qt 5.14. Searching using regular
|
||||
expressions requires 5.3, and for full Unicode support, 5.13. There is no known minimum version
|
||||
requirement for package ``lxml``, but the code was originally written with 4.2, which is therefore
|
||||
set as the minimum. It may work on lower versions. You have to test it.
|
||||
You can download the latest version of novelWriter from the source repository on GitHub_.
|
||||
novelWriter is also hosted on PyPi_, and can be installed on all operating systems that support Qt5
|
||||
and Python 3. It is regularly tested on Linux, Windows and macOS. The latest version of novelWriter
|
||||
is |release|.
|
||||
|
||||
The spell checking extension is optional, but recommended:
|
||||
To install from PyPi you must first have the ``python`` and ``pip`` commands available on your
|
||||
system. If you don't, see specific instructions for your operating system in this documentation.
|
||||
To install novelWriter from PyPi, use the following command:
|
||||
|
||||
* ``pyenchant``, needed for efficient spell checking.
|
||||
.. code-block:: console
|
||||
|
||||
The optional spell check library must be at least 3.0 to work with Windows. On Linux, 2.0 also
|
||||
works fine.
|
||||
pip install novelwriter
|
||||
|
||||
To upgrade an existing installation, use:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --upgrade novelwriter
|
||||
|
||||
When installing via pip, novelWriter can be launched from command line with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
novelWriter
|
||||
|
||||
Make sure the install location for pip is in your PATH variable. This is not always the case by
|
||||
default.
|
||||
|
||||
|
||||
.. _a_started_depend_docs:
|
||||
.. _a_started_i18n:
|
||||
|
||||
Building the Translation Files
|
||||
==============================
|
||||
|
||||
If you installed novelWriter from a package, the translation files should be pre-built and
|
||||
included. If you're running novelWriter from the source code, you will need to generate the files
|
||||
yourself. The files you need will be written to the ``nw/assets/i18n`` folder, and will have the
|
||||
``.qm`` file extension.
|
||||
|
||||
You can build the ``.qm`` files with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python3 setup.py qtlrelease
|
||||
|
||||
This requires that the Qt Linguist tool is installed on your system. On Ubuntu and Debian, the
|
||||
needed package is called `qttools5-dev-tools`.
|
||||
|
||||
.. note::
|
||||
If you want to improve novelWriter with translation files for another language, or update an
|
||||
existing translation, instructions for how to contribute can be found in the README file in the
|
||||
``i18n`` folder of the source code.
|
||||
|
||||
|
||||
.. _a_started_docs:
|
||||
|
||||
Building the Documentation
|
||||
--------------------------
|
||||
==========================
|
||||
|
||||
If you installed novelWriter from a package, the documentation should be included. If you're
|
||||
running novelWriter from the source code, a local copy of this documentation can be generated. It
|
||||
requires the following Python packages on Debian and Ubuntu.
|
||||
If you installed novelWriter from a package, the documentation should be bre-built and included. If
|
||||
you're running novelWriter from the source code, a local copy of this documentation can be
|
||||
generated. It requires the following Python packages on Debian and Ubuntu.
|
||||
|
||||
* ``python3-sphinx``
|
||||
* ``python3-sphinx-rtd-theme``
|
||||
* ``python3-sphinxcontrib.qthelp``
|
||||
|
||||
Or from PyPi:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install sphinx sphinxcontrib-qthelp
|
||||
pip install sphinx sphinx-rtd-theme sphinxcontrib-qthelp
|
||||
|
||||
The documentation can then be built from the ``docs`` folder in the source code by running:
|
||||
|
||||
@@ -145,7 +149,8 @@ The documentation can then be built from the ``docs`` folder in the source code
|
||||
|
||||
make html
|
||||
|
||||
If successful, the documentation should then be available in the ``docs/build/html`` folder.
|
||||
If successful, the documentation should be available in the ``docs/build/html`` folder and you can
|
||||
open the ``index.html`` file in your browser.
|
||||
|
||||
The documentation can also be built for the Qt Assistant. To build the help packages from the
|
||||
documentation source, run the following from the root source folder:
|
||||
@@ -163,54 +168,3 @@ locally first, then send you to the website as a fallback.
|
||||
In order for the local version of help to work, the Qt Assistant must be installed on the local
|
||||
computer. If it isn't available, or novelWriter cannot find it, the help feature will fall back
|
||||
to redirecting you to the documentation website.
|
||||
|
||||
|
||||
.. _a_started_running:
|
||||
|
||||
Running from Source
|
||||
===================
|
||||
|
||||
If all the required dependencies are met, you can run novelWriter from the command line:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python novelWriter.py
|
||||
|
||||
A few switches are supported from the command line, mostly to assist in debugging if an error is
|
||||
encountered. To list all options, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python novelWriter.py --help
|
||||
|
||||
|
||||
.. _a_started_standalone:
|
||||
|
||||
Building a Standalone Executable
|
||||
================================
|
||||
|
||||
A standalone executable can be built with ``pyinstaller``, using the provided python script
|
||||
``make.py`` in the source folder. This script can install dependencies, build a standalone
|
||||
executable of novelWriter, or build a ``setup.exe`` file with Inno Setup.
|
||||
|
||||
For a full list of the script's options, run ``python make.py help``.
|
||||
|
||||
|
||||
.. _a_started_standalone_win:
|
||||
|
||||
Additional Instructions for Windows
|
||||
-----------------------------------
|
||||
|
||||
If you don't have Python installed, you can download it from the python.org website. The installers
|
||||
for Windows are available at https://www.python.org/downloads/windows/
|
||||
|
||||
novelWriter should work with Python 3.6 or higher, and the executable installer is the easiest to
|
||||
install.
|
||||
|
||||
Also, make sure you select the "Add Python to PATH" option.
|
||||
|
||||
.. image:: images/python_win_install.png
|
||||
:width: 600
|
||||
|
||||
Once Python is set up and running, you can either run novelWriter from the folder where you
|
||||
extracted it, or you can build an executable and run that from a desktop icon instead.
|
||||
|
||||
110
docs/source/setup_linux.rst
Normal file
@@ -0,0 +1,110 @@
|
||||
.. _a_setup_linux:
|
||||
|
||||
**************
|
||||
Setup on Linux
|
||||
**************
|
||||
|
||||
.. _GitHub: https://github.com/vkbo/novelWriter/releases
|
||||
.. _main website: https://novelwriter.io
|
||||
|
||||
This is a brief guide to how you can get novelWriter running on a Linux computer. There are
|
||||
currently no install package of novelWriter for Linux, so it is recommended that you download
|
||||
either the full source or minimal package and extract it to a practical location on your system and
|
||||
run the ``setup.py`` script.
|
||||
|
||||
|
||||
Running from Source
|
||||
===================
|
||||
|
||||
To run novelWriter from source, download the latest source package from the release page on
|
||||
GitHub_ or the `main website`_, or if you have git running on your computer, you can also clone the
|
||||
repository.
|
||||
|
||||
|
||||
Step 1: Installing Dependencies
|
||||
-------------------------------
|
||||
|
||||
The dependencies of novelWriter are generally available from Linux distro repositories. For Debian
|
||||
and Ubuntu, they can be installed with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
sudo apt install python3-pyqt5 python3-lxml python3-enchant
|
||||
|
||||
If you prefer to install dependencies via PyPi, or the repository dependencies are out of date, you
|
||||
can install them with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip3 install --user -r requirements.txt
|
||||
|
||||
|
||||
Step 2: Install Package (Optional)
|
||||
----------------------------------
|
||||
|
||||
You can install novelWriter to the default location for Python packages using ``setuptools``. This
|
||||
step is optional as you can also just put the novelWriter program folder wherever you like
|
||||
yourself. For instance in ``/opt/novelWriter``, and then run Step 3 to set up icons and launcher.
|
||||
|
||||
To install novelWriter to the default location, it requires that you have ``setuptools`` installed
|
||||
on your system. If you don't have it installed, it can usually be installed from your distro's
|
||||
repository. For Debian and Ubuntu this is achieved with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
sudo apt install python3-setuptools
|
||||
|
||||
The package is also available from PyPi:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip3 install --user setuptools
|
||||
|
||||
With ``setuptools`` in place, novelWriter can be installed to the user space with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./setup.py install --user
|
||||
|
||||
This should install novelWriter as ``~/.local/bin/novelWriter``. If you instead want to install for
|
||||
all users, i.e. as ``/usr/local/bin/novelWriter``, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
sudo ./setup.py install
|
||||
|
||||
This is equivalent to what the ``pip`` installer does. It puts novelWriter in the location on your
|
||||
system where Python packages are usually kept. This is not really the best suited location for a
|
||||
GUI application like novelWriter, so you may instead copy the entire source to a suiteable location
|
||||
yourself.
|
||||
|
||||
|
||||
Step 3: Create Launcher Icons
|
||||
-----------------------------
|
||||
|
||||
Regardless of where you extract or install the source files, you can set up a standard icon and a
|
||||
launcher. To set up this desktop launcher, the needed icons, and the project file association,
|
||||
run the following from inside the novelWriter folder at the installed or final location:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./setup.py xdg-install
|
||||
|
||||
By default, this command installs the launcher and icons for the current user only. To install for
|
||||
all users, run the script with the ``sudo`` command.
|
||||
|
||||
.. tip::
|
||||
All options of the setup script can be listed with: ``./setup.py help``.
|
||||
|
||||
|
||||
Uninstalling Icons
|
||||
==================
|
||||
|
||||
The steps taken by the ``xdg-install`` step can be reversed by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./setup.py xdg-uninstall
|
||||
|
||||
This will remove the desktop launcher and icons from the system. As above, whether this is done on
|
||||
the current user, or system wide, depends on whether this command is called with ``sudo`` or not.
|
||||
74
docs/source/setup_mac.rst
Normal file
@@ -0,0 +1,74 @@
|
||||
.. _a_setup_mac:
|
||||
|
||||
**************
|
||||
Setup on macOS
|
||||
**************
|
||||
|
||||
.. _GitHub: https://github.com/vkbo/novelWriter/releases
|
||||
.. _main website: https://novelwriter.io
|
||||
.. _brew docs: https://docs.brew.sh/Homebrew-and-Python
|
||||
|
||||
This is a brief guide to how you can get novelWriter running on macOS. There are currently no
|
||||
install package of novelWriter for macOS, so it is recommended that you download either the full
|
||||
source or minimal package and extract it to a practical location on your system and
|
||||
run it.
|
||||
|
||||
|
||||
Running from Source
|
||||
===================
|
||||
|
||||
To run novelWriter from source, download the latest source package from the release page on
|
||||
GitHub_ or the `main website`_, or if you have git running on your computer, you can also clone the
|
||||
repository.
|
||||
|
||||
|
||||
Step 1: Installing Dependencies
|
||||
-------------------------------
|
||||
|
||||
These instructions assume you're using brew, and have Python and pip set up. If not, see the
|
||||
`brew docs`_ for help. Main requirements are installed via the requirements file. You also need to
|
||||
install the ``pyobjc`` package, so you should run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip3 install --user -r requirements.txt
|
||||
pip3 install --user pyobjc
|
||||
|
||||
For spell checking you may also need to install the enchant package. It comes with a lot of default
|
||||
dictionaries.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
brew install enchant
|
||||
|
||||
With the dependencies in place, you can launch the ``novelWriter.py`` script directly to run
|
||||
novelWriter.
|
||||
|
||||
|
||||
Step 2: Install Package (Optional)
|
||||
----------------------------------
|
||||
|
||||
You can install novelWriter to the correct location for Python packages with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./setup.py install --user
|
||||
|
||||
This requires that the package ``setuptools`` is installed on your system. If not, it can be
|
||||
installed with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip3 install --user setuptools
|
||||
|
||||
This is method of install is equivalent to what the ``pip`` installer does. It puts novelWriter in
|
||||
the location on your system where Python packages are usually kept. This is not really the best
|
||||
suited location for a GUI application like novelWriter, so you may instead copy the entire source
|
||||
to a suiteable location yourself.
|
||||
|
||||
After this, you should be able to launch novelWriter by running ``novelWriter`` in a command line
|
||||
window.
|
||||
|
||||
.. note::
|
||||
Right now there isn't a better integration with macOS available. Contributions from someone more
|
||||
familiar with macOS would be very much appreciated.
|
||||
149
docs/source/setup_windows.rst
Normal file
@@ -0,0 +1,149 @@
|
||||
.. _a_setup_win:
|
||||
|
||||
****************
|
||||
Setup on Windows
|
||||
****************
|
||||
|
||||
This is a brief guide to how you can get novelWriter running on a Windows computer.
|
||||
|
||||
Unlike other operating systems, Windows does not come prepared with an environment to run Python
|
||||
applications, so you must first install Python. After that, running novelWriter is straightforward.
|
||||
|
||||
.. tip::
|
||||
If you have any problems, you can always open a question on the project's discussions_ page.
|
||||
This requires a GitHub account.
|
||||
|
||||
.. _discussions: https://github.com/vkbo/novelWriter/discussions
|
||||
|
||||
|
||||
.. _a_setup_win_installer:
|
||||
|
||||
Using the Installer
|
||||
===================
|
||||
|
||||
The installer for Windows is no longer provided. See the `installer issue`_ for more info on why.
|
||||
You can still create the installer yourself if you want to. It can be generated with the provided
|
||||
``setup.py`` script. use the script's ``help`` command to get further instructions.
|
||||
|
||||
Please use the "Running from Source" option below instead. It has been improved and a script has
|
||||
been added that does nearly the same thing.
|
||||
|
||||
.. _installer issue: https://github.com/vkbo/novelWriter/issues/640
|
||||
|
||||
|
||||
.. _a_setup_win_source:
|
||||
|
||||
Running from Source
|
||||
===================
|
||||
|
||||
To run novelWriter from source, download the latest source zip file from the release page on
|
||||
GitHub_, or from the `main website`_. You can download the "Minimal Windows" zip file. It is a
|
||||
minimal version of the source that contains only the files needed for running on Windows. You can
|
||||
also download the full source if you wish.
|
||||
|
||||
In order to make novelWriter run on your system, you must first have Python installed (Step 1).
|
||||
Thereafter, a script will do the rest of the job (Step 2). Alternatively, you can run this step
|
||||
manually from the command prompt if you wish to.
|
||||
|
||||
.. tip::
|
||||
If your system already has all the dependencies installed, you can run novelWriter directly from
|
||||
the extracted folder by double-clicking the ``novelWriter.pyw`` file. However, running the
|
||||
script also adds start icons.
|
||||
|
||||
.. _GitHub: https://github.com/vkbo/novelWriter/releases
|
||||
.. _main website: https://novelwriter.io
|
||||
|
||||
|
||||
Step 1: Installing Python
|
||||
-------------------------
|
||||
|
||||
If you already have Python installed, you can skip this step. If you don't have it installed, you
|
||||
can download it from the python.org_ website. novelWriter should work with Python 3.6 or higher,
|
||||
but it is recommended that you install the latest version of Python.
|
||||
|
||||
Make sure you select the "Add Python to PATH" option during installation, otherwise the ``python``
|
||||
command will not work in the command line window.
|
||||
|
||||
.. image:: images/python_win_install.png
|
||||
:width: 600
|
||||
|
||||
.. _python.org: https://www.python.org/downloads/windows
|
||||
|
||||
|
||||
Step 2: Dependencies and Icons
|
||||
------------------------------
|
||||
|
||||
**Alternative A: By Script**
|
||||
|
||||
Open the folder where you extracted novelWriter, and double-click the file named
|
||||
``windows_install.bat``. This should open a command line window and run the setup script to install
|
||||
dependencies, and add desktop and start menu icons.
|
||||
|
||||
.. note::
|
||||
If you downloaded the full source package, the file may be in the ``setup`` subfolder.
|
||||
|
||||
The script will also check that it can find Python on your system and alert you if it cannot run
|
||||
it. If you are sure you have installed it, but the script cannot find it, you probably didn't
|
||||
install it with the "Add Python to PATH" option mentioned in Step 1.
|
||||
|
||||
.. note::
|
||||
If you upgrade Python to a newer version and the path to ``pythonw.exe`` has therefore changed,
|
||||
you may need to run this script again. You can also run it to upgrade dependencies to the latest
|
||||
version.
|
||||
|
||||
**Alternative B: Manual Installation**
|
||||
|
||||
The above alternative can also be run manually.
|
||||
|
||||
Open the windows command prompt. It can be launched by pressing the :kbd:`Win` key and typing "cmd".
|
||||
The "Command Prompt" app should then be in the list of applications.
|
||||
|
||||
With the command prompt open, navigate to the folder where you extracted the novelWriter source,
|
||||
and run the following commands:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user pywin32 -r requirements.txt
|
||||
python setup.py win-install
|
||||
|
||||
The first command will install the dependencies on your system from the `Python Package Index`_,
|
||||
and the second command will create a desktop icon and a start menu icon. That should be all that
|
||||
you need.
|
||||
|
||||
.. _Python Package Index: https://pypi.org/
|
||||
|
||||
|
||||
Uninstalling
|
||||
============
|
||||
|
||||
**Alternative A: By Script**
|
||||
|
||||
Open the folder where you keep the novelWriter files, and double-click the file named
|
||||
``windows_uninstall.bat``. This should open a command line window and run the setup script to
|
||||
remove the main dependency packages and remove desktop and start menu icons.
|
||||
|
||||
.. note::
|
||||
If you downloaded the full source package, the file may be in the ``setup`` subfolder.
|
||||
|
||||
If you plan to also remove Python from your system, you must run the above script first as it needs
|
||||
Python in order to run.
|
||||
|
||||
.. note::
|
||||
Due to limitations of the ``pip`` installer, dependencies of the dependencies will not be
|
||||
removed, only the ones the setup script directly installed.
|
||||
|
||||
**Alternative B: Manual Uninstallation**
|
||||
|
||||
Like for the install process, the script just runs two commands. You can of course run them
|
||||
yourself if you wish. They are:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python setup.py win-uninstall
|
||||
pip uninstall -r requirements.txt
|
||||
|
||||
There may be other packages on your system installed by ``pip``. To list all packages, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip freeze --user
|
||||
@@ -7,8 +7,8 @@ Technical Information
|
||||
This section contains details of how novelWriter stores and handles the project data.
|
||||
|
||||
|
||||
How Data is Stored
|
||||
==================
|
||||
How Project Data is Stored
|
||||
==========================
|
||||
|
||||
All novelWriter files are written with utf-8 encoding. Since Python automatically converts Unix
|
||||
line endings to Windows line endings on Windows systems, novelWriter does not make any adaptations
|
||||
@@ -20,13 +20,14 @@ operating systems.
|
||||
Main Project File
|
||||
-----------------
|
||||
|
||||
The project itself requires a dedicated folder for storing its files, where novelWriter will create
|
||||
The project itself requires a dedicated folder for storing its files where novelWriter will create
|
||||
its own "file system" where the folder and file hierarchy is described in a project XML file. This
|
||||
is the main project file in the project's root folder with the name ``nwProject.nwx``. This file
|
||||
also contains all the meta data required for the project, and a number of related project settings.
|
||||
|
||||
If this file is lost or corrupted, the structure of the project is lost. It is important to keep
|
||||
this file backed up, either through the built-in backup tool, or your own backup solution.
|
||||
If this file is lost or corrupted, the structure of the project is lost, although not the text
|
||||
itself. It is important to keep this file backed up, either through the built-in backup tool, or
|
||||
your own backup solution.
|
||||
|
||||
.. tip::
|
||||
The novelWriter project folder is structured so that it can easily be added to a version control
|
||||
@@ -36,27 +37,26 @@ this file backed up, either through the built-in backup tool, or your own backup
|
||||
|
||||
The project XML file is indent-formatted, suitable for diff tools and version control since most of
|
||||
the file will stay static, although a timesetamp is set in the meta section on line 2 each time the
|
||||
file is saved.
|
||||
file is saved, and various meta data entries are incremented on each save.
|
||||
|
||||
|
||||
Project Documents
|
||||
-----------------
|
||||
|
||||
The project documents are saved in a folder in the main project folder named ``content``. Each
|
||||
All the project documents are saved in a folder in the main project folder named ``content``. Each
|
||||
document has a file handle taken from the first 13 characters of a SHA256 hash of the system time
|
||||
when the document was first created. The documents are saved with a filename assembled from this
|
||||
hash and the file extension ``.nwd``.
|
||||
|
||||
If you wish to find the physical location of a document in the project, you can either look it up
|
||||
in the project XML file, select :guilabel:`Show File Details` from the :guilabel:`Document` menu
|
||||
If you wish to find the file system location of a document in the project, you can either look it
|
||||
up in the project XML file, select :guilabel:`Show File Details` from the :guilabel:`Document` menu
|
||||
when having the document open, or look in the ``ToC.txt`` file in the root of the project folder.
|
||||
The ``ToC.txt`` file has a list of all document files in the project and where they are saved.
|
||||
The ``ToC.txt`` file has a list of all documents in the project, referenced by their label, and
|
||||
where they are saved.
|
||||
|
||||
The reason for this cryptic file naming is to avoid issues with file naming conventions and
|
||||
restrictions on different operating systems, and also to have a file name that does not depend on
|
||||
what the user names the document within the project, or changes it to. The file meta data in the
|
||||
project tree is mainly saved in the project XML file, although some basic meta data is added to the
|
||||
header of each document file.
|
||||
what the user names the document within the project, or changes it to.
|
||||
|
||||
Each document file contains a plain text version of the text from the editor. The file can in
|
||||
principle be edited in any text editor, and is suitable for diffing and version control if so
|
||||
@@ -83,3 +83,63 @@ For the project XML file, a ``.bak`` file is in addition kept, which will always
|
||||
previous version of the file, although when auto-save is enabled, they may have the same content.
|
||||
If the opening of a project file fails, novelWriter will automatically try to open the ``.bak``
|
||||
file instead.
|
||||
|
||||
|
||||
Project Meta Data
|
||||
=================
|
||||
|
||||
The project folder contains a subfolder named ``meta``, containing a number of files. The meta
|
||||
folder contains semi-important files. That is, they can be lost with only minor impact to the
|
||||
project.
|
||||
|
||||
If you use version control software on your project, you can exclude this folder, although you may
|
||||
want to track the session log file. The JSON files within this folder can safely be ignored as they
|
||||
will be automatically regenerated if lost.
|
||||
|
||||
|
||||
The Project Index
|
||||
-----------------
|
||||
|
||||
Between writing sessions, the project index is saved in a JSON file in ``meta/tagsIndex.json``.
|
||||
This file is not critical. If it is lost, it can be rebuilt from within novelWriter from the
|
||||
:guilabel:`Tools` menu.
|
||||
|
||||
The index is maintained and updated whenever a document or note is saved in the editor. It contains
|
||||
all references and tags in documents and notes, as well as the location of all headers in the
|
||||
project, and the word counts within each header section.
|
||||
|
||||
The integrity of the index is checked when the file is loaded. It is possible to corrupt the index
|
||||
if the file is manually edited and manipulated, so the check is important to avoid sudden crashes
|
||||
of novelWriter. if the file contains errors, novelWriter will automatically build it anew. If the
|
||||
check somehow fails and novelWriter keeps crashing, you can delete the file manually and rebuild
|
||||
the index. If this too fails, you have likely encountered a bug.
|
||||
|
||||
|
||||
Cached GUI Options
|
||||
------------------
|
||||
|
||||
A file named ``meta/guiOptions.json`` contains the latest state of various GUI buttons, switches,
|
||||
dialog window sizes, column sizes, etc, from the GUI. These are the GUI settings that are specific
|
||||
to the project. Global GUI settings are stored in the main config file.
|
||||
|
||||
The file is not critical, but if it is lost, all such GUI options will revert back to their default
|
||||
settings.
|
||||
|
||||
|
||||
Session Stats
|
||||
-------------
|
||||
|
||||
The writing progress is saved in the ``meta/sessionStats.log`` file. This file records the length
|
||||
and word counts of each writing session on the given project. The file is used by the
|
||||
:guilabel:`Writing Statistics` tool. If this file is lost, the history it contains is also lost,
|
||||
but it has otherwise no impact on the project.
|
||||
|
||||
|
||||
Project Cache
|
||||
=============
|
||||
|
||||
The project ``cache`` folder contains non-critical files. If these files are lost, there is no
|
||||
impact on the functionality of novelWriter or the history of the project. It contains temporary
|
||||
files, like the preview document in the :guilabel:`Build Novel Project` tool.
|
||||
|
||||
It should be excluded from version control tools if such are used.
|
||||
|
||||
@@ -14,7 +14,7 @@ Header Formatting
|
||||
=================
|
||||
|
||||
The titles for the five types of titles (the chapter headings come in a numbered and unnumbered
|
||||
version) of story structure can be formatted collectively in the export tool. This is done through
|
||||
version) of story structure can be formatted collectively in the build tool. This is done through
|
||||
a series of keyword–replace steps. They are all on the format ``%keyword%``.
|
||||
|
||||
``%title%``
|
||||
@@ -22,20 +22,20 @@ a series of keyword–replace steps. They are all on the format ``%keyword%``.
|
||||
your document.
|
||||
|
||||
``%ch%``
|
||||
This is replaced by a chapter number. The number is incremented by one each time the build tool
|
||||
sees a new heading of level two in a document with layout :guilabel:`Chapter`. If the document
|
||||
has layout :guilabel:`Unnumbered`, the counter is *not* incremented. The latter is useful for
|
||||
for instance Prologue and Epilogue chapters.
|
||||
This will be replaced by a chapter number. The number is incremented by one each time the build
|
||||
tool sees a new heading of level two in a document with layout :guilabel:`Chapter`. If the
|
||||
document has layout :guilabel:`Unnumbered`, the counter is *not* incremented. The latter is
|
||||
useful for for instance Prologue and Epilogue chapters. Adding an asterisk (``*``) in front of
|
||||
the title text of a level two heading will also disable the chapter counter for that heading.
|
||||
|
||||
``%chw%``
|
||||
This is like ``%ch%``, but the number is expressed as a word like for instance "One", "Two",
|
||||
etc.
|
||||
Behaves like ``%ch%``, but the number is represented as a number word.
|
||||
|
||||
``%chi%``
|
||||
This is also like ``%ch%``, but the number is represented as a lower case Roman number.
|
||||
Begaves like ``%ch%``, but the number is represented as a lower case Roman number.
|
||||
|
||||
``%chI%``
|
||||
This is also like ``%ch%``, but the number is represented as an upper case Roman number.
|
||||
Behaves like ``%ch%``, but the number is represented as an upper case Roman number.
|
||||
|
||||
``%sc%``
|
||||
This is the number counter equivalent for scenes. These are incremented each time a heading of
|
||||
@@ -43,8 +43,8 @@ a series of keyword–replace steps. They are all on the format ``%keyword%``.
|
||||
used for counting scenes within a chapter.
|
||||
|
||||
``%sca%``
|
||||
This is like ``%sc%``, but the number is *not* reset to 1 for each chapter. Instead it runs from
|
||||
1 from the beginning of the novel.
|
||||
Behaves like ``%sc%``, but the number is *not* reset to 1 for each chapter. Instead it runs from
|
||||
1 from the beginning of the novel to produce an absolute scene count.
|
||||
|
||||
``\\``
|
||||
This inserts a line break within the title.
|
||||
@@ -67,11 +67,12 @@ Scene Separators
|
||||
================
|
||||
|
||||
If you don't want any titles for your scenes (or for your sections if you have them), you can leave
|
||||
the boxes empty. If so, an empty paragraph will be inserted between the scenes or sections instead.
|
||||
the formatting boxes empty. If so, an empty paragraph will be inserted between the scenes or
|
||||
sections instead.
|
||||
|
||||
Alternatively, if you want a separator between them, like the common ``* * *``, you can also enter
|
||||
that in the box. In fact, if the format is a piece of static text, it will always be treated as a
|
||||
separator.
|
||||
Alternatively, if you want a separator between them, like the common ``* * *``, you can enter the
|
||||
desired separator text in the formatting box. In fact, if the format is a piece of static text, it
|
||||
will always be treated as a separator.
|
||||
|
||||
|
||||
.. _a_export_files:
|
||||
@@ -82,8 +83,8 @@ File Selection
|
||||
Which documents and notes are selected for export can be controlled from the options on the left
|
||||
side of the dialog window. The switch for :guilabel:`Include novel files` will select any document
|
||||
that isn't classified as a note. The switch for :guilabel:`Include note files` will select any
|
||||
document that *is* a note. This is allows for exporting just the novel, just your notes, or both,
|
||||
as you see fit.
|
||||
document that *is* a note. This allows for exporting just the novel, just your notes, or both, as
|
||||
you see fit.
|
||||
|
||||
In addition, you can select to export the synopsis comments, regular comments, keywords, and even
|
||||
exclude the body text itself.
|
||||
@@ -101,6 +102,16 @@ document has a checkmark after the status icon in the :guilabel:`Flags` column.
|
||||
override these settings.
|
||||
|
||||
|
||||
.. _a_export_print:
|
||||
|
||||
Printing
|
||||
========
|
||||
|
||||
The print button allows you to print the content in the preview window. You can either print to one
|
||||
of your system's printers, or print directly to file. You can also print to file from the regular
|
||||
print dialog. The direct to file option is just a shortcut.
|
||||
|
||||
|
||||
.. _a_export_formats:
|
||||
|
||||
Export Formats
|
||||
@@ -108,35 +119,25 @@ Export Formats
|
||||
|
||||
Currently, six formats are supported for exporting.
|
||||
|
||||
OpenDocument Format
|
||||
This produces an open document ``.odt`` file. The document produced has very little formatting,
|
||||
and may require further editing afterwards. For a better formatted office document, you may get
|
||||
a better result with exporting to HTML and then import that HTML document into your office word
|
||||
processor. They are generally very good at importing HTML documents.
|
||||
|
||||
PDF Format
|
||||
The PDF export is just a shortcut for print to file. For a better PDF result, you may instead
|
||||
want to export to HTML, and use a word processor to convert the HTML document to PDF.
|
||||
Open Document Format
|
||||
The Build tool can produce either an ``.odt`` file, or an ``.fodt`` file. The latter is just a
|
||||
flat version of the document format as a single XML file.
|
||||
|
||||
novelWriter HTML
|
||||
The HTML export format writes a single ``.htm`` file with minimal style formatting. The exported
|
||||
HTML document is suitable for further processing by document conversion tools like Pandoc, for
|
||||
importing in word processors, or for printing from browser. It is generally the best formatted
|
||||
export option and supports all features of novelWriter since it is entirely geenrated by the
|
||||
application and doesn't depend on Qt library features.
|
||||
importing in word processors, or for printing from browser.
|
||||
|
||||
novelWriter Markdown
|
||||
This is simply a concatenation of the project documents selected by the filters. The documents
|
||||
are stacked together in the order they appear in the project tree, with comments, tags, etc.
|
||||
are stacked together in the order they appear in the project tree, with comments, tags, etc.
|
||||
included if they are selected. This is a useful format for exporting the project for later
|
||||
import back into novelWriter.
|
||||
|
||||
Standard Markdown
|
||||
If you have Qt 5.14 or higher, the option to export to plain markdown is available. This feature
|
||||
uses Qt's own markdown export feature.
|
||||
|
||||
Plain Text
|
||||
The plain text export format writes a simple ``.txt`` file without any formatting at all.
|
||||
Standard/GitHub Markdown
|
||||
The Markdown export format comes in both Standard and GitHub flavour. The *only* difference in
|
||||
terms of novelWriter functionality is the support of strikethrough text, which is not supported
|
||||
by the Standard flavour, but *is* supported by the GitHub flavour.
|
||||
|
||||
|
||||
.. _a_export_options:
|
||||
@@ -146,11 +147,11 @@ Additional Export Options
|
||||
|
||||
In addition to the above document formats, the novelWriter HTML and Markdown formats can also be
|
||||
wrapped in a JSON file. These files will have a meta data entry and a body entry. For HTML, also
|
||||
accompanying css styles are exported.
|
||||
the accompanying css styles are exported.
|
||||
|
||||
The text body is saved in a two-level list. The outer list contains one entry per exported file, in
|
||||
the order they appear in the project tree. Each file is then split up into a list as well, with one
|
||||
entry per paragraph in the document.
|
||||
The text body is saved in a two-level list. The outer list contains one entry per exported
|
||||
document, in the order they appear in the project tree. Each document is then split up into a list
|
||||
as well, with one entry per paragraph it contains.
|
||||
|
||||
These files are mainly intended for scripted post-processing for those who want that option. A JSON
|
||||
file can be imported directly into a Python dict object or a PHP array, to mentions a few options.
|
||||
437
docs/source/usage_interface.rst
Normal file
@@ -0,0 +1,437 @@
|
||||
.. _a_ui:
|
||||
|
||||
**************
|
||||
User Interface
|
||||
**************
|
||||
|
||||
The user interface is kept as simple as possible to avoid distractions when writing. This page
|
||||
lists all the main GUI elements, and explains what they do.
|
||||
|
||||
|
||||
.. _a_ui_tree:
|
||||
|
||||
The Project Tree
|
||||
================
|
||||
|
||||
The main window contains a project tree in the left-most panel. It shows the entire structure of
|
||||
the project. It has four columns:
|
||||
|
||||
:guilabel:`Label`
|
||||
The first column shows the item icon and its label. The labels can be edited from the
|
||||
:guilabel:`Project` menu, or by pressing :kbd:`F2` or :kbd:`Ctrl`:kbd:`E`. The label is not the
|
||||
same as the title you set inside the document, but it will appear in the header above the
|
||||
document text itself.
|
||||
|
||||
:guilabel:`Words`
|
||||
The second column shows the word count of the document, or the sum of words of the child items
|
||||
if it is a folder. If the counts seem incorrect, they can be updated by rebuilding the project
|
||||
index from the :guilabel:`Tools` menu, or by pressing :kbd:`F9`.
|
||||
|
||||
:guilabel:`Inc`
|
||||
The third column indicates whether the document is included in the final project build or not.
|
||||
You may want to filter out documents that you no longer want to keep in the final manuscript,
|
||||
but want to keep in the project for reference.
|
||||
|
||||
:guilabel:`Flags`
|
||||
The fourth column shows various meta data flags for the item. The first is an icon indicating
|
||||
the importance or status of the document. These are colour coded status levels that you control
|
||||
and define yourself. They can be changed in :guilabel:`Project Settings` from the
|
||||
:guilabel:`Project` menu. The first character after the icon indicates the class of the item,
|
||||
that is ``N`` for **Novel**, ``C`` for **Character**, etc (see :ref:`a_struct_tags`). The
|
||||
characters after the dot indicate the document layout type (see :ref:`a_proj_roots`).
|
||||
|
||||
Right-clicking an item in the project tree will open a context menu under the cursor, displaying
|
||||
a selection of actions that can be performed on the selected item.
|
||||
|
||||
Below the project tree you will find a small details panel showing the full information of the
|
||||
currently selected item. This panel also includes the latest paragraph and character counts in
|
||||
addition to the word count.
|
||||
|
||||
|
||||
.. _a_ui_tree_novel:
|
||||
|
||||
The Novel Tree
|
||||
--------------
|
||||
|
||||
An alternative way to view the project structure is the novel tree. You can switch to this view by
|
||||
selecting the :guilabel:`Novel` tab under the project tree. This view is a simplified version of
|
||||
the view in the :guilabel:`Outline`. It is convenient when you want to browse the structure of the
|
||||
story itself rather than the document files.
|
||||
|
||||
.. note::
|
||||
You cannot reorganise the entries in the novel tree as that would imply restructuring the
|
||||
content of the document files.
|
||||
|
||||
|
||||
.. _a_ui_tree_status:
|
||||
|
||||
Document Importance and Status
|
||||
------------------------------
|
||||
|
||||
Each document or folder in your project can have either a "Status" or "Importance" flag set. These
|
||||
are flags that you control and define in their respective tabs in :guilabel:`Project Settings`. The
|
||||
"Status" flag is intended to tag a Novel document as for instance a draft or as completed, and the
|
||||
"Importance" flag is intended to tag character notes, or other notes, as for instance a main, major
|
||||
or minor character.
|
||||
|
||||
Whether a document uses a "Status" or "Importance" flag depends on which root folder it lives in.
|
||||
If it's in the :guilabel:`Novel` folder, it uses the "Status" flag, otherwise it uses an
|
||||
"Importance" flag. Some folders, like :guilabel:`Trash` and :guilabel:`Outtakes` allow both.
|
||||
|
||||
|
||||
.. _a_ui_tree_dnd:
|
||||
|
||||
Project Tree Drag & Drop
|
||||
------------------------
|
||||
|
||||
The project tree allows drag & drop to a certain extent. This feature is primarily intended for
|
||||
rearranging the order of your documents within each root folder, and has only limited support for
|
||||
moving documents elsewhere in the project tree. In general, bulk actions are not allowed. This is
|
||||
deliberate to avoid accidentally messing up your project. If you make a mistake, the last move
|
||||
action can be undone by pressing :kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`.
|
||||
|
||||
Documents and their folders can be rearranged freely within their root folders. Novel documents
|
||||
cannot be moved out of the :guilabel:`Novel` folder, except to :guilabel:`Trash` and the
|
||||
:guilabel:`Outtakes` folders. Notes can be moved freely between all root folders, but keep in mind
|
||||
that if you move a note into a :guilabel:`Novel`, its "Importance" setting will be reset to the
|
||||
default "Status" setting. See :ref:`a_ui_tree_status`.
|
||||
|
||||
Folders cannot be moved at all outside their root tree. Neither can a folder containing documents
|
||||
be deleted. You must first delete the containing documents.
|
||||
|
||||
Root folders in the project tree cannot be dragged and dropped at all. If you want to reorder them,
|
||||
you can move them up or down with respect to eachother from the :guilabel:`Project` menu, the
|
||||
right-click context menu, or by pressing :kbd:`Ctrl`:kbd:`Shift` and the :kbd:`Up` or :kbd:`Down`
|
||||
key.
|
||||
|
||||
|
||||
.. _a_ui_edit:
|
||||
|
||||
Editing and Viewing Documents
|
||||
=============================
|
||||
|
||||
To edit a document, double-click it in the project tree, or press the :kbd:`Return` key while
|
||||
having it selected. This will open the document in the document editor. The editor uses a
|
||||
simplified markdown format. The format is described in the :ref:`a_ui_md` section below. The editor
|
||||
has a maximise button (toggles the :guilabel:`Focus Mode`) and a close button in the top-right
|
||||
corner.
|
||||
|
||||
Any document in the project tree can also be viewed in parallel in a right hand side document
|
||||
viewer. To view a document, press :kbd:`Ctrl`:kbd:`R`, or select :guilabel:`View Document` in the
|
||||
menu. If you have a middle mouse button, middle-clicking on the document will also open it in the
|
||||
viewer. The document viewed does not have to be the same document currently being edited. However,
|
||||
If you *are* viewing the same document, pressing :kbd:`Ctrl`:kbd:`R` again will update the document
|
||||
with your latest changes. You can also press the reload button in the top-right corner of the view
|
||||
panel next to the close button to achieve the same thing.
|
||||
|
||||
Both the document editor and viewer will show the label of the document in the header at the top of
|
||||
the edit or view panel. Optionally, the full project path to the document can be shown. This can be
|
||||
set in :guilabel:`Preferences` from the :guilabel:`Tools` menu. Clicking on the document title bar
|
||||
will select and reveal its location in the project tree, making it easier to locate in a large
|
||||
project.
|
||||
|
||||
Any tag reference in the editor can be opened in the viewer by moving the cursor to the label and
|
||||
pressing :kbd:`Ctrl`:kbd:`Return`. You can also control-click them with your mouse. In the viewer,
|
||||
the references become clickable links. Clicking them will replace the content of the viewer with
|
||||
the content of the document the reference points to. The document viewer keeps a history of viewed
|
||||
documents, which you can navigate with the arrow buttons in the top-left corner of the viewer. If
|
||||
your mouse has back and forward navigation buttons, these can be used as well. They work just like
|
||||
the backward and forward features in a browser.
|
||||
|
||||
At the bottom of the view panel there is a :guilabel:`References` panel. (If it is hidden, click
|
||||
the icon to reveal it.) This panel will show links to all documents referring back to the one
|
||||
you're currently viewing, if any has been defined. The :guilabel:`Sticky` button will freeze the
|
||||
content of the panel to the current document, even if you navigate to another document. This is
|
||||
convenient if you want to quickly look through all documents in the list in the
|
||||
:guilabel:`References` panel without losing the list in the process.
|
||||
|
||||
.. note::
|
||||
The :guilabel:`References` panel relies on an up-to-date index of the project. The index is
|
||||
maintained automatically. However, if anything is missing, or seems wrong, the index can always
|
||||
be rebuilt by selecting :guilabel:`Rebuild Index` from the :guilabel:`Tools` menu, or by
|
||||
pressing :kbd:`F9`.
|
||||
|
||||
|
||||
.. _a_ui_edit_search:
|
||||
|
||||
Search & Replace
|
||||
----------------
|
||||
|
||||
The document editor has a search and replace bar that can be activated with :kbd:`Ctrl`:kbd:`F` for
|
||||
search mode or :kbd:`Ctrl`:kbd:`H` for search/replace mode.
|
||||
|
||||
Pressing :kbd:`Return` while in the search box will search for the next occurrence of the word, and
|
||||
:kbd:`Shift`:kbd:`Return` for the previous. Pressing :kbd:`Return` in the replace box, will replace
|
||||
the highlighted text and move to the next word.
|
||||
|
||||
There are a number of settings for the search bar available as toggle switches above the search
|
||||
box. They allows you to search for, in order:,: matched case only, whole word results only, search
|
||||
using regular expressions, loop search when reaching the end of the document, and move to the next
|
||||
document when reaching the end. There is also a switch that will try to match the case of the word
|
||||
when the replacement is made. That is, it will try to keep the word upper, lower, or capitalised to
|
||||
match the word being replaced.
|
||||
|
||||
The regular expression search is somewhat dependant on which version of Qt your system has. If you
|
||||
have Qt 5.13 or higher, there is better support for unicode symbols in the search.
|
||||
|
||||
|
||||
.. _a_ui_edit_auto:
|
||||
|
||||
Auto-Replace as You Type
|
||||
========================
|
||||
|
||||
A few auto-replace features are supported by the editor. You can control every aspect of the
|
||||
auto-replace feature from :guilabel:`Preferences`. You can also disable this feature entirely if
|
||||
you wish.
|
||||
|
||||
.. tip::
|
||||
If you don't like auto-replacement, all symbols inserted by this feature are also available in
|
||||
the :guilabel:`Insert` menu, and via convenient :ref:`a_kb_ins`. You may also be using a
|
||||
`Compose Key`_ setup, which means you may not need the auto-replace feature.
|
||||
|
||||
.. _Compose Key: https://en.wikipedia.org/wiki/Compose_key
|
||||
|
||||
The editor is able to replace two and three hyphens with short and long dashes, triple points with
|
||||
ellipsis, and replace straight single and double quotes with user-defined quote symbols. It will
|
||||
also try to determine whether to use the opening or closing symbol, although this feature isn't
|
||||
always accurate. Especially distinguishing between closing single quote and apostrophe can be
|
||||
tricky for languages that use the same symbol for these.
|
||||
|
||||
.. tip::
|
||||
If the auto-replace feature changes a symbol when you did not want it to change, pressing
|
||||
:kbd:`Ctrl`:kbd:`Z` immediately after the auto-replacement will undo it without undoing the
|
||||
character you typed.
|
||||
|
||||
|
||||
.. _a_ui_md:
|
||||
|
||||
The Markdown-Like Format
|
||||
========================
|
||||
|
||||
The editor itself is a plaintext editor that uses formatting codes for setting meta data values and
|
||||
allowing for some text formatting. The syntax is based on Markdown, but novelWriter is *not* a
|
||||
Markdown editor. It supports basic formatting like emphasis (italic), strong importance (bold)
|
||||
and strikethrough text, as well as four levels of headings.
|
||||
|
||||
In addition to formatting codes, novelWriter allows for comments, a synopsis tag, and a set of
|
||||
keyword and value sets used for tags and references.
|
||||
|
||||
|
||||
.. _a_ui_md_head:
|
||||
|
||||
Headings
|
||||
--------
|
||||
|
||||
Four levels of headings are allowed. For documents of layout ``Note``, they are free to be used as
|
||||
you see fit, but for all other layouts used for the novel text itself, they indicate the structural
|
||||
level of the novel. See :ref:`a_struct_heads` for more details.
|
||||
|
||||
``# Title``
|
||||
Heading level one. If the document is a novel file, the header level indicates the start of a
|
||||
new partition. This heading level can also be used for the title page's novel title.
|
||||
|
||||
``## Title``
|
||||
Heading level two. If the document is a novel file, the header level indicates the start of a
|
||||
new chapter. Chapter numbers can be inserted automatically when exporting the manuscript.
|
||||
|
||||
``### Title``
|
||||
Heading level three. If the document is a novel file, the header level indicates the start of a
|
||||
new scene. Scene numbers or scene separators can be inserted automatically when exporting the
|
||||
manuscript, so you can use the title field as a working title for your scenes if you wish.
|
||||
|
||||
``#### Title``
|
||||
Heading level four. If the document is a novel file, the header level indicates the start of a
|
||||
new section. Section titles can be replaced by separators or removed when exporting the
|
||||
manuscript, so you can use the title field as a working title for your sections if you wish.
|
||||
|
||||
.. note::
|
||||
The space after the ``#`` characters is mandatory. The syntax highlighter will change colour and
|
||||
font size when the heading is correctly formatted.
|
||||
|
||||
.. tip::
|
||||
If you do use the automatic numbering feature for exports, you can tell the export tool to skip
|
||||
assigning a number to a specific chapter by adding a ``*`` as the first character of the title
|
||||
itself. See :ref:`a_struct_heads_unnum` for more details.
|
||||
|
||||
|
||||
.. _a_ui_md_text:
|
||||
|
||||
Text Paragraphs
|
||||
---------------
|
||||
|
||||
A text paragraph is indicated by a blank line. That is, you need two line breaks to separate two
|
||||
fragments of text into two paragraphs. Single line breaks are treated as line breaks within a
|
||||
paragraph.
|
||||
|
||||
In addition, the editor supports a few additional types of whitespaces.
|
||||
|
||||
* A non-breaking space can be inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Space`.
|
||||
* Thin spaces are also supported, and can be inserted with :kbd:`Ctrl`:kbd:`K`,
|
||||
:kbd:`Shift`:kbd:`Space`.
|
||||
* Non-breaking thin space can be inserted with :kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`.
|
||||
|
||||
These are all insert features, and the :guilabel:`Insert` menu has more. They are also listed
|
||||
in :ref:`a_kb_ins`.
|
||||
|
||||
Non-breaking spaces are highlighted by the syntax highlighter with an alternate coloured
|
||||
background, depending on the selected theme.
|
||||
|
||||
.. tip::
|
||||
Non-breaking spaces are the correct type of space to separate a number from its unit. Generally,
|
||||
it prevents the line wrapping algorithms from adding line breaks where it shouldn't.
|
||||
|
||||
|
||||
.. _a_ui_md_align:
|
||||
|
||||
Paragraphs Alignment and Indentation
|
||||
------------------------------------
|
||||
|
||||
Aside from Title Page and Partition which by default have its text centred, all document layouts
|
||||
have the text by default aligned to the left ot justified, depending on your Preferences.
|
||||
|
||||
You can override the default text alignment on individual paragraphs by specifying alignment tags.
|
||||
These tags are double angle brackets. Either ``>>`` or ``<<``. You put them either before or after
|
||||
the paragraph, and they will "push" the text towards the edge the brackets point towards. This
|
||||
should be fairly intuitive.
|
||||
|
||||
Indentation uses a similar syntax. But here you use a single ``>`` or ``<`` to push the text away
|
||||
from the edge.
|
||||
|
||||
Examples:
|
||||
|
||||
.. csv-table:: Text Alignment and Indentation
|
||||
:header: "Syntax", "Description"
|
||||
:widths: 40, 60
|
||||
:class: "tight-table"
|
||||
|
||||
"``>> Right aligned text``", "The text paragraph is right-aligned."
|
||||
"``Left aligned text <<``", "The text paragraph is left-aligned."
|
||||
"``>> Centred text <<``", "The text paragraph is centred."
|
||||
"``> Indented text``", "The text has an increased left margin."
|
||||
"``Indented text <``", "The text has an increased right margin."
|
||||
"``> Indented text <``", "The text has an both margins increased."
|
||||
|
||||
.. note::
|
||||
The text editor will not show the alignment and indentation live. But the viewer will show them
|
||||
when you open the document in the viewer. It will of course also be reflected in the document
|
||||
generated from the build tool.
|
||||
|
||||
|
||||
.. _a_ui_md_emph:
|
||||
|
||||
Text Emphasis
|
||||
-------------
|
||||
|
||||
A minimal set of text emphasis styles are supported.
|
||||
|
||||
``_text_``
|
||||
The text is rendered as emphasised text (italicised).
|
||||
|
||||
``**text**``
|
||||
The text is rendered as strongly important text (bold).
|
||||
|
||||
``~~text~~``
|
||||
Strikethrough text.
|
||||
|
||||
In markdown guides it is often recommended to differentiate between strong importance and emphasis
|
||||
by using ``**`` for strong and ``_`` for emphasis, although markdown generally also supports ``__``
|
||||
for strong and ``*`` for emphasis. However, since the differentiation makes the highlighting and
|
||||
conversion significantly simpler and faster, in novelWriter this is a rule, not just a
|
||||
recommendation.
|
||||
|
||||
In addition, the following rules apply:
|
||||
|
||||
1. The emphasis and strikethrough formatting tags do not allow spaces between the words and the tag
|
||||
itself. That is, ``**text**`` is valid, ``**text **`` is not.
|
||||
2. More generally, the delimiters must be on the outer edge of words. That is, ``some **text in
|
||||
bold** here`` is valid, ``some** text in bold** here`` is not.
|
||||
3. If using both ``**`` and ``_`` to wrap the same text, the underscore must be the inner wrapper.
|
||||
This is due to the underscore also being a valid word character, so if they are on the outside,
|
||||
they violate rule 2.
|
||||
4. Text emphasis does not span past line breaks. If you need to add emphasis to multiple lines or
|
||||
paragraphs, you must apply it to each of them in turn.
|
||||
|
||||
|
||||
.. _a_ui_md_comm:
|
||||
|
||||
Comments and Synopsis
|
||||
---------------------
|
||||
|
||||
In addition to these standard markdown features, novelWriter also allows for comments in documents.
|
||||
The text of a comment is ignored by the word counter. The text can also be filtered out when
|
||||
exporting or viewing the document.
|
||||
|
||||
If the first word of a comment is ``Synopsis:`` (with the colon included), the comment is treated
|
||||
specially and will show up in the :ref:`a_ui_outline` in a dedicated column. The word ``synopsis``
|
||||
is not case sensitive. If it is correctly formatted, the syntax highlighter will indicate this by
|
||||
altering the colour of the word.
|
||||
|
||||
``% text...``
|
||||
This is a comment. The text is not exported by default (this can be overridden), seen in the
|
||||
document viewer, or counted towards word counts.
|
||||
|
||||
``% Synopsis: text...``
|
||||
This is a synopsis comment. It is generally treated in the same way as a regular comment, except
|
||||
that it is also captured by the indexing algorithm and displayed in the :ref:`a_ui_outline`. It
|
||||
can also be filtered separately when exporting the project to for instance generate an outline
|
||||
document of the whole project.
|
||||
|
||||
.. note::
|
||||
Only one comment can be flagged as a synopsis comment for each heading. If multiple comments are
|
||||
flagged as synopsis comments, the last one will be used and the rest ignored.
|
||||
|
||||
|
||||
.. _a_ui_md_tags:
|
||||
|
||||
Tags and References
|
||||
-------------------
|
||||
|
||||
The document editor supports a minimal set of keywords used for setting tags, and making references
|
||||
between documents. The tags and references can be set once per section defined by a heading. Using
|
||||
them multiple times under the same heading will just override the previous setting.
|
||||
|
||||
``@keyword: value``
|
||||
A keyword argument followed by a value, or a comma separated list of values.
|
||||
|
||||
The available tag and reference keywords are listed in the :ref:`a_struct_tags` section. They can
|
||||
also be inserted at the cursor position in the editor via the :guilabel:`Insert` menu.
|
||||
|
||||
|
||||
.. _a_ui_outline:
|
||||
|
||||
Project Outline View
|
||||
====================
|
||||
|
||||
The project's Outline view is available as the second tab on the right hand side of the main window
|
||||
labelled :guilabel:`Outline`. The outline provides an overview of the novel structure, displaying a
|
||||
tree hierarchy of the elements of the novel, that is, the level 1 to 4 headings representing
|
||||
partitions, chapters, scenes and sections.
|
||||
|
||||
The document containing the heading can also be displayed as a separate column, as well as the line
|
||||
number where it occurs. Double-clicking an entry will open the corresponding document in the
|
||||
editor.
|
||||
|
||||
.. note::
|
||||
Since the internal structure of the novel does not depend directly on the folder and document
|
||||
structure of the project tree, these will not necessarily look the same, depending on how you
|
||||
choose to organise your documents. See the :ref:`a_struct` page for more details.
|
||||
|
||||
Various meta data and information extracted from tags can be displayed in columns in the outline.
|
||||
A default set of such columns is visible, but you can turn on or off more columns by right clicking
|
||||
the header and selecting the columns you want to show. The order of the columns can also be
|
||||
rearranged by dragging them to a different position.
|
||||
|
||||
.. note::
|
||||
The :guilabel:`Title` column cannot be disabled or moved.
|
||||
|
||||
The information viewed in the outline is based on the project's main index. While novelWriter does
|
||||
its best to keep the index up to date when contents change, you can always rebuild it manually by
|
||||
pressing :kbd:`F9` if something isn't right.
|
||||
|
||||
The outline view itself can be regenerated by pressing :kbd:`F10`. You can also enable automatic
|
||||
updating in the :guilabel:`Tools` menu, which will trigger an update whenever the index is updated
|
||||
and the :guilabel:`Outline` tab is active. You may want to disable this feature if your project is
|
||||
very large,
|
||||
|
||||
The :guilabel:`Synopsis` column of the outline view takes its information from a specially
|
||||
formatted comment. See :ref:`a_ui_md_comm`.
|
||||
@@ -4,16 +4,18 @@
|
||||
Project Notes
|
||||
*************
|
||||
|
||||
novelWriter doesn't have a database and complicated forms to fill in all details about plot
|
||||
novelWriter doesn't have a database and complicated forms for filling in details about plot
|
||||
elements, characters, and all sorts of additional information that isn't a part of the novel text
|
||||
itself. Instead, all such information is saved in notes. The relation between all these additional
|
||||
elements is extracted from these documents by the project indexer based on the tags and references
|
||||
you set.
|
||||
itself. Instead, all such information is saved in notes that are written and maintained just like
|
||||
all other text in your project.
|
||||
|
||||
These notes are not required, but making at least minimal files for each such plot element, and
|
||||
adding a tag to them, makes it possible to use the :guilabel:`Outline` feature to see how each
|
||||
element intersects with each section of the novel itself, and adds clickable cross-references
|
||||
between documents in the editor and viewer.
|
||||
The relation between all these additional elements is extracted from the documents and notes by the
|
||||
project indexer, based on the tags and references you set within them.
|
||||
|
||||
Using notes is not required, but making at least minimal notes for each plot element, and adding a
|
||||
tag to them, makes it possible to use the :guilabel:`Outline` feature to see how each element
|
||||
intersects with each section of the novel itself, and adds clickable cross-references between
|
||||
documents in the editor and viewer.
|
||||
|
||||
|
||||
.. _a_notes_tags:
|
||||
@@ -24,7 +26,8 @@ Tags in Notes
|
||||
Each new heading in a note can have a tag associated with it. The format of a tag is
|
||||
``@tag: tagname``, where tagname is a unique identifier. Tags can then be referenced in the novel
|
||||
documents, or cross-referenced in other notes, and will show up in the outline view and in the
|
||||
back-reference panel when a document is being viewed.
|
||||
back-reference panel when a document is being viewed. See :ref:`a_struct_tags` for how to reference
|
||||
notes.
|
||||
|
||||
The syntax highlighter will alert the user that the keyword is correctly used and that the tag is
|
||||
allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up
|
||||
@@ -43,5 +46,5 @@ meaningful if you want to be able to click-navigate between them.
|
||||
|
||||
.. tip::
|
||||
If you cross-reference between notes and export your project as an HTML document using the
|
||||
:guilabel:`Build Novel Project` tool, the cross-references become clickable in the exported
|
||||
HTML document as well.
|
||||
:guilabel:`Build Novel Project` tool, the cross-references become clickable links in the
|
||||
exported HTML document.
|
||||
@@ -4,18 +4,20 @@
|
||||
Novel Projects
|
||||
**************
|
||||
|
||||
A novelWriter project requires a dedicated folder for storing its files on the local file system.
|
||||
See the :ref:`a_tech` page for further details on how files are organised.
|
||||
|
||||
A new project can be created from the :guilabel:`Project` menu by selecting
|
||||
:guilabel:`New Project`. This will open the :guilabel:`New Project Wizard` that will assist you in
|
||||
creating a barebone project suited to your needs.
|
||||
creating a barebone project suited to your needs. A novelWriter project requires a dedicated folder
|
||||
for storing its files on the local file system. See :ref:`a_tech` for further details on how files
|
||||
are organised.
|
||||
|
||||
A list of recently opened projects is maintained, and displayed in the :guilabel:`Open Project`
|
||||
dialog. A project can be removed from this list by selecting it and pressing the :kbd:`Del` key.
|
||||
dialog. A project can be removed from this list by selecting it and pressing the :kbd:`Del` key or
|
||||
by clicking the :guilabel:`Remove` button.
|
||||
|
||||
The project specific settings are available in :guilabel:`Project Settings` in the
|
||||
:guilabel:`Project` menu. See further details below in the :ref:`a_proj_settings` section.
|
||||
Project-specific settings are available in :guilabel:`Project Settings` in the :guilabel:`Project`
|
||||
menu. See further details below in the :ref:`a_proj_settings` section. Details about the project,
|
||||
including word counts, and a table of contents with word and page counts, is available through the
|
||||
:guilabel:`Project Details` dialog.
|
||||
|
||||
|
||||
.. _a_proj_roots:
|
||||
@@ -28,7 +30,7 @@ the project tree at the left side of the main window.
|
||||
|
||||
The core novel documents go into a root folder of type :guilabel:`Novel`. Other supporting
|
||||
documents go into the other root folders. These other root folder types are intended for your notes
|
||||
on the various elements of your story. Using these is of course entirely optional.
|
||||
on the various elements of your story. Using them is of course entirely optional.
|
||||
|
||||
A new project may not have all of the root folders present, but you can add the ones you want from
|
||||
:guilabel:`Create Root Folder` in the :guilabel:`Project` menu.
|
||||
@@ -38,8 +40,8 @@ no restrictions are enforced by the application. You can use them however you wa
|
||||
|
||||
:guilabel:`Novel`
|
||||
This is the root folder of all text that goes into the final novel. This class of documents have
|
||||
other rules and features than other documents in the project. See the :ref:`a_struct` page for
|
||||
more details.
|
||||
other rules and features than other documents in the project. See :ref:`a_struct` for more
|
||||
details.
|
||||
|
||||
:guilabel:`Plot`
|
||||
This is the root folder where main plots can be outlined. It is optional, but adding at least
|
||||
@@ -49,8 +51,9 @@ no restrictions are enforced by the application. You can use them however you wa
|
||||
:guilabel:`Characters`
|
||||
Character notes go in this root folder. These are especially important if one wants to use the
|
||||
Outline view to see which character appears where, and which part of the story is told from a
|
||||
specific character's point-of-view. Tags in this folder can be references using the ``@pov``
|
||||
keyword for point-of-view characters, or the ``@char`` keyword for other characters.
|
||||
specific character's point-of-view or focusing on a particular character's storyline. Tags in
|
||||
this folder can be referenced using the ``@pov`` keyword for point-of-view characters,
|
||||
``@focus`` for a focus character, or the ``@char`` keyword for any other characters.
|
||||
|
||||
:guilabel:`Locations`
|
||||
The locations folder is for various scene locations that you want to track. Tags in this folder
|
||||
@@ -79,8 +82,8 @@ information about the tags listed, see :ref:`a_struct_tags`.
|
||||
|
||||
.. tip::
|
||||
You can rename root folders to whatever you want. The first character in the :guilabel:`Flags`
|
||||
column will still indicate what type they are, and so will the icon if you are using one of the
|
||||
Typicons icon sets.
|
||||
column in the project tree will still indicate what type they are, and so will the icon if you
|
||||
are using one of the optional icon sets.
|
||||
|
||||
|
||||
.. _a_proj_roots_del:
|
||||
@@ -95,7 +98,7 @@ referenced.
|
||||
|
||||
Folders and root folders can only be deleted when they are empty. Recursive deletion is not
|
||||
supported. A document or a folder can be deleted from the :guilabel:`Project` menu, or by pressing
|
||||
:kbd:`Ctrl`:kbd:`Del`.
|
||||
:kbd:`Ctrl`:kbd:`Shift`:kbd:`Del`.
|
||||
|
||||
|
||||
.. _a_proj_roots_out:
|
||||
@@ -105,9 +108,9 @@ Archived Documents (Outtakes)
|
||||
|
||||
If you don't want to delete a document, or put it in the :guilabel:`Trash` folder where it may be
|
||||
deleted, but still want it out of your main project tree, you can create an :guilabel:`Outtakes`
|
||||
root folder from the :guilabel:`Project` menu. You are not allowed to move folders to this root
|
||||
folder, only documents. If you need folders in it to organise your documents, you can of course
|
||||
create new ones there.
|
||||
root folder from the :guilabel:`Project` menu. You are not allowed to move entire folders to this
|
||||
root folder, only documents. If you need folders in it to organise your documents, you can of
|
||||
course create new ones there.
|
||||
|
||||
You can drag any document to this folder and preserve its settings. The document will always be
|
||||
excluded from the :guilabel:`Build Novel Project` builds. It is also removed from the project
|
||||
@@ -157,7 +160,8 @@ this warning, and continue opening the project at your own risk.
|
||||
If you choose to ignore the warning and continue opening the project, and multiple instances of
|
||||
the project are in fact open, you are likely to cause inconsistencies and create diverging
|
||||
project files, potentially resulting in loss of data and orphaned files. You are not likely to
|
||||
lose any actual text unless both instances have the same document open in the editor,
|
||||
lose any actual text unless both instances have the same document open in the editor, and
|
||||
novelWriter will try to resolve inconsistencies the next time you open the project.
|
||||
|
||||
|
||||
.. _a_proj_roots_dirs:
|
||||
@@ -174,9 +178,9 @@ and to be able to collapse and hide them in the project tree when you're not wor
|
||||
documents.
|
||||
|
||||
.. tip::
|
||||
You can use folders to sort your scene documents into chapters. You will then need to add a
|
||||
chapter documents as the first item of your folder, and the scene documents as the following
|
||||
items.
|
||||
You can use folders to sort your scene documents into chapters. You will still need to add a
|
||||
chapter document as the first item of your chapter folder, and the scene documents as the
|
||||
following items. Another way to use folders is to make a folder for each act.
|
||||
|
||||
|
||||
.. _a_proj_files:
|
||||
@@ -208,7 +212,7 @@ Word Counts
|
||||
A character, word and paragraph count is maintained for each document, as well as for each section
|
||||
of a document following a header. The word count, and change of words in the current session, is
|
||||
displayed in the footer of any document open in the editor, and all stats are shown in the details
|
||||
panel below the project tree for any document selected in the project tree.
|
||||
panel below the project tree for any document selected in the project or novel tree.
|
||||
|
||||
The word counts are not updated in real time, but run in the background every five seconds for as
|
||||
long as the document is being actively edited.
|
||||
@@ -243,13 +247,9 @@ The :guilabel:`Book Title` and :guilabel:`Book Authors` settings are currently n
|
||||
anything, so setting then is just for the benefit of the author. Future features may be using them,
|
||||
and they are exported on some export formats in the :guilabel:`Build Novel Project` tool.
|
||||
|
||||
|
||||
Details Tab
|
||||
-----------
|
||||
|
||||
This tab presents an overview of meta data for the project. It states where on your file system the
|
||||
project is saved, how may times it has been saved, how many folders and documents it contains, and
|
||||
how many words exist in the entire project.
|
||||
If your project is in a different language than your main spell checking is set to, you can
|
||||
override the default spell checking language here. You can also override the automatic backup
|
||||
setting.
|
||||
|
||||
|
||||
Status and Importance Tabs
|
||||
@@ -265,6 +265,8 @@ use these to indicate at what stage of completion each novel document is, or how
|
||||
content of a note is to the plot. You don't have to use them this way, that's just what they were
|
||||
intended for, but you can make them whatever you want.
|
||||
|
||||
See also :ref:`a_ui_tree_status`.
|
||||
|
||||
.. note::
|
||||
The status or importance level currently in use by one or more documents cannot be deleted, but
|
||||
they can be edited.
|
||||
@@ -277,13 +279,14 @@ A set of automatically replaced keywords can be added in this tab. The keywords
|
||||
will be replaced by the text in the right column when documents are opened in the viewer. They will
|
||||
also be applied to exports.
|
||||
|
||||
The auto-replace feature will replace text in angle brackets that are also in this list. The syntax
|
||||
highlighter will add an alternate colour to text marching the syntax.
|
||||
The auto-replace feature will replace text in angle brackets that are in this list. The syntax
|
||||
highlighter will add an alternate colour to text marching the syntax, but it doesn't check if the
|
||||
text is in this list.
|
||||
|
||||
.. note::
|
||||
A keyword cannot contain any spaces. The angle brackets are added by default, and when used in
|
||||
the text are a part of the keyword to be replaced. This is to ensure that parts of the text
|
||||
aren't unintentionally replaced by the content of the list.
|
||||
A keyword cannot contain spaces. The angle brackets are added by default, and when used in the
|
||||
text are a part of the keyword to be replaced. This is to ensure that parts of the text aren't
|
||||
unintentionally replaced by the content of the list.
|
||||
|
||||
|
||||
.. _a_proj_backup:
|
||||
@@ -295,9 +298,9 @@ An automatic backup system is built into novelWriter. In order to use it, a back
|
||||
the backup files are to be stored must be provided in :guilabel:`Preferences`.
|
||||
|
||||
Backups can be run automatically when a project is closed, which also implies it is run when the
|
||||
application is closed. Backups are date stamped zip files of the entire project folder, and are
|
||||
stored in a subfolder of the backup path with the same name as the project :guilabel:`Working
|
||||
Title` set in :ref:`a_proj_settings`.
|
||||
application itself is closed. Backups are date stamped zip files of the entire project folder, and
|
||||
are stored in a subfolder of the backup path. The subfolder will have the same name as the project
|
||||
:guilabel:`Working Title` set in :ref:`a_proj_settings`.
|
||||
|
||||
The backup feature, when configured, can also be run manually from the :guilabel:`Tools` menu.
|
||||
It is also possible to disable automated backup for a given project in :guilabel:`Project
|
||||
@@ -306,7 +309,7 @@ Settings`.
|
||||
.. note::
|
||||
For the backup to be able to run, the :guilabel:`Working Title` must be set in
|
||||
:guilabel:`Project Settings`. This value is used to generate the folder name for the zip files.
|
||||
Without it, the backup will not run at all, but produce a warning message.
|
||||
Without it, the backup will not run at all, but it will produce a warning message.
|
||||
|
||||
|
||||
.. _a_proj_stats:
|
||||
@@ -325,3 +328,8 @@ A tool to view the content of this file is available in the :guilabel:`Tools` me
|
||||
The tool will show a list of all your sessions, and a set of filters to apply to it. You can also
|
||||
export the filtered data to a JSON file or to a CSV file that can be opened by a spreadsheet
|
||||
application like for instance Libre Office Calc.
|
||||
|
||||
As of version 1.2, the log file also stores how much of the session time was spent idle. The
|
||||
definition of idle here is that the novelWriter main window loses focus, and the user hasn't made
|
||||
any changes to the currently open document in five minutes. The number of minutes can be altered in
|
||||
:guilabel:`Preferences`.
|
||||
147
docs/source/usage_shortcuts.rst
Normal file
@@ -0,0 +1,147 @@
|
||||
.. _a_kb:
|
||||
|
||||
******************
|
||||
Keyboard Shortcuts
|
||||
******************
|
||||
|
||||
Most features in novelWriter are available as keyboard shortcuts.
|
||||
|
||||
|
||||
.. _a_kb_main:
|
||||
|
||||
Main Shortcuts
|
||||
==============
|
||||
|
||||
The main shorcuts are as follows:
|
||||
|
||||
.. csv-table:: Keyboard Shortcuts
|
||||
:header: "Shortcut", "Description"
|
||||
:widths: 30, 70
|
||||
:class: "tight-table"
|
||||
|
||||
":kbd:`Alt`:kbd:`1`", "Switch focus to the project tree. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`1`."
|
||||
":kbd:`Alt`:kbd:`2`", "Switch focus to document editor. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`2`."
|
||||
":kbd:`Alt`:kbd:`3`", "Switch focus to document viewer. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`3`."
|
||||
":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`."
|
||||
":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer."
|
||||
":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer."
|
||||
":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor."
|
||||
":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog."
|
||||
":kbd:`Ctrl`:kbd:`/`", "Toggle block format as comment."
|
||||
":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor."
|
||||
":kbd:`Ctrl`:kbd:`1`", "Change block format to header level 1."
|
||||
":kbd:`Ctrl`:kbd:`2`", "Change block format to header level 2."
|
||||
":kbd:`Ctrl`:kbd:`3`", "Change block format to header level 3."
|
||||
":kbd:`Ctrl`:kbd:`4`", "Change block format to header level 4."
|
||||
":kbd:`Ctrl`:kbd:`5`", "Change block alignment to left-aligned."
|
||||
":kbd:`Ctrl`:kbd:`6`", "Change block alignment to centred."
|
||||
":kbd:`Ctrl`:kbd:`7`", "Change block alignment to right-aligned."
|
||||
":kbd:`Ctrl`:kbd:`8`", "Add a left margin to the block."
|
||||
":kbd:`Ctrl`:kbd:`9`", "Add a right margin to the block."
|
||||
":kbd:`Ctrl`:kbd:`A`", "Select all text in the document."
|
||||
":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)."
|
||||
":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard."
|
||||
":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor."
|
||||
":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`.)"
|
||||
":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected."
|
||||
":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`.)"
|
||||
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
|
||||
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
|
||||
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
|
||||
":kbd:`Ctrl`:kbd:`N`", "Create new document."
|
||||
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
|
||||
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
|
||||
":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
|
||||
":kbd:`Ctrl`:kbd:`S`", "Save the current document in the document editor."
|
||||
":kbd:`Ctrl`:kbd:`V`", "Paste text from clipboard to cursor position."
|
||||
":kbd:`Ctrl`:kbd:`W`", "Close the current document in the document editor."
|
||||
":kbd:`Ctrl`:kbd:`X`", "Cut selected text to clipboard."
|
||||
":kbd:`Ctrl`:kbd:`Y`", "Redo latest undo."
|
||||
":kbd:`Ctrl`:kbd:`Z`", "Undo latest changes."
|
||||
":kbd:`Ctrl`:kbd:`F7`", "Toggle spell checking."
|
||||
":kbd:`Ctrl`:kbd:`F10`", "Toggle automatic updating of project outline."
|
||||
":kbd:`Ctrl`:kbd:`Up`", "Move item one step up in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`Down`", "Move item one step down in the project tree."
|
||||
":kbd:`Ctrl`:kbd:`Del`", "Delete next word in editor."
|
||||
":kbd:`Ctrl`:kbd:`Backspace`", "Delete previous word in editor."
|
||||
":kbd:`Ctrl`:kbd:`'`", "Wrap selected text, or word under cursor, in single quotes."
|
||||
":kbd:`Ctrl`:kbd:`""`", "Wrap selected text, or word under cursor, in double quotes."
|
||||
":kbd:`Ctrl`:kbd:`Retrun`", "Open the tag or reference under the cursor in the Viewer."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`,`", "Open the :guilabel:`Project Settings` dialog."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`/`", "Remove block formatting for block under cursor."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`.)"
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Undo move of project tree item."
|
||||
":kbd:`Ctrl`:kbd:`Shift`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder."
|
||||
":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website."
|
||||
":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)"
|
||||
":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)"
|
||||
":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog."
|
||||
":kbd:`F6`", "Open the :guilabel:`Writing Statistics` dialog."
|
||||
":kbd:`F7`", "Re-run spell checker."
|
||||
":kbd:`F8`", "Activate :guilabel:`Focus Mode`, hiding the project tree and document viewer."
|
||||
":kbd:`F9`", "Re-build the project index."
|
||||
":kbd:`F10`", "Re-build the project outline."
|
||||
":kbd:`F11`", "Activate full screen mode."
|
||||
":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser."
|
||||
":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)"
|
||||
":kbd:`Shift`:kbd:`F6`", "Open the :guilabel:`Project Details` dialog."
|
||||
":kbd:`Return`", "If in the project tree, open a document for editing."
|
||||
|
||||
.. note::
|
||||
On macOS, replace :kbd:`Ctrl` with :kbd:`Cmd`.
|
||||
|
||||
|
||||
.. _a_kb_ins:
|
||||
|
||||
Insert Shortcuts
|
||||
================
|
||||
|
||||
A set of insert features are also available through shortcuts, but they require a double
|
||||
combination of key sequences. The insert feature is activated with :kbd:`Ctrl`:kbd:`K`, followed by
|
||||
a key or key combination for the inserted content.
|
||||
|
||||
.. csv-table:: Keyboard Shortcuts
|
||||
:header: "Shortcut", "Description"
|
||||
:widths: 40, 60
|
||||
:class: "tight-table"
|
||||
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`−`", "Insert a short dash (en dash)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`_`", "Insert a long dash (em dash)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`_`", "Insert a horizontal bar (quotation dash)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`~`", "Insert a figure dash (same width as a number)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert a left single quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert a right single quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert a left double quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert a right double quote."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier apostrophe."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert an ellipsis."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`'`", "Insert a prime."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`""`", "Insert a double prime."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`", "Insert a thin non-breaking space."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`*`", "Insert a list bullet."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`−`", "Insert a hyphen bullet (alternative bullet)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`*`", "Insert a flower mark (alternative bullet)."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`%`", "Insert a per mille symbol."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`O`", "Insert a degree symbol."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`X`", "Insert a times sign."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`D`", "Insert a division sign."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`G`", "Insert a ``@tag`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`V`", "Insert a ``@pov`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`F`", "Insert a ``@focus`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`C`", "Insert a ``@char`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`P`", "Insert a ``@plot`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`T`", "Insert a ``@time`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`L`", "Insert a ``@location`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`O`", "Insert an ``@object`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`E`", "Insert an ``@entity`` keyword."
|
||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`X`", "Insert a ``@custom`` keyword."
|
||||
@@ -18,20 +18,22 @@ Importance of Headings
|
||||
Subfolders under root folders have no impact on the structure of the novel itself. The structure is
|
||||
instead dictated by the heading level of the headers within the documents.
|
||||
|
||||
Four levels of headings are supported, signified by the number of hashes preceding the title. See
|
||||
also the :ref:`a_ui_md` section for more details about the markdown syntax.
|
||||
Four levels of headings are supported, signified by the number of hashes (``#``) preceding the
|
||||
title. See also the :ref:`a_ui_md` section for more details about the markdown syntax.
|
||||
|
||||
.. note::
|
||||
The header levels are not only important when generating the exported novel file, they are also
|
||||
used by the indexer when building the outline tree in the :guilabel:`Outline` tab. Each heading
|
||||
also starts a new region where new references to tags can be set.
|
||||
used by the indexer when building the outline tree in the :guilabel:`Outline` tab as well as the
|
||||
:guilabel:`Novel` tab of the project tree. Each heading also starts a new region where new
|
||||
references and tags can be defined.
|
||||
|
||||
The different header levels are interpreted as specific section types of the novel in the following
|
||||
way:
|
||||
|
||||
``# Header1``
|
||||
Header level one signifies that the text refers to either the novel title or the name of a top
|
||||
level partition useful when you want to split the manuscript up into books, parts, or acts.
|
||||
level partition. The latter is useful when you want to split the manuscript up into books,
|
||||
parts, or acts.
|
||||
|
||||
``## Header2``
|
||||
Header level two signifies a chapter level partition. Each time you want to start a new chapter,
|
||||
@@ -47,10 +49,10 @@ way:
|
||||
manuscript.
|
||||
|
||||
``#### Header4``
|
||||
Header level four signifies a sub-scene level partition, usually called just a section in the
|
||||
documentation and user interface. These can be useful if you want to change tag references
|
||||
Header level four signifies a sub-scene level partition, usually called a "section" in the
|
||||
documentation and the user interface. These can be useful if you want to change tag references
|
||||
mid-scene, like if you change the point-of-view character. You are free to use sections as you
|
||||
wish, and can filter the titles out of the final manuscript just like with scene titles.
|
||||
wish, and can filter them out of the final manuscript just like with scene titles.
|
||||
|
||||
.. tip::
|
||||
There are multiple options of how to process novel titles when exporting the manuscript. For
|
||||
@@ -67,13 +69,13 @@ If you use layout types for your documents, the automatic numbering feature for
|
||||
controlled by whether you use the :guilabel:`Chapter` or :guilabel:`Unnumbered` layout type for
|
||||
your document. However, if you have a different document layout where this isn't practical, you can
|
||||
also switch off chapter numbering for a chapter by making the first character of the chapter title
|
||||
an ``*``. Like so:
|
||||
an asterisk (``*``). Like so:
|
||||
|
||||
``## *Unnumbered Chapter Title``
|
||||
|
||||
The leading asterisk is only considered by the :guilabel:`Build Novel Project` tool, and will be
|
||||
removed before inserted at the location of the ``%title%`` label. See the :ref:`a_export` page for
|
||||
more details.
|
||||
removed before the title is inserted at the location of the ``%title%`` label. See the
|
||||
:ref:`a_export` page for more details.
|
||||
|
||||
.. note::
|
||||
If you need the first character of the title to be an actual asterisk, you must escape it:
|
||||
@@ -85,10 +87,11 @@ more details.
|
||||
Tag References
|
||||
==============
|
||||
|
||||
Each text partition indicated by a heading of any level, can contain references to tags set in the
|
||||
supporting notes of the project. The references are gathered by the indexer and used to generate
|
||||
the outline view on the :guilabel:`Outline` tab of how the different parts of the novel are
|
||||
connected.
|
||||
Each text partition, indicated by a heading of any level, can contain references to tags set in the
|
||||
supporting notes of the project. The references are gathered by the indexer and used to generate an
|
||||
outline view on the :guilabel:`Outline` tab of how the different parts of the novel are connected.
|
||||
This section covers how to set references to tags. See :ref:`a_notes_tags` for how to define tags
|
||||
the references can point to.
|
||||
|
||||
References and tags are also clickable in the document editor and viewer, making it easy to
|
||||
navigate between reference notes while writing. Clicked links are always opened in the view panel.
|
||||
@@ -101,28 +104,33 @@ allow multiple values.
|
||||
The point-of-view character for the current section. The target must be a note tag in the
|
||||
:guilabel:`Character` type root folder.
|
||||
|
||||
``@focus``
|
||||
The character that has the focus for the current section. This can be used in cases where the
|
||||
focus is not a point-of-view character. The target must be a note tag in the
|
||||
:guilabel:`Character` type root folder.
|
||||
|
||||
``@char``
|
||||
Other characters in the current section. The target must be a note tag in a
|
||||
:guilabel:`Character` type root folder. This should not include the point-of-view character(s).
|
||||
Other characters in the current section. The target must be a note tag in the
|
||||
:guilabel:`Character` type root folder. This should not include the point-of-view character.
|
||||
|
||||
``@plot``
|
||||
The plot or subplot advanced in the current section. The target must be a note tag in a
|
||||
The plot or subplot advanced in the current section. The target must be a note tag in the
|
||||
:guilabel:`Plot` type root folder.
|
||||
|
||||
``@time``
|
||||
The timelines touched by the current section. The target must be a note tag in a
|
||||
The timelines touched by the current section. The target must be a note tag in the
|
||||
:guilabel:`Timeline` type root folder.
|
||||
|
||||
``@location``
|
||||
The location the current section takes place in. The target must be a note tag in a
|
||||
The location the current section takes place in. The target must be a note tag in the
|
||||
:guilabel:`Locations` type root folder.
|
||||
|
||||
``@object``
|
||||
Objects present in the current section. The target must be a note tag in an :guilabel:`Object`
|
||||
Objects present in the current section. The target must be a note tag in the :guilabel:`Object`
|
||||
type root folder.
|
||||
|
||||
``@entity``
|
||||
Entities present in the current section. The target must be a note tag in an
|
||||
Entities present in the current section. The target must be a note tag in the
|
||||
:guilabel:`Entities` type root folder.
|
||||
|
||||
``@custom``
|
||||
@@ -142,15 +150,15 @@ the index for a document is regenerated when it is saved, so this shouldn't norm
|
||||
Novel Document Layout
|
||||
=====================
|
||||
|
||||
All documents in a novelWriter project can have a layout format set. These layouts are important
|
||||
when the project is exported as they indicate how to treat the content in terms of formatting,
|
||||
All documents in the project can have a layout format set. These layouts are important when the
|
||||
project is exported as they indicate how to treat the content in terms of text formatting,
|
||||
headings, and page breaks. The layout for each document is indicated as the last set of characters
|
||||
in the :guilabel:`Flags` column of the project tree.
|
||||
|
||||
Not all layout types are actually treated differently, they also help to indicate what each
|
||||
document is for in your project. The :guilabel:`Book` layout is a generic novel document layout
|
||||
that is formatted identically to :guilabel:`Chapter` and :guilabel:`Scene` layout documents, but
|
||||
may help to indicate what each document does in your project.
|
||||
document is intended for in your project. The :guilabel:`Book` layout is a generic novel document
|
||||
layout that is formatted identically to :guilabel:`Chapter` and :guilabel:`Scene` layout documents,
|
||||
but may help to indicate what each document does in your project.
|
||||
|
||||
You can for instance lay out your project using :guilabel:`Book` documents for each act, and then
|
||||
later split those into chapter or scene documents by using the :guilabel:`Split Document` tool.
|
||||
@@ -163,15 +171,15 @@ Some layouts *do* have implications on how the project is exported. Documents wi
|
||||
contained within it. The latter is convenient for Prologue and Epilogue type chapters.
|
||||
|
||||
The above layout formats are only usable in the Novel root folder. Documents that are not a part of
|
||||
the novel itself should have the Note layout. These documents are not getting any special
|
||||
formatting, and it is possible to collectively filter them out during export. Notes can be used
|
||||
anywhere in the project, also in the Novel root folder.
|
||||
the novel itself should have the :guilabel:`Note` layout. These documents are not getting any
|
||||
special formatting, and it is possible to collectively filter them out during export. Notes can be
|
||||
used anywhere in the project, also in the :guilabel:`Novel` root folder.
|
||||
|
||||
Below is an overview of all available layout formats.
|
||||
|
||||
:guilabel:`Title Page`
|
||||
The title page layout. The title should be formatted as a heading level one. All text is
|
||||
automatically centred on exports.
|
||||
The title page layout. The title should be formatted as a heading level one. All text is centred
|
||||
on export.
|
||||
|
||||
:guilabel:`Plain Page`
|
||||
A plain page layout useful for instance for front matter pages. Heading levels are ignored for
|
||||
@@ -184,7 +192,7 @@ Below is an overview of all available layout formats.
|
||||
produce the same result as a collection of :guilabel:`Partition`, :guilabel:`Chapter` and
|
||||
:guilabel:`Scene` layout documents. However, it does not provide the functionality of the
|
||||
:guilabel:`Unnumbered` layout format by default, but this can still be achieved by prefixing the
|
||||
chapter title with a ``*``.
|
||||
chapter title with an asterisk (``*``). See :ref:`a_struct_heads_unnum` for more details.
|
||||
|
||||
:guilabel:`Partition`
|
||||
A partition can be used to split the novel into parts. Partition titles are indicated with a
|
||||
@@ -200,8 +208,8 @@ Below is an overview of all available layout formats.
|
||||
text.
|
||||
|
||||
:guilabel:`Unnumbered`
|
||||
Same as :guilabel:`Chapter`, but when exporting the documents and automatic chapter numbering is
|
||||
enabled, documents with this layout will not increment the chapter number. It also has a
|
||||
Same as :guilabel:`Chapter`, but when exporting the project, and automatic chapter numbering is
|
||||
enabled, documents with this layout will not increment and apply a chapter number. It also has a
|
||||
separate title formatting setting. This makes the layout suitable for Prologue and Epilogue type
|
||||
chapters.
|
||||
|
||||
@@ -210,18 +218,33 @@ Below is an overview of all available layout formats.
|
||||
headers of level four, but there are no layout specifically for sections.
|
||||
|
||||
:guilabel:`Note`
|
||||
A generic document that is optionally ignored when the novel is exported. Use this layout for
|
||||
descriptions of content in the supporting root folders. Notes can also be added to the
|
||||
:guilabel:`Novel` root folder if you need to insert notes there. Note headers receive no special
|
||||
formatting when building the project. They are always exported as-is.
|
||||
A generic document that is optionally ignored when the novel project is exported. Use this
|
||||
layout for descriptions of content in the supporting root folders. Notes can also be added to
|
||||
the :guilabel:`Novel` root folder if you need to insert notes there. Note headers receive no
|
||||
special formatting when building the project. They are always exported as-is.
|
||||
|
||||
.. note::
|
||||
The layout granularity is entirely optional. In principle, you can write the entire novel in a
|
||||
single document with layout :guilabel:`Book`. You can also have a single document per chapter if
|
||||
that suits you better. The :guilabel:`Outline` will show your structure of chapters and scenes
|
||||
regardless of how your documents are organised.
|
||||
regardless of how your documents are organised. So will the novel tree.
|
||||
|
||||
.. tip::
|
||||
You can always start writing with a coarse layout with one or a few documents, and then later
|
||||
use the split tool to automatically split the documents into separate chapter and scene
|
||||
documents.
|
||||
documents. The split tool will try to select the correct layout for each of the new documents.
|
||||
|
||||
|
||||
.. _a_struct_layout_auto:
|
||||
|
||||
Novel Layout Automation
|
||||
-----------------------
|
||||
|
||||
When a document is saved from the document editor, novelWriter will check if the level of the first
|
||||
header of the document matches the selected document layout. If the header level is inconsistent
|
||||
with the layout, the layout will automatically be corrected if it can be unambiguously determined.
|
||||
|
||||
This means that for instance saving a document with header level three, when the layout is set to
|
||||
:guilabel:`Chapter`, the layout will automatically be changed to :guilabel:`Scene`. If, however,
|
||||
the layout was set to :guilabel:`Book`, no change will be made because the :guilabel:`Book` layout
|
||||
is not associated with a specific header level.
|
||||
@@ -7,7 +7,7 @@ Typographical Notes
|
||||
novelWriter has some support for typographical symbols that are not usually easily available in
|
||||
many text editors. This includes for instance the proper unicode quotation marks, dashes, ellipsis,
|
||||
thin spaces, etc. All these symbols are available from the :guilabel:`Insert` menu, and via
|
||||
keyboard shortcuts. See :ref:`a_ui_shortcuts_ins`.
|
||||
keyboard shortcuts. See :ref:`a_kb_ins`.
|
||||
|
||||
This chapter provides some additional information on how novelWriter handles these symbols.
|
||||
|
||||
@@ -17,7 +17,7 @@ This chapter provides some additional information on how novelWriter handles the
|
||||
Special Notes on Symbols
|
||||
========================
|
||||
|
||||
Some additional notes on these symbols.
|
||||
Some additional notes on the available symbols.
|
||||
|
||||
|
||||
Dashes and Ellipsis
|
||||
@@ -28,41 +28,58 @@ automatically to short and long dashes, and three dots to ellipsis. The last aut
|
||||
always be reverted with the undo command :kbd:`Ctrl`:kbd:`Z`, reverting the text to what you typed
|
||||
before the automatic replacement occurred.
|
||||
|
||||
In addition, "Figure Dash" is available. The Figure Dash is a dash that has the same width as the
|
||||
numbers of the same font, for most fonts. It helps to align numbers align nicely in columns when
|
||||
you need to use a dash in them.
|
||||
|
||||
|
||||
Single and Double Quotes
|
||||
------------------------
|
||||
|
||||
All the different quotation marks listed on the `Quotation Mark`_ Wikipedia page are available, and
|
||||
can be selected as auto-replaced symbols for straight single and double quote key strokes. The
|
||||
settings can be found in the :guilabel:`Preferences`.
|
||||
settings can be found in :guilabel:`Preferences`.
|
||||
|
||||
Ordinarily, text wrapped in quotes are highlighted by the editor. This is meant as a convenience
|
||||
for highlighting dialogue between characters. This feature can be disabled in the
|
||||
for highlighting dialogue between characters. This feature can be disabled in
|
||||
:guilabel:`Preferences` if this feature isn't wanted.
|
||||
|
||||
The editor distinguishes between text wrapped in straight quotes and with the user-selected double
|
||||
quote symbols. This is to help the writer recognise which parts of the text are not using the
|
||||
chosen quote symbols. Two convenience functions in the :guilabel:`Format` menu can be used to
|
||||
The editor distinguishes between text wrapped in regular straight quotes and with the user-selected
|
||||
double quote symbols. This is to help the writer recognise which parts of the text are not using
|
||||
the chosen quote symbols. Two convenience functions in the :guilabel:`Format` menu can be used to
|
||||
re-format a selected section of text with the correct quote symbols.
|
||||
|
||||
.. _Quotation Mark: https://en.wikipedia.org/wiki/Quotation_mark
|
||||
|
||||
|
||||
Modifier Letter Apostrophes
|
||||
---------------------------
|
||||
Single and Double Prime
|
||||
------------------------
|
||||
|
||||
Both single and double prime symbols are available in the :guilabel:`Insert` menu. These symbols
|
||||
are the correct symbols to use for unit symbols for feet, inches, minutes and seconds. The usage of
|
||||
these is described in more detail on the Wikipedia Prime_ page. They look very similar to single
|
||||
and double straight quotes, and may be renderred similarly by the font, but they have different
|
||||
codes. Using these correctly will also prevent the auto-replace and dialogue highlighting features
|
||||
misunderstanding their meaning in the text.
|
||||
|
||||
.. _Prime: https://en.wikipedia.org/wiki/Prime_(symbol)
|
||||
|
||||
|
||||
Modifier Letter Apostrophe
|
||||
--------------------------
|
||||
|
||||
The auto-replace feature will consider any right-facing single straight quote as a quote symbol,
|
||||
even if it's intended as an apostrophe. This also includes the syntax highlighter, which may assume
|
||||
the first following apostrophe is the closing symbol of a single quoted region of text.
|
||||
even if it is intended as an apostrophe. This also includes the syntax highlighter, which may
|
||||
assume the first following apostrophe is the closing symbol of a single quoted region of text.
|
||||
|
||||
To get around this, an alternative apostrophe is available. It is a special Unicode character that
|
||||
is not categorised as punctuation, but as a modifier. It is usually renderred the same way as right
|
||||
single quotation marks, but not always, depending on the font. There is a Wikipedia article for the
|
||||
is not categorised as punctuation, but as a modifier. It is usually renderred the same way as the
|
||||
right single quotation marks, depending on the font. There is a Wikipedia article for the
|
||||
`Modifier letter apostrophe`_ with more details.
|
||||
|
||||
.. note::
|
||||
On export with the :guilabel:`Build Novel Project` tool, these apostrophes will be replaced
|
||||
automatically with the corresponding right hand quote symbols as is generally recommended.
|
||||
automatically with the corresponding right hand single quote symbol as is generally recommended.
|
||||
Therefore it doesn't really matter if you only use them to correct highlighting.
|
||||
|
||||
.. _Modifier letter apostrophe: https://en.wikipedia.org/wiki/Modifier_letter_apostrophe
|
||||
@@ -82,6 +99,6 @@ All non-breaking spaces are highlighted with a differently coloured background t
|
||||
spot them in the text. The colour will depend on the selected colour theme.
|
||||
|
||||
The thin and non-breaking spaces are converted to their corresponding HTML codes on export to HTML
|
||||
format. For plain text, they are exported as regular spaces.
|
||||
format.
|
||||
|
||||
.. _thin space: https://en.wikipedia.org/wiki/Thin_space
|
||||
85
i18n/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# novelWriter Internationalisation
|
||||
|
||||
The `i18n` folder contains the translation files for the Qt5 GUI. There are two types of files
|
||||
involved: the `nw_XX.ts` and the `nw_XX.qm` files. The `nw_XX.ts` files are located in the `i18n`
|
||||
folder at the root of the repository, and the `nw_XX.qm` together with the `project_XX.json` files
|
||||
are located in `nw/assets/i18n`. The latter are JSON translation maps for the novelWriter projects,
|
||||
used by the Build Novel Project tool.
|
||||
|
||||
**Note**
|
||||
|
||||
When making a new translation, or updating an existing one, only commit the `nw_XX.ts` you have
|
||||
made changes to. The `qtlupdate` command mentioned below will likely modify all `nw_XX.ts`
|
||||
slightly, but please _don't_ commit those changes to the pull request.
|
||||
|
||||
## Qt GUI Localisation
|
||||
|
||||
The `.qm` files are the actual files used by the application to translate the GUI into another
|
||||
language other than the default British English. The files are not generated by default. They can
|
||||
be built with:
|
||||
```bash
|
||||
python3 setup.py qtlrelease
|
||||
```
|
||||
|
||||
To add a new translation file, first add a new entry in the `novelWriter.pro` file under the
|
||||
`TRANSLATIONS` variable. Please use the underscore syntax for file names as the importer expects
|
||||
this format.
|
||||
|
||||
After adding the entry in the `.pro` file, run:
|
||||
```bash
|
||||
python3 setup.py qtlupdate
|
||||
```
|
||||
|
||||
This will build a new `i18n/nw_XX.ts` file for the language you just added. The file can then be
|
||||
edited with the Qt 5 Linguist application provided by Qt. This is by far easier than manually
|
||||
editing the `.ts` file. Please select "English" and "United Kingdom" as the source langauge when
|
||||
prompted by Qt 5 Linguist.
|
||||
|
||||
When you're done editing, you can build the `nw/assets/i18n/nw_XX.qm` file and test it in
|
||||
novelWriter. The Preferences dialog should list the newly added language, and you can select it and
|
||||
test it.
|
||||
|
||||
Please do not submit the `.qm` files to the repository. Only the `.ts` file you just added in the
|
||||
`i18n` folder is needed.
|
||||
|
||||
**Note**
|
||||
|
||||
These commands require that you have the tools Qt 5 Linguist and the PyQt5 tool `pylupdate5`
|
||||
installed on your system.
|
||||
|
||||
For Ubuntu/Debian, run:
|
||||
```bash
|
||||
sudo apt install qttools5-dev-tools pyqt5-dev-tools
|
||||
```
|
||||
|
||||
### Missing QtBase Translations
|
||||
|
||||
The default Qt dialogs also have translations, for instance for standard buttons like "Yes", "No",
|
||||
"Ok", "Cancel", etc. Generally, these translations files are installed with the Qt libraries on
|
||||
your system, and novelWriter will collect those translations from there. However, these
|
||||
translations are missing for many languages.
|
||||
|
||||
As a starting point, there is no need to translate any entries in the `.ts` files that are under
|
||||
elements starting with the letter "Q", like "QPlatformTheme", "QWizard", etc. If these turn up in
|
||||
English in novelWriter after activating a translation, it means they are probably missing in the Qt
|
||||
library, and you may also need to translate these.
|
||||
|
||||
These additional translation entries are generated from a file named `i18n/qtbase.py`, which is not
|
||||
a file that novelWriter uses. It is there only to generate these additional entries for the `.ts`
|
||||
files.
|
||||
|
||||
## Project Localisation
|
||||
|
||||
Projects can have a different language setting than the GUI itself. The files with format
|
||||
`project_XX.json` in `nw/assets/i18n` are simple translation maps for text that goes into the
|
||||
exported documents generated by the Build Novel Project tool.
|
||||
|
||||
The files are loaded based on the language setting in the build tool. At the present time, no other
|
||||
parts of the application use these files. Since these are used as replacement lookups, they are
|
||||
maintained as plain JSON files. The main usage is to generate chapter headers as number words.
|
||||
|
||||
Adding new translations for these files is easy. Just copy the `project_en.json` file, rename it to
|
||||
the appropriate language coded filename, and edit it. Please use the underscore as separator if a
|
||||
language needs to be localised to a specific country under a language. The importer will for
|
||||
instance look for `en_US` first, then use `en` if it is not found. If there are no matching files,
|
||||
the project falls back to using English.
|
||||
5023
i18n/nw_en_US.ts
Normal file
5024
i18n/nw_fr.ts
Normal file
5023
i18n/nw_nb_NO.ts
Normal file
4943
i18n/nw_pt.ts
Normal file
5025
i18n/nw_zh_CN.ts
Normal file
73
i18n/qtbase.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Qt Base Translation File
|
||||
========================
|
||||
|
||||
This file causes Qt Linguist to generate translation entries for the Qt
|
||||
elements that need translation in novelWriter for those languages who do
|
||||
not yet have a qtbase_xx.qm file shipped with Qt.
|
||||
|
||||
If a qtbase_xx.qm file already exists, do not add a translation for the
|
||||
entries generated from this file.
|
||||
"""
|
||||
|
||||
from PyQt5.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
# QDialogButtonBox
|
||||
# ================
|
||||
|
||||
QT_TRANSLATE_NOOP("QDialogButtonBox", "OK")
|
||||
|
||||
# QGnomeTheme
|
||||
# ===========
|
||||
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "&OK")
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "&Save")
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "&Cancel")
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "&Close")
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "Close without Saving")
|
||||
|
||||
# QGuiApplication
|
||||
# ===============
|
||||
|
||||
QT_TRANSLATE_NOOP("QGuiApplication", (
|
||||
"Translate this string to the string 'LTR' in left-to-right languages or to "
|
||||
"'RTL' in right-to-left languages (such as Hebrew and Arabic) to get proper widget layout."
|
||||
))
|
||||
|
||||
# QPlatformTheme
|
||||
# ==============
|
||||
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "OK")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Save")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Save All")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Open")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "&Yes")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Yes to &All")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "&No")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "N&o to All")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Abort")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Retry")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Ignore")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Close")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Discard")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Help")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Apply")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Reset")
|
||||
QT_TRANSLATE_NOOP("QPlatformTheme", "Restore Defaults")
|
||||
|
||||
# QWizard
|
||||
# =======
|
||||
|
||||
QT_TRANSLATE_NOOP("QWizard", "Go Back")
|
||||
QT_TRANSLATE_NOOP("QWizard", "< &Back")
|
||||
QT_TRANSLATE_NOOP("QWizard", "Continue")
|
||||
QT_TRANSLATE_NOOP("QWizard", "&Next")
|
||||
QT_TRANSLATE_NOOP("QWizard", "&Next >")
|
||||
QT_TRANSLATE_NOOP("QWizard", "Commit")
|
||||
QT_TRANSLATE_NOOP("QWizard", "Done")
|
||||
QT_TRANSLATE_NOOP("QWizard", "&Finish")
|
||||
QT_TRANSLATE_NOOP("QWizard", "Cancel")
|
||||
QT_TRANSLATE_NOOP("QWizard", "Cancel")
|
||||
QT_TRANSLATE_NOOP("QWizard", "Help")
|
||||
QT_TRANSLATE_NOOP("QWizard", "&Help")
|
||||
333
make.py
@@ -1,333 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This make script is intended for building distributable packages of
|
||||
novelWriter. These are either:
|
||||
|
||||
* A single file executable named dist/novelWriter(.exe). This is a
|
||||
quite slow option, and the file is fairly big.
|
||||
* A single directory named dist/novelWriter with a novelWriter(.exe),
|
||||
and all dependecies included.
|
||||
* The latter can be combined with a build stage of a setup.exe file if
|
||||
on Windows. This requires Inno Setup to be installed and in path.
|
||||
|
||||
In addition, providing the pip otion will cause the script to try to
|
||||
install all dependencies needed for runing the build, and for running
|
||||
novelWriter itself.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
OS_NONE = 0
|
||||
OS_LINUX = 1
|
||||
OS_WIN = 2
|
||||
OS_DARWIN = 3
|
||||
|
||||
# =============================================================================================== #
|
||||
# Package Installer
|
||||
# =============================================================================================== #
|
||||
|
||||
def installPackages(hostOS):
|
||||
"""Install package dependencies both for this script and for running
|
||||
novelWriter itself.
|
||||
"""
|
||||
print("")
|
||||
print("Installing Dependencies")
|
||||
print("#######################")
|
||||
print("")
|
||||
|
||||
installQueue = ["pip", "pyinstaller", "-r requirements.txt"]
|
||||
if hostOS == OS_DARWIN:
|
||||
installQueue.append("pyobjc")
|
||||
|
||||
pyCmd = [sys.executable, "-m"]
|
||||
pipCmd = ["pip", "install", "--user", "--upgrade"]
|
||||
for stepCmd in installQueue:
|
||||
pkgCmd = stepCmd.split(" ")
|
||||
try:
|
||||
subprocess.call(pyCmd + pipCmd + pkgCmd)
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Run PyInstaller on Package
|
||||
# =============================================================================================== #
|
||||
|
||||
def freezePackage(buildWindowed, oneFile, makeSetup, hostOS):
|
||||
"""Run PyInstaller to freeze the packages. This assumes all
|
||||
dependencies are already in place.
|
||||
"""
|
||||
import PyInstaller.__main__ # noqa: E402
|
||||
|
||||
print("")
|
||||
print("Running PyInstaller")
|
||||
print("###################")
|
||||
print("")
|
||||
|
||||
if hostOS == OS_WIN:
|
||||
dotDot = ";"
|
||||
else:
|
||||
dotDot = ":"
|
||||
|
||||
sys.modules["FixTk"] = None
|
||||
instOpt = [
|
||||
"--name=novelWriter",
|
||||
"--clean",
|
||||
"--add-data=%s%s%s" % (os.path.join("nw", "assets"), dotDot, "assets"),
|
||||
"--icon=%s" % os.path.join("nw", "assets", "icons", "novelwriter.ico"),
|
||||
"--exclude-module=PyQt5.QtQml",
|
||||
"--exclude-module=PyQt5.QtBluetooth",
|
||||
"--exclude-module=PyQt5.QtDBus",
|
||||
"--exclude-module=PyQt5.QtMultimedia",
|
||||
"--exclude-module=PyQt5.QtMultimediaWidgets",
|
||||
"--exclude-module=PyQt5.QtNetwork",
|
||||
"--exclude-module=PyQt5.QtNetworkAuth",
|
||||
"--exclude-module=PyQt5.QtNfc",
|
||||
"--exclude-module=PyQt5.QtQuick",
|
||||
"--exclude-module=PyQt5.QtQuickWidgets",
|
||||
"--exclude-module=PyQt5.QtRemoteObjects",
|
||||
"--exclude-module=PyQt5.QtSensors",
|
||||
"--exclude-module=PyQt5.QtSerialPort",
|
||||
"--exclude-module=PyQt5.QtSql",
|
||||
"--exclude-module=FixTk",
|
||||
"--exclude-module=tcl",
|
||||
"--exclude-module=tk",
|
||||
"--exclude-module=_tkinter",
|
||||
"--exclude-module=tkinter",
|
||||
"--exclude-module=Tkinter",
|
||||
]
|
||||
|
||||
if buildWindowed:
|
||||
instOpt.append("--windowed")
|
||||
|
||||
if oneFile and not makeSetup:
|
||||
instOpt.append("--onefile")
|
||||
else:
|
||||
instOpt.append("--onedir")
|
||||
|
||||
instOpt.append("novelWriter.py")
|
||||
|
||||
# Make sample.zip first
|
||||
try:
|
||||
subprocess.call([sys.executable, "setup.py", "sample"])
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
PyInstaller.__main__.run(instOpt)
|
||||
|
||||
if not oneFile:
|
||||
# These files are not needed, and take up a fair bit of space.
|
||||
delFiles = []
|
||||
if hostOS == OS_WIN:
|
||||
delFiles = [
|
||||
"Qt5DBus.dll",
|
||||
"Qt5Network.dll",
|
||||
"Qt5Qml.dll",
|
||||
"Qt5QmlModels.dll",
|
||||
"Qt5Quick.dll",
|
||||
"Qt5Quick3D.dll",
|
||||
"Qt5Quick3DAssetImport.dll",
|
||||
"Qt5Quick3DRender.dll",
|
||||
"Qt5Quick3DRuntimeRender.dll",
|
||||
"Qt5Quick3DUtils.dll",
|
||||
"Qt5Sql.dll"
|
||||
]
|
||||
elif hostOS == OS_LINUX:
|
||||
delFiles = [
|
||||
"libQt5DBus.so.5",
|
||||
"libQt5Network.so.5",
|
||||
"libQt5Qml.so.5",
|
||||
"libQt5QmlModels.so.5",
|
||||
"libQt5Quick.so.5",
|
||||
"libQt5Quick3D.so.5",
|
||||
"libQt5Quick3DAssetImport.so.5",
|
||||
"libQt5Quick3DRender.so.5",
|
||||
"libQt5Quick3DRuntimeRender.so.5",
|
||||
"libQt5Quick3DUtils.so.5",
|
||||
"libQt5Sql.so.5"
|
||||
]
|
||||
distDir = os.path.join(os.getcwd(), "dist", "novelWriter")
|
||||
for delFile in delFiles:
|
||||
delPath = os.path.join(distDir, delFile)
|
||||
if os.path.isfile(delPath):
|
||||
print("Deleting file: %s" % delPath)
|
||||
os.unlink(delPath)
|
||||
|
||||
print("")
|
||||
print("Build Finished")
|
||||
print("")
|
||||
print("The novelWriter executable should be in the folder named 'dist'")
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Inno Setup Builder
|
||||
# =============================================================================================== #
|
||||
|
||||
def innoSetup():
|
||||
"""Run the Inno Setup tool to build a setup.exe file for Windows.
|
||||
"""
|
||||
print("")
|
||||
print("Running Inno Setup")
|
||||
print("##################")
|
||||
print("")
|
||||
|
||||
# Read the iss template
|
||||
issData = ""
|
||||
with open(os.path.join("setup", "win_setup.iss"), mode="r") as inFile:
|
||||
issData = inFile.read()
|
||||
|
||||
import nw # noqa: E402
|
||||
issData = issData.replace(r"%%version%%", nw.__version__)
|
||||
issData = issData.replace(r"%%dir%%", os.getcwd())
|
||||
|
||||
with open("setup.iss", mode="w+") as outFile:
|
||||
outFile.write(issData)
|
||||
|
||||
try:
|
||||
subprocess.call(["iscc", "setup.iss"])
|
||||
except Exception as e:
|
||||
print("Inno Setup failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Clean Build and Dist Folders
|
||||
# =============================================================================================== #
|
||||
|
||||
def cleanInstall():
|
||||
"""Recursively delete the 'build' and 'dist' folders.
|
||||
"""
|
||||
print("")
|
||||
print("Cleaning up build environment ...")
|
||||
|
||||
buildDir = os.path.join(os.getcwd(), "build")
|
||||
if os.path.isdir(buildDir):
|
||||
try:
|
||||
shutil.rmtree(buildDir)
|
||||
print("Deleted folder 'build'")
|
||||
except Exception as e:
|
||||
print("Error: Cannot delete 'build' folder.")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Folder 'build' not found")
|
||||
|
||||
distDir = os.path.join(os.getcwd(), "dist")
|
||||
if os.path.isdir(distDir):
|
||||
try:
|
||||
shutil.rmtree(distDir)
|
||||
print("Deleted folder 'dist'")
|
||||
except Exception as e:
|
||||
print("Error: Cannot delete 'dist' folder.")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Folder 'dist' not found")
|
||||
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
# =============================================================================================== #
|
||||
# Process Build Steps
|
||||
# =============================================================================================== #
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""Parse command line options and run the commands.
|
||||
"""
|
||||
# Detect OS
|
||||
if sys.platform.startswith("linux"):
|
||||
hostOS = OS_LINUX
|
||||
elif sys.platform.startswith("darwin"):
|
||||
hostOS = OS_DARWIN
|
||||
elif sys.platform.startswith("win32"):
|
||||
hostOS = OS_WIN
|
||||
elif sys.platform.startswith("cygwin"):
|
||||
hostOS = OS_WIN
|
||||
else:
|
||||
hostOS = OS_NONE
|
||||
|
||||
# Flags and Variables
|
||||
buildWindowed = True
|
||||
oneFile = False
|
||||
makeSetup = False
|
||||
doFreeze = False
|
||||
|
||||
helpMsg = (
|
||||
"\n"
|
||||
"novelWriter Make Tool\n"
|
||||
"=====================\n"
|
||||
"This tool provides build commands for distibuting novelWriter as a\n"
|
||||
"package. The available options are as follows:\n"
|
||||
"\n"
|
||||
"help Print the help message.\n"
|
||||
"freeze Freeze the package and produces a folder of all\n"
|
||||
" dependencies using pyinstaller.\n"
|
||||
"onefile Build a standalone executable with all dependencies\n"
|
||||
" bundled. Implies 'freeze', cannot be used with 'setup'.\n"
|
||||
"pip Run pip to install all package dependencies for\n"
|
||||
" novelWriter and this build tool.\n"
|
||||
"setup Build a setup.exe installer for Windows. This option\n"
|
||||
" automaticall disables the 'onefile' option.\n"
|
||||
"clean This will attempt to delete the 'build' and 'dist'\n"
|
||||
" folders in the current folder.\n"
|
||||
)
|
||||
|
||||
if "help" in sys.argv or len(sys.argv) <= 1:
|
||||
print(helpMsg)
|
||||
sys.exit(0)
|
||||
|
||||
if not os.path.isfile(os.path.join(os.getcwd(), "novelWriter.py")):
|
||||
print("Error: This script must be run in the root folder of novelWriter.")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isdir(os.path.join(os.getcwd(), "nw")):
|
||||
print("Error: This script must be run in the root folder of novelWriter.")
|
||||
sys.exit(1)
|
||||
|
||||
if "clean" in sys.argv:
|
||||
sys.argv.remove("clean")
|
||||
cleanInstall()
|
||||
|
||||
if "pip" in sys.argv:
|
||||
sys.argv.remove("pip")
|
||||
installPackages(hostOS)
|
||||
|
||||
if "freeze" in sys.argv:
|
||||
sys.argv.remove("freeze")
|
||||
doFreeze = True
|
||||
|
||||
if "onefile" in sys.argv:
|
||||
sys.argv.remove("onefile")
|
||||
doFreeze = True
|
||||
oneFile = True
|
||||
|
||||
if "setup" in sys.argv:
|
||||
sys.argv.remove("setup")
|
||||
if hostOS == OS_WIN:
|
||||
oneFile = False
|
||||
makeSetup = True
|
||||
else:
|
||||
print("Error: Argument 'setup' for Inno Setup is Windows only.")
|
||||
sys.exit(1)
|
||||
|
||||
if doFreeze:
|
||||
freezePackage(buildWindowed, oneFile, makeSetup, hostOS)
|
||||
|
||||
if makeSetup:
|
||||
innoSetup()
|
||||
|
||||
# END Main
|
||||
39
novelWriter.pro
Normal file
@@ -0,0 +1,39 @@
|
||||
SOURCES += \
|
||||
i18n/qtbase.py \
|
||||
nw/core/project.py \
|
||||
nw/core/tokenizer.py \
|
||||
nw/dialogs/about.py \
|
||||
nw/dialogs/docmerge.py \
|
||||
nw/dialogs/docsplit.py \
|
||||
nw/dialogs/itemeditor.py \
|
||||
nw/dialogs/preferences.py \
|
||||
nw/dialogs/projload.py \
|
||||
nw/dialogs/projsettings.py \
|
||||
nw/dialogs/wordlist.py \
|
||||
nw/gui/custom.py \
|
||||
nw/gui/doceditor.py \
|
||||
nw/gui/docviewer.py \
|
||||
nw/gui/itemdetails.py \
|
||||
nw/gui/mainmenu.py \
|
||||
nw/gui/noveltree.py \
|
||||
nw/gui/outline.py \
|
||||
nw/gui/outlinedetails.py \
|
||||
nw/gui/projdetails.py \
|
||||
nw/gui/projtree.py \
|
||||
nw/gui/statusbar.py \
|
||||
nw/gui/theme.py \
|
||||
nw/tools/build.py \
|
||||
nw/tools/projwizard.py \
|
||||
nw/tools/writingstats.py \
|
||||
nw/common.py \
|
||||
nw/constants.py \
|
||||
nw/error.py \
|
||||
nw/guimain.py
|
||||
|
||||
TRANSLATIONS += \
|
||||
i18n/nw_en_US.ts \
|
||||
i18n/nw_fr.ts \
|
||||
i18n/nw_nb_NO.ts \
|
||||
i18n/nw_pt.ts \
|
||||
i18n/nw_zh_CN.ts
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
novelWriter – Start Script
|
||||
==========================
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import PyQt5.QtWidgets # noqa: F401
|
||||
import PyQt5.QtGui # noqa: F401
|
||||
import PyQt5.QtCore # noqa: F401
|
||||
import PyQt5.QtWidgets # noqa: F401
|
||||
import PyQt5.QtGui # noqa: F401
|
||||
import PyQt5.QtCore # noqa: F401
|
||||
except Exception:
|
||||
print("ERROR: Failed to load dependency python3-pyqt5")
|
||||
print("ERROR: Failed to load dependency PyQt5")
|
||||
sys.exit(1)
|
||||
|
||||
os.curdir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import nw
|
||||
nw.main(sys.argv[1:])
|
||||
|
||||
170
nw/__init__.py
@@ -1,28 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Init
|
||||
"""
|
||||
novelWriter – Init File
|
||||
=======================
|
||||
Application initialisation
|
||||
|
||||
novelWriter – Init File
|
||||
=========================
|
||||
Application initialisation
|
||||
File History:
|
||||
Created: 2018-09-22 [0.0.1]
|
||||
|
||||
File History:
|
||||
Created: 2018-09-22 [0.0.1]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -32,19 +30,19 @@ import logging
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication, QErrorMessage
|
||||
|
||||
from nw.error import exceptionHandler
|
||||
from nw.error import exceptionHandler, logException
|
||||
from nw.config import Config
|
||||
|
||||
#
|
||||
##
|
||||
# Version Scheme
|
||||
# ================
|
||||
# Generally follows PEP 440
|
||||
# Hex Version:
|
||||
# - Digit 1,2 : Major Version (01, 02, 03)
|
||||
# = Digit 3,4 : Minor Version (01, 09, 10, 99)
|
||||
# - Digit 5,6 : Patch Version (01, 09, 10, 99)
|
||||
# = Digit 7 : Release Type (a: aplha, b: beta, c: candidate, f: final)
|
||||
# - Digit 8 : Release Number (0-9)
|
||||
# - 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: aplha, b: beta, c: candidate, f: final)
|
||||
# - Digit 8 : Release Number (0-f)
|
||||
#
|
||||
# Example : Full Short Description
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -55,7 +53,7 @@ from nw.config import Config
|
||||
# 0x010200f0 : 1.2 1.2 Final release
|
||||
# 0x010200f1 : 1.2-post1 1.2.post1 Post release, but not a code patch!
|
||||
# 0x010201f0 : 1.2.1 1.2.1 Patch release
|
||||
#
|
||||
##
|
||||
|
||||
__package__ = "nw"
|
||||
__copyright__ = "Copyright 2018–2021, Veronica Berglyd Olsen"
|
||||
@@ -63,9 +61,9 @@ __license__ = "GPLv3"
|
||||
__author__ = "Veronica Berglyd Olsen"
|
||||
__maintainer__ = "Veronica Berglyd Olsen"
|
||||
__email__ = "code@vkbo.net"
|
||||
__version__ = "1.0"
|
||||
__hexversion__ = "0x010000f0"
|
||||
__date__ = "2021-01-03"
|
||||
__version__ = "1.4"
|
||||
__hexversion__ = "0x010400f0"
|
||||
__date__ = "2021-07-27"
|
||||
__status__ = "Stable"
|
||||
__domain__ = "novelwriter.io"
|
||||
__url__ = "https://novelwriter.io"
|
||||
@@ -74,12 +72,8 @@ __issuesurl__ = "https://github.com/vkbo/novelWriter/issues"
|
||||
__helpurl__ = "https://github.com/vkbo/novelWriter/discussions"
|
||||
__releaseurl__ = "https://github.com/vkbo/novelWriter/releases/latest"
|
||||
__docurl__ = "https://novelwriter.readthedocs.io"
|
||||
__credits__ = [
|
||||
"Veronica Berglyd Olsen (developer)",
|
||||
"Marian Lückhof (contributor, tester)"
|
||||
]
|
||||
|
||||
#
|
||||
##
|
||||
# Logging
|
||||
# =========
|
||||
# Standard used for logging levels in novelWriter:
|
||||
@@ -90,30 +84,34 @@ __credits__ = [
|
||||
# ----------- SPAM Threshold : Output above should be minimal -----------------
|
||||
# DEBUG Use for descriptions of main program flow
|
||||
# VERBOSE Use for outputting values and program flow details
|
||||
#
|
||||
##
|
||||
|
||||
# Add verbose logging level
|
||||
VERBOSE = 5
|
||||
logging.addLevelName(VERBOSE, "VERBOSE")
|
||||
|
||||
|
||||
def logVerbose(self, message, *args, **kws):
|
||||
if self.isEnabledFor(VERBOSE):
|
||||
self._log(VERBOSE, message, args, **kws)
|
||||
|
||||
|
||||
logging.Logger.verbose = logVerbose
|
||||
|
||||
# Initiating logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#
|
||||
|
||||
##
|
||||
# Main Program
|
||||
# ==============
|
||||
#
|
||||
##
|
||||
|
||||
# Load the main config as a global object
|
||||
CONFIG = Config()
|
||||
|
||||
|
||||
def main(sysArgs=None):
|
||||
"""Parses command line, sets up logging, and launches main GUI.
|
||||
"""Parse command line, set up logging, and launches main GUI.
|
||||
"""
|
||||
if sysArgs is None:
|
||||
sysArgs = sys.argv[1:]
|
||||
@@ -133,39 +131,37 @@ def main(sysArgs=None):
|
||||
]
|
||||
|
||||
helpMsg = (
|
||||
"novelWriter {version} ({status} {date})\n"
|
||||
"novelWriter {version} ({date})\n"
|
||||
"{copyright}\n"
|
||||
"\n"
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||
"GNU General Public License for more details.\n"
|
||||
"GNU General Public Licence for more details.\n"
|
||||
"\n"
|
||||
"Usage:\n"
|
||||
" -h, --help Print this message.\n"
|
||||
" -v, --version Print program version and exit.\n"
|
||||
" --info Print additional runtime information.\n"
|
||||
" --debug Print debug output. Includes --info.\n"
|
||||
" --verbose Increase verbosity of debug output. Includes --debug.\n"
|
||||
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
|
||||
" --config= Alternative config file.\n"
|
||||
" --data= Alternative user data path.\n"
|
||||
" --testmode Do not display GUI. Used by the test suite.\n"
|
||||
" -h, --help Print this message.\n"
|
||||
" -v, --version Print program version and exit.\n"
|
||||
" --info Print additional runtime information.\n"
|
||||
" --debug Print debug output. Includes --info.\n"
|
||||
" --verbose Increase verbosity of debug output. Includes --debug.\n"
|
||||
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
|
||||
" --config= Alternative config file.\n"
|
||||
" --data= Alternative user data path.\n"
|
||||
).format(
|
||||
version = __version__,
|
||||
status = __status__,
|
||||
copyright = __copyright__,
|
||||
date = __date__,
|
||||
version=__version__,
|
||||
copyright=__copyright__,
|
||||
date=__date__,
|
||||
)
|
||||
|
||||
# Defaults
|
||||
debugLevel = logging.WARN
|
||||
logFormat = "{levelname:8} {message:}"
|
||||
confPath = None
|
||||
dataPath = None
|
||||
testMode = False
|
||||
qtStyle = "Fusion"
|
||||
cmdOpen = None
|
||||
logLevel = logging.WARN
|
||||
logFormat = "{levelname:8} {message:}"
|
||||
confPath = None
|
||||
dataPath = None
|
||||
testMode = False
|
||||
qtStyle = "Fusion"
|
||||
cmdOpen = None
|
||||
|
||||
# Parse Options
|
||||
try:
|
||||
@@ -183,17 +179,15 @@ def main(sysArgs=None):
|
||||
print(helpMsg)
|
||||
sys.exit(0)
|
||||
elif inOpt in ("-v", "--version"):
|
||||
print("novelWriter %s Version %s [%s]" % (
|
||||
__status__, __version__, __date__)
|
||||
)
|
||||
print("novelWriter Version %s [%s]" % (__version__, __date__))
|
||||
sys.exit(0)
|
||||
elif inOpt == "--info":
|
||||
debugLevel = logging.INFO
|
||||
logLevel = logging.INFO
|
||||
elif inOpt == "--debug":
|
||||
debugLevel = logging.DEBUG
|
||||
logLevel = logging.DEBUG
|
||||
logFormat = "[{asctime:}] {name:>22}:{lineno:<4d} {levelname:8} {message:}"
|
||||
elif inOpt == "--verbose":
|
||||
debugLevel = VERBOSE
|
||||
logLevel = VERBOSE
|
||||
logFormat = "[{asctime:}] {name:>22}:{lineno:<4d} {levelname:8} {message:}"
|
||||
elif inOpt == "--style":
|
||||
qtStyle = inArg
|
||||
@@ -205,21 +199,17 @@ def main(sysArgs=None):
|
||||
testMode = True
|
||||
|
||||
# Set Config Options
|
||||
CONFIG.showGUI = not testMode
|
||||
CONFIG.debugInfo = debugLevel < logging.INFO
|
||||
CONFIG.cmdOpen = cmdOpen
|
||||
CONFIG.cmdOpen = cmdOpen
|
||||
|
||||
# Set Logging
|
||||
logFmt = logging.Formatter(fmt=logFormat, style="{")
|
||||
cHandle = logging.StreamHandler()
|
||||
cHandle.setLevel(debugLevel)
|
||||
cHandle.setFormatter(logFmt)
|
||||
logger.addHandler(cHandle)
|
||||
cHandle.setFormatter(logging.Formatter(fmt=logFormat, style="{"))
|
||||
|
||||
logger.setLevel(debugLevel)
|
||||
logger.info("Starting novelWriter %s (%s) %s" % (
|
||||
__version__, __hexversion__, __date__
|
||||
))
|
||||
pkgLogger = logging.getLogger(__package__)
|
||||
pkgLogger.addHandler(cHandle)
|
||||
pkgLogger.setLevel(logLevel)
|
||||
|
||||
logger.info("Starting novelWriter %s (%s) %s", __version__, __hexversion__, __date__)
|
||||
|
||||
# Check Packages and Versions
|
||||
errorData = []
|
||||
@@ -229,19 +219,19 @@ def main(sysArgs=None):
|
||||
"At least Python 3.6.0 is required, found %s." % CONFIG.verPyString
|
||||
)
|
||||
errorCode |= 4
|
||||
if CONFIG.verQtValue < 50200:
|
||||
if CONFIG.verQtValue < 50300:
|
||||
errorData.append(
|
||||
"At least Qt5 version 5.2 is required, found %s." % CONFIG.verQtString
|
||||
"At least Qt5 version 5.3 is required, found %s." % CONFIG.verQtString
|
||||
)
|
||||
errorCode |= 8
|
||||
if CONFIG.verPyQtValue < 50200:
|
||||
if CONFIG.verPyQtValue < 50300:
|
||||
errorData.append(
|
||||
"At least PyQt5 version 5.2 is required, found %s." % CONFIG.verPyQtString
|
||||
"At least PyQt5 version 5.3 is required, found %s." % CONFIG.verPyQtString
|
||||
)
|
||||
errorCode |= 16
|
||||
|
||||
try:
|
||||
import lxml # noqa: F401
|
||||
import lxml # noqa: F401
|
||||
except ImportError:
|
||||
errorData.append("Python module 'lxml' is missing.")
|
||||
errorCode |= 32
|
||||
@@ -272,9 +262,9 @@ def main(sysArgs=None):
|
||||
bundle = NSBundle.mainBundle()
|
||||
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
|
||||
info["CFBundleName"] = "novelWriter"
|
||||
except ImportError as e:
|
||||
except ImportError:
|
||||
logger.error("Failed to set application name")
|
||||
logger.error(str(e))
|
||||
logException()
|
||||
|
||||
# Import GUI (after dependency checks), and launch
|
||||
from nw.guimain import GuiMain
|
||||
@@ -292,7 +282,13 @@ def main(sysArgs=None):
|
||||
# Connect the exception handler before making the main GUI
|
||||
sys.excepthook = exceptionHandler
|
||||
|
||||
# Launch main GUI
|
||||
CONFIG.initLocalisation(nwApp)
|
||||
nwGUI = GuiMain()
|
||||
if not nwGUI.hasProject:
|
||||
nwGUI.showProjectLoadDialog()
|
||||
nwGUI.releaseNotes()
|
||||
|
||||
sys.exit(nwApp.exec_())
|
||||
|
||||
# END Function main
|
||||
|
||||
105
nw/assets/i18n/project_de.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Synopsis",
|
||||
"Comment": "Kommentar",
|
||||
"Notes": "Notizen",
|
||||
"0": "Null",
|
||||
"1": "Eins",
|
||||
"2": "Zwei",
|
||||
"3": "Drei",
|
||||
"4": "Vier",
|
||||
"5": "Fünf",
|
||||
"6": "Sechs",
|
||||
"7": "Sieben",
|
||||
"8": "Acht",
|
||||
"9": "Neun",
|
||||
"10": "Zehn",
|
||||
"11": "Elf",
|
||||
"12": "Zwölf",
|
||||
"13": "Dreizehen",
|
||||
"14": "Vierzehn",
|
||||
"15": "Fünfzehn",
|
||||
"16": "Sechzehn",
|
||||
"17": "Siebzehn",
|
||||
"18": "Achtzehn",
|
||||
"19": "Neunzehn",
|
||||
"20": "Zwanzig",
|
||||
"21": "Einundzwanzig",
|
||||
"22": "Zweiundzwanzig",
|
||||
"23": "Dreiundzwanzig",
|
||||
"24": "Vierundzwanzig",
|
||||
"25": "Fünfundzwanzig",
|
||||
"26": "Sechsundzwanzig",
|
||||
"27": "Siebenundzwanzig",
|
||||
"28": "Achtundzwanzig",
|
||||
"29": "Neunundzwanzig",
|
||||
"30": "Dreißig",
|
||||
"31": "Einunddreißig",
|
||||
"32": "Zweiunddreißig",
|
||||
"33": "Dreiunddreißig",
|
||||
"34": "Vierunddreißig",
|
||||
"35": "Fünfunddreißig",
|
||||
"36": "Sechsunddreißig",
|
||||
"37": "Siebenunddreißig",
|
||||
"38": "Achtunddreißig",
|
||||
"39": "Neununddreißig",
|
||||
"40": "Vierzig",
|
||||
"41": "Einundvierzig",
|
||||
"42": "Zweiundvierzig",
|
||||
"43": "Dreiundvierzig",
|
||||
"44": "Vierundvierzig",
|
||||
"45": "Fünfundvierzig",
|
||||
"46": "Sechsundvierzig",
|
||||
"47": "Siebenundvierzig",
|
||||
"48": "Achtundvierzig",
|
||||
"49": "Neunundvierzig",
|
||||
"50": "Fünfzig",
|
||||
"51": "Einundfünfzig",
|
||||
"52": "Zweiundfünfzig",
|
||||
"53": "Dreiundfünfzig",
|
||||
"54": "Vierundfünfzig",
|
||||
"55": "Fünfundfünfzig",
|
||||
"56": "Sechsundfünfzig",
|
||||
"57": "Siebenundfünfzig",
|
||||
"58": "Achtundfünfzig",
|
||||
"59": "Neunundfünfzig",
|
||||
"60": "Sechzig",
|
||||
"61": "Einundsechzig",
|
||||
"62": "Zweiundsechzig",
|
||||
"63": "Dreiundsechzig",
|
||||
"64": "Vierundsechzig",
|
||||
"65": "Fünfundsechzig",
|
||||
"66": "Sechsundsechzig",
|
||||
"67": "Siebenundsechzig",
|
||||
"68": "Achtundsechzig",
|
||||
"69": "Neunundsechzig",
|
||||
"70": "Siebzig",
|
||||
"71": "Einundsiebzig",
|
||||
"72": "Zweiundsiebzig",
|
||||
"73": "Dreiundsiebzig",
|
||||
"74": "Vierundsiebzig",
|
||||
"75": "Fünfundsiebzig",
|
||||
"76": "Sechsundsiebzig",
|
||||
"77": "Siebenundsiebzig",
|
||||
"78": "Achtundsiebzig",
|
||||
"79": "Neunundsiebzig",
|
||||
"80": "Achtzig",
|
||||
"81": "Einundachtzig",
|
||||
"82": "Zweiundachtzig",
|
||||
"83": "Dreiundachtzig",
|
||||
"84": "Vierundachtzig",
|
||||
"85": "Fünfundachtzig",
|
||||
"86": "Sechsundachtzig",
|
||||
"87": "Siebenundachtzig",
|
||||
"88": "Achtundachtzig",
|
||||
"89": "Neunundachtzig",
|
||||
"90": "Neunzig",
|
||||
"91": "Einundneunzig",
|
||||
"92": "Zweiundneunzig",
|
||||
"93": "Dreiundneunzig",
|
||||
"94": "Vierundneunzig",
|
||||
"95": "Fünfundneunzig",
|
||||
"96": "Sechsundneunzig",
|
||||
"97": "Siebenundneunzig",
|
||||
"98": "Achtundneunzig",
|
||||
"99": "Neunundneunzig"
|
||||
}
|
||||
105
nw/assets/i18n/project_en.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Synopsis",
|
||||
"Comment": "Comment",
|
||||
"Notes": "Notes",
|
||||
"0": "Zero",
|
||||
"1": "One",
|
||||
"2": "Two",
|
||||
"3": "Three",
|
||||
"4": "Four",
|
||||
"5": "Five",
|
||||
"6": "Six",
|
||||
"7": "Seven",
|
||||
"8": "Eight",
|
||||
"9": "Nine",
|
||||
"10": "Ten",
|
||||
"11": "Eleven",
|
||||
"12": "Twelve",
|
||||
"13": "Thirteen",
|
||||
"14": "Fourteen",
|
||||
"15": "Fifteen",
|
||||
"16": "Sixteen",
|
||||
"17": "Seventeen",
|
||||
"18": "Eighteen",
|
||||
"19": "Nineteen",
|
||||
"20": "Twenty",
|
||||
"21": "Twenty-One",
|
||||
"22": "Twenty-Two",
|
||||
"23": "Twenty-Three",
|
||||
"24": "Twenty-Four",
|
||||
"25": "Twenty-Five",
|
||||
"26": "Twenty-Six",
|
||||
"27": "Twenty-Seven",
|
||||
"28": "Twenty-Eight",
|
||||
"29": "Twenty-Nine",
|
||||
"30": "Thirty",
|
||||
"31": "Thirty-One",
|
||||
"32": "Thirty-Two",
|
||||
"33": "Thirty-Three",
|
||||
"34": "Thirty-Four",
|
||||
"35": "Thirty-Five",
|
||||
"36": "Thirty-Six",
|
||||
"37": "Thirty-Seven",
|
||||
"38": "Thirty-Eight",
|
||||
"39": "Thirty-Nine",
|
||||
"40": "Forty",
|
||||
"41": "Forty-One",
|
||||
"42": "Forty-Two",
|
||||
"43": "Forty-Three",
|
||||
"44": "Forty-Four",
|
||||
"45": "Forty-Five",
|
||||
"46": "Forty-Six",
|
||||
"47": "Forty-Seven",
|
||||
"48": "Forty-Eight",
|
||||
"49": "Forty-Nine",
|
||||
"50": "Fifty",
|
||||
"51": "Fifty-One",
|
||||
"52": "Fifty-Two",
|
||||
"53": "Fifty-Three",
|
||||
"54": "Fifty-Four",
|
||||
"55": "Fifty-Five",
|
||||
"56": "Fifty-Six",
|
||||
"57": "Fifty-Seven",
|
||||
"58": "Fifty-Eight",
|
||||
"59": "Fifty-Nine",
|
||||
"60": "Sixty",
|
||||
"61": "Sixty-One",
|
||||
"62": "Sixty-Two",
|
||||
"63": "Sixty-Three",
|
||||
"64": "Sixty-Four",
|
||||
"65": "Sixty-Five",
|
||||
"66": "Sixty-Six",
|
||||
"67": "Sixty-Seven",
|
||||
"68": "Sixty-Eight",
|
||||
"69": "Sixty-Nine",
|
||||
"70": "Seventy",
|
||||
"71": "Seventy-One",
|
||||
"72": "Seventy-Two",
|
||||
"73": "Seventy-Three",
|
||||
"74": "Seventy-Four",
|
||||
"75": "Seventy-Five",
|
||||
"76": "Seventy-Six",
|
||||
"77": "Seventy-Seven",
|
||||
"78": "Seventy-Eight",
|
||||
"79": "Seventy-Nine",
|
||||
"80": "Eighty",
|
||||
"81": "Eighty-One",
|
||||
"82": "Eighty-Two",
|
||||
"83": "Eighty-Three",
|
||||
"84": "Eighty-Four",
|
||||
"85": "Eighty-Five",
|
||||
"86": "Eighty-Six",
|
||||
"87": "Eighty-Seven",
|
||||
"88": "Eighty-Eight",
|
||||
"89": "Eighty-Nine",
|
||||
"90": "Ninety",
|
||||
"91": "Ninety-One",
|
||||
"92": "Ninety-Two",
|
||||
"93": "Ninety-Three",
|
||||
"94": "Ninety-Four",
|
||||
"95": "Ninety-Five",
|
||||
"96": "Ninety-Six",
|
||||
"97": "Ninety-Seven",
|
||||
"98": "Ninety-Eight",
|
||||
"99": "Ninety-Nine"
|
||||
}
|
||||
105
nw/assets/i18n/project_fr.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Synopsis",
|
||||
"Comment": "Commentaire",
|
||||
"Notes": "Notes",
|
||||
"0": "Zero",
|
||||
"1": "Un",
|
||||
"2": "Deux",
|
||||
"3": "Trois",
|
||||
"4": "Quatre",
|
||||
"5": "Cinq",
|
||||
"6": "Six",
|
||||
"7": "Sept",
|
||||
"8": "Huit",
|
||||
"9": "Neuf",
|
||||
"10": "Dix",
|
||||
"11": "Onze",
|
||||
"12": "Douze",
|
||||
"13": "Treize",
|
||||
"14": "Quatorze",
|
||||
"15": "Quinze",
|
||||
"16": "Seize",
|
||||
"17": "Dix-sept",
|
||||
"18": "Dix-huit",
|
||||
"19": "Dix-neuf",
|
||||
"20": "Vingt",
|
||||
"21": "Vingt et un",
|
||||
"22": "Vingt-deux",
|
||||
"23": "Vingt-trois",
|
||||
"24": "Vingt-quatre",
|
||||
"25": "Vingt-cinq",
|
||||
"26": "Vingt-six",
|
||||
"27": "Vingt-sept",
|
||||
"28": "Vingt-huit",
|
||||
"29": "Vingt-neuf",
|
||||
"30": "Trente",
|
||||
"31": "Trente et un",
|
||||
"32": "Trente-deux",
|
||||
"33": "Trente-trois",
|
||||
"34": "Trente-quatre",
|
||||
"35": "Trente-cinq",
|
||||
"36": "Trente-six",
|
||||
"37": "Trente-sept",
|
||||
"38": "Trente-huit",
|
||||
"39": "Trente-neuf",
|
||||
"40": "Quarante",
|
||||
"41": "Quarante et un",
|
||||
"42": "Quarante-deux",
|
||||
"43": "Quarante-trois",
|
||||
"44": "Quarante-quatre",
|
||||
"45": "Quarante-cinq",
|
||||
"46": "Quarante-six",
|
||||
"47": "Quarante-sept",
|
||||
"48": "Quarante-huit",
|
||||
"49": "Quarante-neuf",
|
||||
"50": "Cinquante",
|
||||
"51": "Cinquante et un",
|
||||
"52": "Cinquante-deux",
|
||||
"53": "Cinquante-trois",
|
||||
"54": "Cinquante-quatre",
|
||||
"55": "Cinquante-cinq",
|
||||
"56": "Cinquante-six",
|
||||
"57": "Cinquante-sept",
|
||||
"58": "Cinquante-huit",
|
||||
"59": "Cinquante-neuf",
|
||||
"60": "Soixante",
|
||||
"61": "Soixante et un",
|
||||
"62": "Soixante-deux",
|
||||
"63": "Soixante-trois",
|
||||
"64": "Soixante-quatre",
|
||||
"65": "Soixante-cinq",
|
||||
"66": "Soixante-six",
|
||||
"67": "Soixante-sept",
|
||||
"68": "Soixante-huit",
|
||||
"69": "Soixante-neuf",
|
||||
"70": "Soixante-dix",
|
||||
"71": "Soixante et onze",
|
||||
"72": "Soixante-douze",
|
||||
"73": "Soixante-treize",
|
||||
"74": "Soixante-quatorze",
|
||||
"75": "Soixante-quinze",
|
||||
"76": "Soixante-seize",
|
||||
"77": "Soixante-dix-sept",
|
||||
"78": "Soixante-dix-huit",
|
||||
"79": "Soixante-dix-neuf",
|
||||
"80": "Quatre-vingts",
|
||||
"81": "Quatre-vingt-un",
|
||||
"82": "Quatre-vingt-deux",
|
||||
"83": "Quatre-vingt-trois",
|
||||
"84": "Quatre-vingt-quatre",
|
||||
"85": "Quatre-vingt-cinq",
|
||||
"86": "Quatre-vingt-six",
|
||||
"87": "Quatre-vingt-sept",
|
||||
"88": "Quatre-vingt-huit",
|
||||
"89": "Quatre-vingt-neuf",
|
||||
"90": "Quatre-vingt-dix",
|
||||
"91": "Quatre-vingt-onze",
|
||||
"92": "Quatre-vingt-douze",
|
||||
"93": "Quatre-vingt-treize",
|
||||
"94": "Quatre-vingt-quatorze",
|
||||
"95": "Quatre-vingt-quinze",
|
||||
"96": "Quatre-vingt-seize",
|
||||
"97": "Quatre-vingt-dix-sept",
|
||||
"98": "Quatre-vingt-dix-huit",
|
||||
"99": "Quatre-vingt-dix-neuf"
|
||||
}
|
||||
105
nw/assets/i18n/project_nb_NO.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Sammendrag",
|
||||
"Comment": "Kommentar",
|
||||
"Notes": "Notat",
|
||||
"0": "null",
|
||||
"1": "én",
|
||||
"2": "to",
|
||||
"3": "tre",
|
||||
"4": "fire",
|
||||
"5": "fem",
|
||||
"6": "seks",
|
||||
"7": "sju",
|
||||
"8": "åtte",
|
||||
"9": "ni",
|
||||
"10": "ti",
|
||||
"11": "elleve",
|
||||
"12": "tolv",
|
||||
"13": "tretten",
|
||||
"14": "fjorten",
|
||||
"15": "femten",
|
||||
"16": "seksten",
|
||||
"17": "sytten",
|
||||
"18": "atten",
|
||||
"19": "nitten",
|
||||
"20": "tjue",
|
||||
"21": "tjueén",
|
||||
"22": "tjueto",
|
||||
"23": "tjuetre",
|
||||
"24": "tjuefire",
|
||||
"25": "tjuefem",
|
||||
"26": "tjueseks",
|
||||
"27": "tjuesju",
|
||||
"28": "tjueåtte",
|
||||
"29": "tjueni",
|
||||
"30": "tretti",
|
||||
"31": "trettién",
|
||||
"32": "trettito",
|
||||
"33": "trettitre",
|
||||
"34": "trettifire",
|
||||
"35": "trettifem",
|
||||
"36": "trettiseks",
|
||||
"37": "trettisju",
|
||||
"38": "trettiåtte",
|
||||
"39": "trettini",
|
||||
"40": "førti",
|
||||
"41": "førtién",
|
||||
"42": "førtito",
|
||||
"43": "førtitre",
|
||||
"44": "førtifire",
|
||||
"45": "førtifem",
|
||||
"46": "førtiseks",
|
||||
"47": "førtisju",
|
||||
"48": "førtiåtte",
|
||||
"49": "førtini",
|
||||
"50": "femti",
|
||||
"51": "femtién",
|
||||
"52": "femtito",
|
||||
"53": "femtitre",
|
||||
"54": "femtifire",
|
||||
"55": "femtifem",
|
||||
"56": "femtiseks",
|
||||
"57": "femtisju",
|
||||
"58": "femtiåtte",
|
||||
"59": "femtini",
|
||||
"60": "seksti",
|
||||
"61": "sekstién",
|
||||
"62": "sekstito",
|
||||
"63": "sekstitre",
|
||||
"64": "sekstifire",
|
||||
"65": "sekstifem",
|
||||
"66": "sekstiseks",
|
||||
"67": "sekstisju",
|
||||
"68": "sekstiåtte",
|
||||
"69": "sekstini",
|
||||
"70": "sytti",
|
||||
"71": "syttién",
|
||||
"72": "syttito",
|
||||
"73": "syttitre",
|
||||
"74": "syttifire",
|
||||
"75": "syttifem",
|
||||
"76": "syttiseks",
|
||||
"77": "syttisju",
|
||||
"78": "syttiåtte",
|
||||
"79": "syttini",
|
||||
"80": "åtti",
|
||||
"81": "åttién",
|
||||
"82": "åttito",
|
||||
"83": "åttitre",
|
||||
"84": "åttifire",
|
||||
"85": "åttifem",
|
||||
"86": "åttiseks",
|
||||
"87": "åttisju",
|
||||
"88": "åttiåtte",
|
||||
"89": "åttini",
|
||||
"90": "nitti",
|
||||
"91": "nittién",
|
||||
"92": "nittito",
|
||||
"93": "nittitre",
|
||||
"94": "nittifire",
|
||||
"95": "nittifem",
|
||||
"96": "nittiseks",
|
||||
"97": "nittisju",
|
||||
"98": "nittiåtte",
|
||||
"99": "nittini"
|
||||
}
|
||||
105
nw/assets/i18n/project_nn_NO.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Samandrag",
|
||||
"Comment": "Kommentar",
|
||||
"Notes": "Notat",
|
||||
"0": "null",
|
||||
"1": "ein",
|
||||
"2": "to",
|
||||
"3": "tre",
|
||||
"4": "fire",
|
||||
"5": "fem",
|
||||
"6": "seks",
|
||||
"7": "sju",
|
||||
"8": "åtte",
|
||||
"9": "ni",
|
||||
"10": "ti",
|
||||
"11": "elleve",
|
||||
"12": "tolv",
|
||||
"13": "tretten",
|
||||
"14": "fjorten",
|
||||
"15": "femten",
|
||||
"16": "seksten",
|
||||
"17": "sytten",
|
||||
"18": "atten",
|
||||
"19": "nitten",
|
||||
"20": "tjue",
|
||||
"21": "tjueein",
|
||||
"22": "tjueto",
|
||||
"23": "tjuetre",
|
||||
"24": "tjuefire",
|
||||
"25": "tjuefem",
|
||||
"26": "tjueseks",
|
||||
"27": "tjuesju",
|
||||
"28": "tjueåtte",
|
||||
"29": "tjueni",
|
||||
"30": "tretti",
|
||||
"31": "trettiein",
|
||||
"32": "trettito",
|
||||
"33": "trettitre",
|
||||
"34": "trettifire",
|
||||
"35": "trettifem",
|
||||
"36": "trettiseks",
|
||||
"37": "trettisju",
|
||||
"38": "trettiåtte",
|
||||
"39": "trettini",
|
||||
"40": "førti",
|
||||
"41": "førtiein",
|
||||
"42": "førtito",
|
||||
"43": "førtitre",
|
||||
"44": "førtifire",
|
||||
"45": "førtifem",
|
||||
"46": "førtiseks",
|
||||
"47": "førtisju",
|
||||
"48": "førtiåtte",
|
||||
"49": "førtini",
|
||||
"50": "femti",
|
||||
"51": "femtiein",
|
||||
"52": "femtito",
|
||||
"53": "femtitre",
|
||||
"54": "femtifire",
|
||||
"55": "femtifem",
|
||||
"56": "femtiseks",
|
||||
"57": "femtisju",
|
||||
"58": "femtiåtte",
|
||||
"59": "femtini",
|
||||
"60": "seksti",
|
||||
"61": "sekstiein",
|
||||
"62": "sekstito",
|
||||
"63": "sekstitre",
|
||||
"64": "sekstifire",
|
||||
"65": "sekstifem",
|
||||
"66": "sekstiseks",
|
||||
"67": "sekstisju",
|
||||
"68": "sekstiåtte",
|
||||
"69": "sekstini",
|
||||
"70": "sytti",
|
||||
"71": "syttiein",
|
||||
"72": "syttito",
|
||||
"73": "syttitre",
|
||||
"74": "syttifire",
|
||||
"75": "syttifem",
|
||||
"76": "syttiseks",
|
||||
"77": "syttisju",
|
||||
"78": "syttiåtte",
|
||||
"79": "syttini",
|
||||
"80": "åtti",
|
||||
"81": "åttiein",
|
||||
"82": "åttito",
|
||||
"83": "åttitre",
|
||||
"84": "åttifire",
|
||||
"85": "åttifem",
|
||||
"86": "åttiseks",
|
||||
"87": "åttisju",
|
||||
"88": "åttiåtte",
|
||||
"89": "åttini",
|
||||
"90": "nitti",
|
||||
"91": "nittiein",
|
||||
"92": "nittito",
|
||||
"93": "nittitre",
|
||||
"94": "nittifire",
|
||||
"95": "nittifem",
|
||||
"96": "nittiseks",
|
||||
"97": "nittisju",
|
||||
"98": "nittiåtte",
|
||||
"99": "nittini"
|
||||
}
|
||||
105
nw/assets/i18n/project_pt.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Sinopse",
|
||||
"Comment": "Comentários",
|
||||
"Notes": "Notas",
|
||||
"0": "Zero",
|
||||
"1": "Um",
|
||||
"2": "Dois",
|
||||
"3": "Três",
|
||||
"4": "Quatro",
|
||||
"5": "Cinco",
|
||||
"6": "Seis",
|
||||
"7": "Sete",
|
||||
"8": "Oito",
|
||||
"9": "Nove",
|
||||
"10": "Dez",
|
||||
"11": "Onze",
|
||||
"12": "Doze",
|
||||
"13": "Treze",
|
||||
"14": "Quatorze",
|
||||
"15": "Quinze",
|
||||
"16": "Dezesseis",
|
||||
"17": "Dezesete",
|
||||
"18": "Dezoito",
|
||||
"19": "Dezenove",
|
||||
"20": "Vinte",
|
||||
"21": "Vinte e Um",
|
||||
"22": "Vinte e Dois",
|
||||
"23": "Vinte e Três",
|
||||
"24": "Vinte e Quatro",
|
||||
"25": "Vinte e Cinco",
|
||||
"26": "Vinte e Seis",
|
||||
"27": "Vinte e Sete",
|
||||
"28": "Vinte e Oito",
|
||||
"29": "Vinte e Nove",
|
||||
"30": "Trinta",
|
||||
"31": "Trinta e Um",
|
||||
"32": "Trinta e Dois",
|
||||
"33": "Trinta e Três",
|
||||
"34": "Trinta e Quatro",
|
||||
"35": "Trinta e Cinco",
|
||||
"36": "Trinta e Seis",
|
||||
"37": "Trinta e Sete",
|
||||
"38": "Trinta e Oito",
|
||||
"39": "Trinta e Nove",
|
||||
"40": "Quarenta",
|
||||
"41": "Quarenta e Um",
|
||||
"42": "Quarenta e Dois",
|
||||
"43": "Quarenta e Três",
|
||||
"44": "Quarenta e Quatro",
|
||||
"45": "Quarenta e Cinco",
|
||||
"46": "Quarenta e Seis",
|
||||
"47": "Quarenta e Sete",
|
||||
"48": "Quarenta e Oito",
|
||||
"49": "Quarenta e Nove",
|
||||
"50": "Cinquenta",
|
||||
"51": "Cinquenta e Um",
|
||||
"52": "Cinquenta e Dois",
|
||||
"53": "Cinquenta e Três",
|
||||
"54": "Cinquenta e Quatro",
|
||||
"55": "Cinquenta e Cinco",
|
||||
"56": "Cinquenta e Seis",
|
||||
"57": "Cinquenta e Sete",
|
||||
"58": "Cinquenta e Oito",
|
||||
"59": "Cinquenta e Nove",
|
||||
"60": "Sessenta",
|
||||
"61": "Sessenta e Um",
|
||||
"62": "Sessenta e Dois",
|
||||
"63": "Sessenta e Três",
|
||||
"64": "Sessenta e Quatro",
|
||||
"65": "Sessenta e Cinco",
|
||||
"66": "Sessenta e Seis",
|
||||
"67": "Sessenta e Sete",
|
||||
"68": "Sessenta e Oito",
|
||||
"69": "Sessenta e Nove",
|
||||
"70": "Setenta",
|
||||
"71": "Setenta e Um",
|
||||
"72": "Setenta e Dois",
|
||||
"73": "Setenta e Três",
|
||||
"74": "Setenta e Quatro",
|
||||
"75": "Setenta e Cinco",
|
||||
"76": "Setenta e Seis",
|
||||
"77": "Setenta e Sete",
|
||||
"78": "Setenta e Oito",
|
||||
"79": "Setenta e Nove",
|
||||
"80": "Oitenta",
|
||||
"81": "Oitenta e Um",
|
||||
"82": "Oitenta e Dois",
|
||||
"83": "Oitenta e Três",
|
||||
"84": "Oitenta e Quatro",
|
||||
"85": "Oitenta e Cinco",
|
||||
"86": "Oitenta e Seis",
|
||||
"87": "Oitenta e Sete",
|
||||
"88": "Oitenta e Oito",
|
||||
"89": "Oitenta e Nove",
|
||||
"90": "Noventa",
|
||||
"91": "Noventa e Um",
|
||||
"92": "Noventa e Dois",
|
||||
"93": "Noventa e Três",
|
||||
"94": "Noventa e Quatro",
|
||||
"95": "Noventa e Cinco",
|
||||
"96": "Noventa e Seis",
|
||||
"97": "Noventa e Sete",
|
||||
"98": "Noventa e Oito",
|
||||
"99": "Noventa e Nove"
|
||||
}
|
||||
105
nw/assets/i18n/project_zh_CN.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "概要",
|
||||
"Comment": "注释",
|
||||
"Notes": "笔记",
|
||||
"0": "零",
|
||||
"1": "一",
|
||||
"2": "二",
|
||||
"3": "三",
|
||||
"4": "四",
|
||||
"5": "五",
|
||||
"6": "六",
|
||||
"7": "七",
|
||||
"8": "八",
|
||||
"9": "九",
|
||||
"10": "十",
|
||||
"11": "十一",
|
||||
"12": "十二",
|
||||
"13": "十三",
|
||||
"14": "十四",
|
||||
"15": "十五",
|
||||
"16": "十六",
|
||||
"17": "十七",
|
||||
"18": "十八",
|
||||
"19": "十九",
|
||||
"20": "二十",
|
||||
"21": "二十一",
|
||||
"22": "二十二",
|
||||
"23": "二十三",
|
||||
"24": "二十四",
|
||||
"25": "二十五",
|
||||
"26": "二十六",
|
||||
"27": "二十七",
|
||||
"28": "二十八",
|
||||
"29": "二十九",
|
||||
"30": "三十",
|
||||
"31": "三十一",
|
||||
"32": "三十二",
|
||||
"33": "三十三",
|
||||
"34": "三十四",
|
||||
"35": "三十五",
|
||||
"36": "三十六",
|
||||
"37": "三十七",
|
||||
"38": "三十八",
|
||||
"39": "三十九",
|
||||
"40": "四十",
|
||||
"41": "四十一",
|
||||
"42": "四十二",
|
||||
"43": "四十三",
|
||||
"44": "四十四",
|
||||
"45": "四十五",
|
||||
"46": "四十六",
|
||||
"47": "四十七",
|
||||
"48": "四十八",
|
||||
"49": "四十九",
|
||||
"50": "五十",
|
||||
"51": "五十一",
|
||||
"52": "五十二",
|
||||
"53": "五十三",
|
||||
"54": "五十四",
|
||||
"55": "五十五",
|
||||
"56": "五十六",
|
||||
"57": "五十七",
|
||||
"58": "五十八",
|
||||
"59": "五十九",
|
||||
"60": "六十",
|
||||
"61": "六十一",
|
||||
"62": "六十二",
|
||||
"63": "六十三",
|
||||
"64": "六十四",
|
||||
"65": "六十五",
|
||||
"66": "六十六",
|
||||
"67": "六十七",
|
||||
"68": "六十八",
|
||||
"69": "六十九",
|
||||
"70": "七十",
|
||||
"71": "七十一",
|
||||
"72": "七十二",
|
||||
"73": "七十三",
|
||||
"74": "七十四",
|
||||
"75": "七十五",
|
||||
"76": "七十六",
|
||||
"77": "七十七",
|
||||
"78": "七十八",
|
||||
"79": "七十九",
|
||||
"80": "八十",
|
||||
"81": "八十一",
|
||||
"82": "八十二",
|
||||
"83": "八十三",
|
||||
"84": "八十四",
|
||||
"85": "八十五",
|
||||
"86": "八十六",
|
||||
"87": "八十七",
|
||||
"88": "八十八",
|
||||
"89": "八十九",
|
||||
"90": "九十",
|
||||
"91": "九十一",
|
||||
"92": "九十二",
|
||||
"93": "九十三",
|
||||
"94": "九十四",
|
||||
"95": "九十五",
|
||||
"96": "九十六",
|
||||
"97": "九十七",
|
||||
"98": "九十八",
|
||||
"99": "九十九",
|
||||
}
|
||||
31
nw/assets/icons/fallback/settings-dark.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.2"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg1989">
|
||||
<metadata
|
||||
id="metadata1995">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1993" />
|
||||
<path
|
||||
d="M 8.3268645,20.0768 8.9719703,22.448 C 9.2032354,23.3008 10.175241,24 11.131598,24 h 1.738831 c 0.956356,0 1.928363,-0.6992 2.159627,-1.552 l 0.645108,-2.3712 c 0.231264,-0.8528 1.173709,-1.3536 2.091812,-1.1104 l 2.550866,0.672 c 0.91984,0.2416 2.06573,-0.1824 2.542169,-0.9456 l 0.869415,-1.3872 c 0.476441,-0.7632 0.307773,-1.8864 -0.380803,-2.4992 l -1.909236,-1.6976 c -0.688578,-0.6128 -0.688578,-1.6128 0.0017,-2.224 L 23.346891,9.1872 C 24.035507,8.5759996 24.205912,7.4527996 23.729472,6.6895996 L 22.858272,5.3024 C 22.380094,4.5392 21.235943,4.1152 20.317842,4.3568 l -2.550866,0.672 C 16.847134,5.2704 15.906428,4.7712 15.673424,3.9184 L 15.030056,1.5504 C 14.798792,0.69919996 13.826785,0 12.870429,0 H 11.131598 C 10.175241,0 9.2032354,0.69919996 8.9719703,1.552 L 8.3286034,3.92 C 8.0955994,4.7728 7.1548921,5.2736 6.2350507,5.032 L 3.684186,4.36 C 2.7643447,4.1168 1.6184552,4.5424 1.1420155,5.304 L 0.27260019,6.6912 c -0.47643954,0.7632 -0.307773,1.8864 0.38254277,2.4991996 L 2.5609015,10.8848 c 0.6868381,0.6128 0.6868381,1.6128 0,2.2256 L 0.65166536,14.808 c -0.68683817,0.6128 -0.85898242,1.736 -0.38080407,2.4992 l 0.87115421,1.3872 c 0.4764397,0.7632 1.6223292,1.1872 2.5421705,0.9456 l 2.5508647,-0.672 c 0.9181025,-0.2448 1.8605487,0.256 2.0918138,1.1088 z M 12.001014,8.8000004 c 1.919668,0 3.477662,1.4319996 3.477662,3.1999996 0,1.7664 -1.557994,3.2 -3.477662,3.2 -1.91967,0 -3.4776615,-1.4336 -3.4776615,-3.2 0,-1.768 1.5579915,-3.1999996 3.4776615,-3.1999996 z"
|
||||
id="path1987"
|
||||
style="fill:#aeaeae;fill-opacity:1;stroke-width:1.66797" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
31
nw/assets/icons/fallback/settings.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.2"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg1989">
|
||||
<metadata
|
||||
id="metadata1995">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1993" />
|
||||
<path
|
||||
d="M 8.3268645,20.0768 8.9719703,22.448 C 9.2032354,23.3008 10.175241,24 11.131598,24 h 1.738831 c 0.956356,0 1.928363,-0.6992 2.159627,-1.552 l 0.645108,-2.3712 c 0.231264,-0.8528 1.173709,-1.3536 2.091812,-1.1104 l 2.550866,0.672 c 0.91984,0.2416 2.06573,-0.1824 2.542169,-0.9456 l 0.869415,-1.3872 c 0.476441,-0.7632 0.307773,-1.8864 -0.380803,-2.4992 l -1.909236,-1.6976 c -0.688578,-0.6128 -0.688578,-1.6128 0.0017,-2.224 L 23.346891,9.1872 C 24.035507,8.5759996 24.205912,7.4527996 23.729472,6.6895996 L 22.858272,5.3024 C 22.380094,4.5392 21.235943,4.1152 20.317842,4.3568 l -2.550866,0.672 C 16.847134,5.2704 15.906428,4.7712 15.673424,3.9184 L 15.030056,1.5504 C 14.798792,0.69919996 13.826785,0 12.870429,0 H 11.131598 C 10.175241,0 9.2032354,0.69919996 8.9719703,1.552 L 8.3286034,3.92 C 8.0955994,4.7728 7.1548921,5.2736 6.2350507,5.032 L 3.684186,4.36 C 2.7643447,4.1168 1.6184552,4.5424 1.1420155,5.304 L 0.27260019,6.6912 c -0.47643954,0.7632 -0.307773,1.8864 0.38254277,2.4991996 L 2.5609015,10.8848 c 0.6868381,0.6128 0.6868381,1.6128 0,2.2256 L 0.65166536,14.808 c -0.68683817,0.6128 -0.85898242,1.736 -0.38080407,2.4992 l 0.87115421,1.3872 c 0.4764397,0.7632 1.6223292,1.1872 2.5421705,0.9456 l 2.5508647,-0.672 c 0.9181025,-0.2448 1.8605487,0.256 2.0918138,1.1088 z M 12.001014,8.8000004 c 1.919668,0 3.477662,1.4319996 3.477662,3.1999996 0,1.7664 -1.557994,3.2 -3.477662,3.2 -1.91967,0 -3.4776615,-1.4336 -3.4776615,-3.2 0,-1.768 1.5579915,-3.1999996 3.4776615,-3.1999996 z"
|
||||
id="path1987"
|
||||
style="fill:#000000;fill-opacity:0.721569;stroke-width:1.66797" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
31
nw/assets/icons/fallback/status_idle-dark.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.2"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg5582">
|
||||
<metadata
|
||||
id="metadata5588">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs5586" />
|
||||
<path
|
||||
d="m 6.7499997,3 c -1.656,0 -3,1.3440001 -3,3.0000001 V 17.999999 c 0,1.656 1.344,3.000001 3,3.000001 1.6560002,0 2.9999998,-1.344001 2.9999998,-3.000001 V 6.0000001 C 9.7499995,4.3440001 8.4059999,3 6.7499997,3 Z M 17.249999,3 c -1.656,0 -3.000001,1.3440001 -3.000001,3.0000001 V 17.999999 c 0,1.656 1.344001,3.000001 3.000001,3.000001 1.656,0 3.000001,-1.344001 3.000001,-3.000001 V 6.0000001 C 20.25,4.3440001 18.905999,3 17.249999,3 Z"
|
||||
id="path5580"
|
||||
style="fill:#aeaeae;fill-opacity:1;stroke-width:1.49999" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
31
nw/assets/icons/fallback/status_idle.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.2"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg5582">
|
||||
<metadata
|
||||
id="metadata5588">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs5586" />
|
||||
<path
|
||||
d="m 6.7499997,3 c -1.656,0 -3,1.3440001 -3,3.0000001 V 17.999999 c 0,1.656 1.344,3.000001 3,3.000001 1.6560002,0 2.9999998,-1.344001 2.9999998,-3.000001 V 6.0000001 C 9.7499995,4.3440001 8.4059999,3 6.7499997,3 Z M 17.249999,3 c -1.656,0 -3.000001,1.3440001 -3.000001,3.0000001 V 17.999999 c 0,1.656 1.344001,3.000001 3.000001,3.000001 1.656,0 3.000001,-1.344001 3.000001,-3.000001 V 6.0000001 C 20.25,4.3440001 18.905999,3 17.249999,3 Z"
|
||||
id="path5580"
|
||||
style="fill:#000000;fill-opacity:0.780392;stroke-width:1.49999" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
31
nw/assets/icons/typicons_colour_dark/heading0.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg1897"
|
||||
viewBox="0 0 24 24"
|
||||
height="24"
|
||||
width="24"
|
||||
version="1.2">
|
||||
<metadata
|
||||
id="metadata1903">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1901" />
|
||||
<path
|
||||
style="fill:#6699cc;fill-opacity:1;stroke-width:1.42859697"
|
||||
id="path1895"
|
||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -29,6 +29,7 @@ cls_archive = delete.svg
|
||||
cls_trash = trash.svg
|
||||
proj_document = document-text.svg
|
||||
proj_folder = folder.svg
|
||||
doc_h0 = heading0.svg
|
||||
doc_h1 = heading1.svg
|
||||
doc_h2 = heading2.svg
|
||||
doc_h3 = heading3.svg
|
||||
|
||||
31
nw/assets/icons/typicons_colour_light/heading0.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg1897"
|
||||
viewBox="0 0 24 24"
|
||||
height="24"
|
||||
width="24"
|
||||
version="1.2">
|
||||
<metadata
|
||||
id="metadata1903">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1901" />
|
||||
<path
|
||||
style="fill:#4271ae;fill-opacity:1;stroke-width:1.42859697"
|
||||
id="path1895"
|
||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -29,6 +29,7 @@ cls_archive = delete.svg
|
||||
cls_trash = trash.svg
|
||||
proj_document = document-text.svg
|
||||
proj_folder = folder.svg
|
||||
doc_h0 = heading0.svg
|
||||
doc_h1 = heading1.svg
|
||||
doc_h2 = heading2.svg
|
||||
doc_h3 = heading3.svg
|
||||
|
||||
31
nw/assets/icons/typicons_grey_dark/heading0.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg1897"
|
||||
viewBox="0 0 24 24"
|
||||
height="24"
|
||||
width="24"
|
||||
version="1.2">
|
||||
<metadata
|
||||
id="metadata1903">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1901" />
|
||||
<path
|
||||
style="fill:#aeaeae;fill-opacity:1;stroke-width:1.42859697"
|
||||
id="path1895"
|
||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -29,6 +29,7 @@ cls_archive = delete.svg
|
||||
cls_trash = trash.svg
|
||||
proj_document = document-text.svg
|
||||
proj_folder = folder.svg
|
||||
doc_h0 = heading0.svg
|
||||
doc_h1 = heading1.svg
|
||||
doc_h2 = heading2.svg
|
||||
doc_h3 = heading3.svg
|
||||
|
||||
31
nw/assets/icons/typicons_grey_light/heading0.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg1897"
|
||||
viewBox="0 0 24 24"
|
||||
height="24"
|
||||
width="24"
|
||||
version="1.2">
|
||||
<metadata
|
||||
id="metadata1903">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1901" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:0.78039217;stroke-width:1.42859697"
|
||||
id="path1895"
|
||||
d="m 3.1443377,2.8368008 c -1.1157342,1.1157342 -1.1157342,2.9243378 0,4.040072 L 8.2658581,11.999821 3.1443377,17.12277 c -1.1157342,1.115734 -1.1157342,2.924338 0,4.040072 C 3.7014904,21.721423 4.4329321,22 5.1643736,22 5.8958159,22 6.627257,21.721423 7.1844103,21.162842 L 16.348859,11.999821 7.1844103,2.8368008 c -1.1143067,-1.1157343 -2.9257671,-1.1157343 -4.0400726,0 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -29,6 +29,7 @@ cls_archive = delete.svg
|
||||
cls_trash = trash.svg
|
||||
proj_document = document-text.svg
|
||||
proj_folder = folder.svg
|
||||
doc_h0 = heading0.svg
|
||||
doc_h1 = heading1.svg
|
||||
doc_h2 = heading2.svg
|
||||
doc_h3 = heading3.svg
|
||||
|
||||
@@ -2,90 +2,52 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Release Notes for 1.0</h2>
|
||||
<p><i>Released on 3 January 2021</i></p>
|
||||
<p>Based on my own testing and usage, and no serious bugs discovered in quite some time (aside from
|
||||
a few corner case issues), it appears that novelWriter is stable enough for a 1.0 release. Thanks
|
||||
to all the new users who keep providing feedback on bugs, cosmetic issues, or suggesting
|
||||
improvements and new features. I'm glad to hear that others find my application useful, and I will
|
||||
keep making improvements as I get new suggestions and have new ideas myself. At the same time, I
|
||||
will continue to keep novelWriter simple and clean and avoid feature-bloat.</p>
|
||||
<p>This release mainly fixes cosmetic and other minor issues to the user interface and makes a few
|
||||
minor improvements to some less used features. Aside from this, nothing major has changed since the
|
||||
last release candidate.</p>
|
||||
<p>This release concludes over two years of tinkering with this project. The project grew out of
|
||||
numerous lunch and coffee discussions with my colleague Marian Lückhof at my former job. We were
|
||||
both looking for a tool for writing novels on Linux that suited our needs. We started assembling a
|
||||
wish list of features that has become novelWriter. In addition, users on GitHub have continued to
|
||||
test new features, provide very helpful feedback, and make new suggestions for improvements.
|
||||
Especially the feedback from @johnblommers has been helpful during much of the initial development
|
||||
time. Over the last months more users have started posting ideas and feedback. Thanks to all of you
|
||||
for your contributions.</p>
|
||||
<p>The 1.0 release is intended as a first release of the core features of novelWriter. That does
|
||||
not mean that all planned features have been fully implemented. There is a long list of ideas and
|
||||
suggestions to consider and implement. New ideas and suggestions are welcome. Either as feature
|
||||
requests in the issue tracker, or if not fully formed, can be discussed on the
|
||||
<a href="https://github.com/vkbo/novelWriter/discussions">discussions page</a>.
|
||||
<h2>Release Notes for 1.4</h2>
|
||||
<p><i>Released on 27 July 2021</i></p>
|
||||
|
||||
<p><i>The full changelog is available
|
||||
<a href="https://github.com/vkbo/novelWriter/blob/main/CHANGELOG.md">here</a>.</i></p>
|
||||
<p>This release contains some new features and a lot of code refactoring. Among the main new
|
||||
features is paragraph alignment and indentation. Regular line breaks within paragraphs are now also
|
||||
supported. The changes are described in more detail below.</p>
|
||||
|
||||
<hr/>
|
||||
<h3>Internationalisation</h3>
|
||||
<p>Translation files have been added for Simplified Chinese by Qianzhi Long.</p>
|
||||
|
||||
<h2>Release Notes for 1.0 RC2</h2>
|
||||
<p><i>Released on 13 December 2020</i></p>
|
||||
<p>This second release candidate for 1.0 comes with only minor changes and improvements, and a
|
||||
handful of minor bugfixes.</p>
|
||||
<p>Among the improvements is the addition of all the possible @-keywords for tags and references to
|
||||
the "Insert" menu under the sub-menu "Tags and References". The "Help" menu has also received a few
|
||||
improvements and additional links to useful webpages. This release also adds a "Release" notes tab
|
||||
to the "About novelWriter" dialog. The release notes are displayed automatically the first time you
|
||||
launch novelWriter after updating to a new version.</p>
|
||||
<p>Among the fixes is better support for high resolution screens. A few elements on the GUI did not
|
||||
scale properly, among them the document editor and viewer header and footer. These were clipped on
|
||||
high res screens due to an underlying issue with the Qt widget underestimating how much space it
|
||||
required to accommodate the text. Unfortunately, dragging the novelWriter app between screens of
|
||||
different scaling factors is not currently supported. However, the GUI should scale properly to the
|
||||
scaling factor on the screen it is opened on.</p>
|
||||
<p>The work leading up to this release has mostly been focused on improving the test coverage of
|
||||
the source code of novelWriter. This helps to ensure that the code does what it is intended to do,
|
||||
and is able to handle corner cases and unexpected external errors and user actions that may occur.
|
||||
While writing these tests, a number of minor potential issues have been uncovered and handled. Most
|
||||
of these are corner cases that may not even be reachable by unexpected user actions.</p>
|
||||
<p>Hopefully, these changes have resulted in an even more stable version of novelWriter. If no more
|
||||
issues are discovered, the next release will be the final version 1.0 release.</p>
|
||||
<h3>Line Breaks</h3>
|
||||
<p>The way line breaks inside paragraphs work has been changed. A single line break is now treated
|
||||
as a proper line break and will show up in the document viewer and exported documents. A single
|
||||
line break does not start a new paragraph, but forces a break inside the paragraph like a Shift +
|
||||
Enter does in most rich text editors. Two line breaks is still needed to start a new paragraph.</p>
|
||||
<p>The old syntax of adding two spaces at the end of a line to force a line break within a
|
||||
paragraph will still work as before, so there is no need to change your existing text if you've
|
||||
used this feature. However, there is a new highlighting feature that will show you where in the
|
||||
text you have redundant spaces. If you are used to having double spaces between sentences, you may
|
||||
want to switch off this highlighting feature in Preferences as it will also detect those.</p>
|
||||
<p>A helper function has been added to the Format menu that can look through a paragraph and remove
|
||||
line breaks in case you've been using line breaks inside your existing text under the assumption
|
||||
that the exporter and viewer will ignore them.</p>
|
||||
<p>I hope this change will not be too inconvenient. I believe the new behaviour will make more
|
||||
sense for most people. Especially considering some of the feedback I've gotten on how line breaks
|
||||
work. The original implementation was following the Markdown standard, but since novelWriter is not
|
||||
a proper Markdown editor and instead just borrows from Markdown, this behaviour always seemed a bit
|
||||
unnecessary.</p>
|
||||
|
||||
<hr/>
|
||||
<h3>Text Alignment and Indentation</h3>
|
||||
<p>The default text alignment is left or justified based on your preferences. For documents with
|
||||
the layout set to Title Page or Partitions, the default is centred. However, sometimes you may
|
||||
want to override this default. A new set of codes have therefore been added to allow specifying
|
||||
alignment as well as additional text margins on individual paragraphs.</p>
|
||||
<p>The logic of the syntax is as follows:</p>
|
||||
<p>A single angle bracket will push the text away from the edge it points away from. Therefore, a
|
||||
single '>' before the paragraph, or a single '<' after the paragraph, will add indentation on
|
||||
the respective side. It's perfectly valid to do this on both sides at the same time.</p>
|
||||
<p>A double set of angle brackets will push the text all the way towards the opposite side.
|
||||
Therefore, a double set of '>>' before the paragraph will indicate right alignment, and a
|
||||
double set of '<<' after the paragraph will force left alignment. Also here both can be used
|
||||
at the same time, which results in the paragraph being centred.</p>
|
||||
<p>Format menu entries and keyboard shortcuts have been added so that you don't have to memorise
|
||||
these codes.</p>
|
||||
|
||||
<h2>Release Notes for 1.0 RC1</h2>
|
||||
<p><i>Released on 16 November 2020</i></p>
|
||||
<p>This is the first release candidate for the upcoming release of novelWriter 1.0.</p>
|
||||
<p>Since the fifth beta release about four weeks ago, not much has been changed in novelWriter. A
|
||||
few minor tweaks have been made to the GUI.</p>
|
||||
<p>A number of features and tools are now automatically switched off when there is no project or
|
||||
document open for those features to act upon. Previously, this was a bit inconsistent, although no
|
||||
serious bugs have been reported or encountered.</p>
|
||||
<p>Most of the minor changes in this release should not be noticeable to most users. However, there
|
||||
are a couple of noticeable changes.</p>
|
||||
<h3>Typewriter Mode</h3>
|
||||
<p>The "Typewriter Mode" of the editor has been improved. Essentially, this feature is a sort of
|
||||
smart scroll. It tries to keep the cursor stationary in the vertical direction, and will try to
|
||||
scroll the document up when the cursor skips to a new line while typing (or down in case of
|
||||
backspaces). This is similar to the way a typewriter scrolls the paper when hitting the return key.
|
||||
It improves the writing experience as the current active line will stay at the same eye height
|
||||
level on the screen.</p>
|
||||
<p>Previously, the feature would lock the cursor to a given vertical position defined by the user.
|
||||
Now, instead, the cursor will remain stationary in the vertical direction at any position the user
|
||||
sets it to by mouse click or keyboard navigation. The user can define a minimum distance from the
|
||||
top where this feature is activated. These changes makes it more flexible in terms of where the
|
||||
focus is in the editor. The feature can be controlled from the main Preferences.</p>
|
||||
<h3>Switching Syntax Theme</h3>
|
||||
<p>It is now possible to switch syntax highlighting theme without restarting novelWriter.
|
||||
Previously, changing the theme would only half-way update the document, header and footer
|
||||
background and text colours. The new settings would not be fully applied until the application was
|
||||
shut down and started again, thus making it a bit tedious to look through syntax themes to find the
|
||||
one you want.</p>
|
||||
<p>Switching main GUI theme still requires a restart.</p>
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
137
nw/assets/text/release_notes_1.0.htm
Normal file
@@ -0,0 +1,137 @@
|
||||
<!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Release Notes for 1.0.4</h2>
|
||||
<p><i>Released on 3 February 2021</i></p>
|
||||
<p>This patch release fixes a couple of minor issues with the Preferences dialog and the behaviour
|
||||
of one of the keyboard shortcuts.</p>
|
||||
<p>Aside from these fixes, the main point of this patch is to add new setup features for
|
||||
novelWriter on Windows. A Windows installer will no longer be provided for the foreseeable future,
|
||||
and instead functionality has been added to the main setup script to create desktop and start menu
|
||||
icons.</p>
|
||||
|
||||
<p><i>The full changelog is available
|
||||
<a href="https://github.com/vkbo/novelWriter/blob/main/CHANGELOG.md">here</a>.</i></p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.0.3</h2>
|
||||
<p><i>Released on 24 January 2021</i></p>
|
||||
<p>This patch release fixes a minor bug sometimes encountered when running novelWriter from command
|
||||
line on Windows. In addition, the Solarized Dark and Solarized Light themes have been added to the
|
||||
selection of GUI and syntax themes by a user contribution.</p>
|
||||
<p>The main change in this release is to the install scripts and the documentation related to
|
||||
installing and running novelWriter. The primary change is a different method of packaging the app
|
||||
for Windows. Instead of building an <i>.exe</i> file, the new setup instead builds a runnable zip
|
||||
file <i>.pyz</i>. The executable would often be mistakenly flagged by virus control software due to
|
||||
the packaging tools. This is a known problem with pyinstaller and similar tools, but such warnings
|
||||
are always concerning even if they are false positives.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.0.2</h2>
|
||||
<p><i>Released on 19 January 2021</i></p>
|
||||
<p>This patch release fixes a few minor cosmetic issues, a minor issue with the indexer, and a bug
|
||||
when adding words to the user's own spell check dictionary. Additionally, the documentation has
|
||||
been updated based on user feedback, and some install issues resolved.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.0.1</h2>
|
||||
<p><i>Released on 10 January 2021</i></p>
|
||||
<p>This release is mainly to bring the documentation up to date, as I forgot to update the install
|
||||
instructions in the original 1.0 release. I also forgot to change the various settings and help
|
||||
texts that describe novelWriter as under initial development (beta state).</p>
|
||||
<p>Some minor improvements have been made to the "Edit Project Item" dialog and some restrictions
|
||||
on the settings available for documents created in the "Outtakes" folder relaxed. A few minor
|
||||
issues with the document and project changed icons on the status bar have also been resolved. The
|
||||
indicators were previously set to changed status even if no actual change had been made to the
|
||||
project.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.0</h2>
|
||||
<p><i>Released on 3 January 2021</i></p>
|
||||
<p>Based on my own testing and usage, and no serious bugs discovered in quite some time (aside from
|
||||
a few corner case issues), it appears that novelWriter is stable enough for a 1.0 release. Thanks
|
||||
to all the new users who keep providing feedback on bugs, cosmetic issues, or suggesting
|
||||
improvements and new features. I'm glad to hear that others find my application useful, and I will
|
||||
keep making improvements as I get new suggestions and have new ideas myself. At the same time, I
|
||||
will continue to keep novelWriter simple and clean and avoid feature-bloat.</p>
|
||||
<p>This release mainly fixes cosmetic and other minor issues to the user interface and makes a few
|
||||
minor improvements to some less used features. Aside from this, nothing major has changed since the
|
||||
last release candidate.</p>
|
||||
<p>This release concludes over two years of tinkering with this project. The project grew out of
|
||||
numerous lunch and coffee discussions with my colleague Marian Lückhof at my former job. We were
|
||||
both looking for a tool for writing novels on Linux that suited our needs. We started assembling a
|
||||
wish list of features that has become novelWriter. In addition, users on GitHub have continued to
|
||||
test new features, provide very helpful feedback, and make new suggestions for improvements.
|
||||
Especially the feedback from @johnblommers has been helpful during much of the initial development
|
||||
time. Over the last months more users have started posting ideas and feedback. Thanks to all of you
|
||||
for your contributions.</p>
|
||||
<p>The 1.0 release is intended as a first release of the core features of novelWriter. That does
|
||||
not mean that all planned features have been fully implemented. There is a long list of ideas and
|
||||
suggestions to consider and implement. New ideas and suggestions are welcome. Either as feature
|
||||
requests in the issue tracker, or if not fully formed, can be discussed on the
|
||||
<a href="https://github.com/vkbo/novelWriter/discussions">discussions page</a>.
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.0 RC2</h2>
|
||||
<p><i>Released on 13 December 2020</i></p>
|
||||
<p>This second release candidate for 1.0 comes with only minor changes and improvements, and a
|
||||
handful of minor bugfixes.</p>
|
||||
<p>Among the improvements is the addition of all the possible @-keywords for tags and references to
|
||||
the "Insert" menu under the sub-menu "Tags and References". The "Help" menu has also received a few
|
||||
improvements and additional links to useful webpages. This release also adds a "Release" notes tab
|
||||
to the "About novelWriter" dialog. The release notes are displayed automatically the first time you
|
||||
launch novelWriter after updating to a new version.</p>
|
||||
<p>Among the fixes is better support for high resolution screens. A few elements on the GUI did not
|
||||
scale properly, among them the document editor and viewer header and footer. These were clipped on
|
||||
high res screens due to an underlying issue with the Qt widget underestimating how much space it
|
||||
required to accommodate the text. Unfortunately, dragging the novelWriter app between screens of
|
||||
different scaling factors is not currently supported. However, the GUI should scale properly to the
|
||||
scaling factor on the screen it is opened on.</p>
|
||||
<p>The work leading up to this release has mostly been focused on improving the test coverage of
|
||||
the source code of novelWriter. This helps to ensure that the code does what it is intended to do,
|
||||
and is able to handle corner cases and unexpected external errors and user actions that may occur.
|
||||
While writing these tests, a number of minor potential issues have been uncovered and handled. Most
|
||||
of these are corner cases that may not even be reachable by unexpected user actions.</p>
|
||||
<p>Hopefully, these changes have resulted in an even more stable version of novelWriter. If no more
|
||||
issues are discovered, the next release will be the final version 1.0 release.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.0 RC1</h2>
|
||||
<p><i>Released on 16 November 2020</i></p>
|
||||
<p>This is the first release candidate for the upcoming release of novelWriter 1.0.</p>
|
||||
<p>Since the fifth beta release about four weeks ago, not much has been changed in novelWriter. A
|
||||
few minor tweaks have been made to the GUI.</p>
|
||||
<p>A number of features and tools are now automatically switched off when there is no project or
|
||||
document open for those features to act upon. Previously, this was a bit inconsistent, although no
|
||||
serious bugs have been reported or encountered.</p>
|
||||
<p>Most of the minor changes in this release should not be noticeable to most users. However, there
|
||||
are a couple of noticeable changes.</p>
|
||||
<h3>Typewriter Mode</h3>
|
||||
<p>The "Typewriter Mode" of the editor has been improved. Essentially, this feature is a sort of
|
||||
smart scroll. It tries to keep the cursor stationary in the vertical direction, and will try to
|
||||
scroll the document up when the cursor skips to a new line while typing (or down in case of
|
||||
backspaces). This is similar to the way a typewriter scrolls the paper when hitting the return key.
|
||||
It improves the writing experience as the current active line will stay at the same eye height
|
||||
level on the screen.</p>
|
||||
<p>Previously, the feature would lock the cursor to a given vertical position defined by the user.
|
||||
Now, instead, the cursor will remain stationary in the vertical direction at any position the user
|
||||
sets it to by mouse click or keyboard navigation. The user can define a minimum distance from the
|
||||
top where this feature is activated. These changes makes it more flexible in terms of where the
|
||||
focus is in the editor. The feature can be controlled from the main Preferences.</p>
|
||||
<h3>Switching Syntax Theme</h3>
|
||||
<p>It is now possible to switch syntax highlighting theme without restarting novelWriter.
|
||||
Previously, changing the theme would only half-way update the document, header and footer
|
||||
background and text colours. The new settings would not be fully applied until the application was
|
||||
shut down and started again, thus making it a bit tedious to look through syntax themes to find the
|
||||
one you want.</p>
|
||||
<p>Switching main GUI theme still requires a restart.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
49
nw/assets/text/release_notes_1.1.htm
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Release Notes for 1.1.1</h2>
|
||||
<p><i>Released on 21 February 2021</i></p>
|
||||
<p>This patch makes a couple of minor improvements to the GUI. The keyboard shortcut for deleting
|
||||
entries in the project tree has been changed from "Ctrl+Del" to "Ctrl+Shift+Del" to free up that
|
||||
shortcut for the document editor. It is now possible to use the shortcut for deleting the word in
|
||||
front of the cursor, a common and useful feature of many text editors.</p>
|
||||
<p>The way the negative word count filter option works on the Writing Statistics tool has been
|
||||
changed to be more intuitive. Enabling this filter now appears to remove all negative entries
|
||||
without altering the other entries. Previously, the removed negative counts would be included in
|
||||
the following entries to make it consistent with the total word count. In addition, writing
|
||||
sessions shorter than five minutes, and with no change in the word count, are no longer recorded in
|
||||
the session log file.</p>
|
||||
<p>Other changes include improving the speed of the internal spell checker, used when the Enchant
|
||||
spell check library isn't available. The internal spell checker is no longer significantly slower,
|
||||
but is still lacking in functionality compared to Enchant.</p>
|
||||
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.1</h2>
|
||||
<p><i>Released on 7 February 2021</i></p>
|
||||
<p>The main change in this release is the addition of a new tab to the project tree on the left
|
||||
side of the main window. The regular project tree is now on a tab named "Project", while a new tab
|
||||
named "Novel" displays a simpler version of the information on the main "Outline" page. It lists
|
||||
all the headers of the novel part of the project, as well as the word count and point-of-view
|
||||
character of each section. This is an alternative way to navigate the novel part of the project.
|
||||
The various tree views are now also kept better in sync when the user selects various documents and
|
||||
headers.</p>
|
||||
<p>In addition, a new information dialog named "Project Details" has been added. It replaces the
|
||||
"Details" tab in "Project Settings", and adds more information about the novel part of the project.
|
||||
In particular, a "Table of Contents" in the "Contents" tab displays a summary of the main parts
|
||||
and chapters of the project, their total word counts, and an estimated page count. This was made in
|
||||
response to users asking for ways to estimate the total page count of the project. The page count
|
||||
is estimated based on a words per page setting, which can be changed on the dialog window.</p>
|
||||
<p>Since the tabs below the project tree now add some extra room on the GUI, some convenient
|
||||
buttons have been added in the same area, with direct access to "Project Details", "Writing
|
||||
Statistics" and "Project Setting".</p>
|
||||
<p>A few other minor changes have been made as well. The Preferences dialog has been improved with
|
||||
clearer categories and hopefully better help text. Some new options have been added too. They allow
|
||||
syntax highlighting of multi-paragraph quotes. The highlighter can now optionally accept quotes to
|
||||
be left "hanging", that is, no closing quote in the same paragraph.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
104
nw/assets/text/release_notes_1.2.htm
Normal file
@@ -0,0 +1,104 @@
|
||||
<!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<body>
|
||||
|
||||
|
||||
<h2>Release Notes for 1.2.2</h2>
|
||||
<p><i>Released on 28 March 2021</i></p>
|
||||
<p>This patch release is a bug fix release addressing some inconsistencies and issues with the
|
||||
document header buttons when Focus Mode is active. The keyboard shortcuts for search and replace
|
||||
should now also work in Focus Mode. In addition, the setup script for novelWriter has been improved
|
||||
when installing on Windows.</p>
|
||||
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.2.1</h2>
|
||||
<p><i>Released on 21 March 2021</i></p>
|
||||
<p>This patch release is a bug fix release addressing issues with the document editor's search and
|
||||
replace tool. Due to some recently added restrictions on when various tools are active, depending
|
||||
on which part of the main window has the user's focus, the search tool keyboard shortcuts and
|
||||
buttons were blocked when they shouldn't. This release resolves these issues.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.2</h2>
|
||||
<p><i>Released on 14 March 2021</i></p>
|
||||
<p>This release is mainly focused on the Build Novel Project tool. Completely new export classes
|
||||
have been written to support Open Document and Markdown exports. In addition, the way document
|
||||
layouts are handled have been automated a little to assist the user in keeping header levels and
|
||||
document layout flags in sync. The third new additoion is the ability to record and log idle time
|
||||
during a writing session to improve the writing statistics information as requested by several
|
||||
users. Finally, it is now possible to directly edit the project dictionary via a new, simple GUI
|
||||
dialog.</p>
|
||||
|
||||
<h3>The Build Novel Project Tool</h3>
|
||||
<p>The main changes for this release are to the Build Novel Project tool. The Open Document export,
|
||||
as well as the Markdown export, is now handled entirely by code written for novelWriter.
|
||||
Previously, these export features depended on the underlying Qt library's save routines connected
|
||||
to the preview document shown in the build dialog. Using this method of export both meant that the
|
||||
content of the document was dependent on the preview being generated first, and it also meant that
|
||||
the exported document had limited support for novelWriter-specific features and custom formatting.
|
||||
The new export class should generate a much better result, especially for the Open Document
|
||||
formats. The Open Document standard is supported by Open Office, Libre Office, Google Docs,
|
||||
Microsoft Word, and probably a number of other applications too. The Markdown export hasn't changed
|
||||
a lot, but should be a slight improvement on the previous export feature.</p>
|
||||
<p>These changes to the build tool also imply that the saving process is now independent of the
|
||||
content of the preview window, meaning you don't have to rebuild the preview before saving, which
|
||||
was previously the case. To make this more consistent, the PDF export option has been moved to the
|
||||
print button as it is actually a print-to-file feature under the hood, not technically a proper PDF
|
||||
export format. It is exactly the same as printing to file from the print preview dialog.</p>
|
||||
<p>In addition to the changes to the export features, the Build Novel Project tool now also has
|
||||
controls for line height, which applies to all rich text export formats, and the option to replace
|
||||
unicode characters with appropriate html entities for html export.</p>
|
||||
|
||||
<h3>Document Layout Automation</h3>
|
||||
<p>Among other changes in this release are a few improvements to the process of creating and
|
||||
changing documents. When a new document is first created, the header is generated from the assigned
|
||||
label and layout.</p>
|
||||
<p>In addition, for some document layouts, when the user changes the header level of the first
|
||||
header of the document, the document layout setting is updated accordingly. This should reduce the
|
||||
need for the user to maintain two ways of assigning the role of a given document. This automation
|
||||
only applies to combinations of header level and current document layouts where there is no
|
||||
ambiguity. For instance, changing the header level in a "Scene" document from level 3 to 2 changes
|
||||
the document layout automatically to "Chapter". But changing the first header of a "Book" layout
|
||||
document from 1 to 2 does not change the document's layout as the "Book" layout is a generic
|
||||
document layout and it's perfectly reasonable for its first header to be a chapter header.</p>
|
||||
<p>Keep in mind that novelWriter treats documents with layout "Book", "Chapter" and "Scene" exactly
|
||||
the same during exports. The distinction is only meant as a way to indicate the purpose of a
|
||||
document in the project tree. This new automation is meant to assist in keeping this information up
|
||||
to date. The other layouts do have an effect on formatting during export, and are generally left
|
||||
alone.</p>
|
||||
|
||||
<h3>The Session Timer and Idle Time</h3>
|
||||
<p>Another change that has been requested by a couple of users is to have the session timer in the
|
||||
status bar stop counting when the user is inactive (idle). This feature is optional, and can be
|
||||
controlled from Preferences. The definition of "idle" in this context is either that the user is
|
||||
active in a different application than novelWriter (loss of focus) or that the user has not made
|
||||
any changes to the current document for a given amount of time. The time threshold is by default
|
||||
five minutes, but can be altered in Preferences.</p>
|
||||
<p>In addition, the idle time is also recorded in the session log, and can be viewed in the Writing
|
||||
Statistics dialog and exported with the rest of the information. The idle time is recorder in the
|
||||
logs regardless of whether the status bar clock takes idle time into consideration or not. So even
|
||||
if you turn off the idle time switch in Preferences, the other idle time setting still affects the
|
||||
writing stats log entries.</p>
|
||||
|
||||
<h3>Other Changes</h3>
|
||||
<p>The user dictionary of the project, where words added to the dictionary from the document editor
|
||||
go, can now be viewed and edited with a new "Project Word List" tool in the "Tools" menu.</p>
|
||||
<p>A small additional feature added is also the ability to undo the last move of an item in the
|
||||
project tree. The keyboard shortcut for this is `Ctrl+Shift+Z`, or it can be accessed from the
|
||||
menu. The feature can only undo the last move, but it includes both documents moved to trash, moves
|
||||
by up/down keypress or menu entries, and drag and drop moves.</p>
|
||||
<p>A new keyword has been added to mark characters in the story. The new keyword is intended to tag
|
||||
a character as the focus character for a chapter or scene. This is useful for stories where the
|
||||
point-of-view character and the focus character are different.</p>
|
||||
<p>Lastly, two bugfixes have been made as well. The Empty Trash feature was no longer working due
|
||||
to an earlier fix solving another issue. The feature has now been restored. In addition, the
|
||||
indexer now checks that a keyword (tag or reference) is valid before saving it to the index.
|
||||
Previously, an invalid keyword could be saved to the index and potentially crash the application.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
60
nw/assets/text/release_notes_1.3.htm
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Release Notes for 1.3.3</h2>
|
||||
<p><i>Released on 13 June 2021</i></p>
|
||||
<p>This patch release fixes a potential file encoding issue when running setup on Windows, and a
|
||||
minor issue with the project word count not being updated immediately when a file is deleted. In
|
||||
addition, the keyboard shortcuts to change focus between the project tree, the editor, the viewer,
|
||||
and the outline panel, have been changed for Windows users. They keyboard shortcuts were
|
||||
interfering with the Alt codes used for special characters. The shortcuts are unchanged for Linux
|
||||
and macOS.</p>
|
||||
|
||||
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.3.2</h2>
|
||||
<p><i>Released on 30 May 2021</i></p>
|
||||
<p>This is a patch release that fixes some minor issues. One issue was with the split tool, which
|
||||
would drop the last line from the source document during a split if it was missing a final line
|
||||
break. A minor issue with the display of word counts on the details panel under the project tree
|
||||
has also been fixed. In addition, the setup script commands for Linux have been improved a bit.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.3.1</h2>
|
||||
<p><i>Released on 6 May 2021</i></p>
|
||||
<p>This is a patch release primarily to fix a problem with the Qt translation library used to make
|
||||
novelWriter available in multiple languages. One of the function calls made to the library was
|
||||
added recently, in Qt 5.15, which many users on Linux will not have installed on their system. The
|
||||
issue could be resolved by updating the library, but it's an unnecessary and inconvenient
|
||||
restriction.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Release Notes for 1.3</h2>
|
||||
<p><i>Released on 2 May 2021</i></p>
|
||||
<p>The main feature of the 1.3 release is internationalisation. The release introduces support for
|
||||
Portuguese, French, and Norwegian for the main GUI, in addition to the default English. The
|
||||
settings for choosing the GUI language can be found in the main Preferences.</p>
|
||||
<p>The implementation is such that the projects themselves can use a different language than the
|
||||
one selected for the GUI. To allow for better support for multiple language projects, the Build
|
||||
Novel Project tool has the added option to select the language of the injected text into exported
|
||||
documents. In particularly for the number words that can be added for chapter headers. Available
|
||||
languages here are the same as mentioned above for the main GUI, plus German.</p>
|
||||
<p>Settings have also been added in Preferences to allow for automatic injection of spaces or
|
||||
non-breaking spaces in front of, or behind, certain characters. These are features generally
|
||||
available in text editors for French, but the implementation in novelWriter simply makes them a
|
||||
free text box where you can list all characters or symbols where you want a space added
|
||||
automatically.</p>
|
||||
<p>The translation files for this release have been provided by Bruno Meneguello for the Portuguese
|
||||
translation, by Jan Lüdke (jyhelle) for the French translation, by Veronica Berglyd Olsen for the
|
||||
Norwegian translation as well as the minor modifications for US English, and Marian Lückhof
|
||||
provided the translation file for chapter numbers in German.</p>
|
||||
<p>The work to rewrite novelWriter to allow for internationalisation was done by Bruno Meneguello
|
||||
and Veronica Berglyd Olsen.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,7 +23,6 @@ link = 44, 152, 247
|
||||
linkvisited = 44, 152, 247
|
||||
|
||||
[GUI]
|
||||
treewordcount = 197, 200, 198
|
||||
statusnone = 150, 152, 150
|
||||
statussaved = 39, 135, 78
|
||||
statusunsaved = 138, 32, 32
|
||||
|
||||
28
nw/assets/themes/gui/solarized_dark/theme.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
[Main]
|
||||
name = Solarized Dark
|
||||
author = nullbasis
|
||||
credit = Ethan Schoonover
|
||||
url = https://ethanschoonover.com/solarized/
|
||||
license = MIT
|
||||
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||
|
||||
[Palette]
|
||||
window = 0, 43, 54
|
||||
windowtext = 253, 246, 227
|
||||
base = 7, 54, 66
|
||||
alternatebase = 67, 67, 67
|
||||
text = 253, 246, 227
|
||||
tooltipbase = 133, 153, 0
|
||||
tooltiptext = 0, 43, 54
|
||||
button = 7, 54, 66
|
||||
buttontext = 253, 246, 227
|
||||
brighttext = 253, 246, 227
|
||||
highlight = 42, 161, 152
|
||||
highlightedtext = 0, 43, 54
|
||||
link = 38, 139, 210
|
||||
linkvisited = 38, 139, 210
|
||||
|
||||
[GUI]
|
||||
statusnone = 88, 110, 117
|
||||
statussaved = 42, 161, 152
|
||||
statusunsaved = 203, 75, 22
|
||||
28
nw/assets/themes/gui/solarized_light/theme.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
[Main]
|
||||
name = Solarized Light
|
||||
author = nullbasis
|
||||
credit = Ethan Schoonover
|
||||
url = https://ethanschoonover.com/solarized/
|
||||
license = MIT
|
||||
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||
|
||||
[Palette]
|
||||
window = 238, 232, 213
|
||||
windowtext = 0, 43, 54
|
||||
base = 253, 246, 227
|
||||
alternatebase = 238, 232, 213
|
||||
text = 0, 43, 54
|
||||
tooltipbase = 133, 153, 0
|
||||
tooltiptext = 0, 43, 54
|
||||
button = 238, 232, 213
|
||||
buttontext = 0, 43, 54
|
||||
brighttext = 0, 43, 54
|
||||
highlight = 42, 161, 152
|
||||
highlightedtext = 253, 246, 227
|
||||
link = 38, 139, 210
|
||||
linkvisited = 38, 139, 210
|
||||
|
||||
[GUI]
|
||||
statusnone = 88, 110, 117
|
||||
statussaved = 42, 161, 152
|
||||
statusunsaved = 203, 75, 22
|
||||
@@ -20,6 +20,6 @@ hidden = 150, 150, 150
|
||||
keyword = 200, 46, 0
|
||||
value = 184, 200, 0
|
||||
spellcheckline = 200, 46, 0
|
||||
tagerror = 46, 200, 0
|
||||
errorline = 46, 200, 0
|
||||
replacetag = 0, 184, 46
|
||||
modifier = 200, 120, 0
|
||||
|
||||
@@ -20,6 +20,6 @@ hidden = 100, 100, 100
|
||||
keyword = 200, 50, 50
|
||||
value = 50, 150, 50
|
||||
spellcheckline = 200, 0, 0
|
||||
tagerror = 0, 150, 0
|
||||
errorline = 0, 150, 0
|
||||
replacetag = 0, 150, 0
|
||||
modifier = 150, 110, 30
|
||||
|
||||
@@ -20,6 +20,6 @@ hidden = 150, 150, 150
|
||||
keyword = 225, 225, 225
|
||||
value = 200, 200, 200
|
||||
spellcheckline = 200, 46, 0
|
||||
tagerror = 46, 200, 0
|
||||
errorline = 46, 200, 0
|
||||
replacetag = 225, 225, 225
|
||||
modifier = 225, 225, 225
|
||||
|
||||
@@ -20,6 +20,6 @@ hidden = 100, 100, 100
|
||||
keyword = 0, 0, 0
|
||||
value = 20, 20, 20
|
||||
spellcheckline = 200, 0, 0
|
||||
tagerror = 0, 150, 0
|
||||
errorline = 0, 150, 0
|
||||
replacetag = 0, 0, 0
|
||||
modifier = 0, 0, 0
|
||||
|
||||
@@ -40,6 +40,6 @@ hidden = 152, 159, 177
|
||||
keyword = 222, 61, 58
|
||||
value = 150, 74, 193
|
||||
spellcheckline = 222, 61, 58
|
||||
tagerror = 8, 145, 106
|
||||
errorline = 8, 145, 106
|
||||
replacetag = 42, 162, 152
|
||||
modifier = 224, 175, 5
|
||||
|
||||
@@ -40,6 +40,6 @@ hidden = 99, 119, 119
|
||||
keyword = 247, 140, 108
|
||||
value = 199, 146, 234
|
||||
spellcheckline = 247, 140, 108
|
||||
tagerror = 173, 219, 103
|
||||
errorline = 173, 219, 103
|
||||
replacetag = 127, 219, 202
|
||||
modifier = 236, 196, 141
|
||||
|
||||
25
nw/assets/themes/syntax/solarized_dark.conf
Normal file
@@ -0,0 +1,25 @@
|
||||
[Main]
|
||||
name = Solarized Dark
|
||||
author = nullbasis
|
||||
credit = Ethan Schoonover
|
||||
url = https://ethanschoonover.com/solarized/
|
||||
license = MIT
|
||||
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||
|
||||
[Syntax]
|
||||
background = 7, 54, 66
|
||||
text = 253, 246, 227
|
||||
link = 38, 139, 210
|
||||
headertext = 147, 161, 161
|
||||
headertag = 42, 161, 152
|
||||
emphasis = 38, 139, 210
|
||||
straightquotes = 211, 54, 130
|
||||
doublequotes = 42, 161, 152
|
||||
singlequotes = 42, 161, 152
|
||||
hidden = 147, 161, 161
|
||||
keyword = 133, 153, 0
|
||||
value = 203, 75, 22
|
||||
spellcheckline = 203, 75, 22
|
||||
errorline = 220, 50, 47
|
||||
replacetag = 133, 153, 0
|
||||
modifier = 181, 137, 0
|
||||
25
nw/assets/themes/syntax/solarized_light.conf
Normal file
@@ -0,0 +1,25 @@
|
||||
[Main]
|
||||
name = Solarized Light
|
||||
author = nullbasis
|
||||
credit = Ethan Schoonover
|
||||
url = https://ethanschoonover.com/solarized/
|
||||
license = MIT
|
||||
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||
|
||||
[Syntax]
|
||||
background = 253, 246, 227
|
||||
text = 0, 43, 54
|
||||
link = 38, 139, 210
|
||||
headertext = 88, 110, 117
|
||||
headertag = 42, 161, 152
|
||||
emphasis = 38, 139, 210
|
||||
straightquotes = 211, 54, 130
|
||||
doublequotes = 42, 161, 152
|
||||
singlequotes = 42, 161, 152
|
||||
hidden = 88, 110, 117
|
||||
keyword = 133, 153, 0
|
||||
value = 203, 75, 22
|
||||
spellcheckline = 203, 75, 22
|
||||
errorline = 220, 50, 47
|
||||
replacetag = 133, 153, 0
|
||||
modifier = 181, 137, 0
|
||||
@@ -40,6 +40,6 @@ hidden = 142, 144, 140
|
||||
keyword = 240, 40, 41
|
||||
value = 137, 89, 168
|
||||
spellcheckline = 240, 40, 41
|
||||
tagerror = 113, 140, 0
|
||||
errorline = 113, 140, 0
|
||||
replacetag = 62, 153, 159
|
||||
modifier = 245, 135, 31
|
||||
|
||||
@@ -40,6 +40,6 @@ hidden = 150, 152, 150
|
||||
keyword = 204, 102, 102
|
||||
value = 178, 148, 187
|
||||
spellcheckline = 204, 102, 102
|
||||
tagerror = 181, 189, 104
|
||||
errorline = 181, 189, 104
|
||||
replacetag = 138, 190, 183
|
||||
modifier = 222, 147, 95
|
||||
|
||||
@@ -40,6 +40,6 @@ hidden = 114, 133, 183
|
||||
keyword = 255, 157, 164
|
||||
value = 235, 187, 255
|
||||
spellcheckline = 255, 157, 164
|
||||
tagerror = 209, 241, 169
|
||||
errorline = 209, 241, 169
|
||||
replacetag = 153, 255, 255
|
||||
modifier = 255, 197, 143
|
||||
|
||||
@@ -40,6 +40,6 @@ hidden = 150, 152, 150
|
||||
keyword = 213, 78, 83
|
||||
value = 195, 151, 216
|
||||
spellcheckline = 213, 78, 83
|
||||
tagerror = 185, 202, 74
|
||||
errorline = 185, 202, 74
|
||||
replacetag = 112, 192, 177
|
||||
modifier = 231, 140, 69
|
||||
|
||||
@@ -40,6 +40,6 @@ hidden = 153, 153, 153
|
||||
keyword = 242, 119, 122
|
||||
value = 204, 153, 204
|
||||
spellcheckline = 242, 119, 122
|
||||
tagerror = 153, 204, 153
|
||||
errorline = 153, 204, 153
|
||||
replacetag = 102, 204, 204
|
||||
modifier = 249, 145, 57
|
||||
|
||||
289
nw/common.py
@@ -1,28 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Common Functions
|
||||
"""
|
||||
novelWriter – Common Functions
|
||||
==============================
|
||||
Various common functions
|
||||
|
||||
novelWriter – Common Functions
|
||||
================================
|
||||
Various functions used multiple places
|
||||
File History:
|
||||
Created: 2019-05-12 [0.1.0]
|
||||
|
||||
File History:
|
||||
Created: 2019-05-12 [0.1.0]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -30,71 +28,78 @@ import logging
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtWidgets import qApp
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from nw.constants import nwConst, nwUnicode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def checkString(checkValue, defaultValue, allowNone=False):
|
||||
|
||||
def checkString(value, default, allowNone=False):
|
||||
"""Check if a variable is a string or a none.
|
||||
"""
|
||||
if allowNone:
|
||||
if checkValue is None:
|
||||
if value is None:
|
||||
return None
|
||||
if checkValue == "None":
|
||||
if value == "None":
|
||||
return None
|
||||
if isinstance(checkValue, str):
|
||||
return str(checkValue)
|
||||
return defaultValue
|
||||
if isinstance(value, str):
|
||||
return str(value)
|
||||
return default
|
||||
|
||||
def checkInt(checkValue, defaultValue, allowNone=False):
|
||||
|
||||
def checkInt(value, default, allowNone=False):
|
||||
"""Check if a variable is an integer or a none.
|
||||
"""
|
||||
if allowNone:
|
||||
if checkValue is None:
|
||||
if value is None:
|
||||
return None
|
||||
if checkValue == "None":
|
||||
if value == "None":
|
||||
return None
|
||||
try:
|
||||
return int(checkValue)
|
||||
return int(value)
|
||||
except Exception:
|
||||
return defaultValue
|
||||
return default
|
||||
|
||||
def checkBool(checkValue, defaultValue, allowNone=False):
|
||||
|
||||
def checkBool(value, default, allowNone=False):
|
||||
"""Check if a variable is a boolean or a none.
|
||||
"""
|
||||
if allowNone:
|
||||
if checkValue is None:
|
||||
if value is None:
|
||||
return None
|
||||
if checkValue == "None":
|
||||
if value == "None":
|
||||
return None
|
||||
if isinstance(checkValue, str):
|
||||
if checkValue == "True":
|
||||
if isinstance(value, str):
|
||||
if value == "True":
|
||||
return True
|
||||
elif checkValue == "False":
|
||||
elif value == "False":
|
||||
return False
|
||||
else:
|
||||
return defaultValue
|
||||
elif isinstance(checkValue, int):
|
||||
if checkValue == 1:
|
||||
return default
|
||||
elif isinstance(value, int):
|
||||
if value == 1:
|
||||
return True
|
||||
elif checkValue == 0:
|
||||
elif value == 0:
|
||||
return False
|
||||
else:
|
||||
return defaultValue
|
||||
return defaultValue
|
||||
return default
|
||||
return default
|
||||
|
||||
def checkHandle(checkValue, defaultValue, allowNone=False):
|
||||
|
||||
def checkHandle(value, default, allowNone=False):
|
||||
"""Check if a value is a handle.
|
||||
"""
|
||||
if allowNone:
|
||||
if checkValue is None:
|
||||
if value is None:
|
||||
return None
|
||||
if checkValue == "None":
|
||||
if value == "None":
|
||||
return None
|
||||
if isHandle(checkValue):
|
||||
return str(checkValue)
|
||||
return defaultValue
|
||||
if isHandle(value):
|
||||
return str(value)
|
||||
return default
|
||||
|
||||
|
||||
def isHandle(theString):
|
||||
"""Check if a string is a valid novelWriter handle.
|
||||
@@ -104,42 +109,55 @@ def isHandle(theString):
|
||||
return False
|
||||
if len(theString) != 13:
|
||||
return False
|
||||
invalidChar = False
|
||||
for c in theString:
|
||||
if c not in "0123456789abcdef":
|
||||
invalidChar = True
|
||||
return not invalidChar
|
||||
return False
|
||||
return True
|
||||
|
||||
def colRange(rgbStart, rgbEnd, nStep):
|
||||
"""Generate a range of colours from one RGB value to another.
|
||||
|
||||
def isTitleTag(theString):
|
||||
"""Check if a string is a valid title string.
|
||||
"""
|
||||
if len(rgbStart) != 3 and len(rgbEnd) != 3 and nStep < 1:
|
||||
logger.error("Cannot create colour range from given parameters")
|
||||
return None
|
||||
if not isinstance(theString, str):
|
||||
return False
|
||||
if len(theString) != 7:
|
||||
return False
|
||||
if not theString.startswith("T"):
|
||||
return False
|
||||
for c in theString[1:]:
|
||||
if c not in "0123456789":
|
||||
return False
|
||||
return True
|
||||
|
||||
if nStep == 1:
|
||||
return rgbStart
|
||||
elif nStep == 2:
|
||||
return [rgbStart, rgbEnd]
|
||||
|
||||
dC = [0, 0, 0]
|
||||
for c in range(3):
|
||||
cA = rgbStart[c]
|
||||
cB = rgbEnd[c]
|
||||
dC[c] = (cB-cA)/(nStep-1)
|
||||
print(dC)
|
||||
retCol = [rgbStart]
|
||||
for n in range(nStep):
|
||||
if n > 0 and n < nStep:
|
||||
retCol.append([
|
||||
int(retCol[n-1][0] + dC[0]),
|
||||
int(retCol[n-1][1] + dC[1]),
|
||||
int(retCol[n-1][2] + dC[2]),
|
||||
])
|
||||
retCol[-1] = rgbEnd
|
||||
print(retCol)
|
||||
def isItemClass(theString):
|
||||
"""Check if an item is a calid nwItemClass identifier.
|
||||
"""
|
||||
return theString in nwItemClass.__members__
|
||||
|
||||
|
||||
def isItemType(theString):
|
||||
"""Check if an item is a calid nwItemType identifier.
|
||||
"""
|
||||
return theString in nwItemType.__members__
|
||||
|
||||
|
||||
def isItemLayout(theString):
|
||||
"""Check if an item is a calid nwItemLayout identifier.
|
||||
"""
|
||||
return theString in nwItemLayout.__members__
|
||||
|
||||
|
||||
def hexToInt(value, default=0):
|
||||
"""Convert a hex string to an integer.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value, 16)
|
||||
except Exception:
|
||||
return default
|
||||
return default
|
||||
|
||||
return retCol
|
||||
|
||||
def formatInt(theInt):
|
||||
"""Formats an integer with k, M, G etc.
|
||||
@@ -152,14 +170,15 @@ def formatInt(theInt):
|
||||
theVal /= 1000.0
|
||||
if theVal < 1000.0:
|
||||
if theVal < 10.0:
|
||||
return f"{theVal:4.2f}{nwUnicode.U_THNSP}{pF}"
|
||||
return f"{theVal:4.2f}{nwUnicode.U_THSP}{pF}"
|
||||
elif theVal < 100.0:
|
||||
return f"{theVal:4.1f}{nwUnicode.U_THNSP}{pF}"
|
||||
return f"{theVal:4.1f}{nwUnicode.U_THSP}{pF}"
|
||||
else:
|
||||
return f"{theVal:3.0f}{nwUnicode.U_THNSP}{pF}"
|
||||
return f"{theVal:3.0f}{nwUnicode.U_THSP}{pF}"
|
||||
|
||||
return str(theInt)
|
||||
|
||||
|
||||
def formatTimeStamp(theTime, fileSafe=False):
|
||||
"""Take a number (on the format returned by time.time()) and convert
|
||||
it to a timestamp string.
|
||||
@@ -169,6 +188,7 @@ def formatTimeStamp(theTime, fileSafe=False):
|
||||
else:
|
||||
return datetime.fromtimestamp(theTime).strftime(nwConst.FMT_TSTAMP)
|
||||
|
||||
|
||||
def formatTime(tS):
|
||||
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
|
||||
if a full day or longer.
|
||||
@@ -180,6 +200,7 @@ def formatTime(tS):
|
||||
return f"{tS//3600:02d}:{tS%3600//60:02d}:{tS%60:02d}"
|
||||
return "ERROR"
|
||||
|
||||
|
||||
def splitVersionNumber(vString):
|
||||
""" Splits a version string on the form aa.bb.cc into major, minor
|
||||
and patch, and computes an integer value aabbcc.
|
||||
@@ -187,7 +208,7 @@ def splitVersionNumber(vString):
|
||||
vMajor = 0
|
||||
vMinor = 0
|
||||
vPatch = 0
|
||||
vInt = 0
|
||||
vInt = 0
|
||||
|
||||
vBits = vString.split(".")
|
||||
nBits = len(vBits)
|
||||
@@ -203,6 +224,7 @@ def splitVersionNumber(vString):
|
||||
|
||||
return [vMajor, vMinor, vPatch, vInt]
|
||||
|
||||
|
||||
def transferCase(theSource, theTarget):
|
||||
"""Transfers the case of the source word to the target word. This
|
||||
will consider all upper or lower, and first char capitalisation.
|
||||
@@ -224,37 +246,67 @@ def transferCase(theSource, theTarget):
|
||||
|
||||
return theResult
|
||||
|
||||
|
||||
def fuzzyTime(secDiff):
|
||||
"""Converts a time difference in seconds into a fuzzy time string.
|
||||
"""
|
||||
if secDiff < 0:
|
||||
return "in the future"
|
||||
return QCoreApplication.translate(
|
||||
"Common", "in the future"
|
||||
)
|
||||
elif secDiff < 30:
|
||||
return "just now"
|
||||
return QCoreApplication.translate(
|
||||
"Common", "just now"
|
||||
)
|
||||
elif secDiff < 90:
|
||||
return "a minute ago"
|
||||
elif secDiff < 3300: # 55 minutes
|
||||
return "%d minutes ago" % int(round(secDiff/60))
|
||||
elif secDiff < 5400: # 90 minutes
|
||||
return "an hour ago"
|
||||
elif secDiff < 84600: # 23.5 hours
|
||||
return "%d hours ago" % int(round(secDiff/3600))
|
||||
elif secDiff < 129600: # 1.5 days
|
||||
return "a day ago"
|
||||
elif secDiff < 561600: # 6.5 days
|
||||
return "%d days ago" % int(round(secDiff/86400))
|
||||
elif secDiff < 907200: # 10.5 days
|
||||
return "a week ago"
|
||||
elif secDiff < 2419200: # 28 days
|
||||
return "%d weeks ago" % int(round(secDiff/604800))
|
||||
elif secDiff < 3888000: # 45 days
|
||||
return "a month ago"
|
||||
elif secDiff < 29808000: # 345 days
|
||||
return "%d months ago" % int(round(secDiff/2592000))
|
||||
elif secDiff < 47336400: # 1.5 years
|
||||
return "a year ago"
|
||||
return QCoreApplication.translate(
|
||||
"Common", "a minute ago"
|
||||
)
|
||||
elif secDiff < 3300: # 55 minutes
|
||||
return QCoreApplication.translate(
|
||||
"Common", "{0} minutes ago"
|
||||
).format(int(round(secDiff/60)))
|
||||
elif secDiff < 5400: # 90 minutes
|
||||
return QCoreApplication.translate(
|
||||
"Common", "an hour ago"
|
||||
)
|
||||
elif secDiff < 84600: # 23.5 hours
|
||||
return QCoreApplication.translate(
|
||||
"Common", "{0} hours ago"
|
||||
).format(int(round(secDiff/3600)))
|
||||
elif secDiff < 129600: # 1.5 days
|
||||
return QCoreApplication.translate(
|
||||
"Common", "a day ago"
|
||||
)
|
||||
elif secDiff < 561600: # 6.5 days
|
||||
return QCoreApplication.translate(
|
||||
"Common", "{0} days ago"
|
||||
).format(int(round(secDiff/86400)))
|
||||
elif secDiff < 907200: # 10.5 days
|
||||
return QCoreApplication.translate(
|
||||
"Common", "a week ago"
|
||||
)
|
||||
elif secDiff < 2419200: # 28 days
|
||||
return QCoreApplication.translate(
|
||||
"Common", "{0} weeks ago"
|
||||
).format(int(round(secDiff/604800)))
|
||||
elif secDiff < 3888000: # 45 days
|
||||
return QCoreApplication.translate(
|
||||
"Common", "a month ago"
|
||||
)
|
||||
elif secDiff < 29808000: # 345 days
|
||||
return QCoreApplication.translate(
|
||||
"Common", "{0} months ago"
|
||||
).format(int(round(secDiff/2592000)))
|
||||
elif secDiff < 47336400: # 1.5 years
|
||||
return QCoreApplication.translate(
|
||||
"Common", "a year ago"
|
||||
)
|
||||
else:
|
||||
return "%d years ago" % int(round(secDiff/31557600))
|
||||
return QCoreApplication.translate(
|
||||
"Common", "{0} years ago"
|
||||
).format(int(round(secDiff/31557600)))
|
||||
|
||||
|
||||
def makeFileNameSafe(theText):
|
||||
"""Returns a filename safe version of the text.
|
||||
@@ -265,6 +317,7 @@ def makeFileNameSafe(theText):
|
||||
cleanName += c
|
||||
return cleanName
|
||||
|
||||
|
||||
def getGuiItem(theName):
|
||||
"""Returns a QtWidget based on its objectName.
|
||||
"""
|
||||
@@ -272,3 +325,27 @@ def getGuiItem(theName):
|
||||
if qWidget.objectName() == theName:
|
||||
return qWidget
|
||||
return None
|
||||
|
||||
|
||||
def numberToRoman(numVal, isLower=False):
|
||||
"""Convert an integer to a roman number.
|
||||
"""
|
||||
if not isinstance(numVal, int):
|
||||
return "NAN"
|
||||
if numVal < 1 or numVal > 4999:
|
||||
return "OOR"
|
||||
|
||||
theValues = [
|
||||
(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"),
|
||||
(50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"),
|
||||
]
|
||||
|
||||
romNum = ""
|
||||
for theDiv, theSym in theValues:
|
||||
n = numVal//theDiv
|
||||
romNum += n*theSym
|
||||
numVal -= n*theDiv
|
||||
if numVal <= 0:
|
||||
break
|
||||
|
||||
return romNum.lower() if isLower else romNum
|
||||
|
||||
428
nw/config.py
@@ -1,47 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Config Class
|
||||
"""
|
||||
novelWriter – Config Class
|
||||
==========================
|
||||
Class holding the user preferences and handling the config file
|
||||
|
||||
novelWriter – Config Class
|
||||
============================
|
||||
Class reading and holding the preferences of the application
|
||||
File History:
|
||||
Created: 2018-09-22 [0.0.1]
|
||||
|
||||
File History:
|
||||
Created: 2018-09-22 [0.0.1]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
import configparser
|
||||
import shutil
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
from time import time
|
||||
|
||||
from PyQt5.Qt import PYQT_VERSION_STR
|
||||
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
|
||||
from PyQt5.QtCore import (
|
||||
QT_VERSION_STR, QStandardPaths, QSysInfo, QLocale, QLibraryInfo,
|
||||
QTranslator
|
||||
)
|
||||
|
||||
from nw.constants import nwConst, nwFiles, nwUnicode
|
||||
from nw.error import logException
|
||||
from nw.common import splitVersionNumber, formatTimeStamp
|
||||
from nw.constants import nwConst, nwFiles, nwUnicode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Config:
|
||||
|
||||
CNF_STR = 0
|
||||
@@ -50,16 +53,15 @@ class Config:
|
||||
CNF_S_LST = 3
|
||||
CNF_I_LST = 4
|
||||
|
||||
LANG_NW = 1
|
||||
LANG_PROJ = 2
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Set Application Variables
|
||||
self.appName = "novelWriter"
|
||||
self.appHandle = self.appName.lower()
|
||||
|
||||
# Debug Settings
|
||||
self.showGUI = True # Allow blocking the GUI (disabled for testing)
|
||||
self.debugInfo = False # True if log level is DEBUG or VERBOSE
|
||||
|
||||
# Config Error Handling
|
||||
self.hasError = False # True if the config class encountered an error
|
||||
self.errData = [] # List of error messages
|
||||
@@ -80,81 +82,98 @@ class Config:
|
||||
self.helpPath = None # The full path to the novelwriter .qhc help file
|
||||
|
||||
# Runtime Settings and Variables
|
||||
self.confChanged = False # True whenever the config has chenged, false after save
|
||||
self.hasHelp = False # True if the Qt help files are present in the assets folder
|
||||
self.confChanged = False # True whenever the config has chenged, false after save
|
||||
self.hasHelp = False # True if the Qt help files are present in the assets folder
|
||||
|
||||
## General
|
||||
# General
|
||||
self.guiTheme = "default"
|
||||
self.guiSyntax = "default_light"
|
||||
self.guiIcons = "typicons_colour_light"
|
||||
self.guiDark = False # Load icons for dark backgrounds, if available
|
||||
self.guiLang = "en" # Hardcoded for now since the GUI is only in English
|
||||
self.guiFont = "" # Defaults to system default font
|
||||
self.guiFontSize = 11
|
||||
self.guiScale = 1.0 # Set automatically by Theme class
|
||||
self.lastNotes = "" # The latest release notes that have been shown
|
||||
self.guiDark = False # Load icons for dark backgrounds, if available
|
||||
self.guiFont = "" # Defaults to system default font
|
||||
self.guiFontSize = 11 # Is overridden if system default is loaded
|
||||
self.guiScale = 1.0 # Set automatically by Theme class
|
||||
self.lastNotes = "0x0" # The latest release notes that have been shown
|
||||
|
||||
## Sizes
|
||||
self.winGeometry = [1200, 650]
|
||||
self.treeColWidth = [200, 50, 30]
|
||||
self.projColWidth = [200, 60, 140]
|
||||
self.mainPanePos = [300, 800]
|
||||
self.docPanePos = [400, 400]
|
||||
self.viewPanePos = [500, 150]
|
||||
self.outlnPanePos = [500, 150]
|
||||
self.isFullScreen = False
|
||||
# Localisation
|
||||
self.qLocal = QLocale.system()
|
||||
self.guiLang = self.qLocal.name()
|
||||
self.qtLangPath = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
|
||||
self.nwLangPath = None
|
||||
self.qtTrans = {}
|
||||
|
||||
## Features
|
||||
self.hideVScroll = False # Hide vertical scroll bars on main widgets
|
||||
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
|
||||
# Sizes
|
||||
self.winGeometry = [1200, 650]
|
||||
self.prefGeometry = [700, 615]
|
||||
self.treeColWidth = [200, 50, 30]
|
||||
self.novelColWidth = [200, 50]
|
||||
self.projColWidth = [200, 60, 140]
|
||||
self.mainPanePos = [300, 800]
|
||||
self.docPanePos = [400, 400]
|
||||
self.viewPanePos = [500, 150]
|
||||
self.outlnPanePos = [500, 150]
|
||||
self.isFullScreen = False
|
||||
|
||||
## Project
|
||||
self.autoSaveProj = 60 # Interval for auto-saving project in seconds
|
||||
self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
|
||||
# Features
|
||||
self.hideVScroll = False # Hide vertical scroll bars on main widgets
|
||||
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
|
||||
|
||||
## Text Editor
|
||||
self.textFont = None # Editor font
|
||||
self.textSize = 12 # Editor font size
|
||||
self.textFixedW = True # Keep editor text fixed width
|
||||
self.textWidth = 600 # Editor text width
|
||||
self.textMargin = 40 # Editor/viewer text margin
|
||||
self.tabWidth = 40 # Editor tabulator width
|
||||
# Project
|
||||
self.autoSaveProj = 60 # Interval for auto-saving project in seconds
|
||||
self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
|
||||
|
||||
self.focusWidth = 800 # Focus Mode text width
|
||||
self.hideFocusFooter = False # Hide document footer in Focus Mode
|
||||
self.showFullPath = True # Show full document path in editor header
|
||||
self.autoSelect = True # Auto-select word when applying format with no selection
|
||||
# Text Editor
|
||||
self.textFont = None # Editor font
|
||||
self.textSize = 12 # Editor font size
|
||||
self.textFixedW = True # Keep editor text fixed width
|
||||
self.textWidth = 600 # Editor text width
|
||||
self.textMargin = 40 # Editor/viewer text margin
|
||||
self.tabWidth = 40 # Editor tabulator width
|
||||
|
||||
self.doJustify = False # Justify text
|
||||
self.showTabsNSpaces = False # Show tabs and spaces in edior
|
||||
self.showLineEndings = False # Show line endings in editor
|
||||
self.focusWidth = 800 # Focus Mode text width
|
||||
self.hideFocusFooter = False # Hide document footer in Focus Mode
|
||||
self.showFullPath = True # Show full document path in editor header
|
||||
self.autoSelect = True # Auto-select word when applying format with no selection
|
||||
|
||||
self.doReplace = True # Enable auto-replace as you type
|
||||
self.doReplaceSQuote = True # Smart single quotes
|
||||
self.doReplaceDQuote = True # Smart double quotes
|
||||
self.doReplaceDash = True # Replace multiple hyphens with dashes
|
||||
self.doReplaceDots = True # Replace three dots with ellipsis
|
||||
self.doJustify = False # Justify text
|
||||
self.showTabsNSpaces = False # Show tabs and spaces in edior
|
||||
self.showLineEndings = False # Show line endings in editor
|
||||
self.showMultiSpaces = True # Highlight multiple spaces in the text
|
||||
|
||||
self.scrollPastEnd = True # Allow scrolling past end of document
|
||||
self.autoScroll = False # Typewriter-like scrolling
|
||||
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
||||
self.doReplace = True # Enable auto-replace as you type
|
||||
self.doReplaceSQuote = True # Smart single quotes
|
||||
self.doReplaceDQuote = True # Smart double quotes
|
||||
self.doReplaceDash = True # Replace multiple hyphens with dashes
|
||||
self.doReplaceDots = True # Replace three dots with ellipsis
|
||||
|
||||
self.wordCountTimer = 5.0 # Interval for word count update in seconds
|
||||
self.bigDocLimit = 800 # Size threshold for heavy editor features in kilobytes
|
||||
self.scrollPastEnd = True # Allow scrolling past end of document
|
||||
self.autoScroll = False # Typewriter-like scrolling
|
||||
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
||||
|
||||
self.highlightQuotes = True # Highlight text in quotes
|
||||
self.highlightEmph = True # Add colour to text emphasis
|
||||
self.wordCountTimer = 5.0 # Interval for word count update in seconds
|
||||
self.bigDocLimit = 800 # Size threshold for heavy editor features in kilobytes
|
||||
|
||||
## User-Selected Symbols
|
||||
self.highlightQuotes = True # Highlight text in quotes
|
||||
self.allowOpenSQuote = False # Allow open-ended single quotes
|
||||
self.allowOpenDQuote = True # Allow open-ended double quotes
|
||||
self.highlightEmph = True # Add colour to text emphasis
|
||||
|
||||
self.stopWhenIdle = True # Stop the status bar clock when the user is idle
|
||||
self.userIdleTime = 300 # Time of inactivity to consider user idle
|
||||
|
||||
# User-Selected Symbols
|
||||
self.fmtApostrophe = nwUnicode.U_RSQUO
|
||||
self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO]
|
||||
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO]
|
||||
self.fmtPadBefore = ""
|
||||
self.fmtPadAfter = ""
|
||||
self.fmtPadThin = False
|
||||
|
||||
## Spell Checking
|
||||
# Spell Checking
|
||||
self.spellTool = None
|
||||
self.spellLanguage = None
|
||||
|
||||
## Search Bar Switches
|
||||
# Search Bar Switches
|
||||
self.searchCase = False
|
||||
self.searchWord = False
|
||||
self.searchRegEx = False
|
||||
@@ -162,12 +181,12 @@ class Config:
|
||||
self.searchNextFile = False
|
||||
self.searchMatchCap = False
|
||||
|
||||
## Backup
|
||||
# Backup
|
||||
self.backupPath = ""
|
||||
self.backupOnClose = False
|
||||
self.askBeforeBackup = True
|
||||
|
||||
## State
|
||||
# State
|
||||
self.showRefPanel = True
|
||||
self.viewComments = True
|
||||
self.viewSynopsis = True
|
||||
@@ -216,8 +235,8 @@ class Config:
|
||||
self.kernelVer = "Unknown"
|
||||
|
||||
# Packages
|
||||
self.hasEnchant = False # The pyenchant package
|
||||
self.hasAssistant = False # The Qt Assistant executable
|
||||
self.hasEnchant = False # The pyenchant package
|
||||
self.hasAssistant = False # The Qt Assistant executable
|
||||
|
||||
# Recent Cache
|
||||
self.recentProj = {}
|
||||
@@ -253,7 +272,7 @@ class Config:
|
||||
confRoot = QStandardPaths.writableLocation(QStandardPaths.ConfigLocation)
|
||||
self.confPath = os.path.join(os.path.abspath(confRoot), self.appHandle)
|
||||
else:
|
||||
logger.info("Setting config from alternative path: %s" % confPath)
|
||||
logger.info("Setting config from alternative path: %s", confPath)
|
||||
self.confPath = confPath
|
||||
|
||||
if dataPath is None:
|
||||
@@ -263,24 +282,36 @@ class Config:
|
||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
|
||||
self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle)
|
||||
else:
|
||||
logger.info("Setting data path from alternative path: %s" % dataPath)
|
||||
logger.info("Setting data path from alternative path: %s", dataPath)
|
||||
self.dataPath = dataPath
|
||||
|
||||
logger.verbose("Config path: %s" % self.confPath)
|
||||
logger.verbose("Data path: %s" % self.dataPath)
|
||||
logger.verbose("Config path: %s", self.confPath)
|
||||
logger.verbose("Data path: %s", self.dataPath)
|
||||
|
||||
self.confFile = self.appHandle+".conf"
|
||||
self.lastPath = os.path.expanduser("~")
|
||||
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
||||
self.appRoot = os.path.join(self.appPath, os.path.pardir)
|
||||
self.confFile = self.appHandle+".conf"
|
||||
self.lastPath = os.path.expanduser("~")
|
||||
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
||||
self.appRoot = os.path.abspath(os.path.join(self.appPath, os.path.pardir))
|
||||
|
||||
if os.path.isfile(self.appRoot):
|
||||
# novelWriter is packaged as a single file, so the app and
|
||||
# root paths are the same, and equal to the folder that
|
||||
# contains the single executable.
|
||||
self.appRoot = os.path.dirname(self.appRoot)
|
||||
self.appPath = self.appRoot
|
||||
|
||||
# Assets
|
||||
self.assetPath = os.path.join(self.appPath, "assets")
|
||||
self.themeRoot = os.path.join(self.assetPath, "themes")
|
||||
self.dictPath = os.path.join(self.assetPath, "dict")
|
||||
self.iconPath = os.path.join(self.assetPath, "icons")
|
||||
self.appIcon = os.path.join(self.iconPath, "novelwriter.svg")
|
||||
|
||||
logger.verbose("App path: %s" % self.appPath)
|
||||
logger.verbose("Last path: %s" % self.lastPath)
|
||||
# Internationalisation
|
||||
self.nwLangPath = os.path.join(self.assetPath, "i18n")
|
||||
|
||||
logger.verbose("App path: %s", self.appPath)
|
||||
logger.verbose("Last path: %s", self.lastPath)
|
||||
|
||||
# If config folder does not exist, create it.
|
||||
# This assumes that the os config folder itself exists.
|
||||
@@ -288,8 +319,8 @@ class Config:
|
||||
try:
|
||||
os.mkdir(self.confPath)
|
||||
except Exception as e:
|
||||
logger.error("Could not create folder: %s" % self.confPath)
|
||||
logger.error(str(e))
|
||||
logger.error("Could not create folder: %s", self.confPath)
|
||||
logException()
|
||||
self.hasError = True
|
||||
self.errData.append("Could not create folder: %s" % self.confPath)
|
||||
self.errData.append(str(e))
|
||||
@@ -311,8 +342,8 @@ class Config:
|
||||
try:
|
||||
os.mkdir(self.dataPath)
|
||||
except Exception as e:
|
||||
logger.error("Could not create folder: %s" % self.dataPath)
|
||||
logger.error(str(e))
|
||||
logger.error("Could not create folder: %s", self.dataPath)
|
||||
logException()
|
||||
self.hasError = True
|
||||
self.errData.append("Could not create folder: %s" % self.dataPath)
|
||||
self.errData.append(str(e))
|
||||
@@ -343,6 +374,57 @@ class Config:
|
||||
|
||||
return True
|
||||
|
||||
def initLocalisation(self, nwApp):
|
||||
"""Initialise the localisation of the GUI.
|
||||
"""
|
||||
self.qLocal = QLocale(self.guiLang)
|
||||
QLocale.setDefault(self.qLocal)
|
||||
self.qtTrans = {}
|
||||
|
||||
langList = [
|
||||
(self.qtLangPath, "qtbase"), # Qt 5.x
|
||||
(self.nwLangPath, "qtbase"), # Alternative Qt 5.x
|
||||
(self.nwLangPath, "nw"), # novelWriter
|
||||
]
|
||||
for lngPath, lngBase in langList:
|
||||
for lngCode in self.qLocal.uiLanguages():
|
||||
qTrans = QTranslator()
|
||||
lngFile = "%s_%s" % (lngBase, lngCode.replace("-", "_"))
|
||||
if lngFile not in self.qtTrans:
|
||||
if qTrans.load(lngFile, lngPath):
|
||||
logger.debug("Loaded: %s", os.path.join(lngPath, lngFile))
|
||||
nwApp.installTranslator(qTrans)
|
||||
self.qtTrans[lngFile] = qTrans
|
||||
|
||||
return
|
||||
|
||||
def listLanguages(self, lngSet):
|
||||
"""List localisation files in the i18n folder. The default GUI
|
||||
language 'en_GB' is British English.
|
||||
"""
|
||||
if lngSet == self.LANG_NW:
|
||||
fPre = "nw_"
|
||||
fExt = ".qm"
|
||||
langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
|
||||
elif lngSet == self.LANG_PROJ:
|
||||
fPre = "project_"
|
||||
fExt = ".json"
|
||||
langList = {"en": "English"}
|
||||
else:
|
||||
return []
|
||||
|
||||
for qmFile in os.listdir(self.nwLangPath):
|
||||
if not os.path.isfile(os.path.join(self.nwLangPath, qmFile)):
|
||||
continue
|
||||
if not qmFile.startswith(fPre) or not qmFile.endswith(fExt):
|
||||
continue
|
||||
qmLang = qmFile[len(fPre):-len(fExt)]
|
||||
qmName = QLocale(qmLang).nativeLanguageName().title()
|
||||
if qmLang and qmName and qmLang != "en":
|
||||
langList[qmLang] = qmName
|
||||
|
||||
return sorted(langList.items(), key=lambda x: x[0])
|
||||
|
||||
def loadConfig(self):
|
||||
"""Load preferences from file and replace default settings.
|
||||
"""
|
||||
@@ -351,19 +433,19 @@ class Config:
|
||||
return False
|
||||
|
||||
cnfParse = configparser.ConfigParser()
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
try:
|
||||
with open(cnfPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
||||
cnfParse.read_file(inFile)
|
||||
except Exception as e:
|
||||
logger.error("Could not load config file")
|
||||
logger.error(str(e))
|
||||
logException()
|
||||
self.hasError = True
|
||||
self.errData.append("Could not load config file")
|
||||
self.errData.append(str(e))
|
||||
return False
|
||||
|
||||
## Main
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
self.guiTheme = self._parseLine(
|
||||
cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme
|
||||
@@ -386,15 +468,24 @@ class Config:
|
||||
self.lastNotes = self._parseLine(
|
||||
cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes
|
||||
)
|
||||
self.guiLang = self._parseLine(
|
||||
cnfParse, cnfSec, "guilang", self.CNF_STR, self.guiLang
|
||||
)
|
||||
|
||||
## Sizes
|
||||
# Sizes
|
||||
cnfSec = "Sizes"
|
||||
self.winGeometry = self._parseLine(
|
||||
cnfParse, cnfSec, "geometry", self.CNF_I_LST, self.winGeometry
|
||||
)
|
||||
self.prefGeometry = self._parseLine(
|
||||
cnfParse, cnfSec, "preferences", self.CNF_I_LST, self.prefGeometry
|
||||
)
|
||||
self.treeColWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "treecols", self.CNF_I_LST, self.treeColWidth
|
||||
)
|
||||
self.novelColWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "novelcols", self.CNF_I_LST, self.novelColWidth
|
||||
)
|
||||
self.projColWidth = self._parseLine(
|
||||
cnfParse, cnfSec, "projcols", self.CNF_I_LST, self.projColWidth
|
||||
)
|
||||
@@ -420,7 +511,7 @@ class Config:
|
||||
cnfParse, cnfSec, "hidehscroll", self.CNF_BOOL, self.hideHScroll
|
||||
)
|
||||
|
||||
## Project
|
||||
# Project
|
||||
cnfSec = "Project"
|
||||
self.autoSaveProj = self._parseLine(
|
||||
cnfParse, cnfSec, "autosaveproject", self.CNF_INT, self.autoSaveProj
|
||||
@@ -429,7 +520,7 @@ class Config:
|
||||
cnfParse, cnfSec, "autosavedoc", self.CNF_INT, self.autoSaveDoc
|
||||
)
|
||||
|
||||
## Editor
|
||||
# Editor
|
||||
cnfSec = "Editor"
|
||||
self.textFont = self._parseLine(
|
||||
cnfParse, cnfSec, "textfont", self.CNF_STR, self.textFont
|
||||
@@ -491,6 +582,15 @@ class Config:
|
||||
self.fmtDoubleQuotes = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtdoublequote", self.CNF_S_LST, self.fmtDoubleQuotes
|
||||
)
|
||||
self.fmtPadBefore = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtpadbefore", self.CNF_STR, self.fmtPadBefore
|
||||
)
|
||||
self.fmtPadAfter = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtpadafter", self.CNF_STR, self.fmtPadAfter
|
||||
)
|
||||
self.fmtPadThin = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtpadthin", self.CNF_BOOL, self.fmtPadThin
|
||||
)
|
||||
self.spellTool = self._parseLine(
|
||||
cnfParse, cnfSec, "spelltool", self.CNF_STR, self.spellTool
|
||||
)
|
||||
@@ -503,6 +603,9 @@ class Config:
|
||||
self.showLineEndings = self._parseLine(
|
||||
cnfParse, cnfSec, "showlineendings", self.CNF_BOOL, self.showLineEndings
|
||||
)
|
||||
self.showMultiSpaces = self._parseLine(
|
||||
cnfParse, cnfSec, "showmultispaces", self.CNF_BOOL, self.showMultiSpaces
|
||||
)
|
||||
self.bigDocLimit = self._parseLine(
|
||||
cnfParse, cnfSec, "bigdoclimit", self.CNF_INT, self.bigDocLimit
|
||||
)
|
||||
@@ -512,11 +615,23 @@ class Config:
|
||||
self.highlightQuotes = self._parseLine(
|
||||
cnfParse, cnfSec, "highlightquotes", self.CNF_BOOL, self.highlightQuotes
|
||||
)
|
||||
self.allowOpenSQuote = self._parseLine(
|
||||
cnfParse, cnfSec, "allowopensquote", self.CNF_BOOL, self.allowOpenSQuote
|
||||
)
|
||||
self.allowOpenDQuote = self._parseLine(
|
||||
cnfParse, cnfSec, "allowopendquote", self.CNF_BOOL, self.allowOpenDQuote
|
||||
)
|
||||
self.highlightEmph = self._parseLine(
|
||||
cnfParse, cnfSec, "highlightemph", self.CNF_BOOL, self.highlightEmph
|
||||
)
|
||||
self.stopWhenIdle = self._parseLine(
|
||||
cnfParse, cnfSec, "stopwhenidle", self.CNF_BOOL, self.stopWhenIdle
|
||||
)
|
||||
self.userIdleTime = self._parseLine(
|
||||
cnfParse, cnfSec, "useridletime", self.CNF_INT, self.userIdleTime
|
||||
)
|
||||
|
||||
## Backup
|
||||
# Backup
|
||||
cnfSec = "Backup"
|
||||
self.backupPath = self._parseLine(
|
||||
cnfParse, cnfSec, "backuppath", self.CNF_STR, self.backupPath
|
||||
@@ -528,7 +643,7 @@ class Config:
|
||||
cnfParse, cnfSec, "askbeforebackup", self.CNF_BOOL, self.askBeforeBackup
|
||||
)
|
||||
|
||||
## State
|
||||
# State
|
||||
cnfSec = "State"
|
||||
self.showRefPanel = self._parseLine(
|
||||
cnfParse, cnfSec, "showrefpanel", self.CNF_BOOL, self.showRefPanel
|
||||
@@ -558,7 +673,7 @@ class Config:
|
||||
cnfParse, cnfSec, "searchmatchcap", self.CNF_BOOL, self.searchMatchCap
|
||||
)
|
||||
|
||||
## Path
|
||||
# Path
|
||||
cnfSec = "Path"
|
||||
self.lastPath = self._parseLine(
|
||||
cnfParse, cnfSec, "lastpath", self.CNF_STR, self.lastPath
|
||||
@@ -567,6 +682,15 @@ class Config:
|
||||
# Check Certain Values for None
|
||||
self.spellLanguage = self._checkNone(self.spellLanguage)
|
||||
|
||||
# If we're using straight quotes, disable auto-replace
|
||||
if self.fmtSingleQuotes == ["'", "'"] and self.doReplaceSQuote:
|
||||
logger.info("Using straight single quotes, so disabling auto-replace")
|
||||
self.doReplaceSQuote = False
|
||||
|
||||
if self.fmtDoubleQuotes == ["\"", "\""] and self.doReplaceDQuote:
|
||||
logger.info("Using straight double quotes, so disabling auto-replace")
|
||||
self.doReplaceDQuote = False
|
||||
|
||||
return True
|
||||
|
||||
def saveConfig(self):
|
||||
@@ -580,7 +704,7 @@ class Config:
|
||||
|
||||
# Set options
|
||||
|
||||
## Main
|
||||
# Main
|
||||
cnfSec = "Main"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "timestamp", formatTimeStamp(time()))
|
||||
@@ -591,12 +715,15 @@ class Config:
|
||||
cnfParse.set(cnfSec, "guifont", str(self.guiFont))
|
||||
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
|
||||
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
|
||||
cnfParse.set(cnfSec, "guilang", str(self.guiLang))
|
||||
|
||||
## Sizes
|
||||
# Sizes
|
||||
cnfSec = "Sizes"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "geometry", self._packList(self.winGeometry))
|
||||
cnfParse.set(cnfSec, "preferences", self._packList(self.prefGeometry))
|
||||
cnfParse.set(cnfSec, "treecols", self._packList(self.treeColWidth))
|
||||
cnfParse.set(cnfSec, "novelcols", self._packList(self.novelColWidth))
|
||||
cnfParse.set(cnfSec, "projcols", self._packList(self.projColWidth))
|
||||
cnfParse.set(cnfSec, "mainpane", self._packList(self.mainPanePos))
|
||||
cnfParse.set(cnfSec, "docpane", self._packList(self.docPanePos))
|
||||
@@ -606,13 +733,13 @@ class Config:
|
||||
cnfParse.set(cnfSec, "hidevscroll", str(self.hideVScroll))
|
||||
cnfParse.set(cnfSec, "hidehscroll", str(self.hideHScroll))
|
||||
|
||||
## Project
|
||||
# Project
|
||||
cnfSec = "Project"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "autosaveproject", str(self.autoSaveProj))
|
||||
cnfParse.set(cnfSec, "autosavedoc", str(self.autoSaveDoc))
|
||||
|
||||
## Editor
|
||||
# Editor
|
||||
cnfSec = "Editor"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "textfont", str(self.textFont))
|
||||
@@ -635,23 +762,31 @@ class Config:
|
||||
cnfParse.set(cnfSec, "autoscrollpos", str(self.autoScrollPos))
|
||||
cnfParse.set(cnfSec, "fmtsinglequote", self._packList(self.fmtSingleQuotes))
|
||||
cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes))
|
||||
cnfParse.set(cnfSec, "fmtpadbefore", str(self.fmtPadBefore))
|
||||
cnfParse.set(cnfSec, "fmtpadafter", str(self.fmtPadAfter))
|
||||
cnfParse.set(cnfSec, "fmtpadthin", str(self.fmtPadThin))
|
||||
cnfParse.set(cnfSec, "spelltool", str(self.spellTool))
|
||||
cnfParse.set(cnfSec, "spellcheck", str(self.spellLanguage))
|
||||
cnfParse.set(cnfSec, "showtabsnspaces", str(self.showTabsNSpaces))
|
||||
cnfParse.set(cnfSec, "showlineendings", str(self.showLineEndings))
|
||||
cnfParse.set(cnfSec, "showmultispaces", str(self.showMultiSpaces))
|
||||
cnfParse.set(cnfSec, "bigdoclimit", str(self.bigDocLimit))
|
||||
cnfParse.set(cnfSec, "showfullpath", str(self.showFullPath))
|
||||
cnfParse.set(cnfSec, "highlightquotes", str(self.highlightQuotes))
|
||||
cnfParse.set(cnfSec, "allowopensquote", str(self.allowOpenSQuote))
|
||||
cnfParse.set(cnfSec, "allowopendquote", str(self.allowOpenDQuote))
|
||||
cnfParse.set(cnfSec, "highlightemph", str(self.highlightEmph))
|
||||
cnfParse.set(cnfSec, "stopwhenidle", str(self.stopWhenIdle))
|
||||
cnfParse.set(cnfSec, "useridletime", str(self.userIdleTime))
|
||||
|
||||
## Backup
|
||||
# Backup
|
||||
cnfSec = "Backup"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "backuppath", str(self.backupPath))
|
||||
cnfParse.set(cnfSec, "backuponclose", str(self.backupOnClose))
|
||||
cnfParse.set(cnfSec, "askbeforebackup", str(self.askBeforeBackup))
|
||||
|
||||
## State
|
||||
# State
|
||||
cnfSec = "State"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "showrefpanel", str(self.showRefPanel))
|
||||
@@ -664,7 +799,7 @@ class Config:
|
||||
cnfParse.set(cnfSec, "searchnextfile", str(self.searchNextFile))
|
||||
cnfParse.set(cnfSec, "searchmatchcap", str(self.searchMatchCap))
|
||||
|
||||
## Path
|
||||
# Path
|
||||
cnfSec = "Path"
|
||||
cnfParse.add_section(cnfSec)
|
||||
cnfParse.set(cnfSec, "lastpath", str(self.lastPath))
|
||||
@@ -672,12 +807,12 @@ class Config:
|
||||
# Write config file
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
try:
|
||||
with open(cnfPath, mode="w", encoding="utf8") as outFile:
|
||||
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
||||
cnfParse.write(outFile)
|
||||
self.confChanged = False
|
||||
except Exception as e:
|
||||
logger.error("Could not save config file")
|
||||
logger.error(str(e))
|
||||
logException()
|
||||
self.hasError = True
|
||||
self.errData.append("Could not save config file")
|
||||
self.errData.append(str(e))
|
||||
@@ -696,13 +831,13 @@ class Config:
|
||||
|
||||
if os.path.isfile(cacheFile):
|
||||
try:
|
||||
with open(cacheFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = json.load(inFile)
|
||||
|
||||
for projPath in theData.keys():
|
||||
theEntry = theData[projPath]
|
||||
theTitle = ""
|
||||
lastTime = 0
|
||||
theEntry = theData[projPath]
|
||||
theTitle = ""
|
||||
lastTime = 0
|
||||
wordCount = 0
|
||||
if "title" in theEntry.keys():
|
||||
theTitle = theEntry["title"]
|
||||
@@ -711,9 +846,9 @@ class Config:
|
||||
if "words" in theEntry.keys():
|
||||
wordCount = int(theEntry["words"])
|
||||
self.recentProj[projPath] = {
|
||||
"title" : theTitle,
|
||||
"time" : lastTime,
|
||||
"words" : wordCount,
|
||||
"title": theTitle,
|
||||
"time": lastTime,
|
||||
"words": wordCount,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -734,7 +869,7 @@ class Config:
|
||||
cacheTemp = os.path.join(self.dataPath, nwFiles.RECENT_FILE+"~")
|
||||
|
||||
try:
|
||||
with open(cacheTemp, mode="w+", encoding="utf8") as outFile:
|
||||
with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self.recentProj, outFile, indent=2)
|
||||
except Exception as e:
|
||||
self.hasError = True
|
||||
@@ -752,9 +887,9 @@ class Config:
|
||||
"""Add or update recent cache information o9n a given project.
|
||||
"""
|
||||
self.recentProj[os.path.abspath(projPath)] = {
|
||||
"title" : projTitle,
|
||||
"time" : int(saveTime),
|
||||
"words" : int(wordCount),
|
||||
"title": projTitle,
|
||||
"time": int(saveTime),
|
||||
"words": int(wordCount),
|
||||
}
|
||||
return True
|
||||
|
||||
@@ -763,10 +898,10 @@ class Config:
|
||||
"""
|
||||
if thePath in self.recentProj:
|
||||
del self.recentProj[thePath]
|
||||
logger.verbose("Removed recent: %s" % thePath)
|
||||
logger.verbose("Removed recent: %s", thePath)
|
||||
self.saveRecentCache()
|
||||
else:
|
||||
logger.error("Unknown recent: %s" % thePath)
|
||||
logger.error("Unknown recent: %s", thePath)
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -811,11 +946,22 @@ class Config:
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setPreferencesSize(self, newWidth, newHeight):
|
||||
self.prefGeometry[0] = int(newWidth/self.guiScale)
|
||||
self.prefGeometry[1] = int(newHeight/self.guiScale)
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setTreeColWidths(self, colWidths):
|
||||
self.treeColWidth = [int(x/self.guiScale) for x in colWidths]
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setNovelColWidths(self, colWidths):
|
||||
self.novelColWidth = [int(x/self.guiScale) for x in colWidths]
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setProjColWidths(self, colWidths):
|
||||
self.projColWidth = [int(x/self.guiScale) for x in colWidths]
|
||||
self.confChanged = True
|
||||
@@ -869,9 +1015,15 @@ class Config:
|
||||
def getWinSize(self):
|
||||
return [int(x*self.guiScale) for x in self.winGeometry]
|
||||
|
||||
def getPreferencesSize(self):
|
||||
return [int(x*self.guiScale) for x in self.prefGeometry]
|
||||
|
||||
def getTreeColWidths(self):
|
||||
return [int(x*self.guiScale) for x in self.treeColWidth]
|
||||
|
||||
def getNovelColWidths(self):
|
||||
return [int(x*self.guiScale) for x in self.novelColWidth]
|
||||
|
||||
def getProjColWidths(self):
|
||||
return [int(x*self.guiScale) for x in self.projColWidth]
|
||||
|
||||
@@ -945,9 +1097,9 @@ class Config:
|
||||
return self._unpackList(
|
||||
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_S_LST
|
||||
)
|
||||
except ValueError as e:
|
||||
except ValueError:
|
||||
logger.error("Failed to load value from config file.")
|
||||
logger.error(str(e))
|
||||
logException()
|
||||
return cnfDefault
|
||||
|
||||
return cnfDefault
|
||||
@@ -967,9 +1119,9 @@ class Config:
|
||||
"""Cheks if we have the optional packages used by some features.
|
||||
"""
|
||||
try:
|
||||
import enchant # noqa: F401
|
||||
import enchant # noqa: F401
|
||||
self.hasEnchant = True
|
||||
logger.debug("Checking package 'pyenchant': Ok")
|
||||
logger.debug("Checking package 'pyenchant': OK")
|
||||
except Exception:
|
||||
self.hasEnchant = False
|
||||
logger.debug("Checking package 'pyenchant': Missing")
|
||||
@@ -977,7 +1129,7 @@ class Config:
|
||||
assistPath = shutil.which("assistant")
|
||||
self.hasAssistant = assistPath is not None
|
||||
if self.hasAssistant:
|
||||
logger.debug("Checking executable 'assistant': Ok")
|
||||
logger.debug("Checking executable 'assistant': OK")
|
||||
else:
|
||||
logger.debug("Checking executable 'assistant': Missing")
|
||||
|
||||
|
||||
451
nw/constants.py
Normal file
@@ -0,0 +1,451 @@
|
||||
"""
|
||||
novelWriter – Constants
|
||||
=======================
|
||||
Constants and maps for translating flags and enums to text
|
||||
|
||||
File History:
|
||||
Created: 2019-04-28 [0.0.1]
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
|
||||
|
||||
from nw.enum import nwItemClass, nwItemLayout, nwItemType, nwOutline
|
||||
|
||||
|
||||
def trConst(tString):
|
||||
"""Wrapper function for locally translating constants.
|
||||
"""
|
||||
return QCoreApplication.translate("Constant", tString)
|
||||
|
||||
|
||||
class nwConst():
|
||||
|
||||
# Date and Time Formats
|
||||
FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format
|
||||
FMT_FSTAMP = "%Y-%m-%d %H.%M.%S" # FileName safe format
|
||||
FMT_DSTAMP = "%Y-%m-%d" # Date only format
|
||||
|
||||
# Various Hard Limits
|
||||
MAX_DEPTH = 30 # Maximum folder depth of a project
|
||||
MAX_DOCSIZE = 5000000 # Maxium size of a single document
|
||||
MAX_BUILDSIZE = 10000000 # Maxium size of a project build
|
||||
|
||||
# Spell Check Providers
|
||||
SP_INTERNAL = "internal"
|
||||
SP_ENCHANT = "enchant"
|
||||
|
||||
# END Class nwConst
|
||||
|
||||
|
||||
class nwLists():
|
||||
"""Lists used for grouping various other constants.
|
||||
"""
|
||||
# Regular user-accessible item types
|
||||
REG_TYPES = {nwItemType.ROOT, nwItemType.FOLDER, nwItemType.FILE}
|
||||
|
||||
# Item classes where the full list of novel layouts are allowed
|
||||
CLS_NOVEL = {nwItemClass.NOVEL, nwItemClass.ARCHIVE}
|
||||
|
||||
# Item classes which do not require items to have same class
|
||||
FREE_CLASS = {nwItemClass.ARCHIVE, nwItemClass.TRASH}
|
||||
|
||||
# END Class nwLists
|
||||
|
||||
|
||||
class nwRegEx():
|
||||
|
||||
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_EB = r"(?<![\w\\])([\*]{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_ST = r"(?<![\w\\])([~]{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
|
||||
# END Class nwRegEx
|
||||
|
||||
|
||||
class nwFiles():
|
||||
|
||||
PROJ_FILE = "nwProject.nwx"
|
||||
PROJ_DICT = "wordlist.txt"
|
||||
PROJ_LOCK = "nwProject.lock"
|
||||
TOC_TXT = "ToC.txt"
|
||||
SESS_STATS = "sessionStats.log"
|
||||
INDEX_FILE = "tagsIndex.json"
|
||||
OPTS_FILE = "guiOptions.json"
|
||||
RECENT_FILE = "recentProjects.json"
|
||||
BUILD_CACHE = "prevBuild.json"
|
||||
|
||||
# END Class nwFiles
|
||||
|
||||
|
||||
class nwKeyWords:
|
||||
|
||||
TAG_KEY = "@tag"
|
||||
POV_KEY = "@pov"
|
||||
FOCUS_KEY = "@focus"
|
||||
CHAR_KEY = "@char"
|
||||
PLOT_KEY = "@plot"
|
||||
TIME_KEY = "@time"
|
||||
WORLD_KEY = "@location"
|
||||
OBJECT_KEY = "@object"
|
||||
ENTITY_KEY = "@entity"
|
||||
CUSTOM_KEY = "@custom"
|
||||
|
||||
# Set of Valid Keys
|
||||
VALID_KEYS = {
|
||||
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY,
|
||||
WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY
|
||||
}
|
||||
|
||||
# Map from Keys to Item Class
|
||||
KEY_CLASS = {
|
||||
POV_KEY: nwItemClass.CHARACTER,
|
||||
FOCUS_KEY: nwItemClass.CHARACTER,
|
||||
CHAR_KEY: nwItemClass.CHARACTER,
|
||||
PLOT_KEY: nwItemClass.PLOT,
|
||||
TIME_KEY: nwItemClass.TIMELINE,
|
||||
WORLD_KEY: nwItemClass.WORLD,
|
||||
OBJECT_KEY: nwItemClass.OBJECT,
|
||||
ENTITY_KEY: nwItemClass.ENTITY,
|
||||
CUSTOM_KEY: nwItemClass.CUSTOM,
|
||||
}
|
||||
|
||||
# END Class nwKeyWords
|
||||
|
||||
|
||||
class nwLabels():
|
||||
|
||||
CLASS_NAME = {
|
||||
nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
|
||||
nwItemClass.NOVEL: QT_TRANSLATE_NOOP("Constant", "Novel"),
|
||||
nwItemClass.PLOT: QT_TRANSLATE_NOOP("Constant", "Plot"),
|
||||
nwItemClass.CHARACTER: QT_TRANSLATE_NOOP("Constant", "Characters"),
|
||||
nwItemClass.WORLD: QT_TRANSLATE_NOOP("Constant", "Locations"),
|
||||
nwItemClass.TIMELINE: QT_TRANSLATE_NOOP("Constant", "Timeline"),
|
||||
nwItemClass.OBJECT: QT_TRANSLATE_NOOP("Constant", "Objects"),
|
||||
nwItemClass.ENTITY: QT_TRANSLATE_NOOP("Constant", "Entity"),
|
||||
nwItemClass.CUSTOM: QT_TRANSLATE_NOOP("Constant", "Custom"),
|
||||
nwItemClass.ARCHIVE: QT_TRANSLATE_NOOP("Constant", "Outtakes"),
|
||||
nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
|
||||
}
|
||||
CLASS_FLAG = {
|
||||
nwItemClass.NO_CLASS: "0",
|
||||
nwItemClass.NOVEL: "N",
|
||||
nwItemClass.PLOT: "P",
|
||||
nwItemClass.CHARACTER: "C",
|
||||
nwItemClass.WORLD: "L",
|
||||
nwItemClass.TIMELINE: "T",
|
||||
nwItemClass.OBJECT: "O",
|
||||
nwItemClass.ENTITY: "E",
|
||||
nwItemClass.CUSTOM: "X",
|
||||
nwItemClass.ARCHIVE: "U",
|
||||
nwItemClass.TRASH: "R",
|
||||
}
|
||||
CLASS_ICON = {
|
||||
nwItemClass.NO_CLASS: "cls_none",
|
||||
nwItemClass.NOVEL: "cls_novel",
|
||||
nwItemClass.PLOT: "cls_plot",
|
||||
nwItemClass.CHARACTER: "cls_character",
|
||||
nwItemClass.WORLD: "cls_world",
|
||||
nwItemClass.TIMELINE: "cls_timeline",
|
||||
nwItemClass.OBJECT: "cls_object",
|
||||
nwItemClass.ENTITY: "cls_entity",
|
||||
nwItemClass.CUSTOM: "cls_custom",
|
||||
nwItemClass.ARCHIVE: "cls_archive",
|
||||
nwItemClass.TRASH: "cls_trash",
|
||||
}
|
||||
LAYOUT_NAME = {
|
||||
nwItemLayout.NO_LAYOUT: QT_TRANSLATE_NOOP("Constant", "None"),
|
||||
nwItemLayout.TITLE: QT_TRANSLATE_NOOP("Constant", "Title Page"),
|
||||
nwItemLayout.BOOK: QT_TRANSLATE_NOOP("Constant", "Book"),
|
||||
nwItemLayout.PAGE: QT_TRANSLATE_NOOP("Constant", "Plain Page"),
|
||||
nwItemLayout.PARTITION: QT_TRANSLATE_NOOP("Constant", "Partition"),
|
||||
nwItemLayout.UNNUMBERED: QT_TRANSLATE_NOOP("Constant", "Unnumbered"),
|
||||
nwItemLayout.CHAPTER: QT_TRANSLATE_NOOP("Constant", "Chapter"),
|
||||
nwItemLayout.SCENE: QT_TRANSLATE_NOOP("Constant", "Scene"),
|
||||
nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Note"),
|
||||
}
|
||||
LAYOUT_FLAG = {
|
||||
nwItemLayout.NO_LAYOUT: "Xo",
|
||||
nwItemLayout.TITLE: "Tt",
|
||||
nwItemLayout.BOOK: "Bk",
|
||||
nwItemLayout.PAGE: "Pg",
|
||||
nwItemLayout.PARTITION: "Pt",
|
||||
nwItemLayout.UNNUMBERED: "Un",
|
||||
nwItemLayout.CHAPTER: "Ch",
|
||||
nwItemLayout.SCENE: "Sc",
|
||||
nwItemLayout.NOTE: "Nt",
|
||||
}
|
||||
KEY_NAME = {
|
||||
nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"),
|
||||
nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"),
|
||||
nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"),
|
||||
nwKeyWords.CHAR_KEY: QT_TRANSLATE_NOOP("Constant", "Characters"),
|
||||
nwKeyWords.PLOT_KEY: QT_TRANSLATE_NOOP("Constant", "Plot"),
|
||||
nwKeyWords.TIME_KEY: QT_TRANSLATE_NOOP("Constant", "Timeline"),
|
||||
nwKeyWords.WORLD_KEY: QT_TRANSLATE_NOOP("Constant", "Locations"),
|
||||
nwKeyWords.OBJECT_KEY: QT_TRANSLATE_NOOP("Constant", "Objects"),
|
||||
nwKeyWords.ENTITY_KEY: QT_TRANSLATE_NOOP("Constant", "Entities"),
|
||||
nwKeyWords.CUSTOM_KEY: QT_TRANSLATE_NOOP("Constant", "Custom"),
|
||||
}
|
||||
OUTLINE_COLS = {
|
||||
nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
|
||||
nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
|
||||
nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"),
|
||||
nwOutline.LINE: QT_TRANSLATE_NOOP("Constant", "Line"),
|
||||
nwOutline.CCOUNT: QT_TRANSLATE_NOOP("Constant", "Chars"),
|
||||
nwOutline.WCOUNT: QT_TRANSLATE_NOOP("Constant", "Words"),
|
||||
nwOutline.PCOUNT: QT_TRANSLATE_NOOP("Constant", "Pars"),
|
||||
nwOutline.POV: QT_TRANSLATE_NOOP("Constant", "POV"),
|
||||
nwOutline.FOCUS: QT_TRANSLATE_NOOP("Constant", "Focus"),
|
||||
nwOutline.CHAR: KEY_NAME[nwKeyWords.CHAR_KEY],
|
||||
nwOutline.PLOT: KEY_NAME[nwKeyWords.PLOT_KEY],
|
||||
nwOutline.TIME: KEY_NAME[nwKeyWords.TIME_KEY],
|
||||
nwOutline.WORLD: KEY_NAME[nwKeyWords.WORLD_KEY],
|
||||
nwOutline.OBJECT: KEY_NAME[nwKeyWords.OBJECT_KEY],
|
||||
nwOutline.ENTITY: KEY_NAME[nwKeyWords.ENTITY_KEY],
|
||||
nwOutline.CUSTOM: KEY_NAME[nwKeyWords.CUSTOM_KEY],
|
||||
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
||||
}
|
||||
|
||||
# END Class nwLabels
|
||||
|
||||
|
||||
class nwQuotes():
|
||||
"""Allowed quotation marks.
|
||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||
"""
|
||||
SYMBOLS = {
|
||||
"\u0027": QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
|
||||
"\u0022": QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
|
||||
|
||||
"\u2018": QT_TRANSLATE_NOOP("Constant", "Left single quotation mark"),
|
||||
"\u2019": QT_TRANSLATE_NOOP("Constant", "Right single quotation mark"),
|
||||
"\u201a": QT_TRANSLATE_NOOP("Constant", "Single low-9 quotation mark"),
|
||||
"\u201b": QT_TRANSLATE_NOOP("Constant", "Single high-reversed-9 quotation mark"),
|
||||
"\u201c": QT_TRANSLATE_NOOP("Constant", "Left double quotation mark"),
|
||||
"\u201d": QT_TRANSLATE_NOOP("Constant", "Right double quotation mark"),
|
||||
"\u201e": QT_TRANSLATE_NOOP("Constant", "Double low-9 quotation mark"),
|
||||
"\u201f": QT_TRANSLATE_NOOP("Constant", "Double high-reversed-9 quotation mark"),
|
||||
"\u2e42": QT_TRANSLATE_NOOP("Constant", "Double low-reversed-9 quotation mark"),
|
||||
|
||||
"\u2039": QT_TRANSLATE_NOOP("Constant", "Single left-pointing angle quotation mark"),
|
||||
"\u203a": QT_TRANSLATE_NOOP("Constant", "Single right-pointing angle quotation mark"),
|
||||
"\u00ab": QT_TRANSLATE_NOOP("Constant", "Double left-pointing angle quotation mark"),
|
||||
"\u00bb": QT_TRANSLATE_NOOP("Constant", "Double right-pointing angle quotation mark"),
|
||||
|
||||
"\u300c": QT_TRANSLATE_NOOP("Constant", "Left corner bracket"),
|
||||
"\u300d": QT_TRANSLATE_NOOP("Constant", "Right corner bracket"),
|
||||
"\u300e": QT_TRANSLATE_NOOP("Constant", "Left white corner bracket"),
|
||||
"\u300f": QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
|
||||
}
|
||||
|
||||
# END Class nwQuotes
|
||||
|
||||
|
||||
class nwUnicode:
|
||||
"""Supported unicode character constants and their HTML equivalents.
|
||||
"""
|
||||
# Unicode Constants
|
||||
# =================
|
||||
|
||||
# Quotation Marks
|
||||
U_QUOT = "\u0022" # Quotation mark
|
||||
U_APOS = "\u0027" # Apostrophe
|
||||
U_LAQUO = "\u00ab" # Left-pointing double angle quotation mark
|
||||
U_RAQUO = "\u00bb" # Right-pointing double angle quotation mark
|
||||
U_LSQUO = "\u2018" # Left single quotation mark
|
||||
U_RSQUO = "\u2019" # Right single quotation mark
|
||||
U_SBQUO = "\u201a" # Single low-9 quotation mark
|
||||
U_SUQUO = "\u201b" # Single high-reversed-9 quotation mark
|
||||
U_LDQUO = "\u201c" # Left double quotation mark
|
||||
U_RDQUO = "\u201d" # Right double quotation mark
|
||||
U_BDQUO = "\u201e" # Double low-9 quotation mark
|
||||
U_UDQUO = "\u201f" # Double high-reversed-9 quotation mark
|
||||
U_LSAQUO = "\u2039" # Single left-pointing angle quotation mark
|
||||
U_RSAQUO = "\u203a" # Single right-pointing angle quotation mark
|
||||
U_BDRQUO = "\u2e42" # Double low-reversed-9 quotation mark
|
||||
U_LCQUO = "\u300c" # Left corner bracket
|
||||
U_RCQUO = "\u300d" # Right corner bracket
|
||||
U_LWCQUO = "\u300e" # Left white corner bracket
|
||||
U_RWCQUO = "\u300f" # Right white corner bracket
|
||||
|
||||
# Punctuation
|
||||
U_FGDASH = "\u2012" # Figure dash
|
||||
U_ENDASH = "\u2013" # Short dash
|
||||
U_EMDASH = "\u2014" # Long dash
|
||||
U_HBAR = "\u2015" # Horizontal bar
|
||||
U_HELLIP = "\u2026" # Ellipsis
|
||||
U_MAPOSS = "\u02bc" # Modifier letter single apostrophe
|
||||
U_PRIME = "\u2032" # Prime
|
||||
U_DPRIME = "\u2033" # Double prime
|
||||
|
||||
# Spaces and Lines
|
||||
U_NBSP = "\u00a0" # Non-breaking space
|
||||
U_THSP = "\u2009" # Thin space
|
||||
U_THNBSP = "\u202f" # Thin non-breaking space
|
||||
U_ENSP = "\u2002" # Short (en) space
|
||||
U_EMSP = "\u2003" # Long (em) space
|
||||
U_LSEP = "\u2028" # Line separator
|
||||
U_PSEP = "\u2029" # Paragraph separator
|
||||
|
||||
# Symbols
|
||||
U_CHECK = "\u2714" # Heavy check mark
|
||||
U_CROSS = "\u2715" # Heavy cross mark
|
||||
U_BULL = "\u2022" # List bullet
|
||||
U_TRBULL = "\u2023" # Triangle bullet
|
||||
U_HYBULL = "\u2043" # Hyphen bullet
|
||||
U_FLOWER = "\u2055" # Flower punctuation mark
|
||||
U_PERMIL = "\u2030" # Per mille sign
|
||||
U_DEGREE = "\u00b0" # Degree symbol
|
||||
U_MINUS = "\u2212" # Minus sign
|
||||
U_TIMES = "\u00d7" # Multiplaction sign
|
||||
U_DIVIDE = "\u00f7" # Division sign
|
||||
|
||||
# Arrows
|
||||
U_UTRI = "\u25b2" # Up-pointing triangle
|
||||
U_UTRIS = "\u25b4" # Up-pointing triangle, small
|
||||
U_RTRI = "\u25b6" # Right-pointing triangle
|
||||
U_RTRIS = "\u25b8" # Right-pointing triangle, small
|
||||
U_DTRI = "\u25bc" # Down-pointing triangle
|
||||
U_DTRIS = "\u25be" # Down-pointing triangle, small
|
||||
U_LTRI = "\u25c0" # Left-pointing triangle
|
||||
U_LTRIS = "\u25c2" # Left-pointing triangle, small
|
||||
|
||||
# HTML Equivalents
|
||||
# ================
|
||||
|
||||
# Quotes
|
||||
H_QUOT = """
|
||||
H_APOS = "'"
|
||||
H_LAQUO = "«"
|
||||
H_RAQUO = "»"
|
||||
H_LSQUO = "‘"
|
||||
H_RSQUO = "’"
|
||||
H_SBQUO = "‚"
|
||||
H_SUQUO = "‛"
|
||||
H_LDQUO = "“"
|
||||
H_RDQUO = "”"
|
||||
H_BDQUO = "„"
|
||||
H_UDQUO = "‟"
|
||||
H_LSAQUO = "‹"
|
||||
H_RSAQUO = "›"
|
||||
H_BDRQUO = "⹂"
|
||||
H_LCQUO = "「"
|
||||
H_RCQUO = "」"
|
||||
H_LWCQUO = "『"
|
||||
H_RWCQUO = "』"
|
||||
|
||||
# Punctuation
|
||||
H_FGDASH = "‒"
|
||||
H_ENDASH = "–"
|
||||
H_EMDASH = "—"
|
||||
H_HBAR = "―"
|
||||
H_HELLIP = "…"
|
||||
H_MAPOSS = "ʼ"
|
||||
H_PRIME = "′"
|
||||
H_DPRIME = "″"
|
||||
|
||||
# Spaces
|
||||
H_NBSP = " "
|
||||
H_THSP = " "
|
||||
H_THNBSP = " "
|
||||
H_ENSP = " "
|
||||
H_EMSP = " "
|
||||
|
||||
# Symbols
|
||||
H_CHECK = "✔"
|
||||
H_CROSS = "✕"
|
||||
H_BULL = "•"
|
||||
H_TRBULL = "‣"
|
||||
H_HYBULL = "⁃"
|
||||
H_FLOWER = "⁕"
|
||||
H_PERMIL = "‰"
|
||||
H_DEGREE = "°"
|
||||
H_MINUS = "−"
|
||||
H_TIMES = "×"
|
||||
H_DIVIDE = "÷"
|
||||
|
||||
# Arrows
|
||||
H_UTRI = "▲"
|
||||
H_UTRIS = "▴"
|
||||
H_RTRI = "▶"
|
||||
H_RTRIS = "▸"
|
||||
H_DTRI = "▼"
|
||||
H_DTRIS = "▾"
|
||||
H_LTRI = "◀"
|
||||
H_LTRIS = "◂"
|
||||
|
||||
# END Class nwUnicode
|
||||
|
||||
|
||||
class nwHtmlUnicode():
|
||||
|
||||
U_TO_H = {
|
||||
# Quotes
|
||||
nwUnicode.U_QUOT: nwUnicode.H_QUOT,
|
||||
nwUnicode.U_APOS: nwUnicode.H_APOS,
|
||||
nwUnicode.U_LAQUO: nwUnicode.H_LAQUO,
|
||||
nwUnicode.U_RAQUO: nwUnicode.H_RAQUO,
|
||||
nwUnicode.U_LSQUO: nwUnicode.H_LSQUO,
|
||||
nwUnicode.U_RSQUO: nwUnicode.H_RSQUO,
|
||||
nwUnicode.U_SBQUO: nwUnicode.H_SBQUO,
|
||||
nwUnicode.U_SUQUO: nwUnicode.H_SUQUO,
|
||||
nwUnicode.U_LDQUO: nwUnicode.H_LDQUO,
|
||||
nwUnicode.U_RDQUO: nwUnicode.H_RDQUO,
|
||||
nwUnicode.U_BDQUO: nwUnicode.H_BDQUO,
|
||||
nwUnicode.U_UDQUO: nwUnicode.H_UDQUO,
|
||||
nwUnicode.U_LSAQUO: nwUnicode.H_LSAQUO,
|
||||
nwUnicode.U_RSAQUO: nwUnicode.H_RSAQUO,
|
||||
nwUnicode.U_BDRQUO: nwUnicode.H_BDRQUO,
|
||||
nwUnicode.U_LCQUO: nwUnicode.H_LCQUO,
|
||||
nwUnicode.U_RCQUO: nwUnicode.H_RCQUO,
|
||||
nwUnicode.U_LWCQUO: nwUnicode.H_LWCQUO,
|
||||
nwUnicode.U_RWCQUO: nwUnicode.H_RWCQUO,
|
||||
|
||||
# Punctuation
|
||||
nwUnicode.U_FGDASH: nwUnicode.H_FGDASH,
|
||||
nwUnicode.U_ENDASH: nwUnicode.H_ENDASH,
|
||||
nwUnicode.U_EMDASH: nwUnicode.H_EMDASH,
|
||||
nwUnicode.U_HBAR: nwUnicode.H_HBAR,
|
||||
nwUnicode.U_HELLIP: nwUnicode.H_HELLIP,
|
||||
nwUnicode.U_MAPOSS: nwUnicode.H_MAPOSS,
|
||||
nwUnicode.U_PRIME: nwUnicode.H_PRIME,
|
||||
nwUnicode.U_DPRIME: nwUnicode.H_DPRIME,
|
||||
|
||||
# Spaces
|
||||
nwUnicode.U_NBSP: nwUnicode.H_NBSP,
|
||||
nwUnicode.U_THSP: nwUnicode.H_THSP,
|
||||
nwUnicode.U_THNBSP: nwUnicode.H_THNBSP,
|
||||
nwUnicode.U_ENSP: nwUnicode.H_ENSP,
|
||||
nwUnicode.U_EMSP: nwUnicode.H_EMSP,
|
||||
|
||||
# Symbols
|
||||
nwUnicode.U_CHECK: nwUnicode.H_CHECK,
|
||||
nwUnicode.U_CROSS: nwUnicode.H_CROSS,
|
||||
nwUnicode.U_BULL: nwUnicode.H_BULL,
|
||||
nwUnicode.U_TRBULL: nwUnicode.H_TRBULL,
|
||||
nwUnicode.U_HYBULL: nwUnicode.H_HYBULL,
|
||||
nwUnicode.U_FLOWER: nwUnicode.H_FLOWER,
|
||||
nwUnicode.U_PERMIL: nwUnicode.H_PERMIL,
|
||||
nwUnicode.U_DEGREE: nwUnicode.H_DEGREE,
|
||||
nwUnicode.U_MINUS: nwUnicode.H_MINUS,
|
||||
nwUnicode.U_TIMES: nwUnicode.H_TIMES,
|
||||
nwUnicode.U_DIVIDE: nwUnicode.H_DIVIDE,
|
||||
}
|
||||
|
||||
# END Class nwHtmlUnicode
|
||||
@@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from nw.constants.iso import isoLanguage, isoCountry
|
||||
from nw.constants.constants import (
|
||||
nwConst, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode
|
||||
)
|
||||
from nw.constants.enum import (
|
||||
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
|
||||
nwDocInsert
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"isoCountry",
|
||||
"isoLanguage",
|
||||
"nwAlert",
|
||||
"nwConst",
|
||||
"nwDocAction",
|
||||
"nwDocInsert",
|
||||
"nwFiles",
|
||||
"nwItemClass",
|
||||
"nwItemLayout",
|
||||
"nwItemType",
|
||||
"nwKeyWords",
|
||||
"nwLabels",
|
||||
"nwOutline",
|
||||
"nwQuotes",
|
||||
"nwRegEx",
|
||||
"nwUnicode",
|
||||
]
|
||||
@@ -1,340 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Constants
|
||||
|
||||
novelWriter – Constants
|
||||
=========================
|
||||
Constants for translating flags and enums to text
|
||||
|
||||
File History:
|
||||
Created: 2019-04-28 [0.0.1]
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nw.constants.enum import (
|
||||
nwItemClass, nwItemLayout, nwItemType, nwOutline
|
||||
)
|
||||
|
||||
class nwConst():
|
||||
|
||||
# Date and Time Formats
|
||||
FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format
|
||||
FMT_FSTAMP = "%Y-%m-%d %H.%M.%S" # FileName safe format
|
||||
FMT_DSTAMP = "%Y-%m-%d" # Date only format
|
||||
|
||||
# Various Hard Limits
|
||||
MAX_DEPTH = 30 # Maximum folder depth of a project
|
||||
MAX_DOCSIZE = 5000000 # Maxium size of a single document
|
||||
MAX_BUILDSIZE = 10000000 # Maxium size of a project build
|
||||
|
||||
# Spell Check Providers
|
||||
SP_INTERNAL = "internal"
|
||||
SP_ENCHANT = "enchant"
|
||||
|
||||
# Check Lists
|
||||
REG_TYPES = {nwItemType.ROOT, nwItemType.FOLDER, nwItemType.FILE}
|
||||
|
||||
# END Class nwConst
|
||||
|
||||
class nwRegEx():
|
||||
|
||||
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_EB = r"(?<![\w\\])([\*]{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_ST = r"(?<![\w\\])([~]{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
|
||||
# END Class nwRegEx
|
||||
|
||||
class nwFiles():
|
||||
|
||||
PROJ_FILE = "nwProject.nwx"
|
||||
PROJ_DICT = "wordlist.txt"
|
||||
PROJ_LOCK = "nwProject.lock"
|
||||
TOC_TXT = "ToC.txt"
|
||||
SESS_STATS = "sessionStats.log"
|
||||
INDEX_FILE = "tagsIndex.json"
|
||||
OPTS_FILE = "guiOptions.json"
|
||||
RECENT_FILE = "recentProjects.json"
|
||||
BUILD_CACHE = "prevBuild.json"
|
||||
|
||||
# END Class nwFiles
|
||||
|
||||
class nwKeyWords:
|
||||
|
||||
TAG_KEY = "@tag"
|
||||
POV_KEY = "@pov"
|
||||
CHAR_KEY = "@char"
|
||||
PLOT_KEY = "@plot"
|
||||
TIME_KEY = "@time"
|
||||
WORLD_KEY = "@location"
|
||||
OBJECT_KEY = "@object"
|
||||
ENTITY_KEY = "@entity"
|
||||
CUSTOM_KEY = "@custom"
|
||||
|
||||
# Set of Valid Keys
|
||||
VALID_KEYS = {
|
||||
TAG_KEY, POV_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY,
|
||||
WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY
|
||||
}
|
||||
|
||||
# Map from Keys to Item Class
|
||||
KEY_CLASS = {
|
||||
CHAR_KEY : nwItemClass.CHARACTER,
|
||||
POV_KEY : nwItemClass.CHARACTER,
|
||||
PLOT_KEY : nwItemClass.PLOT,
|
||||
TIME_KEY : nwItemClass.TIMELINE,
|
||||
WORLD_KEY : nwItemClass.WORLD,
|
||||
OBJECT_KEY : nwItemClass.OBJECT,
|
||||
ENTITY_KEY : nwItemClass.ENTITY,
|
||||
CUSTOM_KEY : nwItemClass.CUSTOM,
|
||||
}
|
||||
|
||||
# END Class nwKeyWords
|
||||
|
||||
class nwLabels():
|
||||
|
||||
CLASS_NAME = {
|
||||
nwItemClass.NO_CLASS : "None",
|
||||
nwItemClass.NOVEL : "Novel",
|
||||
nwItemClass.PLOT : "Plot",
|
||||
nwItemClass.CHARACTER : "Characters",
|
||||
nwItemClass.WORLD : "Locations",
|
||||
nwItemClass.TIMELINE : "Timeline",
|
||||
nwItemClass.OBJECT : "Objects",
|
||||
nwItemClass.ENTITY : "Entity",
|
||||
nwItemClass.CUSTOM : "Custom",
|
||||
nwItemClass.ARCHIVE : "Outtakes",
|
||||
nwItemClass.TRASH : "Trash",
|
||||
}
|
||||
CLASS_FLAG = {
|
||||
nwItemClass.NO_CLASS : "0",
|
||||
nwItemClass.NOVEL : "N",
|
||||
nwItemClass.PLOT : "P",
|
||||
nwItemClass.CHARACTER : "C",
|
||||
nwItemClass.WORLD : "L",
|
||||
nwItemClass.TIMELINE : "T",
|
||||
nwItemClass.OBJECT : "O",
|
||||
nwItemClass.ENTITY : "E",
|
||||
nwItemClass.CUSTOM : "X",
|
||||
nwItemClass.ARCHIVE : "U",
|
||||
nwItemClass.TRASH : "R",
|
||||
}
|
||||
CLASS_ICON = {
|
||||
nwItemClass.NO_CLASS : "cls_none",
|
||||
nwItemClass.NOVEL : "cls_novel",
|
||||
nwItemClass.PLOT : "cls_plot",
|
||||
nwItemClass.CHARACTER : "cls_character",
|
||||
nwItemClass.WORLD : "cls_world",
|
||||
nwItemClass.TIMELINE : "cls_timeline",
|
||||
nwItemClass.OBJECT : "cls_object",
|
||||
nwItemClass.ENTITY : "cls_entity",
|
||||
nwItemClass.CUSTOM : "cls_custom",
|
||||
nwItemClass.ARCHIVE : "cls_archive",
|
||||
nwItemClass.TRASH : "cls_trash",
|
||||
}
|
||||
LAYOUT_NAME = {
|
||||
nwItemLayout.NO_LAYOUT : "None",
|
||||
nwItemLayout.TITLE : "Title Page",
|
||||
nwItemLayout.BOOK : "Book",
|
||||
nwItemLayout.PAGE : "Plain Page",
|
||||
nwItemLayout.PARTITION : "Partition",
|
||||
nwItemLayout.UNNUMBERED : "Unnumbered",
|
||||
nwItemLayout.CHAPTER : "Chapter",
|
||||
nwItemLayout.SCENE : "Scene",
|
||||
nwItemLayout.NOTE : "Note",
|
||||
}
|
||||
LAYOUT_FLAG = {
|
||||
nwItemLayout.NO_LAYOUT : "Xo",
|
||||
nwItemLayout.TITLE : "Tt",
|
||||
nwItemLayout.BOOK : "Bk",
|
||||
nwItemLayout.PAGE : "Pg",
|
||||
nwItemLayout.PARTITION : "Pt",
|
||||
nwItemLayout.UNNUMBERED : "Un",
|
||||
nwItemLayout.CHAPTER : "Ch",
|
||||
nwItemLayout.SCENE : "Sc",
|
||||
nwItemLayout.NOTE : "Nt",
|
||||
}
|
||||
KEY_NAME = {
|
||||
nwKeyWords.TAG_KEY : "Tag",
|
||||
nwKeyWords.POV_KEY : "Point of View",
|
||||
nwKeyWords.CHAR_KEY : "Characters",
|
||||
nwKeyWords.PLOT_KEY : "Plot",
|
||||
nwKeyWords.TIME_KEY : "Timeline",
|
||||
nwKeyWords.WORLD_KEY : "Locations",
|
||||
nwKeyWords.OBJECT_KEY : "Objects",
|
||||
nwKeyWords.ENTITY_KEY : "Entities",
|
||||
nwKeyWords.CUSTOM_KEY : "Custom",
|
||||
}
|
||||
OUTLINE_COLS = {
|
||||
nwOutline.TITLE : "Title",
|
||||
nwOutline.LEVEL : "Level",
|
||||
nwOutline.LABEL : "Document",
|
||||
nwOutline.LINE : "Line",
|
||||
nwOutline.CCOUNT : "Chars",
|
||||
nwOutline.WCOUNT : "Words",
|
||||
nwOutline.PCOUNT : "Pars",
|
||||
nwOutline.POV : "POV",
|
||||
nwOutline.CHAR : KEY_NAME[nwKeyWords.CHAR_KEY],
|
||||
nwOutline.PLOT : KEY_NAME[nwKeyWords.PLOT_KEY],
|
||||
nwOutline.TIME : KEY_NAME[nwKeyWords.TIME_KEY],
|
||||
nwOutline.WORLD : KEY_NAME[nwKeyWords.WORLD_KEY],
|
||||
nwOutline.OBJECT : KEY_NAME[nwKeyWords.OBJECT_KEY],
|
||||
nwOutline.ENTITY : KEY_NAME[nwKeyWords.ENTITY_KEY],
|
||||
nwOutline.CUSTOM : KEY_NAME[nwKeyWords.CUSTOM_KEY],
|
||||
nwOutline.SYNOP : "Synopsis",
|
||||
}
|
||||
|
||||
# END Class nwLabels
|
||||
|
||||
class nwQuotes():
|
||||
"""Allowed quotation marks.
|
||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||
"""
|
||||
|
||||
SYMBOLS = {
|
||||
"\u0027" : "Straight single quotation mark",
|
||||
"\u0022" : "Straight double quotation mark",
|
||||
|
||||
"\u2018" : "Left single quotation mark",
|
||||
"\u2019" : "Right single quotation mark",
|
||||
"\u201a" : "Single low-9 quotation mark",
|
||||
"\u201b" : "Single high-reversed-9 quotation mark",
|
||||
"\u201c" : "Left double quotation mark",
|
||||
"\u201d" : "Right double quotation mark",
|
||||
"\u201e" : "Double low-9 quotation mark",
|
||||
"\u201f" : "Double high-reversed-9 quotation mark",
|
||||
"\u2e42" : "Double low-reversed-9 quotation mark",
|
||||
|
||||
"\u2039" : "Single left-pointing angle quotation mark",
|
||||
"\u203a" : "Single right-pointing angle quotation mark",
|
||||
"\u00ab" : "Left-pointing double angle quotation mark",
|
||||
"\u00bb" : "Right-pointing double angle quotation mark",
|
||||
|
||||
"\u300c" : "Left corner bracket",
|
||||
"\u300d" : "Right corner bracket",
|
||||
"\u300e" : "Left white corner bracket",
|
||||
"\u300f" : "Right white corner bracket",
|
||||
}
|
||||
|
||||
# END Class nwQuotes
|
||||
|
||||
class nwUnicode:
|
||||
"""Supported unicode character constants and translation maps for HTML.
|
||||
"""
|
||||
|
||||
# Unicode Constants
|
||||
# =================
|
||||
|
||||
## Quotation Marks
|
||||
U_QUOT = "\u0022" # Quotation mark
|
||||
U_APOS = "\u0027" # Apostrophe
|
||||
U_LAQUO = "\u00ab" # Left-pointing double angle quotation mark
|
||||
U_RAQUO = "\u00bb" # Right-pointing double angle quotation mark
|
||||
U_LSQUO = "\u2018" # Left single quotation mark
|
||||
U_RSQUO = "\u2019" # Right single quotation mark
|
||||
U_SBQUO = "\u201a" # Single low-9 quotation mark
|
||||
U_SUQUO = "\u201b" # Single high-reversed-9 quotation mark
|
||||
U_LDQUO = "\u201c" # Left double quotation mark
|
||||
U_RDQUO = "\u201d" # Right double quotation mark
|
||||
U_BDQUO = "\u201e" # Double low-9 quotation mark
|
||||
U_UDQUO = "\u201f" # Double high-reversed-9 quotation mark
|
||||
U_LSAQUO = "\u2039" # Single left-pointing angle quotation mark
|
||||
U_RSAQUO = "\u203a" # Single right-pointing angle quotation mark
|
||||
U_BDRQUO = "\u2e42" # Double low-reversed-9 quotation mark
|
||||
U_LCQUO = "\u300c" # Left corner bracket
|
||||
U_RCQUO = "\u300d" # Right corner bracket
|
||||
U_LWCQUO = "\u300e" # Left white corner bracket
|
||||
U_RECQUO = "\u300f" # Right white corner bracket
|
||||
|
||||
## Punctuation
|
||||
U_ENDASH = "\u2013" # Short dash
|
||||
U_EMDASH = "\u2014" # Long dash
|
||||
U_HELLIP = "\u2026" # Ellipsis
|
||||
U_MAPOSS = "\u02bc" # Modifier letter single apostrophe
|
||||
|
||||
## Spaces and Lines
|
||||
U_NBSP = "\u00a0" # Non-breaking space
|
||||
U_THNSP = "\u2009" # Thin space
|
||||
U_THNBSP = "\u202f" # Thin non-breaking space
|
||||
U_LSEP = "\u2028" # Line separator
|
||||
U_PSEP = "\u2029" # Paragraph separator
|
||||
|
||||
## Symbols
|
||||
U_CHECK = "\u2714" # Heavy check mark
|
||||
U_MULT = "\u2715" # Multiplication x
|
||||
U_BULL = "\u2022" # List bullet
|
||||
|
||||
## Arrows
|
||||
U_UTRI = "\u25b2" # Up-pointing triangle
|
||||
U_UTRIS = "\u25b4" # Up-pointing triangle, small
|
||||
U_RTRI = "\u25b6" # Right-pointing triangle
|
||||
U_RTRIS = "\u25b8" # Right-pointing triangle, small
|
||||
U_DTRI = "\u25bc" # Down-pointing triangle
|
||||
U_DTRIS = "\u25be" # Down-pointing triangle, small
|
||||
U_LTRI = "\u25c0" # Left-pointing triangle
|
||||
U_LTRIS = "\u25c2" # Left-pointing triangle, small
|
||||
|
||||
# HTML Equivalents
|
||||
# ================
|
||||
|
||||
## Quotes
|
||||
H_QUOT = """
|
||||
H_APOS = "'"
|
||||
H_LAQUO = "«"
|
||||
H_RAQUO = "»"
|
||||
H_LSQUO = "‘"
|
||||
H_RSQUO = "’"
|
||||
H_SBQUO = "‚"
|
||||
H_SUQUO = "‛"
|
||||
H_LDQUO = "“"
|
||||
H_RDQUO = "”"
|
||||
H_BDQUO = "„"
|
||||
H_UDQUO = "‟"
|
||||
H_LSAQUO = "‹"
|
||||
H_RSAQUO = "›"
|
||||
H_BDRQUO = "⹂"
|
||||
H_LCQUO = "「"
|
||||
H_RCQUO = "」"
|
||||
H_LWCQUO = "『"
|
||||
H_LWCQUO = "『"
|
||||
|
||||
## Punctuation
|
||||
H_ENDASH = "–"
|
||||
H_EMDASH = "—"
|
||||
H_HELLIP = "…"
|
||||
H_MAPOSS = "ʼ"
|
||||
|
||||
## Spaces
|
||||
H_NBSP = " "
|
||||
H_THNSP = " "
|
||||
H_THNBSP = " "
|
||||
|
||||
## Symbols
|
||||
H_CHECK = "✔"
|
||||
H_MULT = "✕"
|
||||
H_BULL = "•"
|
||||
|
||||
## Arrows
|
||||
H_UTRI = "▲"
|
||||
H_UTRIS = "▴"
|
||||
H_RTRI = "▶"
|
||||
H_RTRIS = "▸"
|
||||
H_DTRI = "▼"
|
||||
H_DTRIS = "▾"
|
||||
H_LTRI = "◀"
|
||||
H_LTRIS = "◂"
|
||||
|
||||
# END Class nwUnicode
|
||||
@@ -1,147 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Enums
|
||||
|
||||
novelWriter – Enums
|
||||
=====================
|
||||
All enum values
|
||||
|
||||
File History:
|
||||
Created: 2018-11-02 [0.0.1]
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class nwItemType(Enum):
|
||||
|
||||
NO_TYPE = 0
|
||||
ROOT = 1
|
||||
FOLDER = 2
|
||||
FILE = 3
|
||||
TRASH = 4
|
||||
|
||||
# END Enum nwItemType
|
||||
|
||||
class nwItemClass(Enum):
|
||||
|
||||
NO_CLASS = 0
|
||||
NOVEL = 1
|
||||
PLOT = 2
|
||||
CHARACTER = 3
|
||||
WORLD = 4
|
||||
TIMELINE = 5
|
||||
OBJECT = 6
|
||||
ENTITY = 7
|
||||
CUSTOM = 8
|
||||
ARCHIVE = 9
|
||||
TRASH = 10
|
||||
|
||||
# END Enum nwItemClass
|
||||
|
||||
class nwItemLayout(Enum):
|
||||
|
||||
NO_LAYOUT = 0
|
||||
TITLE = 1
|
||||
BOOK = 2
|
||||
PAGE = 3
|
||||
PARTITION = 4
|
||||
UNNUMBERED = 5
|
||||
CHAPTER = 6
|
||||
SCENE = 7
|
||||
NOTE = 8
|
||||
|
||||
# END Enum nwItemLayout
|
||||
|
||||
class nwDocAction(Enum):
|
||||
|
||||
NO_ACTION = 0
|
||||
UNDO = 1
|
||||
REDO = 2
|
||||
CUT = 3
|
||||
COPY = 4
|
||||
PASTE = 5
|
||||
EMPH = 6
|
||||
STRONG = 7
|
||||
STRIKE = 8
|
||||
S_QUOTE = 9
|
||||
D_QUOTE = 10
|
||||
SEL_ALL = 11
|
||||
SEL_PARA = 12
|
||||
FIND = 13
|
||||
REPLACE = 14
|
||||
GO_NEXT = 15
|
||||
GO_PREV = 16
|
||||
REPL_NEXT = 17
|
||||
BLOCK_H1 = 18
|
||||
BLOCK_H2 = 19
|
||||
BLOCK_H3 = 20
|
||||
BLOCK_H4 = 21
|
||||
BLOCK_COM = 22
|
||||
BLOCK_TXT = 23
|
||||
REPL_SNG = 24
|
||||
REPL_DBL = 25
|
||||
|
||||
# END Enum nwDocAction
|
||||
|
||||
class nwDocInsert(Enum):
|
||||
|
||||
NO_INSERT = 0
|
||||
HARD_BREAK = 1
|
||||
NB_SPACE = 2
|
||||
THIN_SPACE = 3
|
||||
THIN_NB_SPACE = 4
|
||||
SHORT_DASH = 5
|
||||
LONG_DASH = 6
|
||||
ELLIPSIS = 7
|
||||
QUOTE_LS = 8
|
||||
QUOTE_RS = 9
|
||||
QUOTE_LD = 10
|
||||
QUOTE_RD = 11
|
||||
MODAPOS_S = 12
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
class nwAlert(Enum):
|
||||
|
||||
INFO = 0
|
||||
WARN = 1
|
||||
ERROR = 2
|
||||
BUG = 3
|
||||
|
||||
# END Enum nwAlert
|
||||
|
||||
class nwOutline(Enum):
|
||||
|
||||
TITLE = 0
|
||||
LEVEL = 1
|
||||
LABEL = 2
|
||||
LINE = 3
|
||||
CCOUNT = 4
|
||||
WCOUNT = 5
|
||||
PCOUNT = 6
|
||||
POV = 7
|
||||
CHAR = 8
|
||||
PLOT = 9
|
||||
TIME = 10
|
||||
WORLD = 11
|
||||
OBJECT = 12
|
||||
ENTITY = 13
|
||||
CUSTOM = 14
|
||||
SYNOP = 15
|
||||
|
||||
# END Enum nwOutline
|
||||
@@ -1,473 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter ISO Codes
|
||||
|
||||
novelWriter – ISO Codes
|
||||
=========================
|
||||
Handles translating language codes to language names
|
||||
|
||||
File History:
|
||||
Created: 2019-11-05 [0.4.0]
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
class isoLanguage():
|
||||
|
||||
ISO_639_1 = {
|
||||
"aa" : "Afar",
|
||||
"ab" : "Abkhazian",
|
||||
"ae" : "Avestan",
|
||||
"af" : "Afrikaans",
|
||||
"ak" : "Akan",
|
||||
"am" : "Amharic",
|
||||
"an" : "Aragonese",
|
||||
"ar" : "Arabic",
|
||||
"as" : "Assamese",
|
||||
"av" : "Avaric",
|
||||
"ay" : "Aymara",
|
||||
"az" : "Azerbaijani",
|
||||
"ba" : "Bashkir",
|
||||
"be" : "Belarusian",
|
||||
"bg" : "Bulgarian",
|
||||
"bh" : "Bihari languages",
|
||||
"bi" : "Bislama",
|
||||
"bm" : "Bambara",
|
||||
"bn" : "Bengali",
|
||||
"bo" : "Tibetan",
|
||||
"br" : "Breton",
|
||||
"bs" : "Bosnian",
|
||||
"ca" : "Catalan",
|
||||
"ce" : "Chechen",
|
||||
"ch" : "Chamorro",
|
||||
"co" : "Corsican",
|
||||
"cr" : "Cree",
|
||||
"cs" : "Czech",
|
||||
"cu" : "Church Slavic",
|
||||
"cv" : "Chuvash",
|
||||
"cy" : "Welsh",
|
||||
"da" : "Danish",
|
||||
"de" : "German",
|
||||
"dv" : "Divehi",
|
||||
"dz" : "Dzongkha",
|
||||
"ee" : "Ewe",
|
||||
"el" : "Modern Greek",
|
||||
"en" : "English",
|
||||
"eo" : "Esperanto",
|
||||
"es" : "Spanish",
|
||||
"et" : "Estonian",
|
||||
"eu" : "Basque",
|
||||
"fa" : "Persian",
|
||||
"ff" : "Fulah",
|
||||
"fi" : "Finnish",
|
||||
"fj" : "Fijian",
|
||||
"fo" : "Faroese",
|
||||
"fr" : "French",
|
||||
"fy" : "Western Frisian",
|
||||
"ga" : "Irish",
|
||||
"gd" : "Gaelic",
|
||||
"gl" : "Galician",
|
||||
"gn" : "Guarani",
|
||||
"gu" : "Gujarati",
|
||||
"gv" : "Manx",
|
||||
"ha" : "Hausa",
|
||||
"he" : "Hebrew",
|
||||
"hi" : "Hindi",
|
||||
"ho" : "Hiri Motu",
|
||||
"hr" : "Croatian",
|
||||
"ht" : "Haitian",
|
||||
"hu" : "Hungarian",
|
||||
"hy" : "Armenian",
|
||||
"hz" : "Herero",
|
||||
"ia" : "Interlingua",
|
||||
"id" : "Indonesian",
|
||||
"ie" : "Interlingue",
|
||||
"ig" : "Igbo",
|
||||
"ii" : "Sichuan Yi",
|
||||
"ik" : "Inupiaq",
|
||||
"io" : "Ido",
|
||||
"is" : "Icelandic",
|
||||
"it" : "Italian",
|
||||
"iu" : "Inuktitut",
|
||||
"ja" : "Japanese",
|
||||
"jv" : "Javanese",
|
||||
"ka" : "Georgian",
|
||||
"kg" : "Kongo",
|
||||
"ki" : "Kikuyu",
|
||||
"kj" : "Kuanyama",
|
||||
"kk" : "Kazakh",
|
||||
"kl" : "Kalaallisut",
|
||||
"km" : "Central Khmer",
|
||||
"kn" : "Kannada",
|
||||
"ko" : "Korean",
|
||||
"kr" : "Kanuri",
|
||||
"ks" : "Kashmiri",
|
||||
"ku" : "Kurdish",
|
||||
"kv" : "Komi",
|
||||
"kw" : "Cornish",
|
||||
"ky" : "Kirghiz",
|
||||
"la" : "Latin",
|
||||
"lb" : "Luxembourgish",
|
||||
"lg" : "Ganda",
|
||||
"li" : "Limburgan",
|
||||
"ln" : "Lingala",
|
||||
"lo" : "Lao",
|
||||
"lt" : "Lithuanian",
|
||||
"lu" : "Luba-Katanga",
|
||||
"lv" : "Latvian",
|
||||
"mg" : "Malagasy",
|
||||
"mh" : "Marshallese",
|
||||
"mi" : "Maori",
|
||||
"mk" : "Macedonian",
|
||||
"ml" : "Malayalam",
|
||||
"mn" : "Mongolian",
|
||||
"mr" : "Marathi",
|
||||
"ms" : "Malay",
|
||||
"mt" : "Maltese",
|
||||
"my" : "Burmese",
|
||||
"na" : "Nauru",
|
||||
"nb" : "Norwegian Bokmål",
|
||||
"nd" : "North Ndebele",
|
||||
"ne" : "Nepali",
|
||||
"ng" : "Ndonga",
|
||||
"nl" : "Dutch",
|
||||
"nn" : "Norwegian Nynorsk",
|
||||
"no" : "Norwegian",
|
||||
"nr" : "South Ndebele",
|
||||
"nv" : "Navajo",
|
||||
"ny" : "Chichewa",
|
||||
"oc" : "Occitan",
|
||||
"oj" : "Ojibwa",
|
||||
"om" : "Oromo",
|
||||
"or" : "Oriya",
|
||||
"os" : "Ossetian",
|
||||
"pa" : "Panjabi",
|
||||
"pi" : "Pali",
|
||||
"pl" : "Polish",
|
||||
"ps" : "Pushto",
|
||||
"pt" : "Portuguese",
|
||||
"qu" : "Quechua",
|
||||
"rm" : "Romansh",
|
||||
"rn" : "Rundi",
|
||||
"ro" : "Romanian",
|
||||
"ru" : "Russian",
|
||||
"rw" : "Kinyarwanda",
|
||||
"sa" : "Sanskrit",
|
||||
"sc" : "Sardinian",
|
||||
"sd" : "Sindhi",
|
||||
"se" : "Northern Sami",
|
||||
"sg" : "Sango",
|
||||
"si" : "Sinhala",
|
||||
"sk" : "Slovak",
|
||||
"sl" : "Slovenian",
|
||||
"sm" : "Samoan",
|
||||
"sn" : "Shona",
|
||||
"so" : "Somali",
|
||||
"sq" : "Albanian",
|
||||
"sr" : "Serbian",
|
||||
"ss" : "Swati",
|
||||
"st" : "Southern Sotho",
|
||||
"su" : "Sundanese",
|
||||
"sv" : "Swedish",
|
||||
"sw" : "Swahili",
|
||||
"ta" : "Tamil",
|
||||
"te" : "Telugu",
|
||||
"tg" : "Tajik",
|
||||
"th" : "Thai",
|
||||
"ti" : "Tigrinya",
|
||||
"tk" : "Turkmen",
|
||||
"tl" : "Tagalog",
|
||||
"tn" : "Tswana",
|
||||
"to" : "Tonga",
|
||||
"tr" : "Turkish",
|
||||
"ts" : "Tsonga",
|
||||
"tt" : "Tatar",
|
||||
"tw" : "Twi",
|
||||
"ty" : "Tahitian",
|
||||
"ug" : "Uighur",
|
||||
"uk" : "Ukrainian",
|
||||
"ur" : "Urdu",
|
||||
"uz" : "Uzbek",
|
||||
"ve" : "Venda",
|
||||
"vi" : "Vietnamese",
|
||||
"vo" : "Volapük",
|
||||
"wa" : "Walloon",
|
||||
"wo" : "Wolof",
|
||||
"xh" : "Xhosa",
|
||||
"yi" : "Yiddish",
|
||||
"yo" : "Yoruba",
|
||||
"za" : "Zhuang",
|
||||
"zh" : "Chinese",
|
||||
"zu" : "Zulu",
|
||||
}
|
||||
|
||||
# END Class isoLanguage
|
||||
|
||||
class isoCountry():
|
||||
|
||||
ISO_3166_1_alpha_2 = {
|
||||
"AD" : "Andorra",
|
||||
"AE" : "United Arab Emirates",
|
||||
"AF" : "Afghanistan",
|
||||
"AG" : "Antigua and Barbuda",
|
||||
"AI" : "Anguilla",
|
||||
"AL" : "Albania",
|
||||
"AM" : "Armenia",
|
||||
"AO" : "Angola",
|
||||
"AQ" : "Antarctica",
|
||||
"AR" : "Argentina",
|
||||
"AS" : "American Samoa",
|
||||
"AT" : "Austria",
|
||||
"AU" : "Australia",
|
||||
"AW" : "Aruba",
|
||||
"AX" : "Åland Islands",
|
||||
"AZ" : "Azerbaijan",
|
||||
"BA" : "Bosnia and Herzegovina",
|
||||
"BB" : "Barbados",
|
||||
"BD" : "Bangladesh",
|
||||
"BE" : "Belgium",
|
||||
"BF" : "Burkina Faso",
|
||||
"BG" : "Bulgaria",
|
||||
"BH" : "Bahrain",
|
||||
"BI" : "Burundi",
|
||||
"BJ" : "Benin",
|
||||
"BL" : "Saint Barthélemy",
|
||||
"BM" : "Bermuda",
|
||||
"BN" : "Brunei Darussalam",
|
||||
"BO" : "Plurinational State of Bolivia",
|
||||
"BQ" : "Sint Eustatius and Saba Bonaire",
|
||||
"BR" : "Brazil",
|
||||
"BS" : "Bahamas",
|
||||
"BT" : "Bhutan",
|
||||
"BV" : "Bouvet Island",
|
||||
"BW" : "Botswana",
|
||||
"BY" : "Belarus",
|
||||
"BZ" : "Belize",
|
||||
"CA" : "Canada",
|
||||
"CC" : "Cocos (Keeling) Islands",
|
||||
"CD" : "The Democratic Republic of the Congo",
|
||||
"CF" : "Central African Republic",
|
||||
"CG" : "Congo",
|
||||
"CH" : "Switzerland",
|
||||
"CI" : "Côte d'Ivoire",
|
||||
"CK" : "Cook Islands",
|
||||
"CL" : "Chile",
|
||||
"CM" : "Cameroon",
|
||||
"CN" : "China",
|
||||
"CO" : "Colombia",
|
||||
"CR" : "Costa Rica",
|
||||
"CU" : "Cuba",
|
||||
"CV" : "Cape Verde",
|
||||
"CW" : "Curaçao",
|
||||
"CX" : "Christmas Island",
|
||||
"CY" : "Cyprus",
|
||||
"CZ" : "Czech Republic",
|
||||
"DE" : "Germany",
|
||||
"DJ" : "Djibouti",
|
||||
"DK" : "Denmark",
|
||||
"DM" : "Dominica",
|
||||
"DO" : "Dominican Republic",
|
||||
"DZ" : "Algeria",
|
||||
"EC" : "Ecuador",
|
||||
"EE" : "Estonia",
|
||||
"EG" : "Egypt",
|
||||
"EH" : "Western Sahara",
|
||||
"ER" : "Eritrea",
|
||||
"ES" : "Spain",
|
||||
"ET" : "Ethiopia",
|
||||
"FI" : "Finland",
|
||||
"FJ" : "Fiji",
|
||||
"FK" : "Falkland Islands (Malvinas)",
|
||||
"FM" : "Federated States of Micronesia",
|
||||
"FO" : "Faroe Islands",
|
||||
"FR" : "France",
|
||||
"GA" : "Gabon",
|
||||
"GB" : "United Kingdom",
|
||||
"GD" : "Grenada",
|
||||
"GE" : "Georgia",
|
||||
"GF" : "French Guiana",
|
||||
"GG" : "Guernsey",
|
||||
"GH" : "Ghana",
|
||||
"GI" : "Gibraltar",
|
||||
"GL" : "Greenland",
|
||||
"GM" : "Gambia",
|
||||
"GN" : "Guinea",
|
||||
"GP" : "Guadeloupe",
|
||||
"GQ" : "Equatorial Guinea",
|
||||
"GR" : "Greece",
|
||||
"GS" : "South Georgia and the South Sandwich Islands",
|
||||
"GT" : "Guatemala",
|
||||
"GU" : "Guam",
|
||||
"GW" : "Guinea-Bissau",
|
||||
"GY" : "Guyana",
|
||||
"HK" : "Hong Kong",
|
||||
"HM" : "Heard Island and McDonald Islands",
|
||||
"HN" : "Honduras",
|
||||
"HR" : "Croatia",
|
||||
"HT" : "Haiti",
|
||||
"HU" : "Hungary",
|
||||
"ID" : "Indonesia",
|
||||
"IE" : "Ireland",
|
||||
"IL" : "Israel",
|
||||
"IM" : "Isle of Man",
|
||||
"IN" : "India",
|
||||
"IO" : "British Indian Ocean Territory",
|
||||
"IQ" : "Iraq",
|
||||
"IR" : "Islamic Republic of Iran",
|
||||
"IS" : "Iceland",
|
||||
"IT" : "Italy",
|
||||
"JE" : "Jersey",
|
||||
"JM" : "Jamaica",
|
||||
"JO" : "Jordan",
|
||||
"JP" : "Japan",
|
||||
"KE" : "Kenya",
|
||||
"KG" : "Kyrgyzstan",
|
||||
"KH" : "Cambodia",
|
||||
"KI" : "Kiribati",
|
||||
"KM" : "Comoros",
|
||||
"KN" : "Saint Kitts and Nevis",
|
||||
"KP" : "Democratic People's Republic of Korea",
|
||||
"KR" : "Republic of Korea",
|
||||
"KW" : "Kuwait",
|
||||
"KY" : "Cayman Islands",
|
||||
"KZ" : "Kazakhstan",
|
||||
"LA" : "Lao People's Democratic Republic",
|
||||
"LB" : "Lebanon",
|
||||
"LC" : "Saint Lucia",
|
||||
"LI" : "Liechtenstein",
|
||||
"LK" : "Sri Lanka",
|
||||
"LR" : "Liberia",
|
||||
"LS" : "Lesotho",
|
||||
"LT" : "Lithuania",
|
||||
"LU" : "Luxembourg",
|
||||
"LV" : "Latvia",
|
||||
"LY" : "Libya",
|
||||
"MA" : "Morocco",
|
||||
"MC" : "Monaco",
|
||||
"MD" : "Republic of Moldova",
|
||||
"ME" : "Montenegro",
|
||||
"MF" : "Saint Martin (French part)",
|
||||
"MG" : "Madagascar",
|
||||
"MH" : "Marshall Islands",
|
||||
"MK" : "The Former Yugoslav Republic of Macedonia",
|
||||
"ML" : "Mali",
|
||||
"MM" : "Myanmar",
|
||||
"MN" : "Mongolia",
|
||||
"MO" : "Macao",
|
||||
"MP" : "Northern Mariana Islands",
|
||||
"MQ" : "Martinique",
|
||||
"MR" : "Mauritania",
|
||||
"MS" : "Montserrat",
|
||||
"MT" : "Malta",
|
||||
"MU" : "Mauritius",
|
||||
"MV" : "Maldives",
|
||||
"MW" : "Malawi",
|
||||
"MX" : "Mexico",
|
||||
"MY" : "Malaysia",
|
||||
"MZ" : "Mozambique",
|
||||
"NA" : "Namibia",
|
||||
"NC" : "New Caledonia",
|
||||
"NE" : "Niger",
|
||||
"NF" : "Norfolk Island",
|
||||
"NG" : "Nigeria",
|
||||
"NI" : "Nicaragua",
|
||||
"NL" : "Netherlands",
|
||||
"NO" : "Norway",
|
||||
"NP" : "Nepal",
|
||||
"NR" : "Nauru",
|
||||
"NU" : "Niue",
|
||||
"NZ" : "New Zealand",
|
||||
"OM" : "Oman",
|
||||
"PA" : "Panama",
|
||||
"PE" : "Peru",
|
||||
"PF" : "French Polynesia",
|
||||
"PG" : "Papua New Guinea",
|
||||
"PH" : "Philippines",
|
||||
"PK" : "Pakistan",
|
||||
"PL" : "Poland",
|
||||
"PM" : "Saint Pierre and Miquelon",
|
||||
"PN" : "Pitcairn",
|
||||
"PR" : "Puerto Rico",
|
||||
"PS" : "State of Palestine",
|
||||
"PT" : "Portugal",
|
||||
"PW" : "Palau",
|
||||
"PY" : "Paraguay",
|
||||
"QA" : "Qatar",
|
||||
"RE" : "Réunion",
|
||||
"RO" : "Romania",
|
||||
"RS" : "Serbia",
|
||||
"RU" : "Russian Federation",
|
||||
"RW" : "Rwanda",
|
||||
"SA" : "Saudi Arabia",
|
||||
"SB" : "Solomon Islands",
|
||||
"SC" : "Seychelles",
|
||||
"SD" : "Sudan",
|
||||
"SE" : "Sweden",
|
||||
"SG" : "Singapore",
|
||||
"SH" : "Saint Helena, Ascension and Tristan da Cunha",
|
||||
"SI" : "Slovenia",
|
||||
"SJ" : "Svalbard and Jan Mayen",
|
||||
"SK" : "Slovakia",
|
||||
"SL" : "Sierra Leone",
|
||||
"SM" : "San Marino",
|
||||
"SN" : "Senegal",
|
||||
"SO" : "Somalia",
|
||||
"SR" : "Suriname",
|
||||
"SS" : "South Sudan",
|
||||
"ST" : "Sao Tome and Principe",
|
||||
"SV" : "El Salvador",
|
||||
"SX" : "Sint Maarten",
|
||||
"SY" : "Syrian Arab Republic",
|
||||
"SZ" : "Swaziland",
|
||||
"TC" : "Turks and Caicos Islands",
|
||||
"TD" : "Chad",
|
||||
"TF" : "French Southern Territories",
|
||||
"TG" : "Togo",
|
||||
"TH" : "Thailand",
|
||||
"TJ" : "Tajikistan",
|
||||
"TK" : "Tokelau",
|
||||
"TL" : "Timor-Leste",
|
||||
"TM" : "Turkmenistan",
|
||||
"TN" : "Tunisia",
|
||||
"TO" : "Tonga",
|
||||
"TR" : "Turkey",
|
||||
"TT" : "Trinidad and Tobago",
|
||||
"TV" : "Tuvalu",
|
||||
"TW" : "Taiwan, Province of China",
|
||||
"TZ" : "United Republic of Tanzania",
|
||||
"UA" : "Ukraine",
|
||||
"UG" : "Uganda",
|
||||
"UM" : "United States Minor Outlying Islands",
|
||||
"US" : "United States",
|
||||
"UY" : "Uruguay",
|
||||
"UZ" : "Uzbekistan",
|
||||
"VA" : "Holy See (Vatican City State)",
|
||||
"VC" : "Saint Vincent and the Grenadines",
|
||||
"VE" : "Bolivarian Republic of Venezuela",
|
||||
"VG" : "British Virgin Islands",
|
||||
"VI" : "U.S. Virgin Islands",
|
||||
"VN" : "Viet Nam",
|
||||
"VU" : "Vanuatu",
|
||||
"WF" : "Wallis and Futuna",
|
||||
"WS" : "Samoa",
|
||||
"YE" : "Yemen",
|
||||
"YT" : "Mayotte",
|
||||
"ZA" : "South Africa",
|
||||
"ZM" : "Zambia",
|
||||
"ZW" : "Zimbabwe",
|
||||
}
|
||||
|
||||
# END Class isoCountry
|
||||
@@ -1,16 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Core Init
|
||||
=======================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nw.core.document import NWDoc
|
||||
from nw.core.index import NWIndex
|
||||
from nw.core.index import NWIndex, countWords
|
||||
from nw.core.project import NWProject
|
||||
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
|
||||
from nw.core.tohtml import ToHtml
|
||||
from nw.core.tools import countWords, numberToRoman, numberToWord
|
||||
from nw.core.toodt import ToOdt
|
||||
from nw.core.tomd import ToMarkdown
|
||||
|
||||
__all__ = [
|
||||
"countWords",
|
||||
"numberToRoman",
|
||||
"numberToWord",
|
||||
"NWDoc",
|
||||
"NWIndex",
|
||||
"NWProject",
|
||||
@@ -18,4 +36,6 @@ __all__ = [
|
||||
"NWSpellEnchant",
|
||||
"NWSpellSimple",
|
||||
"ToHtml",
|
||||
"ToOdt",
|
||||
"ToMarkdown",
|
||||
]
|
||||
|
||||
@@ -1,54 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Project Document
|
||||
"""
|
||||
novelWriter – Project Document
|
||||
==============================
|
||||
Data class for a single novelWriter document
|
||||
|
||||
novelWriter – Project Document
|
||||
================================
|
||||
Class holding a single novelWriter document
|
||||
File History:
|
||||
Created: 2018-09-29 [0.0.1]
|
||||
|
||||
File History:
|
||||
Created: 2018-09-29 [0.0.1]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from nw.constants import nwAlert
|
||||
from nw.enum import nwItemLayout, nwItemClass
|
||||
from nw.common import isHandle
|
||||
from nw.constants import nwItemLayout, nwItemClass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWDoc():
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
def __init__(self, theProject, theHandle):
|
||||
|
||||
self.theProject = theProject
|
||||
self.theParent = theParent
|
||||
|
||||
# Internal Variables
|
||||
self._theItem = None # The currently open item
|
||||
self._docHandle = None # The handle of the currently open item
|
||||
self._fileLoc = None # The file location of the currently open item
|
||||
self._docMeta = {} # The meta data of the currently open item
|
||||
self._theItem = None # The currently open item
|
||||
self._docHandle = None # The handle of the currently open item
|
||||
self._fileLoc = None # The file location of the currently open item
|
||||
self._docMeta = {} # The meta data of the currently open item
|
||||
self._docError = "" # The latest encountered IO error
|
||||
|
||||
# Internal Mapping
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
if isHandle(theHandle):
|
||||
self._docHandle = theHandle
|
||||
|
||||
if self._docHandle is not None:
|
||||
self._theItem = self.theProject.projTree[theHandle]
|
||||
|
||||
return
|
||||
|
||||
@@ -56,39 +57,23 @@ class NWDoc():
|
||||
# Class Methods
|
||||
##
|
||||
|
||||
def clearDocument(self):
|
||||
"""Clear the document contents.
|
||||
"""
|
||||
self._theItem = None
|
||||
self._docHandle = None
|
||||
self._fileLoc = None
|
||||
self._docMeta = {}
|
||||
return
|
||||
|
||||
def openDocument(self, tHandle, showStatus=True, isOrphan=False):
|
||||
"""Open a document from handle, capturing potential file system
|
||||
errors and parse meta data. If the document doesn't exist on
|
||||
disk, return an empty string. If something went wrong, return
|
||||
def readDocument(self, isOrphan=False):
|
||||
"""Read a document from set handle, capturing potential file
|
||||
system errors and parse meta data. If the document doesn't exist
|
||||
on disk, return an empty string. If something went wrong, return
|
||||
None.
|
||||
"""
|
||||
if not isHandle(tHandle):
|
||||
self._docError = ""
|
||||
if self._docHandle is None:
|
||||
logger.error("No document handle set")
|
||||
return None
|
||||
|
||||
# Always clear first, since the object will often be reused.
|
||||
self.clearDocument()
|
||||
|
||||
self._docHandle = tHandle
|
||||
if not isOrphan:
|
||||
self._theItem = self.theProject.projTree[tHandle]
|
||||
else:
|
||||
self._theItem = None
|
||||
|
||||
if self._theItem is None and not isOrphan:
|
||||
self.clearDocument()
|
||||
logger.error("Unknown novelWriter document")
|
||||
return None
|
||||
|
||||
docFile = self._docHandle+".nwd"
|
||||
logger.debug("Opening document %s" % docFile)
|
||||
logger.debug("Opening document: %s", docFile)
|
||||
|
||||
docPath = os.path.join(self.theProject.projContent, docFile)
|
||||
self._fileLoc = docPath
|
||||
@@ -97,7 +82,7 @@ class NWDoc():
|
||||
self._docMeta = {}
|
||||
if os.path.isfile(docPath):
|
||||
try:
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(docPath, mode="r", encoding="utf-8") as inFile:
|
||||
|
||||
# Check the first <= 10 lines for metadata
|
||||
for i in range(10):
|
||||
@@ -112,34 +97,30 @@ class NWDoc():
|
||||
theText += inFile.read()
|
||||
|
||||
except Exception as e:
|
||||
self.makeAlert(["Failed to open document file.", str(e)], nwAlert.ERROR)
|
||||
# Note: Document must be cleared in case of an io error,
|
||||
# or else the auto-save or save will try to overwrite it
|
||||
# with an empty file. Return None to alert the caller.
|
||||
self.clearDocument()
|
||||
self._docError = str(e)
|
||||
return None
|
||||
|
||||
else:
|
||||
# The document file does not exist, so we assume it's a new
|
||||
# document and initialise an empty text string.
|
||||
logger.debug("The requested document does not exist.")
|
||||
return ""
|
||||
|
||||
if showStatus and not isOrphan:
|
||||
self.theParent.setStatus("Opened Document: %s" % self._theItem.itemName)
|
||||
|
||||
return theText
|
||||
|
||||
def saveDocument(self, docText):
|
||||
"""Save the document. The file is saved via a temp file in case
|
||||
def writeDocument(self, docText):
|
||||
"""Write the document. The file is saved via a temp file in case
|
||||
of save failure. Returns True if successful, False if not.
|
||||
"""
|
||||
self._docError = ""
|
||||
if self._docHandle is None:
|
||||
logger.error("No document handle set")
|
||||
return False
|
||||
|
||||
self.theProject.ensureFolderStructure()
|
||||
|
||||
docFile = self._docHandle+".nwd"
|
||||
logger.debug("Saving document %s" % docFile)
|
||||
logger.debug("Saving document: %s", docFile)
|
||||
|
||||
docPath = os.path.join(self.theProject.projContent, docFile)
|
||||
docTemp = os.path.join(self.theProject.projContent, docFile+"~")
|
||||
@@ -155,11 +136,11 @@ class NWDoc():
|
||||
)
|
||||
|
||||
try:
|
||||
with open(docTemp, mode="w", encoding="utf8") as outFile:
|
||||
with open(docTemp, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write(docMeta)
|
||||
outFile.write(docText)
|
||||
except Exception as e:
|
||||
self.makeAlert(["Could not save document.", str(e)], nwAlert.ERROR)
|
||||
self._docError = str(e)
|
||||
return False
|
||||
|
||||
# If we're here, the file was successfully saved, so we can
|
||||
@@ -168,19 +149,18 @@ class NWDoc():
|
||||
os.unlink(docPath)
|
||||
os.rename(docTemp, docPath)
|
||||
|
||||
if self._theItem is not None:
|
||||
self.theParent.setStatus("Saved Document: %s" % self._theItem.itemName)
|
||||
|
||||
return True
|
||||
|
||||
def deleteDocument(self, tHandle):
|
||||
def deleteDocument(self):
|
||||
"""Permanently delete a document source file and related files
|
||||
from the project data folder.
|
||||
"""
|
||||
if not isHandle(tHandle):
|
||||
self._docError = ""
|
||||
if self._docHandle is None:
|
||||
logger.error("No document handle set")
|
||||
return False
|
||||
|
||||
docFile = tHandle+".nwd"
|
||||
docFile = self._docHandle+".nwd"
|
||||
|
||||
chkList = []
|
||||
chkList.append(os.path.join(self.theProject.projContent, docFile))
|
||||
@@ -190,9 +170,9 @@ class NWDoc():
|
||||
if os.path.isfile(chkFile):
|
||||
try:
|
||||
os.unlink(chkFile)
|
||||
logger.debug("Deleted: %s" % chkFile)
|
||||
logger.debug("Deleted: %s", chkFile)
|
||||
except Exception as e:
|
||||
self.makeAlert(["Could not delete document file.", str(e)], nwAlert.ERROR)
|
||||
self._docError = str(e)
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -222,6 +202,11 @@ class NWDoc():
|
||||
|
||||
return theName, theParent, theClass, theLayout
|
||||
|
||||
def getError(self):
|
||||
"""Return the last recorded exception.
|
||||
"""
|
||||
return self._docError
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -252,7 +237,7 @@ class NWDoc():
|
||||
self._docMeta["layout"] = nwItemLayout[metaBits[1]]
|
||||
|
||||
else:
|
||||
logger.debug("Ignoring meta data: '%s'" % metaLine.strip())
|
||||
logger.debug("Ignoring meta data: '%s'", metaLine.strip())
|
||||
|
||||
return
|
||||
|
||||
|
||||
809
nw/core/index.py
@@ -1,39 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Project Item Class
|
||||
"""
|
||||
novelWriter – Project Item Class
|
||||
================================
|
||||
Data class for a project tree item
|
||||
|
||||
novelWriter – Project Item Class
|
||||
==================================
|
||||
Class holding the data of a project tree item
|
||||
File History:
|
||||
Created: 2018-10-27 [0.0.1]
|
||||
|
||||
File History:
|
||||
Created: 2018-10-27 [0.0.1]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from nw.common import checkInt, isHandle
|
||||
from nw.constants import nwItemType, nwItemClass, nwItemLayout
|
||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from nw.common import checkInt, isHandle, isItemClass, isItemLayout, isItemType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWItem():
|
||||
|
||||
def __init__(self, theProject):
|
||||
@@ -52,11 +51,11 @@ class NWItem():
|
||||
self.isExported = True
|
||||
|
||||
# Document Meta Data
|
||||
self.charCount = 0 # Current character count
|
||||
self.wordCount = 0 # Current word count
|
||||
self.paraCount = 0 # Current paragraph count
|
||||
self.initCount = 0 # Initial word count
|
||||
self.cursorPos = 0 # Last cursor position
|
||||
self.charCount = 0 # Current character count
|
||||
self.wordCount = 0 # Current word count
|
||||
self.paraCount = 0 # Current paragraph count
|
||||
self.initCount = 0 # Initial word count
|
||||
self.cursorPos = 0 # Last cursor position
|
||||
|
||||
return
|
||||
|
||||
@@ -68,9 +67,9 @@ class NWItem():
|
||||
"""Packs all the data in the class instance into an XML object.
|
||||
"""
|
||||
xPack = etree.SubElement(xParent, "item", attrib={
|
||||
"handle" : str(self.itemHandle),
|
||||
"order" : str(self.itemOrder),
|
||||
"parent" : str(self.itemParent),
|
||||
"handle": str(self.itemHandle),
|
||||
"order": str(self.itemOrder),
|
||||
"parent": str(self.itemParent),
|
||||
})
|
||||
self._subPack(xPack, "name", text=str(self.itemName))
|
||||
self._subPack(xPack, "type", text=str(self.itemType.name))
|
||||
@@ -107,7 +106,6 @@ class NWItem():
|
||||
if "order" in xItem.attrib:
|
||||
self.setOrder(xItem.attrib["order"])
|
||||
|
||||
retStatus = True
|
||||
for xValue in xItem:
|
||||
if xValue.tag == "name":
|
||||
self.setName(xValue.text)
|
||||
@@ -132,10 +130,12 @@ class NWItem():
|
||||
elif xValue.tag == "cursorPos":
|
||||
self.setCursorPos(xValue.text)
|
||||
else:
|
||||
logger.error("Unknown tag '%s'" % xValue.tag)
|
||||
retStatus = False
|
||||
# Sliently skip as we may otherwise cause orphaned
|
||||
# items if an otherwise valid file is opened by a
|
||||
# version of novelWriter that doesn't know the tag.
|
||||
logger.error("Unknown tag '%s'", xValue.tag)
|
||||
|
||||
return retStatus
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _subPack(xParent, name, attrib=None, text=None, none=True):
|
||||
@@ -202,10 +202,10 @@ class NWItem():
|
||||
"""
|
||||
if isinstance(theType, nwItemType):
|
||||
self.itemType = theType
|
||||
elif theType in nwItemType.__members__:
|
||||
elif isItemType(theType):
|
||||
self.itemType = nwItemType[theType]
|
||||
else:
|
||||
logger.error("Unrecognised item type '%s'" % theType)
|
||||
logger.error("Unrecognised item type '%s'", theType)
|
||||
self.itemType = nwItemType.NO_TYPE
|
||||
return
|
||||
|
||||
@@ -215,10 +215,10 @@ class NWItem():
|
||||
"""
|
||||
if isinstance(theClass, nwItemClass):
|
||||
self.itemClass = theClass
|
||||
elif theClass in nwItemClass.__members__:
|
||||
elif isItemClass(theClass):
|
||||
self.itemClass = nwItemClass[theClass]
|
||||
else:
|
||||
logger.error("Unrecognised item class '%s'" % theClass)
|
||||
logger.error("Unrecognised item class '%s'", theClass)
|
||||
self.itemClass = nwItemClass.NO_CLASS
|
||||
return
|
||||
|
||||
@@ -228,10 +228,10 @@ class NWItem():
|
||||
"""
|
||||
if isinstance(theLayout, nwItemLayout):
|
||||
self.itemLayout = theLayout
|
||||
elif theLayout in nwItemLayout.__members__:
|
||||
elif isItemLayout(theLayout):
|
||||
self.itemLayout = nwItemLayout[theLayout]
|
||||
else:
|
||||
logger.error("Unrecognised item layout '%s'" % theLayout)
|
||||
logger.error("Unrecognised item layout '%s'", theLayout)
|
||||
self.itemLayout = nwItemLayout.NO_LAYOUT
|
||||
return
|
||||
|
||||
@@ -251,7 +251,7 @@ class NWItem():
|
||||
if isinstance(expState, str):
|
||||
self.isExpanded = (expState == str(True))
|
||||
else:
|
||||
self.isExpanded = (expState == True) # noqa: E712
|
||||
self.isExpanded = (expState == True) # noqa: E712
|
||||
return
|
||||
|
||||
def setExported(self, expState):
|
||||
@@ -260,7 +260,7 @@ class NWItem():
|
||||
if isinstance(expState, str):
|
||||
self.isExported = (expState == str(True))
|
||||
else:
|
||||
self.isExported = (expState == True) # noqa: E712
|
||||
self.isExported = (expState == True) # noqa: E712
|
||||
return
|
||||
|
||||
##
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Project Options Cache
|
||||
"""
|
||||
novelWriter – Project Options Cache
|
||||
===================================
|
||||
Data class for user-defined GUI project options
|
||||
|
||||
novelWriter – Project Options Cache
|
||||
=====================================
|
||||
Class wrapping the project options state
|
||||
File History:
|
||||
Created: 2019-10-21 [0.3.1]
|
||||
Rewritten: 2020-02-19 [0.4.5]
|
||||
|
||||
File History:
|
||||
Created: 2019-10-21 [0.3.1]
|
||||
Rewritten: 2020-02-19 [0.4.5]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
@@ -34,6 +33,7 @@ from nw.constants import nwFiles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OptionState():
|
||||
|
||||
def __init__(self, theProject):
|
||||
@@ -47,6 +47,7 @@ class OptionState():
|
||||
"widthCol0",
|
||||
"widthCol1",
|
||||
"widthCol2",
|
||||
"widthCol3",
|
||||
"sortCol",
|
||||
"sortOrder",
|
||||
"incNovel",
|
||||
@@ -54,6 +55,7 @@ class OptionState():
|
||||
"hideZeros",
|
||||
"hideNegative",
|
||||
"groupByDay",
|
||||
"showIdleTime",
|
||||
"histMax",
|
||||
},
|
||||
"GuiDocSplit": {
|
||||
@@ -71,12 +73,14 @@ class OptionState():
|
||||
"excludeBody",
|
||||
"textFont",
|
||||
"textSize",
|
||||
"lineHeight",
|
||||
"noStyling",
|
||||
"incSynopsis",
|
||||
"incComments",
|
||||
"incKeywords",
|
||||
"incBodyText",
|
||||
"replaceTabs",
|
||||
"replaceUCode",
|
||||
},
|
||||
"GuiOutline": {
|
||||
"headerOrder",
|
||||
@@ -87,6 +91,24 @@ class OptionState():
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"replaceColW",
|
||||
"statusColW",
|
||||
"importColW",
|
||||
},
|
||||
"GuiProjectDetails": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"widthCol0",
|
||||
"widthCol1",
|
||||
"widthCol2",
|
||||
"widthCol3",
|
||||
"widthCol4",
|
||||
"wordsPerPage",
|
||||
"countFrom",
|
||||
"clearDouble",
|
||||
},
|
||||
"GuiWordList": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,16 +125,16 @@ class OptionState():
|
||||
return False
|
||||
|
||||
stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
|
||||
theState = {}
|
||||
theState = {}
|
||||
|
||||
if os.path.isfile(stateFile):
|
||||
logger.debug("Loading GUI options file")
|
||||
try:
|
||||
with open(stateFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(stateFile, mode="r", encoding="utf-8") as inFile:
|
||||
theState = json.load(inFile)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
logger.error("Failed to load GUI options file")
|
||||
logger.error(str(e))
|
||||
nw.logException()
|
||||
return False
|
||||
|
||||
# Filter out unused variables
|
||||
@@ -135,11 +157,11 @@ class OptionState():
|
||||
logger.debug("Saving GUI options file")
|
||||
|
||||
try:
|
||||
with open(stateFile, mode="w+", encoding="utf8") as outFile:
|
||||
with open(stateFile, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self.theState, outFile, indent=2)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
logger.error("Failed to save GUI options file")
|
||||
logger.error(str(e))
|
||||
nw.logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -152,11 +174,11 @@ class OptionState():
|
||||
"""Saves a value, with a given group and name.
|
||||
"""
|
||||
if setGroup not in self.validMap:
|
||||
logger.error("Unknown option group '%s'" % setGroup)
|
||||
logger.error("Unknown option group '%s'", setGroup)
|
||||
return False
|
||||
|
||||
if setName not in self.validMap[setGroup]:
|
||||
logger.error("Unknown option name '%s'" % setName)
|
||||
logger.error("Unknown option name '%s'", setName)
|
||||
return False
|
||||
|
||||
if setGroup not in self.theState:
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Spell Check Classes
|
||||
"""
|
||||
novelWriter – Spell Check Classes
|
||||
=================================
|
||||
Wrapper classes for spell checking tools
|
||||
|
||||
novelWriter – Spell Check Classes
|
||||
===================================
|
||||
Wrapper class for spell checking tools
|
||||
File History:
|
||||
Created: 2019-06-11 [0.1.5]
|
||||
|
||||
File History:
|
||||
Created: 2019-06-11 [0.1.5]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
@@ -30,10 +28,9 @@ import logging
|
||||
import os
|
||||
import difflib
|
||||
|
||||
from nw.constants import nwConst, isoLanguage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# SpellChecking SuperClass
|
||||
# =============================================================================================== #
|
||||
@@ -50,17 +47,17 @@ class NWSpellCheck():
|
||||
return
|
||||
|
||||
def setLanguage(self, theLang, projectDict=None):
|
||||
"""Dummy function.
|
||||
"""Default function.
|
||||
"""
|
||||
return
|
||||
|
||||
def checkWord(self, theWord):
|
||||
"""Dummy function.
|
||||
"""Default function.
|
||||
"""
|
||||
return True
|
||||
|
||||
def suggestWords(self, theWord):
|
||||
"""Dummy function.
|
||||
"""Default function.
|
||||
"""
|
||||
return []
|
||||
|
||||
@@ -73,33 +70,23 @@ class NWSpellCheck():
|
||||
with open(self.projectDict, mode="a+", encoding="utf-8") as outFile:
|
||||
outFile.write("%s\n" % newWord)
|
||||
self.projDict.append(newWord)
|
||||
except Exception as e:
|
||||
logger.error("Failed to add word to project word list %s" % str(self.projectDict))
|
||||
logger.error(str(e))
|
||||
except Exception:
|
||||
logger.error("Failed to add word to project word list %s", str(self.projectDict))
|
||||
nw.logException()
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def listDictionaries(self):
|
||||
"""Dummy function.
|
||||
"""Default function.
|
||||
"""
|
||||
return []
|
||||
|
||||
def describeDict(self):
|
||||
"""Dummy function.
|
||||
"""Default function.
|
||||
"""
|
||||
return "", ""
|
||||
|
||||
@staticmethod
|
||||
def expandLanguage(spTag):
|
||||
"""Translate a language tag to something more user friendly.
|
||||
"""
|
||||
spBits = spTag.split("_")
|
||||
spLang = isoLanguage.ISO_639_1.get(spBits[0], spBits[0])
|
||||
if len(spBits) > 1:
|
||||
spLang += " (%s)" % spBits[1]
|
||||
return spLang
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -109,30 +96,33 @@ class NWSpellCheck():
|
||||
lookup lists.
|
||||
"""
|
||||
self.projDict = []
|
||||
if projectDict is not None:
|
||||
if not os.path.isfile(projectDict):
|
||||
self.projectDict = None
|
||||
return False
|
||||
else:
|
||||
self.projectDict = projectDict
|
||||
self.projectDict = projectDict
|
||||
|
||||
try:
|
||||
logger.debug("Loading project word list")
|
||||
with open(projectDict, mode="r", encoding="utf-8") as wordsFile:
|
||||
for theLine in wordsFile:
|
||||
theLine = theLine.strip()
|
||||
if len(theLine) > 0 and theLine not in self.projDict:
|
||||
self.projDict.append(theLine)
|
||||
logger.debug("Project word list contains %d words" % len(self.projDict))
|
||||
except Exception as e:
|
||||
logger.error("Failed to load project word list")
|
||||
logger.error(str(e))
|
||||
return False
|
||||
if projectDict is None:
|
||||
return False
|
||||
|
||||
if not os.path.isfile(projectDict):
|
||||
return False
|
||||
|
||||
try:
|
||||
logger.debug("Loading project word list")
|
||||
with open(projectDict, mode="r", encoding="utf-8") as wordsFile:
|
||||
for theLine in wordsFile:
|
||||
theLine = theLine.strip()
|
||||
if len(theLine) > 0 and theLine not in self.projDict:
|
||||
self.projDict.append(theLine)
|
||||
logger.debug("Project word list contains %d words", len(self.projDict))
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load project word list")
|
||||
nw.logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# END Class NWSpellCheck
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Enchant Based SpellChecking
|
||||
# =============================================================================================== #
|
||||
@@ -142,21 +132,28 @@ class NWSpellEnchant(NWSpellCheck):
|
||||
def __init__(self):
|
||||
NWSpellCheck.__init__(self)
|
||||
logger.debug("Enchant spell checking activated")
|
||||
self.theBroker = None
|
||||
return
|
||||
|
||||
def setLanguage(self, theLang, projectDict=None):
|
||||
"""Load a dictionary for the language specified in the config.
|
||||
If that fails, we load a dummy dictionary so that lookups don't
|
||||
If that fails, we load a mock dictionary so that lookups don't
|
||||
crash.
|
||||
"""
|
||||
try:
|
||||
import enchant
|
||||
self.theDict = enchant.Dict(theLang)
|
||||
if self.theBroker is not None:
|
||||
logger.verbose("Deleting old pyenchant broker")
|
||||
del self.theBroker
|
||||
|
||||
self.theBroker = enchant.Broker()
|
||||
self.theDict = self.theBroker.request_dict(theLang)
|
||||
self.spellLanguage = theLang
|
||||
logger.debug("Enchant spell checking for language %s loaded" % theLang)
|
||||
logger.debug("Enchant spell checking for language '%s' loaded", theLang)
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load enchant spell checking for language %s" % theLang)
|
||||
self.theDict = NWSpellEnchantDummy()
|
||||
logger.error("Failed to load enchant spell checking for language '%s'", theLang)
|
||||
self.theDict = FakeEnchant()
|
||||
self.spellLanguage = None
|
||||
|
||||
self._readProjectDictionary(projectDict)
|
||||
@@ -189,10 +186,10 @@ class NWSpellEnchant(NWSpellCheck):
|
||||
try:
|
||||
import enchant
|
||||
for spTag, spProvider in enchant.list_dicts():
|
||||
spName = "%s [%s]" % (self.expandLanguage(spTag), spProvider.name)
|
||||
retList.append((spTag, spName))
|
||||
retList.append((spTag, spProvider.name))
|
||||
except Exception:
|
||||
logger.error("Failed to list languages for enchant spell checking")
|
||||
|
||||
return retList
|
||||
|
||||
def describeDict(self):
|
||||
@@ -202,9 +199,9 @@ class NWSpellEnchant(NWSpellCheck):
|
||||
try:
|
||||
spTag = self.theDict.tag
|
||||
spName = self.theDict.provider.name
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
logger.error("Failed to extract information about the dictionary")
|
||||
logger.error(str(e))
|
||||
nw.logException()
|
||||
spTag = ""
|
||||
spName = ""
|
||||
|
||||
@@ -212,7 +209,8 @@ class NWSpellEnchant(NWSpellCheck):
|
||||
|
||||
# END Class NWSpellEnchant
|
||||
|
||||
class NWSpellEnchantDummy:
|
||||
|
||||
class FakeEnchant:
|
||||
"""Fallback for when Enchant is selected, but not installed.
|
||||
"""
|
||||
def __init__(self):
|
||||
@@ -227,7 +225,8 @@ class NWSpellEnchantDummy:
|
||||
def add_to_session(self, theWord):
|
||||
return
|
||||
|
||||
# END Class NWSpellEnchantDummy
|
||||
# END Class FakeEnchant
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Fallback SpellChecking Using difflib
|
||||
@@ -236,10 +235,9 @@ class NWSpellEnchantDummy:
|
||||
class NWSpellSimple(NWSpellCheck):
|
||||
"""Internal spell check tool that uses standard Python packages with
|
||||
no other external dependencies. This is the fallback spell checker
|
||||
when no other is available. This method is fairly slow compared to
|
||||
other implementations.
|
||||
when no other is available. This method is slower than enchant.
|
||||
"""
|
||||
WORDS = []
|
||||
theWords = set()
|
||||
|
||||
def __init__(self):
|
||||
NWSpellCheck.__init__(self)
|
||||
@@ -251,26 +249,27 @@ class NWSpellSimple(NWSpellCheck):
|
||||
"""Load a dictionary as a list from the app assets folder.
|
||||
"""
|
||||
self.theLang = theLang
|
||||
self.WORDS = []
|
||||
self.theWords = set()
|
||||
dictFile = os.path.join(self.mainConf.dictPath, theLang+".dict")
|
||||
try:
|
||||
with open(dictFile, mode="r", encoding="utf-8") as wordsFile:
|
||||
for theLine in wordsFile:
|
||||
if len(theLine) == 0 or theLine.startswith("#"):
|
||||
continue
|
||||
self.WORDS.append(theLine.strip().lower())
|
||||
logger.debug("Spell check word list for language %s loaded" % theLang)
|
||||
logger.debug("Word list contains %d words" % len(self.WORDS))
|
||||
self.theWords.add(theLine.strip().lower())
|
||||
|
||||
logger.debug("Spell check dictionary for language '%s' loaded", theLang)
|
||||
logger.debug("Dictionary contains %d words", len(self.theWords))
|
||||
self.spellLanguage = theLang
|
||||
except Exception as e:
|
||||
logger.error("Failed to load spell check word list for language %s" % theLang)
|
||||
logger.error(str(e))
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load spell check word list for language '%s'", theLang)
|
||||
nw.logException()
|
||||
self.spellLanguage = None
|
||||
|
||||
self._readProjectDictionary(projectDict)
|
||||
for pWord in self.projDict:
|
||||
if pWord not in self.WORDS:
|
||||
self.WORDS.append(pWord)
|
||||
self.theWords.add(pWord)
|
||||
|
||||
return
|
||||
|
||||
@@ -280,7 +279,7 @@ class NWSpellSimple(NWSpellCheck):
|
||||
word by the syntax highlighter.
|
||||
"""
|
||||
theWord = theWord.replace(self.mainConf.fmtApostrophe, "'").lower()
|
||||
return theWord in self.WORDS
|
||||
return theWord in self.theWords
|
||||
|
||||
def suggestWords(self, theWord):
|
||||
"""Get suggestions for correct word from difflib, and make sure
|
||||
@@ -293,7 +292,7 @@ class NWSpellSimple(NWSpellCheck):
|
||||
if len(theWord) == 0:
|
||||
return []
|
||||
|
||||
theMatches = difflib.get_close_matches(theWord.lower(), self.WORDS, n=10, cutoff=0.75)
|
||||
theMatches = difflib.get_close_matches(theWord.lower(), self.theWords, n=10, cutoff=0.75)
|
||||
theOptions = []
|
||||
for aWord in theMatches:
|
||||
if len(aWord) == 0:
|
||||
@@ -309,8 +308,8 @@ class NWSpellSimple(NWSpellCheck):
|
||||
"""Wrapper for the internal project dictionary feature.
|
||||
"""
|
||||
newWord = newWord.strip().lower()
|
||||
if newWord not in self.WORDS:
|
||||
self.WORDS.append(newWord)
|
||||
if newWord not in self.theWords:
|
||||
self.theWords.add(newWord)
|
||||
NWSpellCheck.addWord(self, newWord)
|
||||
return
|
||||
|
||||
@@ -324,8 +323,7 @@ class NWSpellSimple(NWSpellCheck):
|
||||
if fExt != ".dict":
|
||||
continue
|
||||
|
||||
spName = "%s [%s]" % (self.expandLanguage(fRoot), nwConst.SP_INTERNAL)
|
||||
retList.append((fRoot, spName))
|
||||
retList.append((fRoot, "difflib"))
|
||||
|
||||
return retList
|
||||
|
||||
@@ -333,6 +331,6 @@ class NWSpellSimple(NWSpellCheck):
|
||||
"""Return the tag and provider of the currently loaded
|
||||
dictionary.
|
||||
"""
|
||||
return self.theLang, nwConst.SP_INTERNAL
|
||||
return self.theLang, ""
|
||||
|
||||
# END Class NWSpellSimple
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Project Item Status Class
|
||||
"""
|
||||
novelWriter – Project Item Status Class
|
||||
=======================================
|
||||
Data class for the status/importance settings of a project item
|
||||
|
||||
novelWriter – Project Item Status Class
|
||||
=========================================
|
||||
Class holding the status/importance elements of a project item
|
||||
File History:
|
||||
Created: 2019-05-19 [0.1.3]
|
||||
|
||||
File History:
|
||||
Created: 2019-05-19 [0.1.3]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -33,15 +31,18 @@ from nw.common import checkInt
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWStatus():
|
||||
|
||||
def __init__(self):
|
||||
self._theLabels = []
|
||||
|
||||
self._theLabels = []
|
||||
self._theColours = []
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
|
||||
return
|
||||
|
||||
def addEntry(self, theLabel, theColours):
|
||||
@@ -88,12 +89,12 @@ class NWStatus():
|
||||
replaceMap = {}
|
||||
|
||||
if newList is not None:
|
||||
self._theLabels = []
|
||||
self._theLabels = []
|
||||
self._theColours = []
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
|
||||
for nName, nR, nG, nB, oName in newList:
|
||||
self.addEntry(nName, (nR, nG, nB))
|
||||
@@ -123,9 +124,9 @@ class NWStatus():
|
||||
"""
|
||||
for n in range(self._theLength):
|
||||
xSub = etree.SubElement(xParent, "entry", attrib={
|
||||
"blue" : str(self._theColours[n][2]),
|
||||
"green" : str(self._theColours[n][1]),
|
||||
"red" : str(self._theColours[n][0]),
|
||||
"blue": str(self._theColours[n][2]),
|
||||
"green": str(self._theColours[n][1]),
|
||||
"red": str(self._theColours[n][0]),
|
||||
})
|
||||
xSub.text = self._theLabels[n]
|
||||
return True
|
||||
|
||||
@@ -1,66 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter HTML Text Converter
|
||||
"""
|
||||
novelWriter – HTML Text Converter
|
||||
=================================
|
||||
Extends the Tokenizer class to generate HTML output
|
||||
|
||||
novelWriter – HTML Text Converter
|
||||
===================================
|
||||
Extends the Tokenizer class to write HTML
|
||||
File History:
|
||||
Created: 2019-05-07 [0.0.1]
|
||||
|
||||
File History:
|
||||
Created: 2019-05-07 [0.0.1]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from nw.core.tokenizer import Tokenizer
|
||||
from nw.constants import nwUnicode, nwLabels, nwKeyWords
|
||||
from nw.constants import nwKeyWords, nwLabels, nwHtmlUnicode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ToHtml(Tokenizer):
|
||||
|
||||
M_PREVIEW = 0 # Tweak output for the DocViewer
|
||||
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
||||
M_EBOOK = 2 # Tweak output for converting to epub
|
||||
M_PREVIEW = 0 # Tweak output for the DocViewer
|
||||
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
||||
M_EBOOK = 2 # Tweak output for converting to epub
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
Tokenizer.__init__(self, theProject, theParent)
|
||||
def __init__(self, theProject):
|
||||
Tokenizer.__init__(self, theProject)
|
||||
|
||||
self.genMode = self.M_EXPORT
|
||||
self.cssStyles = True
|
||||
self.fullHTML = []
|
||||
|
||||
self.repDict = {
|
||||
"<" : "<",
|
||||
">" : ">",
|
||||
"&" : "&",
|
||||
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
|
||||
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
|
||||
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
|
||||
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
|
||||
nwUnicode.U_THNSP : nwUnicode.H_THNSP,
|
||||
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
|
||||
nwUnicode.U_MAPOSS : nwUnicode.H_RSQUO,
|
||||
}
|
||||
self.revDict = {}
|
||||
self.reReplace = []
|
||||
self.reReverse = []
|
||||
self._buildRegEx()
|
||||
# Internals
|
||||
self._trMap = {}
|
||||
self.setReplaceUnicode(False)
|
||||
|
||||
return
|
||||
|
||||
@@ -73,7 +59,7 @@ class ToHtml(Tokenizer):
|
||||
need to make a few changes to formatting, which is managed by
|
||||
these flags.
|
||||
"""
|
||||
self.genMode = self.M_PREVIEW
|
||||
self.genMode = self.M_PREVIEW
|
||||
self.doKeywords = True
|
||||
self.doComments = doComments
|
||||
self.doSynopsis = doSynopsis
|
||||
@@ -87,31 +73,35 @@ class ToHtml(Tokenizer):
|
||||
self.cssStyles = cssStyles
|
||||
return
|
||||
|
||||
def setReplaceUnicode(self, doReplace):
|
||||
"""Set the translation map to either minimal or full unicode for
|
||||
html entities replacement.
|
||||
"""
|
||||
# Control characters must always be replaced
|
||||
# Angle brackets are replaced later as they are also used in
|
||||
# formatting codes
|
||||
self._trMap = str.maketrans({"&": "&"})
|
||||
if doReplace:
|
||||
# Extend to all relevant Unicode characters
|
||||
self._trMap.update(str.maketrans(nwHtmlUnicode.U_TO_H))
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Class Methods
|
||||
##
|
||||
|
||||
def doAutoReplace(self):
|
||||
def getFullResultSize(self):
|
||||
"""Return the size of the full HTML result.
|
||||
"""
|
||||
return sum([len(x) for x in self.fullHTML])
|
||||
|
||||
def doPreProcessing(self):
|
||||
"""Extend the auto-replace to also properly encode some unicode
|
||||
characters into their respective HTML entities.
|
||||
"""
|
||||
Tokenizer.doAutoReplace(self)
|
||||
self.theText = self.reReplace.sub(
|
||||
lambda x: self.repDict[x.group(0)], self.theText
|
||||
)
|
||||
return
|
||||
|
||||
def doPostProcessing(self):
|
||||
"""Reverse the html entities replacement on the markdown text.
|
||||
Otherwise, all the &something; bits will also be in there.
|
||||
"""
|
||||
Tokenizer.doPostProcessing(self)
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
# Doesn't matter for preview as we don't use the markdown
|
||||
return
|
||||
self.theMarkdown = self.reReverse.sub(
|
||||
lambda x: self.revDict[x.group(0)], self.theMarkdown
|
||||
)
|
||||
Tokenizer.doPreProcessing(self)
|
||||
self.theText = self.theText.translate(self._trMap)
|
||||
return
|
||||
|
||||
def doConvert(self):
|
||||
@@ -119,22 +109,22 @@ class ToHtml(Tokenizer):
|
||||
to theResult.
|
||||
"""
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
htmlTags = { # HTML4 + CSS2
|
||||
self.FMT_B_B : "<b>",
|
||||
self.FMT_B_E : "</b>",
|
||||
self.FMT_I_B : "<i>",
|
||||
self.FMT_I_E : "</i>",
|
||||
self.FMT_D_B : "<span style='text-decoration: line-through;'>",
|
||||
self.FMT_D_E : "</span>",
|
||||
htmlTags = { # HTML4 + CSS2
|
||||
self.FMT_B_B: "<b>",
|
||||
self.FMT_B_E: "</b>",
|
||||
self.FMT_I_B: "<i>",
|
||||
self.FMT_I_E: "</i>",
|
||||
self.FMT_D_B: "<span style='text-decoration: line-through;'>",
|
||||
self.FMT_D_E: "</span>",
|
||||
}
|
||||
else:
|
||||
htmlTags = { # HTML5
|
||||
self.FMT_B_B : "<strong>",
|
||||
self.FMT_B_E : "</strong>",
|
||||
self.FMT_I_B : "<em>",
|
||||
self.FMT_I_E : "</em>",
|
||||
self.FMT_D_B : "<del>",
|
||||
self.FMT_D_E : "</del>",
|
||||
htmlTags = { # HTML5
|
||||
self.FMT_B_B: "<strong>",
|
||||
self.FMT_B_E: "</strong>",
|
||||
self.FMT_I_B: "<em>",
|
||||
self.FMT_I_E: "</em>",
|
||||
self.FMT_D_B: "<del>",
|
||||
self.FMT_D_E: "</del>",
|
||||
}
|
||||
|
||||
if self.isNovel and self.genMode != self.M_PREVIEW:
|
||||
@@ -158,32 +148,43 @@ class ToHtml(Tokenizer):
|
||||
thisPar = []
|
||||
parStyle = None
|
||||
tmpResult = []
|
||||
hasHardBreak = False
|
||||
|
||||
for tType, tLine, tText, tFormat, tStyle in self.theTokens:
|
||||
|
||||
# Replace < and > before adding html tags
|
||||
tText = tText.replace("<", "<").replace(">", ">")
|
||||
|
||||
# Styles
|
||||
aStyle = []
|
||||
if tStyle is not None and self.cssStyles:
|
||||
if tStyle & self.A_LEFT:
|
||||
aStyle.append("text-align: left;")
|
||||
if tStyle & self.A_RIGHT:
|
||||
elif tStyle & self.A_RIGHT:
|
||||
aStyle.append("text-align: right;")
|
||||
if tStyle & self.A_CENTRE:
|
||||
elif tStyle & self.A_CENTRE:
|
||||
aStyle.append("text-align: center;")
|
||||
if tStyle & self.A_JUSTIFY:
|
||||
elif tStyle & self.A_JUSTIFY:
|
||||
aStyle.append("text-align: justify;")
|
||||
|
||||
if tStyle & self.A_PBB:
|
||||
aStyle.append("page-break-before: always;")
|
||||
if tStyle & self.A_PBB_AV:
|
||||
aStyle.append("page-break-before: avoid;")
|
||||
if tStyle & self.A_PBB_NO:
|
||||
aStyle.append("page-break-before: never;")
|
||||
elif tStyle & self.A_PBB_AUT:
|
||||
aStyle.append("page-break-before: auto;")
|
||||
|
||||
if tStyle & self.A_PBA:
|
||||
aStyle.append("page-break-after: always;")
|
||||
if tStyle & self.A_PBA_AV:
|
||||
aStyle.append("page-break-after: avoid;")
|
||||
if tStyle & self.A_PBA_NO:
|
||||
aStyle.append("page-break-after: never;")
|
||||
elif tStyle & self.A_PBA_AUT:
|
||||
aStyle.append("page-break-after: auto;")
|
||||
|
||||
if tStyle & self.A_Z_BTMMRG:
|
||||
aStyle.append("margin-bottom: 0;")
|
||||
if tStyle & self.A_Z_TOPMRG:
|
||||
aStyle.append("margin-top: 0;")
|
||||
|
||||
if tStyle & self.A_IND_L:
|
||||
aStyle.append("margin-left: %dpx;" % self.mainConf.tabWidth)
|
||||
if tStyle & self.A_IND_R:
|
||||
aStyle.append("margin-right: %dpx;" % self.mainConf.tabWidth)
|
||||
|
||||
if len(aStyle) > 0:
|
||||
hStyle = " style='%s'" % (" ".join(aStyle))
|
||||
@@ -199,16 +200,15 @@ class ToHtml(Tokenizer):
|
||||
if tType == self.T_EMPTY:
|
||||
if parStyle is None:
|
||||
parStyle = ""
|
||||
if hasHardBreak and self.cssStyles:
|
||||
if len(thisPar) > 1 and self.cssStyles:
|
||||
parClass = " class='break'"
|
||||
else:
|
||||
parClass = ""
|
||||
if len(thisPar) > 0:
|
||||
tTemp = "".join(thisPar)
|
||||
tTemp = "<br/>".join(thisPar)
|
||||
tmpResult.append("<p%s%s>%s</p>\n" % (parStyle, parClass, tTemp.rstrip()))
|
||||
thisPar = []
|
||||
parStyle = None
|
||||
hasHardBreak = False
|
||||
|
||||
elif tType == self.T_TITLE:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
@@ -241,12 +241,8 @@ class ToHtml(Tokenizer):
|
||||
if parStyle is None:
|
||||
parStyle = hStyle
|
||||
for xPos, xLen, xFmt in reversed(tFormat):
|
||||
tTemp = tTemp[:xPos]+htmlTags[xFmt]+tTemp[xPos+xLen:]
|
||||
if tText.endswith(" "):
|
||||
thisPar.append(tTemp.rstrip()+"<br/>")
|
||||
hasHardBreak = True
|
||||
else:
|
||||
thisPar.append(tTemp.rstrip()+" ")
|
||||
tTemp = tTemp[:xPos] + htmlTags[xFmt] + tTemp[xPos+xLen:]
|
||||
thisPar.append(tTemp.rstrip())
|
||||
|
||||
elif tType == self.T_SYNOPSIS and self.doSynopsis:
|
||||
tmpResult.append(self._formatSynopsis(tText))
|
||||
@@ -255,11 +251,60 @@ class ToHtml(Tokenizer):
|
||||
tmpResult.append(self._formatComments(tText))
|
||||
|
||||
elif tType == self.T_KEYWORD and self.doKeywords:
|
||||
tmpResult.append(self._formatKeywords(tText))
|
||||
tTemp = "<p%s>%s</p>\n" % (hStyle, self._formatKeywords(tText))
|
||||
tmpResult.append(tTemp)
|
||||
|
||||
self.theResult = "".join(tmpResult)
|
||||
tmpResult = []
|
||||
|
||||
if self.genMode != self.M_PREVIEW:
|
||||
self.fullHTML.append(self.theResult)
|
||||
|
||||
return
|
||||
|
||||
def saveHTML5(self, savePath):
|
||||
"""Save the data to an .html file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
theStyle = self.getStyleSheet()
|
||||
theStyle.append("article {width: 800px; margin: 40px auto;}")
|
||||
bodyText = "".join(self.fullHTML)
|
||||
bodyText = bodyText.replace("\t", "	").rstrip()
|
||||
|
||||
theHtml = (
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
"<meta charset='utf-8'>\n"
|
||||
"<title>{projTitle:s}</title>\n"
|
||||
"</head>\n"
|
||||
"<style>\n"
|
||||
"{htmlStyle:s}\n"
|
||||
"</style>\n"
|
||||
"<body>\n"
|
||||
"<article>\n"
|
||||
"{bodyText:s}\n"
|
||||
"</article>\n"
|
||||
"</body>\n"
|
||||
"</html>\n"
|
||||
).format(
|
||||
projTitle=self.theProject.projName,
|
||||
htmlStyle="\n".join(theStyle),
|
||||
bodyText=bodyText,
|
||||
)
|
||||
outFile.write(theHtml)
|
||||
|
||||
return
|
||||
|
||||
def replaceTabs(self, nSpaces=8, spaceChar=" "):
|
||||
"""Replace tabs with spaces in the html.
|
||||
"""
|
||||
htmlText = []
|
||||
eightSpace = spaceChar*nSpaces
|
||||
for aLine in self.fullHTML:
|
||||
htmlText.append(aLine.replace("\t", eightSpace))
|
||||
|
||||
self.fullHTML = htmlText
|
||||
return
|
||||
|
||||
def getStyleSheet(self):
|
||||
@@ -269,22 +314,86 @@ class ToHtml(Tokenizer):
|
||||
if not self.cssStyles:
|
||||
return theStyles
|
||||
|
||||
if self.doJustify:
|
||||
theStyles.append(r"p {text-align: justify;}")
|
||||
else:
|
||||
theStyles.append(r"p {text-align: left;}")
|
||||
mScale = self.lineHeight/1.15
|
||||
textAlign = "justify" if self.doJustify else "left"
|
||||
|
||||
theStyles.append(r"h1, h2 {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(r"h3, h4 {color: rgb(50, 50, 50);}")
|
||||
theStyles.append(r"h1, h2, h3, h4 {page-break-after: avoid;}")
|
||||
theStyles.append(r"a {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(r".title {font-size: 2.5em;}")
|
||||
theStyles.append(r".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
theStyles.append(r".break {text-align: left;}")
|
||||
theStyles.append(r".sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}")
|
||||
theStyles.append(r".skip {margin-top: 1em; margin-bottom: 1em;}")
|
||||
theStyles.append(r".synopsis {font-style: italic;}")
|
||||
theStyles.append(r".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
||||
theStyles.append("body {font-family: '%s'; font-size: %dpt;}" % (
|
||||
self.textFont, self.textSize
|
||||
))
|
||||
theStyles.append((
|
||||
"p {"
|
||||
"text-align: %s; line-height: %d%%; "
|
||||
"margin-top: %.2fem; margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
textAlign,
|
||||
round(100 * self.lineHeight),
|
||||
mScale * self.marginText[0],
|
||||
mScale * self.marginText[1],
|
||||
))
|
||||
theStyles.append((
|
||||
"h1 {"
|
||||
"color: rgb(66, 113, 174); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
mScale * self.marginHead1[0], mScale * self.marginHead1[1]
|
||||
))
|
||||
theStyles.append((
|
||||
"h2 {"
|
||||
"color: rgb(66, 113, 174); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
mScale * self.marginHead2[0], mScale * self.marginHead2[1]
|
||||
))
|
||||
theStyles.append((
|
||||
"h3 {"
|
||||
"color: rgb(50, 50, 50); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
mScale * self.marginHead3[0], mScale * self.marginHead3[1]
|
||||
))
|
||||
theStyles.append((
|
||||
"h4 {"
|
||||
"color: rgb(50, 50, 50); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
mScale * self.marginHead4[0], mScale * self.marginHead4[1]
|
||||
))
|
||||
theStyles.append((
|
||||
".title {"
|
||||
"font-size: 2.5em; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
mScale * self.marginTitle[0], mScale * self.marginTitle[1]
|
||||
))
|
||||
theStyles.append((
|
||||
".sep, .skip {"
|
||||
"text-align: center; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;}"
|
||||
) % (
|
||||
mScale, mScale
|
||||
))
|
||||
|
||||
theStyles.append("a {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
theStyles.append(".break {text-align: left;}")
|
||||
theStyles.append(".synopsis {font-style: italic;}")
|
||||
theStyles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
||||
|
||||
return theStyles
|
||||
|
||||
@@ -296,17 +405,20 @@ class ToHtml(Tokenizer):
|
||||
"""Apply HTML formatting to synopsis.
|
||||
"""
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
return "<p class='comment'><span class='synopsis'>Synopsis: </span>%s</p>\n" % tText
|
||||
sSynop = self._trSynopsis
|
||||
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {tText}</p>\n"
|
||||
else:
|
||||
return "<p class='synopsis'><strong>Synopsis: </strong>%s</p>\n" % tText
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
return f"<p class='synopsis'><strong>{sSynop}:</strong> {tText}</p>\n"
|
||||
|
||||
def _formatComments(self, tText):
|
||||
"""Apply HTML formatting to comments.
|
||||
"""
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
return "<p class='comment'>%s</p>\n" % tText
|
||||
return f"<p class='comment'>{tText}</p>\n"
|
||||
else:
|
||||
return "<p class='comment'><strong>Comment: </strong>%s</p>\n" % tText
|
||||
sComm = self._localLookup("Comment")
|
||||
return f"<p class='comment'><strong>{sComm}:</strong> {tText}</p>\n"
|
||||
|
||||
def _formatKeywords(self, tText):
|
||||
"""Apply HTML formatting to keywords.
|
||||
@@ -338,18 +450,6 @@ class ToHtml(Tokenizer):
|
||||
))
|
||||
retText += ", ".join(refTags)
|
||||
|
||||
return "<div>%s</div>" % retText
|
||||
|
||||
def _buildRegEx(self):
|
||||
"""Build the regular expressions
|
||||
"""
|
||||
self.revDict = dict(map(reversed, self.repDict.items()))
|
||||
self.reReplace = re.compile(
|
||||
"|".join([re.escape(k) for k in self.repDict.keys()]), flags=re.DOTALL
|
||||
)
|
||||
self.reReverse = re.compile(
|
||||
"|".join([re.escape(k) for k in self.revDict.keys()]), flags=re.DOTALL
|
||||
)
|
||||
return
|
||||
return retText
|
||||
|
||||
# END Class ToHtml
|
||||
|
||||
@@ -1,112 +1,138 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""novelWriter Text Tokenizer
|
||||
"""
|
||||
novelWriter – Text Tokenizer
|
||||
============================
|
||||
Splits a piece of novelWriter markdown text into its elements
|
||||
|
||||
novelWriter – Text Tokenizer
|
||||
==============================
|
||||
Splits a piece of novelWriter markdown text into its elements
|
||||
File History:
|
||||
Created: 2019-05-05 [0.0.1]
|
||||
|
||||
File History:
|
||||
Created: 2019-05-05 [0.0.1]
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import nw
|
||||
import re
|
||||
import logging
|
||||
|
||||
from operator import itemgetter
|
||||
from PyQt5.QtCore import QRegularExpression
|
||||
from functools import partial
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
||||
|
||||
from nw.enum import nwItemLayout, nwItemType
|
||||
from nw.common import numberToRoman
|
||||
from nw.constants import nwConst, nwRegEx, nwUnicode
|
||||
from nw.core.document import NWDoc
|
||||
from nw.core.tools import numberToWord, numberToRoman
|
||||
from nw.constants import nwConst, nwItemLayout, nwItemType, nwRegEx
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Tokenizer():
|
||||
|
||||
FMT_B_B = 1 # Begin bold
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_D_B = 5 # Begin strikeout
|
||||
FMT_D_E = 6 # End strikeout
|
||||
# In-Text Format
|
||||
FMT_B_B = 1 # Begin bold
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_D_B = 5 # Begin strikeout
|
||||
FMT_D_E = 6 # End strikeout
|
||||
|
||||
T_EMPTY = 1 # Empty line (new paragraph)
|
||||
T_SYNOPSIS = 2 # Synopsis comment
|
||||
T_COMMENT = 3 # Comment line
|
||||
T_KEYWORD = 4 # Command line
|
||||
T_TITLE = 5 # Title
|
||||
T_HEAD1 = 6 # Header 1
|
||||
T_HEAD2 = 7 # Header 2
|
||||
T_HEAD3 = 8 # Header 3
|
||||
T_HEAD4 = 9 # Header 4
|
||||
T_TEXT = 10 # Text line
|
||||
T_SEP = 11 # Scene separator
|
||||
T_SKIP = 12 # Paragraph break
|
||||
# Block Type
|
||||
T_EMPTY = 1 # Empty line (new paragraph)
|
||||
T_SYNOPSIS = 2 # Synopsis comment
|
||||
T_COMMENT = 3 # Comment line
|
||||
T_KEYWORD = 4 # Command line
|
||||
T_TITLE = 5 # Title
|
||||
T_HEAD1 = 6 # Header 1
|
||||
T_HEAD2 = 7 # Header 2
|
||||
T_HEAD3 = 8 # Header 3
|
||||
T_HEAD4 = 9 # Header 4
|
||||
T_TEXT = 10 # Text line
|
||||
T_SEP = 11 # Scene separator
|
||||
T_SKIP = 12 # Paragraph break
|
||||
|
||||
A_NONE = 0 # No special style
|
||||
A_LEFT = 1 # Left aligned
|
||||
A_RIGHT = 2 # Right aligned
|
||||
A_CENTRE = 4 # Centred
|
||||
A_JUSTIFY = 8 # Justified
|
||||
A_PBB = 16 # Page break before always
|
||||
A_PBB_AV = 32 # Page break before avoid
|
||||
A_PBB_NO = 64 # Page break before never
|
||||
A_PBA = 128 # Page break after always
|
||||
A_PBA_AV = 256 # Page break after avoid
|
||||
A_PBA_NO = 512 # Page break after avoid
|
||||
# Block Style
|
||||
A_NONE = 0x0000 # No special style
|
||||
A_LEFT = 0x0001 # Left aligned
|
||||
A_RIGHT = 0x0002 # Right aligned
|
||||
A_CENTRE = 0x0004 # Centred
|
||||
A_JUSTIFY = 0x0008 # Justified
|
||||
A_PBB = 0x0010 # Page break before always
|
||||
A_PBB_AUT = 0x0020 # Page break before auto
|
||||
A_PBA = 0x0040 # Page break after always
|
||||
A_PBA_AUT = 0x0080 # Page break after auto
|
||||
A_Z_TOPMRG = 0x0100 # Zero top margin
|
||||
A_Z_BTMMRG = 0x0200 # Zero bottom margin
|
||||
A_IND_L = 0x0400 # Left indentation
|
||||
A_IND_R = 0x0800 # Right indentation
|
||||
|
||||
def __init__(self, theProject, theParent):
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
self.theParent = theParent
|
||||
self.theParent = theProject.theParent
|
||||
self.mainConf = nw.CONFIG
|
||||
|
||||
# Data Variables
|
||||
self.theText = None # The raw text to be tokenized
|
||||
self.theHandle = None # The handle associated with the text
|
||||
self.theItem = None # The NWItem associated with the handle
|
||||
self.theTokens = None # The list of the processed tokens
|
||||
self.theResult = None # The result text after conversion
|
||||
self.theMarkdown = None # The result text in novelWriter markdown
|
||||
self.theText = "" # The raw text to be tokenized
|
||||
self.theHandle = None # The handle associated with the text
|
||||
self.theItem = None # The NWItem associated with the handle
|
||||
self.theTokens = [] # The list of the processed tokens
|
||||
self.theResult = "" # The result of the last document
|
||||
|
||||
self.keepMarkdown = False # Whether to keep the markdown text
|
||||
self.theMarkdown = [] # The result novelWriter markdown of all documents
|
||||
|
||||
# User Settings
|
||||
self.doBodyText = True # Include body text
|
||||
self.doSynopsis = False # Also process synopsis comments
|
||||
self.doComments = False # Also process comments
|
||||
self.doKeywords = False # Also process keywords like tags and references
|
||||
self.doJustify = False # Justify text
|
||||
self.textFont = "Serif" # Output text font
|
||||
self.textSize = 11 # Output text size
|
||||
self.textFixed = False # Fixed width text
|
||||
self.lineHeight = 1.15 # Line height in units of em
|
||||
self.blockIndent = 4.00 # Block indent in units of em
|
||||
self.doJustify = False # Justify text
|
||||
self.doBodyText = True # Include body text
|
||||
self.doSynopsis = False # Also process synopsis comments
|
||||
self.doComments = False # Also process comments
|
||||
self.doKeywords = False # Also process keywords like tags and references
|
||||
|
||||
self.fmtTitle = "%title%" # Formatting for titles
|
||||
self.fmtChapter = "%title%" # Formatting for numbered chapters
|
||||
self.fmtUnNum = "%title%" # Formatting for unnumbered chapters
|
||||
self.fmtScene = "%title%" # Formatting for scenes
|
||||
self.fmtSection = "%title%" # Formatting for sections
|
||||
# Margins
|
||||
self.marginTitle = (1.000, 0.500)
|
||||
self.marginHead1 = (1.000, 0.500)
|
||||
self.marginHead2 = (0.834, 0.500)
|
||||
self.marginHead3 = (0.584, 0.500)
|
||||
self.marginHead4 = (0.584, 0.500)
|
||||
self.marginText = (0.000, 0.584)
|
||||
self.marginMeta = (0.000, 0.584)
|
||||
|
||||
self.hideScene = False # Do not include scene headers
|
||||
self.hideSection = False # Do not include section headers
|
||||
# Title Formats
|
||||
self.fmtTitle = "%title%" # Formatting for titles
|
||||
self.fmtChapter = "%title%" # Formatting for numbered chapters
|
||||
self.fmtUnNum = "%title%" # Formatting for unnumbered chapters
|
||||
self.fmtScene = "%title%" # Formatting for scenes
|
||||
self.fmtSection = "%title%" # Formatting for sections
|
||||
|
||||
self.linkHeaders = False # Add an anchor before headers
|
||||
self.hideScene = False # Do not include scene headers
|
||||
self.hideSection = False # Do not include section headers
|
||||
|
||||
self.linkHeaders = False # Add an anchor before headers
|
||||
|
||||
# Instance Variables
|
||||
self.numChapter = 0 # Counter for chapter numbers
|
||||
self.numChScene = 0 # Counter for scene number within chapter
|
||||
self.numAbsScene = 0 # Counter for scene number within novel
|
||||
self.firstScene = False # Flag to indicate that the first scene of the chapter
|
||||
self.numChapter = 0 # Counter for chapter numbers
|
||||
self.numChScene = 0 # Counter for scene number within chapter
|
||||
self.numAbsScene = 0 # Counter for scene number within novel
|
||||
self.firstScene = False # Flag to indicate that the first scene of the chapter
|
||||
|
||||
# This File
|
||||
self.isNone = False
|
||||
@@ -123,6 +149,13 @@ class Tokenizer():
|
||||
# Error Handling
|
||||
self.errData = []
|
||||
|
||||
# Function Mapping
|
||||
self._localLookup = self.theProject.localLookup
|
||||
self.tr = partial(QCoreApplication.translate, "Tokenizer")
|
||||
|
||||
# Cached Translations
|
||||
self._trSynopsis = self.tr("Synopsis")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
@@ -151,6 +184,52 @@ class Tokenizer():
|
||||
self.hideSection = hideSection
|
||||
return
|
||||
|
||||
def setFont(self, textFont, textSize, textFixed=False):
|
||||
self.textFont = textFont
|
||||
self.textSize = round(int(textSize))
|
||||
self.textFixed = textFixed
|
||||
return
|
||||
|
||||
def setLineHeight(self, lineHeight):
|
||||
self.lineHeight = min(max(float(lineHeight), 0.5), 5.0)
|
||||
return
|
||||
|
||||
def setBlockIndent(self, blockIndent):
|
||||
self.blockIndent = min(max(float(blockIndent), 0.0), 10.0)
|
||||
return
|
||||
|
||||
def setJustify(self, doJustify):
|
||||
self.doJustify = doJustify
|
||||
return
|
||||
|
||||
def setTitleMargins(self, mUpper, mLower):
|
||||
self.marginTitle = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setHead1Margins(self, mUpper, mLower):
|
||||
self.marginHead1 = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setHead2Margins(self, mUpper, mLower):
|
||||
self.marginHead2 = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setHead3Margins(self, mUpper, mLower):
|
||||
self.marginHead3 = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setHead4Margins(self, mUpper, mLower):
|
||||
self.marginHead4 = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setTextMargins(self, mUpper, mLower):
|
||||
self.marginText = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setMetaMargins(self, mUpper, mLower):
|
||||
self.marginMeta = (float(mUpper), float(mLower))
|
||||
return
|
||||
|
||||
def setLinkHeaders(self, linkHeaders):
|
||||
self.linkHeaders = linkHeaders
|
||||
return
|
||||
@@ -171,8 +250,8 @@ class Tokenizer():
|
||||
self.doKeywords = doKeywords
|
||||
return
|
||||
|
||||
def setJustify(self, doJustify):
|
||||
self.doJustify = doJustify
|
||||
def setKeepMarkdown(self, keepMarkdown):
|
||||
self.keepMarkdown = keepMarkdown
|
||||
return
|
||||
|
||||
##
|
||||
@@ -189,12 +268,13 @@ class Tokenizer():
|
||||
if theItem.itemType != nwItemType.ROOT:
|
||||
return False
|
||||
|
||||
theTitle = "Notes: %s" % theItem.itemName
|
||||
theTitle = "%s: %s" % (self._localLookup("Notes"), theItem.itemName)
|
||||
self.theTokens = []
|
||||
self.theTokens.append((
|
||||
self.T_TITLE, 0, theTitle, None, self.A_PBB | self.A_CENTRE
|
||||
))
|
||||
self.theMarkdown = "# %s\n\n" % theTitle
|
||||
if self.keepMarkdown:
|
||||
self.theMarkdown.append("# %s\n\n" % theTitle)
|
||||
|
||||
return True
|
||||
|
||||
@@ -207,20 +287,23 @@ class Tokenizer():
|
||||
if self.theItem is None:
|
||||
return False
|
||||
|
||||
self.theText = ""
|
||||
if theText is not None:
|
||||
# If the text is set, just use that
|
||||
self.theText = theText
|
||||
else:
|
||||
# Otherwise, load it from file
|
||||
theDocument = NWDoc(self.theProject, self.theParent)
|
||||
self.theText = theDocument.openDocument(theHandle)
|
||||
theDoc = NWDoc(self.theProject, theHandle)
|
||||
theText = theDoc.readDocument()
|
||||
if theText:
|
||||
self.theText = theText
|
||||
|
||||
docSize = len(self.theText)
|
||||
if docSize > nwConst.MAX_DOCSIZE:
|
||||
errVal = "Document '%s' is too big (%.2f MB). Skipping." % (
|
||||
self.theItem.itemName, docSize/1.0e6
|
||||
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
|
||||
self.theItem.itemName, f"{docSize/1.0e6:.2f}"
|
||||
)
|
||||
self.theText = "# ERROR\n\n%s\n\n" % errVal
|
||||
self.theText = "# %s\n\n%s\n\n" % (self.tr("ERROR"), errVal)
|
||||
self.errData.append(errVal)
|
||||
|
||||
self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
@@ -236,26 +319,10 @@ class Tokenizer():
|
||||
|
||||
return True
|
||||
|
||||
def getResult(self):
|
||||
"""Return the result from the conversion.
|
||||
"""
|
||||
return self.theResult
|
||||
|
||||
def getResultSize(self):
|
||||
"""Return the size of the result from the conversion.
|
||||
"""
|
||||
if self.theResult is None:
|
||||
return 0
|
||||
return len(self.theResult)
|
||||
|
||||
def getFilteredMarkdown(self):
|
||||
"""Return the novelWriter markdown after the filters have been applied.
|
||||
"""
|
||||
return self.theMarkdown
|
||||
|
||||
def doAutoReplace(self):
|
||||
"""Run through the user's auto-replace dictionary.
|
||||
def doPreProcessing(self):
|
||||
"""Run trough the various replace doctionaries.
|
||||
"""
|
||||
# Process the user's auto-replace dictionary
|
||||
if len(self.theProject.autoReplace) > 0:
|
||||
repDict = {}
|
||||
for aKey, aVal in self.theProject.autoReplace.items():
|
||||
@@ -263,6 +330,10 @@ class Tokenizer():
|
||||
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
||||
self.theText = xRep.sub(lambda x: repDict[x.group(0)], self.theText)
|
||||
|
||||
# Process the character translation map
|
||||
trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO}
|
||||
self.theText = self.theText.translate(str.maketrans(trDict))
|
||||
|
||||
return
|
||||
|
||||
def doPostProcessing(self):
|
||||
@@ -270,9 +341,9 @@ class Tokenizer():
|
||||
does the standard escaped characters.
|
||||
"""
|
||||
escapeDict = {
|
||||
r"\*" : "*",
|
||||
r"\~" : "~",
|
||||
r"\_" : "_",
|
||||
r"\*": "*",
|
||||
r"\~": "~",
|
||||
r"\_": "_",
|
||||
}
|
||||
escReplace = re.compile(
|
||||
"|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL
|
||||
@@ -304,8 +375,13 @@ class Tokenizer():
|
||||
(QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]),
|
||||
]
|
||||
|
||||
# Determine default text alignment
|
||||
if self.isTitle or self.isPart:
|
||||
defAlign = self.A_CENTRE
|
||||
else:
|
||||
defAlign = self.A_NONE
|
||||
|
||||
self.theTokens = []
|
||||
self.theMarkdown = ""
|
||||
tmpMarkdown = []
|
||||
nLine = 0
|
||||
for aLine in self.theText.splitlines():
|
||||
@@ -314,94 +390,99 @@ class Tokenizer():
|
||||
# Tag lines starting with specific characters
|
||||
if len(aLine.strip()) == 0:
|
||||
self.theTokens.append((
|
||||
self.T_EMPTY,
|
||||
nLine,
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_EMPTY, nLine, "", None, defAlign
|
||||
))
|
||||
tmpMarkdown.append("\n")
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("\n")
|
||||
|
||||
elif aLine[0] == "%":
|
||||
cLine = aLine[1:].lstrip()
|
||||
synTag = cLine[:9].lower()
|
||||
if synTag == "synopsis:":
|
||||
self.theTokens.append((
|
||||
self.T_SYNOPSIS,
|
||||
nLine,
|
||||
cLine[9:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, defAlign
|
||||
))
|
||||
if self.doSynopsis:
|
||||
if self.doSynopsis and self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
else:
|
||||
self.theTokens.append((
|
||||
self.T_COMMENT,
|
||||
nLine,
|
||||
aLine[1:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_COMMENT, nLine, aLine[1:].strip(), None, defAlign
|
||||
))
|
||||
if self.doComments:
|
||||
if self.doComments and self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[0] == "@":
|
||||
self.theTokens.append((
|
||||
self.T_KEYWORD,
|
||||
nLine,
|
||||
aLine[1:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_KEYWORD, nLine, aLine[1:].strip(), None, defAlign
|
||||
))
|
||||
if self.doKeywords:
|
||||
if self.doKeywords and self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:2] == "# ":
|
||||
self.theTokens.append((
|
||||
self.T_HEAD1,
|
||||
nLine,
|
||||
aLine[2:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_HEAD1, nLine, aLine[2:].strip(), None, defAlign
|
||||
))
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:3] == "## ":
|
||||
self.theTokens.append((
|
||||
self.T_HEAD2,
|
||||
nLine,
|
||||
aLine[3:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_HEAD2, nLine, aLine[3:].strip(), None, defAlign
|
||||
))
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:4] == "### ":
|
||||
self.theTokens.append((
|
||||
self.T_HEAD3,
|
||||
nLine,
|
||||
aLine[4:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_HEAD3, nLine, aLine[4:].strip(), None, defAlign
|
||||
))
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
elif aLine[:5] == "#### ":
|
||||
self.theTokens.append((
|
||||
self.T_HEAD4,
|
||||
nLine,
|
||||
aLine[5:].strip(),
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_HEAD4, nLine, aLine[5:].strip(), None, defAlign
|
||||
))
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
else:
|
||||
if not self.doBodyText:
|
||||
# Skip all body text
|
||||
continue
|
||||
|
||||
# Check Alignment and Indentation
|
||||
tagLeft = False
|
||||
tagRight = False
|
||||
indLeft = False
|
||||
indRight = False
|
||||
if aLine.startswith(">>"):
|
||||
tagRight = True
|
||||
aLine = aLine[2:].lstrip(" ")
|
||||
elif aLine.startswith(">"):
|
||||
indLeft = True
|
||||
aLine = aLine[1:].lstrip(" ")
|
||||
|
||||
if aLine.endswith("<<"):
|
||||
tagLeft = True
|
||||
aLine = aLine[:-2].rstrip(" ")
|
||||
elif aLine.endswith("<"):
|
||||
indRight = True
|
||||
aLine = aLine[:-1].rstrip(" ")
|
||||
|
||||
textAlign = defAlign
|
||||
if tagLeft and tagRight:
|
||||
textAlign = self.A_CENTRE
|
||||
elif tagLeft:
|
||||
textAlign = self.A_LEFT
|
||||
elif tagRight:
|
||||
textAlign = self.A_RIGHT
|
||||
|
||||
if indLeft:
|
||||
textAlign |= self.A_IND_L
|
||||
if indRight:
|
||||
textAlign |= self.A_IND_R
|
||||
|
||||
# Otherwise we use RegEx to find formatting tags within a line of text
|
||||
fmtPos = []
|
||||
for theRX, theKeys in rxFormats:
|
||||
@@ -418,26 +499,44 @@ class Tokenizer():
|
||||
# sorted by position
|
||||
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
||||
self.theTokens.append((
|
||||
self.T_TEXT,
|
||||
nLine,
|
||||
aLine,
|
||||
fmtPos,
|
||||
self.A_NONE
|
||||
self.T_TEXT, nLine, aLine, fmtPos, textAlign
|
||||
))
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("%s\n" % aLine)
|
||||
|
||||
# Always add an empty line at the end
|
||||
self.theTokens.append((
|
||||
self.T_EMPTY,
|
||||
nLine,
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_EMPTY, nLine, "", None, defAlign
|
||||
))
|
||||
tmpMarkdown.append("\n")
|
||||
if self.keepMarkdown:
|
||||
tmpMarkdown.append("\n")
|
||||
|
||||
self.theMarkdown = "".join(tmpMarkdown)
|
||||
tmpMarkdown = []
|
||||
if self.keepMarkdown:
|
||||
self.theMarkdown.append("".join(tmpMarkdown))
|
||||
|
||||
# Second Pass
|
||||
# ===========
|
||||
# Some items need a second pass
|
||||
|
||||
pToken = (self.T_EMPTY, 0, "", None, defAlign)
|
||||
nToken = (self.T_EMPTY, 0, "", None, defAlign)
|
||||
tCount = len(self.theTokens)
|
||||
for n, tToken in enumerate(self.theTokens):
|
||||
|
||||
if n > 0:
|
||||
pToken = self.theTokens[n-1]
|
||||
if n < tCount - 1:
|
||||
nToken = self.theTokens[n+1]
|
||||
|
||||
if tToken[0] == self.T_KEYWORD:
|
||||
aStyle = tToken[4]
|
||||
if pToken[0] == self.T_KEYWORD:
|
||||
aStyle |= self.A_Z_TOPMRG
|
||||
if nToken[0] == self.T_KEYWORD:
|
||||
aStyle |= self.A_Z_BTMMRG
|
||||
self.theTokens[n] = (
|
||||
tToken[0], tToken[1], tToken[2], tToken[3], aStyle
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -452,9 +551,7 @@ class Tokenizer():
|
||||
# For novel files, we need to handle chapter numbering, scene
|
||||
# numbering, and scene breaks
|
||||
if self.isNovel:
|
||||
for n in range(len(self.theTokens)):
|
||||
|
||||
tToken = self.theTokens[n]
|
||||
for n, tToken in enumerate(self.theTokens):
|
||||
|
||||
# In case we see text before a scene, we reset the flag
|
||||
if tToken[0] == self.T_TEXT:
|
||||
@@ -466,11 +563,7 @@ class Tokenizer():
|
||||
|
||||
tTemp = self._formatHeading(self.fmtTitle, tToken[2])
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tTemp,
|
||||
None,
|
||||
self.A_NONE
|
||||
tToken[0], tToken[1], tTemp, None, self.A_NONE
|
||||
)
|
||||
|
||||
elif tToken[0] == self.T_HEAD2:
|
||||
@@ -488,11 +581,7 @@ class Tokenizer():
|
||||
|
||||
# Format the chapter header
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tTemp,
|
||||
None,
|
||||
self.A_PBB
|
||||
tToken[0], tToken[1], tTemp, None, self.A_PBB
|
||||
)
|
||||
|
||||
# Set scene variables
|
||||
@@ -509,53 +598,29 @@ class Tokenizer():
|
||||
tTemp = self._formatHeading(self.fmtScene, tToken[2])
|
||||
if tTemp == "" and self.hideScene:
|
||||
self.theTokens[n] = (
|
||||
self.T_EMPTY,
|
||||
tToken[1],
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
)
|
||||
elif tTemp == "" and not self.hideScene:
|
||||
if self.firstScene:
|
||||
self.theTokens[n] = (
|
||||
self.T_EMPTY,
|
||||
tToken[1],
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
self.T_SKIP,
|
||||
tToken[1],
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_SKIP, tToken[1], "", None, self.A_NONE
|
||||
)
|
||||
elif tTemp == self.fmtScene:
|
||||
if self.firstScene:
|
||||
self.theTokens[n] = (
|
||||
self.T_EMPTY,
|
||||
tToken[1],
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
self.T_SEP,
|
||||
tToken[1],
|
||||
tTemp,
|
||||
None,
|
||||
self.A_CENTRE
|
||||
self.T_SEP, tToken[1], tTemp, None, self.A_CENTRE
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tTemp,
|
||||
None,
|
||||
self.A_NONE
|
||||
tToken[0], tToken[1], tTemp, None, self.A_NONE
|
||||
)
|
||||
|
||||
# Definitely no longer the first scene
|
||||
@@ -568,104 +633,67 @@ class Tokenizer():
|
||||
tTemp = self._formatHeading(self.fmtSection, tToken[2])
|
||||
if tTemp == "" and self.hideSection:
|
||||
self.theTokens[n] = (
|
||||
self.T_EMPTY,
|
||||
tToken[1],
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||
)
|
||||
elif tTemp == "" and not self.hideSection:
|
||||
self.theTokens[n] = (
|
||||
self.T_SKIP,
|
||||
tToken[1],
|
||||
"",
|
||||
None,
|
||||
self.A_NONE
|
||||
self.T_SKIP, tToken[1], "", None, self.A_NONE
|
||||
)
|
||||
elif tTemp == self.fmtSection:
|
||||
self.theTokens[n] = (
|
||||
self.T_SEP,
|
||||
tToken[1],
|
||||
tTemp,
|
||||
None,
|
||||
self.A_CENTRE
|
||||
self.T_SEP, tToken[1], tTemp, None, self.A_CENTRE
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tTemp,
|
||||
None,
|
||||
self.A_NONE
|
||||
tToken[0], tToken[1], tTemp, None, self.A_NONE
|
||||
)
|
||||
|
||||
# For title page and partitions, we need to centre all text.
|
||||
# For partition, we also add a page break before, and for
|
||||
# both types we always add a page break after the content.
|
||||
# We also swap header level 1 with a title type instead.
|
||||
# For title page we use a different title class, and we will set
|
||||
# an automatic page break before (i.e. added if needed). For
|
||||
# partitions we always need a page break before.
|
||||
if self.isTitle or self.isPart:
|
||||
for n, tToken in enumerate(self.theTokens):
|
||||
aStyle = tToken[4]
|
||||
if n == 0:
|
||||
if self.isTitle:
|
||||
aStyle |= self.A_PBB_AUT
|
||||
else:
|
||||
aStyle |= self.A_PBB
|
||||
|
||||
if tToken[0] == self.T_HEAD1:
|
||||
if self.isTitle:
|
||||
self.theTokens[n] = (
|
||||
self.T_TITLE,
|
||||
tToken[1],
|
||||
tToken[2],
|
||||
tToken[3],
|
||||
self.A_PBB_NO | self.A_CENTRE
|
||||
self.T_TITLE, tToken[1], tToken[2], tToken[3], aStyle
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tToken[2],
|
||||
tToken[3],
|
||||
self.A_PBB | self.A_CENTRE
|
||||
tToken[0], tToken[1], tToken[2], tToken[3], aStyle
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tToken[2],
|
||||
tToken[3],
|
||||
self.A_CENTRE
|
||||
)
|
||||
|
||||
# Add a page break after the last entry
|
||||
n = len(self.theTokens) - 1
|
||||
if n >= 0:
|
||||
tToken = self.theTokens[n]
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tToken[2],
|
||||
tToken[3],
|
||||
tToken[4] | self.A_PBA
|
||||
if len(self.theTokens) > 0:
|
||||
tToken = self.theTokens[-1]
|
||||
self.theTokens[-1] = (
|
||||
tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] | self.A_PBA
|
||||
)
|
||||
|
||||
# A single page is always left-aligned and starts on a fresh
|
||||
# page, unless it's empty.
|
||||
if self.isPage:
|
||||
for n, tToken in enumerate(self.theTokens):
|
||||
if n == 0:
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tToken[2],
|
||||
tToken[3],
|
||||
self.A_LEFT | self.A_PBB
|
||||
)
|
||||
else:
|
||||
self.theTokens[n] = (
|
||||
tToken[0],
|
||||
tToken[1],
|
||||
tToken[2],
|
||||
tToken[3],
|
||||
self.A_LEFT
|
||||
)
|
||||
# A single page always starts on a fresh page, unless it's empty.
|
||||
if self.isPage and len(self.theTokens) > 0:
|
||||
tToken = self.theTokens[0]
|
||||
self.theTokens[0] = (
|
||||
tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] | self.A_PBB
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def saveRawMarkdown(self, savePath):
|
||||
"""Save the data to a plain text file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
for nwdPage in self.theMarkdown:
|
||||
outFile.write(nwdPage)
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -678,11 +706,12 @@ class Tokenizer():
|
||||
theTitle = theTitle.replace(r"%sc%", str(self.numChScene))
|
||||
theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene))
|
||||
if r"%chw%" in theTitle:
|
||||
theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter, "en"))
|
||||
theTitle = theTitle.replace(r"%chw%", self._localLookup(self.numChapter))
|
||||
if r"%chi%" in theTitle:
|
||||
theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True))
|
||||
if r"%chI%" in theTitle:
|
||||
theTitle = theTitle.replace(r"%chI%", numberToRoman(self.numChapter, False))
|
||||
return theTitle
|
||||
|
||||
return theTitle[:1].upper() + theTitle[1:]
|
||||
|
||||
# END Class Tokenizer
|
||||
|
||||