diff -r 2dc55ddafc68 -r 84f7f7867b5f eric7/VCS/StatusWidget.py --- a/eric7/VCS/StatusWidget.py Mon Sep 20 07:29:27 2021 +0200 +++ b/eric7/VCS/StatusWidget.py Mon Sep 20 19:47:18 2021 +0200 @@ -7,12 +7,19 @@ Module implementing a VCS Status widget for the sidebar/toolbar. """ +import contextlib +import os + from PyQt6.QtCore import pyqtSlot, Qt from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QListView, - QListWidget, QListWidgetItem, QToolButton + QListWidget, QListWidgetItem, QToolButton, QAbstractItemView ) +from EricWidgets.EricApplication import ericApp +from EricWidgets import EricMessageBox + +import Preferences import UI.PixmapCache @@ -20,6 +27,8 @@ """ Class implementing a VCS Status widget for the sidebar/toolbox. """ + StatusDataRole = Qt.ItemDataRole.UserRole + 1 + def __init__(self, project, parent=None): """ Constructor @@ -46,8 +55,31 @@ QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) self.__topLayout.addWidget(self.__infoLabel) + self.__commitToggleButton = QToolButton(self) + self.__commitToggleButton.setIcon(UI.PixmapCache.getIcon("check")) + self.__commitToggleButton.setToolTip( + self.tr("Press to toggle the commit markers")) + self.__commitToggleButton.clicked.connect(self.__toggleCheckMark) + self.__topLayout.addWidget(self.__commitToggleButton) + + self.__commitButton = QToolButton(self) + self.__commitButton.setIcon(UI.PixmapCache.getIcon("vcsCommit")) + self.__commitButton.setToolTip( + self.tr("Press to commit the marked entries")) + self.__commitButton.clicked.connect(self.__commit) + self.__topLayout.addWidget(self.__commitButton) + + self.__addButton = QToolButton(self) + self.__addButton.setIcon(UI.PixmapCache.getIcon("vcsAdd")) + self.__addButton.setToolTip( + self.tr("Press to add the selected, untracked entries")) + self.__addButton.clicked.connect(self.__addUntracked) + self.__topLayout.addWidget(self.__addButton) + self.__reloadButton = QToolButton(self) self.__reloadButton.setIcon(UI.PixmapCache.getIcon("reload")) + self.__reloadButton.setToolTip( + self.tr("Press to reload the status list")) self.__reloadButton.clicked.connect(self.__reload) self.__topLayout.addWidget(self.__reloadButton) @@ -58,10 +90,33 @@ self.__statusList.setSortingEnabled(True) self.__statusList.setViewMode(QListView.ViewMode.ListMode) self.__statusList.setTextElideMode(Qt.TextElideMode.ElideLeft) + self.__statusList.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection) self.__layout.addWidget(self.__statusList) self.setLayout(self.__layout) + self.__statusIcons = { + "A": "vcs-added", # added + "M": "vcs-modified", # modified + "O": "vcs-removed", # removed + "R": "vcs-renamed", # renamed + "U": "vcs-update-required", # update needed + "Z": "vcs-conflicting", # conflict + "?": "vcs-untracked", # not tracked + "!": "vcs-missing", # missing + } + self.__statusTexts = { + "A": self.tr("added"), + "M": self.tr("modified"), + "O": self.tr("removed"), + "R": self.tr("renamed"), + "U": self.tr("needs update"), + "Z": self.tr("conflict"), + "?": self.tr("not tracked"), + "!": self.tr("missing"), + } + if self.__project.isOpen(): self.__projectOpened() else: @@ -69,8 +124,10 @@ self.__project.projectOpened.connect(self.__projectOpened) self.__project.projectClosed.connect(self.__projectClosed) + self.__project.vcsCommitted.connect(self.__committed) self.__project.vcsStatusMonitorInfo.connect(self.__setInfoText) - self.__project.vcsStatusMonitorData.connect(self.__processStatusData) + self.__project.vcsStatusMonitorAllData.connect( + self.__processStatusData) @pyqtSlot() def __projectOpened(self): @@ -107,7 +164,7 @@ """ self.__project.checkVCSStatus() - @pyqtSlot(list) + @pyqtSlot(dict) def __processStatusData(self, data): """ Private slot to process the status data emitted by the project. @@ -122,14 +179,106 @@ <li>"R" path was deleted and then re-added</li> <li>"U" path needs an update</li> <li>"Z" path contains a conflict</li> + <li>"?" path is not tracked</li> + <li>"!" path is missing</li> <li>" " path is back at normal</li> </ul> - @param data list of VCS status data - @type list of str + @param data dictionary containing the status data + @type dict """ self.__statusList.clear() - for entry in data: - QListWidgetItem(entry, self.__statusList) + for name, status in data.items(): + if status: + itm = QListWidgetItem(name, self.__statusList) + with contextlib.suppress(KeyError): + itm.setToolTip(self.__statusTexts[status]) + itm.setIcon(UI.PixmapCache.getIcon( + self.__statusIcons[status])) + itm.setData(self.StatusDataRole, status) + if status in "AMOR": + itm.setFlags( + itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) + itm.setCheckState(Qt.CheckState.Checked) + else: + itm.setFlags( + itm.flags() & ~Qt.ItemFlag.ItemIsUserCheckable) + self.__statusList.sortItems(Qt.SortOrder.AscendingOrder) + + @pyqtSlot() + def __toggleCheckMark(self): + """ + Private slot to toggle the check marks. + """ + for row in range(self.__statusList.count()): + itm = self.__statusList.item(row) + if ( + itm.flags() & Qt.ItemFlag.ItemIsUserCheckable == + Qt.ItemFlag.ItemIsUserCheckable + ): + if itm.checkState() == Qt.CheckState.Unchecked: + itm.setCheckState(Qt.CheckState.Checked) + else: + itm.setCheckState(Qt.CheckState.Unchecked) + + @pyqtSlot() + def __commit(self): + """ + Private slot to handle the commit button. + """ + projectPath = self.__project.getProjectPath() + names = [] + + for row in range(self.__statusList.count()): + itm = self.__statusList.item(row) + if itm.checkState() == Qt.CheckState.Checked: + names.append(os.path.join(projectPath, itm.text())) + + if not names: + EricMessageBox.information( + self, + self.tr("Commit"), + self.tr("""There are no entries selected to be""" + """ committed.""")) + return + + if Preferences.getVCS("AutoSaveFiles"): + vm = ericApp().getObject("ViewManager") + for name in names: + vm.saveEditor(name) + vcs = self.__project.getVcs() + vcs and vcs.vcsCommit(names, '') + + @pyqtSlot() + def __committed(self): + """ + Private slot called after the commit has been completed. + """ + self.__reload() + + @pyqtSlot() + def __addUntracked(self): + """ + Private slot to add the selected untracked entries. + """ + projectPath = self.__project.getProjectPath() + + names = [ + os.path.join(projectPath, itm.text()) + for itm in self.__statusList.selectedItems() + if itm.data(self.StatusDataRole) == "?" + ] + + if not names: + EricMessageBox.information( + self, + self.tr("Add"), + self.tr("""There are no unversioned entries""" + """ available/selected.""")) + return + + vcs = self.__project.getVcs() + vcs and vcs.vcsAdd(names) + self.__reload()