mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-06-13 16:34:34 +12:00
Adds new markdown highlighter and many little tweaks
This commit is contained in:
commit
c2dcbf56a0
|
@ -5,7 +5,7 @@ from PyQt5.QtWidgets import QPlainTextEdit, QGroupBox, qApp, QVBoxLayout, QCheck
|
||||||
|
|
||||||
from manuskript.exporter.manuskript.plainText import plainText
|
from manuskript.exporter.manuskript.plainText import plainText
|
||||||
from manuskript.functions import mainWindow
|
from manuskript.functions import mainWindow
|
||||||
from manuskript.ui.editors.MMDHighlighter import MMDHighlighter
|
from manuskript.ui.highlighters import MMDHighlighter
|
||||||
from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings
|
from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,4 +72,4 @@ class markdownSettings(exporterSettings):
|
||||||
self.settings = exporterSettings.getSettings(self)
|
self.settings = exporterSettings.getSettings(self)
|
||||||
self.settings["Preview"]["MarkdownHighlighter"] = self.chkMarkdownHighlighter.isChecked()
|
self.settings["Preview"]["MarkdownHighlighter"] = self.chkMarkdownHighlighter.isChecked()
|
||||||
|
|
||||||
return self.settings
|
return self.settings
|
||||||
|
|
|
@ -6,7 +6,7 @@ import re
|
||||||
from random import *
|
from random import *
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
|
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl, QTimer
|
||||||
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
|
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
from PyQt5.QtWidgets import qApp, QTextEdit
|
from PyQt5.QtWidgets import qApp, QTextEdit
|
||||||
|
@ -357,7 +357,9 @@ def statusMessage(message, duration=5000):
|
||||||
"""
|
"""
|
||||||
Shows a message in MainWindow's status bar.
|
Shows a message in MainWindow's status bar.
|
||||||
"""
|
"""
|
||||||
|
mainWindow().statusBar().show()
|
||||||
mainWindow().statusBar().showMessage(message, duration)
|
mainWindow().statusBar().showMessage(message, duration)
|
||||||
|
QTimer.singleShot(duration, mainWindow().statusBar().hide)
|
||||||
|
|
||||||
def openURL(url):
|
def openURL(url):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -492,9 +492,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
It assumes that the datas have been populated in a different way."""
|
It assumes that the datas have been populated in a different way."""
|
||||||
if loadFromFile and not os.path.exists(project):
|
if loadFromFile and not os.path.exists(project):
|
||||||
print(self.tr("The file {} does not exist. Try again.").format(project))
|
print(self.tr("The file {} does not exist. Try again.").format(project))
|
||||||
self.statusBar().showMessage(
|
F.statusMessage(
|
||||||
self.tr("The file {} does not exist. Try again.").format(project),
|
self.tr("The file {} does not exist. Try again.").format(project))
|
||||||
5000)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if loadFromFile:
|
if loadFromFile:
|
||||||
|
@ -712,7 +711,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
# Giving some feedback
|
# Giving some feedback
|
||||||
print(feedback)
|
print(feedback)
|
||||||
self.statusBar().showMessage(feedback, 5000)
|
F.statusMessage(feedback)
|
||||||
|
|
||||||
def loadEmptyDatas(self):
|
def loadEmptyDatas(self):
|
||||||
self.mdlFlatData = QStandardItemModel(self)
|
self.mdlFlatData = QStandardItemModel(self)
|
||||||
|
@ -732,13 +731,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
# Giving some feedback
|
# Giving some feedback
|
||||||
if not errors:
|
if not errors:
|
||||||
print(self.tr("Project {} loaded.").format(project))
|
print(self.tr("Project {} loaded.").format(project))
|
||||||
self.statusBar().showMessage(
|
F.statusMessage(
|
||||||
self.tr("Project {} loaded.").format(project), 5000)
|
self.tr("Project {} loaded.").format(project), 5000)
|
||||||
else:
|
else:
|
||||||
print(self.tr("Project {} loaded with some errors:").format(project))
|
print(self.tr("Project {} loaded with some errors:").format(project))
|
||||||
for e in errors:
|
for e in errors:
|
||||||
print(self.tr(" * {} wasn't found in project file.").format(e))
|
print(self.tr(" * {} wasn't found in project file.").format(e))
|
||||||
self.statusBar().showMessage(
|
F.statusMessage(
|
||||||
self.tr("Project {} loaded with some errors.").format(project), 5000)
|
self.tr("Project {} loaded with some errors.").format(project), 5000)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
|
@ -64,15 +64,15 @@ textEditor = {
|
||||||
"misspelled": "#F00",
|
"misspelled": "#F00",
|
||||||
"lineSpacing": 100,
|
"lineSpacing": 100,
|
||||||
"tabWidth": 20,
|
"tabWidth": 20,
|
||||||
"indent": True,
|
"indent": False,
|
||||||
"spacingAbove": 5,
|
"spacingAbove": 5,
|
||||||
"spacingBelow": 5,
|
"spacingBelow": 5,
|
||||||
"textAlignment": 0, # 0: left, 1: center, 2: right, 3: justify
|
"textAlignment": 0, # 0: left, 1: center, 2: right, 3: justify
|
||||||
"cursorWidth": 1,
|
"cursorWidth": 1,
|
||||||
"cursorNotBlinking": False,
|
"cursorNotBlinking": False,
|
||||||
"maxWidth": 0,
|
"maxWidth": 600,
|
||||||
"marginsLR": 0,
|
"marginsLR": 0,
|
||||||
"marginsTB": 0,
|
"marginsTB": 20,
|
||||||
"backgroundTransparent": False,
|
"backgroundTransparent": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +185,8 @@ def load(string, fromString=False, protocol=None):
|
||||||
#print("Loading:")
|
#print("Loading:")
|
||||||
#pp.pprint(allSettings)
|
#pp.pprint(allSettings)
|
||||||
|
|
||||||
|
# FIXME: use dict.update(dict) to update settings in newer versions.
|
||||||
|
|
||||||
if "viewSettings" in allSettings:
|
if "viewSettings" in allSettings:
|
||||||
global viewSettings
|
global viewSettings
|
||||||
viewSettings = allSettings["viewSettings"]
|
viewSettings = allSettings["viewSettings"]
|
||||||
|
@ -267,9 +269,9 @@ def load(string, fromString=False, protocol=None):
|
||||||
"textAlignment": 0, # Added in 0.5.0
|
"textAlignment": 0, # Added in 0.5.0
|
||||||
"cursorWidth": 1,
|
"cursorWidth": 1,
|
||||||
"cursorNotBlinking": False, # Added in 0.6.0
|
"cursorNotBlinking": False, # Added in 0.6.0
|
||||||
"maxWidth": 0,
|
"maxWidth": 600,
|
||||||
"marginsLR": 0,
|
"marginsLR": 0,
|
||||||
"marginsTB": 0,
|
"marginsTB": 20,
|
||||||
"backgroundTransparent": False, # Added in 0.6.0
|
"backgroundTransparent": False, # Added in 0.6.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
# --!-- coding: utf8 --!--
|
# --!-- coding: utf8 --!--
|
||||||
from PyQt5.QtCore import pyqtSignal, QModelIndex
|
from PyQt5.QtCore import pyqtSignal, QModelIndex
|
||||||
from PyQt5.QtGui import QPalette
|
from PyQt5.QtGui import QPalette
|
||||||
from PyQt5.QtWidgets import QWidget, QFrame, QSpacerItem, QSizePolicy, QVBoxLayout
|
from PyQt5.QtWidgets import QWidget, QFrame, QSpacerItem, QSizePolicy
|
||||||
|
from PyQt5.QtWidgets import QVBoxLayout, qApp, QStyle
|
||||||
|
|
||||||
from manuskript import settings
|
from manuskript import settings
|
||||||
from manuskript.functions import AUC, mainWindow
|
from manuskript.functions import AUC, mainWindow
|
||||||
from manuskript.ui.editors.editorWidget_ui import Ui_editorWidget_ui
|
from manuskript.ui.editors.editorWidget_ui import Ui_editorWidget_ui
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
from manuskript.ui.views.MDEditView import MDEditView
|
||||||
from manuskript.ui.tools.splitDialog import splitDialog
|
from manuskript.ui.tools.splitDialog import splitDialog
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,10 +61,37 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
||||||
|
|
||||||
self._model = None
|
self._model = None
|
||||||
|
|
||||||
|
# Capture textEdit scrollbar, so that we can put it outside the margins.
|
||||||
|
self.txtEditScrollBar = self.txtRedacText.verticalScrollBar()
|
||||||
|
self.txtEditScrollBar.setParent(self)
|
||||||
|
self.stack.currentChanged.connect(self.setScrollBarVisibility)
|
||||||
|
|
||||||
# def setModel(self, model):
|
# def setModel(self, model):
|
||||||
# self._model = model
|
# self._model = model
|
||||||
# self.setView()
|
# self.setView()
|
||||||
|
|
||||||
|
def resizeEvent(self, event):
|
||||||
|
"""
|
||||||
|
textEdit's scrollBar has been reparented to self. So we need to
|
||||||
|
update it's geomtry when self is resized, and put it where we want it
|
||||||
|
to be.
|
||||||
|
"""
|
||||||
|
# Update scrollbar geometry
|
||||||
|
r = self.geometry()
|
||||||
|
w = 10 # Cf. style.mainEditorTabSS
|
||||||
|
r.setWidth(w)
|
||||||
|
r.moveRight(self.geometry().width())
|
||||||
|
self.txtEditScrollBar.setGeometry(r)
|
||||||
|
|
||||||
|
QWidget.resizeEvent(self, event)
|
||||||
|
|
||||||
|
def setScrollBarVisibility(self):
|
||||||
|
"""
|
||||||
|
Since the texteEdit scrollBar has been reparented to self, it is not
|
||||||
|
hidden when stack changes. We have to do it manually.
|
||||||
|
"""
|
||||||
|
self.txtEditScrollBar.setVisible(self.stack.currentIndex() == 0)
|
||||||
|
|
||||||
def setFolderView(self, v):
|
def setFolderView(self, v):
|
||||||
oldV = self.folderView
|
oldV = self.folderView
|
||||||
if v == "cork":
|
if v == "cork":
|
||||||
|
@ -150,7 +178,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
||||||
self.updateTabTitle()
|
self.updateTabTitle()
|
||||||
|
|
||||||
def addTitle(itm):
|
def addTitle(itm):
|
||||||
edt = textEditView(self, html="<h{l}>{t}</h{l}>".format(l=min(itm.level() + 1, 5), t=itm.title()),
|
edt = MDEditView(self, html="<h{l}>{t}</h{l}>".format(l=min(itm.level() + 1, 5), t=itm.title()),
|
||||||
autoResize=True)
|
autoResize=True)
|
||||||
edt.setFrameShape(QFrame.NoFrame)
|
edt.setFrameShape(QFrame.NoFrame)
|
||||||
self.txtEdits.append(edt)
|
self.txtEdits.append(edt)
|
||||||
|
@ -163,7 +191,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
||||||
l.addWidget(line)
|
l.addWidget(line)
|
||||||
|
|
||||||
def addText(itm):
|
def addText(itm):
|
||||||
edt = textEditView(self,
|
edt = MDEditView(self,
|
||||||
index=itm.index(),
|
index=itm.index(),
|
||||||
spellcheck=self.spellcheck,
|
spellcheck=self.spellcheck,
|
||||||
dict=settings.dict,
|
dict=settings.dict,
|
||||||
|
@ -214,7 +242,12 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
||||||
w = QWidget()
|
w = QWidget()
|
||||||
w.setObjectName("editorWidgetFolderText")
|
w.setObjectName("editorWidgetFolderText")
|
||||||
l = QVBoxLayout(w)
|
l = QVBoxLayout(w)
|
||||||
w.setStyleSheet("background: {};".format(settings.textEditor["background"]))
|
opt = settings.textEditor
|
||||||
|
background = (opt["background"] if not opt["backgroundTransparent"]
|
||||||
|
else "transparent")
|
||||||
|
w.setStyleSheet("background: {};".format(background))
|
||||||
|
self.stack.widget(1).setStyleSheet("background: {}"
|
||||||
|
.format(background))
|
||||||
# self.scroll.setWidgetResizable(False)
|
# self.scroll.setWidgetResizable(False)
|
||||||
|
|
||||||
self.txtEdits = []
|
self.txtEdits = []
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'manuskript/ui/editors/editorWidget_ui.ui'
|
# Form implementation generated from reading ui file 'manuskript/ui/editors/editorWidget_ui.ui'
|
||||||
#
|
#
|
||||||
# Created: Fri Apr 8 20:03:08 2016
|
# Created by: PyQt5 UI code generator 5.5.1
|
||||||
# by: PyQt5 UI code generator 5.2.1
|
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
@ -23,7 +22,7 @@ class Ui_editorWidget_ui(object):
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.text)
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.text)
|
||||||
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
self.txtRedacText = textEditView(self.text)
|
self.txtRedacText = MDEditView(self.text)
|
||||||
self.txtRedacText.setFrameShape(QtWidgets.QFrame.NoFrame)
|
self.txtRedacText.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
self.txtRedacText.setObjectName("txtRedacText")
|
self.txtRedacText.setObjectName("txtRedacText")
|
||||||
self.horizontalLayout_2.addWidget(self.txtRedacText)
|
self.horizontalLayout_2.addWidget(self.txtRedacText)
|
||||||
|
@ -31,8 +30,8 @@ class Ui_editorWidget_ui(object):
|
||||||
self.folder = QtWidgets.QWidget()
|
self.folder = QtWidgets.QWidget()
|
||||||
self.folder.setObjectName("folder")
|
self.folder.setObjectName("folder")
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.folder)
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.folder)
|
||||||
self.verticalLayout.setSpacing(0)
|
|
||||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout.setSpacing(0)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.scroll = QtWidgets.QScrollArea(self.folder)
|
self.scroll = QtWidgets.QScrollArea(self.folder)
|
||||||
self.scroll.setAutoFillBackground(True)
|
self.scroll.setAutoFillBackground(True)
|
||||||
|
@ -75,6 +74,6 @@ class Ui_editorWidget_ui(object):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
editorWidget_ui.setWindowTitle(_translate("editorWidget_ui", "Form"))
|
editorWidget_ui.setWindowTitle(_translate("editorWidget_ui", "Form"))
|
||||||
|
|
||||||
from manuskript.ui.views.outlineView import outlineView
|
from manuskript.ui.views.MDEditView import MDEditView
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
|
||||||
from manuskript.ui.views.corkView import corkView
|
from manuskript.ui.views.corkView import corkView
|
||||||
|
from manuskript.ui.views.outlineView import outlineView
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtRedacText">
|
<widget class="MDEditView" name="txtRedacText">
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -147,12 +147,12 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>textEditView</class>
|
<class>MDEditView</class>
|
||||||
<extends>QTextEdit</extends>
|
<extends>QTextEdit</extends>
|
||||||
<header>manuskript.ui.views.textEditView.h</header>
|
<header>manuskript.ui.views.MDEditView.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>outlineView</class>
|
<class>outlineView</class>
|
||||||
<extends>QTreeView</extends>
|
<extends>QTreeView</extends>
|
||||||
<header>manuskript.ui.views.outlineView.h</header>
|
<header>manuskript.ui.views.outlineView.h</header>
|
||||||
|
|
|
@ -13,10 +13,9 @@ from manuskript import settings
|
||||||
from manuskript.enums import Outline
|
from manuskript.enums import Outline
|
||||||
from manuskript.functions import allPaths, drawProgress
|
from manuskript.functions import allPaths, drawProgress
|
||||||
from manuskript.ui.editors.locker import locker
|
from manuskript.ui.editors.locker import locker
|
||||||
from manuskript.ui.editors.textFormat import textFormat
|
|
||||||
from manuskript.ui.editors.themes import findThemePath, generateTheme, setThemeEditorDatas
|
from manuskript.ui.editors.themes import findThemePath, generateTheme, setThemeEditorDatas
|
||||||
from manuskript.ui.editors.themes import loadThemeDatas
|
from manuskript.ui.editors.themes import loadThemeDatas
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
from manuskript.ui.views.MDEditView import MDEditView
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import enchant
|
import enchant
|
||||||
|
@ -35,11 +34,11 @@ class fullScreenEditor(QWidget):
|
||||||
self._geometries = {}
|
self._geometries = {}
|
||||||
|
|
||||||
# Text editor
|
# Text editor
|
||||||
self.editor = textEditView(self,
|
self.editor = MDEditView(self,
|
||||||
index=index,
|
index=index,
|
||||||
spellcheck=settings.spellcheck,
|
spellcheck=settings.spellcheck,
|
||||||
highlighting=True,
|
highlighting=True,
|
||||||
dict=settings.dict)
|
dict=settings.dict)
|
||||||
self.editor.setFrameStyle(QFrame.NoFrame)
|
self.editor.setFrameStyle(QFrame.NoFrame)
|
||||||
self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
self.editor.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
self.editor.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
|
@ -65,11 +64,7 @@ class fullScreenEditor(QWidget):
|
||||||
|
|
||||||
self.topPanel.layout().addStretch(1)
|
self.topPanel.layout().addStretch(1)
|
||||||
|
|
||||||
# Formatting
|
# Close
|
||||||
self.textFormat = textFormat(self)
|
|
||||||
self.topPanel.layout().addWidget(self.textFormat)
|
|
||||||
self.topPanel.layout().addStretch(1)
|
|
||||||
|
|
||||||
self.btnClose = QPushButton(self)
|
self.btnClose = QPushButton(self)
|
||||||
self.btnClose.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCloseButton))
|
self.btnClose.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCloseButton))
|
||||||
self.btnClose.clicked.connect(self.close)
|
self.btnClose.clicked.connect(self.close)
|
||||||
|
|
|
@ -270,9 +270,6 @@ class mainEditor(QWidget, Ui_mainEditor):
|
||||||
else:
|
else:
|
||||||
visible = True
|
visible = True
|
||||||
|
|
||||||
# Hides / show textFormat
|
|
||||||
self.textFormat.updateFromIndex(index)
|
|
||||||
|
|
||||||
self.btnRedacFolderText.setVisible(visible)
|
self.btnRedacFolderText.setVisible(visible)
|
||||||
self.btnRedacFolderCork.setVisible(visible)
|
self.btnRedacFolderCork.setVisible(visible)
|
||||||
self.btnRedacFolderOutline.setVisible(visible)
|
self.btnRedacFolderOutline.setVisible(visible)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'manuskript/ui/editors/mainEditor_ui.ui'
|
# Form implementation generated from reading ui file 'manuskript/ui/editors/mainEditor_ui.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.9
|
# Created by: PyQt5 UI code generator 5.5.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
@ -65,17 +65,6 @@ class Ui_mainEditor(object):
|
||||||
self.horizontalLayout_19.addWidget(self.sldCorkSizeFactor)
|
self.horizontalLayout_19.addWidget(self.sldCorkSizeFactor)
|
||||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
self.horizontalLayout_19.addItem(spacerItem)
|
self.horizontalLayout_19.addItem(spacerItem)
|
||||||
self.textFormat = textFormat(mainEditor)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.textFormat.sizePolicy().hasHeightForWidth())
|
|
||||||
self.textFormat.setSizePolicy(sizePolicy)
|
|
||||||
self.textFormat.setMinimumSize(QtCore.QSize(20, 20))
|
|
||||||
self.textFormat.setObjectName("textFormat")
|
|
||||||
self.horizontalLayout_19.addWidget(self.textFormat)
|
|
||||||
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
|
||||||
self.horizontalLayout_19.addItem(spacerItem1)
|
|
||||||
self.lblRedacWC = QtWidgets.QLabel(mainEditor)
|
self.lblRedacWC = QtWidgets.QLabel(mainEditor)
|
||||||
self.lblRedacWC.setMinimumSize(QtCore.QSize(10, 0))
|
self.lblRedacWC.setMinimumSize(QtCore.QSize(10, 0))
|
||||||
self.lblRedacWC.setText("")
|
self.lblRedacWC.setText("")
|
||||||
|
@ -110,4 +99,3 @@ class Ui_mainEditor(object):
|
||||||
self.btnRedacFullscreen.setShortcut(_translate("mainEditor", "F11"))
|
self.btnRedacFullscreen.setShortcut(_translate("mainEditor", "F11"))
|
||||||
|
|
||||||
from manuskript.ui.editors.tabSplitter import tabSplitter
|
from manuskript.ui.editors.tabSplitter import tabSplitter
|
||||||
from manuskript.ui.editors.textFormat import textFormat
|
|
||||||
|
|
|
@ -51,8 +51,7 @@
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="go-up">
|
<iconset theme="go-up">
|
||||||
<normaloff/>
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Alt+Up</string>
|
<string>Alt+Up</string>
|
||||||
|
@ -141,35 +140,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="textFormat" name="textFormat" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_2">
|
<spacer name="horizontalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -237,12 +207,6 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
|
||||||
<class>textFormat</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>manuskript.ui.editors.textFormat.h</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>tabSplitter</class>
|
<class>tabSplitter</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
|
|
|
@ -10,7 +10,7 @@ from PyQt5.QtGui import QPixmap, QPainter, QColor, QBrush, QImage, QTextBlockFor
|
||||||
from PyQt5.QtWidgets import qApp, QFrame
|
from PyQt5.QtWidgets import qApp, QFrame
|
||||||
|
|
||||||
from manuskript.functions import allPaths, appPath, findBackground, findFirstFile
|
from manuskript.functions import allPaths, appPath, findBackground, findFirstFile
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
from manuskript.ui.views.MDEditView import MDEditView
|
||||||
|
|
||||||
_thumbCache = {}
|
_thumbCache = {}
|
||||||
|
|
||||||
|
@ -89,13 +89,13 @@ def themeTextRect(themeDatas, screenRect):
|
||||||
def createThemePreview(theme, screenRect, size=QSize(200, 120)):
|
def createThemePreview(theme, screenRect, size=QSize(200, 120)):
|
||||||
"""
|
"""
|
||||||
Generates a QPixmap preview for given theme.
|
Generates a QPixmap preview for given theme.
|
||||||
|
|
||||||
Theme can be either a string containing the filename of the ini
|
Theme can be either a string containing the filename of the ini
|
||||||
file with the theme settings, or it can be a dict with the settings.
|
file with the theme settings, or it can be a dict with the settings.
|
||||||
|
|
||||||
If theme is a filename, the result is cached.
|
If theme is a filename, the result is cached.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Checking whether theme is a string or dict
|
# Checking whether theme is a string or dict
|
||||||
if type(theme) == str and os.path.exists(theme):
|
if type(theme) == str and os.path.exists(theme):
|
||||||
# Theme is the path to an ini file
|
# Theme is the path to an ini file
|
||||||
|
@ -126,7 +126,7 @@ def createThemePreview(theme, screenRect, size=QSize(200, 120)):
|
||||||
painter.setPen(Qt.white)
|
painter.setPen(Qt.white)
|
||||||
painter.drawRect(QRect(w, h, w * 4, h * 5))
|
painter.drawRect(QRect(w, h, w * 4, h * 5))
|
||||||
painter.end()
|
painter.end()
|
||||||
|
|
||||||
# If theme is a themefile, we keep it in cache
|
# If theme is a themefile, we keep it in cache
|
||||||
if fromFile:
|
if fromFile:
|
||||||
_thumbCache[theme] = [themeDatas, px]
|
_thumbCache[theme] = [themeDatas, px]
|
||||||
|
@ -265,11 +265,12 @@ def setThemeEditorDatas(editor, themeDatas, pixmap, screenRect):
|
||||||
)
|
)
|
||||||
|
|
||||||
editor._fromTheme = True
|
editor._fromTheme = True
|
||||||
|
editor._themeData = themeDatas
|
||||||
|
editor.highlighter.updateColorScheme()
|
||||||
|
|
||||||
def addThemePreviewText(pixmap, themeDatas, screenRect):
|
def addThemePreviewText(pixmap, themeDatas, screenRect):
|
||||||
# Text
|
# Text
|
||||||
previewText = textEditView(highlighting=True)
|
previewText = MDEditView(highlighting=True)
|
||||||
previewText.setFrameStyle(QFrame.NoFrame)
|
previewText.setFrameStyle(QFrame.NoFrame)
|
||||||
previewText.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
previewText.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
previewText.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
previewText.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
|
|
|
@ -5,10 +5,10 @@ import re
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QTextCharFormat, QFont, QTextCursor, QFontMetrics
|
from PyQt5.QtGui import QTextCharFormat, QFont, QTextCursor, QFontMetrics
|
||||||
|
|
||||||
from manuskript.ui.editors.basicHighlighter import basicHighlighter
|
from manuskript.ui.highlighters import BasicHighlighter
|
||||||
|
|
||||||
|
|
||||||
class MMDHighlighter(basicHighlighter):
|
class MMDHighlighter(BasicHighlighter):
|
||||||
|
|
||||||
MARKDOWN_REGEX = {
|
MARKDOWN_REGEX = {
|
||||||
'Bold': '(\*\*)(.+?)(\*\*)',
|
'Bold': '(\*\*)(.+?)(\*\*)',
|
||||||
|
@ -27,7 +27,7 @@ class MMDHighlighter(basicHighlighter):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, editor, style="Default"):
|
def __init__(self, editor, style="Default"):
|
||||||
basicHighlighter.__init__(self, editor)
|
BasicHighlighter.__init__(self, editor)
|
||||||
|
|
||||||
self.editor = editor
|
self.editor = editor
|
||||||
|
|
||||||
|
@ -35,13 +35,6 @@ class MMDHighlighter(basicHighlighter):
|
||||||
for key in self.MARKDOWN_REGEX:
|
for key in self.MARKDOWN_REGEX:
|
||||||
self.rules[key] = re.compile(self.MARKDOWN_REGEX[key])
|
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):
|
def doHighlightBlock(self, text):
|
||||||
"""
|
"""
|
||||||
A quick-n-dirty very basic highlighter, that fails in most non-trivial cases. And is ugly.
|
A quick-n-dirty very basic highlighter, that fails in most non-trivial cases. And is ugly.
|
12
manuskript/ui/highlighters/__init__.py
Normal file
12
manuskript/ui/highlighters/__init__.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf8 -*-
|
||||||
|
|
||||||
|
from manuskript.ui.highlighters.basicHighlighter import BasicHighlighter
|
||||||
|
from manuskript.ui.highlighters.MMDHighlighter import MMDHighlighter
|
||||||
|
|
||||||
|
# Markdown highlighter
|
||||||
|
from manuskript.ui.highlighters.markdownEnums import MarkdownState
|
||||||
|
from manuskript.ui.highlighters.markdownEnums import MarkdownTokenType
|
||||||
|
from manuskript.ui.highlighters.markdownEnums import BlockquoteStyle
|
||||||
|
from manuskript.ui.highlighters.markdownTokenizer import MarkdownTokenizer
|
||||||
|
from manuskript.ui.highlighters.markdownHighlighter import MarkdownHighlighter
|
|
@ -4,12 +4,16 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QBrush, QTextCursor, QColor, QFont, QSyntaxHighlighter, QTextBlockFormat, QTextCharFormat
|
from PyQt5.QtGui import QBrush, QTextCursor, QColor, QFont, QSyntaxHighlighter
|
||||||
|
from PyQt5.QtGui import QTextBlockFormat, QTextCharFormat
|
||||||
|
|
||||||
import manuskript.models.references as Ref
|
import manuskript.models.references as Ref
|
||||||
|
import manuskript.ui.style as S
|
||||||
|
from manuskript import settings
|
||||||
|
from manuskript import functions as F
|
||||||
|
|
||||||
|
|
||||||
class basicHighlighter(QSyntaxHighlighter):
|
class BasicHighlighter(QSyntaxHighlighter):
|
||||||
def __init__(self, editor):
|
def __init__(self, editor):
|
||||||
QSyntaxHighlighter.__init__(self, editor.document())
|
QSyntaxHighlighter.__init__(self, editor.document())
|
||||||
|
|
||||||
|
@ -17,6 +21,11 @@ class basicHighlighter(QSyntaxHighlighter):
|
||||||
self._misspelledColor = Qt.red
|
self._misspelledColor = Qt.red
|
||||||
self._defaultBlockFormat = QTextBlockFormat()
|
self._defaultBlockFormat = QTextBlockFormat()
|
||||||
self._defaultCharFormat = QTextCharFormat()
|
self._defaultCharFormat = QTextCharFormat()
|
||||||
|
self.defaultTextColor = QColor(S.text)
|
||||||
|
self.backgroundColor = QColor(S.base)
|
||||||
|
self.markupColor = QColor(S.textLight)
|
||||||
|
self.linkColor = QColor(S.link)
|
||||||
|
self.spellingErrorColor = QColor(Qt.red)
|
||||||
|
|
||||||
def setDefaultBlockFormat(self, bf):
|
def setDefaultBlockFormat(self, bf):
|
||||||
self._defaultBlockFormat = bf
|
self._defaultBlockFormat = bf
|
||||||
|
@ -29,17 +38,63 @@ class basicHighlighter(QSyntaxHighlighter):
|
||||||
def setMisspelledColor(self, color):
|
def setMisspelledColor(self, color):
|
||||||
self._misspelledColor = color
|
self._misspelledColor = color
|
||||||
|
|
||||||
|
def updateColorScheme(self, rehighlight=True):
|
||||||
|
"""
|
||||||
|
Generates a base set of colors that will take account of user
|
||||||
|
preferences, and use system style.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Reading user settings
|
||||||
|
opt = settings.textEditor
|
||||||
|
|
||||||
|
if not self.editor._fromTheme or not self.editor._themeData:
|
||||||
|
|
||||||
|
self.defaultTextColor = QColor(opt["fontColor"])
|
||||||
|
self.backgroundColor = (QColor(opt["background"])
|
||||||
|
if not opt["backgroundTransparent"]
|
||||||
|
else QColor(S.window))
|
||||||
|
self.markupColor = F.mixColors(self.defaultTextColor,
|
||||||
|
self.backgroundColor,
|
||||||
|
.3)
|
||||||
|
self.linkColor = QColor(S.link)
|
||||||
|
self.spellingErrorColor = QColor(opt["misspelled"])
|
||||||
|
self._defaultCharFormat.setForeground(QBrush(self.defaultTextColor))
|
||||||
|
|
||||||
|
# FullscreenEditor probably
|
||||||
|
else:
|
||||||
|
opt = self.editor._themeData
|
||||||
|
self.defaultTextColor = QColor(opt["Text/Color"])
|
||||||
|
self.backgroundColor = F.mixColors(
|
||||||
|
QColor(opt["Foreground/Color"]),
|
||||||
|
QColor(opt["Background/Color"]),
|
||||||
|
int(opt["Foreground/Opacity"])/100.)
|
||||||
|
self.markupColor = F.mixColors(self.defaultTextColor,
|
||||||
|
self.backgroundColor,
|
||||||
|
.3)
|
||||||
|
self.linkColor = QColor(S.link)
|
||||||
|
self.spellingErrorColor = QColor(opt["Text/Misspelled"])
|
||||||
|
|
||||||
|
if rehighlight:
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
def highlightBlock(self, text):
|
def highlightBlock(self, text):
|
||||||
"""Apply syntax highlighting to the given block of text.
|
"""Apply syntax highlighting to the given block of text.
|
||||||
"""
|
"""
|
||||||
self.highlightBlockBefore(text)
|
self.highlightBlockBefore(text)
|
||||||
|
self.doHighlightBlock(text)
|
||||||
self.highlightBlockAfter(text)
|
self.highlightBlockAfter(text)
|
||||||
|
|
||||||
|
def doHighlightBlock(self, text):
|
||||||
|
"""
|
||||||
|
Virtual funtion to subclass.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def highlightBlockBefore(self, text):
|
def highlightBlockBefore(self, text):
|
||||||
"""Highlighting to do before anything else.
|
"""Highlighting to do before anything else.
|
||||||
|
|
||||||
When subclassing basicHighlighter, you must call highlightBlockBefore
|
When subclassing BasicHighlighter, you must call highlightBlockBefore
|
||||||
before you do any custom highlighting.
|
before you do any custom highlighting. Or implement doHighlightBlock.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#print(">", self.currentBlock().document().availableUndoSteps())
|
#print(">", self.currentBlock().document().availableUndoSteps())
|
||||||
|
@ -56,8 +111,8 @@ class basicHighlighter(QSyntaxHighlighter):
|
||||||
def highlightBlockAfter(self, text):
|
def highlightBlockAfter(self, text):
|
||||||
"""Highlighting to do after everything else.
|
"""Highlighting to do after everything else.
|
||||||
|
|
||||||
When subclassing basicHighlighter, you must call highlightBlockAfter
|
When subclassing BasicHighlighter, you must call highlightBlockAfter
|
||||||
after your custom highlighting.
|
after your custom highlighting. Or implement doHighlightBlock.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# References
|
# References
|
||||||
|
@ -91,13 +146,16 @@ class basicHighlighter(QSyntaxHighlighter):
|
||||||
textedText = text + " "
|
textedText = text + " "
|
||||||
|
|
||||||
# Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
|
# Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
|
||||||
WORDS = '(?iu)([\w\']+)[^\'\w]' # (?iu) means case insensitive and unicode
|
WORDS = r'(?iu)([\w\']+)[^\'\w]'
|
||||||
|
# (?iu) means case insensitive and unicode
|
||||||
if hasattr(self.editor, "spellcheck") and self.editor.spellcheck:
|
if hasattr(self.editor, "spellcheck") and self.editor.spellcheck:
|
||||||
for word_object in re.finditer(WORDS, textedText):
|
for word_object in re.finditer(WORDS, textedText):
|
||||||
if self.editor._dict and not self.editor._dict.check(word_object.group(1)):
|
if (self.editor._dict
|
||||||
|
and not self.editor._dict.check(word_object.group(1))):
|
||||||
format = self.format(word_object.start(1))
|
format = self.format(word_object.start(1))
|
||||||
format.setUnderlineColor(self._misspelledColor)
|
format.setUnderlineColor(self._misspelledColor)
|
||||||
# SpellCheckUnderline fails with some fonts
|
# SpellCheckUnderline fails with some fonts
|
||||||
format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
|
format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
|
||||||
self.setFormat(word_object.start(1),
|
self.setFormat(word_object.start(1),
|
||||||
word_object.end(1) - word_object.start(1), format)
|
word_object.end(1) - word_object.start(1),
|
||||||
|
format)
|
100
manuskript/ui/highlighters/markdownEnums.py
Normal file
100
manuskript/ui/highlighters/markdownEnums.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
# MARKDOWN STATES
|
||||||
|
#==============================================================================
|
||||||
|
|
||||||
|
class MarkdownState:
|
||||||
|
MarkdownStateUnknown = -1
|
||||||
|
MarkdownStateParagraphBreak = 0
|
||||||
|
MarkdownStateListLineBreak = 1
|
||||||
|
MarkdownStateParagraph = 2
|
||||||
|
MarkdownStateAtxHeading1 = 3
|
||||||
|
MarkdownStateAtxHeading2 = 4
|
||||||
|
MarkdownStateAtxHeading3 = 5
|
||||||
|
MarkdownStateAtxHeading4 = 6
|
||||||
|
MarkdownStateAtxHeading5 = 7
|
||||||
|
MarkdownStateAtxHeading6 = 8
|
||||||
|
MarkdownStateBlockquote = 9
|
||||||
|
MarkdownStateCodeBlock = 10
|
||||||
|
MarkdownStateInGithubCodeFence = 11
|
||||||
|
MarkdownStateInPandocCodeFence = 12
|
||||||
|
MarkdownStateCodeFenceEnd = 13
|
||||||
|
MarkdownStateComment = 14
|
||||||
|
MarkdownStateHorizontalRule = 15
|
||||||
|
MarkdownStateNumberedList = 16
|
||||||
|
MarkdownStateBulletPointList = 17
|
||||||
|
MarkdownStateSetextHeading1Line1 = 18
|
||||||
|
MarkdownStateSetextHeading1Line2 = 19
|
||||||
|
MarkdownStateSetextHeading2Line1 = 20
|
||||||
|
MarkdownStateSetextHeading2Line2 = 21
|
||||||
|
MarkdownStatePipeTableHeader = 22
|
||||||
|
MarkdownStatePipeTableDivider = 23
|
||||||
|
MarkdownStatePipeTableRow = 24
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
# MARKDOWN TOKEN TYPE
|
||||||
|
#==============================================================================
|
||||||
|
|
||||||
|
class MarkdownTokenType:
|
||||||
|
TokenUnknown = -1
|
||||||
|
|
||||||
|
# Titles
|
||||||
|
TokenAtxHeading1 = 0
|
||||||
|
TokenAtxHeading2 = 1
|
||||||
|
TokenAtxHeading3 = 2
|
||||||
|
TokenAtxHeading4 = 3
|
||||||
|
TokenAtxHeading5 = 4
|
||||||
|
TokenAtxHeading6 = 5
|
||||||
|
TokenSetextHeading1Line1 = 6
|
||||||
|
TokenSetextHeading1Line2 = 7
|
||||||
|
TokenSetextHeading2Line1 = 8
|
||||||
|
TokenSetextHeading2Line2 = 9
|
||||||
|
|
||||||
|
TokenEmphasis = 10
|
||||||
|
TokenStrong = 11
|
||||||
|
TokenStrikethrough = 12
|
||||||
|
TokenVerbatim = 13
|
||||||
|
TokenHtmlTag = 14
|
||||||
|
TokenHtmlEntity = 15
|
||||||
|
TokenAutomaticLink = 16
|
||||||
|
TokenInlineLink = 17
|
||||||
|
TokenReferenceLink = 18
|
||||||
|
TokenReferenceDefinition = 19
|
||||||
|
TokenImage = 20
|
||||||
|
TokenHtmlComment = 21
|
||||||
|
TokenNumberedList = 22
|
||||||
|
TokenBulletPointList = 23
|
||||||
|
TokenHorizontalRule = 24
|
||||||
|
TokenLineBreak = 25
|
||||||
|
TokenBlockquote = 26
|
||||||
|
TokenCodeBlock = 27
|
||||||
|
TokenGithubCodeFence = 28
|
||||||
|
TokenPandocCodeFence = 29
|
||||||
|
TokenCodeFenceEnd = 30
|
||||||
|
TokenMention = 31
|
||||||
|
TokenTableHeader = 32
|
||||||
|
TokenTableDivider = 33
|
||||||
|
TokenTablePipe = 34
|
||||||
|
TokenSuperScript = 35
|
||||||
|
TokenSubScript = 36
|
||||||
|
# CriticMarkup
|
||||||
|
TokenCMAddition = 37 # {++ ++}
|
||||||
|
TokenCMDeletion = 38 # {-- --}
|
||||||
|
TokenCMSubstitution = 39 #{~~ ~> ~~}
|
||||||
|
TokenCMComment = 40 # {>> <<}
|
||||||
|
TokenCMHighlight = 41 # {== ==}{>> <<}
|
||||||
|
TokenLast = 42
|
||||||
|
|
||||||
|
TITLES = [TokenAtxHeading1, TokenAtxHeading2, TokenAtxHeading3,
|
||||||
|
TokenAtxHeading4, TokenAtxHeading5, TokenAtxHeading6,
|
||||||
|
TokenSetextHeading1Line1, TokenSetextHeading1Line2,
|
||||||
|
TokenSetextHeading2Line1, TokenSetextHeading2Line2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BlockquoteStyle:
|
||||||
|
BlockquoteStylePlain = 0
|
||||||
|
BlockquoteStyleItalic = 1
|
||||||
|
BlockquoteStyleFancy = 2
|
665
manuskript/ui/highlighters/markdownHighlighter.py
Normal file
665
manuskript/ui/highlighters/markdownHighlighter.py
Normal file
|
@ -0,0 +1,665 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
A QSyntaxHighlighter for markdown, using tokenizer. More accurate than simple
|
||||||
|
regexp, but not yet perfect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from PyQt5.QtCore import Qt, pyqtSignal, qWarning, QRegExp
|
||||||
|
from PyQt5.QtGui import (QSyntaxHighlighter, QTextBlock, QColor, QFont,
|
||||||
|
QTextCharFormat, QBrush, QPalette)
|
||||||
|
from PyQt5.QtWidgets import qApp, QStyle
|
||||||
|
|
||||||
|
from manuskript.ui.highlighters import BasicHighlighter
|
||||||
|
from manuskript.ui.highlighters import MarkdownTokenizer
|
||||||
|
from manuskript.ui.highlighters import MarkdownState as MS
|
||||||
|
from manuskript.ui.highlighters import MarkdownTokenType as MTT
|
||||||
|
from manuskript.ui.highlighters import BlockquoteStyle as BS
|
||||||
|
from manuskript.ui import style as S
|
||||||
|
from manuskript import settings
|
||||||
|
from manuskript import functions as F
|
||||||
|
|
||||||
|
# Un longue ligne. Un longue ligne. Un longue ligne. Un longue ligne.asdasdasda
|
||||||
|
|
||||||
|
GW_FADE_ALPHA = 140
|
||||||
|
|
||||||
|
# Highlighter based on GhostWriter (http://wereturtle.github.io/ghostwriter/).
|
||||||
|
# GPLV3+.
|
||||||
|
|
||||||
|
#FIXME: Setext heading don't work anymore
|
||||||
|
|
||||||
|
class MarkdownHighlighter(BasicHighlighter):
|
||||||
|
|
||||||
|
highlightBlockAtPosition = pyqtSignal(int)
|
||||||
|
headingFound = pyqtSignal(int, str, QTextBlock)
|
||||||
|
headingRemoved = pyqtSignal(int)
|
||||||
|
|
||||||
|
def __init__(self, editor):
|
||||||
|
BasicHighlighter.__init__(self, editor)
|
||||||
|
|
||||||
|
#default values
|
||||||
|
self.editor = editor
|
||||||
|
self.tokenizer = MarkdownTokenizer()
|
||||||
|
|
||||||
|
self.spellCheckEnabled = False
|
||||||
|
#self.typingPaused = True
|
||||||
|
self.inBlockquote = False
|
||||||
|
self.blockquoteStyle = BS.BlockquoteStyleFancy
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
self.useUndlerlineForEmphasis = False
|
||||||
|
self.highlightLineBreaks = True
|
||||||
|
|
||||||
|
self.highlightBlockAtPosition.connect(self.onHighlightBlockAtPosition,
|
||||||
|
Qt.QueuedConnection)
|
||||||
|
|
||||||
|
self.theme = self.defaultTheme()
|
||||||
|
self.setupHeadingFontSize(True)
|
||||||
|
|
||||||
|
self.highlightedWords = []
|
||||||
|
self.highlightedTags = []
|
||||||
|
self.searchExpression = ""
|
||||||
|
self.searchExpressionRegExp = False
|
||||||
|
self.searchExpressionCase = False
|
||||||
|
|
||||||
|
#f = self.document().defaultFont()
|
||||||
|
#f.setFamily("monospace")
|
||||||
|
#self.document().setDefaultFont(f)
|
||||||
|
|
||||||
|
def doHighlightBlock(self, text):
|
||||||
|
"""
|
||||||
|
Note: Never set the QTextBlockFormat for a QTextBlock from within
|
||||||
|
the highlighter. Depending on how the block format is modified,
|
||||||
|
a recursive call to the highlighter may be triggered, which will
|
||||||
|
cause the application to crash.
|
||||||
|
|
||||||
|
Likewise, don't try to set the QTextBlockFormat outside the highlighter
|
||||||
|
(i.e., from within the text editor). While the application will not
|
||||||
|
crash, the format change will be added to the undo stack. Attempting
|
||||||
|
to undo from that point on will cause the undo stack to be virtually
|
||||||
|
frozen, since undoing the format operation causes the text to be
|
||||||
|
considered changed, thus triggering the slot that changes the text
|
||||||
|
formatting to be triggered yet again.
|
||||||
|
"""
|
||||||
|
|
||||||
|
lastState = self.currentBlockState()
|
||||||
|
self.setFormat(0, len(text), self._defaultCharFormat)
|
||||||
|
|
||||||
|
if self.tokenizer != None:
|
||||||
|
self.tokenizer.clear()
|
||||||
|
block = self.currentBlock()
|
||||||
|
nextState = MS.MarkdownStateUnknown
|
||||||
|
previousState = self.previousBlockState()
|
||||||
|
|
||||||
|
if block.next().isValid():
|
||||||
|
nextState = block.next().userState()
|
||||||
|
|
||||||
|
self.tokenizer.tokenize(text, lastState, previousState, nextState)
|
||||||
|
self.setCurrentBlockState(self.tokenizer.getState())
|
||||||
|
|
||||||
|
self.inBlockquote = self.tokenizer.getState() == MS.MarkdownStateBlockquote
|
||||||
|
|
||||||
|
# STATE FORMATTING
|
||||||
|
# FIXME: generic
|
||||||
|
if self.currentBlockState() in [
|
||||||
|
MS.MarkdownStatePipeTableHeader,
|
||||||
|
MS.MarkdownStatePipeTableDivider,
|
||||||
|
MS.MarkdownStatePipeTableRow]:
|
||||||
|
fmt = QTextCharFormat()
|
||||||
|
f = fmt.font()
|
||||||
|
f.setFamily("Monospace")
|
||||||
|
fmt.setFont(f)
|
||||||
|
self.setFormat(0, len(text), fmt)
|
||||||
|
|
||||||
|
# Monospace the blank chars
|
||||||
|
i = 0
|
||||||
|
while i <= len(text)-1 and text[i] in [" ", "\t"]:
|
||||||
|
fmt = self.format(i)
|
||||||
|
fmt.setFontFamily("Monospace")
|
||||||
|
self.setFormat(i, 1, fmt)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
#if self.currentBlockState() == MS.MarkdownStateBlockquote:
|
||||||
|
#fmt = QTextCharFormat(self._defaultCharFormat)
|
||||||
|
#fmt.setForeground(Qt.lightGray)
|
||||||
|
#self.setFormat(0, len(text), fmt)
|
||||||
|
|
||||||
|
tokens = self.tokenizer.getTokens()
|
||||||
|
|
||||||
|
for token in tokens:
|
||||||
|
if token.type == MTT.TokenUnknown:
|
||||||
|
qWarning("Highlighter found unknown token type in text block.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token.type in [
|
||||||
|
MTT.TokenAtxHeading1,
|
||||||
|
MTT.TokenAtxHeading2,
|
||||||
|
MTT.TokenAtxHeading3,
|
||||||
|
MTT.TokenAtxHeading4,
|
||||||
|
MTT.TokenAtxHeading5,
|
||||||
|
MTT.TokenAtxHeading6,
|
||||||
|
MTT.TokenSetextHeading1Line1,
|
||||||
|
MTT.TokenSetextHeading2Line1,
|
||||||
|
]:
|
||||||
|
self.storeHeadingData(token, text)
|
||||||
|
|
||||||
|
self.applyFormattingForToken(token, text)
|
||||||
|
|
||||||
|
if self.tokenizer.backtrackRequested():
|
||||||
|
previous = self.currentBlock().previous()
|
||||||
|
self.highlightBlockAtPosition.emit(previous.position())
|
||||||
|
|
||||||
|
if self.spellCheckEnabled:
|
||||||
|
self.spellCheck(text)
|
||||||
|
|
||||||
|
# If the block has transitioned from previously being a heading to now
|
||||||
|
# being a non-heading, signal that the position in the document no
|
||||||
|
# longer contains a heading.
|
||||||
|
|
||||||
|
if self.isHeadingBlockState(lastState) and \
|
||||||
|
not self.isHeadingBlockState(self.currentBlockState()):
|
||||||
|
self.headingRemoved.emit(self.currentBlock().position())
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# COLORS & FORMATTING
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
def updateColorScheme(self, rehighlight=True):
|
||||||
|
BasicHighlighter.updateColorScheme(self, rehighlight)
|
||||||
|
self.theme = self.defaultTheme()
|
||||||
|
self.setEnableLargeHeadingSizes(True)
|
||||||
|
|
||||||
|
def defaultTheme(self):
|
||||||
|
|
||||||
|
# Base Colors
|
||||||
|
background = self.backgroundColor
|
||||||
|
text = self.defaultTextColor
|
||||||
|
highlightedText = QColor(S.highlightedText)
|
||||||
|
highlightedTextDark = QColor(S.highlightedTextDark)
|
||||||
|
highlightedTextLight = QColor(S.highlightedTextLight)
|
||||||
|
highlight = QColor(S.highlight)
|
||||||
|
link = self.linkColor
|
||||||
|
linkVisited = QColor(S.linkVisited)
|
||||||
|
|
||||||
|
# titleColor = highlight
|
||||||
|
titleColor = QColor(S.highlightedTextDark)
|
||||||
|
|
||||||
|
# FullscreenEditor probably
|
||||||
|
if self.editor._fromTheme and self.editor._themeData:
|
||||||
|
text = QColor(self.editor._themeData["Text/Color"])
|
||||||
|
background = QColor(self.editor._themeData["Background/Color"])
|
||||||
|
titleColor = text
|
||||||
|
|
||||||
|
# Compositions
|
||||||
|
light = F.mixColors(text, background, .75)
|
||||||
|
markup = F.mixColors(text, background, .5)
|
||||||
|
veryLight = F.mixColors(text, background, .25)
|
||||||
|
listToken = F.mixColors(highlight, background, .4)
|
||||||
|
titleMarkupColor = F.mixColors(titleColor, background, .3)
|
||||||
|
|
||||||
|
|
||||||
|
theme = {
|
||||||
|
"markup": markup}
|
||||||
|
|
||||||
|
#Exemple:
|
||||||
|
#"color": Qt.red,
|
||||||
|
#"deltaSize": 10,
|
||||||
|
#"background": Qt.yellow,
|
||||||
|
#"monospace": True,
|
||||||
|
#"bold": True,
|
||||||
|
#"italic": True,
|
||||||
|
#"underline": True,
|
||||||
|
#"overline": True,
|
||||||
|
#"strike": True,
|
||||||
|
#"formatMarkup": True,
|
||||||
|
#"markupBold": True,
|
||||||
|
#"markupColor": Qt.blue,
|
||||||
|
#"markupBackground": Qt.green,
|
||||||
|
#"markupMonospace": True,
|
||||||
|
#"super":True,
|
||||||
|
#"sub":True
|
||||||
|
|
||||||
|
for i in MTT.TITLES:
|
||||||
|
theme[i] = {
|
||||||
|
"formatMarkup":True,
|
||||||
|
"bold": True,
|
||||||
|
# "monospace": True,
|
||||||
|
"markupColor": titleMarkupColor
|
||||||
|
}
|
||||||
|
|
||||||
|
theme[MTT.TokenAtxHeading1]["color"] = titleColor
|
||||||
|
theme[MTT.TokenAtxHeading2]["color"] = F.mixColors(titleColor,
|
||||||
|
background, .9)
|
||||||
|
theme[MTT.TokenAtxHeading3]["color"] = F.mixColors(titleColor,
|
||||||
|
background, .8)
|
||||||
|
theme[MTT.TokenAtxHeading4]["color"] = F.mixColors(titleColor,
|
||||||
|
background, .7)
|
||||||
|
theme[MTT.TokenAtxHeading5]["color"] = F.mixColors(titleColor,
|
||||||
|
background, .6)
|
||||||
|
theme[MTT.TokenAtxHeading6]["color"] = F.mixColors(titleColor,
|
||||||
|
background, .5)
|
||||||
|
|
||||||
|
theme[MTT.TokenSetextHeading1Line1]["color"] = titleColor
|
||||||
|
theme[MTT.TokenSetextHeading2Line1]["color"] = F.mixColors(titleColor,
|
||||||
|
background,
|
||||||
|
.9)
|
||||||
|
|
||||||
|
for i in [MTT.TokenSetextHeading1Line1, MTT.TokenSetextHeading2Line1]:
|
||||||
|
theme[i]["monospace"] = True
|
||||||
|
|
||||||
|
for i in [MTT.TokenSetextHeading1Line2, MTT.TokenSetextHeading2Line2]:
|
||||||
|
theme[i] = {
|
||||||
|
"color": titleMarkupColor,
|
||||||
|
"monospace":True}
|
||||||
|
|
||||||
|
# Beautifiers
|
||||||
|
theme[MTT.TokenEmphasis] = {
|
||||||
|
"italic":True}
|
||||||
|
theme[MTT.TokenStrong] = {
|
||||||
|
"bold":True}
|
||||||
|
theme[MTT.TokenStrikethrough] = {
|
||||||
|
"strike":True}
|
||||||
|
theme[MTT.TokenVerbatim] = {
|
||||||
|
"monospace":True,
|
||||||
|
"background": veryLight,
|
||||||
|
"formatMarkup": True,
|
||||||
|
"markupColor": markup,
|
||||||
|
"deltaSize": -1}
|
||||||
|
theme[MTT.TokenSuperScript] = {
|
||||||
|
"super":True,
|
||||||
|
"formatMarkup":True}
|
||||||
|
theme[MTT.TokenSubScript] = {
|
||||||
|
"sub":True,
|
||||||
|
"formatMarkup":True}
|
||||||
|
theme[MTT.TokenHtmlTag] = {
|
||||||
|
"color": linkVisited}
|
||||||
|
theme[MTT.TokenHtmlEntity] = { #
|
||||||
|
"color": linkVisited}
|
||||||
|
theme[MTT.TokenAutomaticLink] = {
|
||||||
|
"color": link}
|
||||||
|
theme[MTT.TokenInlineLink] = {
|
||||||
|
"color": link}
|
||||||
|
theme[MTT.TokenReferenceLink] = {
|
||||||
|
"color": link}
|
||||||
|
theme[MTT.TokenReferenceDefinition] = {
|
||||||
|
"color": link}
|
||||||
|
theme[MTT.TokenImage] = {
|
||||||
|
"color": highlightedTextDark}
|
||||||
|
theme[MTT.TokenHtmlComment] = {
|
||||||
|
"color": markup}
|
||||||
|
theme[MTT.TokenNumberedList] = {
|
||||||
|
"markupColor": listToken,
|
||||||
|
"markupBold": True,
|
||||||
|
"markupMonospace": True,}
|
||||||
|
theme[MTT.TokenBulletPointList] = {
|
||||||
|
"markupColor": listToken,
|
||||||
|
"markupBold": True,
|
||||||
|
"markupMonospace": True,}
|
||||||
|
theme[MTT.TokenHorizontalRule] = {
|
||||||
|
"overline": True,
|
||||||
|
"underline": True,
|
||||||
|
"monospace": True,
|
||||||
|
"color": markup}
|
||||||
|
theme[MTT.TokenLineBreak] = {
|
||||||
|
"background": markup}
|
||||||
|
theme[MTT.TokenBlockquote] = {
|
||||||
|
"color": light,
|
||||||
|
"markupColor": veryLight,
|
||||||
|
"markupBackground": veryLight}
|
||||||
|
theme[MTT.TokenCodeBlock] = {
|
||||||
|
"color": light,
|
||||||
|
"markupBackground": veryLight,
|
||||||
|
"formatMarkup": True,
|
||||||
|
"monospace":True,
|
||||||
|
"deltaSize":-1}
|
||||||
|
theme[MTT.TokenGithubCodeFence] = {
|
||||||
|
"color": markup}
|
||||||
|
theme[MTT.TokenPandocCodeFence] = {
|
||||||
|
"color": markup}
|
||||||
|
theme[MTT.TokenCodeFenceEnd] = {
|
||||||
|
"color": markup}
|
||||||
|
theme[MTT.TokenMention] = {} # FIXME
|
||||||
|
theme[MTT.TokenTableHeader] = {
|
||||||
|
"color": light, "monospace":True}
|
||||||
|
theme[MTT.TokenTableDivider] = {
|
||||||
|
"color": markup, "monospace":True}
|
||||||
|
theme[MTT.TokenTablePipe] = {
|
||||||
|
"color": markup, "monospace":True}
|
||||||
|
|
||||||
|
# CriticMarkup
|
||||||
|
theme[MTT.TokenCMAddition] = {
|
||||||
|
"color": QColor("#00bb00"),
|
||||||
|
"markupColor": QColor(F.mixColors("#00bb00", background, .4)),
|
||||||
|
"markupMonospace": True,}
|
||||||
|
theme[MTT.TokenCMDeletion] = {
|
||||||
|
"color": QColor("#dd0000"),
|
||||||
|
"markupColor": QColor(F.mixColors("#dd0000", background, .4)),
|
||||||
|
"markupMonospace": True,
|
||||||
|
"strike": True}
|
||||||
|
theme[MTT.TokenCMSubstitution] = {
|
||||||
|
"color": QColor("#ff8600"),
|
||||||
|
"markupColor": QColor(F.mixColors("#ff8600", background, .4)),
|
||||||
|
"markupMonospace": True,}
|
||||||
|
theme[MTT.TokenCMComment] = {
|
||||||
|
"color": QColor("#0000bb"),
|
||||||
|
"markupColor": QColor(F.mixColors("#0000bb", background, .4)),
|
||||||
|
"markupMonospace": True,}
|
||||||
|
theme[MTT.TokenCMHighlight] = {
|
||||||
|
"color": QColor("#aa53a9"),
|
||||||
|
"background": QColor(F.mixColors("#aa53a9", background, .1)),
|
||||||
|
"markupBackground": QColor(F.mixColors("#aa53a9", background, .1)),
|
||||||
|
"markupColor": QColor(F.mixColors("#aa53a9", background, .5)),
|
||||||
|
"markupMonospace": True,}
|
||||||
|
|
||||||
|
return theme
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# ACTUAL FORMATTING
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
def applyFormattingForToken(self, token, text):
|
||||||
|
if token.type != MTT.TokenUnknown:
|
||||||
|
fmt = self.format(token.position + token.openingMarkupLength)
|
||||||
|
markupFormat = self.format(token.position)
|
||||||
|
if self.theme.get("markup"):
|
||||||
|
markupFormat.setForeground(self.theme["markup"])
|
||||||
|
|
||||||
|
## Debug
|
||||||
|
def debug():
|
||||||
|
print("{}\n{}{}{}{} (state:{})".format(
|
||||||
|
text,
|
||||||
|
" "*token.position,
|
||||||
|
"^"*token.openingMarkupLength,
|
||||||
|
str(token.type).center(token.length
|
||||||
|
- token.openingMarkupLength
|
||||||
|
- token.closingMarkupLength, "-"),
|
||||||
|
"^" * token.closingMarkupLength,
|
||||||
|
self.currentBlockState(),)
|
||||||
|
)
|
||||||
|
|
||||||
|
# if token.type in range(6, 10):
|
||||||
|
# debug()
|
||||||
|
|
||||||
|
theme = self.theme.get(token.type)
|
||||||
|
if theme:
|
||||||
|
fmt, markupFormat = self.formatsFromTheme(theme,
|
||||||
|
fmt,
|
||||||
|
markupFormat)
|
||||||
|
|
||||||
|
# Format openning Markup
|
||||||
|
self.setFormat(token.position, token.openingMarkupLength,
|
||||||
|
markupFormat)
|
||||||
|
|
||||||
|
# Format Text
|
||||||
|
self.setFormat(
|
||||||
|
token.position + token.openingMarkupLength,
|
||||||
|
token.length - token.openingMarkupLength - token.closingMarkupLength,
|
||||||
|
fmt)
|
||||||
|
|
||||||
|
# Format closing Markup
|
||||||
|
if token.closingMarkupLength > 0:
|
||||||
|
self.setFormat(
|
||||||
|
token.position + token.length - token.closingMarkupLength,
|
||||||
|
token.closingMarkupLength,
|
||||||
|
markupFormat)
|
||||||
|
|
||||||
|
else:
|
||||||
|
qWarning("MarkdownHighlighter.applyFormattingForToken() was passed"
|
||||||
|
" in a token of unknown type.")
|
||||||
|
|
||||||
|
def formatsFromTheme(self, theme, format=None,
|
||||||
|
markupFormat=QTextCharFormat()):
|
||||||
|
# Token
|
||||||
|
if theme.get("color"):
|
||||||
|
format.setForeground(theme["color"])
|
||||||
|
if theme.get("deltaSize"):
|
||||||
|
size = self.editor._defaultFontSize + theme["deltaSize"]
|
||||||
|
if size >= 0:
|
||||||
|
f = format.font()
|
||||||
|
f.setPointSize(size)
|
||||||
|
format.setFont(f)
|
||||||
|
if theme.get("background"):
|
||||||
|
format.setBackground(theme["background"])
|
||||||
|
if theme.get("monospace"):
|
||||||
|
format.setFontFamily("Monospace")
|
||||||
|
if theme.get("bold"):
|
||||||
|
format.setFontWeight(QFont.Bold)
|
||||||
|
if theme.get("italic"):
|
||||||
|
format.setFontItalic(theme["italic"])
|
||||||
|
if theme.get("underline"):
|
||||||
|
format.setFontUnderline(theme["underline"])
|
||||||
|
if theme.get("overline"):
|
||||||
|
format.setFontOverline(theme["overline"])
|
||||||
|
if theme.get("strike"):
|
||||||
|
format.setFontStrikeOut(theme["strike"])
|
||||||
|
if theme.get("super"):
|
||||||
|
format.setVerticalAlignment(QTextCharFormat.AlignSuperScript)
|
||||||
|
if theme.get("sub"):
|
||||||
|
format.setVerticalAlignment(QTextCharFormat.AlignSubScript)
|
||||||
|
|
||||||
|
# Markup
|
||||||
|
if theme.get("formatMarkup"):
|
||||||
|
c = markupFormat.foreground()
|
||||||
|
markupFormat = QTextCharFormat(format)
|
||||||
|
markupFormat.setForeground(c)
|
||||||
|
if theme.get("markupBold"):
|
||||||
|
markupFormat.setFontWeight(QFont.Bold)
|
||||||
|
if theme.get("markupColor"):
|
||||||
|
markupFormat.setForeground(theme["markupColor"])
|
||||||
|
if theme.get("markupBackground"):
|
||||||
|
markupFormat.setBackground(theme["markupBackground"])
|
||||||
|
if theme.get("markupMonospace"):
|
||||||
|
markupFormat.setFontFamily("Monospace")
|
||||||
|
|
||||||
|
return format, markupFormat
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# SETTINGS
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
def setHighlighted(self, words, tags):
|
||||||
|
rehighlight = (self.highlightedWords != words
|
||||||
|
or self.highlightedTags != tags)
|
||||||
|
self.highlightedWords = words
|
||||||
|
self.highlightedTags = tags
|
||||||
|
if rehighlight:
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setSearched(self, expression, regExp=False, caseSensitivity=False):
|
||||||
|
"""
|
||||||
|
Define an expression currently searched, to be highlighted.
|
||||||
|
Can be regExp.
|
||||||
|
"""
|
||||||
|
rehighlight = self.searchExpression != expression or \
|
||||||
|
self.searchExpressionRegExp != regExp or \
|
||||||
|
self.searchExpressionCase != caseSensitivity
|
||||||
|
self.searchExpression = expression
|
||||||
|
self.searchExpressionRegExp = regExp
|
||||||
|
self.searchExpressionCase = caseSensitivity
|
||||||
|
if rehighlight:
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setDictionary(self, dictionary):
|
||||||
|
self.dictionary = dictionary
|
||||||
|
if self.spellCheckEnabled:
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def increaseFontSize(self):
|
||||||
|
self._defaultCharFormat.setFontPointSize(
|
||||||
|
self._defaultCharFormat.font().pointSize() + 1.0)
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def decreaseFontSize(self):
|
||||||
|
self._defaultCharFormat.setFontPointSize(
|
||||||
|
self._defaultCharFormat.font().pointSize() - 1.0)
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setEnableLargeHeadingSizes(self, enable):
|
||||||
|
self.setupHeadingFontSize(enable)
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setupHeadingFontSize(self, useLargeHeadings):
|
||||||
|
if useLargeHeadings:
|
||||||
|
self.theme[MTT.TokenSetextHeading1Line1]["deltaSize"] = 7
|
||||||
|
self.theme[MTT.TokenSetextHeading2Line1]["deltaSize"] = 5
|
||||||
|
self.theme[MTT.TokenSetextHeading1Line2]["deltaSize"] = 7
|
||||||
|
self.theme[MTT.TokenSetextHeading2Line2]["deltaSize"] = 5
|
||||||
|
self.theme[MTT.TokenAtxHeading1]["deltaSize"] = 7
|
||||||
|
self.theme[MTT.TokenAtxHeading2]["deltaSize"] = 5
|
||||||
|
self.theme[MTT.TokenAtxHeading3]["deltaSize"] = 3
|
||||||
|
self.theme[MTT.TokenAtxHeading4]["deltaSize"] = 2
|
||||||
|
self.theme[MTT.TokenAtxHeading5]["deltaSize"] = 1
|
||||||
|
self.theme[MTT.TokenAtxHeading6]["deltaSize"] = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i in MTT.TITLES:
|
||||||
|
self.theme[i]["deltaSize"] = 0
|
||||||
|
|
||||||
|
def setUseUnderlineForEmphasis(self, enable):
|
||||||
|
self.useUndlerlineForEmphasis = enable
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setFont(self, fontFamily, fontSize):
|
||||||
|
font = QFont(family=fontFamily, pointSize=fontSize,
|
||||||
|
weight=QFont.Normal, italic=False)
|
||||||
|
self._defaultCharFormat.setFont(font)
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setSpellCheckEnabled(self, enabled):
|
||||||
|
self.spellCheckEnabled = enabled
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setBlockquoteStyle(self, style):
|
||||||
|
self.blockquoteStyle = style
|
||||||
|
|
||||||
|
if style == BS.BlockquoteStyleItalic:
|
||||||
|
self.emphasizeToken[MTT.TokenBlockquote] = True
|
||||||
|
else:
|
||||||
|
self.emphasizeToken[MTT.TokenBlockquote] = False
|
||||||
|
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
def setHighlightLineBreaks(self, enable):
|
||||||
|
self.highlightLineBreaks = enable
|
||||||
|
self.rehighlight()
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# GHOSTWRITER SPECIFIC?
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
def onTypingResumed(self):
|
||||||
|
self.typingPaused = False
|
||||||
|
|
||||||
|
def onTypingPaused(self):
|
||||||
|
self.typingPaused = True
|
||||||
|
block = self.document().findBlock(self.editor.textCursor().position())
|
||||||
|
self.rehighlightBlock(block)
|
||||||
|
|
||||||
|
def onHighlightBlockAtPosition(self, position):
|
||||||
|
block = self.document().findBlock(position)
|
||||||
|
self.rehighlightBlock(block)
|
||||||
|
|
||||||
|
def onTextBlockRemoved(self, block):
|
||||||
|
if self.isHeadingBlockState(block.userState):
|
||||||
|
self.headingRemoved.emit(block.position())
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# SPELLCHECK
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
def spellCheck(self, text):
|
||||||
|
cursorPosition = self.editor.textCursor().position()
|
||||||
|
cursorPosBlock = self.document().findBlock(cursorPosition)
|
||||||
|
cursorPosInBlock = -1
|
||||||
|
|
||||||
|
if self.currentBlock() == cursorPosBlock:
|
||||||
|
cursorPosInBlock = cursorPosition - cursorPosBlock.position()
|
||||||
|
|
||||||
|
misspelledWord = self.dictionary.check(text, 0)
|
||||||
|
|
||||||
|
while not misspelledWord.isNull():
|
||||||
|
startIndex = misspelledWord.position()
|
||||||
|
length = misspelledWord.length()
|
||||||
|
|
||||||
|
if self.typingPaused or cursorPosInBlock != startIndex + length:
|
||||||
|
spellingErrorFormat = self.format(startIndex)
|
||||||
|
spellingErrorFormat.setUnderlineColor(self.spellingErrorColor)
|
||||||
|
spellingErrorFormat.setUnderlineStyle(
|
||||||
|
qApp.stlye().styleHint(QStyle.SH_SpellCheckUnderlineStyle))
|
||||||
|
|
||||||
|
self.setFormat(startIndex, length, spellingErrorFormat)
|
||||||
|
|
||||||
|
startIndex += length
|
||||||
|
misspelledWord = self.dictionary.check(text, startIndex)
|
||||||
|
|
||||||
|
def storeHeadingData(self, token, text):
|
||||||
|
if token.type in [
|
||||||
|
MTT.TokenAtxHeading1,
|
||||||
|
MTT.TokenAtxHeading2,
|
||||||
|
MTT.TokenAtxHeading3,
|
||||||
|
MTT.TokenAtxHeading4,
|
||||||
|
MTT.TokenAtxHeading5,
|
||||||
|
MTT.TokenAtxHeading6]:
|
||||||
|
level = token.type - MTT.TokenAtxHeading1 + 1
|
||||||
|
s = token.position + token.openingMarkupLength
|
||||||
|
l = (token.length
|
||||||
|
- token.openingMarkupLength
|
||||||
|
- token.closingMarkupLength)
|
||||||
|
headingText = text[s:s+l].strip()
|
||||||
|
|
||||||
|
elif token.type == MTT.TokenSetextHeading1Line1:
|
||||||
|
level = 1
|
||||||
|
headingText = text
|
||||||
|
|
||||||
|
elif token.type == MTT.TokenSetextHeading2Line1:
|
||||||
|
level = 2
|
||||||
|
headingText = text
|
||||||
|
|
||||||
|
else:
|
||||||
|
qWarning("MarkdownHighlighter.storeHeadingData() encountered" +
|
||||||
|
" unexpected token: {}".format(token.getType()))
|
||||||
|
return
|
||||||
|
|
||||||
|
# FIXME: TypeError: could not convert 'TextBlockData' to 'QTextBlockUserData'
|
||||||
|
# blockData = self.currentBlockUserData()
|
||||||
|
# if blockData is None:
|
||||||
|
# blockData = TextBlockData(self.document(), self.currentBlock())
|
||||||
|
#
|
||||||
|
# self.setCurrentBlockUserData(blockData)
|
||||||
|
self.headingFound.emit(level, headingText, self.currentBlock())
|
||||||
|
|
||||||
|
def isHeadingBlockState(self, state):
|
||||||
|
return state in [
|
||||||
|
MS.MarkdownStateAtxHeading1,
|
||||||
|
MS.MarkdownStateAtxHeading2,
|
||||||
|
MS.MarkdownStateAtxHeading3,
|
||||||
|
MS.MarkdownStateAtxHeading4,
|
||||||
|
MS.MarkdownStateAtxHeading5,
|
||||||
|
MS.MarkdownStateAtxHeading6,
|
||||||
|
MS.MarkdownStateSetextHeading1Line1,
|
||||||
|
MS.MarkdownStateSetextHeading2Line1,]
|
||||||
|
|
||||||
|
|
||||||
|
def getLuminance(color):
|
||||||
|
return (0.30 * color.redF()) + \
|
||||||
|
(0.59 * color.greenF()) + \
|
||||||
|
(0.11 * color.blueF())
|
||||||
|
|
||||||
|
|
||||||
|
def applyAlphaToChannel(foreground, background, alpha):
|
||||||
|
return (foreground * alpha) + (background * (1.0 - alpha))
|
||||||
|
|
||||||
|
|
||||||
|
def applyAlpha(foreground, background, alpha):
|
||||||
|
blendedColor = QColor(0, 0, 0)
|
||||||
|
normalizedAlpha = alpha / 255.0
|
||||||
|
blendedColor.setRed(applyAlphaToChannel(
|
||||||
|
foreground.red(), background.red(), normalizedAlpha))
|
||||||
|
blendedColor.setGreen(applyAlphaToChannel(
|
||||||
|
foreground.green(), background.green(), normalizedAlpha))
|
||||||
|
blendedColor.setBlue(applyAlphaToChannel(
|
||||||
|
foreground.blue(), background.blue(), normalizedAlpha))
|
||||||
|
return blendedColor
|
902
manuskript/ui/highlighters/markdownTokenizer.py
Normal file
902
manuskript/ui/highlighters/markdownTokenizer.py
Normal file
|
@ -0,0 +1,902 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import re
|
||||||
|
from PyQt5.QtCore import *
|
||||||
|
from PyQt5.QtGui import *
|
||||||
|
from PyQt5.QtWidgets import *
|
||||||
|
|
||||||
|
from manuskript.ui.highlighters import MarkdownState as MS
|
||||||
|
from manuskript.ui.highlighters import MarkdownTokenType as MTT
|
||||||
|
|
||||||
|
# This file is simply a python translation of GhostWriter's Tokenizer.
|
||||||
|
# http://wereturtle.github.io/ghostwriter/
|
||||||
|
# GPLV3+.
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# TOKEN
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class Token:
|
||||||
|
def __init__(self):
|
||||||
|
self.type = -1
|
||||||
|
self.position = 0
|
||||||
|
self.length = 0
|
||||||
|
self.openingMarkupLength = 0
|
||||||
|
self.closingMarkupLength = 0
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# HIGHLIGHT TOKENIZER
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class HighlightTokenizer:
|
||||||
|
def __init__(self):
|
||||||
|
self.tokens = []
|
||||||
|
|
||||||
|
def tokenize(text, currentState, previousState, nextState):
|
||||||
|
# Subclass me
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def getTokens(self):
|
||||||
|
self.tokens = sorted(self.tokens, key=lambda t: t.position)
|
||||||
|
return self.tokens
|
||||||
|
|
||||||
|
def getState(self):
|
||||||
|
return self.state
|
||||||
|
|
||||||
|
def backtrackRequested(self):
|
||||||
|
return self.backtrack
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.tokens = []
|
||||||
|
self.backtrack = False
|
||||||
|
self.state = -1
|
||||||
|
|
||||||
|
def addToken(self, token):
|
||||||
|
self.tokens.append(token)
|
||||||
|
|
||||||
|
if token.type == -1:
|
||||||
|
print("Error here", token.position, token.length)
|
||||||
|
|
||||||
|
def setState(self, state):
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
def requestBacktrack(self):
|
||||||
|
self.backtrack = True
|
||||||
|
|
||||||
|
def tokenLessThan(self, t1, t2):
|
||||||
|
return t1.getPosition() < t2.getPosition()
|
||||||
|
|
||||||
|
|
||||||
|
class MarkdownTokenizer(HighlightTokenizer):
|
||||||
|
|
||||||
|
DUMMY_CHAR = "$"
|
||||||
|
MAX_MARKDOWN_HEADING_LEVEL = 6
|
||||||
|
|
||||||
|
paragraphBreakRegex = QRegExp("^\\s*$")
|
||||||
|
heading1SetextRegex = QRegExp("^===+\\s*$")
|
||||||
|
heading2SetextRegex = QRegExp("^---+\\s*$")
|
||||||
|
blockquoteRegex = QRegExp("^ {0,3}>.*$")
|
||||||
|
githubCodeFenceStartRegex = QRegExp("^```+.*$")
|
||||||
|
githubCodeFenceEndRegex = QRegExp("^```+\\s*$")
|
||||||
|
pandocCodeFenceStartRegex = QRegExp("^~~~+.*$")
|
||||||
|
pandocCodeFenceEndRegex = QRegExp("^~~~+\\s*$")
|
||||||
|
numberedListRegex = QRegExp("^ {0,3}[0-9a-z]+[.)]\\s+.*$")
|
||||||
|
numberedNestedListRegex = QRegExp("^\\s*[0-9a-z]+[.)]\\s+.*$")
|
||||||
|
hruleRegex = QRegExp("\\s*(\\*\\s*){3,}|(\\s*(_\\s*){3,})|((\\s*(-\\s*){3,}))")
|
||||||
|
lineBreakRegex = QRegExp(".*\\s{2,}$")
|
||||||
|
emphasisRegex = QRegExp("(\\*(?![\\s*]).*[^\\s*]\\*)|_(?![\\s_]).*[^\\s_]_")
|
||||||
|
emphasisRegex.setMinimal(True)
|
||||||
|
strongRegex = QRegExp("\\*\\*(?=\\S).*\\S\\*\\*(?!\\*)|__(?=\\S).*\\S__(?!_)")
|
||||||
|
strongRegex.setMinimal(True)
|
||||||
|
strikethroughRegex = QRegExp("~~[^\\s]+.*[^\\s]+~~")
|
||||||
|
strikethroughRegex.setMinimal(True)
|
||||||
|
superScriptRegex = QRegExp("\^([^\\s]|(\\\\\\s))+\^") # Spaces must be escaped "\ "
|
||||||
|
superScriptRegex.setMinimal(True)
|
||||||
|
subScriptRegex = QRegExp("~([^\\s]|(\\\\\\s))+~") # Spaces must be escaped "\ "
|
||||||
|
subScriptRegex.setMinimal(True)
|
||||||
|
verbatimRegex = QRegExp("`+")
|
||||||
|
htmlTagRegex = QRegExp("<[^<>]+>")
|
||||||
|
htmlTagRegex.setMinimal(True)
|
||||||
|
htmlEntityRegex = QRegExp("&[a-zA-Z]+;|&#x?[0-9]+;")
|
||||||
|
automaticLinkRegex = QRegExp("(<[a-zA-Z]+\\:.+>)|(<.+@.+>)")
|
||||||
|
automaticLinkRegex.setMinimal(True)
|
||||||
|
inlineLinkRegex = QRegExp("\\[.+\\]\\(.+\\)")
|
||||||
|
inlineLinkRegex.setMinimal(True)
|
||||||
|
referenceLinkRegex = QRegExp("\\[(.+)\\]")
|
||||||
|
referenceLinkRegex.setMinimal(True)
|
||||||
|
referenceDefinitionRegex = QRegExp("^\\s*\\[.+\\]:")
|
||||||
|
imageRegex = QRegExp("!\\[.*\\]\\(.+\\)")
|
||||||
|
imageRegex.setMinimal(True)
|
||||||
|
htmlInlineCommentRegex = QRegExp("<!--.*-->")
|
||||||
|
htmlInlineCommentRegex.setMinimal(True)
|
||||||
|
mentionRegex = QRegExp("\\B@\\w+(\\-\\w+)*(/\\w+(\\-\\w+)*)?")
|
||||||
|
pipeTableDividerRegex = QRegExp("^ {0,3}(\\|[ :]?)?-{3,}([ :]?\\|[ :]?-{3,}([ :]?\\|)?)+\\s*$")
|
||||||
|
CMAdditionRegex = QRegExp("(\\{\\+\\+.*\\+\\+\\})")
|
||||||
|
CMAdditionRegex.setMinimal(True)
|
||||||
|
CMDeletionRegex = QRegExp("(\\{--.*--\\})")
|
||||||
|
CMDeletionRegex.setMinimal(True)
|
||||||
|
CMSubstitutionRegex = QRegExp("(\\{~~.*~>.*~~\\})")
|
||||||
|
CMSubstitutionRegex.setMinimal(True)
|
||||||
|
CMCommentRegex = QRegExp("(\\{>>.*<<\\})")
|
||||||
|
CMCommentRegex.setMinimal(True)
|
||||||
|
CMHighlightRegex = QRegExp("(\\{==.*==\\})")
|
||||||
|
CMHighlightRegex.setMinimal(True)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
HighlightTokenizer.__init__(self)
|
||||||
|
|
||||||
|
def tokenize(self, text, currentState, previousState, nextState):
|
||||||
|
self.currentState = currentState
|
||||||
|
self.previousState = previousState
|
||||||
|
self.nextState = nextState
|
||||||
|
|
||||||
|
if (self.previousState == MS.MarkdownStateInGithubCodeFence or \
|
||||||
|
self.previousState == MS.MarkdownStateInPandocCodeFence) and \
|
||||||
|
self.tokenizeCodeBlock(text):
|
||||||
|
# No further tokenizing required
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif self.previousState != MS.MarkdownStateComment \
|
||||||
|
and self.paragraphBreakRegex.exactMatch(text):
|
||||||
|
|
||||||
|
if previousState in [MS.MarkdownStateListLineBreak,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList]:
|
||||||
|
self.setState(MS.MarkdownStateListLineBreak)
|
||||||
|
elif previousState != MS.MarkdownStateCodeBlock or \
|
||||||
|
(text[:1] != "\t" and text[-4:] != " "):
|
||||||
|
self.setState(MS.MarkdownStateParagraphBreak)
|
||||||
|
|
||||||
|
elif self.tokenizeSetextHeadingLine2(text) or \
|
||||||
|
self.tokenizeCodeBlock(text) or \
|
||||||
|
self.tokenizeMultilineComment(text) or \
|
||||||
|
self.tokenizeHorizontalRule(text) or \
|
||||||
|
self.tokenizeTableDivider(text):
|
||||||
|
# No further tokenizing required
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif self.tokenizeSetextHeadingLine1(text) or \
|
||||||
|
self.tokenizeAtxHeading(text) or \
|
||||||
|
self.tokenizeBlockquote(text) or \
|
||||||
|
self.tokenizeNumberedList(text) or \
|
||||||
|
self.tokenizeBulletPointList(text):
|
||||||
|
self.tokenizeLineBreak(text)
|
||||||
|
self.tokenizeInline(text)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if previousState in [MS.MarkdownStateListLineBreak,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateNumberedList]:
|
||||||
|
if not self.tokenizeNumberedList(text) and \
|
||||||
|
not self.tokenizeBulletPointList(text) and \
|
||||||
|
(text[:1] == "\t" or text[:4] == " "):
|
||||||
|
self.setState(previousState)
|
||||||
|
else:
|
||||||
|
self.setState(MS.MarkdownStateParagraph)
|
||||||
|
else:
|
||||||
|
self.setState(MS.MarkdownStateParagraph)
|
||||||
|
self.tokenizeLineBreak(text)
|
||||||
|
self.tokenizeInline(text)
|
||||||
|
|
||||||
|
# Make sure that if the second line of a setext heading is removed the
|
||||||
|
# first line is reprocessed. Otherwise, it will still show up in the
|
||||||
|
# document as a heading.
|
||||||
|
if (previousState == MS.MarkdownStateSetextHeading1Line1 and \
|
||||||
|
self.getState() != MS.MarkdownStateSetextHeading1Line2) or \
|
||||||
|
(previousState == MS.MarkdownStateSetextHeading2Line1 and \
|
||||||
|
self.getState() != MS.MarkdownStateSetextHeading2Line2):
|
||||||
|
self.requestBacktrack()
|
||||||
|
|
||||||
|
def tokenizeSetextHeadingLine1(self, text):
|
||||||
|
#Check the next line's state to see if this is a setext-style heading.
|
||||||
|
level = 0
|
||||||
|
token = Token()
|
||||||
|
nextState = self.nextState
|
||||||
|
|
||||||
|
if MS.MarkdownStateSetextHeading1Line2 == nextState:
|
||||||
|
level = 1
|
||||||
|
self.setState(MS.MarkdownStateSetextHeading1Line1)
|
||||||
|
token.type = MTT.TokenSetextHeading1Line1
|
||||||
|
|
||||||
|
elif MS.MarkdownStateSetextHeading2Line2 == nextState:
|
||||||
|
level = 2
|
||||||
|
self.setState(MS.MarkdownStateSetextHeading2Line1)
|
||||||
|
token.type = MTT.TokenSetextHeading2Line1
|
||||||
|
|
||||||
|
if level > 0:
|
||||||
|
token.length = len(text)
|
||||||
|
token.position = 0
|
||||||
|
self.addToken(token)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeSetextHeadingLine2(self, text):
|
||||||
|
level = 0
|
||||||
|
setextMatch = False
|
||||||
|
token = Token()
|
||||||
|
previousState = self.previousState
|
||||||
|
if previousState == MS.MarkdownStateSetextHeading1Line1:
|
||||||
|
level = 1
|
||||||
|
setextMatch = self.heading1SetextRegex.exactMatch(text)
|
||||||
|
self.setState(MS.MarkdownStateSetextHeading1Line2)
|
||||||
|
token.type = MTT.TokenSetextHeading1Line2
|
||||||
|
|
||||||
|
elif previousState == MS.MarkdownStateSetextHeading2Line1:
|
||||||
|
level = 2
|
||||||
|
setextMatch = self.heading2SetextRegex.exactMatch(text)
|
||||||
|
self.setState(MS.MarkdownStateSetextHeading2Line2)
|
||||||
|
token.type = MTT.TokenSetextHeading2Line2
|
||||||
|
|
||||||
|
elif previousState == MS.MarkdownStateParagraph:
|
||||||
|
h1Line2 = self.heading1SetextRegex.exactMatch(text)
|
||||||
|
h2Line2 = self.heading2SetextRegex.exactMatch(text)
|
||||||
|
|
||||||
|
if h1Line2 or h2Line2:
|
||||||
|
# Restart tokenizing on the previous line.
|
||||||
|
self.requestBacktrack()
|
||||||
|
token.length = len(text)
|
||||||
|
token.position = 0
|
||||||
|
|
||||||
|
if h1Line2:
|
||||||
|
self.setState(MS.MarkdownStateSetextHeading1Line2)
|
||||||
|
token.type = MTT.TokenSetextHeading1Line2
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.setState(MS.MarkdownStateSetextHeading2Line2)
|
||||||
|
token.type = MTT.TokenSetextHeading2Line2
|
||||||
|
|
||||||
|
self.addToken(token)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if level > 0:
|
||||||
|
if setextMatch:
|
||||||
|
token.length = len(text)
|
||||||
|
token.position = 0
|
||||||
|
self.addToken(token)
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Restart tokenizing on the previous line.
|
||||||
|
self.requestBacktrack()
|
||||||
|
False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeAtxHeading(self, text):
|
||||||
|
escapedText = self.dummyOutEscapeCharacters(text)
|
||||||
|
trailingPoundCount = 0
|
||||||
|
level = 0
|
||||||
|
|
||||||
|
#Count the number of pound signs at the front of the string,
|
||||||
|
#up to the maximum allowed, to determine the heading level.
|
||||||
|
|
||||||
|
while escapedText[level] == "#":
|
||||||
|
level += 1
|
||||||
|
if level >= len(escapedText) or level >= self.MAX_MARKDOWN_HEADING_LEVEL:
|
||||||
|
break
|
||||||
|
|
||||||
|
if level > 0 and level < len(text):
|
||||||
|
# Count how many pound signs are at the end of the text.
|
||||||
|
while escapedText[-trailingPoundCount -1] == "#":
|
||||||
|
trailingPoundCount += 1
|
||||||
|
|
||||||
|
token = Token()
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
token.type = MTT.TokenAtxHeading1 + level -1
|
||||||
|
token.openingMarkupLength = level
|
||||||
|
token.closingMarkupLength = trailingPoundCount
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateAtxHeading1 + level -1)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeNumberedList(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
if (previousState in [MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateUnknown,
|
||||||
|
MS.MarkdownStateCodeBlock,
|
||||||
|
MS.MarkdownStateCodeFenceEnd,] and \
|
||||||
|
self.numberedListRegex.exactMatch(text)) or \
|
||||||
|
(previousState in [MS.MarkdownStateListLineBreak,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList,] and \
|
||||||
|
self.numberedNestedListRegex.exactMatch(text)):
|
||||||
|
periodIndex = text.find(".")
|
||||||
|
parenthIndex = text.find(")")
|
||||||
|
|
||||||
|
if periodIndex < 0:
|
||||||
|
index = parenthIndex
|
||||||
|
elif parenthIndex < 0:
|
||||||
|
index = periodIndex
|
||||||
|
elif parenthIndex > periodIndex:
|
||||||
|
index = periodIndex
|
||||||
|
else:
|
||||||
|
index = parenthIndex
|
||||||
|
|
||||||
|
if index > 0:
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenNumberedList
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
token.openingMarkupLength = index + 2
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateNumberedList)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeBulletPointList(self, text):
|
||||||
|
foundBulletChar = False
|
||||||
|
bulletCharIndex = -1
|
||||||
|
spaceCount = 0
|
||||||
|
whitespaceFoundAfterBulletChar = False
|
||||||
|
previousState = self.previousState
|
||||||
|
|
||||||
|
if previousState not in [MS.MarkdownStateUnknown,
|
||||||
|
MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateListLineBreak,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList,
|
||||||
|
MS.MarkdownStateCodeBlock,
|
||||||
|
MS.MarkdownStateCodeFenceEnd]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Search for the bullet point character, which can
|
||||||
|
# be either a '+', '-', or '*'.
|
||||||
|
|
||||||
|
for i in range(len(text)):
|
||||||
|
if text[i] == " ":
|
||||||
|
if foundBulletChar:
|
||||||
|
# We've confirmed it's a bullet point by the whitespace that
|
||||||
|
# follows the bullet point character, and can now exit the
|
||||||
|
# loop.
|
||||||
|
|
||||||
|
whitespaceFoundAfterBulletChar = True
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
spaceCount += 1
|
||||||
|
|
||||||
|
# If this list item is the first in the list, ensure the
|
||||||
|
# number of spaces preceeding the bullet point does not
|
||||||
|
# exceed three, as that would indicate a code block rather
|
||||||
|
# than a bullet point list.
|
||||||
|
|
||||||
|
if spaceCount > 3 and previousState not in [
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList,
|
||||||
|
MS.MarkdownStateListLineBreak,] and \
|
||||||
|
previousState in [
|
||||||
|
MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateUnknown,
|
||||||
|
MS.MarkdownStateCodeBlock,
|
||||||
|
MS.MarkdownStateCodeFenceEnd,]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif text[i] == "\t":
|
||||||
|
if foundBulletChar:
|
||||||
|
# We've confirmed it's a bullet point by the whitespace that
|
||||||
|
# follows the bullet point character, and can now exit the
|
||||||
|
# loop.
|
||||||
|
|
||||||
|
whitespaceFoundAfterBulletChar = True
|
||||||
|
break
|
||||||
|
|
||||||
|
elif previousState in [
|
||||||
|
MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateUnknown]:
|
||||||
|
|
||||||
|
# If this list item is the first in the list, ensure that
|
||||||
|
# no tab character preceedes the bullet point, as that would
|
||||||
|
# indicate a code block rather than a bullet point list.
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif text[i] in ["+", "-", "*"]:
|
||||||
|
foundBulletChar = True
|
||||||
|
bulletCharIndex = i
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if bulletCharIndex >= 0 and whitespaceFoundAfterBulletChar:
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenBulletPointList
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
token.openingMarkupLength = bulletCharIndex + 2
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateBulletPointList)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeHorizontalRule (self, text):
|
||||||
|
if self.hruleRegex.exactMatch(text):
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenHorizontalRule
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateHorizontalRule)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeLineBreak(self, text):
|
||||||
|
currentState = self.currentState
|
||||||
|
previousState = self.previousState
|
||||||
|
nextState = self.nextState
|
||||||
|
|
||||||
|
if currentState in [
|
||||||
|
MS.MarkdownStateParagraph,
|
||||||
|
MS.MarkdownStateBlockquote,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList,]:
|
||||||
|
if previousState in [
|
||||||
|
MS.MarkdownStateParagraph,
|
||||||
|
MS.MarkdownStateBlockquote,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList,]:
|
||||||
|
self.requestBacktrack()
|
||||||
|
|
||||||
|
if nextState in [
|
||||||
|
MS.MarkdownStateParagraph,
|
||||||
|
MS.MarkdownStateBlockquote,
|
||||||
|
MS.MarkdownStateNumberedList,
|
||||||
|
MS.MarkdownStateBulletPointList,]:
|
||||||
|
self.requestBacktrack()
|
||||||
|
if self.lineBreakRegex.exactMatch(text):
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenLineBreak
|
||||||
|
token.position = len(text) - 1
|
||||||
|
token.length = 1
|
||||||
|
self.addToken(token)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeBlockquote(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
if previousState == MS.MarkdownStateBlockquote or \
|
||||||
|
self.blockquoteRegex.exactMatch(text):
|
||||||
|
|
||||||
|
# Find any '>' characters at the front of the line.
|
||||||
|
markupLength = 0
|
||||||
|
|
||||||
|
for i in range(len(text)):
|
||||||
|
if text[i] == ">":
|
||||||
|
markupLength = i + 1
|
||||||
|
elif text[i] != " ":
|
||||||
|
# There are no more '>' characters at the front of the line,
|
||||||
|
# so stop processing.
|
||||||
|
break
|
||||||
|
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenBlockquote
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
|
||||||
|
if markupLength > 0:
|
||||||
|
token.openingMarkupLength = markupLength
|
||||||
|
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateBlockquote)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeCodeBlock(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
if previousState in [
|
||||||
|
MS.MarkdownStateInGithubCodeFence,
|
||||||
|
MS.MarkdownStateInPandocCodeFence]:
|
||||||
|
self.setState(previousState)
|
||||||
|
|
||||||
|
if (previousState == MS.MarkdownStateInGithubCodeFence and \
|
||||||
|
self.githubCodeFenceEndRegex.exactMatch(text)) or \
|
||||||
|
(previousState == MS.MarkdownStateInPandocCodeFence and \
|
||||||
|
self.pandocCodeFenceEndRegex.exactMatch(text)):
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenCodeFenceEnd
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateCodeFenceEnd)
|
||||||
|
|
||||||
|
else:
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenCodeBlock
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif previousState in [
|
||||||
|
MS.MarkdownStateCodeBlock,
|
||||||
|
MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateUnknown,] and \
|
||||||
|
(text[:1] == "\t" or text[:4] == " "):
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenCodeBlock
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
token.openingMarkupLength = len(text) - len(text.lstrip())
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateCodeBlock)
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif previousState in [
|
||||||
|
MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateParagraph,
|
||||||
|
MS.MarkdownStateUnknown,
|
||||||
|
MS.MarkdownStateListLineBreak,]:
|
||||||
|
foundCodeFenceStart = False
|
||||||
|
token = Token()
|
||||||
|
if self.githubCodeFenceStartRegex.exactMatch(text):
|
||||||
|
foundCodeFenceStart = True
|
||||||
|
token.type = MTT.TokenGithubCodeFence
|
||||||
|
self.setState(MS.MarkdownStateInGithubCodeFence)
|
||||||
|
|
||||||
|
elif self.pandocCodeFenceStartRegex.exactMatch(text):
|
||||||
|
foundCodeFenceStart = True
|
||||||
|
token.type = MTT.TokenPandocCodeFence
|
||||||
|
self.setState(MS.MarkdownStateInPandocCodeFence)
|
||||||
|
|
||||||
|
if foundCodeFenceStart:
|
||||||
|
token.position = 0
|
||||||
|
token.length = len(text)
|
||||||
|
self.addToken(token)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeMultilineComment(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
|
||||||
|
if previousState == MS.MarkdownStateComment:
|
||||||
|
# Find the end of the comment, if any.
|
||||||
|
index = text.find("-->")
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenHtmlComment
|
||||||
|
token.position = 0
|
||||||
|
|
||||||
|
if index >= 0:
|
||||||
|
token.length = index + 3
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
# Return false so that the rest of the line that isn't within
|
||||||
|
# the commented segment can be highlighted as normal paragraph
|
||||||
|
# text.
|
||||||
|
|
||||||
|
else:
|
||||||
|
token.length = len(text)
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateComment)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeInline(self, text):
|
||||||
|
escapedText = self.dummyOutEscapeCharacters(text)
|
||||||
|
|
||||||
|
# Check if the line is a reference definition.
|
||||||
|
if self.referenceDefinitionRegex.exactMatch(text):
|
||||||
|
colonIndex = escapedText.find(":")
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenReferenceDefinition
|
||||||
|
token.position = 0
|
||||||
|
token.length = colonIndex + 1
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
# Replace the first bracket so that the '[...]:' reference definition
|
||||||
|
# start doesn't get highlighted as a reference link.
|
||||||
|
|
||||||
|
firstBracketIndex = escapedText.find("[")
|
||||||
|
if firstBracketIndex >= 0:
|
||||||
|
i = firstBracketIndex
|
||||||
|
escapedText = escapedText[:i] + self.DUMMY_CHAR + escapedText[i+1:]
|
||||||
|
|
||||||
|
escapedText = self.tokenizeVerbatim(escapedText)
|
||||||
|
escapedText = self.tokenizeHtmlComments(escapedText)
|
||||||
|
escapedText = self.tokenizeTableHeaderRow(escapedText)
|
||||||
|
escapedText = self.tokenizeTableRow(escapedText)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenImage, escapedText, self.imageRegex, 0, 0, False, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenInlineLink, escapedText, self.inlineLinkRegex, 0, 0, False, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenReferenceLink, escapedText, self.referenceLinkRegex, 0, 0, False, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenHtmlEntity, escapedText, self.htmlEntityRegex)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenAutomaticLink, escapedText, self.automaticLinkRegex, 0, 0, False, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenStrong, escapedText, self.strongRegex, 2, 2, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenEmphasis, escapedText, self.emphasisRegex, 1, 1, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenMention, escapedText, self.mentionRegex, 0, 0, False, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenCMAddition, escapedText, self.CMAdditionRegex, 3, 3, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenCMDeletion, escapedText, self.CMDeletionRegex, 3, 3, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenCMSubstitution, escapedText, self.CMSubstitutionRegex, 3, 3, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenCMComment, escapedText, self.CMCommentRegex, 3, 3, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenCMHighlight, escapedText, self.CMHighlightRegex, 3, 3, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenStrikethrough, escapedText, self.strikethroughRegex, 2, 2, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenHtmlTag, escapedText, self.htmlTagRegex)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenSubScript, escapedText, self.subScriptRegex, 1, 1, True)
|
||||||
|
escapedText = self.tokenizeMatches(MTT.TokenSuperScript, escapedText, self.superScriptRegex, 1, 1, True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def tokenizeVerbatim(self, text):
|
||||||
|
index = self.verbatimRegex.indexIn(text)
|
||||||
|
|
||||||
|
while index >= 0:
|
||||||
|
end = ""
|
||||||
|
count = self.verbatimRegex.matchedLength()
|
||||||
|
|
||||||
|
# Search for the matching end, which should have the same number
|
||||||
|
# of back ticks as the start.
|
||||||
|
for i in range(count):
|
||||||
|
end += '`'
|
||||||
|
|
||||||
|
endIndex = text.find(end, index + count)
|
||||||
|
|
||||||
|
# If the end was found, add the verbatim token.
|
||||||
|
if endIndex >= 0:
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenVerbatim
|
||||||
|
token.position = index
|
||||||
|
token.length = endIndex + count - index
|
||||||
|
token.openingMarkupLength = count
|
||||||
|
token.closingMarkupLength = count
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
# Fill out the token match in the string with the dummy
|
||||||
|
# character so that searches for other Markdown elements
|
||||||
|
# don't find anything within this token's range in the string.
|
||||||
|
|
||||||
|
for i in range(index, index + token.length):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
|
||||||
|
index += token.length
|
||||||
|
|
||||||
|
# Else start searching again at the very next character.
|
||||||
|
else:
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
index = self.verbatimRegex.indexIn(text, index)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def tokenizeHtmlComments(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
|
||||||
|
# Check for the end of a multiline comment so that it doesn't get further
|
||||||
|
# tokenized. Don't bother formatting the comment itself, however, because
|
||||||
|
# it should have already been tokenized in tokenizeMultilineComment().
|
||||||
|
if previousState == MS.MarkdownStateComment:
|
||||||
|
commentEnd = text.find("-->")
|
||||||
|
for i in range(commentEnd + 3):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
|
||||||
|
# Now check for inline comments (non-multiline).
|
||||||
|
commentStart = self.htmlInlineCommentRegex.indexIn(text)
|
||||||
|
|
||||||
|
while commentStart >= 0:
|
||||||
|
commentLength = self.htmlInlineCommentRegex.matchedLength()
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenHtmlComment
|
||||||
|
token.position = commentStart
|
||||||
|
token.length = commentLength
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
# Replace comment segment with dummy characters so that it doesn't
|
||||||
|
# get tokenized again.
|
||||||
|
|
||||||
|
for i in range(commentStart, commentStart + commentLength):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
|
||||||
|
commentStart = self.htmlInlineCommentRegex.indexIn(text, commentStart + commentLength)
|
||||||
|
|
||||||
|
# Find multiline comment start, if any.
|
||||||
|
commentStart = text.find("<!--")
|
||||||
|
if commentStart >= 0:
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenHtmlComment
|
||||||
|
token.position = commentStart
|
||||||
|
token.length = len(text) - commentStart
|
||||||
|
self.addToken(token)
|
||||||
|
self.setState(MS.MarkdownStateComment)
|
||||||
|
|
||||||
|
# Replace comment segment with dummy characters so that it doesn't
|
||||||
|
# get tokenized again.
|
||||||
|
|
||||||
|
for i in range(commentStart, len(text)):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def tokenizeTableHeaderRow(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
nextState = self.nextState
|
||||||
|
|
||||||
|
if previousState in [
|
||||||
|
MS.MarkdownStateParagraphBreak,
|
||||||
|
MS.MarkdownStateListLineBreak,
|
||||||
|
MS.MarkdownStateSetextHeading1Line2,
|
||||||
|
MS.MarkdownStateSetextHeading2Line2,
|
||||||
|
MS.MarkdownStateAtxHeading1,
|
||||||
|
MS.MarkdownStateAtxHeading2,
|
||||||
|
MS.MarkdownStateAtxHeading3,
|
||||||
|
MS.MarkdownStateAtxHeading4,
|
||||||
|
MS.MarkdownStateAtxHeading5,
|
||||||
|
MS.MarkdownStateAtxHeading6,
|
||||||
|
MS.MarkdownStateHorizontalRule,
|
||||||
|
MS.MarkdownStateCodeFenceEnd,
|
||||||
|
MS.MarkdownStateUnknown,] and \
|
||||||
|
self.getState() in [
|
||||||
|
MS.MarkdownStateParagraph,
|
||||||
|
MS.MarkdownStateUnknown] and \
|
||||||
|
nextState == MS.MarkdownStatePipeTableDivider:
|
||||||
|
self.setState(MS.MarkdownStatePipeTableHeader)
|
||||||
|
|
||||||
|
headerStart = 0
|
||||||
|
for i in range(len(text)):
|
||||||
|
if text[i] == "|":
|
||||||
|
# Replace pipe with space so that it doesn't get formatted
|
||||||
|
# again with, for example, strong or emphasis formatting.
|
||||||
|
# Note that we use a space rather than DUMMY_CHAR for this,
|
||||||
|
# to prevent formatting such as strong and emphasis from
|
||||||
|
# picking it up.
|
||||||
|
text = text[:i] + " " + text[i+1:]
|
||||||
|
|
||||||
|
token = Token()
|
||||||
|
|
||||||
|
if i > 0:
|
||||||
|
token.type = MTT.TokenTableHeader
|
||||||
|
token.position = headerStart
|
||||||
|
token.length = i - headerStart
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
token.type = MTT.TokenTablePipe
|
||||||
|
token.position = i
|
||||||
|
token.length = 1
|
||||||
|
self.addToken(token)
|
||||||
|
headerStart = i + 1
|
||||||
|
|
||||||
|
if headerStart < len(text):
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenTableHeader
|
||||||
|
token.position = headerStart
|
||||||
|
token.length = len(text) - headerStart
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
def tokenizeTableDivider(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
if previousState == MS.MarkdownStatePipeTableHeader:
|
||||||
|
if self.pipeTableDividerRegex.exactMatch(text):
|
||||||
|
self.setState(MS.MarkdownStatePipeTableDivider)
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenTableDivider
|
||||||
|
token.length = len(text)
|
||||||
|
token.position = 0
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Restart tokenizing on the previous line.
|
||||||
|
self.requestBacktrack()
|
||||||
|
elif previousState == MS.MarkdownStateParagraph:
|
||||||
|
if self.pipeTableDividerRegex.exactMatch(text):
|
||||||
|
# Restart tokenizing on the previous line.
|
||||||
|
self.requestBacktrack()
|
||||||
|
self.setState(MS.MarkdownStatePipeTableDivider)
|
||||||
|
|
||||||
|
token = Token()
|
||||||
|
token.length = len(text)
|
||||||
|
token.position = 0
|
||||||
|
token.type = MTT.TokenTableDivider
|
||||||
|
self.addToken(token)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tokenizeTableRow(self, text):
|
||||||
|
previousState = self.previousState
|
||||||
|
|
||||||
|
if previousState in [
|
||||||
|
MS.MarkdownStatePipeTableDivider,
|
||||||
|
MS.MarkdownStatePipeTableRow]:
|
||||||
|
self.setState(MS.MarkdownStatePipeTableRow)
|
||||||
|
|
||||||
|
for i in range(len(text)):
|
||||||
|
if text[i] == "|":
|
||||||
|
# Replace pipe with space so that it doesn't get formatted
|
||||||
|
# again with, for example, strong or emphasis formatting.
|
||||||
|
# Note that we use a space rather than DUMMY_CHAR for this,
|
||||||
|
# to prevent formatting such as strong and emphasis from
|
||||||
|
# picking it up.
|
||||||
|
|
||||||
|
text = text[:i] + " " + text[i+1:]
|
||||||
|
|
||||||
|
token = Token()
|
||||||
|
token.type = MTT.TokenTablePipe
|
||||||
|
token.position = i
|
||||||
|
token.length = 1
|
||||||
|
self.addToken(token)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
def tokenizeMatches(self, tokenType, text, regex,
|
||||||
|
markupStartCount=0, markupEndCount=0,
|
||||||
|
replaceMarkupChars=False, replaceAllChars=False):
|
||||||
|
"""
|
||||||
|
Tokenizes a block of text, searching for all occurrances of regex.
|
||||||
|
Occurrances are set to the given token type and added to the list of
|
||||||
|
tokens. The markupStartCount and markupEndCount values are used to
|
||||||
|
indicate how many markup special characters preceed and follow the
|
||||||
|
main text, respectively.
|
||||||
|
|
||||||
|
For example, if the matched string is "**bold**", and
|
||||||
|
markupStartCount = 2 and markupEndCount = 2, then the asterisks
|
||||||
|
preceeding and following the word "bold" will be set as opening and
|
||||||
|
closing markup in the token.
|
||||||
|
|
||||||
|
If replaceMarkupChars is true, then the markupStartCount and
|
||||||
|
markupEndCount characters will be replaced with a dummy character in
|
||||||
|
the text QString so that subsequent parsings of the same line do not
|
||||||
|
pick up the original characters.
|
||||||
|
|
||||||
|
If replaceAllChars is true instead, then the entire matched text will
|
||||||
|
be replaced with dummy characters--again, for ease in parsing the
|
||||||
|
same line for other regular expression matches.
|
||||||
|
"""
|
||||||
|
index = regex.indexIn(text)
|
||||||
|
|
||||||
|
while index >= 0:
|
||||||
|
length = regex.matchedLength()
|
||||||
|
token = Token()
|
||||||
|
token.type = tokenType
|
||||||
|
token.position = index
|
||||||
|
token.length = length
|
||||||
|
|
||||||
|
if markupStartCount > 0:
|
||||||
|
token.openingMarkupLength = markupStartCount
|
||||||
|
|
||||||
|
if markupEndCount > 0:
|
||||||
|
token.closingMarkupLength = markupEndCount
|
||||||
|
|
||||||
|
if replaceAllChars:
|
||||||
|
for i in range(index, index + length):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
|
||||||
|
elif replaceMarkupChars:
|
||||||
|
for i in range(index, index + markupStartCount):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
for i in range(index + length - markupEndCount, index + length):
|
||||||
|
text = text[:i] + self.DUMMY_CHAR + text[i+1:]
|
||||||
|
|
||||||
|
self.addToken(token)
|
||||||
|
index = regex.indexIn(text, index + length)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
def dummyOutEscapeCharacters(self, text):
|
||||||
|
"""
|
||||||
|
Replaces escaped characters in text so they aren't picked up
|
||||||
|
during parsing. Returns a copy of the input text string
|
||||||
|
with the escaped characters replaced with a dummy character.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return re.sub("\\\\.", "\$", text)
|
||||||
|
|
||||||
|
#escape = False
|
||||||
|
#escapedText = text
|
||||||
|
|
||||||
|
#for i in range(len(text)):
|
||||||
|
#if escape:
|
||||||
|
#escapedText = escapedText[:i] + self.DUMMY_CHAR + escapedText[i+1:]
|
||||||
|
#escape = False
|
||||||
|
#elif text[i] == "\\":
|
||||||
|
#escape = True
|
||||||
|
#return escapedText
|
|
@ -7,7 +7,7 @@ from PyQt5.QtCore import Qt, QTimer
|
||||||
from PyQt5.QtGui import QBrush, QColor, QIcon
|
from PyQt5.QtGui import QBrush, QColor, QIcon
|
||||||
from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox, QStyle
|
from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox, QStyle
|
||||||
|
|
||||||
from manuskript.functions import writablePath, appPath, openURL
|
from manuskript.functions import writablePath, appPath, openURL, statusMessage
|
||||||
from manuskript.ui.importers.importer_ui import Ui_importer
|
from manuskript.ui.importers.importer_ui import Ui_importer
|
||||||
from manuskript.ui.importers.generalSettings import generalSettings
|
from manuskript.ui.importers.generalSettings import generalSettings
|
||||||
from manuskript.ui import style
|
from manuskript.ui import style
|
||||||
|
@ -259,7 +259,7 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
# Using status bar message instead...
|
# Using status bar message instead...
|
||||||
#QMessageBox.information(self, self.tr("Import status"),
|
#QMessageBox.information(self, self.tr("Import status"),
|
||||||
#self.tr("Import Complete."))
|
#self.tr("Import Complete."))
|
||||||
self.mw.statusBar().showMessage("Import complete!", 5000)
|
statusMessage("Import complete!", 5000)
|
||||||
|
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
@ -320,5 +320,3 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
item.split(self.settingsWidget.splitScenes(), recursive=False)
|
item.split(self.settingsWidget.splitScenes(), recursive=False)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'manuskript/ui/mainWindow.ui'
|
# Form implementation generated from reading ui file 'manuskript/ui/mainWindow.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.9
|
# Created by: PyQt5 UI code generator 5.5.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ class Ui_MainWindow(object):
|
||||||
self.welcomePage = QtWidgets.QWidget()
|
self.welcomePage = QtWidgets.QWidget()
|
||||||
self.welcomePage.setObjectName("welcomePage")
|
self.welcomePage.setObjectName("welcomePage")
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.welcomePage)
|
self.gridLayout = QtWidgets.QGridLayout(self.welcomePage)
|
||||||
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
self.gridLayout.addItem(spacerItem, 1, 0, 1, 1)
|
self.gridLayout.addItem(spacerItem, 1, 0, 1, 1)
|
||||||
|
@ -161,12 +160,11 @@ class Ui_MainWindow(object):
|
||||||
self.tabSummaryPage1 = QtWidgets.QWidget()
|
self.tabSummaryPage1 = QtWidgets.QWidget()
|
||||||
self.tabSummaryPage1.setObjectName("tabSummaryPage1")
|
self.tabSummaryPage1.setObjectName("tabSummaryPage1")
|
||||||
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.tabSummaryPage1)
|
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.tabSummaryPage1)
|
||||||
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||||
self.label = QtWidgets.QLabel(self.tabSummaryPage1)
|
self.label = QtWidgets.QLabel(self.tabSummaryPage1)
|
||||||
self.label.setObjectName("label")
|
self.label.setObjectName("label")
|
||||||
self.verticalLayout_5.addWidget(self.label)
|
self.verticalLayout_5.addWidget(self.label)
|
||||||
self.txtSummarySentence = textEditView(self.tabSummaryPage1)
|
self.txtSummarySentence = MDEditCompleter(self.tabSummaryPage1)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
@ -184,14 +182,13 @@ class Ui_MainWindow(object):
|
||||||
self.tabSummaryPage2 = QtWidgets.QWidget()
|
self.tabSummaryPage2 = QtWidgets.QWidget()
|
||||||
self.tabSummaryPage2.setObjectName("tabSummaryPage2")
|
self.tabSummaryPage2.setObjectName("tabSummaryPage2")
|
||||||
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tabSummaryPage2)
|
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tabSummaryPage2)
|
||||||
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.label_21 = QtWidgets.QLabel(self.tabSummaryPage2)
|
self.label_21 = QtWidgets.QLabel(self.tabSummaryPage2)
|
||||||
self.label_21.setObjectName("label_21")
|
self.label_21.setObjectName("label_21")
|
||||||
self.verticalLayout.addWidget(self.label_21)
|
self.verticalLayout.addWidget(self.label_21)
|
||||||
self.txtSummarySentence_2 = textEditView(self.tabSummaryPage2)
|
self.txtSummarySentence_2 = MDEditCompleter(self.tabSummaryPage2)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
@ -213,7 +210,7 @@ class Ui_MainWindow(object):
|
||||||
self.label_2 = QtWidgets.QLabel(self.tabSummaryPage2)
|
self.label_2 = QtWidgets.QLabel(self.tabSummaryPage2)
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2.setObjectName("label_2")
|
||||||
self.verticalLayout_2.addWidget(self.label_2)
|
self.verticalLayout_2.addWidget(self.label_2)
|
||||||
self.txtSummaryPara = textEditView(self.tabSummaryPage2)
|
self.txtSummaryPara = MDEditCompleter(self.tabSummaryPage2)
|
||||||
self.txtSummaryPara.setObjectName("txtSummaryPara")
|
self.txtSummaryPara.setObjectName("txtSummaryPara")
|
||||||
self.verticalLayout_2.addWidget(self.txtSummaryPara)
|
self.verticalLayout_2.addWidget(self.txtSummaryPara)
|
||||||
self.lblSummaryWCPara = QtWidgets.QLabel(self.tabSummaryPage2)
|
self.lblSummaryWCPara = QtWidgets.QLabel(self.tabSummaryPage2)
|
||||||
|
@ -227,14 +224,13 @@ class Ui_MainWindow(object):
|
||||||
self.tabSummaryPage3 = QtWidgets.QWidget()
|
self.tabSummaryPage3 = QtWidgets.QWidget()
|
||||||
self.tabSummaryPage3.setObjectName("tabSummaryPage3")
|
self.tabSummaryPage3.setObjectName("tabSummaryPage3")
|
||||||
self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.tabSummaryPage3)
|
self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.tabSummaryPage3)
|
||||||
self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
|
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
|
||||||
self.verticalLayout_6 = QtWidgets.QVBoxLayout()
|
self.verticalLayout_6 = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
||||||
self.label_22 = QtWidgets.QLabel(self.tabSummaryPage3)
|
self.label_22 = QtWidgets.QLabel(self.tabSummaryPage3)
|
||||||
self.label_22.setObjectName("label_22")
|
self.label_22.setObjectName("label_22")
|
||||||
self.verticalLayout_6.addWidget(self.label_22)
|
self.verticalLayout_6.addWidget(self.label_22)
|
||||||
self.txtSummaryPara_2 = textEditView(self.tabSummaryPage3)
|
self.txtSummaryPara_2 = MDEditCompleter(self.tabSummaryPage3)
|
||||||
self.txtSummaryPara_2.setReadOnly(True)
|
self.txtSummaryPara_2.setReadOnly(True)
|
||||||
self.txtSummaryPara_2.setObjectName("txtSummaryPara_2")
|
self.txtSummaryPara_2.setObjectName("txtSummaryPara_2")
|
||||||
self.verticalLayout_6.addWidget(self.txtSummaryPara_2)
|
self.verticalLayout_6.addWidget(self.txtSummaryPara_2)
|
||||||
|
@ -251,7 +247,7 @@ class Ui_MainWindow(object):
|
||||||
self.label_17 = QtWidgets.QLabel(self.tabSummaryPage3)
|
self.label_17 = QtWidgets.QLabel(self.tabSummaryPage3)
|
||||||
self.label_17.setObjectName("label_17")
|
self.label_17.setObjectName("label_17")
|
||||||
self.verticalLayout_3.addWidget(self.label_17)
|
self.verticalLayout_3.addWidget(self.label_17)
|
||||||
self.txtSummaryPage = textEditView(self.tabSummaryPage3)
|
self.txtSummaryPage = MDEditCompleter(self.tabSummaryPage3)
|
||||||
self.txtSummaryPage.setObjectName("txtSummaryPage")
|
self.txtSummaryPage.setObjectName("txtSummaryPage")
|
||||||
self.verticalLayout_3.addWidget(self.txtSummaryPage)
|
self.verticalLayout_3.addWidget(self.txtSummaryPage)
|
||||||
self.lblSummaryWCPage = QtWidgets.QLabel(self.tabSummaryPage3)
|
self.lblSummaryWCPage = QtWidgets.QLabel(self.tabSummaryPage3)
|
||||||
|
@ -263,14 +259,13 @@ class Ui_MainWindow(object):
|
||||||
self.tabSummaryPage4 = QtWidgets.QWidget()
|
self.tabSummaryPage4 = QtWidgets.QWidget()
|
||||||
self.tabSummaryPage4.setObjectName("tabSummaryPage4")
|
self.tabSummaryPage4.setObjectName("tabSummaryPage4")
|
||||||
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.tabSummaryPage4)
|
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.tabSummaryPage4)
|
||||||
self.horizontalLayout_8.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
|
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
|
||||||
self.verticalLayout_7 = QtWidgets.QVBoxLayout()
|
self.verticalLayout_7 = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout_7.setObjectName("verticalLayout_7")
|
self.verticalLayout_7.setObjectName("verticalLayout_7")
|
||||||
self.label_23 = QtWidgets.QLabel(self.tabSummaryPage4)
|
self.label_23 = QtWidgets.QLabel(self.tabSummaryPage4)
|
||||||
self.label_23.setObjectName("label_23")
|
self.label_23.setObjectName("label_23")
|
||||||
self.verticalLayout_7.addWidget(self.label_23)
|
self.verticalLayout_7.addWidget(self.label_23)
|
||||||
self.txtSummaryPage_2 = textEditView(self.tabSummaryPage4)
|
self.txtSummaryPage_2 = MDEditCompleter(self.tabSummaryPage4)
|
||||||
self.txtSummaryPage_2.setReadOnly(True)
|
self.txtSummaryPage_2.setReadOnly(True)
|
||||||
self.txtSummaryPage_2.setObjectName("txtSummaryPage_2")
|
self.txtSummaryPage_2.setObjectName("txtSummaryPage_2")
|
||||||
self.verticalLayout_7.addWidget(self.txtSummaryPage_2)
|
self.verticalLayout_7.addWidget(self.txtSummaryPage_2)
|
||||||
|
@ -285,7 +280,7 @@ class Ui_MainWindow(object):
|
||||||
self.label_20 = QtWidgets.QLabel(self.tabSummaryPage4)
|
self.label_20 = QtWidgets.QLabel(self.tabSummaryPage4)
|
||||||
self.label_20.setObjectName("label_20")
|
self.label_20.setObjectName("label_20")
|
||||||
self.verticalLayout_4.addWidget(self.label_20)
|
self.verticalLayout_4.addWidget(self.label_20)
|
||||||
self.txtSummaryFull = textEditView(self.tabSummaryPage4)
|
self.txtSummaryFull = MDEditCompleter(self.tabSummaryPage4)
|
||||||
self.txtSummaryFull.setObjectName("txtSummaryFull")
|
self.txtSummaryFull.setObjectName("txtSummaryFull")
|
||||||
self.verticalLayout_4.addWidget(self.txtSummaryFull)
|
self.verticalLayout_4.addWidget(self.txtSummaryFull)
|
||||||
self.lblSummaryWCFull = QtWidgets.QLabel(self.tabSummaryPage4)
|
self.lblSummaryWCFull = QtWidgets.QLabel(self.tabSummaryPage4)
|
||||||
|
@ -387,42 +382,41 @@ class Ui_MainWindow(object):
|
||||||
self.scrollAreaPersoInfosWidget.setObjectName("scrollAreaPersoInfosWidget")
|
self.scrollAreaPersoInfosWidget.setObjectName("scrollAreaPersoInfosWidget")
|
||||||
self.formLayout_8 = QtWidgets.QFormLayout(self.scrollAreaPersoInfosWidget)
|
self.formLayout_8 = QtWidgets.QFormLayout(self.scrollAreaPersoInfosWidget)
|
||||||
self.formLayout_8.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
|
self.formLayout_8.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
|
||||||
self.formLayout_8.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.formLayout_8.setObjectName("formLayout_8")
|
self.formLayout_8.setObjectName("formLayout_8")
|
||||||
self.label_4 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
self.label_4 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||||
self.label_4.setObjectName("label_4")
|
self.label_4.setObjectName("label_4")
|
||||||
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
||||||
self.txtPersoMotivation = textEditView(self.scrollAreaPersoInfosWidget)
|
self.txtPersoMotivation = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||||
self.txtPersoMotivation.setObjectName("txtPersoMotivation")
|
self.txtPersoMotivation.setObjectName("txtPersoMotivation")
|
||||||
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.txtPersoMotivation)
|
self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.txtPersoMotivation)
|
||||||
self.label_5 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
self.label_5 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||||
self.label_5.setObjectName("label_5")
|
self.label_5.setObjectName("label_5")
|
||||||
self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
||||||
self.txtPersoGoal = textEditView(self.scrollAreaPersoInfosWidget)
|
self.txtPersoGoal = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||||
self.txtPersoGoal.setObjectName("txtPersoGoal")
|
self.txtPersoGoal.setObjectName("txtPersoGoal")
|
||||||
self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.txtPersoGoal)
|
self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.txtPersoGoal)
|
||||||
self.label_6 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
self.label_6 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||||
self.label_6.setObjectName("label_6")
|
self.label_6.setObjectName("label_6")
|
||||||
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
||||||
self.txtPersoConflict = textEditView(self.scrollAreaPersoInfosWidget)
|
self.txtPersoConflict = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||||
self.txtPersoConflict.setObjectName("txtPersoConflict")
|
self.txtPersoConflict.setObjectName("txtPersoConflict")
|
||||||
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtPersoConflict)
|
self.formLayout_8.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.txtPersoConflict)
|
||||||
self.label_7 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
self.label_7 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||||
self.label_7.setObjectName("label_7")
|
self.label_7.setObjectName("label_7")
|
||||||
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
||||||
self.txtPersoEpiphany = textEditView(self.scrollAreaPersoInfosWidget)
|
self.txtPersoEpiphany = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||||
self.txtPersoEpiphany.setObjectName("txtPersoEpiphany")
|
self.txtPersoEpiphany.setObjectName("txtPersoEpiphany")
|
||||||
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.txtPersoEpiphany)
|
self.formLayout_8.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.txtPersoEpiphany)
|
||||||
self.label_24 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
self.label_24 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||||
self.label_24.setObjectName("label_24")
|
self.label_24.setObjectName("label_24")
|
||||||
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_24)
|
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_24)
|
||||||
self.txtPersoSummarySentence = textEditView(self.scrollAreaPersoInfosWidget)
|
self.txtPersoSummarySentence = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||||
self.txtPersoSummarySentence.setObjectName("txtPersoSummarySentence")
|
self.txtPersoSummarySentence.setObjectName("txtPersoSummarySentence")
|
||||||
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummarySentence)
|
self.formLayout_8.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummarySentence)
|
||||||
self.label_8 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
self.label_8 = QtWidgets.QLabel(self.scrollAreaPersoInfosWidget)
|
||||||
self.label_8.setObjectName("label_8")
|
self.label_8.setObjectName("label_8")
|
||||||
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
||||||
self.txtPersoSummaryPara = textEditView(self.scrollAreaPersoInfosWidget)
|
self.txtPersoSummaryPara = MDEditCompleter(self.scrollAreaPersoInfosWidget)
|
||||||
self.txtPersoSummaryPara.setObjectName("txtPersoSummaryPara")
|
self.txtPersoSummaryPara.setObjectName("txtPersoSummaryPara")
|
||||||
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummaryPara)
|
self.formLayout_8.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.txtPersoSummaryPara)
|
||||||
self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
|
self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
|
||||||
|
@ -467,7 +461,7 @@ class Ui_MainWindow(object):
|
||||||
self.tab_11.setObjectName("tab_11")
|
self.tab_11.setObjectName("tab_11")
|
||||||
self.verticalLayout_17 = QtWidgets.QVBoxLayout(self.tab_11)
|
self.verticalLayout_17 = QtWidgets.QVBoxLayout(self.tab_11)
|
||||||
self.verticalLayout_17.setObjectName("verticalLayout_17")
|
self.verticalLayout_17.setObjectName("verticalLayout_17")
|
||||||
self.txtPersoSummaryFull = textEditView(self.tab_11)
|
self.txtPersoSummaryFull = MDEditCompleter(self.tab_11)
|
||||||
self.txtPersoSummaryFull.setObjectName("txtPersoSummaryFull")
|
self.txtPersoSummaryFull.setObjectName("txtPersoSummaryFull")
|
||||||
self.verticalLayout_17.addWidget(self.txtPersoSummaryFull)
|
self.verticalLayout_17.addWidget(self.txtPersoSummaryFull)
|
||||||
self.horizontalLayout_22 = QtWidgets.QHBoxLayout()
|
self.horizontalLayout_22 = QtWidgets.QHBoxLayout()
|
||||||
|
@ -485,7 +479,7 @@ class Ui_MainWindow(object):
|
||||||
self.tab_19.setObjectName("tab_19")
|
self.tab_19.setObjectName("tab_19")
|
||||||
self.horizontalLayout_30 = QtWidgets.QHBoxLayout(self.tab_19)
|
self.horizontalLayout_30 = QtWidgets.QHBoxLayout(self.tab_19)
|
||||||
self.horizontalLayout_30.setObjectName("horizontalLayout_30")
|
self.horizontalLayout_30.setObjectName("horizontalLayout_30")
|
||||||
self.txtPersoNotes = textEditView(self.tab_19)
|
self.txtPersoNotes = MDEditCompleter(self.tab_19)
|
||||||
self.txtPersoNotes.setObjectName("txtPersoNotes")
|
self.txtPersoNotes.setObjectName("txtPersoNotes")
|
||||||
self.horizontalLayout_30.addWidget(self.txtPersoNotes)
|
self.horizontalLayout_30.addWidget(self.txtPersoNotes)
|
||||||
self.tabPersos.addTab(self.tab_19, "")
|
self.tabPersos.addTab(self.tab_19, "")
|
||||||
|
@ -632,10 +626,10 @@ class Ui_MainWindow(object):
|
||||||
self.sldPlotImportance.setSizePolicy(sizePolicy)
|
self.sldPlotImportance.setSizePolicy(sizePolicy)
|
||||||
self.sldPlotImportance.setObjectName("sldPlotImportance")
|
self.sldPlotImportance.setObjectName("sldPlotImportance")
|
||||||
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.sldPlotImportance)
|
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.sldPlotImportance)
|
||||||
self.txtPlotDescription = textEditView(self.infos_2)
|
self.txtPlotDescription = MDEditCompleter(self.infos_2)
|
||||||
self.txtPlotDescription.setObjectName("txtPlotDescription")
|
self.txtPlotDescription.setObjectName("txtPlotDescription")
|
||||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtPlotDescription)
|
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtPlotDescription)
|
||||||
self.txtPlotResult = textEditView(self.infos_2)
|
self.txtPlotResult = MDEditCompleter(self.infos_2)
|
||||||
self.txtPlotResult.setObjectName("txtPlotResult")
|
self.txtPlotResult.setObjectName("txtPlotResult")
|
||||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.txtPlotResult)
|
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.txtPlotResult)
|
||||||
self.tabPlot.addTab(self.infos_2, "")
|
self.tabPlot.addTab(self.infos_2, "")
|
||||||
|
@ -654,7 +648,7 @@ class Ui_MainWindow(object):
|
||||||
self.grpSubPlotSummary.setObjectName("grpSubPlotSummary")
|
self.grpSubPlotSummary.setObjectName("grpSubPlotSummary")
|
||||||
self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.grpSubPlotSummary)
|
self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.grpSubPlotSummary)
|
||||||
self.verticalLayout_11.setObjectName("verticalLayout_11")
|
self.verticalLayout_11.setObjectName("verticalLayout_11")
|
||||||
self.txtSubPlotSummary = textEditView(self.grpSubPlotSummary)
|
self.txtSubPlotSummary = MDEditCompleter(self.grpSubPlotSummary)
|
||||||
self.txtSubPlotSummary.setObjectName("txtSubPlotSummary")
|
self.txtSubPlotSummary.setObjectName("txtSubPlotSummary")
|
||||||
self.verticalLayout_11.addWidget(self.txtSubPlotSummary)
|
self.verticalLayout_11.addWidget(self.txtSubPlotSummary)
|
||||||
self.verticalLayout_28.addWidget(self.grpSubPlotSummary)
|
self.verticalLayout_28.addWidget(self.grpSubPlotSummary)
|
||||||
|
@ -703,27 +697,24 @@ class Ui_MainWindow(object):
|
||||||
self.page = QtWidgets.QWidget()
|
self.page = QtWidgets.QWidget()
|
||||||
self.page.setObjectName("page")
|
self.page.setObjectName("page")
|
||||||
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.page)
|
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.page)
|
||||||
self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
|
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
|
||||||
self.txtPlotSummaryPara = textEditView(self.page)
|
self.txtPlotSummaryPara = MDEditCompleter(self.page)
|
||||||
self.txtPlotSummaryPara.setObjectName("txtPlotSummaryPara")
|
self.txtPlotSummaryPara.setObjectName("txtPlotSummaryPara")
|
||||||
self.horizontalLayout_6.addWidget(self.txtPlotSummaryPara)
|
self.horizontalLayout_6.addWidget(self.txtPlotSummaryPara)
|
||||||
self.stkPlotSummary.addWidget(self.page)
|
self.stkPlotSummary.addWidget(self.page)
|
||||||
self.page_2 = QtWidgets.QWidget()
|
self.page_2 = QtWidgets.QWidget()
|
||||||
self.page_2.setObjectName("page_2")
|
self.page_2.setObjectName("page_2")
|
||||||
self.horizontalLayout_10 = QtWidgets.QHBoxLayout(self.page_2)
|
self.horizontalLayout_10 = QtWidgets.QHBoxLayout(self.page_2)
|
||||||
self.horizontalLayout_10.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.horizontalLayout_10.setObjectName("horizontalLayout_10")
|
self.horizontalLayout_10.setObjectName("horizontalLayout_10")
|
||||||
self.txtPlotSummaryPage = textEditView(self.page_2)
|
self.txtPlotSummaryPage = MDEditCompleter(self.page_2)
|
||||||
self.txtPlotSummaryPage.setObjectName("txtPlotSummaryPage")
|
self.txtPlotSummaryPage.setObjectName("txtPlotSummaryPage")
|
||||||
self.horizontalLayout_10.addWidget(self.txtPlotSummaryPage)
|
self.horizontalLayout_10.addWidget(self.txtPlotSummaryPage)
|
||||||
self.stkPlotSummary.addWidget(self.page_2)
|
self.stkPlotSummary.addWidget(self.page_2)
|
||||||
self.page_3 = QtWidgets.QWidget()
|
self.page_3 = QtWidgets.QWidget()
|
||||||
self.page_3.setObjectName("page_3")
|
self.page_3.setObjectName("page_3")
|
||||||
self.horizontalLayout_13 = QtWidgets.QHBoxLayout(self.page_3)
|
self.horizontalLayout_13 = QtWidgets.QHBoxLayout(self.page_3)
|
||||||
self.horizontalLayout_13.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.horizontalLayout_13.setObjectName("horizontalLayout_13")
|
self.horizontalLayout_13.setObjectName("horizontalLayout_13")
|
||||||
self.txtPlotSummaryFull = textEditView(self.page_3)
|
self.txtPlotSummaryFull = MDEditCompleter(self.page_3)
|
||||||
self.txtPlotSummaryFull.setObjectName("txtPlotSummaryFull")
|
self.txtPlotSummaryFull.setObjectName("txtPlotSummaryFull")
|
||||||
self.horizontalLayout_13.addWidget(self.txtPlotSummaryFull)
|
self.horizontalLayout_13.addWidget(self.txtPlotSummaryFull)
|
||||||
self.stkPlotSummary.addWidget(self.page_3)
|
self.stkPlotSummary.addWidget(self.page_3)
|
||||||
|
@ -798,7 +789,7 @@ class Ui_MainWindow(object):
|
||||||
self.label_32 = QtWidgets.QLabel(self.tab_3)
|
self.label_32 = QtWidgets.QLabel(self.tab_3)
|
||||||
self.label_32.setObjectName("label_32")
|
self.label_32.setObjectName("label_32")
|
||||||
self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_32)
|
self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_32)
|
||||||
self.txtWorldDescription = textEditCompleter(self.tab_3)
|
self.txtWorldDescription = MDEditCompleter(self.tab_3)
|
||||||
self.txtWorldDescription.setObjectName("txtWorldDescription")
|
self.txtWorldDescription.setObjectName("txtWorldDescription")
|
||||||
self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txtWorldDescription)
|
self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txtWorldDescription)
|
||||||
self.tabWorld.addTab(self.tab_3, "")
|
self.tabWorld.addTab(self.tab_3, "")
|
||||||
|
@ -810,13 +801,13 @@ class Ui_MainWindow(object):
|
||||||
self.label_33 = QtWidgets.QLabel(self.tab_4)
|
self.label_33 = QtWidgets.QLabel(self.tab_4)
|
||||||
self.label_33.setObjectName("label_33")
|
self.label_33.setObjectName("label_33")
|
||||||
self.formLayout_7.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_33)
|
self.formLayout_7.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_33)
|
||||||
self.txtWorldPassion = textEditCompleter(self.tab_4)
|
self.txtWorldPassion = MDEditCompleter(self.tab_4)
|
||||||
self.txtWorldPassion.setObjectName("txtWorldPassion")
|
self.txtWorldPassion.setObjectName("txtWorldPassion")
|
||||||
self.formLayout_7.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.txtWorldPassion)
|
self.formLayout_7.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.txtWorldPassion)
|
||||||
self.label_34 = QtWidgets.QLabel(self.tab_4)
|
self.label_34 = QtWidgets.QLabel(self.tab_4)
|
||||||
self.label_34.setObjectName("label_34")
|
self.label_34.setObjectName("label_34")
|
||||||
self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_34)
|
self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_34)
|
||||||
self.txtWorldConflict = textEditCompleter(self.tab_4)
|
self.txtWorldConflict = MDEditCompleter(self.tab_4)
|
||||||
self.txtWorldConflict.setObjectName("txtWorldConflict")
|
self.txtWorldConflict.setObjectName("txtWorldConflict")
|
||||||
self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txtWorldConflict)
|
self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txtWorldConflict)
|
||||||
self.tabWorld.addTab(self.tab_4, "")
|
self.tabWorld.addTab(self.tab_4, "")
|
||||||
|
@ -839,7 +830,6 @@ class Ui_MainWindow(object):
|
||||||
self.layoutWidget = QtWidgets.QWidget(self.splitterOutlineH)
|
self.layoutWidget = QtWidgets.QWidget(self.splitterOutlineH)
|
||||||
self.layoutWidget.setObjectName("layoutWidget")
|
self.layoutWidget.setObjectName("layoutWidget")
|
||||||
self.verticalLayout_14 = QtWidgets.QVBoxLayout(self.layoutWidget)
|
self.verticalLayout_14 = QtWidgets.QVBoxLayout(self.layoutWidget)
|
||||||
self.verticalLayout_14.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.verticalLayout_14.setObjectName("verticalLayout_14")
|
self.verticalLayout_14.setObjectName("verticalLayout_14")
|
||||||
self.splitterOutlineV = QtWidgets.QSplitter(self.layoutWidget)
|
self.splitterOutlineV = QtWidgets.QSplitter(self.layoutWidget)
|
||||||
self.splitterOutlineV.setOrientation(QtCore.Qt.Vertical)
|
self.splitterOutlineV.setOrientation(QtCore.Qt.Vertical)
|
||||||
|
@ -1265,8 +1255,8 @@ class Ui_MainWindow(object):
|
||||||
self.stack.setCurrentIndex(1)
|
self.stack.setCurrentIndex(1)
|
||||||
self.tabMain.setCurrentIndex(3)
|
self.tabMain.setCurrentIndex(3)
|
||||||
self.tabSummary.setCurrentIndex(0)
|
self.tabSummary.setCurrentIndex(0)
|
||||||
self.tabPersos.setCurrentIndex(0)
|
self.tabPersos.setCurrentIndex(2)
|
||||||
self.tabPlot.setCurrentIndex(0)
|
self.tabPlot.setCurrentIndex(1)
|
||||||
self.comboBox_2.setCurrentIndex(0)
|
self.comboBox_2.setCurrentIndex(0)
|
||||||
self.stkPlotSummary.setCurrentIndex(0)
|
self.stkPlotSummary.setCurrentIndex(0)
|
||||||
self.tabWorld.setCurrentIndex(0)
|
self.tabWorld.setCurrentIndex(0)
|
||||||
|
@ -1432,6 +1422,7 @@ class Ui_MainWindow(object):
|
||||||
from manuskript.ui.cheatSheet import cheatSheet
|
from manuskript.ui.cheatSheet import cheatSheet
|
||||||
from manuskript.ui.editors.mainEditor import mainEditor
|
from manuskript.ui.editors.mainEditor import mainEditor
|
||||||
from manuskript.ui.search import search
|
from manuskript.ui.search import search
|
||||||
|
from manuskript.ui.views.MDEditCompleter import MDEditCompleter
|
||||||
from manuskript.ui.views.basicItemView import basicItemView
|
from manuskript.ui.views.basicItemView import basicItemView
|
||||||
from manuskript.ui.views.characterTreeView import characterTreeView
|
from manuskript.ui.views.characterTreeView import characterTreeView
|
||||||
from manuskript.ui.views.lineEditView import lineEditView
|
from manuskript.ui.views.lineEditView import lineEditView
|
||||||
|
@ -1440,7 +1431,5 @@ from manuskript.ui.views.outlineView import outlineView
|
||||||
from manuskript.ui.views.plotTreeView import plotTreeView
|
from manuskript.ui.views.plotTreeView import plotTreeView
|
||||||
from manuskript.ui.views.sldImportance import sldImportance
|
from manuskript.ui.views.sldImportance import sldImportance
|
||||||
from manuskript.ui.views.storylineView import storylineView
|
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.views.treeView import treeView
|
||||||
from manuskript.ui.welcome import welcome
|
from manuskript.ui.welcome import welcome
|
||||||
|
|
|
@ -379,7 +379,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummarySentence">
|
<widget class="MDEditCompleter" name="txtSummarySentence">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -422,7 +422,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummarySentence_2">
|
<widget class="MDEditCompleter" name="txtSummarySentence_2">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -466,7 +466,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryPara"/>
|
<widget class="MDEditCompleter" name="txtSummaryPara"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lblSummaryWCPara">
|
<widget class="QLabel" name="lblSummaryWCPara">
|
||||||
|
@ -504,7 +504,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryPara_2">
|
<widget class="MDEditCompleter" name="txtSummaryPara_2">
|
||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -542,7 +542,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryPage"/>
|
<widget class="MDEditCompleter" name="txtSummaryPage"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lblSummaryWCPage">
|
<widget class="QLabel" name="lblSummaryWCPage">
|
||||||
|
@ -567,7 +567,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryPage_2">
|
<widget class="MDEditCompleter" name="txtSummaryPage_2">
|
||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -592,7 +592,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryFull"/>
|
<widget class="MDEditCompleter" name="txtSummaryFull"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lblSummaryWCFull">
|
<widget class="QLabel" name="lblSummaryWCFull">
|
||||||
|
@ -774,7 +774,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QTabWidget" name="tabPersos">
|
<widget class="QTabWidget" name="tabPersos">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="info">
|
<widget class="QWidget" name="info">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -831,7 +831,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="textEditView" name="txtPersoMotivation"/>
|
<widget class="MDEditCompleter" name="txtPersoMotivation"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
|
@ -841,7 +841,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="textEditView" name="txtPersoGoal"/>
|
<widget class="MDEditCompleter" name="txtPersoGoal"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
|
@ -851,7 +851,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="textEditView" name="txtPersoConflict"/>
|
<widget class="MDEditCompleter" name="txtPersoConflict"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
<item row="7" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
|
@ -861,7 +861,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="textEditView" name="txtPersoEpiphany"/>
|
<widget class="MDEditCompleter" name="txtPersoEpiphany"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0">
|
<item row="8" column="0">
|
||||||
<widget class="QLabel" name="label_24">
|
<widget class="QLabel" name="label_24">
|
||||||
|
@ -871,7 +871,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="1">
|
<item row="8" column="1">
|
||||||
<widget class="textEditView" name="txtPersoSummarySentence"/>
|
<widget class="MDEditCompleter" name="txtPersoSummarySentence"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<item row="9" column="0">
|
||||||
<widget class="QLabel" name="label_8">
|
<widget class="QLabel" name="label_8">
|
||||||
|
@ -881,7 +881,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="textEditView" name="txtPersoSummaryPara"/>
|
<widget class="MDEditCompleter" name="txtPersoSummaryPara"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="10" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||||
|
@ -964,7 +964,7 @@
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_17">
|
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtPersoSummaryFull"/>
|
<widget class="MDEditCompleter" name="txtPersoSummaryFull"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_22">
|
<layout class="QHBoxLayout" name="horizontalLayout_22">
|
||||||
|
@ -1002,7 +1002,7 @@
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_30">
|
<layout class="QHBoxLayout" name="horizontalLayout_30">
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtPersoNotes"/>
|
<widget class="MDEditCompleter" name="txtPersoNotes"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -1173,7 +1173,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QTabWidget" name="tabPlot">
|
<widget class="QTabWidget" name="tabPlot">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="documentMode">
|
<property name="documentMode">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -1303,10 +1303,10 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="textEditView" name="txtPlotDescription"/>
|
<widget class="MDEditCompleter" name="txtPlotDescription"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="textEditView" name="txtPlotResult"/>
|
<widget class="MDEditCompleter" name="txtPlotResult"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -1338,7 +1338,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_11">
|
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSubPlotSummary"/>
|
<widget class="MDEditCompleter" name="txtSubPlotSummary"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -1461,21 +1461,21 @@
|
||||||
<widget class="QWidget" name="page">
|
<widget class="QWidget" name="page">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtPlotSummaryPara"/>
|
<widget class="MDEditCompleter" name="txtPlotSummaryPara"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_2">
|
<widget class="QWidget" name="page_2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtPlotSummaryPage"/>
|
<widget class="MDEditCompleter" name="txtPlotSummaryPage"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_3">
|
<widget class="QWidget" name="page_3">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtPlotSummaryFull"/>
|
<widget class="MDEditCompleter" name="txtPlotSummaryFull"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -1645,7 +1645,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="textEditCompleter" name="txtWorldDescription"/>
|
<widget class="MDEditCompleter" name="txtWorldDescription"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -1665,7 +1665,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="textEditCompleter" name="txtWorldPassion"/>
|
<widget class="MDEditCompleter" name="txtWorldPassion"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_34">
|
<widget class="QLabel" name="label_34">
|
||||||
|
@ -1675,7 +1675,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="textEditCompleter" name="txtWorldConflict"/>
|
<widget class="MDEditCompleter" name="txtWorldConflict"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -2616,6 +2616,11 @@
|
||||||
<extends>QTextEdit</extends>
|
<extends>QTextEdit</extends>
|
||||||
<header>manuskript.ui.views.textEditView.h</header>
|
<header>manuskript.ui.views.textEditView.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>MDEditCompleter</class>
|
||||||
|
<extends>QTextEdit</extends>
|
||||||
|
<header>manuskript.ui.views.MDEditCompleter.h</header>
|
||||||
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>lineEditView</class>
|
<class>lineEditView</class>
|
||||||
<extends>QLineEdit</extends>
|
<extends>QLineEdit</extends>
|
||||||
|
@ -2683,11 +2688,6 @@
|
||||||
<header>manuskript.ui.search.h</header>
|
<header>manuskript.ui.search.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>textEditCompleter</class>
|
|
||||||
<extends>QTextEdit</extends>
|
|
||||||
<header>manuskript.ui.views.textEditCompleter.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>storylineView</class>
|
<class>storylineView</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
|
|
|
@ -24,11 +24,13 @@ button = p.color(QPalette.Button).name() # Button background
|
||||||
buttonText = p.color(QPalette.ButtonText).name() # Button Text
|
buttonText = p.color(QPalette.ButtonText).name() # Button Text
|
||||||
highlight = p.color(QPalette.Highlight).name() # Other background
|
highlight = p.color(QPalette.Highlight).name() # Other background
|
||||||
highlightedText = p.color(QPalette.HighlightedText).name() # Base Text
|
highlightedText = p.color(QPalette.HighlightedText).name() # Base Text
|
||||||
|
link = p.color(QPalette.Link).name() # Link
|
||||||
|
linkVisited = p.color(QPalette.LinkVisited).name() # Link visited
|
||||||
|
|
||||||
light = p.color(QPalette.Light).name() # Lighter than Button color
|
light = p.color(QPalette.Light).name() # Lighter than Button color
|
||||||
midlight = p.color(QPalette.Midlight).name() # Between Button and Light
|
midlight = p.color(QPalette.Midlight).name() # Between Button and Light
|
||||||
dark = p.color(QPalette.Dark).name() # Darker than Button
|
|
||||||
mid = p.color(QPalette.Mid).name() # Between Button and Dark
|
mid = p.color(QPalette.Mid).name() # Between Button and Dark
|
||||||
|
dark = p.color(QPalette.Dark).name() # Darker than Button
|
||||||
shadow = p.color(QPalette.Shadow).name() # A very dark color
|
shadow = p.color(QPalette.Shadow).name() # A very dark color
|
||||||
|
|
||||||
highlightLight = F.mixColors(highlight, window, .3)
|
highlightLight = F.mixColors(highlight, window, .3)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from PyQt5.QtGui import QTextCursor, QFont, QFontMetrics
|
||||||
from PyQt5.QtWidgets import QAction, qApp, QToolTip, QTextEdit
|
from PyQt5.QtWidgets import QAction, qApp, QToolTip, QTextEdit
|
||||||
|
|
||||||
from manuskript.ui.editors.completer import completer
|
from manuskript.ui.editors.completer import completer
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
from manuskript.ui.views.MDEditView import MDEditView
|
||||||
from manuskript.models import references as Ref
|
from manuskript.models import references as Ref
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -16,10 +16,10 @@ except ImportError:
|
||||||
enchant = None
|
enchant = None
|
||||||
|
|
||||||
|
|
||||||
class textEditCompleter(textEditView):
|
class MDEditCompleter(MDEditView):
|
||||||
def __init__(self, parent=None, index=None, html=None, spellcheck=True, highlighting=False, dict="",
|
def __init__(self, parent=None, index=None, html=None, spellcheck=True, highlighting=False, dict="",
|
||||||
autoResize=False):
|
autoResize=False):
|
||||||
textEditView.__init__(self, parent=parent, index=index, html=html, spellcheck=spellcheck, highlighting=True,
|
MDEditView.__init__(self, parent=parent, index=index, html=html, spellcheck=spellcheck, highlighting=True,
|
||||||
dict=dict, autoResize=autoResize)
|
dict=dict, autoResize=autoResize)
|
||||||
|
|
||||||
self.completer = None
|
self.completer = None
|
||||||
|
@ -30,7 +30,7 @@ class textEditCompleter(textEditView):
|
||||||
self.document().documentLayoutChanged.connect(self.getRefRects)
|
self.document().documentLayoutChanged.connect(self.getRefRects)
|
||||||
|
|
||||||
def setCurrentModelIndex(self, index):
|
def setCurrentModelIndex(self, index):
|
||||||
textEditView.setCurrentModelIndex(self, index)
|
MDEditView.setCurrentModelIndex(self, index)
|
||||||
if self._index and not self.completer:
|
if self._index and not self.completer:
|
||||||
self.setCompleter(completer())
|
self.setCompleter(completer())
|
||||||
|
|
||||||
|
@ -69,10 +69,10 @@ class textEditCompleter(textEditView):
|
||||||
# else:
|
# else:
|
||||||
# QToolTip.hideText()
|
# QToolTip.hideText()
|
||||||
# return True
|
# return True
|
||||||
# return textEditView.event(self, event)
|
# return MDEditView.event(self, event)
|
||||||
|
|
||||||
def createStandardContextMenu(self):
|
def createStandardContextMenu(self):
|
||||||
menu = textEditView.createStandardContextMenu(self)
|
menu = MDEditView.createStandardContextMenu(self)
|
||||||
|
|
||||||
a = QAction(self.tr("Insert reference"), menu)
|
a = QAction(self.tr("Insert reference"), menu)
|
||||||
a.triggered.connect(self.popupCompleter)
|
a.triggered.connect(self.popupCompleter)
|
||||||
|
@ -96,7 +96,7 @@ class textEditCompleter(textEditView):
|
||||||
|
|
||||||
if not self.completer or not isShortcut:
|
if not self.completer or not isShortcut:
|
||||||
self.completer.setVisible(False)
|
self.completer.setVisible(False)
|
||||||
textEditView.keyPressEvent(self, event)
|
MDEditView.keyPressEvent(self, event)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.popupCompleter()
|
self.popupCompleter()
|
||||||
|
@ -110,7 +110,7 @@ class textEditCompleter(textEditView):
|
||||||
self.completer.popup(self.textUnderCursor(select=True))
|
self.completer.popup(self.textUnderCursor(select=True))
|
||||||
|
|
||||||
def mouseMoveEvent(self, event):
|
def mouseMoveEvent(self, event):
|
||||||
textEditView.mouseMoveEvent(self, event)
|
MDEditView.mouseMoveEvent(self, event)
|
||||||
|
|
||||||
onRef = [r for r in self.refRects if r.contains(event.pos())]
|
onRef = [r for r in self.refRects if r.contains(event.pos())]
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ class textEditCompleter(textEditView):
|
||||||
QToolTip.showText(self.mapToGlobal(event.pos()), Ref.tooltip(ref))
|
QToolTip.showText(self.mapToGlobal(event.pos()), Ref.tooltip(ref))
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
textEditView.mouseReleaseEvent(self, event)
|
MDEditView.mouseReleaseEvent(self, event)
|
||||||
onRef = [r for r in self.refRects if r.contains(event.pos())]
|
onRef = [r for r in self.refRects if r.contains(event.pos())]
|
||||||
if onRef:
|
if onRef:
|
||||||
cursor = self.cursorForPosition(event.pos())
|
cursor = self.cursorForPosition(event.pos())
|
||||||
|
@ -137,7 +137,7 @@ class textEditCompleter(textEditView):
|
||||||
qApp.restoreOverrideCursor()
|
qApp.restoreOverrideCursor()
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
textEditView.resizeEvent(self, event)
|
MDEditView.resizeEvent(self, event)
|
||||||
self.getRefRects()
|
self.getRefRects()
|
||||||
|
|
||||||
def getRefRects(self):
|
def getRefRects(self):
|
274
manuskript/ui/views/MDEditView.py
Normal file
274
manuskript/ui/views/MDEditView.py
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# --!-- coding: utf8 --!--
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QRegExp, Qt
|
||||||
|
from PyQt5.QtGui import QTextCursor
|
||||||
|
# from PyQt5.QtWidgets import
|
||||||
|
|
||||||
|
from manuskript.ui.views.textEditView import textEditView
|
||||||
|
from manuskript.ui.highlighters import MarkdownHighlighter
|
||||||
|
# from manuskript.ui.editors.textFormat import textFormat
|
||||||
|
# from manuskript.ui.editors.MDFunctions import MDFormatSelection
|
||||||
|
|
||||||
|
|
||||||
|
class MDEditView(textEditView):
|
||||||
|
def __init__(self, parent=None, index=None, html=None, spellcheck=True,
|
||||||
|
highlighting=False, dict="", autoResize=False):
|
||||||
|
textEditView.__init__(self, parent, index, html, spellcheck,
|
||||||
|
highlighting=True, dict=dict,
|
||||||
|
autoResize=autoResize)
|
||||||
|
|
||||||
|
# Highlighter
|
||||||
|
self._textFormat = "md"
|
||||||
|
self._highlighterClass = MarkdownHighlighter
|
||||||
|
|
||||||
|
if index:
|
||||||
|
# We have to setup things anew, for the highlighter notably
|
||||||
|
self.setCurrentModelIndex(index)
|
||||||
|
|
||||||
|
# def focusInEvent(self, event):
|
||||||
|
# """Finds textFormatter and attach them to that view."""
|
||||||
|
# textEditView.focusInEvent(self, event)
|
||||||
|
#
|
||||||
|
# p = self.parent()
|
||||||
|
# while p.parent():
|
||||||
|
# p = p.parent()
|
||||||
|
#
|
||||||
|
# if self._index:
|
||||||
|
# for tF in p.findChildren(textFormat, QRegExp(".*"),
|
||||||
|
# Qt.FindChildrenRecursively):
|
||||||
|
# tF.updateFromIndex(self._index)
|
||||||
|
# tF.setTextEdit(self)
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# FORMATTING (#FIXME)
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
def applyFormat(self, _format):
|
||||||
|
|
||||||
|
if self._textFormat == "md":
|
||||||
|
if _format == "Bold": self.bold()
|
||||||
|
elif _format == "Italic": self.italic()
|
||||||
|
elif _format == "Code": self.verbatim()
|
||||||
|
elif _format == "Clear": self.clearFormat()
|
||||||
|
|
||||||
|
def bold(self): self.insertFormattingMarkup("**")
|
||||||
|
def italic(self): self.insertFormattingMarkup("*")
|
||||||
|
def strike(self): self.insertFormattingMarkup("~~")
|
||||||
|
def verbatim(self): self.insertFormattingMarkup("`")
|
||||||
|
def superscript(self): self.insertFormattingMarkup("^")
|
||||||
|
def subscript(self): self.insertFormattingMarkup("~")
|
||||||
|
|
||||||
|
def selectWord(self, cursor):
|
||||||
|
if cursor.selectedText():
|
||||||
|
return
|
||||||
|
end = cursor.selectionEnd()
|
||||||
|
cursor.movePosition(QTextCursor.StartOfWord)
|
||||||
|
cursor.setPosition(end, QTextCursor.KeepAnchor)
|
||||||
|
cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor)
|
||||||
|
|
||||||
|
def selectBlock(self, cursor):
|
||||||
|
cursor.movePosition(QTextCursor.StartOfBlock)
|
||||||
|
cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor)
|
||||||
|
|
||||||
|
def comment(self):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
|
||||||
|
# Select begining and end of words
|
||||||
|
self.selectWord(cursor)
|
||||||
|
|
||||||
|
if cursor.hasSelection():
|
||||||
|
text = cursor.selectedText()
|
||||||
|
cursor.insertText("<!-- " + text + " -->")
|
||||||
|
else:
|
||||||
|
cursor.insertText("<!-- -->")
|
||||||
|
cursor.movePosition(QTextCursor.PreviousCharacter,
|
||||||
|
QTextCursor.MoveAnchor, 4)
|
||||||
|
self.setTextCursor(cursor)
|
||||||
|
|
||||||
|
def commentLine(self):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
|
||||||
|
start = cursor.selectionStart()
|
||||||
|
end = cursor.selectionEnd()
|
||||||
|
block = self.document().findBlock(start)
|
||||||
|
block2 = self.document().findBlock(end)
|
||||||
|
|
||||||
|
if True:
|
||||||
|
# Method 1
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
while block.isValid():
|
||||||
|
self.commentBlock(block)
|
||||||
|
if block == block2: break
|
||||||
|
block = block.next()
|
||||||
|
cursor.endEditBlock()
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Method 2
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
cursor.setPosition(block.position())
|
||||||
|
cursor.insertText("<!--\n")
|
||||||
|
cursor.setPosition(block2.position() + block2.length() - 1)
|
||||||
|
cursor.insertText("\n-->")
|
||||||
|
cursor.endEditBlock()
|
||||||
|
|
||||||
|
def commentBlock(self, block):
|
||||||
|
cursor = QTextCursor(block)
|
||||||
|
text = block.text()
|
||||||
|
if text[:5] == "<!-- " and \
|
||||||
|
text[-4:] == " -->":
|
||||||
|
text2 = text[5:-4]
|
||||||
|
else:
|
||||||
|
text2 = "<!-- " + text + " -->"
|
||||||
|
self.selectBlock(cursor)
|
||||||
|
cursor.insertText(text2)
|
||||||
|
|
||||||
|
def insertFormattingMarkup(self, markup):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
|
||||||
|
# Select begining and end of words
|
||||||
|
self.selectWord(cursor)
|
||||||
|
|
||||||
|
if cursor.hasSelection():
|
||||||
|
start = cursor.selectionStart()
|
||||||
|
end = cursor.selectionEnd() + len(markup)
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
cursor.setPosition(start)
|
||||||
|
cursor.insertText(markup)
|
||||||
|
cursor.setPosition(end)
|
||||||
|
cursor.insertText(markup)
|
||||||
|
cursor.endEditBlock()
|
||||||
|
cursor.movePosition(QTextCursor.PreviousCharacter,
|
||||||
|
QTextCursor.KeepAnchor, len(markup))
|
||||||
|
#self.setTextCursor(cursor)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Insert markup twice (for opening and closing around the cursor),
|
||||||
|
# and then move the cursor to be between the pair.
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
cursor.insertText(markup)
|
||||||
|
cursor.insertText(markup)
|
||||||
|
cursor.movePosition(QTextCursor.PreviousCharacter,
|
||||||
|
QTextCursor.MoveAnchor, len(markup))
|
||||||
|
cursor.endEditBlock()
|
||||||
|
self.setTextCursor(cursor)
|
||||||
|
|
||||||
|
def clearFormat(self):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
text = cursor.selectedText()
|
||||||
|
if not text:
|
||||||
|
self.selectBlock(cursor)
|
||||||
|
text = cursor.selectedText()
|
||||||
|
text = self.clearedFormat(text)
|
||||||
|
cursor.insertText(text)
|
||||||
|
|
||||||
|
def clearedFormat(self, text):
|
||||||
|
# FIXME: clear also block formats
|
||||||
|
for reg, rep, flags in [
|
||||||
|
("\*\*(.*?)\*\*", "\\1", None), # bold
|
||||||
|
("__(.*?)__", "\\1", None), # bold
|
||||||
|
("\*(.*?)\*", "\\1", None), # emphasis
|
||||||
|
("_(.*?)_", "\\1", None), # emphasis
|
||||||
|
("`(.*?)`", "\\1", None), # verbatim
|
||||||
|
("~~(.*?)~~", "\\1", None), # strike
|
||||||
|
("\^(.*?)\^", "\\1", None), # superscript
|
||||||
|
("~(.*?)~", "\\1", None), # subscript
|
||||||
|
("<!--(.*)-->", "\\1", re.S), # comments
|
||||||
|
|
||||||
|
|
||||||
|
# LINES OR BLOCKS
|
||||||
|
(r"^#*\s*(.+?)\s*", "\\1", re.M), # ATX
|
||||||
|
(r"^[=-]*$", "", re.M), # Setext
|
||||||
|
(r"^`*$", "", re.M), # Code block fenced
|
||||||
|
(r"^\s*[-+*]\s*(.*?)\s*$", "\\1", re.M), # Bullet List
|
||||||
|
(r"^\s*[0-9a-z](\.|\))\s*(.*?)\s*$", "\\2", re.M), # Bullet List
|
||||||
|
(r"\s*[>\s]*(.*?)\s*$", "\\1", re.M), # Code block and blockquote
|
||||||
|
|
||||||
|
]:
|
||||||
|
text = re.sub(reg, rep, text, flags if flags else 0)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def clearedFormatForStats(self, text):
|
||||||
|
# Remove stuff that musn't be counted
|
||||||
|
# FIXME: clear also block formats
|
||||||
|
for reg, rep, flags in [
|
||||||
|
("<!--.*-->", "", re.S), # comments
|
||||||
|
]:
|
||||||
|
text = re.sub(reg, rep, text, flags if flags else 0)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def titleSetext(self, level):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
# Is it already a Setext header?
|
||||||
|
if cursor.block().userState() in [
|
||||||
|
MS.MarkdownStateSetextHeading1Line2,
|
||||||
|
MS.MarkdownStateSetextHeading2Line2]:
|
||||||
|
cursor.movePosition(QTextCursor.PreviousBlock)
|
||||||
|
|
||||||
|
text = cursor.block().text()
|
||||||
|
|
||||||
|
if cursor.block().userState() in [
|
||||||
|
MS.MarkdownStateSetextHeading1Line1,
|
||||||
|
MS.MarkdownStateSetextHeading2Line1]:
|
||||||
|
# Need to remove line below
|
||||||
|
c = QTextCursor(cursor.block().next())
|
||||||
|
self.selectBlock(c)
|
||||||
|
c.insertText("")
|
||||||
|
|
||||||
|
char = "=" if level == 1 else "-"
|
||||||
|
text = re.sub("^#*\s*(.*)\s*#*", "\\1", text) # Removes #
|
||||||
|
sub = char * len(text)
|
||||||
|
text = text + "\n" + sub
|
||||||
|
|
||||||
|
self.selectBlock(cursor)
|
||||||
|
cursor.insertText(text)
|
||||||
|
cursor.endEditBlock()
|
||||||
|
|
||||||
|
def titleATX(self, level):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
text = cursor.block().text()
|
||||||
|
|
||||||
|
# Are we in a Setext Header?
|
||||||
|
if cursor.block().userState() in [
|
||||||
|
MS.MarkdownStateSetextHeading1Line1,
|
||||||
|
MS.MarkdownStateSetextHeading2Line1]:
|
||||||
|
# Need to remove line below
|
||||||
|
cursor.beginEditBlock()
|
||||||
|
c = QTextCursor(cursor.block().next())
|
||||||
|
self.selectBlock(c)
|
||||||
|
c.insertText("")
|
||||||
|
|
||||||
|
self.selectBlock(cursor)
|
||||||
|
cursor.insertText(text)
|
||||||
|
cursor.endEditBlock()
|
||||||
|
return
|
||||||
|
|
||||||
|
elif cursor.block().userState() in [
|
||||||
|
MS.MarkdownStateSetextHeading1Line2,
|
||||||
|
MS.MarkdownStateSetextHeading2Line2]:
|
||||||
|
cursor.movePosition(QTextCursor.PreviousBlock)
|
||||||
|
self.setTextCursor(cursor)
|
||||||
|
self.titleATX(level)
|
||||||
|
return
|
||||||
|
|
||||||
|
m = re.match("^(#+)(\s*)(.+)", text)
|
||||||
|
if m:
|
||||||
|
pre = m.group(1)
|
||||||
|
space = m.group(2)
|
||||||
|
txt = m.group(3)
|
||||||
|
|
||||||
|
if len(pre) == level:
|
||||||
|
# Remove title
|
||||||
|
text = txt
|
||||||
|
else:
|
||||||
|
text = "#" * level + space + txt
|
||||||
|
|
||||||
|
else:
|
||||||
|
text = "#" * level + " " + text
|
||||||
|
|
||||||
|
self.selectBlock(cursor)
|
||||||
|
cursor.insertText(text)
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'manuskript/ui/views/basicItemView_ui.ui'
|
# Form implementation generated from reading ui file 'manuskript/ui/views/basicItemView_ui.ui'
|
||||||
#
|
#
|
||||||
# Created: Thu Mar 3 17:26:11 2016
|
# Created by: PyQt5 UI code generator 5.5.1
|
||||||
# by: PyQt5 UI code generator 5.2.1
|
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ class Ui_basicItemView(object):
|
||||||
self.label_9 = QtWidgets.QLabel(basicItemView)
|
self.label_9 = QtWidgets.QLabel(basicItemView)
|
||||||
self.label_9.setObjectName("label_9")
|
self.label_9.setObjectName("label_9")
|
||||||
self.verticalLayout.addWidget(self.label_9)
|
self.verticalLayout.addWidget(self.label_9)
|
||||||
self.txtSummaryFull = textEditView(basicItemView)
|
self.txtSummaryFull = MDEditCompleter(basicItemView)
|
||||||
self.txtSummaryFull.setObjectName("txtSummaryFull")
|
self.txtSummaryFull.setObjectName("txtSummaryFull")
|
||||||
self.verticalLayout.addWidget(self.txtSummaryFull)
|
self.verticalLayout.addWidget(self.txtSummaryFull)
|
||||||
|
|
||||||
|
@ -67,6 +66,6 @@ class Ui_basicItemView(object):
|
||||||
self.txtSummarySentence.setPlaceholderText(_translate("basicItemView", "One line summary"))
|
self.txtSummarySentence.setPlaceholderText(_translate("basicItemView", "One line summary"))
|
||||||
self.label_9.setText(_translate("basicItemView", "Few sentences summary:"))
|
self.label_9.setText(_translate("basicItemView", "Few sentences summary:"))
|
||||||
|
|
||||||
|
from manuskript.ui.views.MDEditCompleter import MDEditCompleter
|
||||||
from manuskript.ui.views.cmbOutlineCharacterChoser import cmbOutlineCharacterChoser
|
from manuskript.ui.views.cmbOutlineCharacterChoser import cmbOutlineCharacterChoser
|
||||||
from manuskript.ui.views.lineEditView import lineEditView
|
from manuskript.ui.views.lineEditView import lineEditView
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
|
||||||
|
|
|
@ -101,15 +101,15 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryFull"/>
|
<widget class="MDEditCompleter" name="txtSummaryFull"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>textEditView</class>
|
<class>MDEditCompleter</class>
|
||||||
<extends>QTextEdit</extends>
|
<extends>QTextEdit</extends>
|
||||||
<header>manuskript.ui.views.textEditView.h</header>
|
<header>manuskript.ui.views.MDEditCompleter.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>cmbOutlineCharacterChoser</class>
|
<class>cmbOutlineCharacterChoser</class>
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'manuskript/ui/views/metadataView_ui.ui'
|
# Form implementation generated from reading ui file 'manuskript/ui/views/metadataView_ui.ui'
|
||||||
#
|
#
|
||||||
# Created: Fri Apr 8 14:24:47 2016
|
# Created by: PyQt5 UI code generator 5.5.1
|
||||||
# by: PyQt5 UI code generator 5.2.1
|
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
@ -14,8 +13,8 @@ class Ui_metadataView(object):
|
||||||
metadataView.setObjectName("metadataView")
|
metadataView.setObjectName("metadataView")
|
||||||
metadataView.resize(400, 537)
|
metadataView.resize(400, 537)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(metadataView)
|
self.verticalLayout = QtWidgets.QVBoxLayout(metadataView)
|
||||||
self.verticalLayout.setSpacing(0)
|
|
||||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout.setSpacing(0)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.grpProperties = collapsibleGroupBox2(metadataView)
|
self.grpProperties = collapsibleGroupBox2(metadataView)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
@ -27,8 +26,8 @@ class Ui_metadataView(object):
|
||||||
self.grpProperties.setCheckable(True)
|
self.grpProperties.setCheckable(True)
|
||||||
self.grpProperties.setObjectName("grpProperties")
|
self.grpProperties.setObjectName("grpProperties")
|
||||||
self.verticalLayout_28 = QtWidgets.QVBoxLayout(self.grpProperties)
|
self.verticalLayout_28 = QtWidgets.QVBoxLayout(self.grpProperties)
|
||||||
self.verticalLayout_28.setSpacing(0)
|
|
||||||
self.verticalLayout_28.setContentsMargins(0, 0, 0, 0)
|
self.verticalLayout_28.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout_28.setSpacing(0)
|
||||||
self.verticalLayout_28.setObjectName("verticalLayout_28")
|
self.verticalLayout_28.setObjectName("verticalLayout_28")
|
||||||
self.properties = propertiesView(self.grpProperties)
|
self.properties = propertiesView(self.grpProperties)
|
||||||
self.properties.setMinimumSize(QtCore.QSize(0, 50))
|
self.properties.setMinimumSize(QtCore.QSize(0, 50))
|
||||||
|
@ -40,8 +39,8 @@ class Ui_metadataView(object):
|
||||||
self.grpSummary.setCheckable(True)
|
self.grpSummary.setCheckable(True)
|
||||||
self.grpSummary.setObjectName("grpSummary")
|
self.grpSummary.setObjectName("grpSummary")
|
||||||
self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.grpSummary)
|
self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.grpSummary)
|
||||||
self.verticalLayout_22.setSpacing(0)
|
|
||||||
self.verticalLayout_22.setContentsMargins(0, 0, 0, 0)
|
self.verticalLayout_22.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout_22.setSpacing(0)
|
||||||
self.verticalLayout_22.setObjectName("verticalLayout_22")
|
self.verticalLayout_22.setObjectName("verticalLayout_22")
|
||||||
self.txtSummarySentence = lineEditView(self.grpSummary)
|
self.txtSummarySentence = lineEditView(self.grpSummary)
|
||||||
self.txtSummarySentence.setInputMask("")
|
self.txtSummarySentence.setInputMask("")
|
||||||
|
@ -53,10 +52,9 @@ class Ui_metadataView(object):
|
||||||
self.line.setLineWidth(0)
|
self.line.setLineWidth(0)
|
||||||
self.line.setMidLineWidth(0)
|
self.line.setMidLineWidth(0)
|
||||||
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
|
||||||
self.line.setObjectName("line")
|
self.line.setObjectName("line")
|
||||||
self.verticalLayout_22.addWidget(self.line)
|
self.verticalLayout_22.addWidget(self.line)
|
||||||
self.txtSummaryFull = textEditView(self.grpSummary)
|
self.txtSummaryFull = MDEditCompleter(self.grpSummary)
|
||||||
self.txtSummaryFull.setFrameShape(QtWidgets.QFrame.NoFrame)
|
self.txtSummaryFull.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
self.txtSummaryFull.setObjectName("txtSummaryFull")
|
self.txtSummaryFull.setObjectName("txtSummaryFull")
|
||||||
self.verticalLayout_22.addWidget(self.txtSummaryFull)
|
self.verticalLayout_22.addWidget(self.txtSummaryFull)
|
||||||
|
@ -66,10 +64,10 @@ class Ui_metadataView(object):
|
||||||
self.grpNotes.setCheckable(True)
|
self.grpNotes.setCheckable(True)
|
||||||
self.grpNotes.setObjectName("grpNotes")
|
self.grpNotes.setObjectName("grpNotes")
|
||||||
self.horizontalLayout_29 = QtWidgets.QHBoxLayout(self.grpNotes)
|
self.horizontalLayout_29 = QtWidgets.QHBoxLayout(self.grpNotes)
|
||||||
self.horizontalLayout_29.setSpacing(0)
|
|
||||||
self.horizontalLayout_29.setContentsMargins(0, 0, 0, 0)
|
self.horizontalLayout_29.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.horizontalLayout_29.setSpacing(0)
|
||||||
self.horizontalLayout_29.setObjectName("horizontalLayout_29")
|
self.horizontalLayout_29.setObjectName("horizontalLayout_29")
|
||||||
self.txtNotes = textEditCompleter(self.grpNotes)
|
self.txtNotes = MDEditCompleter(self.grpNotes)
|
||||||
self.txtNotes.setFrameShape(QtWidgets.QFrame.NoFrame)
|
self.txtNotes.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
self.txtNotes.setObjectName("txtNotes")
|
self.txtNotes.setObjectName("txtNotes")
|
||||||
self.horizontalLayout_29.addWidget(self.txtNotes)
|
self.horizontalLayout_29.addWidget(self.txtNotes)
|
||||||
|
@ -79,8 +77,8 @@ class Ui_metadataView(object):
|
||||||
self.grpRevisions.setCheckable(True)
|
self.grpRevisions.setCheckable(True)
|
||||||
self.grpRevisions.setObjectName("grpRevisions")
|
self.grpRevisions.setObjectName("grpRevisions")
|
||||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.grpRevisions)
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.grpRevisions)
|
||||||
self.verticalLayout_2.setSpacing(0)
|
|
||||||
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout_2.setSpacing(0)
|
||||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
self.revisions = revisions(self.grpRevisions)
|
self.revisions = revisions(self.grpRevisions)
|
||||||
self.revisions.setMinimumSize(QtCore.QSize(0, 50))
|
self.revisions.setMinimumSize(QtCore.QSize(0, 50))
|
||||||
|
@ -103,8 +101,7 @@ class Ui_metadataView(object):
|
||||||
self.grpRevisions.setTitle(_translate("metadataView", "Revisions"))
|
self.grpRevisions.setTitle(_translate("metadataView", "Revisions"))
|
||||||
|
|
||||||
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
|
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
|
||||||
from manuskript.ui.views.textEditView import textEditView
|
|
||||||
from manuskript.ui.views.textEditCompleter import textEditCompleter
|
|
||||||
from manuskript.ui.views.propertiesView import propertiesView
|
|
||||||
from manuskript.ui.views.lineEditView import lineEditView
|
|
||||||
from manuskript.ui.revisions import revisions
|
from manuskript.ui.revisions import revisions
|
||||||
|
from manuskript.ui.views.MDEditCompleter import MDEditCompleter
|
||||||
|
from manuskript.ui.views.lineEditView import lineEditView
|
||||||
|
from manuskript.ui.views.propertiesView import propertiesView
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditView" name="txtSummaryFull">
|
<widget class="MDEditCompleter" name="txtSummaryFull">
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="textEditCompleter" name="txtNotes">
|
<widget class="MDEditCompleter" name="txtNotes">
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -228,9 +228,9 @@
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>textEditView</class>
|
<class>MDEditCompleter</class>
|
||||||
<extends>QTextEdit</extends>
|
<extends>QTextEdit</extends>
|
||||||
<header>manuskript.ui.views.textEditView.h</header>
|
<header>manuskript.ui.views.MDEditCompleter.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>lineEditView</class>
|
<class>lineEditView</class>
|
||||||
|
@ -249,11 +249,6 @@
|
||||||
<header>manuskript.ui.views.propertiesView.h</header>
|
<header>manuskript.ui.views.propertiesView.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>textEditCompleter</class>
|
|
||||||
<extends>QTextEdit</extends>
|
|
||||||
<header>manuskript.ui.views.textEditCompleter.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>revisions</class>
|
<class>revisions</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
|
|
|
@ -7,13 +7,10 @@ from PyQt5.QtGui import QTextBlockFormat, QTextCharFormat, QFont, QColor, QIcon,
|
||||||
from PyQt5.QtWidgets import QWidget, QTextEdit, qApp, QAction, QMenu
|
from PyQt5.QtWidgets import QWidget, QTextEdit, qApp, QAction, QMenu
|
||||||
|
|
||||||
from manuskript import settings
|
from manuskript import settings
|
||||||
from manuskript.enums import Outline
|
from manuskript.enums import Outline, World, Character, Plot
|
||||||
from manuskript import functions as F
|
from manuskript import functions as F
|
||||||
from manuskript.models.outlineModel import outlineModel
|
from manuskript.models.outlineModel import outlineModel
|
||||||
from manuskript.ui.editors.MDFunctions import MDFormatSelection
|
from manuskript.ui.highlighters import BasicHighlighter
|
||||||
from manuskript.ui.editors.MMDHighlighter import MMDHighlighter
|
|
||||||
from manuskript.ui.editors.basicHighlighter import basicHighlighter
|
|
||||||
from manuskript.ui.editors.textFormat import textFormat
|
|
||||||
from manuskript.ui import style as S
|
from manuskript.ui import style as S
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -23,8 +20,8 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class textEditView(QTextEdit):
|
class textEditView(QTextEdit):
|
||||||
def __init__(self, parent=None, index=None, html=None, spellcheck=True, highlighting=False, dict="",
|
def __init__(self, parent=None, index=None, html=None, spellcheck=True,
|
||||||
autoResize=False):
|
highlighting=False, dict="", autoResize=False):
|
||||||
QTextEdit.__init__(self, parent)
|
QTextEdit.__init__(self, parent)
|
||||||
self._column = Outline.text
|
self._column = Outline.text
|
||||||
self._index = None
|
self._index = None
|
||||||
|
@ -42,16 +39,18 @@ class textEditView(QTextEdit):
|
||||||
# position, so we only have it's ID as reference. We store it to
|
# position, so we only have it's ID as reference. We store it to
|
||||||
# update at the propper time.
|
# update at the propper time.
|
||||||
self._updateIndexFromID = None
|
self._updateIndexFromID = None
|
||||||
|
self._themeData = None
|
||||||
|
self._highlighterClass = BasicHighlighter
|
||||||
|
|
||||||
self.spellcheck = spellcheck
|
self.spellcheck = spellcheck
|
||||||
self.currentDict = dict if dict else settings.dict
|
self.currentDict = dict if dict else settings.dict
|
||||||
|
self._defaultFontSize = qApp.font().pointSize()
|
||||||
self.highlighter = None
|
self.highlighter = None
|
||||||
self.setAutoResize(autoResize)
|
self.setAutoResize(autoResize)
|
||||||
self._defaultBlockFormat = QTextBlockFormat()
|
self._defaultBlockFormat = QTextBlockFormat()
|
||||||
self._defaultCharFormat = QTextCharFormat()
|
self._defaultCharFormat = QTextCharFormat()
|
||||||
self.highlightWord = ""
|
self.highlightWord = ""
|
||||||
self.highligtCS = False
|
self.highligtCS = False
|
||||||
self.defaultFontPointSize = qApp.font().pointSize()
|
|
||||||
self._dict = None
|
self._dict = None
|
||||||
# self.document().contentsChanged.connect(self.submit, F.AUC)
|
# self.document().contentsChanged.connect(self.submit, F.AUC)
|
||||||
|
|
||||||
|
@ -80,7 +79,8 @@ class textEditView(QTextEdit):
|
||||||
# Spellchecking
|
# Spellchecking
|
||||||
if enchant and self.spellcheck:
|
if enchant and self.spellcheck:
|
||||||
try:
|
try:
|
||||||
self._dict = enchant.Dict(self.currentDict if self.currentDict else self.getDefaultLocale())
|
self._dict = enchant.Dict(self.currentDict if self.currentDict
|
||||||
|
else self.getDefaultLocale())
|
||||||
except enchant.errors.DictNotFoundError:
|
except enchant.errors.DictNotFoundError:
|
||||||
self.spellcheck = False
|
self.spellcheck = False
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ class textEditView(QTextEdit):
|
||||||
self.spellcheck = False
|
self.spellcheck = False
|
||||||
|
|
||||||
if self._highlighting and not self.highlighter:
|
if self._highlighting and not self.highlighter:
|
||||||
self.highlighter = basicHighlighter(self)
|
self.highlighter = self._highlighterClass(self)
|
||||||
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
|
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
|
||||||
|
|
||||||
def getDefaultLocale(self):
|
def getDefaultLocale(self):
|
||||||
|
@ -177,27 +177,11 @@ class textEditView(QTextEdit):
|
||||||
self.updateText()
|
self.updateText()
|
||||||
|
|
||||||
def setupEditorForIndex(self, index):
|
def setupEditorForIndex(self, index):
|
||||||
# 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, Outline.notes]:
|
|
||||||
self._textFormat = "text"
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._textFormat = "md"
|
|
||||||
|
|
||||||
# Setting highlighter
|
# Setting highlighter
|
||||||
if self._highlighting:
|
if self._highlighting:
|
||||||
item = index.internalPointer()
|
self.highlighter = self._highlighterClass(self)
|
||||||
if self._column in [Outline.text, Outline.notes]:
|
|
||||||
self.highlighter = MMDHighlighter(self)
|
|
||||||
else:
|
|
||||||
self.highlighter = basicHighlighter(self)
|
|
||||||
|
|
||||||
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
|
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
|
||||||
|
self.highlighter.updateColorScheme()
|
||||||
|
|
||||||
def loadFontSettings(self):
|
def loadFontSettings(self):
|
||||||
if self._fromTheme or \
|
if self._fromTheme or \
|
||||||
|
@ -209,8 +193,10 @@ class textEditView(QTextEdit):
|
||||||
opt = settings.textEditor
|
opt = settings.textEditor
|
||||||
f = QFont()
|
f = QFont()
|
||||||
f.fromString(opt["font"])
|
f.fromString(opt["font"])
|
||||||
background = opt["background"] if not opt["backgroundTransparent"] else "transparent"
|
background = (opt["background"] if not opt["backgroundTransparent"]
|
||||||
foreground = opt["fontColor"] if not opt["backgroundTransparent"] else S.text
|
else "transparent")
|
||||||
|
foreground = opt["fontColor"] # if not opt["backgroundTransparent"]
|
||||||
|
# else S.text
|
||||||
# self.setFont(f)
|
# self.setFont(f)
|
||||||
self.setStyleSheet("""QTextEdit{{
|
self.setStyleSheet("""QTextEdit{{
|
||||||
background: {bg};
|
background: {bg};
|
||||||
|
@ -230,6 +216,7 @@ class textEditView(QTextEdit):
|
||||||
maxWidth = "max-width: {}px;".format(opt["maxWidth"]) if opt["maxWidth"] else "",
|
maxWidth = "max-width: {}px;".format(opt["maxWidth"]) if opt["maxWidth"] else "",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self._defaultFontSize = f.pointSize()
|
||||||
|
|
||||||
# We set the parent background to the editor's background in case
|
# We set the parent background to the editor's background in case
|
||||||
# there are margins. We check that the parent class is a QWidget because
|
# there are margins. We check that the parent class is a QWidget because
|
||||||
|
@ -266,6 +253,7 @@ class textEditView(QTextEdit):
|
||||||
self._defaultBlockFormat = bf
|
self._defaultBlockFormat = bf
|
||||||
|
|
||||||
if self.highlighter:
|
if self.highlighter:
|
||||||
|
self.highlighter.updateColorScheme()
|
||||||
self.highlighter.setMisspelledColor(QColor(opt["misspelled"]))
|
self.highlighter.setMisspelledColor(QColor(opt["misspelled"]))
|
||||||
self.highlighter.setDefaultCharFormat(self._defaultCharFormat)
|
self.highlighter.setDefaultCharFormat(self._defaultCharFormat)
|
||||||
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
|
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
|
||||||
|
@ -415,7 +403,8 @@ class textEditView(QTextEdit):
|
||||||
self.sizeChange()
|
self.sizeChange()
|
||||||
|
|
||||||
def sizeChange(self):
|
def sizeChange(self):
|
||||||
docHeight = self.document().size().height()
|
opt = settings.textEditor
|
||||||
|
docHeight = self.document().size().height() + 2 * opt["marginsTB"]
|
||||||
if self.heightMin <= docHeight <= self.heightMax:
|
if self.heightMin <= docHeight <= self.heightMax:
|
||||||
self.setMinimumHeight(docHeight)
|
self.setMinimumHeight(docHeight)
|
||||||
|
|
||||||
|
@ -461,6 +450,31 @@ class textEditView(QTextEdit):
|
||||||
Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
|
Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
|
||||||
QTextEdit.mousePressEvent(self, event)
|
QTextEdit.mousePressEvent(self, event)
|
||||||
|
|
||||||
|
def wheelEvent(self, event):
|
||||||
|
"""
|
||||||
|
We catch wheelEvent if key modifier is CTRL to change font size.
|
||||||
|
Note: this should be in a class specific for main textEditView (#TODO).
|
||||||
|
"""
|
||||||
|
if event.modifiers() & Qt.ControlModifier:
|
||||||
|
# Get the wheel angle.
|
||||||
|
d = event.angleDelta().y() / 120
|
||||||
|
|
||||||
|
# Update settings
|
||||||
|
f = QFont()
|
||||||
|
f.fromString(settings.textEditor["font"])
|
||||||
|
f.setPointSizeF(f.pointSizeF() + d)
|
||||||
|
settings.textEditor["font"] = f.toString()
|
||||||
|
|
||||||
|
# Update font to all textEditView. Drastically.
|
||||||
|
for w in F.mainWindow().findChildren(textEditView, QRegExp(".*")):
|
||||||
|
w.loadFontSettings()
|
||||||
|
|
||||||
|
# We tell the world that we accepted this event
|
||||||
|
event.accept()
|
||||||
|
return
|
||||||
|
|
||||||
|
QTextEdit.wheelEvent(self, event)
|
||||||
|
|
||||||
class SpellAction(QAction):
|
class SpellAction(QAction):
|
||||||
"""A special QAction that returns the text in a signal. Used for spellckech."""
|
"""A special QAction that returns the text in a signal. Used for spellckech."""
|
||||||
|
|
||||||
|
@ -560,32 +574,6 @@ class textEditView(QTextEdit):
|
||||||
QTextEdit.focusOutEvent(self, event)
|
QTextEdit.focusOutEvent(self, event)
|
||||||
self.submit()
|
self.submit()
|
||||||
|
|
||||||
def focusInEvent(self, event):
|
|
||||||
"""Finds textFormatter and attach them to that view."""
|
|
||||||
QTextEdit.focusInEvent(self, event)
|
|
||||||
|
|
||||||
p = self.parent()
|
|
||||||
while p.parent():
|
|
||||||
p = p.parent()
|
|
||||||
|
|
||||||
if self._index:
|
|
||||||
for tF in p.findChildren(textFormat, QRegExp(".*"), Qt.FindChildrenRecursively):
|
|
||||||
tF.updateFromIndex(self._index)
|
|
||||||
tF.setTextEdit(self)
|
|
||||||
|
|
||||||
def applyFormat(self, _format):
|
|
||||||
|
|
||||||
if self._textFormat == "md":
|
|
||||||
|
|
||||||
if _format == "Bold":
|
|
||||||
MDFormatSelection(self, 0)
|
|
||||||
elif _format == "Italic":
|
|
||||||
MDFormatSelection(self, 1)
|
|
||||||
elif _format == "Code":
|
|
||||||
MDFormatSelection(self, 2)
|
|
||||||
elif _format == "Clear":
|
|
||||||
MDFormatSelection(self)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# KEYBOARD SHORTCUTS
|
# KEYBOARD SHORTCUTS
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -596,7 +584,7 @@ class textEditView(QTextEdit):
|
||||||
edit that has focus. So we can pass it the call for documents
|
edit that has focus. So we can pass it the call for documents
|
||||||
edits like: duplicate, move up, etc.
|
edits like: duplicate, move up, etc.
|
||||||
"""
|
"""
|
||||||
if self._index and self._column == Outline.text.value:
|
if self._index and self._column == Outline.text:
|
||||||
function = getattr(F.mainWindow().treeRedacOutline, functionName)
|
function = getattr(F.mainWindow().treeRedacOutline, functionName)
|
||||||
function()
|
function()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue