Plugins/VcsPlugins/vcsMercurial/GpgExtension/HgGpgSignaturesDialog.py

changeset 1075
75bfe8bd4243
child 1131
7781e396c903
equal deleted inserted replaced
1074:ed2585464f12 1075:75bfe8bd4243
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog showing signed changesets.
8 """
9
10 import os
11
12 from PyQt4.QtCore import pyqtSlot, QProcess, QTimer, Qt, QRegExp
13 from PyQt4.QtGui import QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem, \
14 QLineEdit
15
16 from E5Gui import E5MessageBox
17
18 from .Ui_HgGpgSignaturesDialog import Ui_HgGpgSignaturesDialog
19
20 import Preferences
21 import UI.PixmapCache
22
23
24 class HgGpgSignaturesDialog(QDialog, Ui_HgGpgSignaturesDialog):
25 """
26 Class implementing a dialog showing signed changesets.
27 """
28 def __init__(self, vcs, parent=None):
29 """
30 Constructor
31
32 @param vcs reference to the vcs object
33 @param parent reference to the parent widget (QWidget)
34 """
35 QDialog.__init__(self, parent)
36 self.setupUi(self)
37
38 self.clearRxEditButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
39
40 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
41 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
42
43 self.process = QProcess()
44 self.vcs = vcs
45
46 self.process.finished.connect(self.__procFinished)
47 self.process.readyReadStandardOutput.connect(self.__readStdout)
48 self.process.readyReadStandardError.connect(self.__readStderr)
49
50 def closeEvent(self, e):
51 """
52 Private slot implementing a close event handler.
53
54 @param e close event (QCloseEvent)
55 """
56 if self.process is not None and \
57 self.process.state() != QProcess.NotRunning:
58 self.process.terminate()
59 QTimer.singleShot(2000, self.process.kill)
60 self.process.waitForFinished(3000)
61
62 e.accept()
63
64 def start(self, path):
65 """
66 Public slot to start the list command.
67
68 @param path name of directory (string)
69 """
70 self.errorGroup.hide()
71
72 self.intercept = False
73 self.activateWindow()
74
75 self.__path = path
76 dname, fname = self.vcs.splitPath(path)
77
78 # find the root of the repo
79 repodir = dname
80 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
81 repodir = os.path.dirname(repodir)
82 if repodir == os.sep:
83 return
84
85 args = []
86 args.append('sigs')
87
88 self.process.kill()
89 self.process.setWorkingDirectory(repodir)
90
91 self.process.start('hg', args)
92 procStarted = self.process.waitForStarted()
93 if not procStarted:
94 self.inputGroup.setEnabled(False)
95 self.inputGroup.hide()
96 E5MessageBox.critical(self,
97 self.trUtf8('Process Generation Error'),
98 self.trUtf8(
99 'The process {0} could not be started. '
100 'Ensure, that it is in the search path.'
101 ).format('hg'))
102 else:
103 self.inputGroup.setEnabled(True)
104 self.inputGroup.show()
105
106 def __finish(self):
107 """
108 Private slot called when the process finished or the user pressed the button.
109 """
110 if self.process is not None and \
111 self.process.state() != QProcess.NotRunning:
112 self.process.terminate()
113 QTimer.singleShot(2000, self.process.kill)
114 self.process.waitForFinished(3000)
115
116 self.inputGroup.setEnabled(False)
117 self.inputGroup.hide()
118
119 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
120 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
121 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
122 self.buttonBox.button(QDialogButtonBox.Close).setFocus(Qt.OtherFocusReason)
123
124 self.process = None
125
126 if self.signaturesList.topLevelItemCount() == 0:
127 # no patches present
128 self.__generateItem("", "", self.trUtf8("no signatures found"))
129 self.signaturesList.doItemsLayout()
130 self.__resizeColumns()
131 self.__resort()
132
133 def on_buttonBox_clicked(self, button):
134 """
135 Private slot called by a button of the button box clicked.
136
137 @param button button that was clicked (QAbstractButton)
138 """
139 if button == self.buttonBox.button(QDialogButtonBox.Close):
140 self.close()
141 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
142 self.__finish()
143
144 def __procFinished(self, exitCode, exitStatus):
145 """
146 Private slot connected to the finished signal.
147
148 @param exitCode exit code of the process (integer)
149 @param exitStatus exit status of the process (QProcess.ExitStatus)
150 """
151 self.__finish()
152
153 def __resort(self):
154 """
155 Private method to resort the tree.
156 """
157 self.signaturesList.sortItems(self.signaturesList.sortColumn(),
158 self.signaturesList.header().sortIndicatorOrder())
159
160 def __resizeColumns(self):
161 """
162 Private method to resize the list columns.
163 """
164 self.signaturesList.header().resizeSections(QHeaderView.ResizeToContents)
165 self.signaturesList.header().setStretchLastSection(True)
166
167 def __generateItem(self, revision, changeset, signature):
168 """
169 Private method to generate a patch item in the list of patches.
170
171 @param revision revision number (string)
172 @param changeset changeset of the bookmark (string)
173 @param signature signature of the changeset (string)
174 """
175 if revision == "" and changeset == "":
176 QTreeWidgetItem(self.signaturesList, [signature])
177 else:
178 revString = "{0:>7}:{1}".format(revision, changeset)
179 topItems = self.signaturesList.findItems(revString, Qt.MatchExactly)
180 if len(topItems) == 0:
181 # first signature for this changeset
182 topItm = QTreeWidgetItem(self.signaturesList, [
183 "{0:>7}:{1}".format(revision, changeset)])
184 topItm.setExpanded(True)
185 font = topItm.font(0)
186 font.setBold(True)
187 topItm.setFont(0, font)
188 else:
189 topItm = topItems[0]
190 QTreeWidgetItem(topItm, [signature])
191
192 def __readStdout(self):
193 """
194 Private slot to handle the readyReadStdout signal.
195
196 It reads the output of the process, formats it and inserts it into
197 the contents pane.
198 """
199 self.process.setReadChannel(QProcess.StandardOutput)
200
201 while self.process.canReadLine():
202 s = str(self.process.readLine(),
203 Preferences.getSystem("IOEncoding"),
204 'replace').strip()
205 l = s.split()
206 if l[-1][0] in "1234567890":
207 # last element is a rev:changeset
208 rev, changeset = l[-1].split(":", 1)
209 del l[-1]
210 signature = " ".join(l)
211 self.__generateItem(rev, changeset, signature)
212
213 def __readStderr(self):
214 """
215 Private slot to handle the readyReadStderr signal.
216
217 It reads the error output of the process and inserts it into the
218 error pane.
219 """
220 if self.process is not None:
221 self.errorGroup.show()
222 s = str(self.process.readAllStandardError(),
223 Preferences.getSystem("IOEncoding"),
224 'replace')
225 self.errors.insertPlainText(s)
226 self.errors.ensureCursorVisible()
227
228 @pyqtSlot()
229 def on_signaturesList_itemSelectionChanged(self):
230 """
231 Private slot handling changes of the selection.
232 """
233 selectedItems = self.signaturesList.selectedItems()
234 if len(selectedItems) == 1 and \
235 self.signaturesList.indexOfTopLevelItem(selectedItems[0]) != -1:
236 self.verifyButton.setEnabled(True)
237 else:
238 self.verifyButton.setEnabled(False)
239
240 @pyqtSlot()
241 def on_verifyButton_clicked(self):
242 """
243 Private slot to verify the signatures of the selected revision.
244 """
245 rev = self.signaturesList.selectedItems()[0].text(0).split(":")[0].strip()
246 self.vcs.getExtensionObject("gpg")\
247 .hgGpgVerifySignatures(self.__path, rev)
248
249 @pyqtSlot(str)
250 def on_categoryCombo_activated(self, txt):
251 """
252 Private slot called, when a new filter category is selected.
253
254 @param txt text of the selected category (string)
255 """
256 self.__filterSignatures()
257
258 @pyqtSlot(str)
259 def on_rxEdit_textChanged(self, txt):
260 """
261 Private slot called, when a filter expression is entered.
262
263 @param txt filter expression (string)
264 """
265 self.__filterSignatures()
266
267 def __filterSignatures(self):
268 """
269 Private method to filter the log entries.
270 """
271 searchRxText = self.rxEdit.text()
272 filterTop = self.categoryCombo.currentText() == self.trUtf8("Revision")
273 if filterTop and searchRxText.startswith("^"):
274 searchRx = QRegExp("^\s*{0}".format(searchRxText[1:]), Qt.CaseInsensitive)
275 else:
276 searchRx = QRegExp(searchRxText, Qt.CaseInsensitive)
277 for topIndex in range(self.signaturesList.topLevelItemCount()):
278 topLevelItem = self.signaturesList.topLevelItem(topIndex)
279 if filterTop:
280 topLevelItem.setHidden(searchRx.indexIn(topLevelItem.text(0)) == -1)
281 else:
282 visibleChildren = topLevelItem.childCount()
283 for childIndex in range(topLevelItem.childCount()):
284 childItem = topLevelItem.child(childIndex)
285 if searchRx.indexIn(childItem.text(0)) == -1:
286 childItem.setHidden(True)
287 visibleChildren -= 1
288 else:
289 childItem.setHidden(False)
290 topLevelItem.setHidden(visibleChildren == 0)
291
292 def on_passwordCheckBox_toggled(self, isOn):
293 """
294 Private slot to handle the password checkbox toggled.
295
296 @param isOn flag indicating the status of the check box (boolean)
297 """
298 if isOn:
299 self.input.setEchoMode(QLineEdit.Password)
300 else:
301 self.input.setEchoMode(QLineEdit.Normal)
302
303 @pyqtSlot()
304 def on_sendButton_clicked(self):
305 """
306 Private slot to send the input to the subversion process.
307 """
308 input = self.input.text()
309 input += os.linesep
310
311 if self.passwordCheckBox.isChecked():
312 self.errors.insertPlainText(os.linesep)
313 self.errors.ensureCursorVisible()
314 else:
315 self.errors.insertPlainText(input)
316 self.errors.ensureCursorVisible()
317
318 self.process.write(input)
319
320 self.passwordCheckBox.setChecked(False)
321 self.input.clear()
322
323 def on_input_returnPressed(self):
324 """
325 Private slot to handle the press of the return key in the input field.
326 """
327 self.intercept = True
328 self.on_sendButton_clicked()
329
330 def keyPressEvent(self, evt):
331 """
332 Protected slot to handle a key press event.
333
334 @param evt the key press event (QKeyEvent)
335 """
336 if self.intercept:
337 self.intercept = False
338 evt.accept()
339 return
340 QDialog.keyPressEvent(self, evt)

eric ide

mercurial