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 |
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 |
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 |