Merge branch 'develop' into optional-pov

This commit is contained in:
Tobias Frisch 2021-02-19 16:02:45 +01:00 committed by GitHub
commit e8c61e74ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 906 additions and 283 deletions

View file

@ -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

View file

@ -5,7 +5,7 @@
<message>
<location filename="../manuskript/exporter/manuskript/__init__.py" line="15"/>
<source>Default exporter, provides basic formats used by other exporters.</source>
<translation>Exporteur de base, fournit les formats de base utilisés par les autres exporteurs.</translation>
<translation>Exportateur de base, fournit les formats de base utilisés par les autres exportateurs.</translation>
</message>
<message>
<location filename="../manuskript/exporter/manuskript/HTML.py" line="18"/>
@ -28,7 +28,7 @@
formats.&lt;/p&gt;
&lt;p&gt;Website: &lt;a href=&quot;http://www.pandoc.org&quot;&gt;http://pandoc.org/&lt;/a&gt;&lt;/p&gt;
</source>
<translation>&lt;p&gt;Un convertisseur universel de document. Peut être utilisé pour convertir du Markdown dans un grand nombre de formats.&lt;/p&gt;
<translation>&lt;p&gt;Un convertisseur universel de documents. Peut être utilisé pour convertir du Markdown dans un grand nombre de formats.&lt;/p&gt;
&lt;p&gt;Site internet : &lt;a href=&quot;http://www.pandoc.org&quot;&gt;http://pandoc.org/&lt;/a&gt;&lt;/p&gt;
</translation>
</message>
@ -36,7 +36,7 @@
<location filename="../manuskript/exporter/manuskript/markdown.py" line="14"/>
<source>Just like plain text, excepts adds markdown titles.
Presupposes that texts are formatted in markdown.</source>
<translation>Comme en texte brute, mais rajoute les titres markdown.</translation>
<translation>Comme en texte brut, mais ajoute les titres markdown.</translation>
</message>
<message>
<location filename="../manuskript/exporter/manuskript/markdown.py" line="60"/>
@ -46,7 +46,7 @@
<message>
<location filename="../manuskript/exporter/manuskript/plainText.py" line="14"/>
<source>Plain text</source>
<translation>Texte brute</translation>
<translation>Texte brut</translation>
</message>
<message>
<location filename="../manuskript/exporter/manuskript/plainText.py" line="15"/>
@ -99,7 +99,7 @@ Par exemple &lt;a href=&apos;www.fountain.io&apos;&gt;Fountain&lt;/a&gt;.</trans
<location filename="../manuskript/exporter/pandoc/plainText.py" line="10"/>
<source>Export to markdown, using pandoc. Allows more formatting options
than the basic manuskript exporter.</source>
<translation>Export vers markdown en utilisant pandoc. Permet plus de possibilités que l&apos;exporteur basique intégré à manuskript.</translation>
<translation>Export vers markdown en utilisant pandoc. Permet plus de possibilités que l&apos;exportateur basique intégré à manuskript.</translation>
</message>
<message>
<location filename="../manuskript/exporter/pandoc/plainText.py" line="22"/>
@ -110,7 +110,7 @@ Par exemple &lt;a href=&apos;www.fountain.io&apos;&gt;Fountain&lt;/a&gt;.</trans
<location filename="../manuskript/exporter/pandoc/plainText.py" line="33"/>
<source>LaTeX is a word processor and document markup language used to create
beautiful documents.</source>
<translation>LaTeX est une suite d&apos;outil et un format utilisé pour créer des documents magnifiques.</translation>
<translation>LaTeX est une suite d&apos;outils et un format utilisé pour créer des documents magnifiques.</translation>
</message>
<message>
<location filename="../manuskript/exporter/pandoc/abstractPlainText.py" line="97"/>
@ -125,7 +125,7 @@ Par exemple &lt;a href=&apos;www.fountain.io&apos;&gt;Fountain&lt;/a&gt;.</trans
<message>
<location filename="../manuskript/exporter/pandoc/abstractPlainText.py" line="103"/>
<source>Number of sections level to include in TOC: </source>
<translation>Nombre de niveau à inclure dans la table des matières: </translation>
<translation>Nombre de niveaux à inclure dans la table des matières : </translation>
</message>
<message>
<location filename="../manuskript/exporter/pandoc/abstractPlainText.py" line="107"/>
@ -140,7 +140,7 @@ Par exemple &lt;a href=&apos;www.fountain.io&apos;&gt;Fountain&lt;/a&gt;.</trans
<message>
<location filename="../manuskript/exporter/pandoc/abstractPlainText.py" line="112"/>
<source>Specify the base level for headers: </source>
<translation>Spécifier le niveau de base pour les titres: </translation>
<translation>Spécifier le niveau de base pour les titres : </translation>
</message>
<message>
<location filename="../manuskript/exporter/pandoc/abstractPlainText.py" line="119"/>
@ -191,7 +191,7 @@ Par exemple &lt;a href=&apos;www.fountain.io&apos;&gt;Fountain&lt;/a&gt;.</trans
<location filename="../manuskript/exporter/pandoc/PDF.py" line="19"/>
<source>a valid LaTeX installation. Pandoc recommendations can be found on:
&lt;a href=&quot;https://pandoc.org/installing.html&quot;&gt;pandoc.org/installing.html&lt;/a&gt;. If you want Unicode support, you need XeLaTeX.</source>
<translation type="unfinished">nécessite une installation latex valide. Voir les recommendations de pandoc sur &lt;a href=&quot;http://pandoc.org/installing.html&quot;&gt;http://pandoc.org/installing.html&lt;/a&gt;. Pour le support unicode, il vous faut xelatex.</translation>
<translation type="unfinished">nécessite une installation LaTeX valide. Voir les recommandations de pandoc sur &lt;a href=&quot;http://pandoc.org/installing.html&quot;&gt;http://pandoc.org/installing.html&lt;/a&gt;. Pour le support unicode, il vous faut XeLaTeX.</translation>
</message>
<message>
<location filename="../manuskript/exporter/pandoc/plainText.py" line="45"/>
@ -222,7 +222,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/exporter/manuskript/plainText.py" line="70"/>
<source>Choose output file&#xe2;&#x80;&#xa6;</source>
<translation type="unfinished">Choisir le fichier de sortie...</translation>
<translation type="unfinished">Choisir le fichier de sortie</translation>
</message>
</context>
<context>
@ -230,7 +230,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="14"/>
<source>Manage Exporters</source>
<translation>Gérer les Exporteurs</translation>
<translation>Gérer les Exportateurs</translation>
</message>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="21"/>
@ -250,27 +250,27 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="184"/>
<source>Status</source>
<translation>Status</translation>
<translation>Statut</translation>
</message>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="190"/>
<source>Status:</source>
<translation>Status :</translation>
<translation>Statut :</translation>
</message>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="210"/>
<source>Version:</source>
<translation>Version:</translation>
<translation>Version :</translation>
</message>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="233"/>
<source>Path:</source>
<translation>Chemin:</translation>
<translation>Chemin :</translation>
</message>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="258"/>
<source>...</source>
<translation>...</translation>
<translation></translation>
</message>
<message>
<location filename="../manuskript/ui/exporters/exportersManager_ui.ui" line="273"/>
@ -298,7 +298,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/tools/frequency_ui.ui" line="49"/>
<source>Minimum size:</source>
<translation>Taille minimum:</translation>
<translation>Taille minimum :</translation>
</message>
<message>
<location filename="../manuskript/ui/tools/frequency_ui.ui" line="63"/>
@ -318,7 +318,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/tools/frequency_ui.ui" line="115"/>
<source>Number of words: from</source>
<translation>Nombre de mots: de</translation>
<translation>Nombre de mots : de</translation>
</message>
<message>
<location filename="../manuskript/ui/tools/frequency_ui.ui" line="129"/>
@ -355,7 +355,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
are added as scene.&lt;/p&gt;
&lt;p&gt;Only text files are supported (not images, binary or others).&lt;/p&gt;</source>
<translation>&lt;p&gt;&lt;b&gt;Info:&lt;/b&gt; Importe la structure d&apos;un dossier. Les dossiers sont crées comme dossiers, et les documents textes sont ajoutés comme des scènes.&lt;/p&gt;
&lt;p&gt;Seulement les fichiers textes sont supportés (pas les images, fichiers binaires ou autres).&lt;/p&gt;</translation>
&lt;p&gt;Seuls les fichiers textes sont pris en charge (pas les images, fichiers binaires ou autres).&lt;/p&gt;</translation>
</message>
<message>
<location filename="../manuskript/importer/folderImporter.py" line="108"/>
@ -375,7 +375,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/importer/folderImporter.py" line="117"/>
<source>Import folder then files</source>
<translation>Importer les dossiers en premier</translation>
<translation>Importer les dossiers puis les fichiers/translation>
</message>
<message>
<location filename="../manuskript/importer/opmlImporter.py" line="65"/>
@ -385,7 +385,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/importer/opmlImporter.py" line="37"/>
<source>File open failed.</source>
<translation>Echec de l&apos;ouverture du fichier</translation>
<translation>Échec de l&apos;ouverture du fichier</translation>
</message>
<message>
<location filename="../manuskript/importer/opmlImporter.py" line="65"/>
@ -410,12 +410,12 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/importer/pandocImporters.py" line="69"/>
<source>Import using:</source>
<translation>Importer via:</translation>
<translation>Importer via :</translation>
</message>
<message>
<location filename="../manuskript/importer/pandocImporters.py" line="73"/>
<source>Wrap lines:</source>
<translation>Replier les lignes:</translation>
<translation>Replier les lignes :</translation>
</message>
<message>
<location filename="../manuskript/importer/pandocImporters.py" line="73"/>
@ -425,8 +425,8 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
&lt;b&gt;none&lt;/b&gt;: no line wrap.&lt;br&gt;
&lt;b&gt;preserve&lt;/b&gt;: tries to preserves line wrap from the
original document.&lt;/p&gt;</source>
<translation>&lt;p&gt;Pandoc doit-il créer des retours à la lignes cosmétiques/non-sémantiques?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;auto&lt;/b&gt;: plier les lignes à 72 charactères.&lt;br&gt;
<translation>&lt;p&gt;Pandoc doit-il créer des retours à la ligne cosmétiques/non-sémantiques?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;auto&lt;/b&gt;: retour à la ligne après 72 caractères.&lt;br&gt;
&lt;b&gt;none&lt;/b&gt;: pas de retours à la lignes.&lt;br&gt;
&lt;b&gt;preserve&lt;/b&gt;: essaie de préserver les retours à la lignes du document original.&lt;/p&gt;</translation>
</message>
@ -448,7 +448,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/importer/mindMapImporter.py" line="74"/>
<source>Import tip as:</source>
<translation>Importer les pointes comme:</translation>
<translation>Importer les astuces comme :</translation>
</message>
<message>
<location filename="../manuskript/importer/mindMapImporter.py" line="90"/>
@ -494,7 +494,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="236"/>
<source>License</source>
<translation>License</translation>
<translation>Licence</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="249"/>
@ -729,17 +729,17 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="322"/>
<source>Situation:</source>
<translation>Situation:</translation>
<translation>Situation :</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="1337"/>
<source>Summary:</source>
<translation>Résumé:</translation>
<translation>Résumé :</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="676"/>
<source>What if...?</source>
<translation>Et si... ?</translation>
<translation>Et si ?</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1476"/>
@ -839,7 +839,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="1215"/>
<source>Project tree</source>
<translation>Arborescence</translation>
<translation>Arborescence du projet</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1216"/>
@ -854,7 +854,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="1295"/>
<source>Create your characters.</source>
<translation>Créez ici vos personnage.</translation>
<translation>Créez ici vos personnages.</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1298"/>
@ -874,7 +874,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="1310"/>
<source>Debug info. Sometimes useful.</source>
<translation>Des informations pour débugger des fois pendant qu&apos;on code c&apos;est utile.</translation>
<translation>Des informations pour débugger pendant qu&apos;on code c&apos;est parfois utile.</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1324"/>
@ -889,7 +889,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="1468"/>
<source>POV</source>
<translation>POV</translation>
<translation>Pt de vue</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1469"/>
@ -919,7 +919,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="1487"/>
<source>Icon</source>
<translation>Icone</translation>
<translation>Icône</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1488"/>
@ -1004,7 +1004,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="2389"/>
<source>&amp;Show help texts</source>
<translation>&amp;Afficher les bulles d&apos;aides</translation>
<translation>&amp;Afficher les bulles d&apos;aide</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="2407"/>
@ -1019,7 +1019,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="2428"/>
<source>&amp;Status...</source>
<translation>&amp;Status</translation>
<translation>&amp;Statut</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="2444"/>
@ -1054,7 +1054,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="1217"/>
<source>Story line</source>
<translation></translation>
<translation><Fil narratif/translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1285"/>
@ -1081,7 +1081,7 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="2509"/>
<source>&amp;About</source>
<translation>&amp;A propos</translation>
<translation>&amp;À propos</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="2512"/>
@ -1096,12 +1096,12 @@ Cochez ceci si vous avez des erreurs liées à YAML.</translation>
<message>
<location filename="../manuskript/mainWindow.py" line="843"/>
<source>WARNING: Project {} not saved.</source>
<translation>ATTENTION: Le projet {} n&apos;a pas é enregistré.</translation>
<translation>ATTENTION : Le projet {} n&apos;a pas é enregistré.</translation>
</message>
<message>
<location filename="../manuskript/mainWindow.py" line="1301"/>
<source>Build worlds. Create hierarchy of broad categories down to specific details.</source>
<translation>Construire des mondes. Crée une hierarchie en partant des catégories les plus larges jusqu&apos;au détails les plus spécifiques.</translation>
<translation>Construire des mondes. Crée une hiérarchie en partant des catégories les plus larges jusqu&apos;aux détails les plus spécifiques.</translation>
</message>
<message>
<location filename="../manuskript/ui/mainWindow.ui" line="1351"/>

