mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-17 11:22:28 +12:00
Adds: import documents from several sources (txt, md, html, epub, docx, OPML, odt, etc.) #200
This commit is contained in:
commit
15ccaa513b
|
@ -129,7 +129,7 @@ class basicFormat:
|
|||
@classmethod
|
||||
def isValid(cls):
|
||||
return True
|
||||
|
||||
|
||||
@classmethod
|
||||
def projectPath(cls):
|
||||
return os.path.dirname(os.path.abspath(mainWindow().currentProject))
|
||||
|
|
26
manuskript/importer/__init__.py
Normal file
26
manuskript/importer/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.importer.folderImporter import folderImporter
|
||||
from manuskript.importer.markdownImporter import markdownImporter
|
||||
from manuskript.importer.opmlImporter import opmlImporter
|
||||
from manuskript.importer.pandocImporters import markdownPandocImporter, \
|
||||
odtPandocImporter, ePubPandocImporter, docXPandocImporter, HTMLPandocImporter, \
|
||||
rstPandocImporter, LaTeXPandocImporter, OPMLPandocImporter
|
||||
|
||||
importers = [
|
||||
# Internal
|
||||
markdownImporter,
|
||||
folderImporter,
|
||||
opmlImporter,
|
||||
|
||||
# Pandoc
|
||||
markdownPandocImporter,
|
||||
odtPandocImporter,
|
||||
ePubPandocImporter,
|
||||
docXPandocImporter,
|
||||
HTMLPandocImporter,
|
||||
rstPandocImporter,
|
||||
LaTeXPandocImporter,
|
||||
OPMLPandocImporter,
|
||||
]
|
199
manuskript/importer/abstractImporter.py
Normal file
199
manuskript/importer/abstractImporter.py
Normal file
|
@ -0,0 +1,199 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from PyQt5.QtCore import QSettings
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHBoxLayout, \
|
||||
QLabel, QSpinBox, QComboBox, QLineEdit
|
||||
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
|
||||
from manuskript.ui import style
|
||||
|
||||
|
||||
class abstractImporter:
|
||||
"""
|
||||
abstractImporter is used to import documents into manuskript.
|
||||
|
||||
The startImport function must be subclassed. It takes a filePath (str to
|
||||
the document to import), and must return `outlineItem`s.
|
||||
"""
|
||||
|
||||
name = ""
|
||||
description = ""
|
||||
fileFormat = "" # File format accepted. For example: "OPML Files (*.opml)"
|
||||
# For folder, use "<<folder>>"
|
||||
icon = ""
|
||||
engine = "Internal"
|
||||
|
||||
def __init__(self):
|
||||
self.settings = {}
|
||||
|
||||
def startImport(self, filePath, parentItem, settingsWidget):
|
||||
"""
|
||||
Takes a str path to the file/folder to import, and the settingsWidget
|
||||
returnend by `self.settingsWidget()` containing the user set settings,
|
||||
and return `outlineItem`s.
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def isValid(cls):
|
||||
return False
|
||||
|
||||
|
||||
def settingsWidget(self, widget):
|
||||
"""
|
||||
Takes a QWidget that can be modified and must be returned.
|
||||
"""
|
||||
return widget
|
||||
|
||||
def addPage(self, widget, title):
|
||||
"""
|
||||
Convenience function to add a page to the settingsWidget `widget`, at
|
||||
the end.
|
||||
|
||||
Returns the page widget.
|
||||
"""
|
||||
w = QWidget(widget)
|
||||
w.setLayout(QVBoxLayout())
|
||||
widget.toolBox.insertItem(widget.toolBox.count(), w, title)
|
||||
widget.toolBox.layout().setSpacing(0)
|
||||
return w
|
||||
|
||||
def addGroup(self, parent, title):
|
||||
"""
|
||||
Adds a collapsible group to the given widget.
|
||||
"""
|
||||
g = collapsibleGroupBox2(title=title)
|
||||
parent.layout().addWidget(g)
|
||||
g.setLayout(QVBoxLayout())
|
||||
return g
|
||||
|
||||
def addSetting(self, name, type, label, widget=None, default=None,
|
||||
tooltip=None, min=None, max=None, vals=None, suffix=""):
|
||||
|
||||
self.settings[name] = self.setting(name, type, label, widget, default,
|
||||
tooltip, min, max, vals, suffix)
|
||||
|
||||
def widget(self, name):
|
||||
if name in self.settings:
|
||||
return self.settings[name].widget()
|
||||
|
||||
def getSetting(self, name):
|
||||
if name in self.settings:
|
||||
return self.settings[name]
|
||||
|
||||
|
||||
class setting:
|
||||
"""
|
||||
A class used to store setting, and display a widget for the user to
|
||||
modify it.
|
||||
"""
|
||||
def __init__(self, name, type, label, widget=None, default=None,
|
||||
tooltip=None, min=None, max=None, vals=None, suffix=""):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.label = label
|
||||
self._widget = widget
|
||||
self.default = default
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.vals = vals.split("|") if vals else []
|
||||
self.suffix = suffix
|
||||
self.tooltip = tooltip
|
||||
|
||||
def widget(self, parent=None):
|
||||
"""
|
||||
Returns the widget used, or creates it if not done yet. If parent
|
||||
is given, widget is inserted in parent's layout.
|
||||
"""
|
||||
if self._widget:
|
||||
return self._widget
|
||||
|
||||
else:
|
||||
|
||||
if "checkbox" in self.type:
|
||||
self._widget = QCheckBox(self.label)
|
||||
if self.default:
|
||||
self._widget.setChecked(True)
|
||||
if parent:
|
||||
parent.layout().addWidget(self._widget)
|
||||
|
||||
elif "number" in self.type:
|
||||
l = QHBoxLayout()
|
||||
label = QLabel(self.label, parent)
|
||||
label.setWordWrap(True)
|
||||
l.addWidget(label, 8)
|
||||
self._widget = QSpinBox()
|
||||
self._widget.setValue(self.default if self.default else 0)
|
||||
if self.min:
|
||||
self._widget.setMinimum(self.min)
|
||||
if self.max:
|
||||
self._widget.setMaximum(self.max)
|
||||
if self.suffix:
|
||||
self._widget.setSuffix(self.suffix)
|
||||
l.addWidget(self._widget, 2)
|
||||
if parent:
|
||||
parent.layout().addLayout(l)
|
||||
|
||||
elif "combo" in self.type:
|
||||
l = QHBoxLayout()
|
||||
label = QLabel(self.label, parent)
|
||||
label.setWordWrap(True)
|
||||
l.addWidget(label, 6)
|
||||
self._widget = QComboBox()
|
||||
self._widget.addItems(self.vals)
|
||||
if self.default:
|
||||
self._widget.setCurrentText(self.default)
|
||||
l.addWidget(self._widget, 2)
|
||||
if parent:
|
||||
parent.layout().addLayout(l)
|
||||
|
||||
elif "text" in self.type:
|
||||
l = QHBoxLayout()
|
||||
label = QLabel(self.label, parent)
|
||||
label.setWordWrap(True)
|
||||
l.addWidget(label, 5)
|
||||
self._widget = QLineEdit()
|
||||
self._widget.setStyleSheet(style.lineEditSS())
|
||||
if self.default:
|
||||
self._widget.setText(self.default)
|
||||
l.addWidget(self._widget, 3)
|
||||
if parent:
|
||||
parent.layout().addLayout(l)
|
||||
|
||||
elif "label" in self.type:
|
||||
self._widget = QLabel(self.label, parent)
|
||||
self._widget.setWordWrap(True)
|
||||
if parent:
|
||||
parent.layout().addWidget(self._widget)
|
||||
|
||||
if self.tooltip:
|
||||
self._widget.setToolTip(self.tooltip)
|
||||
|
||||
return self._widget
|
||||
|
||||
def value(self):
|
||||
"""
|
||||
Return the value contained in the widget.
|
||||
"""
|
||||
if not self._widget:
|
||||
return self.default
|
||||
|
||||
else:
|
||||
|
||||
if "checkbox" in self.type:
|
||||
return self._widget.isChecked()
|
||||
|
||||
elif "number" in self.type:
|
||||
return self._widget.value()
|
||||
|
||||
elif "combo" in self.type:
|
||||
return self._widget.currentText()
|
||||
|
||||
elif "text" in self.type:
|
||||
return self._widget.text()
|
||||
|
||||
|
||||
|
121
manuskript/importer/folderImporter.py
Normal file
121
manuskript/importer/folderImporter.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
import os
|
||||
from manuskript.importer.abstractImporter import abstractImporter
|
||||
from manuskript.models.outlineModel import outlineItem
|
||||
from manuskript.enums import Outline
|
||||
from PyQt5.QtWidgets import qApp
|
||||
|
||||
|
||||
class folderImporter(abstractImporter):
|
||||
|
||||
name = "Folder"
|
||||
description = ""
|
||||
fileFormat = "<<folder>>"
|
||||
icon = "folder"
|
||||
|
||||
@classmethod
|
||||
def isValid(cls):
|
||||
return True
|
||||
|
||||
def startImport(self, filePath, parentItem, settingsWidget, fromString=None):
|
||||
"""
|
||||
Imports from a folder.
|
||||
"""
|
||||
ext = self.getSetting("ext").value()
|
||||
ext = [e.strip().replace("*", "") for e in ext.split(",")]
|
||||
|
||||
sorting = self.getSetting("sortItems").value()
|
||||
|
||||
items = []
|
||||
stack = {}
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(filePath):
|
||||
|
||||
if dirpath in stack:
|
||||
item = stack[dirpath]
|
||||
else:
|
||||
# It's the parent folder, and we are not including it
|
||||
# so every item is attached to parentItem
|
||||
item = parentItem
|
||||
|
||||
def addFile(f):
|
||||
fName, fExt = os.path.splitext(f)
|
||||
if fExt in ext:
|
||||
child = outlineItem(title=fName, _type="md", parent=item)
|
||||
with open(os.path.join(dirpath, f), "r") as fr:
|
||||
child._data[Outline.text] = fr.read()
|
||||
items.append(child)
|
||||
|
||||
def addFolder(d):
|
||||
child = outlineItem(title=d, parent=item)
|
||||
items.append(child)
|
||||
stack[os.path.join(dirpath, d)] = child
|
||||
|
||||
if not self.getSetting("separateFolderFiles").value():
|
||||
# Import folder and files together (only makes differences if
|
||||
# they are sorted, really)
|
||||
allFiles = dirnames + filenames
|
||||
if sorting:
|
||||
allFiles = sorted(allFiles)
|
||||
|
||||
for f in allFiles:
|
||||
if f in dirnames:
|
||||
addFolder(f)
|
||||
else:
|
||||
addFile(f)
|
||||
|
||||
else:
|
||||
# Import first folders, then files
|
||||
if sorting:
|
||||
dirnames = sorted(dirnames)
|
||||
filenames = sorted(filenames)
|
||||
|
||||
# Import folders
|
||||
for d in dirnames:
|
||||
addFolder(d)
|
||||
|
||||
# Import files
|
||||
for f in filenames:
|
||||
addFile(f)
|
||||
|
||||
return items
|
||||
|
||||
def settingsWidget(self, widget):
|
||||
"""
|
||||
Takes a QWidget that can be modified and must be returned.
|
||||
"""
|
||||
|
||||
# Add group
|
||||
group = self.addGroup(widget.toolBox.widget(0),
|
||||
qApp.translate("Import", "Folder import"))
|
||||
#group = cls.addPage(widget, "Folder import")
|
||||
|
||||
self.addSetting("info", "label",
|
||||
qApp.translate("Import", """<b>Info:</b> Imports a whole
|
||||
directory structure. Folders are added as folders, and
|
||||
plaintext documents within (you chose which ones by extension)
|
||||
are added as scene.<br/> """))
|
||||
|
||||
self.addSetting("ext", "text",
|
||||
qApp.translate("Import", "Include only those extensions:"),
|
||||
default="*.txt, *.md",
|
||||
tooltip=qApp.translate("Import", "Coma separated values")),
|
||||
|
||||
self.addSetting("sortItems", "checkbox",
|
||||
qApp.translate("Import", "Sort items by name"),
|
||||
default=True),
|
||||
|
||||
self.addSetting("separateFolderFiles", "checkbox",
|
||||
qApp.translate("Import", "Import folder then files"),
|
||||
default=True),
|
||||
|
||||
for s in self.settings:
|
||||
self.settings[s].widget(group)
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
|
||||
|
187
manuskript/importer/markdownImporter.py
Normal file
187
manuskript/importer/markdownImporter.py
Normal file
|
@ -0,0 +1,187 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.importer.abstractImporter import abstractImporter
|
||||
from manuskript.models.outlineModel import outlineItem
|
||||
from manuskript.enums import Outline
|
||||
from PyQt5.QtWidgets import qApp
|
||||
import re, os
|
||||
|
||||
|
||||
class markdownImporter(abstractImporter):
|
||||
|
||||
name = "Markdown"
|
||||
description = ""
|
||||
fileFormat = "Markdown files (*.md *.txt *)"
|
||||
icon = "text-x-markdown"
|
||||
|
||||
@classmethod
|
||||
def isValid(cls):
|
||||
return True
|
||||
|
||||
def startImport(self, filePath, parentItem, settingsWidget, fromString=None):
|
||||
"""
|
||||
Very simple import from markdown. We just look at ATX headers (we
|
||||
ignore setext for the sake of simplicity, for now.)
|
||||
|
||||
**A difficulty:** in the following example, we can do things with
|
||||
markdown headers (like go from level 1 to level 4 and back to level 2)
|
||||
that we cannot do in an outline.
|
||||
|
||||
```
|
||||
# Level 1
|
||||
# Level 1
|
||||
## Level 2
|
||||
### Level 3
|
||||
#### Level 4
|
||||
##### Level 5
|
||||
### Level 3
|
||||
# Level 1
|
||||
#### Level 4? → Level 2
|
||||
### Level 3? → Level 2
|
||||
## Level 2 → Level 2
|
||||
#### Level 4? → Level 3
|
||||
```
|
||||
|
||||
I think the current version of the imported manages that quite well.
|
||||
|
||||
**A question:** In the following sample, the first Level 1 becomes a
|
||||
text element, because it has no other sub elements. But the content of
|
||||
second Level 1 becomes a text element, with no name. What name should
|
||||
we give it?
|
||||
|
||||
```
|
||||
# Level 1
|
||||
Some texte content.
|
||||
Level 1 will become a text element.
|
||||
# Level 1
|
||||
This content has no name.
|
||||
## Level 2
|
||||
...
|
||||
```
|
||||
"""
|
||||
|
||||
if not fromString:
|
||||
# Read file
|
||||
with open(filePath, "r") as f:
|
||||
txt = f.read()
|
||||
else:
|
||||
txt = fromString
|
||||
|
||||
items = []
|
||||
|
||||
parent = parentItem
|
||||
lastLevel = 0
|
||||
content = ""
|
||||
|
||||
def saveContent(content, parent):
|
||||
if content.strip():
|
||||
child = outlineItem(title=parent.title(), parent=parent, _type="md")
|
||||
child._data[Outline.text] = content
|
||||
items.append(child)
|
||||
return ""
|
||||
|
||||
def addTitle(name, parent, level):
|
||||
child = outlineItem(title=name, parent=parent)
|
||||
child.__miLevel = level
|
||||
items.append(child)
|
||||
return child
|
||||
|
||||
ATXHeader = re.compile(r"(\#+)\s*(.+?)\s*\#*$")
|
||||
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
|
||||
|
||||
txt = txt.split("\n")
|
||||
skipNextLine = False
|
||||
for i in range(len(txt)):
|
||||
|
||||
l = txt[i]
|
||||
l2 = "\n".join(txt[i:i+2])
|
||||
|
||||
header = False
|
||||
|
||||
if skipNextLine:
|
||||
# Last line was a setext-style header.
|
||||
skipNextLine = False
|
||||
continue
|
||||
|
||||
# Check ATX Header
|
||||
m = ATXHeader.match(l)
|
||||
if m:
|
||||
header = True
|
||||
level = len(m.group(1))
|
||||
name = m.group(2)
|
||||
|
||||
# Check setext header
|
||||
m = setextHeader1.match(l2)
|
||||
|
||||
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 and len(m.group(1)) == len(m.group(2)):
|
||||
header = True
|
||||
level = 2
|
||||
name = m.group(1)
|
||||
skipNextLine = True
|
||||
|
||||
if header:
|
||||
|
||||
# save content
|
||||
content = saveContent(content, parent)
|
||||
|
||||
# get parent level
|
||||
while parent.__miLevel >= level:
|
||||
parent = parent.parent()
|
||||
|
||||
# create title
|
||||
child = addTitle(name, parent, level)
|
||||
child.__miLevel = level
|
||||
|
||||
# title becomes the new parent
|
||||
parent = child
|
||||
|
||||
lastLevel = level
|
||||
|
||||
else:
|
||||
content += l + "\n"
|
||||
|
||||
saveContent(content, parent)
|
||||
|
||||
# Clean up
|
||||
for i in items:
|
||||
if i.childCount() == 1 and i.children()[0].isText():
|
||||
# We have a folder with only one text item
|
||||
# So we make it a text item
|
||||
i._data[Outline.type] = "md"
|
||||
i._data[Outline.text] = i.children()[0].text()
|
||||
c = i.removeChild(0)
|
||||
items.remove(c)
|
||||
|
||||
return items
|
||||
|
||||
def settingsWidget(self, widget):
|
||||
"""
|
||||
Takes a QWidget that can be modified and must be returned.
|
||||
"""
|
||||
|
||||
# Add group
|
||||
group = self.addGroup(widget.toolBox.widget(0),
|
||||
qApp.translate("Import", "Markdown import"))
|
||||
#group = cls.addPage(widget, "Folder import")
|
||||
|
||||
self.addSetting("info", "label",
|
||||
qApp.translate("Import", """<b>Info:</b> A very simple
|
||||
parser that will go through a markdown document and
|
||||
create items for each titles.<br/> """))
|
||||
|
||||
for s in self.settings:
|
||||
self.settings[s].widget(group)
|
||||
|
||||
return widget
|
124
manuskript/importer/opmlImporter.py
Normal file
124
manuskript/importer/opmlImporter.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from PyQt5.QtWidgets import qApp, QMessageBox
|
||||
from manuskript.models.outlineModel import outlineItem
|
||||
from manuskript.enums import Outline
|
||||
from lxml import etree as ET
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.importer.abstractImporter import abstractImporter
|
||||
|
||||
class opmlImporter(abstractImporter):
|
||||
|
||||
name = "OPML"
|
||||
description = ""
|
||||
fileFormat = "OPML Files (*.opml *.xml)"
|
||||
icon = "text-x-opml+xml"
|
||||
|
||||
@classmethod
|
||||
def isValid(cls):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def startImport(cls, filePath, parentItem, settingsWidget, fromString=None):
|
||||
"""
|
||||
Import/export outline cards in OPML format.
|
||||
"""
|
||||
ret = False
|
||||
|
||||
if filePath != "":
|
||||
# We have a filePath, so we read the file
|
||||
try:
|
||||
with open(filePath, 'rb') as opmlFile:
|
||||
#opmlContent = cls.saveNewlines(opmlFile.read())
|
||||
opmlContent = opmlFile.read()
|
||||
except:
|
||||
QMessageBox.critical(settingsWidget,
|
||||
qApp.translate("Import", "OPML Import"),
|
||||
qApp.translate("Import", "File open failed."))
|
||||
return None
|
||||
|
||||
elif fromString == "":
|
||||
# We have neither filePath nor fromString, so we leave
|
||||
return None
|
||||
|
||||
else:
|
||||
# We load from string
|
||||
opmlContent = bytes(fromString, "utf-8")
|
||||
|
||||
parsed = ET.fromstring(opmlContent)
|
||||
|
||||
opmlNode = parsed
|
||||
bodyNode = opmlNode.find("body")
|
||||
items = []
|
||||
|
||||
if bodyNode is not None:
|
||||
outlineEls = bodyNode.findall("outline")
|
||||
|
||||
if outlineEls is not None:
|
||||
for element in outlineEls:
|
||||
items.extend(cls.parseItems(element, parentItem))
|
||||
ret = True
|
||||
|
||||
if not ret:
|
||||
QMessageBox.critical(
|
||||
settingsWidget,
|
||||
qApp.translate("Import", "OPML Import"),
|
||||
qApp.translate("Import", "This does not appear to be a valid OPML file."))
|
||||
|
||||
return None
|
||||
|
||||
return items
|
||||
|
||||
@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')
|
||||
if note is not None and not cls.isWhitespaceOnly(note):
|
||||
#body = cls.restoreNewLines(note)
|
||||
body = note
|
||||
|
||||
children = underElement.findall('outline')
|
||||
if children is not None and len(children) > 0:
|
||||
for el in children:
|
||||
items.extend(cls.parseItems(el, card))
|
||||
else:
|
||||
card.setData(Outline.type.value, 'md')
|
||||
card.setData(Outline.text.value, body)
|
||||
|
||||
return items
|
||||
|
||||
@classmethod
|
||||
def saveNewlines(cls, inString):
|
||||
"""
|
||||
Since XML parsers are notorious for stripping out significant newlines,
|
||||
save them in a form we can restore after the parse.
|
||||
"""
|
||||
inString = inString.replace("\r\n", "\n")
|
||||
inString = inString.replace("\n", "{{lf}}")
|
||||
|
||||
return inString
|
||||
|
||||
@classmethod
|
||||
def restoreNewLines(cls, inString):
|
||||
"""
|
||||
Restore any significant newlines
|
||||
"""
|
||||
return inString.replace("{{lf}}", "\n")
|
||||
|
||||
@classmethod
|
||||
def isWhitespaceOnly(cls, inString):
|
||||
"""
|
||||
Determine whether or not a string only contains whitespace.
|
||||
"""
|
||||
s = cls.restoreNewLines(inString)
|
||||
s = ''.join(s.split())
|
||||
|
||||
return len(s) is 0
|
156
manuskript/importer/pandocImporters.py
Normal file
156
manuskript/importer/pandocImporters.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.importer.abstractImporter import abstractImporter
|
||||
from manuskript.exporter.pandoc import pandocExporter
|
||||
from manuskript.importer.opmlImporter import opmlImporter
|
||||
from manuskript.importer.markdownImporter import markdownImporter
|
||||
from PyQt5.QtWidgets import qApp
|
||||
|
||||
|
||||
class pandocImporter(abstractImporter):
|
||||
|
||||
formatFrom = ""
|
||||
engine = "Pandoc"
|
||||
extraArgs = []
|
||||
|
||||
@classmethod
|
||||
def isValid(cls):
|
||||
return pandocExporter().isValid()
|
||||
|
||||
def startImport(self, filePath, parentItem, settingsWidget):
|
||||
|
||||
formatTo = self.getSetting("formatTo").value().lower()
|
||||
wrap = self.getSetting("wrap").value().lower()
|
||||
|
||||
# pandoc --from=markdown filename --to=opml --standalone
|
||||
args = [
|
||||
"--from={}".format(self.formatFrom),
|
||||
filePath,
|
||||
"--to={}".format(formatTo),
|
||||
"--wrap={}".format(wrap),
|
||||
]
|
||||
|
||||
if formatTo == "opml":
|
||||
args.append("--standalone")
|
||||
|
||||
args += self.extraArgs
|
||||
|
||||
r = pandocExporter().run(args)
|
||||
|
||||
if formatTo == "opml":
|
||||
return self.opmlImporter.startImport("", parentItem,
|
||||
settingsWidget, fromString=r)
|
||||
elif formatTo == "markdown":
|
||||
return self.mdImporter.startImport(filePath, parentItem,
|
||||
settingsWidget, fromString=r)
|
||||
|
||||
def settingsWidget(self, widget):
|
||||
"""
|
||||
Takes a QWidget that can be modified and must be returned.
|
||||
"""
|
||||
|
||||
# Add group
|
||||
group = self.addGroup(widget.toolBox.widget(0),
|
||||
qApp.translate("Import", "Pandoc import"))
|
||||
|
||||
self.addSetting("info", "label",
|
||||
qApp.translate("Import", """<b>Info:</b> Manuskript can
|
||||
import from <b>markdown</b> or <b>OPML</b>. Pandoc will
|
||||
convert your document to either (see option below), and
|
||||
then it will be imported in manuskript. One or the other
|
||||
might give better result depending on your document.
|
||||
<br/> """))
|
||||
|
||||
self.addSetting("formatTo", "combo",
|
||||
qApp.translate("Import", "Import using:"),
|
||||
vals="markdown|OPML")
|
||||
|
||||
self.addSetting("wrap", "combo",
|
||||
qApp.translate("Import", "Wrap lines:"),
|
||||
vals="auto|none|preserve",
|
||||
default="none",
|
||||
tooltip=qApp.translate("Import", """<p>Should pandoc create
|
||||
cosmetic / non-semantic line-breaks?</p><p>
|
||||
<b>auto</b>: wraps at 72 characters.<br>
|
||||
<b>none</b>: no line wrap.<br>
|
||||
<b>preserve</b>: tries to preserves line wrap from the
|
||||
original document.</p>"""))
|
||||
|
||||
for s in self.settings:
|
||||
self.settings[s].widget(group)
|
||||
|
||||
self.mdImporter = markdownImporter()
|
||||
widget = self.mdImporter.settingsWidget(widget)
|
||||
self.opmlImporter = opmlImporter()
|
||||
widget = self.opmlImporter.settingsWidget(widget)
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
class markdownPandocImporter(pandocImporter):
|
||||
|
||||
name = "Markdown"
|
||||
description = "Markdown, using pandoc"
|
||||
fileFormat = "Markdown files (*.md *.txt *)"
|
||||
icon = "text-x-markdown"
|
||||
formatFrom = "markdown"
|
||||
|
||||
class ePubPandocImporter(pandocImporter):
|
||||
|
||||
name = "ePub"
|
||||
description = ""
|
||||
fileFormat = "ePub files (*.epub)"
|
||||
icon = "application-epub+zip"
|
||||
formatFrom = "epub"
|
||||
|
||||
class docXPandocImporter(pandocImporter):
|
||||
|
||||
name = "DocX"
|
||||
description = ""
|
||||
fileFormat = "DocX files (*.docx)"
|
||||
icon = "application-vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
formatFrom = "docx"
|
||||
|
||||
class odtPandocImporter(pandocImporter):
|
||||
|
||||
name = "ODT"
|
||||
description = ""
|
||||
fileFormat = "Open Document files (*.odt)"
|
||||
icon = "application-vnd.oasis.opendocument.text"
|
||||
formatFrom = "odt"
|
||||
|
||||
class rstPandocImporter(pandocImporter):
|
||||
|
||||
name = "reStructuredText"
|
||||
description = ""
|
||||
fileFormat = "reStructuredText files (*.rst)"
|
||||
icon = "text-plain"
|
||||
formatFrom = "rst"
|
||||
|
||||
class HTMLPandocImporter(pandocImporter):
|
||||
|
||||
name = "HTML"
|
||||
description = ""
|
||||
fileFormat = "HTML files (*.htm *.html)"
|
||||
icon = "text-html"
|
||||
formatFrom = "html"
|
||||
|
||||
class LaTeXPandocImporter(pandocImporter):
|
||||
|
||||
name = "LaTeX"
|
||||
description = ""
|
||||
fileFormat = "LaTeX files (*.tex)"
|
||||
icon = "text-x-tex"
|
||||
formatFrom = "latex"
|
||||
|
||||
class OPMLPandocImporter(pandocImporter):
|
||||
|
||||
name = "OPML"
|
||||
description = ""
|
||||
fileFormat = "OPML files (*.opml *.xml)"
|
||||
icon = "text-x-opml+xml"
|
||||
formatFrom = "opml"
|
||||
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ from manuskript.settingsWindow import settingsWindow
|
|||
from manuskript.ui import style
|
||||
from manuskript.ui.about import aboutDialog
|
||||
from manuskript.ui.collapsibleDockWidgets import collapsibleDockWidgets
|
||||
from manuskript.ui.importers.importer import importerDialog
|
||||
from manuskript.ui.exporters.exporter import exporterDialog
|
||||
from manuskript.ui.helpLabel import helpLabel
|
||||
from manuskript.ui.mainWindow import Ui_MainWindow
|
||||
|
@ -97,12 +98,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# Main Menu
|
||||
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
|
||||
self.menuEdit, self.menuView, self.menuTools, self.menuHelp,
|
||||
self.actCompile, self.actSettings]:
|
||||
self.actImport, self.actCompile, self.actSettings]:
|
||||
i.setEnabled(False)
|
||||
|
||||
self.actOpen.triggered.connect(self.welcome.openFile)
|
||||
self.actSave.triggered.connect(self.saveDatas)
|
||||
self.actSaveAs.triggered.connect(self.welcome.saveAsFile)
|
||||
self.actImport.triggered.connect(self.doImport)
|
||||
self.actCompile.triggered.connect(self.doCompile)
|
||||
self.actLabels.triggered.connect(self.settingsLabel)
|
||||
self.actStatus.triggered.connect(self.settingsStatus)
|
||||
|
@ -381,6 +383,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
def openIndex(self, index):
|
||||
self.treeRedacOutline.setCurrentIndex(index)
|
||||
|
||||
def openIndexes(self, indexes, newTab=True):
|
||||
self.mainEditor.openIndexes(indexes, newTab=True)
|
||||
|
||||
###############################################################################
|
||||
# LOAD AND SAVE
|
||||
###############################################################################
|
||||
|
@ -408,7 +413,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.makeConnections()
|
||||
|
||||
# Load settings
|
||||
if settings.openIndexes:
|
||||
if settings.openIndexes and settings.openIndexes != [""]:
|
||||
self.mainEditor.tabSplitter.restoreOpenIndexes(settings.openIndexes)
|
||||
self.generateViewMenu()
|
||||
self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor)
|
||||
|
@ -460,7 +465,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
i.setEnabled(False)
|
||||
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
|
||||
self.menuEdit, self.menuView, self.menuTools, self.menuHelp,
|
||||
self.actCompile, self.actSettings]:
|
||||
self.actImport, self.actCompile, self.actSettings]:
|
||||
i.setEnabled(True)
|
||||
|
||||
# Add project name to Window's name
|
||||
|
@ -504,7 +509,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
i.setEnabled(True)
|
||||
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
|
||||
self.menuEdit, self.menuView, self.menuTools, self.menuHelp,
|
||||
self.actCompile, self.actSettings]:
|
||||
self.actImport, self.actCompile, self.actSettings]:
|
||||
i.setEnabled(False)
|
||||
|
||||
# Set Window's name - no project loaded
|
||||
|
@ -1306,9 +1311,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
# POV in settings / views
|
||||
|
||||
###############################################################################
|
||||
# COMPILE
|
||||
# IMPORT / EXPORT
|
||||
###############################################################################
|
||||
|
||||
def doImport(self):
|
||||
self.dialog = importerDialog(mw=self)
|
||||
self.dialog.show()
|
||||
|
||||
r = self.dialog.geometry()
|
||||
r2 = self.geometry()
|
||||
self.dialog.move(r2.center() - r.center())
|
||||
|
||||
def doCompile(self):
|
||||
self.dialog = exporterDialog(mw=self)
|
||||
self.dialog.show()
|
||||
|
|
|
@ -821,10 +821,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")
|
||||
|
@ -884,10 +930,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())
|
||||
|
@ -900,7 +945,7 @@ class outlineItem():
|
|||
self.IDs = self.listAllIDs()
|
||||
|
||||
if max([self.IDs.count(i) for i in self.IDs if i]) != 1:
|
||||
print("There are some doublons:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
|
||||
print("WARNING ! There are some items with same IDs:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
|
||||
|
||||
def checkChildren(item):
|
||||
for c in item.children():
|
||||
|
@ -918,8 +963,9 @@ class outlineItem():
|
|||
return IDs
|
||||
|
||||
def findUniqueID(self):
|
||||
k = 0
|
||||
while str(k) in self.IDs:
|
||||
IDs = [int(i) for i in self.IDs]
|
||||
k = 1
|
||||
while k in IDs:
|
||||
k += 1
|
||||
self.IDs.append(str(k))
|
||||
return str(k)
|
||||
|
|
|
@ -55,6 +55,8 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
self.mw = mainWindow()
|
||||
self._tabWidget = None # set by mainEditor on creation
|
||||
|
||||
self._model = None
|
||||
|
||||
# def setModel(self, model):
|
||||
# self._model = model
|
||||
# self.setView()
|
||||
|
@ -83,8 +85,10 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
|
||||
if r.isValid():
|
||||
count = r.internalPointer().childCount()
|
||||
elif self._model:
|
||||
count = self._model.rootItem.childCount()
|
||||
else:
|
||||
count = self.mw.mdlOutline.rootItem.childCount()
|
||||
count = 0
|
||||
|
||||
for c in range(count):
|
||||
self.corkView.itemDelegate().sizeHintChanged.emit(r.child(c, 0))
|
||||
|
@ -102,8 +106,10 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
|
||||
if self.currentIndex.isValid():
|
||||
item = self.currentIndex.internalPointer()
|
||||
elif self._model:
|
||||
item = self._model.rootItem
|
||||
else:
|
||||
item = self.mw.mdlOutline.rootItem
|
||||
return
|
||||
|
||||
i = self._tabWidget.indexOf(self)
|
||||
self._tabWidget.setTabText(i, item.title())
|
||||
|
@ -202,7 +208,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
|
||||
self.txtEdits = []
|
||||
|
||||
if item != self.mw.mdlOutline.rootItem:
|
||||
if item != self._model.rootItem:
|
||||
addTitle(item)
|
||||
|
||||
addChildren(item)
|
||||
|
@ -211,7 +217,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
|
||||
elif item and item.isFolder() and self.folderView == "cork":
|
||||
self.stack.setCurrentIndex(2)
|
||||
self.corkView.setModel(self.mw.mdlOutline)
|
||||
self.corkView.setModel(self._model)
|
||||
self.corkView.setRootIndex(self.currentIndex)
|
||||
try:
|
||||
self.corkView.selectionModel().selectionChanged.connect(mainWindow().redacMetadata.selectionChanged, AUC)
|
||||
|
@ -225,7 +231,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
self.outlineView.setModelCharacters(mainWindow().mdlCharacter)
|
||||
self.outlineView.setModelLabels(mainWindow().mdlLabels)
|
||||
self.outlineView.setModelStatus(mainWindow().mdlStatus)
|
||||
self.outlineView.setModel(self.mw.mdlOutline)
|
||||
self.outlineView.setModel(self._model)
|
||||
self.outlineView.setRootIndex(self.currentIndex)
|
||||
|
||||
try:
|
||||
|
@ -242,9 +248,9 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
self.txtRedacText.setCurrentModelIndex(QModelIndex())
|
||||
|
||||
try:
|
||||
self.mw.mdlOutline.dataChanged.connect(self.modelDataChanged, AUC)
|
||||
self.mw.mdlOutline.rowsInserted.connect(self.updateIndexFromID, AUC)
|
||||
self.mw.mdlOutline.rowsRemoved.connect(self.updateIndexFromID, AUC)
|
||||
self._model.dataChanged.connect(self.modelDataChanged, AUC)
|
||||
self._model.rowsInserted.connect(self.updateIndexFromID, AUC)
|
||||
self._model.rowsRemoved.connect(self.updateIndexFromID, AUC)
|
||||
#self.mw.mdlOutline.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC)
|
||||
except TypeError:
|
||||
pass
|
||||
|
@ -254,8 +260,8 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
def setCurrentModelIndex(self, index=None):
|
||||
if index.isValid():
|
||||
self.currentIndex = index
|
||||
self.currentID = self.mw.mdlOutline.ID(index)
|
||||
# self._model = index.model()
|
||||
self._model = index.model()
|
||||
self.currentID = self._model.ID(index)
|
||||
else:
|
||||
self.currentIndex = QModelIndex()
|
||||
self.currentID = None
|
||||
|
@ -267,7 +273,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
|
|||
Index might have changed (through drag an drop), so we keep current
|
||||
item's ID and update index. Item might have been deleted too.
|
||||
"""
|
||||
idx = self.mw.mdlOutline.getIndexByID(self.currentID)
|
||||
idx = self._model.getIndexByID(self.currentID)
|
||||
|
||||
# If we have an ID but the ID does not exist, it has been deleted
|
||||
if self.currentID and idx == QModelIndex():
|
||||
|
|
|
@ -50,7 +50,9 @@
|
|||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up"/>
|
||||
<iconset theme="go-up">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Alt+Up</string>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/exporters/manuskript/plainTextSettings_ui.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.4.2
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
@ -28,9 +28,10 @@ class Ui_exporterSettings(object):
|
|||
"}")
|
||||
self.toolBox.setObjectName("toolBox")
|
||||
self.content = QtWidgets.QWidget()
|
||||
self.content.setGeometry(QtCore.QRect(0, 0, 491, 842))
|
||||
self.content.setGeometry(QtCore.QRect(0, 0, 497, 834))
|
||||
self.content.setObjectName("content")
|
||||
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.content)
|
||||
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||
self.label = QtWidgets.QLabel(self.content)
|
||||
self.label.setObjectName("label")
|
||||
|
@ -111,9 +112,10 @@ class Ui_exporterSettings(object):
|
|||
self.verticalLayout_5.addItem(spacerItem1)
|
||||
self.toolBox.addItem(self.content, "")
|
||||
self.separations = QtWidgets.QWidget()
|
||||
self.separations.setGeometry(QtCore.QRect(0, 0, 511, 522))
|
||||
self.separations.setGeometry(QtCore.QRect(0, 0, 511, 534))
|
||||
self.separations.setObjectName("separations")
|
||||
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.separations)
|
||||
self.verticalLayout_8.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_8.setObjectName("verticalLayout_8")
|
||||
self.label_3 = QtWidgets.QLabel(self.separations)
|
||||
font = QtGui.QFont()
|
||||
|
@ -319,10 +321,11 @@ class Ui_exporterSettings(object):
|
|||
self.verticalLayout_8.addItem(spacerItem6)
|
||||
self.toolBox.addItem(self.separations, "")
|
||||
self.transformations = QtWidgets.QWidget()
|
||||
self.transformations.setGeometry(QtCore.QRect(0, 0, 511, 522))
|
||||
self.transformations.setGeometry(QtCore.QRect(0, 0, 511, 534))
|
||||
self.transformations.setStyleSheet("QGroupBox{font-weight:bold;}")
|
||||
self.transformations.setObjectName("transformations")
|
||||
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.transformations)
|
||||
self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
||||
self.grpTransTypo = collapsibleGroupBox2(self.transformations)
|
||||
self.grpTransTypo.setStyleSheet("")
|
||||
|
@ -481,10 +484,11 @@ class Ui_exporterSettings(object):
|
|||
self.verticalLayout_6.addItem(spacerItem10)
|
||||
self.toolBox.addItem(self.transformations, "")
|
||||
self.preview = QtWidgets.QWidget()
|
||||
self.preview.setGeometry(QtCore.QRect(0, 0, 511, 522))
|
||||
self.preview.setGeometry(QtCore.QRect(0, 0, 511, 534))
|
||||
self.preview.setStyleSheet("QGroupBox{font-weight:bold;}")
|
||||
self.preview.setObjectName("preview")
|
||||
self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.preview)
|
||||
self.verticalLayout_11.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_11.setObjectName("verticalLayout_11")
|
||||
self.groupBox = QtWidgets.QGroupBox(self.preview)
|
||||
self.groupBox.setObjectName("groupBox")
|
||||
|
|
|
@ -53,8 +53,8 @@ QToolBox::tab:selected, QToolBox::tab:hover{
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>491</width>
|
||||
<height>842</height>
|
||||
<width>497</width>
|
||||
<height>834</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
|
@ -233,7 +233,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>511</width>
|
||||
<height>522</height>
|
||||
<height>534</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
|
@ -773,7 +773,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>511</width>
|
||||
<height>522</height>
|
||||
<height>534</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
|
@ -1162,7 +1162,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>511</width>
|
||||
<height>522</height>
|
||||
<height>534</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
|
|
0
manuskript/ui/importers/__init__.py
Normal file
0
manuskript/ui/importers/__init__.py
Normal file
90
manuskript/ui/importers/generalSettings.py
Normal file
90
manuskript/ui/importers/generalSettings.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
import json
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize, QSortFilterProxyModel, QModelIndex
|
||||
from PyQt5.QtGui import QIcon, QFontMetrics, QFont
|
||||
from PyQt5.QtWidgets import QWidget, QTableWidgetItem, QListWidgetItem, QTreeView
|
||||
|
||||
from manuskript.functions import mainWindow, writablePath
|
||||
from manuskript.ui.importers.generalSettings_ui import Ui_generalSettings
|
||||
from manuskript.enums import Outline
|
||||
from manuskript.ui import style
|
||||
|
||||
|
||||
class generalSettings(QWidget, Ui_generalSettings):
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.mw = mainWindow()
|
||||
self.txtGeneralSplitScenes.setStyleSheet(style.lineEditSS())
|
||||
|
||||
# TreeView to select parent
|
||||
# We use a proxy to display only folders
|
||||
proxy = QSortFilterProxyModel()
|
||||
proxy.setFilterKeyColumn(Outline.type.value)
|
||||
proxy.setFilterFixedString("folder")
|
||||
proxy.setSourceModel(self.mw.mdlOutline)
|
||||
self.treeGeneralParent.setModel(proxy)
|
||||
for i in range(1, self.mw.mdlOutline.columnCount()):
|
||||
self.treeGeneralParent.hideColumn(i)
|
||||
self.treeGeneralParent.setCurrentIndex(self.getParentIndex())
|
||||
self.chkGeneralParent.toggled.connect(self.treeGeneralParent.setVisible)
|
||||
self.treeGeneralParent.hide()
|
||||
|
||||
def getParentIndex(self):
|
||||
"""
|
||||
Returns the currently selected index in the mainWindow.
|
||||
"""
|
||||
if len(self.mw.treeRedacOutline.selectionModel().
|
||||
selection().indexes()) == 0:
|
||||
idx = QModelIndex()
|
||||
else:
|
||||
idx = self.mw.treeRedacOutline.currentIndex()
|
||||
return idx
|
||||
|
||||
def importUnderID(self):
|
||||
"""
|
||||
Returns the ID of the item selected in treeGeneralParent, if checked.
|
||||
"""
|
||||
if self.chkGeneralParent.isChecked():
|
||||
idx = self.treeGeneralParent.currentIndex()
|
||||
# We used a filter proxy model, so we have to map back to source
|
||||
# to get an index from mdlOutline
|
||||
idx = self.treeGeneralParent.model().mapToSource(idx)
|
||||
if idx.isValid():
|
||||
return idx.internalPointer().ID()
|
||||
|
||||
return "0" # 0 is root's ID
|
||||
|
||||
def importInTopLevelFolder(self):
|
||||
"""
|
||||
Should the import be flat in the parent folder, or create a top-level
|
||||
folder?
|
||||
"""
|
||||
return self.chkGeneralTopLevel.isChecked()
|
||||
|
||||
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
|
||||
|
77
manuskript/ui/importers/generalSettings_ui.py
Normal file
77
manuskript/ui/importers/generalSettings_ui.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/importers/generalSettings_ui.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_generalSettings(object):
|
||||
def setupUi(self, generalSettings):
|
||||
generalSettings.setObjectName("generalSettings")
|
||||
generalSettings.resize(267, 401)
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(generalSettings)
|
||||
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_2.setSpacing(10)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.toolBox = QtWidgets.QToolBox(generalSettings)
|
||||
self.toolBox.setStyleSheet("QToolBox::tab{\n"
|
||||
" background-color: #BBB;\n"
|
||||
" padding: 2px;\n"
|
||||
" border: none;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"QToolBox::tab:selected, QToolBox::tab:hover{\n"
|
||||
" background-color:skyblue;\n"
|
||||
"}")
|
||||
self.toolBox.setObjectName("toolBox")
|
||||
self.general = QtWidgets.QWidget()
|
||||
self.general.setGeometry(QtCore.QRect(0, 0, 267, 378))
|
||||
self.general.setObjectName("general")
|
||||
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.general)
|
||||
self.verticalLayout_5.setContentsMargins(6, 6, 6, 6)
|
||||
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||
self.formLayout_4 = QtWidgets.QFormLayout()
|
||||
self.formLayout_4.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows)
|
||||
self.formLayout_4.setObjectName("formLayout_4")
|
||||
self.chkGeneralSplitScenes = QtWidgets.QCheckBox(self.general)
|
||||
self.chkGeneralSplitScenes.setObjectName("chkGeneralSplitScenes")
|
||||
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.chkGeneralSplitScenes)
|
||||
self.txtGeneralSplitScenes = QtWidgets.QLineEdit(self.general)
|
||||
self.txtGeneralSplitScenes.setText("")
|
||||
self.txtGeneralSplitScenes.setObjectName("txtGeneralSplitScenes")
|
||||
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtGeneralSplitScenes)
|
||||
self.chkGeneralTrimTitles = QtWidgets.QCheckBox(self.general)
|
||||
self.chkGeneralTrimTitles.setObjectName("chkGeneralTrimTitles")
|
||||
self.formLayout_4.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralTrimTitles)
|
||||
self.treeGeneralParent = QtWidgets.QTreeView(self.general)
|
||||
self.treeGeneralParent.setHeaderHidden(True)
|
||||
self.treeGeneralParent.setObjectName("treeGeneralParent")
|
||||
self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.treeGeneralParent)
|
||||
self.chkGeneralParent = QtWidgets.QCheckBox(self.general)
|
||||
self.chkGeneralParent.setObjectName("chkGeneralParent")
|
||||
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.SpanningRole, self.chkGeneralTopLevel)
|
||||
self.verticalLayout_5.addLayout(self.formLayout_4)
|
||||
self.toolBox.addItem(self.general, "")
|
||||
self.verticalLayout_2.addWidget(self.toolBox)
|
||||
|
||||
self.retranslateUi(generalSettings)
|
||||
self.toolBox.setCurrentIndex(0)
|
||||
self.toolBox.layout().setSpacing(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(generalSettings)
|
||||
|
||||
def retranslateUi(self, generalSettings):
|
||||
_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"))
|
||||
self.toolBox.setItemText(self.toolBox.indexOf(self.general), _translate("generalSettings", "General"))
|
||||
|
136
manuskript/ui/importers/generalSettings_ui.ui
Normal file
136
manuskript/ui/importers/generalSettings_ui.ui
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>generalSettings</class>
|
||||
<widget class="QWidget" name="generalSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>267</width>
|
||||
<height>401</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolBox::tab{
|
||||
background-color: #BBB;
|
||||
padding: 2px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QToolBox::tab:selected, QToolBox::tab:hover{
|
||||
background-color:skyblue;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="tabSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="general">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>267</width>
|
||||
<height>378</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<property name="rowWrapPolicy">
|
||||
<enum>QFormLayout::WrapLongRows</enum>
|
||||
</property>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="chkGeneralSplitScenes">
|
||||
<property name="text">
|
||||
<string>Split scenes at:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="txtGeneralSplitScenes">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>\n---\n</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="chkGeneralTrimTitles">
|
||||
<property name="text">
|
||||
<string>Trim long titles (> 32 chars)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QTreeView" name="treeGeneralParent">
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="chkGeneralParent">
|
||||
<property name="text">
|
||||
<string>Import under:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="chkGeneralTopLevel">
|
||||
<property name="text">
|
||||
<string>Import in a top-level folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
320
manuskript/ui/importers/importer.py
Normal file
320
manuskript/ui/importers/importer.py
Normal file
|
@ -0,0 +1,320 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
import json
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer, QUrl
|
||||
from PyQt5.QtGui import QBrush, QColor, QIcon, QDesktopServices
|
||||
from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox, QStyle
|
||||
|
||||
from manuskript.functions import lightBlue, writablePath, appPath
|
||||
from manuskript.ui.importers.importer_ui import Ui_importer
|
||||
from manuskript.ui.importers.generalSettings import generalSettings
|
||||
from manuskript.ui import style
|
||||
from manuskript import importer
|
||||
from manuskript.models.outlineModel import outlineModel, outlineItem
|
||||
from manuskript.enums import Outline
|
||||
from manuskript.exporter.pandoc import pandocExporter
|
||||
|
||||
class importerDialog(QWidget, Ui_importer):
|
||||
|
||||
formatsIcon = {
|
||||
".epub": "application-epub+zip",
|
||||
".odt": "application-vnd.oasis.opendocument.text",
|
||||
".docx": "application-vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".md": "text-x-markdown",
|
||||
".rst": "text-plain",
|
||||
".tex": "text-x-tex",
|
||||
".opml": "text-x-opml+xml",
|
||||
".xml": "text-x-opml+xml",
|
||||
".html": "text-html",
|
||||
}
|
||||
|
||||
def __init__(self, parent=None, mw=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
# Var
|
||||
self.mw = mw
|
||||
self.fileName = ""
|
||||
self.setStyleSheet(style.mainWindowSS())
|
||||
self.tree.setStyleSheet("QTreeView{background:transparent;}")
|
||||
self.editor.setStyleSheet("QWidget{background:transparent;}")
|
||||
self.editor.toggleSpellcheck(False)
|
||||
|
||||
# Register importFormats:
|
||||
self.importers = importer.importers
|
||||
|
||||
# Populate combo box with formats
|
||||
self.populateImportList()
|
||||
|
||||
# Connections
|
||||
self.btnChoseFile.clicked.connect(self.selectFile)
|
||||
self.btnClearFileName.clicked.connect(self.setFileName)
|
||||
self.btnPreview.clicked.connect(self.preview)
|
||||
self.btnImport.clicked.connect(self.doImport)
|
||||
self.cmbImporters.currentTextChanged.connect(self.updateSettings)
|
||||
|
||||
self.setFileName("")
|
||||
self.updateSettings()
|
||||
|
||||
############################################################################
|
||||
# Combobox / Formats
|
||||
############################################################################
|
||||
|
||||
def populateImportList(self):
|
||||
|
||||
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")
|
||||
self.cmbImporters.setItemData(self.cmbImporters.count() - 1, QBrush(QColor(Qt.darkBlue)), Qt.ForegroundRole)
|
||||
self.cmbImporters.setItemData(self.cmbImporters.count() - 1, QBrush(lightBlue()), Qt.BackgroundRole)
|
||||
item = self.cmbImporters.model().item(self.cmbImporters.count() - 1)
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
|
||||
lastEngine = ""
|
||||
|
||||
for f in self.importers:
|
||||
# Header
|
||||
if f.engine != lastEngine:
|
||||
addHeader(f.engine)
|
||||
lastEngine = f.engine
|
||||
|
||||
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)
|
||||
|
||||
if not pandocExporter().isValid():
|
||||
self.cmbImporters.addItem(
|
||||
self.style().standardIcon(QStyle.SP_MessageBoxWarning),
|
||||
"Install pandoc to import from much more formats",
|
||||
"::URL::http://pandoc.org/installing.html")
|
||||
|
||||
self.cmbImporters.setCurrentIndex(1)
|
||||
|
||||
def currentFormat(self):
|
||||
formatIdentifier = self.cmbImporters.currentData()
|
||||
|
||||
if formatIdentifier == "header":
|
||||
return None
|
||||
|
||||
F = [F for F in self.importers
|
||||
if formatIdentifier == "{}:{}".format(F.engine, F.name)][0]
|
||||
# We instantiate the class
|
||||
return F()
|
||||
|
||||
############################################################################
|
||||
# Import file
|
||||
############################################################################
|
||||
|
||||
def selectFile(self):
|
||||
"""
|
||||
Called to select a file in the file system. Uses QFileDialog.
|
||||
"""
|
||||
|
||||
# We find the current selected format
|
||||
F = self._format
|
||||
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
if F.fileFormat == "<<folder>>":
|
||||
options = QFileDialog.DontUseNativeDialog | QFileDialog.ShowDirsOnly
|
||||
fileName = QFileDialog.getExistingDirectory(self, "Select import folder",
|
||||
"", options=options)
|
||||
else:
|
||||
fileName, _ = QFileDialog.getOpenFileName(self, "Import from file", "",
|
||||
F.fileFormat, options=options)
|
||||
self.setFileName(fileName)
|
||||
|
||||
def setFileName(self, fileName):
|
||||
"""
|
||||
Updates Ui with given filename. Filename can be empty.
|
||||
"""
|
||||
if fileName:
|
||||
self.fileName = fileName
|
||||
self.lblFileName.setText(os.path.basename(fileName))
|
||||
self.lblFileName.setToolTip(fileName)
|
||||
ext = os.path.splitext(fileName)[1]
|
||||
icon = None
|
||||
if ext and ext in self.formatsIcon:
|
||||
icon = QIcon.fromTheme(self.formatsIcon[ext])
|
||||
elif os.path.isdir(fileName):
|
||||
icon = QIcon.fromTheme("folder")
|
||||
|
||||
if icon:
|
||||
self.lblIcon.setVisible(True)
|
||||
h = self.lblFileName.height()
|
||||
self.lblIcon.setPixmap(icon.pixmap(h, h))
|
||||
else:
|
||||
self.lblIcon.hide()
|
||||
|
||||
else:
|
||||
self.fileName = None
|
||||
self.lblFileName.setText("")
|
||||
|
||||
hasFile = True if fileName else False
|
||||
|
||||
self.btnClearFileName.setVisible(hasFile)
|
||||
self.lblIcon.setVisible(hasFile)
|
||||
self.btnChoseFile.setVisible(not hasFile)
|
||||
self.btnPreview.setEnabled(hasFile)
|
||||
self.btnImport.setEnabled(hasFile)
|
||||
|
||||
############################################################################
|
||||
# UI
|
||||
############################################################################
|
||||
|
||||
def updateSettings(self):
|
||||
"""
|
||||
When the current format change (through the combobox), we update the
|
||||
settings widget using the current format provided settings widget.
|
||||
"""
|
||||
|
||||
# We check if we have to open an URL
|
||||
data = self.cmbImporters.currentData()
|
||||
if data and data[:7] == "::URL::" and data[7:]:
|
||||
# FIXME: use functions.openURL after merge with feature/Exporters
|
||||
QDesktopServices.openUrl(QUrl(data[7:]))
|
||||
return
|
||||
|
||||
F = self.currentFormat()
|
||||
self._format = F
|
||||
|
||||
# Checking if we have a valid importer (otherwise a header)
|
||||
if not F:
|
||||
self.grpSettings.setEnabled(False)
|
||||
self.grpPreview.setEnabled(False)
|
||||
return
|
||||
self.grpSettings.setEnabled(True)
|
||||
self.grpPreview.setEnabled(True)
|
||||
|
||||
self.settingsWidget = generalSettings()
|
||||
#TODO: custom format widget
|
||||
self.settingsWidget = F.settingsWidget(self.settingsWidget)
|
||||
|
||||
# Set the settings widget in place
|
||||
self.setGroupWidget(self.grpSettings, self.settingsWidget)
|
||||
self.grpSettings.setMinimumWidth(200)
|
||||
|
||||
# Clear file name
|
||||
self.setFileName("")
|
||||
|
||||
def setGroupWidget(self, group, widget):
|
||||
"""
|
||||
Sets the given widget as main widget for QGroupBox group.
|
||||
"""
|
||||
|
||||
# Removes every items from given layout.
|
||||
l = group.layout()
|
||||
while l.count():
|
||||
item = l.itemAt(0)
|
||||
l.removeItem(item)
|
||||
item.widget().deleteLater()
|
||||
|
||||
l.addWidget(widget)
|
||||
widget.setParent(group)
|
||||
|
||||
############################################################################
|
||||
# Preview / Import
|
||||
############################################################################
|
||||
|
||||
def preview(self):
|
||||
|
||||
# Creating a temporary outlineModel
|
||||
previewModel = outlineModel(self)
|
||||
previewModel.loadFromXML(
|
||||
self.mw.mdlOutline.saveToXML(),
|
||||
fromString=True)
|
||||
|
||||
# Inserting elements
|
||||
result = self.startImport(previewModel)
|
||||
|
||||
if result:
|
||||
self.tree.setModel(previewModel)
|
||||
for i in range(1, previewModel.columnCount()):
|
||||
self.tree.hideColumn(i)
|
||||
self.tree.selectionModel().currentChanged.connect(self.editor.setCurrentModelIndex)
|
||||
self.previewSplitter.setStretchFactor(0, 10)
|
||||
self.previewSplitter.setStretchFactor(1, 40)
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
Called by the Import button.
|
||||
"""
|
||||
self.startImport(self.mw.mdlOutline)
|
||||
|
||||
# Signal every views that important model changes have happened.
|
||||
self.mw.mdlOutline.layoutChanged.emit()
|
||||
|
||||
# I'm getting seg fault over this message sometimes...
|
||||
# Using status bar message instead...
|
||||
#QMessageBox.information(self, self.tr("Import status"),
|
||||
#self.tr("Import Complete."))
|
||||
self.mw.statusBar().showMessage("Import complete!", 5000)
|
||||
|
||||
self.close()
|
||||
|
||||
def startImport(self, outlineModel):
|
||||
"""
|
||||
Where most of the magic happens.
|
||||
Is used by preview and by doImport (actual import).
|
||||
|
||||
`outlineModel` is the model where the imported items are added.
|
||||
|
||||
FIXME: Optimisation: when adding many outlineItems, outlineItem.updateWordCount
|
||||
is a bottleneck. It gets called a crazy number of time, and its not
|
||||
necessary.
|
||||
"""
|
||||
|
||||
items = []
|
||||
|
||||
# We find the current selected format
|
||||
F = self._format
|
||||
|
||||
# Parent item
|
||||
ID = self.settingsWidget.importUnderID()
|
||||
parentItem = outlineModel.getItemByID(ID)
|
||||
|
||||
# Import in top-level folder?
|
||||
if self.settingsWidget.importInTopLevelFolder():
|
||||
parent = outlineItem(title=os.path.basename(self.fileName),
|
||||
parent=parentItem)
|
||||
parentItem = parent
|
||||
items.append(parent)
|
||||
|
||||
# Calling the importer
|
||||
rItems = F.startImport(self.fileName,
|
||||
parentItem,
|
||||
self.settingsWidget)
|
||||
|
||||
items.extend(rItems)
|
||||
|
||||
# Do transformations
|
||||
items = self.doTransformations(items)
|
||||
|
||||
return True
|
||||
|
||||
def doTransformations(self, items):
|
||||
"""
|
||||
Do general transformations.
|
||||
"""
|
||||
|
||||
# Trim long titles
|
||||
if self.settingsWidget.trimLongTitles():
|
||||
for item in items:
|
||||
if len(item.title()) > 32:
|
||||
item.setData(Outline.title.value, item.title()[:32])
|
||||
|
||||
# Split at
|
||||
if self.settingsWidget.splitScenes():
|
||||
for item in items:
|
||||
item.split(self.settingsWidget.splitScenes(), recursive=False)
|
||||
|
||||
return items
|
||||
|
||||
|
107
manuskript/ui/importers/importer_ui.py
Normal file
107
manuskript/ui/importers/importer_ui.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/importers/importer_ui.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_importer(object):
|
||||
def setupUi(self, importer):
|
||||
importer.setObjectName("importer")
|
||||
importer.resize(867, 560)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(importer)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.label = QtWidgets.QLabel(importer)
|
||||
self.label.setObjectName("label")
|
||||
self.horizontalLayout.addWidget(self.label)
|
||||
self.cmbImporters = QtWidgets.QComboBox(importer)
|
||||
self.cmbImporters.setObjectName("cmbImporters")
|
||||
self.horizontalLayout.addWidget(self.cmbImporters)
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.btnChoseFile = QtWidgets.QPushButton(importer)
|
||||
icon = QtGui.QIcon.fromTheme("document-import")
|
||||
self.btnChoseFile.setIcon(icon)
|
||||
self.btnChoseFile.setObjectName("btnChoseFile")
|
||||
self.horizontalLayout.addWidget(self.btnChoseFile)
|
||||
self.lblIcon = QtWidgets.QLabel(importer)
|
||||
self.lblIcon.setText("")
|
||||
self.lblIcon.setObjectName("lblIcon")
|
||||
self.horizontalLayout.addWidget(self.lblIcon)
|
||||
self.lblFileName = QtWidgets.QLabel(importer)
|
||||
font = QtGui.QFont()
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.lblFileName.setFont(font)
|
||||
self.lblFileName.setText("")
|
||||
self.lblFileName.setObjectName("lblFileName")
|
||||
self.horizontalLayout.addWidget(self.lblFileName)
|
||||
self.btnClearFileName = QtWidgets.QPushButton(importer)
|
||||
self.btnClearFileName.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("edit-clear")
|
||||
self.btnClearFileName.setIcon(icon)
|
||||
self.btnClearFileName.setFlat(True)
|
||||
self.btnClearFileName.setObjectName("btnClearFileName")
|
||||
self.horizontalLayout.addWidget(self.btnClearFileName)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem1)
|
||||
self.btnPreview = QtWidgets.QPushButton(importer)
|
||||
icon = QtGui.QIcon.fromTheme("document-print-preview")
|
||||
self.btnPreview.setIcon(icon)
|
||||
self.btnPreview.setFlat(True)
|
||||
self.btnPreview.setObjectName("btnPreview")
|
||||
self.horizontalLayout.addWidget(self.btnPreview)
|
||||
self.btnImport = QtWidgets.QPushButton(importer)
|
||||
icon = QtGui.QIcon.fromTheme("document-import")
|
||||
self.btnImport.setIcon(icon)
|
||||
self.btnImport.setObjectName("btnImport")
|
||||
self.horizontalLayout.addWidget(self.btnImport)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.splitter = QtWidgets.QSplitter(importer)
|
||||
self.splitter.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.splitter.setChildrenCollapsible(False)
|
||||
self.splitter.setObjectName("splitter")
|
||||
self.grpSettings = QtWidgets.QGroupBox(self.splitter)
|
||||
self.grpSettings.setObjectName("grpSettings")
|
||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.grpSettings)
|
||||
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_3.setSpacing(0)
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.grpPreview = QtWidgets.QGroupBox(self.splitter)
|
||||
self.grpPreview.setObjectName("grpPreview")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.grpPreview)
|
||||
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_2.setSpacing(0)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.previewSplitter = QtWidgets.QSplitter(self.grpPreview)
|
||||
self.previewSplitter.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.previewSplitter.setObjectName("previewSplitter")
|
||||
self.tree = QtWidgets.QTreeView(self.previewSplitter)
|
||||
self.tree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.tree.setHeaderHidden(True)
|
||||
self.tree.setObjectName("tree")
|
||||
self.editor = editorWidget(self.previewSplitter)
|
||||
self.editor.setObjectName("editor")
|
||||
self.verticalLayout_2.addWidget(self.previewSplitter)
|
||||
self.verticalLayout.addWidget(self.splitter)
|
||||
|
||||
self.retranslateUi(importer)
|
||||
QtCore.QMetaObject.connectSlotsByName(importer)
|
||||
|
||||
def retranslateUi(self, importer):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
importer.setWindowTitle(_translate("importer", "Import"))
|
||||
self.label.setText(_translate("importer", "Format:"))
|
||||
self.btnChoseFile.setText(_translate("importer", "Chose file"))
|
||||
self.btnClearFileName.setToolTip(_translate("importer", "Clear file"))
|
||||
self.btnPreview.setText(_translate("importer", "Preview"))
|
||||
self.btnImport.setText(_translate("importer", "Import"))
|
||||
self.grpSettings.setTitle(_translate("importer", "Settings"))
|
||||
self.grpPreview.setTitle(_translate("importer", "Preview"))
|
||||
|
||||
from manuskript.ui.editors.editorWidget import editorWidget
|
211
manuskript/ui/importers/importer_ui.ui
Normal file
211
manuskript/ui/importers/importer_ui.ui
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>importer</class>
|
||||
<widget class="QWidget" name="importer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>867</width>
|
||||
<height>560</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cmbImporters"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnChoseFile">
|
||||
<property name="text">
|
||||
<string>Chose file</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-import">
|
||||
<normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblIcon">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblFileName">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnClearFileName">
|
||||
<property name="toolTip">
|
||||
<string>Clear file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-clear">
|
||||
<normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnPreview">
|
||||
<property name="text">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-print-preview">
|
||||
<normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnImport">
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-import"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="grpSettings">
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="grpPreview">
|
||||
<property name="title">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="previewSplitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeView" name="tree">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="editorWidget" name="editor" native="true"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>editorWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>manuskript.ui.editors.editorWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1189,12 +1189,17 @@ class Ui_MainWindow(object):
|
|||
icon = QtGui.QIcon.fromTheme("stock_view-details")
|
||||
self.actAbout.setIcon(icon)
|
||||
self.actAbout.setObjectName("actAbout")
|
||||
self.actImport = QtWidgets.QAction(MainWindow)
|
||||
icon = QtGui.QIcon.fromTheme("document-import")
|
||||
self.actImport.setIcon(icon)
|
||||
self.actImport.setObjectName("actImport")
|
||||
self.menuFile.addAction(self.actOpen)
|
||||
self.menuFile.addAction(self.menuRecents.menuAction())
|
||||
self.menuFile.addAction(self.actSave)
|
||||
self.menuFile.addAction(self.actSaveAs)
|
||||
self.menuFile.addAction(self.actCloseProject)
|
||||
self.menuFile.addSeparator()
|
||||
self.menuFile.addAction(self.actImport)
|
||||
self.menuFile.addAction(self.actCompile)
|
||||
self.menuFile.addSeparator()
|
||||
self.menuFile.addAction(self.actQuit)
|
||||
|
@ -1360,6 +1365,8 @@ class Ui_MainWindow(object):
|
|||
self.actToolFrequency.setText(_translate("MainWindow", "&Frequency Analyzer"))
|
||||
self.actAbout.setText(_translate("MainWindow", "&About"))
|
||||
self.actAbout.setToolTip(_translate("MainWindow", "About Manuskript"))
|
||||
self.actImport.setText(_translate("MainWindow", "Import…"))
|
||||
self.actImport.setShortcut(_translate("MainWindow", "F7"))
|
||||
|
||||
from manuskript.ui.cheatSheet import cheatSheet
|
||||
from manuskript.ui.editors.mainEditor import mainEditor
|
||||
|
|
|
@ -2116,6 +2116,7 @@
|
|||
<addaction name="actSaveAs"/>
|
||||
<addaction name="actCloseProject"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actImport"/>
|
||||
<addaction name="actCompile"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actQuit"/>
|
||||
|
@ -2482,6 +2483,17 @@ QListView::item:hover {
|
|||
<string>About Manuskript</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actImport">
|
||||
<property name="icon">
|
||||
<iconset theme="document-import"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import…</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F7</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
from PyQt5.QtCore import Qt, QSignalMapper, QSize
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtGui import QIcon, QCursor
|
||||
from PyQt5.QtWidgets import QAbstractItemView, qApp, QMenu, QAction
|
||||
from PyQt5.QtWidgets import QListWidget, QWidgetAction, QListWidgetItem, QLineEdit
|
||||
|
||||
|
@ -14,7 +14,7 @@ from manuskript.models.outlineModel import outlineItem
|
|||
|
||||
class outlineBasics(QAbstractItemView):
|
||||
def __init__(self, parent=None):
|
||||
pass
|
||||
self._indexesToOpen = None
|
||||
|
||||
def getSelection(self):
|
||||
sel = []
|
||||
|
@ -39,11 +39,44 @@ class outlineBasics(QAbstractItemView):
|
|||
|
||||
menu = QMenu(self)
|
||||
|
||||
# Open items
|
||||
self.actOpen = QAction(QIcon.fromTheme("go-right"), qApp.translate("outlineBasics", "Open Item"), menu)
|
||||
# Get index under cursor
|
||||
pos = self.viewport().mapFromGlobal(QCursor.pos())
|
||||
mouseIndex = self.indexAt(pos)
|
||||
|
||||
# Get index's title
|
||||
if mouseIndex.isValid():
|
||||
title = mouseIndex.internalPointer().title()
|
||||
|
||||
elif self.rootIndex().parent().isValid():
|
||||
# mouseIndex is the background of an item, so we check the parent
|
||||
mouseIndex = self.rootIndex().parent()
|
||||
title = mouseIndex.internalPointer().title()
|
||||
|
||||
else:
|
||||
title = self.tr("Root")
|
||||
|
||||
if len(title) > 25:
|
||||
title = title[:25] + "…"
|
||||
|
||||
# Open Item action
|
||||
self.actOpen = QAction(QIcon.fromTheme("go-right"),
|
||||
qApp.translate("outlineBasics", "Open {}".format(title)),
|
||||
menu)
|
||||
self.actOpen.triggered.connect(self.openItem)
|
||||
menu.addAction(self.actOpen)
|
||||
|
||||
# Open item(s) in new tab
|
||||
if mouseIndex in sel and len(sel) > 1:
|
||||
actionTitle = self.tr("Open {} items in new tabs").format(len(sel))
|
||||
self._indexesToOpen = sel
|
||||
else:
|
||||
actionTitle = self.tr("Open {} in a new tab").format(title)
|
||||
self._indexesToOpen = [mouseIndex]
|
||||
|
||||
self.actNewTab = QAction(QIcon.fromTheme("go-right"), actionTitle, menu)
|
||||
self.actNewTab.triggered.connect(self.openItemsInNewTabs)
|
||||
menu.addAction(self.actNewTab)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
# Rename / add / remove items
|
||||
|
@ -185,7 +218,6 @@ class outlineBasics(QAbstractItemView):
|
|||
self.actAddText.setEnabled(False)
|
||||
|
||||
if len(sel) == 0:
|
||||
self.actOpen.setEnabled(False)
|
||||
self.actCopy.setEnabled(False)
|
||||
self.actCut.setEnabled(False)
|
||||
self.actRename.setEnabled(False)
|
||||
|
@ -201,10 +233,15 @@ class outlineBasics(QAbstractItemView):
|
|||
return menu
|
||||
|
||||
def openItem(self):
|
||||
idx = self.currentIndex()
|
||||
#idx = self.currentIndex()
|
||||
idx = self._indexesToOpen[0]
|
||||
from manuskript.functions import MW
|
||||
MW.openIndex(idx)
|
||||
|
||||
def openItemsInNewTabs(self):
|
||||
from manuskript.functions import MW
|
||||
MW.openIndexes(self._indexesToOpen)
|
||||
|
||||
def rename(self):
|
||||
if len(self.getSelection()) == 1:
|
||||
index = self.currentIndex()
|
||||
|
|
|
@ -31,30 +31,13 @@ class treeView(QTreeView, dndView, outlineBasics):
|
|||
|
||||
def makePopupMenu(self):
|
||||
menu = outlineBasics.makePopupMenu(self)
|
||||
first = menu.actions()[0]
|
||||
first = menu.actions()[3]
|
||||
|
||||
# Open item in new tab
|
||||
sel = self.selectedIndexes()
|
||||
#sel = self.selectedIndexes()
|
||||
pos = self.viewport().mapFromGlobal(QCursor.pos())
|
||||
mouseIndex = self.indexAt(pos)
|
||||
|
||||
if mouseIndex.isValid():
|
||||
mouseTitle = mouseIndex.internalPointer().title()
|
||||
else:
|
||||
mouseTitle = self.tr("Root")
|
||||
|
||||
if mouseIndex in sel and len(sel) > 1:
|
||||
actionTitle = self.tr("Open {} items in new tabs").format(len(sel))
|
||||
self._indexesToOpen = sel
|
||||
else:
|
||||
actionTitle = self.tr("Open {} in a new tab").format(mouseTitle)
|
||||
self._indexesToOpen = [mouseIndex]
|
||||
|
||||
self.actNewTab = QAction(actionTitle, menu)
|
||||
self.actNewTab.triggered.connect(self.openNewTab)
|
||||
menu.insertAction(first, self.actNewTab)
|
||||
menu.insertSeparator(first)
|
||||
|
||||
# Expand /collapse item
|
||||
if mouseIndex.isValid():
|
||||
# index = self.currentIndex()
|
||||
|
@ -83,9 +66,6 @@ class treeView(QTreeView, dndView, outlineBasics):
|
|||
|
||||
return menu
|
||||
|
||||
def openNewTab(self):
|
||||
mainWindow().mainEditor.openIndexes(self._indexesToOpen, newTab=True)
|
||||
|
||||
def expandCurrentIndex(self, index=None):
|
||||
if index is None or type(index) == bool:
|
||||
index = self._indexesToOpen[0] # self.currentIndex()
|
||||
|
|
Loading…
Reference in a new issue