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 = [ importers = [
# Internal # Internal
opmlImporter,
folderImporter,
markdownImporter, markdownImporter,
folderImporter,
opmlImporter,
# Pandoc # Pandoc
markdownPandocImporter, markdownPandocImporter,

View file

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

View file

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

View file

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

View file

@ -816,10 +816,56 @@ class outlineItem():
return qApp.translate("outlineModel", "{} words").format( return qApp.translate("outlineModel", "{} words").format(
locale.format("%d", wc, grouping=True)) 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
############################################################################### def split(self, splitMark, recursive=True):
# XML """
############################################################################### 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): def toXML(self):
item = ET.Element("outlineItem") item = ET.Element("outlineItem")
@ -879,10 +925,9 @@ class outlineItem():
elif child.tag == "revision": elif child.tag == "revision":
self.appendRevision(child.attrib["timestamp"], child.attrib["text"]) self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
###############################################################################
############################################################################### # IDS
# IDS ###############################################################################
###############################################################################
def getUniqueID(self): def getUniqueID(self):
self.setData(Outline.ID.value, self._model.rootItem.findUniqueID()) self.setData(Outline.ID.value, self._model.rootItem.findUniqueID())

View file

@ -69,3 +69,22 @@ class generalSettings(QWidget, Ui_generalSettings):
def trimLongTitles(self): def trimLongTitles(self):
return self.chkGeneralTrimTitles.isChecked() 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.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows)
self.formLayout_4.setObjectName("formLayout_4") self.formLayout_4.setObjectName("formLayout_4")
self.chkGeneralSplitScenes = QtWidgets.QCheckBox(self.general) self.chkGeneralSplitScenes = QtWidgets.QCheckBox(self.general)
self.chkGeneralSplitScenes.setEnabled(False)
self.chkGeneralSplitScenes.setObjectName("chkGeneralSplitScenes") self.chkGeneralSplitScenes.setObjectName("chkGeneralSplitScenes")
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.chkGeneralSplitScenes) self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.chkGeneralSplitScenes)
self.txtGeneralSplitScenes = QtWidgets.QLineEdit(self.general) self.txtGeneralSplitScenes = QtWidgets.QLineEdit(self.general)
self.txtGeneralSplitScenes.setEnabled(False) self.txtGeneralSplitScenes.setText("")
self.txtGeneralSplitScenes.setObjectName("txtGeneralSplitScenes") self.txtGeneralSplitScenes.setObjectName("txtGeneralSplitScenes")
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtGeneralSplitScenes) self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtGeneralSplitScenes)
self.chkGeneralTrimTitles = QtWidgets.QCheckBox(self.general) 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.formLayout_4.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralParent)
self.chkGeneralTopLevel = QtWidgets.QCheckBox(self.general) self.chkGeneralTopLevel = QtWidgets.QCheckBox(self.general)
self.chkGeneralTopLevel.setObjectName("chkGeneralTopLevel") 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.verticalLayout_5.addLayout(self.formLayout_4)
self.toolBox.addItem(self.general, "") self.toolBox.addItem(self.general, "")
self.verticalLayout_2.addWidget(self.toolBox) self.verticalLayout_2.addWidget(self.toolBox)
@ -70,6 +69,7 @@ class Ui_generalSettings(object):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
generalSettings.setWindowTitle(_translate("generalSettings", "Form")) generalSettings.setWindowTitle(_translate("generalSettings", "Form"))
self.chkGeneralSplitScenes.setText(_translate("generalSettings", "Split scenes at:")) 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.chkGeneralTrimTitles.setText(_translate("generalSettings", "Trim long titles (> 32 chars)"))
self.chkGeneralParent.setText(_translate("generalSettings", "Import under:")) self.chkGeneralParent.setText(_translate("generalSettings", "Import under:"))
self.chkGeneralTopLevel.setText(_translate("generalSettings", "Import in a top-level folder")) self.chkGeneralTopLevel.setText(_translate("generalSettings", "Import in a top-level folder"))

View file

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

View file

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