2016-03-05 09:57:38 +13:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# --!-- coding: utf8 --!--
|
|
|
|
|
|
|
|
# Version 1 of file saving format.
|
|
|
|
# Aims at providing a plain-text way of saving a project
|
|
|
|
# (except for some elements), allowing collaborative work
|
|
|
|
# versioning and third-partty editing.
|
|
|
|
import os
|
|
|
|
import zipfile
|
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
from PyQt5.QtGui import QColor
|
|
|
|
|
2016-03-05 09:57:38 +13:00
|
|
|
from manuskript import settings
|
2016-03-05 12:35:14 +13:00
|
|
|
from manuskript.functions import mainWindow, iconColor
|
2016-03-05 09:57:38 +13:00
|
|
|
|
|
|
|
try:
|
|
|
|
import zlib # Used with zipfile for compression
|
|
|
|
|
|
|
|
compression = zipfile.ZIP_DEFLATED
|
|
|
|
except:
|
|
|
|
compression = zipfile.ZIP_STORED
|
|
|
|
|
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
cache = {}
|
|
|
|
|
2016-03-05 09:57:38 +13:00
|
|
|
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
|
|
|
|
and some XML or zip? for settings and stuff.
|
|
|
|
If zip is True, everything is saved as a single zipped file. Easier to carry around, but does not allow
|
|
|
|
collaborative work, versionning, or third-party editing.
|
|
|
|
@param zip: if True, saves as a single file. If False, saves as plain-text. If None, tries to determine based on
|
|
|
|
settings.
|
|
|
|
@return: Nothing
|
|
|
|
"""
|
|
|
|
if zip is None:
|
2016-03-05 12:35:14 +13:00
|
|
|
zip = True
|
2016-03-05 09:57:38 +13:00
|
|
|
# Fixme
|
|
|
|
|
|
|
|
|
|
|
|
files = []
|
|
|
|
mw = mainWindow()
|
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
# General infos (book and author)
|
|
|
|
# Saved in plain text, in infos.txt
|
|
|
|
|
|
|
|
path = "infos.txt"
|
|
|
|
content = ""
|
|
|
|
for name, col in [
|
|
|
|
("Title", 0),
|
|
|
|
("Subtitle", 1),
|
|
|
|
("Serie", 2),
|
|
|
|
("Volume", 3),
|
|
|
|
("Genre", 4),
|
|
|
|
("License", 5),
|
|
|
|
("Author", 6),
|
|
|
|
("Email", 7),
|
|
|
|
]:
|
|
|
|
val = mw.mdlFlatData.item(0, col).text().strip()
|
|
|
|
if val:
|
|
|
|
content += "{name}:{spaces}{value}\n".format(
|
|
|
|
name=name,
|
|
|
|
spaces=" " * (15 - len(name)),
|
|
|
|
value=val
|
|
|
|
)
|
|
|
|
files.append((path, content))
|
|
|
|
|
|
|
|
# Summary
|
|
|
|
# In plain text, in summary.txt
|
|
|
|
|
|
|
|
path = "summary.txt"
|
|
|
|
content = ""
|
|
|
|
for name, col in [
|
|
|
|
("Situation", 0),
|
|
|
|
("Sentence", 1),
|
|
|
|
("Paragraph", 2),
|
|
|
|
("Page", 3),
|
|
|
|
("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
|
|
|
|
)
|
|
|
|
files.append((path, content))
|
|
|
|
|
|
|
|
# Label & Status
|
|
|
|
# In plain text
|
|
|
|
|
|
|
|
for mdl, path in [
|
|
|
|
(mw.mdlStatus, "status.txt"),
|
|
|
|
(mw.mdlLabels, "labels.txt")
|
|
|
|
]:
|
|
|
|
|
|
|
|
content = ""
|
|
|
|
|
|
|
|
# We skip the first row, which is empty and transparent
|
|
|
|
for i in range(1, mdl.rowCount()):
|
|
|
|
color = ""
|
|
|
|
if mdl.data(mdl.index(i, 0), Qt.DecorationRole) is not None:
|
|
|
|
color = iconColor(mdl.data(mdl.index(i, 0), Qt.DecorationRole)).name(QColor.HexRgb)
|
|
|
|
color = color if color != "#ff000000" else "#00000000"
|
|
|
|
|
|
|
|
text = mdl.data(mdl.index(i, 0))
|
|
|
|
|
|
|
|
if text:
|
|
|
|
content += "{name}{color}\n".format(
|
|
|
|
name=text,
|
|
|
|
color= "" if color == "" else ":" + " " * (20 - len(text)) + color
|
|
|
|
)
|
|
|
|
|
|
|
|
files.append((path, content))
|
|
|
|
|
|
|
|
# Characters (self.mdlCharacter)
|
|
|
|
# In a character folder
|
|
|
|
|
|
|
|
# TODO
|
|
|
|
|
|
|
|
# Texts
|
|
|
|
# In an outline folder
|
|
|
|
|
|
|
|
# TODO
|
|
|
|
|
|
|
|
# World (mw.mdlWorld)
|
|
|
|
# Either in an XML file, or in lots of plain texts?
|
|
|
|
# More probably text, since there might be writing done in third-party.
|
|
|
|
|
|
|
|
# TODO
|
|
|
|
|
|
|
|
# Plots (mw.mdlPlots)
|
|
|
|
# Either in XML or lots of plain texts?
|
|
|
|
# More probably XML since there is not really a lot if writing to do (third-party)
|
|
|
|
|
|
|
|
# TODO
|
|
|
|
|
|
|
|
# Settings
|
|
|
|
# Saved in readable text (json) for easier versionning. But they mustn't be shared, it seems.
|
|
|
|
# Maybe include them only if zipped?
|
|
|
|
# Well, for now, we keep them here...
|
|
|
|
files.append(("settings.txt", settings.save(protocol=0)))
|
2016-03-05 09:57:38 +13:00
|
|
|
|
|
|
|
project = mw.currentProject
|
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
# Save to zip
|
|
|
|
if zip:
|
|
|
|
project = os.path.join(
|
|
|
|
os.path.dirname(project),
|
|
|
|
"_" + os.path.basename(project)
|
|
|
|
)
|
|
|
|
|
|
|
|
zf = zipfile.ZipFile(project, mode="w")
|
|
|
|
|
|
|
|
for filename, content in files:
|
|
|
|
zf.writestr(filename, content, compress_type=compression)
|
|
|
|
|
|
|
|
zf.close()
|
2016-03-05 09:57:38 +13:00
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
# Save to plain text
|
|
|
|
else:
|
|
|
|
dir = os.path.dirname(project)
|
|
|
|
folder = os.path.splitext(os.path.basename(project))[0]
|
|
|
|
print("Saving to folder", folder)
|
2016-03-05 09:57:38 +13:00
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
for path, content in files:
|
|
|
|
filename = os.path.join(dir, folder, path)
|
|
|
|
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
|
|
|
print("* Saving file", filename)
|
2016-03-05 09:57:38 +13:00
|
|
|
|
2016-03-05 12:35:14 +13:00
|
|
|
# TODO: the first time it saves, it will overwrite everything, since it's not yet in cache.
|
|
|
|
# Or we have to cache while loading.
|
|
|
|
|
|
|
|
if not path in cache or cache[path] != content:
|
|
|
|
print(" Not in cache or changed: we write")
|
|
|
|
mode = "w"+ ("b" if type(content) == bytes else "")
|
|
|
|
with open(filename, mode) as f:
|
|
|
|
f.write(content)
|
|
|
|
cache[path] = content
|
|
|
|
|
|
|
|
else:
|
|
|
|
print(" In cache, and identical. Do nothing.")
|
2016-03-05 09:57:38 +13:00
|
|
|
|
|
|
|
|
|
|
|
def loadProject(project):
|
|
|
|
"""
|
|
|
|
Loads a project.
|
|
|
|
@param project: the filename of the project to open.
|
|
|
|
@return: an array of errors, empty if None.
|
|
|
|
"""
|
2016-03-05 12:35:14 +13:00
|
|
|
|
|
|
|
# Don't forget to cache everything that is loaded
|
|
|
|
# In order to save only what has changed.
|
|
|
|
|
2016-03-05 09:57:38 +13:00
|
|
|
pass
|