diff --git a/icons/NumixMsk/16x16/actions/document-import.svg b/icons/NumixMsk/16x16/actions/document-import.svg
new file mode 100644
index 0000000..8ddaea3
--- /dev/null
+++ b/icons/NumixMsk/16x16/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/22x22/actions/document-import.svg b/icons/NumixMsk/22x22/actions/document-import.svg
new file mode 100644
index 0000000..a78c48c
--- /dev/null
+++ b/icons/NumixMsk/22x22/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/24x24/actions/document-import.svg b/icons/NumixMsk/24x24/actions/document-import.svg
new file mode 100644
index 0000000..77e06c3
--- /dev/null
+++ b/icons/NumixMsk/24x24/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/32x32/actions/document-import.svg b/icons/NumixMsk/32x32/actions/document-import.svg
new file mode 100644
index 0000000..53d3682
--- /dev/null
+++ b/icons/NumixMsk/32x32/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/48x48/actions/document-import.svg b/icons/NumixMsk/48x48/actions/document-import.svg
new file mode 100644
index 0000000..b2e1682
--- /dev/null
+++ b/icons/NumixMsk/48x48/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/64x64/actions/document-import.svg b/icons/NumixMsk/64x64/actions/document-import.svg
new file mode 100644
index 0000000..3760178
--- /dev/null
+++ b/icons/NumixMsk/64x64/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/manuskript/converters/__init__.py b/manuskript/converters/__init__.py
new file mode 100644
index 0000000..b4c7eb1
--- /dev/null
+++ b/manuskript/converters/__init__.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+"""
+The converters package provide functions to quickly convert on the fly from
+one format to another. It is responsible to check what external library are
+present, and do the job as best as possible with what we have in hand.
+"""
+
+from manuskript.converters.abstractConverter import abstractConverter
+from manuskript.converters.pandocConverter import pandocConverter
+#from manuskript.converters.markdownConverter import markdownConverter
+
+
+def HTML2MD(html):
+
+ # Convert using pandoc
+ if pandocConverter.isValid():
+ return pandocConverter.convert(html, _from="html", to="markdown")
+
+ # Convert to plain text using QTextEdit
+ return HTML2PlainText(html)
+
+
+def HTML2PlainText(html):
+ """
+ Convert from HTML to plain text.
+ """
+
+ if pandocConverter.isValid():
+ return pandocConverter.convert(html, _from="html", to="plain")
+
+ # Last resort: probably resource ineficient
+ e = QTextEdit()
+ e.setHtml(html)
+ return e.toPlainText()
diff --git a/manuskript/converters/abstractConverter.py b/manuskript/converters/abstractConverter.py
new file mode 100644
index 0000000..d38c3af
--- /dev/null
+++ b/manuskript/converters/abstractConverter.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+
+class abstractConverter:
+ """
+ A convertor is used to convert (duh) between stuff. They provide access
+ to external libraries that may or may not be present.
+
+ Now, things are a bit messy, since classes in `exporter` (and `importer` to a lesser extent) do
+ the same. In a better world, classes from `exporter` and `importer` would
+ use convertors to do their stuff. (TODO)
+ """
+ name = ""
+
+ @classmethod
+ def isValid(cls):
+ return False
diff --git a/manuskript/converters/markdownConverter.py b/manuskript/converters/markdownConverter.py
new file mode 100644
index 0000000..f0e577b
--- /dev/null
+++ b/manuskript/converters/markdownConverter.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import os
+import shutil
+import subprocess
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import qApp, QMessageBox
+from PyQt5.QtGui import QCursor
+
+from manuskript.converters import abstractConverter
+from manuskript.functions import mainWindow
+
+try:
+ import markdown as MD
+except ImportError:
+ MD = None
+
+
+class markdownConverter(abstractConverter):
+ """
+ Converter using python module markdown.
+ """
+
+ name = "python module markdown"
+
+ @classmethod
+ def isValid(self):
+ return MD is not None
+
+ @classmethod
+ def convert(self, markdown):
+ if not self.isValid:
+ print("ERROR: markdownConverter is called but not valid.")
+ return ""
+
+ html = MD.markdown(markdown)
+ return html
diff --git a/manuskript/converters/pandocConverter.py b/manuskript/converters/pandocConverter.py
new file mode 100644
index 0000000..ea91e9a
--- /dev/null
+++ b/manuskript/converters/pandocConverter.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import os
+import shutil
+import subprocess
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import qApp, QMessageBox
+from PyQt5.QtGui import QCursor
+
+from manuskript.converters import abstractConverter
+from manuskript.functions import mainWindow
+
+
+class pandocConverter(abstractConverter):
+
+ name = "pandoc"
+ cmd = "pandoc"
+
+ @classmethod
+ def isValid(self):
+ if self.path() != None:
+ return 2
+ elif self.customPath() and os.path.exists(self.customPath):
+ return 1
+ else:
+ return 0
+
+ @classmethod
+ def customPath(self):
+ settings = QSettings()
+ return settings.value("Exporters/{}_customPath".format(self.name), "")
+
+ @classmethod
+ def path(self):
+ return shutil.which(self.cmd)
+
+ @classmethod
+ def convert(self, src, _from="markdown", to="html", args=None, outputfile=None):
+ if not self.isValid:
+ print("ERROR: pandocConverter is called but not valid.")
+ return ""
+
+ cmd = [self.runCmd()]
+
+ cmd += ["--from={}".format(_from)]
+ cmd += ["--to={}".format(to)]
+
+ if args:
+ cmd += args
+
+ if outputfile:
+ cmd.append("--output={}".format(outputfile))
+
+ qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
+
+ p = subprocess.Popen(
+ cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+
+ if not type(src) == bytes:
+ src = src.encode("utf-8") # assumes utf-8
+
+ stdout, stderr = p.communicate(src)
+
+ qApp.restoreOverrideCursor()
+
+ if stderr:
+ err = stderr.decode("utf-8")
+ print(err)
+ QMessageBox.critical(mainWindow().dialog,
+ qApp.translate("Export", "Error"), err)
+ return None
+
+ return stdout.decode("utf-8")
+
+ @classmethod
+ def runCmd(self):
+ if self.isValid() == 2:
+ return self.cmd
+ elif self.isValid() == 1:
+ return self.customPath
diff --git a/manuskript/functions.py b/manuskript/functions.py
index 03e2a79..7db44c9 100644
--- a/manuskript/functions.py
+++ b/manuskript/functions.py
@@ -276,17 +276,6 @@ def findFirstFile(regex, path="resources"):
if re.match(regex, l):
return os.path.join(p, l)
-
-def HTML2PlainText(html):
- """
- Ressource-inefficient way to convert HTML to plain text.
- @param html:
- @return:
- """
- e = QTextEdit()
- e.setHtml(html)
- return e.toPlainText()
-
def customIcons():
"""
Returns a list of possible customIcons. String from theme.
diff --git a/manuskript/importer/mindMapImporter.py b/manuskript/importer/mindMapImporter.py
index f5599f1..6d377ca 100644
--- a/manuskript/importer/mindMapImporter.py
+++ b/manuskript/importer/mindMapImporter.py
@@ -7,6 +7,7 @@ from manuskript.enums import Outline
from lxml import etree as ET
from manuskript.functions import mainWindow
from manuskript.importer.abstractImporter import abstractImporter
+from manuskript.converters import HTML2MD, HTML2PlainText
class mindMapImporter(abstractImporter):
@@ -71,7 +72,7 @@ class mindMapImporter(abstractImporter):
self.addSetting("importTipAs", "combo",
qApp.translate("Import", "Import tip as:"),
- vals="Folder|Text",
+ vals="Text|Folder",
)
for s in self.settings:
@@ -81,17 +82,65 @@ class mindMapImporter(abstractImporter):
def parseItems(self, underElement, parentItem=None):
items = []
- title = underElement.get('TEXT')
- if title is not None:
- item = outlineItem(parent=parentItem, title=title)
- items.append(item)
+ # Title
+ title = underElement.get('TEXT', "").replace("\n", " ")
+ if not title:
+ title = qApp.translate("Import", "Untitled")
- children = underElement.findall('node')
- if children is not None and len(children) > 0:
- for c in children:
- items.extend(self.parseItems(c, item))
- elif self.getSetting("importTipAs").value() == "Text":
- item.setData(Outline.type.value, 'md')
+ item = outlineItem(parent=parentItem, title=title)
+ items.append(item)
+
+ # URL
+ url = underElement.get('LINK', None)
+
+ # Rich text content
+ content = ""
+ content = underElement.find("richcontent")
+ if content is not None:
+ # In Freemind, can be note or node
+ # Note: it's a note
+ # Node: it's the title of the node, in rich text
+ content_type = content.get("TYPE", "NOTE")
+ content = ET.tostring(content.find("html"))
+
+ if content and content_type == "NODE":
+ # Content is title
+ # convert rich text title (in html) to plain text
+ title = HTML2PlainText(content) #.replace("\n", " ").strip()
+ # Count the number of lines
+ lines = [l.strip() for l in title.split("\n") if l.strip()]
+
+ # If there is one line, we use it as title.
+ # Otherwise we leave it to be inserted as a note.
+ if len(lines) == 1:
+ item.setData(Outline.title.value, "".join(lines))
+ content = ""
+
+ if content:
+ # Set the note content as text value
+ content = HTML2MD(content)
+ item.setData(Outline.notes.value, content)
+
+ if url:
+ # Set the url in notes
+ item.setData(Outline.notes.value,
+ item.data(Outline.notes.value) + "\n\n" + url)
+
+ children = underElement.findall('node')
+
+ # Process children
+ if children is not None and len(children) > 0:
+ for c in children:
+ items.extend(self.parseItems(c, item))
+
+ # Process if no children
+ elif self.getSetting("importTipAs").value() == "Text":
+ # Transform item to text
+ item.setData(Outline.type.value, 'md')
+ # Move notes to text
+ if item.data(Outline.notes.value):
+ item.setData(Outline.text.value, item.data(Outline.notes.value))
+ item.setData(Outline.notes.value, "")
return items
diff --git a/manuskript/load_save/version_1.py b/manuskript/load_save/version_1.py
index be1eded..6015a3e 100644
--- a/manuskript/load_save/version_1.py
+++ b/manuskript/load_save/version_1.py
@@ -18,7 +18,8 @@ from PyQt5.QtGui import QColor, QStandardItem
from manuskript import settings
from manuskript.enums import Character, World, Plot, PlotStep, Outline
-from manuskript.functions import mainWindow, iconColor, iconFromColorString, HTML2PlainText
+from manuskript.functions import mainWindow, iconColor, iconFromColorString
+from manuskript.converters import HTML2PlainText
from lxml import etree as ET
from manuskript.load_save.version_0 import loadFilesFromZip
diff --git a/manuskript/models/outlineModel.py b/manuskript/models/outlineModel.py
index 140def2..4ce6772 100644
--- a/manuskript/models/outlineModel.py
+++ b/manuskript/models/outlineModel.py
@@ -15,7 +15,8 @@ from manuskript import settings
from lxml import etree as ET
from manuskript.enums import Outline
-from manuskript.functions import mainWindow, toInt, wordCount, HTML2PlainText
+from manuskript.functions import mainWindow, toInt, wordCount
+from manuskript.converters import HTML2PlainText
try:
locale.setlocale(locale.LC_ALL, '')
diff --git a/manuskript/ui/importers/importer.py b/manuskript/ui/importers/importer.py
index e944183..ac03ef5 100644
--- a/manuskript/ui/importers/importer.py
+++ b/manuskript/ui/importers/importer.py
@@ -226,6 +226,9 @@ class importerDialog(QWidget, Ui_importer):
def preview(self):
+ if not self.fileName:
+ return
+
# Creating a temporary outlineModel
previewModel = outlineModel(self)
previewModel.loadFromXML(
diff --git a/manuskript/ui/importers/importer_ui.py b/manuskript/ui/importers/importer_ui.py
index d23c8e4..327914c 100644
--- a/manuskript/ui/importers/importer_ui.py
+++ b/manuskript/ui/importers/importer_ui.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/importers/importer_ui.ui'
#
-# Created by: PyQt5 UI code generator 5.9
+# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
diff --git a/manuskript/ui/importers/importer_ui.ui b/manuskript/ui/importers/importer_ui.ui
index a7f2f90..cbf72b9 100644
--- a/manuskript/ui/importers/importer_ui.ui
+++ b/manuskript/ui/importers/importer_ui.ui
@@ -45,8 +45,7 @@
Chose file
-
- ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
+