novelWriter 2.4b1__py3-none-any.whl → 2.4.1__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.
Files changed (89) hide show
  1. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/METADATA +5 -6
  2. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/RECORD +79 -83
  3. novelwriter/__init__.py +15 -8
  4. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  5. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  6. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  7. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  8. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  9. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  10. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  11. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  12. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  13. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  14. novelwriter/assets/icons/none.svg +4 -0
  15. novelwriter/assets/icons/typicons_dark/icons.conf +2 -2
  16. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
  17. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
  18. novelwriter/assets/icons/typicons_light/icons.conf +2 -2
  19. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
  20. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
  21. novelwriter/assets/manual.pdf +0 -0
  22. novelwriter/assets/sample.zip +0 -0
  23. novelwriter/common.py +11 -3
  24. novelwriter/config.py +12 -4
  25. novelwriter/core/buildsettings.py +7 -7
  26. novelwriter/core/coretools.py +21 -22
  27. novelwriter/core/docbuild.py +2 -2
  28. novelwriter/core/projectxml.py +1 -1
  29. novelwriter/core/spellcheck.py +3 -3
  30. novelwriter/core/status.py +3 -2
  31. novelwriter/core/tokenizer.py +3 -3
  32. novelwriter/core/toodt.py +333 -356
  33. novelwriter/dialogs/about.py +9 -11
  34. novelwriter/dialogs/docmerge.py +17 -14
  35. novelwriter/dialogs/docsplit.py +14 -12
  36. novelwriter/dialogs/editlabel.py +5 -4
  37. novelwriter/dialogs/preferences.py +29 -34
  38. novelwriter/dialogs/projectsettings.py +31 -28
  39. novelwriter/dialogs/quotes.py +10 -9
  40. novelwriter/dialogs/wordlist.py +17 -14
  41. novelwriter/error.py +14 -12
  42. novelwriter/extensions/circularprogress.py +12 -8
  43. novelwriter/extensions/configlayout.py +1 -3
  44. novelwriter/extensions/modified.py +33 -2
  45. novelwriter/extensions/pagedsidebar.py +16 -14
  46. novelwriter/extensions/simpleprogress.py +3 -1
  47. novelwriter/extensions/statusled.py +3 -1
  48. novelwriter/extensions/switch.py +10 -9
  49. novelwriter/extensions/switchbox.py +14 -13
  50. novelwriter/gui/doceditor.py +205 -246
  51. novelwriter/gui/dochighlight.py +26 -9
  52. novelwriter/gui/docviewer.py +55 -59
  53. novelwriter/gui/docviewerpanel.py +16 -13
  54. novelwriter/gui/editordocument.py +4 -4
  55. novelwriter/gui/itemdetails.py +45 -48
  56. novelwriter/gui/mainmenu.py +2 -2
  57. novelwriter/gui/noveltree.py +23 -21
  58. novelwriter/gui/outline.py +93 -94
  59. novelwriter/gui/projtree.py +32 -30
  60. novelwriter/gui/search.py +75 -29
  61. novelwriter/gui/sidebar.py +24 -28
  62. novelwriter/gui/statusbar.py +14 -14
  63. novelwriter/gui/theme.py +61 -39
  64. novelwriter/guimain.py +37 -33
  65. novelwriter/shared.py +21 -9
  66. novelwriter/text/counting.py +1 -0
  67. novelwriter/tools/dictionaries.py +15 -14
  68. novelwriter/tools/lipsum.py +20 -17
  69. novelwriter/tools/manusbuild.py +44 -35
  70. novelwriter/tools/manuscript.py +112 -112
  71. novelwriter/tools/manussettings.py +91 -98
  72. novelwriter/tools/noveldetails.py +20 -18
  73. novelwriter/tools/welcome.py +51 -48
  74. novelwriter/tools/writingstats.py +61 -55
  75. novelwriter/types.py +90 -0
  76. novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +0 -4
  77. novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +0 -4
  78. novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +0 -4
  79. novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +0 -4
  80. novelwriter/core/__init__.py +0 -3
  81. novelwriter/dialogs/__init__.py +0 -3
  82. novelwriter/extensions/__init__.py +0 -3
  83. novelwriter/gui/__init__.py +0 -3
  84. novelwriter/text/__init__.py +0 -3
  85. novelwriter/tools/__init__.py +0 -3
  86. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/LICENSE.md +0 -0
  87. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/WHEEL +0 -0
  88. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/entry_points.txt +0 -0
  89. {novelWriter-2.4b1.dist-info → novelWriter-2.4.1.dist-info}/top_level.txt +0 -0
@@ -25,34 +25,35 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from pathlib import Path
29
28
  from datetime import datetime
29
+ from pathlib import Path
30
30
 
31
- from PyQt5.QtGui import QCloseEvent, QColor, QFont, QPaintEvent, QPainter, QPen
32
31
  from PyQt5.QtCore import (
33
32
  QAbstractListModel, QEvent, QModelIndex, QObject, QPoint, QSize, Qt,
34
33
  pyqtSignal, pyqtSlot
35
34
  )