View file

@ -61,6 +61,7 @@ class Outline(IntEnum):
textFormat = 15
revisions = 16
customIcon = 17
charCount = 18
class Abstract(IntEnum):
title = 0

View file

@ -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):
"""

View file

@ -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)

View file

@ -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]

View file

@ -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"]

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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"))

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>658</width>
<width>681</width>
<height>598</height>
</rect>
</property>
@ -54,7 +54,7 @@
<item>
<widget class="QStackedWidget" name="stack">
<property name="currentIndex">
<number>2</number>
<number>0</number>
</property>
<widget class="QWidget" name="stackedWidgetPage1">
<layout class="QVBoxLayout" name="verticalLayout_7">
@ -98,93 +98,139 @@
<string>Application settings</string>
</property>
<layout class="QFormLayout" name="formLayout_13">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<item row="3" column="0">
<widget class="QLabel" name="label_56">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Style:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cmbStyle">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_57">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Language:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="cmbTranslation">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_58">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Font size:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="spnGeneralFontSize">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Restarting Manuskript ensures all settings take effect.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<item row="0" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Restarting Manuskript ensures all settings take effect.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="formLayout_14">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_56">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Style:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cmbStyle">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_57">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Language:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cmbTranslation">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_58">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Font size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spnGeneralFontSize">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_15">
<item row="0" column="1">
<widget class="QCheckBox" name="chkProgressChars">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Show progress in chars next
to words</string>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
@ -817,7 +863,7 @@
<item>
<widget class="QTabWidget" name="tabViews">
<property name="currentIndex">
<number>3</number>
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="icon">
@ -1055,6 +1101,59 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="horizontalGroupBox">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="title">
<string>Char/Word Counter</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QCheckBox" name="chkCountSpaces">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Count spaces as chars</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
@ -1095,6 +1194,19 @@
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rdoTreeCC">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Show char c&amp;ount</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rdoTreeProgress">
<property name="font">
@ -1165,6 +1277,19 @@
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rdoTreeTextCC">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Sho&amp;w char count</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rdoTreeTextProgress">
<property name="font">
@ -1224,6 +1349,11 @@
</spacer>
</item>
</layout>
<zorder>rdoTreeTextCC</zorder>
<zorder>rdoTreeTextWC</zorder>
<zorder>rdoTreeTextProgress</zorder>
<zorder>rdoTreeTextSummary</zorder>
<zorder>rdoTreeTextNothing</zorder>
</widget>
</item>
</layout>
@ -2974,7 +3104,7 @@
<item>
<widget class="QStackedWidget" name="themeStack">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="stackedWidgetPage1_3">
<layout class="QVBoxLayout" name="verticalLayout_12">

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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: