Compare commits

...

96 commits

Author SHA1 Message Date
Jacki 50b9fd4980
Implement filter for characters
Signed-off-by: Jacki <jacki@thejackimonster.de>
2023-12-17 15:01:43 +01:00
TheJackiMonster 5a109250bb
========== manuskript-0.16.1 ==========
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-14 14:21:49 +01:00
TheJackiMonster 8beea301ec
Fix path inconsistencies
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-12 16:26:31 +01:00
TheJackiMonster f274cd489f
Use text mode writing files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-12 16:22:46 +01:00
TheJackiMonster 41e59d71c1
Fix newline changes to read universally
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-12 16:18:43 +01:00
TheJackiMonster 98d6eb4975
Remove usage of hardcoded path separators
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-12 15:53:37 +01:00
TheJackiMonster 530352c78b
========== manuskript-0.16.0 ==========
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 19:42:24 +01:00
TheJackiMonster 82fe3262af
Prevent initial crash by changes in spellchecking
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 19:36:37 +01:00
TheJackiMonster 3707d9e0ee
Update .ts files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 18:59:24 +01:00
Hosted Weblate d0e4c81132
Merge branch 'origin/develop' into Weblate. 2023-12-07 18:54:27 +01:00
TheJackiMonster 4d79bef20c
Update ui and translation
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 18:53:14 +01:00
TheJackiMonster a7abc68f8e
Initialize textColor with None
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 16:08:44 +01:00
Tobias Frisch 3d4eef2b49
Merge pull request #1214 from TheShadowblast123/develop
Update spellchecker.py And Fixed crashing
2023-12-07 16:06:03 +01:00
TheJackiMonster 1fd8762f5a
Fix crash #1218
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 16:00:27 +01:00
TheJackiMonster d93e8eb544
Merge branch 'develop' of github.com:olivierkes/manuskript into develop 2023-12-07 15:41:25 +01:00
TheJackiMonster 79ba7b59ed
Set templateIndex to none when marked as deleted
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 15:41:18 +01:00
Tobias Frisch 892f8f0592
Merge pull request #1226 from drmousse/develop
Crash on spellcheck context menu #1224
2023-12-07 15:35:59 +01:00
TheJackiMonster 14392909c3
Fix word count for template selection (fix #1163)
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-12-07 15:34:01 +01:00
Tobias Frisch 5f3933bde2
Merge pull request #1198 from Niwreg/develop
added extra check for the filebase dir not to start with an .
2023-12-07 15:07:14 +01:00
Tobias Frisch cf4ff9f35a
Merge pull request #1196 from Dreaded-Gnu/cross-platform-fix
Fix inconsistent newline handling across operating systems
2023-12-07 15:04:25 +01:00
phlostically b963186071
Translated using Weblate (Esperanto)
Currently translated at 93.7% (709 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/eo/
2023-11-09 18:37:09 +01:00
Eryk Michalak 1c7b246e80
Translated using Weblate (Polish)
Currently translated at 98.6% (746 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pl/
2023-10-01 13:02:56 +02:00
mousse 56dac84932 issue 1224: contextmenu on unknown word in context of pyspellchecker and symspellpy 2023-09-20 16:24:55 +02:00
Shadow 161290686b Update corkDelegate.py 2023-09-18 14:28:30 -07:00
Shadow 7d31628977 Resolved critical error that crashes
textColor isn't always set to a value and whenever it was used as an empty variable the program would crash. I'm uncertain as to whether or not this solution is appropriate, but this is one way to handle it and maintain functionality. Another would be setting textColor to a default value, but this default would have to depend on the theme set by the user.
2023-09-18 14:11:30 -07:00
Mickas Jensen 919df53b5f
Translated using Weblate (Danish)
Currently translated at 11.1% (84 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/da/
2023-09-18 11:00:49 +02:00
Shadow fbab930c44 Update spellchecker.py
Corrected issue where words within certain quotations would show up as incorrectly spelled (ex: 'I'm fine') would indicate both words are misspelled
2023-09-12 18:57:03 -07:00
Tobias Frisch 87da52efd4
Merge pull request #1212 from sagev9000/skip-comments-fix
Fix regex for ignoring multi-line comments.
2023-09-05 14:59:16 +02:00
Sage Vaillancourt d149ae45e7 Better regex dot config 2023-09-04 20:24:41 -04:00
Sage Vaillancourt 18089ae44f Fix regex for ignoring multi-line comments. 2023-09-03 20:33:25 -04:00
Павел Протасов 11294a5914
Translated using Weblate (Russian)
Currently translated at 100.0% (756 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/ru/
2023-08-28 18:10:58 +02:00
Tobias Frisch dccfa85d1f
Merge pull request #1205 from sagev9000/skip-comments-in-word-count
Skip comments in word count
2023-08-25 23:06:48 +02:00
Sage Vaillancourt f25b47149b Use regex to remove comments before counting 2023-08-25 14:46:45 -04:00
Sage Vaillancourt 6f6abc6f65 I think this reads slightly cleaner. 2023-08-19 21:38:30 -04:00
Sage Vaillancourt 80e90b5ddc Skip comments in word count. 2023-08-19 21:36:13 -04:00
Gerwin van der Kamp 8a5dd11075 added extra check for the filebase dir not to start with an . 2023-08-12 02:39:48 +02:00
Christian Freitag e267699667 - Adjusted open calls to use \n as newline for all platforms instead of guessing it 2023-08-07 13:09:55 +02:00
Tobias Frisch 6ee7e881be
Merge pull request #1195 from Dreaded-Gnu/manuskriptw-startup-fix
Fix manuskriptw startup error
2023-08-07 12:02:53 +02:00
Christian Freitag 42245d38c3 - Added catch of RuntimeError triggered on when sys.stderr is none 2023-08-07 10:04:26 +02:00
Aurélio Julio Arantes Silveira cc0fc29b28
Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.9% (748 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt_BR/
2023-06-29 04:45:09 +02:00
TheJackiMonster 1933d53c1c
Fix match and case syntax for python 3.9 and below
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-06-15 22:38:05 +02:00
TheJackiMonster 13486ad083
Update translation files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-06-15 22:31:33 +02:00
Tobias Frisch 4ebbf5efd6
Merge pull request #1165 from alfar/develop
Simple history back/forward navigation
2023-06-15 22:25:58 +02:00
Tymofii Lytvynenko 40a9cabece
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (756 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/uk/
2023-06-12 01:51:29 +02:00
Павел Протасов 14dd845e4c
Translated using Weblate (Russian)
Currently translated at 100.0% (756 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/ru/
2023-06-04 20:50:44 +02:00
Herb Huang e36c31f3a9
Translated using Weblate (Chinese (Simplified))
Currently translated at 91.1% (689 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/zh_Hans/
2023-05-30 13:10:04 +02:00
Herb Huang 15b9d6f0e1
Translated using Weblate (Chinese (Simplified))
Currently translated at 89.1% (674 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/zh_Hans/
2023-05-30 01:52:18 +02:00
Павел Протасов 0ce2ae61a0
Translated using Weblate (Russian)
Currently translated at 97.0% (734 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/ru/
2023-05-25 19:50:09 +02:00
Arne Sostack b05377b417 Implemented history back and forward navigation 2023-05-14 11:59:07 +02:00
Tymofii Lytvynenko 8932adf635
Translated using Weblate (Ukrainian)
Currently translated at 99.3% (751 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/uk/
2023-05-14 08:49:37 +02:00
phlostically 549a089eb0
Translated using Weblate (Esperanto)
Currently translated at 90.7% (686 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/eo/
2023-04-24 16:48:42 +02:00
albanobattistella 4abc6cacfd
Translated using Weblate (Italian)
Currently translated at 99.6% (753 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/it/
2023-04-11 10:52:44 +02:00
ruine e8199931d9
Translated using Weblate (Japanese)
Currently translated at 91.6% (693 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/ja/
2023-04-04 04:44:58 +02:00
albanobattistella 69fec580cb
Translated using Weblate (Italian)
Currently translated at 99.4% (752 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/it/
2023-04-02 21:41:24 +02:00
TheJackiMonster 8e298c0788
Merge branch 'master' into develop 2023-03-29 22:01:26 +02:00
TheJackiMonster 2f93c2f6fe
Fix missing python-markdown in Windows builds
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-29 22:00:47 +02:00
Milo Ivir e84056ecbe
Translated using Weblate (Croatian)
Currently translated at 100.0% (756 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/hr/
2023-03-27 21:41:58 +02:00
Milo Ivir 8ffdeefbdf
Translated using Weblate (Croatian)
Currently translated at 99.6% (753 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/hr/
2023-03-25 15:41:11 +01:00
gallegonovato d8fbea13ba
Translated using Weblate (Spanish)
Currently translated at 100.0% (756 of 756 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/es/
2023-03-20 16:02:05 +01:00
Pedro Albuquerque 5f9335cc91
Translated using Weblate (Portuguese (Portugal))
Currently translated at 100.0% (757 of 757 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt_PT/
2023-03-20 08:34:06 +01:00
TheJackiMonster cdad1aa2d4
Update translation files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-20 04:59:01 +01:00
Tobias Frisch 131bdc5505
Merge pull request #1144 from tntscreed/develop
Bulk Info Manager bug fix and style changes.
2023-03-20 04:55:27 +01:00
TheJackiMonster 7de81a96f3
Update QM files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-20 04:54:22 +01:00
Hosted Weblate 1dc709fa20
Merge branch 'origin/develop' into Weblate. 2023-03-20 04:49:00 +01:00
TheJackiMonster fe213894cb
Add status of pytest workflow to README.md
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-20 04:44:49 +01:00
gallegonovato 60ea5f9e14
Translated using Weblate (Spanish)
Currently translated at 100.0% (754 of 754 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/es/
2023-03-17 20:38:04 +01:00
Ettore Atalan 7d4151328c
Translated using Weblate (German)
Currently translated at 99.4% (750 of 754 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/de/
2023-03-17 20:38:03 +01:00
tntscreed 863e8df483 Bug and style fixes.
- Made small changes to better fit the style of the project.

- Fixed a bug that would result in a crash when details were applied with the bulk info manager.

- Improved the Bulk Manager UI by adding the list-add and list-remove icons, to better fit it to Manuskript's style. Also added tooltips.
2023-03-16 16:06:48 +01:00
tntscreed 9b372eac73 Added remaining QMessageBox text to self.tr method
I'm assuming it was left out on accident. Sorry, but I can't update the translation files on my own.
2023-03-16 13:19:19 +01:00
tntscreed 389cb19f55 Merge branch 'origin_develop' into develop 2023-03-16 13:03:28 +01:00
Pedro Albuquerque 1164c3c988
Translated using Weblate (Portuguese (Portugal))
Currently translated at 100.0% (755 of 755 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt_PT/
2023-03-16 07:47:49 +01:00
TheJackiMonster 3faaf1187a
Update qm files via makefile
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-15 21:46:08 +01:00
TheJackiMonster 5e068c0603
Merge branch 'translations' into develop
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-15 21:44:10 +01:00
TheJackiMonster 7d76bd6726
Update QM files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-15 21:12:52 +01:00
TheJackiMonster b1e23fd9fc
Update translation files
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-15 21:11:41 +01:00
TheJackiMonster e62432307f
Make selection adjustment permanent and improve consistency
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
2023-03-15 21:06:31 +01:00
ssantos 5f8569718d
Translated using Weblate (Portuguese)
Currently translated at 100.0% (787 of 787 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt/
2023-03-12 22:37:31 +01:00
Pedro Albuquerque 66f6aecf1c
Translated using Weblate (Portuguese (Portugal))
Currently translated at 100.0% (788 of 788 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt_PT/
2023-03-12 22:37:31 +01:00
Mounim 763d9284f0
Translated using Weblate (Dutch)
Currently translated at 99.8% (786 of 787 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/nl/
2023-03-12 22:37:30 +01:00
Pedro Albuquerque 91b22292cc
Translated using Weblate (Portuguese (Portugal))
Currently translated at 100.0% (788 of 788 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt_PT/
2023-03-10 17:37:10 +01:00
tntscreed 92b1e0b648 Made small improvements to the bulk detail manager.
-Cleaned the code up a bit.

-Added warning message before discarding unapplied entries in the bulk manager. The bulk manager now doesn't automatically close when selecting a single (or no) character, but asks for the user's consent.
2023-03-09 16:24:35 +01:00
tntscreed 1fd45ba1d2 Fixed a typo.
The warning message-box for when the bulk manager was empty has been fixed.
2023-03-07 21:56:30 +01:00
tntscreed 2ead298b00 Removed the absolute path left in by pyuic.
Sorry about that. It didn't exactly doxx me, but it was weird.
2023-03-07 21:19:29 +01:00
tntscreed 953ce4bd15 Multi-Deletion of Characters + Confirmation Dialog
-Since multi-selection of characters is now possible, it's more intuitive that all selected characters should be deleted.

-Added a confirmation dialog before character-deletion? Why the heck  was this not a thing before? It was way too easy to accidentally delete characters, with no way to restore them without external source-control or backups.
2023-03-07 20:56:54 +01:00
tntscreed 06f8ab3519 The bulk info managers is now completely functional.
Also, a change has been made to the character tree view. Theoretically it shouldn't have any functional significance, but "addCharacterInfo" is used for adding info to only one character, so it makes sense to remove the old multi-selection info-adding functionality, even if it wouldn't ever be accessible.
2023-03-07 20:27:16 +01:00
tntscreed 66df68af69 Made the code a little nicer, added addition and deletion
The bulk manager has been made partially functional.
2023-03-07 18:14:01 +01:00
tntscreed b5dfee59d7 Created a (not yet functional) bulk manager widget.
-Some variables had to be declared in the mainWindow.py __init__ function.

-Added functions for creating the bulkPersoInfoManager widget as a tab when multiple characters are selected. All other tabs get stored and deleted, and restored after only one character is selected.

-Started work on functions that create the intended behaviours of the widget.
2023-03-06 21:43:31 +01:00
Samuel Marques 5d0a9d3019
Translated using Weblate (Portuguese)
Currently translated at 92.1% (725 of 787 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt/
2023-03-06 12:39:53 +01:00
albanobattistella 77f7018cb2
Translated using Weblate (Italian)
Currently translated at 100.0% (787 of 787 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/it/
2023-03-06 12:39:53 +01:00
Samuel Marques fc7a716c76
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (787 of 787 strings)

Translation: Manuskript/Translations
Translate-URL: https://hosted.weblate.org/projects/manuskript/translations/pt_BR/
2023-03-06 12:39:52 +01:00
tntscreed 25b64ad971 Merge remote-tracking branch 'origin/develop' into develop 2023-03-05 17:49:22 +01:00
tntscreed d55e88599d Made multi-selection of characters a bit more intuitive 2023-03-05 17:40:05 +01:00
tntscreed 49fb32deab Made selection a bit more intuitive 2023-03-05 15:04:56 +01:00
tntscreed fc86ed5df6 Started work on character multi-selection features.
-Added currentCharacters method, a list counterpart of currentCharacter.

-Replaced the connection for currentItemChanged with itemSelectionChanged. This is to accommodate for the bulk-changes when multiple characters are selected. The currentItemChanged method is now not called with a connection, but from the handleCharacterSelectionChanged method. The latter is called with the itemSelectionChanged connection.

-If no valid characters are selected, the tabPersos widget is disabled. This obviously breaks the functionality of bulk-adding info to characters. This will have to be fixed by only disabling the parts of the tabPersos widget that should not be affected with multiple selection operations.
2023-03-05 14:14:58 +01:00
tntscreed eaebfa9dfa Merge remote-tracking branch 'origin/develop' into develop 2023-03-05 01:10:48 +01:00
tntscreed a046656acf Made bulk-adding "Detailed info" to characters possible
-Changed the selection mode to extended selection on the lstCharacters tree-view. This will not affect anything else other than the "detailed info" rows. Every other change to a character's descriptions, motivations and such only affect the last selected one.

-Made a method to get all the IDs of the selected characters.

-Added a character-info dialog UI. Originally, adding information worked by adding a placeholder and then changing it. Users never want to just add a placeholder without initialising the values. And the bulk-adding only works this way.
2023-03-05 00:58:25 +01:00
104 changed files with 15482 additions and 19253 deletions

View file

@ -38,7 +38,7 @@ jobs:
- name: Install Python build dependencies
run: |
python -m pip install --upgrade pip
pip install pyqt5==5.15.7 lxml pytest pytest-faulthandler language_tool_python symspellpy pyspellchecker pyenchant
pip install pyqt5==5.15.7 lxml pytest pytest-faulthandler markdown language_tool_python symspellpy pyspellchecker pyenchant
pip install pyinstaller
- name: pyinstaller build
run: |

1
.gitignore vendored
View file

@ -6,6 +6,7 @@
*.msk
*.nja
*.pyc
*.glade~
.cache
.directory
.idea

View file

@ -1,6 +1,51 @@
# Changelog
## [0.15.0](https://github.com/olivierkes/manuskript/tree/0.14.0) (2023-03-04)
## [0.16.1](https://github.com/olivierkes/manuskript/tree/0.16.1) (2023-12-14)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.16.0...0.16.1)
**Fixed bugs:**
- Characters, Plots and Worlds are greyed out [\#1249](https://github.com/olivierkes/manuskript/issues/1249)
- Missing Pages with Version 0.16.0 [\#1248](https://github.com/olivierkes/manuskript/issues/1248)
- Software crashing every time I open [\#1247](https://github.com/olivierkes/manuskript/issues/1247)
## [0.16.0](https://github.com/olivierkes/manuskript/tree/0.16.0) (2023-12-07)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.15.0...0.16.0)
**Fixed bugs:**
- my manuskript is not launching i don't know why [\#1207](https://github.com/olivierkes/manuskript/issues/1207)
- cannot download to ubuntu [\#1199](https://github.com/olivierkes/manuskript/issues/1199)
- LanguageTool crashes app [\#1143](https://github.com/olivierkes/manuskript/issues/1143)
- can't load LanguageTool [\#1142](https://github.com/olivierkes/manuskript/issues/1142)
- Crash on spellcheck context menu [\#1224](https://github.com/olivierkes/manuskript/issues/1224)
- Trouble launching 0.15 in Windows 10 Home [\#1222](https://github.com/olivierkes/manuskript/issues/1222)
- Crash on Search [\#1218](https://github.com/olivierkes/manuskript/issues/1218)
- Crash on Mac not on windows with same project [\#1169](https://github.com/olivierkes/manuskript/issues/1169)
- Fedora 38 application crashes when cycling through the home options at first launch [\#1163](https://github.com/olivierkes/manuskript/issues/1163)
- RuntimeError with manuskriptw.exe \(sys.stderr is None\) [\#1138](https://github.com/olivierkes/manuskript/issues/1138)
**Closed issues:**
- Allow chapter/scene cards to be scrolled [\#1170](https://github.com/olivierkes/manuskript/issues/1170)
- Kubuntu 20.40.6 getting undefined symbol: krb5\_ser\_context\_init, version krb5\_3\_MIT [\#1155](https://github.com/olivierkes/manuskript/issues/1155)
**Merged pull requests:**
- Bulk Info Manager bug fix and style changes. [\#1144](https://github.com/olivierkes/manuskript/pull/1144) ([tntscreed](https://github.com/tntscreed))
- I added a feature to add "detailed info" entries to multiple characters simultaneously via a dialog box. [\#1137](https://github.com/olivierkes/manuskript/pull/1137) ([tntscreed](https://github.com/tntscreed))
- Crash on spellcheck context menu \#1224 [\#1226](https://github.com/olivierkes/manuskript/pull/1226) ([drmousse](https://github.com/drmousse))
- Update spellchecker.py And Fixed crashing [\#1214](https://github.com/olivierkes/manuskript/pull/1214) ([TheShadowblast123](https://github.com/TheShadowblast123))
- Fix regex for ignoring multi-line comments. [\#1212](https://github.com/olivierkes/manuskript/pull/1212) ([sagev9000](https://github.com/sagev9000))
- Skip comments in word count [\#1205](https://github.com/olivierkes/manuskript/pull/1205) ([sagev9000](https://github.com/sagev9000))
- added extra check for the filebase dir not to start with an . [\#1198](https://github.com/olivierkes/manuskript/pull/1198) ([Niwreg](https://github.com/Niwreg))
- Fix inconsistent newline handling across operating systems [\#1196](https://github.com/olivierkes/manuskript/pull/1196) ([Dreaded-Gnu](https://github.com/Dreaded-Gnu))
- Fix manuskriptw startup error [\#1195](https://github.com/olivierkes/manuskript/pull/1195) ([Dreaded-Gnu](https://github.com/Dreaded-Gnu))
- Simple history back/forward navigation [\#1165](https://github.com/olivierkes/manuskript/pull/1165) ([alfar](https://github.com/alfar))
## [0.15.0](https://github.com/olivierkes/manuskript/tree/0.15.0) (2023-03-04)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.14.0...0.15.0)
@ -10,42 +55,42 @@
**Fixed bugs:**
- 0.14 won't open [\#1105](https://github.com/olivierkes/manuskript/issues/1105)
- click on "Plan" makes Manuskript 0.14.0 crash [\#1094](https://github.com/olivierkes/manuskript/issues/1094)
- mouse over goes blank in fiction [\#1093](https://github.com/olivierkes/manuskript/issues/1093)
- Crashes on hitting "+" while editing plot step summary [\#1081](https://github.com/olivierkes/manuskript/issues/1081)
- Type error on launch [\#1043](https://github.com/olivierkes/manuskript/issues/1043)
- Plot-step summary disappearing and not being saved! [\#1008](https://github.com/olivierkes/manuskript/issues/1008)
- Don't open msk from command line [\#993](https://github.com/olivierkes/manuskript/issues/993)
- Metadata: References [\#756](https://github.com/olivierkes/manuskript/issues/756)
- Context/right click Menu slow and only in english [\#487](https://github.com/olivierkes/manuskript/issues/487)
- Translations breaks shortcuts [\#1135](https://github.com/olivierkes/manuskript/issues/1135)
- Missing visual indicator for collapsables like metadata fields [\#1132](https://github.com/olivierkes/manuskript/issues/1132)
- New search bug [\#1125](https://github.com/olivierkes/manuskript/issues/1125)
- One-time glitch: dragged "World" entry to top of list and it disappeared [\#1122](https://github.com/olivierkes/manuskript/issues/1122)
- Loading Error with fileno [\#1121](https://github.com/olivierkes/manuskript/issues/1121)
- 0.14 won't open [\#1105](https://github.com/olivierkes/manuskript/issues/1105)
- Search no longer works [\#1095](https://github.com/olivierkes/manuskript/issues/1095)
- click on "Plan" makes Manuskript 0.14.0 crash [\#1094](https://github.com/olivierkes/manuskript/issues/1094)
- mouse over goes blank in fiction [\#1093](https://github.com/olivierkes/manuskript/issues/1093)
- Crash when opening a plot in the cheat sheet containing deleted characters [\#1082](https://github.com/olivierkes/manuskript/issues/1082)
- Crashes on hitting "+" while editing plot step summary [\#1081](https://github.com/olivierkes/manuskript/issues/1081)
- CTD Crash To Desktop after "inserting link" [\#1071](https://github.com/olivierkes/manuskript/issues/1071)
- Spellcheck intermittantly looses location in editor [\#1065](https://github.com/olivierkes/manuskript/issues/1065)
- Translation - missing lines in .ts file etc. issues [\#1052](https://github.com/olivierkes/manuskript/issues/1052)
- Debian installer zst compression error [\#1047](https://github.com/olivierkes/manuskript/issues/1047)
- manuskriptw.exe can not be executed due to unhandled exception "NullWriter" [\#1044](https://github.com/olivierkes/manuskript/issues/1044)
- Type error on launch [\#1043](https://github.com/olivierkes/manuskript/issues/1043)
- Crash when attempting to add an inexistent reference [\#1042](https://github.com/olivierkes/manuskript/issues/1042)
- Setting the language to German deletes shortcut-functions like "Ctrl+S" [\#1012](https://github.com/olivierkes/manuskript/issues/1012)
- Plot-step summary disappearing and not being saved! [\#1008](https://github.com/olivierkes/manuskript/issues/1008)
- Don't open msk from command line [\#993](https://github.com/olivierkes/manuskript/issues/993)
- Open in new tab doesn't work from tree [\#919](https://github.com/olivierkes/manuskript/issues/919)
- Metadata: References [\#756](https://github.com/olivierkes/manuskript/issues/756)
- \[Bug\] Next Button in Characters Pane [\#584](https://github.com/olivierkes/manuskript/issues/584)
- Context/right click Menu slow and only in english [\#487](https://github.com/olivierkes/manuskript/issues/487)
- Top level World items with sub-items do not visually indicate sub-items exist [\#305](https://github.com/olivierkes/manuskript/issues/305)
- saving error [\#274](https://github.com/olivierkes/manuskript/issues/274)
- Crashing when creating a new project - locale C [\#130](https://github.com/olivierkes/manuskript/issues/130)
**Closed issues:**
- Update files about languages [\#1133](https://github.com/olivierkes/manuskript/issues/1133)
- Adding a clearer discription to contributing guidlines [\#1115](https://github.com/olivierkes/manuskript/issues/1115)
- I can't click boxes to edit them [\#1104](https://github.com/olivierkes/manuskript/issues/1104)
- Minor Python error during Installation - Linux Mint 20.3 [\#1097](https://github.com/olivierkes/manuskript/issues/1097)
- Package as App Bundle for macOS [\#567](https://github.com/olivierkes/manuskript/issues/567)
- Update files about languages [\#1133](https://github.com/olivierkes/manuskript/issues/1133)
**Merged pull requests:**
@ -335,7 +380,7 @@
- Fix Linux Travis CI build error - pyenv: version `3.6.3' not installed [\#610](https://github.com/olivierkes/manuskript/pull/610) ([gedakc](https://github.com/gedakc))
- Fix crash when setting word Goal on new Text \(scene\) in Outline pane [\#609](https://github.com/olivierkes/manuskript/pull/609) ([gedakc](https://github.com/gedakc))
- Spelling: Manuscript, could not, process, … No content [\#588](https://github.com/olivierkes/manuskript/pull/588) ([comradekingu](https://github.com/comradekingu))
- fix issue \#468 'unit' is reset [\#587](https://github.com/olivierkes/manuskript/pull/587) ([NocturnalFred](https://github.com/NocturnalFred))
- fix issue \#468 'unit' is reset [\#587](https://github.com/olivierkes/manuskript/pull/587) ([SOLIDFred](https://github.com/SOLIDFred))
- Fix pandoc export crashes is project title is empty [\#585](https://github.com/olivierkes/manuskript/pull/585) ([gedakc](https://github.com/gedakc))
- Track dirty state and have the UI behave accordingly [\#583](https://github.com/olivierkes/manuskript/pull/583) ([worstje](https://github.com/worstje))
- Fix crash if invalid character is inserted into the text. [\#578](https://github.com/olivierkes/manuskript/pull/578) ([kakaroto](https://github.com/kakaroto))

View file

@ -3,6 +3,7 @@
[Manuskript](https://www.theologeek.ch/manuskript) is an open-source
tool for writers.
[![pytest](https://github.com/olivierkes/manuskript/workflows/Pytest%20Run%20%28Linux%29/badge.svg)](https://github.com/olivierkes/manuskript/actions/workflows/pytest.yml)
[![manuskript](https://snapcraft.io/manuskript/badge.svg)](https://snapcraft.io/manuskript)
[![translations](https://hosted.weblate.org/widgets/manuskript/-/translations/svg-badge.svg)](https://hosted.weblate.org/projects/manuskript/translations)

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,8 @@ from manuskript.exporter.manuskript.markdown import markdown
from manuskript.exporter.manuskript.plainText import plainText
from manuskript.functions import appPath, safeTranslate
import os
class manuskriptExporter(basicExporter):
@ -19,7 +21,7 @@ class manuskriptExporter(basicExporter):
HTML(),
basicFormat("OPML", icon="text-x-opml+xml")
]
icon = appPath("icons/Manuskript/icon-256px.png")
icon = appPath(os.path.join("icons", "Manuskript", "icon-256px.png"))
@classmethod
def isValid(cls):

View file

@ -96,7 +96,7 @@ class plainText(basicFormat):
LOGGER.error("No content. Nothing saved.")
return
with open(filename, "w", encoding='utf8') as f:
with open(filename, "wt", encoding="utf8", newline="\n") as f:
f.write(content)
def preview(self, settingsWidget, previewWidget):
@ -217,4 +217,3 @@ class plainText(basicFormat):
content += "\n"
return content

View file

@ -30,7 +30,7 @@ def safeTranslate(qApp, group, text):
return text
def wordCount(text):
return len(re.findall(r"\S+", text))
return len(re.findall(r"\S+", re.sub(r"(<!--).+?(-->)", "", text, flags=re.DOTALL)))
def charCount(text, use_spaces = True):
@ -252,7 +252,7 @@ def colorifyPixmap(pixmap, color):
def appPath(suffix=None):
p = os.path.realpath(os.path.join(os.path.split(__file__)[0], "../.."))
p = os.path.realpath(os.path.join(os.path.split(__file__)[0], os.path.join("..", "..")))
if suffix:
p = os.path.join(p, suffix)
return p
@ -305,7 +305,7 @@ def findBackground(filename):
"""
Returns the full path to a background file of name filename within resources folders.
"""
return findFirstFile(re.escape(filename), "resources/backgrounds")
return findFirstFile(re.escape(filename), os.path.join("resources", "backgrounds"))
def findFirstFile(regex, path="resources"):
@ -511,7 +511,7 @@ def getManuskriptPath(follow_symlinks=True):
path = os.path.abspath(sys.executable)
else:
import inspect
path = inspect.getabsfile(getManuskriptPath) + "/../.."
path = os.path.join(inspect.getabsfile(getManuskriptPath), "..", "..")
if follow_symlinks:
path = os.path.realpath(path)
return os.path.dirname(path)

View file

@ -0,0 +1,56 @@
from manuskript.functions.history.NavigatedEvent import NavigatedEvent
from manuskript.functions.history.Signal import Signal
class History():
def __init__(self) -> None:
self._entries = []
self._position = 0
self.navigated = Signal()
self._navigating = False
def next(self, entry):
if self._navigating:
return
while self._position < len(self._entries) - 1:
self._entries.pop()
self._entries.append(entry)
self._position = len(self._entries) - 1
self._navigating = True
self.navigated.fire(NavigatedEvent(self._position, len(self._entries), entry))
self._navigating = False
def replace(self, entry):
if self._navigating:
return
while self._position < len(self._entries):
self._entries.pop()
self._entries.append(entry)
self._position = len(self._entries) - 1
self._navigating = True
self.navigated.fire(NavigatedEvent(self._position, len(self._entries), entry))
self._navigating = False
def forward(self):
if self._position < len(self._entries) - 1:
self._position += 1
self._navigating = True
self.navigated.fire(NavigatedEvent(self._position, len(self._entries), self._entries[self._position]))
self._navigating = False
def back(self):
if self._position > 0:
self._position -= 1
self._navigating = True
self.navigated.fire(NavigatedEvent(self._position, len(self._entries), self._entries[self._position]))
self._navigating = False
def reset(self):
self._entries.clear()
self._position = 0
self.navigated.fire(NavigatedEvent(self._position, len(self._entries), None))

View file

@ -0,0 +1,5 @@
class NavigatedEvent():
def __init__(self, position, count, entry) -> None:
self.position = position
self.count = count
self.entry = entry

View file

@ -0,0 +1,23 @@
class Signal():
def __init__(self) -> None:
self._methods = []
def connect(self, func):
self._methods.append(func)
def disconnect(self, func):
try:
self._methods.remove(func)
except ValueError:
raise TypeError
def disconnect(self):
if len(self._methods) == 0:
raise TypeError
self._methods.pop()
def fire(self, data):
for m in self._methods:
m(data)

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import os, gzip, json, glob, re
import os, gzip, json, glob, re, string
from PyQt5.QtCore import QLocale
from collections import OrderedDict
from manuskript.functions import writablePath
@ -23,7 +23,7 @@ try:
if distutils.version.LooseVersion(symspellpy.__version__) < SYMSPELLPY_MIN_VERSION:
symspellpy = None
except ImportError:
symspellpy = None
@ -148,7 +148,7 @@ class BasicDictionary:
self._customDict = set()
customPath = self.getCustomDictionaryPath()
try:
with gzip.open(customPath, "rt", encoding='utf-8') as f:
with gzip.open(customPath, 'rt', encoding='utf-8') as f:
self._customDict = set(json.loads(f.read()))
for word in self._customDict:
self._dict.create_dictionary_entry(word, self.CUSTOM_COUNT)
@ -187,6 +187,7 @@ class BasicDictionary:
def checkText(self, text):
# Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
WORDS = r'(?iu)((?:[^_\W]|\')+)[^A-Za-z0-9\']'
# (?iu) means case insensitive and Unicode
# ((?:[^_\W]|\')+) means words exclude underscores but include apostrophes
# [^A-Za-z0-9\'] used with above hack to prevent spellcheck while typing word
@ -197,8 +198,30 @@ class BasicDictionary:
for word_object in re.finditer(WORDS, text):
word = word_object.group(1)
mispelled = self.isMisspelled(word)
if mispelled == False:
continue
punctuation = string.punctuation.replace('-', '')
FALSE_POSITIVE = r'^[^\w]|([^{}])$'.format(punctuation)
#inorder to prevent apostrophes causing false positives and keep the same functionality otherwise,
#check that the word doesn't have any additional punctuation on it.
if re.match(FALSE_POSITIVE, word):
# ^[^\w] checks that it doesn't start with a word character
# ([\p{P}'])$ checks it doesn't end with punctuation characters
apostrophe_WORDS = r'(?iu)\b(?<=[\s\'"(])((?:[a-zA-Z]|\')+)(?=\b)'
# \b(?<=[\s\'"(]) looks for nonword characters and starts grouping after
# (?=\b) looks for the word boundary
# ((?:[a-zA-Z]|\')+) greedily matches for letters and apostrophes
temp = re.match(apostrophe_WORDS, word)
mispelled = self.isMisspelled(temp.group(1)) if temp else False
if (mispelled and not self.isCustomWord(word)):
if (self.isMisspelled(word) and not self.isCustomWord(word)):
matches.append(BasicMatch(
word_object.start(1), word_object.end(1)
))
@ -250,7 +273,7 @@ class BasicDictionary:
def _saveCustomDict(self):
customPath = self.getCustomDictionaryPath()
with gzip.open(customPath, "wt") as f:
with gzip.open(customPath, "wt", newline="\n") as f:
f.write(json.dumps(list(self._customDict)))
@ -363,7 +386,7 @@ class PySpellcheckerDictionary(BasicDictionary):
def getSuggestions(self, word):
candidates = self._dict.candidates(word)
if word in candidates:
if candidates and word in candidates:
candidates.remove(word)
return candidates
@ -393,7 +416,7 @@ class SymSpellDictionary(BasicDictionary):
if pyspellchecker:
path = os.path.join(pyspellchecker.__path__[0], "resources", "{}.json.gz".format(self.name))
if os.path.exists(path):
with gzip.open(path, "rt", encoding='utf-8') as f:
with gzip.open(path, 'rt', encoding='utf-8') as f:
data = json.loads(f.read())
for key in data:
self._dict.create_dictionary_entry(key, data[key])

View file

@ -46,7 +46,7 @@ class folderImporter(abstractImporter):
fName, fExt = os.path.splitext(f)
if fExt.lower() in ext:
try:
with open(os.path.join(dirpath, f), "r", encoding="utf-8") as fr:
with open(os.path.join(dirpath, f), 'rt', encoding="utf-8") as fr:
content = fr.read()
child = outlineItem(title=fName, _type="md", parent=item)
child._data[Outline.text] = content
@ -122,7 +122,3 @@ class folderImporter(abstractImporter):
self.addSettingsTo(group)
return widget

View file

@ -65,7 +65,7 @@ class markdownImporter(abstractImporter):
if not fromString:
# Read file
with open(filePath, "r", encoding="utf-8") as f:
with open(filePath, 'rt', encoding="utf-8") as f:
txt = f.read()
else:
txt = fromString

View file

@ -56,7 +56,7 @@ def loadProject(project):
# Not a zip
else:
with open(project, "r", encoding="utf-8") as f:
with open(project, 'rt', encoding="utf-8") as f:
version = int(f.read())
LOGGER.info("Loading: %s", project)

View file

@ -388,7 +388,7 @@ def saveProject(zip=None):
filesWithPermissionErrors.append(filename)
else:
try:
with open(filename, "w", encoding='utf8') as f:
with open(filename, "wt", encoding="utf8", newline="\n") as f:
f.write(content)
except PermissionError as e:
LOGGER.error("Cannot open file " + filename + " for writing: " + e.strerror)
@ -423,7 +423,7 @@ def saveProject(zip=None):
# Write the project file's content
try:
with open(project, "w", encoding='utf8') as f:
with open(project, "wt", encoding="utf8", newline="\n") as f:
f.write("1") # Format number
except PermissionError as e:
LOGGER.error("Cannot open file " + project + " for writing: " + e.strerror)
@ -678,6 +678,9 @@ def loadProject(project, zip=None):
# Skip directories that begin with a period
if p[:1] == ".":
continue
#skip if the basedir of the file starts with an .
if os.path.basename(p)[:1] == ".":
continue
for f in filenames:
# Skip filenames that begin with a period
if f[:1] == ".":
@ -689,7 +692,7 @@ def loadProject(project, zip=None):
else:
try:
filename = os.path.join(dirpath, f)
with open(filename, "r", encoding="utf8") as fo:
with open(filename, 'rt', encoding="utf8") as fo:
files[os.path.join(p, f)] = fo.read()
except PermissionError as e:
LOGGER.error("Cannot open file " + filename + ": " + e.strerror)
@ -1127,5 +1130,3 @@ def parseMMDFile(text, asDict=False):
return md, body
else:
return mdd, body

View file

@ -16,7 +16,7 @@ from manuskript.version import getVersion
try:
faulthandler.enable()
except AttributeError:
except (AttributeError, RuntimeError):
print("Faulthandler failed")
import logging
@ -45,7 +45,7 @@ def prepare(arguments, tests=False):
icon = QIcon()
for i in [16, 32, 64, 128, 256, 512]:
icon.addFile(appPath("icons/Manuskript/icon-{}px.png".format(i)))
icon.addFile(appPath(os.path.join("icons", "Manuskript", "icon-{}px.png".format(i))))
qApp.setWindowIcon(icon)
app.setStyle("Fusion")

View file

@ -7,15 +7,16 @@ import re
from PyQt5.Qt import qVersion, PYQT_VERSION_STR
from PyQt5.QtCore import (pyqtSignal, QSignalMapper, QTimer, QSettings, Qt, QPoint,
QRegExp, QUrl, QSize, QModelIndex)
from PyQt5.QtGui import QStandardItemModel, QIcon, QColor
from PyQt5.QtGui import QStandardItemModel, QIcon, QColor, QStandardItem
from PyQt5.QtWidgets import QMainWindow, QHeaderView, qApp, QMenu, QActionGroup, QAction, QStyle, QListWidgetItem, \
QLabel, QDockWidget, QWidget, QMessageBox, QLineEdit
QLabel, QDockWidget, QWidget, QMessageBox, QLineEdit, QTextEdit, QTreeView, QDialog, QTableView
from manuskript import settings
from manuskript.enums import Character, PlotStep, Plot, World, Outline
from manuskript.functions import wordCount, appPath, findWidgetsOfClass, openURL, showInFolder
import manuskript.functions as F
from manuskript import loadSave
from manuskript.functions.history.History import History
from manuskript.logging import getLogFilePath
from manuskript.models.characterModel import characterModel
from manuskript.models import outlineModel
@ -23,6 +24,7 @@ from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.settingsWindow import settingsWindow
from manuskript.ui import style
from manuskript.ui import characterInfoDialog
from manuskript.ui.about import aboutDialog
from manuskript.ui.collapsibleDockWidgets import collapsibleDockWidgets
from manuskript.ui.importers.importer import importerDialog
@ -35,6 +37,7 @@ from manuskript.ui.views.outlineDelegates import outlineCharacterDelegate
from manuskript.ui.views.plotDelegate import plotDelegate
from manuskript.ui.views.MDEditView import MDEditView
from manuskript.ui.statusLabel import statusLabel
from manuskript.ui.bulkInfoManager import Ui_BulkInfoManager
# Spellcheck support
from manuskript.ui.views.textEditView import textEditView
@ -71,6 +74,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# value. In manuskript.main.
self._autoLoadProject = None # Used to load a command line project
self.sessionStartWordCount = 0 # Used to track session targets
self.history = History()
self._previousSelectionEmpty = True
self.readSettings()
@ -102,7 +107,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Main Menu
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuView, self.menuOrganize,
self.menuTools, self.menuHelp, self.actImport,
self.menuNavigate, self.menuTools, self.menuHelp, self.actImport,
self.actCompile, self.actSettings]:
i.setEnabled(False)
@ -156,6 +161,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.actSplitCursor.triggered.connect(self.documentsSplitCursor)
self.actMerge.triggered.connect(self.documentsMerge)
# Main menu:: Navigate
self.actBack.triggered.connect(self.navigateBack)
self.actForward.triggered.connect(self.navigateForward)
# Main Menu:: view
self.generateViewMenu()
self.actModeGroup = QActionGroup(self)
@ -179,6 +188,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# self.loadProject(os.path.join(appPath(), "test_project.zip"))
# Bulk Character Info Management
self.tabsData = self.saveCharacterTabs() # Used for restoring tabsData with loadCharacterTabs() methods.
self.BulkManageUi = None
self.bulkAffectedCharacters = []
self.isPersoBulkModeEnabled = False
def updateDockVisibility(self, restore=False):
"""
Saves the state of the docks visibility. Or if `restore` is True,
@ -248,6 +263,54 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.actDelete,
self.actRename]:
i.setEnabled(tabIsEditor)
tabIndex = self.tabMain.currentIndex()
if tabIndex == self.TabPersos:
selectedCharacters = self.lstCharacters.currentCharacters()
characterSelectionIsEmpty = not any(selectedCharacters)
if characterSelectionIsEmpty:
self.pushHistory(("character", None))
self._previousSelectionEmpty = True
else:
character = selectedCharacters[0]
self.pushHistory(("character", character.ID()))
self._previousSelectionEmpty = False
elif tabIndex == self.TabPlots:
id = self.lstPlots.currentPlotID()
self.pushHistory(("plot", id))
self._previousSelectionEmpty = id is None
elif tabIndex == self.TabWorld:
index = self.mdlWorld.selectedIndex()
if index.isValid():
id = self.mdlWorld.ID(index)
self.pushHistory(("world", id))
self._previousSelectionEmpty = id is not None
else:
self.pushHistory(("world", None))
self._previousSelectionEmpty = True
elif tabIndex == self.TabOutline:
index = self.treeOutlineOutline.selectionModel().currentIndex()
if index.isValid():
id = self.mdlOutline.ID(index)
self.pushHistory(("outline", id))
self._previousSelectionEmpty = id is not None
else:
self.pushHistory(("outline", None))
self._previousSelectionEmpty = False
elif tabIndex == self.TabRedac:
index = self.treeRedacOutline.selectionModel().currentIndex()
if index.isValid():
id = self.mdlOutline.ID(index)
self.pushHistory(("redac", id))
self._previousSelectionEmpty = id is not None
else:
self.pushHistory(("redac", None))
self._previousSelectionEmpty = False
else:
self.pushHistory(("main", self.tabMain.currentIndex()))
self._previousSelectionEmpty = False
def focusChanged(self, old, new):
"""
@ -287,6 +350,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# OUTLINE
###############################################################################
def outlineChanged(self, selected, deselected):
index = self.treeOutlineOutline.selectionModel().currentIndex()
if not index.isValid():
self.pushHistory(("outline", None))
self._previousSelectionEmpty = True
return
self.pushHistory(("outline", self.mdlOutline.ID(index)))
self._previousSelectionEmpty = False
def outlineRemoveItemsRedac(self):
self.treeRedacOutline.delete()
@ -297,18 +371,177 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# CHARACTERS
###############################################################################
def changeCurrentCharacter(self, trash=None):
"""
def setPersoBulkMode(self, enabled: bool):
if enabled and self.BulkManageUi is None: # Delete all tabs and create the manager one
# Create the widget
bulkPersoInfoManager = QWidget()
bulkPersoInfoManagerUi = Ui_BulkInfoManager()
bulkPersoInfoManagerUi.setupUi(bulkPersoInfoManager)
@return:
"""
c = self.lstCharacters.currentCharacter()
if not c:
self.tabPersos.setEnabled(False)
self.BulkManageUi = bulkPersoInfoManagerUi # for global use
model = QStandardItemModel()
# Set the column headers
model.setColumnCount(2)
model.setHorizontalHeaderLabels([self.tr("Name"), self.tr("Value")])
# Set the width
self.updatePersoInfoView(bulkPersoInfoManagerUi.tableView)
bulkPersoInfoManagerUi.tableView.setModel(model) # Set the model of tableView
self.tabPersos.clear()
self.tabPersos.addTab(bulkPersoInfoManager, self.tr("Bulk Info Manager"))
self.isPersoBulkModeEnabled = True
self.refreshBulkAffectedCharacters()
# Showing the character names on the label
labelText = self.createCharacterSelectionString()
bulkPersoInfoManagerUi.lblCharactersDynamic.setText(labelText)
# Making the connections
self.makeBulkInfoConnections(bulkPersoInfoManagerUi)
elif enabled and self.BulkManageUi is not None: # If yet another character is selected, refresh the label
labelText = self.createCharacterSelectionString()
self.BulkManageUi.lblCharactersDynamic.setText(labelText)
else: # Delete manager tab and restore the others
if self.BulkManageUi is not None:
self.tabPersos.clear()
self.loadCharacterTabs()
self.BulkManageUi = None
self.bulkAffectedCharacters.clear()
def createCharacterSelectionString(self):
self.refreshBulkAffectedCharacters()
labelText = ""
length = len(self.bulkAffectedCharacters)
for i in range(length-1):
labelText += '"' + self.bulkAffectedCharacters[i] + '"' + ", "
labelText += '"' + self.bulkAffectedCharacters[length-1] + '"'
return labelText
def makeBulkInfoConnections(self, bulkUi):
# A lambda has to be used to pass in the argument
bulkUi.btnPersoBulkAddInfo.clicked.connect(lambda: self.addBulkInfo(bulkUi))
bulkUi.btnPersoBulkRmInfo.clicked.connect(lambda: self.removeBulkInfo(bulkUi))
bulkUi.btnPersoBulkApply.clicked.connect(lambda: self.applyBulkInfo(bulkUi))
def applyBulkInfo(self, bulkUi):
selectedItems = self.lstCharacters.currentCharacterIDs()
# Get the data from the tableview
model = bulkUi.tableView.model()
if model.rowCount() == 0:
QMessageBox.warning(self, self.tr("No Entries!"),
self.tr("Please add entries to apply to the selected characters."))
return
# Loop through each selected character and add the bulk info to them
for ID in selectedItems:
for row in range(model.rowCount()):
description = model.item(row, 0).text()
value = model.item(row, 1).text()
self.lstCharacters._model.addCharacterInfo(ID, description, value)
QMessageBox.information(self, self.tr("Bulk Info Applied"),
self.tr("The bulk info has been applied to the selected characters."))
# Remove all rows from the table
model.removeRows(0, model.rowCount())
def addBulkInfo(self, bulkUi): # Adds an item to the list
charInfoDialog = QDialog()
charInfoUi = characterInfoDialog.Ui_characterInfoDialog()
charInfoUi.setupUi(charInfoDialog)
if charInfoDialog.exec_() == QDialog.Accepted:
# User clicked OK, get the input values
description = charInfoUi.descriptionLineEdit.text()
value = charInfoUi.valueLineEdit.text()
# Add a new row to the model with the description and value
row = [QStandardItem(description), QStandardItem(value)]
bulkUi.tableView.model().appendRow(row)
bulkUi.tableView.update()
def removeBulkInfo(self, bulkUi):
# Get the selected rows
selection = bulkUi.tableView.selectionModel().selectedRows()
# Iterate over the rows and remove them (reversed, so the iteration is not affected)
for index in reversed(selection):
bulkUi.tableView.model().removeRow(index.row())
def saveCharacterTabs(self):
tabsData = []
for i in range(self.tabPersos.count()):
tabData = {}
widget = self.tabPersos.widget(i)
tabData['widget'] = widget
tabData['title'] = self.tabPersos.tabText(i)
tabsData.append(tabData)
return tabsData
def loadCharacterTabs(self):
for tabData in self.tabsData:
widget = tabData['widget']
title = tabData['title']
self.tabPersos.addTab(widget, title)
def handleCharacterSelectionChanged(self):
selectedCharacters = self.lstCharacters.currentCharacters()
characterSelectionIsEmpty = not any(selectedCharacters)
if characterSelectionIsEmpty:
self.pushHistory(("character", None))
self.tabPersos.setEnabled(False)
self._previousSelectionEmpty = True
return
cList = list(filter(None, self.lstCharacters.currentCharacters())) #cList contains all valid characters
character = cList[0]
self.changeCurrentCharacter(character)
self.pushHistory(("character", character.ID()))
self._previousSelectionEmpty = False
if len(selectedCharacters) > 1:
self.setPersoBulkMode(True)
else:
if self.BulkManageUi is not None:
self.refreshBulkAffectedCharacters()
self.BulkManageUi.lblCharactersDynamic.setText( self.createCharacterSelectionString() )
tableview_model = self.BulkManageUi.tableView.model()
if tableview_model.rowCount() > 0:
confirm = QMessageBox.warning(
self, self.tr("Un-applied data!"),
self.tr("There are un-applied entries in this tab. Discard them?"),
QMessageBox.Yes | QMessageBox.No,
defaultButton = QMessageBox.No
)
if confirm != QMessageBox.Yes:
return
self.setPersoBulkMode(False)
self.tabPersos.setEnabled(True)
index = c.index()
def refreshBulkAffectedCharacters(self): # Characters affected by a potential bulk-info modification
self.bulkAffectedCharacters = []
for character in self.lstCharacters.currentCharacters():
self.bulkAffectedCharacters.append(character.name())
def changeCurrentCharacter(self, character):
if character is None:
return
index = character.index()
for w in [
self.txtPersoName,
@ -325,24 +558,25 @@ class MainWindow(QMainWindow, Ui_MainWindow):
w.setCurrentModelIndex(index)
# Button color
self.updateCharacterColor(c.ID())
self.updateCharacterColor(character.ID())
# Slider importance
self.updateCharacterImportance(c.ID())
self.updateCharacterImportance(character.ID())
# POV state
self.updateCharacterPOVState(c.ID())
self.updateCharacterPOVState(character.ID())
# Character Infos
self.tblPersoInfos.setRootIndex(index)
if self.mdlCharacter.rowCount(index):
self.updatePersoInfoView()
self.updatePersoInfoView(self.tblPersoInfos)
def updatePersoInfoView(self):
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
self.tblPersoInfos.verticalHeader().hide()
def updatePersoInfoView(self, infoView):
infoView.horizontalHeader().setStretchLastSection(True)
infoView.horizontalHeader().setMinimumSectionSize(20)
infoView.horizontalHeader().setMaximumSectionSize(500)
infoView.verticalHeader().hide()
def updateCharacterColor(self, ID):
c = self.mdlCharacter.getCharacterByID(ID)
@ -370,7 +604,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
pass
def deleteCharacter(self):
ID = self.lstCharacters.removeCharacter()
ID = self.lstCharacters.removeCharacters()
if ID is None:
return
for itemID in self.mdlOutline.findItemsByPOV(ID):
@ -384,11 +618,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def changeCurrentPlot(self):
index = self.lstPlots.currentPlotIndex()
id = self.lstPlots.currentPlotID()
if not index.isValid():
self.tabPlot.setEnabled(False)
self.pushHistory(("plot", None))
self._previousSelectionEmpty = True
return
self.pushHistory(("plot", id))
self._previousSelectionEmpty = False
self.tabPlot.setEnabled(True)
self.txtPlotName.setCurrentModelIndex(index)
self.txtPlotDescription.setCurrentModelIndex(index)
@ -457,8 +697,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if not index.isValid():
self.tabWorld.setEnabled(False)
self.pushHistory(("world", None))
self._previousSelectionEmpty = True
return
self.pushHistory(("world", self.mdlWorld.ID(index)))
self._previousSelectionEmpty = False
self.tabWorld.setEnabled(True)
self.txtWorldName.setCurrentModelIndex(index)
self.txtWorldDescription.setCurrentModelIndex(index)
@ -469,6 +714,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# EDITOR
###############################################################################
def redacOutlineChanged(self):
index = self.treeRedacOutline.selectionModel().currentIndex()
if not index.isValid():
self.pushHistory(("redac", None))
self._previousSelectionEmpty = True
return
self.pushHistory(("redac", self.mdlOutline.ID(index)))
self._previousSelectionEmpty = False
def openIndex(self, index):
self.treeRedacOutline.setCurrentIndex(index)
@ -566,6 +821,92 @@ class MainWindow(QMainWindow, Ui_MainWindow):
"Merges selected item(s)."
if self._lastFocus: self._lastFocus.merge()
# Navigate
def navigateBack(self):
self.history.back()
def navigateForward(self):
self.history.forward()
def pushHistory(self, entry):
if self._previousSelectionEmpty:
self.history.replace(entry)
else:
self.history.next(entry)
def navigated(self, event):
if event.entry:
first_entry = event.entry[0]
if first_entry == "character":
if self.tabMain.currentIndex() != self.TabPersos:
self.tabMain.setCurrentIndex(self.TabPersos)
if event.entry[1] is None:
self.lstCharacters.setCurrentItem(None)
self.lstCharacters.clearSelection()
else:
if self.lstCharacters.currentCharacterID() != event.entry[1]:
char = self.lstCharacters.getItemByID(event.entry[1])
if char != None:
self.lstCharacters.clearSelection()
self.lstCharacters.setCurrentItem(char)
elif first_entry == "plot":
if self.tabMain.currentIndex() != self.TabPlots:
self.tabMain.setCurrentIndex(self.TabPlots)
if event.entry[1] is None:
self.lstPlots.setCurrentItem(None)
else:
index = self.lstPlots.currentPlotIndex()
if index and index.row() != event.entry[1]:
plot = self.lstPlots.getItemByID(event.entry[1])
if plot != None:
self.lstPlots.setCurrentItem(plot)
elif first_entry == "world":
if self.tabMain.currentIndex() != self.TabWorld:
self.tabMain.setCurrentIndex(self.TabWorld)
if event.entry[1] is None:
self.treeWorld.selectionModel().clear()
else:
index = self.mdlWorld.selectedIndex()
if index and self.mdlWorld.ID(index) != event.entry[1]:
world = self.mdlWorld.indexByID(event.entry[1])
if world != None:
self.treeWorld.setCurrentIndex(world)
elif first_entry == "outline":
if self.tabMain.currentIndex() != self.TabOutline:
self.tabMain.setCurrentIndex(self.TabOutline)
if event.entry[1] is None:
self.treeOutlineOutline.selectionModel().clear()
else:
index = self.treeOutlineOutline.selectionModel().currentIndex()
if index and self.mdlOutline.ID(index) != event.entry[1]:
outline = self.mdlOutline.getIndexByID(event.entry[1])
if outline is not None:
self.treeOutlineOutline.setCurrentIndex(outline)
elif first_entry == "redac":
if self.tabMain.currentIndex() != self.TabRedac:
self.tabMain.setCurrentIndex(self.TabRedac)
if event.entry[1] is None:
self.treeRedacOutline.selectionModel().clear()
else:
index = self.treeRedacOutline.selectionModel().currentIndex()
if index and self.mdlOutline.ID(index) != event.entry[1]:
outline = self.mdlOutline.getIndexByID(event.entry[1])
if outline is not None:
self.treeRedacOutline.setCurrentIndex(outline)
elif first_entry == "main":
if self.tabMain.currentIndex() != event.entry[1]:
self.lstTabs.setCurrentRow(event.entry[1])
self.actBack.setEnabled(event.position > 0)
self.actForward.setEnabled(event.position < event.count - 1)
###############################################################################
# LOAD AND SAVE
###############################################################################
@ -575,6 +916,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
If ``loadFromFile`` is False, then it does not load datas from file.
It assumes that the datas have been populated in a different way."""
# Convert project path to OS norm
project = os.path.normpath(project)
if loadFromFile and not os.path.exists(project):
LOGGER.warning("The file {} does not exist. Has it been moved or deleted?".format(project))
F.statusMessage(
@ -642,6 +987,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
i.setEnabled(False)
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuView, self.menuOrganize,
self.menuNavigate,
self.menuTools, self.menuHelp, self.actImport,
self.actCompile, self.actSettings]:
i.setEnabled(True)
@ -659,6 +1005,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Add project name to Window's name
self.setWindowTitle(self.projectName() + " - " + self.tr("Manuskript"))
# Reset history
self.history.reset()
# Show main Window
self.switchToProject()
@ -903,12 +1252,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def makeUIConnections(self):
"Connections that have to be made once only, even when a new project is loaded."
self.lstCharacters.currentItemChanged.connect(self.changeCurrentCharacter, F.AUC)
# Characters
self.txtPersosFilter.textChanged.connect(self.lstCharacters.setFilter, F.AUC)
self.lstCharacters.itemSelectionChanged.connect(self.handleCharacterSelectionChanged, F.AUC)
# Plots
self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, F.AUC)
self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, F.AUC)
self.lstSubPlots.clicked.connect(self.changeCurrentSubPlot, F.AUC)
# Outline
self.btnRedacAddFolder.clicked.connect(self.treeRedacOutline.addFolder, F.AUC)
self.btnOutlineAddFolder.clicked.connect(self.treeOutlineOutline.addFolder, F.AUC)
self.btnRedacAddText.clicked.connect(self.treeRedacOutline.addText, F.AUC)
@ -919,6 +1272,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.tabMain.currentChanged.connect(self.toolbar.setCurrentGroup)
self.tabMain.currentChanged.connect(self.tabMainChanged)
self.history.navigated.connect(self.navigated)
qApp.focusChanged.connect(self.focusChanged)
def makeConnections(self):
@ -956,6 +1311,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
widget.setCurrentModelIndex(self.mdlFlatData.index(0, col))
# Characters
self.updatePersoInfoView(self.tblPersoInfos)
self.lstCharacters.setCharactersModel(self.mdlCharacter)
self.tblPersoInfos.setModel(self.mdlCharacter)
try:
@ -1056,10 +1412,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# self.redacEditor.setModel(self.mdlOutline)
self.storylineView.setModels(self.mdlOutline, self.mdlCharacter, self.mdlPlots)
self.treeOutlineOutline.selectionModel().selectionChanged.connect(self.outlineChanged, F.AUC)
self.treeOutlineOutline.selectionModel().selectionChanged.connect(self.outlineItemEditor.selectionChanged, F.AUC)
self.treeOutlineOutline.clicked.connect(self.outlineItemEditor.selectionChanged, F.AUC)
# Sync selection
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.redacOutlineChanged, F.AUC)
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.redacMetadata.selectionChanged, F.AUC)
self.treeRedacOutline.clicked.connect(self.redacMetadata.selectionChanged, F.AUC)
@ -1167,6 +1525,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.steps)))
self.disconnectAll(self.history.navigated)
###############################################################################
# HELP

View file

@ -220,6 +220,17 @@ class characterModel(QAbstractItemModel, searchableModel):
self.endInsertRows()
mainWindow().updatePersoInfoView()
def addCharacterInfo(self, ID, description, value):
c = self.getCharacterByID(ID)
self.beginInsertRows(c.index(), len(c.infos), len(c.infos))
c.infos.append(CharacterInfo(
c,
description=self.tr(description),
value=self.tr(value)
))
self.endInsertRows()
mainWindow().updatePersoInfoView(mainWindow().tblPersoInfos)
def removeCharacterInfo(self, ID):
c = self.getCharacterByID(ID)

View file

@ -351,7 +351,8 @@ class plotItemSearchWrapper(searchableItem):
return [self.translate("Plot")] + _path(self.getItem(self.rowIndex, Plot.name)) + [self.translate(self.searchColumnLabel(column))]
def searchData(self, column):
return self.getItem(self.rowIndex, column).text()
item = self.getItem(self.rowIndex, column)
return item.text() if item else None
def plotStepPath(self, plotName, plotStepName, column):
return [self.translate("Plot"), plotName, plotStepName, self.translate(self.searchColumnLabel(column))]

View file

@ -513,7 +513,7 @@ class settingsWindow(QWidget, Ui_Settings):
# self.cmbDelegate = cmbPixmapDelegate()
# self.cmbCorkImage.setItemDelegate(self.cmbDelegate)
paths = allPaths("resources/backgrounds")
paths = allPaths(os.path.join("resources", "backgrounds"))
cmb.clear()
cmb.addItem(QIcon.fromTheme("list-remove"), "", "")
for p in paths:
@ -541,7 +541,7 @@ class settingsWindow(QWidget, Ui_Settings):
valid = px.load(filename)
del px
if valid:
shutil.copy(filename, writablePath("resources/backgrounds"))
shutil.copy(filename, writablePath(os.path.join("resources", "backgrounds")))
return os.path.basename(filename)
else:
QMessageBox.warning(self, self.tr("Error"),
@ -727,7 +727,7 @@ class settingsWindow(QWidget, Ui_Settings):
self.btnThemeRemove.setEnabled(False)
def newTheme(self):
path = writablePath("resources/themes")
path = writablePath(os.path.join("resources", "themes"))
name = self.tr("newtheme")
if os.path.exists(os.path.join(path, "{}.theme".format(name))):
i = 1
@ -756,7 +756,7 @@ class settingsWindow(QWidget, Ui_Settings):
self.populatesThemesList()
def populatesThemesList(self):
paths = allPaths("resources/themes")
paths = allPaths(os.path.join("resources", "themes"))
current = settings.fullScreenTheme
self.lstThemes.clear()

View file

@ -3,7 +3,7 @@
"""Tests for functions"""
import re
import re, os
from manuskript import functions as F
def test_wordCount():
@ -81,7 +81,7 @@ def test_paths():
assert len(F.allPaths("suffix")) == 2
assert F.tempFile("yop") != None
f = F.findBackground("spacedreams.jpg")
assert "resources/backgrounds/spacedreams.jpg" in f
assert os.path.join("resources", "backgrounds", "spacedreams.jpg") in f
assert len(F.customIcons()) > 1
def test_mainWindow():

View file

@ -10,6 +10,9 @@ from manuskript.functions import appPath
from manuskript.ui.about_ui import Ui_about
from manuskript.version import getVersion
import os
class aboutDialog(QWidget, Ui_about):
def __init__(self, parent=None, mw=None):
QWidget.__init__(self, parent)
@ -19,10 +22,10 @@ class aboutDialog(QWidget, Ui_about):
def populateFields(self):
# Fill in all the fields in the About dialog
iconPic = appPath("icons/Manuskript/icon-64px.png")
iconPic = appPath(os.path.join("icons", "Manuskript", "icon-64px.png"))
self.setWindowIcon(QIcon(iconPic))
logoPic = QPixmap(appPath("icons/Manuskript/logo-400x104.png"))
logoPic = QPixmap(appPath(os.path.join("icons", "Manuskript", "logo-400x104.png")))
self.labelLogo.setPixmap(logoPic)
self.labelManuskriptVersion.setText(

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/about_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.

View file

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'manuskript\manuskript\ui\bulkInfoManager.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_BulkInfoManager(object):
def setupUi(self, BulkInfoManager):
BulkInfoManager.setObjectName("BulkInfoManager")
BulkInfoManager.setWindowModality(QtCore.Qt.WindowModal)
BulkInfoManager.resize(644, 556)
self.verticalLayout = QtWidgets.QVBoxLayout(BulkInfoManager)
self.verticalLayout.setObjectName("verticalLayout")
self.lblStaticMessage = QtWidgets.QLabel(BulkInfoManager)
self.lblStaticMessage.setObjectName("lblStaticMessage")
self.verticalLayout.addWidget(self.lblStaticMessage)
self.lblCharactersDynamic = QtWidgets.QLabel(BulkInfoManager)
self.lblCharactersDynamic.setObjectName("lblCharactersDynamic")
self.verticalLayout.addWidget(self.lblCharactersDynamic)
self.tableView = QtWidgets.QTableView(BulkInfoManager)
self.tableView.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedKingdom))
self.tableView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.tableView.setAlternatingRowColors(True)
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableView.setTextElideMode(QtCore.Qt.ElideNone)
self.tableView.setCornerButtonEnabled(False)
self.tableView.setObjectName("tableView")
self.tableView.verticalHeader().setVisible(False)
self.verticalLayout.addWidget(self.tableView)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.btnPersoBulkAddInfo = QtWidgets.QPushButton(BulkInfoManager)
self.btnPersoBulkAddInfo.setText("")
icon = QtGui.QIcon.fromTheme("list-add")
self.btnPersoBulkAddInfo.setIcon(icon)
self.btnPersoBulkAddInfo.setObjectName("btnPersoBulkAddInfo")
self.horizontalLayout.addWidget(self.btnPersoBulkAddInfo)
self.btnPersoBulkRmInfo = QtWidgets.QPushButton(BulkInfoManager)
self.btnPersoBulkRmInfo.setText("")
icon = QtGui.QIcon.fromTheme("list-remove")
self.btnPersoBulkRmInfo.setIcon(icon)
self.btnPersoBulkRmInfo.setObjectName("btnPersoBulkRmInfo")
self.horizontalLayout.addWidget(self.btnPersoBulkRmInfo)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.btnPersoBulkApply = QtWidgets.QPushButton(BulkInfoManager)
self.btnPersoBulkApply.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedKingdom))
self.btnPersoBulkApply.setObjectName("btnPersoBulkApply")
self.horizontalLayout.addWidget(self.btnPersoBulkApply)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(1, 1)
self.horizontalLayout.setStretch(2, 5)
self.horizontalLayout.setStretch(3, 1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.retranslateUi(BulkInfoManager)
QtCore.QMetaObject.connectSlotsByName(BulkInfoManager)
def retranslateUi(self, BulkInfoManager):
_translate = QtCore.QCoreApplication.translate
BulkInfoManager.setWindowTitle(_translate("BulkInfoManager", "Form"))
self.lblStaticMessage.setText(_translate("BulkInfoManager", "Affected Characters:"))
self.lblCharactersDynamic.setText(_translate("BulkInfoManager", "NONE"))
self.btnPersoBulkAddInfo.setToolTip(_translate("BulkInfoManager", "Add entry to the list."))
self.btnPersoBulkRmInfo.setToolTip(_translate("BulkInfoManager", "Remove entry from the list."))
self.btnPersoBulkApply.setToolTip(_translate("BulkInfoManager", "Adds all items to the selected characters."))
self.btnPersoBulkApply.setText(_translate("BulkInfoManager", "Apply Changes"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
BulkInfoManager = QtWidgets.QWidget()
ui = Ui_BulkInfoManager()
ui.setupUi(BulkInfoManager)
BulkInfoManager.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BulkInfoManager</class>
<widget class="QWidget" name="BulkInfoManager">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>644</width>
<height>556</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblStaticMessage">
<property name="text">
<string>Affected Characters:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblCharactersDynamic">
<property name="text">
<string>NONE</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableView">
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideNone</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1,5,1">
<item>
<widget class="QPushButton" name="btnPersoBulkAddInfo">
<property name="toolTip">
<string>Add entry to the list.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-add">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnPersoBulkRmInfo">
<property name="toolTip">
<string>Remove entry from the list.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-remove">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnPersoBulkApply">
<property name="toolTip">
<string>Adds all items to the selected characters.</string>
</property>
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<property name="text">
<string>Apply Changes</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'manuskript/ui/characterInfoDialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_characterInfoDialog(object):
def setupUi(self, characterInfoDialog):
characterInfoDialog.setObjectName("characterInfoDialog")
characterInfoDialog.setWindowModality(QtCore.Qt.WindowModal)
characterInfoDialog.resize(481, 148)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(characterInfoDialog.sizePolicy().hasHeightForWidth())
characterInfoDialog.setSizePolicy(sizePolicy)
characterInfoDialog.setMinimumSize(QtCore.QSize(481, 148))
characterInfoDialog.setMaximumSize(QtCore.QSize(481, 148))
characterInfoDialog.setTabletTracking(False)
characterInfoDialog.setFocusPolicy(QtCore.Qt.NoFocus)
characterInfoDialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedKingdom))
self.gridLayout = QtWidgets.QGridLayout(characterInfoDialog)
self.gridLayout.setObjectName("gridLayout")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_2 = QtWidgets.QLabel(characterInfoDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
self.label_2.setSizePolicy(sizePolicy)
self.label_2.setObjectName("label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.valueLineEdit = QtWidgets.QLineEdit(characterInfoDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.valueLineEdit.sizePolicy().hasHeightForWidth())
self.valueLineEdit.setSizePolicy(sizePolicy)
self.valueLineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
self.valueLineEdit.setLayoutDirection(QtCore.Qt.RightToLeft)
self.valueLineEdit.setObjectName("valueLineEdit")
self.horizontalLayout_2.addWidget(self.valueLineEdit)
self.gridLayout.addLayout(self.horizontalLayout_2, 3, 0, 1, 1)
self.horizontalLayout_1 = QtWidgets.QHBoxLayout()
self.horizontalLayout_1.setObjectName("horizontalLayout_1")
self.label = QtWidgets.QLabel(characterInfoDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label")
self.horizontalLayout_1.addWidget(self.label)
self.descriptionLineEdit = QtWidgets.QLineEdit(characterInfoDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.descriptionLineEdit.sizePolicy().hasHeightForWidth())
self.descriptionLineEdit.setSizePolicy(sizePolicy)
self.descriptionLineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
self.descriptionLineEdit.setLayoutDirection(QtCore.Qt.RightToLeft)
self.descriptionLineEdit.setObjectName("descriptionLineEdit")
self.horizontalLayout_1.addWidget(self.descriptionLineEdit)
self.gridLayout.addLayout(self.horizontalLayout_1, 2, 0, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(characterInfoDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth())
self.buttonBox.setSizePolicy(sizePolicy)
self.buttonBox.setFocusPolicy(QtCore.Qt.StrongFocus)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 1)
self.retranslateUi(characterInfoDialog)
self.buttonBox.accepted.connect(characterInfoDialog.accept) # type: ignore
self.buttonBox.rejected.connect(characterInfoDialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(characterInfoDialog)
characterInfoDialog.setTabOrder(self.descriptionLineEdit, self.valueLineEdit)
def retranslateUi(self, characterInfoDialog):
_translate = QtCore.QCoreApplication.translate
characterInfoDialog.setWindowTitle(_translate("characterInfoDialog", "Add Character Info"))
self.label_2.setText(_translate("characterInfoDialog", "Value:"))
self.label.setText(_translate("characterInfoDialog", "Name:"))

View file

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>characterInfoDialog</class>
<widget class="QDialog" name="characterInfoDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>481</width>
<height>148</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>481</width>
<height>148</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>481</width>
<height>148</height>
</size>
</property>
<property name="tabletTracking">
<bool>false</bool>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle">
<string>Add Character Info</string>
</property>
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Value:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="valueLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="descriptionLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>descriptionLineEdit</tabstop>
<tabstop>valueLineEdit</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>characterInfoDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>259</x>
<y>136</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>147</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>characterInfoDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>327</x>
<y>136</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>147</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -116,7 +116,7 @@ class fullScreenEditor(QWidget):
self.bottomPanel.layout().addSpacing(24)
self.lstThemes = QComboBox(self)
self.lstThemes.setAttribute(Qt.WA_TranslucentBackground)
paths = allPaths("resources/themes")
paths = allPaths(os.path.join("resources", "themes"))
for p in paths:
lst = [i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"]
for t in lst:

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import locale
import locale, os
from PyQt5.QtCore import QModelIndex, QRect, QPoint
from PyQt5.QtCore import Qt
@ -95,11 +95,11 @@ class mainEditor(QWidget, Ui_mainEditor):
# Cf. https://github.com/qtproject/qtbase/commit/a8621a3f85e64f1252a80ae81a6e22554f7b3f44
# Since those are important, we provide fallback.
self.btnRedacFolderCork.setIcon(QIcon.fromTheme("view-cards",
QIcon(appPath("icons/NumixMsk/256x256/actions/view-cards.svg"))))
QIcon(appPath(os.path.join("icons", "NumixMsk", "256x256", "actions", "view-cards.svg")))))
self.btnRedacFolderOutline.setIcon(QIcon.fromTheme("view-outline",
QIcon(appPath("icons/NumixMsk/256x256/actions/view-outline.svg"))))
QIcon(appPath(os.path.join("icons", "NumixMsk", "256x256", "actions", "view-outline.svg")))))
self.btnRedacFolderText.setIcon(QIcon.fromTheme("view-text",
QIcon(appPath("icons/NumixMsk/256x256/actions/view-text.svg"))))
QIcon(appPath(os.path.join("icons", "NumixMsk", "256x256", "actions", "view-text.svg")))))
for btn in [self.btnRedacFolderCork, self.btnRedacFolderText, self.btnRedacFolderOutline]:
btn.setToolTip(btn.text())

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import locale
import locale, os
from PyQt5.QtCore import QModelIndex, QRect, QPoint, Qt, QObject, QSize
from PyQt5.QtGui import QIcon, QPalette
@ -150,7 +150,7 @@ class tabSplitter(QWidget, Ui_tabSplitter):
self.updateTargetIcon(self.isTarget)
def updateTargetIcon(self, val):
icon = QIcon.fromTheme("set-target", QIcon(appPath("icons/NumixMsk/256x256/actions/set-target.svg")))
icon = QIcon.fromTheme("set-target", QIcon(appPath(os.path.join("icons", "NumixMsk", "256x256", "actions", "set-target.svg"))))
if not val:
icon = QIcon(icon.pixmap(128, 128, icon.Disabled))
self.btnTarget.setIcon(icon)

View file

@ -135,9 +135,9 @@ def createThemePreview(theme, screenRect, size=QSize(200, 120)):
def findThemePath(themeName):
p = findFirstFile(re.escape("{}.theme".format(themeName)), "resources/themes")
p = findFirstFile(re.escape("{}.theme".format(themeName)), os.path.join("resources", "themes"))
if not p:
return findFirstFile(r".*\.theme", "resources/themes")
return findFirstFile(r".*\.theme", os.path.join("resources", "themes"))
else:
return p
@ -276,7 +276,7 @@ def addThemePreviewText(pixmap, themeDatas, screenRect):
previewText.setFrameStyle(QFrame.NoFrame)
previewText.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
previewText.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
f = QFile(appPath("resources/themes/preview.txt"))
f = QFile(appPath(os.path.join("resources", "themes", "preview.txt")))
f.open(QIODevice.ReadOnly)
previewText.setPlainText(QTextStream(f).readAll())

View file

@ -128,7 +128,7 @@ class exporterSettings(QWidget, Ui_exporterSettings):
def loadSettings(self):
filename = self.getSettingsPath()
if os.path.exists(filename):
with open(filename, "r", encoding="utf-8") as f:
with open(filename, 'r', encoding="utf-8") as f:
self.settings = json.load(f)
self.updateFromSettings()
@ -138,7 +138,7 @@ class exporterSettings(QWidget, Ui_exporterSettings):
def writeSettings(self):
self.getSettings()
with open(self.getSettingsPath(), 'w', encoding="utf-8") as f:
with open(self.getSettingsPath(), 'w', encoding="utf-8", newline="\n") as f:
# json.dumps(json.loads(json.dumps(allSettings)), indent=4, sort_keys=True)
json.dump(self.settings, f, indent=4, sort_keys=True)
@ -419,6 +419,3 @@ class exporterSettings(QWidget, Ui_exporterSettings):
lst.setMinimumSize(QSize(0, h+2))
lst.setMaximumSize(QSize(16777215, h+2))

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/listDialog_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/mainWindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@ -315,6 +315,7 @@ class Ui_MainWindow(object):
self.lstCharacters = characterTreeView(self.groupBox)
self.lstCharacters.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.lstCharacters.setDragEnabled(True)
self.lstCharacters.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.lstCharacters.setObjectName("lstCharacters")
self.lstCharacters.headerItem().setText(0, "1")
self.verticalLayout_8.addWidget(self.lstCharacters)
@ -1015,6 +1016,8 @@ class Ui_MainWindow(object):
self.menuMode.setObjectName("menuMode")
self.menuOrganize = QtWidgets.QMenu(self.menubar)
self.menuOrganize.setObjectName("menuOrganize")
self.menuNavigate = QtWidgets.QMenu(self.menubar)
self.menuNavigate.setObjectName("menuNavigate")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
@ -1199,6 +1202,16 @@ class Ui_MainWindow(object):
self.actMoveDown.setIcon(icon)
self.actMoveDown.setShortcut("Ctrl+Shift+Down")
self.actMoveDown.setObjectName("actMoveDown")
self.actBack = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("arrow-left")
self.actBack.setIcon(icon)
self.actBack.setShortcut("Ctrl+<")
self.actBack.setObjectName("actBack")
self.actForward = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("arrow-right")
self.actForward.setIcon(icon)
self.actForward.setShortcut("Ctrl+>")
self.actForward.setObjectName("actForward")
self.actRename = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("edit-rename")
self.actRename.setIcon(icon)
@ -1354,9 +1367,12 @@ class Ui_MainWindow(object):
self.menuOrganize.addAction(self.actMerge)
self.menuOrganize.addAction(self.actSplitDialog)
self.menuOrganize.addAction(self.actSplitCursor)
self.menuNavigate.addAction(self.actBack)
self.menuNavigate.addAction(self.actForward)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuEdit.menuAction())
self.menubar.addAction(self.menuOrganize.menuAction())
self.menubar.addAction(self.menuNavigate.menuAction())
self.menubar.addAction(self.menuView.menuAction())
self.menubar.addAction(self.menuTools.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
@ -1558,6 +1574,7 @@ class Ui_MainWindow(object):
self.menuView.setTitle(_translate("MainWindow", "&View"))
self.menuMode.setTitle(_translate("MainWindow", "&Mode"))
self.menuOrganize.setTitle(_translate("MainWindow", "Organi&ze"))
self.menuNavigate.setTitle(_translate("MainWindow", "&Navigate"))
self.dckCheatSheet.setWindowTitle(_translate("MainWindow", "Cheat Sheet"))
self.dckSearch.setWindowTitle(_translate("MainWindow", "Search"))
self.dckNavigation.setWindowTitle(_translate("MainWindow", "&Navigation"))
@ -1591,6 +1608,8 @@ class Ui_MainWindow(object):
self.actDelete.setText(_translate("MainWindow", "&Delete"))
self.actMoveUp.setText(_translate("MainWindow", "&Move Up"))
self.actMoveDown.setText(_translate("MainWindow", "M&ove Down"))
self.actBack.setText(_translate("MainWindow", "Go &back"))
self.actForward.setText(_translate("MainWindow", "Go &forward"))
self.actRename.setText(_translate("MainWindow", "&Rename"))
self.actHeaderSetextL1.setText(_translate("MainWindow", "&Level 1 (setext)"))
self.actHeaderSetextL2.setText(_translate("MainWindow", "Level &2"))

View file

@ -670,6 +670,9 @@
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<column>
<property name="text">
<string notr="true">1</string>
@ -2121,9 +2124,17 @@
<addaction name="actSplitDialog"/>
<addaction name="actSplitCursor"/>
</widget>
<widget class="QMenu" name="menuNavigate">
<property name="title">
<string>&amp;Navigate</string>
</property>
<addaction name="actBack"/>
<addaction name="actForward"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuOrganize"/>
<addaction name="menuNavigate"/>
<addaction name="menuView"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/>
@ -2565,6 +2576,30 @@
<string notr="true">Ctrl+Shift+Down</string>
</property>
</action>
<action name="actBack">
<property name="icon">
<iconset theme="arrow-left">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Go &amp;back</string>
</property>
<property name="shortcut">
<string notr="true">Ctrl+&lt;</string>
</property>
</action>
<action name="actForward">
<property name="icon">
<iconset theme="arrow-right">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Go &amp;forward</string>
</property>
<property name="shortcut">
<string notr="true">Ctrl+&gt;</string>
</property>
</action>
<action name="actRename">
<property name="icon">
<iconset theme="edit-rename">

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/settings_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.

View file

@ -2,11 +2,12 @@
# --!-- coding: utf8 --!--
from PyQt5.QtCore import QSize, QModelIndex, Qt
from PyQt5.QtGui import QPixmap, QColor, QIcon, QBrush
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QColorDialog
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QColorDialog, QDialog, QMessageBox
from manuskript.enums import Character
from manuskript.functions import iconColor, mainWindow
from manuskript.ui import style as S
from manuskript.ui import characterInfoDialog
class characterTreeView(QTreeWidget):
@ -137,15 +138,29 @@ class characterTreeView(QTreeWidget):
self._model.addCharacter(importance=curr_importance)
def removeCharacter(self):
def removeCharacters(self):
"""
Removes selected character.
Removes selected characters.
"""
ID = self.currentCharacterID()
if ID is None:
IDs = self.currentCharacterIDs()
# If none of the IDs are valid, do nothing.
if not any(IDs):
return None
self._model.removeCharacter(ID)
return ID
#Get confirmation from user
confirm = QMessageBox.warning(
self, "Delete selected character(s)?",
"Are you sure you want to delete the selected character(s)?",
QMessageBox.Yes | QMessageBox.No
)
if confirm != QMessageBox.Yes:
return None
#Delete all selected characters
for ID in IDs:
self._model.removeCharacter(ID)
return IDs
def choseCharacterColor(self):
ID = self.currentCharacterID()
@ -170,7 +185,21 @@ class characterTreeView(QTreeWidget):
mainWindow().updateCharacterPOVState(ID)
def addCharacterInfo(self):
self._model.addCharacterInfo(self.currentCharacterID())
#Setting up dialog
charInfoDialog = QDialog()
charInfoUi = characterInfoDialog.Ui_characterInfoDialog()
charInfoUi.setupUi(charInfoDialog)
if charInfoDialog.exec_() == QDialog.Accepted:
# User clicked OK, get the input values
description = charInfoUi.descriptionLineEdit.text()
value = charInfoUi.valueLineEdit.text()
# Add the character info with the input values
ID = self.currentCharacterID()
self._model.addCharacterInfo(ID, description, value)
def removeCharacterInfo(self):
self._model.removeCharacterInfo(self.currentCharacterID())
@ -182,6 +211,14 @@ class characterTreeView(QTreeWidget):
return ID
def currentCharacterIDs(self): #Exactly like currentCharacterID(), except for multiselection
IDs = []
for item in self.selectedItems():
ID = item.data(0, Qt.UserRole)
if ID is not None:
IDs.append(ID)
return IDs
def currentCharacter(self):
"""
Returns the selected character
@ -189,6 +226,16 @@ class characterTreeView(QTreeWidget):
"""
ID = self.currentCharacterID()
return self._model.getCharacterByID(ID)
def currentCharacters(self):
"""
Returns the selected characters (when multiple are selected)
@return: List of Characters
"""
IDs = self.currentCharacterIDs()
characters = []
for ID in IDs:
characters.append(self._model.getCharacterByID(ID))
return characters
def getItemByID(self, ID):
for t in range(self.topLevelItemCount()):

View file

@ -320,6 +320,7 @@ class corkDelegate(QStyledItemDelegate):
# Draw title
p.save()
text = index.data()
textColor = None
if text:
p.setPen(Qt.black)
@ -384,7 +385,7 @@ class corkDelegate(QStyledItemDelegate):
# Draw Summary
# One line
if lineSummary:
if lineSummary and textColor:
p.save()
f = QFont(option.font)
f.setBold(True)
@ -396,7 +397,7 @@ class corkDelegate(QStyledItemDelegate):
p.restore()
# Full summary
if fullSummary:
if fullSummary and textColor:
p.save()
p.setFont(option.font)
p.setPen(textColor)

View file

@ -65,13 +65,17 @@ class plotTreeView(QTreeWidget):
return find(self.invisibleRootItem(), ID)
def currentPlotIndex(self):
"Returns index of the current item in plot model."
"Returns index of the current item in plot model."
return self._model.getIndexFromID(self.currentPlotID())
def currentPlotID(self):
"Returns ID of the current item in plot model."
ID = None
if self.currentItem():
ID = self.currentItem().data(0, Qt.UserRole)
return self._model.getIndexFromID(ID)
return ID
###############################################################################
# UPDATES
###############################################################################

Some files were not shown because too many files have changed in this diff Show more