Plugins/VcsPlugins/vcsSubversion/SvnDiffDialog.py

changeset 0
de9c2efb9d02
child 12
1d8dd9706f46
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the output of the svn diff command process.
8 """
9
10 import os
11 import sys
12 import types
13
14 from PyQt4.QtCore import *
15 from PyQt4.QtGui import *
16
17 from E4Gui.E4Application import e4App
18
19 from Ui_SvnDiffDialog import Ui_SvnDiffDialog
20
21 import Utilities
22
23 class SvnDiffDialog(QWidget, Ui_SvnDiffDialog):
24 """
25 Class implementing a dialog to show the output of the svn diff command process.
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 QWidget.__init__(self, parent)
35 self.setupUi(self)
36
37 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False)
38 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
39
40 self.process = QProcess()
41 self.vcs = vcs
42
43 if Utilities.isWindowsPlatform():
44 self.contents.setFontFamily("Lucida Console")
45 else:
46 self.contents.setFontFamily("Monospace")
47
48 self.cNormalFormat = self.contents.currentCharFormat()
49 self.cAddedFormat = self.contents.currentCharFormat()
50 self.cAddedFormat.setBackground(QBrush(QColor(190, 237, 190)))
51 self.cRemovedFormat = self.contents.currentCharFormat()
52 self.cRemovedFormat.setBackground(QBrush(QColor(237, 190, 190)))
53 self.cLineNoFormat = self.contents.currentCharFormat()
54 self.cLineNoFormat.setBackground(QBrush(QColor(255, 220, 168)))
55
56 self.connect(self.process, SIGNAL('finished(int, QProcess::ExitStatus)'),
57 self.__procFinished)
58 self.connect(self.process, SIGNAL('readyReadStandardOutput()'),
59 self.__readStdout)
60 self.connect(self.process, SIGNAL('readyReadStandardError()'),
61 self.__readStderr)
62
63 def closeEvent(self, e):
64 """
65 Private slot implementing a close event handler.
66
67 @param e close event (QCloseEvent)
68 """
69 if self.process is not None and \
70 self.process.state() != QProcess.NotRunning:
71 self.process.terminate()
72 QTimer.singleShot(2000, self.process, SLOT('kill()'))
73 self.process.waitForFinished(3000)
74
75 e.accept()
76
77 def __getVersionArg(self, version):
78 """
79 Private method to get a svn revision argument for the given revision.
80
81 @param version revision (integer or string)
82 @return version argument (string)
83 """
84 if version == "WORKING":
85 return None
86 else:
87 return str(version)
88
89 def start(self, fn, versions = None, urls = None, summary = False):
90 """
91 Public slot to start the svn diff command.
92
93 @param fn filename to be diffed (string)
94 @param versions list of versions to be diffed (list of up to 2 strings or None)
95 @keyparam urls list of repository URLs (list of 2 strings)
96 @keyparam summary flag indicating a summarizing diff
97 (only valid for URL diffs) (boolean)
98 """
99 self.errorGroup.hide()
100 self.intercept = False
101 self.filename = fn
102
103 self.process.kill()
104
105 self.contents.clear()
106 self.paras = 0
107
108 args = []
109 args.append('diff')
110 self.vcs.addArguments(args, self.vcs.options['global'])
111 self.vcs.addArguments(args, self.vcs.options['diff'])
112 if '--diff-cmd' in self.vcs.options['diff']:
113 self.buttonBox.button(QDialogButtonBox.Save).hide()
114
115 if versions is not None:
116 self.raise_()
117 self.activateWindow()
118
119 rev1 = self.__getVersionArg(versions[0])
120 rev2 = None
121 if len(versions) == 2:
122 rev2 = self.__getVersionArg(versions[1])
123
124 if rev1 is not None or rev2 is not None:
125 args.append('-r')
126 if rev1 is not None and rev2 is not None:
127 args.append('%s:%s' % (rev1, rev2))
128 elif rev2 is None:
129 args.append(rev1)
130 elif rev1 is None:
131 args.append(rev2)
132
133 self.summaryPath = None
134 if urls is not None:
135 if summary:
136 args.append("--summarize")
137 self.summaryPath = urls[0]
138 args.append("--old=%s" % urls[0])
139 args.append("--new=%s" % urls[1])
140 if type(fn) is types.ListType:
141 dname, fnames = self.vcs.splitPathList(fn)
142 else:
143 dname, fname = self.vcs.splitPath(fn)
144 fnames = [fname]
145 ppath = e4App().getObject('Project').getProjectPath()
146 if dname == ppath:
147 path = ""
148 else:
149 path = dname.replace(ppath + os.sep, '')
150 if path:
151 path += "/"
152 for fname in fnames:
153 args.append(path + fname)
154 else:
155 if type(fn) is types.ListType:
156 dname, fnames = self.vcs.splitPathList(fn)
157 self.vcs.addArguments(args, fnames)
158 else:
159 dname, fname = self.vcs.splitPath(fn)
160 args.append(fname)
161
162 self.process.setWorkingDirectory(dname)
163
164 self.process.start('svn', args)
165 procStarted = self.process.waitForStarted()
166 if not procStarted:
167 self.inputGroup.setEnabled(False)
168 QMessageBox.critical(None,
169 self.trUtf8('Process Generation Error'),
170 self.trUtf8(
171 'The process {0} could not be started. '
172 'Ensure, that it is in the search path.'
173 ).format('svn'))
174
175 def __procFinished(self, exitCode, exitStatus):
176 """
177 Private slot connected to the finished signal.
178
179 @param exitCode exit code of the process (integer)
180 @param exitStatus exit status of the process (QProcess.ExitStatus)
181 """
182 self.inputGroup.setEnabled(False)
183
184 if self.paras == 0:
185 self.contents.insertPlainText(\
186 self.trUtf8('There is no difference.'))
187 return
188
189 self.buttonBox.button(QDialogButtonBox.Save).setEnabled(True)
190 tc = self.contents.textCursor()
191 tc.movePosition(QTextCursor.Start)
192 self.contents.setTextCursor(tc)
193 self.contents.ensureCursorVisible()
194
195 def __appendText(self, txt, format):
196 """
197 Private method to append text to the end of the contents pane.
198
199 @param txt text to insert (string)
200 @param format text format to be used (QTextCharFormat)
201 """
202 tc = self.contents.textCursor()
203 tc.movePosition(QTextCursor.End)
204 self.contents.setTextCursor(tc)
205 self.contents.setCurrentCharFormat(format)
206 self.contents.insertPlainText(txt)
207
208 def __readStdout(self):
209 """
210 Private slot to handle the readyReadStandardOutput signal.
211
212 It reads the output of the process, formats it and inserts it into
213 the contents pane.
214 """
215 self.process.setReadChannel(QProcess.StandardOutput)
216
217 while self.process.canReadLine():
218 line = unicode(self.process.readLine())
219 if self.summaryPath:
220 line = line.replace(self.summaryPath + '/', '')
221 line = " ".join(line.split())
222 if line.startswith('+') or line.startswith('>') or line.startswith('A '):
223 format = self.cAddedFormat
224 elif line.startswith('-') or line.startswith('<') or line.startswith('D '):
225 format = self.cRemovedFormat
226 elif line.startswith('@@'):
227 format = self.cLineNoFormat
228 else:
229 format = self.cNormalFormat
230 self.__appendText(line, format)
231 self.paras += 1
232
233 def __readStderr(self):
234 """
235 Private slot to handle the readyReadStandardError signal.
236
237 It reads the error output of the process and inserts it into the
238 error pane.
239 """
240 if self.process is not None:
241 self.errorGroup.show()
242 s = unicode(self.process.readAllStandardError())
243 self.errors.insertPlainText(s)
244 self.errors.ensureCursorVisible()
245
246 def on_buttonBox_clicked(self, button):
247 """
248 Private slot called by a button of the button box clicked.
249
250 @param button button that was clicked (QAbstractButton)
251 """
252 if button == self.buttonBox.button(QDialogButtonBox.Save):
253 self.on_saveButton_clicked()
254
255 @pyqtSlot()
256 def on_saveButton_clicked(self):
257 """
258 Private slot to handle the Save button press.
259
260 It saves the diff shown in the dialog to a file in the local
261 filesystem.
262 """
263 if type(self.filename) is types.ListType:
264 if len(self.filename) > 1:
265 fname = self.vcs.splitPathList(self.filename)[0]
266 else:
267 dname, fname = self.vcs.splitPath(self.filename[0])
268 if fname != '.':
269 fname = "%s.diff" % self.filename[0]
270 else:
271 fname = dname
272 else:
273 fname = self.vcs.splitPath(self.filename)[0]
274
275 fname, selectedFilter = QFileDialog.getSaveFileNameAndFilter(\
276 self,
277 self.trUtf8("Save Diff"),
278 fname,
279 self.trUtf8("Patch Files (*.diff)"),
280 None,
281 QFileDialog.Options(QFileDialog.DontConfirmOverwrite))
282
283 if not fname:
284 return # user aborted
285
286 ext = QFileInfo(fname).suffix()
287 if not ext:
288 ex = selectedFilter.split("(*")[1].split(")")[0]
289 if ex:
290 fname += ex
291 if QFileInfo(fname).exists():
292 res = QMessageBox.warning(self,
293 self.trUtf8("Save Diff"),
294 self.trUtf8("<p>The patch file <b>{0}</b> already exists.</p>")
295 .format(fname),
296 QMessageBox.StandardButtons(\
297 QMessageBox.Abort | \
298 QMessageBox.Save),
299 QMessageBox.Abort)
300 if res != QMessageBox.Save:
301 return
302 fname = Utilities.toNativeSeparators(fname)
303
304 try:
305 f = open(fname, "wb")
306 f.write(self.contents.toPlainText())
307 f.close()
308 except IOError, why:
309 QMessageBox.critical(self, self.trUtf8('Save Diff'),
310 self.trUtf8('<p>The patch file <b>{0}</b> could not be saved.'
311 '<br>Reason: {1}</p>')
312 .format(fname, unicode(why)))
313
314 def on_passwordCheckBox_toggled(self, isOn):
315 """
316 Private slot to handle the password checkbox toggled.
317
318 @param isOn flag indicating the status of the check box (boolean)
319 """
320 if isOn:
321 self.input.setEchoMode(QLineEdit.Password)
322 else:
323 self.input.setEchoMode(QLineEdit.Normal)
324
325 @pyqtSlot()
326 def on_sendButton_clicked(self):
327 """
328 Private slot to send the input to the subversion process.
329 """
330 input = self.input.text()
331 input += os.linesep
332
333 if self.passwordCheckBox.isChecked():
334 self.errors.insertPlainText(os.linesep)
335 self.errors.ensureCursorVisible()
336 else:
337 self.errors.insertPlainText(input)
338 self.errors.ensureCursorVisible()
339
340 self.process.write(input)
341
342 self.passwordCheckBox.setChecked(False)
343 self.input.clear()
344
345 def on_input_returnPressed(self):
346 """
347 Private slot to handle the press of the return key in the input field.
348 """
349 self.intercept = True
350 self.on_sendButton_clicked()
351
352 def keyPressEvent(self, evt):
353 """
354 Protected slot to handle a key press event.
355
356 @param evt the key press event (QKeyEvent)
357 """
358 if self.intercept:
359 self.intercept = False
360 evt.accept()
361 return
362 QWidget.keyPressEvent(self, evt)

eric ide

mercurial