Plugins/VcsPlugins/vcsMercurial/QueuesExtension/HgQueuesListDialog.py

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

eric ide

mercurial