35
+ from PyQt5.QtGui import QCloseEvent, QColor, QFont, QPaintEvent, QPainter, QPen
36
36
  from PyQt5.QtWidgets import (
37
- QAction, QDialog, QFileDialog, QFormLayout, QHBoxLayout, QLabel, QLineEdit,
38
- QListView, QMenu, QPushButton, QScrollArea, QShortcut, QStackedWidget,
39
- QStyle, QStyleOptionViewItem, QStyledItemDelegate, QToolButton,
40
- QVBoxLayout, QWidget, qApp
37
+ QAction, QApplication, QDialog, QFileDialog, QFormLayout, QHBoxLayout,
38
+ QLabel, QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
39
+ QStackedWidget, QStyleOptionViewItem, QStyledItemDelegate, QVBoxLayout,
40
+ QWidget
41
41
  )
42
42
 
43
43
  from novelwriter import CONFIG, SHARED
44
- from novelwriter.enum import nwItemClass
45
- from novelwriter.common import formatInt, makeFileNameSafe
44
+ from novelwriter.common import cssCol, formatInt, makeFileNameSafe
46
45
  from novelwriter.constants import nwFiles
47
46
  from novelwriter.core.coretools import ProjectBuilder
47
+ from novelwriter.enum import nwItemClass
48
48
  from novelwriter.extensions.configlayout import NWrappedWidgetBox
49
+ from novelwriter.extensions.modified import NIconToolButton, NSpinBox
49
50
  from novelwriter.extensions.switch import NSwitch
50
- from novelwriter.extensions.modified import NSpinBox
51
51
  from novelwriter.extensions.versioninfo import VersionInfoWidget
52
+ from novelwriter.types import QtAlignLeft, QtAlignRightTop, QtSelected
52
53
 
53
54
  logger = logging.getLogger(__name__)
54
55
 
55
- PANEL_ALPHA = 0.7
56
+ PANEL_ALPHA = 178
56
57
 
57
58
 
58
59
  class GuiWelcome(QDialog):
@@ -75,8 +76,9 @@ class GuiWelcome(QDialog):
75
76
  hD = CONFIG.pxInt(36)
76
77
  hE = CONFIG.pxInt(48)
77
78
  hF = CONFIG.pxInt(128)
78
- self._hPx = CONFIG.pxInt(600)
79
+ btnIconSize = SHARED.theme.buttonIconSize
79
80
 
81
+ self._hPx = CONFIG.pxInt(600)
80
82
  self.resize(*CONFIG.welcomeWinSize)
81
83
 
82
84
  # Elements
@@ -109,26 +111,32 @@ class GuiWelcome(QDialog):
109
111
 
110
112
  self.btnList = QPushButton(self.tr("List"), self)
111
113
  self.btnList.setIcon(SHARED.theme.getIcon("list"))
114
+ self.btnList.setIconSize(btnIconSize)
112
115
  self.btnList.clicked.connect(self._showOpenProjectPage)
113
116
 
114
117
  self.btnNew = QPushButton(self.tr("New"), self)
115
118
  self.btnNew.setIcon(SHARED.theme.getIcon("add"))
119
+ self.btnNew.setIconSize(btnIconSize)
116
120
  self.btnNew.clicked.connect(self._showNewProjectPage)
117
121
 
118
122
  self.btnBrowse = QPushButton(self.tr("Browse"), self)
119
123
  self.btnBrowse.setIcon(SHARED.theme.getIcon("browse"))
124
+ self.btnBrowse.setIconSize(btnIconSize)
120
125
  self.btnBrowse.clicked.connect(self._browseForProject)
121
126
 
122
127
  self.btnCancel = QPushButton(self.tr("Cancel"), self)
123
128
  self.btnCancel.setIcon(SHARED.theme.getIcon("cross"))
129
+ self.btnCancel.setIconSize(btnIconSize)
124
130
  self.btnCancel.clicked.connect(self.close)
125
131
 
126
132
  self.btnCreate = QPushButton(self.tr("Create"), self)
127
133
  self.btnCreate.setIcon(SHARED.theme.getIcon("star"))
134
+ self.btnCreate.setIconSize(btnIconSize)
128
135
  self.btnCreate.clicked.connect(self.tabNew.createNewProject)
129
136
 
130
137
  self.btnOpen = QPushButton(self.tr("Open"), self)
131
138
  self.btnOpen.setIcon(SHARED.theme.getIcon("open"))
139
+ self.btnOpen.setIconSize(btnIconSize)
132
140
  self.btnOpen.clicked.connect(self._openSelectedItem)
133
141
 
134
142
  self.btnBox = QHBoxLayout()
@@ -153,10 +161,8 @@ class GuiWelcome(QDialog):
153
161
  self.innerBox.addSpacing(hB)
154
162
  self.innerBox.addLayout(self.btnBox)
155
163
 
156
- topRight = Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignRight
157
-
158
164
  self.outerBox = QHBoxLayout()
159
- self.outerBox.addWidget(self.nwLogo, 3, topRight)
165
+ self.outerBox.addWidget(self.nwLogo, 3, QtAlignRightTop)
160
166
  self.outerBox.addLayout(self.innerBox, 9)
161
167
  self.outerBox.setContentsMargins(hF, hE, hC, hE)
162
168
 
@@ -306,12 +312,12 @@ class _OpenProjectPage(QWidget):
306
312
 
307
313
  self._selectFirstItem()
308
314
 
309
- baseCol = self.palette().base().color()
310
- self.setStyleSheet((
311
- "QListView {{border: none; background: rgba({r},{g},{b},{a});}} "
312
- "QLineEdit {{border: none; background: rgba({r},{g},{b},{a}); padding: {m}px;}} "
313
- ).format(r=baseCol.red(), g=baseCol.green(), b=baseCol.blue(),
314
- a=PANEL_ALPHA, m=CONFIG.pxInt(4)))
315
+ mPx = CONFIG.pxInt(4)
316
+ baseCol = cssCol(self.palette().base().color(), PANEL_ALPHA)
317
+ self.setStyleSheet(
318
+ f"QListView {{border: none; background: {baseCol};}} "
319
+ f"QLineEdit {{border: none; background: {baseCol}; padding: {mPx}px;}} "
320
+ )
315
321
 
316
322
  return
317
323
 
@@ -371,7 +377,7 @@ class _OpenProjectPage(QWidget):
371
377
  action.triggered.connect(self.openSelectedItem)
372
378
  action = ctxMenu.addAction(self.tr("Remove Project"))
373
379
  action.triggered.connect(self._deleteSelectedItem)
374
- ctxMenu.exec_(self.mapToGlobal(pos))
380
+ ctxMenu.exec(self.mapToGlobal(pos))
375
381
  ctxMenu.deleteLater()
376
382
  return
377
383
 
@@ -405,11 +411,11 @@ class _ProjectListItem(QStyledItemDelegate):
405
411
  self._pPx = (mPx//2, 3*mPx//2, iPx + mPx, mPx, mPx + tPx) # Painter coordinates
406
412
  self._hPx = 2*mPx + tPx + fPx # Fixed height
407
413
 
408
- self._tFont = qApp.font()
414
+ self._tFont = QApplication.font()
409
415
  self._tFont.setPointSizeF(1.2*fPt)
410
416
  self._tFont.setWeight(QFont.Weight.Bold)
411
417
 
412
- self._dFont = qApp.font()
418
+ self._dFont = QApplication.font()
413
419
  self._dFont.setPointSizeF(fPt)
414
420
  self._dPen = QPen(SHARED.theme.helpText)
415
421
 
@@ -425,9 +431,9 @@ class _ProjectListItem(QStyledItemDelegate):
425
431
  ix, iy, x, y1, y2 = self._pPx
426
432
 
427
433
  painter.save()
428
- if opt.state & QStyle.StateFlag.State_Selected == QStyle.StateFlag.State_Selected:
434
+ if opt.state & QtSelected == QtSelected:
429
435
  painter.setOpacity(0.25)
430
- painter.fillRect(rect, qApp.palette().highlight())
436
+ painter.fillRect(rect, QApplication.palette().highlight())
431
437
  painter.setOpacity(1.0)
432
438
 
433
439
  painter.drawPixmap(ix, rect.top() + iy, self._icon)
@@ -518,11 +524,11 @@ class _NewProjectPage(QWidget):
518
524
  # Styles
519
525
  # ======
520
526
 
521
- baseCol = self.palette().base().color()
522
- self.setStyleSheet((
523
- "QScrollArea {{border: none; background: rgba({r},{g},{b},{a});}} "
524
- "_NewProjectForm {{border: none; background: rgba({r},{g},{b},{a});}} "
525
- ).format(r=baseCol.red(), g=baseCol.green(), b=baseCol.blue(), a=PANEL_ALPHA))
527
+ baseCol = cssCol(self.palette().base().color(), PANEL_ALPHA)
528
+ self.setStyleSheet(
529
+ f"QScrollArea {{border: none; background: {baseCol};}} "
530
+ f"_NewProjectForm {{border: none; background: {baseCol};}} "
531
+ )
526
532
 
527
533
  return
528
534
 
@@ -554,11 +560,12 @@ class _NewProjectForm(QWidget):
554
560
  def __init__(self, parent: QWidget) -> None:
555
561
  super().__init__(parent=parent)
556
562
 
557
- self._basePath = CONFIG.lastPath()
563
+ self._basePath = CONFIG.homePath()
558
564
  self._fillMode = self.FILL_BLANK
559
565
  self._copyPath = None
560
566
 
561
- iPx = SHARED.theme.baseIconSize
567
+ iPx = SHARED.theme.baseIconHeight
568
+ iSz = SHARED.theme.baseIconSize
562
569
  sPx = CONFIG.pxInt(16)
563
570
 
564
571
  # Project Settings
@@ -579,8 +586,7 @@ class _NewProjectForm(QWidget):
579
586
  self.projPath = QLineEdit(self)
580
587
  self.projPath.setReadOnly(True)
581
588
 
582
- self.browsePath = QToolButton(self)
583
- self.browsePath.setIcon(SHARED.theme.getIcon("browse"))
589
+ self.browsePath = NIconToolButton(self, iSz, "browse")
584
590
  self.browsePath.clicked.connect(self._doBrowse)
585
591
 
586
592
  self.pathBox = QHBoxLayout()
@@ -591,8 +597,7 @@ class _NewProjectForm(QWidget):
591
597
  self.projFill = QLineEdit(self)
592
598
  self.projFill.setReadOnly(True)
593
599
 
594
- self.browseFill = QToolButton(self)
595
- self.browseFill.setIcon(SHARED.theme.getIcon("add_document"))
600
+ self.browseFill = NIconToolButton(self, iSz, "add_document")
596
601
 
597
602
  self.fillMenu = _PopLeftDirectionMenu(self.browseFill)
598
603
 
@@ -609,7 +614,6 @@ class _NewProjectForm(QWidget):
609
614
  self.fillCopy.triggered.connect(self._setFillCopy)
610
615
 
611
616
  self.browseFill.setMenu(self.fillMenu)
612
- self.browseFill.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
613
617
 
614
618
  self.fillBox = QHBoxLayout()
615
619
  self.fillBox.addWidget(self.projFill)
@@ -617,7 +621,7 @@ class _NewProjectForm(QWidget):
617
621
 
618
622
  # Project Form
619
623
  self.projectForm = QFormLayout()
620
- self.projectForm.setAlignment(Qt.AlignmentFlag.AlignLeft)
624
+ self.projectForm.setAlignment(QtAlignLeft)
621
625
  self.projectForm.addRow(self.tr("Project Name"), self.projName)
622
626
  self.projectForm.addRow(self.tr("Author"), self.projAuthor)
623
627
  self.projectForm.addRow(self.tr("Project Path"), self.pathBox)
@@ -628,7 +632,7 @@ class _NewProjectForm(QWidget):
628
632
 
629
633
  self.numChapters = NSpinBox(self)
630
634
  self.numChapters.setRange(0, 200)
631
- self.numChapters.setValue(5)
635
+ self.numChapters.setValue(0)
632
636
  self.numChapters.setToolTip(self.tr("Set to 0 to only add scenes"))
633
637
 
634
638
  self.chapterBox = NWrappedWidgetBox(
@@ -638,7 +642,7 @@ class _NewProjectForm(QWidget):
638
642
 
639
643
  self.numScenes = NSpinBox(self)
640
644
  self.numScenes.setRange(0, 200)
641
- self.numScenes.setValue(5)
645
+ self.numScenes.setValue(0)
642
646
 
643
647
  self.sceneBox = NWrappedWidgetBox(
644
648
  self.tr("Add {0} scene documents (to each chapter)"), self.numScenes
@@ -668,7 +672,7 @@ class _NewProjectForm(QWidget):
668
672
  self.addNotes.setChecked(False)
669
673
 
670
674
  self.notesForm = QFormLayout()
671
- self.notesForm.setAlignment(Qt.AlignmentFlag.AlignLeft)
675
+ self.notesForm.setAlignment(QtAlignLeft)
672
676
  self.notesForm.addRow(self.tr("Add a folder for plot notes"), self.addPlot)
673
677
  self.notesForm.addRow(self.tr("Add a folder for character notes"), self.addChar)
674
678
  self.notesForm.addRow(self.tr("Add a folder for location notes"), self.addWorld)
@@ -678,10 +682,10 @@ class _NewProjectForm(QWidget):
678
682
  # ========
679
683
 
680
684
  self.extraBox = QVBoxLayout()
681
- self.extraBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Chapters and Scenes"))))
685
+ self.extraBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Chapters and Scenes")), self))
682
686
  self.extraBox.addLayout(self.novelForm)
683
687
  self.extraBox.addSpacing(sPx)
684
- self.extraBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Project Notes"))))
688
+ self.extraBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Project Notes")), self))
685
689
  self.extraBox.addLayout(self.notesForm)
686
690
  self.extraBox.setContentsMargins(0, 0, 0, 0)
687
691
 
@@ -690,7 +694,7 @@ class _NewProjectForm(QWidget):
690
694
  self.extraWidget.setContentsMargins(0, 0, 0, 0)
691
695
 
692
696
  self.formBox = QVBoxLayout()
693
- self.formBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Create New Project"))))
697
+ self.formBox.addWidget(QLabel("<b>{0}</b>".format(self.tr("Create New Project")), self))
694
698
  self.formBox.addLayout(self.projectForm)
695
699
  self.formBox.addSpacing(sPx)
696
700
  self.formBox.addWidget(self.extraWidget)
@@ -734,11 +738,10 @@ class _NewProjectForm(QWidget):
734
738
  """Select a project folder."""
735
739
  if projDir := QFileDialog.getExistingDirectory(
736
740
  self, self.tr("Select Project Folder"),
737
- str(self._basePath), options=QFileDialog.ShowDirsOnly
741
+ str(self._basePath), options=QFileDialog.Option.ShowDirsOnly
738
742
  ):
739
743
  self._basePath = Path(projDir)
740
744
  self._updateProjPath()
741
- CONFIG.setLastPath(self._basePath)
742
745
  return
743
746
 
744
747
  @pyqtSlot()
@@ -749,7 +752,7 @@ class _NewProjectForm(QWidget):
749
752
  return
750
753
 
751
754
  @pyqtSlot()
752
- def _syncSwitches(self):
755
+ def _syncSwitches(self) -> None:
753
756
  """Check if the add notes option should also be switched off."""
754
757
  addPlot = self.addPlot.isChecked()
755
758
  addChar = self.addChar.isChecked()
@@ -809,7 +812,7 @@ class _PopLeftDirectionMenu(QMenu):
809
812
 
810
813
  def event(self, event: QEvent) -> bool:
811
814
  """Overload the show event and move the menu popup location."""
812
- if event.type() == QEvent.Show:
815
+ if event.type() == QEvent.Type.Show:
813
816
  if isinstance(parent := self.parent(), QWidget):
814
817
  offset = QPoint(parent.width() - self.width(), parent.height())
815
818
  self.move(parent.mapToGlobal(offset))
@@ -29,18 +29,23 @@ import logging
29
29
  from datetime import datetime
30
30
  from typing import TYPE_CHECKING
31
31
 
32
- from PyQt5.QtGui import QCloseEvent, QPixmap, QCursor
33
32
  from PyQt5.QtCore import Qt, pyqtSlot
33
+ from PyQt5.QtGui import QCloseEvent, QCursor, QPixmap
34
34
  from PyQt5.QtWidgets import (
35
- qApp, QDialog, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QGridLayout,
36
- QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout
35
+ QAction, QApplication, QDialog, QDialogButtonBox, QFileDialog, QGridLayout,
36
+ QGroupBox, QHBoxLayout, QLabel, QMenu, QSpinBox, QTreeWidget,
37
+ QTreeWidgetItem
37
38
  )
38
39
 
39
40
  from novelwriter import CONFIG, SHARED
40
- from novelwriter.error import formatException
41
41
  from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
42
42
  from novelwriter.constants import nwConst
43
+ from novelwriter.error import formatException
43
44
  from novelwriter.extensions.switch import NSwitch
45
+ from novelwriter.types import (
46
+ QtAlignLeftMiddle, QtAlignRight, QtAlignRightMiddle, QtDecoration,
47
+ QtDialogClose, QtRoleAction
48
+ )
44
49
 
45
50
  if TYPE_CHECKING: # pragma: no cover
46
51
  from novelwriter.guimain import GuiMain
@@ -100,7 +105,7 @@ class GuiWritingStats(QDialog):
100
105
  pOptions.getInt("GuiWritingStats", "widthCol3", 80)
101
106
  )
102
107
 
103
- self.listBox = QTreeWidget()
108
+ self.listBox = QTreeWidget(self)
104
109
  self.listBox.setHeaderLabels([
105
110
  self.tr("Session Start"),
106
111
  self.tr("Length"),
@@ -116,14 +121,15 @@ class GuiWritingStats(QDialog):
116
121
 
117
122
  hHeader = self.listBox.headerItem()
118
123
  if hHeader is not None:
119
- hHeader.setTextAlignment(self.C_LENGTH, Qt.AlignRight)
120
- hHeader.setTextAlignment(self.C_IDLE, Qt.AlignRight)
121
- hHeader.setTextAlignment(self.C_COUNT, Qt.AlignRight)
124
+ hHeader.setTextAlignment(self.C_LENGTH, QtAlignRight)
125
+ hHeader.setTextAlignment(self.C_IDLE, QtAlignRight)
126
+ hHeader.setTextAlignment(self.C_COUNT, QtAlignRight)
122
127
 
128
+ sDec = Qt.SortOrder.DescendingOrder
129
+ sAsc = Qt.SortOrder.AscendingOrder
123
130
  sortCol = minmax(pOptions.getInt("GuiWritingStats", "sortCol", 0), 0, 2)
124
131
  sortOrder = checkIntTuple(
125
- pOptions.getInt("GuiWritingStats", "sortOrder", Qt.DescendingOrder),
126
- (Qt.AscendingOrder, Qt.DescendingOrder), Qt.DescendingOrder
132
+ pOptions.getInt("GuiWritingStats", "sortOrder", sDec), (sAsc, sDec), sDec
127
133
  )
128
134
  self.listBox.sortByColumn(sortCol, sortOrder) # type: ignore
129
135
  self.listBox.setSortingEnabled(True)
@@ -139,36 +145,36 @@ class GuiWritingStats(QDialog):
139
145
  self.infoForm = QGridLayout(self)
140
146
  self.infoBox.setLayout(self.infoForm)
141
147
 
142
- self.labelTotal = QLabel(formatTime(0))
148
+ self.labelTotal = QLabel(formatTime(0), self)
143
149
  self.labelTotal.setFont(SHARED.theme.guiFontFixed)
144
- self.labelTotal.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
150
+ self.labelTotal.setAlignment(QtAlignRightMiddle)
145
151
 
146
- self.labelIdleT = QLabel(formatTime(0))
152
+ self.labelIdleT = QLabel(formatTime(0), self)
147
153
  self.labelIdleT.setFont(SHARED.theme.guiFontFixed)
148
- self.labelIdleT.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
154
+ self.labelIdleT.setAlignment(QtAlignRightMiddle)
149
155
 
150
- self.labelFilter = QLabel(formatTime(0))
156
+ self.labelFilter = QLabel(formatTime(0), self)
151
157
  self.labelFilter.setFont(SHARED.theme.guiFontFixed)
152
- self.labelFilter.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
158
+ self.labelFilter.setAlignment(QtAlignRightMiddle)
153
159
 
154
- self.novelWords = QLabel("0")
160
+ self.novelWords = QLabel("0", self)
155
161
  self.novelWords.setFont(SHARED.theme.guiFontFixed)
156
- self.novelWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
162
+ self.novelWords.setAlignment(QtAlignRightMiddle)
157
163
 
158
- self.notesWords = QLabel("0")
164
+ self.notesWords = QLabel("0", self)
159
165
  self.notesWords.setFont(SHARED.theme.guiFontFixed)
160
- self.notesWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
166
+ self.notesWords.setAlignment(QtAlignRightMiddle)
161
167
 
162
- self.totalWords = QLabel("0")
168
+ self.totalWords = QLabel("0", self)
163
169
  self.totalWords.setFont(SHARED.theme.guiFontFixed)
164
- self.totalWords.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
170
+ self.totalWords.setAlignment(QtAlignRightMiddle)
165
171
 
166
- lblTTime = QLabel(self.tr("Total Time:"))
167
- lblITime = QLabel(self.tr("Idle Time:"))
168
- lblFTime = QLabel(self.tr("Filtered Time:"))
169
- lblNvCount = QLabel(self.tr("Novel Word Count:"))
170
- lblNtCount = QLabel(self.tr("Notes Word Count:"))
171
- lblTtCount = QLabel(self.tr("Total Word Count:"))
172
+ lblTTime = QLabel(self.tr("Total Time:"), self)
173
+ lblITime = QLabel(self.tr("Idle Time:"), self)
174
+ lblFTime = QLabel(self.tr("Filtered Time:"), self)
175
+ lblNvCount = QLabel(self.tr("Novel Word Count:"), self)
176
+ lblNtCount = QLabel(self.tr("Notes Word Count:"), self)
177
+ lblTtCount = QLabel(self.tr("Total Word Count:"), self)
172
178
 
173
179
  self.infoForm.addWidget(lblTTime, 0, 0)
174
180
  self.infoForm.addWidget(lblITime, 1, 0)
@@ -187,54 +193,54 @@ class GuiWritingStats(QDialog):
187
193
  self.infoForm.setRowStretch(6, 1)
188
194
 
189
195
  # Filter Options
190
- sPx = SHARED.theme.baseIconSize
196
+ iPx = SHARED.theme.baseIconHeight
191
197
 
192
198
  self.filterBox = QGroupBox(self.tr("Filters"), self)
193
199
  self.filterForm = QGridLayout(self)
194
200
  self.filterBox.setLayout(self.filterForm)
195
201
 
196
- self.incNovel = NSwitch(self, height=sPx)
202
+ self.incNovel = NSwitch(self, height=iPx)
197
203
  self.incNovel.setChecked(
198
204
  pOptions.getBool("GuiWritingStats", "incNovel", True)
199
205
  )
200
206
  self.incNovel.clicked.connect(self._updateListBox)
201
207
 
202
- self.incNotes = NSwitch(self, height=sPx)
208
+ self.incNotes = NSwitch(self, height=iPx)
203
209
  self.incNotes.setChecked(
204
210
  pOptions.getBool("GuiWritingStats", "incNotes", True)
205
211
  )
206
212
  self.incNotes.clicked.connect(self._updateListBox)
207
213
 
208
- self.hideZeros = NSwitch(self, height=sPx)
214
+ self.hideZeros = NSwitch(self, height=iPx)
209
215
  self.hideZeros.setChecked(
210
216
  pOptions.getBool("GuiWritingStats", "hideZeros", True)
211
217
  )
212
218
  self.hideZeros.clicked.connect(self._updateListBox)
213
219
 
214
- self.hideNegative = NSwitch(self, height=sPx)
220
+ self.hideNegative = NSwitch(self, height=iPx)
215
221
  self.hideNegative.setChecked(
216
222
  pOptions.getBool("GuiWritingStats", "hideNegative", False)
217
223
  )
218
224
  self.hideNegative.clicked.connect(self._updateListBox)
219
225
 
220
- self.groupByDay = NSwitch(self, height=sPx)
226
+ self.groupByDay = NSwitch(self, height=iPx)
221
227
  self.groupByDay.setChecked(
222
228
  pOptions.getBool("GuiWritingStats", "groupByDay", False)
223
229
  )
224
230
  self.groupByDay.clicked.connect(self._updateListBox)
225
231
 
226
- self.showIdleTime = NSwitch(self, height=sPx)
232
+ self.showIdleTime = NSwitch(self, height=iPx)
227
233
  self.showIdleTime.setChecked(
228
234
  pOptions.getBool("GuiWritingStats", "showIdleTime", False)
229
235
  )
230
236
  self.showIdleTime.clicked.connect(self._updateListBox)
231
237
 
232
- self.filterForm.addWidget(QLabel(self.tr("Count novel files")), 0, 0)
233
- self.filterForm.addWidget(QLabel(self.tr("Count note files")), 1, 0)
234
- self.filterForm.addWidget(QLabel(self.tr("Hide zero word count")), 2, 0)
235
- self.filterForm.addWidget(QLabel(self.tr("Hide negative word count")), 3, 0)
236
- self.filterForm.addWidget(QLabel(self.tr("Group entries by day")), 4, 0)
237
- self.filterForm.addWidget(QLabel(self.tr("Show idle time")), 5, 0)
238
+ self.filterForm.addWidget(QLabel(self.tr("Count novel files"), self), 0, 0)
239
+ self.filterForm.addWidget(QLabel(self.tr("Count note files"), self), 1, 0)
240
+ self.filterForm.addWidget(QLabel(self.tr("Hide zero word count"), self), 2, 0)
241
+ self.filterForm.addWidget(QLabel(self.tr("Hide negative word count"), self), 3, 0)
242
+ self.filterForm.addWidget(QLabel(self.tr("Group entries by day"), self), 4, 0)
243
+ self.filterForm.addWidget(QLabel(self.tr("Show idle time"), self), 5, 0)
238
244
  self.filterForm.addWidget(self.incNovel, 0, 1)
239
245
  self.filterForm.addWidget(self.incNotes, 1, 1)
240
246
  self.filterForm.addWidget(self.hideZeros, 2, 1)
@@ -255,17 +261,17 @@ class GuiWritingStats(QDialog):
255
261
 
256
262
  self.optsBox = QHBoxLayout()
257
263
  self.optsBox.addStretch(1)
258
- self.optsBox.addWidget(QLabel(self.tr("Word count cap for the histogram")), 0)
264
+ self.optsBox.addWidget(QLabel(self.tr("Word count cap for the histogram"), self), 0)
259
265
  self.optsBox.addWidget(self.histMax, 0)
260
266
 
261
267
  # Buttons
262
- self.buttonBox = QDialogButtonBox()
268
+ self.buttonBox = QDialogButtonBox(self)
263
269
  self.buttonBox.rejected.connect(self._doClose)
264
270
 
265
- self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
271
+ self.btnClose = self.buttonBox.addButton(QtDialogClose)
266
272
  self.btnClose.setAutoDefault(False)
267
273
 
268
- self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QDialogButtonBox.ActionRole)
274
+ self.btnSave = self.buttonBox.addButton(self.tr("Save As"), QtRoleAction)
269
275
  self.btnSave.setAutoDefault(False)
270
276
 
271
277
  self.saveMenu = QMenu(self)
@@ -300,10 +306,10 @@ class GuiWritingStats(QDialog):
300
306
 
301
307
  def populateGUI(self) -> None:
302
308
  """Populate list box with data from the log file."""
303
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
309
+ QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
304
310
  self._loadLogFile()
305
311
  self._updateListBox()
306
- qApp.restoreOverrideCursor()
312
+ QApplication.restoreOverrideCursor()
307
313
  return
308
314
 
309
315
  ##
@@ -569,6 +575,8 @@ class GuiWritingStats(QDialog):
569
575
  pcTotal = wcTotal
570
576
 
571
577
  # Populate the list
578
+ mTrans = Qt.TransformationMode.FastTransformation
579
+ mAspect = Qt.AspectRatioMode.IgnoreAspectRatio
572
580
  showIdleTime = self.showIdleTime.isChecked()
573
581
  for _, sStart, sDiff, nWords, _, _, sIdle in self.filterData:
574
582
 
@@ -587,16 +595,14 @@ class GuiWritingStats(QDialog):
587
595
  if nWords > 0 and listMax > 0:
588
596
  wBar = self.barImage.scaled(
589
597
  int(200*min(nWords, histMax)/listMax),
590
- self.barHeight,
591
- Qt.IgnoreAspectRatio,
592
- Qt.FastTransformation
598
+ self.barHeight, mAspect, mTrans
593
599
  )
594
- newItem.setData(self.C_BAR, Qt.DecorationRole, wBar)
600
+ newItem.setData(self.C_BAR, QtDecoration, wBar)
595
601
 
596
- newItem.setTextAlignment(self.C_LENGTH, Qt.AlignRight)
597
- newItem.setTextAlignment(self.C_IDLE, Qt.AlignRight)
598
- newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight)
599
- newItem.setTextAlignment(self.C_BAR, Qt.AlignLeft | Qt.AlignVCenter)
602
+ newItem.setTextAlignment(self.C_LENGTH, QtAlignRight)
603
+ newItem.setTextAlignment(self.C_IDLE, QtAlignRight)
604
+ newItem.setTextAlignment(self.C_COUNT, QtAlignRight)
605
+ newItem.setTextAlignment(self.C_BAR, QtAlignLeftMiddle)
600
606
 
601
607
  newItem.setFont(self.C_TIME, SHARED.theme.guiFontFixed)
602
608
  newItem.setFont(self.C_LENGTH, SHARED.theme.guiFontFixed)
novelwriter/types.py ADDED
@@ -0,0 +1,90 @@
1
+ """
2
+ novelWriter – Types and Flags
3
+ =============================
4
+
5
+ File History:
6
+ Created: 2024-04-01 [2.4rc1]
7
+
8
+ This file is a part of novelWriter
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
10
+
11
+ This program is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful, but
17
+ WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
+ General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ from PyQt5.QtCore import Qt
27
+ from PyQt5.QtGui import QColor, QPainter, QTextCursor
28
+ from PyQt5.QtWidgets import QDialogButtonBox, QStyle
29
+
30
+ # Qt Alignment Flags
31
+
32
+ QtAlignAbsolute = Qt.AlignmentFlag.AlignAbsolute
33
+ QtAlignCenter = Qt.AlignmentFlag.AlignCenter
34
+ QtAlignCenterTop = Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop
35
+ QtAlignJustify = Qt.AlignmentFlag.AlignJustify
36
+ QtAlignLeft = Qt.AlignmentFlag.AlignLeft
37
+ QtAlignLeftBase = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBaseline
38
+ QtAlignLeftMiddle = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter
39
+ QtAlignLeftTop = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
40
+ QtAlignMiddle = Qt.AlignmentFlag.AlignVCenter
41
+ QtAlignRight = Qt.AlignmentFlag.AlignRight
42
+ QtAlignRightBase = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBaseline
43
+ QtAlignRightMiddle = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
44
+ QtAlignRightTop = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop
45
+ QtAlignTop = Qt.AlignmentFlag.AlignTop
46
+
47
+ # Qt Painter Types
48
+
49
+ QtTransparent = QColor(0, 0, 0, 0)
50
+ QtNoBrush = Qt.BrushStyle.NoBrush
51
+ QtNoPen = Qt.PenStyle.NoPen
52
+ QtRoundCap = Qt.PenCapStyle.RoundCap
53
+ QtSolidLine = Qt.PenStyle.SolidLine
54
+ QtPaintAnitAlias = QPainter.RenderHint.Antialiasing
55
+ QtMouseOver = QStyle.StateFlag.State_MouseOver
56
+ QtSelected = QStyle.StateFlag.State_Selected
57
+
58
+ # Qt Tree and Table Types
59
+
60
+ QtDecoration = Qt.ItemDataRole.DecorationRole
61
+ QtUserRole = Qt.ItemDataRole.UserRole
62
+
63
+ # Keyboard and Mouse Buttons
64
+
65
+ QtModCtrl = Qt.KeyboardModifier.ControlModifier
66
+ QtModeNone = Qt.KeyboardModifier.NoModifier
67
+ QtModShift = Qt.KeyboardModifier.ShiftModifier
68
+ QtMouseLeft = Qt.MouseButton.LeftButton
69
+ QtMouseMiddle = Qt.MouseButton.MiddleButton
70
+
71
+ # Dialog Button Box Types
72
+
73
+ QtDialogApply = QDialogButtonBox.StandardButton.Apply
74
+ QtDialogCancel = QDialogButtonBox.StandardButton.Cancel
75
+ QtDialogClose = QDialogButtonBox.StandardButton.Close
76
+ QtDialogOk = QDialogButtonBox.StandardButton.Ok
77
+ QtDialogReset = QDialogButtonBox.StandardButton.Reset
78
+ QtDialogSave = QDialogButtonBox.StandardButton.Save
79
+
80
+ QtRoleAccept = QDialogButtonBox.ButtonRole.AcceptRole
81
+ QtRoleAction = QDialogButtonBox.ButtonRole.ActionRole
82
+ QtRoleApply = QDialogButtonBox.ButtonRole.ApplyRole
83
+ QtRoleReject = QDialogButtonBox.ButtonRole.RejectRole
84
+
85
+ # Cursor Types
86
+
87
+ QtKeepAnchor = QTextCursor.MoveMode.KeepAnchor
88
+ QtMoveAnchor = QTextCursor.MoveMode.MoveAnchor
89
+ QtMoveLeft = QTextCursor.MoveOperation.Left
90
+ QtMoveRight = QTextCursor.MoveOperation.Right
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
- <path d="m1.5077 8.1077 10.492 10.662 10.492-10.662c0.33846-0.33846 0.50769-0.84615 0.50769-1.1846 0-0.33846-0.16923-0.84615-0.50769-1.1846-0.33846-0.33846-0.67692-0.50769-1.1846-0.50769h-18.615c-0.50769 0-0.84615 0.16923-1.1846 0.50769-0.33846 0.33846-0.50769 0.84615-0.50769 1.1846 0 0.33846 0.16923 0.84615 0.50769 1.1846z" fill="#aeaeae" stroke-width="1.6923"/>
4
- </svg>
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
- <path d="m8.1077 22.492 10.662-10.492-10.662-10.492c-0.33846-0.33846-0.84615-0.50769-1.1846-0.50769-0.33846 0-0.84615 0.16923-1.1846 0.50769-0.33846 0.33846-0.50769 0.67692-0.50769 1.1846v18.615c0 0.50769 0.16923 0.84615 0.50769 1.1846 0.33846 0.33846 0.84615 0.50769 1.1846 0.50769 0.33846 0 0.84615-0.16923 1.1846-0.50769z" fill="#aeaeae" stroke-width="1.6923"/>
4
- </svg>
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
- <path d="m1.5077 8.1077 10.492 10.662 10.492-10.662c0.33846-0.33846 0.50769-0.84615 0.50769-1.1846 0-0.33846-0.16923-0.84615-0.50769-1.1846-0.33846-0.33846-0.67692-0.50769-1.1846-0.50769h-18.615c-0.50769 0-0.84615 0.16923-1.1846 0.50769-0.33846 0.33846-0.50769 0.84615-0.50769 1.1846 0 0.33846 0.16923 0.84615 0.50769 1.1846z" fill-opacity=".78039" stroke-width="1.6923"/>
4
- </svg>
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
- <path d="m8.1077 22.492 10.662-10.492-10.662-10.492c-0.33846-0.33846-0.84615-0.50769-1.1846-0.50769-0.33846 0-0.84615 0.16923-1.1846 0.50769-0.33846 0.33846-0.50769 0.67692-0.50769 1.1846v18.615c0 0.50769 0.16923 0.84615 0.50769 1.1846 0.33846 0.33846 0.84615 0.50769 1.1846 0.50769 0.33846 0 0.84615-0.16923 1.1846-0.50769z" fill-opacity=".78039" stroke-width="1.6923"/>
4
- </svg>
@@ -1,3 +0,0 @@
1
- """
2
- novelWriter – Core Init File
3
- """