diff --git a/.travis.yml b/.travis.yml
index a39e4233..995f0289 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ language: generic
os:
- osx
- linux
-osx_image: xcode10.1
+osx_image: xcode11
sudo: required
install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then package/prepare_osx.sh; fi
diff --git a/i18n/manuskript_fr.ts b/i18n/manuskript_fr.ts
index a53aea61..0ee622c1 100644
--- a/i18n/manuskript_fr.ts
+++ b/i18n/manuskript_fr.ts
@@ -5,7 +5,7 @@
- Exporteur de base, fournit les formats de base utilisés par les autres exporteurs.
+ Exportateur de base, fournit les formats de base utilisés par les autres exportateurs.
@@ -28,7 +28,7 @@
formats.</p>
<p>Website: <a href="http://www.pandoc.org">http://pandoc.org/</a></p>
- <p>Un convertisseur universel de document. Peut être utilisé pour convertir du Markdown dans un grand nombre de formats.</p>
+ <p>Un convertisseur universel de documents. Peut être utilisé pour convertir du Markdown dans un grand nombre de formats.</p>
<p>Site internet : <a href="http://www.pandoc.org">http://pandoc.org/</a></p>
@@ -36,7 +36,7 @@
- Comme en texte brute, mais rajoute les titres markdown.
+ Comme en texte brut, mais ajoute les titres markdown.
@@ -46,7 +46,7 @@
- Texte brute
+ Texte brut
@@ -99,7 +99,7 @@ Par exemple <a href='www.fountain.io'>Fountain</a>.
- Export vers markdown en utilisant pandoc. Permet plus de possibilités que l'exporteur basique intégré à manuskript.
+ Export vers markdown en utilisant pandoc. Permet plus de possibilités que l'exportateur basique intégré à manuskript.
@@ -110,7 +110,7 @@ Par exemple <a href='www.fountain.io'>Fountain</a>.
- LaTeX est une suite d'outil et un format utilisé pour créer des documents magnifiques.
+ LaTeX est une suite d'outils et un format utilisé pour créer des documents magnifiques.
@@ -125,7 +125,7 @@ Par exemple <a href='www.fountain.io'>Fountain</a>.
- Nombre de niveau à inclure dans la table des matières:
+ Nombre de niveaux à inclure dans la table des matières :
@@ -140,7 +140,7 @@ Par exemple <a href='www.fountain.io'>Fountain</a>.
- Spécifier le niveau de base pour les titres:
+ Spécifier le niveau de base pour les titres :
@@ -191,7 +191,7 @@ Par exemple <a href='www.fountain.io'>Fountain</a>.
- nécessite une installation latex valide. Voir les recommendations de pandoc sur <a href="http://pandoc.org/installing.html">http://pandoc.org/installing.html</a>. Pour le support unicode, il vous faut xelatex.
+ nécessite une installation LaTeX valide. Voir les recommandations de pandoc sur <a href="http://pandoc.org/installing.html">http://pandoc.org/installing.html</a>. Pour le support unicode, il vous faut XeLaTeX.
@@ -222,7 +222,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Choisir le fichier de sortie...
+ Choisir le fichier de sortie…
@@ -230,7 +230,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Gérer les Exporteurs
+ Gérer les Exportateurs
@@ -250,27 +250,27 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Status
+ Statut
- Status :
+ Statut :
- Version:
+ Version :
- Chemin:
+ Chemin :
- ...
+ …
@@ -298,7 +298,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Taille minimum:
+ Taille minimum :
@@ -318,7 +318,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Nombre de mots: de
+ Nombre de mots : de
@@ -355,7 +355,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
are added as scene.</p>
<p>Only text files are supported (not images, binary or others).</p>
<p><b>Info:</b> Importe la structure d'un dossier. Les dossiers sont crées comme dossiers, et les documents textes sont ajoutés comme des scènes.</p>
-<p>Seulement les fichiers textes sont supportés (pas les images, fichiers binaires ou autres).</p>
+<p>Seuls les fichiers textes sont pris en charge (pas les images, fichiers binaires ou autres).</p>
@@ -375,7 +375,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Importer les dossiers en premier
+ Importer les dossiers puis les fichiers/translation>
@@ -385,7 +385,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Echec de l'ouverture du fichier
+ Échec de l'ouverture du fichier
@@ -410,12 +410,12 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Importer via:
+ Importer via :
- Replier les lignes:
+ Replier les lignes :
@@ -425,8 +425,8 @@ Cochez ceci si vous avez des erreurs liées à YAML.
<b>none</b>: no line wrap.<br>
<b>preserve</b>: tries to preserves line wrap from the
original document.</p>
- <p>Pandoc doit-il créer des retours à la lignes cosmétiques/non-sémantiques?</p>
-<p><b>auto</b>: plier les lignes à 72 charactères.<br>
+ <p>Pandoc doit-il créer des retours à la ligne cosmétiques/non-sémantiques?</p>
+<p><b>auto</b>: retour à la ligne après 72 caractères.<br>
<b>none</b>: pas de retours à la lignes.<br>
<b>preserve</b>: essaie de préserver les retours à la lignes du document original.</p>
@@ -448,7 +448,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Importer les pointes comme:
+ Importer les astuces comme :
@@ -494,7 +494,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- License
+ Licence
@@ -729,17 +729,17 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Situation:
+ Situation :
- Résumé:
+ Résumé :
- Et si... ?
+ Et si… ?
@@ -839,7 +839,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Arborescence
+ Arborescence du projet
@@ -854,7 +854,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Créez ici vos personnage.
+ Créez ici vos personnages.
@@ -874,7 +874,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Des informations pour débugger des fois pendant qu'on code c'est utile.
+ Des informations pour débugger pendant qu'on code c'est parfois utile.
@@ -889,7 +889,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- POV
+ Pt de vue
@@ -919,7 +919,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- Icone
+ Icône
@@ -1004,7 +1004,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- &Afficher les bulles d'aides
+ &Afficher les bulles d'aide
@@ -1019,7 +1019,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- &Status…
+ &Statut…
@@ -1054,7 +1054,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
-
+
@@ -1081,7 +1081,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- &A propos
+ &À propos
@@ -1096,12 +1096,12 @@ Cochez ceci si vous avez des erreurs liées à YAML.
- ATTENTION: Le projet {} n'a pas été enregistré.
+ ATTENTION : Le projet {} n'a pas été enregistré.
- Construire des mondes. Crée une hierarchie en partant des catégories les plus larges jusqu'au détails les plus spécifiques.
+ Construire des mondes. Crée une hiérarchie en partant des catégories les plus larges jusqu'aux détails les plus spécifiques.
diff --git a/manuskript/enums.py b/manuskript/enums.py
index e5751a7d..0b86392f 100644
--- a/manuskript/enums.py
+++ b/manuskript/enums.py
@@ -61,6 +61,7 @@ class Outline(IntEnum):
textFormat = 15
revisions = 16
customIcon = 17
+ charCount = 18
class Abstract(IntEnum):
title = 0
diff --git a/manuskript/functions/__init__.py b/manuskript/functions/__init__.py
index 8d491921..5f790b58 100644
--- a/manuskript/functions/__init__.py
+++ b/manuskript/functions/__init__.py
@@ -23,6 +23,14 @@ def wordCount(text):
t = [l for l in t if l]
return len(t)
+def charCount(text, use_spaces = True):
+ t = text.strip()
+
+ if not use_spaces:
+ t = t.replace(" ", "")
+
+ return len(t)
+
validate_ok = lambda *args, **kwargs: True
def uiParse(input, default, converter, validator=validate_ok):
"""
diff --git a/manuskript/functions/spellchecker.py b/manuskript/functions/spellchecker.py
index da41f0db..d3b90144 100644
--- a/manuskript/functions/spellchecker.py
+++ b/manuskript/functions/spellchecker.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
-import os, gzip, json, glob
+import os, gzip, json, glob, re
from PyQt5.QtCore import QLocale
from collections import OrderedDict
from manuskript.functions import writablePath
@@ -28,6 +28,11 @@ except ImportError:
symspellpy = None
+try:
+ import language_check as languagetool
+except:
+ languagetool = None
+
class Spellchecker:
dictionaries = {}
# In order of priority
@@ -117,6 +122,17 @@ class Spellchecker:
pass
return None
+class BasicMatch:
+ def __init__(self, startIndex, endIndex):
+ self.start = startIndex
+ self.end = endIndex
+ self.locqualityissuetype = 'misspelling'
+ self.replacements = []
+ self.msg = ''
+
+ def getWord(self, text):
+ return text[self.start:self.end]
+
class BasicDictionary:
def __init__(self, name):
self._lang = name
@@ -162,12 +178,45 @@ class BasicDictionary:
def availableDictionaries():
raise NotImplemented
+ def checkText(self, text):
+ # Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
+ WORDS = r'(?iu)((?:[^_\W]|\')+)[^A-Za-z0-9\']'
+ # (?iu) means case insensitive and Unicode
+ # ((?:[^_\W]|\')+) means words exclude underscores but include apostrophes
+ # [^A-Za-z0-9\'] used with above hack to prevent spellcheck while typing word
+ #
+ # See also https://stackoverflow.com/questions/2062169/regex-w-in-utf-8
+
+ matches = []
+
+ for word_object in re.finditer(WORDS, text):
+ word = word_object.group(1)
+
+ if (self.isMisspelled(word) and not self.isCustomWord(word)):
+ matches.append(BasicMatch(
+ word_object.start(1), word_object.end(1)
+ ))
+
+ return matches
+
def isMisspelled(self, word):
raise NotImplemented
def getSuggestions(self, word):
raise NotImplemented
+ def findSuggestions(self, text, start, end):
+ if start < end:
+ word = text[start:end]
+
+ if (self.isMisspelled(word) and not self.isCustomWord(word)):
+ match = BasicMatch(start, end)
+ match.replacements = self.getSuggestions(word)
+
+ return [ match ]
+
+ return []
+
def isCustomWord(self, word):
return word.lower() in self._customDict
@@ -248,6 +297,9 @@ class EnchantDictionary(BasicDictionary):
def getSuggestions(self, word):
return self._dict.suggest(word)
+ def findSuggestions(self, text, start, end):
+ return []
+
def isCustomWord(self, word):
return self._dict.is_added(word)
@@ -422,8 +474,152 @@ class SymSpellDictionary(BasicDictionary):
# Since 6.3.8
self._dict.delete_dictionary_entry(word)
+class LanguageToolCache:
+
+ def __init__(self, tool, text):
+ self._length = len(text)
+ self._matches = self._buildMatches(tool, text)
+
+ def getMatches(self):
+ return self._matches
+
+ def _buildMatches(self, tool, text):
+ matches = []
+
+ for match in tool.check(text):
+ start = match.offset
+ end = start + match.errorlength
+
+ basic_match = BasicMatch(start, end)
+ basic_match.locqualityissuetype = match.locqualityissuetype
+ basic_match.replacements = match.replacements
+ basic_match.msg = match.msg
+
+ matches.append(basic_match)
+
+ return matches
+
+ def update(self, tool, text):
+ if len(text) != self._length:
+ self._matches = self._buildMatches(tool, text)
+
+class LanguageToolDictionary(BasicDictionary):
+
+ def __init__(self, name):
+ BasicDictionary.__init__(self, name)
+
+ if not (self._lang and self._lang in languagetool.get_languages()):
+ self._lang = self.getDefaultDictionary()
+
+ self._tool = languagetool.LanguageTool(self._lang)
+ self._cache = {}
+
+ @staticmethod
+ def getLibraryName():
+ return "LanguageCheck"
+
+ @staticmethod
+ def getLibraryURL():
+ return "https://pypi.org/project/language-check/"
+
+ @staticmethod
+ def isInstalled():
+ if languagetool is not None:
+
+ # This check, if Java is installed, is necessary to
+ # make sure LanguageTool can be run without problems.
+ #
+ return (os.system('java -version') == 0)
+
+ return False
+
+ @staticmethod
+ def availableDictionaries():
+ if LanguageToolDictionary.isInstalled():
+ languages = list(languagetool.get_languages())
+ languages.sort()
+ return languages
+ return []
+
+ @staticmethod
+ def getDefaultDictionary():
+ if not LanguageToolDictionary.isInstalled():
+ return None
+
+ default_locale = languagetool.get_locale_language()
+ if default_locale and not default_locale in languagetool.get_languages():
+ default_locale = None
+
+ if default_locale is None:
+ default_locale = QLocale.system().name()
+ if default_locale is None:
+ default_locale = self.availableDictionaries()[0]
+
+ return default_locale
+
+ def checkText(self, text):
+ matches = []
+
+ if len(text) == 0:
+ return matches
+
+ textId = hash(text)
+ cacheEntry = None
+
+ if not textId in self._cache:
+ cacheEntry = LanguageToolCache(self._tool, text)
+
+ self._cache[textId] = cacheEntry
+ else:
+ cacheEntry = self._cache[textId]
+ cacheEntry.update(self._tool, text)
+
+ for match in cacheEntry.getMatches():
+ word = match.getWord(text)
+
+ if not (match.locqualityissuetype == 'misspelling' and self.isCustomWord(word)):
+ matches.append(match)
+
+ return matches
+
+ def isMisspelled(self, word):
+ if self.isCustomWord(word):
+ return False
+
+ for match in self.checkText(word):
+ if match.locqualityissuetype == 'misspelling':
+ return True
+
+ return False
+
+ def getSuggestions(self, word):
+ suggestions = []
+
+ for match in self.checkText(word):
+ suggestions += match.replacements
+
+ return suggestions
+
+ def findSuggestions(self, text, start, end):
+ matches = []
+ checked = self.checkText(text)
+
+ if start == end:
+ # Check for containing area:
+ for match in checked:
+ if (start >= match.start and start <= match.end):
+ matches.append(match)
+ else:
+ # Check for overlapping area:
+ for match in checked:
+ if (match.end > start and match.start < end):
+ matches.append(match)
+
+ return matches
+
# Register the implementations in order of priority
-Spellchecker.implementations.append(EnchantDictionary)
+Spellchecker.registerImplementation(EnchantDictionary)
Spellchecker.registerImplementation(SymSpellDictionary)
Spellchecker.registerImplementation(PySpellcheckerDictionary)
+Spellchecker.registerImplementation(LanguageToolDictionary)
diff --git a/manuskript/models/outlineItem.py b/manuskript/models/outlineItem.py
index 56b64649..b2ab2fa7 100644
--- a/manuskript/models/outlineItem.py
+++ b/manuskript/models/outlineItem.py
@@ -80,6 +80,9 @@ class outlineItem(abstractItem):
def wordCount(self):
return self._data.get(self.enum.wordCount, 0)
+ def charCount(self):
+ return self._data.get(self.enum.charCount, 0)
+
def __str__(self):
return "{id}: {folder}{title}{children}".format(
id=self.ID(),
@@ -89,6 +92,9 @@ class outlineItem(abstractItem):
)
__repr__ = __str__
+
+ def charCount(self):
+ return self._data.get(self.enum.charCount, 0)
#######################################################################
# Data
@@ -119,7 +125,7 @@ class outlineItem(abstractItem):
elif role == Qt.FontRole:
f = QFont()
- if column == E.wordCount and self.isFolder():
+ if (column == E.wordCount or column == E.charCount) and self.isFolder():
f.setItalic(True)
elif column == E.goal and self.isFolder() and not self.data(E.setGoal):
f.setItalic(True)
@@ -140,7 +146,7 @@ class outlineItem(abstractItem):
# Checking if we will have to recount words
updateWordCount = False
- if column in [E.wordCount, E.goal, E.setGoal]:
+ if column in [E.wordCount, E.charCount, E.goal, E.setGoal]:
updateWordCount = not column in self._data or self._data[column] != data
# Stuff to do before
@@ -153,7 +159,9 @@ class outlineItem(abstractItem):
# Stuff to do afterwards
if column == E.text:
wc = F.wordCount(data)
+ cc = F.charCount(data, settings.countSpaces)
self.setData(E.wordCount, wc)
+ self.setData(E.charCount, cc)
if column == E.compile:
# Title changes when compile changes
@@ -195,9 +203,12 @@ class outlineItem(abstractItem):
else:
wc = 0
+ cc = 0
for c in self.children():
wc += F.toInt(c.data(self.enum.wordCount))
+ cc += F.toInt(c.data(self.enum.charCount))
self._data[self.enum.wordCount] = wc
+ self._data[self.enum.charCount] = cc
setGoal = F.toInt(self.data(self.enum.setGoal))
goal = F.toInt(self.data(self.enum.goal))
@@ -218,7 +229,8 @@ class outlineItem(abstractItem):
self.setData(self.enum.goalPercentage, "")
self.emitDataChanged([self.enum.goal, self.enum.setGoal,
- self.enum.wordCount, self.enum.goalPercentage])
+ self.enum.wordCount, self.enum.charCount,
+ self.enum.goalPercentage])
if self.parent():
self.parent().updateWordCount()
@@ -467,6 +479,7 @@ class outlineItem(abstractItem):
# We don't want to write some datas (computed)
XMLExclude = [enums.Outline.wordCount,
+ enums.Outline.charCount,
enums.Outline.goal,
enums.Outline.goalPercentage,
enums.Outline.revisions]
diff --git a/manuskript/settings.py b/manuskript/settings.py
index 96a658e1..8f4884ea 100644
--- a/manuskript/settings.py
+++ b/manuskript/settings.py
@@ -47,6 +47,8 @@ corkSizeFactor = 100
folderView = "cork"
lastTab = 0
openIndexes = [""]
+progressChars = False
+countSpaces = True
autoSave = False
autoSaveDelay = 5
autoSaveNoChanges = True
@@ -123,7 +125,7 @@ def initDefaultValues():
def save(filename=None, protocol=None):
global spellcheck, dict, corkSliderFactor, viewSettings, corkSizeFactor, folderView, lastTab, openIndexes, \
- autoSave, autoSaveDelay, saveOnQuit, autoSaveNoChanges, autoSaveNoChangesDelay, outlineViewColumns, \
+ progressChars, autoSave, autoSaveDelay, saveOnQuit, autoSaveNoChanges, autoSaveNoChangesDelay, outlineViewColumns, \
corkBackground, corkStyle, fullScreenTheme, defaultTextType, textEditor, revisions, frequencyAnalyzer, viewMode, \
saveToZip, dontShowDeleteWarning, fullscreenSettings
@@ -136,6 +138,8 @@ def save(filename=None, protocol=None):
"folderView": folderView,
"lastTab": lastTab,
"openIndexes": openIndexes,
+ "progressChars": progressChars,
+ "countSpaces": countSpaces,
"autoSave":autoSave,
"autoSaveDelay":autoSaveDelay,
# TODO: Settings Cleanup Task -- Rename saveOnQuit to saveOnProjectClose -- see PR #615
@@ -235,6 +239,14 @@ def load(string, fromString=False, protocol=None):
global openIndexes
openIndexes = allSettings["openIndexes"]
+ if "progressChars" in allSettings:
+ global progressChars
+ progressChars = allSettings["progressChars"]
+
+ if "countSpaces" in allSettings:
+ global countSpaces
+ countSpaces = allSettings["countSpaces"]
+
if "autoSave" in allSettings:
global autoSave
autoSave = allSettings["autoSave"]
diff --git a/manuskript/settingsWindow.py b/manuskript/settingsWindow.py
index 4ae34bf6..ac8f8551 100644
--- a/manuskript/settingsWindow.py
+++ b/manuskript/settingsWindow.py
@@ -111,6 +111,9 @@ class settingsWindow(QWidget, Ui_Settings):
self.spnGeneralFontSize.setValue(f.pointSize())
self.spnGeneralFontSize.valueChanged.connect(self.setAppFontSize)
+ self.chkProgressChars.setChecked(settings.progressChars);
+ self.chkProgressChars.stateChanged.connect(self.charSettingsChanged)
+
self.txtAutoSave.setValidator(QIntValidator(0, 999, self))
self.txtAutoSaveNoChanges.setValidator(QIntValidator(0, 999, self))
self.chkAutoSave.setChecked(settings.autoSave)
@@ -164,10 +167,12 @@ class settingsWindow(QWidget, Ui_Settings):
for item, what, value in [
(self.rdoTreeItemCount, "InfoFolder", "Count"),
(self.rdoTreeWC, "InfoFolder", "WC"),
+ (self.rdoTreeCC, "InfoFolder", "CC"),
(self.rdoTreeProgress, "InfoFolder", "Progress"),
(self.rdoTreeSummary, "InfoFolder", "Summary"),
(self.rdoTreeNothing, "InfoFolder", "Nothing"),
(self.rdoTreeTextWC, "InfoText", "WC"),
+ (self.rdoTreeTextCC, "InfoText", "CC"),
(self.rdoTreeTextProgress, "InfoText", "Progress"),
(self.rdoTreeTextSummary, "InfoText", "Summary"),
(self.rdoTreeTextNothing, "InfoText", "Nothing"),
@@ -180,6 +185,9 @@ class settingsWindow(QWidget, Ui_Settings):
lambda v: self.lblTreeIconSize.setText("{}x{}".format(v, v)))
self.sldTreeIconSize.setValue(settings.viewSettings["Tree"]["iconSize"])
+ self.chkCountSpaces.setChecked(settings.countSpaces);
+ self.chkCountSpaces.stateChanged.connect(self.countSpacesChanged)
+
self.rdoCorkOldStyle.setChecked(settings.corkStyle == "old")
self.rdoCorkNewStyle.setChecked(settings.corkStyle == "new")
self.rdoCorkNewStyle.toggled.connect(self.setCorkStyle)
@@ -338,6 +346,11 @@ class settingsWindow(QWidget, Ui_Settings):
sttgs = QSettings(qApp.organizationName(), qApp.applicationName())
sttgs.setValue("appFontSize", val)
+ def charSettingsChanged(self):
+ settings.progressChars = True if self.chkProgressChars.checkState() else False
+
+ self.mw.mainEditor.updateStats()
+
def saveSettingsChanged(self):
if self.txtAutoSave.text() in ["", "0"]:
self.txtAutoSave.setText("1")
@@ -427,10 +440,12 @@ class settingsWindow(QWidget, Ui_Settings):
for item, what, value in [
(self.rdoTreeItemCount, "InfoFolder", "Count"),
(self.rdoTreeWC, "InfoFolder", "WC"),
+ (self.rdoTreeCC, "InfoFolder", "CC"),
(self.rdoTreeProgress, "InfoFolder", "Progress"),
(self.rdoTreeSummary, "InfoFolder", "Summary"),
(self.rdoTreeNothing, "InfoFolder", "Nothing"),
(self.rdoTreeTextWC, "InfoText", "WC"),
+ (self.rdoTreeTextCC, "InfoText", "CC"),
(self.rdoTreeTextProgress, "InfoText", "Progress"),
(self.rdoTreeTextSummary, "InfoText", "Summary"),
(self.rdoTreeTextNothing, "InfoText", "Nothing"),
@@ -445,6 +460,11 @@ class settingsWindow(QWidget, Ui_Settings):
self.mw.treeRedacOutline.viewport().update()
+ def countSpacesChanged(self):
+ settings.countSpaces = True if self.chkCountSpaces.checkState() else False
+
+ self.mw.mainEditor.updateStats()
+
def setCorkColor(self):
color = QColor(settings.corkBackground["color"])
self.colorDialog = QColorDialog(color, self)
diff --git a/manuskript/ui/editors/mainEditor.py b/manuskript/ui/editors/mainEditor.py
index 967f2832..882292ee 100644
--- a/manuskript/ui/editors/mainEditor.py
+++ b/manuskript/ui/editors/mainEditor.py
@@ -292,6 +292,7 @@ class mainEditor(QWidget, Ui_mainEditor):
return
index = self.currentEditor().currentIndex
+
if index.isValid():
item = index.internalPointer()
else:
@@ -300,15 +301,21 @@ class mainEditor(QWidget, Ui_mainEditor):
if not item:
item = self.mw.mdlOutline.rootItem
+ cc = item.data(Outline.charCount)
wc = item.data(Outline.wordCount)
goal = item.data(Outline.goal)
+ chars = item.data(Outline.charCount) # len(item.data(Outline.text))
progress = item.data(Outline.goalPercentage)
goal = uiParse(goal, None, int, lambda x: x>=0)
progress = uiParse(progress, 0.0, float)
+ if not cc:
+ cc = 0
+
if not wc:
wc = 0
+
if goal:
self.lblRedacProgress.show()
rect = self.lblRedacProgress.geometry()
@@ -319,13 +326,31 @@ class mainEditor(QWidget, Ui_mainEditor):
drawProgress(p, rect, progress, 2)
del p
self.lblRedacProgress.setPixmap(self.px)
- self.lblRedacWC.setText(self.tr("{} words / {} ").format(
- locale.format_string("%d", wc, grouping=True),
- locale.format_string("%d", goal, grouping=True)))
+
+ if settings.progressChars:
+ self.lblRedacWC.setText(self.tr("({} chars) {} words / {} ").format(
+ locale.format("%d", cc, grouping=True),
+ locale.format("%d", wc, grouping=True),
+ locale.format("%d", goal, grouping=True)))
+ self.lblRedacWC.setToolTip("")
+ else:
+ self.lblRedacWC.setText(self.tr("{} words / {} ").format(
+ locale.format("%d", wc, grouping=True),
+ locale.format("%d", goal, grouping=True)))
+ self.lblRedacWC.setToolTip(self.tr("{} chars").format(
+ locale.format("%d", cc, grouping=True)))
else:
self.lblRedacProgress.hide()
- self.lblRedacWC.setText(self.tr("{} words ").format(
- locale.format_string("%d", wc, grouping=True)))
+
+ if settings.progressChars:
+ self.lblRedacWC.setText(self.tr("{} chars ").format(
+ locale.format("%d", cc, grouping=True)))
+ self.lblRedacWC.setToolTip("")
+ else:
+ self.lblRedacWC.setText(self.tr("{} words ").format(
+ locale.format("%d", wc, grouping=True)))
+ self.lblRedacWC.setToolTip(self.tr("{} chars").format(
+ locale.format("%d", cc, grouping=True)))
###############################################################################
# VIEWS
diff --git a/manuskript/ui/highlighters/basicHighlighter.py b/manuskript/ui/highlighters/basicHighlighter.py
index 362ee5a2..17ae8bd7 100644
--- a/manuskript/ui/highlighters/basicHighlighter.py
+++ b/manuskript/ui/highlighters/basicHighlighter.py
@@ -18,7 +18,6 @@ class BasicHighlighter(QSyntaxHighlighter):
QSyntaxHighlighter.__init__(self, editor.document())
self.editor = editor
- self._misspelledColor = Qt.red
self._defaultBlockFormat = QTextBlockFormat()
self._defaultCharFormat = QTextCharFormat()
self.defaultTextColor = QColor(S.text)
@@ -27,6 +26,40 @@ class BasicHighlighter(QSyntaxHighlighter):
self.linkColor = QColor(S.link)
self.spellingErrorColor = QColor(Qt.red)
+ # Matches during checking can be separated by their type (all of them listed here):
+ # https://languagetool.org/development/api/org/languagetool/rules/ITSIssueType.html
+ #
+ # These are the colors for actual spell-, grammar- and style-checking:
+ self._errorColors = {
+ 'addition' : QColor(255, 215, 0), # gold
+ 'characters' : QColor(135, 206, 235), # sky blue
+ 'duplication' : QColor(0, 255, 255), # cyan / aqua
+ 'formatting' : QColor(0, 128, 128), # teal
+ 'grammar' : QColor(0, 0, 255), # blue
+ 'inconsistency' : QColor(128, 128, 0), # olive
+ 'inconsistententities' : QColor(46, 139, 87), # sea green
+ 'internationalization' : QColor(255, 165, 0), # orange
+ 'legal' : QColor(255, 69, 0), # orange red
+ 'length' : QColor(47, 79, 79), # dark slate gray
+ 'localespecificcontent' : QColor(188, 143, 143),# rosy brown
+ 'localeviolation' : QColor(128, 0, 0), # maroon
+ 'markup' : QColor(128, 0, 128), # purple
+ 'misspelling' : QColor(255, 0, 0), # red
+ 'mistranslation' : QColor(255, 0, 255), # magenta / fuchsia
+ 'nonconformance' : QColor(255, 218, 185), # peach puff
+ 'numbers' : QColor(65, 105, 225), # royal blue
+ 'omission' : QColor(255, 20, 147), # deep pink
+ 'other' : QColor(138, 43, 226), # blue violet
+ 'patternproblem' : QColor(0, 128, 0), # green
+ 'register' : QColor(112,128,144), # slate gray
+ 'style' : QColor(0, 255, 0), # lime
+ 'terminology' : QColor(0, 0, 128), # navy
+ 'typographical' : QColor(255, 255, 0), # yellow
+ 'uncategorized' : QColor(128, 128, 128), # gray
+ 'untranslated' : QColor(210, 105, 30), # chocolate
+ 'whitespace' : QColor(192, 192, 192) # silver
+ }
+
def setDefaultBlockFormat(self, bf):
self._defaultBlockFormat = bf
self.rehighlight()
@@ -36,7 +69,7 @@ class BasicHighlighter(QSyntaxHighlighter):
self.rehighlight()
def setMisspelledColor(self, color):
- self._misspelledColor = color
+ self._errorColors['misspelled'] = color
def updateColorScheme(self, rehighlight=True):
"""
@@ -134,32 +167,25 @@ class BasicHighlighter(QSyntaxHighlighter):
txt.end() - txt.start(),
fmt)
- # Spell checking
+ if hasattr(self.editor, "spellcheck") and self.editor.spellcheck and self.editor._dict:
+ # Spell checking
- # Following algorithm would not check words at the end of line.
- # This hacks adds a space to every line where the text cursor is not
- # So that it doesn't spellcheck while typing, but still spellchecks at
- # end of lines. See github's issue #166.
- textedText = text
- if self.currentBlock().position() + len(text) != \
- self.editor.textCursor().position():
- textedText = text + " "
+ # Following algorithm would not check words at the end of line.
+ # This hacks adds a space to every line where the text cursor is not
+ # So that it doesn't spellcheck while typing, but still spellchecks at
+ # end of lines. See github's issue #166.
+ textedText = text
+ if self.currentBlock().position() + len(text) != \
+ self.editor.textCursor().position():
+ textedText = text + " "
- # Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
- WORDS = r'(?iu)((?:[^_\W]|\')+)[^A-Za-z0-9\']'
- # (?iu) means case insensitive and Unicode
- # ((?:[^_\W]|\')+) means words exclude underscores but include apostrophes
- # [^A-Za-z0-9\'] used with above hack to prevent spellcheck while typing word
- #
- # See also https://stackoverflow.com/questions/2062169/regex-w-in-utf-8
- if hasattr(self.editor, "spellcheck") and self.editor.spellcheck:
- for word_object in re.finditer(WORDS, textedText):
- if (self.editor._dict
- and self.editor._dict.isMisspelled(word_object.group(1))):
- format = self.format(word_object.start(1))
- format.setUnderlineColor(self._misspelledColor)
+ # The text should only be checked once as a whole
+ for match in self.editor._dict.checkText(textedText):
+ if match.locqualityissuetype in self._errorColors:
+ highlight_color = self._errorColors[match.locqualityissuetype]
+
+ format = self.format(match.start)
+ format.setUnderlineColor(highlight_color)
# SpellCheckUnderline fails with some fonts
format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
- self.setFormat(word_object.start(1),
- word_object.end(1) - word_object.start(1),
- format)
+ self.setFormat(match.start, match.end - match.start, format)
diff --git a/manuskript/ui/settings_ui.py b/manuskript/ui/settings_ui.py
index 9053f31a..b34d9823 100644
--- a/manuskript/ui/settings_ui.py
+++ b/manuskript/ui/settings_ui.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
-# Form implementation generated from reading ui file 'manuskript/ui/settings_ui.ui'
+# Form implementation generated from reading ui file 'settings_ui.ui'
#
-# Created by: PyQt5 UI code generator 5.14.1
+# Created by: PyQt5 UI code generator 5.15.0
#
-# WARNING! All changes made in this file will be lost!
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
@@ -13,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Settings(object):
def setupUi(self, Settings):
Settings.setObjectName("Settings")
- Settings.resize(658, 598)
+ Settings.resize(681, 598)
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(Settings)
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.lstMenu = QtWidgets.QListWidget(Settings)
@@ -55,50 +56,9 @@ class Ui_Settings(object):
self.groupBox_2.setFont(font)
self.groupBox_2.setObjectName("groupBox_2")
self.formLayout_13 = QtWidgets.QFormLayout(self.groupBox_2)
- self.formLayout_13.setFieldGrowthPolicy(QtWidgets.QFormLayout.FieldsStayAtSizeHint)
self.formLayout_13.setObjectName("formLayout_13")
- self.label_56 = QtWidgets.QLabel(self.groupBox_2)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.label_56.setFont(font)
- self.label_56.setObjectName("label_56")
- self.formLayout_13.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_56)
- self.cmbStyle = QtWidgets.QComboBox(self.groupBox_2)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.cmbStyle.setFont(font)
- self.cmbStyle.setObjectName("cmbStyle")
- self.formLayout_13.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.cmbStyle)
- self.label_57 = QtWidgets.QLabel(self.groupBox_2)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.label_57.setFont(font)
- self.label_57.setObjectName("label_57")
- self.formLayout_13.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_57)
- self.cmbTranslation = QtWidgets.QComboBox(self.groupBox_2)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.cmbTranslation.setFont(font)
- self.cmbTranslation.setObjectName("cmbTranslation")
- self.formLayout_13.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.cmbTranslation)
- self.label_58 = QtWidgets.QLabel(self.groupBox_2)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.label_58.setFont(font)
- self.label_58.setObjectName("label_58")
- self.formLayout_13.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_58)
- self.spnGeneralFontSize = QtWidgets.QSpinBox(self.groupBox_2)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.spnGeneralFontSize.setFont(font)
- self.spnGeneralFontSize.setObjectName("spnGeneralFontSize")
- self.formLayout_13.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.spnGeneralFontSize)
+ self.gridLayout_4 = QtWidgets.QGridLayout()
+ self.gridLayout_4.setObjectName("gridLayout_4")
self.label_2 = QtWidgets.QLabel(self.groupBox_2)
font = QtGui.QFont()
font.setBold(False)
@@ -106,7 +66,70 @@ class Ui_Settings(object):
self.label_2.setFont(font)
self.label_2.setWordWrap(True)
self.label_2.setObjectName("label_2")
- self.formLayout_13.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.label_2)
+ self.gridLayout_4.addWidget(self.label_2, 0, 0, 1, 1)
+ self.horizontalLayout_12 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_12.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+ self.horizontalLayout_12.setObjectName("horizontalLayout_12")
+ self.formLayout_14 = QtWidgets.QFormLayout()
+ self.formLayout_14.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
+ self.formLayout_14.setObjectName("formLayout_14")
+ self.label_56 = QtWidgets.QLabel(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.label_56.setFont(font)
+ self.label_56.setObjectName("label_56")
+ self.formLayout_14.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_56)
+ self.cmbStyle = QtWidgets.QComboBox(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.cmbStyle.setFont(font)
+ self.cmbStyle.setObjectName("cmbStyle")
+ self.formLayout_14.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cmbStyle)
+ self.label_57 = QtWidgets.QLabel(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.label_57.setFont(font)
+ self.label_57.setObjectName("label_57")
+ self.formLayout_14.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_57)
+ self.cmbTranslation = QtWidgets.QComboBox(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.cmbTranslation.setFont(font)
+ self.cmbTranslation.setObjectName("cmbTranslation")
+ self.formLayout_14.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.cmbTranslation)
+ self.label_58 = QtWidgets.QLabel(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.label_58.setFont(font)
+ self.label_58.setObjectName("label_58")
+ self.formLayout_14.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_58)
+ self.spnGeneralFontSize = QtWidgets.QSpinBox(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.spnGeneralFontSize.setFont(font)
+ self.spnGeneralFontSize.setObjectName("spnGeneralFontSize")
+ self.formLayout_14.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.spnGeneralFontSize)
+ self.horizontalLayout_12.addLayout(self.formLayout_14)
+ self.formLayout_15 = QtWidgets.QFormLayout()
+ self.formLayout_15.setObjectName("formLayout_15")
+ self.chkProgressChars = QtWidgets.QCheckBox(self.groupBox_2)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.chkProgressChars.setFont(font)
+ self.chkProgressChars.setObjectName("chkProgressChars")
+ self.formLayout_15.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.chkProgressChars)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.formLayout_15.setItem(0, QtWidgets.QFormLayout.LabelRole, spacerItem)
+ self.horizontalLayout_12.addLayout(self.formLayout_15)
+ self.gridLayout_4.addLayout(self.horizontalLayout_12, 1, 0, 1, 1)
+ self.formLayout_13.setLayout(0, QtWidgets.QFormLayout.SpanningRole, self.gridLayout_4)
self.verticalLayout_7.addWidget(self.groupBox_2)
self.groupBox_10 = QtWidgets.QGroupBox(self.stackedWidgetPage1)
font = QtGui.QFont()
@@ -166,8 +189,8 @@ class Ui_Settings(object):
self.label.setFont(font)
self.label.setObjectName("label")
self.horizontalLayout_5.addWidget(self.label)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout_5.addItem(spacerItem)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_5.addItem(spacerItem1)
self.verticalLayout_6.addLayout(self.horizontalLayout_5)
self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
@@ -202,8 +225,8 @@ class Ui_Settings(object):
self.label_14.setFont(font)
self.label_14.setObjectName("label_14")
self.horizontalLayout_7.addWidget(self.label_14)
- spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout_7.addItem(spacerItem1)
+ spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_7.addItem(spacerItem2)
self.verticalLayout_6.addLayout(self.horizontalLayout_7)
self.chkSaveOnQuit = QtWidgets.QCheckBox(self.groupBox)
font = QtGui.QFont()
@@ -223,8 +246,8 @@ class Ui_Settings(object):
self.chkSaveToZip.setObjectName("chkSaveToZip")
self.verticalLayout_6.addWidget(self.chkSaveToZip)
self.verticalLayout_7.addWidget(self.groupBox)
- spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_7.addItem(spacerItem2)
+ spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_7.addItem(spacerItem3)
self.stack.addWidget(self.stackedWidgetPage1)
self.page_3 = QtWidgets.QWidget()
self.page_3.setObjectName("page_3")
@@ -388,8 +411,8 @@ class Ui_Settings(object):
self.label_51.setObjectName("label_51")
self.gridLayout_2.addWidget(self.label_51, 6, 1, 1, 1)
self.verticalLayout.addWidget(self.chkRevisionRemove)
- spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem3)
+ spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem4)
self.label_revisionDeprecation = QtWidgets.QLabel(self.page_3)
self.label_revisionDeprecation.setWordWrap(True)
self.label_revisionDeprecation.setOpenExternalLinks(True)
@@ -524,6 +547,25 @@ class Ui_Settings(object):
self.sldTreeIconSize.setObjectName("sldTreeIconSize")
self.horizontalLayout_11.addWidget(self.sldTreeIconSize)
self.verticalLayout_17.addWidget(self.groupBox_16)
+ self.horizontalGroupBox = QtWidgets.QGroupBox(self.tab)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.horizontalGroupBox.setFont(font)
+ self.horizontalGroupBox.setObjectName("horizontalGroupBox")
+ self.horizontalLayout_13 = QtWidgets.QHBoxLayout(self.horizontalGroupBox)
+ self.horizontalLayout_13.setContentsMargins(9, 9, 9, 9)
+ self.horizontalLayout_13.setObjectName("horizontalLayout_13")
+ self.chkCountSpaces = QtWidgets.QCheckBox(self.horizontalGroupBox)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.chkCountSpaces.setFont(font)
+ self.chkCountSpaces.setObjectName("chkCountSpaces")
+ self.horizontalLayout_13.addWidget(self.chkCountSpaces)
+ spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_13.addItem(spacerItem5)
+ self.verticalLayout_17.addWidget(self.horizontalGroupBox)
self.horizontalLayout_9 = QtWidgets.QHBoxLayout()
self.horizontalLayout_9.setObjectName("horizontalLayout_9")
self.groupBox_8 = QtWidgets.QGroupBox(self.tab)
@@ -548,6 +590,13 @@ class Ui_Settings(object):
self.rdoTreeWC.setFont(font)
self.rdoTreeWC.setObjectName("rdoTreeWC")
self.verticalLayout_15.addWidget(self.rdoTreeWC)
+ self.rdoTreeCC = QtWidgets.QRadioButton(self.groupBox_8)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.rdoTreeCC.setFont(font)
+ self.rdoTreeCC.setObjectName("rdoTreeCC")
+ self.verticalLayout_15.addWidget(self.rdoTreeCC)
self.rdoTreeProgress = QtWidgets.QRadioButton(self.groupBox_8)
font = QtGui.QFont()
font.setBold(False)
@@ -586,6 +635,13 @@ class Ui_Settings(object):
self.rdoTreeTextWC.setFont(font)
self.rdoTreeTextWC.setObjectName("rdoTreeTextWC")
self.verticalLayout_16.addWidget(self.rdoTreeTextWC)
+ self.rdoTreeTextCC = QtWidgets.QRadioButton(self.groupBox_9)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.rdoTreeTextCC.setFont(font)
+ self.rdoTreeTextCC.setObjectName("rdoTreeTextCC")
+ self.verticalLayout_16.addWidget(self.rdoTreeTextCC)
self.rdoTreeTextProgress = QtWidgets.QRadioButton(self.groupBox_9)
font = QtGui.QFont()
font.setBold(False)
@@ -607,12 +663,17 @@ class Ui_Settings(object):
self.rdoTreeTextNothing.setFont(font)
self.rdoTreeTextNothing.setObjectName("rdoTreeTextNothing")
self.verticalLayout_16.addWidget(self.rdoTreeTextNothing)
- spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_16.addItem(spacerItem4)
+ spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_16.addItem(spacerItem6)
+ self.rdoTreeTextCC.raise_()
+ self.rdoTreeTextWC.raise_()
+ self.rdoTreeTextProgress.raise_()
+ self.rdoTreeTextSummary.raise_()
+ self.rdoTreeTextNothing.raise_()
self.horizontalLayout_9.addWidget(self.groupBox_9)
self.verticalLayout_17.addLayout(self.horizontalLayout_9)
- spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_17.addItem(spacerItem5)
+ spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_17.addItem(spacerItem7)
icon = QtGui.QIcon.fromTheme("view-list-tree")
self.tabViews.addTab(self.tab, icon, "")
self.tab_2 = QtWidgets.QWidget()
@@ -774,8 +835,8 @@ class Ui_Settings(object):
self.chkOutlineTitle.setObjectName("chkOutlineTitle")
self.gridLayout.addWidget(self.chkOutlineTitle, 3, 0, 1, 1)
self.verticalLayout_11.addWidget(self.groupBox_6)
- spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_11.addItem(spacerItem6)
+ spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_11.addItem(spacerItem8)
icon = QtGui.QIcon.fromTheme("view-outline")
self.tabViews.addTab(self.tab_2, icon, "")
self.tab_3 = QtWidgets.QWidget()
@@ -821,8 +882,8 @@ class Ui_Settings(object):
self.cmbCorkImage.setFont(font)
self.cmbCorkImage.setObjectName("cmbCorkImage")
self.verticalLayout_8.addWidget(self.cmbCorkImage)
- spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_8.addItem(spacerItem7)
+ spacerItem9 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_8.addItem(spacerItem9)
self.gridLayout_3.addWidget(self.groupBox_7, 1, 1, 1, 1)
self.groupBox_11 = QtWidgets.QGroupBox(self.tab_3)
font = QtGui.QFont()
@@ -1380,8 +1441,8 @@ class Ui_Settings(object):
self.btnLabelColor.setIconSize(QtCore.QSize(64, 64))
self.btnLabelColor.setObjectName("btnLabelColor")
self.verticalLayout_2.addWidget(self.btnLabelColor)
- spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_2.addItem(spacerItem8)
+ spacerItem10 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_2.addItem(spacerItem10)
self.horizontalLayout_2.addLayout(self.verticalLayout_2)
self.verticalLayout_3.addLayout(self.horizontalLayout_2)
self.horizontalLayout = QtWidgets.QHBoxLayout()
@@ -1398,8 +1459,8 @@ class Ui_Settings(object):
self.btnLabelRemove.setIcon(icon)
self.btnLabelRemove.setObjectName("btnLabelRemove")
self.horizontalLayout.addWidget(self.btnLabelRemove)
- spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout.addItem(spacerItem9)
+ spacerItem11 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem11)
self.verticalLayout_3.addLayout(self.horizontalLayout)
self.stack.addWidget(self.stackedWidgetPage3)
self.stackedWidgetPage4 = QtWidgets.QWidget()
@@ -1433,8 +1494,8 @@ class Ui_Settings(object):
self.btnStatusRemove.setIcon(icon)
self.btnStatusRemove.setObjectName("btnStatusRemove")
self.horizontalLayout_3.addWidget(self.btnStatusRemove)
- spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout_3.addItem(spacerItem10)
+ spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem12)
self.verticalLayout_4.addLayout(self.horizontalLayout_3)
self.stack.addWidget(self.stackedWidgetPage4)
self.page = QtWidgets.QWidget()
@@ -1482,8 +1543,8 @@ class Ui_Settings(object):
self.btnThemeRemove.setIcon(icon)
self.btnThemeRemove.setObjectName("btnThemeRemove")
self.horizontalLayout_6.addWidget(self.btnThemeRemove)
- spacerItem11 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.horizontalLayout_6.addItem(spacerItem11)
+ spacerItem13 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_6.addItem(spacerItem13)
self.verticalLayout_12.addLayout(self.horizontalLayout_6)
self.themeStack.addWidget(self.stackedWidgetPage1_3)
self.stackedWidgetPage2_3 = QtWidgets.QWidget()
@@ -1823,9 +1884,9 @@ class Ui_Settings(object):
self.horizontalLayout_8.addWidget(self.stack)
self.retranslateUi(Settings)
- self.stack.setCurrentIndex(2)
- self.tabViews.setCurrentIndex(3)
- self.themeStack.setCurrentIndex(1)
+ self.stack.setCurrentIndex(0)
+ self.tabViews.setCurrentIndex(0)
+ self.themeStack.setCurrentIndex(0)
self.themeEditStack.setCurrentIndex(3)
self.lstMenu.currentRowChanged['int'].connect(self.stack.setCurrentIndex)
self.chkRevisionsKeep.toggled['bool'].connect(self.chkRevisionRemove.setEnabled)
@@ -1851,10 +1912,12 @@ class Ui_Settings(object):
self.lstMenu.setSortingEnabled(__sortingEnabled)
self.lblTitleGeneral.setText(_translate("Settings", "General settings"))
self.groupBox_2.setTitle(_translate("Settings", "Application settings"))
+ self.label_2.setText(_translate("Settings", "Restarting Manuskript ensures all settings take effect."))
self.label_56.setText(_translate("Settings", "Style:"))
self.label_57.setText(_translate("Settings", "Language:"))
self.label_58.setText(_translate("Settings", "Font size:"))
- self.label_2.setText(_translate("Settings", "Restarting Manuskript ensures all settings take effect."))
+ self.chkProgressChars.setText(_translate("Settings", "Show progress in chars next\n"
+" to words"))
self.groupBox_10.setTitle(_translate("Settings", "Loading"))
self.chkAutoLoad.setText(_translate("Settings", "Automatically load last project on startup"))
self.groupBox.setTitle(_translate("Settings", "Saving"))
@@ -1899,14 +1962,18 @@ class Ui_Settings(object):
self.cmbTreeBackground.setItemText(4, _translate("Settings", "Compile"))
self.groupBox_16.setTitle(_translate("Settings", "Icon Size"))
self.lblTreeIconSize.setText(_translate("Settings", "TextLabel"))
+ self.horizontalGroupBox.setTitle(_translate("Settings", "Char/Word Counter"))
+ self.chkCountSpaces.setText(_translate("Settings", "Count spaces as chars"))
self.groupBox_8.setTitle(_translate("Settings", "Folders"))
self.rdoTreeItemCount.setText(_translate("Settings", "Show ite&m count"))
self.rdoTreeWC.setText(_translate("Settings", "Show &word count"))
+ self.rdoTreeCC.setText(_translate("Settings", "Show char c&ount"))
self.rdoTreeProgress.setText(_translate("Settings", "S&how progress"))
self.rdoTreeSummary.setText(_translate("Settings", "Show summar&y"))
self.rdoTreeNothing.setText(_translate("Settings", "&Nothing"))
self.groupBox_9.setTitle(_translate("Settings", "Text"))
self.rdoTreeTextWC.setText(_translate("Settings", "&Show word count"))
+ self.rdoTreeTextCC.setText(_translate("Settings", "Sho&w char count"))
self.rdoTreeTextProgress.setText(_translate("Settings", "Show p&rogress"))
self.rdoTreeTextSummary.setText(_translate("Settings", "Show summary"))
self.rdoTreeTextNothing.setText(_translate("Settings", "Nothing"))
diff --git a/manuskript/ui/settings_ui.ui b/manuskript/ui/settings_ui.ui
index 6563bcbb..6b1a658d 100644
--- a/manuskript/ui/settings_ui.ui
+++ b/manuskript/ui/settings_ui.ui
@@ -6,7 +6,7 @@
0
0
- 658
+ 681
598
@@ -54,7 +54,7 @@
-
- 2
+ 0
@@ -98,93 +98,139 @@
Application settings
-
- QFormLayout::FieldsStayAtSizeHint
-
-
-
-
-
-
- 50
- false
-
-
-
- Style:
-
-
-
- -
-
-
-
- 50
- false
-
-
-
-
- -
-
-
-
- 50
- false
-
-
-
- Language:
-
-
-
- -
-
-
-
- 50
- false
-
-
-
-
- -
-
-
-
- 50
- false
-
-
-
- Font size:
-
-
-
- -
-
-
-
- 50
- false
-
-
-
-
- -
-
-
-
- 50
- false
-
-
-
- Restarting Manuskript ensures all settings take effect.
-
-
- true
-
-
+
-
+
+
-
+
+
+
+ 50
+ false
+
+
+
+ Restarting Manuskript ensures all settings take effect.
+
+
+ true
+
+
+
+ -
+
+
+ QLayout::SetDefaultConstraint
+
+
-
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+
+ 50
+ false
+
+
+
+ Style:
+
+
+
+ -
+
+
+
+ 50
+ false
+
+
+
+
+ -
+
+
+
+ 50
+ false
+
+
+
+ Language:
+
+
+
+ -
+
+
+
+ 50
+ false
+
+
+
+
+ -
+
+
+
+ 50
+ false
+
+
+
+ Font size:
+
+
+
+ -
+
+
+
+ 50
+ false
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 50
+ false
+
+
+
+ Show progress in chars next
+ to words
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
@@ -817,7 +863,7 @@
-
- 3
+ 0
@@ -1055,6 +1101,59 @@
+ -
+
+
+
+ 75
+ true
+
+
+
+ Char/Word Counter
+
+
+
+ 9
+
+
+ 9
+
+
+ 9
+
+
+ 9
+
+
-
+
+
+
+ 50
+ false
+
+
+
+ Count spaces as chars
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
-
-
@@ -1095,6 +1194,19 @@
+ -
+
+
+
+ 50
+ false
+
+
+
+ Show char c&ount
+
+
+
-
@@ -1165,6 +1277,19 @@
+ -
+
+
+
+ 50
+ false
+
+
+
+ Sho&w char count
+
+
+
-
@@ -1224,6 +1349,11 @@
+ rdoTreeTextCC
+ rdoTreeTextWC
+ rdoTreeTextProgress
+ rdoTreeTextSummary
+ rdoTreeTextNothing
@@ -2974,7 +3104,7 @@
-
- 1
+ 0
diff --git a/manuskript/ui/views/MDEditCompleter.py b/manuskript/ui/views/MDEditCompleter.py
index e0db6808..0101238d 100644
--- a/manuskript/ui/views/MDEditCompleter.py
+++ b/manuskript/ui/views/MDEditCompleter.py
@@ -106,13 +106,18 @@ class MDEditCompleter(MDEditView):
self.completer.popup(self.textUnderCursor(select=True))
def mouseMoveEvent(self, event):
+ """
+ When mouse moves, we show tooltip when appropriate.
+ """
+ self.beginTooltipMoveEvent()
MDEditView.mouseMoveEvent(self, event)
+ self.endTooltipMoveEvent()
onRef = [r for r in self.refRects if r.contains(event.pos())]
if not onRef:
qApp.restoreOverrideCursor()
- QToolTip.hideText()
+ self.hideTooltip()
return
cursor = self.cursorForPosition(event.pos())
@@ -120,7 +125,8 @@ class MDEditCompleter(MDEditView):
if ref:
if not qApp.overrideCursor():
qApp.setOverrideCursor(Qt.PointingHandCursor)
- QToolTip.showText(self.mapToGlobal(event.pos()), Ref.tooltip(ref))
+
+ self.showTooltip(self.mapToGlobal(event.pos()), Ref.tooltip(ref))
def mouseReleaseEvent(self, event):
MDEditView.mouseReleaseEvent(self, event)
diff --git a/manuskript/ui/views/MDEditView.py b/manuskript/ui/views/MDEditView.py
index e8eb5644..c5f4c338 100644
--- a/manuskript/ui/views/MDEditView.py
+++ b/manuskript/ui/views/MDEditView.py
@@ -506,13 +506,15 @@ class MDEditView(textEditView):
"""
When mouse moves, we show tooltip when appropriate.
"""
+ self.beginTooltipMoveEvent()
textEditView.mouseMoveEvent(self, event)
+ self.endTooltipMoveEvent()
onRect = [r for r in self.clickRects if r.rect.contains(event.pos())]
if not onRect:
qApp.restoreOverrideCursor()
- QToolTip.hideText()
+ self.hideTooltip()
return
ct = onRect[0]
@@ -534,7 +536,7 @@ class MDEditView(textEditView):
if tooltip:
tooltip = self.tr("{} (CTRL+Click to open)").format(tooltip)
- QToolTip.showText(self.mapToGlobal(event.pos()), tooltip)
+ self.showTooltip(self.mapToGlobal(event.pos()), tooltip)
def mouseReleaseEvent(self, event):
textEditView.mouseReleaseEvent(self, event)
diff --git a/manuskript/ui/views/textEditView.py b/manuskript/ui/views/textEditView.py
index 41324eb5..ea58ed81 100644
--- a/manuskript/ui/views/textEditView.py
+++ b/manuskript/ui/views/textEditView.py
@@ -1,11 +1,11 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
-import re
+import re, textwrap
from PyQt5.Qt import QApplication
from PyQt5.QtCore import QTimer, QModelIndex, Qt, QEvent, pyqtSignal, QRegExp, QLocale, QPersistentModelIndex, QMutex
from PyQt5.QtGui import QTextBlockFormat, QTextCharFormat, QFont, QColor, QIcon, QMouseEvent, QTextCursor
-from PyQt5.QtWidgets import QWidget, QTextEdit, qApp, QAction, QMenu
+from PyQt5.QtWidgets import QWidget, QTextEdit, qApp, QAction, QMenu, QToolTip
from manuskript import settings
from manuskript.enums import Outline, World, Character, Plot
@@ -47,6 +47,8 @@ class textEditView(QTextEdit):
self.highlightWord = ""
self.highligtCS = False
self._dict = None
+ self._tooltip = { 'depth' : 0, 'active' : 0 }
+
# self.document().contentsChanged.connect(self.submit, F.AUC)
# Submit text changed only after 500ms without modifications
@@ -393,6 +395,49 @@ class textEditView(QTextEdit):
Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
QTextEdit.mousePressEvent(self, event)
+ def beginTooltipMoveEvent(self):
+ self._tooltip['depth'] += 1
+
+ def endTooltipMoveEvent(self):
+ self._tooltip['depth'] -= 1
+
+ def showTooltip(self, pos, text):
+ QToolTip.showText(pos, text)
+ self._tooltip['active'] = self._tooltip['depth']
+
+ def hideTooltip(self):
+ if self._tooltip['active'] == self._tooltip['depth']:
+ QToolTip.hideText()
+
+ def mouseMoveEvent(self, event):
+ """
+ When mouse moves, we show tooltip when appropriate.
+ """
+ self.beginTooltipMoveEvent()
+ QTextEdit.mouseMoveEvent(self, event)
+ self.endTooltipMoveEvent()
+
+ match = None
+
+ # Check if the selected word has any suggestions for correction
+ if self.spellcheck and self._dict:
+ cursor = self.cursorForPosition(event.pos())
+
+ # Searches for correlating/overlapping matches
+ suggestions = self._dict.findSuggestions(self.toPlainText(), cursor.selectionStart(), cursor.selectionEnd())
+
+ if len(suggestions) > 0:
+ # I think it should focus on one type of error at a time.
+ match = suggestions[0]
+
+ if match:
+ # Wrap the message into a fitting width
+ msg_lines = textwrap.wrap(match.msg, 48)
+
+ self.showTooltip(event.globalPos(), "\n".join(msg_lines))
+ else:
+ self.hideTooltip()
+
def wheelEvent(self, event):
"""
We catch wheelEvent if key modifier is CTRL to change font size.
@@ -440,42 +485,108 @@ class textEditView(QTextEdit):
if not self.spellcheck:
return popup_menu
- # Select the word under the cursor.
- # But only if there is no selection (otherwise it's impossible to select more text to copy/cut)
cursor = self.textCursor()
- if not cursor.hasSelection():
- cursor.select(QTextCursor.WordUnderCursor)
- self.setTextCursor(cursor)
+ suggestions = []
+ selectedWord = None
+
+ # Check for any suggestions for corrections at the cursors position
+ if self._dict:
+ text = self.toPlainText()
+
+ suggestions = self._dict.findSuggestions(text, cursor.selectionStart(), cursor.selectionEnd())
+
+ # Select the word under the cursor if necessary.
+ # But only if there is no selection (otherwise it's impossible to select more text to copy/cut)
+ if (not cursor.hasSelection() and len(suggestions) == 0):
+ cursor.select(QTextCursor.WordUnderCursor)
+ self.setTextCursor(cursor)
+
+ if cursor.hasSelection():
+ selectedWord = cursor.selectedText()
+
+ # Check if the selected word is misspelled and offer spelling
+ # suggestions if it is.
+ suggestions = self._dict.findSuggestions(text, cursor.selectionStart(), cursor.selectionEnd())
+
+ if (len(suggestions) > 0 or selectedWord):
+ valid = len(suggestions) == 0
- # Check if the selected word is misspelled and offer spelling
- # suggestions if it is.
- if self._dict and cursor.hasSelection():
- text = str(cursor.selectedText())
- valid = not self._dict.isMisspelled(text)
- selectedWord = cursor.selectedText()
if not valid:
- spell_menu = QMenu(self.tr('Spelling Suggestions'), self)
- spell_menu.setIcon(F.themeIcon("spelling"))
- for word in self._dict.getSuggestions(text):
- action = self.SpellAction(word, spell_menu)
- action.correct.connect(self.correctWord)
- spell_menu.addAction(action)
+ # I think it should focus on one type of error at a time.
+ match = suggestions[0]
+
popup_menu.insertSeparator(popup_menu.actions()[0])
- # Adds: add to dictionary
- addAction = QAction(self.tr("&Add to dictionary"), popup_menu)
- addAction.setIcon(QIcon.fromTheme("list-add"))
- addAction.triggered.connect(self.addWordToDict)
- addAction.setData(selectedWord)
- popup_menu.insertAction(popup_menu.actions()[0], addAction)
- # Only add the spelling suggests to the menu if there are
- # suggestions.
- if len(spell_menu.actions()) != 0:
- # Adds: suggestions
- popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
- # popup_menu.insertSeparator(popup_menu.actions()[0])
+
+ if match.locqualityissuetype == 'misspelling':
+ spell_menu = QMenu(self.tr('Spelling Suggestions'), self)
+ spell_menu.setIcon(F.themeIcon("spelling"))
+
+ if (match.end > match.start and not selectedWord):
+ # Select the actual area of the match
+ cursor = self.textCursor()
+ cursor.setPosition(match.start, QTextCursor.MoveAnchor);
+ cursor.setPosition(match.end, QTextCursor.KeepAnchor);
+ self.setTextCursor(cursor)
+
+ selectedWord = cursor.selectedText()
+
+ for word in match.replacements:
+ action = self.SpellAction(word, spell_menu)
+ action.correct.connect(self.correctWord)
+ spell_menu.addAction(action)
+
+ # Adds: add to dictionary
+ addAction = QAction(self.tr("&Add to dictionary"), popup_menu)
+ addAction.setIcon(QIcon.fromTheme("list-add"))
+ addAction.triggered.connect(self.addWordToDict)
+ addAction.setData(selectedWord)
+
+ popup_menu.insertAction(popup_menu.actions()[0], addAction)
+
+ # Only add the spelling suggests to the menu if there are
+ # suggestions.
+ if len(match.replacements) > 0:
+ # Adds: suggestions
+ popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
+ else:
+ correct_menu = None
+ correct_action = None
+
+ if (len(match.replacements) > 0 and match.end > match.start):
+ # Select the actual area of the match
+ cursor = self.textCursor()
+ cursor.setPosition(match.start, QTextCursor.MoveAnchor);
+ cursor.setPosition(match.end, QTextCursor.KeepAnchor);
+ self.setTextCursor(cursor)
+
+ if len(match.replacements) > 0:
+ correct_menu = QMenu(self.tr('&Correction Suggestions'), self)
+ correct_menu.setIcon(F.themeIcon("spelling"))
+
+ for word in match.replacements:
+ action = self.SpellAction(word, correct_menu)
+ action.correct.connect(self.correctWord)
+ correct_menu.addAction(action)
+
+ if correct_menu == None:
+ correct_action = QAction(self.tr('&Correction Suggestion'), popup_menu)
+ correct_action.setIcon(F.themeIcon("spelling"))
+ correct_action.setEnabled(False)
+
+ # Wrap the message into a fitting width
+ msg_lines = textwrap.wrap(match.msg, 48)
+
+ # Insert the lines of the message backwards
+ for i in range(0, len(msg_lines)):
+ popup_menu.insertSection(popup_menu.actions()[0], msg_lines[len(msg_lines) - (i + 1)])
+
+ if correct_menu != None:
+ popup_menu.insertMenu(popup_menu.actions()[0], correct_menu)
+ else:
+ popup_menu.insertAction(popup_menu.actions()[0], correct_action)
# If word was added to custom dict, give the possibility to remove it
- elif valid and self._dict.isCustomWord(selectedWord):
+ elif self._dict.isCustomWord(selectedWord):
popup_menu.insertSeparator(popup_menu.actions()[0])
# Adds: remove from dictionary
rmAction = QAction(self.tr("&Remove from custom dictionary"), popup_menu)
diff --git a/manuskript/ui/views/treeDelegates.py b/manuskript/ui/views/treeDelegates.py
index fa59702c..f95a3c79 100644
--- a/manuskript/ui/views/treeDelegates.py
+++ b/manuskript/ui/views/treeDelegates.py
@@ -111,6 +111,9 @@ class treeTitleDelegate(QStyledItemDelegate):
elif settings.viewSettings["Tree"]["InfoFolder"] == "WC":
extraText = item.wordCount()
extraText = " ({})".format(extraText)
+ elif settings.viewSettings["Tree"]["InfoFolder"] == "CC":
+ extraText = item.charCount()
+ extraText = " ({})".format(extraText)
elif settings.viewSettings["Tree"]["InfoFolder"] == "Progress":
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
if extraText:
@@ -124,6 +127,9 @@ class treeTitleDelegate(QStyledItemDelegate):
if settings.viewSettings["Tree"]["InfoText"] == "WC":
extraText = item.wordCount()
extraText = " ({})".format(extraText)
+ elif settings.viewSettings["Tree"]["InfoText"] == "CC":
+ extraText = item.charCount()
+ extraText = " ({})".format(extraText)
elif settings.viewSettings["Tree"]["InfoText"] == "Progress":
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
if extraText: