diff --git a/eddy/core/commands/project.py b/eddy/core/commands/project.py index 4394c2b8..0f9b3699 100644 --- a/eddy/core/commands/project.py +++ b/eddy/core/commands/project.py @@ -38,6 +38,33 @@ from eddy.core.datatypes.graphol import Item +class CommandProjectRename(QtWidgets.QUndoCommand): + """ + Extends QtWidgets.QUndoCommand with facilities to rename the Project. + """ + def __init__(self, undo, redo, project): + """ + Initialize the command. + :type undo: str + :type redo: str + :type project: Project + """ + super().__init__("rename project '{0}' to '{1}'".format(undo, redo)) + self.project = project + self.undo = undo + self.redo = redo + + def redo(self): + """redo the command""" + self.project.name = self.redo + self.project.sgnUpdated.emit() + + def undo(self): + """undo the command""" + self.project.name = self.undo + self.project.sgnUpdated.emit() + + ############################################# # Ontology IRI ################################# diff --git a/eddy/plugins/project-explorer/project_explorer.py b/eddy/plugins/project-explorer/project_explorer.py index 5c805efb..b0cc14a6 100644 --- a/eddy/plugins/project-explorer/project_explorer.py +++ b/eddy/plugins/project-explorer/project_explorer.py @@ -33,14 +33,18 @@ ########################################################################## -from PyQt5 import QtCore -from PyQt5 import QtGui -from PyQt5 import QtWidgets +from PyQt5 import ( + QtCore, + QtGui, + QtWidgets, +) from eddy.core.datatypes.qt import Font +from eddy.core.diagram import Diagram from eddy.core.functions.misc import first, natsorted from eddy.core.functions.signals import connect, disconnect from eddy.core.plugin import AbstractPlugin +from eddy.core.project import Project from eddy.ui.dock import DockWidget @@ -61,6 +65,7 @@ def onSessionReady(self): self.debug('Connecting to project: %s', self.project.name) connect(self.project.sgnDiagramAdded, widget.doAddDiagram) connect(self.project.sgnDiagramRemoved, widget.doRemoveDiagram) + connect(self.project.sgnUpdated, widget.onProjectUpdated) widget.setProject(self.project) ############################################# @@ -76,6 +81,7 @@ def dispose(self): self.debug('Disconnecting from project: %s', self.project.name) disconnect(self.project.sgnDiagramAdded, widget.doAddDiagram) disconnect(self.project.sgnDiagramRemoved, widget.doRemoveDiagram) + disconnect(self.project.sgnUpdated, widget.onProjectUpdated) # DISCONNECT FROM ACTIVE SESSION self.debug('Disconnecting from active session') @@ -246,7 +252,7 @@ def onItemActivated(self, index): # noinspection PyArgumentList if QtWidgets.QApplication.mouseButtons() == QtCore.Qt.NoButton: item = self.model.itemFromIndex(self.proxy.mapToSource(index)) - if item and item.data(): + if item and isinstance(item.data(), Diagram): self.sgnItemActivated.emit(item.data()) # KEEP FOCUS ON THE TREE VIEW UNLESS SHIFT IS PRESSED if QtWidgets.QApplication.queryKeyboardModifiers() & QtCore.Qt.SHIFT: @@ -268,7 +274,7 @@ def onItemDoubleClicked(self, index): # noinspection PyArgumentList if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton: item = self.model.itemFromIndex(self.proxy.mapToSource(index)) - if item and item.data(): + if item and isinstance(item.data(), Diagram): self.sgnItemDoubleClicked.emit(item.data()) @QtCore.pyqtSlot('QModelIndex') @@ -280,9 +286,16 @@ def onItemPressed(self, index): # noinspection PyArgumentList if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton: item = self.model.itemFromIndex(self.proxy.mapToSource(index)) - if item and item.data(): + if item and isinstance(item.data(), Diagram): self.sgnItemClicked.emit(item.data()) + @QtCore.pyqtSlot() + def onProjectUpdated(self): + """ + Executed when the project is updated. + """ + self.root.setText(self.project.name) + ############################################# # EVENTS ################################# @@ -318,9 +331,11 @@ def setProject(self, project): Set the project explorer to browse the given project. :type project: Project """ + self.project = project self.model.clear() self.model.appendRow(self.root) self.root.setText(project.name) + self.root.setData(project) connect(self.sgnFakeDiagramAdded, self.doAddDiagram) for diagram in project.diagrams(): self.sgnFakeDiagramAdded.emit(diagram) @@ -400,8 +415,8 @@ def mouseReleaseEvent(self, mouseEvent): model = self.model().sourceModel() index = self.model().mapToSource(index) item = model.itemFromIndex(index) - diagram = item.data() - if diagram: + itemData = item.data() + if isinstance(itemData, Diagram): menu = QtWidgets.QMenu() menu.addAction(self.session.action('new_diagram')) menu.addSeparator() @@ -409,9 +424,14 @@ def mouseReleaseEvent(self, mouseEvent): menu.addAction(self.session.action('remove_diagram')) menu.addSeparator() menu.addAction(self.session.action('diagram_properties')) - self.session.action('rename_diagram').setData(diagram) - self.session.action('remove_diagram').setData(diagram) - self.session.action('diagram_properties').setData(diagram) + self.session.action('rename_diagram').setData(itemData) + self.session.action('remove_diagram').setData(itemData) + self.session.action('diagram_properties').setData(itemData) + menu.exec_(mouseEvent.screenPos().toPoint()) + elif isinstance(itemData, Project): + menu = QtWidgets.QMenu() + menu.addAction(self.session.action('rename_project')) + self.session.action('rename_project').setData(itemData) menu.exec_(mouseEvent.screenPos().toPoint()) super().mouseReleaseEvent(mouseEvent) diff --git a/eddy/ui/forms.py b/eddy/ui/forms.py index 0bc12073..f3a3923a 100644 --- a/eddy/ui/forms.py +++ b/eddy/ui/forms.py @@ -546,3 +546,42 @@ def onNameFieldChanged(self, name): self.warnLabel.setVisible(not isEmpty(caption)) self.confirmationBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) self.setFixedSize(self.sizeHint()) + + +class RenameProjectForm(AbstractDiagramForm): + """ + This class is used to display a modal window used to rename the project. + """ + + def __init__(self, project, parent=None): + """ + Initialize the new dialog. + :type project: Project + :type parent: QtWidgets.QWidget + """ + super().__init__(project, parent) + self.project = project + self.nameField.setText(self.project.name) + self.setWindowTitle('Rename project: {0}'.format(self.project.name)) + + ############################################# + # SLOTS + ################################# + + @QtCore.pyqtSlot(str) + def onNameFieldChanged(self, name): + """ + Executed when the content of the input field changes. + :type name: str + """ + name = name.strip() + if not name: + caption = '' + enabled = False + else: + caption = '' + enabled = True + self.warnLabel.setText(caption) + self.warnLabel.setVisible(not isEmpty(caption)) + self.confirmationBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) + self.setFixedSize(self.sizeHint()) diff --git a/eddy/ui/session.py b/eddy/ui/session.py index 5142db08..81d81242 100644 --- a/eddy/ui/session.py +++ b/eddy/ui/session.py @@ -84,6 +84,7 @@ CommandNodeSwitchTo, ) from eddy.core.commands.project import ( + CommandProjectRename, CommandProjectSetProfile, ) from eddy.core.common import ( @@ -215,6 +216,7 @@ NewDiagramForm, RefactorNameForm, RenameDiagramForm, + RenameProjectForm, ) from eddy.ui.import_ontology import ImportOntologyDialog from eddy.ui.iri import ( @@ -653,6 +655,11 @@ def initActions(self) -> None: statusTip='Open a diagram and add it to the current project', triggered=self.doOpen)) + self.addAction(QtWidgets.QAction( + QtGui.QIcon(':/icons/24/ic_label_outline_black'), 'Rename...', + self, objectName='rename_project', statusTip='Rename project', + triggered=self.doRenameProject)) + self.addAction(QtWidgets.QAction( QtGui.QIcon(':/icons/24/ic_close_black'), 'Close Project', self, objectName='close_project', shortcut='CTRL+SHIFT+W', @@ -2649,6 +2656,19 @@ def doRenameDiagram(self) -> None: name = form.nameField.value() self.undostack.push(CommandDiagramRename(diagram.name, name, diagram, self.project)) + @QtCore.pyqtSlot() + def doRenameProject(self) -> None: + """ + Renames a project. + """ + action = self.sender() + project = action.data() + if project: + form = RenameProjectForm(project, self) + if form.exec_() == RenameProjectForm.Accepted: + name = form.nameField.value() + self.undostack.push(CommandProjectRename(project.name, name, project)) + @QtCore.pyqtSlot() def doSave(self) -> None: """ @@ -3265,6 +3285,7 @@ def doUpdateState(self) -> None: self.widget('select_reasoner').setEnabled(not isProjectEmpty) # self.action('reset_reasoner').setEnabled(not isProjectEmpty) self.action('ontology_consistency_check').setEnabled(not isProjectEmpty) + self.setWindowTitle(self.project) @QtCore.pyqtSlot() def doRenderByFullIRI(self) -> None: