From 648b4b67ebedbd5e5d7a08285b20b253b9e1885c Mon Sep 17 00:00:00 2001 From: Olivier Keshavjee Date: Fri, 20 Oct 2017 23:39:57 +0200 Subject: [PATCH] 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 --- manuskript/mainWindow.py | 24 ++++++------- manuskript/models/outlineModel.py | 16 +++++---- manuskript/ui/editors/editorWidget.py | 52 +++++++++++++++++++++++++-- manuskript/ui/editors/mainEditor.py | 23 ++++++------ manuskript/ui/editors/tabSplitter.py | 2 +- manuskript/ui/views/metadataView.py | 9 +++-- manuskript/ui/views/outlineBasics.py | 6 ++-- manuskript/ui/views/textEditView.py | 8 ++--- 8 files changed, 97 insertions(+), 43 deletions(-) diff --git a/manuskript/mainWindow.py b/manuskript/mainWindow.py index 484710bf..7fa6bb60 100644 --- a/manuskript/mainWindow.py +++ b/manuskript/mainWindow.py @@ -124,14 +124,14 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.makeUIConnections() # self.loadProject(os.path.join(appPath(), "test_project.zip")) - + def updateDockVisibility(self, restore=False): """ - Saves the state of the docks visibility. Or if `restore` is True, + Saves the state of the docks visibility. Or if `restore` is True, restores from `self._dckVisibility`. This allows to hide the docks while showing the welcome screen, and then restore them as they were. - + If `self._dckVisibility` contains "LOCK", then we don't override values with current visibility state. This is used the first time we load. "LOCK" is then removed. @@ -141,7 +141,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.dckNavigation, self.dckSearch, ] - + for d in docks: if not restore: # We store the values, but only if "LOCK" is not present @@ -152,11 +152,11 @@ class MainWindow(QMainWindow, Ui_MainWindow): else: # Restore the dock's visibily based on stored value d.setVisible(self._dckVisibility[d.objectName()]) - + # Lock is used only once, at start up. We can remove it if "LOCK" in self._dckVisibility: self._dckVisibility.pop("LOCK") - + def switchToWelcome(self): """ While switching to welcome screen, we have to hide all the docks. @@ -171,7 +171,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.toolbar.setVisible(False) # Switch to welcome screen self.stack.setCurrentIndex(0) - + def switchToProject(self): """Restores docks and toolbar visibility, and switch to project.""" # Restores the docks visibility @@ -549,14 +549,14 @@ class MainWindow(QMainWindow, Ui_MainWindow): sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState()) sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState()) sttgns.setValue("toolbar", self.toolbar.saveState()) - + # If we are not in the welcome window, we update the visibility # of the docks widgets if self.stack.currentIndex() == 1: self.updateDockVisibility() # Storing the visibility of docks to restore it on restart sttgns.setValue("docks", self._dckVisibility) - + # Specific settings to save before quitting settings.lastTab = self.tabMain.currentIndex() @@ -588,7 +588,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): r = loadSave.saveProject() # version=0 self.saveTimerNoChanges.stop() - + if r: feedback = self.tr("Project {} saved.").format(self.currentProject) else: @@ -976,7 +976,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): #"" #] #self.tabMain.setTabIcon(i, QIcon(appPath("icons/Custom/Tabs/{}".format(icons[i])))) - + icons = [QIcon.fromTheme("stock_view-details"), #info QIcon.fromTheme("application-text-template"), #applications-publishing F.themeIcon("characters"), @@ -987,7 +987,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): QIcon.fromTheme("applications-debugging") ] self.tabMain.setTabIcon(i, icons[i]) - + item = QListWidgetItem(self.tabMain.tabIcon(i), self.tabMain.tabText(i)) item.setSizeHint(QSize(item.sizeHint().width(), 64)) diff --git a/manuskript/models/outlineModel.py b/manuskript/models/outlineModel.py index 4acc034a..244130be 100644 --- a/manuskript/models/outlineModel.py +++ b/manuskript/models/outlineModel.py @@ -25,10 +25,11 @@ class outlineModel(QAbstractItemModel): def __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. self.removed = [] + self._removingRows = False def index(self, row, column, parent): @@ -266,13 +267,13 @@ class outlineModel(QAbstractItemModel): items = self.decodeMimeData(data) if items is None: return False - + # Get the parent item if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() - + for item in items: # Get parentItem's parents IDs in a list path = parentItem.pathID() # path to item in the form [(ID, title), ...] @@ -281,7 +282,7 @@ class outlineModel(QAbstractItemModel): # as a children of himself. if item.ID() in path: return False - + return True def decodeMimeData(self, data): @@ -300,7 +301,7 @@ class outlineModel(QAbstractItemModel): if child.tag == "outlineItem": item = outlineItem(xml=ET.tostring(child)) items.append(item) - + return items def dropMimeData(self, data, action, row, column, parent): @@ -311,7 +312,7 @@ class outlineModel(QAbstractItemModel): items = self.decodeMimeData(data) if items is None: return False - + if column > 0: column = 0 @@ -408,11 +409,14 @@ class outlineModel(QAbstractItemModel): else: 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) for i in range(count): item = parentItem.removeChild(row) self.removed.append(item) + self._removingRows = False self.endRemoveRows() return True diff --git a/manuskript/ui/editors/editorWidget.py b/manuskript/ui/editors/editorWidget.py index 014bf1a7..75abb568 100644 --- a/manuskript/ui/editors/editorWidget.py +++ b/manuskript/ui/editors/editorWidget.py @@ -53,6 +53,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui): self.spellcheck = True self.folderView = "cork" self.mw = mainWindow() + self._tabWidget = None # set by mainEditor on creation # def setModel(self, model): # self._model = model @@ -88,6 +89,25 @@ class editorWidget(QWidget, Ui_editorWidget_ui): for c in range(count): 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): # index = mainWindow().treeRedacOutline.currentIndex() @@ -110,6 +130,8 @@ class editorWidget(QWidget, Ui_editorWidget_ui): else: item = self.mw.mdlOutline.rootItem + self.updateTabTitle() + def addTitle(itm): edt = textEditView(self, html="{t}".format(l=min(itm.level() + 1, 5), t=itm.title()), autoResize=True) @@ -213,14 +235,17 @@ class editorWidget(QWidget, Ui_editorWidget_ui): except TypeError: pass - else: + if item and item.isText(): self.txtRedacText.setCurrentModelIndex(self.currentIndex) self.stack.setCurrentIndex(0) # Single text item + else: + 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.mw.mdlOutline.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC) except TypeError: pass @@ -233,16 +258,30 @@ class editorWidget(QWidget, Ui_editorWidget_ui): # self._model = index.model() else: self.currentIndex = QModelIndex() + self.currentID = None self.setView() def updateIndexFromID(self): """ 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) - 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.setView() @@ -254,6 +293,13 @@ class editorWidget(QWidget, Ui_editorWidget_ui): if topLeft.row() <= self.currentIndex.row() <= bottomRight.row(): 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): # Update progress # if self.currentIndex and self.currentIndex.isValid(): diff --git a/manuskript/ui/editors/mainEditor.py b/manuskript/ui/editors/mainEditor.py index 99c4825a..16358b08 100644 --- a/manuskript/ui/editors/mainEditor.py +++ b/manuskript/ui/editors/mainEditor.py @@ -179,14 +179,17 @@ class mainEditor(QWidget, Ui_mainEditor): if self._updating: return - if len(self.mw.treeRedacOutline.selectionModel(). - selection().indexes()) == 0: - idx = QModelIndex() - else: - idx = self.mw.treeRedacOutline.currentIndex() + # This might be called during a drag n drop operation, or while deleting + # items. If so, we don't want to do anything. + if not self.mw.mdlOutline._removingRows: + if len(self.mw.treeRedacOutline.selectionModel(). + selection().indexes()) == 0: + idx = QModelIndex() + else: + idx = self.mw.treeRedacOutline.currentIndex() - self.setCurrentModelIndex(idx) - self.updateThingsVisible(idx) + self.setCurrentModelIndex(idx) + self.updateThingsVisible(idx) def openIndexes(self, indexes, newTab=False): for i in indexes: @@ -194,8 +197,7 @@ class mainEditor(QWidget, Ui_mainEditor): def goToParentItem(self): idx = self.currentEditor().currentIndex - from manuskript.functions import MW - MW.treeRedacOutline.setCurrentIndex(idx.parent()) + self.mw.treeRedacOutline.setCurrentIndex(idx.parent()) def setCurrentModelIndex(self, index, newTab=False, tabWidget=None): @@ -216,11 +218,12 @@ class mainEditor(QWidget, Ui_mainEditor): if newTab or not tabWidget.count(): editor = editorWidget(self) editor.setCurrentModelIndex(index) + editor._tabWidget = tabWidget tabWidget.addTab(editor, title) tabWidget.setCurrentIndex(tabWidget.count() - 1) else: self.currentEditor(tabWidget).setCurrentModelIndex(index) - tabWidget.setTabText(tabWidget.currentIndex(), title) + #tabWidget.setTabText(tabWidget.currentIndex(), title) def updateTargets(self): """Updates all tabSplitter that are targets. This is called from editorWidget.""" diff --git a/manuskript/ui/editors/tabSplitter.py b/manuskript/ui/editors/tabSplitter.py index 0bdab1dc..c543e774 100644 --- a/manuskript/ui/editors/tabSplitter.py +++ b/manuskript/ui/editors/tabSplitter.py @@ -249,4 +249,4 @@ class tabSplitter(QWidget, Ui_tabSplitter): # }}""".format(self.splitter.objectName())) self.setStyleSheet(style.mainEditorTabSS()) - return QWidget.eventFilter(self, object, event) \ No newline at end of file + return QWidget.eventFilter(self, object, event) diff --git a/manuskript/ui/views/metadataView.py b/manuskript/ui/views/metadataView.py index e4942f9d..535f0608 100644 --- a/manuskript/ui/views/metadataView.py +++ b/manuskript/ui/views/metadataView.py @@ -16,9 +16,9 @@ class metadataView(QWidget, Ui_metadataView): self.txtSummaryFull.setColumn(Outline.summaryFull.value) self.txtNotes.setColumn(Outline.notes.value) self.revisions.setEnabled(False) - + self.txtSummarySentence.setStyleSheet(style.lineEditSS()) - self.txtSummaryFull.setStyleSheet(style.transparentSS() + + self.txtSummaryFull.setStyleSheet(style.transparentSS() + style.simpleScrollBarV()) self.txtNotes.setStyleSheet(style.transparentSS() + style.simpleScrollBarV()) @@ -60,6 +60,9 @@ class metadataView(QWidget, Ui_metadataView): if len(indexes) == 0: self.setEnabled(False) self.revisions.setEnabled(False) + self.txtSummarySentence.setCurrentModelIndex(QModelIndex()) + self.txtSummaryFull.setCurrentModelIndex(QModelIndex()) + self.txtNotes.setCurrentModelIndex(QModelIndex()) # One item selected elif len(indexes) == 1: @@ -89,7 +92,7 @@ class metadataView(QWidget, Ui_metadataView): # Behavior 2: # 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. #self.txtSummarySentence.setCurrentModelIndexes(indexes) #self.txtSummaryFull.setCurrentModelIndexes(indexes) diff --git a/manuskript/ui/views/outlineBasics.py b/manuskript/ui/views/outlineBasics.py index 0d014d68..b460d47e 100644 --- a/manuskript/ui/views/outlineBasics.py +++ b/manuskript/ui/views/outlineBasics.py @@ -42,9 +42,9 @@ class outlineBasics(QAbstractItemView): self.actOpen = QAction(QIcon.fromTheme("go-right"), qApp.translate("outlineBasic", "Open Item"), menu) self.actOpen.triggered.connect(self.openItem) menu.addAction(self.actOpen) - + menu.addSeparator() - + # Add / remove items self.actAddFolder = QAction(QIcon.fromTheme("folder-new"), qApp.translate("outlineBasics", "New Folder"), menu) self.actAddFolder.triggered.connect(self.addFolder) @@ -154,7 +154,7 @@ class outlineBasics(QAbstractItemView): idx = self.currentIndex() from manuskript.functions import MW MW.openIndex(idx) - + def addFolder(self): self.addItem("folder") diff --git a/manuskript/ui/views/textEditView.py b/manuskript/ui/views/textEditView.py index 52dc2e54..2db1df45 100644 --- a/manuskript/ui/views/textEditView.py +++ b/manuskript/ui/views/textEditView.py @@ -208,11 +208,11 @@ class textEditView(QTextEdit): if self.parent().__class__ == QWidget: self.parent().setStyleSheet("background: {bg};".format( bg=opt["background"])) - + cf = QTextCharFormat() # cf.setFont(f) # cf.setForeground(QColor(opt["fontColor"])) - + self.setCursorWidth(opt["cursorWidth"]) bf = QTextBlockFormat() @@ -237,8 +237,7 @@ class textEditView(QTextEdit): if self._updating: return - elif self._index: - + elif self._index and self._index.isValid(): if topLeft.parent() != self._index.parent(): return @@ -266,7 +265,6 @@ class textEditView(QTextEdit): first <= self._index.row() <= last: self._index = None self.setEnabled(False) - # FIXME: self._indexes def disconnectDocument(self):