Fixes more bugs in drag n drop: seg fault when creating an item after deleting an item that was open for editing, and other small stuff

This commit is contained in:
Olivier Keshavjee 2017-10-20 23:39:57 +02:00
parent 1a8d4c5c72
commit 648b4b67eb
8 changed files with 97 additions and 43 deletions

View file

@ -124,14 +124,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.makeUIConnections() self.makeUIConnections()
# self.loadProject(os.path.join(appPath(), "test_project.zip")) # self.loadProject(os.path.join(appPath(), "test_project.zip"))
def updateDockVisibility(self, restore=False): 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 restores from `self._dckVisibility`. This allows to hide the docks
while showing the welcome screen, and then restore them as they while showing the welcome screen, and then restore them as they
were. were.
If `self._dckVisibility` contains "LOCK", then we don't override values If `self._dckVisibility` contains "LOCK", then we don't override values
with current visibility state. This is used the first time we load. with current visibility state. This is used the first time we load.
"LOCK" is then removed. "LOCK" is then removed.
@ -141,7 +141,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.dckNavigation, self.dckNavigation,
self.dckSearch, self.dckSearch,
] ]
for d in docks: for d in docks:
if not restore: if not restore:
# We store the values, but only if "LOCK" is not present # We store the values, but only if "LOCK" is not present
@ -152,11 +152,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
else: else:
# Restore the dock's visibily based on stored value # Restore the dock's visibily based on stored value
d.setVisible(self._dckVisibility[d.objectName()]) d.setVisible(self._dckVisibility[d.objectName()])
# Lock is used only once, at start up. We can remove it # Lock is used only once, at start up. We can remove it
if "LOCK" in self._dckVisibility: if "LOCK" in self._dckVisibility:
self._dckVisibility.pop("LOCK") self._dckVisibility.pop("LOCK")
def switchToWelcome(self): def switchToWelcome(self):
""" """
While switching to welcome screen, we have to hide all the docks. While switching to welcome screen, we have to hide all the docks.
@ -171,7 +171,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.toolbar.setVisible(False) self.toolbar.setVisible(False)
# Switch to welcome screen # Switch to welcome screen
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
def switchToProject(self): def switchToProject(self):
"""Restores docks and toolbar visibility, and switch to project.""" """Restores docks and toolbar visibility, and switch to project."""
# Restores the docks visibility # Restores the docks visibility
@ -549,14 +549,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState()) sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState())
sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState()) sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState())
sttgns.setValue("toolbar", self.toolbar.saveState()) sttgns.setValue("toolbar", self.toolbar.saveState())
# If we are not in the welcome window, we update the visibility # If we are not in the welcome window, we update the visibility
# of the docks widgets # of the docks widgets
if self.stack.currentIndex() == 1: if self.stack.currentIndex() == 1:
self.updateDockVisibility() self.updateDockVisibility()
# Storing the visibility of docks to restore it on restart # Storing the visibility of docks to restore it on restart
sttgns.setValue("docks", self._dckVisibility) sttgns.setValue("docks", self._dckVisibility)
# Specific settings to save before quitting # Specific settings to save before quitting
settings.lastTab = self.tabMain.currentIndex() settings.lastTab = self.tabMain.currentIndex()
@ -588,7 +588,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
r = loadSave.saveProject() # version=0 r = loadSave.saveProject() # version=0
self.saveTimerNoChanges.stop() self.saveTimerNoChanges.stop()
if r: if r:
feedback = self.tr("Project {} saved.").format(self.currentProject) feedback = self.tr("Project {} saved.").format(self.currentProject)
else: else:
@ -976,7 +976,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
#"" #""
#] #]
#self.tabMain.setTabIcon(i, QIcon(appPath("icons/Custom/Tabs/{}".format(icons[i])))) #self.tabMain.setTabIcon(i, QIcon(appPath("icons/Custom/Tabs/{}".format(icons[i]))))
icons = [QIcon.fromTheme("stock_view-details"), #info icons = [QIcon.fromTheme("stock_view-details"), #info
QIcon.fromTheme("application-text-template"), #applications-publishing QIcon.fromTheme("application-text-template"), #applications-publishing
F.themeIcon("characters"), F.themeIcon("characters"),
@ -987,7 +987,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
QIcon.fromTheme("applications-debugging") QIcon.fromTheme("applications-debugging")
] ]
self.tabMain.setTabIcon(i, icons[i]) self.tabMain.setTabIcon(i, icons[i])
item = QListWidgetItem(self.tabMain.tabIcon(i), item = QListWidgetItem(self.tabMain.tabIcon(i),
self.tabMain.tabText(i)) self.tabMain.tabText(i))
item.setSizeHint(QSize(item.sizeHint().width(), 64)) item.setSizeHint(QSize(item.sizeHint().width(), 64))

