mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-10 07:52:35 +12:00
Merge branch 'develop' into develop
This commit is contained in:
commit
7c93567279
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,3 +20,4 @@ icons/Numix
|
|||
manuskript/pycallgraph.txt
|
||||
snowflake*
|
||||
test-projects
|
||||
main.pyproject.user
|
||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
3
main.pyproject
Normal file
3
main.pyproject
Normal file
File diff suppressed because one or more lines are too long
|
@ -26,7 +26,7 @@ class markdownConverter(abstractConverter):
|
|||
|
||||
@classmethod
|
||||
def isValid(self):
|
||||
return MD is not None
|
||||
return MD != None
|
||||
|
||||
@classmethod
|
||||
def convert(self, markdown):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
#--!-- coding: utf8 --!--
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from enum import IntEnum
|
||||
|
@ -16,6 +16,8 @@ class Character(IntEnum):
|
|||
summaryPara = 8
|
||||
summaryFull = 9
|
||||
notes = 10
|
||||
pov = 11
|
||||
infos = 12
|
||||
|
||||
class Plot(IntEnum):
|
||||
name = 0
|
||||
|
@ -60,8 +62,24 @@ class Outline(IntEnum):
|
|||
textFormat = 15
|
||||
revisions = 16
|
||||
customIcon = 17
|
||||
charCount = 18
|
||||
|
||||
class Abstract(IntEnum):
|
||||
title = 0
|
||||
ID = 1
|
||||
type = 2
|
||||
|
||||
class FlatData(IntEnum):
|
||||
summarySituation = 0,
|
||||
summarySentence = 1,
|
||||
summaryPara = 2,
|
||||
summaryPage = 3,
|
||||
summaryFull = 4
|
||||
|
||||
class Model(IntEnum):
|
||||
Character = 0
|
||||
Plot = 1
|
||||
PlotStep = 2
|
||||
World = 3
|
||||
Outline = 4
|
||||
FlatData = 5
|
|
@ -24,7 +24,7 @@ class HTML(markdown):
|
|||
exportDefaultSuffix = ".html"
|
||||
|
||||
def isValid(self):
|
||||
return MD is not None
|
||||
return MD != None
|
||||
|
||||
def settingsWidget(self):
|
||||
w = markdownSettings(self)
|
||||
|
|
|
@ -51,10 +51,10 @@ class plainText(basicFormat):
|
|||
|
||||
def getExportFilename(self, settingsWidget, varName=None, filter=None):
|
||||
|
||||
if varName is None:
|
||||
if varName == None:
|
||||
varName = self.exportVarName
|
||||
|
||||
if filter is None:
|
||||
if filter == None:
|
||||
filter = self.exportFilter
|
||||
|
||||
settings = settingsWidget.getSettings()
|
||||
|
|
|
@ -31,7 +31,7 @@ class PDF(abstractOutput):
|
|||
|
||||
def isValid(self):
|
||||
path = shutil.which("pdflatex") or shutil.which("xelatex")
|
||||
return path is not None
|
||||
return path != None
|
||||
|
||||
def output(self, settingsWidget, outputfile=None):
|
||||
args = settingsWidget.runnableSettings()
|
||||
|
|
|
@ -75,7 +75,7 @@ class pandocSetting:
|
|||
"""Return whether the specific setting is active with the given format."""
|
||||
|
||||
# Empty formats means all
|
||||
if self.formats is "":
|
||||
if self.formats == "":
|
||||
return True
|
||||
|
||||
# "html" in "html markdown latex"
|
||||
|
|
|
@ -9,7 +9,7 @@ from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
|
|||
from PyQt5.QtCore import QUrl, QTimer
|
||||
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtWidgets import qApp, QFileDialog, QTextEdit
|
||||
from PyQt5.QtWidgets import qApp, QFileDialog
|
||||
|
||||
from manuskript.enums import Outline
|
||||
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -442,5 +450,48 @@ def inspect():
|
|||
s.function))
|
||||
print(" " + "".join(s.code_context))
|
||||
|
||||
|
||||
def search(searchRegex, text):
|
||||
"""
|
||||
Search all occurrences of a regex in a text.
|
||||
|
||||
:param searchRegex: a regex object with the search to perform
|
||||
:param text: text to search on
|
||||
:return: list of tuples (startPos, endPos)
|
||||
"""
|
||||
if text is not None:
|
||||
return [(m.start(), m.end(), getSearchResultContext(text, m.start(), m.end())) for m in searchRegex.finditer(text)]
|
||||
else:
|
||||
return []
|
||||
|
||||
def getSearchResultContext(text, startPos, endPos):
|
||||
matchSize = endPos - startPos
|
||||
maxContextSize = max(matchSize, 600)
|
||||
extraContextSize = int((maxContextSize - matchSize) / 2)
|
||||
separator = "[...]"
|
||||
|
||||
context = ""
|
||||
|
||||
i = startPos - 1
|
||||
while i > 0 and (startPos - i) < extraContextSize and text[i] != '\n':
|
||||
i -= 1
|
||||
contextStartPos = i
|
||||
if i > 0:
|
||||
context += separator + " "
|
||||
context += text[contextStartPos:startPos].replace('\n', '')
|
||||
|
||||
context += '<b>' + text[startPos:endPos].replace('\n', '') + '</b>'
|
||||
|
||||
i = endPos
|
||||
while i < len(text) and (i - endPos) < extraContextSize and text[i] != '\n':
|
||||
i += 1
|
||||
contextEndPos = i
|
||||
|
||||
context += text[endPos:contextEndPos].replace('\n', '')
|
||||
if i < len(text):
|
||||
context += " " + separator
|
||||
|
||||
return context
|
||||
|
||||
# Spellchecker loads writablePath from this file, so we need to load it after they get defined
|
||||
from manuskript.functions.spellchecker import Spellchecker
|
||||
|
|
|
@ -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,17 @@ except ImportError:
|
|||
symspellpy = None
|
||||
|
||||
|
||||
use_language_check = False
|
||||
|
||||
try:
|
||||
try:
|
||||
import language_tool_python as languagetool
|
||||
except:
|
||||
import language_check as languagetool
|
||||
use_language_check = True
|
||||
except:
|
||||
languagetool = None
|
||||
|
||||
class Spellchecker:
|
||||
dictionaries = {}
|
||||
# In order of priority
|
||||
|
@ -106,7 +117,7 @@ class Spellchecker:
|
|||
(lib, name) = values
|
||||
try:
|
||||
d = Spellchecker.dictionaries.get(dictionary, None)
|
||||
if d is None:
|
||||
if d == None:
|
||||
for impl in Spellchecker.implementations:
|
||||
if impl.isInstalled() and lib == impl.getLibraryName():
|
||||
d = impl(name)
|
||||
|
@ -117,6 +128,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 +184,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
|
||||
|
||||
|
@ -218,7 +273,7 @@ class EnchantDictionary(BasicDictionary):
|
|||
|
||||
@staticmethod
|
||||
def isInstalled():
|
||||
return enchant is not None
|
||||
return enchant != None
|
||||
|
||||
@staticmethod
|
||||
def availableDictionaries():
|
||||
|
@ -235,9 +290,9 @@ class EnchantDictionary(BasicDictionary):
|
|||
if default_locale and not enchant.dict_exists(default_locale):
|
||||
default_locale = None
|
||||
|
||||
if default_locale is None:
|
||||
if default_locale == None:
|
||||
default_locale = QLocale.system().name()
|
||||
if default_locale is None:
|
||||
if default_locale == None:
|
||||
default_locale = self.availableDictionaries()[0]
|
||||
|
||||
return default_locale
|
||||
|
@ -278,7 +333,7 @@ class PySpellcheckerDictionary(BasicDictionary):
|
|||
|
||||
@staticmethod
|
||||
def isInstalled():
|
||||
return pyspellchecker is not None
|
||||
return pyspellchecker != None
|
||||
|
||||
@staticmethod
|
||||
def availableDictionaries():
|
||||
|
@ -298,7 +353,7 @@ class PySpellcheckerDictionary(BasicDictionary):
|
|||
default_locale = QLocale.system().name()
|
||||
if default_locale:
|
||||
default_locale = default_locale[0:2]
|
||||
if default_locale is None:
|
||||
if default_locale == None:
|
||||
default_locale = "en"
|
||||
|
||||
return default_locale
|
||||
|
@ -363,7 +418,7 @@ class SymSpellDictionary(BasicDictionary):
|
|||
|
||||
@staticmethod
|
||||
def isInstalled():
|
||||
return symspellpy is not None
|
||||
return symspellpy != None
|
||||
|
||||
@classmethod
|
||||
def availableDictionaries(cls):
|
||||
|
@ -422,8 +477,181 @@ class SymSpellDictionary(BasicDictionary):
|
|||
# Since 6.3.8
|
||||
self._dict.delete_dictionary_entry(word)
|
||||
|
||||
def get_languagetool_match_errorLength(match):
|
||||
if use_language_check:
|
||||
return match.errorlength
|
||||
else:
|
||||
return match.errorLength
|
||||
|
||||
def get_languagetool_match_ruleIssueType(match):
|
||||
if use_language_check:
|
||||
return match.locqualityissuetype
|
||||
else:
|
||||
return match.ruleIssueType
|
||||
|
||||
def get_languagetool_match_message(match):
|
||||
if use_language_check:
|
||||
return match.msg
|
||||
else:
|
||||
return match.message
|
||||
|
||||
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 + get_languagetool_match_errorLength(match)
|
||||
|
||||
basic_match = BasicMatch(start, end)
|
||||
basic_match.locqualityissuetype = get_languagetool_match_ruleIssueType(match)
|
||||
basic_match.replacements = match.replacements
|
||||
basic_match.msg = get_languagetool_match_message(match)
|
||||
|
||||
matches.append(basic_match)
|
||||
|
||||
return matches
|
||||
|
||||
def update(self, tool, text):
|
||||
if len(text) != self._length:
|
||||
self._matches = self._buildMatches(tool, text)
|
||||
|
||||
def get_languagetool_languages():
|
||||
if use_language_check:
|
||||
return languagetool.get_languages()
|
||||
else:
|
||||
return languagetool.LanguageTool()._get_languages()
|
||||
|
||||
class LanguageToolDictionary(BasicDictionary):
|
||||
|
||||
def __init__(self, name):
|
||||
BasicDictionary.__init__(self, name)
|
||||
|
||||
if not (self._lang and self._lang in get_languagetool_languages()):
|
||||
self._lang = self.getDefaultDictionary()
|
||||
|
||||
self._tool = languagetool.LanguageTool(self._lang)
|
||||
self._cache = {}
|
||||
|
||||
@staticmethod
|
||||
def getLibraryName():
|
||||
return "LanguageTool"
|
||||
|
||||
@staticmethod
|
||||
def getLibraryURL():
|
||||
if use_language_check:
|
||||
return "https://pypi.org/project/language-check/"
|
||||
else:
|
||||
return "https://pypi.org/project/language-tool-python/"
|
||||
|
||||
@staticmethod
|
||||
def isInstalled():
|
||||
if languagetool != 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(get_languagetool_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 get_languagetool_languages():
|
||||
default_locale = None
|
||||
|
||||
if default_locale == None:
|
||||
default_locale = QLocale.system().name()
|
||||
if default_locale == 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)
|
||||
|
|
|
@ -47,7 +47,7 @@ class mindMapImporter(abstractImporter):
|
|||
node = root.find("node")
|
||||
items = []
|
||||
|
||||
if node is not None:
|
||||
if node != None:
|
||||
items.extend(self.parseItems(node, parentItem))
|
||||
ret = True
|
||||
|
||||
|
@ -97,7 +97,7 @@ class mindMapImporter(abstractImporter):
|
|||
# Rich text content
|
||||
content = ""
|
||||
content = underElement.find("richcontent")
|
||||
if content is not None:
|
||||
if content != None:
|
||||
# In Freemind, can be note or node
|
||||
# Note: it's a note
|
||||
# Node: it's the title of the node, in rich text
|
||||
|
@ -130,7 +130,7 @@ class mindMapImporter(abstractImporter):
|
|||
children = underElement.findall('node')
|
||||
|
||||
# Process children
|
||||
if children is not None and len(children) > 0:
|
||||
if children != None and len(children) > 0:
|
||||
for c in children:
|
||||
items.extend(self.parseItems(c, item))
|
||||
|
||||
|
|
|
@ -52,10 +52,10 @@ class opmlImporter(abstractImporter):
|
|||
bodyNode = opmlNode.find("body")
|
||||
items = []
|
||||
|
||||
if bodyNode is not None:
|
||||
if bodyNode != None:
|
||||
outlineEls = bodyNode.findall("outline")
|
||||
|
||||
if outlineEls is not None:
|
||||
if outlineEls != None:
|
||||
for element in outlineEls:
|
||||
items.extend(cls.parseItems(element, parentItem))
|
||||
ret = True
|
||||
|
@ -74,19 +74,20 @@ class opmlImporter(abstractImporter):
|
|||
def parseItems(cls, underElement, parentItem=None):
|
||||
items = []
|
||||
title = underElement.get('text')
|
||||
if title is not None:
|
||||
|
||||
if title != None:
|
||||
card = outlineItem(parent=parentItem, title=title)
|
||||
items.append(card)
|
||||
|
||||
body = ""
|
||||
note = underElement.get('_note')
|
||||
if note is not None and not cls.isWhitespaceOnly(note):
|
||||
|
||||
if note != None and not cls.isWhitespaceOnly(note):
|
||||
#body = cls.restoreNewLines(note)
|
||||
body = note
|
||||
|
||||
children = underElement.findall('outline')
|
||||
if children is not None and len(children) > 0:
|
||||
|
||||
if children != None and len(children) > 0:
|
||||
for el in children:
|
||||
items.extend(cls.parseItems(el, card))
|
||||
else:
|
||||
|
@ -121,4 +122,4 @@ class opmlImporter(abstractImporter):
|
|||
s = cls.restoreNewLines(inString)
|
||||
s = ''.join(s.split())
|
||||
|
||||
return len(s) is 0
|
||||
return len(s) == 0
|
||||
|
|
|
@ -38,7 +38,7 @@ class pandocImporter(abstractImporter):
|
|||
|
||||
r = pandocExporter().run(args)
|
||||
|
||||
if r is None:
|
||||
if r == None:
|
||||
return None
|
||||
|
||||
if formatTo == "opml":
|
||||
|
|
|
@ -40,6 +40,7 @@ characterMap = OrderedDict([
|
|||
(Character.name, "Name"),
|
||||
(Character.ID, "ID"),
|
||||
(Character.importance, "Importance"),
|
||||
(Character.pov, "POV"),
|
||||
(Character.motivation, "Motivation"),
|
||||
(Character.goal, "Goal"),
|
||||
(Character.conflict, "Conflict"),
|
||||
|
@ -47,7 +48,7 @@ characterMap = OrderedDict([
|
|||
(Character.summarySentence, "Phrase Summary"),
|
||||
(Character.summaryPara, "Paragraph Summary"),
|
||||
(Character.summaryFull, "Full Summary"),
|
||||
(Character.notes, "Notes"),
|
||||
(Character.notes, "Notes")
|
||||
])
|
||||
|
||||
# If true, logs infos while saving and loading.
|
||||
|
@ -106,7 +107,7 @@ def saveProject(zip=None):
|
|||
settings.
|
||||
@return: True if successful, False otherwise.
|
||||
"""
|
||||
if zip is None:
|
||||
if zip == None:
|
||||
zip = settings.saveToZip
|
||||
|
||||
log("\n\nSaving to:", "zip" if zip else "folder")
|
||||
|
@ -123,7 +124,7 @@ def saveProject(zip=None):
|
|||
project = mw.currentProject
|
||||
|
||||
# Sanity check (see PR-583): make sure we actually have a current project.
|
||||
if project is None:
|
||||
if project == None:
|
||||
print("Error: cannot save project because there is no current project in the UI.")
|
||||
return False
|
||||
|
||||
|
@ -197,7 +198,7 @@ def saveProject(zip=None):
|
|||
# We skip the first row, which is empty and transparent
|
||||
for i in range(1, mdl.rowCount()):
|
||||
color = ""
|
||||
if mdl.data(mdl.index(i, 0), Qt.DecorationRole) is not None:
|
||||
if mdl.data(mdl.index(i, 0), Qt.DecorationRole) != None:
|
||||
color = iconColor(mdl.data(mdl.index(i, 0), Qt.DecorationRole)).name(QColor.HexRgb)
|
||||
color = color if color != "#ff000000" else "#00000000"
|
||||
|
||||
|
@ -900,7 +901,7 @@ def addTextItems(mdl, odict, parent=None):
|
|||
@param odict: OrderedDict
|
||||
@return: nothing
|
||||
"""
|
||||
if parent is None:
|
||||
if parent == None:
|
||||
parent = mdl.rootItem
|
||||
|
||||
for k in odict:
|
||||
|
|
|
@ -4,6 +4,7 @@ import faulthandler
|
|||
import os
|
||||
import platform
|
||||
import sys
|
||||
import signal
|
||||
|
||||
import manuskript.ui.views.webView
|
||||
from PyQt5.QtCore import QLocale, QTranslator, QSettings, Qt
|
||||
|
@ -15,11 +16,12 @@ from manuskript.version import getVersion
|
|||
|
||||
faulthandler.enable()
|
||||
|
||||
|
||||
def prepare(tests=False):
|
||||
app = QApplication(sys.argv)
|
||||
app.setOrganizationName("manuskript"+("_tests" if tests else ""))
|
||||
app.setOrganizationName("manuskript" + ("_tests" if tests else ""))
|
||||
app.setOrganizationDomain("www.theologeek.ch")
|
||||
app.setApplicationName("manuskript"+("_tests" if tests else ""))
|
||||
app.setApplicationName("manuskript" + ("_tests" if tests else ""))
|
||||
app.setApplicationVersion(getVersion())
|
||||
|
||||
print("Running manuskript version {}.".format(getVersion()))
|
||||
|
@ -38,6 +40,7 @@ def prepare(tests=False):
|
|||
|
||||
# Translation process
|
||||
appTranslator = QTranslator(app)
|
||||
|
||||
# By default: locale
|
||||
|
||||
def tryLoadTranslation(translation, source):
|
||||
|
@ -101,19 +104,21 @@ def prepare(tests=False):
|
|||
|
||||
def respectSystemDarkThemeSetting():
|
||||
"""Adjusts the Qt theme to match the OS 'dark theme' setting configured by the user."""
|
||||
if platform.system() is not 'Windows':
|
||||
if platform.system() != 'Windows':
|
||||
return
|
||||
|
||||
# Basic Windows 10 Dark Theme support.
|
||||
# Source: https://forum.qt.io/topic/101391/windows-10-dark-theme/4
|
||||
themeSettings = QSettings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings.NativeFormat)
|
||||
themeSettings = QSettings(
|
||||
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
QSettings.NativeFormat)
|
||||
if themeSettings.value("AppsUseLightTheme") == 0:
|
||||
darkPalette = QPalette()
|
||||
darkColor = QColor(45,45,45)
|
||||
disabledColor = QColor(127,127,127)
|
||||
darkColor = QColor(45, 45, 45)
|
||||
disabledColor = QColor(127, 127, 127)
|
||||
darkPalette.setColor(QPalette.Window, darkColor)
|
||||
darkPalette.setColor(QPalette.WindowText, Qt.white)
|
||||
darkPalette.setColor(QPalette.Base, QColor(18,18,18))
|
||||
darkPalette.setColor(QPalette.Base, QColor(18, 18, 18))
|
||||
darkPalette.setColor(QPalette.AlternateBase, darkColor)
|
||||
darkPalette.setColor(QPalette.ToolTipBase, Qt.white)
|
||||
darkPalette.setColor(QPalette.ToolTipText, Qt.white)
|
||||
|
@ -137,7 +142,7 @@ def prepare(tests=False):
|
|||
|
||||
# This broke the Settings Dialog at one point... and then it stopped breaking it.
|
||||
# TODO: Why'd it break? Check if tooltips look OK... and if not, make them look OK.
|
||||
#app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }")
|
||||
# app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }")
|
||||
|
||||
respectSystemDarkThemeSetting()
|
||||
|
||||
|
@ -166,9 +171,9 @@ def prepare(tests=False):
|
|||
|
||||
return app, MW
|
||||
|
||||
def launch(app, MW = None):
|
||||
|
||||
if MW is None:
|
||||
def launch(app, MW=None):
|
||||
if MW == None:
|
||||
from manuskript.functions import mainWindow
|
||||
MW = mainWindow()
|
||||
|
||||
|
@ -176,7 +181,7 @@ def launch(app, MW = None):
|
|||
|
||||
# Support for IPython Jupyter QT Console as a debugging aid.
|
||||
# Last argument must be --console to enable it
|
||||
# Code reference :
|
||||
# Code reference :
|
||||
# https://github.com/ipython/ipykernel/blob/master/examples/embedding/ipkernel_qtapp.py
|
||||
# https://github.com/ipython/ipykernel/blob/master/examples/embedding/internal_ipkernel.py
|
||||
if len(sys.argv) > 1 and sys.argv[-1] == "--console":
|
||||
|
@ -188,7 +193,7 @@ def launch(app, MW = None):
|
|||
|
||||
# Create IPython kernel within our application
|
||||
kernel = IPKernelApp.instance()
|
||||
|
||||
|
||||
# Initialize it and use matplotlib for main event loop integration with QT
|
||||
kernel.initialize(['python', '--matplotlib=qt'])
|
||||
|
||||
|
@ -207,6 +212,7 @@ def launch(app, MW = None):
|
|||
app.quit()
|
||||
console.kill()
|
||||
kernel.io_loop.stop()
|
||||
|
||||
app.lastWindowClosed.connect(console_cleanup)
|
||||
|
||||
# Very important, IPython-specific step: this gets GUI event loop
|
||||
|
@ -221,6 +227,20 @@ def launch(app, MW = None):
|
|||
qApp.exec_()
|
||||
qApp.deleteLater()
|
||||
|
||||
|
||||
def sigint_handler(sig, MW):
|
||||
def handler(*args):
|
||||
MW.close()
|
||||
print(f'{sig} received, quit.')
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def setup_signal_handlers(MW):
|
||||
signal.signal(signal.SIGINT, sigint_handler("SIGINT", MW))
|
||||
signal.signal(signal.SIGTERM, sigint_handler("SIGTERM", MW))
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Run separates prepare and launch for two reasons:
|
||||
|
@ -229,9 +249,11 @@ def run():
|
|||
"""
|
||||
# Need to return and keep `app` otherwise it gets deleted.
|
||||
app, MW = prepare()
|
||||
setup_signal_handlers(MW)
|
||||
# Separating launch to avoid segfault, so it seem.
|
||||
# Cf. http://stackoverflow.com/questions/12433491/is-this-pyqt-4-python-bug-or-wrongly-behaving-code
|
||||
launch(app, MW)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
|
|
@ -9,7 +9,7 @@ from PyQt5.QtCore import (pyqtSignal, QSignalMapper, QTimer, QSettings, Qt, QPoi
|
|||
QRegExp, QUrl, QSize, QModelIndex)
|
||||
from PyQt5.QtGui import QStandardItemModel, QIcon, QColor
|
||||
from PyQt5.QtWidgets import QMainWindow, QHeaderView, qApp, QMenu, QActionGroup, QAction, QStyle, QListWidgetItem, \
|
||||
QLabel, QDockWidget, QWidget, QMessageBox
|
||||
QLabel, QDockWidget, QWidget, QMessageBox, QLineEdit
|
||||
|
||||
from manuskript import settings
|
||||
from manuskript.enums import Character, PlotStep, Plot, World, Outline
|
||||
|
@ -129,6 +129,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.actCopy.triggered.connect(self.documentsCopy)
|
||||
self.actCut.triggered.connect(self.documentsCut)
|
||||
self.actPaste.triggered.connect(self.documentsPaste)
|
||||
self.actSearch.triggered.connect(self.doSearch)
|
||||
self.actRename.triggered.connect(self.documentsRename)
|
||||
self.actDuplicate.triggered.connect(self.documentsDuplicate)
|
||||
self.actDelete.triggered.connect(self.documentsDelete)
|
||||
|
@ -270,7 +271,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.mainEditor
|
||||
]
|
||||
|
||||
while new is not None:
|
||||
while new != None:
|
||||
if new in targets:
|
||||
self._lastFocus = new
|
||||
break
|
||||
|
@ -346,6 +347,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# Slider importance
|
||||
self.updateCharacterImportance(c.ID())
|
||||
|
||||
# POV state
|
||||
self.updateCharacterPOVState(c.ID())
|
||||
|
||||
# Character Infos
|
||||
self.tblPersoInfos.setRootIndex(index)
|
||||
|
||||
|
@ -366,6 +370,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
c = self.mdlCharacter.getCharacterByID(ID)
|
||||
self.sldPersoImportance.setValue(int(c.importance()))
|
||||
|
||||
def updateCharacterPOVState(self, ID):
|
||||
c = self.mdlCharacter.getCharacterByID(ID)
|
||||
self.disconnectAll(self.chkPersoPOV.stateChanged, self.lstCharacters.changeCharacterPOVState)
|
||||
|
||||
if c.pov():
|
||||
self.chkPersoPOV.setCheckState(Qt.Checked)
|
||||
else:
|
||||
self.chkPersoPOV.setCheckState(Qt.Unchecked)
|
||||
|
||||
try:
|
||||
self.chkPersoPOV.stateChanged.connect(self.lstCharacters.changeCharacterPOVState, F.AUC)
|
||||
self.chkPersoPOV.setEnabled(len(self.mdlOutline.findItemsByPOV(ID)) == 0)
|
||||
except TypeError:
|
||||
#don't know what's up with this
|
||||
pass
|
||||
|
||||
###############################################################################
|
||||
# PLOTS
|
||||
###############################################################################
|
||||
|
@ -480,6 +500,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
def documentsPaste(self):
|
||||
"Paste clipboard item(s) into selected item."
|
||||
if self._lastFocus: self._lastFocus.paste()
|
||||
def doSearch(self):
|
||||
"Do a global search."
|
||||
self.dckSearch.show()
|
||||
self.dckSearch.activateWindow()
|
||||
searchTextInput = self.dckSearch.findChild(QLineEdit, 'searchTextInput')
|
||||
searchTextInput.setFocus()
|
||||
searchTextInput.selectAll()
|
||||
def documentsRename(self):
|
||||
"Rename selected item."
|
||||
if self._lastFocus: self._lastFocus.rename()
|
||||
|
@ -823,7 +850,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# risk a scenario where the timer somehow triggers a new save while saving.
|
||||
self.saveTimerNoChanges.stop()
|
||||
|
||||
if self.currentProject is None:
|
||||
if self.currentProject == None:
|
||||
# No UI feedback here as this code path indicates a race condition that happens
|
||||
# after the user has already closed the project through some way. But in that
|
||||
# scenario, this code should not be reachable to begin with.
|
||||
|
@ -933,11 +960,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# Characters
|
||||
self.lstCharacters.setCharactersModel(self.mdlCharacter)
|
||||
self.tblPersoInfos.setModel(self.mdlCharacter)
|
||||
|
||||
self.btnAddPerso.clicked.connect(self.mdlCharacter.addCharacter, F.AUC)
|
||||
try:
|
||||
self.btnAddPerso.clicked.connect(self.lstCharacters.addCharacter, F.AUC)
|
||||
self.btnRmPerso.clicked.connect(self.lstCharacters.removeCharacter, F.AUC)
|
||||
|
||||
self.btnPersoColor.clicked.connect(self.lstCharacters.choseCharacterColor, F.AUC)
|
||||
self.chkPersoPOV.stateChanged.connect(self.lstCharacters.changeCharacterPOVState, F.AUC)
|
||||
|
||||
self.btnPersoAddInfo.clicked.connect(self.lstCharacters.addCharacterInfo, F.AUC)
|
||||
self.btnPersoRmInfo.clicked.connect(self.lstCharacters.removeCharacterInfo, F.AUC)
|
||||
except TypeError:
|
||||
|
@ -1078,7 +1107,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# disconnect only removes one connection at a time.
|
||||
while True:
|
||||
try:
|
||||
if oldHandler is not None:
|
||||
if oldHandler != None:
|
||||
signal.disconnect(oldHandler)
|
||||
else:
|
||||
signal.disconnect()
|
||||
|
@ -1089,9 +1118,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# Break connections for UI elements that were connected in makeConnections()
|
||||
|
||||
# Characters
|
||||
self.disconnectAll(self.btnAddPerso.clicked, self.mdlCharacter.addCharacter)
|
||||
self.disconnectAll(self.btnAddPerso.clicked, self.lstCharacters.addCharacter)
|
||||
self.disconnectAll(self.btnRmPerso.clicked, self.lstCharacters.removeCharacter)
|
||||
|
||||
self.disconnectAll(self.btnPersoColor.clicked, self.lstCharacters.choseCharacterColor)
|
||||
self.disconnectAll(self.chkPersoPOV.stateChanged, self.lstCharacters.changeCharacterPOVState)
|
||||
|
||||
self.disconnectAll(self.btnPersoAddInfo.clicked, self.lstCharacters.addCharacterInfo)
|
||||
self.disconnectAll(self.btnPersoRmInfo.clicked, self.lstCharacters.removeCharacterInfo)
|
||||
|
||||
|
@ -1355,7 +1387,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
dictionaries = Spellchecker.availableDictionaries()
|
||||
|
||||
# Set first run dictionary
|
||||
if settings.dict is None:
|
||||
if settings.dict == None:
|
||||
settings.dict = Spellchecker.getDefaultDictionary()
|
||||
|
||||
# Check if project dict is unavailable on this machine
|
||||
|
@ -1566,7 +1598,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
w.cmbPOV.setVisible(val)
|
||||
|
||||
# POV in outline view
|
||||
if val is None and Outline.POV in settings.outlineViewColumns:
|
||||
if val == None and Outline.POV in settings.outlineViewColumns:
|
||||
settings.outlineViewColumns.remove(Outline.POV)
|
||||
|
||||
from manuskript.ui.views.outlineView import outlineView
|
||||
|
|
|
@ -39,7 +39,7 @@ class abstractItem():
|
|||
self._data[self.enum.title] = title
|
||||
self._data[self.enum.type] = _type
|
||||
|
||||
if xml is not None:
|
||||
if xml != None:
|
||||
self.setFromXML(xml)
|
||||
|
||||
if parent:
|
||||
|
|
|
@ -140,7 +140,7 @@ class abstractModel(QAbstractItemModel):
|
|||
# Check whether the parent is the root, or is otherwise invalid.
|
||||
# That is to say: no parent or the parent lacks a parent.
|
||||
if (parentItem == self.rootItem) or \
|
||||
(parentItem is None) or (parentItem.parent() is None):
|
||||
(parentItem == None) or (parentItem.parent() == None):
|
||||
return QModelIndex()
|
||||
|
||||
return self.createIndex(parentItem.row(), 0, parentItem)
|
||||
|
@ -292,7 +292,7 @@ class abstractModel(QAbstractItemModel):
|
|||
|
||||
# # Gets encoded mime data to retrieve the item
|
||||
items = self.decodeMimeData(data)
|
||||
if items is None:
|
||||
if items == None:
|
||||
return False
|
||||
|
||||
# We check if parent is not a child of one of the items
|
||||
|
@ -330,7 +330,7 @@ class abstractModel(QAbstractItemModel):
|
|||
return None
|
||||
encodedData = bytes(data.data("application/xml")).decode()
|
||||
root = ET.XML(encodedData)
|
||||
if root is None:
|
||||
if root == None:
|
||||
return None
|
||||
|
||||
if root.tag != "outlineItems":
|
||||
|
@ -390,7 +390,7 @@ class abstractModel(QAbstractItemModel):
|
|||
|
||||
items = self.decodeMimeData(data)
|
||||
|
||||
if items is None:
|
||||
if items == None:
|
||||
return False
|
||||
|
||||
if column > 0:
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
from PyQt5.QtCore import QModelIndex, Qt, QAbstractItemModel, QVariant
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
||||
|
||||
from manuskript.functions import randomColor, iconColor, mainWindow
|
||||
from manuskript.enums import Character as C
|
||||
from manuskript.functions import randomColor, iconColor, mainWindow, search
|
||||
from manuskript.enums import Character as C, Model
|
||||
from manuskript.searchLabels import CharacterSearchLabels
|
||||
|
||||
from manuskript.models.searchableModel import searchableModel
|
||||
from manuskript.models.searchableItem import searchableItem
|
||||
|
||||
class characterModel(QAbstractItemModel):
|
||||
class characterModel(QAbstractItemModel, searchableModel):
|
||||
|
||||
def __init__(self, parent):
|
||||
QAbstractItemModel.__init__(self, parent)
|
||||
|
@ -132,6 +135,9 @@ class characterModel(QAbstractItemModel):
|
|||
def importance(self, row):
|
||||
return self.character(row).importance()
|
||||
|
||||
def pov(self, row):
|
||||
return self.character(row).pov()
|
||||
|
||||
###############################################################################
|
||||
# MODEL QUERIES
|
||||
###############################################################################
|
||||
|
@ -143,29 +149,36 @@ class characterModel(QAbstractItemModel):
|
|||
@return: array of array of ´character´, by importance.
|
||||
"""
|
||||
r = [[], [], []]
|
||||
|
||||
for c in self.characters:
|
||||
r[2-int(c.importance())].append(c)
|
||||
|
||||
return r
|
||||
|
||||
def getCharacterByID(self, ID):
|
||||
if ID is not None:
|
||||
if ID != None:
|
||||
ID = str(ID)
|
||||
for c in self.characters:
|
||||
if c.ID() == ID:
|
||||
return c
|
||||
|
||||
return None
|
||||
|
||||
###############################################################################
|
||||
# ADDING / REMOVING
|
||||
###############################################################################
|
||||
|
||||
def addCharacter(self):
|
||||
def addCharacter(self, importance = 0, name="New character"):
|
||||
"""
|
||||
Creates a new character
|
||||
@param importance: the importance level of the character
|
||||
@return: the character
|
||||
"""
|
||||
c = Character(model=self, name=self.tr("New character"))
|
||||
self.beginInsertRows(QModelIndex(), len(self.characters), len(self.characters))
|
||||
if not name:
|
||||
name="New Character"
|
||||
c = Character(model=self, name=self.tr(name), importance = importance)
|
||||
self.beginInsertRows(QModelIndex(), len(
|
||||
self.characters), len(self.characters))
|
||||
self.characters.append(c)
|
||||
self.endInsertRows()
|
||||
return c
|
||||
|
@ -177,7 +190,8 @@ class characterModel(QAbstractItemModel):
|
|||
@return: nothing
|
||||
"""
|
||||
c = self.getCharacterByID(ID)
|
||||
self.beginRemoveRows(QModelIndex(), self.characters.index(c), self.characters.index(c))
|
||||
self.beginRemoveRows(QModelIndex(), self.characters.index(
|
||||
c), self.characters.index(c))
|
||||
self.characters.remove(c)
|
||||
self.endRemoveRows()
|
||||
|
||||
|
@ -197,7 +211,8 @@ class characterModel(QAbstractItemModel):
|
|||
def addCharacterInfo(self, ID):
|
||||
c = self.getCharacterByID(ID)
|
||||
self.beginInsertRows(c.index(), len(c.infos), len(c.infos))
|
||||
c.infos.append(CharacterInfo(c, description="Description", value="Value"))
|
||||
c.infos.append(CharacterInfo(
|
||||
c, description="Description", value="Value"))
|
||||
self.endInsertRows()
|
||||
|
||||
mainWindow().updatePersoInfoView()
|
||||
|
@ -217,12 +232,15 @@ class characterModel(QAbstractItemModel):
|
|||
c.infos.pop(r)
|
||||
self.endRemoveRows()
|
||||
|
||||
def searchableItems(self):
|
||||
return self.characters
|
||||
|
||||
###############################################################################
|
||||
# CHARACTER
|
||||
###############################################################################
|
||||
|
||||
class Character():
|
||||
def __init__(self, model, name="No name"):
|
||||
class Character(searchableItem):
|
||||
def __init__(self, model, name="No name", importance = 0):
|
||||
self._model = model
|
||||
self.lastPath = ""
|
||||
|
||||
|
@ -230,13 +248,19 @@ class Character():
|
|||
self._data[C.name.value] = name
|
||||
self.assignUniqueID()
|
||||
self.assignRandomColor()
|
||||
self._data[C.importance.value] = "0"
|
||||
self._data[C.importance.value] = str(importance)
|
||||
self._data[C.pov.value] = "True"
|
||||
|
||||
self.infos = []
|
||||
|
||||
super().__init__(CharacterSearchLabels)
|
||||
|
||||
def name(self):
|
||||
return self._data[C.name.value]
|
||||
|
||||
def setName(self, value):
|
||||
self._data[C.name.value] = value
|
||||
|
||||
def importance(self):
|
||||
return self._data[C.importance.value]
|
||||
|
||||
|
@ -246,6 +270,12 @@ class Character():
|
|||
def index(self, column=0):
|
||||
return self._model.indexFromItem(self, column)
|
||||
|
||||
def data(self, column):
|
||||
if column == "Info":
|
||||
return self.infos
|
||||
else:
|
||||
return self._data.get(column, None)
|
||||
|
||||
def assignRandomColor(self):
|
||||
"""
|
||||
Assigns a random color the the character.
|
||||
|
@ -274,6 +304,22 @@ class Character():
|
|||
"""
|
||||
return iconColor(self.icon)
|
||||
|
||||
def setPOVEnabled(self, enabled):
|
||||
if enabled != self.pov():
|
||||
if enabled:
|
||||
self._data[C.pov.value] = 'True'
|
||||
else:
|
||||
self._data[C.pov.value] = 'False'
|
||||
|
||||
try:
|
||||
self._model.dataChanged.emit(self.index(), self.index())
|
||||
except:
|
||||
# If it is the initialisation, won't be able to emit
|
||||
pass
|
||||
|
||||
def pov(self):
|
||||
return self._data[C.pov.value] == 'True'
|
||||
|
||||
def assignUniqueID(self, parent=QModelIndex()):
|
||||
"""Assigns an unused character ID."""
|
||||
vals = []
|
||||
|
@ -292,6 +338,42 @@ class Character():
|
|||
r.append((i.description, i.value))
|
||||
return r
|
||||
|
||||
def searchTitle(self, column):
|
||||
return self.name()
|
||||
|
||||
def searchOccurrences(self, searchRegex, column):
|
||||
results = []
|
||||
|
||||
data = self.searchData(column)
|
||||
if isinstance(data, list):
|
||||
for i in range(0, len(data)):
|
||||
# For detailed info we will highlight the full row, so we pass the row index
|
||||
# to the highlighter instead of the (startPos, endPos) of the match itself.
|
||||
results += [self.wrapSearchOccurrence(column, i, 0, context) for
|
||||
(startPos, endPos, context) in search(searchRegex, data[i].description)]
|
||||
results += [self.wrapSearchOccurrence(column, i, 0, context) for
|
||||
(startPos, endPos, context) in search(searchRegex, data[i].value)]
|
||||
else:
|
||||
results += super().searchOccurrences(searchRegex, column)
|
||||
|
||||
return results
|
||||
|
||||
def searchID(self):
|
||||
return self.ID()
|
||||
|
||||
def searchPath(self, column):
|
||||
return [self.translate("Characters"), self.name(), self.translate(self.searchColumnLabel(column))]
|
||||
|
||||
def searchData(self, column):
|
||||
if column == C.infos:
|
||||
return self.infos
|
||||
else:
|
||||
return self.data(column)
|
||||
|
||||
def searchModel(self):
|
||||
return Model.Character
|
||||
|
||||
|
||||
class CharacterInfo():
|
||||
def __init__(self, character, description="", value=""):
|
||||
self.description = description
|
||||
|
|
47
manuskript/models/characterPOVModel.py
Normal file
47
manuskript/models/characterPOVModel.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
from PyQt5.QtCore import QModelIndex, QSortFilterProxyModel
|
||||
|
||||
|
||||
class characterPOVModel(QSortFilterProxyModel):
|
||||
|
||||
def __init__(self, sourceModel, parent=None):
|
||||
QSortFilterProxyModel.__init__(self, parent)
|
||||
|
||||
self.setSourceModel(sourceModel)
|
||||
|
||||
if sourceModel:
|
||||
sourceModel.dataChanged.connect(self.sourceDataChanged)
|
||||
|
||||
def filterAcceptsRow(self, sourceRow, sourceParent):
|
||||
return self.sourceModel().pov(sourceRow)
|
||||
|
||||
def rowToSource(self, row):
|
||||
index = self.index(row, 0)
|
||||
sourceIndex = self.mapToSource(index)
|
||||
return sourceIndex.row()
|
||||
|
||||
def sourceDataChanged(self, topLeft, bottomRight):
|
||||
self.invalidateFilter()
|
||||
|
||||
###############################################################################
|
||||
# CHARACTER QUERIES
|
||||
###############################################################################
|
||||
|
||||
def character(self, row):
|
||||
return self.sourceModel().character(self.rowToSource(row))
|
||||
|
||||
def name(self, row):
|
||||
return self.sourceModel().name(self.rowToSource(row))
|
||||
|
||||
def icon(self, row):
|
||||
return self.sourceModel().icon(self.rowToSource(row))
|
||||
|
||||
def ID(self, row):
|
||||
return self.sourceModel().ID(self.rowToSource(row))
|
||||
|
||||
def importance(self, row):
|
||||
return self.sourceModel().importance(self.rowToSource(row))
|
||||
|
||||
def pov(self, row):
|
||||
return self.sourceModel().pov(self.rowToSource(row))
|
53
manuskript/models/flatDataModelWrapper.py
Normal file
53
manuskript/models/flatDataModelWrapper.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.enums import FlatData, Model
|
||||
from manuskript.searchLabels import FlatDataSearchLabels
|
||||
|
||||
from manuskript.models.searchableModel import searchableModel
|
||||
from manuskript.models.searchableItem import searchableItem
|
||||
|
||||
"""
|
||||
All searches are performed on models inheriting from searchableModel, but special metadata such as book summaries
|
||||
are stored directly on a GUI element (QStandardItemModel). We wrap this GUI element inside this wrapper class
|
||||
so it exposes the same interface for searches.
|
||||
"""
|
||||
class flatDataModelWrapper(searchableModel, searchableItem):
|
||||
def __init__(self, qstandardItemModel):
|
||||
self.qstandardItemModel = qstandardItemModel
|
||||
|
||||
def searchableItems(self):
|
||||
return [flatDataItemWrapper(self.qstandardItemModel)]
|
||||
|
||||
|
||||
class flatDataItemWrapper(searchableItem):
|
||||
def __init__(self, qstandardItemModel):
|
||||
super().__init__(FlatDataSearchLabels)
|
||||
self.qstandardItemModel = qstandardItemModel
|
||||
|
||||
def searchModel(self):
|
||||
return Model.FlatData
|
||||
|
||||
def searchID(self):
|
||||
return None
|
||||
|
||||
def searchTitle(self, column):
|
||||
return self.translate(self.searchColumnLabel(column))
|
||||
|
||||
def searchPath(self, column):
|
||||
return [self.translate("Summary"), self.translate(self.searchColumnLabel(column))]
|
||||
|
||||
def searchData(self, column):
|
||||
return self.qstandardItemModel.item(1, self.searchDataIndex(column)).text()
|
||||
|
||||
@staticmethod
|
||||
def searchDataIndex(column):
|
||||
columnIndices = {
|
||||
FlatData.summarySituation: 0,
|
||||
FlatData.summarySentence: 1,
|
||||
FlatData.summaryPara: 2,
|
||||
FlatData.summaryPage: 3,
|
||||
FlatData.summaryFull: 4
|
||||
}
|
||||
|
||||
return columnIndices[column]
|
|
@ -8,10 +8,13 @@ from PyQt5.QtGui import QFont, QIcon
|
|||
from PyQt5.QtWidgets import qApp
|
||||
from lxml import etree as ET
|
||||
from manuskript.models.abstractItem import abstractItem
|
||||
from manuskript.models.searchableItem import searchableItem
|
||||
from manuskript import enums
|
||||
from manuskript import functions as F
|
||||
from manuskript import settings
|
||||
from manuskript.converters import HTML2PlainText
|
||||
from manuskript.searchLabels import OutlineSearchLabels
|
||||
from manuskript.enums import Outline, Model
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
@ -21,7 +24,7 @@ except:
|
|||
pass
|
||||
|
||||
|
||||
class outlineItem(abstractItem):
|
||||
class outlineItem(abstractItem, searchableItem):
|
||||
|
||||
enum = enums.Outline
|
||||
|
||||
|
@ -30,6 +33,7 @@ class outlineItem(abstractItem):
|
|||
|
||||
def __init__(self, model=None, title="", _type="folder", xml=None, parent=None, ID=None):
|
||||
abstractItem.__init__(self, model, title, _type, xml, parent, ID)
|
||||
searchableItem.__init__(self, OutlineSearchLabels)
|
||||
|
||||
self.defaultTextType = None
|
||||
if not self._data.get(self.enum.compile):
|
||||
|
@ -80,6 +84,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 +96,9 @@ class outlineItem(abstractItem):
|
|||
)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def charCount(self):
|
||||
return self._data.get(self.enum.charCount, 0)
|
||||
|
||||
#######################################################################
|
||||
# Data
|
||||
|
@ -119,7 +129,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 +150,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 +163,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 +207,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 +233,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()
|
||||
|
@ -343,8 +359,7 @@ class outlineItem(abstractItem):
|
|||
|
||||
return lst
|
||||
|
||||
def findItemsContaining(self, text, columns, mainWindow=F.mainWindow(),
|
||||
caseSensitive=False, recursive=True):
|
||||
def findItemsContaining(self, text, columns, mainWindow=F.mainWindow(), caseSensitive=False, recursive=True):
|
||||
"""Returns a list if IDs of all subitems
|
||||
containing ``text`` in columns ``columns``
|
||||
(being a list of int).
|
||||
|
@ -357,16 +372,14 @@ class outlineItem(abstractItem):
|
|||
|
||||
return lst
|
||||
|
||||
def itemContains(self, text, columns, mainWindow=F.mainWindow(),
|
||||
caseSensitive=False):
|
||||
def itemContains(self, text, columns, mainWindow=F.mainWindow(), caseSensitive=False):
|
||||
lst = []
|
||||
text = text.lower() if not caseSensitive else text
|
||||
for c in columns:
|
||||
|
||||
if c == self.enum.POV and self.POV():
|
||||
c = mainWindow.mdlCharacter.getCharacterByID(self.POV())
|
||||
if c:
|
||||
searchIn = c.name()
|
||||
character = mainWindow.mdlCharacter.getCharacterByID(self.POV())
|
||||
if character:
|
||||
searchIn = character.name()
|
||||
else:
|
||||
searchIn = ""
|
||||
print("Character POV not found:", self.POV())
|
||||
|
@ -381,7 +394,6 @@ class outlineItem(abstractItem):
|
|||
searchIn = self.data(c)
|
||||
|
||||
searchIn = searchIn.lower() if not caseSensitive else searchIn
|
||||
|
||||
if text in searchIn:
|
||||
if not self.ID() in lst:
|
||||
lst.append(self.ID())
|
||||
|
@ -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]
|
||||
|
@ -502,3 +515,39 @@ class outlineItem(abstractItem):
|
|||
for child in root:
|
||||
if child.tag == "revision":
|
||||
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
|
||||
|
||||
#######################################################################
|
||||
# Search
|
||||
#######################################################################
|
||||
def searchModel(self):
|
||||
return Model.Outline
|
||||
|
||||
def searchID(self):
|
||||
return self.data(Outline.ID)
|
||||
|
||||
def searchTitle(self, column):
|
||||
return self.title()
|
||||
|
||||
def searchPath(self, column):
|
||||
return [self.translate("Outline")] + self.path().split(' > ') + [self.translate(self.searchColumnLabel(column))]
|
||||
|
||||
def searchData(self, column):
|
||||
mainWindow = F.mainWindow()
|
||||
|
||||
searchData = None
|
||||
|
||||
if column == self.enum.POV and self.POV():
|
||||
character = mainWindow.mdlCharacter.getCharacterByID(self.POV())
|
||||
if character:
|
||||
searchData = character.name()
|
||||
|
||||
elif column == self.enum.status:
|
||||
searchData = mainWindow.mdlStatus.item(F.toInt(self.status()), 0).text()
|
||||
|
||||
elif column == self.enum.label:
|
||||
searchData = mainWindow.mdlLabels.item(F.toInt(self.label()), 0).text()
|
||||
|
||||
else:
|
||||
searchData = self.data(column)
|
||||
|
||||
return searchData
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.models.abstractModel import abstractModel
|
||||
from manuskript.models import outlineItem
|
||||
from manuskript.models.searchableModel import searchableModel
|
||||
from manuskript.models.outlineItem import outlineItem
|
||||
|
||||
|
||||
class outlineModel(abstractModel):
|
||||
class outlineModel(abstractModel, searchableModel):
|
||||
def __init__(self, parent):
|
||||
abstractModel.__init__(self, parent)
|
||||
self.rootItem = outlineItem(model=self, title="Root", ID="0")
|
||||
|
@ -14,3 +14,19 @@ class outlineModel(abstractModel):
|
|||
def findItemsByPOV(self, POV):
|
||||
"Returns a list of IDs of all items whose POV is ``POV``."
|
||||
return self.rootItem.findItemsByPOV(POV)
|
||||
|
||||
def searchableItems(self):
|
||||
result = []
|
||||
|
||||
for child in self.rootItem.children():
|
||||
result += self._searchableItems(child)
|
||||
|
||||
return result
|
||||
|
||||
def _searchableItems(self, item):
|
||||
result = [item]
|
||||
|
||||
for child in item.children():
|
||||
result += self._searchableItems(child)
|
||||
|
||||
return result
|
||||
|
|
|
@ -8,12 +8,15 @@ from PyQt5.QtGui import QStandardItem
|
|||
from PyQt5.QtGui import QStandardItemModel
|
||||
from PyQt5.QtWidgets import QAction, QMenu
|
||||
|
||||
from manuskript.enums import Plot
|
||||
from manuskript.enums import PlotStep
|
||||
from manuskript.enums import Plot, PlotStep, Model
|
||||
from manuskript.functions import toInt, mainWindow
|
||||
from manuskript.models.searchResultModel import searchResultModel
|
||||
from manuskript.searchLabels import PlotSearchLabels, PLOT_STEP_COLUMNS_OFFSET
|
||||
from manuskript.functions import search
|
||||
from manuskript.models.searchableModel import searchableModel
|
||||
from manuskript.models.searchableItem import searchableItem
|
||||
|
||||
|
||||
class plotModel(QStandardItemModel):
|
||||
class plotModel(QStandardItemModel, searchableModel):
|
||||
def __init__(self, parent):
|
||||
QStandardItemModel.__init__(self, 0, 3, parent)
|
||||
self.setHorizontalHeaderLabels([i.name for i in Plot])
|
||||
|
@ -73,7 +76,7 @@ class plotModel(QStandardItemModel):
|
|||
if i == row:
|
||||
importance = self.item(i, Plot.importance).text()
|
||||
return importance
|
||||
return "0" # Default to "Minor"
|
||||
return "0" # Default to "Minor"
|
||||
|
||||
def getSubPlotTextsByID(self, plotID, subplotRaw):
|
||||
"""Returns a tuple (name, summary) for the subplot whose raw in the model
|
||||
|
@ -102,12 +105,15 @@ class plotModel(QStandardItemModel):
|
|||
# ADDING / REMOVING
|
||||
###############################################################################
|
||||
|
||||
def addPlot(self):
|
||||
p = QStandardItem(self.tr("New plot"))
|
||||
def addPlot(self, name="New plot"):
|
||||
if not name:
|
||||
name="New Plot"
|
||||
p = QStandardItem(self.tr(name))
|
||||
_id = QStandardItem(self.getUniqueID())
|
||||
importance = QStandardItem(str(0))
|
||||
self.appendRow([p, _id, importance, QStandardItem("Characters"),
|
||||
QStandardItem(), QStandardItem(), QStandardItem("Resolution steps")])
|
||||
return p, _id
|
||||
|
||||
def getUniqueID(self, parent=QModelIndex()):
|
||||
"""Returns an unused ID"""
|
||||
|
@ -147,8 +153,8 @@ class plotModel(QStandardItemModel):
|
|||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
if index.parent().isValid() and \
|
||||
index.parent().column() == Plot.steps and \
|
||||
index.column() == PlotStep.meta:
|
||||
index.parent().column() == Plot.steps and \
|
||||
index.column() == PlotStep.meta:
|
||||
if role == Qt.TextAlignmentRole:
|
||||
return Qt.AlignRight | Qt.AlignVCenter
|
||||
elif role == Qt.ForegroundRole:
|
||||
|
@ -186,7 +192,8 @@ class plotModel(QStandardItemModel):
|
|||
# Don't know why, if summary is in third position, then drag/drop deletes it...
|
||||
parentItem.appendRow([p, _id, QStandardItem(), summary])
|
||||
# Select last index
|
||||
self.mw.lstSubPlots.setCurrentIndex(parent.child(self.rowCount(parent) - 1, 0))
|
||||
self.mw.lstSubPlots.setCurrentIndex(
|
||||
parent.child(self.rowCount(parent) - 1, 0))
|
||||
|
||||
def removeSubPlot(self):
|
||||
"""
|
||||
|
@ -262,3 +269,118 @@ class plotModel(QStandardItemModel):
|
|||
|
||||
mpr.mapped.connect(self.addPlotPerso)
|
||||
self.mw.btnAddPlotPerso.setMenu(menu)
|
||||
|
||||
#######################################################################
|
||||
# Search
|
||||
#######################################################################
|
||||
def searchableItems(self):
|
||||
items = []
|
||||
|
||||
for i in range(self.rowCount()):
|
||||
items.append(plotItemSearchWrapper(i, self.item, self.mw.mdlCharacter.getCharacterByID))
|
||||
|
||||
return items
|
||||
|
||||
|
||||
class plotItemSearchWrapper(searchableItem):
|
||||
def __init__(self, rowIndex, getItem, getCharacterByID):
|
||||
self.rowIndex = rowIndex
|
||||
self.getItem = getItem
|
||||
self.getCharacterByID = getCharacterByID
|
||||
super().__init__(PlotSearchLabels)
|
||||
|
||||
def searchOccurrences(self, searchRegex, column):
|
||||
results = []
|
||||
|
||||
plotName = self.getItem(self.rowIndex, Plot.name).text()
|
||||
if column >= PLOT_STEP_COLUMNS_OFFSET:
|
||||
results += self.searchInPlotSteps(self.rowIndex, plotName, column, column - PLOT_STEP_COLUMNS_OFFSET, searchRegex, False)
|
||||
else:
|
||||
item_name = self.getItem(self.rowIndex, Plot.name).text()
|
||||
if column == Plot.characters:
|
||||
charactersList = self.getItem(self.rowIndex, Plot.characters)
|
||||
|
||||
for i in range(charactersList.rowCount()):
|
||||
characterID = charactersList.child(i).text()
|
||||
|
||||
character = self.getCharacterByID(characterID)
|
||||
if character:
|
||||
columnText = character.name()
|
||||
|
||||
characterResults = search(searchRegex, columnText)
|
||||
if len(characterResults):
|
||||
# We will highlight the full character row in the plot characters list, so we
|
||||
# return the row index instead of the match start and end positions.
|
||||
results += [
|
||||
searchResultModel(Model.Plot, self.getItem(self.rowIndex, Plot.ID).text(), column,
|
||||
self.translate(item_name),
|
||||
self.searchPath(column),
|
||||
[(i, 0)], context) for start, end, context in
|
||||
search(searchRegex, columnText)]
|
||||
else:
|
||||
results += super().searchOccurrences(searchRegex, column)
|
||||
if column == Plot.name:
|
||||
results += self.searchInPlotSteps(self.rowIndex, plotName, Plot.name, PlotStep.name,
|
||||
searchRegex, False)
|
||||
elif column == Plot.summary:
|
||||
results += self.searchInPlotSteps(self.rowIndex, plotName, Plot.summary, PlotStep.summary,
|
||||
searchRegex, True)
|
||||
|
||||
return results
|
||||
|
||||
def searchModel(self):
|
||||
return Model.Plot
|
||||
|
||||
def searchID(self):
|
||||
return self.getItem(self.rowIndex, Plot.ID).text()
|
||||
|
||||
def searchTitle(self, column):
|
||||
return self.getItem(self.rowIndex, Plot.name).text()
|
||||
|
||||
def searchPath(self, column):
|
||||
def _path(item):
|
||||
path = []
|
||||
|
||||
if item.parent():
|
||||
path += _path(item.parent())
|
||||
path.append(item.text())
|
||||
|
||||
return path
|
||||
|
||||
return [self.translate("Plot")] + _path(self.getItem(self.rowIndex, Plot.name)) + [self.translate(self.searchColumnLabel(column))]
|
||||
|
||||
def searchData(self, column):
|
||||
return self.getItem(self.rowIndex, column).text()
|
||||
|
||||
def plotStepPath(self, plotName, plotStepName, column):
|
||||
return [self.translate("Plot"), plotName, plotStepName, self.translate(self.searchColumnLabel(column))]
|
||||
|
||||
def searchInPlotSteps(self, plotIndex, plotName, plotColumn, plotStepColumn, searchRegex, searchInsidePlotStep):
|
||||
results = []
|
||||
|
||||
# Plot step info can be found in two places: the own list of plot steps (this is the case for ie. name and meta
|
||||
# fields) and "inside" the plot step once it is selected in the list (as it's the case for the summary).
|
||||
if searchInsidePlotStep:
|
||||
# We are searching *inside* the plot step, so we return both the row index (for selecting the right plot
|
||||
# step in the list), and (start, end) positions of the match inside the text field for highlighting it.
|
||||
getSearchData = lambda rowIndex, start, end, context: ([(rowIndex, 0), (start, end)], context)
|
||||
else:
|
||||
# We are searching *in the plot step row*, so we only return the row index for selecting the right plot
|
||||
# step in the list when highlighting search results.
|
||||
getSearchData = lambda rowIndex, start, end, context: ([(rowIndex, 0)], context)
|
||||
|
||||
item = self.getItem(plotIndex, Plot.steps)
|
||||
for i in range(item.rowCount()):
|
||||
if item.child(i, PlotStep.ID):
|
||||
plotStepName = item.child(i, PlotStep.name).text()
|
||||
plotStepText = item.child(i, plotStepColumn).text()
|
||||
|
||||
# We will highlight the full plot step row in the plot steps list, so we
|
||||
# return the row index instead of the match start and end positions.
|
||||
results += [searchResultModel(Model.PlotStep, self.getItem(plotIndex, Plot.ID).text(), plotStepColumn,
|
||||
self.translate(plotStepName),
|
||||
self.plotStepPath(plotName, plotStepName, plotColumn),
|
||||
*getSearchData(i, start, end, context)) for start, end, context in
|
||||
search(searchRegex, plotStepText)]
|
||||
|
||||
return results
|
|
@ -187,7 +187,7 @@ def infos(ref):
|
|||
elif _type == CharacterLetter:
|
||||
m = mainWindow().mdlCharacter
|
||||
c = m.getCharacterByID(int(_ref))
|
||||
if c is None:
|
||||
if c == None:
|
||||
return qApp.translate("references", "Unknown reference: {}.").format(ref)
|
||||
|
||||
index = c.index()
|
||||
|
|
32
manuskript/models/searchFilter.py
Normal file
32
manuskript/models/searchFilter.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
class searchFilter:
|
||||
def __init__(self, label, enabled, modelColumns = None):
|
||||
if not isinstance(label, str):
|
||||
raise TypeError("label must be a str")
|
||||
|
||||
if not isinstance(enabled, bool):
|
||||
raise TypeError("enabled must be a bool")
|
||||
|
||||
if modelColumns is not None and (not isinstance(modelColumns, list)):
|
||||
raise TypeError("modelColumns must be a list or None")
|
||||
|
||||
self._label = label
|
||||
self._enabled = enabled
|
||||
self._modelColumns = modelColumns
|
||||
if self._modelColumns is None:
|
||||
self._modelColumns = []
|
||||
|
||||
def label(self):
|
||||
return self._label
|
||||
|
||||
def enabled(self):
|
||||
return self._enabled
|
||||
|
||||
def modelColumns(self):
|
||||
return self._modelColumns
|
||||
|
||||
def setEnabled(self, enabled):
|
||||
self._enabled = enabled
|
44
manuskript/models/searchResultModel.py
Normal file
44
manuskript/models/searchResultModel.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
class searchResultModel():
|
||||
def __init__(self, model_type, model_id, column, title, path, pos, context):
|
||||
self._type = model_type
|
||||
self._id = model_id
|
||||
self._column = column
|
||||
self._title = title
|
||||
self._path = path
|
||||
self._pos = pos
|
||||
self._context = context
|
||||
|
||||
def type(self):
|
||||
return self._type
|
||||
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
def column(self):
|
||||
return self._column
|
||||
|
||||
def title(self):
|
||||
return self._title
|
||||
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
def pos(self):
|
||||
return self._pos
|
||||
|
||||
def context(self):
|
||||
return self._context
|
||||
|
||||
def __repr__(self):
|
||||
return "(%s, %s, %s, %s, %s, %s, %s)" % (self._type, self._id, self._column, self._title, self._path, self._pos, self._context)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.type() == other.type() and \
|
||||
self.id() == other.id() and \
|
||||
self.column == other.column and \
|
||||
self.pos() == other.pos() and \
|
||||
self.context == other.context
|
38
manuskript/models/searchableItem.py
Normal file
38
manuskript/models/searchableItem.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from manuskript.models.searchResultModel import searchResultModel
|
||||
from manuskript.functions import search
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
class searchableItem():
|
||||
def __init__(self, searchColumnLabels):
|
||||
self._searchColumnLabels = searchColumnLabels
|
||||
|
||||
def searchOccurrences(self, searchRegex, column):
|
||||
return [self.wrapSearchOccurrence(column, startPos, endPos, context) for (startPos, endPos, context) in search(searchRegex, self.searchData(column))]
|
||||
|
||||
def wrapSearchOccurrence(self, column, startPos, endPos, context):
|
||||
return searchResultModel(self.searchModel(), self.searchID(), column, self.searchTitle(column), self.searchPath(column), [(startPos, endPos)], context)
|
||||
|
||||
def searchModel(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def searchID(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def searchTitle(self, column):
|
||||
raise NotImplementedError
|
||||
|
||||
def searchPath(self, column):
|
||||
return []
|
||||
|
||||
def searchData(self, column):
|
||||
raise NotImplementedError
|
||||
|
||||
def searchColumnLabel(self, column):
|
||||
return self._searchColumnLabels.get(column, "")
|
||||
|
||||
def translate(self, text):
|
||||
return QCoreApplication.translate("MainWindow", text)
|
15
manuskript/models/searchableModel.py
Normal file
15
manuskript/models/searchableModel.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
class searchableModel():
|
||||
|
||||
def searchOccurrences(self, searchRegex, columns):
|
||||
results = []
|
||||
for item in self.searchableItems():
|
||||
for column in columns:
|
||||
results += item.searchOccurrences(searchRegex, column)
|
||||
return results
|
||||
|
||||
def searchableItems(self):
|
||||
raise NotImplementedError
|
|
@ -1,18 +1,20 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
from PyQt5.QtCore import QModelIndex
|
||||
from PyQt5.QtCore import QSize
|
||||
from PyQt5.QtCore import QModelIndex, QSize
|
||||
from PyQt5.QtCore import Qt, QMimeData, QByteArray
|
||||
from PyQt5.QtGui import QStandardItem, QBrush, QFontMetrics
|
||||
from PyQt5.QtGui import QStandardItemModel, QColor
|
||||
from PyQt5.QtWidgets import QMenu, QAction, qApp
|
||||
|
||||
from manuskript.enums import World
|
||||
from manuskript.enums import World, Model
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.ui import style as S
|
||||
from manuskript.models.searchableModel import searchableModel
|
||||
from manuskript.models.searchableItem import searchableItem
|
||||
from manuskript.searchLabels import WorldSearchLabels
|
||||
|
||||
|
||||
class worldModel(QStandardItemModel):
|
||||
class worldModel(QStandardItemModel, searchableModel):
|
||||
def __init__(self, parent):
|
||||
QStandardItemModel.__init__(self, 0, len(World), parent)
|
||||
self.mw = mainWindow()
|
||||
|
@ -136,6 +138,9 @@ class worldModel(QStandardItemModel):
|
|||
_id = QStandardItem(self.getUniqueID())
|
||||
row = [name, _id] + [QStandardItem() for i in range(2, len(World))]
|
||||
parent.appendRow(row)
|
||||
|
||||
self.mw.treeWorld.setExpanded(self.selectedIndex(), True)
|
||||
self.mw.treeWorld.setCurrentIndex(self.indexFromItem(name))
|
||||
return name
|
||||
|
||||
def getUniqueID(self):
|
||||
|
@ -186,7 +191,7 @@ class worldModel(QStandardItemModel):
|
|||
for index in indexes:
|
||||
item = self.itemFromIndex(index)
|
||||
parent = item.parent()
|
||||
if parent is None:
|
||||
if parent == None:
|
||||
parent = self.invisibleRootItem()
|
||||
row_indexes.append((parent, item.row()))
|
||||
|
||||
|
@ -353,3 +358,51 @@ class worldModel(QStandardItemModel):
|
|||
return QSize(0, h + 6)
|
||||
|
||||
return QStandardItemModel.data(self, index, role)
|
||||
|
||||
#######################################################################
|
||||
# Search
|
||||
#######################################################################
|
||||
def searchableItems(self):
|
||||
def readAll(item):
|
||||
items = [WorldItemSearchWrapper(item, self.itemID(item), self.indexFromItem(item), self.data)]
|
||||
|
||||
for c in self.children(item):
|
||||
items += readAll(c)
|
||||
|
||||
return items
|
||||
|
||||
return readAll(self.invisibleRootItem())
|
||||
|
||||
class WorldItemSearchWrapper(searchableItem):
|
||||
def __init__(self, item, itemID, itemIndex, getColumnData):
|
||||
super().__init__(WorldSearchLabels)
|
||||
self.item = item
|
||||
self.itemID = itemID
|
||||
self.itemIndex = itemIndex
|
||||
self.getColumnData = getColumnData
|
||||
|
||||
def searchModel(self):
|
||||
return Model.World
|
||||
|
||||
def searchID(self):
|
||||
return self.itemID
|
||||
|
||||
def searchTitle(self, column):
|
||||
return self.item.text()
|
||||
|
||||
def searchPath(self, column):
|
||||
|
||||
def _path(item):
|
||||
path = []
|
||||
|
||||
if item.parent():
|
||||
path += _path(item.parent())
|
||||
path.append(item.text())
|
||||
|
||||
return path
|
||||
|
||||
return [self.translate("World")] + _path(self.item) + [self.translate(self.searchColumnLabel(column))]
|
||||
|
||||
def searchData(self, column):
|
||||
return self.getColumnData(self.itemIndex.sibling(self.itemIndex.row(), column))
|
||||
|
||||
|
|
56
manuskript/searchLabels.py
Normal file
56
manuskript/searchLabels.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.enums import Outline, Character, FlatData, World, Plot, PlotStep
|
||||
|
||||
OutlineSearchLabels = {
|
||||
Outline.title: "Title",
|
||||
Outline.text: "Text",
|
||||
Outline.summarySentence: "One sentence summary",
|
||||
Outline.summaryFull: "Summary",
|
||||
Outline.POV: "POV",
|
||||
Outline.notes: "Notes",
|
||||
Outline.status: "Status",
|
||||
Outline.label: "Label"
|
||||
}
|
||||
|
||||
CharacterSearchLabels = {
|
||||
Character.name: "Name",
|
||||
Character.motivation: "Motivation",
|
||||
Character.goal: "Goal",
|
||||
Character.conflict: "Conflict",
|
||||
Character.epiphany: "Epiphany",
|
||||
Character.summarySentence: "One sentence summary",
|
||||
Character.summaryPara: "One paragraph summary",
|
||||
Character.summaryFull: "Summary",
|
||||
Character.notes: "Notes",
|
||||
Character.infos: "Detailed info"
|
||||
}
|
||||
|
||||
FlatDataSearchLabels = {
|
||||
FlatData.summarySituation: "Situation",
|
||||
FlatData.summarySentence: "One sentence summary",
|
||||
FlatData.summaryPara: "One paragraph summary",
|
||||
FlatData.summaryPage: "One page summary",
|
||||
FlatData.summaryFull: "Full summary"
|
||||
}
|
||||
|
||||
WorldSearchLabels = {
|
||||
World.name: "Name",
|
||||
World.description: "Description",
|
||||
World.passion: "Passion",
|
||||
World.conflict: "Conflict"
|
||||
}
|
||||
|
||||
# Search menu includes one single option for both plot and plotStep models. For plotStep related fields
|
||||
# (like PlotStep.meta) we add an offset so it is not confused with the Plot enum value mapping to the same integer.
|
||||
PLOT_STEP_COLUMNS_OFFSET = 30
|
||||
|
||||
PlotSearchLabels = {
|
||||
Plot.name: "Name",
|
||||
Plot.description: "Description",
|
||||
Plot.characters: "Characters",
|
||||
Plot.result: "Result",
|
||||
Plot.summary: "Summary",
|
||||
PLOT_STEP_COLUMNS_OFFSET + PlotStep.meta: "Meta"
|
||||
}
|
|
@ -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"]
|
||||
|
|
|
@ -59,11 +59,16 @@ class settingsWindow(QWidget, Ui_Settings):
|
|||
self.lstMenu.setMaximumWidth(140)
|
||||
self.lstMenu.setMinimumWidth(140)
|
||||
|
||||
lowerKeys = [i.lower() for i in list(QStyleFactory.keys())]
|
||||
|
||||
# General
|
||||
self.cmbStyle.addItems(list(QStyleFactory.keys()))
|
||||
self.cmbStyle.setCurrentIndex(
|
||||
[i.lower() for i in list(QStyleFactory.keys())]
|
||||
.index(qApp.style().objectName()))
|
||||
|
||||
try:
|
||||
self.cmbStyle.setCurrentIndex(lowerKeys.index(qApp.style().objectName()))
|
||||
except ValueError:
|
||||
self.cmbStyle.setCurrentIndex(0)
|
||||
|
||||
self.cmbStyle.currentIndexChanged[str].connect(self.setStyle)
|
||||
|
||||
self.cmbTranslation.clear()
|
||||
|
@ -111,6 +116,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 +172,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 +190,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 +351,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 +445,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 +465,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)
|
||||
|
|
|
@ -12,7 +12,7 @@ def MW():
|
|||
"""
|
||||
from manuskript import functions as F
|
||||
MW = F.mainWindow()
|
||||
assert MW is not None
|
||||
assert MW != None
|
||||
assert MW == F.MW
|
||||
|
||||
return MW
|
||||
|
@ -23,7 +23,7 @@ def MWNoProject(MW):
|
|||
Take the MainWindow and close andy possibly open project.
|
||||
"""
|
||||
MW.closeProject()
|
||||
assert MW.currentProject is None
|
||||
assert MW.currentProject == None
|
||||
return MW
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -35,9 +35,9 @@ def MWEmptyProject(MW):
|
|||
tf = tempfile.NamedTemporaryFile(suffix=".msk")
|
||||
|
||||
MW.closeProject()
|
||||
assert MW.currentProject is None
|
||||
assert MW.currentProject == None
|
||||
MW.welcome.createFile(tf.name, overwrite=True)
|
||||
assert MW.currentProject is not None
|
||||
assert MW.currentProject != None
|
||||
return MW
|
||||
|
||||
# If using with: @pytest.fixture(scope='session', autouse=True)
|
||||
|
@ -67,6 +67,6 @@ def MWSampleProject(MW):
|
|||
shutil.copyfile(src, tf.name)
|
||||
shutil.copytree(src[:-4], tf.name[:-4])
|
||||
MW.loadProject(tf.name)
|
||||
assert MW.currentProject is not None
|
||||
assert MW.currentProject != None
|
||||
|
||||
return MW
|
||||
|
|
|
@ -130,9 +130,9 @@ def test_modelStuff(outlineModelBasic):
|
|||
folder.setModel(k)
|
||||
assert folder.columnCount() == len(folder.enum)
|
||||
text1 = text2.copy()
|
||||
assert text1.ID() is None
|
||||
assert text1.ID() == None
|
||||
folder.appendChild(text1)
|
||||
assert text1.ID() is not None
|
||||
assert text1.ID() != None
|
||||
assert folder.childCountRecursive() == 2
|
||||
assert text1.path() == "Folder > Text"
|
||||
assert len(text1.pathID()) == 2
|
||||
|
|
|
@ -39,7 +39,7 @@ def test_references(MWSampleProject):
|
|||
assert "\n" in Ref.infos(Ref.plotReference(plotID))
|
||||
assert "Not a ref" in Ref.infos("<invalid>")
|
||||
assert "Unknown" in Ref.infos(Ref.plotReference("999"))
|
||||
assert Ref.shortInfos(Ref.plotReference(plotID)) is not None
|
||||
assert Ref.shortInfos(Ref.plotReference(plotID)) != None
|
||||
assert Ref.shortInfos(Ref.plotReference("999")) == None
|
||||
assert Ref.shortInfos("<invalidref>") == -1
|
||||
|
||||
|
@ -50,7 +50,7 @@ def test_references(MWSampleProject):
|
|||
charID = IDs[0]
|
||||
assert "\n" in Ref.infos(Ref.characterReference(charID))
|
||||
assert "Unknown" in Ref.infos(Ref.characterReference("999"))
|
||||
assert Ref.shortInfos(Ref.characterReference(charID)) is not None
|
||||
assert Ref.shortInfos(Ref.characterReference(charID)) != None
|
||||
assert Ref.shortInfos(Ref.characterReference("999")) == None
|
||||
assert Ref.shortInfos("<invalidref>") == -1
|
||||
|
||||
|
@ -62,7 +62,7 @@ def test_references(MWSampleProject):
|
|||
|
||||
assert "\n" in Ref.infos(Ref.textReference(textID))
|
||||
assert "Unknown" in Ref.infos(Ref.textReference("999"))
|
||||
assert Ref.shortInfos(Ref.textReference(textID)) is not None
|
||||
assert Ref.shortInfos(Ref.textReference(textID)) != None
|
||||
assert Ref.shortInfos(Ref.textReference("999")) == None
|
||||
assert Ref.shortInfos("<invalidref>") == -1
|
||||
|
||||
|
@ -73,7 +73,7 @@ def test_references(MWSampleProject):
|
|||
|
||||
assert "\n" in Ref.infos(Ref.worldReference(worldID))
|
||||
assert "Unknown" in Ref.infos(Ref.worldReference("999"))
|
||||
assert Ref.shortInfos(Ref.worldReference(worldID)) is not None
|
||||
assert Ref.shortInfos(Ref.worldReference(worldID)) != None
|
||||
assert Ref.shortInfos(Ref.worldReference("999")) == None
|
||||
assert Ref.shortInfos("<invalidref>") == -1
|
||||
|
||||
|
@ -84,9 +84,9 @@ def test_references(MWSampleProject):
|
|||
|
||||
# Titles
|
||||
for ref in refs:
|
||||
assert Ref.title(ref) is not None
|
||||
assert Ref.title("<invalid>") is None
|
||||
assert Ref.title(Ref.plotReference("999")) is None
|
||||
assert Ref.title(ref) != None
|
||||
assert Ref.title("<invalid>") == None
|
||||
assert Ref.title(Ref.plotReference("999")) == None
|
||||
|
||||
# Other stuff
|
||||
assert Ref.type(Ref.plotReference(plotID)) == Ref.PlotLetter
|
||||
|
@ -94,10 +94,10 @@ def test_references(MWSampleProject):
|
|||
assert "Unknown" in Ref.tooltip(Ref.worldReference("999"))
|
||||
assert "Not a ref" in Ref.tooltip("<invalid>")
|
||||
for ref in refs:
|
||||
assert Ref.tooltip(ref) is not None
|
||||
assert Ref.tooltip(ref) != None
|
||||
|
||||
# Links
|
||||
assert Ref.refToLink("<invalid>") is None
|
||||
assert Ref.refToLink("<invalid>") == None
|
||||
assert Ref.refToLink(Ref.plotReference("999")) == Ref.plotReference("999")
|
||||
assert Ref.refToLink(Ref.characterReference("999")) == Ref.characterReference("999")
|
||||
assert Ref.refToLink(Ref.textReference("999")) == Ref.textReference("999")
|
||||
|
@ -106,7 +106,7 @@ def test_references(MWSampleProject):
|
|||
assert "<a href" in Ref.refToLink(ref)
|
||||
|
||||
# Open
|
||||
assert Ref.open("<invalid>") is None
|
||||
assert Ref.open("<invalid>") == None
|
||||
assert Ref.open(Ref.plotReference("999")) == False
|
||||
assert Ref.open(Ref.characterReference("999")) == False
|
||||
assert Ref.open(Ref.textReference("999")) == False
|
||||
|
|
41
manuskript/tests/models/test_searchFilter.py
Normal file
41
manuskript/tests/models/test_searchFilter.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
import pytest
|
||||
from manuskript.models.searchFilter import searchFilter
|
||||
|
||||
|
||||
def test_searchFilter_constructionOk():
|
||||
filter = searchFilter("label", True, [3])
|
||||
assert filter.label() == "label"
|
||||
assert filter.enabled() is True
|
||||
assert filter.modelColumns() == [3]
|
||||
|
||||
|
||||
def test_searchFilter_constructionOkWithNoneModelColumn():
|
||||
filter = searchFilter("label", True)
|
||||
assert filter.label() == "label"
|
||||
assert filter.enabled() is True
|
||||
assert filter.modelColumns() == []
|
||||
|
||||
|
||||
def test_searchFilter_constructionBadLabelType():
|
||||
with pytest.raises(TypeError, match=r".*label must be a str.*"):
|
||||
searchFilter(13, True, [3])
|
||||
|
||||
|
||||
def test_searchFilter_constructionBadEnabledType():
|
||||
with pytest.raises(TypeError, match=r".*enabled must be a bool.*"):
|
||||
searchFilter("label", 3, [3])
|
||||
|
||||
|
||||
def test_searchFilter_constructionBadModelColumnType():
|
||||
with pytest.raises(TypeError, match=r".*modelColumns must be a list or None.*"):
|
||||
searchFilter("label", False, True)
|
||||
|
||||
|
||||
def test_searchFilter_setEnabled():
|
||||
filter = searchFilter("label", True, [3])
|
||||
assert filter.enabled() is True
|
||||
filter.setEnabled(False)
|
||||
assert filter.enabled() is False
|
16
manuskript/tests/models/test_searchResultModel.py
Normal file
16
manuskript/tests/models/test_searchResultModel.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.models.searchResultModel import searchResultModel
|
||||
from manuskript.enums import Character
|
||||
|
||||
|
||||
def test_searchResultModel_constructionOk():
|
||||
searchResult = searchResultModel("Character", "3", Character.notes, "Lucas", "A > B > C", (15, 18), "This is <b>Lucas</b>")
|
||||
assert searchResult.id() == "3"
|
||||
assert searchResult.column() == Character.notes
|
||||
assert searchResult.title() == "Lucas"
|
||||
assert searchResult.path() == "A > B > C"
|
||||
assert searchResult.pos() == (15, 18)
|
||||
assert searchResult.context() == "This is <b>Lucas</b>"
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
"""Tests for functions"""
|
||||
|
||||
import re
|
||||
from manuskript import functions as F
|
||||
|
||||
def test_wordCount():
|
||||
|
@ -46,8 +47,8 @@ def test_several():
|
|||
assert F.iconColor(icon).name().lower() == "#ff0000"
|
||||
|
||||
# themeIcon
|
||||
assert F.themeIcon("text") is not None
|
||||
assert F.themeIcon("nonexistingname") is not None
|
||||
assert F.themeIcon("text") != None
|
||||
assert F.themeIcon("nonexistingname") != None
|
||||
|
||||
# randomColor
|
||||
c1 = F.randomColor()
|
||||
|
@ -75,10 +76,10 @@ def test_outlineItemColors():
|
|||
|
||||
def test_paths():
|
||||
|
||||
assert F.appPath() is not None
|
||||
assert F.writablePath is not None
|
||||
assert F.appPath() != None
|
||||
assert F.writablePath != None
|
||||
assert len(F.allPaths("suffix")) == 2
|
||||
assert F.tempFile("yop") is not None
|
||||
assert F.tempFile("yop") != None
|
||||
f = F.findBackground("spacedreams.jpg")
|
||||
assert "resources/backgrounds/spacedreams.jpg" in f
|
||||
assert len(F.customIcons()) > 1
|
||||
|
@ -87,10 +88,59 @@ def test_mainWindow():
|
|||
|
||||
from PyQt5.QtWidgets import QWidget, QLCDNumber
|
||||
|
||||
assert F.mainWindow() is not None
|
||||
assert F.MW is not None
|
||||
assert F.mainWindow() != None
|
||||
assert F.MW != None
|
||||
|
||||
F.statusMessage("Test")
|
||||
F.printObjects()
|
||||
assert len(F.findWidgetsOfClass(QWidget)) > 0
|
||||
assert len(F.findWidgetsOfClass(QLCDNumber)) == 0
|
||||
|
||||
|
||||
def test_search_noMatch():
|
||||
assert F.search(re.compile("text"), "foo") == []
|
||||
|
||||
|
||||
def test_search_singleLine_fullMatch():
|
||||
assert F.search(re.compile("text"), "text") == [(0, 4, "<b>text</b>")]
|
||||
|
||||
|
||||
def test_search_singleLine_start():
|
||||
assert F.search(re.compile("text"), "text is this") == [(0, 4, "<b>text</b> is this")]
|
||||
|
||||
|
||||
def test_search_singleLine_end():
|
||||
assert F.search(re.compile("text"), "This is text") == [(8, 12, "This is <b>text</b>")]
|
||||
|
||||
|
||||
def test_search_multipleLines_fullMatch():
|
||||
assert F.search(re.compile("text"), "This is\ntext\nOK") == [(8, 12, "[...] <b>text</b> [...]")]
|
||||
|
||||
|
||||
def test_search_multipleLines_start():
|
||||
assert F.search(re.compile("text"), "This is\ntext oh yeah\nOK") == [(8, 12, "[...] <b>text</b> oh yeah [...]")]
|
||||
|
||||
|
||||
def test_search_multipleLines_end():
|
||||
assert F.search(re.compile("text"), "This is\nsome text\nOK") == [(13, 17, "[...] some <b>text</b> [...]")]
|
||||
|
||||
def test_search_multipleLines_full():
|
||||
assert F.search(re.compile("text"), "This is\ntext\nOK") == [(8, 12, "[...] <b>text</b> [...]")]
|
||||
|
||||
|
||||
def test_search_multiple_strMatches():
|
||||
assert F.search(re.compile("text"), "text, text and more text") == [
|
||||
(0, 4, "<b>text</b>, text and more text"),
|
||||
(6, 10, "text, <b>text</b> and more text"),
|
||||
(20, 24, "text, text and more <b>text</b>")
|
||||
]
|
||||
|
||||
|
||||
def test_search_multiple_strMatches_caseSensitive():
|
||||
assert F.search(re.compile("text"), "TeXt, TEXT and more text") == [(20, 24, "TeXt, TEXT and more <b>text</b>")]
|
||||
|
||||
assert F.search(re.compile("text", re.IGNORECASE), "TeXt, TEXT and more text") == [
|
||||
(0, 4, "<b>TeXt</b>, TEXT and more text"),
|
||||
(6, 10, "TeXt, <b>TEXT</b> and more text"),
|
||||
(20, 24, "TeXt, TEXT and more <b>text</b>")
|
||||
]
|
|
@ -55,7 +55,7 @@ def test_general(MWSampleProject):
|
|||
state = settings()
|
||||
assert chk.isChecked() == state
|
||||
chk.setChecked(not state)
|
||||
assert chk.isChecked() is not state
|
||||
assert chk.isChecked() != state
|
||||
|
||||
# Loading and Saving
|
||||
SW.txtAutoSave.setText("0")
|
||||
|
@ -86,7 +86,7 @@ def test_general(MWSampleProject):
|
|||
SW.chkOutlineTitle.setChecked(Qt.Unchecked)
|
||||
SW.chkOutlineTitle.setChecked(Qt.Checked)
|
||||
# Can't test because of the dialog
|
||||
# assert SW.setCorkColor() is None
|
||||
# assert SW.setCorkColor() == None
|
||||
SW.sldTreeIconSize.setValue(SW.sldTreeIconSize.value() + 1)
|
||||
SW.rdoCorkNewStyle.toggled.emit(True)
|
||||
SW.cmbCorkImage.currentIndexChanged.emit(0)
|
||||
|
@ -98,7 +98,7 @@ def test_general(MWSampleProject):
|
|||
# Test editor
|
||||
switchCheckBoxAndAssert(SW.chkEditorBackgroundTransparent,
|
||||
lambda: S.textEditor["backgroundTransparent"])
|
||||
assert SW.restoreEditorColors() is None
|
||||
assert SW.restoreEditorColors() == None
|
||||
switchCheckBoxAndAssert(SW.chkEditorNoBlinking,
|
||||
lambda: S.textEditor["cursorNotBlinking"])
|
||||
# Twice on purpose: set and restore
|
||||
|
@ -108,7 +108,7 @@ def test_general(MWSampleProject):
|
|||
SW.updateAllWidgets()
|
||||
|
||||
# Labels
|
||||
assert SW.updateLabelColor(MW.mdlLabels.item(1).index()) is None
|
||||
assert SW.updateLabelColor(MW.mdlLabels.item(1).index()) == None
|
||||
rc = MW.mdlLabels.rowCount()
|
||||
SW.addLabel()
|
||||
SW.lstLabels.setCurrentIndex(
|
||||
|
@ -150,7 +150,7 @@ def test_general(MWSampleProject):
|
|||
for i in range(4):
|
||||
SW.updateLineSpacing(i)
|
||||
SW.updateUIFromTheme() # No time to wait on timer
|
||||
assert SW._editingTheme is not None
|
||||
assert SW._editingTheme != None
|
||||
SW.resize(SW.geometry().size()) # resizeEvent
|
||||
#TODO: other edit test (see SW.loadTheme
|
||||
SW.saveTheme()
|
||||
|
|
56
manuskript/tests/ui/test_searchMenu.py
Normal file
56
manuskript/tests/ui/test_searchMenu.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.ui.searchMenu import searchMenu
|
||||
from manuskript.enums import Outline, Character, FlatData, World, Plot, PlotStep, Model
|
||||
from manuskript.searchLabels import PLOT_STEP_COLUMNS_OFFSET
|
||||
|
||||
|
||||
def triggerFilter(filterKey, actions):
|
||||
list(filter(lambda action: action.data() == filterKey, actions))[0].trigger()
|
||||
|
||||
|
||||
def test_searchMenu_defaultColumns():
|
||||
"""
|
||||
By default all model columns are selected.
|
||||
"""
|
||||
search_menu = searchMenu()
|
||||
|
||||
assert set(search_menu.columns(Model.Outline)) == {
|
||||
Outline.title, Outline.text, Outline.summaryFull,
|
||||
Outline.summarySentence, Outline.notes, Outline.POV,
|
||||
Outline.status, Outline.label
|
||||
}
|
||||
|
||||
assert set(search_menu.columns(Model.Character)) == {
|
||||
Character.name, Character.motivation, Character.goal, Character.conflict,
|
||||
Character.epiphany, Character.summarySentence, Character.summaryPara,
|
||||
Character.summaryFull, Character.notes, Character.infos
|
||||
}
|
||||
|
||||
assert set(search_menu.columns(Model.FlatData)) == {
|
||||
FlatData.summarySituation, FlatData.summarySentence, FlatData.summaryPara,
|
||||
FlatData.summaryPage, FlatData.summaryFull
|
||||
}
|
||||
|
||||
assert set(search_menu.columns(Model.World)) == {
|
||||
World.name, World.description, World.passion, World.conflict
|
||||
}
|
||||
|
||||
assert set(search_menu.columns(Model.Plot)) == {
|
||||
Plot.name, Plot.description, Plot.characters, Plot.result,
|
||||
Plot.summary, PLOT_STEP_COLUMNS_OFFSET + PlotStep.meta
|
||||
}
|
||||
|
||||
|
||||
def test_searchMenu_someColumns():
|
||||
"""
|
||||
When deselecting some filters the columns associated to those filters are not returned.
|
||||
"""
|
||||
search_menu = searchMenu()
|
||||
|
||||
triggerFilter(Model.Outline, search_menu.actions())
|
||||
triggerFilter(Model.Character, search_menu.actions())
|
||||
|
||||
assert set(search_menu.columns(Model.Outline)) == set()
|
||||
assert set(search_menu.columns(Model.Character)) == set()
|
|
@ -96,7 +96,7 @@ class collapsibleDockWidgets(QToolBar):
|
|||
def setCurrentGroup(self, group):
|
||||
self.currentGroup = group
|
||||
for btn, action, widget, grp in self.otherWidgets:
|
||||
if not grp == group or grp is None:
|
||||
if not grp == group or grp == None:
|
||||
action.setVisible(False)
|
||||
else:
|
||||
action.setVisible(True)
|
||||
|
|
|
@ -8,7 +8,7 @@ class blockUserData(QTextBlockUserData):
|
|||
def getUserData(block):
|
||||
"""Returns userData if it exists, or a blank one."""
|
||||
data = block.userData()
|
||||
if data is None:
|
||||
if data == None:
|
||||
data = blockUserData()
|
||||
return data
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from PyQt5.QtCore import Qt, QSize, QPoint, QRect, QEvent, QTime, QTimer
|
|||
from PyQt5.QtGui import QFontMetrics, QColor, QBrush, QPalette, QPainter, QPixmap, QCursor
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QFrame, QWidget, QPushButton, qApp, QStyle, QComboBox, QLabel, QScrollBar, \
|
||||
QStyleOptionSlider, QHBoxLayout, QVBoxLayout, QMenu, QAction
|
||||
QStyleOptionSlider, QHBoxLayout, QVBoxLayout, QMenu, QAction, QDesktopWidget
|
||||
|
||||
# Spell checker support
|
||||
from manuskript import settings
|
||||
|
@ -21,7 +21,7 @@ from manuskript.functions import Spellchecker
|
|||
|
||||
|
||||
class fullScreenEditor(QWidget):
|
||||
def __init__(self, index, parent=None):
|
||||
def __init__(self, index, parent=None, screenNumber=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.setAttribute(Qt.WA_DeleteOnClose, True)
|
||||
self._background = None
|
||||
|
@ -162,6 +162,12 @@ class fullScreenEditor(QWidget):
|
|||
self.topPanel.setAutoHideVariable('autohide-top')
|
||||
self.leftPanel.setAutoHideVariable('autohide-left')
|
||||
|
||||
# Set the screen to the same screen as the main window
|
||||
if screenNumber is not None:
|
||||
screenres = QDesktopWidget().screenGeometry(screenNumber);
|
||||
self.move(QPoint(screenres.x(), screenres.y()));
|
||||
self.resize(screenres.width(), screenres.height());
|
||||
|
||||
# Connection
|
||||
self._index.model().dataChanged.connect(self.dataChanged)
|
||||
|
||||
|
@ -338,8 +344,8 @@ class fullScreenEditor(QWidget):
|
|||
item = self._index.internalPointer()
|
||||
previousItem = self.previousTextItem(item)
|
||||
nextItem = self.nextTextItem(item)
|
||||
self.btnPrevious.setEnabled(previousItem is not None)
|
||||
self.btnNext.setEnabled(nextItem is not None)
|
||||
self.btnPrevious.setEnabled(previousItem != None)
|
||||
self.btnNext.setEnabled(nextItem != None)
|
||||
self.wPath.setItem(item)
|
||||
|
||||
def updateStatusBar(self):
|
||||
|
@ -572,11 +578,11 @@ class myPanel(QWidget):
|
|||
def addWidgetSetting(self, label, config_name, widgets):
|
||||
setting = (label, config_name, widgets)
|
||||
self._settings.append(setting)
|
||||
if settings.fullscreenSettings.get(config_name, None) is not None:
|
||||
if settings.fullscreenSettings.get(config_name, None) != None:
|
||||
self._setSettingValue(setting, settings.fullscreenSettings[config_name])
|
||||
|
||||
def addSetting(self, label, config_name, default=True):
|
||||
if settings.fullscreenSettings.get(config_name, None) is None:
|
||||
if settings.fullscreenSettings.get(config_name, None) == None:
|
||||
self._setConfig(config_name, default)
|
||||
self.addWidgetSetting(label, config_name, None)
|
||||
|
||||
|
@ -651,7 +657,7 @@ class myPath(QWidget):
|
|||
if i == item:
|
||||
a.setIcon(QIcon.fromTheme("stock_yes"))
|
||||
a.setEnabled(False)
|
||||
elif self.editor.firstTextItem(i) is None:
|
||||
elif self.editor.firstTextItem(i) == None:
|
||||
a.setEnabled(False)
|
||||
else:
|
||||
a.triggered.connect(gen_cb(i))
|
||||
|
|
|
@ -105,6 +105,6 @@ class locker(QWidget, Ui_locker):
|
|||
text))
|
||||
|
||||
# Word locked
|
||||
elif self._target is not None:
|
||||
elif self._target != None:
|
||||
self.btnLock.setText(self.tr("{} words remaining").format(
|
||||
self._target - self._words))
|
||||
|
|
|
@ -5,7 +5,7 @@ import locale
|
|||
from PyQt5.QtCore import QModelIndex, QRect, QPoint
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QPixmap, QPainter, QIcon
|
||||
from PyQt5.QtWidgets import QWidget, qApp
|
||||
from PyQt5.QtWidgets import QWidget, qApp, QDesktopWidget
|
||||
|
||||
from manuskript import settings
|
||||
from manuskript.enums import Outline
|
||||
|
@ -120,7 +120,7 @@ class mainEditor(QWidget, Ui_mainEditor):
|
|||
return self.tabSplitter.tab
|
||||
|
||||
def currentEditor(self, tabWidget=None):
|
||||
if tabWidget is None:
|
||||
if tabWidget == None:
|
||||
tabWidget = self.currentTabWidget()
|
||||
return tabWidget.currentWidget()
|
||||
# return self.tab.currentWidget()
|
||||
|
@ -153,7 +153,7 @@ class mainEditor(QWidget, Ui_mainEditor):
|
|||
|
||||
def allTabs(self, tabWidget=None):
|
||||
"""Returns all the tabs from the given tabWidget. If tabWidget is None, from the current tabWidget."""
|
||||
if tabWidget is None:
|
||||
if tabWidget == None:
|
||||
tabWidget = self.currentTabWidget()
|
||||
return [tabWidget.widget(i) for i in range(tabWidget.count())]
|
||||
|
||||
|
@ -205,7 +205,7 @@ class mainEditor(QWidget, Ui_mainEditor):
|
|||
|
||||
title = self.getIndexTitle(index)
|
||||
|
||||
if tabWidget is None:
|
||||
if tabWidget == None:
|
||||
tabWidget = self.currentTabWidget()
|
||||
|
||||
# Checking if tab is already opened
|
||||
|
@ -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
|
||||
|
@ -354,7 +379,10 @@ class mainEditor(QWidget, Ui_mainEditor):
|
|||
|
||||
def showFullScreen(self):
|
||||
if self.currentEditor():
|
||||
self._fullScreen = fullScreenEditor(self.currentEditor().currentIndex)
|
||||
currentScreenNumber = QDesktopWidget().screenNumber(widget=self)
|
||||
self._fullScreen = fullScreenEditor(
|
||||
self.currentEditor().currentIndex,
|
||||
screenNumber=currentScreenNumber)
|
||||
|
||||
###############################################################################
|
||||
# DICT AND STUFF LIKE THAT
|
||||
|
|
|
@ -145,8 +145,8 @@ class tabSplitter(QWidget, Ui_tabSplitter):
|
|||
|
||||
def split(self, toggled=None, state=None):
|
||||
|
||||
if state is None and self.splitState == 0 or state == 1:
|
||||
if self.secondTab is None:
|
||||
if state == None and self.splitState == 0 or state == 1:
|
||||
if self.secondTab == None:
|
||||
self.addSecondTab()
|
||||
|
||||
self.splitState = 1
|
||||
|
@ -155,8 +155,8 @@ class tabSplitter(QWidget, Ui_tabSplitter):
|
|||
self.btnSplit.setIcon(QIcon.fromTheme("split-vertical"))
|
||||
self.btnSplit.setToolTip(self.tr("Split horizontally"))
|
||||
|
||||
elif state is None and self.splitState == 1 or state == 2:
|
||||
if self.secondTab is None:
|
||||
elif state == None and self.splitState == 1 or state == 2:
|
||||
if self.secondTab == None:
|
||||
self.addSecondTab()
|
||||
|
||||
self.splitter.setOrientation(Qt.Vertical)
|
||||
|
@ -212,7 +212,7 @@ class tabSplitter(QWidget, Ui_tabSplitter):
|
|||
# self.btnSplit.setGeometry(QRect(0, 0, 24, 24))
|
||||
|
||||
def focusChanged(self, old, new):
|
||||
if self.secondTab is None or new is None:
|
||||
if self.secondTab == None or new == None:
|
||||
return
|
||||
|
||||
oldFT = self.focusTab
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -713,7 +713,7 @@ class MarkdownHighlighter(BasicHighlighter):
|
|||
|
||||
# FIXME: TypeError: could not convert 'TextBlockData' to 'QTextBlockUserData'
|
||||
# blockData = self.currentBlockUserData()
|
||||
# if blockData is None:
|
||||
# if blockData == None:
|
||||
# blockData = TextBlockData(self.document(), self.currentBlock())
|
||||
#
|
||||
# self.setCurrentBlockUserData(blockData)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
class abstractSearchResultHighlighter():
|
||||
"""
|
||||
Interface for all classes highlighting search results on widgets.
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def highlightSearchResult(self, searchResult):
|
||||
raise NotImplementedError
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.widgetSelectionHighlighter import widgetSelectionHighlighter
|
||||
|
||||
|
||||
class abstractSearchResultHighlighter():
|
||||
def __init__(self):
|
||||
self._widgetSelectionHighlighter = widgetSelectionHighlighter()
|
||||
|
||||
def highlightSearchResult(self, searchResult):
|
||||
self.openView(searchResult)
|
||||
widgets = self.retrieveWidget(searchResult)
|
||||
if not isinstance(widgets, list):
|
||||
widgets = [widgets]
|
||||
for i in range(len(widgets)):
|
||||
self._widgetSelectionHighlighter.highlight_widget_selection(widgets[i], searchResult.pos()[i][0], searchResult.pos()[i][1], i == len(widgets) - 1)
|
||||
|
||||
def openView(self, searchResult):
|
||||
raise RuntimeError
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
raise RuntimeError
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from manuskript.models import references as Ref
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.enums import Character
|
||||
from PyQt5.QtWidgets import QTextEdit, QTableView, QLineEdit
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSpecificSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
|
||||
|
||||
class characterSearchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def openView(self, searchResult):
|
||||
r = Ref.characterReference(searchResult.id())
|
||||
Ref.open(r)
|
||||
mainWindow().tabPersos.setEnabled(True)
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
textEditMap = {
|
||||
Character.name: (0, "txtPersoName", QLineEdit),
|
||||
Character.goal: (0, "txtPersoGoal", QTextEdit),
|
||||
Character.motivation: (0, "txtPersoMotivation", QTextEdit),
|
||||
Character.conflict: (0, "txtPersoConflict", QTextEdit),
|
||||
Character.epiphany: (0, "txtPersoEpiphany", QTextEdit),
|
||||
Character.summarySentence: (0, "txtPersoSummarySentence", QTextEdit),
|
||||
Character.summaryPara: (0, "txtPersoSummaryPara", QTextEdit),
|
||||
Character.summaryFull: (1, "txtPersoSummaryFull", QTextEdit),
|
||||
Character.notes: (2, "txtPersoNotes", QTextEdit),
|
||||
Character.infos: (3, "tblPersoInfos", QTableView)
|
||||
}
|
||||
|
||||
characterTabIndex, characterWidgetName, characterWidgetClass = textEditMap[searchResult.column()]
|
||||
|
||||
mainWindow().tabPersos.setCurrentIndex(characterTabIndex)
|
||||
return mainWindow().tabPersos.findChild(characterWidgetClass, characterWidgetName)
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.enums import FlatData
|
||||
from PyQt5.QtWidgets import QTextEdit, QLineEdit
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSpecificSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
|
||||
|
||||
class flatDataSearchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def openView(self, searchResult):
|
||||
mainWindow().tabMain.setCurrentIndex(mainWindow().TabSummary)
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
editors = {
|
||||
FlatData.summarySituation: (0, "txtSummarySituation", QLineEdit, mainWindow()),
|
||||
FlatData.summarySentence: (0, "txtSummarySentence", QTextEdit, mainWindow().tabSummary),
|
||||
FlatData.summaryPara: (1, "txtSummaryPara", QTextEdit, mainWindow().tabSummary),
|
||||
FlatData.summaryPage: (2, "txtSummaryPage", QTextEdit, mainWindow().tabSummary),
|
||||
FlatData.summaryFull: (3, "txtSummaryFull", QTextEdit, mainWindow().tabSummary)
|
||||
}
|
||||
|
||||
stackIndex, editorName, editorClass, rootWidget = editors[searchResult.column()]
|
||||
|
||||
mainWindow().tabSummary.setCurrentIndex(stackIndex)
|
||||
return rootWidget.findChild(editorClass, editorName)
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.models import references as Ref
|
||||
from manuskript.enums import Outline
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSpecificSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
from manuskript.functions import mainWindow
|
||||
from PyQt5.QtWidgets import QTextEdit, QLineEdit, QLabel
|
||||
from manuskript.ui.views.metadataView import metadataView
|
||||
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
|
||||
|
||||
|
||||
class outlineSearchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.outline_index = None
|
||||
|
||||
def openView(self, searchResult):
|
||||
r = Ref.textReference(searchResult.id())
|
||||
Ref.open(r)
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
editors = {
|
||||
Outline.text: ("txtRedacText", QTextEdit, None),
|
||||
Outline.title: ("txtTitle", QLineEdit, "grpProperties"),
|
||||
Outline.summarySentence: ("txtSummarySentence", QLineEdit, "grpSummary"),
|
||||
Outline.summaryFull: ("txtSummaryFull", QTextEdit, "grpSummary"),
|
||||
Outline.notes: ("txtNotes", QTextEdit, "grpNotes"),
|
||||
|
||||
# TODO: Tried to highlight the combo box themselves (ie. cmbPOV) but didn't succeed.
|
||||
Outline.POV: ("lblPOV", QLabel, "grpProperties"),
|
||||
Outline.status: ("lblStatus", QLabel, "grpProperties"),
|
||||
Outline.label: ("lblLabel", QLabel, "grpProperties")
|
||||
}
|
||||
|
||||
editorName, editorClass, parentName = editors[searchResult.column()]
|
||||
|
||||
# Metadata columns are inside a splitter widget that my be hidden, so we show them.
|
||||
if parentName:
|
||||
metadataViewWidget = mainWindow().findChild(metadataView, "redacMetadata")
|
||||
metadataViewWidget.show()
|
||||
metadataViewWidget.findChild(collapsibleGroupBox2, parentName).button.setChecked(True)
|
||||
widget = metadataViewWidget.findChild(editorClass, editorName)
|
||||
else:
|
||||
widget = mainWindow().mainEditor.currentEditor().findChild(editorClass, editorName)
|
||||
|
||||
return widget
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from manuskript.models import references as Ref
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.enums import Plot
|
||||
from PyQt5.QtWidgets import QTextEdit, QLineEdit, QListView
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSpecificSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
|
||||
|
||||
class plotSearchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def openView(self, searchResult):
|
||||
r = Ref.plotReference(searchResult.id())
|
||||
Ref.open(r)
|
||||
mainWindow().tabPlot.setEnabled(True)
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
textEditMap = {
|
||||
Plot.name: (0, "txtPlotName", QLineEdit),
|
||||
Plot.description: (0, "txtPlotDescription", QTextEdit),
|
||||
Plot.characters: (0, "lstPlotPerso", QListView),
|
||||
Plot.result: (0, "txtPlotResult", QTextEdit)
|
||||
}
|
||||
|
||||
tabIndex, widgetName, widgetClass = textEditMap[searchResult.column()]
|
||||
|
||||
mainWindow().tabPlot.setCurrentIndex(tabIndex)
|
||||
return mainWindow().tabPlot.findChild(widgetClass, widgetName)
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from manuskript.models import references as Ref
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.enums import PlotStep
|
||||
from PyQt5.QtWidgets import QTableView, QTextEdit
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSpecificSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
|
||||
|
||||
class plotStepSearchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def openView(self, searchResult):
|
||||
r = Ref.plotReference(searchResult.id())
|
||||
Ref.open(r)
|
||||
mainWindow().tabPlot.setEnabled(True)
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
textEditMap = {
|
||||
PlotStep.name: [(1, "lstSubPlots", QTableView)],
|
||||
PlotStep.meta: [(1, "lstSubPlots", QTableView)],
|
||||
PlotStep.summary: [(1, "lstSubPlots", QTableView), (1, "txtSubPlotSummary", QTextEdit)]
|
||||
}
|
||||
|
||||
map = textEditMap[searchResult.column()]
|
||||
widgets = []
|
||||
for tabIndex, widgetName, widgetClass in map:
|
||||
mainWindow().tabPlot.setCurrentIndex(tabIndex)
|
||||
|
||||
widgets.append(mainWindow().tabPlot.findChild(widgetClass, widgetName))
|
||||
|
||||
return widgets
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.characterSearchResultHighlighter import characterSearchResultHighlighter
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.flatDataSearchResultHighlighter import flatDataSearchResultHighlighter
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.outlineSearchResultHighlighter import outlineSearchResultHighlighter
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.worldSearchResultHighlighter import worldSearchResultHighlighter
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.plotSearchResultHighlighter import plotSearchResultHighlighter
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.plotStepSearchResultHighlighter import plotStepSearchResultHighlighter
|
||||
from manuskript.enums import Model
|
||||
|
||||
|
||||
class searchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def highlightSearchResult(self, searchResult):
|
||||
if searchResult.type() == Model.Character:
|
||||
highlighter = characterSearchResultHighlighter()
|
||||
elif searchResult.type() == Model.FlatData:
|
||||
highlighter = flatDataSearchResultHighlighter()
|
||||
elif searchResult.type() == Model.Outline:
|
||||
highlighter = outlineSearchResultHighlighter()
|
||||
elif searchResult.type() == Model.World:
|
||||
highlighter = worldSearchResultHighlighter()
|
||||
elif searchResult.type() == Model.Plot:
|
||||
highlighter = plotSearchResultHighlighter()
|
||||
elif searchResult.type() == Model.PlotStep:
|
||||
highlighter = plotStepSearchResultHighlighter()
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
highlighter.highlightSearchResult(searchResult)
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
from PyQt5.QtWidgets import QTextEdit, QTableView, QListView, QLineEdit, QPlainTextEdit, QLabel
|
||||
|
||||
|
||||
class widgetSelectionHighlighter():
|
||||
"""
|
||||
Utility class for highlighting a search result on a widget.
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def highlight_widget_selection(self, widget, startPos, endPos, clearOnFocusOut=True):
|
||||
if isinstance(widget, QTextEdit) or isinstance(widget, QPlainTextEdit):
|
||||
self._highlightTextEditSearchResult(widget, startPos, endPos, clearOnFocusOut)
|
||||
elif isinstance(widget, QLineEdit):
|
||||
self._highlightLineEditSearchResult(widget, startPos, endPos, clearOnFocusOut)
|
||||
elif isinstance(widget, QTableView):
|
||||
self._highlightTableViewSearchResult(widget, startPos, clearOnFocusOut)
|
||||
elif isinstance(widget, QListView):
|
||||
self._highlightListViewSearchResult(widget, startPos, clearOnFocusOut)
|
||||
elif isinstance(widget, QLabel):
|
||||
self._highlightLabelSearchResult(widget, clearOnFocusOut)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
widget.setFocus(True)
|
||||
|
||||
@staticmethod
|
||||
def generateClearHandler(widget, clearCallback):
|
||||
"""
|
||||
Generates a clear handler to be run when the given widget loses focus.
|
||||
|
||||
:param widget: widget we want to attach the handler to
|
||||
:param clearCallback: callback to be called when the given widget loses focus.
|
||||
:return:
|
||||
"""
|
||||
def clearHandler(_widget, previous_on_focus_out_event):
|
||||
clearCallback(_widget)
|
||||
_widget.focusOutEvent = previous_on_focus_out_event
|
||||
|
||||
widget.focusOutEvent = lambda e: clearHandler(widget, widget.focusOutEvent)
|
||||
|
||||
def _highlightTextEditSearchResult(self, textEdit, startPos, endPos, clearOnFocusOut):
|
||||
# On focus out, clear text edit selection.
|
||||
oldTextCursor = textEdit.textCursor()
|
||||
if clearOnFocusOut:
|
||||
self.generateClearHandler(textEdit, lambda widget: widget.setTextCursor(oldTextCursor))
|
||||
|
||||
# Highlight search result on the text edit.
|
||||
c = textEdit.textCursor()
|
||||
c.setPosition(startPos)
|
||||
c.setPosition(endPos, QTextCursor.KeepAnchor)
|
||||
textEdit.setTextCursor(c)
|
||||
|
||||
def _highlightLineEditSearchResult(self, lineEdit, startPos, endPos, clearOnFocusOut):
|
||||
# On focus out, clear line edit selection.
|
||||
if clearOnFocusOut:
|
||||
self.generateClearHandler(lineEdit, lambda widget: widget.deselect())
|
||||
|
||||
# Highlight search result on line edit.
|
||||
lineEdit.setCursorPosition(startPos)
|
||||
lineEdit.cursorForward(True, endPos - startPos)
|
||||
|
||||
def _highlightTableViewSearchResult(self, tableView, startPos, clearOnFocusOut):
|
||||
# On focus out, clear table selection.
|
||||
if clearOnFocusOut:
|
||||
self.generateClearHandler(tableView, lambda widget: widget.clearSelection())
|
||||
|
||||
# Highlight table row containing search result.
|
||||
tableView.selectRow(startPos)
|
||||
|
||||
def _highlightListViewSearchResult(self, listView, startPos, clearOnFocusOut):
|
||||
# On focus out, clear table selection.
|
||||
if clearOnFocusOut:
|
||||
self.generateClearHandler(listView, lambda widget: widget.selectionModel().clearSelection())
|
||||
|
||||
# Highlight list item containing search result.
|
||||
listView.setCurrentIndex(listView.model().index(startPos, 0, listView.rootIndex()))
|
||||
|
||||
def _highlightLabelSearchResult(self, label, clearOnFocusOut):
|
||||
# On focus out, clear label selection.
|
||||
# FIXME: This would overwrite all styles!
|
||||
oldStyle = label.styleSheet()
|
||||
if clearOnFocusOut:
|
||||
self.generateClearHandler(label, lambda widget: widget.setStyleSheet(oldStyle))
|
||||
|
||||
# Highlight search result on label.
|
||||
label.setStyleSheet("background-color: steelblue")
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
from manuskript.models import references as Ref
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.enums import World
|
||||
from PyQt5.QtWidgets import QTextEdit, QLineEdit
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.abstractSpecificSearchResultHighlighter import abstractSearchResultHighlighter
|
||||
|
||||
|
||||
class worldSearchResultHighlighter(abstractSearchResultHighlighter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def openView(self, searchResult):
|
||||
r = Ref.worldReference(searchResult.id())
|
||||
Ref.open(r)
|
||||
mainWindow().tabWorld.setEnabled(True)
|
||||
|
||||
def retrieveWidget(self, searchResult):
|
||||
textEditMap = {
|
||||
World.name: (0, "txtWorldName", QLineEdit),
|
||||
World.description: (0, "txtWorldDescription", QTextEdit),
|
||||
World.passion: (1, "txtWorldPassion", QTextEdit),
|
||||
World.conflict: (1, "txtWorldConflict", QTextEdit),
|
||||
}
|
||||
|
||||
tabIndex, widgetName, widgetClass = textEditMap[searchResult.column()]
|
||||
|
||||
mainWindow().tabWorld.setCurrentIndex(tabIndex)
|
||||
return mainWindow().tabWorld.findChild(widgetClass, widgetName)
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/mainWindow.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.5.1
|
||||
# Created by: PyQt5 UI code generator 5.14.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
|
@ -378,69 +380,11 @@ class Ui_MainWindow(object):
|
|||
self.scrollAreaPersoInfos.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
|
||||
self.scrollAreaPersoInfos.setObjectName("scrollAreaPersoInfos")
|
||||
self.scrollAreaPersoInfosWidget = QtWidgets.QWidget()
|
||||
self.scrollAreaPersoInfosWidget.setGeometry(QtCore.QRect(0, 0, 204, 606))
|
||||
self.scrollAreaPersoInfosWidget.setGeometry(QtCore.QRect(0, 0, 453, 695))
|
||||
self.scrollAreaPersoInfosWidget.setObjectName("scrollAreaPersoInfosWidget")
|
||||
self.formLayout_8 = QtWidgets.QFormLayout(self.scrollAreaPersoInfosWidget)
|
||||
self.formLayout_8.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
|
||||
self.formLayout_8.setObjectName("formLayout_8")
|
||||
self.label_4 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
||||
self.txtPersoMotivation = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoMotivation.setObjectName("txtPersoMotivation")
|
||||
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.txtPersoMotivation)
|
||||
self.label_5 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
||||
self.txtPersoGoal = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoGoal.setObjectName("txtPersoGoal")
|
||||
self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.txtPersoGoal)
|
||||
self.label_6 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_6.setObjectName("label_6")
|
||||
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
||||
self.txtPersoConflict = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoConflict.setObjectName("txtPersoConflict")
|
||||
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtPersoConflict)
|
||||
self.label_7 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_7.setObjectName("label_7")
|
||||
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
||||
self.txtPersoEpiphany = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoEpiphany.setObjectName("txtPersoEpiphany")
|
||||
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.txtPersoEpiphany)
|
||||
self.label_24 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_24.setObjectName("label_24")
|
||||
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_24)
|
||||
self.txtPersoSummarySentence = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoSummarySentence.setObjectName("txtPersoSummarySentence")
|
||||
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummarySentence)
|
||||
self.label_8 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_8.setObjectName("label_8")
|
||||
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
||||
self.txtPersoSummaryPara = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoSummaryPara.setObjectName("txtPersoSummaryPara")
|
||||
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummaryPara)
|
||||
self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_21.setObjectName("horizontalLayout_21")
|
||||
spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_21.addItem(spacerItem10)
|
||||
self.btnStepFour = QtWidgets.QPushButton(self.scrollAreaPersoInfosWidget)
|
||||
icon = QtGui.QIcon.fromTheme("go-next")
|
||||
self.btnStepFour.setIcon(icon)
|
||||
self.btnStepFour.setFlat(True)
|
||||
self.btnStepFour.setObjectName("btnStepFour")
|
||||
self.horizontalLayout_21.addWidget(self.btnStepFour)
|
||||
self.formLayout_8.setLayout(10, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_21)
|
||||
self.label_18 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_18.setObjectName("label_18")
|
||||
self.formLayout_8.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_18)
|
||||
self.sldPersoImportance = sldImportance(self.scrollAreaPersoInfosWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.sldPersoImportance.sizePolicy().hasHeightForWidth())
|
||||
self.sldPersoImportance.setSizePolicy(sizePolicy)
|
||||
self.sldPersoImportance.setObjectName("sldPersoImportance")
|
||||
self.formLayout_8.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.sldPersoImportance)
|
||||
self.label_3 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.formLayout_8.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3)
|
||||
|
@ -454,6 +398,73 @@ class Ui_MainWindow(object):
|
|||
self.btnPersoColor.setObjectName("btnPersoColor")
|
||||
self.horizontalLayout_3.addWidget(self.btnPersoColor)
|
||||
self.formLayout_8.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3)
|
||||
self.horizontalLayout_20 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_20.setObjectName("horizontalLayout_20")
|
||||
self.sldPersoImportance = sldImportance(self.scrollAreaPersoInfosWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.sldPersoImportance.sizePolicy().hasHeightForWidth())
|
||||
self.sldPersoImportance.setSizePolicy(sizePolicy)
|
||||
self.sldPersoImportance.setObjectName("sldPersoImportance")
|
||||
self.horizontalLayout_20.addWidget(self.sldPersoImportance)
|
||||
self.chkPersoPOV = QtWidgets.QCheckBox(self.scrollAreaPersoInfosWidget)
|
||||
self.chkPersoPOV.setChecked(False)
|
||||
self.chkPersoPOV.setAutoRepeat(False)
|
||||
self.chkPersoPOV.setTristate(False)
|
||||
self.chkPersoPOV.setObjectName("chkPersoPOV")
|
||||
self.horizontalLayout_20.addWidget(self.chkPersoPOV)
|
||||
self.formLayout_8.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_20)
|
||||
self.label_4 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
||||
self.txtPersoMotivation = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoMotivation.setObjectName("txtPersoMotivation")
|
||||
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtPersoMotivation)
|
||||
self.label_5 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
||||
self.txtPersoGoal = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoGoal.setObjectName("txtPersoGoal")
|
||||
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.txtPersoGoal)
|
||||
self.label_6 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_6.setObjectName("label_6")
|
||||
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
||||
self.txtPersoConflict = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoConflict.setObjectName("txtPersoConflict")
|
||||
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.txtPersoConflict)
|
||||
self.label_7 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_7.setObjectName("label_7")
|
||||
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
||||
self.txtPersoEpiphany = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoEpiphany.setObjectName("txtPersoEpiphany")
|
||||
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.txtPersoEpiphany)
|
||||
self.label_24 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_24.setObjectName("label_24")
|
||||
self.formLayout_8.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.label_24)
|
||||
self.txtPersoSummarySentence = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoSummarySentence.setObjectName("txtPersoSummarySentence")
|
||||
self.formLayout_8.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummarySentence)
|
||||
self.label_8 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_8.setObjectName("label_8")
|
||||
self.formLayout_8.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
||||
self.txtPersoSummaryPara = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||
self.txtPersoSummaryPara.setObjectName("txtPersoSummaryPara")
|
||||
self.formLayout_8.setWidget(11, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummaryPara)
|
||||
self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_21.setObjectName("horizontalLayout_21")
|
||||
spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_21.addItem(spacerItem10)
|
||||
self.btnStepFour = QtWidgets.QPushButton(self.scrollAreaPersoInfosWidget)
|
||||
icon = QtGui.QIcon.fromTheme("go-next")
|
||||
self.btnStepFour.setIcon(icon)
|
||||
self.btnStepFour.setFlat(True)
|
||||
self.btnStepFour.setObjectName("btnStepFour")
|
||||
self.horizontalLayout_21.addWidget(self.btnStepFour)
|
||||
self.formLayout_8.setLayout(12, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_21)
|
||||
self.label_18 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||
self.label_18.setObjectName("label_18")
|
||||
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_18)
|
||||
self.scrollAreaPersoInfos.setWidget(self.scrollAreaPersoInfosWidget)
|
||||
self.verticalLayout_20.addWidget(self.scrollAreaPersoInfos)
|
||||
self.tabPersos.addTab(self.info, "")
|
||||
|
@ -745,7 +756,7 @@ class Ui_MainWindow(object):
|
|||
self.treeWorld.setRootIsDecorated(False)
|
||||
self.treeWorld.setObjectName("treeWorld")
|
||||
self.treeWorld.header().setVisible(False)
|
||||
self.treeWorld.header().setDefaultSectionSize(0)
|
||||
self.treeWorld.header().setDefaultSectionSize(25)
|
||||
self.verticalLayout_32.addWidget(self.treeWorld)
|
||||
self.horizontalLayout_19 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_19.setObjectName("horizontalLayout_19")
|
||||
|
@ -833,6 +844,7 @@ class Ui_MainWindow(object):
|
|||
self.layoutWidget = QtWidgets.QWidget(self.splitterOutlineH)
|
||||
self.layoutWidget.setObjectName("layoutWidget")
|
||||
self.verticalLayout_14 = QtWidgets.QVBoxLayout(self.layoutWidget)
|
||||
self.verticalLayout_14.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_14.setObjectName("verticalLayout_14")
|
||||
self.splitterOutlineV = QtWidgets.QSplitter(self.layoutWidget)
|
||||
self.splitterOutlineV.setOrientation(QtCore.Qt.Vertical)
|
||||
|
@ -1029,7 +1041,7 @@ class Ui_MainWindow(object):
|
|||
self.horizontalLayout_2.addWidget(self.stack)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1112, 30))
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1112, 24))
|
||||
self.menubar.setObjectName("menubar")
|
||||
self.menuFile = QtWidgets.QMenu(self.menubar)
|
||||
self.menuFile.setObjectName("menuFile")
|
||||
|
@ -1270,6 +1282,10 @@ class Ui_MainWindow(object):
|
|||
self.actFormatList.setObjectName("actFormatList")
|
||||
self.actFormatBlockquote = QtWidgets.QAction(MainWindow)
|
||||
self.actFormatBlockquote.setObjectName("actFormatBlockquote")
|
||||
self.actSearch = QtWidgets.QAction(MainWindow)
|
||||
icon = QtGui.QIcon.fromTheme("edit-find")
|
||||
self.actSearch.setIcon(icon)
|
||||
self.actSearch.setObjectName("actSearch")
|
||||
self.menuFile.addAction(self.actOpen)
|
||||
self.menuFile.addAction(self.menuRecents.menuAction())
|
||||
self.menuFile.addAction(self.actSave)
|
||||
|
@ -1313,6 +1329,7 @@ class Ui_MainWindow(object):
|
|||
self.menuEdit.addAction(self.actCopy)
|
||||
self.menuEdit.addAction(self.actPaste)
|
||||
self.menuEdit.addAction(self.actDelete)
|
||||
self.menuEdit.addAction(self.actSearch)
|
||||
self.menuEdit.addAction(self.actRename)
|
||||
self.menuEdit.addSeparator()
|
||||
self.menuEdit.addAction(self.mnuFormat.menuAction())
|
||||
|
@ -1339,7 +1356,7 @@ class Ui_MainWindow(object):
|
|||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.stack.setCurrentIndex(1)
|
||||
self.tabMain.setCurrentIndex(0)
|
||||
self.tabMain.setCurrentIndex(2)
|
||||
self.tabSummary.setCurrentIndex(0)
|
||||
self.tabPersos.setCurrentIndex(0)
|
||||
self.tabPlot.setCurrentIndex(0)
|
||||
|
@ -1484,6 +1501,8 @@ class Ui_MainWindow(object):
|
|||
self.tabMain.setTabText(self.tabMain.indexOf(self.lytTabSummary), _translate("MainWindow", "Summary"))
|
||||
self.groupBox.setTitle(_translate("MainWindow", "Names"))
|
||||
self.txtPersosFilter.setPlaceholderText(_translate("MainWindow", "Filter"))
|
||||
self.label_3.setText(_translate("MainWindow", "Name"))
|
||||
self.chkPersoPOV.setText(_translate("MainWindow", "Allow POV"))
|
||||
self.label_4.setText(_translate("MainWindow", "Motivation"))
|
||||
self.label_5.setText(_translate("MainWindow", "Goal"))
|
||||
self.label_6.setText(_translate("MainWindow", "Conflict"))
|
||||
|
@ -1492,7 +1511,6 @@ class Ui_MainWindow(object):
|
|||
self.label_8.setText(_translate("MainWindow", "<html><head/><body><p align=\"right\">One paragraph<br/>summary</p></body></html>"))
|
||||
self.btnStepFour.setText(_translate("MainWindow", "Next"))
|
||||
self.label_18.setText(_translate("MainWindow", "Importance"))
|
||||
self.label_3.setText(_translate("MainWindow", "Name"))
|
||||
self.tabPersos.setTabText(self.tabPersos.indexOf(self.info), _translate("MainWindow", "Basic info"))
|
||||
self.btnStepSix.setText(_translate("MainWindow", "Next"))
|
||||
self.tabPersos.setTabText(self.tabPersos.indexOf(self.tab_11), _translate("MainWindow", "Summary"))
|
||||
|
@ -1635,6 +1653,8 @@ class Ui_MainWindow(object):
|
|||
self.actFormatOrderedList.setText(_translate("MainWindow", "&Ordered list"))
|
||||
self.actFormatList.setText(_translate("MainWindow", "&Unordered list"))
|
||||
self.actFormatBlockquote.setText(_translate("MainWindow", "B&lockquote"))
|
||||
self.actSearch.setText(_translate("MainWindow", "Search"))
|
||||
self.actSearch.setShortcut(_translate("MainWindow", "Ctrl+F"))
|
||||
|
||||
from manuskript.ui.cheatSheet import cheatSheet
|
||||
from manuskript.ui.editors.mainEditor import mainEditor
|
||||
|
|
|
@ -124,7 +124,7 @@
|
|||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
|
@ -815,75 +815,126 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>204</width>
|
||||
<height>606</height>
|
||||
<width>453</width>
|
||||
<height>695</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_8">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="4" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="lineEditView" name="txtPersoName"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnPersoColor">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_20">
|
||||
<item>
|
||||
<widget class="sldImportance" name="sldPersoImportance" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chkPersoPOV">
|
||||
<property name="text">
|
||||
<string>Allow POV</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoRepeat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Motivation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="MDEditCompleter" name="txtPersoMotivation"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Goal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="MDEditCompleter" name="txtPersoGoal"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Conflict</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="MDEditCompleter" name="txtPersoConflict"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Epiphany</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="MDEditCompleter" name="txtPersoEpiphany"/>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="right">One sentence<br/>summary</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="MDEditCompleter" name="txtPersoSummarySentence"/>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="right">One paragraph<br/>summary</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="MDEditCompleter" name="txtPersoSummaryPara"/>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="12" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
|
@ -914,44 +965,13 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Importance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="sldImportance" name="sldPersoImportance" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="lineEditView" name="txtPersoName"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnPersoColor">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -1547,7 +1567,7 @@
|
|||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>0</number>
|
||||
<number>25</number>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -2095,7 +2115,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1112</width>
|
||||
<height>30</height>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
|
@ -2183,6 +2203,7 @@
|
|||
<addaction name="actCopy"/>
|
||||
<addaction name="actPaste"/>
|
||||
<addaction name="actDelete"/>
|
||||
<addaction name="actSearch"/>
|
||||
<addaction name="actRename"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="mnuFormat"/>
|
||||
|
@ -2818,6 +2839,17 @@
|
|||
<string>B&lockquote</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actSearch">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-find"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
|
@ -1,147 +1,151 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
from PyQt5.QtCore import Qt, QRect
|
||||
from PyQt5.QtGui import QPalette, QFontMetrics
|
||||
from PyQt5.QtWidgets import QWidget, QMenu, QAction, qApp, QListWidgetItem, QStyledItemDelegate, QStyle
|
||||
from PyQt5.QtCore import Qt, QRect, QEvent, QCoreApplication
|
||||
from PyQt5.QtGui import QPalette, QFontMetrics, QKeySequence
|
||||
from PyQt5.QtWidgets import QWidget, qApp, QListWidgetItem, QStyledItemDelegate, QStyle, QLabel, QToolTip, QShortcut
|
||||
|
||||
|
||||
from manuskript.enums import Outline
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.ui import style
|
||||
from manuskript.ui.search_ui import Ui_search
|
||||
from manuskript.models import references as Ref
|
||||
from manuskript.enums import Model
|
||||
|
||||
from manuskript.models.flatDataModelWrapper import flatDataModelWrapper
|
||||
from manuskript.ui.searchMenu import searchMenu
|
||||
from manuskript.ui.highlighters.searchResultHighlighters.searchResultHighlighter import searchResultHighlighter
|
||||
|
||||
|
||||
class search(QWidget, Ui_search):
|
||||
def __init__(self, parent=None):
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.options = {
|
||||
"All": True,
|
||||
"Title": True,
|
||||
"Text": True,
|
||||
"Summary": False,
|
||||
"Notes": False,
|
||||
"POV": False,
|
||||
"Status": False,
|
||||
"Label": False,
|
||||
"CS": True
|
||||
}
|
||||
self.searchTextInput.returnPressed.connect(self.search)
|
||||
|
||||
self.text.returnPressed.connect(self.search)
|
||||
self.generateOptionMenu()
|
||||
self.searchMenu = searchMenu()
|
||||
self.btnOptions.setMenu(self.searchMenu)
|
||||
|
||||
self.delegate = listResultDelegate(self)
|
||||
self.result.setItemDelegate(self.delegate)
|
||||
self.result.setMouseTracking(True)
|
||||
self.result.itemClicked.connect(self.openItem)
|
||||
|
||||
self.result.setStyleSheet(style.searchResultSS())
|
||||
self.text.setStyleSheet(style.lineEditSS())
|
||||
self.searchTextInput.setStyleSheet(style.lineEditSS())
|
||||
|
||||
def generateOptionMenu(self):
|
||||
self.menu = QMenu(self)
|
||||
a = QAction(self.tr("Search in:"), self.menu)
|
||||
a.setEnabled(False)
|
||||
self.menu.addAction(a)
|
||||
for i, d in [
|
||||
(self.tr("All"), "All"),
|
||||
(self.tr("Title"), "Title"),
|
||||
(self.tr("Text"), "Text"),
|
||||
(self.tr("Summary"), "Summary"),
|
||||
(self.tr("Notes"), "Notes"),
|
||||
(self.tr("POV"), "POV"),
|
||||
(self.tr("Status"), "Status"),
|
||||
(self.tr("Label"), "Label"),
|
||||
]:
|
||||
a = QAction(i, self.menu)
|
||||
a.setCheckable(True)
|
||||
a.setChecked(self.options[d])
|
||||
a.setData(d)
|
||||
a.triggered.connect(self.updateOptions)
|
||||
self.menu.addAction(a)
|
||||
self.menu.addSeparator()
|
||||
self.searchResultHighlighter = searchResultHighlighter()
|
||||
|
||||
a = QAction(self.tr("Options:"), self.menu)
|
||||
a.setEnabled(False)
|
||||
self.menu.addAction(a)
|
||||
for i, d in [
|
||||
(self.tr("Case sensitive"), "CS"),
|
||||
]:
|
||||
a = QAction(i, self.menu)
|
||||
a.setCheckable(True)
|
||||
a.setChecked(self.options[d])
|
||||
a.setData(d)
|
||||
a.triggered.connect(self.updateOptions)
|
||||
self.menu.addAction(a)
|
||||
self.menu.addSeparator()
|
||||
self.noResultsLabel = QLabel(_translate("Search", "No results found"), self.result)
|
||||
self.noResultsLabel.setVisible(False)
|
||||
self.noResultsLabel.setStyleSheet("QLabel {color: gray;}")
|
||||
|
||||
self.btnOptions.setMenu(self.menu)
|
||||
# Add shortcuts for navigating through search results
|
||||
QShortcut(QKeySequence(_translate("MainWindow", "F3")), self.searchTextInput, self.nextSearchResult)
|
||||
QShortcut(QKeySequence(_translate("MainWindow", "Shift+F3")), self.searchTextInput, self.previousSearchResult)
|
||||
|
||||
def updateOptions(self):
|
||||
a = self.sender()
|
||||
self.options[a.data()] = a.isChecked()
|
||||
# These texts are already included in translation files but including ":" at the end. We force here the
|
||||
# translation for them without ":"
|
||||
_translate("MainWindow", "Situation")
|
||||
_translate("MainWindow", "Status")
|
||||
|
||||
def nextSearchResult(self):
|
||||
if self.result.currentRow() < self.result.count() - 1:
|
||||
self.result.setCurrentRow(self.result.currentRow() + 1)
|
||||
else:
|
||||
self.result.setCurrentRow(0)
|
||||
|
||||
if 0 < self.result.currentRow() < self.result.count():
|
||||
self.openItem(self.result.currentItem())
|
||||
|
||||
def previousSearchResult(self):
|
||||
if self.result.currentRow() > 0:
|
||||
self.result.setCurrentRow(self.result.currentRow() - 1)
|
||||
else:
|
||||
self.result.setCurrentRow(self.result.count() - 1)
|
||||
|
||||
if 0 < self.result.currentRow() < self.result.count():
|
||||
self.openItem(self.result.currentItem())
|
||||
|
||||
def prepareRegex(self, searchText):
|
||||
import re
|
||||
|
||||
flags = re.UNICODE
|
||||
|
||||
if self.searchMenu.caseSensitive() is False:
|
||||
flags |= re.IGNORECASE
|
||||
|
||||
if self.searchMenu.regex() is False:
|
||||
searchText = re.escape(searchText)
|
||||
|
||||
if self.searchMenu.matchWords() is True:
|
||||
# Source: https://stackoverflow.com/a/15863102
|
||||
searchText = r'\b' + searchText + r'\b'
|
||||
|
||||
return re.compile(searchText, flags)
|
||||
|
||||
def search(self):
|
||||
text = self.text.text()
|
||||
|
||||
# Choosing the right columns
|
||||
lstColumns = [
|
||||
("Title", Outline.title),
|
||||
("Text", Outline.text),
|
||||
("Summary", Outline.summarySentence),
|
||||
("Summary", Outline.summaryFull),
|
||||
("Notes", Outline.notes),
|
||||
("POV", Outline.POV),
|
||||
("Status", Outline.status),
|
||||
("Label", Outline.label),
|
||||
]
|
||||
columns = [c[1] for c in lstColumns if self.options[c[0]] or self.options["All"]]
|
||||
|
||||
# Setting override cursor
|
||||
qApp.setOverrideCursor(Qt.WaitCursor)
|
||||
|
||||
# Searching
|
||||
model = mainWindow().mdlOutline
|
||||
results = model.findItemsContaining(text, columns, self.options["CS"])
|
||||
|
||||
# Showing results
|
||||
self.result.clear()
|
||||
for r in results:
|
||||
index = model.getIndexByID(r)
|
||||
if not index.isValid():
|
||||
continue
|
||||
item = index.internalPointer()
|
||||
i = QListWidgetItem(item.title(), self.result)
|
||||
i.setData(Qt.UserRole, r)
|
||||
i.setData(Qt.UserRole + 1, item.path())
|
||||
self.result.addItem(i)
|
||||
self.result.setCurrentRow(0)
|
||||
|
||||
# Removing override cursor
|
||||
qApp.restoreOverrideCursor()
|
||||
searchText = self.searchTextInput.text()
|
||||
if len(searchText) > 0:
|
||||
searchRegex = self.prepareRegex(searchText)
|
||||
results = []
|
||||
|
||||
# Set override cursor
|
||||
qApp.setOverrideCursor(Qt.WaitCursor)
|
||||
|
||||
for model, modelName in [
|
||||
(mainWindow().mdlOutline, Model.Outline),
|
||||
(mainWindow().mdlCharacter, Model.Character),
|
||||
(flatDataModelWrapper(mainWindow().mdlFlatData), Model.FlatData),
|
||||
(mainWindow().mdlWorld, Model.World),
|
||||
(mainWindow().mdlPlots, Model.Plot)
|
||||
]:
|
||||
filteredColumns = self.searchMenu.columns(modelName)
|
||||
|
||||
# Searching
|
||||
if len(filteredColumns):
|
||||
results += model.searchOccurrences(searchRegex, filteredColumns)
|
||||
|
||||
# Showing results
|
||||
self.generateResultsLists(results)
|
||||
|
||||
# Remove override cursor
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
def generateResultsLists(self, results):
|
||||
self.noResultsLabel.setVisible(len(results) == 0)
|
||||
for result in results:
|
||||
item = QListWidgetItem(result.title(), self.result)
|
||||
item.setData(Qt.UserRole, result)
|
||||
item.setData(Qt.UserRole + 1, ' > '.join(result.path()))
|
||||
item.setData(Qt.UserRole + 2, result.context())
|
||||
self.result.addItem(item)
|
||||
|
||||
def openItem(self, item):
|
||||
r = Ref.textReference(item.data(Qt.UserRole))
|
||||
Ref.open(r)
|
||||
# mw = mainWindow()
|
||||
# index = mw.mdlOutline.getIndexByID(item.data(Qt.UserRole))
|
||||
# mw.mainEditor.setCurrentModelIndex(index, newTab=True)
|
||||
self.searchResultHighlighter.highlightSearchResult(item.data(Qt.UserRole))
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self.delegate.mouseLeave()
|
||||
|
||||
class listResultDelegate(QStyledItemDelegate):
|
||||
def __init__(self, parent=None):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self._tooltipRowIndex = -1
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
extra = index.data(Qt.UserRole + 1)
|
||||
|
||||
if not extra:
|
||||
return QStyledItemDelegate.paint(self, painter, option, index)
|
||||
|
||||
else:
|
||||
if option.state & QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.color(QPalette.Highlight))
|
||||
|
||||
title = index.data()
|
||||
extra = " - {}".format(extra)
|
||||
painter.drawText(option.rect.adjusted(2, 1, 0, 0), Qt.AlignLeft, title)
|
||||
|
||||
fm = QFontMetrics(option.font)
|
||||
|
@ -153,5 +157,18 @@ class listResultDelegate(QStyledItemDelegate):
|
|||
painter.setPen(Qt.white)
|
||||
else:
|
||||
painter.setPen(Qt.gray)
|
||||
painter.drawText(r.adjusted(2, 1, 0, 0), Qt.AlignLeft, extra)
|
||||
painter.drawText(r.adjusted(2, 1, 0, 0), Qt.AlignLeft, " - {}".format(extra))
|
||||
painter.restore()
|
||||
|
||||
def editorEvent(self, event, model, option, index):
|
||||
if event.type() == QEvent.MouseMove and self._tooltipRowIndex != index.row():
|
||||
self._tooltipRowIndex = index.row()
|
||||
context = index.data(Qt.UserRole + 2)
|
||||
extra = index.data(Qt.UserRole + 1)
|
||||
QToolTip.showText(event.globalPos(),
|
||||
"<p>#" + str(index.row()) + " - " + extra + "</p><p>" + context + "</p>")
|
||||
return True
|
||||
return False
|
||||
|
||||
def mouseLeave(self):
|
||||
self._tooltipRowIndex = -1
|
||||
|
|
108
manuskript/ui/searchMenu.py
Normal file
108
manuskript/ui/searchMenu.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
from PyQt5.QtWidgets import QMenu, QAction
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from manuskript.searchLabels import OutlineSearchLabels, CharacterSearchLabels, FlatDataSearchLabels, WorldSearchLabels, PlotSearchLabels
|
||||
from manuskript.models.searchFilter import searchFilter
|
||||
from manuskript.enums import Model
|
||||
|
||||
|
||||
def filterKey(modelPreffix, column):
|
||||
return modelPreffix + str(column)
|
||||
|
||||
|
||||
class searchMenu(QMenu):
|
||||
def __init__(self, parent=None):
|
||||
QMenu.__init__(self, parent)
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
# Model keys must match the ones used in search widget class
|
||||
self.filters = {
|
||||
Model.Outline: searchFilter(_translate("MainWindow", "Outline"), True, list(OutlineSearchLabels.keys())),
|
||||
Model.Character: searchFilter(_translate("MainWindow", "Characters"), True, list(CharacterSearchLabels.keys())),
|
||||
Model.FlatData: searchFilter(_translate("MainWindow", "FlatData"), True, list(FlatDataSearchLabels.keys())),
|
||||
Model.World: searchFilter(_translate("MainWindow", "World"), True, list(WorldSearchLabels.keys())),
|
||||
Model.Plot: searchFilter(_translate("MainWindow", "Plot"), True, list(PlotSearchLabels.keys()))
|
||||
}
|
||||
|
||||
self.options = {
|
||||
"CS": [self.tr("Case sensitive"), True],
|
||||
"MatchWords": [self.tr("Match words"), False],
|
||||
"Regex": [self.tr("Regex"), False]
|
||||
}
|
||||
|
||||
self._generateOptions()
|
||||
|
||||
def _generateOptions(self):
|
||||
a = QAction(self.tr("Search in:"), self)
|
||||
a.setEnabled(False)
|
||||
self.addAction(a)
|
||||
for filterKey in self.filters:
|
||||
a = QAction(self.tr(self.filters[filterKey].label()), self)
|
||||
a.setCheckable(True)
|
||||
a.setChecked(self.filters[filterKey].enabled())
|
||||
a.setData(filterKey)
|
||||
a.triggered.connect(self._updateFilters)
|
||||
self.addAction(a)
|
||||
self.addSeparator()
|
||||
|
||||
a = QAction(self.tr("Options:"), self)
|
||||
a.setEnabled(False)
|
||||
self.addAction(a)
|
||||
for optionKey in self.options:
|
||||
a = QAction(self.options[optionKey][0], self)
|
||||
a.setCheckable(True)
|
||||
a.setChecked(self.options[optionKey][1])
|
||||
a.setData(optionKey)
|
||||
a.triggered.connect(self._updateOptions)
|
||||
self.addAction(a)
|
||||
self.addSeparator()
|
||||
|
||||
def _updateFilters(self):
|
||||
a = self.sender()
|
||||
self.filters[a.data()].setEnabled(a.isChecked())
|
||||
|
||||
def _updateOptions(self):
|
||||
a = self.sender()
|
||||
self.options[a.data()][1] = a.isChecked()
|
||||
|
||||
def columns(self, modelName):
|
||||
if self.filters[modelName].enabled():
|
||||
return self.filters[modelName].modelColumns()
|
||||
else:
|
||||
return []
|
||||
|
||||
def caseSensitive(self):
|
||||
return self.options["CS"][1]
|
||||
|
||||
def matchWords(self):
|
||||
return self.options["MatchWords"][1]
|
||||
|
||||
def regex(self):
|
||||
return self.options["Regex"][1]
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
# Workaround for enabling / disabling actions without closing the menu.
|
||||
# Source: https://stackoverflow.com/a/14967212
|
||||
action = self.activeAction()
|
||||
if action:
|
||||
action.setEnabled(False)
|
||||
QMenu.mouseReleaseEvent(self, event)
|
||||
action.setEnabled(True)
|
||||
action.trigger()
|
||||
else:
|
||||
QMenu.mouseReleaseEvent(self, event)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
# Workaround for enabling / disabling actions without closing the menu.
|
||||
# Source: https://stackoverflow.com/a/14967212
|
||||
action = self.activeAction()
|
||||
if action and event.key() == QtCore.Qt.Key_Return:
|
||||
action.setEnabled(False)
|
||||
QMenu.keyPressEvent(self, event)
|
||||
action.setEnabled(True)
|
||||
action.trigger()
|
||||
else:
|
||||
QMenu.keyPressEvent(self, event)
|
|
@ -19,12 +19,12 @@ class Ui_search(object):
|
|||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setSpacing(0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.text = QtWidgets.QLineEdit(search)
|
||||
self.text.setInputMask("")
|
||||
self.text.setFrame(False)
|
||||
self.text.setClearButtonEnabled(True)
|
||||
self.text.setObjectName("text")
|
||||
self.horizontalLayout.addWidget(self.text)
|
||||
self.searchTextInput = QtWidgets.QLineEdit(search)
|
||||
self.searchTextInput.setInputMask("")
|
||||
self.searchTextInput.setFrame(False)
|
||||
self.searchTextInput.setClearButtonEnabled(True)
|
||||
self.searchTextInput.setObjectName("searchTextInput")
|
||||
self.horizontalLayout.addWidget(self.searchTextInput)
|
||||
self.btnOptions = QtWidgets.QPushButton(search)
|
||||
self.btnOptions.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("edit-find")
|
||||
|
@ -45,5 +45,5 @@ class Ui_search(object):
|
|||
def retranslateUi(self, search):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
search.setWindowTitle(_translate("search", "Form"))
|
||||
self.text.setPlaceholderText(_translate("search", "Search for..."))
|
||||
self.searchTextInput.setPlaceholderText(_translate("search", "Search for..."))
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="text">
|
||||
<widget class="QLineEdit" name="searchTextInput">
|
||||
<property name="inputMask">
|
||||
<string/>
|
||||
</property>
|
||||
|
|
|
@ -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.13.0
|
||||
# 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"))
|
||||
|
|
|
@ -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&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&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">
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -29,6 +29,8 @@ class characterTreeView(QTreeWidget):
|
|||
self._rootItem = QTreeWidgetItem()
|
||||
self.insertTopLevelItem(0, self._rootItem)
|
||||
|
||||
self.importanceMap = {self.tr("Main"):2, self.tr("Secondary"):1, self.tr("Minor"):0}
|
||||
|
||||
def setCharactersModel(self, model):
|
||||
self._model = model
|
||||
self._model.dataChanged.connect(self.updateMaybe)
|
||||
|
@ -64,7 +66,7 @@ class characterTreeView(QTreeWidget):
|
|||
for child in range(item.childCount()):
|
||||
sub = item.child(child)
|
||||
ID = sub.data(0, Qt.UserRole)
|
||||
if ID is not None:
|
||||
if ID != None:
|
||||
# Update name
|
||||
c = self._model.getCharacterByID(ID)
|
||||
name = c.name()
|
||||
|
@ -86,11 +88,9 @@ class characterTreeView(QTreeWidget):
|
|||
self.clear()
|
||||
characters = self._model.getCharactersByImportance()
|
||||
|
||||
h = [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]
|
||||
|
||||
for i in range(3):
|
||||
for i, importanceLevel in enumerate(self.importanceMap):
|
||||
# Create category item
|
||||
cat = QTreeWidgetItem(self, [h[i]])
|
||||
cat = QTreeWidgetItem(self, [importanceLevel])
|
||||
cat.setBackground(0, QBrush(QColor(S.highlightLight)))
|
||||
cat.setForeground(0, QBrush(QColor(S.highlightedTextDark)))
|
||||
cat.setTextAlignment(0, Qt.AlignCenter)
|
||||
|
@ -119,6 +119,24 @@ class characterTreeView(QTreeWidget):
|
|||
self.expandAll()
|
||||
self._updating = False
|
||||
|
||||
def addCharacter(self):
|
||||
curr_item = self.currentItem()
|
||||
curr_importance = 0
|
||||
|
||||
# check if an item is selected
|
||||
if curr_item != None:
|
||||
if curr_item.parent() == None:
|
||||
# this is a top-level category, so find its importance
|
||||
# get the current text, then look up the importance level
|
||||
text = curr_item.text(0)
|
||||
curr_importance = self.importanceMap[text]
|
||||
else:
|
||||
# get the importance from the currently-highlighted character
|
||||
curr_character = self.currentCharacter()
|
||||
curr_importance = curr_character.importance()
|
||||
|
||||
self._model.addCharacter(importance=curr_importance)
|
||||
|
||||
def removeCharacter(self):
|
||||
"""
|
||||
Removes selected character.
|
||||
|
@ -130,22 +148,30 @@ class characterTreeView(QTreeWidget):
|
|||
def choseCharacterColor(self):
|
||||
ID = self.currentCharacterID()
|
||||
c = self._model.getCharacterByID(ID)
|
||||
|
||||
if c:
|
||||
color = iconColor(c.icon)
|
||||
else:
|
||||
color = Qt.white
|
||||
|
||||
self.colorDialog = QColorDialog(color, mainWindow())
|
||||
color = self.colorDialog.getColor(color)
|
||||
|
||||
if color.isValid():
|
||||
c.setColor(color)
|
||||
mainWindow().updateCharacterColor(ID)
|
||||
|
||||
def changeCharacterPOVState(self, state):
|
||||
ID = self.currentCharacterID()
|
||||
c = self._model.getCharacterByID(ID)
|
||||
c.setPOVEnabled(state == Qt.Checked)
|
||||
mainWindow().updateCharacterPOVState(ID)
|
||||
|
||||
def addCharacterInfo(self):
|
||||
self._model.addCharacterInfo(self.currentCharacterID())
|
||||
|
||||
def removeCharacterInfo(self):
|
||||
self._model.removeCharacterInfo(self.currentCharacterID(),
|
||||
)
|
||||
self._model.removeCharacterInfo(self.currentCharacterID())
|
||||
|
||||
def currentCharacterID(self):
|
||||
ID = None
|
||||
|
|
|
@ -43,11 +43,19 @@ class corkDelegate(QStyledItemDelegate):
|
|||
return QStyledItemDelegate.editorEvent(self, event, model, option, index)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
# When the user performs a global search and selects an Outline result (title or summary), the
|
||||
# associated chapter is selected in cork view, triggering a call to this method with the results
|
||||
# list widget set in self.sender(). In this case we store the searched column so we know which
|
||||
# editor should be created.
|
||||
searchedColumn = None
|
||||
if self.sender() is not None and self.sender().objectName() == 'result' and self.sender().currentItem():
|
||||
searchedColumn = self.sender().currentItem().data(Qt.UserRole).column()
|
||||
|
||||
self.updateRects(option, index)
|
||||
|
||||
bgColor = self.bgColors.get(index, "white")
|
||||
|
||||
if self.mainLineRect.contains(self.lastPos):
|
||||
if searchedColumn == Outline.summarySentence or (self.lastPos is not None and self.mainLineRect.contains(self.lastPos)):
|
||||
# One line summary
|
||||
self.editing = Outline.summarySentence
|
||||
edt = QLineEdit(parent)
|
||||
|
@ -64,7 +72,7 @@ class corkDelegate(QStyledItemDelegate):
|
|||
edt.setStyleSheet("background: {}; color: black;".format(bgColor))
|
||||
return edt
|
||||
|
||||
elif self.titleRect.contains(self.lastPos):
|
||||
elif searchedColumn == Outline.title or (self.lastPos is not None and self.titleRect.contains(self.lastPos)):
|
||||
# Title
|
||||
self.editing = Outline.title
|
||||
edt = QLineEdit(parent)
|
||||
|
|
|
@ -27,6 +27,8 @@ class corkView(QListView, dndView, outlineBasics):
|
|||
def updateBackground(self):
|
||||
if settings.corkBackground["image"] != "":
|
||||
img = findBackground(settings.corkBackground["image"])
|
||||
if img == None:
|
||||
img = ""
|
||||
else:
|
||||
# No background image
|
||||
img = ""
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue