mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-10 07:52:35 +12:00
Adds: ability to split scenes at custom points. #200
This commit is contained in:
parent
24607bca59
commit
a231721bdb
|
@ -10,9 +10,9 @@ from manuskript.importer.pandocImporters import markdownPandocImporter, \
|
|||
|
||||
importers = [
|
||||
# Internal
|
||||
opmlImporter,
|
||||
folderImporter,
|
||||
markdownImporter,
|
||||
folderImporter,
|
||||
opmlImporter,
|
||||
|
||||
# Pandoc
|
||||
markdownPandocImporter,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue