Plugins/VcsPlugins/vcsMercurial/HgAnnotateDialog.py

changeset 178
dd9f0bca5e2f
child 221
38689444e922
equal deleted inserted replaced
177:c822ccc4d138 178:dd9f0bca5e2f
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2010 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the output of the hg annotate command.
8 """
9
10 import os
11
12 from PyQt4.QtCore import pyqtSlot, SIGNAL, QProcess, QTimer, Qt
13 from PyQt4.QtGui import QDialog, QDialogButtonBox, QFont, QMessageBox, QHeaderView, \
14 QTreeWidgetItem, QLineEdit
15
16 from .Ui_HgAnnotateDialog import Ui_HgAnnotateDialog
17
18 import Preferences
19 import Utilities
20
21 class HgAnnotateDialog(QDialog, Ui_HgAnnotateDialog):
22 """
23 Class implementing a dialog to show the output of the hg annotate command.
24 """
25 def __init__(self, vcs, parent = None):
26 """
27 Constructor
28
29 @param vcs reference to the vcs object
30 @param parent parent widget (QWidget)
31 """
32 QDialog.__init__(self, parent)
33 self.setupUi(self)
34
35 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
36 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
37
38 self.process = QProcess()
39 self.vcs = vcs
40
41 self.annotateList.headerItem().setText(self.annotateList.columnCount(), "")
42 font = QFont(self.annotateList.font())
43 if Utilities.isWindowsPlatform():
44 font.setFamily("Lucida Console")
45 else:
46 font.setFamily("Monospace")
47 self.annotateList.setFont(font)
48
49 self.__ioEncoding = Preferences.getSystem("IOEncoding")
50
51 self.connect(self.process, SIGNAL('finished(int, QProcess::ExitStatus)'),
52 self.__procFinished)
53 self.connect(self.process, SIGNAL('readyReadStandardOutput()'),
54 self.__readStdout)
55 self.connect(self.process, SIGNAL('readyReadStandardError()'),
56 self.__readStderr)
57
58 def closeEvent(self, e):
59 """
60 Private slot implementing a close event handler.
61
62 @param e close event (QCloseEvent)
63 """
64 if self.process is not None and \
65 self.process.state() != QProcess.NotRunning:
66 self.process.terminate()
67 QTimer.singleShot(2000, self.process.kill)
68 self.process.waitForFinished(3000)
69
70 e.accept()
71
72 def start(self, fn):
73 """
74 Public slot to start the annotate command.
75
76 @param fn filename to show the log for (string)
77 """
78 self.errorGroup.hide()
79 self.intercept = False
80 self.activateWindow()
81 self.lineno = 1
82
83 dname, fname = self.vcs.splitPath(fn)
84
85 # find the root of the repo
86 repodir = dname
87 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
88 repodir = os.path.dirname(repodir)
89 if repodir == os.sep:
90 return
91
92 args = []
93 args.append('annotate')
94 args.append('--follow')
95 args.append('--user')
96 args.append('--date')
97 args.append('--number')
98 args.append('--changeset')
99 args.append('--quiet')
100 args.append(fn)
101
102 self.process.kill()
103 self.process.setWorkingDirectory(repodir)
104
105 self.process.start('hg', args)
106 procStarted = self.process.waitForStarted()
107 if not procStarted:
108 self.inputGroup.setEnabled(False)
109 self.inputGroup.hide()
110 QMessageBox.critical(None,
111 self.trUtf8('Process Generation Error'),
112 self.trUtf8(
113 'The process {0} could not be started. '
114 'Ensure, that it is in the search path.'
115 ).format('hg'))
116 else:
117 self.inputGroup.setEnabled(True)
118 self.inputGroup.show()
119
120 def __finish(self):
121 """
122 Private slot called when the process finished or the user pressed the button.
123 """
124 if self.process is not None and \
125 self.process.state() != QProcess.NotRunning:
126 self.process.terminate()
127 QTimer.singleShot(2000, self.process.kill)
128 self.process.waitForFinished(3000)
129
130 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
131 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
132 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
133
134 self.inputGroup.setEnabled(False)
135 self.inputGroup.hide()
136
137 self.process = None
138
139 self.annotateList.doItemsLayout()
140 self.__resizeColumns()
141
142 def on_buttonBox_clicked(self, button):
143 """
144 Private slot called by a button of the button box clicked.
145
146 @param button button that was clicked (QAbstractButton)
147 """
148 if button == self.buttonBox.button(QDialogButtonBox.Close):
149 self.close()
150 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
151 self.__finish()
152
153 def __procFinished(self, exitCode, exitStatus):
154 """
155 Private slot connected to the finished signal.
156
157 @param exitCode exit code of the process (integer)
158 @param exitStatus exit status of the process (QProcess.ExitStatus)
159 """
160 self.__finish()
161
162 def __resizeColumns(self):
163 """
164 Private method to resize the list columns.
165 """
166 self.annotateList.header().resizeSections(QHeaderView.ResizeToContents)
167
168 def __generateItem(self, revision, changeset, author, date, text):
169 """
170 Private method to generate a tag item in the taglist.
171
172 @param revision revision string (string)
173 @param changeset changeset string (string)
174 @param author author of the change (string)
175 @param date date of the tag (string)
176 @param name name (path) of the tag (string)
177 """
178 itm = QTreeWidgetItem(self.annotateList,
179 [revision, changeset, author, date, "%d" % self.lineno, text])
180 self.lineno += 1
181 itm.setTextAlignment(0, Qt.AlignRight)
182 itm.setTextAlignment(4, Qt.AlignRight)
183
184 def __readStdout(self):
185 """
186 Private slot to handle the readyReadStdout signal.
187
188 It reads the output of the process, formats it and inserts it into
189 the annotation list.
190 """
191 self.process.setReadChannel(QProcess.StandardOutput)
192
193 while self.process.canReadLine():
194 s = str(self.process.readLine(), self.__ioEncoding, 'replace').strip()
195 try:
196 info, text = s.split(": ", 1)
197 except ValueError:
198 info = s[:-2]
199 text = ""
200 author, rev, changeset, date, file = info.split()
201 self.__generateItem(rev, changeset, author, date, text)
202
203 def __readStderr(self):
204 """
205 Private slot to handle the readyReadStderr signal.
206
207 It reads the error output of the process and inserts it into the
208 error pane.
209 """
210 if self.process is not None:
211 self.errorGroup.show()
212 s = str(self.process.readAllStandardError(),
213 Preferences.getSystem("IOEncoding"),
214 'replace')
215 self.errors.insertPlainText(s)
216 self.errors.ensureCursorVisible()
217
218 def on_passwordCheckBox_toggled(self, isOn):
219 """
220 Private slot to handle the password checkbox toggled.
221
222 @param isOn flag indicating the status of the check box (boolean)
223 """
224 if isOn:
225 self.input.setEchoMode(QLineEdit.Password)
226 else:
227 self.input.setEchoMode(QLineEdit.Normal)
228
229 @pyqtSlot()
230 def on_sendButton_clicked(self):
231 """
232 Private slot to send the input to the subversion process.
233 """
234 input = self.input.text()
235 input += os.linesep
236
237 if self.passwordCheckBox.isChecked():
238 self.errors.insertPlainText(os.linesep)
239 self.errors.ensureCursorVisible()
240 else:
241 self.errors.insertPlainText(input)
242 self.errors.ensureCursorVisible()
243
244 self.process.write(input)
245
246 self.passwordCheckBox.setChecked(False)
247 self.input.clear()
248
249 def on_input_returnPressed(self):
250 """
251 Private slot to handle the press of the return key in the input field.
252 """
253 self.intercept = True
254 self.on_sendButton_clicked()
255
256 def keyPressEvent(self, evt):
257 """
258 Protected slot to handle a key press event.
259
260 @param evt the key press event (QKeyEvent)
261 """
262 if self.intercept:
263 self.intercept = False
264 evt.accept()
265 return
266 QDialog.keyPressEvent(self, evt)

eric ide

mercurial