View file

@ -25,10 +25,11 @@ class outlineModel(QAbstractItemModel):
def __init__(self, parent): def __init__(self, parent):
QAbstractItemModel.__init__(self, parent) QAbstractItemModel.__init__(self, parent)
self.rootItem = outlineItem(self, title="root", ID="0") self.rootItem = outlineItem(self, title="Root", ID="0")
# Stores removed item, in order to remove them on disk when saving, depending on the file format. # Stores removed item, in order to remove them on disk when saving, depending on the file format.
self.removed = [] self.removed = []
self._removingRows = False
def index(self, row, column, parent): def index(self, row, column, parent):
@ -266,13 +267,13 @@ class outlineModel(QAbstractItemModel):
items = self.decodeMimeData(data) items = self.decodeMimeData(data)
if items is None: if items is None:
return False return False
# Get the parent item # Get the parent item
if not parent.isValid(): if not parent.isValid():
parentItem = self.rootItem parentItem = self.rootItem
else: else:
parentItem = parent.internalPointer() parentItem = parent.internalPointer()
for item in items: for item in items:
# Get parentItem's parents IDs in a list # Get parentItem's parents IDs in a list
path = parentItem.pathID() # path to item in the form [(ID, title), ...] path = parentItem.pathID() # path to item in the form [(ID, title), ...]
@ -281,7 +282,7 @@ class outlineModel(QAbstractItemModel):
# as a children of himself. # as a children of himself.
if item.ID() in path: if item.ID() in path:
return False return False
return True return True
def decodeMimeData(self, data): def decodeMimeData(self, data):
@ -300,7 +301,7 @@ class outlineModel(QAbstractItemModel):
if child.tag == "outlineItem": if child.tag == "outlineItem":
item = outlineItem(xml=ET.tostring(child)) item = outlineItem(xml=ET.tostring(child))
items.append(item) items.append(item)
return items return items
def dropMimeData(self, data, action, row, column, parent): def dropMimeData(self, data, action, row, column, parent):
@ -311,7 +312,7 @@ class outlineModel(QAbstractItemModel):
items = self.decodeMimeData(data) items = self.decodeMimeData(data)
if items is None: if items is None:
return False return False
if column > 0: if column > 0:
column = 0 column = 0
@ -408,11 +409,14 @@ class outlineModel(QAbstractItemModel):
else: else:
parentItem = parent.internalPointer() parentItem = parent.internalPointer()
self._removingRows = True # Views that are updating can easily know
# if this is due to row removal.
self.beginRemoveRows(parent, row, row + count - 1) self.beginRemoveRows(parent, row, row + count - 1)
for i in range(count): for i in range(count):
item = parentItem.removeChild(row) item = parentItem.removeChild(row)
self.removed.append(item) self.removed.append(item)
self._removingRows = False
self.endRemoveRows() self.endRemoveRows()
return True return True

View file

@ -53,6 +53,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
self.spellcheck = True self.spellcheck = True
self.folderView = "cork" self.folderView = "cork"
self.mw = mainWindow() self.mw = mainWindow()
self._tabWidget = None # set by mainEditor on creation
# def setModel(self, model): # def setModel(self, model):
# self._model = model # self._model = model
@ -88,6 +89,25 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
for c in range(count): for c in range(count):
self.corkView.itemDelegate().sizeHintChanged.emit(r.child(c, 0)) self.corkView.itemDelegate().sizeHintChanged.emit(r.child(c, 0))
def updateTabTitle(self):
"""
`editorWidget` belongs to a `QTabWidget` in a `tabSplitter`. We update
the tab title to reflect that of current item.
"""
# `self._tabWidget` is set by mainEditor when creating tab and `editorWidget`.
# if `editorWidget` is ever used out of `mainEditor`, this could throw
# an error.
if not self._tabWidget:
return
if self.currentIndex.isValid():
item = self.currentIndex.internalPointer()
else:
item = self.mw.mdlOutline.rootItem
i = self._tabWidget.indexOf(self)
self._tabWidget.setTabText(i, item.title())
def setView(self): def setView(self):
# index = mainWindow().treeRedacOutline.currentIndex() # index = mainWindow().treeRedacOutline.currentIndex()
@ -110,6 +130,8 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
else: else:
item = self.mw.mdlOutline.rootItem item = self.mw.mdlOutline.rootItem
self.updateTabTitle()
def addTitle(itm): def addTitle(itm):
edt = textEditView(self, html="<h{l}>{t}</h{l}>".format(l=min(itm.level() + 1, 5), t=itm.title()), edt = textEditView(self, html="<h{l}>{t}</h{l}>".format(l=min(itm.level() + 1, 5), t=itm.title()),
autoResize=True) autoResize=True)
@ -213,14 +235,17 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
except TypeError: except TypeError:
pass pass
else: if item and item.isText():
self.txtRedacText.setCurrentModelIndex(self.currentIndex) self.txtRedacText.setCurrentModelIndex(self.currentIndex)
self.stack.setCurrentIndex(0) # Single text item self.stack.setCurrentIndex(0) # Single text item
else:
self.txtRedacText.setCurrentModelIndex(QModelIndex())
try: try:
self.mw.mdlOutline.dataChanged.connect(self.modelDataChanged, AUC) self.mw.mdlOutline.dataChanged.connect(self.modelDataChanged, AUC)
self.mw.mdlOutline.rowsInserted.connect(self.updateIndexFromID, AUC) self.mw.mdlOutline.rowsInserted.connect(self.updateIndexFromID, AUC)
self.mw.mdlOutline.rowsRemoved.connect(self.updateIndexFromID, AUC) self.mw.mdlOutline.rowsRemoved.connect(self.updateIndexFromID, AUC)
#self.mw.mdlOutline.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC)
except TypeError: except TypeError:
pass pass
@ -233,16 +258,30 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
# self._model = index.model() # self._model = index.model()
else: else:
self.currentIndex = QModelIndex() self.currentIndex = QModelIndex()
self.currentID = None
self.setView() self.setView()
def updateIndexFromID(self): def updateIndexFromID(self):
""" """
Index might have changed (through drag an drop), so we keep current Index might have changed (through drag an drop), so we keep current
item's ID and update index. item's ID and update index. Item might have been deleted too.
""" """
idx = self.mw.mdlOutline.getIndexByID(self.currentID) idx = self.mw.mdlOutline.getIndexByID(self.currentID)
if idx != self.currentIndex:
# If we have an ID but the ID does not exist, it has been deleted
if self.currentID and idx == QModelIndex():
# Item has been deleted, we open the parent instead
self.setCurrentModelIndex(self.currentIndex.parent())
# FIXME: selection in self.mw.treeRedacOutline is not updated
# but we cannot simply setCurrentIndex through treeRedacOutline
# because this might be a tab in the background / out of focus
# Also the UI of mainEditor is not updated (so the folder icons
# are not display, button "up" doesn't work, etc.).
# Item has been moved
elif idx != self.currentIndex:
# We update the index
self.currentIndex = idx self.currentIndex = idx
self.setView() self.setView()
@ -254,6 +293,13 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
if topLeft.row() <= self.currentIndex.row() <= bottomRight.row(): if topLeft.row() <= self.currentIndex.row() <= bottomRight.row():
self.updateStatusBar() self.updateStatusBar()
#def rowsAboutToBeRemoved(self, parent, first, last):
#if self.currentIndex:
#if self.currentIndex.parent() == parent and \
#first <= self.currentIndex.row() <= last:
## Item deleted, close tab
#self.mw.mainEditor.tab.removeTab(self.mw.mainEditor.tab.indexOf(self))
def updateStatusBar(self): def updateStatusBar(self):
# Update progress # Update progress
# if self.currentIndex and self.currentIndex.isValid(): # if self.currentIndex and self.currentIndex.isValid():

View file

@ -179,14 +179,17 @@ class mainEditor(QWidget, Ui_mainEditor):
if self._updating: if self._updating:
return return
if len(self.mw.treeRedacOutline.selectionModel(). # This might be called during a drag n drop operation, or while deleting
selection().indexes()) == 0: # items. If so, we don't want to do anything.
idx = QModelIndex() if not self.mw.mdlOutline._removingRows:
else: if len(self.mw.treeRedacOutline.selectionModel().
idx = self.mw.treeRedacOutline.currentIndex() selection().indexes()) == 0:
idx = QModelIndex()
else:
idx = self.mw.treeRedacOutline.currentIndex()
self.setCurrentModelIndex(idx) self.setCurrentModelIndex(idx)
self.updateThingsVisible(idx) self.updateThingsVisible(idx)
def openIndexes(self, indexes, newTab=False): def openIndexes(self, indexes, newTab=False):
for i in indexes: for i in indexes:
@ -194,8 +197,7 @@ class mainEditor(QWidget, Ui_mainEditor):
def goToParentItem(self): def goToParentItem(self):
idx = self.currentEditor().currentIndex idx = self.currentEditor().currentIndex
from manuskript.functions import MW self.mw.treeRedacOutline.setCurrentIndex(idx.parent())
MW.treeRedacOutline.setCurrentIndex(idx.parent())
def setCurrentModelIndex(self, index, newTab=False, tabWidget=None): def setCurrentModelIndex(self, index, newTab=False, tabWidget=None):
@ -216,11 +218,12 @@ class mainEditor(QWidget, Ui_mainEditor):
if newTab or not tabWidget.count(): if newTab or not tabWidget.count():
editor = editorWidget(self) editor = editorWidget(self)
editor.setCurrentModelIndex(index) editor.setCurrentModelIndex(index)
editor._tabWidget = tabWidget
tabWidget.addTab(editor, title) tabWidget.addTab(editor, title)
tabWidget.setCurrentIndex(tabWidget.count() - 1) tabWidget.setCurrentIndex(tabWidget.count() - 1)
else: else:
self.currentEditor(tabWidget).setCurrentModelIndex(index) self.currentEditor(tabWidget).setCurrentModelIndex(index)
tabWidget.setTabText(tabWidget.currentIndex(), title) #tabWidget.setTabText(tabWidget.currentIndex(), title)
def updateTargets(self): def updateTargets(self):
"""Updates all tabSplitter that are targets. This is called from editorWidget.""" """Updates all tabSplitter that are targets. This is called from editorWidget."""

View file

@ -249,4 +249,4 @@ class tabSplitter(QWidget, Ui_tabSplitter):
# }}""".format(self.splitter.objectName())) # }}""".format(self.splitter.objectName()))
self.setStyleSheet(style.mainEditorTabSS()) self.setStyleSheet(style.mainEditorTabSS())
return QWidget.eventFilter(self, object, event) return QWidget.eventFilter(self, object, event)

View file

