diff --git a/manuskript/load_save/version_0.py b/manuskript/load_save/version_0.py index 1874b050..8bc15fcf 100644 --- a/manuskript/load_save/version_0.py +++ b/manuskript/load_save/version_0.py @@ -14,7 +14,7 @@ from lxml import etree as ET from manuskript import settings from manuskript.functions import iconColor, iconFromColorString, mainWindow -from manuskript.models.characterModel import Character +from manuskript.models.characterModel import Character, CharacterInfo try: import zlib # Used with zipfile for compression @@ -250,18 +250,40 @@ def loadItem(root, mdl, parent=QModelIndex()): def loadStandardItemModelXMLForCharacters(mdl, xml): + """ + Loads a standardItemModel saved to XML by version 0, but for the new characterModel. + @param mdl: characterModel + @param xml: the content of the xml + @return: nothing + """ mdl = mainWindow().mdlCharacter root = ET.fromstring(xml) data = root.find("data") + for row in data: char = Character(mdl) + for col in row: c = int(col.attrib["col"]) + # Value if col.text: char._data[c] = col.text + # Color if "color" in col.attrib: char.setColor(QColor(col.attrib["color"])) - # TODO: infos + + # Infos + if len(col) != 0: + for rrow in col: + info = CharacterInfo(char) + for ccol in rrow: + cc = int(ccol.attrib["col"]) + if cc == 11 and ccol.text: + info.description = ccol.text + if cc == 12 and ccol.text: + info.value = ccol.text + char.infos.append(info) + mdl.characters.append(char) \ No newline at end of file diff --git a/manuskript/load_save/version_1.py b/manuskript/load_save/version_1.py index ede22ddb..095d5ae3 100644 --- a/manuskript/load_save/version_1.py +++ b/manuskript/load_save/version_1.py @@ -6,14 +6,17 @@ # (except for some elements), allowing collaborative work # versioning and third-partty editing. import os +import string import zipfile -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QModelIndex from PyQt5.QtGui import QColor from manuskript import settings -from manuskript.enums import Character +from manuskript.enums import Character, World from manuskript.functions import mainWindow, iconColor +from lxml import etree as ET + try: import zlib # Used with zipfile for compression @@ -28,7 +31,17 @@ cache = {} def formatMetaData(name, value, tabLength=10): - # TODO: escape ":" in name + # Multiline formatting + if len(value.split("\n")) > 1: + value = "\n".join([" " * (tabLength + 1) + l for l in value.split("\n")])[tabLength + 1:] + + # Avoid empty description (don't know how much MMD loves that) + if name == "": + name = "None" + + # Escapes ":" in name + name = name.replace(":", "_.._") + return "{name}:{spaces}{value}\n".format( name=name, spaces=" " * (tabLength - len(name)), @@ -36,6 +49,23 @@ def formatMetaData(name, value, tabLength=10): ) +def slugify(name): + """ + A basic slug function, that escapes all spaces to "_" and all non letters/digits to "-". + @param name: name to slugify (str) + @return: str + """ + valid = string.ascii_letters + string.digits + newName = "" + for c in name: + if c in valid: + newName += c + elif c in string.whitespace: + newName += "_" + else: + newName += "-" + return newName + def saveProject(zip=None): """ Saves the project. If zip is False, the project is saved as a multitude of plain-text files for the most parts @@ -50,8 +80,11 @@ def saveProject(zip=None): zip = False # Fixme - + # List of files to be written files = [] + # List of files to be removed + removes = [] + mw = mainWindow() # General infos (book and author) @@ -91,13 +124,9 @@ def saveProject(zip=None): ("Full", 4), ]: val = mw.mdlFlatData.item(1, col).text().strip() - val = "\n".join([" " * 13 + l for l in val.split("\n")])[13:] if val: - content += "{name}:{spaces}{value}\n".format( - name=name, - spaces=" " * (12 - len(name)), - value=val - ) + content += formatMetaData(name, val, 12) + files.append((path, content)) # Label & Status @@ -146,25 +175,23 @@ def saveProject(zip=None): mdl = mw.mdlCharacter for c in mdl.characters: content = "" - LENGTH = 20 for m, name in _map: val = mdl.data(c.index(m.value)).strip() if val: - # Multiline formatting - if len(val.split("\n")) > 1: - val = "\n".join([" " * (LENGTH + 1) + l for l in val.split("\n")])[LENGTH + 1:] - - content += formatMetaData(name, val, LENGTH) + content += formatMetaData(name, val, 20) for info in c.infos: - content += formatMetaData(info.description, info.value, LENGTH) + content += formatMetaData(info.description, info.value, 20) - name = "{ID}-{slugName}".format( + cpath = path.format(name="{ID}-{slugName}".format( ID=c.ID(), - slugName="FIXME" - ) + slugName=slugify(c.name()) + )) + if c.lastPath and cpath != c.lastPath: + removes.append(c.lastPath) + c.lastPath = cpath files.append(( - path.format(name=name), + cpath, content)) # Texts @@ -176,7 +203,15 @@ def saveProject(zip=None): # Either in an XML file, or in lots of plain texts? # More probably text, since there might be writing done in third-party. - # TODO + path = "world.opml" + mdl = mw.mdlWorld + + root = ET.Element("opml") + root.attrib["version"] = "1.0" + body = ET.SubElement(root, "body") + addWorldItem(body, mdl) + content = ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True) + files.append((path, content)) # Plots (mw.mdlPlots) # Either in XML or lots of plain texts? @@ -212,6 +247,13 @@ def saveProject(zip=None): folder = os.path.splitext(os.path.basename(project))[0] print("Saving to folder", folder) + for path in removes: + if path not in [p for p,c in files]: + filename = os.path.join(dir, folder, path) + print("* Removing", filename) + os.remove(filename) + cache.pop(path) + for path, content in files: filename = os.path.join(dir, folder, path) os.makedirs(os.path.dirname(filename), exist_ok=True) @@ -230,6 +272,34 @@ def saveProject(zip=None): else: print(" In cache, and identical. Do nothing.") +def addWorldItem(root, mdl, parent=QModelIndex()): + """ + Lists elements in a world model and create an OPML xml file. + @param root: an Etree element + @param mdl: a worldModel + @param parent: the parent index in the world model + @return: root, to which sub element have been added + """ + # List every row (every world item) + for x in range(mdl.rowCount(parent)): + + # For each row, create an outline item. + outline = ET.SubElement(root, "outline") + for y in range(mdl.columnCount(parent)): + + val = mdl.data(mdl.index(x, y, parent)) + + if not val: + continue + + for w in World: + if y == w.value: + outline.attrib[w.name] = val + + if mdl.hasChildren(mdl.index(x, y, parent)): + addWorldItem(outline, mdl, mdl.index(x, y, parent)) + + return root def loadProject(project): """ diff --git a/manuskript/models/characterModel.py b/manuskript/models/characterModel.py index 0a922bd7..84133d96 100644 --- a/manuskript/models/characterModel.py +++ b/manuskript/models/characterModel.py @@ -215,6 +215,7 @@ class characterModel(QAbstractItemModel): class Character(): def __init__(self, model, name="No name"): self._model = model + self.lastPath = "" self._data = {} self._data[C.name.value] = name