Releasing manuskript 0.3.0

This commit is contained in:
Olivier Keshavjee 2016-03-31 16:13:13 +02:00
commit d67f743ba0
165 changed files with 8435 additions and 3627 deletions

View file

@ -23,3 +23,4 @@ ratings:
- "**.rb"
exclude_paths:
- libs/**/*
- **/*_ui.py

3
.gitignore vendored
View file

@ -10,4 +10,5 @@ ExportTest
icons/Numix
.idea
dist
build
build
test-projects

View file

@ -2,9 +2,11 @@
[Manuskript](http://www.theologeek.ch/manuskript) is an open-source tool for writers.
![Main view](http://www.theologeek.ch/manuskript/wp-content/uploads/2016/02/index-cards-1.jpg)
![Main view](http://www.theologeek.ch/manuskript/wp-content/uploads/2016/03/manuskript-0.3.0.jpg)
## [Download](http://www.theologeek.ch/manuskript/download)
## Running from sources
To run the application without installing just:
@ -23,4 +25,4 @@ Be sure to have all **dependencies** installed !
Optional:
- pyenchant
- zlib
- zlib

View file

@ -1,58 +1,64 @@
FORMS += ../manuskript/ui/mainWindow.ui
FORMS += ../manuskript/ui/settings_ui.ui
FORMS += ../manuskript/ui/welcome_ui.ui
FORMS += ../manuskript/ui/sldImportance_ui.ui
FORMS += ../manuskript/ui/cheatSheet_ui.ui
FORMS += ../manuskript/ui/compileDialog_ui.ui
FORMS += ../manuskript/ui/revisions_ui.ui
FORMS += ../manuskript/ui/mainWindow.ui
FORMS += ../manuskript/ui/compileDialog_ui.ui
FORMS += ../manuskript/ui/search_ui.ui
FORMS += ../manuskript/ui/tools/frequency_ui.ui
FORMS += ../manuskript/ui/welcome_ui.ui
FORMS += ../manuskript/ui/cheatSheet_ui.ui
FORMS += ../manuskript/ui/settings_ui.ui
FORMS += ../manuskript/ui/editors/editorWidget_ui.ui
FORMS += ../manuskript/ui/editors/textFormat_ui.ui
FORMS += ../manuskript/ui/editors/locker_ui.ui
FORMS += ../manuskript/ui/editors/completer_ui.ui
FORMS += ../manuskript/ui/editors/mainEditor_ui.ui
FORMS += ../manuskript/ui/views/propertiesView_ui.ui
FORMS += ../manuskript/ui/views/basicItemView_ui.ui
FORMS += ../manuskript/ui/views/metadataView_ui.ui
FORMS += ../manuskript/ui/views/basicItemView_ui.ui
FORMS += ../manuskript/ui/views/sldImportance_ui.ui
FORMS += ../manuskript/ui/views/storylineView_ui.ui
SOURCES += ../manuskript/exporter/__init__.py
SOURCES += ../manuskript/load_save/version_0.py
SOURCES += ../manuskript/main.py
SOURCES += ../manuskript/loadSave.py
SOURCES += ../manuskript/mainWindow.py
SOURCES += ../manuskript/settingsWindow.py
SOURCES += ../manuskript/models/characterModel.py
SOURCES += ../manuskript/models/outlineModel.py
SOURCES += ../manuskript/models/persosProxyModel.py
SOURCES += ../manuskript/models/plotModel.py
SOURCES += ../manuskript/models/worldModel.py
SOURCES += ../manuskript/models/persosModel.py
SOURCES += ../manuskript/models/plotsProxyModel.py
SOURCES += ../manuskript/models/references.py
SOURCES += ../manuskript/exporter/__init__.py
SOURCES += ../manuskript/ui/helpLabel.py
SOURCES += ../manuskript/ui/sldImportance.py
SOURCES += ../manuskript/ui/welcome.py
SOURCES += ../manuskript/models/worldModel.py
SOURCES += ../manuskript/settingsWindow.py
SOURCES += ../manuskript/ui/cheatSheet.py
SOURCES += ../manuskript/ui/compileDialog.py
SOURCES += ../manuskript/ui/revisions.py
SOURCES += ../manuskript/ui/collapsibleDockWidgets.py
SOURCES += ../manuskript/ui/editors/editorWidget.py
SOURCES += ../manuskript/ui/compileDialog.py
SOURCES += ../manuskript/ui/editors/fullScreenEditor.py
SOURCES += ../manuskript/ui/editors/locker.py
SOURCES += ../manuskript/ui/editors/textFormat.py
SOURCES += ../manuskript/ui/editors/completer.py
SOURCES += ../manuskript/ui/editors/mainEditor.py
SOURCES += ../manuskript/ui/views/corkDelegate.py
SOURCES += ../manuskript/ui/views/outlineDelegates.py
SOURCES += ../manuskript/ui/views/outlineBasics.py
SOURCES += ../manuskript/ui/editors/textFormat.py
SOURCES += ../manuskript/ui/helpLabel.py
SOURCES += ../manuskript/ui/revisions.py
SOURCES += ../manuskript/ui/search.py
SOURCES += ../manuskript/ui/tools/frequencyAnalyzer.py
SOURCES += ../manuskript/ui/views/characterTreeView.py
SOURCES += ../manuskript/ui/views/cmbOutlineCharacterChoser.py
SOURCES += ../manuskript/ui/views/cmbOutlineLabelChoser.py
SOURCES += ../manuskript/ui/views/cmbOutlinePersoChoser.py
SOURCES += ../manuskript/ui/views/cmbOutlineStatusChoser.py
SOURCES += ../manuskript/ui/views/treeView.py
SOURCES += ../manuskript/ui/views/corkDelegate.py
SOURCES += ../manuskript/ui/views/lineEditView.py
SOURCES += ../manuskript/ui/views/textEditView.py
SOURCES += ../manuskript/ui/views/plotTreeView.py
SOURCES += ../manuskript/ui/views/outlineBasics.py
SOURCES += ../manuskript/ui/views/outlineDelegates.py
SOURCES += ../manuskript/ui/views/plotDelegate.py
SOURCES += ../manuskript/ui/views/plotTreeView.py
SOURCES += ../manuskript/ui/views/sldImportance.py
SOURCES += ../manuskript/ui/views/storylineView.py
SOURCES += ../manuskript/ui/views/textEditCompleter.py
SOURCES += ../manuskript/ui/views/textEditView.py
SOURCES += ../manuskript/ui/views/treeView.py
SOURCES += ../manuskript/ui/welcome.py
TRANSLATIONS += manuskript_fr.ts
TRANSLATIONS += manuskript_es.ts

BIN
i18n/manuskript_es.qm Normal file

Binary file not shown.

3110
i18n/manuskript_es.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

17
i18n/tool.txt Normal file
View file

@ -0,0 +1,17 @@
List all forms:
find .. -iname *.ui
List all files containing ".tr":
grep -rin "\.tr(" ../manuskript > list.txt
List all files containing ".translate":
grep -rin "\.translate(" ../manuskript >> list.txt
Then clean list.txt, and copy it in LibreOffice Calc, then:
- Data, Filter, Advanced
- From: whole column
- No duplication
- → filter

View file

@ -10,13 +10,13 @@ run: $(UIs)
bin/manuskript
debug: $(UIs)
gdb --args python3 manuskript/main.py
gdb --args python3 bin/manuskript
lineprof:
kernprof -l -v manuskript/main.py
profile:
python3 -m cProfile -s 'cumtime' manuskript/main.py | more
python3 -m cProfile -s 'cumtime' bin/manuskript | more
compile:
cd manuskript && python3 setup.py build_ext --inplace

View file

@ -4,7 +4,7 @@ block_cipher = None
a = Analysis(['bin/manuskript'],
pathex=['/home/olivier/Dropbox/Documents/Travail/Geekeries/Python/PyCharmProjects/manuskript_pyinstaller tests'],
pathex=['.'],
binaries=None,
datas=[
("icons", "icons"),
@ -12,7 +12,7 @@ a = Analysis(['bin/manuskript'],
("resources", "resources"),
("sample-projects", "sample-projects"),
],
hiddenimports=[],
hiddenimports=["xml.dom"],
hookspath=[],
runtime_hooks=[],
excludes=[],

View file

@ -9,7 +9,7 @@ from enum import Enum
#def enum(**enums):
#return type(str('Enum'), (), enums)
class Perso(Enum):
class Character(Enum):
name = 0
ID = 1
importance = 2
@ -17,24 +17,22 @@ class Perso(Enum):
goal = 4
conflict = 5
epiphany = 6
summarySentance = 7
summarySentence = 7
summaryPara = 8
summaryFull = 9
notes = 10
infoName = 11
infoData = 12
class Plot(Enum):
name = 0
ID = 1
importance = 2
persos = 3
characters = 3
description = 4
result = 5
subplots = 6
steps = 6
summary = 7
class Subplot(Enum):
class PlotStep(Enum):
name = 0
ID = 1
meta = 2
@ -51,7 +49,7 @@ class Outline(Enum):
title = 0
ID = 1
type = 2
summarySentance = 3
summarySentence = 3
summaryFull = 4
POV = 5
notes = 6
@ -66,4 +64,3 @@ class Outline(Enum):
# (sum of all sub-items' goals)
textFormat = 15
revisions = 16

View file

@ -29,9 +29,7 @@ class arboExporter():
writeItem(c, path2)
else:
ext = ".t2t" if item.isT2T() else \
".html" if item.isHTML() else \
".txt"
ext = ".md"
path2 = os.path.join(path, item.title() + ext)
f = open(path2, "w")
text = self.formatText(item.text(), item.type())

View file

@ -2,43 +2,49 @@
#--!-- coding: utf8 --!--
import os
import re
from random import *
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp
# Used to detect multiple connections
from PyQt5.QtGui import QBrush, QIcon, QPainter
from PyQt5.QtGui import QColor
from PyQt5.QtGui import QImage
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import qApp
from PyQt5.QtWidgets import qApp, QTextEdit
from manuskript.enums import Outline
AUC = Qt.AutoConnection | Qt.UniqueConnection
MW = None
def wordCount(text):
return len(text.strip().replace(" ", "\n").split("\n")) if text else 0
def toInt(text):
if text:
return int(text)
else:
return 0
def toFloat(text):
if text:
return float(text)
else:
return 0.
def toString(text):
if text in [None, "None"]:
return ""
else:
return str(text)
def drawProgress(painter, rect, progress, radius=0):
painter.setPen(Qt.NoPen)
painter.setBrush(QColor("#dddddd"))
@ -49,7 +55,8 @@ def drawProgress(painter, rect, progress, radius=0):
r2 = QRect(rect)
r2.setWidth(r2.width() * min(progress, 1))
painter.drawRoundedRect(r2, radius, radius)
def colorFromProgress(progress):
progress = toFloat(progress)
c1 = QColor(Qt.red)
@ -65,7 +72,8 @@ def colorFromProgress(progress):
return c4
else:
return c3
def mainWindow():
global MW
if not MW:
@ -77,6 +85,7 @@ def mainWindow():
else:
return MW
def iconColor(icon):
"""Returns a QRgb from a QIcon, assuming its all the same color"""
px = icon.pixmap(5, 5)
@ -85,14 +94,17 @@ def iconColor(icon):
else:
return QColor(Qt.transparent)
def iconFromColor(color):
px = QPixmap(32, 32)
px.fill(color)
return QIcon(px)
def iconFromColorString(string):
return iconFromColor(QColor(string))
def randomColor(mix=None):
"""Generates a random color. If mix (QColor) is given, mixes the random color and mix."""
r = randint(0, 255)
@ -106,6 +118,7 @@ def randomColor(mix=None):
return QColor(r, g, b)
def mixColors(col1, col2, f=.5):
f2 = 1-f
r = col1.red() * f + col2.red() * f2
@ -113,6 +126,7 @@ def mixColors(col1, col2, f=.5):
b = col1.blue() * f + col2.blue() * f2
return QColor(r, g, b)
def outlineItemColors(item):
"""Takes an OutlineItem and returns a dict of colors."""
colors = {}
@ -121,9 +135,9 @@ def outlineItemColors(item):
# POV
colors["POV"] = QColor(Qt.transparent)
POV = item.data(Outline.POV.value)
for i in range(mw.mdlPersos.rowCount()):
if mw.mdlPersos.ID(i) == POV:
colors["POV"] = iconColor(mw.mdlPersos.icon(i))
for i in range(mw.mdlCharacter.rowCount()):
if mw.mdlCharacter.ID(i) == POV:
colors["POV"] = iconColor(mw.mdlCharacter.icon(i))
# Label
lbl = item.data(Outline.label.value)
@ -144,7 +158,8 @@ def outlineItemColors(item):
colors["Compile"] = QColor(Qt.black)
return colors
def colorifyPixmap(pixmap, color):
# FIXME: ugly
p = QPainter(pixmap)
@ -152,12 +167,14 @@ def colorifyPixmap(pixmap, color):
p.fillRect(pixmap.rect(), color)
return pixmap
def appPath(suffix=None):
p = os.path.realpath(os.path.join(os.path.split(__file__)[0], ".."))
if suffix:
p = os.path.join(p, suffix)
return p
def writablePath(suffix=None):
if hasattr(QStandardPaths, "AppLocalDataLocation"):
p = QStandardPaths.writableLocation(QStandardPaths.AppLocalDataLocation)
@ -170,6 +187,7 @@ def writablePath(suffix=None):
os.makedirs(p)
return p
def allPaths(suffix=None):
paths = []
# src directory
@ -178,11 +196,54 @@ def allPaths(suffix=None):
paths.append(writablePath(suffix))
return paths
def lightBlue():
"""
A light blue used in several places in manuskript.
@return: QColor
"""
return QColor(Qt.blue).lighter(190)
def totalObjects():
return len(mainWindow().findChildren(QObject))
def printObjects():
print("Objects:", str(totalObjects()))
print("Objects:", str(totalObjects()))
def findWidgetsOfClass(cls):
"""
Returns all widgets, children of MainWindow, whose class is cls.
@param cls: a class
@return: list of QWidgets
"""
return mainWindow().findChildren(cls, QRegExp())
def findBackground(filename):
"""
Returns the full path to a background file of name filename within ressource folders.
"""
return findFirstFile(re.escape(filename), "resources/backgrounds")
def findFirstFile(regex, path="resources"):
paths = allPaths(path)
for p in paths:
lst = os.listdir(p)
for l in lst:
if re.match(regex, l):
return os.path.join(p, l)
def HTML2PlainText(html):
"""
Ressource-inefficient way to convert HTML to plain text.
@param html:
@return:
"""
e = QTextEdit()
e.setHtml(html)
return e.toPlainText()

View file

@ -1,149 +1,66 @@
#!/usr/bin/env python
#--!-- coding: utf8 --!--
# --!-- coding: utf8 --!--
# The loadSave file calls the propper functions to load and save file
# trying to detect the proper file format if it comes from an older version
import os
import zipfile
from PyQt5.QtCore import QModelIndex, Qt
from PyQt5.QtGui import QColor, QStandardItem
from PyQt5.QtWidgets import qApp
from lxml import etree as ET
import manuskript.load_save.version_0 as v0
import manuskript.load_save.version_1 as v1
from manuskript.functions import iconColor, iconFromColorString
try:
import zlib # Used with zipfile for compression
compression = zipfile.ZIP_DEFLATED
except:
compression = zipfile.ZIP_STORED
def saveProject(version=None):
def saveFilesToZip(files, zipname):
"""Saves given files to zipname.
files is actually a list of (content, filename)."""
zf = zipfile.ZipFile(zipname, mode="w")
for content, filename in files:
zf.writestr(filename, content, compress_type=compression)
zf.close()
def loadFilesFromZip(zipname):
"""Returns the content of zipfile as a dict of filename:content."""
print(zipname)
zf = zipfile.ZipFile(zipname)
files = {}
for f in zf.namelist():
files[f] = zf.read(f)
return files
def saveStandardItemModelXML(mdl, xml=None):
"""Saves the given QStandardItemModel to XML.
If xml (filename) is given, saves to xml. Otherwise returns as string."""
root = ET.Element("model")
root.attrib["version"] = qApp.applicationVersion()
# Header
header = ET.SubElement(root, "header")
vHeader = ET.SubElement(header, "vertical")
for x in range(mdl.rowCount()):
vH = ET.SubElement(vHeader, "label")
vH.attrib["row"] = str(x)
vH.attrib["text"] = str(mdl.headerData(x, Qt.Vertical))
hHeader = ET.SubElement(header, "horizontal")
for y in range(mdl.columnCount()):
hH = ET.SubElement(hHeader, "label")
hH.attrib["row"] = str(y)
hH.attrib["text"] = str(mdl.headerData(y, Qt.Horizontal))
# Data
data = ET.SubElement(root, "data")
saveItem(data, mdl)
#print(qApp.tr("Saving to {}.").format(xml))
if xml:
ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True)
# While debugging, we don't save the project
# return
if version == 0:
v0.saveProject()
else:
return ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True)
def saveItem(root, mdl, parent=QModelIndex()):
for x in range(mdl.rowCount(parent)):
row = ET.SubElement(root, "row")
row.attrib["row"] = str(x)
for y in range(mdl.columnCount(parent)):
col = ET.SubElement(row, "col")
col.attrib["col"] = str(y)
if mdl.data(mdl.index(x, y, parent), Qt.DecorationRole) != None:
color = iconColor(mdl.data(mdl.index(x, y, parent), Qt.DecorationRole)).name(QColor.HexArgb)
col.attrib["color"] = color if color != "#ff000000" else "#00000000"
if mdl.data(mdl.index(x, y, parent)) != "":
col.text = mdl.data(mdl.index(x, y, parent))
if mdl.hasChildren(mdl.index(x, y, parent)):
saveItem(col, mdl, mdl.index(x, y, parent))
def loadStandardItemModelXML(mdl, xml, fromString=False):
"""Load data to a QStandardItemModel mdl from xml.
By default xml is a filename. If fromString=True, xml is a string containg the data."""
#print(qApp.tr("Loading {}... ").format(xml), end="")
if not fromString:
try:
tree = ET.parse(xml)
except:
print("Failed.")
return
v1.saveProject()
def clearSaveCache():
v1.cache = {}
def loadProject(project):
# Detect version
isZip = False
version = 0
# Is it a zip?
try:
zf = zipfile.ZipFile(project)
isZip = True
except zipfile.BadZipFile:
isZip = False
# Does it have a VERSION in zip root?
# Was used in transition between 0.2.0 and 0.3.0
# So VERSION part can be deleted for manuskript 0.4.0
if isZip and "VERSION" in zf.namelist():
version = int(zf.read("VERSION"))
# Does it have a MANUSKRIPT in zip root?
elif isZip and "MANUSKRIPT" in zf.namelist():
version = int(zf.read("MANUSKRIPT"))
# Zip but no VERSION/MANUSKRIPT: oldest file format
elif isZip:
version = 0
# Not a zip
else:
root = ET.fromstring(xml)
#root = tree.getroot()
#Header
hLabels = []
vLabels = []
for l in root.find("header").find("horizontal").findall("label"):
hLabels.append(l.attrib["text"])
for l in root.find("header").find("vertical").findall("label"):
vLabels.append(l.attrib["text"])
#print(root.find("header").find("vertical").text)
#mdl.setVerticalHeaderLabels(vLabels)
#mdl.setHorizontalHeaderLabels(hLabels)
# Populates with empty items
for i in enumerate(vLabels):
row = []
for r in enumerate(hLabels):
row.append(QStandardItem())
mdl.appendRow(row)
#Data
data = root.find("data")
loadItem(data, mdl)
return True
def loadItem(root, mdl, parent=QModelIndex()):
for row in root:
r = int(row.attrib["row"])
for col in row:
c = int(col.attrib["col"])
item = mdl.itemFromIndex(mdl.index(r, c, parent))
if not item:
item = QStandardItem()
mdl.itemFromIndex(parent).setChild(r, c, item)
if col.text:
#mdl.setData(mdl.index(r, c, parent), col.text)
item.setText(col.text)
if "color" in col.attrib:
#mdl.itemFromIndex(mdl.index(r, c, parent)).setIcon(iconFromColorString(col.attrib["color"]))
item.setIcon(iconFromColorString(col.attrib["color"]))
if len(col) != 0:
#loadItem(col, mdl, mdl.index(r, c, parent))
loadItem(col, mdl, mdl.indexFromItem(item))
with open(project, "r") as f:
version = int(f.read())
print("Loading:", project)
print("Detected file format version: {}. Zip: {}.".format(version, isZip))
if version == 0:
v0.loadProject(project)
else:
v1.loadProject(project, zip=isZip)

View file

View file

@ -0,0 +1,289 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
# Version 0 of file saving format.
# Was used at the begining and up util version XXX when
# it was superseded by Version 1, which is more open and flexible
import zipfile
from PyQt5.QtCore import QModelIndex, Qt
from PyQt5.QtGui import QColor, QStandardItem
from PyQt5.QtWidgets import qApp
from lxml import etree as ET
from manuskript import settings
from manuskript.functions import iconColor, iconFromColorString, mainWindow
from manuskript.models.characterModel import Character, CharacterInfo
try:
import zlib # Used with zipfile for compression
compression = zipfile.ZIP_DEFLATED
except:
compression = zipfile.ZIP_STORED
###########################################################################################
# SAVE
###########################################################################################
def saveProject():
"""
Saves the whole project. Call this function to save the project in Version 0 format.
"""
files = []
mw = mainWindow()
files.append((saveStandardItemModelXML(mw.mdlFlatData),
"flatModel.xml"))
print("ERROR: file format 0 does not save characters !")
# files.append((saveStandardItemModelXML(mw.mdlCharacter),
# "perso.xml"))
files.append((saveStandardItemModelXML(mw.mdlWorld),
"world.xml"))
files.append((saveStandardItemModelXML(mw.mdlLabels),
"labels.xml"))
files.append((saveStandardItemModelXML(mw.mdlStatus),
"status.xml"))
files.append((saveStandardItemModelXML(mw.mdlPlots),
"plots.xml"))
files.append((mw.mdlOutline.saveToXML(),
"outline.xml"))
files.append((settings.save(),
"settings.pickle"))
saveFilesToZip(files, mw.currentProject)
def saveFilesToZip(files, zipname):
"""Saves given files to zipname.
files is actually a list of (content, filename)."""
zf = zipfile.ZipFile(zipname, mode="w")
for content, filename in files:
zf.writestr(filename, content, compress_type=compression)
zf.close()
def saveStandardItemModelXML(mdl, xml=None):
"""Saves the given QStandardItemModel to XML.
If xml (filename) is given, saves to xml. Otherwise returns as string."""
root = ET.Element("model")
root.attrib["version"] = qApp.applicationVersion()
# Header
header = ET.SubElement(root, "header")
vHeader = ET.SubElement(header, "vertical")
for x in range(mdl.rowCount()):
vH = ET.SubElement(vHeader, "label")
vH.attrib["row"] = str(x)
vH.attrib["text"] = str(mdl.headerData(x, Qt.Vertical))
hHeader = ET.SubElement(header, "horizontal")
for y in range(mdl.columnCount()):
hH = ET.SubElement(hHeader, "label")
hH.attrib["row"] = str(y)
hH.attrib["text"] = str(mdl.headerData(y, Qt.Horizontal))
# Data
data = ET.SubElement(root, "data")
saveItem(data, mdl)
# print(qApp.tr("Saving to {}.").format(xml))
if xml:
ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True)
else:
return ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True)
def saveItem(root, mdl, parent=QModelIndex()):
for x in range(mdl.rowCount(parent)):
row = ET.SubElement(root, "row")
row.attrib["row"] = str(x)
for y in range(mdl.columnCount(parent)):
col = ET.SubElement(row, "col")
col.attrib["col"] = str(y)
if mdl.data(mdl.index(x, y, parent), Qt.DecorationRole) != None:
color = iconColor(mdl.data(mdl.index(x, y, parent), Qt.DecorationRole)).name(QColor.HexArgb)
col.attrib["color"] = color if color != "#ff000000" else "#00000000"
if mdl.data(mdl.index(x, y, parent)) != "":
col.text = mdl.data(mdl.index(x, y, parent))
if mdl.hasChildren(mdl.index(x, y, parent)):
saveItem(col, mdl, mdl.index(x, y, parent))
###########################################################################################
# LOAD
###########################################################################################
def loadProject(project):
files = loadFilesFromZip(project)
mw = mainWindow()
errors = []
if "flatModel.xml" in files:
loadStandardItemModelXML(mw.mdlFlatData,
files["flatModel.xml"], fromString=True)
else:
errors.append("flatModel.xml")
if "perso.xml" in files:
loadStandardItemModelXMLForCharacters(mw.mdlCharacter, files["perso.xml"])
else:
errors.append("perso.xml")
if "world.xml" in files:
loadStandardItemModelXML(mw.mdlWorld,
files["world.xml"], fromString=True)
else:
errors.append("world.xml")
if "labels.xml" in files:
loadStandardItemModelXML(mw.mdlLabels,
files["labels.xml"], fromString=True)
else:
errors.append("labels.xml")
if "status.xml" in files:
loadStandardItemModelXML(mw.mdlStatus,
files["status.xml"], fromString=True)
else:
errors.append("status.xml")
if "plots.xml" in files:
loadStandardItemModelXML(mw.mdlPlots,
files["plots.xml"], fromString=True)
else:
errors.append("plots.xml")
if "outline.xml" in files:
mw.mdlOutline.loadFromXML(files["outline.xml"], fromString=True)
else:
errors.append("outline.xml")
if "settings.pickle" in files:
settings.load(files["settings.pickle"], fromString=True)
else:
errors.append("settings.pickle")
return errors
def loadFilesFromZip(zipname):
"""Returns the content of zipfile as a dict of filename:content."""
zf = zipfile.ZipFile(zipname)
files = {}
for f in zf.namelist():
files[f] = zf.read(f)
return files
def loadStandardItemModelXML(mdl, xml, fromString=False):
"""Load data to a QStandardItemModel mdl from xml.
By default xml is a filename. If fromString=True, xml is a string containg the data."""
# print(qApp.tr("Loading {}... ").format(xml), end="")
if not fromString:
try:
tree = ET.parse(xml)
except:
print("Failed.")
return
else:
root = ET.fromstring(xml)
# root = tree.getroot()
# Header
hLabels = []
vLabels = []
for l in root.find("header").find("horizontal").findall("label"):
hLabels.append(l.attrib["text"])
for l in root.find("header").find("vertical").findall("label"):
vLabels.append(l.attrib["text"])
# print(root.find("header").find("vertical").text)
# mdl.setVerticalHeaderLabels(vLabels)
# mdl.setHorizontalHeaderLabels(hLabels)
# Populates with empty items
for i in enumerate(vLabels):
row = []
for r in enumerate(hLabels):
row.append(QStandardItem())
mdl.appendRow(row)
# Data
data = root.find("data")
loadItem(data, mdl)
return True
def loadItem(root, mdl, parent=QModelIndex()):
for row in root:
r = int(row.attrib["row"])
for col in row:
c = int(col.attrib["col"])
item = mdl.itemFromIndex(mdl.index(r, c, parent))
if not item:
item = QStandardItem()
mdl.itemFromIndex(parent).setChild(r, c, item)
if col.text:
# mdl.setData(mdl.index(r, c, parent), col.text)
item.setText(col.text)
if "color" in col.attrib:
# mdl.itemFromIndex(mdl.index(r, c, parent)).setIcon(iconFromColorString(col.attrib["color"]))
item.setIcon(iconFromColorString(col.attrib["color"]))
if len(col) != 0:
# loadItem(col, mdl, mdl.index(r, c, parent))
loadItem(col, mdl, mdl.indexFromItem(item))
def loadStandardItemModelXMLForCharacters(mdl, xml):
"""
Loads a standardItemModel saved to XML by version 0, but for the new characterModel.
@param mdl: characterModel
@param xml: the content of the xml
@return: nothing
"""
mdl = mainWindow().mdlCharacter
root = ET.fromstring(xml)
data = root.find("data")
for row in data:
char = Character(mdl)
for col in row:
c = int(col.attrib["col"])
# Value
if col.text:
char._data[c] = col.text
# Color
if "color" in col.attrib:
char.setColor(QColor(col.attrib["color"]))
# Infos
if len(col) != 0:
for rrow in col:
info = CharacterInfo(char)
for ccol in rrow:
cc = int(ccol.attrib["col"])
if cc == 11 and ccol.text:
info.description = ccol.text
if cc == 12 and ccol.text:
info.value = ccol.text
char.infos.append(info)
mdl.characters.append(char)

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
import faulthandler
import os
import sys
from PyQt5.QtCore import QLocale, QTranslator, QSettings
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, qApp
from manuskript.functions import appPath
from manuskript.functions import appPath, writablePath
_version = "0.2.0"
_version = "0.3.0"
faulthandler.enable()
@ -28,27 +29,33 @@ def run():
app.setStyle("Fusion")
# Translation process
locale = QLocale.system().name()
# locale = "fr_CH"
appTranslator = QTranslator()
if appTranslator.load(appPath("i18n/manuskript_{}.qm").format(locale)):
app.installTranslator(appTranslator)
print(app.tr("Loaded transation: {}.").format(locale))
else:
print(app.tr("Failed to load translator for {}...").format(locale))
# Load style from QSettings
settings = QSettings(app.organizationName(), app.applicationName())
if settings.contains("applicationStyle"):
style = settings.value("applicationStyle")
app.setStyle(style)
# Translation process
locale = QLocale.system().name()
appTranslator = QTranslator()
# By default: locale
translation = appPath(os.path.join("i18n", "manuskript_{}.qm".format(locale)))
# Load translation from settings
if settings.contains("applicationTranslation"):
translation = appPath(os.path.join("i18n", settings.value("applicationTranslation")))
print("Found translation in settings:", translation)
if appTranslator.load(translation):
app.installTranslator(appTranslator)
print(app.tr("Loaded translation: {}.").format(translation))
else:
print(app.tr("Warning: failed to load translator for locale {}...").format(locale))
QIcon.setThemeSearchPaths(QIcon.themeSearchPaths() + [appPath("icons")])
QIcon.setThemeName("NumixMsk")
print(QIcon.hasThemeIcon("dialog-no"))
print(QIcon.themeSearchPaths())
# qApp.setWindowIcon(QIcon.fromTheme("im-aim"))
# Seperating launch to avoid segfault, so it seem.

View file

@ -9,13 +9,11 @@ from PyQt5.QtWidgets import QMainWindow, QHeaderView, qApp, QMenu, QActionGroup,
QLabel
from manuskript import settings
from manuskript.enums import Perso, Subplot, Plot, World
from manuskript.functions import AUC, wordCount, appPath
from manuskript.loadSave import loadStandardItemModelXML, loadFilesFromZip
from manuskript.loadSave import saveFilesToZip
from manuskript.loadSave import saveStandardItemModelXML
from manuskript.enums import Character, PlotStep, Plot, World, Outline
from manuskript.functions import AUC, wordCount, appPath, findWidgetsOfClass
from manuskript import loadSave
from manuskript.models.characterModel import characterModel
from manuskript.models.outlineModel import outlineModel
from manuskript.models.persosModel import persosModel
from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.settingsWindow import settingsWindow
@ -24,7 +22,7 @@ from manuskript.ui.compileDialog import compileDialog
from manuskript.ui.helpLabel import helpLabel
from manuskript.ui.mainWindow import Ui_MainWindow
from manuskript.ui.tools.frequencyAnalyzer import frequencyAnalyzer
from manuskript.ui.views.outlineDelegates import outlinePersoDelegate
from manuskript.ui.views.outlineDelegates import outlineCharacterDelegate
from manuskript.ui.views.plotDelegate import plotDelegate
# Spellcheck support
@ -60,13 +58,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Welcome
self.welcome.updateValues()
# self.welcome.btnCreate.clicked.connect
self.stack.setCurrentIndex(0)
# Word count
self.mprWordCount = QSignalMapper(self)
for t, i in [
(self.txtSummarySentance, 0),
(self.txtSummarySentence, 0),
(self.txtSummaryPara, 1),
(self.txtSummaryPage, 2),
(self.txtSummaryFull, 3)
@ -96,8 +93,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Main Menu
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuMode, self.menuView, self.menuTools,
self.menuHelp]:
self.menuEdit, self.menuView, self.menuTools, self.menuHelp]:
i.setEnabled(False)
self.actOpen.triggered.connect(self.welcome.openFile)
@ -112,6 +108,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.actToolFrequency.triggered.connect(self.frequencyAnalyzer)
self.generateViewMenu()
self.actModeGroup = QActionGroup(self)
self.actModeSimple.setActionGroup(self.actModeGroup)
self.actModeFiction.setActionGroup(self.actModeGroup)
self.actModeSnowflake.setActionGroup(self.actModeGroup)
self.actModeSimple.triggered.connect(self.setViewModeSimple)
self.actModeFiction.triggered.connect(self.setViewModeFiction)
self.actModeSnowflake.setEnabled(False)
self.makeUIConnections()
# self.loadProject(os.path.join(appPath(), "test_project.zip"))
@ -141,18 +145,21 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.treeOutlineOutline.delete()
###############################################################################
# PERSOS
# CHARACTERS
###############################################################################
def changeCurrentPerso(self, trash=None):
def changeCurrentCharacter(self, trash=None):
"""
index = self.lstPersos.currentPersoIndex()
if not index.isValid():
@return:
"""
c = self.lstCharacters.currentCharacter()
if not c:
self.tabPlot.setEnabled(False)
return
self.tabPersos.setEnabled(True)
index = c.index()
for w in [
self.txtPersoName,
@ -161,7 +168,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.txtPersoGoal,
self.txtPersoConflict,
self.txtPersoEpiphany,
self.txtPersoSummarySentance,
self.txtPersoSummarySentence,
self.txtPersoSummaryPara,
self.txtPersoSummaryFull,
self.txtPersoNotes,
@ -169,27 +176,24 @@ class MainWindow(QMainWindow, Ui_MainWindow):
w.setCurrentModelIndex(index)
# Button color
self.mdlPersos.updatePersoColor(index)
self.updateCharacterColor(c.ID())
# Perso Infos
# Character Infos
self.tblPersoInfos.setRootIndex(index)
if self.mdlPersos.rowCount(index):
if self.mdlCharacter.rowCount(index):
self.updatePersoInfoView()
def updatePersoInfoView(self):
# Hide columns
for i in range(self.mdlPersos.columnCount()):
self.tblPersoInfos.hideColumn(i)
self.tblPersoInfos.showColumn(Perso.infoName.value)
self.tblPersoInfos.showColumn(Perso.infoData.value)
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(
Perso.infoName.value, QHeaderView.ResizeToContents)
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(
Perso.infoData.value, QHeaderView.Stretch)
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
self.tblPersoInfos.verticalHeader().hide()
def updateCharacterColor(self, ID):
c = self.mdlCharacter.getCharacterByID(ID)
color = c.color().name()
self.btnPersoColor.setStyleSheet("background:{};".format(color))
###############################################################################
# PLOTS
###############################################################################
@ -207,8 +211,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.txtPlotResult.setCurrentModelIndex(index)
self.sldPlotImportance.setCurrentModelIndex(index)
self.lstPlotPerso.setRootIndex(index.sibling(index.row(),
Plot.persos.value))
subplotindex = index.sibling(index.row(), Plot.subplots.value)
Plot.characters.value))
subplotindex = index.sibling(index.row(), Plot.steps.value)
self.lstSubPlots.setRootIndex(subplotindex)
if self.mdlPlots.rowCount(subplotindex):
self.updateSubPlotView()
@ -224,18 +228,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Hide columns
for i in range(self.mdlPlots.columnCount()):
self.lstSubPlots.hideColumn(i)
self.lstSubPlots.showColumn(Subplot.name.value)
self.lstSubPlots.showColumn(Subplot.meta.value)
self.lstSubPlots.showColumn(PlotStep.name.value)
self.lstSubPlots.showColumn(PlotStep.meta.value)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
Subplot.name.value, QHeaderView.Stretch)
PlotStep.name.value, QHeaderView.Stretch)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
Subplot.meta.value, QHeaderView.ResizeToContents)
PlotStep.meta.value, QHeaderView.ResizeToContents)
self.lstSubPlots.verticalHeader().hide()
def changeCurrentSubPlot(self, index):
# Got segfaults when using textEditView model system, so ad hoc stuff.
index = index.sibling(index.row(), Subplot.summary.value)
index = index.sibling(index.row(), PlotStep.summary.value)
item = self.mdlPlots.itemFromIndex(index)
if not item:
self.txtSubPlotSummary.setEnabled(False)
@ -253,7 +257,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
index = self.lstSubPlots.currentIndex()
if not index.isValid():
return
index = index.sibling(index.row(), Subplot.summary.value)
index = index.sibling(index.row(), PlotStep.summary.value)
item = self.mdlPlots.itemFromIndex(index)
self._updatingSubPlot = True
@ -280,11 +284,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.txtWorldName.setCurrentModelIndex(index)
self.txtWorldDescription.setCurrentModelIndex(index)
self.txtWorldPassion.setCurrentModelIndex(index)
self.txtWorldConflict.setCurrentModelIndex(index)
###############################################################################
# LOAD AND SAVE
###############################################################################
# self.txtWorldConflict.setCurrentModelIndex(index)
#
# ###############################################################################
# # LOAD AND SAVE
# ###############################################################################
def loadProject(self, project, loadFromFile=True):
"""Loads the project ``project``.
@ -310,7 +314,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Load settings
for i in settings.openIndexes:
idx = self.mdlOutline.indexFromPath(i)
idx = self.mdlOutline.getIndexByID(i)
self.mainEditor.setCurrentModelIndex(idx, newTab=True)
self.generateViewMenu()
self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor)
@ -325,6 +329,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# We force to emit even if it opens on the current tab
self.tabMain.currentChanged.emit(settings.lastTab)
self.mainEditor.updateCorkBackground()
if settings.viewMode == "simple":
self.setViewModeSimple()
else:
self.setViewModeFiction()
# Set autosave
self.saveTimer = QTimer()
@ -340,7 +348,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.saveTimerNoChanges.setSingleShot(True)
self.mdlFlatData.dataChanged.connect(self.startTimerNoChanges)
self.mdlOutline.dataChanged.connect(self.startTimerNoChanges)
self.mdlPersos.dataChanged.connect(self.startTimerNoChanges)
self.mdlCharacter.dataChanged.connect(self.startTimerNoChanges)
self.mdlPlots.dataChanged.connect(self.startTimerNoChanges)
self.mdlWorld.dataChanged.connect(self.startTimerNoChanges)
# self.mdlPersosInfos.dataChanged.connect(self.startTimerNoChanges)
@ -352,8 +360,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# UI
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuMode, self.menuView, self.menuTools,
self.menuHelp]:
self.menuEdit, self.menuView, self.menuTools, self.menuHelp]:
i.setEnabled(True)
# FIXME: set Window's name: project name
@ -367,23 +374,27 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.stack.setCurrentIndex(1)
def closeProject(self):
if not self.currentProject:
return
# Close open tabs in editor
self.mainEditor.closeAllTabs()
# Save datas
self.saveDatas()
self.currentProject = None
QSettings().setValue("lastProject", "")
# FIXME: close all opened tabs in mainEditor
# Clear datas
self.loadEmptyDatas()
self.saveTimer.stop()
loadSave.clearSaveCache()
# UI
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuMode, self.menuView, self.menuTools,
self.menuHelp]:
self.menuEdit, self.menuView, self.menuTools, self.menuHelp]:
i.setEnabled(False)
# Reload recent files
@ -434,10 +445,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
settings.lastTab = self.tabMain.currentIndex()
if self.currentProject:
# Remembering the current items
# Remembering the current items (stores outlineItem's ID)
sel = []
for i in range(self.mainEditor.tab.count()):
sel.append(self.mdlOutline.pathToIndex(self.mainEditor.tab.widget(i).currentIndex))
sel.append(self.mdlOutline.ID(self.mainEditor.tab.widget(i).currentIndex))
settings.openIndexes = sel
# Save data from models
@ -462,27 +473,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.currentProject = projectName
QSettings().setValue("lastProject", projectName)
# Saving
files = []
files.append((saveStandardItemModelXML(self.mdlFlatData),
"flatModel.xml"))
files.append((saveStandardItemModelXML(self.mdlPersos),
"perso.xml"))
files.append((saveStandardItemModelXML(self.mdlWorld),
"world.xml"))
files.append((saveStandardItemModelXML(self.mdlLabels),
"labels.xml"))
files.append((saveStandardItemModelXML(self.mdlStatus),
"status.xml"))
files.append((saveStandardItemModelXML(self.mdlPlots),
"plots.xml"))
files.append((self.mdlOutline.saveToXML(),
"outline.xml"))
files.append((settings.save(),
"settings.pickle"))
saveFilesToZip(files, self.currentProject)
loadSave.saveProject() # version=0
self.saveTimerNoChanges.stop()
# Giving some feedback
print(self.tr("Project {} saved.").format(self.currentProject))
@ -491,7 +483,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def loadEmptyDatas(self):
self.mdlFlatData = QStandardItemModel(self)
self.mdlPersos = persosModel(self)
self.mdlCharacter = characterModel(self)
# self.mdlPersosProxy = persosProxyModel(self)
# self.mdlPersosInfos = QStandardItemModel(self)
self.mdlLabels = QStandardItemModel(self)
@ -501,56 +493,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.mdlWorld = worldModel(self)
def loadDatas(self, project):
# Loading
files = loadFilesFromZip(project)
errors = []
if "flatModel.xml" in files:
loadStandardItemModelXML(self.mdlFlatData,
files["flatModel.xml"], fromString=True)
else:
errors.append("flatModel.xml")
if "perso.xml" in files:
loadStandardItemModelXML(self.mdlPersos,
files["perso.xml"], fromString=True)
else:
errors.append("perso.xml")
if "world.xml" in files:
loadStandardItemModelXML(self.mdlWorld,
files["world.xml"], fromString=True)
else:
errors.append("world.xml")
if "labels.xml" in files:
loadStandardItemModelXML(self.mdlLabels,
files["labels.xml"], fromString=True)
else:
errors.append("perso.xml")
if "status.xml" in files:
loadStandardItemModelXML(self.mdlStatus,
files["status.xml"], fromString=True)
else:
errors.append("perso.xml")
if "plots.xml" in files:
loadStandardItemModelXML(self.mdlPlots,
files["plots.xml"], fromString=True)
else:
errors.append("perso.xml")
if "outline.xml" in files:
self.mdlOutline.loadFromXML(files["outline.xml"], fromString=True)
else:
errors.append("perso.xml")
if "settings.pickle" in files:
settings.load(files["settings.pickle"], fromString=True)
else:
errors.append("perso.xml")
errors = loadSave.loadProject(project)
# Giving some feedback
if not errors:
@ -569,8 +513,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
###############################################################################
def makeUIConnections(self):
"Connections that have to be made once only, event when new project is loaded."
self.lstPersos.currentItemChanged.connect(self.changeCurrentPerso, AUC)
"Connections that have to be made once only, even when a new project is loaded."
self.lstCharacters.currentItemChanged.connect(self.changeCurrentCharacter, AUC)
self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, AUC)
self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, AUC)
@ -592,8 +536,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Flat datas (Summary and general infos)
for widget, col in [
(self.txtSummarySituation, 0),
(self.txtSummarySentance, 1),
(self.txtSummarySentance_2, 1),
(self.txtSummarySentence, 1),
(self.txtSummarySentence_2, 1),
(self.txtSummaryPara, 2),
(self.txtSummaryPara_2, 2),
(self.txtPlotSummaryPara, 2),
@ -621,30 +565,33 @@ class MainWindow(QMainWindow, Ui_MainWindow):
widget.setColumn(col)
widget.setCurrentModelIndex(self.mdlFlatData.index(0, col))
# Persos
self.lstPersos.setPersosModel(self.mdlPersos)
self.tblPersoInfos.setModel(self.mdlPersos)
# Characters
self.lstCharacters.setCharactersModel(self.mdlCharacter)
self.tblPersoInfos.setModel(self.mdlCharacter)
self.btnAddPerso.clicked.connect(self.mdlPersos.addPerso, AUC)
self.btnRmPerso.clicked.connect(self.mdlPersos.removePerso, AUC)
self.btnPersoColor.clicked.connect(self.mdlPersos.chosePersoColor, AUC)
self.btnPersoAddInfo.clicked.connect(self.mdlPersos.addPersoInfo, AUC)
self.btnPersoRmInfo.clicked.connect(self.mdlPersos.removePersoInfo, AUC)
self.btnAddPerso.clicked.connect(self.mdlCharacter.addCharacter, AUC)
try:
self.btnRmPerso.clicked.connect(self.lstCharacters.removeCharacter, AUC)
self.btnPersoColor.clicked.connect(self.lstCharacters.choseCharacterColor, AUC)
self.btnPersoAddInfo.clicked.connect(self.lstCharacters.addCharacterInfo, AUC)
self.btnPersoRmInfo.clicked.connect(self.lstCharacters.removeCharacterInfo, AUC)
except TypeError:
# Connection has already been made
pass
for w, c in [
(self.txtPersoName, Perso.name.value),
(self.sldPersoImportance, Perso.importance.value),
(self.txtPersoMotivation, Perso.motivation.value),
(self.txtPersoGoal, Perso.goal.value),
(self.txtPersoConflict, Perso.conflict.value),
(self.txtPersoEpiphany, Perso.epiphany.value),
(self.txtPersoSummarySentance, Perso.summarySentance.value),
(self.txtPersoSummaryPara, Perso.summaryPara.value),
(self.txtPersoSummaryFull, Perso.summaryFull.value),
(self.txtPersoNotes, Perso.notes.value)
(self.txtPersoName, Character.name.value),
(self.sldPersoImportance, Character.importance.value),
(self.txtPersoMotivation, Character.motivation.value),
(self.txtPersoGoal, Character.goal.value),
(self.txtPersoConflict, Character.conflict.value),
(self.txtPersoEpiphany, Character.epiphany.value),
(self.txtPersoSummarySentence, Character.summarySentence.value),
(self.txtPersoSummaryPara, Character.summaryPara.value),
(self.txtPersoSummaryFull, Character.summaryFull.value),
(self.txtPersoNotes, Character.notes.value)
]:
w.setModel(self.mdlPersos)
w.setModel(self.mdlCharacter)
w.setColumn(c)
self.tabPersos.setEnabled(False)
@ -672,13 +619,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.tabPlot.setEnabled(False)
self.mdlPlots.updatePlotPersoButton()
self.mdlPersos.dataChanged.connect(self.mdlPlots.updatePlotPersoButton)
self.mdlCharacter.dataChanged.connect(self.mdlPlots.updatePlotPersoButton)
self.lstOutlinePlots.setPlotModel(self.mdlPlots)
self.lstOutlinePlots.setShowSubPlot(True)
self.plotPersoDelegate = outlinePersoDelegate(self.mdlPersos, self)
self.lstPlotPerso.setItemDelegate(self.plotPersoDelegate)
self.plotCharacterDelegate = outlineCharacterDelegate(self.mdlCharacter, self)
self.lstPlotPerso.setItemDelegate(self.plotCharacterDelegate)
self.plotDelegate = plotDelegate(self)
self.lstSubPlots.setItemDelegateForColumn(Subplot.meta.value, self.plotDelegate)
self.lstSubPlots.setItemDelegateForColumn(PlotStep.meta.value, self.plotDelegate)
# World
self.treeWorld.setModel(self.mdlWorld)
@ -702,18 +649,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Outline
self.treeRedacOutline.setModel(self.mdlOutline)
self.treeOutlineOutline.setModelPersos(self.mdlPersos)
self.treeOutlineOutline.setModelCharacters(self.mdlCharacter)
self.treeOutlineOutline.setModelLabels(self.mdlLabels)
self.treeOutlineOutline.setModelStatus(self.mdlStatus)
self.redacMetadata.setModels(self.mdlOutline, self.mdlPersos,
self.redacMetadata.setModels(self.mdlOutline, self.mdlCharacter,
self.mdlLabels, self.mdlStatus)
self.outlineItemEditor.setModels(self.mdlOutline, self.mdlPersos,
self.outlineItemEditor.setModels(self.mdlOutline, self.mdlCharacter,
self.mdlLabels, self.mdlStatus)
self.treeOutlineOutline.setModel(self.mdlOutline)
# self.redacEditor.setModel(self.mdlOutline)
self.storylineView.setModels(self.mdlOutline, self.mdlPersos, self.mdlPlots)
self.storylineView.setModels(self.mdlOutline, self.mdlCharacter, self.mdlPlots)
self.treeOutlineOutline.selectionModel().selectionChanged.connect(lambda:
self.outlineItemEditor.selectionChanged(
@ -735,12 +682,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Debug
self.mdlFlatData.setVerticalHeaderLabels(["Infos générales", "Summary"])
self.tblDebugFlatData.setModel(self.mdlFlatData)
self.tblDebugPersos.setModel(self.mdlPersos)
self.tblDebugPersosInfos.setModel(self.mdlPersos)
self.tblDebugPersos.setModel(self.mdlCharacter)
self.tblDebugPersosInfos.setModel(self.mdlCharacter)
self.tblDebugPersos.selectionModel().currentChanged.connect(
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlPersos.index(
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
self.tblDebugPersos.selectionModel().currentIndex().row(),
Perso.name.value)), AUC)
Character.name.value)), AUC)
self.tblDebugPlots.setModel(self.mdlPlots)
self.tblDebugPlotsPersos.setModel(self.mdlPlots)
@ -748,11 +695,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.tblDebugPlots.selectionModel().currentChanged.connect(
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.persos.value)), AUC)
Plot.characters.value)), AUC)
self.tblDebugPlots.selectionModel().currentChanged.connect(
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.subplots.value)), AUC)
Plot.steps.value)), AUC)
self.treeDebugWorld.setModel(self.mdlWorld)
self.treeDebugOutline.setModel(self.mdlOutline)
self.lstDebugLabels.setModel(self.mdlLabels)
@ -787,14 +734,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def wordCount(self, i):
src = {
0: self.txtSummarySentance,
0: self.txtSummarySentence,
1: self.txtSummaryPara,
2: self.txtSummaryPage,
3: self.txtSummaryFull
}[i]
lbl = {
0: self.lblSummaryWCSentance,
0: self.lblSummaryWCSentence,
1: self.lblSummaryWCPara,
2: self.lblSummaryWCPage,
3: self.lblSummaryWCFull
@ -881,7 +828,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1),
(self.lytSummary,
self.tr(
"""Take time to think about a one sentance (~50 words) summary of your book. Then expand it to
"""Take time to think about a one sentence (~50 words) summary of your book. Then expand it to
a paragraph, then to a page, then to a full summary."""),
1),
(self.lytTabPersos,
@ -1046,6 +993,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
}
self.menuView.clear()
self.menuView.addMenu(self.menuMode)
self.menuView.addSeparator()
# print("Generating menus with", settings.viewSettings)
@ -1081,6 +1030,48 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if item == "Tree":
self.treeRedacOutline.viewport().update()
###############################################################################
# VIEW MODES
###############################################################################
def setViewModeSimple(self):
settings.viewMode = "simple"
self.tabMain.setCurrentIndex(self.TabRedac)
self.viewModeFictionVisibilitySwitch(False)
self.actModeSimple.setChecked(True)
def setViewModeFiction(self):
settings.viewMode = "fiction"
self.viewModeFictionVisibilitySwitch(True)
self.actModeFiction.setChecked(True)
def viewModeFictionVisibilitySwitch(self, val):
"""
Swtiches the visibility of some UI components useful for fiction only
@param val: sets visibility to val
"""
# Menu navigation & boutton in toolbar
self.toolbar.setDockVisibility(self.dckNavigation, val)
# POV in metadatas
from manuskript.ui.views.propertiesView import propertiesView
for w in findWidgetsOfClass(propertiesView):
w.lblPOV.setVisible(val)
w.cmbPOV.setVisible(val)
# POV in outline view
if Outline.POV.value in settings.outlineViewColumns:
settings.outlineViewColumns.remove(Outline.POV.value)
from manuskript.ui.views.outlineView import outlineView
for w in findWidgetsOfClass(outlineView):
w.hideColumns()
# TODO: clean up all other fiction things in non-fiction view mode
# Character in search widget
# POV in settings / views
###############################################################################
# COMPILE
###############################################################################

View file

@ -0,0 +1,299 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
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
class characterModel(QAbstractItemModel):
def __init__(self, parent):
QAbstractItemModel.__init__(self, parent)
# CharacterItems are stored in this list
self.characters = []
###############################################################################
# QAbstractItemModel subclassed
###############################################################################
def rowCount(self, parent=QModelIndex()):
if parent.isValid():
c = parent.internalPointer()
return len(c.infos)
else:
return len(self.characters)
def columnCount(self, parent=QModelIndex()):
if parent.isValid():
# Returns characters infos
return 2
else:
return len(C)
def data(self, index, role=Qt.DisplayRole):
c = index.internalPointer()
if type(c) == Character:
if role == Qt.DisplayRole:
if index.column() in c._data:
return c._data[index.column()]
else:
return ""
elif role == Qt.DecorationRole:
if index.column() == C.name.value:
return c.icon
else:
return QVariant()
elif type(c) == CharacterInfo:
if role == Qt.DisplayRole or role == Qt.EditRole:
if index.column() == 0:
return c.description
elif index.column() == 1:
return c.value
def setData(self, index, value, role=Qt.EditRole):
c = index.internalPointer()
if type(c) == Character:
if role == Qt.EditRole:
# We update only if data is different
if index.column() not in c._data or c._data[index.column()] != value:
c._data[index.column()] = value
self.dataChanged.emit(index, index)
return True
elif type(c) == CharacterInfo:
if role == Qt.EditRole:
if index.column() == 0:
c.description = value
elif index.column() == 1:
c.value = value
self.dataChanged.emit(index, index)
return True
return False
def index(self, row, column, parent=QModelIndex()):
if not parent.isValid():
return self.createIndex(row, column, self.characters[row])
else:
c = parent.internalPointer()
if row < len(c.infos):
return self.createIndex(row, column, c.infos[row])
else:
return QModelIndex()
def indexFromItem(self, item, column=0):
if not item:
return QModelIndex()
row = self.characters.index(item)
col = column
return self.createIndex(row, col, item)
def parent(self, index):
if not index.isValid():
return QModelIndex()
child = index.internalPointer()
if type(child) == Character:
return QModelIndex()
elif type(child) == CharacterInfo:
return child.character.index()
def flags(self, index):
if index.parent().isValid():
return QAbstractItemModel.flags(self, index) | Qt.ItemIsEditable
else:
return QAbstractItemModel.flags(self, index)
###############################################################################
# CHARACTER QUERRIES
###############################################################################
def character(self, row):
return self.characters[row]
def name(self, row):
return self.character(row).name()
def icon(self, row):
return self.character(row).icon
def ID(self, row):
return self.character(row).ID()
def importance(self, row):
return self.character(row).importance()
###############################################################################
# MODEL QUERRIES
###############################################################################
def getCharactersByImportance(self):
"""
Lists characters by importance.
@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:
ID = str(ID)
for c in self.characters:
if c.ID() == ID:
return c
return None
###############################################################################
# ADDING / REMOVING
###############################################################################
def addCharacter(self):
"""
Creates a new character
@return: the character
"""
c = Character(model=self, name=self.tr("New character"))
self.beginInsertRows(QModelIndex(), len(self.characters), len(self.characters))
self.characters.append(c)
self.endInsertRows()
return c
def removeCharacter(self, ID):
"""
Removes character whose ID is ID...
@param ID: the ID of the character to remove
@return: nothing
"""
c = self.getCharacterByID(ID)
self.beginRemoveRows(QModelIndex(), self.characters.index(c), self.characters.index(c))
self.characters.remove(c)
self.endRemoveRows()
###############################################################################
# CHARACTER INFOS
###############################################################################
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
if section == 0:
return self.tr("Name")
elif section == 1:
return self.tr("Value")
else:
return C(section).name
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"))
self.endInsertRows()
mainWindow().updatePersoInfoView()
def removeCharacterInfo(self, ID):
c = self.getCharacterByID(ID)
rm = []
for idx in mainWindow().tblPersoInfos.selectedIndexes():
if not idx.row() in rm:
rm.append(idx.row())
rm.sort()
rm.reverse()
for r in rm:
self.beginRemoveRows(c.index(), r, r)
c.infos.pop(r)
self.endRemoveRows()
###############################################################################
# CHARACTER
###############################################################################
class Character():
def __init__(self, model, name="No name"):
self._model = model
self.lastPath = ""
self._data = {}
self._data[C.name.value] = name
self.assignUniqueID()
self.assignRandomColor()
self._data[C.importance.value] = "0"
self.infos = []
def name(self):
return self._data[C.name.value]
def importance(self):
return self._data[C.importance.value]
def ID(self):
return self._data[C.ID.value]
def index(self, column=0):
return self._model.indexFromItem(self, column)
def assignRandomColor(self):
"""
Assigns a random color the the character.
"""
color = randomColor(QColor(Qt.white))
self.setColor(color)
def setColor(self, color):
"""
Sets the character's color
@param color: QColor.
"""
px = QPixmap(32, 32)
px.fill(color)
self.icon = QIcon(px)
try:
self._model.dataChanged.emit(self.index(), self.index())
except:
# If it is the initialisation, won't be able to emit
pass
def color(self):
"""
Returns character's color in QColor
@return: QColor
"""
return iconColor(self.icon)
def assignUniqueID(self, parent=QModelIndex()):
"""Assigns an unused character ID."""
vals = []
for c in self._model.characters:
vals.append(int(c.ID()))
k = 0
while k in vals:
k += 1
self._data[C.ID.value] = str(k)
def listInfos(self):
r = []
for i in self.infos:
r.append((i.description, i.value))
return r
class CharacterInfo():
def __init__(self, character, description="", value=""):
self.description = description
self.value = value
self.character = character

View file

@ -15,10 +15,10 @@ from manuskript import settings
from lxml import etree as ET
from manuskript.enums import Outline
from manuskript.functions import mainWindow, toInt, wordCount
from manuskript.functions import mainWindow, toInt, wordCount, HTML2PlainText
locale.setlocale(locale.LC_ALL, '')
import time
import time, os
class outlineModel(QAbstractItemModel):
@ -27,6 +27,9 @@ class outlineModel(QAbstractItemModel):
self.rootItem = outlineItem(self, title="root", ID="0")
# Stores removed item, in order to remove them on disk when saving, depending on the file format.
self.removed = []
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
@ -74,9 +77,7 @@ class outlineModel(QAbstractItemModel):
in columns ``columns`` (being a list of int)."""
return self.rootItem.findItemsContaining(text, columns, mainWindow(), caseSensitive)
def getIndexByID(self, ID):
"Returns the index of item whose ID is ``ID``. If none, returns QModelIndex()."
def getItemByID(self, ID):
def search(item):
if item.ID() == ID:
return item
@ -86,6 +87,11 @@ class outlineModel(QAbstractItemModel):
return r
item = search(self.rootItem)
return item
def getIndexByID(self, ID):
"Returns the index of item whose ID is ``ID``. If none, returns QModelIndex()."
item = self.getItemByID(ID)
if not item:
return QModelIndex()
else:
@ -363,7 +369,8 @@ class outlineModel(QAbstractItemModel):
self.beginRemoveRows(parent, row, row + count - 1)
for i in range(count):
parentItem.removeChild(row)
item = parentItem.removeChild(row)
self.removed.append(item)
self.endRemoveRows()
return True
@ -400,19 +407,6 @@ class outlineModel(QAbstractItemModel):
self.rootItem = outlineItem(model=self, xml=ET.tostring(root), ID="0")
self.rootItem.checkIDs()
def pathToIndex(self, index, path=""):
# FIXME: Use item's ID instead of rows
if not index.isValid():
return ""
if index.parent().isValid():
path = self.pathToIndex(index.parent())
if path:
path = "{},{}".format(path, str(index.row()))
else:
path = str(index.row())
return path
def indexFromPath(self, path):
path = path.split(",")
item = self.rootItem
@ -431,6 +425,8 @@ class outlineItem():
self._model = model
self.defaultTextType = None
self.IDs = [] # used by root item to store unique IDs
self._lastPath = "" # used by loadSave version_1 to remember which files the items comes from,
# in case it is renamed / removed
if title:
self._data[Outline.title] = title
@ -485,12 +481,8 @@ class outlineItem():
elif role == Qt.DecorationRole and column == Outline.title.value:
if self.isFolder():
return QIcon.fromTheme("folder")
elif self.isText():
elif self.isMD():
return QIcon.fromTheme("text-x-generic")
elif self.isT2T():
return QIcon.fromTheme("text-x-script")
elif self.isHTML():
return QIcon.fromTheme("text-html")
# elif role == Qt.ForegroundRole:
# if self.isCompile() in [0, "0"]:
@ -531,17 +523,7 @@ class outlineItem():
updateWordCount = not Outline(column) in self._data or self._data[Outline(column)] != data
# Stuff to do before
if column == Outline.type.value:
oldType = self._data[Outline.type]
if oldType == "html" and data in ["txt", "t2t"]:
# Resource inneficient way to convert HTML to plain text
e = QTextEdit()
e.setHtml(self._data[Outline.text])
self._data[Outline.text] = e.toPlainText()
elif oldType in ["txt", "t2t"] and data == "html" and Outline.text in self._data:
self._data[Outline.text] = self._data[Outline.text].replace("\n", "<br>")
elif column == Outline.text.value:
if column == Outline.text.value:
self.addRevision()
# Setting data
@ -603,7 +585,7 @@ class outlineItem():
self.parent().updateWordCount(emit)
def row(self):
if self.parent:
if self.parent():
return self.parent().childItems.index(self)
def appendChild(self, child):
@ -645,9 +627,15 @@ class outlineItem():
c.emitDataChanged(cols, recursive=True)
def removeChild(self, row):
self.childItems.pop(row)
"""
Removes child at position `row` and returns it.
@param row: index (int) of the child to remove.
@return: the removed outlineItem
"""
r = self.childItems.pop(row)
# Might be causing segfault when updateWordCount emits dataChanged
self.updateWordCount(emit=False)
return r
def parent(self):
return self._parent
@ -658,14 +646,14 @@ class outlineItem():
def isFolder(self):
return self._data[Outline.type] == "folder"
def isT2T(self):
return self._data[Outline.type] == "t2t"
def isHTML(self):
return self._data[Outline.type] == "html"
def isText(self):
return self._data[Outline.type] == "txt"
return self._data[Outline.type] == "md"
def isMD(self):
return self._data[Outline.type] == "md"
def isMMD(self):
return self._data[Outline.type] == "md"
def text(self):
return self.data(Outline.text.value)
@ -758,6 +746,9 @@ class outlineItem():
revItem.set("text", r[1])
item.append(revItem)
# Saving lastPath
item.set("lastPath", self._lastPath)
for i in self.childItems:
item.append(ET.XML(i.toXML()))
@ -773,6 +764,18 @@ class outlineItem():
# else:
self.setData(Outline.__members__[k].value, str(root.attrib[k]))
if "lastPath" in root.attrib:
self._lastPath = root.attrib["lastPath"]
# If loading from an old file format, convert to md and remove html markup
if self.type() in ["txt", "t2t"]:
self.setData(Outline.type.value, "md")
elif self.type() == "html":
self.setData(Outline.type.value, "md")
self.setData(Outline.text.value, HTML2PlainText(self.data(Outline.text.value)))
self.setData(Outline.notes.value, HTML2PlainText(self.data(Outline.notes.value)))
for child in root:
if child.tag == "outlineItem":
item = outlineItem(self._model, xml=ET.tostring(child), parent=self)
@ -854,8 +857,13 @@ class outlineItem():
text = text.lower() if not caseSensitive else text
for c in columns:
if c == Outline.POV.value:
searchIn = mainWindow.mdlPersos.getPersoNameByID(self.POV())
if c == Outline.POV.value and self.POV():
c = mainWindow.mdlCharacter.getCharacterByID(self.POV())
if c:
searchIn = c.name()
else:
searchIn = ""
print("Character POV not found:", self.POV())
elif c == Outline.status.value:
searchIn = mainWindow.mdlStatus.item(toInt(self.status()), 0).text()

View file

@ -1,183 +0,0 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import QModelIndex, Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QColor, QPixmap, QIcon
from PyQt5.QtWidgets import QColorDialog
from manuskript.enums import Perso
from manuskript.enums import Plot
from manuskript.functions import iconColor
from manuskript.functions import mainWindow
from manuskript.functions import randomColor
from manuskript.functions import toInt
class persosModel(QStandardItemModel):
def __init__(self, parent):
QStandardItemModel.__init__(self, 0, 3, parent)
self.setHorizontalHeaderLabels([i.name for i in Perso])
self.mw = mainWindow()
# self._proxy = plotsProxyModel()
# self._proxy.setSourceModel(self)
###############################################################################
# PERSOS QUERRIES
###############################################################################
def name(self, row):
return self.item(row, Perso.name.value).text()
def icon(self, row):
return self.item(row, Perso.name.value).icon()
def ID(self, row):
return self.item(row, Perso.ID.value).text()
def importance(self, row):
return self.item(row, Perso.importance.value).text()
###############################################################################
# MODEL QUERRIES
###############################################################################
def getPersosByImportance(self):
persos = [[], [], []]
for i in range(self.rowCount()):
importance = self.item(i, Perso.importance.value).text()
ID = self.item(i, Perso.ID.value).text()
persos[2-toInt(importance)].append(ID)
return persos
def getPersoNameByID(self, ID):
index = self.getIndexFromID(ID)
if index.isValid():
return self.name(index.row())
return ""
def getIndexFromID(self, ID):
for i in range(self.rowCount()):
_ID = self.item(i, Perso.ID.value).text()
if _ID == ID or toInt(_ID) == ID:
return self.index(i, 0)
return QModelIndex()
def getPersoColorByID(self, ID):
idx = self.getIndexFromID(ID)
return self.getPersoColorName(idx)
def getPersoColorName(self, index):
icon = self.item(index.row()).icon()
return iconColor(icon).name() if icon else ""
def currentListIndex(self):
i = self.mw.lstPersos.currentIndex()
if i .isValid():
return i
else:
return None
def currentPersoIndex(self):
return self.mw.lstPersos.currentPersoIndex()
###############################################################################
# ADDING / REMOVING
###############################################################################
def addPerso(self):
"""Creates a perso by adding a row in mdlPersos
and a column in mdlPersosInfos with same ID"""
p = QStandardItem(self.tr("New character"))
self.setPersoColor(p, randomColor(QColor(Qt.white)))
pid = self.getUniqueID()
self.appendRow([p, QStandardItem(pid), QStandardItem("0")])
def getUniqueID(self, parent=QModelIndex()):
"""Returns an unused perso ID (row 1)."""
vals = []
for i in range(self.rowCount(parent)):
index = self.index(i, Perso.ID.value, parent)
if index.isValid() and index.data():
vals.append(int(index.data()))
k = 0
while k in vals:
k += 1
return str(k)
def removePerso(self):
index = self.currentPersoIndex()
self.takeRow(index.row())
def setPersoColor(self, item, color):
px = QPixmap(32, 32)
px.fill(color)
item.setIcon(QIcon(px))
def chosePersoColor(self):
idx = self.currentPersoIndex()
item = self.item(idx.row(), Perso.name.value)
if item:
color = iconColor(item.icon())
else:
color = Qt.white
self.colorDialog = QColorDialog(color, self.mw)
color = self.colorDialog.getColor(color)
if color.isValid():
self.setPersoColor(item, color)
self.updatePersoColor(idx)
###############################################################################
# UI
###############################################################################
def updatePersoColor(self, idx):
# idx = self.currentPersoIndex()
color = self.getPersoColorName(idx)
self.mw.btnPersoColor.setStyleSheet("background:{};".format(color))
###############################################################################
# PERSO INFOS
###############################################################################
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
if section == Perso.infoName.value:
return self.tr("Name")
elif section == Perso.infoData.value:
return self.tr("Value")
else:
return Perso(section).name
else:
return QStandardItemModel.headerData(self, section, orientation, role)
def addPersoInfo(self):
perso = self.itemFromIndex(self.currentPersoIndex())
row = perso.rowCount()
perso.setChild(row, Perso.infoName.value, QStandardItem(""))
perso.setChild(row, Perso.infoData.value, QStandardItem(""))
self.mw.updatePersoInfoView()
def removePersoInfo(self):
perso = self.itemFromIndex(self.currentPersoIndex())
rm = []
for idx in self.mw.tblPersoInfos.selectedIndexes():
if not idx.row() in rm:
rm.append(idx.row())
rm.sort()
rm.reverse()
for r in rm:
perso.takeRow(r)
def listPersoInfos(self, index):
infos = []
for i in range(self.rowCount(index)):
name = self.data(index.child(i, Perso.infoName.value))
val = self.data(index.child(i, Perso.infoData.value))
infos.append((name, val))
return infos

View file

@ -9,7 +9,7 @@ from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QAction, QMenu
from manuskript.enums import Plot
from manuskript.enums import Subplot
from manuskript.enums import PlotStep
from manuskript.functions import toInt, mainWindow
@ -37,7 +37,7 @@ class plotModel(QStandardItemModel):
index = self.getIndexFromID(ID)
if not index.isValid():
return
index = index.sibling(index.row(), Plot.subplots.value)
index = index.sibling(index.row(), Plot.steps.value)
item = self.itemFromIndex(index)
lst = []
for i in range(item.rowCount()):
@ -86,8 +86,8 @@ class plotModel(QStandardItemModel):
p = QStandardItem(self.tr("New plot"))
_id = QStandardItem(self.getUniqueID())
importance = QStandardItem(str(0))
self.appendRow([p, _id, importance, QStandardItem("Persos"),
QStandardItem(), QStandardItem(), QStandardItem("Subplots")])
self.appendRow([p, _id, importance, QStandardItem("Characters"),
QStandardItem(), QStandardItem(), QStandardItem("Resolution steps")])
def getUniqueID(self, parent=QModelIndex()):
"""Returns an unused ID"""
@ -114,9 +114,9 @@ class plotModel(QStandardItemModel):
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
if section == Subplot.name.value:
if section == PlotStep.name.value:
return self.tr("Name")
elif section == Subplot.meta.value:
elif section == PlotStep.meta.value:
return self.tr("Meta")
else:
return ""
@ -127,8 +127,8 @@ class plotModel(QStandardItemModel):
def data(self, index, role=Qt.DisplayRole):
if index.parent().isValid() and \
index.parent().column() == Plot.subplots.value and \
index.column() == Subplot.meta.value:
index.parent().column() == Plot.steps.value and \
index.column() == PlotStep.meta.value:
if role == Qt.TextAlignmentRole:
return Qt.AlignRight | Qt.AlignVCenter
elif role == Qt.ForegroundRole:
@ -144,13 +144,13 @@ class plotModel(QStandardItemModel):
if not index.isValid():
return
parent = index.sibling(index.row(), Plot.subplots.value)
parentItem = self.item(index.row(), Plot.subplots.value)
parent = index.sibling(index.row(), Plot.steps.value)
parentItem = self.item(index.row(), Plot.steps.value)
if not parentItem:
return
p = QStandardItem(self.tr("New subplot"))
p = QStandardItem(self.tr("New step"))
_id = QStandardItem(self.getUniqueID(parent))
summary = QStandardItem()
@ -182,10 +182,10 @@ class plotModel(QStandardItemModel):
def addPlotPerso(self, v):
index = self.mw.lstPlots.currentPlotIndex()
if index.isValid():
if not self.item(index.row(), Plot.persos.value):
self.setItem(index.row(), Plot.persos.value, QStandardItem())
if not self.item(index.row(), Plot.characters.value):
self.setItem(index.row(), Plot.characters.value, QStandardItem())
item = self.item(index.row(), Plot.persos.value)
item = self.item(index.row(), Plot.characters.value)
# We check that the PersoID is not in the list yet
for i in range(item.rowCount()):
@ -212,13 +212,13 @@ class plotModel(QStandardItemModel):
menu.addMenu(m)
mpr = QSignalMapper(menu)
for i in range(self.mw.mdlPersos.rowCount()):
a = QAction(self.mw.mdlPersos.name(i), menu)
a.setIcon(self.mw.mdlPersos.icon(i))
for i in range(self.mw.mdlCharacter.rowCount()):
a = QAction(self.mw.mdlCharacter.name(i), menu)
a.setIcon(self.mw.mdlCharacter.icon(i))
a.triggered.connect(mpr.map)
mpr.setMapping(a, int(self.mw.mdlPersos.ID(i)))
mpr.setMapping(a, int(self.mw.mdlCharacter.ID(i)))
imp = toInt(self.mw.mdlPersos.importance(i))
imp = toInt(self.mw.mdlCharacter.importance(i))
menus[2 - imp].addAction(a)

View file

@ -11,9 +11,9 @@ import re
from PyQt5.QtWidgets import qApp
from manuskript.enums import Outline
from manuskript.enums import Perso
from manuskript.enums import Character
from manuskript.enums import Plot
from manuskript.enums import Subplot
from manuskript.enums import PlotStep
from manuskript.functions import mainWindow
RegEx = r"{(\w):(\d+):?.*?}"
@ -22,7 +22,7 @@ RegExNonCapturing = r"{\w:\d+:?.*?}"
# The basic format of the references
EmptyRef = "{{{}:{}:{}}}"
EmptyRefSearchable = "{{{}:{}:"
PersoLetter = "C"
CharacterLetter = "C"
TextLetter = "T"
PlotLetter = "P"
WorldLetter = "W"
@ -37,13 +37,13 @@ def plotReference(ID, searchable=False):
return EmptyRefSearchable.format(PlotLetter, ID, "")
def persoReference(ID, searchable=False):
def characterReference(ID, searchable=False):
"""Takes the ID of a character and returns a reference for that character.
@searchable: returns a stripped version that allows simple text search."""
if not searchable:
return EmptyRef.format(PersoLetter, ID, "")
return EmptyRef.format(CharacterLetter, ID, "")
else:
return EmptyRefSearchable.format(PersoLetter, ID, "")
return EmptyRefSearchable.format(CharacterLetter, ID, "")
def textReference(ID, searchable=False):
@ -103,8 +103,8 @@ def infos(ref):
POV = ""
if item.POV():
POV = "<a href='{ref}'>{text}</a>".format(
ref=persoReference(item.POV()),
text=mainWindow().mdlPersos.getPersoNameByID(item.POV()))
ref=characterReference(item.POV()),
text=mainWindow().mdlCharacter.getCharacterByID(item.POV()).name())
# The status of the scene
status = item.status()
@ -130,7 +130,7 @@ def infos(ref):
path = " > ".join(pathStr)
# Summaries and notes
ss = item.data(Outline.summarySentance.value)
ss = item.data(Outline.summarySentence.value)
ls = item.data(Outline.summaryFull.value)
notes = item.data(Outline.notes.value)
@ -167,17 +167,19 @@ def infos(ref):
ls=ls.replace("\n", "<br>")) if ls.strip() else "",
notes="<p><b>{notesTitle}</b><br>{notes}</p>".format(
notesTitle=notesTitle,
notes=linkifyAllRefs(basicT2TFormat(notes))) if notes.strip() else "",
notes=linkifyAllRefs(notes)) if notes.strip() else "",
references=listReferences(ref)
)
return text
# A character
elif _type == PersoLetter:
m = mainWindow().mdlPersos
index = m.getIndexFromID(_ref)
name = m.name(index.row())
elif _type == CharacterLetter:
m = mainWindow().mdlCharacter
c = m.getCharacterByID(int(_ref))
index = c.index()
name = c.name()
# Titles
basicTitle = qApp.translate("references", "Basic infos")
@ -191,14 +193,16 @@ def infos(ref):
# basic infos
basic = []
for i in [
(Perso.motivation, qApp.translate("references", "Motivation"), False),
(Perso.goal, qApp.translate("references", "Goal"), False),
(Perso.conflict, qApp.translate("references", "Conflict"), False),
(Perso.epiphany, qApp.translate("references", "Epiphany"), False),
(Perso.summarySentance, qApp.translate("references", "Short summary"), True),
(Perso.summaryPara, qApp.translate("references", "Longer summary"), True),
(Character.motivation, qApp.translate("references", "Motivation"), False),
(Character.goal, qApp.translate("references", "Goal"), False),
(Character.conflict, qApp.translate("references", "Conflict"), False),
(Character.epiphany, qApp.translate("references", "Epiphany"), False),
(Character.summarySentence, qApp.translate("references", "Short summary"), True),
(Character.summaryPara, qApp.translate("references", "Longer summary"), True),
]:
val = m.data(index.sibling(index.row(), i[0].value))
if val:
basic.append("<b>{title}:</b>{n}{val}".format(
title=i[1],
@ -208,7 +212,7 @@ def infos(ref):
# detailed infos
detailed = []
for _name, _val in m.listPersoInfos(index):
for _name, _val in c.listInfos():
detailed.append("<b>{}:</b> {}".format(
_name,
_val))
@ -272,24 +276,24 @@ def infos(ref):
Plot.result.value))
# Characters
pM = mainWindow().mdlPersos
item = m.item(index.row(), Plot.persos.value)
pM = mainWindow().mdlCharacter
item = m.item(index.row(), Plot.characters.value)
characters = ""
if item:
for r in range(item.rowCount()):
ID = item.child(r, 0).text()
characters += "<li><a href='{link}'>{text}</a>".format(
link=persoReference(ID),
link=characterReference(ID),
text=pM.getPersoNameByID(ID))
# Resolution steps
steps = ""
item = m.item(index.row(), Plot.subplots.value)
item = m.item(index.row(), Plot.steps.value)
if item:
for r in range(item.rowCount()):
title = item.child(r, Subplot.name.value).text()
summary = item.child(r, Subplot.summary.value).text()
meta = item.child(r, Subplot.meta.value).text()
title = item.child(r, PlotStep.name.value).text()
summary = item.child(r, PlotStep.summary.value).text()
meta = item.child(r, PlotStep.meta.value).text()
if meta:
meta = " <span style='color:gray;'>({})</span>".format(meta)
steps += "<li><b>{title}</b>{summary}{meta}</li>".format(
@ -373,56 +377,126 @@ def infos(ref):
return qApp.translate("references", "Unknown reference: {}.").format(ref)
def tooltip(ref):
"""Returns a tooltip in HTML for the reference ``ref``."""
def shortInfos(ref):
"""Returns infos about reference ``ref``.
Returns -1 if ``ref`` is not a valid reference, and None if it is valid but unknown."""
match = re.fullmatch(RegEx, ref)
if not match:
return qApp.translate("references", "Not a reference: {}.").format(ref)
return -1
_type = match.group(1)
_ref = match.group(2)
infos = {}
infos["ID"] = _ref
if _type == TextLetter:
infos["type"] = TextLetter
m = mainWindow().mdlOutline
idx = m.getIndexByID(_ref)
if not idx.isValid():
return qApp.translate("references", "Unknown reference: {}.").format(ref)
return None
item = idx.internalPointer()
if item.isFolder():
tt = qApp.translate("references", "Folder: <b>{}</b>").format(item.title())
infos["text_type"] = "folder"
else:
tt = qApp.translate("references", "Text: <b>{}</b>").format(item.title())
tt += "<br><i>{}</i>".format(item.path())
infos["text_type"] = "text"
return tt
infos["title"] = item.title()
infos["path"] = item.path()
return infos
elif _type == PersoLetter:
m = mainWindow().mdlPersos
item = m.item(int(_ref), Perso.name.value)
if item:
return qApp.translate("references", "Character: <b>{}</b>").format(item.text())
elif _type == CharacterLetter:
infos["type"] = CharacterLetter
m = mainWindow().mdlCharacter
c = m.getCharacterByID(_ref)
if c:
infos["title"] = c.name()
infos["name"] = c.name()
return infos
elif _type == PlotLetter:
infos["type"] = PlotLetter
m = mainWindow().mdlPlots
name = m.getPlotNameByID(_ref)
if name:
return qApp.translate("references", "Plot: <b>{}</b>").format(name)
infos["title"] = name
return infos
elif _type == WorldLetter:
infos["type"] = WorldLetter
m = mainWindow().mdlWorld
item = m.itemByID(_ref)
if item:
name = item.text()
path = m.path(item)
return qApp.translate("references", "World: <b>{name}</b>{path}").format(
name=name,
path=" <span style='color:gray;'>({})</span>".format(path) if path else "")
infos["title"] = name
infos["path"] = path
return infos
return qApp.translate("references", "<b>Unknown reference:</b> {}.").format(ref)
return None
def title(ref):
"""Returns a the title (or name) for the reference ``ref``."""
infos = shortInfos(ref)
if infos and infos != -1 and "title" in infos:
return infos["title"]
else:
return None
def type(ref):
infos = shortInfos(ref)
if infos and infos != -1:
return infos["type"]
def ID(ref):
infos = shortInfos(ref)
if infos and infos != -1:
return infos["ID"]
def tooltip(ref):
"""Returns a tooltip in HTML for the reference ``ref``."""
infos = shortInfos(ref)
if not infos:
return qApp.translate("references", "<b>Unknown reference:</b> {}.").format(ref)
if infos == -1:
return qApp.translate("references", "Not a reference: {}.").format(ref)
if infos["type"] == TextLetter:
if infos["text_type"] == "folder":
tt = qApp.translate("references", "Folder: <b>{}</b>").format(infos["title"])
else:
tt = qApp.translate("references", "Text: <b>{}</b>").format(infos["title"])
tt += "<br><i>{}</i>".format(infos["path"])
return tt
elif infos["type"] == CharacterLetter:
return qApp.translate("references", "Character: <b>{}</b>").format(infos["title"])
elif infos["type"] == PlotLetter:
return qApp.translate("references", "Plot: <b>{}</b>").format(infos["title"])
elif infos["type"] == WorldLetter:
return qApp.translate("references", "World: <b>{name}</b>{path}").format(
name=infos["title"],
path=" <span style='color:gray;'>({})</span>".format(infos["path"]) if infos["path"] else "")
###############################################################################
@ -446,9 +520,9 @@ def refToLink(ref):
item = idx.internalPointer()
text = item.title()
elif _type == PersoLetter:
m = mainWindow().mdlPersos
text = m.item(int(_ref), Perso.name.value).text()
elif _type == CharacterLetter:
m = mainWindow().mdlCharacter
text = m.getCharacterByID(int(_ref)).name()
elif _type == PlotLetter:
m = mainWindow().mdlPlots
@ -508,30 +582,6 @@ def listReferences(ref, title=qApp.translate("references", "Referenced in:")):
ref=listRefs) if listRefs else ""
def basicT2TFormat(text, formatting=True, EOL=True, titles=True):
"""A very basic t2t formatter to display notes and texts."""
text = text.splitlines()
for n, line in enumerate(text):
if formatting:
line = re.sub("\*\*(.*?)\*\*", "<b>\\1</b>", line)
line = re.sub("//(.*?)//", "<i>\\1</i>", line)
line = re.sub("__(.*?)__", "<u>\\1</u>", line)
if titles:
for i in range(1, 6):
r1 = '^\s*{s}([^=].*[^=]){s}\s*$'.format(s="=" * i)
r2 = '^\s*{s}([^\+].*[^\+]){s}\s*$'.format(s="\\+" * i)
t = "<h{n}>\\1</h{n}>".format(n=i)
line = re.sub(r1, t, line)
line = re.sub(r2, t, line)
text[n] = line
text = "\n".join(text)
if EOL:
text = text.replace("\n", "<br>")
return text
def basicFormat(text):
if not text:
return ""
@ -549,16 +599,16 @@ def open(ref):
_type = match.group(1)
_ref = match.group(2)
if _type == PersoLetter:
if _type == CharacterLetter:
mw = mainWindow()
item = mw.lstPersos.getItemByID(_ref)
item = mw.lstCharacters.getItemByID(int(_ref))
if item:
mw.tabMain.setCurrentIndex(mw.TabPersos)
mw.lstPersos.setCurrentItem(item)
mw.lstCharacters.setCurrentItem(item)
return True
print("Ref not found")
print("Error: Ref {} not found".format(ref))
return False
elif _type == TextLetter:

View file

@ -1,12 +1,16 @@
# -*- coding: utf-8 -*-
import collections
import json
import pickle
from PyQt5.QtWidgets import qApp
from manuskript.enums import Outline
# TODO: move some/all of those settings to application settings and not project settings
# in order to allow a shared project between several writers
viewSettings = {
"Tree": {
"Icon": "Nothing",
@ -28,7 +32,8 @@ viewSettings = {
"Background": "Nothing",
},
}
# Application
spellcheck = False
dict = None
corkSizeFactor = 100
@ -47,7 +52,7 @@ corkBackground = {
"color": "#926239",
"image": ""
}
defaultTextType = "t2t"
defaultTextType = "md"
fullScreenTheme = "spacedreams"
textEditor = {
@ -80,12 +85,16 @@ frequencyAnalyzer = {
"phraseMin": 2,
"phraseMax": 5
}
viewMode = "fiction" # simple, fiction
saveToZip = True
def save(filename=None):
def save(filename=None, protocol=None):
global spellcheck, dict, corkSliderFactor, viewSettings, corkSizeFactor, folderView, lastTab, openIndexes, \
autoSave, autoSaveDelay, saveOnQuit, autoSaveNoChanges, autoSaveNoChangesDelay, outlineViewColumns, \
corkBackground, fullScreenTheme, defaultTextType, textEditor, revisions, frequencyAnalyzer
corkBackground, fullScreenTheme, defaultTextType, textEditor, revisions, frequencyAnalyzer, viewMode, \
saveToZip
allSettings = {
"viewSettings": viewSettings,
@ -106,8 +115,10 @@ def save(filename=None):
"defaultTextType":defaultTextType,
"textEditor":textEditor,
"revisions":revisions,
"frequencyAnalyzer": frequencyAnalyzer
}
"frequencyAnalyzer": frequencyAnalyzer,
"viewMode": viewMode,
"saveToZip": saveToZip,
}
#pp=pprint.PrettyPrinter(indent=4, compact=False)
#print("Saving:")
@ -117,9 +128,16 @@ def save(filename=None):
f = open(filename, "wb")
pickle.dump(allSettings, f)
else:
return pickle.dumps(allSettings)
def load(string, fromString=False):
if protocol == 0:
# This looks stupid
# But a simple json.dumps with sort_keys will throw a TypeError
# because of unorderable types.
return json.dumps(json.loads(json.dumps(allSettings)), indent=4, sort_keys=True)
else:
return pickle.dumps(allSettings)
def load(string, fromString=False, protocol=None):
"""Load settings from 'string'. 'string' is the filename of the pickle dump.
If fromString=True, string is the data of the pickle dumps."""
global allSettings
@ -133,8 +151,11 @@ def load(string, fromString=False):
print("{} doesn't exist, cannot load settings.".format(string))
return
else:
allSettings = pickle.loads(string)
if protocol == 0:
allSettings = json.loads(string)
else:
allSettings = pickle.loads(string)
#pp=pprint.PrettyPrinter(indent=4, compact=False)
#print("Loading:")
#pp.pprint(allSettings)
@ -211,7 +232,28 @@ def load(string, fromString=False):
global revisions
revisions = allSettings["revisions"]
# With JSON we had to convert int keys to str, and None to "null", so we roll back.
r = {}
for i in revisions["rules"]:
if i == "null":
r[None] = revisions["rules"]["null"]
elif i == None:
r[None] = revisions["rules"][None]
else:
r[int(i)] = revisions["rules"][i]
revisions["rules"] = r
if "frequencyAnalyzer" in allSettings:
global frequencyAnalyzer
frequencyAnalyzer = allSettings["frequencyAnalyzer"]
if "viewMode" in allSettings:
global viewMode
viewMode = allSettings["viewMode"]
if "saveToZip" in allSettings:
global saveToZip
saveToZip = allSettings["saveToZip"]

View file

@ -1,23 +1,26 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import os
from collections import OrderedDict
from PyQt5.QtCore import QSize, QSettings, QRegExp
from PyQt5.QtCore import QSize, QSettings, QRegExp, QTranslator, QObject
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIntValidator, QIcon, QFont, QColor, QPixmap, QStandardItem, QPainter
from PyQt5.QtWidgets import QStyleFactory, QWidget, QStyle, QColorDialog, QListWidgetItem
from PyQt5.QtWidgets import QStyleFactory, QWidget, QStyle, QColorDialog, QListWidgetItem, QMessageBox
from PyQt5.QtWidgets import qApp
# Spell checker support
from manuskript import settings
from manuskript.enums import Outline
from manuskript.functions import allPaths, iconColor, writablePath, appPath
from manuskript.functions import allPaths, iconColor, writablePath, appPath, findWidgetsOfClass
from manuskript.functions import mainWindow
from manuskript.ui.editors.themes import createThemePreview
from manuskript.ui.editors.themes import getThemeName
from manuskript.ui.editors.themes import loadThemeDatas
from manuskript.ui.settings_ui import Ui_Settings
from manuskript.ui.views.outlineView import outlineView
from manuskript.ui.views.textEditView import textEditView
from manuskript.ui.welcome import welcome
try:
import enchant
@ -43,6 +46,21 @@ class settingsWindow(QWidget, Ui_Settings):
self.cmbStyle.setCurrentIndex([i.lower() for i in list(QStyleFactory.keys())].index(qApp.style().objectName()))
self.cmbStyle.currentIndexChanged[str].connect(self.setStyle)
self.cmbTranslation.clear()
tr = OrderedDict()
tr["English"] = ""
tr["Français"] = "manuskript_fr.qm"
tr["Español"] = "manuskript_es.qm"
for name in tr:
self.cmbTranslation.addItem(name, tr[name])
sttgs = QSettings(qApp.organizationName(), qApp.applicationName())
if sttgs.contains("applicationTranslation") and sttgs.value("applicationTranslation") in tr.values():
self.cmbTranslation.setCurrentText([i for i in tr if tr[i] == sttgs.value("applicationTranslation")][0])
self.cmbTranslation.currentIndexChanged.connect(self.setTranslation)
self.txtAutoSave.setValidator(QIntValidator(0, 999, self))
self.txtAutoSaveNoChanges.setValidator(QIntValidator(0, 999, self))
self.chkAutoSave.setChecked(settings.autoSave)
@ -50,28 +68,17 @@ class settingsWindow(QWidget, Ui_Settings):
self.txtAutoSave.setText(str(settings.autoSaveDelay))
self.txtAutoSaveNoChanges.setText(str(settings.autoSaveNoChangesDelay))
self.chkSaveOnQuit.setChecked(settings.saveOnQuit)
self.chkSaveToZip.setChecked(settings.saveToZip)
self.chkAutoSave.stateChanged.connect(self.saveSettingsChanged)
self.chkAutoSaveNoChanges.stateChanged.connect(self.saveSettingsChanged)
self.chkSaveOnQuit.stateChanged.connect(self.saveSettingsChanged)
self.chkSaveToZip.stateChanged.connect(self.saveSettingsChanged)
self.txtAutoSave.textEdited.connect(self.saveSettingsChanged)
self.txtAutoSaveNoChanges.textEdited.connect(self.saveSettingsChanged)
autoLoad, last = self.mw.welcome.getAutoLoadValues()
self.chkAutoLoad.setChecked(autoLoad)
self.chkAutoLoad.stateChanged.connect(self.saveSettingsChanged)
dtt = [
("t2t", self.tr("Txt2Tags"), "text-x-script"),
("html", self.tr("Rich Text (html)"), "text-html"),
("txt", self.tr("Plain Text"), "text-x-generic"),
]
self.cmbDefaultTextType.clear()
for t in dtt:
self.cmbDefaultTextType.addItem(QIcon.fromTheme(t[2]), t[1], t[0])
i = self.cmbDefaultTextType.findData(settings.defaultTextType)
if i != -1:
self.cmbDefaultTextType.setCurrentIndex(i)
self.cmbDefaultTextType.currentIndexChanged.connect(self.saveSettingsChanged)
# Revisions
opt = settings.revisions
self.chkRevisionsKeep.setChecked(opt["keep"])
@ -102,6 +109,8 @@ class settingsWindow(QWidget, Ui_Settings):
chk.setChecked(col in settings.outlineViewColumns)
chk.stateChanged.connect(self.outlineColumnsChanged)
self.chkOutlinePOV.setVisible(settings.viewMode != "simple") # Hides checkbox if non-fiction view mode
for item, what, value in [
(self.rdoTreeItemCount, "InfoFolder", "Count"),
(self.rdoTreeWC, "InfoFolder", "WC"),
@ -209,6 +218,14 @@ class settingsWindow(QWidget, Ui_Settings):
sttgs.setValue("applicationStyle", style)
qApp.setStyle(style)
def setTranslation(self, index):
path = self.cmbTranslation.currentData()
# Save settings
sttgs = QSettings(qApp.organizationName(), qApp.applicationName())
sttgs.setValue("applicationTranslation", path)
# QMessageBox.information(self, "Warning", "You'll have to restart manuskript.")
def saveSettingsChanged(self):
if self.txtAutoSave.text() in ["", "0"]:
self.txtAutoSave.setText("1")
@ -222,11 +239,11 @@ class settingsWindow(QWidget, Ui_Settings):
settings.autoSave = True if self.chkAutoSave.checkState() else False
settings.autoSaveNoChanges = True if self.chkAutoSaveNoChanges.checkState() else False
settings.saveOnQuit = True if self.chkSaveOnQuit.checkState() else False
settings.saveToZip = True if self.chkSaveToZip.checkState() else False
settings.autoSaveDelay = int(self.txtAutoSave.text())
settings.autoSaveNoChangesDelay = int(self.txtAutoSaveNoChanges.text())
self.mw.saveTimer.setInterval(settings.autoSaveDelay * 60 * 1000)
self.mw.saveTimerNoChanges.setInterval(settings.autoSaveNoChangesDelay * 1000)
settings.defaultTextType = self.cmbDefaultTextType.currentData()
####################################################################################################
# REVISION #
@ -267,6 +284,7 @@ class settingsWindow(QWidget, Ui_Settings):
item, part = self.viewSettingsDatas()[cmb]
element = lst[cmb.currentIndex()]
self.mw.setViewSettings(item, part, element)
self.mw.generateViewMenu()
def outlineColumnsData(self):
return {
@ -290,8 +308,8 @@ class settingsWindow(QWidget, Ui_Settings):
settings.outlineViewColumns.remove(col)
# Update views
self.mw.redacEditor.outlineView.hideColumns()
self.mw.treePlanOutline.hideColumns()
for w in findWidgetsOfClass(outlineView):
w.hideColumns()
def treeViewSettignsChanged(self):
for item, what, value in [
@ -325,6 +343,7 @@ class settingsWindow(QWidget, Ui_Settings):
def setCorkBackground(self, i):
img = self.cmbCorkImage.itemData(i)
img = os.path.basename(img)
if img:
settings.corkBackground["image"] = img
else:

View file

@ -4,7 +4,7 @@ from PyQt5.QtCore import pyqtSignal, Qt, QTimer, QRect
from PyQt5.QtGui import QBrush, QCursor, QPalette, QFontMetrics
from PyQt5.QtWidgets import QWidget, QListWidgetItem, QToolTip, QStyledItemDelegate, QStyle
from manuskript.enums import Perso
from manuskript.enums import Character
from manuskript.enums import Plot
from manuskript.functions import lightBlue
from manuskript.functions import mainWindow
@ -36,7 +36,7 @@ class cheatSheet(QWidget, Ui_cheatSheet):
self.line.hide()
self.outlineModel = None
self.persoModel = None
self.characterModel = None
self.plotModel = None
self.worldModel = None
@ -53,12 +53,14 @@ class cheatSheet(QWidget, Ui_cheatSheet):
def setModels(self):
mw = mainWindow()
self.outlineModel = mw.mdlOutline
self.persoModel = mw.mdlPersos
self.characterModel = mw.mdlCharacter
self.plotModel = mw.mdlPlots
self.worldModel = mw.mdlWorld
self.outlineModel.dataChanged.connect(self.populateTimer.start)
self.persoModel.dataChanged.connect(self.populateTimer.start)
self.characterModel.dataChanged.connect(self.populateTimer.start)
self.characterModel.rowsInserted.connect(self.populateTimer.start)
self.characterModel.rowsRemoved.connect(self.populateTimer.start)
self.plotModel.dataChanged.connect(self.populateTimer.start)
self.worldModel.dataChanged.connect(self.populateTimer.start)
@ -75,17 +77,14 @@ class cheatSheet(QWidget, Ui_cheatSheet):
self.list.hide()
def populate(self):
if self.persoModel:
if self.characterModel:
d = []
for r in range(self.persoModel.rowCount()):
name = self.persoModel.item(r, Perso.name.value).text()
ID = self.persoModel.item(r, Perso.ID.value).text()
imp = self.persoModel.item(r, Perso.importance.value).text()
imp = [self.tr("Minor"), self.tr("Secondary"), self.tr("Main")][int(imp)]
d.append((name, ID, imp))
for c in self.characterModel.characters:
imp = [self.tr("Minor"), self.tr("Secondary"), self.tr("Main")][int(c.importance())]
d.append((c.name(), c.ID(), imp))
self.data[(self.tr("Characters"), Ref.PersoLetter)] = d
self.data[(self.tr("Characters"), Ref.CharacterLetter)] = d
if self.outlineModel:
d = []

View file

@ -2,7 +2,7 @@
# --!-- coding: utf8 --!--
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QToolBar, QDockWidget, QAction, QToolButton, QSizePolicy, QStylePainter, \
QStyleOptionButton, QStyle, QLabel
QStyleOptionButton, QStyle
class collapsibleDockWidgets(QToolBar):
@ -31,6 +31,8 @@ class collapsibleDockWidgets(QToolBar):
# self.setAllowedAreas(self.TRANSPOSED_AREA[self._area])
self.parent().addToolBar(self.TRANSPOSED_AREA[self._area], self)
self._dockToButtonAction = {}
# Dock widgets
for d in self._dockWidgets():
b = verticalButton(self)
@ -48,7 +50,8 @@ class collapsibleDockWidgets(QToolBar):
background: lightBlue;
}
""")
self.addWidget(b)
a = self.addWidget(b)
self._dockToButtonAction[d] = a
self.addSeparator()
@ -88,6 +91,10 @@ class collapsibleDockWidgets(QToolBar):
else:
action.setVisible(True)
def setDockVisibility(self, dock, val):
dock.setVisible(val)
self._dockToButtonAction[dock].setVisible(val)
def saveState(self):
# We just need to save states of the custom widgets.
state = []

View file

@ -2,11 +2,12 @@
# Form implementation generated from reading ui file 'manuskript/ui/compileDialog_ui.ui'
#
# Created by: PyQt5 UI code generator 5.4.1
# Created: Wed Mar 2 00:30:17 2016
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_compileDialog(object):
def setupUi(self, compileDialog):

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QTextCursor
def MDFormatSelection(editor, style):
"""
Formats the current selection of ``editor`` in the format given by ``style``,
style being:
0: bold
1: italic
2: code
"""
print("Formatting:", style, " (Unimplemented yet !)")
# FIXME

View file

@ -0,0 +1,130 @@
#!/usr/bin/python
# -*- coding: utf8 -*-
import re
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextCharFormat, QFont, QTextCursor, QFontMetrics
from manuskript.ui.editors.basicHighlighter import basicHighlighter
class MMDHighlighter(basicHighlighter):
MARKDOWN_REGEX = {
'Bold': '(\*\*)(.+?)(\*\*)',
'Bold2': '(__)(.+?)(__)',
'Italic': '(\*)([^\*].+?[^\*])(\*)',
'Italic2': '(_)([^_].+?[^_])(_)',
'Title': '^(#+)(\s*)(.*)(#*)',
'HTML': '<.+?>',
'Blockquotes': '^(> )+.*$',
'OrderedList': '^\d+\.\s+',
'UnorderedList': '^[\*\+-]\s+',
'Code': '^\s{4,}.*$',
'Links-inline': '(\[)(.*?)(\])(\()(.*?)(\))',
'Links-ref': '(\[)(.*?)(\])\s?(\[)(.*?)(\])',
'Links-ref2': '^\s{,3}(\[)(.*?)(\]:)\s+([^\s]*)\s*(.*?)*$',
}
def __init__(self, editor, style="Default"):
basicHighlighter.__init__(self, editor)
self.editor = editor
self.rules = {}
for key in self.MARKDOWN_REGEX:
self.rules[key] = re.compile(self.MARKDOWN_REGEX[key])
def highlightBlock(self, text):
basicHighlighter.highlightBlockBefore(self, text)
self.doHighlightBlock(text)
basicHighlighter.highlightBlockAfter(self, text)
def doHighlightBlock(self, text):
"""
A quick-n-dirty very basic highlighter, that fails in most non-trivial cases. And is ugly.
"""
# Creates textCharFormat
cfOperator = QTextCharFormat()
cfOperator.setForeground(Qt.lightGray)
cfBold = QTextCharFormat()
cfBold.setFontWeight(QFont.Bold)
cfItalic = QTextCharFormat()
cfItalic.setFontItalic(True)
# Titles (only atx-style, with #, not underlined)
defaultSize = self._defaultCharFormat.font().pointSize()
r = self.rules["Title"]
for m in r.finditer(text):
cfOperator.setFontPointSize(defaultSize + 12 - 2 * len(m.group(1)))
cfBold.setFontPointSize(defaultSize + 12 - 2 * len(m.group(1)))
self.setFormat(m.start(1), len(m.group(1)), cfOperator)
self.setFormat(m.start(3), len(m.group(3)), cfBold)
self.setFormat(m.start(4), len(m.group(4)), cfOperator)
# Code blocks
r = self.rules["Code"]
format = QTextCharFormat()
format.setForeground(Qt.darkGray)
format.setFontFixedPitch(True)
for m in r.finditer(text):
self.setFormat(m.start(), m.end() - m.start(), format)
# Basic stuff
stuff = [
("Blockquotes", Qt.blue),
("OrderedList", Qt.red),
("UnorderedList", Qt.darkRed),
("HTML", Qt.darkGreen),
]
for name, color in stuff:
r = self.rules[name]
format = QTextCharFormat()
format.setForeground(color)
for m in r.finditer(text):
self.setFormat(m.start(), m.end() - m.start(), format)
# Bold and Italic
for name, style in [
("Italic", cfItalic),
("Italic2", cfItalic),
("Bold", cfBold),
("Bold2", cfBold),
]:
r = self.rules[name]
for m in r.finditer(text):
self.setFormat(m.start(1), len(m.group(1)), cfOperator)
self.setFormat(m.start(2), len(m.group(2)), style)
self.setFormat(m.start(3), len(m.group(3)), cfOperator)
# Links
cfURL = QTextCharFormat()
cfURL.setForeground(Qt.darkGreen)
cfURL.setFontItalic(True)
cfText = QTextCharFormat()
cfText.setForeground(Qt.darkBlue)
cfIdentifier = QTextCharFormat()
cfIdentifier.setForeground(Qt.darkMagenta)
for type in ['Links-inline', 'Links-ref']:
r = self.rules[type]
for m in r.finditer(text):
self.setFormat(m.start(1), len(m.group(1)), cfOperator)
self.setFormat(m.start(2), len(m.group(2)), cfText)
self.setFormat(m.start(3), len(m.group(3)), cfOperator)
self.setFormat(m.start(4), len(m.group(4)), cfOperator)
self.setFormat(m.start(5), len(m.group(5)), cfURL if "inline" in type else cfIdentifier)
self.setFormat(m.start(6), len(m.group(6)), cfOperator)
r = self.rules["Links-ref2"]
for m in r.finditer(text):
self.setFormat(m.start(1), len(m.group(1)), cfOperator)
self.setFormat(m.start(2), len(m.group(2)), cfIdentifier)
self.setFormat(m.start(3), len(m.group(3)), cfOperator)
self.setFormat(m.start(4), len(m.group(4)), cfURL)
self.setFormat(m.start(5), len(m.group(5)), cfText)

View file

@ -61,7 +61,7 @@ class basicHighlighter(QSyntaxHighlighter):
fmt.setFontWeight(QFont.DemiBold)
if txt.group(1) == Ref.TextLetter:
fmt.setBackground(QBrush(QColor(Qt.blue).lighter(190)))
elif txt.group(1) == Ref.PersoLetter:
elif txt.group(1) == Ref.CharacterLetter:
fmt.setBackground(QBrush(QColor(Qt.yellow).lighter(170)))
elif txt.group(1) == Ref.PlotLetter:
fmt.setBackground(QBrush(QColor(Qt.red).lighter(170)))

View file

@ -107,7 +107,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
autoResize=True)
edt.setFrameShape(QFrame.NoFrame)
edt.setStyleSheet("background: {};".format(settings.textEditor["background"]))
edt.setStatusTip("{} ({})".format(itm.path(), itm.type()))
edt.setStatusTip("{}".format(itm.path()))
self.toggledSpellcheck.connect(edt.toggleSpellcheck, AUC)
self.dictChanged.connect(edt.setDict, AUC)
# edt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
@ -173,7 +173,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
elif item and item.isFolder() and self.folderView == "outline":
self.stack.setCurrentIndex(3)
self.outlineView.setModelPersos(mainWindow().mdlPersos)
self.outlineView.setModelCharacters(mainWindow().mdlCharacter)
self.outlineView.setModelLabels(mainWindow().mdlLabels)
self.outlineView.setModelStatus(mainWindow().mdlStatus)
self.outlineView.setModel(self.mw.mdlOutline)

View file

@ -94,8 +94,13 @@ class fullScreenEditor(QWidget):
lst = [i for i in os.listdir(p) if os.path.splitext(i)[1] == ".theme"]
for t in lst:
themeIni = os.path.join(p, t)
self.lstThemes.addItem(os.path.splitext(t)[0])
self.lstThemes.setCurrentText(settings.fullScreenTheme)
name = loadThemeDatas(themeIni)["Name"]
# self.lstThemes.addItem(os.path.splitext(t)[0])
self.lstThemes.addItem(name)
self.lstThemes.setItemData(self.lstThemes.count()-1, os.path.splitext(t)[0])
self.lstThemes.setCurrentIndex(self.lstThemes.findData(settings.fullScreenTheme))
# self.lstThemes.setCurrentText(settings.fullScreenTheme)
self.lstThemes.currentTextChanged.connect(self.setTheme)
self.lstThemes.setMaximumSize(QSize(300, QFontMetrics(qApp.font()).height()))
self.bottomPanel.layout().addWidget(QLabel(self.tr("Theme:"), self))
@ -125,6 +130,7 @@ class fullScreenEditor(QWidget):
self.btnClose.setVisible(not val)
def setTheme(self, themeName):
themeName = self.lstThemes.currentData()
settings.fullScreenTheme = themeName
self._theme = findThemePath(themeName)
self._themeDatas = loadThemeDatas(self._theme)

View file

@ -78,11 +78,15 @@ class mainEditor(QWidget, Ui_mainEditor):
self.updateThingsVisible(index)
def closeTab(self, index):
# FIXME: submit data if textedit?
w = self.tab.widget(index)
self.tab.removeTab(index)
w.setCurrentModelIndex(QModelIndex())
w.deleteLater()
def closeAllTabs(self):
while(self.tab.count()):
self.closeTab(0)
def allTabs(self):
return [self.tab.widget(i) for i in range(self.tab.count())]

View file

@ -1,376 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QTextCursor
def t2tFormatSelection(editor, style):
"""
Formats the current selection of ``editor`` in the format given by ``style``,
style being:
0: bold
1: italic
2: underline
3: strike
4: code
5: tagged
"""
print("Formatting:", style)
formatChar = "*/_-`'"[style]
# FIXME: doesn't work if selection spans over several blocks.
cursor = editor.textCursor()
cursor.beginEditBlock()
if cursor.hasSelection():
pass
else:
# If no selection, selects the word in which the cursor is now
cursor.movePosition(QTextCursor.StartOfWord,
QTextCursor.MoveAnchor)
cursor.movePosition(QTextCursor.EndOfWord,
QTextCursor.KeepAnchor)
if not cursor.hasSelection():
# No selection means we are outside a word,
# so we insert markup and put cursor in the middle
cursor.insertText(formatChar * 4)
cursor.setPosition(cursor.position() - 2)
cursor.endEditBlock()
editor.setTextCursor(cursor)
# And we are done
return
# Get start and end of selection
start = cursor.selectionStart() - cursor.block().position()
end = cursor.selectionEnd() - cursor.block().position()
if start > end:
start, end = end, start
# Whole block
text = cursor.block().text()
# Adjusts selection to exclude the markup
while text[start:start + 1] == formatChar:
start += 1
while text[end - 1:end] == formatChar:
end -= 1
# Get the text without formatting, and the array of format
fText, fArray = textToFormatArrayNoMarkup(text)
# Get the translated start and end of selection in the unformated text
tStart, tEnd = translateSelectionToUnformattedText(text, start, end)
# We want only the array that contains the propper formatting
propperArray = fArray[style]
if 0 in propperArray[tStart:tEnd]:
# have some unformated text in the selection, so we format the
# whole selection
propperArray = propperArray[:tStart] + [1] * \
(tEnd - tStart) + propperArray[tEnd:]
else:
# The whole selection is already formatted, so we remove the
# formatting
propperArray = propperArray[:tStart] + [0] * \
(tEnd - tStart) + propperArray[tEnd:]
fArray = fArray[0:style] + [propperArray] + fArray[style + 1:]
text = reformatText(fText, fArray)
# Replaces the whole block
cursor.movePosition(QTextCursor.StartOfBlock)
cursor.movePosition(QTextCursor.EndOfBlock,
QTextCursor.KeepAnchor)
cursor.insertText(text)
cursor.setPosition(end + cursor.block().position())
cursor.endEditBlock()
editor.setTextCursor(cursor)
def t2tClearFormat(editor):
"""Clears format on ``editor``'s current selection."""
cursor = editor.textCursor()
cursor.beginEditBlock()
text = cursor.selectedText()
t, a = textToFormatArrayNoMarkup(text)
cursor.insertText(t)
cursor.endEditBlock()
editor.setTextCursor(cursor)
def textToFormatArray(text):
"""
Take some text and returns an array of array containing informations
about how the text is formatted:
r = [ [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
...
]
Each sub-array is for one of the beautifier:
0: bold
1: italic
2: underline
3: strike
4: code
5: tagged
Each sub-array contains an element for each character of the text, with the
value 1 if it is formatted in the specific format, -1 if it is markup, and
0 otherwise.
removeMarks returns a both the array and a string, in which all of the
formatting marks have been removed.
"""
result = []
for markup in ["\*", "/", "_", "-", "`", "\'"]:
rList = []
r = QRegExp(r'(' + markup * 2 + ')(.+)(' + markup * 2 + ')')
r.setMinimal(True)
pos = r.indexIn(text, 0)
lastPos = 0
while pos >= 0:
# We have a winner
rList += [0] * (pos - lastPos)
rList += [2] * 2
rList += [1] * len(r.cap(2))
rList += [2] * 2
lastPos = pos + len(r.cap(0))
pos = r.indexIn(text, len(rList))
if len(rList) < len(text):
rList += [0] * (len(text) - len(rList))
result.append(rList)
return result
def textToFormatArrayNoMarkup(text):
"""
Same as textToFormatArray, except that it removes all the markup from the
text and returns two elements:
the array
the text without markup
"""
r = textToFormatArray(text)
result = [[], [], [], [], [], []]
rText = ""
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t != 2:
rText += text[i]
[result[k].append(r[k][i]) for k in range(len(r))]
return rText, result
def translateSelectionToUnformattedText(text, start, end):
"""
Translate the start / end of selection from a formatted text to an
unformatted one.
"""
r = textToFormatArray(text)
rStart, rEnd = start, end
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t == 2: # t == 2 means this character is markup
if i <= start: rStart -= 1
if i < end: rEnd -= 1
return rStart, rEnd
def translateSelectionToFormattedText(text, start, end):
"""
Translate the start / end of selection from a formatted text to an
unformatted one.
"""
r = textToFormatArray(text)
rStart, rEnd = start, end
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t == 2: # t == 2 means this character is markup
if i <= start: rStart -= 1
if i < end: rEnd -= 1
return rStart, rEnd
def printArray(array):
print(("".join([str(j) for j in array])))
def printArrays(arrays):
for i in arrays: printArray(i)
def reformatText(text, markupArray):
"""
Takes a text without formatting markup, and an array generated by
textToFormatArray, and adds the propper markup.
"""
rText = ""
markup = ["**", "//", "__", "--", "``", "''"]
for k, m in enumerate(markupArray):
# m = markupArray[k]
_open = False # Are we in an _openned markup
d = 0
alreadySeen = []
for i, t in enumerate(text):
insert = False
if not _open and m[i] == 1:
insert = True
_open = True
if _open and m[i] == 0:
insert = True
_open = False
if _open and m[i] > 1:
z = i
while m[z] == m[i]: z += 1
if m[z] != 1 and not m[i] in alreadySeen:
insert = True
_open = False
alreadySeen.append(m[i])
if insert:
rText += markup[k]
for j, m2 in enumerate(markupArray):
# The other array still have the same length
if j > k:
# Insert 2 for bold, 3 for italic, etc.
m2.insert(i + d, k + 2)
m2.insert(i + d, k + 2)
alreadySeen = []
d += 2
rText += t
if _open:
rText += markup[k]
for j, m2 in enumerate(markupArray):
# The other array still have the same length
if j > k:
# Insert 2 for bold, 3 for italic, etc.
m2.insert(i + d, k + 2)
m2.insert(i + d, k + 2)
text = rText
rText = ""
# Clean up
# Exclude first and last space of the markup
for markup in ["\*", "/", "_", "-", "`", "\'"]:
# r = QRegExp(r'(' + markup * 2 + ')(\s+)(.+)(' + markup * 2 + ')')
# r.setMinimal(True)
# text.replace(r, "\\2\\1\\3\\4")
text = re.sub(r'(' + markup * 2 + ')(\s+?)(.+?)(' + markup * 2 + ')',
"\\2\\1\\3\\4",
text)
# r = QRegExp(r'(' + markup * 2 + ')(.+)(\s+)(' + markup * 2 + ')')
# r.setMinimal(True)
# text.replace(r, "\\1\\2\\4\\3")
text = re.sub(r'(' + markup * 2 + ')(.+?)(\s+?)(' + markup * 2 + ')',
"\\1\\2\\4\\3",
text)
return text
def cleanFormat(text):
"""Makes markup clean (removes doubles, etc.)"""
t, a = textToFormatArrayNoMarkup(text)
return reformatText(t, a)
class State:
NORMAL = 0
TITLE_1 = 1
TITLE_2 = 2
TITLE_3 = 3
TITLE_4 = 4
TITLE_5 = 5
NUMBERED_TITLE_1 = 6
NUMBERED_TITLE_2 = 7
NUMBERED_TITLE_3 = 8
NUMBERED_TITLE_4 = 9
NUMBERED_TITLE_5 = 10
TITLES = [TITLE_1, TITLE_2, TITLE_3, TITLE_4, TITLE_5, NUMBERED_TITLE_1,
NUMBERED_TITLE_2, NUMBERED_TITLE_3, NUMBERED_TITLE_4,
NUMBERED_TITLE_5]
# AREA
COMMENT_AREA = 11
CODE_AREA = 12
RAW_AREA = 13
TAGGED_AREA = 14
# AREA MARKUP
COMMENT_AREA_BEGINS = 15
COMMENT_AREA_ENDS = 16
CODE_AREA_BEGINS = 17
CODE_AREA_ENDS = 18
RAW_AREA_BEGINS = 19
RAW_AREA_ENDS = 20
TAGGED_AREA_BEGINS = 21
TAGGED_AREA_ENDS = 22
# LINE
COMMENT_LINE = 30
CODE_LINE = 31
RAW_LINE = 32
TAGGED_LINE = 33
SETTINGS_LINE = 34
BLOCKQUOTE_LINE = 35
HORIZONTAL_LINE = 36
HEADER_LINE = 37
# LIST
LIST_BEGINS = 40
LIST_ENDS = 41
LIST_EMPTY = 42
LIST_BULLET = 43
LIST_BULLET_ENDS = 44
LIST = [40, 41, 42] + list(range(100, 201))
# TABLE
TABLE_LINE = 50
TABLE_HEADER = 51
# OTHER
MARKUP = 60
LINKS = 61
MACRO = 62
DEFAULT = 63
@staticmethod
def titleLevel(state):
"""
Returns the level of the title, from the block state.
"""
return {
State.TITLE_1: 1,
State.TITLE_2: 2,
State.TITLE_3: 3,
State.TITLE_4: 4,
State.TITLE_5: 5,
State.NUMBERED_TITLE_1: 1,
State.NUMBERED_TITLE_2: 2,
State.NUMBERED_TITLE_3: 3,
State.NUMBERED_TITLE_4: 4,
State.NUMBERED_TITLE_5: 5,
}.get(state, -1)

View file

@ -1,547 +0,0 @@
#!/usr/bin/python
# -*- coding: utf8 -*-
# This is aiming at implementing every rule from www.txt2tags.org/rules.html
# But we're not there yet.
# FIXME: macro words not hilighted properly if at the begining of a line.
# TODO: parse %!postproc et !%preproc, et si la ligne se termine par une couleur en commentaire (%#FF00FF),
# utiliser cette couleur pour highlighter. Permet des règles customisées par document, facilement.
import re
from PyQt5.QtCore import QRegExp, QDir, QFileInfo
from PyQt5.QtGui import QTextBlockFormat, QTextCursor, QTextCharFormat, QBrush
from manuskript.ui.editors.basicHighlighter import basicHighlighter
from manuskript.ui.editors.blockUserData import blockUserData
from manuskript.ui.editors.t2tFunctions import State, textToFormatArray
from manuskript.ui.editors.t2tHighlighterStyle import t2tHighlighterStyle
class t2tHighlighter(basicHighlighter):
"""Syntax highlighter for the Txt2Tags language.
"""
def __init__(self, editor, style="Default"):
basicHighlighter.__init__(self, editor)
# Stupid variable that fixes the loss of QTextBlockUserData.
self.thisDocument = editor.document()
self.style = t2tHighlighterStyle(self.editor, self._defaultCharFormat, style)
self.inDocRules = []
rules = [
(r'^\s*[-=_]{20,}\s*$', State.HORIZONTAL_LINE),
(r'^\s*(\+{1})([^\+].*[^\+])(\+{1})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_1),
(r'^\s*(\+{2})([^\+].*[^\+])(\+{2})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_2),
(r'^\s*(\+{3})([^\+].*[^\+])(\+{3})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_3),
(r'^\s*(\+{4})([^\+].*[^\+])(\+{4})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_4),
(r'^\s*(\+{5})([^\+].*[^\+])(\+{5})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_5),
(r'^\s*(={1})([^=].*[^=])(={1})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_1),
(r'^\s*(={2})([^=].*[^=])(={2})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_2),
(r'^\s*(={3})([^=].*[^=])(={3})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_3),
(r'^\s*(={4})([^=].*[^=])(={4})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_4),
(r'^\s*(={5})([^=].*[^=])(={5})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_5),
(r'^%!.*$', State.SETTINGS_LINE),
(r'^%[^!]?.*$', State.COMMENT_LINE),
(r'^\t.+$', State.BLOCKQUOTE_LINE),
(r'^(```)(.+)$', State.CODE_LINE),
(r'^(""")(.+)$', State.RAW_LINE),
(r'^(\'\'\')(.+)$', State.TAGGED_LINE),
(r'^\s*[-+:] [^ ].*$', State.LIST_BEGINS),
(r'^\s*[-+:]\s*$', State.LIST_ENDS),
(r'^ *\|\| .*$', State.TABLE_HEADER),
(r'^ *\| .*$', State.TABLE_LINE)
]
# Generate rules to identify blocks
State.Rules = [(QRegExp(pattern), state)
for (pattern, state) in rules]
State.Recursion = 0
def setDefaultCharFormat(self, cf):
self._defaultCharFormat = cf
self.setStyle()
self.rehighlight()
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
basicHighlighter.highlightBlockBefore(self, text)
# Check if syntax highlighting is enabled
if self.style is None:
default = QTextBlockFormat()
QTextCursor(self.currentBlock()).setBlockFormat(default)
print("t2tHighlighter.py: is style supposed to be None?")
return
block = self.currentBlock()
oldState = blockUserData.getUserState(block)
self.identifyBlock(block)
# formatBlock prevent undo/redo from working
# TODO: find a todo/undo compatible way of formatting block
# self.formatBlock(block)
state = blockUserData.getUserState(block)
data = blockUserData.getUserData(block)
inList = self.isList(block)
op = self.style.format(State.MARKUP)
# self.setFormat(0, len(text), self.style.format(State.DEFAULT))
# InDocRules: is it a settings which might have a specific rule,
# a comment which contains color infos, or a include conf?
# r'^%!p[or][se]t?proc[^\s]*\s*:\s*\'(.*)\'\s*\'.*\''
rlist = [QRegExp(r'^%!p[or][se]t?proc[^\s]*\s*:\s*((\'[^\']*\'|\"[^\"]*\")\s*(\'[^\']*\'|\"[^\"]*\"))'),
# pre/postproc
QRegExp(r'^%.*\s\((.*)\)'), # comment
QRegExp(r'^%!includeconf:\s*([^\s]*)\s*')] # includeconf
for r in rlist:
if r.indexIn(text) != -1:
self.parseInDocRules()
# Format the whole line:
for lineState in [
State.BLOCKQUOTE_LINE,
State.HORIZONTAL_LINE,
State.HEADER_LINE,
]:
if not inList and state == lineState:
self.setFormat(0, len(text), self.style.format(lineState))
for (lineState, marker) in [
(State.COMMENT_LINE, "%"),
(State.CODE_LINE, "```"),
(State.RAW_LINE, "\"\"\""),
(State.TAGGED_LINE, "'''"),
(State.SETTINGS_LINE, "%!")
]:
if state == lineState and \
not (inList and state == State.SETTINGS_LINE):
n = 0
# If it's a comment, we want to highlight all '%'.
if state == State.COMMENT_LINE:
while text[n:n + 1] == "%":
n += 1
n -= 1
# Apply Format
self.setFormat(0, len(marker) + n, op)
self.setFormat(len(marker) + n,
len(text) - len(marker) - n,
self.style.format(lineState))
# If it's a setting, we might do something
if state == State.SETTINGS_LINE:
# Target
r = QRegExp(r'^%!([^\s]+)\s*:\s*(\b\w*\b)$')
if r.indexIn(text) != -1:
setting = r.cap(1)
val = r.cap(2)
if setting == "target" and \
val in self.editor.main.targetsNames:
self.editor.fileWidget.preview.setPreferredTarget(val)
# Pre/postproc
r = QRegExp(r'^%!p[or][se]t?proc[^\s]*\s*:\s*((\'[^\']*\'|\"[^\"]*\")\s*(\'[^\']*\'|\"[^\"]*\"))')
if r.indexIn(text) != -1:
p = r.pos(1)
length = len(r.cap(1))
self.setFormat(p, length, self.style.makeFormat(base=self.format(p),
fixedPitch=True))
# Tables
for lineState in [State.TABLE_LINE, State.TABLE_HEADER]:
if state == lineState:
for i, t in enumerate(text):
if t == "|":
self.setFormat(i, 1, op)
else:
self.setFormat(i, 1, self.style.format(lineState))
# Lists
# if text == " p": print(data.isList())
if data.isList():
r = QRegExp(r'^\s*[\+\-\:]? ?')
r.indexIn(text)
self.setFormat(0, r.matchedLength(), self.style.format(State.LIST_BULLET))
# if state == State.LIST_BEGINS:
# r = QRegExp(r'^\s*[+-:] ')
# r.indexIn(text)
# self.setFormat(0, r.matchedLength(), self.style.format(State.LIST_BULLET))
if state == State.LIST_ENDS:
self.setFormat(0, len(text), self.style.format(State.LIST_BULLET_ENDS))
# Titles
if not inList and state in State.TITLES:
r = [i for (i, s) in State.Rules if s == state][0]
pos = r.indexIn(text)
if pos >= 0:
f = self.style.format(state)
# Uncomment for markup to be same size as title
# op = self.formats(preset="markup",
# base=self.formats(preset=state))
self.setFormat(r.pos(2), len(r.cap(2)), f)
self.setFormat(r.pos(1), len(r.cap(1)), op)
self.setFormat(r.pos(3), len(r.cap(3)), op)
# Areas: comment, code, raw tagged
for (begins, middle, ends) in [
(State.COMMENT_AREA_BEGINS, State.COMMENT_AREA, State.COMMENT_AREA_ENDS),
(State.CODE_AREA_BEGINS, State.CODE_AREA, State.CODE_AREA_ENDS),
(State.RAW_AREA_BEGINS, State.RAW_AREA, State.RAW_AREA_ENDS),
(State.TAGGED_AREA_BEGINS, State.TAGGED_AREA, State.TAGGED_AREA_ENDS),
]:
if state == middle:
self.setFormat(0, len(text), self.style.format(middle))
elif state in [begins, ends]:
self.setFormat(0, len(text), op)
# Inline formatting
if state not in [
# State.COMMENT_AREA,
# State.COMMENT_LINE,
State.RAW_AREA,
State.RAW_LINE,
State.CODE_AREA,
State.CODE_LINE,
State.TAGGED_AREA,
State.TAGGED_LINE,
State.SETTINGS_LINE,
State.HORIZONTAL_LINE,
] and state not in State.TITLES:
formatArray = textToFormatArray(text)
# InDocRules
for (r, c) in self.inDocRules:
i = re.finditer(r.decode('utf8'), text, re.UNICODE)
for m in i:
f = self.format(m.start())
l = m.end() - m.start()
if "," in c:
c1, c2 = c.split(",")
self.setFormat(m.start(), l,
self.style.makeFormat(color=c1, bgcolor=c2, base=f))
else:
self.setFormat(m.start(), l,
self.style.makeFormat(color=c, base=f))
# Links
if state not in [State.COMMENT_LINE, State.COMMENT_AREA]:
r = QRegExp(r'\[(\[[^\]]*\])?[^\]]*\s*([^\s]+)\]')
r.setMinimal(False)
pos = r.indexIn(text)
links = []
while pos >= 0:
# TODO: The text should not be formatted if [**not bold**]
# if max([k[pos] for k in formatArray]) == 0 or 1 == 1:
self.setFormat(pos, 1,
self.style.format(State.MARKUP))
self.setFormat(pos + 1, len(r.cap(0)) - 1,
self.style.format(State.LINKS))
self.setFormat(pos + len(r.cap(0)) - 1, 1,
self.style.format(State.MARKUP))
if r.pos(2) > 0:
_f = QTextCharFormat(self.style.format(State.LINKS))
_f.setForeground(QBrush(_f.foreground()
.color().lighter()))
_f.setFontUnderline(True)
self.setFormat(r.pos(2), len(r.cap(2)), _f)
links.append([pos, len(r.cap(0))]) # To remember for the next highlighter (single links)
pos = r.indexIn(text, pos + 1)
# Links like www.theologeek.ch, http://www.fsf.org, ...
# FIXME: - "http://adresse et http://adresse" is detected also as italic
# - some error, like "http://adress.htm." also color the final "."
# - also: adresse@email.com, ftp://, www2, www3, etc.
# - But for now, does the job
r = QRegExp(r'http://[^\s]*|www\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+[^\s]*')
# r.setMinimal(True)
pos = r.indexIn(text)
while pos >= 0:
for k in links:
# print pos, k[0], k[1]
if k[0] < pos < k[0] + k[1]: # already highlighted
break
else:
self.setFormat(pos, len(r.cap(0)), self.style.format(State.LINKS))
pos = r.indexIn(text, pos + 1)
# Bold, Italic, Underline, Code, Tagged, Strikeout
for i, t in enumerate(text):
f = self.format(i)
beautifiers = [k[i] for k in formatArray]
self.setFormat(i, 1, self.style.beautifyFormat(f, beautifiers))
# Macro words
for r in [r'(%%)\b\w+\b', r'(%%)\b\w+\b\(.+\)']:
r = QRegExp(r)
r.setMinimal(True)
pos = r.indexIn(text)
while pos >= 0:
if max([k[pos] for k in formatArray]) == 0:
self.setFormat(pos, len(r.cap(0)),
self.style.format(State.MACRO))
pos = r.indexIn(text, pos + 1)
# Highlighted word (for search)
if self.editor.highlightWord:
if self.editor.highligtCS and self.editor.highlightWord in text or \
not self.editor.highlightCs and self.editor.highlightWord.lower() in text.lower():
# if self.editor.highlightCS:
# s = self.editor.highlightWord
# else:
# s = self.editor.highlightWord.toLower()
# print(s)
p = text.indexOf(self.editor.highlightWord, cs=self.editor.highlightCS)
while p >= 0:
self.setFormat(p, len(self.editor.highlightWord),
self.style.makeFormat(preset="higlighted", base=self.format(p)))
p = text.indexOf(self.editor.highlightWord, p + 1, cs=self.editor.highlightCS)
### Highlight Selection
### TODO: way to slow, find another way.
##sel = self.editor.textCursor().selectedText()
##if len(sel) > 5: self.keywordRules.append((QRegExp(sel), "selected"))
## Do keyword formatting
# for expression, style in self.keywordRules:
# expression.setMinimal( True )
# index = expression.indexIn(text, 0)
## There might be more than one on the same line
# while index >= 0:
# length = expression.cap(0).length()
# f = self.formats(preset=style, base=self.formats(index))
# self.setFormat(index, length, f)
# index = expression.indexIn(text, index + length)
basicHighlighter.highlightBlockAfter(self, text)
def identifyBlock(self, block):
"""Identifies what block type it is, and set userState and userData
accordingly."""
text = block.text()
data = blockUserData.getUserData(block)
# Header Lines
# No header line here
# if block.blockNumber() == 0:
# block.setUserState(State.HEADER_LINE)
# return
# elif block.blockNumber() in [1, 2] and \
# self.document().findBlockByNumber(0).text():
# block.setUserState(State.HEADER_LINE)
# return
state = 0
inList = False
blankLinesBefore = 0
# if text.contains(QRegExp(r'^\s*[-+:] [^ ].*[^-+]{1}\s*$')):
if QRegExp(r'^\s*[-+:] [^ ].*[^-+]{1}\s*$').indexIn(text) != -1:
state = State.LIST_BEGINS
# List stuff
if self.isList(block.previous()) or state == State.LIST_BEGINS:
inList = True
# listLevel and leadingSpaces
# FIXME: not behaving exactly correctly...
lastData = blockUserData.getUserData(block.previous())
if state == State.LIST_BEGINS:
leadingSpaces = QRegExp(r'[-+:]').indexIn(text, 0)
data.setLeadingSpaces(leadingSpaces)
data.setListSymbol(text[leadingSpaces])
if self.isList(block.previous()):
# The last block was also a list.
# We need to check if this is the same level, or a sublist
if leadingSpaces > lastData.leadingSpaces():
# This is a sublevel list
data.setListLevel(lastData.listLevel() + 1)
else:
# This is same level
data.setListLevel(lastData.listLevel())
else:
data.setListLevel(1)
else:
data.setListLevel(lastData.listLevel())
data.setLeadingSpaces(lastData.leadingSpaces())
data.setListSymbol(lastData.listSymbol())
# Blank lines before (two = end of list)
blankLinesBefore = self.getBlankLines(block.previous())
if not QRegExp(r'^\s*$').indexIn(block.previous().text()) != -1 and \
not blockUserData.getUserState(block.previous()) in [State.COMMENT_LINE,
State.COMMENT_AREA, State.COMMENT_AREA_BEGINS,
State.COMMENT_AREA_ENDS]:
blankLinesBefore = 0
elif not blockUserData.getUserState(block.previous()) in \
[State.COMMENT_LINE, State.COMMENT_AREA,
State.COMMENT_AREA_BEGINS, State.COMMENT_AREA_ENDS]:
blankLinesBefore += 1
if blankLinesBefore == 2:
# End of list.
blankLinesBefore = 0
inList = False
if inList and QRegExp(r'^\s*$').indexIn(text) != -1:
state = State.LIST_EMPTY
# Areas
for (begins, middle, ends, marker) in [
(State.COMMENT_AREA_BEGINS, State.COMMENT_AREA, State.COMMENT_AREA_ENDS, "^%%%\s*$"),
(State.CODE_AREA_BEGINS, State.CODE_AREA, State.CODE_AREA_ENDS, "^```\s*$"),
(State.RAW_AREA_BEGINS, State.RAW_AREA, State.RAW_AREA_ENDS, "^\"\"\"\s*$"),
(State.TAGGED_AREA_BEGINS, State.TAGGED_AREA, State.TAGGED_AREA_ENDS, '^\'\'\'\s*$'),
]:
if QRegExp(marker).indexIn(text) != -1:
if blockUserData.getUserState(block.previous()) in [begins, middle]:
state = ends
break
else:
state = begins
break
if blockUserData.getUserState(block.previous()) in [middle, begins]:
state = middle
break
# Patterns (for lines)
if not state:
for (pattern, lineState) in State.Rules:
pos = pattern.indexIn(text)
if pos >= 0:
state = lineState
break
if state in [State.BLOCKQUOTE_LINE, State.LIST_ENDS]:
# FIXME: doesn't work exactly. Closes only the current level, not
# FIXME: the whole list.
inList = False
if inList and not state == State.LIST_BEGINS:
state += 100
if blankLinesBefore:
state += 100
block.setUserState(state)
block.setUserData(data)
def formatBlock(self, block):
"""
Formats the block according to its state.
"""
# TODO: Use QTextDocument format presets, and QTextBlock's
# TODO: blockFormatIndex. And move that in t2tHighlighterStyle.
state = block.userState()
blockFormat = QTextBlockFormat()
if state in [State.BLOCKQUOTE_LINE,
State.HEADER_LINE] + State.LIST:
blockFormat = self.style.formatBlock(block, state)
QTextCursor(block).setBlockFormat(blockFormat)
def getBlankLines(self, block):
"""Returns if there is a blank line before in the list."""
state = block.userState()
if state >= 200:
return 1
else:
return 0
def isList(self, block):
"""Returns TRUE if the block is in a list."""
if block.userState() == State.LIST_BEGINS or \
block.userState() >= 100:
return True
def setStyle(self, style="Default"):
if style in t2tHighlighterStyle.validStyles:
self.style = t2tHighlighterStyle(self.editor, self._defaultCharFormat, style)
else:
self.style = None
self.rehighlight()
def setFontPointSize(self, size):
self.defaultFontPointSize = size
self.style = t2tHighlighterStyle(self.editor, self.style.name)
self.rehighlight()
def parseInDocRules(self):
oldRules = self.inDocRules
self.inDocRules = []
t = self.thisDocument.toPlainText()
# Get all conf files
confs = []
lines = t.split("\n")
for l in lines:
r = QRegExp(r'^%!includeconf:\s*([^\s]*)\s*')
if r.indexIn(l) != -1:
confs.append(r.cap(1))
# Try to load conf files
for c in confs:
try:
import codecs
f = self.editor.fileWidget.file
d = QDir.cleanPath(QFileInfo(f).absoluteDir().absolutePath() + "/" + c)
file = codecs.open(d, 'r', "utf-8")
except:
print(("Error: cannot open {}.".format(c)))
continue
# We add the content to the current lines of the current document
lines += file.readlines() # lines.extend(file.readlines())
# b = self.thisDocument.firstBlock()
lastColor = ""
# while b.isValid():
for l in lines:
text = l # b.text()
r = QRegExp(r'^%!p[or][se]t?proc[^\s]*\s*:\s*(\'[^\']*\'|\"[^\"]*\")\s*(\'[^\']*\'|\"[^\"]*\")')
if r.indexIn(text) != -1:
rule = r.cap(1)[1:-1]
# Check if there was a color-comment above that post/preproc bloc
if lastColor:
self.inDocRules.append((str(rule), lastColor))
# Check if previous block is a comment like it should
else:
previousText = lines[lines.indexOf(l) - 1] # b.previous().text()
r = QRegExp(r'^%.*\s\((.*)\)')
if r.indexIn(previousText) != -1:
lastColor = r.cap(1)
self.inDocRules.append((str(rule), lastColor))
else:
lastColor = ""
# b = b.next()
if oldRules != self.inDocRules:
# Rules have changed, we need to rehighlight
# print("Rules have changed.", len(self.inDocRules))
# self.rehighlight() # Doesn't work (seg fault), why?
pass
# b = self.thisDocument.firstBlock()
# while b.isValid():
# for (r, c) in self.inDocRules:
# r = QRegExp(r)
# pos = r.indexIn(b.text())
# if pos >= 0:
# print("rehighlighting:", b.text())
# self.rehighlightBlock(b)
# break
# b = b.next()

View file

@ -1,252 +0,0 @@
#!/usr/bin/python
# -*- coding: utf8 -*-
# TODO: creates a general way to generate styles (and edit/import/export)
from PyQt5.QtCore import QRegExp, Qt
from PyQt5.QtGui import QFont, QTextBlockFormat, QColor, QFontMetrics, QTextCharFormat
from PyQt5.QtWidgets import qApp
from manuskript.ui.editors.blockUserData import blockUserData
from manuskript.ui.editors.t2tFunctions import State
class t2tHighlighterStyle():
"""Style for the Syntax highlighter for the Txt2Tags language.
"""
validStyles = ["Default", "Monospace"]
def __init__(self, editor, charFormat, name="Default"):
self.editor = editor
self.name = name
self._defaultCharFormat = charFormat
# Defaults
# self.defaultFontPointSize = self.editor.defaultFontPointSize
self.defaultFontFamily = qApp.font().family()
self.tabStopWidth = 40
self.setupEditor()
if self.name == "Default":
self.initDefaults()
# Temporary other theme
elif self.name == "Monospace":
self.defaultFontFamily = "Monospace"
self.initDefaults()
for i in self.styles:
f = self.styles[i]
f.setFontFixedPitch(True)
f.setFontFamily(self.defaultFontFamily)
f.setFontPointSize(self._defaultCharFormat.font().pointSize())
self.styles[i] = f
def setupEditor(self):
self.editor.setTabStopWidth(self.tabStopWidth)
def initDefaults(self):
self.styles = {}
for i in [State.CODE_AREA,
State.CODE_LINE,
State.COMMENT_AREA,
State.COMMENT_LINE,
State.SETTINGS_LINE,
State.BLOCKQUOTE_LINE,
State.RAW_AREA,
State.RAW_LINE,
State.TAGGED_AREA,
State.TAGGED_LINE,
State.TITLE_1,
State.TITLE_2,
State.TITLE_3,
State.TITLE_4,
State.TITLE_5,
State.NUMBERED_TITLE_1,
State.NUMBERED_TITLE_2,
State.NUMBERED_TITLE_3,
State.NUMBERED_TITLE_4,
State.NUMBERED_TITLE_5,
State.TABLE_HEADER,
State.TABLE_LINE,
State.HORIZONTAL_LINE,
State.MARKUP,
State.LIST_BULLET,
State.LIST_BULLET_ENDS,
State.LINKS,
State.MACRO,
State.DEFAULT,
State.HEADER_LINE]:
self.styles[i] = self.makeFormat(preset=i)
def format(self, state):
return self.styles[state]
def beautifyFormat(self, base, beautifiers):
"""Apply beautifiers given in beautifiers array to format"""
if max(beautifiers) == 2:
return self.makeFormat(preset=State.MARKUP, base=base)
else:
if beautifiers[0]: # bold
base.setFontWeight(QFont.Bold)
if beautifiers[1]: # italic
base.setFontItalic(True)
if beautifiers[2]: # underline
base.setFontUnderline(True)
if beautifiers[3]: # strikeout
base.setFontStrikeOut(True)
if beautifiers[4]: # code
base = self.makeFormat(base=base, preset=State.CODE_LINE)
if beautifiers[5]: # tagged
base = self.makeFormat(base=base, preset=State.TAGGED_LINE)
return base
def formatBlock(self, block, state):
"""Apply transformation to given block."""
blockFormat = QTextBlockFormat()
if state == State.BLOCKQUOTE_LINE:
# Number of tabs
n = block.text().indexOf(QRegExp(r'[^\t]'), 0)
blockFormat.setIndent(0)
blockFormat.setTextIndent(-self.tabStopWidth * n)
blockFormat.setLeftMargin(self.tabStopWidth * n)
# blockFormat.setRightMargin(self.editor.contentsRect().width()
# - self.editor.lineNumberAreaWidth()
# - fm.width("X") * self.editor.LimitLine
# + self.editor.tabStopWidth())
blockFormat.setAlignment(Qt.AlignJustify)
if self.name == "Default":
blockFormat.setTopMargin(5)
blockFormat.setBottomMargin(5)
elif state == State.HEADER_LINE:
blockFormat.setBackground(QColor("#EEEEEE"))
elif state in State.LIST:
data = blockUserData.getUserData(block)
if str(data.listSymbol()) in "+-":
blockFormat.setBackground(QColor("#EEFFEE"))
else:
blockFormat.setBackground(QColor("#EEEEFA"))
n = blockUserData.getUserData(block).leadingSpaces() + 1
f = QFontMetrics(QFont(self.defaultFontFamily,
self._defaultCharFormat.font().pointSize()))
fm = f.width(" " * n +
blockUserData.getUserData(block).listSymbol())
blockFormat.setTextIndent(-fm)
blockFormat.setLeftMargin(fm)
if blockUserData.getUserState(block) == State.LIST_BEGINS and \
self.name == "Default":
blockFormat.setTopMargin(5)
return blockFormat
def makeFormat(self, color='', style='', size='', base='', fixedPitch='',
preset='', title_level='', bgcolor=''):
"""
Returns a QTextCharFormat with the given attributes, using presets.
"""
_color = QColor()
# _format = QTextCharFormat()
# _format.setFont(self.editor.font())
# size = _format.fontPointSize()
_format = QTextCharFormat(self._defaultCharFormat)
# Base
if base:
_format = base
# Presets
if preset in [State.CODE_AREA, State.CODE_LINE, "code"]:
style = "bold"
color = "black"
fixedPitch = True
_format.setBackground(QColor("#EEEEEE"))
if preset in [State.COMMENT_AREA, State.COMMENT_LINE, "comment"]:
style = "italic"
color = "darkGreen"
if preset in [State.SETTINGS_LINE, "setting", State.MACRO]:
# style = "italic"
color = "magenta"
if preset in [State.BLOCKQUOTE_LINE]:
color = "red"
if preset in [State.HEADER_LINE]:
size *= 2
# print size
if preset in [State.RAW_AREA, State.RAW_LINE, "raw"]:
color = "blue"
if preset in [State.TAGGED_AREA, State.TAGGED_LINE, "tagged"]:
color = "purple"
if preset in State.TITLES:
style = "bold"
color = "darkRed" if State.titleLevel(preset) % 2 == 1 else "blue"
size = (self._defaultCharFormat.font().pointSize()
+ 11 - 2 * State.titleLevel(preset))
if preset == State.TABLE_HEADER:
style = "bold"
color = "darkMagenta"
if preset == State.TABLE_LINE:
color = "darkMagenta"
if preset == State.LIST_BULLET:
color = "red"
style = "bold"
fixedPitch = True
if preset == State.LIST_BULLET_ENDS:
color = "darkGray"
fixedPitch = True
if preset in [State.MARKUP, "markup"]:
color = "darkGray"
if preset in [State.HORIZONTAL_LINE]:
color = "cyan"
fixedPitch = True
if preset == State.LINKS:
color = "blue"
# style="underline"
if preset == "selected":
_format.setBackground(QColor("yellow"))
if preset == "higlighted":
bgcolor = "yellow"
# if preset == State.DEFAULT:
# size = self.defaultFontPointSize
# _format.setFontFamily(self.defaultFontFamily)
# Manual formatting
if color:
_color.setNamedColor(color)
_format.setForeground(_color)
if bgcolor:
_color.setNamedColor(bgcolor)
_format.setBackground(_color)
if 'bold' in style:
_format.setFontWeight(QFont.Bold)
if 'italic' in style:
_format.setFontItalic(True)
if 'strike' in style:
_format.setFontStrikeOut(True)
if 'underline' in style:
_format.setFontUnderline(True)
if size:
_format.setFontPointSize(size)
if fixedPitch:
_format.setFontFixedPitch(True)
return _format

View file

@ -61,9 +61,6 @@ class textFormat(QWidget, Ui_textFormat):
elif item.isText():
self.align.setVisible(False)
self.format.setVisible(False)
elif item.isT2T():
self.align.setVisible(False)
def setFormat(self):
act = self.sender()

View file

@ -9,7 +9,7 @@ from PyQt5.QtCore import QSettings, QRect, QSize, Qt, QPoint, QFile, QIODevice,
from PyQt5.QtGui import QPixmap, QPainter, QColor, QBrush, QImage, QTextBlockFormat, QTextCharFormat, QFont, qGray
from PyQt5.QtWidgets import qApp, QFrame
from manuskript.functions import allPaths, appPath
from manuskript.functions import allPaths, appPath, findBackground, findFirstFile
from manuskript.ui.views.textEditView import textEditView
@ -119,19 +119,6 @@ def findThemePath(themeName):
return p
def findBackground(filename):
return findFirstFile(re.escape(filename), "resources/backgrounds")
def findFirstFile(regex, path="resources"):
paths = allPaths(path)
for p in paths:
lst = os.listdir(p)
for l in lst:
if re.match(regex, l):
return os.path.join(p, l)
def generateTheme(themeDatas, screenRect):
# Window Background
px = QPixmap(screenRect.size())

View file

@ -2,8 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/mainWindow.ui'
#
# Created: Sun Feb 28 13:22:17 2016
# by: PyQt5 UI code generator 5.2.1
# Created by: PyQt5 UI code generator 5.4.2
#
# WARNING! All changes made in this file will be lost!
@ -17,8 +16,8 @@ class Ui_MainWindow(object):
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout_2.setSpacing(0)
self.horizontalLayout_2.setContentsMargins(0, 6, 0, 0)
self.horizontalLayout_2.setSpacing(0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.stack = QtWidgets.QStackedWidget(self.centralwidget)
self.stack.setObjectName("stack")
@ -165,18 +164,18 @@ class Ui_MainWindow(object):
self.label = QtWidgets.QLabel(self.tabSummaryPage1)
self.label.setObjectName("label")
self.verticalLayout_5.addWidget(self.label)
self.txtSummarySentance = textEditView(self.tabSummaryPage1)
self.txtSummarySentence = textEditView(self.tabSummaryPage1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.txtSummarySentance.sizePolicy().hasHeightForWidth())
self.txtSummarySentance.setSizePolicy(sizePolicy)
self.txtSummarySentance.setObjectName("txtSummarySentance")
self.verticalLayout_5.addWidget(self.txtSummarySentance)
self.lblSummaryWCSentance = QtWidgets.QLabel(self.tabSummaryPage1)
self.lblSummaryWCSentance.setText("")
self.lblSummaryWCSentance.setObjectName("lblSummaryWCSentance")
self.verticalLayout_5.addWidget(self.lblSummaryWCSentance)
sizePolicy.setHeightForWidth(self.txtSummarySentence.sizePolicy().hasHeightForWidth())
self.txtSummarySentence.setSizePolicy(sizePolicy)
self.txtSummarySentence.setObjectName("txtSummarySentence")
self.verticalLayout_5.addWidget(self.txtSummarySentence)
self.lblSummaryWCSentence = QtWidgets.QLabel(self.tabSummaryPage1)
self.lblSummaryWCSentence.setText("")
self.lblSummaryWCSentence.setObjectName("lblSummaryWCSentence")
self.verticalLayout_5.addWidget(self.lblSummaryWCSentence)
spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_5.addItem(spacerItem5)
self.tabSummary.addWidget(self.tabSummaryPage1)
@ -189,15 +188,15 @@ class Ui_MainWindow(object):
self.label_21 = QtWidgets.QLabel(self.tabSummaryPage2)
self.label_21.setObjectName("label_21")
self.verticalLayout.addWidget(self.label_21)
self.txtSummarySentance_2 = textEditView(self.tabSummaryPage2)
self.txtSummarySentence_2 = textEditView(self.tabSummaryPage2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.txtSummarySentance_2.sizePolicy().hasHeightForWidth())
self.txtSummarySentance_2.setSizePolicy(sizePolicy)
self.txtSummarySentance_2.setReadOnly(True)
self.txtSummarySentance_2.setObjectName("txtSummarySentance_2")
self.verticalLayout.addWidget(self.txtSummarySentance_2)
sizePolicy.setHeightForWidth(self.txtSummarySentence_2.sizePolicy().hasHeightForWidth())
self.txtSummarySentence_2.setSizePolicy(sizePolicy)
self.txtSummarySentence_2.setReadOnly(True)
self.txtSummarySentence_2.setObjectName("txtSummarySentence_2")
self.verticalLayout.addWidget(self.txtSummarySentence_2)
spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem6)
self.horizontalLayout_4.addLayout(self.verticalLayout)
@ -337,12 +336,12 @@ class Ui_MainWindow(object):
self.groupBox.setObjectName("groupBox")
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.groupBox)
self.verticalLayout_8.setObjectName("verticalLayout_8")
self.lstPersos = persoTreeView(self.groupBox)
self.lstPersos.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.lstPersos.setDragEnabled(True)
self.lstPersos.setObjectName("lstPersos")
self.lstPersos.headerItem().setText(0, "1")
self.verticalLayout_8.addWidget(self.lstPersos)
self.lstCharacters = characterTreeView(self.groupBox)
self.lstCharacters.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.lstCharacters.setDragEnabled(True)
self.lstCharacters.setObjectName("lstCharacters")
self.lstCharacters.headerItem().setText(0, "1")
self.verticalLayout_8.addWidget(self.lstCharacters)
self.horizontalLayout_14 = QtWidgets.QHBoxLayout()
self.horizontalLayout_14.setObjectName("horizontalLayout_14")
self.btnAddPerso = QtWidgets.QPushButton(self.groupBox)
@ -433,9 +432,9 @@ class Ui_MainWindow(object):
self.txtPersoEpiphany = textEditView(self.infos)
self.txtPersoEpiphany.setObjectName("txtPersoEpiphany")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.txtPersoEpiphany)
self.txtPersoSummarySentance = textEditView(self.infos)
self.txtPersoSummarySentance.setObjectName("txtPersoSummarySentance")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummarySentance)
self.txtPersoSummarySentence = textEditView(self.infos)
self.txtPersoSummarySentence.setObjectName("txtPersoSummarySentence")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummarySentence)
self.txtPersoSummaryPara = textEditView(self.infos)
self.txtPersoSummaryPara.setObjectName("txtPersoSummaryPara")
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummaryPara)
@ -809,7 +808,6 @@ 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)
@ -996,7 +994,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, 20))
self.menubar.setGeometry(QtCore.QRect(0, 0, 1112, 30))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
@ -1004,8 +1002,6 @@ class Ui_MainWindow(object):
icon = QtGui.QIcon.fromTheme("folder-recent")
self.menuRecents.setIcon(icon)
self.menuRecents.setObjectName("menuRecents")
self.menuMode = QtWidgets.QMenu(self.menubar)
self.menuMode.setObjectName("menuMode")
self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
self.menuTools = QtWidgets.QMenu(self.menubar)
@ -1014,6 +1010,8 @@ class Ui_MainWindow(object):
self.menuEdit.setObjectName("menuEdit")
self.menuView = QtWidgets.QMenu(self.menubar)
self.menuView.setObjectName("menuView")
self.menuMode = QtWidgets.QMenu(self.menuView)
self.menuMode.setObjectName("menuMode")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
@ -1113,16 +1111,16 @@ class Ui_MainWindow(object):
self.actStatus.setObjectName("actStatus")
self.actViewTree = QtWidgets.QAction(MainWindow)
self.actViewTree.setObjectName("actViewTree")
self.actModeNorma = QtWidgets.QAction(MainWindow)
self.actModeNorma.setCheckable(True)
self.actModeNorma.setChecked(True)
self.actModeNorma.setObjectName("actModeNorma")
self.actModeSimple = QtWidgets.QAction(MainWindow)
self.actModeSimple.setCheckable(True)
self.actModeSimple.setChecked(True)
self.actModeSimple.setObjectName("actModeSimple")
self.actModeFractal = QtWidgets.QAction(MainWindow)
self.actModeFractal.setCheckable(True)
self.actModeFractal.setObjectName("actModeFractal")
self.actModeFiction = QtWidgets.QAction(MainWindow)
self.actModeFiction.setCheckable(True)
self.actModeFiction.setObjectName("actModeFiction")
self.actModeSnowflake = QtWidgets.QAction(MainWindow)
self.actModeSnowflake.setCheckable(True)
self.actModeSnowflake.setObjectName("actModeSnowflake")
self.actViewCork = QtWidgets.QAction(MainWindow)
self.actViewCork.setObjectName("actViewCork")
self.actViewOutline = QtWidgets.QAction(MainWindow)
@ -1148,25 +1146,26 @@ class Ui_MainWindow(object):
self.menuFile.addAction(self.actCompile)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actQuit)
self.menuMode.addAction(self.actModeNorma)
self.menuMode.addAction(self.actModeSimple)
self.menuMode.addAction(self.actModeFractal)
self.menuHelp.addAction(self.actShowHelp)
self.menuTools.addAction(self.actSpellcheck)
self.menuTools.addAction(self.actToolFrequency)
self.menuEdit.addAction(self.actLabels)
self.menuEdit.addAction(self.actStatus)
self.menuEdit.addAction(self.actSettings)
self.menuMode.addAction(self.actModeSimple)
self.menuMode.addAction(self.actModeFiction)
self.menuMode.addAction(self.actModeSnowflake)
self.menuView.addAction(self.menuMode.menuAction())
self.menuView.addSeparator()
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuEdit.menuAction())
self.menubar.addAction(self.menuMode.menuAction())
self.menubar.addAction(self.menuView.menuAction())
self.menubar.addAction(self.menuTools.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.retranslateUi(MainWindow)
self.stack.setCurrentIndex(1)
self.tabMain.setCurrentIndex(6)
self.tabMain.setCurrentIndex(4)
self.tabSummary.setCurrentIndex(0)
self.tabPersos.setCurrentIndex(0)
self.tabPlot.setCurrentIndex(0)
@ -1196,12 +1195,12 @@ class Ui_MainWindow(object):
self.tabMain.setTabText(self.tabMain.indexOf(self.lytTabOverview), _translate("MainWindow", "General"))
self.label_9.setText(_translate("MainWindow", "Situation:"))
self.label_29.setText(_translate("MainWindow", "Summary:"))
self.cmbSummary.setItemText(0, _translate("MainWindow", "One sentance"))
self.cmbSummary.setItemText(0, _translate("MainWindow", "One sentence"))
self.cmbSummary.setItemText(1, _translate("MainWindow", "One paragraph"))
self.cmbSummary.setItemText(2, _translate("MainWindow", "One page"))
self.cmbSummary.setItemText(3, _translate("MainWindow", "Full"))
self.label.setText(_translate("MainWindow", "One sentance summary"))
self.label_21.setText(_translate("MainWindow", "One sentance summary"))
self.label.setText(_translate("MainWindow", "One sentence summary"))
self.label_21.setText(_translate("MainWindow", "One sentence summary"))
self.label_2.setText(_translate("MainWindow", "One paragraph summary"))
self.label_22.setText(_translate("MainWindow", "One paragraph summary"))
self.label_17.setText(_translate("MainWindow", "Expand each sentence of your one paragraph summary to a paragraph"))
@ -1221,7 +1220,7 @@ class Ui_MainWindow(object):
self.label_5.setText(_translate("MainWindow", "Goal"))
self.label_6.setText(_translate("MainWindow", "Conflict"))
self.label_7.setText(_translate("MainWindow", "Epiphany"))
self.label_24.setText(_translate("MainWindow", "<html><head/><body><p align=\"right\">One sentance<br/>summary</p></body></html>"))
self.label_24.setText(_translate("MainWindow", "<html><head/><body><p align=\"right\">One sentence<br/>summary</p></body></html>"))
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.tabPersos.setTabText(self.tabPersos.indexOf(self.infos), _translate("MainWindow", "Basic infos"))
@ -1266,13 +1265,13 @@ class Ui_MainWindow(object):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_20), _translate("MainWindow", "Outline"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Labels"))
self.tabMain.setTabText(self.tabMain.indexOf(self.lytTabDebug), _translate("MainWindow", "Debug"))
self.menuFile.setTitle(_translate("MainWindow", "Fi&le"))
self.menuFile.setTitle(_translate("MainWindow", "&File"))
self.menuRecents.setTitle(_translate("MainWindow", "&Recents"))
self.menuMode.setTitle(_translate("MainWindow", "&Mode"))
self.menuHelp.setTitle(_translate("MainWindow", "Help"))
self.menuHelp.setTitle(_translate("MainWindow", "&Help"))
self.menuTools.setTitle(_translate("MainWindow", "&Tools"))
self.menuEdit.setTitle(_translate("MainWindow", "E&dit"))
self.menuEdit.setTitle(_translate("MainWindow", "&Edit"))
self.menuView.setTitle(_translate("MainWindow", "&View"))
self.menuMode.setTitle(_translate("MainWindow", "&Mode"))
self.dckCheatSheet.setWindowTitle(_translate("MainWindow", "&Cheat sheet"))
self.dckSearch.setWindowTitle(_translate("MainWindow", "Sea&rch"))
self.dckNavigation.setWindowTitle(_translate("MainWindow", "&Navigation"))
@ -1291,9 +1290,9 @@ class Ui_MainWindow(object):
self.actLabels.setText(_translate("MainWindow", "&Labels..."))
self.actStatus.setText(_translate("MainWindow", "&Status..."))
self.actViewTree.setText(_translate("MainWindow", "Tree"))
self.actModeNorma.setText(_translate("MainWindow", "&Normal"))
self.actModeSimple.setText(_translate("MainWindow", "&Simple"))
self.actModeFractal.setText(_translate("MainWindow", "&Fractal"))
self.actModeFiction.setText(_translate("MainWindow", "&Fiction"))
self.actModeSnowflake.setText(_translate("MainWindow", "S&nowflake"))
self.actViewCork.setText(_translate("MainWindow", "Index cards"))
self.actViewOutline.setText(_translate("MainWindow", "Outline"))
self.actSettings.setText(_translate("MainWindow", "S&ettings"))
@ -1303,18 +1302,18 @@ class Ui_MainWindow(object):
self.actCompile.setShortcut(_translate("MainWindow", "F6"))
self.actToolFrequency.setText(_translate("MainWindow", "&Frequency Analyzer"))
from manuskript.ui.views.lineEditView import lineEditView
from manuskript.ui.cheatSheet import cheatSheet
from manuskript.ui.editors.mainEditor import mainEditor
from manuskript.ui.views.outlineView import outlineView
from manuskript.ui.views.treeView import treeView
from manuskript.ui.views.textEditView import textEditView
from manuskript.ui.views.storylineView import storylineView
from manuskript.ui.views.persoTreeView import persoTreeView
from manuskript.ui.views.plotTreeView import plotTreeView
from manuskript.ui.search import search
from manuskript.ui.views.basicItemView import basicItemView
from manuskript.ui.welcome import welcome
from manuskript.ui.views.textEditCompleter import textEditCompleter
from manuskript.ui.sldImportance import sldImportance
from manuskript.ui.views.characterTreeView import characterTreeView
from manuskript.ui.views.lineEditView import lineEditView
from manuskript.ui.views.metadataView import metadataView
from manuskript.ui.views.outlineView import outlineView
from manuskript.ui.views.plotTreeView import plotTreeView
from manuskript.ui.views.sldImportance import sldImportance
from manuskript.ui.views.storylineView import storylineView
from manuskript.ui.views.textEditCompleter import textEditCompleter
from manuskript.ui.views.textEditView import textEditView
from manuskript.ui.views.treeView import treeView
from manuskript.ui.welcome import welcome

View file

@ -124,7 +124,7 @@
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>6</number>
<number>4</number>
</property>
<property name="documentMode">
<bool>true</bool>
@ -342,7 +342,7 @@
<widget class="QComboBox" name="cmbSummary">
<item>
<property name="text">
<string>One sentance</string>
<string>One sentence</string>
</property>
</item>
<item>
@ -374,12 +374,12 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>One sentance summary</string>
<string>One sentence summary</string>
</property>
</widget>
</item>
<item>
<widget class="textEditView" name="txtSummarySentance">
<widget class="textEditView" name="txtSummarySentence">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -389,7 +389,7 @@
</widget>
</item>
<item>
<widget class="QLabel" name="lblSummaryWCSentance">
<widget class="QLabel" name="lblSummaryWCSentence">
<property name="text">
<string/>
</property>
@ -417,12 +417,12 @@
<item>
<widget class="QLabel" name="label_21">
<property name="text">
<string>One sentance summary</string>
<string>One sentence summary</string>
</property>
</widget>
</item>
<item>
<widget class="textEditView" name="txtSummarySentance_2">
<widget class="textEditView" name="txtSummarySentence_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -714,7 +714,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="persoTreeView" name="lstPersos">
<widget class="characterTreeView" name="lstCharacters">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
@ -823,7 +823,7 @@
<item row="6" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;One sentance&lt;br/&gt;summary&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;One sentence&lt;br/&gt;summary&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@ -899,7 +899,7 @@
<widget class="textEditView" name="txtPersoEpiphany"/>
</item>
<item row="6" column="1">
<widget class="textEditView" name="txtPersoSummarySentance"/>
<widget class="textEditView" name="txtPersoSummarySentence"/>
</item>
<item row="7" column="1">
<widget class="textEditView" name="txtPersoSummaryPara"/>
@ -1979,12 +1979,12 @@
<x>0</x>
<y>0</y>
<width>1112</width>
<height>20</height>
<height>30</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>Fi&amp;le</string>
<string>&amp;File</string>
</property>
<widget class="QMenu" name="menuRecents">
<property name="title">
@ -2006,17 +2006,9 @@
<addaction name="separator"/>
<addaction name="actQuit"/>
</widget>
<widget class="QMenu" name="menuMode">
<property name="title">
<string>&amp;Mode</string>
</property>
<addaction name="actModeNorma"/>
<addaction name="actModeSimple"/>
<addaction name="actModeFractal"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
<string>&amp;Help</string>
</property>
<addaction name="actShowHelp"/>
</widget>
@ -2029,7 +2021,7 @@
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>E&amp;dit</string>
<string>&amp;Edit</string>
</property>
<addaction name="actLabels"/>
<addaction name="actStatus"/>
@ -2039,10 +2031,19 @@
<property name="title">
<string>&amp;View</string>
</property>
<widget class="QMenu" name="menuMode">
<property name="title">
<string>&amp;Mode</string>
</property>
<addaction name="actModeSimple"/>
<addaction name="actModeFiction"/>
<addaction name="actModeSnowflake"/>
</widget>
<addaction name="menuMode"/>
<addaction name="separator"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuMode"/>
<addaction name="menuView"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/>
@ -2266,31 +2267,31 @@ QListView::item:hover {
<string>Tree</string>
</property>
</action>
<action name="actModeNorma">
<action name="actModeSimple">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Normal</string>
</property>
</action>
<action name="actModeSimple">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Simple</string>
</property>
</action>
<action name="actModeFractal">
<action name="actModeFiction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Fractal</string>
<string>&amp;Fiction</string>
</property>
</action>
<action name="actModeSnowflake">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>S&amp;nowflake</string>
</property>
</action>
<action name="actViewCork">
@ -2358,7 +2359,7 @@ QListView::item:hover {
<customwidget>
<class>sldImportance</class>
<extends>QWidget</extends>
<header>manuskript.ui.sldImportance.h</header>
<header>manuskript.ui.views.sldImportance.h</header>
<container>1</container>
</customwidget>
<customwidget>
@ -2396,9 +2397,9 @@ QListView::item:hover {
<container>1</container>
</customwidget>
<customwidget>
<class>persoTreeView</class>
<class>characterTreeView</class>
<extends>QTreeWidget</extends>
<header>manuskript.ui.views.persoTreeView.h</header>
<header>manuskript.ui.views.characterTreeView.h</header>
</customwidget>
<customwidget>
<class>cheatSheet</class>

View file

@ -144,8 +144,6 @@ class revisions(QWidget, Ui_revisions):
textBefore = [r[1] for r in item.revisions() if r[0] == ts][0]
if self.actShowVersion.isChecked():
if item.type() == "t2t":
textBefore = Ref.basicT2TFormat(textBefore)
self.view.setText(textBefore)
return
@ -160,7 +158,7 @@ class revisions(QWidget, Ui_revisions):
else:
_format = lambda x: x
extra = "" if item.type() == "html" else "<br>"
extra = "<br>"
diff = [d for d in diff if d and not d[:2] == "? "]
mydiff = ""
skip = False
@ -177,8 +175,6 @@ class revisions(QWidget, Ui_revisions):
# Same line
if op == " " and not self.actDiffOnly.isChecked():
if item.type() == "t2t":
txt = Ref.basicT2TFormat(txt)
mydiff += "{}{}".format(txt, extra)
elif op == "- " and op2 == "+ ":

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/revisions_ui.ui'
#
# Created: Tue Jul 14 19:00:07 2015
# Created: Wed Mar 2 00:30:17 2016
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!

View file

@ -84,7 +84,7 @@ class search(QWidget, Ui_search):
lstColumns = [
("Title", Outline.title.value),
("Text", Outline.text.value),
("Summary", Outline.summarySentance.value),
("Summary", Outline.summarySentence.value),
("Summary", Outline.summaryFull.value),
("Notes", Outline.notes.value),
("POV", Outline.POV.value),

View file

@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Settings(object):
def setupUi(self, Settings):
Settings.setObjectName("Settings")
Settings.resize(658, 491)
Settings.resize(658, 507)
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(Settings)
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.lstMenu = QtWidgets.QListWidget(Settings)
@ -76,6 +76,30 @@ class Ui_Settings(object):
self.cmbStyle.setObjectName("cmbStyle")
self.verticalLayout_5.addWidget(self.cmbStyle)
self.verticalLayout_7.addWidget(self.groupBox_2)
self.groupBox_14 = QtWidgets.QGroupBox(self.stackedWidgetPage1)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.groupBox_14.setFont(font)
self.groupBox_14.setObjectName("groupBox_14")
self.verticalLayout_20 = QtWidgets.QVBoxLayout(self.groupBox_14)
self.verticalLayout_20.setObjectName("verticalLayout_20")
self.label_52 = QtWidgets.QLabel(self.groupBox_14)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.label_52.setFont(font)
self.label_52.setWordWrap(True)
self.label_52.setObjectName("label_52")
self.verticalLayout_20.addWidget(self.label_52)
self.cmbTranslation = QtWidgets.QComboBox(self.groupBox_14)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.cmbTranslation.setFont(font)
self.cmbTranslation.setObjectName("cmbTranslation")
self.verticalLayout_20.addWidget(self.cmbTranslation)
self.verticalLayout_7.addWidget(self.groupBox_14)
self.groupBox_10 = QtWidgets.QGroupBox(self.stackedWidgetPage1)
font = QtGui.QFont()
font.setBold(True)
@ -181,31 +205,16 @@ class Ui_Settings(object):
self.chkSaveOnQuit.setChecked(True)
self.chkSaveOnQuit.setObjectName("chkSaveOnQuit")
self.verticalLayout_6.addWidget(self.chkSaveOnQuit)
self.chkSaveToZip = QtWidgets.QCheckBox(self.groupBox)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.chkSaveToZip.setFont(font)
self.chkSaveToZip.setStatusTip("")
self.chkSaveToZip.setChecked(True)
self.chkSaveToZip.setObjectName("chkSaveToZip")
self.verticalLayout_6.addWidget(self.chkSaveToZip)
self.verticalLayout_7.addWidget(self.groupBox)
self.groupBox_11 = QtWidgets.QGroupBox(self.stackedWidgetPage1)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.groupBox_11.setFont(font)
self.groupBox_11.setObjectName("groupBox_11")
self.verticalLayout_19 = QtWidgets.QVBoxLayout(self.groupBox_11)
self.verticalLayout_19.setObjectName("verticalLayout_19")
self.label_35 = QtWidgets.QLabel(self.groupBox_11)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.label_35.setFont(font)
self.label_35.setWordWrap(True)
self.label_35.setObjectName("label_35")
self.verticalLayout_19.addWidget(self.label_35)
self.cmbDefaultTextType = QtWidgets.QComboBox(self.groupBox_11)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.cmbDefaultTextType.setFont(font)
self.cmbDefaultTextType.setObjectName("cmbDefaultTextType")
self.verticalLayout_19.addWidget(self.cmbDefaultTextType)
self.verticalLayout_7.addWidget(self.groupBox_11)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_7.addItem(spacerItem2)
self.stack.addWidget(self.stackedWidgetPage1)
@ -1599,7 +1608,7 @@ class Ui_Settings(object):
self.horizontalLayout_8.addWidget(self.stack)
self.retranslateUi(Settings)
self.stack.setCurrentIndex(2)
self.stack.setCurrentIndex(0)
self.tabViews.setCurrentIndex(0)
self.themeStack.setCurrentIndex(1)
self.themeEditStack.setCurrentIndex(0)
@ -1628,6 +1637,8 @@ class Ui_Settings(object):
self.lblTitleGeneral.setText(_translate("Settings", "General settings"))
self.groupBox_2.setTitle(_translate("Settings", "Application style"))
self.label_2.setText(_translate("Settings", "You might need to restart manuskript in order to avoid some visual issues."))
self.groupBox_14.setTitle(_translate("Settings", "Application language"))
self.label_52.setText(_translate("Settings", "You will need to restart manuskript for the translation to take effect."))
self.groupBox_10.setTitle(_translate("Settings", "Loading"))
self.chkAutoLoad.setText(_translate("Settings", "Automatically load last project on startup"))
self.groupBox.setTitle(_translate("Settings", "Saving"))
@ -1636,8 +1647,8 @@ class Ui_Settings(object):
self.chkAutoSaveNoChanges.setText(_translate("Settings", "If no changes during"))
self.label_14.setText(_translate("Settings", "seconds."))
self.chkSaveOnQuit.setText(_translate("Settings", "Save on quit"))
self.groupBox_11.setTitle(_translate("Settings", "Default text format"))
self.label_35.setText(_translate("Settings", "The format set by default when you create a new text item. You can change this on a per item basis."))
self.chkSaveToZip.setToolTip(_translate("Settings", "<html><head/><body><p>If you check this option, your project will be save as one single file. Easier to copy or backup, but does not allow collaborative editing, or versionning.<br/>If this is unchecked, your project will be save as a folder containing many small files.</p></body></html>"))
self.chkSaveToZip.setText(_translate("Settings", "Save to one single file"))
self.lblTitleGeneral_2.setText(_translate("Settings", "Revisions"))
self.label_44.setText(_translate("Settings", "Revisions are a way to keep track of modifications. For each text item, it stores any changes you make to the main text, allowing you to see and restoring previous versions."))
self.chkRevisionsKeep.setText(_translate("Settings", "Keep revisions"))
@ -1674,7 +1685,7 @@ class Ui_Settings(object):
self.rdoTreeWC.setText(_translate("Settings", "Show wordcount"))
self.rdoTreeProgress.setText(_translate("Settings", "Show progress"))
self.rdoTreeSummary.setText(_translate("Settings", "Show summary"))
self.rdoTreeNothing.setText(_translate("Settings", "Nothing"))
self.rdoTreeNothing.setText(_translate("Settings", "&Nothing"))
self.groupBox_9.setTitle(_translate("Settings", "Text"))
self.rdoTreeTextWC.setText(_translate("Settings", "Show wordcount"))
self.rdoTreeTextProgress.setText(_translate("Settings", "Show progress"))

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>658</width>
<height>491</height>
<height>507</height>
</rect>
</property>
<property name="windowTitle">
@ -51,7 +51,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">
@ -133,6 +133,47 @@ text-align:center;</string>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_14">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="title">
<string>Application language</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<widget class="QLabel" name="label_52">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>You will need to restart manuskript for the translation to take effect.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbTranslation">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="font">
@ -358,47 +399,28 @@ text-align:center;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="title">
<string>Default text format</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QLabel" name="label_35">
<widget class="QCheckBox" name="chkSaveToZip">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If you check this option, your project will be save as one single file. Easier to copy or backup, but does not allow collaborative editing, or versionning.&lt;br/&gt;If this is unchecked, your project will be save as a folder containing many small files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="text">
<string>The format set by default when you create a new text item. You can change this on a per item basis.</string>
<string>Save to one single file</string>
</property>
<property name="wordWrap">
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbDefaultTextType">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -1032,7 +1054,7 @@ text-align:center;</string>
</font>
</property>
<property name="text">
<string>Nothing</string>
<string>&amp;Nothing</string>
</property>
<property name="checked">
<bool>true</bool>

View file

@ -45,12 +45,6 @@ class frequencyAnalyzer(QWidget, Ui_FrequencyAnalyzer):
def listPhrases(item, nMin, nMax):
txt = item.text()
# Convert to plain text
if item.isHTML():
e = QTextEdit()
e.setHtml(item.text())
txt = e.toPlainText()
# Split into words
lst = re.findall(r"[\w']+", txt) # Ignores punctuation
# lst = re.findall(r"[\w']+|[.,!?;]", txt) # Includes punctuation
@ -92,12 +86,6 @@ class frequencyAnalyzer(QWidget, Ui_FrequencyAnalyzer):
def listWords(item):
txt = item.text()
# Convert to plain text
if item.isHTML():
e = QTextEdit()
e.setHtml(item.text())
txt = e.toPlainText()
lst = re.findall(r"[\w']+", txt)
for c in item.children():
lst += listWords(c)

View file

@ -10,13 +10,13 @@ class basicItemView(QWidget, Ui_basicItemView):
def __init__(self, parent=None):
QWidget.__init__(self)
self.setupUi(self)
self.txtSummarySentance.setColumn(Outline.summarySentance.value)
self.txtSummarySentence.setColumn(Outline.summarySentence.value)
self.txtSummaryFull.setColumn(Outline.summaryFull.value)
self.txtGoal.setColumn(Outline.setGoal.value)
def setModels(self, mdlOutline, mdlPersos, mdlLabels, mdlStatus):
self.cmbPOV.setModels(mdlPersos, mdlOutline)
self.txtSummarySentance.setModel(mdlOutline)
def setModels(self, mdlOutline, mdlCharacter, mdlLabels, mdlStatus):
self.cmbPOV.setModels(mdlCharacter, mdlOutline)
self.txtSummarySentence.setModel(mdlOutline)
self.txtSummaryFull.setModel(mdlOutline)
self.txtGoal.setModel(mdlOutline)
@ -43,14 +43,14 @@ class basicItemView(QWidget, Ui_basicItemView):
elif len(indexes) == 1:
self.setEnabled(True)
idx = indexes[0]
self.txtSummarySentance.setCurrentModelIndex(idx)
self.txtSummarySentence.setCurrentModelIndex(idx)
self.txtSummaryFull.setCurrentModelIndex(idx)
self.cmbPOV.setCurrentModelIndex(idx)
self.txtGoal.setCurrentModelIndex(idx)
else:
self.setEnabled(True)
self.txtSummarySentance.setCurrentModelIndexes(indexes)
self.txtSummarySentence.setCurrentModelIndexes(indexes)
self.txtSummaryFull.setCurrentModelIndexes(indexes)
self.cmbPOV.setCurrentModelIndexes(indexes)
self.txtGoal.setCurrentModelIndexes(indexes)

View file

@ -2,7 +2,8 @@
# Form implementation generated from reading ui file 'manuskript/ui/views/basicItemView_ui.ui'
#
# Created by: PyQt5 UI code generator 5.4.2
# Created: Thu Mar 3 17:26:11 2016
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
@ -23,7 +24,7 @@ class Ui_basicItemView(object):
self.lblPlanPOV.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lblPlanPOV.setObjectName("lblPlanPOV")
self.horizontalLayout_11.addWidget(self.lblPlanPOV)
self.cmbPOV = cmbOutlinePersoChoser(basicItemView)
self.cmbPOV = cmbOutlineCharacterChoser(basicItemView)
self.cmbPOV.setFrame(False)
self.cmbPOV.setObjectName("cmbPOV")
self.horizontalLayout_11.addWidget(self.cmbPOV)
@ -43,10 +44,10 @@ class Ui_basicItemView(object):
self.txtGoal.setObjectName("txtGoal")
self.horizontalLayout_11.addWidget(self.txtGoal)
self.verticalLayout.addLayout(self.horizontalLayout_11)
self.txtSummarySentance = lineEditView(basicItemView)
self.txtSummarySentance.setText("")
self.txtSummarySentance.setObjectName("txtSummarySentance")
self.verticalLayout.addWidget(self.txtSummarySentance)
self.txtSummarySentence = lineEditView(basicItemView)
self.txtSummarySentence.setText("")
self.txtSummarySentence.setObjectName("txtSummarySentence")
self.verticalLayout.addWidget(self.txtSummarySentence)
self.label_9 = QtWidgets.QLabel(basicItemView)
self.label_9.setObjectName("label_9")
self.verticalLayout.addWidget(self.label_9)
@ -63,9 +64,9 @@ class Ui_basicItemView(object):
self.lblPlanPOV.setText(_translate("basicItemView", "POV:"))
self.lblGoal.setText(_translate("basicItemView", "Goal:"))
self.txtGoal.setPlaceholderText(_translate("basicItemView", "Word count"))
self.txtSummarySentance.setPlaceholderText(_translate("basicItemView", "One line summary"))
self.txtSummarySentence.setPlaceholderText(_translate("basicItemView", "One line summary"))
self.label_9.setText(_translate("basicItemView", "Few sentences summary:"))
from manuskript.ui.views.cmbOutlinePersoChoser import cmbOutlinePersoChoser
from manuskript.ui.views.cmbOutlineCharacterChoser import cmbOutlineCharacterChoser
from manuskript.ui.views.lineEditView import lineEditView
from manuskript.ui.views.textEditView import textEditView

View file

@ -43,7 +43,7 @@
</widget>
</item>
<item>
<widget class="cmbOutlinePersoChoser" name="cmbPOV">
<widget class="cmbOutlineCharacterChoser" name="cmbPOV">
<property name="frame">
<bool>false</bool>
</property>
@ -84,7 +84,7 @@
</layout>
</item>
<item>
<widget class="lineEditView" name="txtSummarySentance">
<widget class="lineEditView" name="txtSummarySentence">
<property name="text">
<string/>
</property>
@ -112,9 +112,9 @@
<header>manuskript.ui.views.textEditView.h</header>
</customwidget>
<customwidget>
<class>cmbOutlinePersoChoser</class>
<class>cmbOutlineCharacterChoser</class>
<extends>QComboBox</extends>
<header>manuskript.ui.views.cmbOutlinePersoChoser.h</header>
<header>manuskript.ui.views.cmbOutlineCharacterChoser.h</header>
</customwidget>
<customwidget>
<class>lineEditView</class>

View file

@ -2,12 +2,16 @@
# --!-- coding: utf8 --!--
from PyQt5.QtCore import QSize, QModelIndex, Qt
from PyQt5.QtGui import QPixmap, QColor, QIcon, QBrush
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QColorDialog
from manuskript.enums import Perso
from manuskript.enums import Character
from manuskript.functions import iconColor, mainWindow
class persoTreeView(QTreeWidget):
class characterTreeView(QTreeWidget):
"""
A QTreeWidget that displays characters from a characterModel in respect of their importance.
"""
def __init__(self, parent=None):
QTreeWidget.__init__(self, parent)
self._model = None
@ -24,7 +28,7 @@ class persoTreeView(QTreeWidget):
self._rootItem = QTreeWidgetItem()
self.insertTopLevelItem(0, self._rootItem)
def setPersosModel(self, model):
def setCharactersModel(self, model):
self._model = model
self._model.dataChanged.connect(self.updateMaybe)
self._model.rowsInserted.connect(self.updateMaybe2)
@ -39,11 +43,11 @@ class persoTreeView(QTreeWidget):
if topLeft.parent() != QModelIndex():
return
if topLeft.column() <= Perso.name.value <= bottomRight.column():
if topLeft.column() <= Character.name.value <= bottomRight.column():
# Update name
self.updateNames()
elif topLeft.column() <= Perso.importance.value <= bottomRight.column():
elif topLeft.column() <= Character.importance.value <= bottomRight.column():
# Importance changed
self.updateItems()
@ -56,16 +60,17 @@ class persoTreeView(QTreeWidget):
for i in range(self.topLevelItemCount()):
item = self.topLevelItem(i)
for c in range(item.childCount()):
sub = item.child(c)
for child in range(item.childCount()):
sub = item.child(child)
ID = sub.data(0, Qt.UserRole)
if ID:
if ID is not None:
# Update name
name = self._model.getPersoNameByID(ID)
c = self._model.getCharacterByID(ID)
name = c.name()
sub.setText(0, name)
# Update icon
px = QPixmap(32, 32)
color = QColor(self._model.getPersoColorByID(ID))
color = c.color()
px.fill(color)
sub.setIcon(0, QIcon(px))
@ -78,10 +83,12 @@ class persoTreeView(QTreeWidget):
self._updating = True
self.clear()
persos = self._model.getPersosByImportance()
characters = self._model.getCharactersByImportance()
h = [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]
for i in range(3):
# Create category item
cat = QTreeWidgetItem(self, [h[i]])
cat.setBackground(0, QBrush(QColor(Qt.blue).lighter(190)))
cat.setForeground(0, QBrush(Qt.darkBlue))
@ -92,23 +99,68 @@ class persoTreeView(QTreeWidget):
self.addTopLevelItem(cat)
# cat.setChildIndicatorPolicy(cat.DontShowIndicator)
for ID in persos[i]:
name = self._model.getPersoNameByID(ID)
for c in characters[i]:
name = c.name()
# Check if name passes filter
if not self._filter.lower() in name.lower():
continue
item = QTreeWidgetItem(cat, [name])
item.setData(0, Qt.UserRole, ID)
item.setData(0, Qt.UserRole, c.ID())
px = QPixmap(32, 32)
color = QColor(self._model.getPersoColorByID(ID))
color = QColor(c.color())
px.fill(color)
item.setIcon(0, QIcon(px))
if ID == self._lastID:
if c.ID() == self._lastID:
self.setCurrentItem(item)
self.expandAll()
self._updating = False
def removeCharacter(self):
"""
Removes selected character.
"""
ID = self.currentCharacterID()
if ID:
self._model.removeCharacter(ID)
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 addCharacterInfo(self):
self._model.addCharacterInfo(self.currentCharacterID())
def removeCharacterInfo(self):
self._model.removeCharacterInfo(self.currentCharacterID(),
)
def currentCharacterID(self):
ID = None
if self.currentItem():
ID = self.currentItem().data(0, Qt.UserRole)
return ID
def currentCharacter(self):
"""
Returns the selected character
@return: Character
"""
ID = self.currentCharacterID()
return self._model.getCharacterByID(ID)
def getItemByID(self, ID):
for t in range(self.topLevelItemCount()):
for i in range(self.topLevelItem(t).childCount()):
@ -116,13 +168,6 @@ class persoTreeView(QTreeWidget):
if item.data(0, Qt.UserRole) == ID:
return item
def currentPersoIndex(self):
ID = None
if self.currentItem():
ID = self.currentItem().data(0, Qt.UserRole)
return self._model.getIndexFromID(ID)
def mouseDoubleClickEvent(self, event):
item = self.currentItem()
# Catching double clicks to forbid collapsing of toplevel items

View file

@ -8,7 +8,7 @@ from manuskript.enums import Outline
from manuskript.functions import toInt
class cmbOutlinePersoChoser(QComboBox):
class cmbOutlineCharacterChoser(QComboBox):
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.activated[int].connect(self.submit)
@ -18,9 +18,12 @@ class cmbOutlinePersoChoser(QComboBox):
self._updating = False
self._various = False
def setModels(self, mdlPersos, mdlOutline):
self.mdlPersos = mdlPersos
self.mdlPersos.dataChanged.connect(self.updateItems)
def setModels(self, mdlCharacter, mdlOutline):
self.mdlCharacters = mdlCharacter
self.mdlCharacters.dataChanged.connect(self.updateItems)
self.mdlCharacters.rowsInserted.connect(self.updateItems)
self.mdlCharacters.rowsRemoved.connect(self.updateItems)
self.mdlOutline = mdlOutline
self.mdlOutline.dataChanged.connect(self.update)
self.updateItems()
@ -37,14 +40,14 @@ class cmbOutlinePersoChoser(QComboBox):
self.setItemData(self.count() - 1, QBrush(QColor(Qt.blue).lighter(190)), Qt.BackgroundRole)
item = self.model().item(self.count() - 1)
item.setFlags(Qt.ItemIsEnabled)
for i in range(self.mdlPersos.rowCount()):
imp = toInt(self.mdlPersos.importance(i))
for i in range(self.mdlCharacters.rowCount()):
imp = toInt(self.mdlCharacters.importance(i))
if not 2 - imp == importance:
continue
self.addItem(self.mdlPersos.icon(i), self.mdlPersos.name(i), self.mdlPersos.ID(i))
self.setItemData(self.count() - 1, self.mdlPersos.name(i), Qt.ToolTipRole)
self.addItem(self.mdlCharacters.icon(i), self.mdlCharacters.name(i), self.mdlCharacters.ID(i))
self.setItemData(self.count() - 1, self.mdlCharacters.name(i), Qt.ToolTipRole)
self._various = False

View file

@ -1,145 +0,0 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QBrush
from PyQt5.QtWidgets import QComboBox
from manuskript.enums import Outline
class cmbOutlineTypeChoser(QComboBox):
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.activated[int].connect(self.submit)
self._column = Outline.type.value
self._index = None
self._indexes = None
self._updating = False
self._various = False
def setModel(self, mdlOutline):
self.mdlOutline = mdlOutline
self.mdlOutline.dataChanged.connect(self.update)
self.updateItems()
def updateItems(self):
self.clear()
types = [
("t2t", self.tr("Txt2Tags"), "text-x-generic"),
("html", self.tr("Rich Text (html)"), "text-html"),
("txt", self.tr("Plain Text"), "text-x-generic"),
]
for t in types:
self.addItem(QIcon.fromTheme(t[2]), t[1], t[0])
self._various = False
if self._index or self._indexes:
self.updateSelectedItem()
def setCurrentModelIndex(self, index):
self._indexes = None
if index.column() != self._column:
index = index.sibling(index.row(), self._column)
self._index = index
# Disabled if item type is not text
self.setEnabled(index.internalPointer().type() in ["t2t", "html", "txt"])
self.updateItems()
self.updateSelectedItem()
def setCurrentModelIndexes(self, indexes):
self._indexes = []
self._index = None
hasText = False
for i in indexes:
if i.isValid():
if i.column() != self._column:
i = i.sibling(i.row(), self._column)
self._indexes.append(i)
if i.internalPointer().type() in ["t2t", "html", "txt"]:
hasText = True
self.setEnabled(hasText)
self.updateItems()
self.updateSelectedItem()
def update(self, topLeft, bottomRight):
if self._updating:
# We are currently putting data in the model, so no updates
return
if self._index:
if topLeft.row() <= self._index.row() <= bottomRight.row():
self.updateSelectedItem()
elif self._indexes:
update = False
for i in self._indexes:
if topLeft.row() <= i.row() <= bottomRight.row():
update = True
if update:
self.updateSelectedItem()
def getType(self, index):
item = index.internalPointer()
return item.type()
def updateSelectedItem(self):
if self._updating:
return
if self._index:
_type = self.getType(self._index)
i = self.findData(_type)
if i != -1:
self.setCurrentIndex(i)
elif self._indexes:
types = []
same = True
for i in self._indexes:
types.append(self.getType(i))
for t in types[1:]:
if t != types[0]:
same = False
break
if same:
self._various = False
i = self.findData(types[0])
if i != -1:
self.setCurrentIndex(i)
else:
if not self._various:
self.insertItem(0, self.tr("Various"))
f = self.font()
f.setItalic(True)
self.setItemData(0, f, Qt.FontRole)
self.setItemData(0, QBrush(Qt.darkGray), Qt.ForegroundRole)
self._various = True
self.setCurrentIndex(0)
else:
self.setCurrentIndex(0)
def submit(self, idx):
if self._index:
self.mdlOutline.setData(self._index, self.currentData())
elif self._indexes:
value = self.currentData()
if self._various and self.currentIndex() == 0:
return
self._updating = True
for i in self._indexes:
self.mdlOutline.setData(i, value)
self._updating = False

View file

@ -38,7 +38,7 @@ class corkDelegate(QStyledItemDelegate):
if self.mainLineRect.contains(self.lastPos):
# One line summary
self.editing = Outline.summarySentance
self.editing = Outline.summarySentence
edt = QLineEdit(parent)
edt.setFocusPolicy(Qt.StrongFocus)
edt.setFrame(False)
@ -69,12 +69,16 @@ class corkDelegate(QStyledItemDelegate):
edt = QPlainTextEdit(parent)
edt.setFocusPolicy(Qt.StrongFocus)
edt.setFrameShape(QFrame.NoFrame)
edt.setPlaceholderText(self.tr("Full summary"))
try:
# QPlainTextEdit.setPlaceholderText was introduced in Qt 5.3
edt.setPlaceholderText(self.tr("Full summary"))
except AttributeError:
pass
return edt
def updateEditorGeometry(self, editor, option, index):
if self.editing == Outline.summarySentance:
if self.editing == Outline.summarySentence:
# One line summary
editor.setGeometry(self.mainLineRect)
@ -89,9 +93,9 @@ class corkDelegate(QStyledItemDelegate):
def setEditorData(self, editor, index):
item = index.internalPointer()
if self.editing == Outline.summarySentance:
if self.editing == Outline.summarySentence:
# One line summary
editor.setText(item.data(Outline.summarySentance.value))
editor.setText(item.data(Outline.summarySentence.value))
elif self.editing == Outline.title:
# Title
@ -103,9 +107,9 @@ class corkDelegate(QStyledItemDelegate):
def setModelData(self, editor, model, index):
if self.editing == Outline.summarySentance:
if self.editing == Outline.summarySentence:
# One line summary
model.setData(index.sibling(index.row(), Outline.summarySentance.value), editor.text())
model.setData(index.sibling(index.row(), Outline.summarySentence.value), editor.text())
elif self.editing == Outline.title:
# Title
@ -133,7 +137,7 @@ class corkDelegate(QStyledItemDelegate):
self.mainRect.topRight() + QPoint(0, iconSize))
self.mainTextRect = QRect(self.mainLineRect.bottomLeft() + QPoint(0, margin),
self.mainRect.bottomRight())
if not item.data(Outline.summarySentance.value):
if not item.data(Outline.summarySentence.value):
self.mainTextRect.setTopLeft(self.mainLineRect.topLeft())
if item.data(Outline.label.value) in ["", "0"]:
self.titleRect.setBottomRight(self.labelRect.bottomRight() - QPoint(self.margin, self.margin))
@ -218,7 +222,7 @@ class corkDelegate(QStyledItemDelegate):
p.drawLine(self.labelRect.topLeft(), self.labelRect.bottomLeft())
# One line summary background
lineSummary = item.data(Outline.summarySentance.value)
lineSummary = item.data(Outline.summarySentence.value)
fullSummary = item.data(Outline.summaryFull.value)
if lineSummary or not fullSummary:
m = self.margin

View file

@ -3,6 +3,7 @@
from PyQt5.QtWidgets import QListView
from manuskript import settings
from manuskript.functions import findBackground
from manuskript.ui.views.corkDelegate import corkDelegate
from manuskript.ui.views.dndView import dndView
from manuskript.ui.views.outlineBasics import outlineBasics
@ -24,12 +25,13 @@ class corkView(QListView, dndView, outlineBasics):
self.updateBackground()
def updateBackground(self):
img = findBackground(settings.corkBackground["image"])
self.setStyleSheet("""QListView {{
background:{color};
background-image: url({url});
}}""".format(
color=settings.corkBackground["color"],
url=settings.corkBackground["image"]
url=img
))
def dragMoveEvent(self, event):

View file

@ -11,14 +11,14 @@ class metadataView(QWidget, Ui_metadataView):
QWidget.__init__(self, parent)
self.setupUi(self)
self._lastIndexes = None
self.txtSummarySentance.setColumn(Outline.summarySentance.value)
self.txtSummarySentence.setColumn(Outline.summarySentence.value)
self.txtSummaryFull.setColumn(Outline.summaryFull.value)
self.txtNotes.setColumn(Outline.notes.value)
self.revisions.setEnabled(False)
def setModels(self, mdlOutline, mdlPersos, mdlLabels, mdlStatus):
self.properties.setModels(mdlOutline, mdlPersos, mdlLabels, mdlStatus)
self.txtSummarySentance.setModel(mdlOutline)
def setModels(self, mdlOutline, mdlCharacter, mdlLabels, mdlStatus):
self.properties.setModels(mdlOutline, mdlCharacter, mdlLabels, mdlStatus)
self.txtSummarySentence.setModel(mdlOutline)
self.txtSummaryFull.setModel(mdlOutline)
self.txtNotes.setModel(mdlOutline)
self.revisions.setModel(mdlOutline)
@ -49,7 +49,7 @@ class metadataView(QWidget, Ui_metadataView):
elif len(indexes) == 1:
self.setEnabled(True)
idx = indexes[0]
self.txtSummarySentance.setCurrentModelIndex(idx)
self.txtSummarySentence.setCurrentModelIndex(idx)
self.txtSummaryFull.setCurrentModelIndex(idx)
self.txtNotes.setCurrentModelIndex(idx)
self.revisions.setEnabled(True)
@ -57,7 +57,7 @@ class metadataView(QWidget, Ui_metadataView):
else:
self.setEnabled(True)
self.txtSummarySentance.setCurrentModelIndexes(indexes)
self.txtSummarySentence.setCurrentModelIndexes(indexes)
self.txtSummaryFull.setCurrentModelIndexes(indexes)
self.txtNotes.setCurrentModelIndexes(indexes)
self.revisions.setEnabled(False)

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/views/metadataView_ui.ui'
#
# Created: Mon Feb 8 09:48:05 2016
# Created: Wed Mar 2 00:30:18 2016
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
@ -43,11 +43,11 @@ class Ui_metadataView(object):
self.verticalLayout_22.setSpacing(0)
self.verticalLayout_22.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_22.setObjectName("verticalLayout_22")
self.txtSummarySentance = lineEditView(self.grpSummary)
self.txtSummarySentance.setInputMask("")
self.txtSummarySentance.setFrame(False)
self.txtSummarySentance.setObjectName("txtSummarySentance")
self.verticalLayout_22.addWidget(self.txtSummarySentance)
self.txtSummarySentence = lineEditView(self.grpSummary)
self.txtSummarySentence.setInputMask("")
self.txtSummarySentence.setFrame(False)
self.txtSummarySentence.setObjectName("txtSummarySentence")
self.verticalLayout_22.addWidget(self.txtSummarySentence)
self.line = QtWidgets.QFrame(self.grpSummary)
self.line.setFrameShadow(QtWidgets.QFrame.Plain)
self.line.setLineWidth(0)
@ -95,15 +95,15 @@ class Ui_metadataView(object):
metadataView.setWindowTitle(_translate("metadataView", "Form"))
self.grpProperties.setTitle(_translate("metadataView", "Properties"))
self.grpSummary.setTitle(_translate("metadataView", "Summary"))
self.txtSummarySentance.setPlaceholderText(_translate("metadataView", "One line summary"))
self.txtSummarySentence.setPlaceholderText(_translate("metadataView", "One line summary"))
self.txtSummaryFull.setPlaceholderText(_translate("metadataView", "Full summary"))
self.grpNotes.setTitle(_translate("metadataView", "Notes / References"))
self.txtNotes.setPlaceholderText(_translate("metadataView", "Notes / References"))
self.grpRevisions.setTitle(_translate("metadataView", "Revisions"))
from manuskript.ui.revisions import revisions
from manuskript.ui.views.lineEditView import lineEditView
from manuskript.ui.views.propertiesView import propertiesView
from manuskript.ui.views.textEditCompleter import textEditCompleter
from manuskript.ui.views.textEditView import textEditView
from manuskript.ui.views.lineEditView import lineEditView
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
from manuskript.ui.revisions import revisions

View file

@ -103,7 +103,7 @@
<number>0</number>
</property>
<item>
<widget class="lineEditView" name="txtSummarySentance">
<widget class="lineEditView" name="txtSummarySentence">
<property name="inputMask">
<string/>
</property>

View file

@ -83,12 +83,12 @@ class outlineBasics(QAbstractItemView):
self.menuPOV.addMenu(m)
mpr = QSignalMapper(self.menuPOV)
for i in range(mw.mdlPersos.rowCount()):
a = QAction(mw.mdlPersos.icon(i), mw.mdlPersos.name(i), self.menuPOV)
for i in range(mw.mdlCharacter.rowCount()):
a = QAction(mw.mdlCharacter.icon(i), mw.mdlCharacter.name(i), self.menuPOV)
a.triggered.connect(mpr.map)
mpr.setMapping(a, int(mw.mdlPersos.ID(i)))
mpr.setMapping(a, int(mw.mdlCharacter.ID(i)))
imp = toInt(mw.mdlPersos.importance(i))
imp = toInt(mw.mdlCharacter.importance(i))
menus[2 - imp].addAction(a)

View file

@ -6,7 +6,7 @@ from PyQt5.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem, QStyle, Q
from PyQt5.QtWidgets import qApp
from manuskript import settings
from manuskript.enums import Perso, Outline
from manuskript.enums import Character, Outline
from manuskript.functions import outlineItemColors, mixColors, colorifyPixmap, toInt, toFloat, drawProgress
@ -92,18 +92,18 @@ class outlineTitleDelegate(QStyledItemDelegate):
# QStyledItemDelegate.paint(self, painter, option, index)
class outlinePersoDelegate(QStyledItemDelegate):
def __init__(self, mdlPersos, parent=None):
class outlineCharacterDelegate(QStyledItemDelegate):
def __init__(self, mdlCharacter, parent=None):
QStyledItemDelegate.__init__(self, parent)
self.mdlPersos = mdlPersos
self.mdlCharacter = mdlCharacter
def sizeHint(self, option, index):
# s = QStyledItemDelegate.sizeHint(self, option, index)
item = QModelIndex()
for i in range(self.mdlPersos.rowCount()):
if self.mdlPersos.ID(i) == index.data():
item = self.mdlPersos.index(i, Perso.name.value)
character = self.mdlCharacter.getCharacterByID(index.data())
if character:
item = character.index(Character.name.value)
opt = QStyleOptionViewItem(option)
self.initStyleOption(opt, item)
@ -136,13 +136,13 @@ class outlinePersoDelegate(QStyledItemDelegate):
editor.setItemData(editor.count() - 1, QBrush(QColor(Qt.blue).lighter(190)), Qt.BackgroundRole)
item = editor.model().item(editor.count() - 1)
item.setFlags(Qt.ItemIsEnabled)
for i in range(self.mdlPersos.rowCount()):
imp = toInt(self.mdlPersos.importance(i))
for i in range(self.mdlCharacter.rowCount()):
imp = toInt(self.mdlCharacter.importance(i))
if not 2 - imp == importance: continue
# try:
editor.addItem(self.mdlPersos.icon(i), self.mdlPersos.name(i), self.mdlPersos.ID(i))
editor.setItemData(editor.count() - 1, self.mdlPersos.name(i), Qt.ToolTipRole)
editor.addItem(self.mdlCharacter.icon(i), self.mdlCharacter.name(i), self.mdlCharacter.ID(i))
editor.setItemData(editor.count() - 1, self.mdlCharacter.name(i), Qt.ToolTipRole)
# except:
# pass
@ -158,18 +158,18 @@ class outlinePersoDelegate(QStyledItemDelegate):
# QStyledItemDelegate.paint(self, painter, option, index)
##option.rect.setWidth(option.rect.width() + 18)
item = QModelIndex()
for i in range(self.mdlPersos.rowCount()):
if self.mdlPersos.ID(i) == index.data():
item = self.mdlPersos.index(i, Perso.name.value)
itemIndex = QModelIndex()
character = self.mdlCharacter.getCharacterByID(index.data())
if character:
itemIndex = character.index(Character.name.value)
opt = QStyleOptionViewItem(option)
self.initStyleOption(opt, item)
self.initStyleOption(opt, itemIndex)
qApp.style().drawControl(QStyle.CE_ItemViewItem, opt, painter)
# if index.isValid() and index.internalPointer().data(Outline.POV.value) not in ["", None]:
if index.isValid() and self.mdlPersos.data(index) not in ["", None]:
if itemIndex.isValid() and self.mdlCharacter.data(itemIndex) not in ["", None]:
opt = QStyleOptionComboBox()
opt.rect = option.rect
r = qApp.style().subControlRect(QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxArrow)
@ -278,7 +278,7 @@ class outlineStatusDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, index)
if index.isValid() and index.internalPointer().data(Outline.status.value) not in ["", None, "0"]:
if index.isValid() and index.internalPointer().data(Outline.status.value) not in ["", None, "0", 0]:
opt = QStyleOptionComboBox()
opt.rect = option.rect
r = qApp.style().subControlRect(QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxArrow)
@ -344,7 +344,7 @@ class outlineLabelDelegate(QStyledItemDelegate):
qApp.style().drawControl(QStyle.CE_ItemViewItem, opt, painter)
# Drop down indicator
if index.isValid() and index.internalPointer().data(Outline.label.value) not in ["", None, "0"]:
if index.isValid() and index.internalPointer().data(Outline.label.value) not in ["", None, "0", 0]:
opt = QStyleOptionComboBox()
opt.rect = option.rect
r = qApp.style().subControlRect(QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxArrow)

View file

@ -6,25 +6,25 @@ from manuskript import settings
from manuskript.enums import Outline
from manuskript.ui.views.dndView import dndView
from manuskript.ui.views.outlineBasics import outlineBasics
from manuskript.ui.views.outlineDelegates import outlineTitleDelegate, outlinePersoDelegate, outlineCompileDelegate, \
from manuskript.ui.views.outlineDelegates import outlineTitleDelegate, outlineCharacterDelegate, outlineCompileDelegate, \
outlineStatusDelegate, outlineGoalPercentageDelegate, outlineLabelDelegate
class outlineView(QTreeView, dndView, outlineBasics):
def __init__(self, parent=None, modelPersos=None, modelLabels=None, modelStatus=None):
def __init__(self, parent=None, modelCharacters=None, modelLabels=None, modelStatus=None):
QTreeView.__init__(self, parent)
dndView.__init__(self)
outlineBasics.__init__(self, parent)
self.modelPersos = modelPersos
self.modelCharacters = modelCharacters
self.modelLabels = modelLabels
self.modelStatus = modelStatus
self.header().setStretchLastSection(False)
def setModelPersos(self, model):
# This is used by outlinePersoDelegate to select character
self.modelPersos = model
def setModelCharacters(self, model):
# This is used by outlineCharacterDelegate to select character
self.modelCharacters = model
def setModelLabels(self, model):
# This is used by outlineLabelDelegate to display labels
@ -41,8 +41,8 @@ class outlineView(QTreeView, dndView, outlineBasics):
self.outlineTitleDelegate = outlineTitleDelegate(self)
# self.outlineTitleDelegate.setView(self)
self.setItemDelegateForColumn(Outline.title.value, self.outlineTitleDelegate)
self.outlinePersoDelegate = outlinePersoDelegate(self.modelPersos)
self.setItemDelegateForColumn(Outline.POV.value, self.outlinePersoDelegate)
self.outlineCharacterDelegate = outlineCharacterDelegate(self.modelCharacters)
self.setItemDelegateForColumn(Outline.POV.value, self.outlineCharacterDelegate)
self.outlineCompileDelegate = outlineCompileDelegate()
self.setItemDelegateForColumn(Outline.compile.value, self.outlineCompileDelegate)
self.outlineStatusDelegate = outlineStatusDelegate(self.modelStatus)
@ -65,6 +65,10 @@ class outlineView(QTreeView, dndView, outlineBasics):
self.header().setSectionResizeMode(Outline.goalPercentage.value, QHeaderView.ResizeToContents)
def hideColumns(self):
if not self.model():
# outlineView is probably not initialized, because editorWidgets shows index cards or text.
return
for c in range(self.model().columnCount()):
self.hideColumn(c)
for c in settings.outlineViewColumns:

View file

@ -12,11 +12,10 @@ class propertiesView(QWidget, Ui_propertiesView):
self.setupUi(self)
self.txtGoal.setColumn(Outline.setGoal.value)
def setModels(self, mdlOutline, mdlPersos, mdlLabels, mdlStatus):
self.cmbPOV.setModels(mdlPersos, mdlOutline)
def setModels(self, mdlOutline, mdlCharacter, mdlLabels, mdlStatus):
self.cmbPOV.setModels(mdlCharacter, mdlOutline)
self.cmbLabel.setModels(mdlLabels, mdlOutline)
self.cmbStatus.setModels(mdlStatus, mdlOutline)
self.cmbType.setModel(mdlOutline)
self.chkCompile.setModel(mdlOutline)
self.txtTitle.setModel(mdlOutline)
self.txtGoal.setModel(mdlOutline)
@ -52,8 +51,6 @@ class propertiesView(QWidget, Ui_propertiesView):
self.txtTitle.setCurrentModelIndex(idx)
self.txtGoal.setCurrentModelIndex(idx)
self.cmbType.setCurrentModelIndex(idx)
else:
self.setEnabled(True)
self.setLabelsItalic(True)
@ -64,8 +61,6 @@ class propertiesView(QWidget, Ui_propertiesView):
self.cmbLabel.setCurrentModelIndexes(indexes)
self.cmbStatus.setCurrentModelIndexes(indexes)
self.cmbType.setCurrentModelIndexes(indexes)
def setLabelsItalic(self, value):
f = self.lblPOV.font()
f.setItalic(value)

View file

@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_propertiesView(object):
def setupUi(self, propertiesView):
propertiesView.setObjectName("propertiesView")
propertiesView.resize(192, 159)
propertiesView.resize(192, 186)
self.verticalLayout = QtWidgets.QVBoxLayout(propertiesView)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
@ -36,7 +36,7 @@ class Ui_propertiesView(object):
self.lblPOV = QtWidgets.QLabel(self.page)
self.lblPOV.setObjectName("lblPOV")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lblPOV)
self.cmbPOV = cmbOutlinePersoChoser(self.page)
self.cmbPOV = cmbOutlineCharacterChoser(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -71,14 +71,14 @@ class Ui_propertiesView(object):
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.cmbLabel)
self.lblCompile = QtWidgets.QLabel(self.page)
self.lblCompile.setObjectName("lblCompile")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.lblCompile)
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lblCompile)
self.chkCompile = chkOutlineCompile(self.page)
self.chkCompile.setText("")
self.chkCompile.setObjectName("chkCompile")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.chkCompile)
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.chkCompile)
self.lblGoal = QtWidgets.QLabel(self.page)
self.lblGoal.setObjectName("lblGoal")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.lblGoal)
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.lblGoal)
self.txtGoal = lineEditView(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@ -89,19 +89,7 @@ class Ui_propertiesView(object):
self.txtGoal.setStyleSheet("border-radius: 6px;")
self.txtGoal.setFrame(False)
self.txtGoal.setObjectName("txtGoal")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtGoal)
self.lblLabel_2 = QtWidgets.QLabel(self.page)
self.lblLabel_2.setObjectName("lblLabel_2")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lblLabel_2)
self.cmbType = cmbOutlineTypeChoser(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.cmbType.sizePolicy().hasHeightForWidth())
self.cmbType.setSizePolicy(sizePolicy)
self.cmbType.setFrame(False)
self.cmbType.setObjectName("cmbType")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.cmbType)
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.txtGoal)
self.verticalLayout_2.addLayout(self.formLayout)
self.stack.addWidget(self.page)
self.page_2 = QtWidgets.QWidget()
@ -115,7 +103,7 @@ class Ui_propertiesView(object):
self.lblPOV_2 = QtWidgets.QLabel(self.page_2)
self.lblPOV_2.setObjectName("lblPOV_2")
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lblPOV_2)
self.cmbPOVMulti = cmbOutlinePersoChoser(self.page_2)
self.cmbPOVMulti = cmbOutlineCharacterChoser(self.page_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -186,7 +174,6 @@ class Ui_propertiesView(object):
self.lblCompile.setText(_translate("propertiesView", "Compile"))
self.lblGoal.setText(_translate("propertiesView", "Goal"))
self.txtGoal.setPlaceholderText(_translate("propertiesView", "Word count"))
self.lblLabel_2.setText(_translate("propertiesView", "Text type:"))
self.lblPOV_2.setText(_translate("propertiesView", "POV"))
self.label_31.setText(_translate("propertiesView", "Status"))
self.label_34.setText(_translate("propertiesView", "Label"))
@ -195,8 +182,7 @@ class Ui_propertiesView(object):
self.txtGoalMulti.setPlaceholderText(_translate("propertiesView", "Word count"))
from manuskript.ui.views.chkOutlineCompile import chkOutlineCompile
from manuskript.ui.views.cmbOutlineCharacterChoser import cmbOutlineCharacterChoser
from manuskript.ui.views.cmbOutlineLabelChoser import cmbOutlineLabelChoser
from manuskript.ui.views.cmbOutlinePersoChoser import cmbOutlinePersoChoser
from manuskript.ui.views.cmbOutlineStatusChoser import cmbOutlineStatusChoser
from manuskript.ui.views.cmbOutlineTypeChoser import cmbOutlineTypeChoser
from manuskript.ui.views.lineEditView import lineEditView

View file

@ -7,14 +7,23 @@
<x>0</x>
<y>0</y>
<width>192</width>
<height>159</height>
<height>186</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -40,7 +49,16 @@
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -53,7 +71,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="cmbOutlinePersoChoser" name="cmbPOV">
<widget class="cmbOutlineCharacterChoser" name="cmbPOV">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -105,28 +123,28 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="4" column="0">
<widget class="QLabel" name="lblCompile">
<property name="text">
<string>Compile</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="4" column="1">
<widget class="chkOutlineCompile" name="chkCompile">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="5" column="0">
<widget class="QLabel" name="lblGoal">
<property name="text">
<string>Goal</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="5" column="1">
<widget class="lineEditView" name="txtGoal">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -148,26 +166,6 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblLabel_2">
<property name="text">
<string>Text type:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="cmbOutlineTypeChoser" name="cmbType">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frame">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -177,7 +175,16 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -190,7 +197,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="cmbOutlinePersoChoser" name="cmbPOVMulti">
<widget class="cmbOutlineCharacterChoser" name="cmbPOVMulti">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -300,9 +307,9 @@
<header>manuskript.ui.views.lineEditView.h</header>
</customwidget>
<customwidget>
<class>cmbOutlinePersoChoser</class>
<class>cmbOutlineCharacterChoser</class>
<extends>QComboBox</extends>
<header>manuskript.ui.views.cmbOutlinePersoChoser.h</header>
<header>manuskript.ui.views.cmbOutlineCharacterChoser.h</header>
</customwidget>
<customwidget>
<class>cmbOutlineStatusChoser</class>
@ -319,11 +326,6 @@
<extends>QComboBox</extends>
<header>manuskript.ui.views.cmbOutlineLabelChoser.h</header>
</customwidget>
<customwidget>
<class>cmbOutlineTypeChoser</class>
<extends>QComboBox</extends>
<header>manuskript.ui.views.cmbOutlineTypeChoser.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View file

@ -2,9 +2,9 @@
# --!-- coding: utf8 --!--
from PyQt5.QtCore import pyqtSignal, pyqtProperty
from PyQt5.QtWidgets import QWidget
from manuskript.ui.views.sldImportance_ui import Ui_sldImportance
from manuskript.functions import toInt
from manuskript.ui.sldImportance_ui import Ui_sldImportance
class sldImportance(QWidget, Ui_sldImportance):

View file

@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'manuskript/ui/sldImportance_ui.ui'
# Form implementation generated from reading ui file 'manuskript/ui/views/sldImportance_ui.ui'
#
# Created by: PyQt5 UI code generator 5.4.1
# Created: Thu Mar 3 18:52:22 2016
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_sldImportance(object):
def setupUi(self, sldImportance):

View file

@ -1,12 +1,11 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import Qt, QTimer, QRectF
from PyQt5.QtGui import QBrush, QPen, QFontMetrics, QFontMetricsF
from PyQt5.QtGui import QBrush, QPen, QFontMetrics, QFontMetricsF, QColor
from PyQt5.QtWidgets import QWidget, QGraphicsScene, QGraphicsSimpleTextItem, QMenu, QAction, QGraphicsRectItem, \
QGraphicsLineItem, QGraphicsEllipseItem
from manuskript.enums import Outline
from manuskript.functions import randomColor
from manuskript.models import references
from manuskript.ui.views.storylineView_ui import Ui_storylineView
@ -32,33 +31,69 @@ class storylineView(QWidget, Ui_storylineView):
def generateMenu(self):
m = QMenu()
for i in [
self.tr("Show Plots"),
self.tr("Show Characters"),
self.tr("Show Objects"),
]:
a = QAction(i, m)
a.setCheckable(True)
a.setEnabled(False)
m.addAction(a)
self.actPlots = QAction(self.tr("Show Plots"), m)
self.actPlots.setCheckable(True)
self.actPlots.setChecked(True)
self.actPlots.toggled.connect(self.reloadTimer.start)
m.addAction(self.actPlots)
self.actCharacters = QAction(self.tr("Show Characters"), m)
self.actCharacters.setCheckable(True)
self.actCharacters.setChecked(False)
self.actCharacters.toggled.connect(self.reloadTimer.start)
m.addAction(self.actCharacters)
self.btnSettings.setMenu(m)
def setModels(self, mdlOutline, mdlPersos, mdlPlots):
def setModels(self, mdlOutline, mdlCharacter, mdlPlots):
self._mdlPlots = mdlPlots
# self._mdlPlots.dataChanged.connect(self.refresh)
# self._mdlPlots.rowsInserted.connect(self.refresh)
self._mdlOutline = mdlOutline
self._mdlOutline.dataChanged.connect(self.reloadTimer.start)
self._mdlOutline.dataChanged.connect(self.updateMaybe)
self._mdlPersos = mdlPersos
self._mdlPersos.dataChanged.connect(self.reloadTimer.start)
self._mdlCharacter = mdlCharacter
self._mdlCharacter.dataChanged.connect(self.reloadTimer.start)
def updateMaybe(self, topLeft, bottomRight):
if topLeft.column() <= Outline.notes.value <= bottomRight.column():
self.reloadTimer.start
def plotReferences(self):
"Returns a list of plot references"
if not self._mdlPlots:
pass
plotsID = self._mdlPlots.getPlotsByImportance()
r = []
for importance in plotsID:
for ID in importance:
ref = references.plotReference(ID)
r.append(ref)
return r
def charactersReferences(self):
"Returns a list of character references"
if not self._mdlCharacter:
pass
chars = self._mdlCharacter.getCharactersByImportance()
r = []
for importance in chars:
for c in importance:
ref = references.characterReference(c.ID())
r.append(ref)
return r
def refresh(self):
if not self._mdlPlots or not self._mdlOutline or not self._mdlPersos:
pass
if not self._mdlPlots or not self._mdlOutline or not self._mdlCharacter:
return
if not self.isVisible():
return
LINE_HEIGHT = 18
SPACING = 3
@ -82,24 +117,24 @@ class storylineView(QWidget, Ui_storylineView):
MAX_LEVEL = maxLevel(root)
# Generate left entries
# (As of now, plot only)
plotsID = self._mdlPlots.getPlotsByImportance()
# Get the list of tracked items (array of references)
trackedItems = []
fm = QFontMetrics(s.font())
max_name = 0
for importance in plotsID:
for ID in importance:
name = self._mdlPlots.getPlotNameByID(ID)
ref = references.plotReference(ID, searchable=True)
if self.actPlots.isChecked():
trackedItems += self.plotReferences()
trackedItems.append((ID, ref, name))
max_name = max(fm.width(name), max_name)
if self.actCharacters.isChecked():
trackedItems += self.charactersReferences()
ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )
TITLE_WIDTH = max_name + 2 * SPACING
fm = QFontMetrics(s.font())
max_name = 0
for ref in trackedItems:
name = references.title(ref)
max_name = max(fm.width(name), max_name)
TITLE_WIDTH = max_name + 2 * SPACING
# Add Folders and Texts
outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
@ -150,9 +185,22 @@ class storylineView(QWidget, Ui_storylineView):
rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
# Find tracked references in that scene (or parent folders)
for ID, ref, name in trackedItems:
for ref in trackedItems:
result = []
# Tests if POV
scenePOV = False # Will hold true of character is POV of the current text, not containing folder
if references.type(ref) == references.CharacterLetter:
ID = references.ID(ref)
c = child
while c:
if c.POV() == ID:
result.append(c.ID())
if c == child: scenePOV = True
c = c.parent()
# Search in notes/references
c = child
while c:
result += references.findReferencesTo(ref, c, recursive=False)
@ -162,7 +210,7 @@ class storylineView(QWidget, Ui_storylineView):
ref2 = result[0]
# Create a RefCircle with the reference
c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2)
c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2, important=scenePOV)
# Store it, with the position of that item, to display it on the line later on
refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos())))
@ -173,22 +221,34 @@ class storylineView(QWidget, Ui_storylineView):
OUTLINE_WIDTH = itemWidth(root)
# Add Plots
# Add Tracked items
i = 0
itemsRect = s.addRect(0, 0, 0, 0)
itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)
for ID, ref, name in trackedItems:
color = randomColor()
# Set of colors for plots (as long as they don't have their own colors)
colors = [
"#D97777", "#AE5F8C", "#D9A377", "#FFC2C2", "#FFDEC2", "#D2A0BC",
"#7B0F0F", "#7B400F", "#620C3D", "#AA3939", "#AA6C39", "#882D61",
"#4C0000", "#4C2200", "#3D0022",
]
for ref in trackedItems:
if references.type(ref) == references.CharacterLetter:
color = self._mdlCharacter.getCharacterByID(references.ID(ref)).color()
else:
color = QColor(colors[i % len(colors)])
# Rect
r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
r.setPen(QPen(Qt.NoPen))
r.setBrush(QBrush(color))
r.setPos(0, i * LINE_HEIGHT + i * SPACING)
r.setToolTip(references.tooltip(ref))
i += 1
# Text
name = references.title(ref)
txt = QGraphicsSimpleTextItem(name, r)
txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
@ -198,7 +258,7 @@ class storylineView(QWidget, Ui_storylineView):
line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
s.addItem(line)
line.setPen(QPen(color, 5))
line.setToolTip(self.tr("Plot: ") + name)
line.setToolTip(references.tooltip(ref))
# We add the circles / references to text, on the line
for ref2, circle, pos in refCircles:
@ -225,13 +285,15 @@ class OutlineRect(QGraphicsRectItem):
class RefCircle(QGraphicsEllipseItem):
def __init__(self, x, y, diameter, parent=None, ID=None):
def __init__(self, x, y, diameter, parent=None, ID=None, important=False):
QGraphicsEllipseItem.__init__(self, x, y, diameter, diameter, parent)
self.setBrush(Qt.white)
self._ref = references.textReference(ID)
self.setToolTip(references.tooltip(self._ref))
self.setPen(QPen(Qt.black, 2))
self.setAcceptHoverEvents(True)
if important:
self.setBrush(Qt.black)
def multiplyDiameter(self, factor):
r1 = self.rect()

