novelWriter 2.6.3__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/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 +50 -36
- 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 +11 -14
- novelwriter/gui/mainmenu.py +146 -145
- novelwriter/gui/noveltree.py +239 -405
- novelwriter/gui/outline.py +133 -72
- 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 +101 -114
- 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.3.dist-info → novelwriter-2.7b1.dist-info}/METADATA +6 -6
- novelwriter-2.7b1.dist-info/RECORD +159 -0
- {novelWriter-2.6.3.dist-info → novelwriter-2.7b1.dist-info}/WHEEL +1 -1
- novelWriter-2.6.3.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.3.dist-info → novelwriter-2.7b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.6.3.dist-info → novelwriter-2.7b1.dist-info/licenses}/LICENSE.md +0 -0
- {novelWriter-2.6.3.dist-info → novelwriter-2.7b1.dist-info}/top_level.txt +0 -0
novelwriter/gui/noveltree.py
CHANGED
@@ -3,9 +3,11 @@ novelWriter – GUI Novel Tree
|
|
3
3
|
============================
|
4
4
|
|
5
5
|
File History:
|
6
|
-
Created:
|
7
|
-
Created:
|
8
|
-
Created:
|
6
|
+
Created: 2020-12-20 [1.1rc1] GuiNovelTree
|
7
|
+
Created: 2022-06-12 [2.0rc1] GuiNovelView
|
8
|
+
Created: 2022-06-12 [2.0rc1] GuiNovelToolBar
|
9
|
+
Rewritten: 2025-02-22 [2.7b1] GuiNovelView
|
10
|
+
Rewritten: 2025-02-22 [2.7b1] GuiNovelToolBar
|
9
11
|
|
10
12
|
This file is a part of novelWriter
|
11
13
|
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
|
@@ -28,40 +30,30 @@ from __future__ import annotations
|
|
28
30
|
import logging
|
29
31
|
|
30
32
|
from enum import Enum
|
31
|
-
from time import time
|
32
33
|
|
33
|
-
from
|
34
|
-
from
|
35
|
-
from
|
36
|
-
QAbstractItemView,
|
37
|
-
|
34
|
+
from PyQt6.QtCore import QModelIndex, QPoint, pyqtSignal, pyqtSlot
|
35
|
+
from PyQt6.QtGui import QActionGroup, QFont, QPainter, QPalette, QResizeEvent
|
36
|
+
from PyQt6.QtWidgets import (
|
37
|
+
QAbstractItemView, QFrame, QHBoxLayout, QInputDialog, QMenu,
|
38
|
+
QStyleOptionViewItem, QToolTip, QVBoxLayout, QWidget
|
38
39
|
)
|
39
40
|
|
40
41
|
from novelwriter import CONFIG, SHARED
|
41
|
-
from novelwriter.common import minmax, qtLambda
|
42
|
-
from novelwriter.constants import nwKeyWords, nwLabels,
|
43
|
-
from novelwriter.core.
|
44
|
-
from novelwriter.enum import nwChange, nwDocMode,
|
45
|
-
from novelwriter.extensions.modified import NIconToolButton
|
42
|
+
from novelwriter.common import minmax, qtAddAction, qtAddMenu, qtLambda
|
43
|
+
from novelwriter.constants import nwKeyWords, nwLabels, trConst
|
44
|
+
from novelwriter.core.novelmodel import NovelModel
|
45
|
+
from novelwriter.enum import nwChange, nwDocMode, nwNovelExtra, nwOutline
|
46
|
+
from novelwriter.extensions.modified import NIconToolButton, NTreeView
|
46
47
|
from novelwriter.extensions.novelselector import NovelSelector
|
47
48
|
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
48
49
|
from novelwriter.types import (
|
49
|
-
|
50
|
-
|
51
|
-
QtSizeExpanding, QtUserRole
|
50
|
+
QtHeaderStretch, QtHeaderToContents, QtScrollAlwaysOff, QtScrollAsNeeded,
|
51
|
+
QtSizeExpanding
|
52
52
|
)
|
53
53
|
|
54
54
|
logger = logging.getLogger(__name__)
|
55
55
|
|
56
56
|
|
57
|
-
class NovelTreeColumn(Enum):
|
58
|
-
|
59
|
-
HIDDEN = 0
|
60
|
-
POV = 1
|
61
|
-
FOCUS = 2
|
62
|
-
PLOT = 3
|
63
|
-
|
64
|
-
|
65
57
|
class GuiNovelView(QWidget):
|
66
58
|
|
67
59
|
# Signals for user interaction with the novel tree
|
@@ -86,6 +78,7 @@ class GuiNovelView(QWidget):
|
|
86
78
|
self.setLayout(self.outerBox)
|
87
79
|
|
88
80
|
# Function Mappings
|
81
|
+
self.setActive = self.novelBar.setActive
|
89
82
|
self.getSelectedHandle = self.novelTree.getSelectedHandle
|
90
83
|
|
91
84
|
return
|
@@ -97,8 +90,6 @@ class GuiNovelView(QWidget):
|
|
97
90
|
def updateTheme(self) -> None:
|
98
91
|
"""Update theme elements."""
|
99
92
|
self.novelBar.updateTheme()
|
100
|
-
self.novelTree.updateTheme()
|
101
|
-
self.refreshTree()
|
102
93
|
return
|
103
94
|
|
104
95
|
def initSettings(self) -> None:
|
@@ -108,21 +99,18 @@ class GuiNovelView(QWidget):
|
|
108
99
|
|
109
100
|
def clearNovelView(self) -> None:
|
110
101
|
"""Clear project-related GUI content."""
|
111
|
-
self.novelTree.clearContent()
|
112
102
|
self.novelBar.clearContent()
|
113
103
|
self.novelBar.setEnabled(False)
|
104
|
+
self.novelTree.clearContent()
|
114
105
|
return
|
115
106
|
|
116
107
|
def openProjectTasks(self) -> None:
|
117
108
|
"""Run open project tasks."""
|
118
|
-
lastNovel = SHARED.project.data.getLastHandle("
|
119
|
-
if lastNovel and lastNovel not in SHARED.project.tree:
|
120
|
-
lastNovel = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
121
|
-
|
109
|
+
lastNovel = SHARED.project.data.getLastHandle("novel")
|
122
110
|
logger.debug("Setting novel tree to root item '%s'", lastNovel)
|
123
111
|
|
124
112
|
lastCol = SHARED.project.options.getEnum(
|
125
|
-
"GuiNovelView", "lastCol",
|
113
|
+
"GuiNovelView", "lastCol", nwNovelExtra, nwNovelExtra.HIDDEN
|
126
114
|
)
|
127
115
|
lastColSize = SHARED.project.options.getInt(
|
128
116
|
"GuiNovelView", "lastColSize", 25
|
@@ -140,13 +128,17 @@ class GuiNovelView(QWidget):
|
|
140
128
|
|
141
129
|
def closeProjectTasks(self) -> None:
|
142
130
|
"""Run closing project tasks."""
|
131
|
+
logger.debug("Saving State: GuiNovelView")
|
132
|
+
|
143
133
|
lastColType = self.novelTree.lastColType
|
144
134
|
lastColSize = self.novelTree.lastColSize
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
135
|
+
|
136
|
+
options = SHARED.project.options
|
137
|
+
options.setValue("GuiNovelView", "lastCol", lastColType)
|
138
|
+
options.setValue("GuiNovelView", "lastColSize", lastColSize)
|
139
|
+
|
149
140
|
self.clearNovelView()
|
141
|
+
|
150
142
|
return
|
151
143
|
|
152
144
|
def setTreeFocus(self) -> None:
|
@@ -162,32 +154,24 @@ class GuiNovelView(QWidget):
|
|
162
154
|
# Public Slots
|
163
155
|
##
|
164
156
|
|
157
|
+
@pyqtSlot(str)
|
158
|
+
def setCurrentNovel(self, rootHandle: str | None) -> None:
|
159
|
+
"""Set the current novel to display."""
|
160
|
+
self.novelTree.setNovelModel(rootHandle)
|
161
|
+
return
|
162
|
+
|
165
163
|
@pyqtSlot(str)
|
166
164
|
def setActiveHandle(self, tHandle: str) -> None:
|
167
165
|
"""Highlight the rows associated with a given handle."""
|
168
166
|
self.novelTree.setActiveHandle(tHandle)
|
169
167
|
return
|
170
168
|
|
171
|
-
@pyqtSlot()
|
172
|
-
def refreshTree(self) -> None:
|
173
|
-
"""Refresh the current tree."""
|
174
|
-
self.novelTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("novelTree"))
|
175
|
-
return
|
176
|
-
|
177
169
|
@pyqtSlot(str, Enum)
|
178
170
|
def updateRootItem(self, tHandle: str, change: nwChange) -> None:
|
179
171
|
"""If any root item changes, rebuild the novel root menu."""
|
180
172
|
self.novelBar.buildNovelRootMenu()
|
181
173
|
return
|
182
174
|
|
183
|
-
@pyqtSlot(str)
|
184
|
-
def updateNovelItemMeta(self, tHandle: str) -> None:
|
185
|
-
"""The meta data of a novel item has changed, and the tree item
|
186
|
-
needs to be refreshed.
|
187
|
-
"""
|
188
|
-
self.novelTree.refreshHandle(tHandle)
|
189
|
-
return
|
190
|
-
|
191
175
|
|
192
176
|
class GuiNovelToolBar(QWidget):
|
193
177
|
|
@@ -198,10 +182,13 @@ class GuiNovelToolBar(QWidget):
|
|
198
182
|
|
199
183
|
self.novelView = novelView
|
200
184
|
|
185
|
+
self._active = False
|
186
|
+
self._refresh: dict[str, bool] = {}
|
187
|
+
|
201
188
|
iSz = SHARED.theme.baseIconSize
|
202
|
-
mPx = CONFIG.pxInt(2)
|
203
189
|
|
204
190
|
self.setContentsMargins(0, 0, 0, 0)
|
191
|
+
self.setBackgroundRole(QPalette.ColorRole.Base)
|
205
192
|
self.setAutoFillBackground(True)
|
206
193
|
|
207
194
|
# Novel Selector
|
@@ -211,7 +198,7 @@ class GuiNovelToolBar(QWidget):
|
|
211
198
|
self.novelValue = NovelSelector(self)
|
212
199
|
self.novelValue.setFont(selFont)
|
213
200
|
self.novelValue.setListFormat(self.tr("Outline of {0}"))
|
214
|
-
self.novelValue.setMinimumWidth(
|
201
|
+
self.novelValue.setMinimumWidth(150)
|
215
202
|
self.novelValue.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
216
203
|
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
|
217
204
|
|
@@ -222,21 +209,21 @@ class GuiNovelToolBar(QWidget):
|
|
222
209
|
# Refresh Button
|
223
210
|
self.tbRefresh = NIconToolButton(self, iSz)
|
224
211
|
self.tbRefresh.setToolTip(self.tr("Refresh"))
|
225
|
-
self.tbRefresh.clicked.connect(self.
|
212
|
+
self.tbRefresh.clicked.connect(self._forceRefreshNovelTree)
|
226
213
|
|
227
214
|
# More Options Menu
|
228
215
|
self.mMore = QMenu(self)
|
229
216
|
|
230
|
-
self.mLastCol = self.mMore
|
217
|
+
self.mLastCol = qtAddMenu(self.mMore, self.tr("Last Column"))
|
231
218
|
self.gLastCol = QActionGroup(self.mMore)
|
232
219
|
self.aLastCol = {}
|
233
|
-
self._addLastColAction(
|
234
|
-
self._addLastColAction(
|
235
|
-
self._addLastColAction(
|
236
|
-
self._addLastColAction(
|
220
|
+
self._addLastColAction(nwNovelExtra.HIDDEN, self.tr("Hidden"))
|
221
|
+
self._addLastColAction(nwNovelExtra.POV, self.tr("Point of View Character"))
|
222
|
+
self._addLastColAction(nwNovelExtra.FOCUS, self.tr("Focus Character"))
|
223
|
+
self._addLastColAction(nwNovelExtra.PLOT, self.tr("Novel Plot"))
|
237
224
|
|
238
225
|
self.mLastCol.addSeparator()
|
239
|
-
self.aLastColSize = self.mLastCol
|
226
|
+
self.aLastColSize = qtAddAction(self.mLastCol, self.tr("Column Size"))
|
240
227
|
self.aLastColSize.triggered.connect(self._selectLastColumnSize)
|
241
228
|
|
242
229
|
self.tbMore = NIconToolButton(self, iSz)
|
@@ -249,13 +236,16 @@ class GuiNovelToolBar(QWidget):
|
|
249
236
|
self.outerBox.addWidget(self.tbNovel)
|
250
237
|
self.outerBox.addWidget(self.tbRefresh)
|
251
238
|
self.outerBox.addWidget(self.tbMore)
|
252
|
-
self.outerBox.setContentsMargins(
|
239
|
+
self.outerBox.setContentsMargins(4, 2, 0, 2)
|
253
240
|
self.outerBox.setSpacing(0)
|
254
241
|
|
255
242
|
self.setLayout(self.outerBox)
|
256
243
|
|
257
244
|
self.updateTheme()
|
258
245
|
|
246
|
+
# Connect Signals
|
247
|
+
SHARED.novelStructureChanged.connect(self._refreshNovelTree)
|
248
|
+
|
259
249
|
logger.debug("Ready: GuiNovelToolBar")
|
260
250
|
|
261
251
|
return
|
@@ -267,13 +257,9 @@ class GuiNovelToolBar(QWidget):
|
|
267
257
|
def updateTheme(self) -> None:
|
268
258
|
"""Update theme elements."""
|
269
259
|
# Icons
|
270
|
-
self.tbNovel.setThemeIcon("cls_novel")
|
271
|
-
self.tbRefresh.setThemeIcon("refresh")
|
272
|
-
self.tbMore.setThemeIcon("
|
273
|
-
|
274
|
-
qPalette = self.palette()
|
275
|
-
qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
|
276
|
-
self.setPalette(qPalette)
|
260
|
+
self.tbNovel.setThemeIcon("cls_novel", "red")
|
261
|
+
self.tbRefresh.setThemeIcon("refresh", "green")
|
262
|
+
self.tbMore.setThemeIcon("more_vertical")
|
277
263
|
|
278
264
|
# StyleSheets
|
279
265
|
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
@@ -285,9 +271,11 @@ class GuiNovelToolBar(QWidget):
|
|
285
271
|
"QComboBox {border-style: none; padding-left: 0;} "
|
286
272
|
"QComboBox::drop-down {border-style: none}"
|
287
273
|
)
|
288
|
-
self.novelValue.
|
274
|
+
self.novelValue.updateTheme()
|
289
275
|
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
290
276
|
|
277
|
+
self._forceRefreshNovelTree()
|
278
|
+
|
291
279
|
return
|
292
280
|
|
293
281
|
def clearContent(self) -> None:
|
@@ -299,19 +287,39 @@ class GuiNovelToolBar(QWidget):
|
|
299
287
|
def buildNovelRootMenu(self) -> None:
|
300
288
|
"""Build the novel root menu."""
|
301
289
|
self.novelValue.refreshNovelList()
|
290
|
+
self.novelView.setCurrentNovel(self.novelValue.handle)
|
302
291
|
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
303
292
|
return
|
304
293
|
|
305
294
|
def setCurrentRoot(self, rootHandle: str | None) -> None:
|
306
295
|
"""Set the current active root handle."""
|
296
|
+
if rootHandle is None or rootHandle not in SHARED.project.tree:
|
297
|
+
rootHandle = self.novelValue.firstHandle
|
307
298
|
self.novelValue.setHandle(rootHandle)
|
308
|
-
|
299
|
+
SHARED.project.data.setLastHandle(rootHandle, "novel")
|
300
|
+
self.novelView.setCurrentNovel(rootHandle)
|
309
301
|
return
|
310
302
|
|
311
|
-
def setLastColType(self, colType:
|
303
|
+
def setLastColType(self, colType: nwNovelExtra, doRefresh: bool = True) -> None:
|
312
304
|
"""Set the last column type."""
|
313
305
|
self.aLastCol[colType].setChecked(True)
|
314
|
-
self.novelView.novelTree.setLastColType(colType
|
306
|
+
self.novelView.novelTree.setLastColType(colType)
|
307
|
+
if doRefresh:
|
308
|
+
self._forceRefreshNovelTree()
|
309
|
+
self.novelView.novelTree.resizeColumns()
|
310
|
+
return
|
311
|
+
|
312
|
+
def setActive(self, state: bool) -> None:
|
313
|
+
"""Set the widget active state, which enables automatic tree
|
314
|
+
refresh when content structure changes.
|
315
|
+
"""
|
316
|
+
self._active = state
|
317
|
+
if (
|
318
|
+
self._active
|
319
|
+
and (handle := self.novelValue.handle)
|
320
|
+
and self._refresh.get(handle, False)
|
321
|
+
):
|
322
|
+
self._refreshNovelTree(self.novelValue.handle)
|
315
323
|
return
|
316
324
|
|
317
325
|
##
|
@@ -319,10 +327,22 @@ class GuiNovelToolBar(QWidget):
|
|
319
327
|
##
|
320
328
|
|
321
329
|
@pyqtSlot()
|
322
|
-
def
|
330
|
+
def _forceRefreshNovelTree(self) -> None:
|
323
331
|
"""Rebuild the current tree."""
|
324
|
-
|
325
|
-
|
332
|
+
if tHandle := self.novelValue.handle:
|
333
|
+
self.novelView.setCurrentNovel(tHandle)
|
334
|
+
SHARED.project.index.refreshNovelModel(tHandle)
|
335
|
+
self._refresh[tHandle] = False
|
336
|
+
return
|
337
|
+
|
338
|
+
@pyqtSlot(str)
|
339
|
+
def _refreshNovelTree(self, tHandle: str) -> None:
|
340
|
+
"""Refresh or schedule refresh of a novel tree."""
|
341
|
+
if self._active:
|
342
|
+
SHARED.project.index.refreshNovelModel(tHandle)
|
343
|
+
self._refresh[tHandle] = False
|
344
|
+
else:
|
345
|
+
self._refresh[tHandle] = True
|
326
346
|
return
|
327
347
|
|
328
348
|
@pyqtSlot()
|
@@ -334,16 +354,16 @@ class GuiNovelToolBar(QWidget):
|
|
334
354
|
)
|
335
355
|
if isOk:
|
336
356
|
self.novelView.novelTree.setLastColSize(newSize)
|
337
|
-
self.
|
357
|
+
self.novelView.novelTree.resizeColumns()
|
338
358
|
return
|
339
359
|
|
340
360
|
##
|
341
361
|
# Internal Functions
|
342
362
|
##
|
343
363
|
|
344
|
-
def _addLastColAction(self, colType:
|
364
|
+
def _addLastColAction(self, colType: nwNovelExtra, actionLabel: str) -> None:
|
345
365
|
"""Add a column selection entry to the last column menu."""
|
346
|
-
aLast = self.mLastCol
|
366
|
+
aLast = qtAddAction(self.mLastCol, actionLabel)
|
347
367
|
aLast.setCheckable(True)
|
348
368
|
aLast.setActionGroup(self.gLastCol)
|
349
369
|
aLast.triggered.connect(qtLambda(self.setLastColType, colType))
|
@@ -351,18 +371,7 @@ class GuiNovelToolBar(QWidget):
|
|
351
371
|
return
|
352
372
|
|
353
373
|
|
354
|
-
class GuiNovelTree(
|
355
|
-
|
356
|
-
C_DATA = 0
|
357
|
-
C_TITLE = 0
|
358
|
-
C_WORDS = 1
|
359
|
-
C_EXTRA = 2
|
360
|
-
C_MORE = 3
|
361
|
-
|
362
|
-
D_HANDLE = QtUserRole
|
363
|
-
D_TITLE = QtUserRole + 1
|
364
|
-
D_KEY = QtUserRole + 2
|
365
|
-
D_EXTRA = QtUserRole + 3
|
374
|
+
class GuiNovelTree(NTreeView):
|
366
375
|
|
367
376
|
def __init__(self, novelView: GuiNovelView) -> None:
|
368
377
|
super().__init__(parent=novelView)
|
@@ -372,63 +381,30 @@ class GuiNovelTree(QTreeWidget):
|
|
372
381
|
self.novelView = novelView
|
373
382
|
|
374
383
|
# Internal Variables
|
375
|
-
self._lastBuild = 0
|
376
|
-
self._lastCol = NovelTreeColumn.POV
|
377
|
-
self._lastColSize = 0.25
|
378
384
|
self._actHandle = None
|
379
|
-
self.
|
380
|
-
|
381
|
-
# Cached Strings
|
382
|
-
self._povLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
|
383
|
-
self._focLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
|
384
|
-
self._pltLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
|
385
|
-
|
386
|
-
# Build GUI
|
387
|
-
# =========
|
388
|
-
|
389
|
-
iPx = SHARED.theme.baseIconHeight
|
390
|
-
iSz = SHARED.theme.baseIconSize
|
391
|
-
cMg = CONFIG.pxInt(6)
|
385
|
+
self._lastColType = nwNovelExtra.POV
|
386
|
+
self._lastColSize = 0.25
|
392
387
|
|
393
|
-
|
388
|
+
# Widget Setup
|
389
|
+
self.setIconSize(SHARED.theme.baseIconSize)
|
394
390
|
self.setFrameStyle(QFrame.Shape.NoFrame)
|
395
391
|
self.setUniformRowHeights(True)
|
396
392
|
self.setAllColumnsShowFocus(True)
|
397
393
|
self.setHeaderHidden(True)
|
398
|
-
self.setIndentation(
|
399
|
-
self.setColumnCount(4)
|
400
|
-
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
401
|
-
self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
402
|
-
self.setExpandsOnDoubleClick(False)
|
394
|
+
self.setIndentation(2)
|
403
395
|
self.setDragEnabled(False)
|
404
396
|
|
405
|
-
#
|
406
|
-
|
407
|
-
|
408
|
-
treeHeader.setMinimumSectionSize(iPx + cMg)
|
409
|
-
treeHeader.setSectionResizeMode(self.C_TITLE, QtHeaderStretch)
|
410
|
-
treeHeader.setSectionResizeMode(self.C_WORDS, QtHeaderToContents)
|
411
|
-
treeHeader.setSectionResizeMode(self.C_EXTRA, QtHeaderToContents)
|
412
|
-
treeHeader.setSectionResizeMode(self.C_MORE, QtHeaderToContents)
|
413
|
-
|
414
|
-
# Pre-Generate Tree Formatting
|
415
|
-
fH1 = self.font()
|
416
|
-
fH1.setBold(True)
|
417
|
-
fH1.setUnderline(True)
|
418
|
-
|
419
|
-
fH2 = self.font()
|
420
|
-
fH2.setBold(True)
|
421
|
-
|
422
|
-
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
397
|
+
# Set selection options
|
398
|
+
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
399
|
+
self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
423
400
|
|
424
401
|
# Connect signals
|
425
|
-
self.clicked.connect(self.
|
426
|
-
self.
|
427
|
-
self.
|
402
|
+
self.clicked.connect(self._onSingleClick)
|
403
|
+
self.doubleClicked.connect(self._onDoubleClick)
|
404
|
+
self.middleClicked.connect(self._onMiddleClick)
|
428
405
|
|
429
406
|
# Set custom settings
|
430
407
|
self.initSettings()
|
431
|
-
self.updateTheme()
|
432
408
|
|
433
409
|
logger.debug("Ready: GuiNovelTree")
|
434
410
|
|
@@ -436,23 +412,14 @@ class GuiNovelTree(QTreeWidget):
|
|
436
412
|
|
437
413
|
def initSettings(self) -> None:
|
438
414
|
"""Set or update tree widget settings."""
|
439
|
-
# Scroll bars
|
440
415
|
if CONFIG.hideVScroll:
|
441
416
|
self.setVerticalScrollBarPolicy(QtScrollAlwaysOff)
|
442
417
|
else:
|
443
418
|
self.setVerticalScrollBarPolicy(QtScrollAsNeeded)
|
444
|
-
|
445
419
|
if CONFIG.hideHScroll:
|
446
420
|
self.setHorizontalScrollBarPolicy(QtScrollAlwaysOff)
|
447
421
|
else:
|
448
422
|
self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
|
449
|
-
|
450
|
-
return
|
451
|
-
|
452
|
-
def updateTheme(self) -> None:
|
453
|
-
"""Update theme elements."""
|
454
|
-
iPx = SHARED.theme.baseIconHeight
|
455
|
-
self._pMore = SHARED.theme.loadDecoration("deco_doc_more", h=iPx)
|
456
423
|
return
|
457
424
|
|
458
425
|
##
|
@@ -460,313 +427,180 @@ class GuiNovelTree(QTreeWidget):
|
|
460
427
|
##
|
461
428
|
|
462
429
|
@property
|
463
|
-
def lastColType(self) ->
|
464
|
-
|
430
|
+
def lastColType(self) -> nwNovelExtra:
|
431
|
+
"""The data type of the extra column."""
|
432
|
+
return self._lastColType
|
465
433
|
|
466
434
|
@property
|
467
435
|
def lastColSize(self) -> int:
|
436
|
+
"""Return the size of the extra column."""
|
468
437
|
return int(self._lastColSize * 100)
|
469
438
|
|
470
439
|
##
|
471
|
-
#
|
440
|
+
# Getters
|
472
441
|
##
|
473
442
|
|
474
|
-
def clearContent(self) -> None:
|
475
|
-
"""Clear the GUI content and the related maps."""
|
476
|
-
self.clear()
|
477
|
-
self._treeMap = {}
|
478
|
-
self._lastBuild = 0
|
479
|
-
return
|
480
|
-
|
481
|
-
def refreshTree(self, rootHandle: str | None = None, overRide: bool = False) -> None:
|
482
|
-
"""Refresh the tree if it has been changed."""
|
483
|
-
logger.debug("Requesting refresh of the novel tree")
|
484
|
-
if rootHandle is None:
|
485
|
-
rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
486
|
-
|
487
|
-
titleKey = None
|
488
|
-
if selItems := self.selectedItems():
|
489
|
-
titleKey = selItems[0].data(self.C_DATA, self.D_KEY)
|
490
|
-
|
491
|
-
self._populateTree(rootHandle)
|
492
|
-
SHARED.project.data.setLastHandle(rootHandle, "novelTree")
|
493
|
-
|
494
|
-
if titleKey is not None and titleKey in self._treeMap:
|
495
|
-
self._treeMap[titleKey].setSelected(True)
|
496
|
-
|
497
|
-
return
|
498
|
-
|
499
|
-
def refreshHandle(self, tHandle: str) -> None:
|
500
|
-
"""Refresh the data for a given handle."""
|
501
|
-
if idxData := SHARED.project.index.getItemData(tHandle):
|
502
|
-
logger.debug("Refreshing meta data for item '%s'", tHandle)
|
503
|
-
for sTitle, tHeading in idxData.items():
|
504
|
-
sKey = f"{tHandle}:{sTitle}"
|
505
|
-
if trItem := self._treeMap.get(sKey, None):
|
506
|
-
self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
|
507
|
-
else:
|
508
|
-
logger.debug("Heading '%s' not in novel tree", sKey)
|
509
|
-
self.refreshTree()
|
510
|
-
return
|
511
|
-
return
|
512
|
-
|
513
443
|
def getSelectedHandle(self) -> tuple[str | None, str | None]:
|
514
444
|
"""Get the currently selected or active handle. If multiple
|
515
445
|
items are selected, return the first.
|
516
446
|
"""
|
517
|
-
|
518
|
-
|
519
|
-
if isinstance(trItem, QTreeWidgetItem):
|
520
|
-
tHandle = trItem.data(self.C_DATA, self.D_HANDLE)
|
521
|
-
sTitle = trItem.data(self.C_DATA, self.D_TITLE)
|
522
|
-
return tHandle, sTitle
|
447
|
+
if (model := self._getModel()) and (index := self.currentIndex()).isValid():
|
448
|
+
return model.handle(index), model.key(index)
|
523
449
|
return None, None
|
524
450
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
logger.debug("Changing last column to %s", colType.name)
|
529
|
-
self._lastCol = colType
|
530
|
-
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
|
531
|
-
if doRefresh:
|
532
|
-
lastNovel = SHARED.project.data.getLastHandle("novelTree")
|
533
|
-
self.refreshTree(rootHandle=lastNovel, overRide=True)
|
534
|
-
return
|
451
|
+
##
|
452
|
+
# Setters
|
453
|
+
##
|
535
454
|
|
536
|
-
def
|
537
|
-
"""Set the
|
538
|
-
|
455
|
+
def setNovelModel(self, tHandle: str | None) -> None:
|
456
|
+
"""Set the current novel model."""
|
457
|
+
if tHandle and (model := SHARED.project.index.getNovelModel(tHandle)):
|
458
|
+
if model is not self.model():
|
459
|
+
self.setModel(model)
|
460
|
+
self.resizeColumns()
|
461
|
+
else:
|
462
|
+
self.clearContent()
|
539
463
|
return
|
540
464
|
|
541
465
|
def setActiveHandle(self, tHandle: str | None) -> None:
|
542
|
-
"""
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
if pHandle := self._actHandle:
|
547
|
-
for key, item in self._treeMap.items():
|
548
|
-
if key.startswith(pHandle):
|
549
|
-
for i in range(self.columnCount()):
|
550
|
-
item.setBackground(i, brushOff)
|
551
|
-
if tHandle:
|
552
|
-
for key, item in self._treeMap.items():
|
553
|
-
if key.startswith(tHandle):
|
554
|
-
for i in range(self.columnCount()):
|
555
|
-
item.setBackground(i, brushOn)
|
556
|
-
if not didScroll:
|
557
|
-
self.scrollToItem(item, QAbstractItemView.ScrollHint.PositionAtCenter)
|
558
|
-
didScroll = True
|
559
|
-
self._actHandle = tHandle or None
|
466
|
+
"""Set the handle to be highlighted."""
|
467
|
+
self._actHandle = tHandle
|
468
|
+
if viewport := self.viewport():
|
469
|
+
viewport.repaint()
|
560
470
|
return
|
561
471
|
|
562
|
-
|
563
|
-
|
564
|
-
|
472
|
+
def setLastColType(self, colType: nwNovelExtra) -> None:
|
473
|
+
"""Set the extra column type."""
|
474
|
+
self._lastColType = colType
|
475
|
+
SHARED.project.index.setNovelModelExtraColumn(colType)
|
476
|
+
return
|
565
477
|
|
566
|
-
def
|
567
|
-
"""
|
568
|
-
|
569
|
-
|
570
|
-
"""
|
571
|
-
super().mousePressEvent(event)
|
478
|
+
def setLastColSize(self, colSize: int) -> None:
|
479
|
+
"""Set the extra column size between 15% and 75%."""
|
480
|
+
self._lastColSize = minmax(colSize, 15, 75)/100.0
|
481
|
+
return
|
572
482
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
self.clearSelection()
|
483
|
+
##
|
484
|
+
# Class Methods
|
485
|
+
##
|
577
486
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
487
|
+
def clearContent(self) -> None:
|
488
|
+
"""Clear the tree view."""
|
489
|
+
self.setModel(None)
|
490
|
+
return
|
582
491
|
|
583
|
-
|
584
|
-
|
585
|
-
|
492
|
+
def resizeColumns(self) -> None:
|
493
|
+
"""Set the correct column sizes."""
|
494
|
+
if (header := self.header()) and (model := self._getModel()) and (vp := self.viewport()):
|
495
|
+
header.setStretchLastSection(False)
|
496
|
+
header.setMinimumSectionSize(SHARED.theme.baseIconHeight + 6)
|
497
|
+
header.setSectionResizeMode(0, QtHeaderStretch)
|
498
|
+
header.setSectionResizeMode(1, QtHeaderToContents)
|
499
|
+
header.setSectionResizeMode(2, QtHeaderToContents)
|
500
|
+
if model.columns == 4:
|
501
|
+
header.setSectionResizeMode(3, QtHeaderToContents)
|
502
|
+
header.setMaximumSectionSize(int(self._lastColSize * vp.width()))
|
503
|
+
return
|
586
504
|
|
587
|
-
|
505
|
+
##
|
506
|
+
# Overloads
|
507
|
+
##
|
588
508
|
|
509
|
+
def drawRow(self, painter: QPainter, opt: QStyleOptionViewItem, index: QModelIndex) -> None:
|
510
|
+
"""Draw a box on the active row."""
|
511
|
+
if (model := self._getModel()) and model.handle(index) == self._actHandle:
|
512
|
+
painter.fillRect(opt.rect, self.palette().alternateBase())
|
513
|
+
super().drawRow(painter, opt, index)
|
589
514
|
return
|
590
515
|
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
self.clearSelection()
|
595
|
-
return
|
516
|
+
##
|
517
|
+
# Events
|
518
|
+
##
|
596
519
|
|
597
520
|
def resizeEvent(self, event: QResizeEvent) -> None:
|
598
|
-
"""
|
521
|
+
"""Process size changed."""
|
599
522
|
super().resizeEvent(event)
|
600
|
-
|
601
|
-
oldW = event.oldSize().width()
|
602
|
-
if newW != oldW:
|
603
|
-
eliW = int(self._lastColSize * newW)
|
604
|
-
fMetric = self.fontMetrics()
|
605
|
-
for i in range(self.topLevelItemCount()):
|
606
|
-
trItem = self.topLevelItem(i)
|
607
|
-
if isinstance(trItem, QTreeWidgetItem):
|
608
|
-
lastText = trItem.data(self.C_DATA, self.D_EXTRA)
|
609
|
-
trItem.setText(
|
610
|
-
self.C_EXTRA,
|
611
|
-
fMetric.elidedText(lastText, Qt.TextElideMode.ElideRight, eliW)
|
612
|
-
)
|
523
|
+
self.resizeColumns()
|
613
524
|
return
|
614
525
|
|
615
526
|
##
|
616
527
|
# Private Slots
|
617
528
|
##
|
618
529
|
|
619
|
-
@pyqtSlot(
|
620
|
-
def
|
621
|
-
"""The user clicked
|
622
|
-
if index.
|
623
|
-
tHandle
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
530
|
+
@pyqtSlot(QModelIndex)
|
531
|
+
def _onSingleClick(self, index: QModelIndex) -> None:
|
532
|
+
"""The user single-clicked an index."""
|
533
|
+
if index.isValid() and (model := self._getModel()):
|
534
|
+
if (tHandle := model.handle(index)) and (sTitle := model.key(index)):
|
535
|
+
self.novelView.selectedItemChanged.emit(tHandle)
|
536
|
+
if index.column() == model.columnCount(index) - 1:
|
537
|
+
pos = self.mapToGlobal(self.visualRect(index).topRight())
|
538
|
+
self._popMetaBox(pos, tHandle, sTitle)
|
539
|
+
return
|
540
|
+
|
541
|
+
@pyqtSlot(QModelIndex)
|
542
|
+
def _onDoubleClick(self, index: QModelIndex) -> None:
|
543
|
+
"""The user double-clicked an index."""
|
544
|
+
if (
|
545
|
+
(model := self._getModel())
|
546
|
+
and (tHandle := model.handle(index))
|
547
|
+
and (sTitle := model.key(index))
|
548
|
+
):
|
549
|
+
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle, False)
|
550
|
+
return
|
551
|
+
|
552
|
+
@pyqtSlot(QModelIndex)
|
553
|
+
def _onMiddleClick(self, index: QModelIndex) -> None:
|
554
|
+
"""The user middle-clicked an index."""
|
555
|
+
if (
|
556
|
+
(model := self._getModel())
|
557
|
+
and (tHandle := model.handle(index))
|
558
|
+
and (sTitle := model.key(index))
|
559
|
+
):
|
560
|
+
self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, sTitle, False)
|
647
561
|
return
|
648
562
|
|
649
563
|
##
|
650
564
|
# Internal Functions
|
651
565
|
##
|
652
566
|
|
653
|
-
def
|
654
|
-
"""
|
655
|
-
self.
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
novStruct = SHARED.project.index.novelStructure(rootHandle=rootHandle, activeOnly=True)
|
660
|
-
for tKey, tHandle, sTitle, novIdx in novStruct:
|
661
|
-
if novIdx.level == "H0":
|
662
|
-
continue
|
663
|
-
|
664
|
-
newItem = QTreeWidgetItem()
|
665
|
-
newItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
|
666
|
-
newItem.setData(self.C_DATA, self.D_TITLE, sTitle)
|
667
|
-
newItem.setData(self.C_DATA, self.D_KEY, tKey)
|
668
|
-
newItem.setTextAlignment(self.C_WORDS, QtAlignRight)
|
669
|
-
|
670
|
-
self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
|
671
|
-
self._treeMap[tKey] = newItem
|
672
|
-
self.addTopLevelItem(newItem)
|
673
|
-
|
674
|
-
self.setActiveHandle(self._actHandle)
|
675
|
-
|
676
|
-
logger.debug("Novel Tree built in %.3f ms", (time() - tStart)*1000)
|
677
|
-
self._lastBuild = time()
|
678
|
-
|
679
|
-
return
|
680
|
-
|
681
|
-
def _updateTreeItemValues(self, trItem: QTreeWidgetItem, idxItem: IndexHeading,
|
682
|
-
tHandle: str, sTitle: str) -> None:
|
683
|
-
"""Set the tree item values from the index entry."""
|
684
|
-
iLevel = nwStyles.H_LEVEL.get(idxItem.level, 0)
|
685
|
-
hDec = SHARED.theme.getHeaderDecoration(iLevel)
|
686
|
-
|
687
|
-
trItem.setData(self.C_TITLE, QtDecoration, hDec)
|
688
|
-
trItem.setText(self.C_TITLE, idxItem.title)
|
689
|
-
trItem.setFont(self.C_TITLE, self._hFonts[iLevel])
|
690
|
-
trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}")
|
691
|
-
trItem.setData(self.C_MORE, QtDecoration, self._pMore)
|
692
|
-
|
693
|
-
# Custom column
|
694
|
-
mW = int(self._lastColSize * self.viewport().width())
|
695
|
-
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
|
696
|
-
elideText = self.fontMetrics().elidedText(lastText, Qt.TextElideMode.ElideRight, mW)
|
697
|
-
trItem.setText(self.C_EXTRA, elideText)
|
698
|
-
trItem.setData(self.C_DATA, self.D_EXTRA, lastText)
|
699
|
-
trItem.setToolTip(self.C_EXTRA, toolTip)
|
700
|
-
|
701
|
-
return
|
702
|
-
|
703
|
-
def _getLastColumnText(self, tHandle: str, sTitle: str) -> tuple[str, str]:
|
704
|
-
"""Generate text for the last column based on user settings."""
|
705
|
-
if self._lastCol == NovelTreeColumn.HIDDEN:
|
706
|
-
return "", ""
|
707
|
-
|
708
|
-
refData = []
|
709
|
-
refName = ""
|
710
|
-
refs = SHARED.project.index.getReferences(tHandle, sTitle)
|
711
|
-
if self._lastCol == NovelTreeColumn.POV:
|
712
|
-
refData = refs[nwKeyWords.POV_KEY]
|
713
|
-
refName = self._povLabel
|
714
|
-
|
715
|
-
elif self._lastCol == NovelTreeColumn.FOCUS:
|
716
|
-
refData = refs[nwKeyWords.FOCUS_KEY]
|
717
|
-
refName = self._focLabel
|
718
|
-
|
719
|
-
elif self._lastCol == NovelTreeColumn.PLOT:
|
720
|
-
refData = refs[nwKeyWords.PLOT_KEY]
|
721
|
-
refName = self._pltLabel
|
722
|
-
|
723
|
-
if refData:
|
724
|
-
toolText = ", ".join(refData)
|
725
|
-
return toolText, f"{refName}: {toolText}"
|
726
|
-
|
727
|
-
return "", ""
|
567
|
+
def _getModel(self) -> NovelModel | None:
|
568
|
+
"""Return the model, if it exists."""
|
569
|
+
if isinstance(model := self.model(), NovelModel):
|
570
|
+
return model
|
571
|
+
return None
|
728
572
|
|
729
573
|
def _popMetaBox(self, qPos: QPoint, tHandle: str, sTitle: str) -> None:
|
730
574
|
"""Show the novel meta data box."""
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
refTags = pIndex.getReferences(tHandle, sTitle)
|
736
|
-
if not novIdx:
|
575
|
+
def appendTags(refs: dict, key: str, lines: list[str]) -> None:
|
576
|
+
"""Generate a reference list for a given reference key."""
|
577
|
+
if tags := ", ".join(refs.get(key, [])):
|
578
|
+
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}:</b> {tags}")
|
737
579
|
return
|
738
580
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
return
|
765
|
-
|
766
|
-
@staticmethod
|
767
|
-
def _appendMetaTag(refs: dict, key: str, lines: list[str]) -> list[str]:
|
768
|
-
"""Generate a reference list for a given reference key."""
|
769
|
-
tags = ", ".join(refs.get(key, []))
|
770
|
-
if tags:
|
771
|
-
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
|
772
|
-
return lines
|
581
|
+
if head := SHARED.project.index.getItemHeading(tHandle, sTitle):
|
582
|
+
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
|
583
|
+
if synopsis := head.synopsis:
|
584
|
+
label = trConst(nwLabels.OUTLINE_COLS[nwOutline.SYNOP])
|
585
|
+
synopsis = f"<p><b>{label}:</b> {synopsis}</p>"
|
586
|
+
|
587
|
+
lines = []
|
588
|
+
if head := SHARED.project.index.getItemHeading(tHandle, sTitle):
|
589
|
+
tags = head.getReferences()
|
590
|
+
appendTags(tags, nwKeyWords.POV_KEY, lines)
|
591
|
+
appendTags(tags, nwKeyWords.FOCUS_KEY, lines)
|
592
|
+
appendTags(tags, nwKeyWords.CHAR_KEY, lines)
|
593
|
+
appendTags(tags, nwKeyWords.PLOT_KEY, lines)
|
594
|
+
appendTags(tags, nwKeyWords.TIME_KEY, lines)
|
595
|
+
appendTags(tags, nwKeyWords.WORLD_KEY, lines)
|
596
|
+
appendTags(tags, nwKeyWords.OBJECT_KEY, lines)
|
597
|
+
appendTags(tags, nwKeyWords.ENTITY_KEY, lines)
|
598
|
+
appendTags(tags, nwKeyWords.CUSTOM_KEY, lines)
|
599
|
+
|
600
|
+
text = ""
|
601
|
+
if lines:
|
602
|
+
refs = "<br>".join(lines)
|
603
|
+
text = f"<p>{refs}</p>"
|
604
|
+
if tooltip := (text + synopsis or self.tr("No meta data")):
|
605
|
+
QToolTip.showText(qPos, tooltip)
|
606
|
+
return
|