Adds: ability to split scenes at custom points. #200

This commit is contained in:
Olivier Keshavjee 2017-11-09 15:18:21 +01:00
parent 24607bca59
commit a231721bdb
9 changed files with 115 additions and 41 deletions

View file

@ -10,9 +10,9 @@ from manuskript.importer.pandocImporters import markdownPandocImporter, \
importers = [
# Internal
opmlImporter,
folderImporter,
markdownImporter,
folderImporter,
opmlImporter,
# Pandoc
markdownPandocImporter,

View file

@ -88,8 +88,8 @@ class markdownImporter(abstractImporter):
return child
ATXHeader = re.compile(r"(\#+)\s*(.+?)\s*\#*$")
setextHeader1 = re.compile(r"(.+)\n===+$", re.MULTILINE)
setextHeader2 = re.compile(r"(.+)\n---+$", re.MULTILINE)
setextHeader1 = re.compile(r"([^\#-=].+)\n(===+)$", re.MULTILINE)
setextHeader2 = re.compile(r"([^\#-=].+)\n(---+)$", re.MULTILINE)
# We store the level of each item in a temporary var
parent.__miLevel = 0 # markdown importer header level
@ -117,14 +117,15 @@ class markdownImporter(abstractImporter):
# Check setext header
m = setextHeader1.match(l2)
if not header and m:
if not header and m and len(m.group(1)) == len(m.group(2)):
header = True
level = 1
name = m.group(1)
skipNextLine = True
m = setextHeader2.match(l2)
if not header and m:
if not header and m and len(m.group(1)) == len(m.group(2)):
header = True
level = 2
name = m.group(1)
@ -160,7 +161,8 @@ class markdownImporter(abstractImporter):
# So we make it a text item
i._data[Outline.type] = "md"
i._data[Outline.text] = i.children()[0].text()
i.removeChild(0)
c = i.removeChild(0)
items.remove(c)
return items

View file

@ -57,7 +57,7 @@ class opmlImporter(abstractImporter):
if outlineEls is not None:
for element in outlineEls:
items.append(cls.parseItems(element, parentItem))
items.extend(cls.parseItems(element, parentItem))
ret = True
if not ret:
@ -72,10 +72,12 @@ class opmlImporter(abstractImporter):
@classmethod
def parseItems(cls, underElement, parentItem=None):
items = []
title = underElement.get('text')
if title is not None:
card = outlineItem(parent=parentItem, title=title)
items.append(card)
body = ""
note = underElement.get('_note')
@ -86,12 +88,12 @@ class opmlImporter(abstractImporter):
children = underElement.findall('outline')
if children is not None and len(children) > 0:
for el in children:
cls.parseItems(el, card)
items.extend(cls.parseItems(el, card))
else:
card.setData(Outline.type.value, 'md')
card.setData(Outline.text.value, body)
return card
return items
@classmethod
def saveNewlines(cls, inString):

View file

@ -12,6 +12,7 @@ class pandocImporter(abstractImporter):
formatFrom = ""
engine = "Pandoc"
extraArgs = []
@classmethod
def isValid(cls):
@ -27,10 +28,14 @@ class pandocImporter(abstractImporter):
"--from={}".format(self.formatFrom),
filePath,
"--to={}".format(formatTo),
"--standalone",
"--wrap={}".format(wrap),
]
if formatTo == "opml":
args.append("--standalone")
args += self.extraArgs
r = pandocExporter().run(args)
if formatTo == "opml":
@ -149,4 +154,3 @@ class OPMLPandocImporter(pandocImporter):

View file

@ -816,10 +816,56 @@ class outlineItem():
return qApp.translate("outlineModel", "{} words").format(
locale.format("%d", wc, grouping=True))
def copy(self):
"""
Returns a copy of item, with no parent, and no ID.
"""
item = outlineItem(xml=self.toXML())
item.setData(Outline.ID.value, None)
return item
###############################################################################
# XML
###############################################################################
def split(self, splitMark, recursive=True):
"""
Split scene at splitMark. If multiple splitMark, multiple splits.
If called on a folder and recursive is True, then it is recursively
applied to every children.
"""
if self.isFolder() and recursive:
for c in self.children():
c.split(splitMark)
else:
txt = self.text().split(splitMark)
if len(txt) == 1:
# Mark not found
return False
else:
# Stores the new text
self.setData(Outline.text.value, txt[0])
k = 1
for subTxt in txt[1:]:
# Create a copy
item = self.copy()
# Change title adding _k
item.setData(Outline.title.value,
"{}_{}".format(item.title(), k+1))
# Set text
item.setData(Outline.text.value, subTxt)
# Inserting item
self.parent().insertChild(self.row()+k, item)
k += 1
###############################################################################
# XML
###############################################################################
def toXML(self):
item = ET.Element("outlineItem")
@ -879,10 +925,9 @@ class outlineItem():
elif child.tag == "revision":
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
###############################################################################
# IDS
###############################################################################
###############################################################################
# IDS
###############################################################################
def getUniqueID(self):
self.setData(Outline.ID.value, self._model.rootItem.findUniqueID())

View file

@ -69,3 +69,22 @@ class generalSettings(QWidget, Ui_generalSettings):
def trimLongTitles(self):
return self.chkGeneralTrimTitles.isChecked()
def splitScenes(self):
"""
Return wheter the user wants to split scenes.
If unchecked, returns False.
If checked, returns the escaped split mark, or default (in placeholderText).
"""
if self.chkGeneralSplitScenes.isChecked():
split = self.txtGeneralSplitScenes.text()
if not split:
split = self.txtGeneralSplitScenes.placeholderText()
split = split.replace("\\n", "\n")
split = split.replace("\\t", "\t")
return split
else:
return False

View file

@ -37,11 +37,10 @@ class Ui_generalSettings(object):
self.formLayout_4.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows)
self.formLayout_4.setObjectName("formLayout_4")
self.chkGeneralSplitScenes = QtWidgets.QCheckBox(self.general)
self.chkGeneralSplitScenes.setEnabled(False)
self.chkGeneralSplitScenes.setObjectName("chkGeneralSplitScenes")
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.chkGeneralSplitScenes)
self.txtGeneralSplitScenes = QtWidgets.QLineEdit(self.general)
self.txtGeneralSplitScenes.setEnabled(False)
self.txtGeneralSplitScenes.setText("")
self.txtGeneralSplitScenes.setObjectName("txtGeneralSplitScenes")
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtGeneralSplitScenes)
self.chkGeneralTrimTitles = QtWidgets.QCheckBox(self.general)
@ -56,7 +55,7 @@ class Ui_generalSettings(object):
self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralParent)
self.chkGeneralTopLevel = QtWidgets.QCheckBox(self.general)
self.chkGeneralTopLevel.setObjectName("chkGeneralTopLevel")
self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.chkGeneralTopLevel)
self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralTopLevel)
self.verticalLayout_5.addLayout(self.formLayout_4)
self.toolBox.addItem(self.general, "")
self.verticalLayout_2.addWidget(self.toolBox)
@ -70,6 +69,7 @@ class Ui_generalSettings(object):
_translate = QtCore.QCoreApplication.translate
generalSettings.setWindowTitle(_translate("generalSettings", "Form"))
self.chkGeneralSplitScenes.setText(_translate("generalSettings", "Split scenes at:"))
self.txtGeneralSplitScenes.setPlaceholderText(_translate("generalSettings", "\\n---\\n"))
self.chkGeneralTrimTitles.setText(_translate("generalSettings", "Trim long titles (> 32 chars)"))
self.chkGeneralParent.setText(_translate("generalSettings", "Import under:"))
self.chkGeneralTopLevel.setText(_translate("generalSettings", "Import in a top-level folder"))

View file

@ -80,9 +80,6 @@ QToolBox::tab:selected, QToolBox::tab:hover{
</property>
<item row="3" column="0">
<widget class="QCheckBox" name="chkGeneralSplitScenes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Split scenes at:</string>
</property>
@ -90,8 +87,11 @@ QToolBox::tab:selected, QToolBox::tab:hover{
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="txtGeneralSplitScenes">
<property name="enabled">
<bool>false</bool>
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>\n---\n</string>
</property>
</widget>
</item>
@ -116,7 +116,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
</property>
</widget>
</item>
<item row="2" column="0">
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="chkGeneralTopLevel">
<property name="text">
<string>Import in a top-level folder</string>

View file

@ -64,8 +64,10 @@ class importerDialog(QWidget, Ui_importer):
def populateImportList(self):
def addFormat(name, icon):
self.cmbImporters.addItem(QIcon.fromTheme(icon), name)
def addFormat(name, icon, identifier):
# Identifier serves to distingues 2 importers that would have the
# same name.
self.cmbImporters.addItem(QIcon.fromTheme(icon), name, identifier)
def addHeader(name):
self.cmbImporters.addItem(name, "header")
@ -82,7 +84,7 @@ class importerDialog(QWidget, Ui_importer):
addHeader(f.engine)
lastEngine = f.engine
addFormat(f.name, f.icon)
addFormat(f.name, f.icon, "{}:{}".format(f.engine, f.name))
if not f.isValid():
item = self.cmbImporters.model().item(self.cmbImporters.count() - 1)
item.setFlags(Qt.NoItemFlags)
@ -96,12 +98,13 @@ class importerDialog(QWidget, Ui_importer):
self.cmbImporters.setCurrentIndex(1)
def currentFormat(self):
formatName = self.cmbImporters.currentText()
formatIdentifier = self.cmbImporters.currentData()
if self.cmbImporters.currentData() == "header":
if formatIdentifier == "header":
return None
F = [F for F in self.importers if F.name == formatName][0]
F = [F for F in self.importers
if formatIdentifier == "{}:{}".format(F.engine, F.name)][0]
# We instantiate the class
return F()
@ -303,15 +306,14 @@ class importerDialog(QWidget, Ui_importer):
# Trim long titles
if self.settingsWidget.trimLongTitles():
def trim(item):
for item in items:
if len(item.title()) > 32:
item.setData(Outline.title.value, item.title()[:32])
# I think it's overkill to do it recursively, since items
# is supposed to contain every imported items.
for c in item.children():
trim(c)
for i in items:
trim(i)
# Split at
if self.settingsWidget.splitScenes():
for item in items:
item.split(self.settingsWidget.splitScenes(), recursive=False)
return items