28 |
32 |
29 class SvnLogBrowserDialog(QWidget, Ui_SvnLogBrowserDialog): |
33 class SvnLogBrowserDialog(QWidget, Ui_SvnLogBrowserDialog): |
30 """ |
34 """ |
31 Class implementing a dialog to browse the log history. |
35 Class implementing a dialog to browse the log history. |
32 """ |
36 """ |
|
37 |
33 def __init__(self, vcs, parent=None): |
38 def __init__(self, vcs, parent=None): |
34 """ |
39 """ |
35 Constructor |
40 Constructor |
36 |
41 |
37 @param vcs reference to the vcs object |
42 @param vcs reference to the vcs object |
38 @param parent parent widget (QWidget) |
43 @param parent parent widget (QWidget) |
39 """ |
44 """ |
40 super().__init__(parent) |
45 super().__init__(parent) |
41 self.setupUi(self) |
46 self.setupUi(self) |
42 |
47 |
43 self.__position = QPoint() |
48 self.__position = QPoint() |
44 |
49 |
45 self.buttonBox.button( |
50 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
46 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
51 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
47 self.buttonBox.button( |
52 |
48 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
49 |
|
50 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
53 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) |
51 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) |
54 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) |
52 |
55 |
53 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") |
56 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") |
54 self.filesTree.header().setSortIndicator( |
57 self.filesTree.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) |
55 0, Qt.SortOrder.AscendingOrder) |
58 |
56 |
|
57 self.vcs = vcs |
59 self.vcs = vcs |
58 |
60 |
59 self.__initData() |
61 self.__initData() |
60 |
62 |
61 self.fromDate.setDisplayFormat("yyyy-MM-dd") |
63 self.fromDate.setDisplayFormat("yyyy-MM-dd") |
62 self.toDate.setDisplayFormat("yyyy-MM-dd") |
64 self.toDate.setDisplayFormat("yyyy-MM-dd") |
63 self.__resetUI() |
65 self.__resetUI() |
64 |
66 |
65 self.__messageRole = Qt.ItemDataRole.UserRole |
67 self.__messageRole = Qt.ItemDataRole.UserRole |
66 self.__changesRole = Qt.ItemDataRole.UserRole + 1 |
68 self.__changesRole = Qt.ItemDataRole.UserRole + 1 |
67 |
69 |
68 self.__process = EricOverrideCursorProcess() |
70 self.__process = EricOverrideCursorProcess() |
69 self.__process.finished.connect(self.__procFinished) |
71 self.__process.finished.connect(self.__procFinished) |
70 self.__process.readyReadStandardOutput.connect(self.__readStdout) |
72 self.__process.readyReadStandardOutput.connect(self.__readStdout) |
71 self.__process.readyReadStandardError.connect(self.__readStderr) |
73 self.__process.readyReadStandardError.connect(self.__readStderr) |
72 |
74 |
73 self.rx_sep1 = re.compile('\\-+\\s*') |
75 self.rx_sep1 = re.compile("\\-+\\s*") |
74 self.rx_sep2 = re.compile('=+\\s*') |
76 self.rx_sep2 = re.compile("=+\\s*") |
75 self.rx_rev1 = re.compile( |
77 self.rx_rev1 = re.compile(r"rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*") |
76 r'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*') |
|
77 # "rev" followed by one or more decimals followed by a colon followed |
78 # "rev" followed by one or more decimals followed by a colon followed |
78 # anything up to " | " (twice) followed by one or more decimals |
79 # anything up to " | " (twice) followed by one or more decimals |
79 # followed by anything |
80 # followed by anything |
80 self.rx_rev2 = re.compile( |
81 self.rx_rev2 = re.compile(r"r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*") |
81 r'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*') |
|
82 # "r" followed by one or more decimals followed by " | " followed |
82 # "r" followed by one or more decimals followed by " | " followed |
83 # anything up to " | " (twice) followed by one or more decimals |
83 # anything up to " | " (twice) followed by one or more decimals |
84 # followed by anything |
84 # followed by anything |
85 self.rx_flags1 = re.compile( |
85 self.rx_flags1 = re.compile( |
86 r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""") |
86 r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""" |
|
87 ) |
87 # three blanks followed by A or D or M followed by path followed by |
88 # three blanks followed by A or D or M followed by path followed by |
88 # path copied from followed by copied from revision |
89 # path copied from followed by copied from revision |
89 self.rx_flags2 = re.compile(' ([ADM]) (.*)\\s*') |
90 self.rx_flags2 = re.compile(" ([ADM]) (.*)\\s*") |
90 # three blanks followed by A or D or M followed by path |
91 # three blanks followed by A or D or M followed by path |
91 |
92 |
92 self.flags = { |
93 self.flags = { |
93 'A': self.tr('Added'), |
94 "A": self.tr("Added"), |
94 'D': self.tr('Deleted'), |
95 "D": self.tr("Deleted"), |
95 'M': self.tr('Modified'), |
96 "M": self.tr("Modified"), |
96 'R': self.tr('Replaced'), |
97 "R": self.tr("Replaced"), |
97 } |
98 } |
98 self.intercept = False |
99 self.intercept = False |
99 |
100 |
100 self.__logTreeNormalFont = self.logTree.font() |
101 self.__logTreeNormalFont = self.logTree.font() |
101 self.__logTreeNormalFont.setBold(False) |
102 self.__logTreeNormalFont.setBold(False) |
102 self.__logTreeBoldFont = self.logTree.font() |
103 self.__logTreeBoldFont = self.logTree.font() |
103 self.__logTreeBoldFont.setBold(True) |
104 self.__logTreeBoldFont.setBold(True) |
104 |
105 |
105 self.__finishCallbacks = [] |
106 self.__finishCallbacks = [] |
106 |
107 |
107 def __addFinishCallback(self, callback): |
108 def __addFinishCallback(self, callback): |
108 """ |
109 """ |
109 Private method to add a method to be called once the process finished. |
110 Private method to add a method to be called once the process finished. |
110 |
111 |
111 The callback methods are invoke in a FIFO style and are consumed. If |
112 The callback methods are invoke in a FIFO style and are consumed. If |
112 a callback method needs to be called again, it must be added again. |
113 a callback method needs to be called again, it must be added again. |
113 |
114 |
114 @param callback callback method |
115 @param callback callback method |
115 @type function |
116 @type function |
116 """ |
117 """ |
117 if callback not in self.__finishCallbacks: |
118 if callback not in self.__finishCallbacks: |
118 self.__finishCallbacks.append(callback) |
119 self.__finishCallbacks.append(callback) |
119 |
120 |
120 def __initData(self): |
121 def __initData(self): |
121 """ |
122 """ |
122 Private method to (re-)initialize some data. |
123 Private method to (re-)initialize some data. |
123 """ |
124 """ |
124 self.__maxDate = QDate() |
125 self.__maxDate = QDate() |
125 self.__minDate = QDate() |
126 self.__minDate = QDate() |
126 self.__filterLogsEnabled = True |
127 self.__filterLogsEnabled = True |
127 |
128 |
128 self.buf = [] # buffer for stdout |
129 self.buf = [] # buffer for stdout |
129 self.diff = None |
130 self.diff = None |
130 self.__started = False |
131 self.__started = False |
131 self.__lastRev = 0 |
132 self.__lastRev = 0 |
132 |
133 |
133 def closeEvent(self, e): |
134 def closeEvent(self, e): |
134 """ |
135 """ |
135 Protected slot implementing a close event handler. |
136 Protected slot implementing a close event handler. |
136 |
137 |
137 @param e close event (QCloseEvent) |
138 @param e close event (QCloseEvent) |
138 """ |
139 """ |
139 if ( |
140 if ( |
140 self.__process is not None and |
141 self.__process is not None |
141 self.__process.state() != QProcess.ProcessState.NotRunning |
142 and self.__process.state() != QProcess.ProcessState.NotRunning |
142 ): |
143 ): |
143 self.__process.terminate() |
144 self.__process.terminate() |
144 QTimer.singleShot(2000, self.__process.kill) |
145 QTimer.singleShot(2000, self.__process.kill) |
145 self.__process.waitForFinished(3000) |
146 self.__process.waitForFinished(3000) |
146 |
147 |
147 self.__position = self.pos() |
148 self.__position = self.pos() |
148 |
149 |
149 e.accept() |
150 e.accept() |
150 |
151 |
151 def show(self): |
152 def show(self): |
152 """ |
153 """ |
153 Public slot to show the dialog. |
154 Public slot to show the dialog. |
154 """ |
155 """ |
155 if not self.__position.isNull(): |
156 if not self.__position.isNull(): |
156 self.move(self.__position) |
157 self.move(self.__position) |
157 self.__resetUI() |
158 self.__resetUI() |
158 |
159 |
159 super().show() |
160 super().show() |
160 |
161 |
161 def __resetUI(self): |
162 def __resetUI(self): |
162 """ |
163 """ |
163 Private method to reset the user interface. |
164 Private method to reset the user interface. |
164 """ |
165 """ |
165 self.fromDate.setDate(QDate.currentDate()) |
166 self.fromDate.setDate(QDate.currentDate()) |
166 self.toDate.setDate(QDate.currentDate()) |
167 self.toDate.setDate(QDate.currentDate()) |
167 self.fieldCombo.setCurrentIndex(self.fieldCombo.findText( |
168 self.fieldCombo.setCurrentIndex(self.fieldCombo.findText(self.tr("Message"))) |
168 self.tr("Message"))) |
169 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences("LogLimit")) |
169 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences( |
170 self.stopCheckBox.setChecked( |
170 "LogLimit")) |
171 self.vcs.getPlugin().getPreferences("StopLogOnCopy") |
171 self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences( |
172 ) |
172 "StopLogOnCopy")) |
173 |
173 |
|
174 self.logTree.clear() |
174 self.logTree.clear() |
175 |
175 |
176 self.nextButton.setEnabled(True) |
176 self.nextButton.setEnabled(True) |
177 self.limitSpinBox.setEnabled(True) |
177 self.limitSpinBox.setEnabled(True) |
178 |
178 |
179 def __resizeColumnsLog(self): |
179 def __resizeColumnsLog(self): |
180 """ |
180 """ |
181 Private method to resize the log tree columns. |
181 Private method to resize the log tree columns. |
182 """ |
182 """ |
183 self.logTree.header().resizeSections( |
183 self.logTree.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) |
184 QHeaderView.ResizeMode.ResizeToContents) |
|
185 self.logTree.header().setStretchLastSection(True) |
184 self.logTree.header().setStretchLastSection(True) |
186 |
185 |
187 def __resortLog(self): |
186 def __resortLog(self): |
188 """ |
187 """ |
189 Private method to resort the log tree. |
188 Private method to resort the log tree. |
190 """ |
189 """ |
191 self.logTree.sortItems( |
190 self.logTree.sortItems( |
192 self.logTree.sortColumn(), |
191 self.logTree.sortColumn(), self.logTree.header().sortIndicatorOrder() |
193 self.logTree.header().sortIndicatorOrder()) |
192 ) |
194 |
193 |
195 def __resizeColumnsFiles(self): |
194 def __resizeColumnsFiles(self): |
196 """ |
195 """ |
197 Private method to resize the changed files tree columns. |
196 Private method to resize the changed files tree columns. |
198 """ |
197 """ |
199 self.filesTree.header().resizeSections( |
198 self.filesTree.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) |
200 QHeaderView.ResizeMode.ResizeToContents) |
|
201 self.filesTree.header().setStretchLastSection(True) |
199 self.filesTree.header().setStretchLastSection(True) |
202 |
200 |
203 def __resortFiles(self): |
201 def __resortFiles(self): |
204 """ |
202 """ |
205 Private method to resort the changed files tree. |
203 Private method to resort the changed files tree. |
206 """ |
204 """ |
207 sortColumn = self.filesTree.sortColumn() |
205 sortColumn = self.filesTree.sortColumn() |
|
206 self.filesTree.sortItems(1, self.filesTree.header().sortIndicatorOrder()) |
208 self.filesTree.sortItems( |
207 self.filesTree.sortItems( |
209 1, self.filesTree.header().sortIndicatorOrder()) |
208 sortColumn, self.filesTree.header().sortIndicatorOrder() |
210 self.filesTree.sortItems( |
209 ) |
211 sortColumn, self.filesTree.header().sortIndicatorOrder()) |
210 |
212 |
|
213 def __generateLogItem(self, author, date, message, revision, changedPaths): |
211 def __generateLogItem(self, author, date, message, revision, changedPaths): |
214 """ |
212 """ |
215 Private method to generate a log tree entry. |
213 Private method to generate a log tree entry. |
216 |
214 |
217 @param author author info (string) |
215 @param author author info (string) |
218 @param date date info (string) |
216 @param date date info (string) |
219 @param message text of the log message (list of strings) |
217 @param message text of the log message (list of strings) |
220 @param revision revision info (string) |
218 @param revision revision info (string) |
221 @param changedPaths list of dictionary objects containing |
219 @param changedPaths list of dictionary objects containing |
223 @return reference to the generated item (QTreeWidgetItem) |
221 @return reference to the generated item (QTreeWidgetItem) |
224 """ |
222 """ |
225 msg = [] |
223 msg = [] |
226 for line in message: |
224 for line in message: |
227 msg.append(line.strip()) |
225 msg.append(line.strip()) |
228 |
226 |
229 itm = QTreeWidgetItem(self.logTree) |
227 itm = QTreeWidgetItem(self.logTree) |
230 itm.setData(0, Qt.ItemDataRole.DisplayRole, int(revision)) |
228 itm.setData(0, Qt.ItemDataRole.DisplayRole, int(revision)) |
231 itm.setData(1, Qt.ItemDataRole.DisplayRole, author) |
229 itm.setData(1, Qt.ItemDataRole.DisplayRole, author) |
232 itm.setData(2, Qt.ItemDataRole.DisplayRole, date) |
230 itm.setData(2, Qt.ItemDataRole.DisplayRole, date) |
233 itm.setData(3, Qt.ItemDataRole.DisplayRole, " ".join(msg)) |
231 itm.setData(3, Qt.ItemDataRole.DisplayRole, " ".join(msg)) |
234 |
232 |
235 itm.setData(0, self.__messageRole, message) |
233 itm.setData(0, self.__messageRole, message) |
236 itm.setData(0, self.__changesRole, changedPaths) |
234 itm.setData(0, self.__changesRole, changedPaths) |
237 |
235 |
238 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) |
236 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) |
239 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignLeft) |
237 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignLeft) |
240 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignLeft) |
238 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignLeft) |
241 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignLeft) |
239 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignLeft) |
242 itm.setTextAlignment(4, Qt.AlignmentFlag.AlignLeft) |
240 itm.setTextAlignment(4, Qt.AlignmentFlag.AlignLeft) |
243 |
241 |
244 try: |
242 try: |
245 self.__lastRev = int(revision) |
243 self.__lastRev = int(revision) |
246 except ValueError: |
244 except ValueError: |
247 self.__lastRev = 0 |
245 self.__lastRev = 0 |
248 |
246 |
249 return itm |
247 return itm |
250 |
248 |
251 def __generateFileItem(self, action, path, copyFrom, copyRev): |
249 def __generateFileItem(self, action, path, copyFrom, copyRev): |
252 """ |
250 """ |
253 Private method to generate a changed files tree entry. |
251 Private method to generate a changed files tree entry. |
254 |
252 |
255 @param action indicator for the change action ("A", "D" or "M") |
253 @param action indicator for the change action ("A", "D" or "M") |
256 @param path path of the file in the repository (string) |
254 @param path path of the file in the repository (string) |
257 @param copyFrom path the file was copied from (None, string) |
255 @param copyFrom path the file was copied from (None, string) |
258 @param copyRev revision the file was copied from (None, string) |
256 @param copyRev revision the file was copied from (None, string) |
259 @return reference to the generated item (QTreeWidgetItem) |
257 @return reference to the generated item (QTreeWidgetItem) |
260 """ |
258 """ |
261 itm = QTreeWidgetItem(self.filesTree, [ |
259 itm = QTreeWidgetItem( |
262 self.flags[action], |
260 self.filesTree, |
263 path, |
261 [ |
264 copyFrom, |
262 self.flags[action], |
265 copyRev, |
263 path, |
266 ]) |
264 copyFrom, |
267 |
265 copyRev, |
|
266 ], |
|
267 ) |
|
268 |
268 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignRight) |
269 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignRight) |
269 |
270 |
270 return itm |
271 return itm |
271 |
272 |
272 def __getLogEntries(self, startRev=None): |
273 def __getLogEntries(self, startRev=None): |
273 """ |
274 """ |
274 Private method to retrieve log entries from the repository. |
275 Private method to retrieve log entries from the repository. |
275 |
276 |
276 @param startRev revision number to start from (integer, string) |
277 @param startRev revision number to start from (integer, string) |
277 """ |
278 """ |
278 self.buttonBox.button( |
279 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
279 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
280 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
280 self.buttonBox.button( |
281 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
281 QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
|
282 self.buttonBox.button( |
|
283 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
284 QApplication.processEvents() |
282 QApplication.processEvents() |
285 |
283 |
286 self.intercept = False |
284 self.intercept = False |
287 self.__process.kill() |
285 self.__process.kill() |
288 |
286 |
289 self.buf = [] |
287 self.buf = [] |
290 self.cancelled = False |
288 self.cancelled = False |
291 self.errors.clear() |
289 self.errors.clear() |
292 |
290 |
293 args = [] |
291 args = [] |
294 args.append('log') |
292 args.append("log") |
295 self.vcs.addArguments(args, self.vcs.options['global']) |
293 self.vcs.addArguments(args, self.vcs.options["global"]) |
296 self.vcs.addArguments(args, self.vcs.options['log']) |
294 self.vcs.addArguments(args, self.vcs.options["log"]) |
297 args.append('--verbose') |
295 args.append("--verbose") |
298 args.append('--limit') |
296 args.append("--limit") |
299 args.append('{0:d}'.format(self.limitSpinBox.value())) |
297 args.append("{0:d}".format(self.limitSpinBox.value())) |
300 if startRev is not None: |
298 if startRev is not None: |
301 args.append('--revision') |
299 args.append("--revision") |
302 args.append('{0}:0'.format(startRev)) |
300 args.append("{0}:0".format(startRev)) |
303 if self.stopCheckBox.isChecked(): |
301 if self.stopCheckBox.isChecked(): |
304 args.append('--stop-on-copy') |
302 args.append("--stop-on-copy") |
305 args.append(self.fname) |
303 args.append(self.fname) |
306 |
304 |
307 self.__process.setWorkingDirectory(self.dname) |
305 self.__process.setWorkingDirectory(self.dname) |
308 |
306 |
309 self.inputGroup.setEnabled(True) |
307 self.inputGroup.setEnabled(True) |
310 self.inputGroup.show() |
308 self.inputGroup.show() |
311 |
309 |
312 self.__process.start('svn', args) |
310 self.__process.start("svn", args) |
313 procStarted = self.__process.waitForStarted(5000) |
311 procStarted = self.__process.waitForStarted(5000) |
314 if not procStarted: |
312 if not procStarted: |
315 self.inputGroup.setEnabled(False) |
313 self.inputGroup.setEnabled(False) |
316 self.inputGroup.hide() |
314 self.inputGroup.hide() |
317 EricMessageBox.critical( |
315 EricMessageBox.critical( |
318 self, |
316 self, |
319 self.tr('Process Generation Error'), |
317 self.tr("Process Generation Error"), |
320 self.tr( |
318 self.tr( |
321 'The process {0} could not be started. ' |
319 "The process {0} could not be started. " |
322 'Ensure, that it is in the search path.' |
320 "Ensure, that it is in the search path." |
323 ).format('svn')) |
321 ).format("svn"), |
324 |
322 ) |
|
323 |
325 def start(self, fn, isFile=False): |
324 def start(self, fn, isFile=False): |
326 """ |
325 """ |
327 Public slot to start the svn log command. |
326 Public slot to start the svn log command. |
328 |
327 |
329 @param fn filename to show the log for (string) |
328 @param fn filename to show the log for (string) |
330 @param isFile flag indicating log for a file is to be shown |
329 @param isFile flag indicating log for a file is to be shown |
331 (boolean) |
330 (boolean) |
332 """ |
331 """ |
333 self.sbsCheckBox.setEnabled(isFile) |
332 self.sbsCheckBox.setEnabled(isFile) |
334 self.sbsCheckBox.setVisible(isFile) |
333 self.sbsCheckBox.setVisible(isFile) |
335 |
334 |
336 self.errorGroup.hide() |
335 self.errorGroup.hide() |
337 QApplication.processEvents() |
336 QApplication.processEvents() |
338 |
337 |
339 self.__initData() |
338 self.__initData() |
340 |
339 |
341 self.filename = fn |
340 self.filename = fn |
342 self.dname, self.fname = self.vcs.splitPath(fn) |
341 self.dname, self.fname = self.vcs.splitPath(fn) |
343 |
342 |
344 self.activateWindow() |
343 self.activateWindow() |
345 self.raise_() |
344 self.raise_() |
346 |
345 |
347 self.logTree.clear() |
346 self.logTree.clear() |
348 self.__started = True |
347 self.__started = True |
349 self.__getLogEntries() |
348 self.__getLogEntries() |
350 |
349 |
351 def __procFinished(self, exitCode, exitStatus): |
350 def __procFinished(self, exitCode, exitStatus): |
352 """ |
351 """ |
353 Private slot connected to the finished signal. |
352 Private slot connected to the finished signal. |
354 |
353 |
355 @param exitCode exit code of the process (integer) |
354 @param exitCode exit code of the process (integer) |
356 @param exitStatus exit status of the process (QProcess.ExitStatus) |
355 @param exitStatus exit status of the process (QProcess.ExitStatus) |
357 """ |
356 """ |
358 self.__processBuffer() |
357 self.__processBuffer() |
359 self.__finish() |
358 self.__finish() |
360 |
359 |
361 def __finish(self): |
360 def __finish(self): |
362 """ |
361 """ |
363 Private slot called when the process finished or the user pressed the |
362 Private slot called when the process finished or the user pressed the |
364 button. |
363 button. |
365 """ |
364 """ |
366 if ( |
365 if ( |
367 self.__process is not None and |
366 self.__process is not None |
368 self.__process.state() != QProcess.ProcessState.NotRunning |
367 and self.__process.state() != QProcess.ProcessState.NotRunning |
369 ): |
368 ): |
370 self.__process.terminate() |
369 self.__process.terminate() |
371 QTimer.singleShot(2000, self.__process.kill) |
370 QTimer.singleShot(2000, self.__process.kill) |
372 self.__process.waitForFinished(3000) |
371 self.__process.waitForFinished(3000) |
373 |
372 |
374 self.buttonBox.button( |
373 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
375 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
374 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
376 self.buttonBox.button( |
375 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
377 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
376 |
378 self.buttonBox.button( |
|
379 QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
380 |
|
381 self.inputGroup.setEnabled(False) |
377 self.inputGroup.setEnabled(False) |
382 self.inputGroup.hide() |
378 self.inputGroup.hide() |
383 |
379 |
384 while self.__finishCallbacks: |
380 while self.__finishCallbacks: |
385 self.__finishCallbacks.pop(0)() |
381 self.__finishCallbacks.pop(0)() |
386 |
382 |
387 def __processBuffer(self): |
383 def __processBuffer(self): |
388 """ |
384 """ |
389 Private method to process the buffered output of the svn log command. |
385 Private method to process the buffered output of the svn log command. |
390 """ |
386 """ |
391 noEntries = 0 |
387 noEntries = 0 |
392 log = {"message": []} |
388 log = {"message": []} |
393 changedPaths = [] |
389 changedPaths = [] |
394 for s in self.buf: |
390 for s in self.buf: |
395 match = ( |
391 match = ( |
396 self.rx_rev1.fullmatch(s) or |
392 self.rx_rev1.fullmatch(s) |
397 self.rx_rev2.fullmatch(s) or |
393 or self.rx_rev2.fullmatch(s) |
398 self.rx_flags1.fullmatch(s) or |
394 or self.rx_flags1.fullmatch(s) |
399 self.rx_flags2.fullmatch(s) or |
395 or self.rx_flags2.fullmatch(s) |
400 self.rx_sep1.fullmatch(s) or |
396 or self.rx_sep1.fullmatch(s) |
401 self.rx_sep2.fullmatch(s) |
397 or self.rx_sep2.fullmatch(s) |
402 ) |
398 ) |
403 if match is None: |
399 if match is None: |
404 if s.strip().endswith(":") or not s.strip(): |
400 if s.strip().endswith(":") or not s.strip(): |
405 continue |
401 continue |
406 else: |
402 else: |
414 log["revision"] = match.group(1) |
410 log["revision"] = match.group(1) |
415 log["author"] = match.group(2) |
411 log["author"] = match.group(2) |
416 log["date"] = " ".join(match.group(3).split()[:2]) |
412 log["date"] = " ".join(match.group(3).split()[:2]) |
417 # number of lines is ignored |
413 # number of lines is ignored |
418 elif match.re is self.rx_flags1: |
414 elif match.re is self.rx_flags1: |
419 changedPaths.append({ |
415 changedPaths.append( |
420 "action": match.group(1).strip(), |
416 { |
421 "path": match.group(2).strip(), |
417 "action": match.group(1).strip(), |
422 "copyfrom_path": match.group(3).strip(), |
418 "path": match.group(2).strip(), |
423 "copyfrom_revision": match.group(4).strip(), |
419 "copyfrom_path": match.group(3).strip(), |
424 }) |
420 "copyfrom_revision": match.group(4).strip(), |
|
421 } |
|
422 ) |
425 elif match.re is self.rx_flags2: |
423 elif match.re is self.rx_flags2: |
426 changedPaths.append({ |
424 changedPaths.append( |
427 "action": match.group(1).strip(), |
425 { |
428 "path": match.group(2).strip(), |
426 "action": match.group(1).strip(), |
429 "copyfrom_path": "", |
427 "path": match.group(2).strip(), |
430 "copyfrom_revision": "", |
428 "copyfrom_path": "", |
431 }) |
429 "copyfrom_revision": "", |
432 elif ( |
430 } |
433 (match.re is self.rx_sep1 or match.re is self.rx_sep2) and |
431 ) |
434 len(log) > 1 |
432 elif (match.re is self.rx_sep1 or match.re is self.rx_sep2) and len( |
435 ): |
433 log |
|
434 ) > 1: |
436 self.__generateLogItem( |
435 self.__generateLogItem( |
437 log["author"], log["date"], log["message"], |
436 log["author"], |
438 log["revision"], changedPaths) |
437 log["date"], |
|
438 log["message"], |
|
439 log["revision"], |
|
440 changedPaths, |
|
441 ) |
439 dt = QDate.fromString(log["date"], Qt.DateFormat.ISODate) |
442 dt = QDate.fromString(log["date"], Qt.DateFormat.ISODate) |
440 if ( |
443 if not self.__maxDate.isValid() and not self.__minDate.isValid(): |
441 not self.__maxDate.isValid() and |
|
442 not self.__minDate.isValid() |
|
443 ): |
|
444 self.__maxDate = dt |
444 self.__maxDate = dt |
445 self.__minDate = dt |
445 self.__minDate = dt |
446 else: |
446 else: |
447 if self.__maxDate < dt: |
447 if self.__maxDate < dt: |
448 self.__maxDate = dt |
448 self.__maxDate = dt |
449 if self.__minDate > dt: |
449 if self.__minDate > dt: |
450 self.__minDate = dt |
450 self.__minDate = dt |
451 noEntries += 1 |
451 noEntries += 1 |
452 log = {"message": []} |
452 log = {"message": []} |
453 changedPaths = [] |
453 changedPaths = [] |
454 |
454 |
455 self.__resizeColumnsLog() |
455 self.__resizeColumnsLog() |
456 self.__resortLog() |
456 self.__resortLog() |
457 |
457 |
458 if self.__started: |
458 if self.__started: |
459 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
459 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) |
460 self.__started = False |
460 self.__started = False |
461 |
461 |
462 if noEntries < self.limitSpinBox.value() and not self.cancelled: |
462 if noEntries < self.limitSpinBox.value() and not self.cancelled: |
463 self.nextButton.setEnabled(False) |
463 self.nextButton.setEnabled(False) |
464 self.limitSpinBox.setEnabled(False) |
464 self.limitSpinBox.setEnabled(False) |
465 |
465 |
466 self.__filterLogsEnabled = False |
466 self.__filterLogsEnabled = False |
467 self.fromDate.setMinimumDate(self.__minDate) |
467 self.fromDate.setMinimumDate(self.__minDate) |
468 self.fromDate.setMaximumDate(self.__maxDate) |
468 self.fromDate.setMaximumDate(self.__maxDate) |
469 self.fromDate.setDate(self.__minDate) |
469 self.fromDate.setDate(self.__minDate) |
470 self.toDate.setMinimumDate(self.__minDate) |
470 self.toDate.setMinimumDate(self.__minDate) |
471 self.toDate.setMaximumDate(self.__maxDate) |
471 self.toDate.setMaximumDate(self.__maxDate) |
472 self.toDate.setDate(self.__maxDate) |
472 self.toDate.setDate(self.__maxDate) |
473 self.__filterLogsEnabled = True |
473 self.__filterLogsEnabled = True |
474 self.__filterLogs() |
474 self.__filterLogs() |
475 |
475 |
476 def __readStdout(self): |
476 def __readStdout(self): |
477 """ |
477 """ |
478 Private slot to handle the readyReadStandardOutput signal. |
478 Private slot to handle the readyReadStandardOutput signal. |
479 |
479 |
480 It reads the output of the process and inserts it into a buffer. |
480 It reads the output of the process and inserts it into a buffer. |
481 """ |
481 """ |
482 self.__process.setReadChannel(QProcess.ProcessChannel.StandardOutput) |
482 self.__process.setReadChannel(QProcess.ProcessChannel.StandardOutput) |
483 |
483 |
484 while self.__process.canReadLine(): |
484 while self.__process.canReadLine(): |
485 line = str(self.__process.readLine(), |
485 line = str( |
486 Preferences.getSystem("IOEncoding"), |
486 self.__process.readLine(), |
487 'replace') |
487 Preferences.getSystem("IOEncoding"), |
|
488 "replace", |
|
489 ) |
488 self.buf.append(line) |
490 self.buf.append(line) |
489 |
491 |
490 def __readStderr(self): |
492 def __readStderr(self): |
491 """ |
493 """ |
492 Private slot to handle the readyReadStandardError signal. |
494 Private slot to handle the readyReadStandardError signal. |
493 |
495 |
494 It reads the error output of the process and inserts it into the |
496 It reads the error output of the process and inserts it into the |
495 error pane. |
497 error pane. |
496 """ |
498 """ |
497 if self.__process is not None: |
499 if self.__process is not None: |
498 self.errorGroup.show() |
500 self.errorGroup.show() |
499 s = str(self.__process.readAllStandardError(), |
501 s = str( |
500 Preferences.getSystem("IOEncoding"), |
502 self.__process.readAllStandardError(), |
501 'replace') |
503 Preferences.getSystem("IOEncoding"), |
|
504 "replace", |
|
505 ) |
502 self.errors.insertPlainText(s) |
506 self.errors.insertPlainText(s) |
503 self.errors.ensureCursorVisible() |
507 self.errors.ensureCursorVisible() |
504 |
508 |
505 def __diffRevisions(self, rev1, rev2): |
509 def __diffRevisions(self, rev1, rev2): |
506 """ |
510 """ |
507 Private method to do a diff of two revisions. |
511 Private method to do a diff of two revisions. |
508 |
512 |
509 @param rev1 first revision number (integer) |
513 @param rev1 first revision number (integer) |
510 @param rev2 second revision number (integer) |
514 @param rev2 second revision number (integer) |
511 """ |
515 """ |
512 if self.sbsCheckBox.isEnabled() and self.sbsCheckBox.isChecked(): |
516 if self.sbsCheckBox.isEnabled() and self.sbsCheckBox.isChecked(): |
513 self.vcs.vcsSbsDiff(self.filename, |
517 self.vcs.vcsSbsDiff(self.filename, revisions=(str(rev1), str(rev2))) |
514 revisions=(str(rev1), str(rev2))) |
|
515 else: |
518 else: |
516 if self.diff is None: |
519 if self.diff is None: |
517 from .SvnDiffDialog import SvnDiffDialog |
520 from .SvnDiffDialog import SvnDiffDialog |
|
521 |
518 self.diff = SvnDiffDialog(self.vcs) |
522 self.diff = SvnDiffDialog(self.vcs) |
519 self.diff.show() |
523 self.diff.show() |
520 self.diff.raise_() |
524 self.diff.raise_() |
521 self.diff.start(self.filename, [rev1, rev2]) |
525 self.diff.start(self.filename, [rev1, rev2]) |
522 |
526 |
523 def on_buttonBox_clicked(self, button): |
527 def on_buttonBox_clicked(self, button): |
524 """ |
528 """ |
525 Private slot called by a button of the button box clicked. |
529 Private slot called by a button of the button box clicked. |
526 |
530 |
527 @param button button that was clicked (QAbstractButton) |
531 @param button button that was clicked (QAbstractButton) |
528 """ |
532 """ |
529 if button == self.buttonBox.button( |
533 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
530 QDialogButtonBox.StandardButton.Close |
|
531 ): |
|
532 self.close() |
534 self.close() |
533 elif button == self.buttonBox.button( |
535 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): |
534 QDialogButtonBox.StandardButton.Cancel |
|
535 ): |
|
536 self.cancelled = True |
536 self.cancelled = True |
537 self.__finish() |
537 self.__finish() |
538 |
538 |
539 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
539 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
540 def on_logTree_currentItemChanged(self, current, previous): |
540 def on_logTree_currentItemChanged(self, current, previous): |
541 """ |
541 """ |
542 Private slot called, when the current item of the log tree changes. |
542 Private slot called, when the current item of the log tree changes. |
543 |
543 |
544 @param current reference to the new current item (QTreeWidgetItem) |
544 @param current reference to the new current item (QTreeWidgetItem) |
545 @param previous reference to the old current item (QTreeWidgetItem) |
545 @param previous reference to the old current item (QTreeWidgetItem) |
546 """ |
546 """ |
547 if current is not None: |
547 if current is not None: |
548 self.messageEdit.clear() |
548 self.messageEdit.clear() |
549 for line in current.data(0, self.__messageRole): |
549 for line in current.data(0, self.__messageRole): |
550 self.messageEdit.append(line.strip()) |
550 self.messageEdit.append(line.strip()) |
551 |
551 |
552 self.filesTree.clear() |
552 self.filesTree.clear() |
553 changes = current.data(0, self.__changesRole) |
553 changes = current.data(0, self.__changesRole) |
554 if len(changes) > 0: |
554 if len(changes) > 0: |
555 for change in changes: |
555 for change in changes: |
556 self.__generateFileItem( |
556 self.__generateFileItem( |
557 change["action"], change["path"], |
557 change["action"], |
|
558 change["path"], |
558 change["copyfrom_path"], |
559 change["copyfrom_path"], |
559 change["copyfrom_revision"]) |
560 change["copyfrom_revision"], |
|
561 ) |
560 self.__resizeColumnsFiles() |
562 self.__resizeColumnsFiles() |
561 self.__resortFiles() |
563 self.__resortFiles() |
562 |
564 |
563 self.diffPreviousButton.setEnabled( |
565 self.diffPreviousButton.setEnabled( |
564 current != self.logTree.topLevelItem( |
566 current != self.logTree.topLevelItem(self.logTree.topLevelItemCount() - 1) |
565 self.logTree.topLevelItemCount() - 1)) |
567 ) |
566 |
568 |
567 # Highlight the current entry using a bold font |
569 # Highlight the current entry using a bold font |
568 for col in range(self.logTree.columnCount()): |
570 for col in range(self.logTree.columnCount()): |
569 current and current.setFont(col, self.__logTreeBoldFont) |
571 current and current.setFont(col, self.__logTreeBoldFont) |
570 previous and previous.setFont(col, self.__logTreeNormalFont) |
572 previous and previous.setFont(col, self.__logTreeNormalFont) |
571 |
573 |
572 # set the state of the up and down buttons |
574 # set the state of the up and down buttons |
573 self.upButton.setEnabled( |
575 self.upButton.setEnabled( |
574 current is not None and |
576 current is not None and self.logTree.indexOfTopLevelItem(current) > 0 |
575 self.logTree.indexOfTopLevelItem(current) > 0) |
577 ) |
576 self.downButton.setEnabled( |
578 self.downButton.setEnabled(current is not None and int(current.text(0)) > 1) |
577 current is not None and |
579 |
578 int(current.text(0)) > 1) |
|
579 |
|
580 @pyqtSlot() |
580 @pyqtSlot() |
581 def on_logTree_itemSelectionChanged(self): |
581 def on_logTree_itemSelectionChanged(self): |
582 """ |
582 """ |
583 Private slot called, when the selection has changed. |
583 Private slot called, when the selection has changed. |
584 """ |
584 """ |
585 self.diffRevisionsButton.setEnabled( |
585 self.diffRevisionsButton.setEnabled(len(self.logTree.selectedItems()) == 2) |
586 len(self.logTree.selectedItems()) == 2) |
586 |
587 |
|
588 @pyqtSlot() |
587 @pyqtSlot() |
589 def on_nextButton_clicked(self): |
588 def on_nextButton_clicked(self): |
590 """ |
589 """ |
591 Private slot to handle the Next button. |
590 Private slot to handle the Next button. |
592 """ |
591 """ |
593 if self.__lastRev > 1: |
592 if self.__lastRev > 1: |
594 self.__getLogEntries(self.__lastRev - 1) |
593 self.__getLogEntries(self.__lastRev - 1) |
595 |
594 |
596 @pyqtSlot() |
595 @pyqtSlot() |
597 def on_diffPreviousButton_clicked(self): |
596 def on_diffPreviousButton_clicked(self): |
598 """ |
597 """ |
599 Private slot to handle the Diff to Previous button. |
598 Private slot to handle the Diff to Previous button. |
600 """ |
599 """ |
601 itm = self.logTree.currentItem() |
600 itm = self.logTree.currentItem() |
602 if itm is None: |
601 if itm is None: |
603 self.diffPreviousButton.setEnabled(False) |
602 self.diffPreviousButton.setEnabled(False) |
604 return |
603 return |
605 rev2 = int(itm.text(0)) |
604 rev2 = int(itm.text(0)) |
606 |
605 |
607 itm = self.logTree.topLevelItem( |
606 itm = self.logTree.topLevelItem(self.logTree.indexOfTopLevelItem(itm) + 1) |
608 self.logTree.indexOfTopLevelItem(itm) + 1) |
|
609 if itm is None: |
607 if itm is None: |
610 self.diffPreviousButton.setEnabled(False) |
608 self.diffPreviousButton.setEnabled(False) |
611 return |
609 return |
612 rev1 = int(itm.text(0)) |
610 rev1 = int(itm.text(0)) |
613 |
611 |
614 self.__diffRevisions(rev1, rev2) |
612 self.__diffRevisions(rev1, rev2) |
615 |
613 |
616 @pyqtSlot() |
614 @pyqtSlot() |
617 def on_diffRevisionsButton_clicked(self): |
615 def on_diffRevisionsButton_clicked(self): |
618 """ |
616 """ |
619 Private slot to handle the Compare Revisions button. |
617 Private slot to handle the Compare Revisions button. |
620 """ |
618 """ |
621 items = self.logTree.selectedItems() |
619 items = self.logTree.selectedItems() |
622 if len(items) != 2: |
620 if len(items) != 2: |
623 self.diffRevisionsButton.setEnabled(False) |
621 self.diffRevisionsButton.setEnabled(False) |
624 return |
622 return |
625 |
623 |
626 rev2 = int(items[0].text(0)) |
624 rev2 = int(items[0].text(0)) |
627 rev1 = int(items[1].text(0)) |
625 rev1 = int(items[1].text(0)) |
628 |
626 |
629 self.__diffRevisions(min(rev1, rev2), max(rev1, rev2)) |
627 self.__diffRevisions(min(rev1, rev2), max(rev1, rev2)) |
630 |
628 |
631 @pyqtSlot(QDate) |
629 @pyqtSlot(QDate) |
632 def on_fromDate_dateChanged(self, date): |
630 def on_fromDate_dateChanged(self, date): |
633 """ |
631 """ |
634 Private slot called, when the from date changes. |
632 Private slot called, when the from date changes. |
635 |
633 |
636 @param date new date (QDate) |
634 @param date new date (QDate) |
637 """ |
635 """ |
638 self.__filterLogs() |
636 self.__filterLogs() |
639 |
637 |
640 @pyqtSlot(QDate) |
638 @pyqtSlot(QDate) |
641 def on_toDate_dateChanged(self, date): |
639 def on_toDate_dateChanged(self, date): |
642 """ |
640 """ |
643 Private slot called, when the from date changes. |
641 Private slot called, when the from date changes. |
644 |
642 |
645 @param date new date (QDate) |
643 @param date new date (QDate) |
646 """ |
644 """ |
647 self.__filterLogs() |
645 self.__filterLogs() |
648 |
646 |
649 @pyqtSlot(int) |
647 @pyqtSlot(int) |
650 def on_fieldCombo_activated(self, index): |
648 def on_fieldCombo_activated(self, index): |
651 """ |
649 """ |
652 Private slot called, when a new filter field is selected. |
650 Private slot called, when a new filter field is selected. |
653 |
651 |
654 @param index index of the selected entry |
652 @param index index of the selected entry |
655 @type int |
653 @type int |
656 """ |
654 """ |
657 self.__filterLogs() |
655 self.__filterLogs() |
658 |
656 |
659 @pyqtSlot(str) |
657 @pyqtSlot(str) |
660 def on_rxEdit_textChanged(self, txt): |
658 def on_rxEdit_textChanged(self, txt): |
661 """ |
659 """ |
662 Private slot called, when a filter expression is entered. |
660 Private slot called, when a filter expression is entered. |
663 |
661 |
664 @param txt filter expression (string) |
662 @param txt filter expression (string) |
665 """ |
663 """ |
666 self.__filterLogs() |
664 self.__filterLogs() |
667 |
665 |
668 def __filterLogs(self): |
666 def __filterLogs(self): |
669 """ |
667 """ |
670 Private method to filter the log entries. |
668 Private method to filter the log entries. |
671 """ |
669 """ |
672 if self.__filterLogsEnabled: |
670 if self.__filterLogsEnabled: |
678 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
676 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
679 elif txt == self.tr("Revision"): |
677 elif txt == self.tr("Revision"): |
680 fieldIndex = 0 |
678 fieldIndex = 0 |
681 txt = self.rxEdit.text() |
679 txt = self.rxEdit.text() |
682 if txt.startswith("^"): |
680 if txt.startswith("^"): |
683 searchRx = re.compile( |
681 searchRx = re.compile(r"^\s*{0}".format(txt[1:]), re.IGNORECASE) |
684 r"^\s*{0}".format(txt[1:]), re.IGNORECASE) |
|
685 else: |
682 else: |
686 searchRx = re.compile(txt, re.IGNORECASE) |
683 searchRx = re.compile(txt, re.IGNORECASE) |
687 else: |
684 else: |
688 fieldIndex = 3 |
685 fieldIndex = 3 |
689 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
686 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) |
690 |
687 |
691 currentItem = self.logTree.currentItem() |
688 currentItem = self.logTree.currentItem() |
692 for topIndex in range(self.logTree.topLevelItemCount()): |
689 for topIndex in range(self.logTree.topLevelItemCount()): |
693 topItem = self.logTree.topLevelItem(topIndex) |
690 topItem = self.logTree.topLevelItem(topIndex) |
694 if ( |
691 if ( |
695 topItem.text(2) <= to_ and |
692 topItem.text(2) <= to_ |
696 topItem.text(2) >= from_ and |
693 and topItem.text(2) >= from_ |
697 searchRx.match(topItem.text(fieldIndex)) is not None |
694 and searchRx.match(topItem.text(fieldIndex)) is not None |
698 ): |
695 ): |
699 topItem.setHidden(False) |
696 topItem.setHidden(False) |
700 if topItem is currentItem: |
697 if topItem is currentItem: |
701 self.on_logTree_currentItemChanged(topItem, None) |
698 self.on_logTree_currentItemChanged(topItem, None) |
702 else: |
699 else: |
703 topItem.setHidden(True) |
700 topItem.setHidden(True) |
704 if topItem is currentItem: |
701 if topItem is currentItem: |
705 self.messageEdit.clear() |
702 self.messageEdit.clear() |
706 self.filesTree.clear() |
703 self.filesTree.clear() |
707 |
704 |
708 @pyqtSlot(bool) |
705 @pyqtSlot(bool) |
709 def on_stopCheckBox_clicked(self, checked): |
706 def on_stopCheckBox_clicked(self, checked): |
710 """ |
707 """ |
711 Private slot called, when the stop on copy/move checkbox is clicked. |
708 Private slot called, when the stop on copy/move checkbox is clicked. |
712 |
709 |
713 @param checked flag indicating the checked state (boolean) |
710 @param checked flag indicating the checked state (boolean) |
714 """ |
711 """ |
715 self.vcs.getPlugin().setPreferences("StopLogOnCopy", |
712 self.vcs.getPlugin().setPreferences( |
716 self.stopCheckBox.isChecked()) |
713 "StopLogOnCopy", self.stopCheckBox.isChecked() |
|
714 ) |
717 self.nextButton.setEnabled(True) |
715 self.nextButton.setEnabled(True) |
718 self.limitSpinBox.setEnabled(True) |
716 self.limitSpinBox.setEnabled(True) |
719 |
717 |
720 @pyqtSlot() |
718 @pyqtSlot() |
721 def on_upButton_clicked(self): |
719 def on_upButton_clicked(self): |
722 """ |
720 """ |
723 Private slot to move the current item up one entry. |
721 Private slot to move the current item up one entry. |
724 """ |
722 """ |
725 itm = self.logTree.itemAbove(self.logTree.currentItem()) |
723 itm = self.logTree.itemAbove(self.logTree.currentItem()) |
726 if itm: |
724 if itm: |
727 self.logTree.setCurrentItem(itm) |
725 self.logTree.setCurrentItem(itm) |
728 |
726 |
729 @pyqtSlot() |
727 @pyqtSlot() |
730 def on_downButton_clicked(self): |
728 def on_downButton_clicked(self): |
731 """ |
729 """ |
732 Private slot to move the current item down one entry. |
730 Private slot to move the current item down one entry. |
733 """ |
731 """ |