Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
136 changes: 136 additions & 0 deletions manuskript/import_export/opml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--

# Import/export outline cards in OPML format
from PyQt5.QtWidgets import QMessageBox
from manuskript.models.outlineModel import outlineItem
from manuskript.enums import Outline
from lxml import etree as ET
from manuskript.functions import mainWindow


def importOpml(opmlFilePath, idx):
ret = False
mw = mainWindow()

try:
with open(opmlFilePath, 'r') as opmlFile:
opmlContent = saveNewlines(opmlFile.read())
except:
# TODO: Translation
QMessageBox.critical(mw, mw.tr("OPML Import"),
mw.tr("File open failed."))
return False

mdl = mw.mdlOutline

if idx.internalPointer() is not None:
parentItem = idx.internalPointer()
else:
parentItem = mdl.rootItem

try:
parsed = ET.fromstring(bytes(opmlContent, 'utf-8'))

opmlNode = parsed
bodyNode = opmlNode.find("body")

if bodyNode is not None:
outlineEls = bodyNode.findall("outline")

if outlineEls is not None:
for element in outlineEls:
parseItems(element, parentItem)

mdl.layoutChanged.emit()
mw.treeRedacOutline.viewport().update()
ret = True
except:
pass

# TODO: Translation
if ret:
QMessageBox.information(mw, mw.tr("OPML Import"),
mw.tr("Import Complete."))
else:
QMessageBox.critical(mw, mw.tr("OPML Import"),
mw.tr("This does not appear to be a valid OPML file."))

return ret


def parseItems(underElement, parentItem):
text = underElement.get('text')
if text is not None:
"""
In the case where the title is exceptionally long, trim it so it isn't
distracting in the tab label
"""
title = text[0:32]
if len(title) < len(text):
title += '...'

card = outlineItem(parent=parentItem, title=title)

body = ""
summary = ""
note = underElement.get('_note')
if note is not None and not isWhitespaceOnly(note):
body = restoreNewLines(note)
summary = body[0:128]
else:
"""
There's no note (body), but there is a title. Fill the
body with the title to support cards that consist only
of a title.
"""
body = text

card.setData(Outline.summaryFull.value, summary)

children = underElement.findall('outline')
if children is not None and len(children) > 0:
for el in children:
parseItems(el, card)
else:
card.setData(Outline.type.value, 'md')
card.setData(Outline.text.value, body)

# I assume I don't have to do the following
# parentItem.appendChild(card)

return


"""
Since XML parsers are notorious for stripping out significant newlines,
save them in a form we can restore after the parse.
"""


def saveNewlines(inString):
inString = inString.replace("\r\n", "\n")
inString = inString.replace("\n", "{{lf}}")

return inString


"""
Restore any significant newlines
"""


def restoreNewLines(inString):
return inString.replace("{{lf}}", "\n")


"""
Determine whether or not a string only contains whitespace.
"""


def isWhitespaceOnly(inString):
str = restoreNewLines(inString)
str = ''.join(str.split())

return len(str) is 0
18 changes: 9 additions & 9 deletions manuskript/mainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ def __init__(self):
self.makeUIConnections()

# self.loadProject(os.path.join(appPath(), "test_project.zip"))

def updateDockVisibility(self, restore=False):
"""
Saves the state of the docks visibility. Or if `restore` is True,
Saves the state of the docks visibility. Or if `restore` is True,
restores from `self._dckVisibility`. This allows to hide the docks
while showing the welcome screen, and then restore them as they
were.

If `self._dckVisibility` contains "LOCK", then we don't override values
with current visibility state. This is used the first time we load.
"LOCK" is then removed.
Expand All @@ -141,7 +141,7 @@ def updateDockVisibility(self, restore=False):
self.dckNavigation,
self.dckSearch,
]

for d in docks:
if not restore:
# We store the values, but only if "LOCK" is not present
Expand All @@ -152,11 +152,11 @@ def updateDockVisibility(self, restore=False):
else:
# Restore the dock's visibily based on stored value
d.setVisible(self._dckVisibility[d.objectName()])

# Lock is used only once, at start up. We can remove it
if "LOCK" in self._dckVisibility:
self._dckVisibility.pop("LOCK")

def switchToWelcome(self):
"""
While switching to welcome screen, we have to hide all the docks.
Expand All @@ -171,7 +171,7 @@ def switchToWelcome(self):
self.toolbar.setVisible(False)
# Switch to welcome screen
self.stack.setCurrentIndex(0)

def switchToProject(self):
"""Restores docks and toolbar visibility, and switch to project."""
# Restores the docks visibility
Expand Down Expand Up @@ -549,14 +549,14 @@ def closeEvent(self, event):
sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState())
sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState())
sttgns.setValue("toolbar", self.toolbar.saveState())

# If we are not in the welcome window, we update the visibility
# of the docks widgets
if self.stack.currentIndex() == 1:
self.updateDockVisibility()
# Storing the visibility of docks to restore it on restart
sttgns.setValue("docks", self._dckVisibility)

# Specific settings to save before quitting
settings.lastTab = self.tabMain.currentIndex()

Expand Down
20 changes: 20 additions & 0 deletions manuskript/ui/editors/mainEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from manuskript.ui.editors.editorWidget import editorWidget
from manuskript.ui.editors.fullScreenEditor import fullScreenEditor
from manuskript.ui.editors.mainEditor_ui import Ui_mainEditor
from manuskript.import_export import opml as opmlInputExport

locale.setlocale(locale.LC_ALL, '')

Expand Down Expand Up @@ -44,6 +45,10 @@ def __init__(self, parent=None):
self.btnRedacFullscreen.clicked.connect(
self.showFullScreen, AUC)

self.btnImport.clicked.connect(
lambda v: self.importOPML()
)

# self.tab.setDocumentMode(False)

# Bug in Qt < 5.5: doesn't always load icons from custom theme.
Expand Down Expand Up @@ -217,6 +222,7 @@ def updateThingsVisible(self, index):
self.btnRedacFolderText.setVisible(visible)
self.btnRedacFolderCork.setVisible(visible)
self.btnRedacFolderOutline.setVisible(visible)
self.btnImport.setVisible(visible)
self.sldCorkSizeFactor.setVisible(visible and self.btnRedacFolderCork.isChecked())
self.btnRedacFullscreen.setVisible(not visible)

Expand Down Expand Up @@ -296,6 +302,20 @@ def showFullScreen(self):
if self.currentEditor():
self._fullScreen = fullScreenEditor(self.currentEditor().currentIndex)

def importOPML(self):
from PyQt5.QtWidgets import QFileDialog
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
fileName, _ = QFileDialog.getOpenFileName(self, "Import OPML", "",
"OPML Files (*.opml)", options=options)
if fileName:
if len(self.mw.treeRedacOutline.selectionModel().
selection().indexes()) == 0:
idx = QModelIndex()
else:
idx = self.mw.treeRedacOutline.currentIndex()
opmlInputExport.importOpml(fileName, idx)

###############################################################################
# DICT AND STUFF LIKE THAT
###############################################################################
Expand Down
10 changes: 10 additions & 0 deletions manuskript/ui/editors/mainEditor_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ def setupUi(self, mainEditor):
self.btnRedacFolderOutline.setObjectName("btnRedacFolderOutline")
self.buttonGroup.addButton(self.btnRedacFolderOutline)
self.horizontalLayout_19.addWidget(self.btnRedacFolderOutline)
self.btnImport = QtWidgets.QPushButton(mainEditor)
self.btnImport.setText("")
icon = QtGui.QIcon.fromTheme("document-open")
self.btnImport.setIcon(icon)
self.btnImport.setFlat(True)
self.btnImport.setObjectName("btnImport")
self.buttonGroup.addButton(self.btnImport)
self.horizontalLayout_19.addWidget(self.btnImport)
self.sldCorkSizeFactor = QtWidgets.QSlider(mainEditor)
self.sldCorkSizeFactor.setMinimumSize(QtCore.QSize(100, 0))
self.sldCorkSizeFactor.setMaximumSize(QtCore.QSize(200, 16777215))
Expand Down Expand Up @@ -109,6 +117,8 @@ def retranslateUi(self, mainEditor):
self.btnRedacFolderCork.setText(_translate("mainEditor", "Index cards"))
self.btnRedacFolderOutline.setText(_translate("mainEditor", "Outline"))
self.btnRedacFullscreen.setShortcut(_translate("mainEditor", "F11"))
# TODO: Translation
self.btnImport.setToolTip(_translate("mainEditor", "Import items from an OPML file into the current folder"))

from manuskript.ui.editors.tabSplitter import tabSplitter
from manuskript.ui.editors.textFormat import textFormat
17 changes: 17 additions & 0 deletions manuskript/ui/editors/mainEditor_ui.ui
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@
</attribute>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnImport">
<property name="toolTip">
<!-- TODO: Translation -->
<string>Import items from an OPML file into the current folder</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="document-open"/>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sldCorkSizeFactor">
<property name="minimumSize">
Expand Down