Mon, 20 Sep 2021 19:47:18 +0200
Continued implementing the VCS status widget for the left side.
# -*- coding: utf-8 -*- # Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de> # """ 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, QAbstractItemView ) from EricWidgets.EricApplication import ericApp from EricWidgets import EricMessageBox import Preferences import UI.PixmapCache class StatusWidget(QWidget): """ Class implementing a VCS Status widget for the sidebar/toolbox. """ StatusDataRole = Qt.ItemDataRole.UserRole + 1 def __init__(self, project, parent=None): """ Constructor @param project reference to the project object @type Project @param parent reference to the parent widget (defaults to None) @type QWidget (optional) """ super().__init__(parent) self.setObjectName("VcsStatusWidget") self.__project = project self.__layout = QVBoxLayout() self.__layout.setObjectName("MainLayout") self.__layout.setContentsMargins(0, 3, 0, 0) self.__topLayout = QHBoxLayout() self.__topLayout.setObjectName("topLayout") # Create the top row self.__infoLabel = QLabel(self) self.__infoLabel.setSizePolicy( 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) self.__layout.addLayout(self.__topLayout) self.__statusList = QListWidget(self) self.__statusList.setAlternatingRowColors(True) 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: self.__projectClosed() 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.vcsStatusMonitorAllData.connect( self.__processStatusData) @pyqtSlot() def __projectOpened(self): """ Private slot to handle the opening of a project. """ self.__reloadButton.setEnabled(True) @pyqtSlot() def __projectClosed(self): """ Private slot to handle the closing of a project. """ self.__infoLabel.setText(self.tr("No project open.")) self.__reloadButton.setEnabled(False) self.__statusList.clear() @pyqtSlot(str) def __setInfoText(self, info): """ Private slot to set the info label text. @param info text to be shown @type str """ self.__infoLabel.setText(info) @pyqtSlot() def __reload(self): """ Private slot to reload the status list. """ self.__project.checkVCSStatus() @pyqtSlot(dict) def __processStatusData(self, data): """ Private slot to process the status data emitted by the project. Each entry of the status data consists of a status flag and and the path relative to the project directory starting with the third column. The known status flags are: <ul> <li>"A" path was added but not yet committed</li> <li>"M" path has local changes</li> <li>"O" path was removed</li> <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 dictionary containing the status data @type dict """ self.__statusList.clear() 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()