80 # path copied from followed by copied from revision |
81 # path copied from followed by copied from revision |
81 self.rx_flags2 = QRegExp(' ([ADM]) (.*)\\s*') |
82 self.rx_flags2 = QRegExp(' ([ADM]) (.*)\\s*') |
82 # three blanks followed by A or D or M followed by path |
83 # three blanks followed by A or D or M followed by path |
83 |
84 |
84 self.flags = { |
85 self.flags = { |
85 'A' : self.trUtf8('Added'), |
86 'A': self.trUtf8('Added'), |
86 'D' : self.trUtf8('Deleted'), |
87 'D': self.trUtf8('Deleted'), |
87 'M' : self.trUtf8('Modified'), |
88 'M': self.trUtf8('Modified'), |
88 } |
89 } |
89 |
90 |
90 self.buf = [] # buffer for stdout |
91 self.buf = [] # buffer for stdout |
91 self.diff = None |
92 self.diff = None |
92 self.__started = False |
93 self.__started = False |
130 def __resortFiles(self): |
131 def __resortFiles(self): |
131 """ |
132 """ |
132 Private method to resort the changed files tree. |
133 Private method to resort the changed files tree. |
133 """ |
134 """ |
134 sortColumn = self.filesTree.sortColumn() |
135 sortColumn = self.filesTree.sortColumn() |
135 self.filesTree.sortItems(1, |
136 self.filesTree.sortItems(1, |
136 self.filesTree.header().sortIndicatorOrder()) |
137 self.filesTree.header().sortIndicatorOrder()) |
137 self.filesTree.sortItems(sortColumn, |
138 self.filesTree.sortItems(sortColumn, |
138 self.filesTree.header().sortIndicatorOrder()) |
139 self.filesTree.header().sortIndicatorOrder()) |
139 |
140 |
140 def __generateLogItem(self, author, date, message, revision, changedPaths): |
141 def __generateLogItem(self, author, date, message, revision, changedPaths): |
141 """ |
142 """ |
142 Private method to generate a log tree entry. |
143 Private method to generate a log tree entry. |
144 @param author author info (string) |
145 @param author author info (string) |
145 @param date date info (string) |
146 @param date date info (string) |
146 @param message text of the log message (list of strings) |
147 @param message text of the log message (list of strings) |
147 @param revision revision info (string) |
148 @param revision revision info (string) |
148 @param changedPaths list of dictionary objects containing |
149 @param changedPaths list of dictionary objects containing |
149 info about the changed files/directories |
150 info about the changed files/directories |
150 @return reference to the generated item (QTreeWidgetItem) |
151 @return reference to the generated item (QTreeWidgetItem) |
151 """ |
152 """ |
152 msg = [] |
153 msg = [] |
153 for line in message: |
154 for line in message: |
154 msg.append(line.strip()) |
155 msg.append(line.strip()) |
155 |
156 |
156 itm = QTreeWidgetItem(self.logTree, [ |
157 itm = QTreeWidgetItem(self.logTree, [ |
157 "{0:7}".format(revision), |
158 "{0:7}".format(revision), |
158 author, |
159 author, |
159 date, |
160 date, |
160 " ".join(msg), |
161 " ".join(msg), |
161 ]) |
162 ]) |
162 |
163 |
163 itm.setData(0, self.__messageRole, message) |
164 itm.setData(0, self.__messageRole, message) |
164 itm.setData(0, self.__changesRole, changedPaths) |
165 itm.setData(0, self.__changesRole, changedPaths) |
165 |
166 |
185 @param copyFrom path the file was copied from (None, string) |
186 @param copyFrom path the file was copied from (None, string) |
186 @param copyRev revision the file was copied from (None, string) |
187 @param copyRev revision the file was copied from (None, string) |
187 @return reference to the generated item (QTreeWidgetItem) |
188 @return reference to the generated item (QTreeWidgetItem) |
188 """ |
189 """ |
189 itm = QTreeWidgetItem(self.filesTree, [ |
190 itm = QTreeWidgetItem(self.filesTree, [ |
190 self.flags[action], |
191 self.flags[action], |
191 path, |
192 path, |
192 copyFrom, |
193 copyFrom, |
193 copyRev, |
194 copyRev, |
194 ]) |
195 ]) |
195 |
196 |
196 itm.setTextAlignment(3, Qt.AlignRight) |
197 itm.setTextAlignment(3, Qt.AlignRight) |
197 |
198 |
198 return itm |
199 return itm |
199 |
200 |
200 def __getLogEntries(self, startRev = None): |
201 def __getLogEntries(self, startRev=None): |
201 """ |
202 """ |
202 Private method to retrieve log entries from the repository. |
203 Private method to retrieve log entries from the repository. |
203 |
204 |
204 @param startRev revision number to start from (integer, string) |
205 @param startRev revision number to start from (integer, string) |
205 """ |
206 """ |
297 Private method to process the buffered output of the svn log command. |
298 Private method to process the buffered output of the svn log command. |
298 """ |
299 """ |
299 ioEncoding = Preferences.getSystem("IOEncoding") |
300 ioEncoding = Preferences.getSystem("IOEncoding") |
300 |
301 |
301 noEntries = 0 |
302 noEntries = 0 |
302 log = {"message" : []} |
303 log = {"message": []} |
303 changedPaths = [] |
304 changedPaths = [] |
304 for s in self.buf: |
305 for s in self.buf: |
305 if self.rx_rev1.exactMatch(s): |
306 if self.rx_rev1.exactMatch(s): |
306 log["revision"] = self.rx_rev.cap(1) |
307 log["revision"] = self.rx_rev.cap(1) |
307 log["author"] = self.rx_rev.cap(2) |
308 log["author"] = self.rx_rev.cap(2) |
308 log["date"] = self.rx_rev.cap(3) |
309 log["date"] = self.rx_rev.cap(3) |
309 # number of lines is ignored |
310 # number of lines is ignored |
310 elif self.rx_rev2.exactMatch(s): |
311 elif self.rx_rev2.exactMatch(s): |
311 log["revision"] = self.rx_rev2.cap(1) |
312 log["revision"] = self.rx_rev2.cap(1) |
312 log["author"] = self.rx_rev2.cap(2) |
313 log["author"] = self.rx_rev2.cap(2) |
313 log["date"] = self.rx_rev2.cap(3) |
314 log["date"] = self.rx_rev2.cap(3) |
314 # number of lines is ignored |
315 # number of lines is ignored |
315 elif self.rx_flags1.exactMatch(s): |
316 elif self.rx_flags1.exactMatch(s): |
316 changedPaths.append({\ |
317 changedPaths.append({\ |
317 "action" : |
318 "action": |
318 str(self.rx_flags1.cap(1).strip(), ioEncoding, 'replace'), |
319 str(self.rx_flags1.cap(1).strip(), ioEncoding, 'replace'), |
319 "path" : |
320 "path": |
320 str(self.rx_flags1.cap(2).strip(), ioEncoding, 'replace'), |
321 str(self.rx_flags1.cap(2).strip(), ioEncoding, 'replace'), |
321 "copyfrom_path" : |
322 "copyfrom_path": |
322 str(self.rx_flags1.cap(3).strip(), ioEncoding, 'replace'), |
323 str(self.rx_flags1.cap(3).strip(), ioEncoding, 'replace'), |
323 "copyfrom_revision" : |
324 "copyfrom_revision": |
324 str(self.rx_flags1.cap(4).strip(), ioEncoding, 'replace'), |
325 str(self.rx_flags1.cap(4).strip(), ioEncoding, 'replace'), |
325 }) |
326 }) |
326 elif self.rx_flags2.exactMatch(s): |
327 elif self.rx_flags2.exactMatch(s): |
327 changedPaths.append({\ |
328 changedPaths.append({\ |
328 "action" : |
329 "action": |
329 str(self.rx_flags2.cap(1).strip(), ioEncoding, 'replace'), |
330 str(self.rx_flags2.cap(1).strip(), ioEncoding, 'replace'), |
330 "path" : |
331 "path": |
331 str(self.rx_flags2.cap(2).strip(), ioEncoding, 'replace'), |
332 str(self.rx_flags2.cap(2).strip(), ioEncoding, 'replace'), |
332 "copyfrom_path" : "", |
333 "copyfrom_path": "", |
333 "copyfrom_revision" : "", |
334 "copyfrom_revision": "", |
334 }) |
335 }) |
335 elif self.rx_sep1.exactMatch(s) or self.rx_sep2.exactMatch(s): |
336 elif self.rx_sep1.exactMatch(s) or self.rx_sep2.exactMatch(s): |
336 if len(log) > 1: |
337 if len(log) > 1: |
337 self.__generateLogItem(log["author"], log["date"], |
338 self.__generateLogItem(log["author"], log["date"], |
338 log["message"], log["revision"], changedPaths) |
339 log["message"], log["revision"], changedPaths) |
339 dt = QDate.fromString(log["date"], Qt.ISODate) |
340 dt = QDate.fromString(log["date"], Qt.ISODate) |
340 if not self.__maxDate.isValid() and not self.__minDate.isValid(): |
341 if not self.__maxDate.isValid() and not self.__minDate.isValid(): |
341 self.__maxDate = dt |
342 self.__maxDate = dt |
342 self.__minDate = dt |
343 self.__minDate = dt |
376 self.__filterLogsEnabled = True |
377 self.__filterLogsEnabled = True |
377 self.__filterLogs() |
378 self.__filterLogs() |
378 |
379 |
379 def __readStdout(self): |
380 def __readStdout(self): |
380 """ |
381 """ |
381 Private slot to handle the readyReadStandardOutput signal. |
382 Private slot to handle the readyReadStandardOutput signal. |
382 |
383 |
383 It reads the output of the process and inserts it into a buffer. |
384 It reads the output of the process and inserts it into a buffer. |
384 """ |
385 """ |
385 self.process.setReadChannel(QProcess.StandardOutput) |
386 self.process.setReadChannel(QProcess.StandardOutput) |
386 |
387 |
387 while self.process.canReadLine(): |
388 while self.process.canReadLine(): |
388 line = str(self.process.readLine(), |
389 line = str(self.process.readLine(), |
389 Preferences.getSystem("IOEncoding"), |
390 Preferences.getSystem("IOEncoding"), |
390 'replace') |
391 'replace') |
391 self.buf.append(line) |
392 self.buf.append(line) |
392 |
393 |
393 def __readStderr(self): |
394 def __readStderr(self): |
394 """ |
395 """ |
397 It reads the error output of the process and inserts it into the |
398 It reads the error output of the process and inserts it into the |
398 error pane. |
399 error pane. |
399 """ |
400 """ |
400 if self.process is not None: |
401 if self.process is not None: |
401 self.errorGroup.show() |
402 self.errorGroup.show() |
402 s = str(self.process.readAllStandardError(), |
403 s = str(self.process.readAllStandardError(), |
403 Preferences.getSystem("IOEncoding"), |
404 Preferences.getSystem("IOEncoding"), |
404 'replace') |
405 'replace') |
405 self.errors.insertPlainText(s) |
406 self.errors.insertPlainText(s) |
406 self.errors.ensureCursorVisible() |
407 self.errors.ensureCursorVisible() |
407 |
408 |
408 def __diffRevisions(self, rev1, rev2): |
409 def __diffRevisions(self, rev1, rev2): |
445 |
446 |
446 self.filesTree.clear() |
447 self.filesTree.clear() |
447 changes = current.data(0, self.__changesRole) |
448 changes = current.data(0, self.__changesRole) |
448 if len(changes) > 0: |
449 if len(changes) > 0: |
449 for change in changes: |
450 for change in changes: |
450 self.__generateFileItem(change["action"], change["path"], |
451 self.__generateFileItem(change["action"], change["path"], |
451 change["copyfrom_path"], change["copyfrom_revision"]) |
452 change["copyfrom_path"], change["copyfrom_revision"]) |
452 self.__resizeColumnsFiles() |
453 self.__resizeColumnsFiles() |
453 self.__resortFiles() |
454 self.__resortFiles() |
454 |
455 |
455 self.diffPreviousButton.setEnabled( |
456 self.diffPreviousButton.setEnabled( |
586 @pyqtSlot(bool) |
587 @pyqtSlot(bool) |
587 def on_stopCheckBox_clicked(self, checked): |
588 def on_stopCheckBox_clicked(self, checked): |
588 """ |
589 """ |
589 Private slot called, when the stop on copy/move checkbox is clicked |
590 Private slot called, when the stop on copy/move checkbox is clicked |
590 """ |
591 """ |
591 self.vcs.getPlugin().setPreferences("StopLogOnCopy", |
592 self.vcs.getPlugin().setPreferences("StopLogOnCopy", |
592 self.stopCheckBox.isChecked()) |
593 self.stopCheckBox.isChecked()) |
593 self.nextButton.setEnabled(True) |
594 self.nextButton.setEnabled(True) |
594 self.limitSpinBox.setEnabled(True) |
595 self.limitSpinBox.setEnabled(True) |
595 |
596 |
596 def on_passwordCheckBox_toggled(self, isOn): |
597 def on_passwordCheckBox_toggled(self, isOn): |