novelWriter 2.6.2__py3-none-any.whl → 2.7b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- novelwriter/__init__.py +84 -74
- novelwriter/assets/i18n/nw_cs_CZ.qm +0 -0
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/icons/font_awesome.icons +109 -0
- novelwriter/assets/icons/material_filled_bold.icons +109 -0
- novelwriter/assets/icons/material_filled_normal.icons +109 -0
- novelwriter/assets/icons/material_filled_thin.icons +109 -0
- novelwriter/assets/icons/material_rounded_bold.icons +109 -0
- novelwriter/assets/icons/material_rounded_normal.icons +109 -0
- novelwriter/assets/icons/material_rounded_thin.icons +109 -0
- novelwriter/assets/icons/remix_filled.icons +108 -0
- novelwriter/assets/icons/remix_outline.icons +108 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/{manual_fr_FR.pdf → manual_fr.pdf} +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +1 -1
- novelwriter/assets/text/credits_en.htm +6 -6
- novelwriter/assets/themes/cyberpunk_night.conf +23 -7
- novelwriter/assets/themes/default_dark.conf +20 -4
- novelwriter/assets/themes/default_light.conf +21 -5
- novelwriter/assets/themes/dracula.conf +20 -4
- novelwriter/assets/themes/solarized_dark.conf +24 -8
- novelwriter/assets/themes/solarized_light.conf +22 -6
- novelwriter/common.py +33 -26
- novelwriter/config.py +118 -127
- novelwriter/constants.py +75 -56
- novelwriter/core/buildsettings.py +23 -16
- novelwriter/core/coretools.py +11 -7
- novelwriter/core/docbuild.py +19 -13
- novelwriter/core/document.py +2 -2
- novelwriter/core/index.py +142 -432
- novelwriter/core/indexdata.py +403 -0
- novelwriter/core/item.py +35 -28
- novelwriter/core/itemmodel.py +27 -26
- novelwriter/core/novelmodel.py +223 -0
- novelwriter/core/options.py +1 -1
- novelwriter/core/project.py +10 -11
- novelwriter/core/projectdata.py +5 -5
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +3 -2
- novelwriter/core/spellcheck.py +4 -3
- novelwriter/core/status.py +12 -15
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tree.py +46 -8
- novelwriter/dialogs/about.py +19 -22
- novelwriter/dialogs/docmerge.py +21 -23
- novelwriter/dialogs/docsplit.py +20 -23
- novelwriter/dialogs/editlabel.py +9 -13
- novelwriter/dialogs/preferences.py +111 -48
- novelwriter/dialogs/projectsettings.py +48 -54
- novelwriter/dialogs/quotes.py +14 -19
- novelwriter/dialogs/wordlist.py +25 -30
- novelwriter/enum.py +8 -0
- novelwriter/error.py +16 -16
- novelwriter/extensions/configlayout.py +18 -18
- novelwriter/extensions/eventfilters.py +9 -5
- novelwriter/extensions/modified.py +34 -15
- novelwriter/extensions/novelselector.py +18 -5
- novelwriter/extensions/pagedsidebar.py +39 -49
- novelwriter/extensions/progressbars.py +10 -8
- novelwriter/extensions/statusled.py +6 -13
- novelwriter/extensions/switch.py +19 -30
- novelwriter/extensions/switchbox.py +7 -3
- novelwriter/extensions/versioninfo.py +4 -4
- novelwriter/formats/shared.py +1 -1
- novelwriter/formats/todocx.py +14 -10
- novelwriter/formats/tohtml.py +7 -5
- novelwriter/formats/tokenizer.py +36 -33
- novelwriter/formats/tomarkdown.py +6 -2
- novelwriter/formats/toodt.py +27 -22
- novelwriter/formats/toqdoc.py +19 -14
- novelwriter/formats/toraw.py +6 -2
- novelwriter/gui/doceditor.py +216 -265
- novelwriter/gui/dochighlight.py +46 -45
- novelwriter/gui/docviewer.py +102 -104
- novelwriter/gui/docviewerpanel.py +47 -51
- novelwriter/gui/editordocument.py +8 -5
- novelwriter/gui/itemdetails.py +15 -18
- novelwriter/gui/mainmenu.py +147 -146
- novelwriter/gui/noveltree.py +239 -405
- novelwriter/gui/outline.py +137 -76
- novelwriter/gui/projtree.py +138 -132
- novelwriter/gui/search.py +33 -31
- novelwriter/gui/sidebar.py +13 -18
- novelwriter/gui/statusbar.py +13 -15
- novelwriter/gui/theme.py +533 -430
- novelwriter/guimain.py +27 -29
- novelwriter/shared.py +32 -23
- novelwriter/text/comments.py +70 -0
- novelwriter/text/patterns.py +4 -4
- novelwriter/tools/dictionaries.py +20 -29
- novelwriter/tools/lipsum.py +16 -17
- novelwriter/tools/manusbuild.py +39 -42
- novelwriter/tools/manuscript.py +113 -126
- novelwriter/tools/manussettings.py +78 -83
- novelwriter/tools/noveldetails.py +51 -64
- novelwriter/tools/welcome.py +56 -75
- novelwriter/tools/writingstats.py +44 -57
- novelwriter/types.py +5 -7
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/METADATA +6 -6
- novelwriter-2.7b1.dist-info/RECORD +159 -0
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/WHEEL +1 -1
- novelWriter-2.6.2.dist-info/RECORD +0 -363
- novelwriter/assets/icons/typicons_dark/README.md +0 -29
- novelwriter/assets/icons/typicons_dark/icons.conf +0 -134
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +0 -4
- novelwriter/assets/icons/typicons_dark/mixed_document-chapter.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_document-note.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-scene.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-section.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-title.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_edit.svg +0 -4
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_input-none.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_search-replace.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_deco-h0.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h1.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h2.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h3.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h4.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-noveltree-more.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_font.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_panel.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-case.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-preserve.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-regex.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-word.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_arrow-down-thick-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-forward.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-maximise.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-minimise.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-repeat-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_book-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_book.svg +0 -6
- novelwriter/assets/icons/typicons_dark/typ_bookmark.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_calendar.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_cancel-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_cancel.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chart-bar-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-down.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-left.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-right.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-up.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_cog.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_delete-full.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_delete.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_directions-full.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_document-text.svg +0 -8
- novelwriter/assets/icons/typicons_dark/typ_document.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_export-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_export.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_eye.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_flag.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_folder-open.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_folder.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_globe-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_key.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_lightbulb-full.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_location.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_media-pause-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_media-record-outline.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_media-record.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_minus.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_pencil.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_pin-outline.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_pin.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_plus.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_puzzle-outline.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_puzzle.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_refresh.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_search.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_star.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_stopwatch-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-list-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-list.svg +0 -9
- novelwriter/assets/icons/typicons_dark/typ_times.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_trash.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_user.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_warning-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/README.md +0 -29
- novelwriter/assets/icons/typicons_light/icons.conf +0 -134
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +0 -4
- novelwriter/assets/icons/typicons_light/mixed_document-chapter.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-new.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_document-note.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-scene.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-section.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-title.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_edit.svg +0 -4
- novelwriter/assets/icons/typicons_light/mixed_import.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_input-checked.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_input-none.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_search-replace.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_deco-h0.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h1.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h2.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h3.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h4.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-note.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-noveltree-more.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_font.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_panel.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_quote.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-case.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-preserve.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-regex.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-word.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_arrow-down-thick-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-forward.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-maximise.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-minimise.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-repeat-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_book-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_book.svg +0 -6
- novelwriter/assets/icons/typicons_light/typ_bookmark.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_calendar.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_cancel-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_cancel.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chart-bar-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-down.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-left.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-right.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-up.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_cog.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_delete-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_delete.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_directions-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_document-text.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_document.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_export-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_export.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_eye.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_flag.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_folder-open.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_folder.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_globe-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_key.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_lightbulb-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_location.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_media-pause-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_media-record-outline.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_media-record.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_minus.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_pencil.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_pin-outline.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_pin.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_plus.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_puzzle-outline.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_puzzle.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_refresh.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_search-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_search.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_star.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_stopwatch-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-list-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-list.svg +0 -9
- novelwriter/assets/icons/typicons_light/typ_times.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_trash.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_user.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_warning-full.svg +0 -4
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info/licenses}/LICENSE.md +0 -0
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
"""
|
2
|
+
novelWriter – Project Index Data
|
3
|
+
================================
|
4
|
+
|
5
|
+
File History:
|
6
|
+
Created: 2022-05-28 [2.0rc1] IndexNode
|
7
|
+
Created: 2022-05-28 [2.0rc1] IndexHeading
|
8
|
+
Moved: 2025-02-22 [2.7b1] IndexNode
|
9
|
+
Moved: 2025-02-22 [2.7b1] IndexHeading
|
10
|
+
|
11
|
+
This file is a part of novelWriter
|
12
|
+
Copyright (C) 2025 Veronica Berglyd Olsen and novelWriter contributors
|
13
|
+
|
14
|
+
This program is free software: you can redistribute it and/or modify
|
15
|
+
it under the terms of the GNU General Public License as published by
|
16
|
+
the Free Software Foundation, either version 3 of the License, or
|
17
|
+
(at your option) any later version.
|
18
|
+
|
19
|
+
This program is distributed in the hope that it will be useful, but
|
20
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
21
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
22
|
+
General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License
|
25
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
26
|
+
"""
|
27
|
+
from __future__ import annotations
|
28
|
+
|
29
|
+
import logging
|
30
|
+
|
31
|
+
from typing import TYPE_CHECKING, Literal
|
32
|
+
|
33
|
+
from novelwriter import CONFIG
|
34
|
+
from novelwriter.common import checkInt, compact, isListInstance, isTitleTag
|
35
|
+
from novelwriter.constants import nwKeyWords, nwStyles
|
36
|
+
|
37
|
+
if TYPE_CHECKING:
|
38
|
+
from collections.abc import ItemsView, Sequence
|
39
|
+
|
40
|
+
from novelwriter.core.index import IndexCache
|
41
|
+
from novelwriter.core.item import NWItem
|
42
|
+
from novelwriter.enum import nwComment
|
43
|
+
|
44
|
+
logger = logging.getLogger(__name__)
|
45
|
+
|
46
|
+
T_NoteTypes = Literal["footnotes", "comments"]
|
47
|
+
|
48
|
+
TT_NONE = "T0000" # Default title key
|
49
|
+
NOTE_TYPES: list[T_NoteTypes] = ["footnotes", "comments"]
|
50
|
+
|
51
|
+
|
52
|
+
class IndexNode:
|
53
|
+
"""Core: Single Index Item Node Class
|
54
|
+
|
55
|
+
This object represents the index data of a project item (NWItem).
|
56
|
+
It holds a record of all the headings in the text, and the meta data
|
57
|
+
associated with each heading. It also holds a pointer to the project
|
58
|
+
item. The main heading level of the item is also held here since it
|
59
|
+
must be reset each time the item is re-indexed.
|
60
|
+
"""
|
61
|
+
|
62
|
+
__slots__ = ("_cache", "_count", "_handle", "_headings", "_item", "_notes")
|
63
|
+
|
64
|
+
def __init__(self, cache: IndexCache, tHandle: str, nwItem: NWItem) -> None:
|
65
|
+
self._cache = cache
|
66
|
+
self._handle = tHandle
|
67
|
+
self._item = nwItem
|
68
|
+
self._headings: dict[str, IndexHeading] = {TT_NONE: IndexHeading(self._cache, TT_NONE)}
|
69
|
+
self._notes: dict[str, set[str]] = {}
|
70
|
+
self._count = 0
|
71
|
+
return
|
72
|
+
|
73
|
+
def __repr__(self) -> str:
|
74
|
+
return f"<IndexNode handle='{self._handle}'>"
|
75
|
+
|
76
|
+
def __len__(self) -> int:
|
77
|
+
return len(self._headings)
|
78
|
+
|
79
|
+
def __getitem__(self, sTitle: str) -> IndexHeading | None:
|
80
|
+
return self._headings.get(sTitle, None)
|
81
|
+
|
82
|
+
def __contains__(self, sTitle: str) -> bool:
|
83
|
+
return sTitle in self._headings
|
84
|
+
|
85
|
+
##
|
86
|
+
# Properties
|
87
|
+
##
|
88
|
+
|
89
|
+
@property
|
90
|
+
def handle(self) -> str:
|
91
|
+
"""Return the item handle of the index item."""
|
92
|
+
return self._handle
|
93
|
+
|
94
|
+
@property
|
95
|
+
def item(self) -> NWItem:
|
96
|
+
"""Return the project item of the index item."""
|
97
|
+
return self._item
|
98
|
+
|
99
|
+
##
|
100
|
+
# Setters
|
101
|
+
##
|
102
|
+
|
103
|
+
def addHeading(self, tHeading: IndexHeading) -> None:
|
104
|
+
"""Add a heading to the item. Also remove the placeholder entry
|
105
|
+
if it exists.
|
106
|
+
"""
|
107
|
+
if TT_NONE in self._headings:
|
108
|
+
self._headings.pop(TT_NONE)
|
109
|
+
self._headings[tHeading.key] = tHeading
|
110
|
+
return
|
111
|
+
|
112
|
+
def setHeadingCounts(self, sTitle: str, cCount: int, wCount: int, pCount: int) -> None:
|
113
|
+
"""Set the character, word and paragraph count of a heading."""
|
114
|
+
if sTitle in self._headings:
|
115
|
+
self._headings[sTitle].setCounts([cCount, wCount, pCount])
|
116
|
+
return
|
117
|
+
|
118
|
+
def setHeadingComment(self, sTitle: str, comment: nwComment, key: str, text: str) -> None:
|
119
|
+
"""Set the comment text of a heading."""
|
120
|
+
if sTitle in self._headings:
|
121
|
+
self._headings[sTitle].setComment(comment.name, key, text)
|
122
|
+
return
|
123
|
+
|
124
|
+
def setHeadingTag(self, sTitle: str, tag: str) -> None:
|
125
|
+
"""Set the tag of a heading."""
|
126
|
+
if sTitle in self._headings:
|
127
|
+
self._headings[sTitle].setTag(tag)
|
128
|
+
return
|
129
|
+
|
130
|
+
def addHeadingRef(self, sTitle: str, tags: list[str], keyword: str) -> None:
|
131
|
+
"""Add a reference key and all its types to a heading."""
|
132
|
+
if sTitle in self._headings:
|
133
|
+
for tag in tags:
|
134
|
+
self._headings[sTitle].addReference(tag, keyword)
|
135
|
+
return
|
136
|
+
|
137
|
+
def addNoteKey(self, style: T_NoteTypes, key: str) -> None:
|
138
|
+
"""Add a note key to the index."""
|
139
|
+
if style not in self._notes:
|
140
|
+
self._notes[style] = set()
|
141
|
+
self._notes[style].add(key)
|
142
|
+
return
|
143
|
+
|
144
|
+
##
|
145
|
+
# Data Methods
|
146
|
+
##
|
147
|
+
|
148
|
+
def items(self) -> ItemsView[str, IndexHeading]:
|
149
|
+
"""Return IndexHeading items."""
|
150
|
+
return self._headings.items()
|
151
|
+
|
152
|
+
def headings(self) -> list[str]:
|
153
|
+
"""Return heading keys in sorted order."""
|
154
|
+
return sorted(self._headings.keys())
|
155
|
+
|
156
|
+
def allTags(self) -> list[str]:
|
157
|
+
"""Return a list of all tags in the current item."""
|
158
|
+
return [h.tag for h in self._headings.values() if h.tag]
|
159
|
+
|
160
|
+
def nextHeading(self) -> str:
|
161
|
+
"""Return the next heading key to be used."""
|
162
|
+
self._count += 1
|
163
|
+
return f"T{self._count:04d}"
|
164
|
+
|
165
|
+
def noteKeys(self, style: T_NoteTypes) -> set[str]:
|
166
|
+
"""Return a set of all note keys."""
|
167
|
+
return self._notes.get(style, set())
|
168
|
+
|
169
|
+
##
|
170
|
+
# Pack/Unpack
|
171
|
+
##
|
172
|
+
|
173
|
+
def packData(self) -> dict:
|
174
|
+
"""Pack the indexed item's data into a dictionary."""
|
175
|
+
data = {}
|
176
|
+
for sTitle, hItem in self._headings.items():
|
177
|
+
data[sTitle] = hItem.packData()
|
178
|
+
if self._notes:
|
179
|
+
data["document"] = {style: list(keys) for style, keys in self._notes.items()}
|
180
|
+
return data
|
181
|
+
|
182
|
+
def unpackData(self, data: dict) -> None:
|
183
|
+
"""Unpack an item entry from the data."""
|
184
|
+
for key, entry in data.items():
|
185
|
+
if isTitleTag(key):
|
186
|
+
heading = IndexHeading(self._cache, key)
|
187
|
+
heading.unpackData(entry)
|
188
|
+
self.addHeading(heading)
|
189
|
+
elif key == "document":
|
190
|
+
for style, keys in entry.items():
|
191
|
+
if style not in NOTE_TYPES:
|
192
|
+
raise ValueError("The notes style is invalid")
|
193
|
+
if not isListInstance(keys, str):
|
194
|
+
raise ValueError("The notes keys must be a list of strings")
|
195
|
+
self._notes[style] = set(keys)
|
196
|
+
else:
|
197
|
+
raise KeyError("Index node contains an invalid key")
|
198
|
+
return
|
199
|
+
|
200
|
+
|
201
|
+
class IndexHeading:
|
202
|
+
"""Core: Single Index Heading Class
|
203
|
+
|
204
|
+
This object represents a section of text in a project item
|
205
|
+
associated with a single (valid) heading. It holds a separate record
|
206
|
+
of all references made under the heading.
|
207
|
+
"""
|
208
|
+
|
209
|
+
__slots__ = (
|
210
|
+
"_cache", "_comments", "_counts", "_key", "_level", "_line", "_refs",
|
211
|
+
"_tag", "_title",
|
212
|
+
)
|
213
|
+
|
214
|
+
def __init__(
|
215
|
+
self, cache: IndexCache, key: str, line: int = 0,
|
216
|
+
level: str = "H0", title: str = "",
|
217
|
+
) -> None:
|
218
|
+
self._cache = cache
|
219
|
+
self._key = key
|
220
|
+
self._line = line
|
221
|
+
self._level = level
|
222
|
+
self._title = title
|
223
|
+
self._counts: tuple[int, int, int] = (0, 0, 0)
|
224
|
+
self._tag = ""
|
225
|
+
self._refs: dict[str, set[str]] = {}
|
226
|
+
self._comments: dict[str, str] = {}
|
227
|
+
return
|
228
|
+
|
229
|
+
def __repr__(self) -> str:
|
230
|
+
return f"<IndexHeading key='{self._key}'>"
|
231
|
+
|
232
|
+
##
|
233
|
+
# Properties
|
234
|
+
##
|
235
|
+
|
236
|
+
@property
|
237
|
+
def key(self) -> str:
|
238
|
+
return self._key
|
239
|
+
|
240
|
+
@property
|
241
|
+
def line(self) -> int:
|
242
|
+
return self._line
|
243
|
+
|
244
|
+
@property
|
245
|
+
def level(self) -> str:
|
246
|
+
return self._level
|
247
|
+
|
248
|
+
@property
|
249
|
+
def title(self) -> str:
|
250
|
+
return self._title
|
251
|
+
|
252
|
+
@property
|
253
|
+
def mainCount(self) -> int:
|
254
|
+
return self._counts[0 if CONFIG.useCharCount else 1]
|
255
|
+
|
256
|
+
@property
|
257
|
+
def charCount(self) -> int:
|
258
|
+
return self._counts[0]
|
259
|
+
|
260
|
+
@property
|
261
|
+
def wordCount(self) -> int:
|
262
|
+
return self._counts[1]
|
263
|
+
|
264
|
+
@property
|
265
|
+
def paraCount(self) -> int:
|
266
|
+
return self._counts[2]
|
267
|
+
|
268
|
+
@property
|
269
|
+
def synopsis(self) -> str:
|
270
|
+
return self._comments.get("summary", "")
|
271
|
+
|
272
|
+
@property
|
273
|
+
def comments(self) -> dict[str, str]:
|
274
|
+
return self._comments
|
275
|
+
|
276
|
+
@property
|
277
|
+
def tag(self) -> str:
|
278
|
+
return self._tag
|
279
|
+
|
280
|
+
@property
|
281
|
+
def references(self) -> dict[str, set[str]]:
|
282
|
+
return self._refs
|
283
|
+
|
284
|
+
##
|
285
|
+
# Setters
|
286
|
+
##
|
287
|
+
|
288
|
+
def setLevel(self, level: str) -> None:
|
289
|
+
"""Set the level of the heading if it's a valid value."""
|
290
|
+
if level in nwStyles.H_VALID:
|
291
|
+
self._level = level
|
292
|
+
return
|
293
|
+
|
294
|
+
def setLine(self, line: int) -> None:
|
295
|
+
"""Set the line number of a heading."""
|
296
|
+
self._line = max(0, checkInt(line, 0))
|
297
|
+
return
|
298
|
+
|
299
|
+
def setCounts(self, counts: Sequence[int]) -> None:
|
300
|
+
"""Set the character, word and paragraph count. Make sure the
|
301
|
+
value is an integer and is not smaller than 0.
|
302
|
+
"""
|
303
|
+
if len(counts) == 3:
|
304
|
+
self._counts = (
|
305
|
+
max(0, checkInt(counts[0], 0)),
|
306
|
+
max(0, checkInt(counts[1], 0)),
|
307
|
+
max(0, checkInt(counts[2], 0)),
|
308
|
+
)
|
309
|
+
return
|
310
|
+
|
311
|
+
def setComment(self, comment: str, key: str, text: str) -> None:
|
312
|
+
"""Set the text for a comment and make sure it is a string."""
|
313
|
+
match comment.lower():
|
314
|
+
case "short" | "synopsis" | "summary":
|
315
|
+
self._comments["summary"] = str(text)
|
316
|
+
case "story" if key:
|
317
|
+
self._cache.story.add(key)
|
318
|
+
self._comments[f"story.{key}"] = str(text)
|
319
|
+
return
|
320
|
+
|
321
|
+
def setTag(self, tag: str) -> None:
|
322
|
+
"""Set the tag for references, and make sure it is a string."""
|
323
|
+
self._tag = str(tag).lower()
|
324
|
+
return
|
325
|
+
|
326
|
+
def addReference(self, tag: str, keyword: str) -> None:
|
327
|
+
"""Add a record of a reference tag, and what keyword types it is
|
328
|
+
associated with.
|
329
|
+
"""
|
330
|
+
if keyword in nwKeyWords.VALID_KEYS:
|
331
|
+
tag = tag.lower()
|
332
|
+
if tag not in self._refs:
|
333
|
+
self._refs[tag] = set()
|
334
|
+
self._refs[tag].add(keyword)
|
335
|
+
return
|
336
|
+
|
337
|
+
##
|
338
|
+
# Getters
|
339
|
+
##
|
340
|
+
|
341
|
+
def getReferences(self) -> dict[str, list[str]]:
|
342
|
+
"""Extract all references for this heading."""
|
343
|
+
refs = {x: [] for x in nwKeyWords.VALID_KEYS}
|
344
|
+
for tag, types in self._refs.items():
|
345
|
+
for keyword in types:
|
346
|
+
if keyword in refs and (name := self._cache.tags.tagName(tag)):
|
347
|
+
refs[keyword].append(name)
|
348
|
+
return refs
|
349
|
+
|
350
|
+
def getReferencesByKeyword(self, keyword: str) -> list[str]:
|
351
|
+
"""Extract all references for this heading."""
|
352
|
+
refs = []
|
353
|
+
for tag, types in self._refs.items():
|
354
|
+
if keyword in types and (name := self._cache.tags.tagName(tag)):
|
355
|
+
refs.append(name)
|
356
|
+
return refs
|
357
|
+
|
358
|
+
##
|
359
|
+
# Data Methods
|
360
|
+
##
|
361
|
+
|
362
|
+
def packData(self) -> dict:
|
363
|
+
"""Pack the values into a dictionary for saving to cache."""
|
364
|
+
data = {}
|
365
|
+
data["meta"] = {
|
366
|
+
"level": self._level,
|
367
|
+
"title": self._title,
|
368
|
+
"line": self._line,
|
369
|
+
"tag": self._tag,
|
370
|
+
"counts": self._counts,
|
371
|
+
}
|
372
|
+
if self._refs:
|
373
|
+
data["refs"] = {k: ",".join(sorted(list(v))) for k, v in self._refs.items()}
|
374
|
+
if self._comments:
|
375
|
+
data.update(self._comments)
|
376
|
+
return data
|
377
|
+
|
378
|
+
def unpackData(self, data: dict) -> None:
|
379
|
+
"""Unpack a heading entry from a dictionary."""
|
380
|
+
for key, entry in data.items():
|
381
|
+
if key == "meta":
|
382
|
+
self.setLevel(entry.get("level", "H0"))
|
383
|
+
self._title = str(entry.get("title", ""))
|
384
|
+
self._tag = str(entry.get("tag", ""))
|
385
|
+
self.setLine(entry.get("line", 0))
|
386
|
+
self.setCounts(entry.get("counts", [0, 0, 0]))
|
387
|
+
elif key == "refs":
|
388
|
+
for tag, value in entry.items():
|
389
|
+
if not isinstance(tag, str):
|
390
|
+
raise ValueError("Heading reference key must be a string")
|
391
|
+
if not isinstance(value, str):
|
392
|
+
raise ValueError("Heading reference value must be a string")
|
393
|
+
for keyword in value.split(","):
|
394
|
+
if keyword in nwKeyWords.VALID_KEYS:
|
395
|
+
self.addReference(tag, keyword)
|
396
|
+
else:
|
397
|
+
raise ValueError("Heading reference contains an invalid keyword")
|
398
|
+
elif key == "summary" or key.startswith("story"):
|
399
|
+
comment, _, kind = str(key).partition(".")
|
400
|
+
self.setComment(comment, compact(kind), str(entry))
|
401
|
+
else:
|
402
|
+
raise KeyError("Unknown key in heading entry")
|
403
|
+
return
|
novelwriter/core/item.py
CHANGED
@@ -27,8 +27,6 @@ import logging
|
|
27
27
|
|
28
28
|
from typing import TYPE_CHECKING, Any
|
29
29
|
|
30
|
-
from PyQt5.QtGui import QFont, QIcon
|
31
|
-
|
32
30
|
from novelwriter import CONFIG, SHARED
|
33
31
|
from novelwriter.common import (
|
34
32
|
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified,
|
@@ -37,7 +35,9 @@ from novelwriter.common import (
|
|
37
35
|
from novelwriter.constants import nwLabels, nwStyles, trConst
|
38
36
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
39
37
|
|
40
|
-
if TYPE_CHECKING:
|
38
|
+
if TYPE_CHECKING:
|
39
|
+
from PyQt6.QtGui import QFont, QIcon
|
40
|
+
|
41
41
|
from novelwriter.core.project import NWProject
|
42
42
|
|
43
43
|
logger = logging.getLogger(__name__)
|
@@ -53,10 +53,10 @@ class NWItem:
|
|
53
53
|
"""
|
54
54
|
|
55
55
|
__slots__ = (
|
56
|
-
"
|
57
|
-
"
|
58
|
-
"
|
59
|
-
"
|
56
|
+
"_active", "_charCount", "_class", "_cursorPos", "_expanded",
|
57
|
+
"_handle", "_heading", "_import", "_initCount", "_layout", "_name",
|
58
|
+
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
|
59
|
+
"_type", "_wordCount",
|
60
60
|
)
|
61
61
|
|
62
62
|
def __init__(self, project: NWProject, handle: str) -> None:
|
@@ -264,25 +264,25 @@ class NWItem:
|
|
264
264
|
@classmethod
|
265
265
|
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
266
266
|
"""Make a copy of an item."""
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
return
|
267
|
+
new = cls(source._project, handle)
|
268
|
+
new._name = source._name
|
269
|
+
new._parent = source._parent
|
270
|
+
new._root = source._root
|
271
|
+
new._order = source._order
|
272
|
+
new._type = source._type
|
273
|
+
new._class = source._class
|
274
|
+
new._layout = source._layout
|
275
|
+
new._status = source._status
|
276
|
+
new._import = source._import
|
277
|
+
new._active = source._active
|
278
|
+
new._expanded = source._expanded
|
279
|
+
new._heading = source._heading
|
280
|
+
new._charCount = source._charCount
|
281
|
+
new._wordCount = source._wordCount
|
282
|
+
new._paraCount = source._paraCount
|
283
|
+
new._cursorPos = source._cursorPos
|
284
|
+
new._initCount = source._initCount
|
285
|
+
return new
|
286
286
|
|
287
287
|
##
|
288
288
|
# Action Methods
|
@@ -293,6 +293,12 @@ class NWItem:
|
|
293
293
|
self._project.tree.refreshItems([self._handle])
|
294
294
|
return
|
295
295
|
|
296
|
+
def notifyNovelStructureChange(self) -> None:
|
297
|
+
"""Notify that the structure of a novel has changed."""
|
298
|
+
if self._root and self._class == nwItemClass.NOVEL:
|
299
|
+
self._project.tree.novelStructureChanged(self._root)
|
300
|
+
return
|
301
|
+
|
296
302
|
##
|
297
303
|
# Lookup Methods
|
298
304
|
##
|
@@ -350,11 +356,12 @@ class NWItem:
|
|
350
356
|
"""
|
351
357
|
if self.isFileType():
|
352
358
|
key = "checked" if self._active else "unchecked"
|
359
|
+
color = "green" if self._active else "red"
|
353
360
|
text = trConst(nwLabels.ACTIVE_NAME[key])
|
354
|
-
icon = SHARED.theme.getIcon(key)
|
361
|
+
icon = SHARED.theme.getIcon(key, color)
|
355
362
|
else:
|
356
363
|
text = ""
|
357
|
-
icon = SHARED.theme.getIcon("noncheckable")
|
364
|
+
icon = SHARED.theme.getIcon("noncheckable", "faded")
|
358
365
|
return text, icon
|
359
366
|
|
360
367
|
##
|
novelwriter/core/itemmodel.py
CHANGED
@@ -28,8 +28,8 @@ import logging
|
|
28
28
|
|
29
29
|
from typing import TYPE_CHECKING
|
30
30
|
|
31
|
-
from
|
32
|
-
from
|
31
|
+
from PyQt6.QtCore import QAbstractItemModel, QMimeData, QModelIndex, Qt
|
32
|
+
from PyQt6.QtGui import QFont, QIcon
|
33
33
|
|
34
34
|
from novelwriter.common import decodeMimeHandles, encodeMimeHandles, minmax
|
35
35
|
from novelwriter.constants import nwConst
|
@@ -37,7 +37,7 @@ from novelwriter.core.item import NWItem
|
|
37
37
|
from novelwriter.enum import nwItemClass
|
38
38
|
from novelwriter.types import QtAlignRight
|
39
39
|
|
40
|
-
if TYPE_CHECKING:
|
40
|
+
if TYPE_CHECKING:
|
41
41
|
from novelwriter.core.tree import NWTree
|
42
42
|
|
43
43
|
logger = logging.getLogger(__name__)
|
@@ -60,9 +60,7 @@ NODE_FLAGS = Qt.ItemFlag.ItemIsEnabled
|
|
60
60
|
NODE_FLAGS |= Qt.ItemFlag.ItemIsSelectable
|
61
61
|
NODE_FLAGS |= Qt.ItemFlag.ItemIsDropEnabled
|
62
62
|
|
63
|
-
|
64
|
-
# Requires Python 3.10
|
65
|
-
T_NodeData = str | QIcon | QFont | Qt.AlignmentFlag | None
|
63
|
+
T_NodeData = str | QIcon | QFont | Qt.AlignmentFlag | None
|
66
64
|
|
67
65
|
|
68
66
|
class ProjectNode:
|
@@ -91,7 +89,7 @@ class ProjectNode:
|
|
91
89
|
C_ACTIVE = 2
|
92
90
|
C_STATUS = 3
|
93
91
|
|
94
|
-
__slots__ = ("
|
92
|
+
__slots__ = ("_cache", "_children", "_count", "_flags", "_item", "_parent", "_row")
|
95
93
|
|
96
94
|
def __init__(self, item: NWItem) -> None:
|
97
95
|
self._item = item
|
@@ -164,7 +162,7 @@ class ProjectNode:
|
|
164
162
|
|
165
163
|
def updateCount(self, propagate: bool = True) -> None:
|
166
164
|
"""Update counts, and propagate upwards in the tree."""
|
167
|
-
self._count = self._item.wordCount + sum(c._count for c in self._children)
|
165
|
+
self._count = self._item.wordCount + sum(c._count for c in self._children) # noqa: SLF001
|
168
166
|
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
169
167
|
if propagate and (parent := self._parent):
|
170
168
|
parent.updateCount()
|
@@ -195,7 +193,7 @@ class ProjectNode:
|
|
195
193
|
return self._parent
|
196
194
|
|
197
195
|
def child(self, row: int) -> ProjectNode | None:
|
198
|
-
"""Return a child
|
196
|
+
"""Return a child of the node."""
|
199
197
|
if 0 <= row < len(self._children):
|
200
198
|
return self._children[row]
|
201
199
|
return None
|
@@ -220,6 +218,7 @@ class ProjectNode:
|
|
220
218
|
child._row = len(self._children)
|
221
219
|
self._children.append(child)
|
222
220
|
self._refreshChildrenPos()
|
221
|
+
self._item.notifyNovelStructureChange()
|
223
222
|
return
|
224
223
|
|
225
224
|
def takeChild(self, pos: int) -> ProjectNode | None:
|
@@ -228,6 +227,7 @@ class ProjectNode:
|
|
228
227
|
node = self._children.pop(pos)
|
229
228
|
self._refreshChildrenPos()
|
230
229
|
self.updateCount()
|
230
|
+
self._item.notifyNovelStructureChange()
|
231
231
|
return node
|
232
232
|
return None
|
233
233
|
|
@@ -238,6 +238,7 @@ class ProjectNode:
|
|
238
238
|
node = self._children.pop(source)
|
239
239
|
self._children.insert(target, node)
|
240
240
|
self._refreshChildrenPos()
|
241
|
+
self._item.notifyNovelStructureChange()
|
241
242
|
return
|
242
243
|
|
243
244
|
def setExpanded(self, state: bool) -> None:
|
@@ -256,13 +257,13 @@ class ProjectNode:
|
|
256
257
|
"""Recursively add all nodes to a list."""
|
257
258
|
for node in self._children:
|
258
259
|
children.append(node)
|
259
|
-
node._recursiveAppendChildren(children)
|
260
|
+
node._recursiveAppendChildren(children) # noqa: SLF001
|
260
261
|
return
|
261
262
|
|
262
263
|
def _refreshChildrenPos(self) -> None:
|
263
264
|
"""Update the row value on all children."""
|
264
265
|
for n, child in enumerate(self._children):
|
265
|
-
child._row = n
|
266
|
+
child._row = n # noqa: SLF001
|
266
267
|
child.item.setOrder(n)
|
267
268
|
return
|
268
269
|
|
@@ -289,12 +290,12 @@ class ProjectModel(QAbstractItemModel):
|
|
289
290
|
methods needed primarily by the project tree GUI component.
|
290
291
|
"""
|
291
292
|
|
292
|
-
__slots__ = ("
|
293
|
+
__slots__ = ("_root", "_tree")
|
293
294
|
|
294
295
|
def __init__(self, tree: NWTree) -> None:
|
295
296
|
super().__init__()
|
296
297
|
self._tree = tree
|
297
|
-
self._root = ProjectNode(NWItem(tree.
|
298
|
+
self._root = ProjectNode(NWItem(tree.project, INV_ROOT))
|
298
299
|
self._root.item.setName("Invisible Root")
|
299
300
|
logger.debug("Ready: ProjectModel")
|
300
301
|
return
|
@@ -332,8 +333,9 @@ class ProjectModel(QAbstractItemModel):
|
|
332
333
|
return self.createIndex(parent.row(), 0, parent)
|
333
334
|
return QModelIndex()
|
334
335
|
|
335
|
-
def index(self, row: int, column: int, parent: QModelIndex =
|
336
|
-
"""
|
336
|
+
def index(self, row: int, column: int, parent: QModelIndex | None = None) -> QModelIndex:
|
337
|
+
"""Get the index of a child item of a parent."""
|
338
|
+
parent = parent or QModelIndex()
|
337
339
|
if self.hasIndex(row, column, parent):
|
338
340
|
node: ProjectNode = parent.internalPointer() if parent.isValid() else self._root
|
339
341
|
if child := node.child(row):
|
@@ -389,10 +391,10 @@ class ProjectModel(QAbstractItemModel):
|
|
389
391
|
) -> bool:
|
390
392
|
"""Process mime data drop."""
|
391
393
|
if self.canDropMimeData(data, action, row, column, parent):
|
392
|
-
items = [
|
393
|
-
|
394
|
-
if (index := self.indexFromHandle(handle)).isValid()
|
395
|
-
|
394
|
+
items = [
|
395
|
+
index for handle in decodeMimeHandles(data)
|
396
|
+
if (index := self.indexFromHandle(handle)).isValid()
|
397
|
+
]
|
396
398
|
self.multiMove(items, parent, row)
|
397
399
|
return True
|
398
400
|
return False
|
@@ -488,7 +490,7 @@ class ProjectModel(QAbstractItemModel):
|
|
488
490
|
if temp := self.removeChild(index.parent(), index.row()):
|
489
491
|
self.insertChild(temp, target, pos)
|
490
492
|
for child in reversed(node.allChildren()):
|
491
|
-
node._updateRelationships(child)
|
493
|
+
node._updateRelationships(child) # noqa: SLF001
|
492
494
|
child.item.notifyToRefresh()
|
493
495
|
node.item.notifyToRefresh()
|
494
496
|
return
|
@@ -499,16 +501,15 @@ class ProjectModel(QAbstractItemModel):
|
|
499
501
|
|
500
502
|
def clear(self) -> None:
|
501
503
|
"""Clear the project model."""
|
502
|
-
self._root.
|
504
|
+
self._root.children.clear()
|
503
505
|
return
|
504
506
|
|
505
507
|
def allExpanded(self) -> list[QModelIndex]:
|
506
508
|
"""Return a list of all expanded items."""
|
507
|
-
|
508
|
-
|
509
|
-
if node.
|
510
|
-
|
511
|
-
return expanded
|
509
|
+
return [
|
510
|
+
self.createIndex(node.row(), 0, node) for node in self._root.allChildren()
|
511
|
+
if node.item.isExpanded
|
512
|
+
]
|
512
513
|
|
513
514
|
def trashSelection(self, indices: list[QModelIndex]) -> bool:
|
514
515
|
"""Check if a selection of indices are all in trash or not."""
|