View file

@ -11,10 +11,9 @@ from manuskript.enums import Outline
from manuskript.functions import AUC
from manuskript.functions import toString
from manuskript.models.outlineModel import outlineModel
from manuskript.ui.editors.MDFunctions import MDFormatSelection
from manuskript.ui.editors.MMDHighlighter import MMDHighlighter
from manuskript.ui.editors.basicHighlighter import basicHighlighter
from manuskript.ui.editors.t2tFunctions import t2tClearFormat
from manuskript.ui.editors.t2tFunctions import t2tFormatSelection
from manuskript.ui.editors.t2tHighlighter import t2tHighlighter
from manuskript.ui.editors.textFormat import textFormat
try:
@ -76,7 +75,11 @@ class textEditView(QTextEdit):
# Spellchecking
if enchant and self.spellcheck:
self._dict = enchant.Dict(self.currentDict if self.currentDict else enchant.get_default_language())
try:
self._dict = enchant.Dict(self.currentDict if self.currentDict else enchant.get_default_language())
except enchant.errors.DictNotFoundError:
self.spellcheck = False
else:
self.spellcheck = False
@ -146,36 +149,25 @@ class textEditView(QTextEdit):
self.updateText()
def setupEditorForIndex(self, index):
# what type of text are we editing?
# In which model are we editing?
if type(index.model()) != outlineModel:
self._textFormat = "text"
return
# what type of text are we editing?
if self._column not in [Outline.text.value, Outline.notes.value]:
self._textFormat = "text"
else:
item = index.internalPointer()
if item.isHTML():
self._textFormat = "html"
elif item.isT2T():
self._textFormat = "t2t"
else:
self._textFormat = "text"
# Accept richtext maybe
if self._textFormat == "html":
self.setAcceptRichText(True)
else:
self.setAcceptRichText(False)
self._textFormat = "md"
# Setting highlighter
if self._highlighting:
item = index.internalPointer()
if self._column == Outline.text.value and not item.isT2T():
self.highlighter = basicHighlighter(self)
if self._column in [Outline.text.value, Outline.notes.value]:
self.highlighter = MMDHighlighter(self)
else:
self.highlighter = t2tHighlighter(self)
self.highlighter = basicHighlighter(self)
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
@ -190,11 +182,12 @@ class textEditView(QTextEdit):
f = QFont()
f.fromString(opt["font"])
# self.setFont(f)
self.setStyleSheet("""
self.setStyleSheet("""QTextEdit{{
background: {bg};
color: {foreground};
font-family: {ff};
font-size: {fs};
}}
""".format(
bg=opt["background"],
foreground=opt["fontColor"],
@ -235,15 +228,7 @@ class textEditView(QTextEdit):
# self.objectName(), self.parent().objectName()))
if topLeft.row() <= self._index.row() <= bottomRight.row():
if self._column == Outline.text.value and \
topLeft.column() <= Outline.type.value <= bottomRight.column():
# If item type change, and we display the main text,
# we reset the index to set the proper
# highlighter and other defaults
self.setupEditorForIndex(self._index)
self.updateText()
elif topLeft.column() <= self._column <= bottomRight.column():
if topLeft.column() <= self._column <= bottomRight.column():
self.updateText()
elif self._indexes:
@ -279,15 +264,9 @@ class textEditView(QTextEdit):
self._updating = True
if self._index:
self.disconnectDocument()
if self._textFormat == "html":
if self.toHtml() != toString(self._model.data(self._index)):
# print(" Updating html")
html = self._model.data(self._index)
self.document().setHtml(toString(html))
else:
if self.toPlainText() != toString(self._model.data(self._index)):
# print(" Updating plaintext")
self.document().setPlainText(toString(self._model.data(self._index)))
if self.toPlainText() != toString(self._model.data(self._index)):
# print(" Updating plaintext")
self.document().setPlainText(toString(self._model.data(self._index)))
self.reconnectDocument()
elif self._indexes:
@ -304,7 +283,6 @@ class textEditView(QTextEdit):
break
if same:
# Assuming that we don't use HTML with multiple items
self.document().setPlainText(t[0])
else:
self.document().setPlainText("")
@ -323,26 +301,11 @@ class textEditView(QTextEdit):
# print("Submitting", self.objectName())
if self._index:
# item = self._index.internalPointer()
if self._textFormat == "html":
if self.toHtml() != self._model.data(self._index):
# print(" Submitting html")
self._updating = True
html = self.toHtml()
# We don't store paragraph and font settings
html = re.sub(r"font-family:.*?;\s*", "", html)
html = re.sub(r"font-size:.*?;\s*", "", html)
html = re.sub(r"margin-.*?;\s*", "", html)
html = re.sub(r"text-indent:.*?;\s*", "", html)
html = re.sub(r"line-height:.*?;\s*", "", html)
# print("Submitting:", html)
self._model.setData(self._index, html)
self._updating = False
else:
if self.toPlainText() != self._model.data(self._index):
# print(" Submitting plain text")
self._updating = True
self._model.setData(self._index, self.toPlainText())
self._updating = False
if self.toPlainText() != self._model.data(self._index):
# print(" Submitting plain text")
self._updating = True
self._model.setData(self._index, self.toPlainText())
self._updating = False
elif self._indexes:
self._updating = True
@ -393,7 +356,13 @@ class textEditView(QTextEdit):
def toggleSpellcheck(self, v):
self.spellcheck = v
if enchant and self.spellcheck and not self._dict:
self._dict = enchant.Dict(self.currentDict if self.currentDict else enchant.get_default_language())
if self.currentDict:
self._dict = enchant.Dict(self.currentDict)
elif enchant.dict_exists(enchant.get_default_language()):
self._dict = enchant.Dict(enchant.get_default_language())
else:
self.spellcheck = False
if self.highlighter:
self.highlighter.rehighlight()
else:
@ -430,15 +399,19 @@ class textEditView(QTextEdit):
return popup_menu
# Select the word under the cursor.
# But only if there is no selection (otherwise it's impossible to select more text to copy/cut)
cursor = self.textCursor()
# cursor = self.cursorForPosition(pos)
cursor.select(QTextCursor.WordUnderCursor)
self.setTextCursor(cursor)
if not cursor.hasSelection():
cursor.select(QTextCursor.WordUnderCursor)
self.setTextCursor(cursor)
# Check if the selected word is misspelled and offer spelling
# suggestions if it is.
if cursor.hasSelection():
text = str(cursor.selectedText())
if not self._dict.check(text):
valid = self._dict.check(text)
selectedWord = cursor.selectedText()
if not valid:
spell_menu = QMenu(self.tr('Spelling Suggestions'), self)
for word in self._dict.suggest(text):
action = self.SpellAction(word, spell_menu)
@ -448,7 +421,23 @@ class textEditView(QTextEdit):
# suggestions.
if len(spell_menu.actions()) != 0:
popup_menu.insertSeparator(popup_menu.actions()[0])
# Adds: add to dictionary
addAction = QAction(self.tr("&Add to dictionary"), popup_menu)
addAction.triggered.connect(self.addWordToDict)
addAction.setData(selectedWord)
popup_menu.insertAction(popup_menu.actions()[0], addAction)
# Adds: suggestions
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
# popup_menu.insertSeparator(popup_menu.actions()[0])
# If word was added to custom dict, give the possibility to remove it
elif valid and self._dict.is_added(selectedWord):
popup_menu.insertSeparator(popup_menu.actions()[0])
# Adds: remove from dictionary
rmAction = QAction(self.tr("&Remove from custom dictionary"), popup_menu)
rmAction.triggered.connect(self.rmWordFromDict)
rmAction.setData(selectedWord)
popup_menu.insertAction(popup_menu.actions()[0], rmAction)
return popup_menu
@ -464,6 +453,16 @@ class textEditView(QTextEdit):
cursor.endEditBlock()
def addWordToDict(self):
word = self.sender().data()
self._dict.add(word)
self.highlighter.rehighlight()
def rmWordFromDict(self):
word = self.sender().data()
self._dict.remove(word)
self.highlighter.rehighlight()
###############################################################################
# FORMATTING
###############################################################################
@ -488,66 +487,13 @@ class textEditView(QTextEdit):
def applyFormat(self, _format):
if self._textFormat == "html":
if self._textFormat == "md":
if _format == "Clear":
cursor = self.textCursor()
if _format == "Clear":
fmt = self._defaultCharFormat
cursor.setCharFormat(fmt)
bf = self._defaultBlockFormat
cursor.setBlockFormat(bf)
elif _format in ["Bold", "Italic", "Underline"]:
cursor = self.textCursor()
# If no selection, selects the word in which the cursor is now
if not cursor.hasSelection():
cursor.movePosition(QTextCursor.StartOfWord,
QTextCursor.MoveAnchor)
cursor.movePosition(QTextCursor.EndOfWord,
QTextCursor.KeepAnchor)
fmt = cursor.charFormat()
if _format == "Bold":
fmt.setFontWeight(QFont.Bold if fmt.fontWeight() != QFont.Bold else QFont.Normal)
elif _format == "Italic":
fmt.setFontItalic(not fmt.fontItalic())
elif _format == "Underline":
fmt.setFontUnderline(not fmt.fontUnderline())
fmt2 = self._defaultCharFormat
fmt2.setFontWeight(fmt.fontWeight())
fmt2.setFontItalic(fmt.fontItalic())
fmt2.setFontUnderline(fmt.fontUnderline())
cursor.mergeCharFormat(fmt2)
elif _format in ["Left", "Center", "Right", "Justify"]:
cursor = self.textCursor()
# bf = cursor.blockFormat()
bf = QTextBlockFormat()
bf.setAlignment(
Qt.AlignLeft if _format == "Left" else
Qt.AlignHCenter if _format == "Center" else
Qt.AlignRight if _format == "Right" else
Qt.AlignJustify)
cursor.setBlockFormat(bf)
self.setTextCursor(cursor)
elif self._textFormat == "t2t":
if _format == "Bold":
t2tFormatSelection(self, 0)
MDFormatSelection(self, 0)
elif _format == "Italic":
t2tFormatSelection(self, 1)
elif _format == "Underline":
t2tFormatSelection(self, 2)
MDFormatSelection(self, 1)
elif _format == "Code":
MDFormatSelection(self, 2)
elif _format == "Clear":
t2tClearFormat(self)
MDFormatSelection(self)

View file

@ -105,7 +105,7 @@ class treeTitleDelegate(QStyledItemDelegate):
if extraText:
extraText = " ({}%)".format(extraText)
elif settings.viewSettings["Tree"]["InfoFolder"] == "Summary":
extraText = item.data(Outline.summarySentance.value)
extraText = item.data(Outline.summarySentence.value)
if extraText:
extraText = " - {}".format(extraText)
@ -118,7 +118,7 @@ class treeTitleDelegate(QStyledItemDelegate):
if extraText:
extraText = " ({}%)".format(extraText)
elif settings.viewSettings["Tree"]["InfoText"] == "Summary":
extraText = item.data(Outline.summarySentance.value)
extraText = item.data(Outline.summarySentence.value)
if extraText:
extraText = " - {}".format(extraText)

View file

@ -7,14 +7,15 @@ import os
from PyQt5.QtCore import QSettings, QRegExp, Qt, QDir
from PyQt5.QtGui import QIcon, QBrush, QColor, QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QWidget, QAction, QFileDialog, QSpinBox, QLineEdit, QLabel, QPushButton, QTreeWidgetItem
from PyQt5.QtWidgets import QWidget, QAction, QFileDialog, QSpinBox, QLineEdit, QLabel, QPushButton, QTreeWidgetItem, \
qApp
from manuskript import settings
from manuskript.enums import Outline
from manuskript.functions import mainWindow, iconFromColor, appPath
from manuskript.models.characterModel import characterModel
from manuskript.models.outlineModel import outlineItem
from manuskript.models.outlineModel import outlineModel
from manuskript.models.persosModel import persosModel
from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.ui.welcome_ui import Ui_welcome
@ -65,7 +66,7 @@ class welcome(QWidget, Ui_welcome):
def getAutoLoadValues(self):
sttgns = QSettings()
if sttgns.contains("autoLoad"):
autoLoad = True if sttgns.value("autoLoad") == "true" else False
autoLoad = True if sttgns.value("autoLoad") in ["true", True] else False
else:
autoLoad = False
if autoLoad and sttgns.contains("lastProject"):
@ -88,7 +89,7 @@ class welcome(QWidget, Ui_welcome):
if sttgns.contains("recentFiles"):
lst = sttgns.value("recentFiles")
self.mw.menuRecents.clear()
for f in lst:
for f in [f for f in lst if os.path.exists(f)]:
name = os.path.split(f)[1]
a = QAction(name, self)
a.setData(f)
@ -114,6 +115,7 @@ class welcome(QWidget, Ui_welcome):
def loadRecentFile(self):
act = self.sender()
self.appendToRecentFiles(act.data())
self.mw.closeProject()
self.mw.loadProject(act.data())
###############################################################################
@ -151,7 +153,7 @@ class welcome(QWidget, Ui_welcome):
self.tr("Manuskript project (*.msk)"))[0]
if filename:
if filename[:-4] != ".msk":
if filename[-4:] != ".msk":
filename += ".msk"
self.appendToRecentFiles(filename)
self.loadDefaultDatas()
@ -163,51 +165,49 @@ class welcome(QWidget, Ui_welcome):
def templates(self):
return [
(self.tr("Empty"), []),
(self.tr("Empty fiction"), [], "Fiction"),
(self.tr("Novel"), [
(20, self.tr("Chapter")),
(5, self.tr("Scene")),
(500, None) # A line with None is word count
]),
], "Fiction"),
(self.tr("Novella"), [
(10, self.tr("Chapter")),
(5, self.tr("Scene")),
(500, None)
]),
], "Fiction"),
(self.tr("Short Story"), [
(10, self.tr("Scene")),
(1000, None)
]),
], "Fiction"),
(self.tr("Trilogy"), [
(3, self.tr("Book")),
(3, self.tr("Section")),
(10, self.tr("Chapter")),
(5, self.tr("Scene")),
(500, None)
]),
], "Fiction"),
(self.tr("Empty non-fiction"), [], "Non-fiction"),
(self.tr("Research paper"), [
(3, self.tr("Section")),
(1000, None)
])
]
def defaultTextType(self):
return [
("t2t", self.tr("Txt2Tags"), "text-x-generic"),
("html", self.tr("Rich Text (html)"), "text-html"),
("txt", self.tr("Plain Text"), "text-x-generic"),
], "Non-fiction")
]
def changeTemplate(self, item, column):
template = [i for i in self.templates() if i[0] == item.text(0)]
self.btnCreate.setText(self.btnCreateText)
# Selected item is a template
if len(template):
self.template = template[0][1]
self.template = template[0]
self.updateTemplate()
# Selected item is a sample project
elif item.data(0, Qt.UserRole):
name = item.data(0, Qt.UserRole)
# Clear templates
self.template = self.templates()[0][1]
self.template = self.templates()[0]
self.updateTemplate()
# Change button text
self.btnCreate.setText("Open {}".format(name))
@ -232,7 +232,7 @@ class welcome(QWidget, Ui_welcome):
k = 0
hasWC = False
for d in self.template:
for d in self.template[1]:
spin = QSpinBox(self)
spin.setRange(0, 999999)
spin.setValue(d[0])
@ -261,29 +261,29 @@ class welcome(QWidget, Ui_welcome):
self.lytTemplate.addWidget(txt, k, 2)
k += 1
self.btnAddWC.setEnabled(not hasWC and len(self.template) > 0)
self.btnAddWC.setEnabled(not hasWC and len(self.template[1]) > 0)
self.btnAddLevel.setEnabled(True)
self.lblTotal.setVisible(hasWC)
self.updateWordCount()
def templateAddLevel(self):
if len(self.template) > 0 and \
self.template[len(self.template) - 1][1] == None:
if len(self.template[1]) > 0 and \
self.template[1][len(self.template[1]) - 1][1] == None:
# has word cound, so insert before
self.template.insert(len(self.template) - 1, (10, self.tr("Text")))
self.template[1].insert(len(self.template[1]) - 1, (10, self.tr("Text")))
else:
# No word count, so insert at end
self.template.append((10, self.tr("Something")))
self.template[1].append((10, self.tr("Something")))
self.updateTemplate()
def templateAddWordCount(self):
self.template.append((500, None))
self.template[1].append((500, None))
self.updateTemplate()
def deleteTemplateRow(self):
btn = self.sender()
row = btn.property("deleteRow")
self.template.pop(row)
self.template[1].pop(row)
self.updateTemplate()
def updateWordCount(self):
@ -316,11 +316,18 @@ class welcome(QWidget, Ui_welcome):
self.tree.setIndentation(0)
# Add templates
item = self.addTopLevelItem(self.tr("Templates"))
templates = self.templates()
item = self.addTopLevelItem(self.tr("Fiction"))
templates = [i for i in self.templates() if i[2] == "Fiction"]
for t in templates:
sub = QTreeWidgetItem(item, [t[0]])
# Add templates: non-fiction
item = self.addTopLevelItem(self.tr("Non-fiction"))
templates = [i for i in self.templates() if i[2] == "Non-fiction"]
for t in templates:
sub = QTreeWidgetItem(item, [t[0]])
# Add Demo project
item = self.addTopLevelItem(self.tr("Demo projects"))
dir = QDir(appPath("sample-projects"))
@ -328,24 +335,21 @@ class welcome(QWidget, Ui_welcome):
sub = QTreeWidgetItem(item, [f[:-4]])
sub.setData(0, Qt.UserRole, f)
# Populates default text type
self.cmbDefaultType.clear()
for t in self.defaultTextType():
self.cmbDefaultType.addItem(QIcon.fromTheme(t[2]), t[1], t[0])
self.tree.expandAll()
def loadDefaultDatas(self):
# Empty settings
imp.reload(settings)
t = [i for i in self.templates() if i[0] == self.template[0]]
if t and t[0][2] == "Non-fiction": settings.viewMode = "simple"
# Données
self.mw.mdlFlatData = QStandardItemModel(2, 8, self.mw)
# Persos
# self.mw.mdlPersos = QStandardItemModel(0, 0, self.mw)
self.mw.mdlPersos = persosModel(self.mw)
self.mw.mdlCharacter = characterModel(self.mw)
# self.mdlPersosProxy = None # persosProxyModel() # None
# self.mw.mdlPersosProxy = persosProxyModel(self.mw)
@ -386,8 +390,7 @@ class welcome(QWidget, Ui_welcome):
self.mw.mdlWorld = worldModel(self.mw)
root = self.mw.mdlOutline.rootItem
_type = self.cmbDefaultType.currentData()
settings.defaultTextType = _type
_type = "md"
def addElement(parent, datas):
if len(datas) == 2 and datas[1][1] == None or \
@ -416,5 +419,5 @@ class welcome(QWidget, Ui_welcome):
# parent.appendChild(item)
addElement(item, datas[1:])
if self.template:
addElement(root, self.template)
if self.template[1]:
addElement(root, self.template[1])

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/welcome_ui.ui'
#
# Created by: PyQt5 UI code generator 5.4.1
# Created by: PyQt5 UI code generator 5.4.2
#
# WARNING! All changes made in this file will be lost!
@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_welcome(object):
def setupUi(self, welcome):
welcome.setObjectName("welcome")
welcome.resize(705, 422)
welcome.resize(728, 459)
self.horizontalLayout = QtWidgets.QHBoxLayout(welcome)
self.horizontalLayout.setObjectName("horizontalLayout")
self.frame_2 = QtWidgets.QFrame(welcome)
@ -78,15 +78,6 @@ class Ui_welcome(object):
self.btnAddWC.setObjectName("btnAddWC")
self.horizontalLayout_2.addWidget(self.btnAddWC)
self.templateLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.lblTotal_2 = QtWidgets.QLabel(self.frame_2)
self.lblTotal_2.setObjectName("lblTotal_2")
self.horizontalLayout_3.addWidget(self.lblTotal_2)
self.cmbDefaultType = QtWidgets.QComboBox(self.frame_2)
self.cmbDefaultType.setObjectName("cmbDefaultType")
self.horizontalLayout_3.addWidget(self.cmbDefaultType)
self.templateLayout.addLayout(self.horizontalLayout_3)
self.horizontalLayout_23.addLayout(self.templateLayout)
self.verticalLayout_32.addLayout(self.horizontalLayout_23)
self.line_4 = QtWidgets.QFrame(self.frame_2)
@ -133,7 +124,6 @@ class Ui_welcome(object):
self.tree.setSortingEnabled(__sortingEnabled)
self.btnAddLevel.setText(_translate("welcome", "Add level"))
self.btnAddWC.setText(_translate("welcome", "Add wordcount"))
self.lblTotal_2.setText(_translate("welcome", "Default text type:"))
self.chkLoadLastProject.setText(_translate("welcome", "Next time, automatically open last project"))
self.btnOpen.setText(_translate("welcome", "Open..."))
self.btnRecent.setText(_translate("welcome", "Recent"))

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>705</width>
<height>422</height>
<width>728</width>
<height>459</height>
</rect>
</property>
<property name="windowTitle">
@ -190,20 +190,6 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="lblTotal_2">
<property name="text">
<string>Default text type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbDefaultType"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>

Binary file not shown.

View file

@ -0,0 +1 @@
1

View file

@ -0,0 +1,4 @@
Name: Peter
ID: 0
Importance: 2
Color: #ff0000

View file

@ -0,0 +1,4 @@
Name: Paul
ID: 1
Importance: 2
Color: #005500

View file

@ -0,0 +1,4 @@
Name: Philip
ID: 2
Importance: 1
Color: #ffff00

View file

@ -0,0 +1,4 @@
Name: Stephen
ID: 3
Importance: 1
Color: #ff5500

View file

@ -0,0 +1,4 @@
Name: Herod
ID: 4
Importance: 0
Color: #000000

View file

@ -0,0 +1,4 @@
Name: Barnabas
ID: 5
Importance: 1
Color: #a9b0b6

View file

@ -0,0 +1,3 @@
Title: The Acts of the Apostles
License: Public Domain (World English Bible)
Author: Luke

View file

@ -0,0 +1,5 @@
Idea: #ffff00
Note: #00ff00
Chapter: #0000ff
Scene: #ff0000
Research: #00ffff

View file

@ -0,0 +1,7 @@
title: Introduction
ID: 1
type: md
compile: 2
1 The first book I wrote, Theophilus, concerned all that Jesus began both to do and to teach, 2 until the day in which he was received up, after he had given commandment through the Holy Spirit to the apostles whom he had chosen. 3 To these he also showed himself alive after he suffered, by many proofs, appearing to them over a period of forty days, and speaking about Gods Kingdom.

View file

@ -0,0 +1,11 @@
title: Jesus taken up into heaven
ID: 2
type: md
notes: {P:0:The good news spreads from Jerusalem to Rome}
compile: 2
4 Being assembled together with them, he commanded them, “Dont depart from Jerusalem, but wait for the promise of the Father, which you heard from me. 5 For John indeed baptized in water, but you will be baptized in the Holy Spirit not many days from now.”
6 Therefore when they had come together, they asked him, “Lord, are you now restoring the kingdom to Israel?”
7 He said to them, “It isnt for you to know times or seasons which the Father has set within his own authority. 8 But you will receive power when the Holy Spirit has come upon you. You will be witnesses to me in Jerusalem, in all Judea and Samaria, and to the uttermost parts of the earth.”
9 When he had said these things, as they were looking, he was taken up, and a cloud received him out of their sight. 10 While they were looking steadfastly into the sky as he went, behold,* two men stood by them in white clothing, 11 who also said, “You men of Galilee, why do you stand looking into the sky? This Jesus, who was received up from you into the sky, will come back in the same way as you saw him going into the sky.”

View file

@ -0,0 +1,15 @@
title: Matthias chosen to replace Judas
ID: 3
type: md
notes: {C:2:Philip}
compile: 2
12 Then they returned to Jerusalem from the mountain called Olivet, which is near Jerusalem, a Sabbath days journey away. 13 When they had come in, they went up into the upper room where they were staying; that is Peter, John, James, Andrew, Philip, Thomas, Bartholomew, Matthew, James the son of Alphaeus, Simon the Zealot, and Judas the son of James. 14 All these with one accord continued steadfastly in prayer and supplication, along with the women, and Mary the mother of Jesus, and with his brothers.
15 In these days, Peter stood up in the middle of the disciples (and the number of names was about one hundred twenty), and said, 16 “Brothers, it was necessary that this Scripture should be fulfilled, which the Holy Spirit spoke before by the mouth of David concerning Judas, who was guide to those who took Jesus. 17 For he was counted with us, and received his portion in this ministry. 18 Now this man obtained a field with the reward for his wickedness, and falling headlong, his body burst open, and all his intestines gushed out. 19 It became known to everyone who lived in Jerusalem that in their language that field was called Akeldama, that is, The field of blood. 20 For it is written in the book of Psalms,
Let his habitation be made desolate.
Let no one dwell in it;
and,
Let another take his office.
21 “Of the men therefore who have accompanied us all the time that the Lord Jesus went in and out among us, 22 beginning from the baptism of John, to the day that he was received up from us, of these one must become a witness with us of his resurrection.”
23 They put forward two, Joseph called Barsabbas, who was also called Justus, and Matthias. 24 They prayed and said, “You, Lord, who know the hearts of all men, show which one of these two you have chosen 25 to take part in this ministry and apostleship from which Judas fell away, that he might go to his own place.” 26 They drew lots for them, and the lot fell on Matthias, and he was counted with the eleven apostles.

View file

@ -0,0 +1,7 @@
title: Chapter 1
ID: 5
type: folder
compile: 2
setGoal: 1000

View file

@ -0,0 +1,7 @@
title: The promised Spirit
ID: 4
type: md
compile: 2
1 Now when the day of Pentecost had come, they were all with one accord in one place. 2 Suddenly there came from the sky a sound like the rushing of a mighty wind, and it filled all the house where they were sitting. 3 Tongues like fire appeared and were distributed to them, and one sat on each of them. 4 They were all filled with the Holy Spirit, and began to speak with other languages, as the Spirit gave them the ability to speak. 5 Now there were dwelling in Jerusalem Jews, devout men, from every nation under the sky. 6 When this sound was heard, the multitude came together and were bewildered, because everyone heard them speaking in his own language. 7 They were all amazed and marveled, saying to one another, “Behold, arent all these who speak Galileans? 8 How do we hear, everyone in our own native language? 9 Parthians, Medes, Elamites, and people from Mesopotamia, Judea, Cappadocia, Pontus, Asia, 10 Phrygia, Pamphylia, Egypt, the parts of Libya around Cyrene, visitors from Rome, both Jews and proselytes, 11 Cretans and Arabians: we hear them speaking in our languages the mighty works of God!” 12 They were all amazed, and were perplexed, saying to one another, “What does this mean?” 13 Others, mocking, said, “They are filled with new wine.”

View file

@ -0,0 +1,39 @@
title: Peter adresses the crowd
ID: 15
type: md
POV: 0
notes: {P:0:The good news spreads from Jerusalem to Rome}
compile: 2
14 But Peter, standing up with the eleven, lifted up his voice, and spoke out to them, “You men of Judea, and all you who dwell at Jerusalem, let this be known to you, and listen to my words. 15 For these arent drunken, as you suppose, seeing it is only the third hour of the day. 16 But this is what has been spoken through the prophet Joel:
17 It will be in the last days, says God,
that I will pour out my Spirit on all flesh.
Your sons and your daughters will prophesy.
Your young men will see visions.
Your old men will dream dreams.
18 Yes, and on my servants and on my handmaidens in those days,
I will pour out my Spirit, and they will prophesy.
19 I will show wonders in the sky above,
and signs on the earth beneath;
blood, and fire, and billows of smoke.
20 The sun will be turned into darkness,
and the moon into blood,
before the great and glorious day of the Lord comes.
21 It will be that whoever will call on the name of the Lord will be saved.
22 “Men of Israel, hear these words! Jesus of Nazareth, a man approved by God to you by mighty works and wonders and signs which God did by him among you, even as you yourselves know, 23 him, being delivered up by the determined counsel and foreknowledge of God, you have taken by the hand of lawless men, crucified and killed; 24 whom God raised up, having freed him from the agony of death, because it was not possible that he should be held by it. 25 For David says concerning him,
I saw the Lord always before my face,
for he is on my right hand, that I should not be moved.
26 Therefore my heart was glad, and my tongue rejoiced.
Moreover my flesh also will dwell in hope;
27 because you will not leave my soul in Hades,†
neither will you allow your Holy One to see decay.
28 You made known to me the ways of life.
You will make me full of gladness with your presence.
29 “Brothers, I may tell you freely of the patriarch David, that he both died and was buried, and his tomb is with us to this day. 30 Therefore, being a prophet, and knowing that God had sworn with an oath to him that of the fruit of his body, according to the flesh, he would raise up the Christ to sit on his throne, 31 he foreseeing this spoke about the resurrection of the Christ, that his soul wasnt left in Hades,‡ and his flesh didnt see decay. 32 This Jesus God raised up, to which we all are witnesses. 33 Being therefore exalted by the right hand of God, and having received from the Father the promise of the Holy Spirit, he has poured out this, which you now see and hear. 34 For David didnt ascend into the heavens, but he says himself,
The Lord said to my Lord, “Sit by my right hand
35 until I make your enemies a footstool for your feet.”
36 “Let all the house of Israel therefore know certainly that God has made him both Lord and Christ, this Jesus whom you crucified.”
37 Now when they heard this, they were cut to the heart, and said to Peter and the rest of the apostles, “Brothers, what shall we do?”
38 Peter said to them, “Repent, and be baptized, every one of you, in the name of Jesus Christ for the forgiveness of sins, and you will receive the gift of the Holy Spirit. 39 For the promise is to you, and to your children, and to all who are far off, even as many as the Lord our God will call to himself.” 40 With many other words he testified, and exhorted them, saying, “Save yourselves from this crooked generation!”
41 Then those who gladly received his word were baptized. There were added that day about three thousand souls.

View file

@ -0,0 +1,7 @@
title: The life of the first believers
ID: 14
type: md
compile: 2
42 They continued steadfastly in the apostles teaching and fellowship, in the breaking of bread, and prayer. 43 Fear came on every soul, and many wonders and signs were done through the apostles. 44 All who believed were together, and had all things in common. 45 They sold their possessions and goods, and distributed them to all, according as anyone had need. 46 Day by day, continuing steadfastly with one accord in the temple, and breaking bread at home, they took their food with gladness and singleness of heart, 47 praising God, and having favor with all the people. The Lord added to the assembly day by day those who were being saved.

View file

@ -0,0 +1,7 @@
title: Chapter 2
ID: 6
type: folder
compile: 2
setGoal: 1000

View file

@ -0,0 +1,8 @@
title: Peter heals a beggar
ID: 13
type: md
POV: 0
compile: 2
1 Peter and John were going up into the temple at the hour of prayer, the ninth hour.* 2 A certain man who was lame from his mothers womb was being carried, whom they laid daily at the door of the temple which is called Beautiful, to ask gifts for the needy of those who entered into the temple. 3 Seeing Peter and John about to go into the temple, he asked to receive gifts for the needy. 4 Peter, fastening his eyes on him, with John, said, “Look at us.” 5 He listened to them, expecting to receive something from them. 6 But Peter said, “I have no silver or gold, but what I have, that I give you. In the name of Jesus Christ of Nazareth, get up and walk!” 7 He took him by the right hand and raised him up. Immediately his feet and his ankle bones received strength. 8 Leaping up, he stood and began to walk. He entered with them into the temple, walking, leaping, and praising God. 9 All the people saw him walking and praising God. 10 They recognized him, that it was he who used to sit begging for gifts for the needy at the Beautiful Gate of the temple. They were filled with wonder and amazement at what had happened to him.

View file

@ -0,0 +1,11 @@
title: Peter preaches
ID: 12
type: md
POV: 0
compile: 2
11 As the lame man who was healed held on to Peter and John, all the people ran together to them in the porch that is called Solomons, greatly wondering.
12 When Peter saw it, he responded to the people, “You men of Israel, why do you marvel at this man? Why do you fasten your eyes on us, as though by our own power or godliness we had made him walk? 13 The God of Abraham, Isaac, and Jacob, the God of our fathers, has glorified his Servant Jesus, whom you delivered up, and denied in the presence of Pilate, when he had determined to release him. 14 But you denied the Holy and Righteous One and asked for a murderer to be granted to you, 15 and killed the Prince of life, whom God raised from the dead, to which we are witnesses. 16 By faith in his name, his name has made this man strong, whom you see and know. Yes, the faith which is through him has given him this perfect soundness in the presence of you all.
17 “Now, brothers,† I know that you did this in ignorance, as did also your rulers. 18 But the things which God announced by the mouth of all his prophets, that Christ should suffer, he thus fulfilled.
19 “Repent therefore, and turn again, that your sins may be blotted out, so that there may come times of refreshing from the presence of the Lord, 20 and that he may send Christ Jesus, who was ordained for you before, 21 whom heaven must receive until the times of restoration of all things, which God spoke long ago by the mouth of his holy prophets. 22 For Moses indeed said to the fathers, The Lord God will raise up a prophet for you from among your brothers, like me. You shall listen to him in all things whatever he says to you. 23 It will be that every soul that will not listen to that prophet will be utterly destroyed from among the people.’✡ 24 Yes, and all the prophets from Samuel and those who followed after, as many as have spoken, they also told of these days. 25 You are the children of the prophets, and of the covenant which God made with our fathers, saying to Abraham, All the families of the earth will be blessed through your offspring.‡ .’✡ 26 God, having raised up his servant Jesus, sent him to you first to bless you, in turning away every one of you from your wickedness.”

View file

@ -0,0 +1,7 @@
title: Chapter 3
ID: 7
type: folder
compile: 2
setGoal: 1000

View file

@ -0,0 +1,13 @@
title: Peter and John in front of the Sanhedrin
ID: 16
type: md
POV: 0
compile: 2
1 As they spoke to the people, the priests and the captain of the temple and the Sadducees came to them, 2 being upset because they taught the people and proclaimed in Jesus the resurrection from the dead. 3 They laid hands on them, and put them in custody until the next day, for it was now evening. 4 But many of those who heard the word believed, and the number of the men came to be about five thousand.
5 In the morning, their rulers, elders, and scribes were gathered together in Jerusalem. 6 Annas the high priest was there, with Caiaphas, John, Alexander, and as many as were relatives of the high priest. 7 When they had stood Peter and John in the middle of them, they inquired, “By what power, or in what name, have you done this?”
8 Then Peter, filled with the Holy Spirit, said to them, “You rulers of the people, and elders of Israel, 9 if we are examined today concerning a good deed done to a crippled man, by what means this man has been healed, 10 may it be known to you all, and to all the people of Israel, that in the name of Jesus Christ of Nazareth, whom you crucified, whom God raised from the dead, in him does this man stand here before you whole. 11 He is the stone which was regarded as worthless by you, the builders, which has become the head of the corner.’✡ 12 There is salvation in no one else, for there is no other name under heaven that is given among men, by which we must be saved!”
13 Now when they saw the boldness of Peter and John, and had perceived that they were unlearned and ignorant men, they marveled. They recognized that they had been with Jesus. 14 Seeing the man who was healed standing with them, they could say nothing against it. 15 But when they had commanded them to go aside out of the council, they conferred among themselves, 16 saying, “What shall we do to these men? Because indeed a notable miracle has been done through them, as can be plainly seen by all who dwell in Jerusalem, and we cant deny it. 17 But so that this spreads no further among the people, lets threaten them, that from now on they dont speak to anyone in this name.” 18 They called them, and commanded them not to speak at all nor teach in the name of Jesus.
19 But Peter and John answered them, “Whether it is right in the sight of God to listen to you rather than to God, judge for yourselves, 20 for we cant help telling the things which we saw and heard.”
21 When they had further threatened them, they let them go, finding no way to punish them, because of the people; for everyone glorified God for that which was done. 22 For the man on whom this miracle of healing was performed was more than forty years old.

View file

@ -0,0 +1,15 @@
title: The believers pray
ID: 17
type: md
notes: Mention: {C:4:Herod}
compile: 2
23 Being let go, they came to their own company and reported all that the chief priests and the elders had said to them. 24 When they heard it, they lifted up their voice to God with one accord, and said, “O Lord, you are God, who made the heaven, the earth, the sea, and all that is in them; 25 who by the mouth of your servant, David, said,
Why do the nations rage,
and the peoples plot a vain thing?
26 The kings of the earth take a stand,
and the rulers take council together,
against the Lord, and against his Christ.*✡
27 “For truly, in this city against your holy servant, Jesus, whom you anointed, both Herod and Pontius Pilate, with the Gentiles and the people of Israel, were gathered together 28 to do whatever your hand and your council foreordained to happen. 29 Now, Lord, look at their threats, and grant to your servants to speak your word with all boldness, 30 while you stretch out your hand to heal; and that signs and wonders may be done through the name of your holy Servant Jesus.”
31 When they had prayed, the place was shaken where they were gathered together. They were all filled with the Holy Spirit, and they spoke the word of God with boldness.

View file

@ -0,0 +1,8 @@
title: The believers share
ID: 18
type: md
notes: {C:5:Barnabas}
compile: 2
32 The multitude of those who believed were of one heart and soul. Not one of them claimed that anything of the things which he possessed was his own, but they had all things in common. 33 With great power, the apostles gave their testimony of the resurrection of the Lord Jesus. Great grace was on them all. 34 For neither was there among them any who lacked, for as many as were owners of lands or houses sold them, and brought the proceeds of the things that were sold, 35 and laid them at the apostles feet, and distribution was made to each, according as anyone had need. 36 Joses, who by the apostles was also called Barnabas (which is, being interpreted, Son of Encouragement), a Levite, a man of Cyprus by race, 37 having a field, sold it and brought the money and laid it at the apostles feet.

View file

@ -0,0 +1,7 @@
title: Chapter 4
ID: 8
type: folder
compile: 2
setGoal: 1000

Some files were not shown because too many files have changed in this diff Show more