src/eric7/Plugins/VcsPlugins/vcsSubversion/SvnStatusDialog.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the output of the svn status command
8 process.
9 """
10
11 import re
12 import os
13
14 from PyQt6.QtCore import pyqtSlot, Qt, QTimer, QProcess
15 from PyQt6.QtWidgets import (
16 QWidget, QHeaderView, QLineEdit, QApplication, QMenu, QDialogButtonBox,
17 QTreeWidgetItem
18 )
19
20 from EricWidgets.EricApplication import ericApp
21 from EricWidgets import EricMessageBox
22
23 from .Ui_SvnStatusDialog import Ui_SvnStatusDialog
24
25 import Preferences
26 from Globals import strToQByteArray
27
28
29 class SvnStatusDialog(QWidget, Ui_SvnStatusDialog):
30 """
31 Class implementing a dialog to show the output of the svn status command
32 process.
33 """
34 def __init__(self, vcs, parent=None):
35 """
36 Constructor
37
38 @param vcs reference to the vcs object
39 @param parent parent widget (QWidget)
40 """
41 super().__init__(parent)
42 self.setupUi(self)
43
44 self.__toBeCommittedColumn = 0
45 self.__changelistColumn = 1
46 self.__statusColumn = 2
47 self.__propStatusColumn = 3
48 self.__lockedColumn = 4
49 self.__historyColumn = 5
50 self.__switchedColumn = 6
51 self.__lockinfoColumn = 7
52 self.__upToDateColumn = 8
53 self.__pathColumn = 12
54 self.__lastColumn = self.statusList.columnCount()
55
56 self.refreshButton = self.buttonBox.addButton(
57 self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole)
58 self.refreshButton.setToolTip(
59 self.tr("Press to refresh the status display"))
60 self.refreshButton.setEnabled(False)
61 self.buttonBox.button(
62 QDialogButtonBox.StandardButton.Close).setEnabled(False)
63 self.buttonBox.button(
64 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
65
66 self.diff = None
67 self.vcs = vcs
68 self.vcs.committed.connect(self.__committed)
69
70 self.process = QProcess()
71 self.process.finished.connect(self.__procFinished)
72 self.process.readyReadStandardOutput.connect(self.__readStdout)
73 self.process.readyReadStandardError.connect(self.__readStderr)
74
75 self.statusList.headerItem().setText(self.__lastColumn, "")
76 self.statusList.header().setSortIndicator(self.__pathColumn,
77 Qt.SortOrder.AscendingOrder)
78 if self.vcs.version < (1, 5, 0):
79 self.statusList.header().hideSection(self.__changelistColumn)
80
81 self.menuactions = []
82 self.menu = QMenu()
83 self.menuactions.append(self.menu.addAction(
84 self.tr("Commit changes to repository..."), self.__commit))
85 self.menuactions.append(self.menu.addAction(
86 self.tr("Select all for commit"), self.__commitSelectAll))
87 self.menuactions.append(self.menu.addAction(
88 self.tr("Deselect all from commit"), self.__commitDeselectAll))
89 self.menu.addSeparator()
90 self.menuactions.append(self.menu.addAction(
91 self.tr("Add to repository"), self.__add))
92 self.menuactions.append(self.menu.addAction(
93 self.tr("Show differences"), self.__diff))
94 self.menuactions.append(self.menu.addAction(
95 self.tr("Show differences side-by-side"), self.__sbsDiff))
96 self.menuactions.append(self.menu.addAction(
97 self.tr("Revert changes"), self.__revert))
98 self.menuactions.append(self.menu.addAction(
99 self.tr("Restore missing"), self.__restoreMissing))
100 if self.vcs.version >= (1, 5, 0):
101 self.menu.addSeparator()
102 self.menuactions.append(self.menu.addAction(
103 self.tr("Add to Changelist"), self.__addToChangelist))
104 self.menuactions.append(self.menu.addAction(
105 self.tr("Remove from Changelist"),
106 self.__removeFromChangelist))
107 if self.vcs.version >= (1, 2, 0):
108 self.menu.addSeparator()
109 self.menuactions.append(self.menu.addAction(
110 self.tr("Lock"), self.__lock))
111 self.menuactions.append(self.menu.addAction(
112 self.tr("Unlock"), self.__unlock))
113 self.menuactions.append(self.menu.addAction(
114 self.tr("Break lock"),
115 self.__breakLock))
116 self.menuactions.append(self.menu.addAction(
117 self.tr("Steal lock"),
118 self.__stealLock))
119 self.menu.addSeparator()
120 self.menuactions.append(self.menu.addAction(
121 self.tr("Adjust column sizes"),
122 self.__resizeColumns))
123 for act in self.menuactions:
124 act.setEnabled(False)
125
126 self.statusList.setContextMenuPolicy(
127 Qt.ContextMenuPolicy.CustomContextMenu)
128 self.statusList.customContextMenuRequested.connect(
129 self.__showContextMenu)
130
131 self.modifiedIndicators = [
132 self.tr('added'),
133 self.tr('deleted'),
134 self.tr('modified'),
135 ]
136
137 self.missingIndicators = [
138 self.tr('missing'),
139 ]
140
141 self.unversionedIndicators = [
142 self.tr('unversioned'),
143 ]
144
145 self.lockedIndicators = [
146 self.tr('locked'),
147 ]
148
149 self.stealBreakLockIndicators = [
150 self.tr('other lock'),
151 self.tr('stolen lock'),
152 self.tr('broken lock'),
153 ]
154
155 self.unlockedIndicators = [
156 self.tr('not locked'),
157 ]
158
159 self.status = {
160 ' ': self.tr('normal'),
161 'A': self.tr('added'),
162 'C': self.tr('conflict'),
163 'D': self.tr('deleted'),
164 'I': self.tr('ignored'),
165 'M': self.tr('modified'),
166 'R': self.tr('replaced'),
167 'X': self.tr('external'),
168 '?': self.tr('unversioned'),
169 '!': self.tr('missing'),
170 '~': self.tr('type error'),
171 }
172 self.propStatus = {
173 ' ': self.tr('normal'),
174 'M': self.tr('modified'),
175 'C': self.tr('conflict'),
176 }
177 self.locked = {
178 ' ': self.tr('no'),
179 'L': self.tr('yes'),
180 }
181 self.history = {
182 ' ': self.tr('no'),
183 '+': self.tr('yes'),
184 }
185 self.switched = {
186 ' ': self.tr('no'),
187 'S': self.tr('yes'),
188 'X': self.tr('external')
189 }
190 self.lockinfo = {
191 ' ': self.tr('not locked'),
192 'K': self.tr('locked'),
193 'O': self.tr('other lock'),
194 'T': self.tr('stolen lock'),
195 'B': self.tr('broken lock'),
196 }
197 self.uptodate = {
198 ' ': self.tr('yes'),
199 '*': self.tr('no'),
200 }
201
202 self.rx_status = re.compile(
203 '(.{8,9})\\s+([0-9-]+)\\s+([0-9?]+)\\s+(\\S+)\\s+(.+)\\s*')
204 # flags (8 or 9 anything), revision, changed rev, author, path
205 self.rx_status2 = re.compile('(.{8,9})\\s+(.+)\\s*')
206 # flags (8 or 9 anything), path
207 self.rx_changelist = re.compile('--- \\S+ .([\\w\\s]+).:\\s+')
208 # three dashes, Changelist (translated), quote,
209 # changelist name, quote, :
210
211 self.__nonverbose = True
212
213 def __resort(self):
214 """
215 Private method to resort the tree.
216 """
217 self.statusList.sortItems(
218 self.statusList.sortColumn(),
219 self.statusList.header().sortIndicatorOrder())
220
221 def __resizeColumns(self):
222 """
223 Private method to resize the list columns.
224 """
225 self.statusList.header().resizeSections(
226 QHeaderView.ResizeMode.ResizeToContents)
227 self.statusList.header().setStretchLastSection(True)
228
229 def __generateItem(self, status, propStatus, locked, history, switched,
230 lockinfo, uptodate, revision, change, author, path):
231 """
232 Private method to generate a status item in the status list.
233
234 @param status status indicator (string)
235 @param propStatus property status indicator (string)
236 @param locked locked indicator (string)
237 @param history history indicator (string)
238 @param switched switched indicator (string)
239 @param lockinfo lock indicator (string)
240 @param uptodate up to date indicator (string)
241 @param revision revision string (string)
242 @param change revision of last change (string)
243 @param author author of the last change (string)
244 @param path path of the file or directory (string)
245 """
246 if (
247 self.__nonverbose and
248 status == " " and
249 propStatus == " " and
250 locked == " " and
251 history == " " and
252 switched == " " and
253 lockinfo == " " and
254 uptodate == " " and
255 self.currentChangelist == ""
256 ):
257 return
258
259 if revision == "":
260 rev = ""
261 else:
262 try:
263 rev = int(revision)
264 except ValueError:
265 rev = revision
266 if change == "":
267 chg = ""
268 else:
269 try:
270 chg = int(change)
271 except ValueError:
272 chg = change
273 statusText = self.status[status]
274
275 itm = QTreeWidgetItem(self.statusList)
276 itm.setData(0, Qt.ItemDataRole.DisplayRole, "")
277 itm.setData(1, Qt.ItemDataRole.DisplayRole, self.currentChangelist)
278 itm.setData(2, Qt.ItemDataRole.DisplayRole, statusText)
279 itm.setData(3, Qt.ItemDataRole.DisplayRole,
280 self.propStatus[propStatus])
281 itm.setData(4, Qt.ItemDataRole.DisplayRole, self.locked[locked])
282 itm.setData(5, Qt.ItemDataRole.DisplayRole, self.history[history])
283 itm.setData(6, Qt.ItemDataRole.DisplayRole, self.switched[switched])
284 itm.setData(7, Qt.ItemDataRole.DisplayRole, self.lockinfo[lockinfo])
285 itm.setData(8, Qt.ItemDataRole.DisplayRole, self.uptodate[uptodate])
286 itm.setData(9, Qt.ItemDataRole.DisplayRole, rev)
287 itm.setData(10, Qt.ItemDataRole.DisplayRole, chg)
288 itm.setData(11, Qt.ItemDataRole.DisplayRole, author)
289 itm.setData(12, Qt.ItemDataRole.DisplayRole, path)
290
291 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignLeft)
292 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignHCenter)
293 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignHCenter)
294 itm.setTextAlignment(4, Qt.AlignmentFlag.AlignHCenter)
295 itm.setTextAlignment(5, Qt.AlignmentFlag.AlignHCenter)
296 itm.setTextAlignment(6, Qt.AlignmentFlag.AlignHCenter)
297 itm.setTextAlignment(7, Qt.AlignmentFlag.AlignHCenter)
298 itm.setTextAlignment(8, Qt.AlignmentFlag.AlignHCenter)
299 itm.setTextAlignment(9, Qt.AlignmentFlag.AlignRight)
300 itm.setTextAlignment(10, Qt.AlignmentFlag.AlignRight)
301 itm.setTextAlignment(11, Qt.AlignmentFlag.AlignLeft)
302 itm.setTextAlignment(12, Qt.AlignmentFlag.AlignLeft)
303
304 if status in "ADM" or propStatus in "M":
305 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable)
306 itm.setCheckState(self.__toBeCommittedColumn,
307 Qt.CheckState.Checked)
308 else:
309 itm.setFlags(itm.flags() & ~Qt.ItemFlag.ItemIsUserCheckable)
310
311 self.hidePropertyStatusColumn = (
312 self.hidePropertyStatusColumn and
313 propStatus == " "
314 )
315 self.hideLockColumns = (
316 self.hideLockColumns and
317 locked == " " and
318 lockinfo == " "
319 )
320 self.hideUpToDateColumn = self.hideUpToDateColumn and uptodate == " "
321 self.hideHistoryColumn = self.hideHistoryColumn and history == " "
322 self.hideSwitchedColumn = self.hideSwitchedColumn and switched == " "
323
324 if statusText not in self.__statusFilters:
325 self.__statusFilters.append(statusText)
326
327 def closeEvent(self, e):
328 """
329 Protected slot implementing a close event handler.
330
331 @param e close event (QCloseEvent)
332 """
333 if (
334 self.process is not None and
335 self.process.state() != QProcess.ProcessState.NotRunning
336 ):
337 self.process.terminate()
338 QTimer.singleShot(2000, self.process.kill)
339 self.process.waitForFinished(3000)
340
341 e.accept()
342
343 def start(self, fn):
344 """
345 Public slot to start the svn status command.
346
347 @param fn filename(s)/directoryname(s) to show the status of
348 (string or list of strings)
349 """
350 self.errorGroup.hide()
351 self.intercept = False
352 self.args = fn
353
354 for act in self.menuactions:
355 act.setEnabled(False)
356
357 self.addButton.setEnabled(False)
358 self.commitButton.setEnabled(False)
359 self.diffButton.setEnabled(False)
360 self.sbsDiffButton.setEnabled(False)
361 self.revertButton.setEnabled(False)
362 self.restoreButton.setEnabled(False)
363
364 self.statusFilterCombo.clear()
365 self.__statusFilters = []
366 self.statusList.clear()
367
368 self.currentChangelist = ""
369 self.changelistFound = False
370
371 self.hidePropertyStatusColumn = True
372 self.hideLockColumns = True
373 self.hideUpToDateColumn = True
374 self.hideHistoryColumn = True
375 self.hideSwitchedColumn = True
376
377 self.process.kill()
378
379 args = []
380 args.append('status')
381 self.vcs.addArguments(args, self.vcs.options['global'])
382 self.vcs.addArguments(args, self.vcs.options['status'])
383 if (
384 '--verbose' not in self.vcs.options['global'] and
385 '--verbose' not in self.vcs.options['status']
386 ):
387 args.append('--verbose')
388 self.__nonverbose = True
389 else:
390 self.__nonverbose = False
391 if (
392 '--show-updates' in self.vcs.options['status'] or
393 '-u' in self.vcs.options['status']
394 ):
395 self.activateWindow()
396 self.raise_()
397 if isinstance(fn, list):
398 self.dname, fnames = self.vcs.splitPathList(fn)
399 self.vcs.addArguments(args, fnames)
400 else:
401 self.dname, fname = self.vcs.splitPath(fn)
402 args.append(fname)
403
404 self.process.setWorkingDirectory(self.dname)
405
406 self.setWindowTitle(self.tr('Subversion Status'))
407
408 self.process.start('svn', args)
409 procStarted = self.process.waitForStarted(5000)
410 if not procStarted:
411 self.inputGroup.setEnabled(False)
412 self.inputGroup.hide()
413 EricMessageBox.critical(
414 self,
415 self.tr('Process Generation Error'),
416 self.tr(
417 'The process {0} could not be started. '
418 'Ensure, that it is in the search path.'
419 ).format('svn'))
420 else:
421 self.buttonBox.button(
422 QDialogButtonBox.StandardButton.Close).setEnabled(False)
423 self.buttonBox.button(
424 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
425 self.buttonBox.button(
426 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
427
428 self.inputGroup.setEnabled(True)
429 self.inputGroup.show()
430 self.refreshButton.setEnabled(False)
431
432 def __finish(self):
433 """
434 Private slot called when the process finished or the user pressed
435 the button.
436 """
437 if (
438 self.process is not None and
439 self.process.state() != QProcess.ProcessState.NotRunning
440 ):
441 self.process.terminate()
442 QTimer.singleShot(2000, self.process.kill)
443 self.process.waitForFinished(3000)
444
445 self.buttonBox.button(
446 QDialogButtonBox.StandardButton.Close).setEnabled(True)
447 self.buttonBox.button(
448 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
449 self.buttonBox.button(
450 QDialogButtonBox.StandardButton.Close).setDefault(True)
451 self.buttonBox.button(
452 QDialogButtonBox.StandardButton.Close).setFocus(
453 Qt.FocusReason.OtherFocusReason)
454
455 self.inputGroup.setEnabled(False)
456 self.inputGroup.hide()
457 self.refreshButton.setEnabled(True)
458
459 self.__statusFilters.sort()
460 self.__statusFilters.insert(0, "<{0}>".format(self.tr("all")))
461 self.statusFilterCombo.addItems(self.__statusFilters)
462
463 for act in self.menuactions:
464 act.setEnabled(True)
465
466 self.__resort()
467 self.__resizeColumns()
468
469 self.statusList.setColumnHidden(self.__changelistColumn,
470 not self.changelistFound)
471 self.statusList.setColumnHidden(self.__propStatusColumn,
472 self.hidePropertyStatusColumn)
473 self.statusList.setColumnHidden(self.__lockedColumn,
474 self.hideLockColumns)
475 self.statusList.setColumnHidden(self.__lockinfoColumn,
476 self.hideLockColumns)
477 self.statusList.setColumnHidden(self.__upToDateColumn,
478 self.hideUpToDateColumn)
479 self.statusList.setColumnHidden(self.__historyColumn,
480 self.hideHistoryColumn)
481 self.statusList.setColumnHidden(self.__switchedColumn,
482 self.hideSwitchedColumn)
483
484 self.__updateButtons()
485 self.__updateCommitButton()
486
487 def on_buttonBox_clicked(self, button):
488 """
489 Private slot called by a button of the button box clicked.
490
491 @param button button that was clicked (QAbstractButton)
492 """
493 if button == self.buttonBox.button(
494 QDialogButtonBox.StandardButton.Close
495 ):
496 self.close()
497 elif button == self.buttonBox.button(
498 QDialogButtonBox.StandardButton.Cancel
499 ):
500 self.__finish()
501 elif button == self.refreshButton:
502 self.on_refreshButton_clicked()
503
504 def __procFinished(self, exitCode, exitStatus):
505 """
506 Private slot connected to the finished signal.
507
508 @param exitCode exit code of the process (integer)
509 @param exitStatus exit status of the process (QProcess.ExitStatus)
510 """
511 self.__finish()
512
513 def __readStdout(self):
514 """
515 Private slot to handle the readyReadStandardOutput signal.
516
517 It reads the output of the process, formats it and inserts it into
518 the contents pane.
519 """
520 if self.process is not None:
521 self.process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
522
523 while self.process.canReadLine():
524 s = str(self.process.readLine(),
525 Preferences.getSystem("IOEncoding"),
526 'replace')
527 match = (
528 self.rx_status.fullmatch(s) or
529 self.rx_status2.fullmatch(s) or
530 self.rx_changelist.fullmatch(s)
531 )
532 if match.re is self.rx_status:
533 flags = match.group(1)
534 rev = match.group(2)
535 change = match.group(3)
536 author = match.group(4)
537 path = match.group(5).strip()
538
539 self.__generateItem(flags[0], flags[1], flags[2], flags[3],
540 flags[4], flags[5], flags[-1], rev,
541 change, author, path)
542 elif match.re is self.rx_status2:
543 flags = match.group(1)
544 path = match.group(2).strip()
545
546 if flags[-1] in self.uptodate:
547 self.__generateItem(flags[0], flags[1], flags[2],
548 flags[3], flags[4], flags[5],
549 flags[-1], "", "", "", path)
550 elif match.re is self.rx_changelist:
551 self.currentChangelist = match.group(1)
552 self.changelistFound = True
553
554 def __readStderr(self):
555 """
556 Private slot to handle the readyReadStandardError signal.
557
558 It reads the error output of the process and inserts it into the
559 error pane.
560 """
561 if self.process is not None:
562 self.errorGroup.show()
563 s = str(self.process.readAllStandardError(),
564 Preferences.getSystem("IOEncoding"),
565 'replace')
566 self.errors.insertPlainText(s)
567 self.errors.ensureCursorVisible()
568
569 def on_passwordCheckBox_toggled(self, isOn):
570 """
571 Private slot to handle the password checkbox toggled.
572
573 @param isOn flag indicating the status of the check box (boolean)
574 """
575 if isOn:
576 self.input.setEchoMode(QLineEdit.EchoMode.Password)
577 else:
578 self.input.setEchoMode(QLineEdit.EchoMode.Normal)
579
580 @pyqtSlot()
581 def on_sendButton_clicked(self):
582 """
583 Private slot to send the input to the subversion process.
584 """
585 inputTxt = self.input.text()
586 inputTxt += os.linesep
587
588 if self.passwordCheckBox.isChecked():
589 self.errors.insertPlainText(os.linesep)
590 self.errors.ensureCursorVisible()
591 else:
592 self.errors.insertPlainText(inputTxt)
593 self.errors.ensureCursorVisible()
594
595 self.process.write(strToQByteArray(inputTxt))
596
597 self.passwordCheckBox.setChecked(False)
598 self.input.clear()
599
600 def on_input_returnPressed(self):
601 """
602 Private slot to handle the press of the return key in the input field.
603 """
604 self.intercept = True
605 self.on_sendButton_clicked()
606
607 def keyPressEvent(self, evt):
608 """
609 Protected slot to handle a key press event.
610
611 @param evt the key press event (QKeyEvent)
612 """
613 if self.intercept:
614 self.intercept = False
615 evt.accept()
616 return
617 super().keyPressEvent(evt)
618
619 @pyqtSlot()
620 def on_refreshButton_clicked(self):
621 """
622 Private slot to refresh the status display.
623 """
624 self.start(self.args)
625
626 def __updateButtons(self):
627 """
628 Private method to update the VCS buttons status.
629 """
630 modified = len(self.__getModifiedItems())
631 unversioned = len(self.__getUnversionedItems())
632 missing = len(self.__getMissingItems())
633
634 self.addButton.setEnabled(unversioned)
635 self.diffButton.setEnabled(modified)
636 self.sbsDiffButton.setEnabled(modified == 1)
637 self.revertButton.setEnabled(modified)
638 self.restoreButton.setEnabled(missing)
639
640 def __updateCommitButton(self):
641 """
642 Private method to update the Commit button status.
643 """
644 commitable = len(self.__getCommitableItems())
645 self.commitButton.setEnabled(commitable)
646
647 @pyqtSlot(int)
648 def on_statusFilterCombo_activated(self, index):
649 """
650 Private slot to react to the selection of a status filter.
651
652 @param index index of the selected entry
653 @type int
654 """
655 txt = self.statusFilterCombo.itemText(index)
656 if txt == "<{0}>".format(self.tr("all")):
657 for topIndex in range(self.statusList.topLevelItemCount()):
658 topItem = self.statusList.topLevelItem(topIndex)
659 topItem.setHidden(False)
660 else:
661 for topIndex in range(self.statusList.topLevelItemCount()):
662 topItem = self.statusList.topLevelItem(topIndex)
663 topItem.setHidden(topItem.text(self.__statusColumn) != txt)
664
665 @pyqtSlot(QTreeWidgetItem, int)
666 def on_statusList_itemChanged(self, item, column):
667 """
668 Private slot to act upon item changes.
669
670 @param item reference to the changed item (QTreeWidgetItem)
671 @param column index of column that changed (integer)
672 """
673 if column == self.__toBeCommittedColumn:
674 self.__updateCommitButton()
675
676 @pyqtSlot()
677 def on_statusList_itemSelectionChanged(self):
678 """
679 Private slot to act upon changes of selected items.
680 """
681 self.__updateButtons()
682
683 @pyqtSlot()
684 def on_commitButton_clicked(self):
685 """
686 Private slot to handle the press of the Commit button.
687 """
688 self.__commit()
689
690 @pyqtSlot()
691 def on_addButton_clicked(self):
692 """
693 Private slot to handle the press of the Add button.
694 """
695 self.__add()
696
697 @pyqtSlot()
698 def on_diffButton_clicked(self):
699 """
700 Private slot to handle the press of the Differences button.
701 """
702 self.__diff()
703
704 @pyqtSlot()
705 def on_sbsDiffButton_clicked(self):
706 """
707 Private slot to handle the press of the Side-by-Side Diff button.
708 """
709 self.__sbsDiff()
710
711 @pyqtSlot()
712 def on_revertButton_clicked(self):
713 """
714 Private slot to handle the press of the Revert button.
715 """
716 self.__revert()
717
718 @pyqtSlot()
719 def on_restoreButton_clicked(self):
720 """
721 Private slot to handle the press of the Restore button.
722 """
723 self.__restoreMissing()
724
725 ###########################################################################
726 ## Context menu handling methods
727 ###########################################################################
728
729 def __showContextMenu(self, coord):
730 """
731 Private slot to show the context menu of the status list.
732
733 @param coord the position of the mouse pointer (QPoint)
734 """
735 self.menu.popup(self.statusList.mapToGlobal(coord))
736
737 def __commit(self):
738 """
739 Private slot to handle the Commit context menu entry.
740 """
741 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
742 for itm in self.__getCommitableItems()]
743 if not names:
744 EricMessageBox.information(
745 self,
746 self.tr("Commit"),
747 self.tr("""There are no entries selected to be"""
748 """ committed."""))
749 return
750
751 if Preferences.getVCS("AutoSaveFiles"):
752 vm = ericApp().getObject("ViewManager")
753 for name in names:
754 vm.saveEditor(name)
755 self.vcs.vcsCommit(names, '')
756
757 def __committed(self):
758 """
759 Private slot called after the commit has finished.
760 """
761 if self.isVisible():
762 self.on_refreshButton_clicked()
763 self.vcs.checkVCSStatus()
764
765 def __commitSelectAll(self):
766 """
767 Private slot to select all entries for commit.
768 """
769 self.__commitSelect(True)
770
771 def __commitDeselectAll(self):
772 """
773 Private slot to deselect all entries from commit.
774 """
775 self.__commitSelect(False)
776
777 def __add(self):
778 """
779 Private slot to handle the Add context menu entry.
780 """
781 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
782 for itm in self.__getUnversionedItems()]
783 if not names:
784 EricMessageBox.information(
785 self,
786 self.tr("Add"),
787 self.tr("""There are no unversioned entries"""
788 """ available/selected."""))
789 return
790
791 self.vcs.vcsAdd(names)
792 self.on_refreshButton_clicked()
793
794 project = ericApp().getObject("Project")
795 for name in names:
796 project.getModel().updateVCSStatus(name)
797 self.vcs.checkVCSStatus()
798
799 def __revert(self):
800 """
801 Private slot to handle the Revert context menu entry.
802 """
803 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
804 for itm in self.__getModifiedItems()]
805 if not names:
806 EricMessageBox.information(
807 self,
808 self.tr("Revert"),
809 self.tr("""There are no uncommitted changes"""
810 """ available/selected."""))
811 return
812
813 self.vcs.vcsRevert(names)
814 self.raise_()
815 self.activateWindow()
816 self.on_refreshButton_clicked()
817
818 project = ericApp().getObject("Project")
819 for name in names:
820 project.getModel().updateVCSStatus(name)
821 self.vcs.checkVCSStatus()
822
823 def __restoreMissing(self):
824 """
825 Private slot to handle the Restore Missing context menu entry.
826 """
827 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
828 for itm in self.__getMissingItems()]
829 if not names:
830 EricMessageBox.information(
831 self,
832 self.tr("Revert"),
833 self.tr("""There are no missing entries"""
834 """ available/selected."""))
835 return
836
837 self.vcs.vcsRevert(names)
838 self.on_refreshButton_clicked()
839 self.vcs.checkVCSStatus()
840
841 def __diff(self):
842 """
843 Private slot to handle the Diff context menu entry.
844 """
845 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
846 for itm in self.__getModifiedItems()]
847 if not names:
848 EricMessageBox.information(
849 self,
850 self.tr("Differences"),
851 self.tr("""There are no uncommitted changes"""
852 """ available/selected."""))
853 return
854
855 if self.diff is None:
856 from .SvnDiffDialog import SvnDiffDialog
857 self.diff = SvnDiffDialog(self.vcs)
858 self.diff.show()
859 QApplication.processEvents()
860 self.diff.start(names, refreshable=True)
861
862 def __sbsDiff(self):
863 """
864 Private slot to handle the Side-by-Side Diff context menu entry.
865 """
866 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
867 for itm in self.__getModifiedItems()]
868 if not names:
869 EricMessageBox.information(
870 self,
871 self.tr("Side-by-Side Diff"),
872 self.tr("""There are no uncommitted changes"""
873 """ available/selected."""))
874 return
875 elif len(names) > 1:
876 EricMessageBox.information(
877 self,
878 self.tr("Side-by-Side Diff"),
879 self.tr("""Only one file with uncommitted changes"""
880 """ must be selected."""))
881 return
882
883 self.vcs.vcsSbsDiff(names[0])
884
885 def __lock(self):
886 """
887 Private slot to handle the Lock context menu entry.
888 """
889 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
890 for itm in self.__getLockActionItems(self.unlockedIndicators)]
891 if not names:
892 EricMessageBox.information(
893 self,
894 self.tr("Lock"),
895 self.tr("""There are no unlocked files"""
896 """ available/selected."""))
897 return
898
899 self.vcs.svnLock(names, parent=self)
900 self.on_refreshButton_clicked()
901
902 def __unlock(self):
903 """
904 Private slot to handle the Unlock context menu entry.
905 """
906 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
907 for itm in self.__getLockActionItems(self.lockedIndicators)]
908 if not names:
909 EricMessageBox.information(
910 self,
911 self.tr("Unlock"),
912 self.tr("""There are no locked files"""
913 """ available/selected."""))
914 return
915
916 self.vcs.svnUnlock(names, parent=self)
917 self.on_refreshButton_clicked()
918
919 def __breakLock(self):
920 """
921 Private slot to handle the Break Lock context menu entry.
922 """
923 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
924 for itm in self.__getLockActionItems(
925 self.stealBreakLockIndicators)]
926 if not names:
927 EricMessageBox.information(
928 self,
929 self.tr("Break Lock"),
930 self.tr("""There are no locked files"""
931 """ available/selected."""))
932 return
933
934 self.vcs.svnUnlock(names, parent=self, breakIt=True)
935 self.on_refreshButton_clicked()
936
937 def __stealLock(self):
938 """
939 Private slot to handle the Break Lock context menu entry.
940 """
941 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
942 for itm in self.__getLockActionItems(
943 self.stealBreakLockIndicators)]
944 if not names:
945 EricMessageBox.information(
946 self,
947 self.tr("Steal Lock"),
948 self.tr("""There are no locked files"""
949 """ available/selected."""))
950 return
951
952 self.vcs.svnLock(names, parent=self, stealIt=True)
953 self.on_refreshButton_clicked()
954
955 def __addToChangelist(self):
956 """
957 Private slot to add entries to a changelist.
958 """
959 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
960 for itm in self.__getNonChangelistItems()]
961 if not names:
962 EricMessageBox.information(
963 self,
964 self.tr("Remove from Changelist"),
965 self.tr(
966 """There are no files available/selected not """
967 """belonging to a changelist."""
968 )
969 )
970 return
971 self.vcs.svnAddToChangelist(names)
972 self.on_refreshButton_clicked()
973
974 def __removeFromChangelist(self):
975 """
976 Private slot to remove entries from their changelists.
977 """
978 names = [os.path.join(self.dname, itm.text(self.__pathColumn))
979 for itm in self.__getChangelistItems()]
980 if not names:
981 EricMessageBox.information(
982 self,
983 self.tr("Remove from Changelist"),
984 self.tr(
985 """There are no files available/selected belonging"""
986 """ to a changelist."""
987 )
988 )
989 return
990 self.vcs.svnRemoveFromChangelist(names)
991 self.on_refreshButton_clicked()
992
993 def __getCommitableItems(self):
994 """
995 Private method to retrieve all entries the user wants to commit.
996
997 @return list of all items, the user has checked
998 """
999 commitableItems = []
1000 for index in range(self.statusList.topLevelItemCount()):
1001 itm = self.statusList.topLevelItem(index)
1002 if (
1003 itm.checkState(self.__toBeCommittedColumn) ==
1004 Qt.CheckState.Checked
1005 ):
1006 commitableItems.append(itm)
1007 return commitableItems
1008
1009 def __getModifiedItems(self):
1010 """
1011 Private method to retrieve all entries, that have a modified status.
1012
1013 @return list of all items with a modified status
1014 """
1015 modifiedItems = []
1016 for itm in self.statusList.selectedItems():
1017 if (
1018 itm.text(self.__statusColumn) in self.modifiedIndicators or
1019 itm.text(self.__propStatusColumn) in self.modifiedIndicators
1020 ):
1021 modifiedItems.append(itm)
1022 return modifiedItems
1023
1024 def __getUnversionedItems(self):
1025 """
1026 Private method to retrieve all entries, that have an unversioned
1027 status.
1028
1029 @return list of all items with an unversioned status
1030 """
1031 unversionedItems = []
1032 for itm in self.statusList.selectedItems():
1033 if itm.text(self.__statusColumn) in self.unversionedIndicators:
1034 unversionedItems.append(itm)
1035 return unversionedItems
1036
1037 def __getMissingItems(self):
1038 """
1039 Private method to retrieve all entries, that have a missing status.
1040
1041 @return list of all items with a missing status
1042 """
1043 missingItems = []
1044 for itm in self.statusList.selectedItems():
1045 if itm.text(self.__statusColumn) in self.missingIndicators:
1046 missingItems.append(itm)
1047 return missingItems
1048
1049 def __getLockActionItems(self, indicators):
1050 """
1051 Private method to retrieve all emtries, that have a locked status.
1052
1053 @param indicators list of indicators to check against (list of strings)
1054 @return list of all items with a locked status
1055 """
1056 lockitems = []
1057 for itm in self.statusList.selectedItems():
1058 if itm.text(self.__lockinfoColumn) in indicators:
1059 lockitems.append(itm)
1060 return lockitems
1061
1062 def __getChangelistItems(self):
1063 """
1064 Private method to retrieve all entries, that are members of
1065 a changelist.
1066
1067 @return list of all items belonging to a changelist
1068 """
1069 clitems = []
1070 for itm in self.statusList.selectedItems():
1071 if itm.text(self.__changelistColumn) != "":
1072 clitems.append(itm)
1073 return clitems
1074
1075 def __getNonChangelistItems(self):
1076 """
1077 Private method to retrieve all entries, that are not members of
1078 a changelist.
1079
1080 @return list of all items not belonging to a changelist
1081 """
1082 clitems = []
1083 for itm in self.statusList.selectedItems():
1084 if itm.text(self.__changelistColumn) == "":
1085 clitems.append(itm)
1086 return clitems
1087
1088 def __commitSelect(self, selected):
1089 """
1090 Private slot to select or deselect all entries.
1091
1092 @param selected commit selection state to be set (boolean)
1093 """
1094 for index in range(self.statusList.topLevelItemCount()):
1095 itm = self.statusList.topLevelItem(index)
1096 if (
1097 itm.flags() & Qt.ItemFlag.ItemIsUserCheckable ==
1098 Qt.ItemFlag.ItemIsUserCheckable
1099 ):
1100 if selected:
1101 itm.setCheckState(self.__toBeCommittedColumn,
1102 Qt.CheckState.Checked)
1103 else:
1104 itm.setCheckState(self.__toBeCommittedColumn,
1105 Qt.CheckState.Unchecked)

eric ide

mercurial