|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2007 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to browse the log history. |
|
8 """ |
|
9 |
|
10 import re |
|
11 import os |
|
12 |
|
13 import pysvn |
|
14 |
|
15 from PyQt6.QtCore import Qt, QDate, pyqtSlot, QPoint |
|
16 from PyQt6.QtWidgets import ( |
|
17 QHeaderView, QWidget, QApplication, QDialogButtonBox, QTreeWidgetItem |
|
18 ) |
|
19 |
|
20 from EricWidgets import EricMessageBox |
|
21 from EricGui.EricOverrideCursor import EricOverrideCursor |
|
22 |
|
23 from EricUtilities.EricMutexLocker import EricMutexLocker |
|
24 |
|
25 from .SvnUtilities import formatTime, dateFromTime_t |
|
26 from .SvnDialogMixin import SvnDialogMixin |
|
27 |
|
28 from .Ui_SvnLogBrowserDialog import Ui_SvnLogBrowserDialog |
|
29 |
|
30 import UI.PixmapCache |
|
31 |
|
32 |
|
33 class SvnLogBrowserDialog(QWidget, SvnDialogMixin, Ui_SvnLogBrowserDialog): |
|
34 """ |
|
35 Class implementing a dialog to browse the log history. |
|
36 """ |
|
37 def __init__(self, vcs, parent=None): |
|
38 """ |
|
39 Constructor |
|
40 |
|
41 @param vcs reference to the vcs object |
|
42 @param parent parent widget (QWidget) |
|
43 """ |
|
44 super().__init__(parent) |
|
45 self.setupUi(self) |
|
46 SvnDialogMixin.__init__(self) |
|
47 |
|
48 self.__position = QPoint() |
|
49 |
|
50 self.buttonBox.button( |
|
51 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
52 self.buttonBox.button( |
|
53 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
54 |
|
55 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
|
56 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) |
|
57 |
|
58 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") |
|
59 self.filesTree.header().setSortIndicator( |
|
60 0, Qt.SortOrder.AscendingOrder) |
|
61 |
|
62 self.vcs = vcs |
|
63 |
|
64 self.__initData() |
|
65 |
|
66 self.fromDate.setDisplayFormat("yyyy-MM-dd") |
|
67 self.toDate.setDisplayFormat("yyyy-MM-dd") |
|
68 self.__resetUI() |
|
69 |
|
70 self.__messageRole = Qt.ItemDataRole.UserRole |
|
71 self.__changesRole = Qt.ItemDataRole.UserRole + 1 |
|
72 |
|
73 self.flags = { |
|
74 'A': self.tr('Added'), |
|
75 'D': self.tr('Deleted'), |
|
76 'M': self.tr('Modified'), |
|
77 'R': self.tr('Replaced'), |
|
78 } |
|
79 |
|
80 self.__logTreeNormalFont = self.logTree.font() |
|
81 self.__logTreeNormalFont.setBold(False) |
|
82 self.__logTreeBoldFont = self.logTree.font() |
|
83 self.__logTreeBoldFont.setBold(True) |
|
84 |
|
85 self.client = self.vcs.getClient() |
|
86 self.client.callback_cancel = self._clientCancelCallback |
|
87 self.client.callback_get_login = self._clientLoginCallback |
|
88 self.client.callback_ssl_server_trust_prompt = ( |
|
89 self._clientSslServerTrustPromptCallback |
|
90 ) |
|
91 |
|
92 def __initData(self): |
|
93 """ |
|
94 Private method to (re-)initialize some data. |
|
95 """ |
|
96 self.__maxDate = QDate() |
|
97 self.__minDate = QDate() |
|
98 self.__filterLogsEnabled = True |
|
99 |
|
100 self.diff = None |
|
101 self.__lastRev = 0 |
|
102 |
|
103 def closeEvent(self, e): |
|
104 """ |
|
105 Protected slot implementing a close event handler. |
|
106 |
|
107 @param e close event (QCloseEvent) |
|
108 """ |
|
109 self.__position = self.pos() |
|
110 |
|
111 e.accept() |
|
112 |
|
113 def show(self): |
|
114 """ |
|
115 Public slot to show the dialog. |
|
116 """ |
|
117 if not self.__position.isNull(): |
|
118 self.move(self.__position) |
|
119 self.__resetUI() |
|
120 |
|
121 super().show() |
|
122 |
|
123 def __resetUI(self): |
|
124 """ |
|
125 Private method to reset the user interface. |
|
126 """ |
|
127 self.fromDate.setDate(QDate.currentDate()) |
|
128 self.toDate.setDate(QDate.currentDate()) |
|
129 self.fieldCombo.setCurrentIndex(self.fieldCombo.findText( |
|
130 self.tr("Message"))) |
|
131 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences( |
|
132 "LogLimit")) |
|
133 self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences( |
|
134 "StopLogOnCopy")) |
|
135 |
|
136 self.logTree.clear() |
|
137 |
|
138 self.nextButton.setEnabled(True) |
|
139 self.limitSpinBox.setEnabled(True) |
|
140 |
|
141 def _reset(self): |
|
142 """ |
|
143 Protected method to reset the internal state of the dialog. |
|
144 """ |
|
145 SvnDialogMixin._reset(self) |
|
146 |
|
147 self.cancelled = False |
|
148 |
|
149 self.buttonBox.button( |
|
150 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
151 self.buttonBox.button( |
|
152 QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
|
153 self.buttonBox.button( |
|
154 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
155 QApplication.processEvents() |
|
156 |
|
157 def __resizeColumnsLog(self): |
|
158 """ |
|
159 Private method to resize the log tree columns. |
|
160 """ |
|
161 self.logTree.header().resizeSections( |
|
162 QHeaderView.ResizeMode.ResizeToContents) |
|
163 self.logTree.header().setStretchLastSection(True) |
|
164 |
|
165 def __resortLog(self): |
|
166 """ |
|
167 Private method to resort the log tree. |
|
168 """ |
|
169 self.logTree.sortItems( |
|
170 self.logTree.sortColumn(), |
|
171 self.logTree.header().sortIndicatorOrder()) |
|
172 |
|
173 def __resizeColumnsFiles(self): |
|
174 """ |
|
175 Private method to resize the changed files tree columns. |
|
176 """ |
|
177 self.filesTree.header().resizeSections( |
|
178 QHeaderView.ResizeMode.ResizeToContents) |
|
179 self.filesTree.header().setStretchLastSection(True) |
|
180 |
|
181 def __resortFiles(self): |
|
182 """ |
|
183 Private method to resort the changed files tree. |
|
184 """ |
|
185 sortColumn = self.filesTree.sortColumn() |
|
186 self.filesTree.sortItems( |
|
187 1, self.filesTree.header().sortIndicatorOrder()) |
|
188 self.filesTree.sortItems( |
|
189 sortColumn, self.filesTree.header().sortIndicatorOrder()) |
|
190 |
|
191 def __generateLogItem(self, author, date, message, revision, changedPaths): |
|
192 """ |
|
193 Private method to generate a log tree entry. |
|
194 |
|
195 @param author author info (string) |
|
196 @param date date info (integer) |
|
197 @param message text of the log message (string) |
|
198 @param revision revision info (string or pysvn.opt_revision_kind) |
|
199 @param changedPaths list of pysvn dictionary like objects containing |
|
200 info about the changed files/directories |
|
201 @return reference to the generated item (QTreeWidgetItem) |
|
202 """ |
|
203 if revision == "": |
|
204 rev = "" |
|
205 self.__lastRev = 0 |
|
206 else: |
|
207 rev = revision.number |
|
208 self.__lastRev = revision.number |
|
209 dt = formatTime(date) if date else "" |
|
210 |
|
211 itm = QTreeWidgetItem(self.logTree) |
|
212 itm.setData(0, Qt.ItemDataRole.DisplayRole, rev) |
|
213 itm.setData(1, Qt.ItemDataRole.DisplayRole, author) |
|
214 itm.setData(2, Qt.ItemDataRole.DisplayRole, dt) |
|
215 itm.setData(3, Qt.ItemDataRole.DisplayRole, |
|
216 " ".join(message.splitlines())) |
|
217 |
|
218 changes = [] |
|
219 for changedPath in changedPaths: |
|
220 copyPath = ( |
|
221 "" |
|
222 if changedPath["copyfrom_path"] is None else |
|
223 changedPath["copyfrom_path"] |
|
224 ) |
|
225 copyRev = ( |
|
226 "" |
|
227 if changedPath["copyfrom_revision"] is None else |
|
228 "{0:7d}".format(changedPath["copyfrom_revision"].number) |
|
229 ) |
|
230 change = { |
|
231 "action": changedPath["action"], |
|
232 "path": changedPath["path"], |
|
233 "copyfrom_path": copyPath, |
|
234 "copyfrom_revision": copyRev, |
|
235 } |
|
236 changes.append(change) |
|
237 itm.setData(0, self.__messageRole, message) |
|
238 itm.setData(0, self.__changesRole, changes) |
|
239 |
|
240 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) |
|
241 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignLeft) |
|
242 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignLeft) |
|
243 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignLeft) |
|
244 itm.setTextAlignment(4, Qt.AlignmentFlag.AlignLeft) |
|
245 |
|
246 return itm |
|
247 |
|
248 def __generateFileItem(self, action, path, copyFrom, copyRev): |
|
249 """ |
|
250 Private method to generate a changed files tree entry. |
|
251 |
|
252 @param action indicator for the change action ("A", "D" or "M") |
|
253 @param path path of the file in the repository (string) |
|
254 @param copyFrom path the file was copied from (None, string) |
|
255 @param copyRev revision the file was copied from (None, string) |
|
256 @return reference to the generated item (QTreeWidgetItem) |
|
257 """ |
|
258 itm = QTreeWidgetItem( |
|
259 self.filesTree, |
|
260 [self.flags[action], path, copyFrom, copyRev] |
|
261 ) |
|
262 |
|
263 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignRight) |
|
264 |
|
265 return itm |
|
266 |
|
267 def __getLogEntries(self, startRev=None): |
|
268 """ |
|
269 Private method to retrieve log entries from the repository. |
|
270 |
|
271 @param startRev revision number to start from (integer, string) |
|
272 """ |
|
273 fetchLimit = 10 |
|
274 self._reset() |
|
275 |
|
276 limit = self.limitSpinBox.value() |
|
277 if startRev is None: |
|
278 start = pysvn.Revision(pysvn.opt_revision_kind.head) |
|
279 else: |
|
280 try: |
|
281 start = pysvn.Revision(pysvn.opt_revision_kind.number, |
|
282 int(startRev)) |
|
283 except TypeError: |
|
284 start = pysvn.Revision(pysvn.opt_revision_kind.head) |
|
285 |
|
286 with EricOverrideCursor(): |
|
287 cwd = os.getcwd() |
|
288 os.chdir(self.dname) |
|
289 try: |
|
290 nextRev = 0 |
|
291 fetched = 0 |
|
292 logs = [] |
|
293 with EricMutexLocker(self.vcs.vcsExecutionMutex): |
|
294 while fetched < limit: |
|
295 flimit = min(fetchLimit, limit - fetched) |
|
296 revstart = ( |
|
297 start |
|
298 if fetched == 0 else |
|
299 pysvn.Revision(pysvn.opt_revision_kind.number, |
|
300 nextRev) |
|
301 ) |
|
302 allLogs = self.client.log( |
|
303 self.fname, revision_start=revstart, |
|
304 discover_changed_paths=True, limit=flimit + 1, |
|
305 strict_node_history=self.stopCheckBox.isChecked()) |
|
306 if ( |
|
307 len(allLogs) <= flimit or |
|
308 self._clientCancelCallback() |
|
309 ): |
|
310 logs.extend(allLogs) |
|
311 break |
|
312 else: |
|
313 logs.extend(allLogs[:-1]) |
|
314 nextRev = allLogs[-1]["revision"].number |
|
315 fetched += fetchLimit |
|
316 |
|
317 for log in logs: |
|
318 author = log["author"] |
|
319 message = log["message"] |
|
320 self.__generateLogItem( |
|
321 author, log["date"], message, |
|
322 log["revision"], log['changed_paths']) |
|
323 dt = dateFromTime_t(log["date"]) |
|
324 if ( |
|
325 not self.__maxDate.isValid() and |
|
326 not self.__minDate.isValid() |
|
327 ): |
|
328 self.__maxDate = dt |
|
329 self.__minDate = dt |
|
330 else: |
|
331 if self.__maxDate < dt: |
|
332 self.__maxDate = dt |
|
333 if self.__minDate > dt: |
|
334 self.__minDate = dt |
|
335 if len(logs) < limit and not self.cancelled: |
|
336 self.nextButton.setEnabled(False) |
|
337 self.limitSpinBox.setEnabled(False) |
|
338 self.__filterLogsEnabled = False |
|
339 self.fromDate.setMinimumDate(self.__minDate) |
|
340 self.fromDate.setMaximumDate(self.__maxDate) |
|
341 self.fromDate.setDate(self.__minDate) |
|
342 self.toDate.setMinimumDate(self.__minDate) |
|
343 self.toDate.setMaximumDate(self.__maxDate) |
|
344 self.toDate.setDate(self.__maxDate) |
|
345 self.__filterLogsEnabled = True |
|
346 |
|
347 self.__resizeColumnsLog() |
|
348 self.__resortLog() |
|
349 self.__filterLogs() |
|
350 except pysvn.ClientError as e: |
|
351 self.__showError(e.args[0]) |
|
352 os.chdir(cwd) |
|
353 self.__finish() |
|
354 |
|
355 def start(self, fn, isFile=False): |
|
356 """ |
|
357 Public slot to start the svn log command. |
|
358 |
|
359 @param fn filename to show the log for (string) |
|
360 @param isFile flag indicating log for a file is to be shown |
|
361 (boolean) |
|
362 """ |
|
363 self.sbsCheckBox.setEnabled(isFile) |
|
364 self.sbsCheckBox.setVisible(isFile) |
|
365 |
|
366 self.__initData() |
|
367 |
|
368 self.filename = fn |
|
369 self.dname, self.fname = self.vcs.splitPath(fn) |
|
370 |
|
371 self.activateWindow() |
|
372 self.raise_() |
|
373 |
|
374 self.logTree.clear() |
|
375 self.__getLogEntries() |
|
376 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
|
377 |
|
378 def __finish(self): |
|
379 """ |
|
380 Private slot called when the user pressed the button. |
|
381 """ |
|
382 self.buttonBox.button( |
|
383 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
384 self.buttonBox.button( |
|
385 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
|
386 self.buttonBox.button( |
|
387 QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
388 |
|
389 self._cancel() |
|
390 |
|
391 def __diffRevisions(self, rev1, rev2, peg_rev): |
|
392 """ |
|
393 Private method to do a diff of two revisions. |
|
394 |
|
395 @param rev1 first revision number (integer) |
|
396 @param rev2 second revision number (integer) |
|
397 @param peg_rev revision number to use as a reference (integer) |
|
398 """ |
|
399 if self.sbsCheckBox.isEnabled() and self.sbsCheckBox.isChecked(): |
|
400 self.vcs.vcsSbsDiff(self.filename, |
|
401 revisions=(str(rev1), str(rev2))) |
|
402 else: |
|
403 if self.diff is None: |
|
404 from .SvnDiffDialog import SvnDiffDialog |
|
405 self.diff = SvnDiffDialog(self.vcs) |
|
406 self.diff.show() |
|
407 self.diff.raise_() |
|
408 QApplication.processEvents() |
|
409 self.diff.start(self.filename, [rev1, rev2], pegRev=peg_rev) |
|
410 |
|
411 def on_buttonBox_clicked(self, button): |
|
412 """ |
|
413 Private slot called by a button of the button box clicked. |
|
414 |
|
415 @param button button that was clicked (QAbstractButton) |
|
416 """ |
|
417 if button == self.buttonBox.button( |
|
418 QDialogButtonBox.StandardButton.Close |
|
419 ): |
|
420 self.close() |
|
421 elif button == self.buttonBox.button( |
|
422 QDialogButtonBox.StandardButton.Cancel |
|
423 ): |
|
424 self.cancelled = True |
|
425 self.__finish() |
|
426 |
|
427 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
|
428 def on_logTree_currentItemChanged(self, current, previous): |
|
429 """ |
|
430 Private slot called, when the current item of the log tree changes. |
|
431 |
|
432 @param current reference to the new current item (QTreeWidgetItem) |
|
433 @param previous reference to the old current item (QTreeWidgetItem) |
|
434 """ |
|
435 if current is not None: |
|
436 self.messageEdit.setPlainText(current.data(0, self.__messageRole)) |
|
437 |
|
438 self.filesTree.clear() |
|
439 changes = current.data(0, self.__changesRole) |
|
440 if len(changes) > 0: |
|
441 for change in changes: |
|
442 self.__generateFileItem( |
|
443 change["action"], change["path"], |
|
444 change["copyfrom_path"], change["copyfrom_revision"]) |
|
445 self.__resizeColumnsFiles() |
|
446 self.__resortFiles() |
|
447 |
|
448 self.diffPreviousButton.setEnabled( |
|
449 current != self.logTree.topLevelItem( |
|
450 self.logTree.topLevelItemCount() - 1)) |
|
451 |
|
452 # Highlight the current entry using a bold font |
|
453 for col in range(self.logTree.columnCount()): |
|
454 current and current.setFont(col, self.__logTreeBoldFont) |
|
455 previous and previous.setFont(col, self.__logTreeNormalFont) |
|
456 |
|
457 # set the state of the up and down buttons |
|
458 self.upButton.setEnabled( |
|
459 current is not None and |
|
460 self.logTree.indexOfTopLevelItem(current) > 0) |
|
461 self.downButton.setEnabled( |
|
462 current is not None and |
|
463 int(current.text(0)) > 1) |
|
464 |
|
465 @pyqtSlot() |
|
466 def on_logTree_itemSelectionChanged(self): |
|
467 """ |
|
468 Private slot called, when the selection has changed. |
|
469 """ |
|
470 self.diffRevisionsButton.setEnabled( |
|
471 len(self.logTree.selectedItems()) == 2) |
|
472 |
|
473 @pyqtSlot() |
|
474 def on_nextButton_clicked(self): |
|
475 """ |
|
476 Private slot to handle the Next button. |
|
477 """ |
|
478 if self.__lastRev > 1: |
|
479 self.__getLogEntries(self.__lastRev - 1) |
|
480 |
|
481 @pyqtSlot() |
|
482 def on_diffPreviousButton_clicked(self): |
|
483 """ |
|
484 Private slot to handle the Diff to Previous button. |
|
485 """ |
|
486 itm = self.logTree.topLevelItem(0) |
|
487 if itm is None: |
|
488 self.diffPreviousButton.setEnabled(False) |
|
489 return |
|
490 peg_rev = int(itm.text(0)) |
|
491 |
|
492 itm = self.logTree.currentItem() |
|
493 if itm is None: |
|
494 self.diffPreviousButton.setEnabled(False) |
|
495 return |
|
496 rev2 = int(itm.text(0)) |
|
497 |
|
498 itm = self.logTree.topLevelItem( |
|
499 self.logTree.indexOfTopLevelItem(itm) + 1) |
|
500 if itm is None: |
|
501 self.diffPreviousButton.setEnabled(False) |
|
502 return |
|
503 rev1 = int(itm.text(0)) |
|
504 |
|
505 self.__diffRevisions(rev1, rev2, peg_rev) |
|
506 |
|
507 @pyqtSlot() |
|
508 def on_diffRevisionsButton_clicked(self): |
|
509 """ |
|
510 Private slot to handle the Compare Revisions button. |
|
511 """ |
|
512 items = self.logTree.selectedItems() |
|
513 if len(items) != 2: |
|
514 self.diffRevisionsButton.setEnabled(False) |
|
515 return |
|
516 |
|
517 rev2 = int(items[0].text(0)) |
|
518 rev1 = int(items[1].text(0)) |
|
519 |
|
520 itm = self.logTree.topLevelItem(0) |
|
521 if itm is None: |
|
522 self.diffPreviousButton.setEnabled(False) |
|
523 return |
|
524 peg_rev = int(itm.text(0)) |
|
525 |
|
526 self.__diffRevisions(min(rev1, rev2), max(rev1, rev2), peg_rev) |
|
527 |
|
528 def __showError(self, msg): |
|
529 """ |
|
530 Private slot to show an error message. |
|
531 |
|
532 @param msg error message to show (string) |
|
533 """ |
|
534 EricMessageBox.critical( |
|
535 self, |
|
536 self.tr("Subversion Error"), |
|
537 msg) |
|
538 |
|
539 @pyqtSlot(QDate) |
|
540 def on_fromDate_dateChanged(self, date): |
|
541 """ |
|
542 Private slot called, when the from date changes. |
|
543 |
|
544 @param date new date (QDate) |
|
545 """ |
|
546 self.__filterLogs() |
|
547 |
|
548 @pyqtSlot(QDate) |
|
549 def on_toDate_dateChanged(self, date): |
|
550 """ |
|
551 Private slot called, when the from date changes. |
|
552 |
|
553 @param date new date (QDate) |
|
554 """ |
|
555 self.__filterLogs() |
|
556 |
|
557 @pyqtSlot(int) |
|
558 def on_fieldCombo_activated(self, index): |
|
559 """ |
|
560 Private slot called, when a new filter field is selected. |
|
561 |
|
562 @param index index of the selected entry |
|
563 @type int |
|
564 """ |
|
565 self.__filterLogs() |
|
566 |
|
567 @pyqtSlot(str) |
|
568 def on_rxEdit_textChanged(self, txt): |
|
569 """ |
|
570 Private slot called, when a filter expression is entered. |
|
571 |
|
572 @param txt filter expression (string) |
|
573 """ |
|
574 self.__filterLogs() |
|
575 |
|
576 def __filterLogs(self): |
|
577 """ |
|
578 Private method to filter the log entries. |
|
579 """ |
|
580 if self.__filterLogsEnabled: |
|
581 from_ = self.fromDate.date().toString("yyyy-MM-dd") |
|
582 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") |
|
583 txt = self.fieldCombo.currentText() |
|
584 if txt == self.tr("Author"): |
|
585 fieldIndex = 1 |
|
586 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
|
587 elif txt == self.tr("Revision"): |
|
588 fieldIndex = 0 |
|
589 txt = self.rxEdit.text() |
|
590 if txt.startswith("^"): |
|
591 searchRx = re.compile( |
|
592 r"^\s*{0}".format(txt[1:]), re.IGNORECASE) |
|
593 else: |
|
594 searchRx = re.compile(txt, re.IGNORECASE) |
|
595 else: |
|
596 fieldIndex = 3 |
|
597 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
|
598 |
|
599 currentItem = self.logTree.currentItem() |
|
600 for topIndex in range(self.logTree.topLevelItemCount()): |
|
601 topItem = self.logTree.topLevelItem(topIndex) |
|
602 if ( |
|
603 topItem.text(2) <= to_ and |
|
604 topItem.text(2) >= from_ and |
|
605 searchRx.search(topItem.text(fieldIndex)) is not None |
|
606 ): |
|
607 topItem.setHidden(False) |
|
608 if topItem is currentItem: |
|
609 self.on_logTree_currentItemChanged(topItem, None) |
|
610 else: |
|
611 topItem.setHidden(True) |
|
612 if topItem is currentItem: |
|
613 self.messageEdit.clear() |
|
614 self.filesTree.clear() |
|
615 |
|
616 @pyqtSlot(bool) |
|
617 def on_stopCheckBox_clicked(self, checked): |
|
618 """ |
|
619 Private slot called, when the stop on copy/move checkbox is clicked. |
|
620 |
|
621 @param checked flag indicating the check box state (boolean) |
|
622 """ |
|
623 self.vcs.getPlugin().setPreferences("StopLogOnCopy", |
|
624 int(self.stopCheckBox.isChecked())) |
|
625 self.nextButton.setEnabled(True) |
|
626 self.limitSpinBox.setEnabled(True) |
|
627 |
|
628 @pyqtSlot() |
|
629 def on_upButton_clicked(self): |
|
630 """ |
|
631 Private slot to move the current item up one entry. |
|
632 """ |
|
633 itm = self.logTree.itemAbove(self.logTree.currentItem()) |
|
634 if itm: |
|
635 self.logTree.setCurrentItem(itm) |
|
636 |
|
637 @pyqtSlot() |
|
638 def on_downButton_clicked(self): |
|
639 """ |
|
640 Private slot to move the current item down one entry. |
|
641 """ |
|
642 itm = self.logTree.itemBelow(self.logTree.currentItem()) |
|
643 if itm: |
|
644 self.logTree.setCurrentItem(itm) |
|
645 else: |
|
646 # load the next bunch and try again |
|
647 self.on_nextButton_clicked() |
|
648 self.on_downButton_clicked() |