diff -r f99d60d6b59b -r 2602857055c5 eric6/Plugins/VcsPlugins/vcsMercurial/QueuesExtension/HgQueuesDefineGuardsDialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/Plugins/VcsPlugins/vcsMercurial/QueuesExtension/HgQueuesDefineGuardsDialog.py Sun Apr 14 15:09:21 2019 +0200 @@ -0,0 +1,314 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2011 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to define guards for patches. +""" + +from __future__ import unicode_literals +try: + str = unicode +except NameError: + pass + +import os + +from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QCoreApplication +from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \ + QListWidgetItem + +from E5Gui import E5MessageBox + +from .Ui_HgQueuesDefineGuardsDialog import Ui_HgQueuesDefineGuardsDialog + +import UI.PixmapCache + + +class HgQueuesDefineGuardsDialog(QDialog, Ui_HgQueuesDefineGuardsDialog): + """ + Class implementing a dialog to define guards for patches. + """ + def __init__(self, vcs, extension, patchesList, parent=None): + """ + Constructor + + @param vcs reference to the vcs object + @param extension reference to the extension module (Queues) + @param patchesList list of patches (list of strings) + @param parent reference to the parent widget (QWidget) + """ + super(HgQueuesDefineGuardsDialog, self).__init__(parent) + self.setupUi(self) + self.setWindowFlags(Qt.Window) + + self.vcs = vcs + self.extension = extension + self.__hgClient = vcs.getClient() + + self.__patches = patchesList[:] + self.patchSelector.addItems([""] + self.__patches) + + self.plusButton.setIcon(UI.PixmapCache.getIcon("plus.png")) + self.minusButton.setIcon(UI.PixmapCache.getIcon("minus.png")) + + self.__dirtyList = False + self.__currentPatch = "" + + self.show() + QCoreApplication.processEvents() + + def closeEvent(self, e): + """ + Protected slot implementing a close event handler. + + @param e close event (QCloseEvent) + """ + if self.__hgClient: + if self.__hgClient.isExecuting(): + self.__hgClient.cancel() + + if self.__dirtyList: + res = E5MessageBox.question( + self, + self.tr("Unsaved Changes"), + self.tr("""The guards list has been changed.""" + """ Shall the changes be applied?"""), + E5MessageBox.StandardButtons( + E5MessageBox.Apply | + E5MessageBox.Discard), + E5MessageBox.Apply) + if res == E5MessageBox.Apply: + self.__applyGuards() + else: + self.__dirtyList = False + + e.accept() + + def start(self, path): + """ + Public slot to start the list command. + + @param path name of directory to be listed (string) + """ + dname, fname = self.vcs.splitPath(path) + + # find the root of the repo + repodir = dname + while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)): + repodir = os.path.dirname(repodir) + if os.path.splitdrive(repodir)[1] == os.sep: + return + + self.__repodir = repodir + self.on_patchSelector_activated("") + + @pyqtSlot(str) + def on_patchSelector_activated(self, patch): + """ + Private slot to get the list of guards defined for the given patch + name. + + @param patch selected patch name (empty for current patch) + """ + if self.__dirtyList: + res = E5MessageBox.question( + self, + self.tr("Unsaved Changes"), + self.tr("""The guards list has been changed.""" + """ Shall the changes be applied?"""), + E5MessageBox.StandardButtons( + E5MessageBox.Apply | + E5MessageBox.Discard), + E5MessageBox.Apply) + if res == E5MessageBox.Apply: + self.__applyGuards() + else: + self.__dirtyList = False + + self.guardsList.clear() + self.patchNameLabel.setText("") + + self.guardCombo.clear() + guardsList = self.extension.getGuardsList(self.__repodir) + self.guardCombo.addItems(guardsList) + self.guardCombo.setEditText("") + + args = self.vcs.initCommand("qguard") + if patch: + args.append(patch) + + output = "" + if self.__hgClient: + output = self.__hgClient.runcommand(args)[0] + else: + process = QProcess() + process.setWorkingDirectory(self.__repodir) + process.start('hg', args) + procStarted = process.waitForStarted(5000) + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = str(process.readAllStandardOutput(), + self.vcs.getEncoding(), 'replace').strip() + + if output: + patchName, guards = output.split(":", 1) + self.patchNameLabel.setText(patchName) + guardsList = guards.strip().split() + for guard in guardsList: + if guard.startswith("+"): + icon = UI.PixmapCache.getIcon("plus.png") + guard = guard[1:] + sign = "+" + elif guard.startswith("-"): + icon = UI.PixmapCache.getIcon("minus.png") + guard = guard[1:] + sign = "-" + else: + continue + itm = QListWidgetItem(icon, guard, self.guardsList) + itm.setData(Qt.UserRole, sign) + + self.on_guardsList_itemSelectionChanged() + + @pyqtSlot() + def on_guardsList_itemSelectionChanged(self): + """ + Private slot to handle changes of the selection of guards. + """ + self.removeButton.setEnabled( + len(self.guardsList.selectedItems()) > 0) + + def __getGuard(self, guard): + """ + Private method to get a reference to a named guard. + + @param guard name of the guard (string) + @return reference to the guard item (QListWidgetItem) + """ + items = self.guardsList.findItems(guard, Qt.MatchCaseSensitive) + if items: + return items[0] + else: + return None + + @pyqtSlot(str) + def on_guardCombo_editTextChanged(self, txt): + """ + Private slot to handle changes of the text of the guard combo. + + @param txt contents of the guard combo line edit (string) + """ + self.addButton.setEnabled(txt != "") + + @pyqtSlot() + def on_addButton_clicked(self): + """ + Private slot to add a guard definition to the list or change it. + """ + guard = self.guardCombo.currentText() + if self.plusButton.isChecked(): + sign = "+" + icon = UI.PixmapCache.getIcon("plus.png") + else: + sign = "-" + icon = UI.PixmapCache.getIcon("minus.png") + + guardItem = self.__getGuard(guard) + if guardItem: + # guard already exists, remove it first + row = self.guardsList.row(guardItem) + itm = self.guardsList.takeItem(row) + del itm + + itm = QListWidgetItem(icon, guard, self.guardsList) + itm.setData(Qt.UserRole, sign) + self.guardsList.sortItems() + + self.__dirtyList = True + + @pyqtSlot() + def on_removeButton_clicked(self): + """ + Private slot to remove guard definitions from the list. + """ + res = E5MessageBox.yesNo( + self, + self.tr("Remove Guards"), + self.tr( + """Do you really want to remove the selected guards?""")) + if res: + for guardItem in self.guardsList.selectedItems(): + row = self.guardsList.row(guardItem) + itm = self.guardsList.takeItem(row) # __IGNORE_WARNING__ + del itm + + self.__dirtyList = True + + @pyqtSlot(QAbstractButton) + def on_buttonBox_clicked(self, button): + """ + Private slot called by a button of the button box clicked. + + @param button button that was clicked (QAbstractButton) + """ + if button == self.buttonBox.button(QDialogButtonBox.Apply): + self.__applyGuards() + elif button == self.buttonBox.button(QDialogButtonBox.Close): + self.close() + + @pyqtSlot() + def __applyGuards(self): + """ + Private slot to apply the defined guards to the current patch. + """ + if self.__dirtyList: + guardsList = [] + for row in range(self.guardsList.count()): + itm = self.guardsList.item(row) + guard = itm.data(Qt.UserRole) + itm.text() + guardsList.append(guard) + + args = self.vcs.initCommand("qguard") + args.append(self.patchNameLabel.text()) + if guardsList: + args.append("--") + args.extend(guardsList) + else: + args.append("--none") + + error = "" + if self.__hgClient: + error = self.__hgClient.runcommand(args)[1] + else: + process = QProcess() + process.setWorkingDirectory(self.__repodir) + process.start('hg', args) + procStarted = process.waitForStarted(5000) + if procStarted: + finished = process.waitForFinished(30000) + if finished: + if process.exitCode() != 0: + error = str(process.readAllStandardError(), + self.vcs.getEncoding(), 'replace') + else: + E5MessageBox.warning( + self, + self.tr("Apply Guard Definitions"), + self.tr( + """The Mercurial process did not finish""" + """ in time.""")) + + if error: + E5MessageBox.warning( + self, + self.tr("Apply Guard Definitions"), + self.tr("""<p>The defined guards could not be""" + """ applied.</p><p>Reason: {0}</p>""") + .format(error)) + else: + self.__dirtyList = False + self.on_patchSelector_activated( + self.patchNameLabel.text())