Plugins/VcsPlugins/vcsMercurial/HgStatusDialog.py

changeset 5522
e07d1df4c64a
parent 5520
2a97a4d58bc6
child 5587
ea526b78ee6c
equal deleted inserted replaced
5521:37089cbeb1b9 5522:e07d1df4c64a
14 except NameError: 14 except NameError:
15 pass 15 pass
16 16
17 import os 17 import os
18 18
19 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer, QSize 19 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QProcess, QTimer, QSize
20 from PyQt5.QtGui import QTextCursor 20 from PyQt5.QtGui import QTextCursor, QCursor
21 from PyQt5.QtWidgets import QWidget, QDialogButtonBox, QMenu, QHeaderView, \ 21 from PyQt5.QtWidgets import QWidget, QDialogButtonBox, QMenu, QHeaderView, \
22 QTreeWidgetItem, QLineEdit 22 QTreeWidgetItem, QLineEdit, QToolTip
23 23
24 from E5Gui.E5Application import e5App 24 from E5Gui.E5Application import e5App
25 from E5Gui import E5MessageBox 25 from E5Gui import E5MessageBox
26 26
27 from .Ui_HgStatusDialog import Ui_HgStatusDialog 27 from .Ui_HgStatusDialog import Ui_HgStatusDialog
28 28
29 from .HgDiffHighlighter import HgDiffHighlighter 29 from .HgDiffHighlighter import HgDiffHighlighter
30 from .HgDiffGenerator import HgDiffGenerator 30 from .HgDiffGenerator import HgDiffGenerator
31 31
32 import Preferences 32 import Preferences
33 33 import UI.PixmapCache
34 34
35 # TODO: convert action buttons to a tool button with menu and delete status 35
36 # list context menu (i.e. make it the action menu)
37 class HgStatusDialog(QWidget, Ui_HgStatusDialog): 36 class HgStatusDialog(QWidget, Ui_HgStatusDialog):
38 """ 37 """
39 Class implementing a dialog to show the output of the hg status command 38 Class implementing a dialog to show the output of the hg status command
40 process. 39 process.
41 """ 40 """
74 self.process = QProcess() 73 self.process = QProcess()
75 self.process.finished.connect(self.__procFinished) 74 self.process.finished.connect(self.__procFinished)
76 self.process.readyReadStandardOutput.connect(self.__readStdout) 75 self.process.readyReadStandardOutput.connect(self.__readStdout)
77 self.process.readyReadStandardError.connect(self.__readStderr) 76 self.process.readyReadStandardError.connect(self.__readStderr)
78 77
79 self.diffSplitter.setSizes([300, 300])
80
81 self.statusList.headerItem().setText(self.__lastColumn, "") 78 self.statusList.headerItem().setText(self.__lastColumn, "")
82 self.statusList.header().setSortIndicator( 79 self.statusList.header().setSortIndicator(
83 self.__pathColumn, Qt.AscendingOrder) 80 self.__pathColumn, Qt.AscendingOrder)
84 81
85 font = Preferences.getEditorOtherFonts("MonospacedFont") 82 font = Preferences.getEditorOtherFonts("MonospacedFont")
89 self.diffHighlighter = HgDiffHighlighter(self.diffEdit.document()) 86 self.diffHighlighter = HgDiffHighlighter(self.diffEdit.document())
90 self.__diffGenerator = HgDiffGenerator(vcs, self) 87 self.__diffGenerator = HgDiffGenerator(vcs, self)
91 self.__diffGenerator.finished.connect(self.__generatorFinished) 88 self.__diffGenerator.finished.connect(self.__generatorFinished)
92 89
93 self.__selectedName = "" 90 self.__selectedName = ""
94
95 if mq:
96 self.buttonsLine.setVisible(False)
97 self.addButton.setVisible(False)
98 self.diffButton.setVisible(False)
99 self.sbsDiffButton.setVisible(False)
100 self.revertButton.setVisible(False)
101 self.forgetButton.setVisible(False)
102 self.restoreButton.setVisible(False)
103
104 self.diffEdit.setVisible(False)
105
106 self.menuactions = []
107 self.lfActions = []
108 self.menu = QMenu()
109 if not mq:
110 self.__commitAct = self.menu.addAction(
111 self.tr("Commit changes to repository..."), self.__commit)
112 self.menuactions.append(self.__commitAct)
113 self.menuactions.append(self.menu.addAction(
114 self.tr("Select all for commit"), self.__commitSelectAll))
115 self.menuactions.append(self.menu.addAction(
116 self.tr("Deselect all from commit"),
117 self.__commitDeselectAll))
118 self.menu.addSeparator()
119 self.__addAct = self.menu.addAction(
120 self.tr("Add to repository"), self.__add)
121 self.menuactions.append(self.__addAct)
122 self.lfActions.append(self.menu.addAction(
123 self.tr("Add as Large File"),
124 lambda: self.__lfAdd("large")))
125 self.lfActions.append(self.menu.addAction(
126 self.tr("Add as Normal File"),
127 lambda: self.__lfAdd("normal")))
128 self.menu.addSeparator()
129 self.__diffAct = self.menu.addAction(
130 self.tr("Show differences"), self.__diff)
131 self.menuactions.append(self.__diffAct)
132 self.__sbsDiffAct = self.menu.addAction(
133 self.tr("Show differences side-by-side"), self.__sbsDiff)
134 self.menuactions.append(self.__sbsDiffAct)
135 self.menu.addSeparator()
136 self.__revertAct = self.menu.addAction(
137 self.tr("Revert changes"), self.__revert)
138 self.menuactions.append(self.__revertAct)
139 self.__forgetAct = self.menu.addAction(
140 self.tr("Forget missing"), self.__forget)
141 self.menuactions.append(self.__forgetAct)
142 self.__restoreAct = self.menu.addAction(
143 self.tr("Restore missing"), self.__restoreMissing)
144 self.menuactions.append(self.__restoreAct)
145 self.menu.addSeparator()
146 self.menuactions.append(self.menu.addAction(
147 self.tr("Adjust column sizes"), self.__resizeColumns))
148 for act in self.menuactions:
149 act.setEnabled(False)
150 for act in self.lfActions:
151 act.setEnabled(False)
152
153 self.statusList.setContextMenuPolicy(Qt.CustomContextMenu)
154 self.statusList.customContextMenuRequested.connect(
155 self.__showContextMenu)
156
157 if not mq:
158 self.__lfAddActions = []
159 self.__addButtonMenu = QMenu()
160 self.__addButtonMenu.addAction(self.tr("Add"), self.__add)
161 self.__lfAddActions.append(
162 self.__addButtonMenu.addAction(self.tr("Add as Large File"),
163 lambda: self.__lfAdd("large")))
164 self.__lfAddActions.append(
165 self.__addButtonMenu.addAction(self.tr("Add as Normal File"),
166 lambda: self.__lfAdd("normal")))
167 self.__addButtonMenu.aboutToShow.connect(self.__showAddMenu)
168 if self.vcs.isExtensionActive("largefiles"):
169 self.addButton.setMenu(self.__addButtonMenu)
170
171 if not mq:
172 self.vcs.activeExtensionsChanged.connect(
173 self.__activeExtensionsChanged)
174 91
175 self.modifiedIndicators = [ 92 self.modifiedIndicators = [
176 self.tr('added'), 93 self.tr('added'),
177 self.tr('modified'), 94 self.tr('modified'),
178 self.tr('removed'), 95 self.tr('removed'),
193 'M': self.tr('modified'), 110 'M': self.tr('modified'),
194 'R': self.tr('removed'), 111 'R': self.tr('removed'),
195 '?': self.tr('not tracked'), 112 '?': self.tr('not tracked'),
196 '!': self.tr('missing'), 113 '!': self.tr('missing'),
197 } 114 }
115
116 self.__initActionsMenu()
117
118 if mq:
119 self.diffLabel.setVisible(False)
120 self.diffEdit.setVisible(False)
121 self.actionsButton.setEnabled(False)
122 self.diffSplitter.setSizes([600, 0])
123 else:
124 self.diffSplitter.setSizes([300, 300])
125
126 def __initActionsMenu(self):
127 """
128 Private method to initialize the actions menu.
129 """
130 self.__actionsMenu = QMenu()
131 self.__actionsMenu.setTearOffEnabled(True)
132 if qVersion() >= "5.1.0":
133 self.__actionsMenu.setToolTipsVisible(True)
134 else:
135 self.__actionsMenu.hovered.connect(self.__actionsMenuHovered)
136 self.__actionsMenu.aboutToShow.connect(self.__showActionsMenu)
137
138 self.__commitAct = self.__actionsMenu.addAction(
139 self.tr("Commit"), self.__commit)
140 self.__commitAct.setToolTip(self.tr("Commit the selected changes"))
141 self.__commitSelectAct = self.__actionsMenu.addAction(
142 self.tr("Select all for commit"), self.__commitSelectAll)
143 self.__commitDeselectAct = self.__actionsMenu.addAction(
144 self.tr("Unselect all from commit"), self.__commitDeselectAll)
145
146 self.__actionsMenu.addSeparator()
147
148 self.__addAct = self.__actionsMenu.addAction(
149 self.tr("Add"), self.__add)
150 self.__addAct.setToolTip(self.tr("Add the selected files"))
151 self.__lfAddLargeAct = self.__actionsMenu.addAction(
152 self.tr("Add as Large Files"), lambda: self.__lfAdd("large"))
153 self.__lfAddLargeAct.setToolTip(self.tr(
154 "Add the selected files as a large files using the 'Large Files'"
155 " extension"))
156 self.__lfAddNormalAct = self.__actionsMenu.addAction(
157 self.tr("Add as Normal Files"), lambda: self.__lfAdd("normal"))
158 self.__lfAddNormalAct.setToolTip(self.tr(
159 "Add the selected files as a normal files using the 'Large Files'"
160 " extension"))
161
162 self.__actionsMenu.addSeparator()
163
164 self.__diffAct = self.__actionsMenu.addAction(
165 self.tr("Differences"), self.__diff)
166 self.__diffAct.setToolTip(self.tr(
167 "Shows the differences of the selected entry in a"
168 " separate dialog"))
169 self.__sbsDiffAct = self.__actionsMenu.addAction(
170 self.tr("Differences Side-By-Side"), self.__sbsDiff)
171 self.__sbsDiffAct.setToolTip(self.tr(
172 "Shows the differences of the selected entry side-by-side in"
173 " a separate dialog"))
174
175 self.__actionsMenu.addSeparator()
176
177 self.__revertAct = self.__actionsMenu.addAction(
178 self.tr("Revert"), self.__revert)
179 self.__revertAct.setToolTip(self.tr(
180 "Reverts the changes of the selected files"))
181
182 self.__actionsMenu.addSeparator()
183
184 self.__forgetAct = self.__actionsMenu.addAction(
185 self.tr("Forget missing"), self.__forget)
186 self.__forgetAct.setToolTip(self.tr(
187 "Forgets about the selected missing files"))
188 self.__restoreAct = self.__actionsMenu.addAction(
189 self.tr("Restore missing"), self.__restoreMissing)
190 self.__restoreAct.setToolTip(self.tr(
191 "Restores the selected missing files"))
192
193 self.__actionsMenu.addSeparator()
194
195 act = self.__actionsMenu.addAction(
196 self.tr("Adjust column sizes"), self.__resizeColumns)
197 act.setToolTip(self.tr(
198 "Adjusts the width of all columns to their contents"))
199
200 self.actionsButton.setIcon(
201 UI.PixmapCache.getIcon("actionsToolButton.png"))
202 self.actionsButton.setMenu(self.__actionsMenu)
203
204 def __actionsMenuHovered(self, action):
205 """
206 Private slot to show the tooltip for an action menu entry.
207
208 @param action action to show tooltip for
209 @type QAction
210 """
211 QToolTip.showText(
212 QCursor.pos(), action.toolTip(),
213 self.__actionsMenu, self.__actionsMenu.actionGeometry(action))
198 214
199 def closeEvent(self, e): 215 def closeEvent(self, e):
200 """ 216 """
201 Protected slot implementing a close event handler. 217 Protected slot implementing a close event handler.
202 218
249 else: 265 else:
250 diffSplitterState = self.vcs.getPlugin().getPreferences( 266 diffSplitterState = self.vcs.getPlugin().getPreferences(
251 "StatusDialogSplitterState") 267 "StatusDialogSplitterState")
252 if diffSplitterState is not None: 268 if diffSplitterState is not None:
253 self.diffSplitter.restoreState(diffSplitterState) 269 self.diffSplitter.restoreState(diffSplitterState)
254
255 def __activeExtensionsChanged(self):
256 """
257 Private slot handling a change in the activated extensions.
258 """
259 if self.vcs.isExtensionActive("largefiles"):
260 if self.addButton.menu() is None:
261 self.addButton.setMenu(self.__addButtonMenu)
262 else:
263 if self.addButton.menu() is not None:
264 self.addButton.setMenu(None)
265 270
266 def __resort(self): 271 def __resort(self):
267 """ 272 """
268 Private method to resort the tree. 273 Private method to resort the tree.
269 """ 274 """
313 """ 318 """
314 self.errorGroup.hide() 319 self.errorGroup.hide()
315 self.intercept = False 320 self.intercept = False
316 self.args = fn 321 self.args = fn
317 322
318 for act in self.menuactions: 323 self.actionsButton.setEnabled(False)
319 act.setEnabled(False)
320 for act in self.lfActions:
321 act.setEnabled(False)
322
323 self.addButton.setEnabled(False)
324 self.commitButton.setEnabled(False)
325 self.diffButton.setEnabled(False)
326 self.sbsDiffButton.setEnabled(False)
327 self.revertButton.setEnabled(False)
328 self.forgetButton.setEnabled(False)
329 self.restoreButton.setEnabled(False)
330 324
331 self.statusFilterCombo.clear() 325 self.statusFilterCombo.clear()
332 self.__statusFilters = [] 326 self.__statusFilters = []
333 self.statusList.clear() 327 self.statusList.clear()
334 328
425 419
426 self.__statusFilters.sort() 420 self.__statusFilters.sort()
427 self.__statusFilters.insert(0, "<{0}>".format(self.tr("all"))) 421 self.__statusFilters.insert(0, "<{0}>".format(self.tr("all")))
428 self.statusFilterCombo.addItems(self.__statusFilters) 422 self.statusFilterCombo.addItems(self.__statusFilters)
429 423
430 for act in self.menuactions: 424 if not self.__mq:
431 act.setEnabled(True) 425 self.actionsButton.setEnabled(True)
432 426
433 self.__resort() 427 self.__resort()
434 self.__resizeColumns() 428 self.__resizeColumns()
435
436 self.__updateButtons()
437 self.__updateCommitButton()
438 429
439 self.__refreshDiff() 430 self.__refreshDiff()
440 431
441 def on_buttonBox_clicked(self, button): 432 def on_buttonBox_clicked(self, button):
442 """ 433 """
576 else: 567 else:
577 self.__selectedName = "" 568 self.__selectedName = ""
578 569
579 self.start(self.args) 570 self.start(self.args)
580 571
581 def __updateButtons(self):
582 """
583 Private method to update the VCS buttons status.
584 """
585 modified = len(self.__getModifiedItems())
586 unversioned = len(self.__getUnversionedItems())
587 missing = len(self.__getMissingItems())
588
589 self.addButton.setEnabled(unversioned)
590 self.diffButton.setEnabled(modified)
591 self.sbsDiffButton.setEnabled(modified == 1)
592 self.revertButton.setEnabled(modified)
593 self.forgetButton.setEnabled(missing)
594 self.restoreButton.setEnabled(missing)
595
596 def __updateCommitButton(self):
597 """
598 Private method to update the Commit button status.
599 """
600 commitable = len(self.__getCommitableItems())
601 self.commitButton.setEnabled(commitable)
602
603 @pyqtSlot(str) 572 @pyqtSlot(str)
604 def on_statusFilterCombo_activated(self, txt): 573 def on_statusFilterCombo_activated(self, txt):
605 """ 574 """
606 Private slot to react to the selection of a status filter. 575 Private slot to react to the selection of a status filter.
607 576
614 else: 583 else:
615 for topIndex in range(self.statusList.topLevelItemCount()): 584 for topIndex in range(self.statusList.topLevelItemCount()):
616 topItem = self.statusList.topLevelItem(topIndex) 585 topItem = self.statusList.topLevelItem(topIndex)
617 topItem.setHidden(topItem.text(self.__statusColumn) != txt) 586 topItem.setHidden(topItem.text(self.__statusColumn) != txt)
618 587
619 @pyqtSlot(QTreeWidgetItem, int)
620 def on_statusList_itemChanged(self, item, column):
621 """
622 Private slot to act upon item changes.
623
624 @param item reference to the changed item (QTreeWidgetItem)
625 @param column index of column that changed (integer)
626 """
627 if column == self.__toBeCommittedColumn:
628 self.__updateCommitButton()
629
630 @pyqtSlot() 588 @pyqtSlot()
631 def on_statusList_itemSelectionChanged(self): 589 def on_statusList_itemSelectionChanged(self):
632 """ 590 """
633 Private slot to act upon changes of selected items. 591 Private slot to act upon changes of selected items.
634 """ 592 """
635 self.__updateButtons()
636 self.__generateDiffs() 593 self.__generateDiffs()
637 594
638 @pyqtSlot()
639 def on_commitButton_clicked(self):
640 """
641 Private slot to handle the press of the Commit button.
642 """
643 self.__commit()
644
645 @pyqtSlot()
646 def on_addButton_clicked(self):
647 """
648 Private slot to handle the press of the Add button.
649 """
650 self.__add()
651
652 @pyqtSlot()
653 def on_diffButton_clicked(self):
654 """
655 Private slot to handle the press of the Differences button.
656 """
657 self.__diff()
658
659 @pyqtSlot()
660 def on_sbsDiffButton_clicked(self):
661 """
662 Private slot to handle the press of the Side-by-Side Diff button.
663 """
664 self.__sbsDiff()
665
666 @pyqtSlot()
667 def on_revertButton_clicked(self):
668 """
669 Private slot to handle the press of the Revert button.
670 """
671 self.__revert()
672
673 @pyqtSlot()
674 def on_forgetButton_clicked(self):
675 """
676 Private slot to handle the press of the Forget button.
677 """
678 self.__forget()
679
680 @pyqtSlot()
681 def on_restoreButton_clicked(self):
682 """
683 Private slot to handle the press of the Restore button.
684 """
685 self.__restoreMissing()
686
687 ########################################################################### 595 ###########################################################################
688 ## Context menu handling methods 596 ## Menu handling methods
689 ########################################################################### 597 ###########################################################################
690 598
691 def __showContextMenu(self, coord): 599 def __showActionsMenu(self):
692 """ 600 """
693 Private slot to show the context menu of the status list. 601 Private slot to prepare the actions button menu before it is shown.
694
695 @param coord the position of the mouse pointer (QPoint)
696 """ 602 """
697 modified = len(self.__getModifiedItems()) 603 modified = len(self.__getModifiedItems())
698 unversioned = len(self.__getUnversionedItems()) 604 unversioned = len(self.__getUnversionedItems())
699 missing = len(self.__getMissingItems()) 605 missing = len(self.__getMissingItems())
700 commitable = len(self.__getCommitableItems()) 606 commitable = len(self.__getCommitableItems())
607 commitableUnselected = len(self.__getCommitableUnselectedItems())
701 608
702 self.__addAct.setEnabled(unversioned) 609 self.__addAct.setEnabled(unversioned)
703 self.__diffAct.setEnabled(modified) 610 self.__diffAct.setEnabled(modified)
704 self.__sbsDiffAct.setEnabled(modified == 1) 611 self.__sbsDiffAct.setEnabled(modified == 1)
705 self.__revertAct.setEnabled(modified) 612 self.__revertAct.setEnabled(modified)
706 self.__forgetAct.setEnabled(missing) 613 self.__forgetAct.setEnabled(missing)
707 self.__restoreAct.setEnabled(missing) 614 self.__restoreAct.setEnabled(missing)
708 self.__commitAct.setEnabled(commitable) 615 self.__commitAct.setEnabled(commitable)
616 self.__commitSelectAct.setEnabled(commitableUnselected)
617 self.__commitDeselectAct.setEnabled(commitable)
709 618
710 if self.vcs.isExtensionActive("largefiles"): 619 if self.vcs.isExtensionActive("largefiles"):
711 enable = len(self.__getUnversionedItems()) > 0 620 enable = bool(unversioned)
712 else: 621 else:
713 enable = False 622 enable = False
714 for act in self.lfActions: 623 self.__lfAddLargeAct.setEnabled(enable)
715 act.setEnabled(enable) 624 self.__lfAddNormalAct.setEnabled(enable)
716 self.menu.popup(self.statusList.mapToGlobal(coord))
717
718 def __showAddMenu(self):
719 """
720 Private slot to prepare the Add button menu before it is shown.
721 """
722 enable = self.vcs.isExtensionActive("largefiles")
723 for act in self.__lfAddActions:
724 act.setEnabled(enable)
725 625
726 def __commit(self): 626 def __commit(self):
727 """ 627 """
728 Private slot to handle the Commit context menu entry. 628 Private slot to handle the Commit context menu entry.
729 """ 629 """
926 itm = self.statusList.topLevelItem(index) 826 itm = self.statusList.topLevelItem(index)
927 if itm.checkState(self.__toBeCommittedColumn) == Qt.Checked: 827 if itm.checkState(self.__toBeCommittedColumn) == Qt.Checked:
928 commitableItems.append(itm) 828 commitableItems.append(itm)
929 return commitableItems 829 return commitableItems
930 830
831 def __getCommitableUnselectedItems(self):
832 """
833 Private method to retrieve all entries the user may commit but hasn't
834 selected.
835
836 @return list of all items, the user has checked
837 """
838 items = []
839 for index in range(self.statusList.topLevelItemCount()):
840 itm = self.statusList.topLevelItem(index)
841 if itm.flags() & Qt.ItemIsUserCheckable and \
842 itm.checkState(self.__toBeCommittedColumn) == Qt.Unchecked:
843 items.append(itm)
844 return items
845
931 def __getModifiedItems(self): 846 def __getModifiedItems(self):
932 """ 847 """
933 Private method to retrieve all entries, that have a modified status. 848 Private method to retrieve all entries, that have a modified status.
934 849
935 @return list of all items with a modified status 850 @return list of all items with a modified status

eric ide

mercurial