@ -16,9 +16,9 @@ class metadataView(QWidget, Ui_metadataView):
self.txtSummaryFull.setColumn(Outline.summaryFull.value) self.txtSummaryFull.setColumn(Outline.summaryFull.value)
self.txtNotes.setColumn(Outline.notes.value) self.txtNotes.setColumn(Outline.notes.value)
self.revisions.setEnabled(False) self.revisions.setEnabled(False)
self.txtSummarySentence.setStyleSheet(style.lineEditSS()) self.txtSummarySentence.setStyleSheet(style.lineEditSS())
self.txtSummaryFull.setStyleSheet(style.transparentSS() + self.txtSummaryFull.setStyleSheet(style.transparentSS() +
style.simpleScrollBarV()) style.simpleScrollBarV())
self.txtNotes.setStyleSheet(style.transparentSS() + self.txtNotes.setStyleSheet(style.transparentSS() +
style.simpleScrollBarV()) style.simpleScrollBarV())
@ -60,6 +60,9 @@ class metadataView(QWidget, Ui_metadataView):
if len(indexes) == 0: if len(indexes) == 0:
self.setEnabled(False) self.setEnabled(False)
self.revisions.setEnabled(False) self.revisions.setEnabled(False)
self.txtSummarySentence.setCurrentModelIndex(QModelIndex())
self.txtSummaryFull.setCurrentModelIndex(QModelIndex())
self.txtNotes.setCurrentModelIndex(QModelIndex())
# One item selected # One item selected
elif len(indexes) == 1: elif len(indexes) == 1:
@ -89,7 +92,7 @@ class metadataView(QWidget, Ui_metadataView):
# Behavior 2: # Behavior 2:
# Allow edition of multiple indexes. # Allow edition of multiple indexes.
# Bug: Multiple selections of items sometimes gets Notes/references # Bug: Multiple selections of items sometimes gets Notes/references
# field to be ereased. See #10 on github. # field to be ereased. See #10 on github.
#self.txtSummarySentence.setCurrentModelIndexes(indexes) #self.txtSummarySentence.setCurrentModelIndexes(indexes)
#self.txtSummaryFull.setCurrentModelIndexes(indexes) #self.txtSummaryFull.setCurrentModelIndexes(indexes)

View file

@ -42,9 +42,9 @@ class outlineBasics(QAbstractItemView):
self.actOpen = QAction(QIcon.fromTheme("go-right"), qApp.translate("outlineBasic", "Open Item"), menu) self.actOpen = QAction(QIcon.fromTheme("go-right"), qApp.translate("outlineBasic", "Open Item"), menu)
self.actOpen.triggered.connect(self.openItem) self.actOpen.triggered.connect(self.openItem)
menu.addAction(self.actOpen) menu.addAction(self.actOpen)
menu.addSeparator() menu.addSeparator()
# Add / remove items # Add / remove items
self.actAddFolder = QAction(QIcon.fromTheme("folder-new"), qApp.translate("outlineBasics", "New Folder"), menu) self.actAddFolder = QAction(QIcon.fromTheme("folder-new"), qApp.translate("outlineBasics", "New Folder"), menu)
self.actAddFolder.triggered.connect(self.addFolder) self.actAddFolder.triggered.connect(self.addFolder)
@ -154,7 +154,7 @@ class outlineBasics(QAbstractItemView):
idx = self.currentIndex() idx = self.currentIndex()
from manuskript.functions import MW from manuskript.functions import MW
MW.openIndex(idx) MW.openIndex(idx)
def addFolder(self): def addFolder(self):
self.addItem("folder") self.addItem("folder")

View file

@ -208,11 +208,11 @@ class textEditView(QTextEdit):
if self.parent().__class__ == QWidget: if self.parent().__class__ == QWidget:
self.parent().setStyleSheet("background: {bg};".format( self.parent().setStyleSheet("background: {bg};".format(
bg=opt["background"])) bg=opt["background"]))
cf = QTextCharFormat() cf = QTextCharFormat()
# cf.setFont(f) # cf.setFont(f)
# cf.setForeground(QColor(opt["fontColor"])) # cf.setForeground(QColor(opt["fontColor"]))
self.setCursorWidth(opt["cursorWidth"]) self.setCursorWidth(opt["cursorWidth"])
bf = QTextBlockFormat() bf = QTextBlockFormat()
@ -237,8 +237,7 @@ class textEditView(QTextEdit):
if self._updating: if self._updating:
return return
elif self._index: elif self._index and self._index.isValid():
if topLeft.parent() != self._index.parent(): if topLeft.parent() != self._index.parent():
return return
@ -266,7 +265,6 @@ class textEditView(QTextEdit):
first <= self._index.row() <= last: first <= self._index.row() <= last:
self._index = None self._index = None
self.setEnabled(False) self.setEnabled(False)
# FIXME: self._indexes # FIXME: self._indexes
def disconnectDocument(self): def disconnectDocument(self):