1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2010 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the VCS project browser helper for Mercurial.
8"""
9
10import os
11
12from PyQt5.QtWidgets import QMenu, QDialog
13
14from E5Gui.E5Application import e5App
15
16from Project.ProjectBrowserModel import ProjectBrowserFileItem
17
18from VCS.ProjectBrowserHelper import VcsProjectBrowserHelper
19
20import UI.PixmapCache
21
22
23class HgProjectBrowserHelper(VcsProjectBrowserHelper):
24    """
25    Class implementing the VCS project browser helper for Mercurial.
26    """
27    def __init__(self, vcsObject, browserObject, projectObject,
28                 isTranslationsBrowser, parent=None, name=None):
29        """
30        Constructor
31
32        @param vcsObject reference to the vcs object
33        @param browserObject reference to the project browser object
34        @param projectObject reference to the project object
35        @param isTranslationsBrowser flag indicating, the helper is requested
36            for the translations browser (this needs some special treatment)
37        @param parent parent widget (QWidget)
38        @param name name of this object (string)
39        """
40        VcsProjectBrowserHelper.__init__(self, vcsObject, browserObject,
41                                         projectObject, isTranslationsBrowser,
42                                         parent, name)
43
44        # instantiate the extensions
45        from .ShelveExtension.ProjectBrowserHelper import (
46            ShelveProjectBrowserHelper
47        )
48        from .LargefilesExtension.ProjectBrowserHelper import (
49            LargefilesProjectBrowserHelper
50        )
51        self.__extensions = {
52            "shelve": ShelveProjectBrowserHelper(
53                vcsObject, browserObject, projectObject),
54            "largefiles": LargefilesProjectBrowserHelper(
55                vcsObject, browserObject, projectObject),
56        }
57
58        self.__extensionMenuTitles = {}
59        for extension in self.__extensions:
60            self.__extensionMenuTitles[
61                self.__extensions[extension].menuTitle()] = extension
62        self.__extensionMenus = {}
63        for extension in self.__extensions:
64            self.__extensionMenus[extension] = (
65                self.__extensions[extension].initMenus()
66            )
67
68    def __showExtensionMenu(self, key, controlled):
69        """
70        Private slot showing the extensions menu.
71
72        @param key menu key (string, one of 'mainMenu', 'multiMenu',
73            'backMenu', 'dirMenu' or 'dirMultiMenu')
74        @param controlled flag indicating to show the menu for a
75            version controlled entry or a non-version controlled entry
76            (boolean)
77        """
78        for extensionName in self.__extensionMenus:
79            if key in self.__extensionMenus[extensionName]:
80                self.__extensionMenus[extensionName][key].setEnabled(
81                    self.vcs.isExtensionActive(extensionName))
82                if self.__extensionMenus[extensionName][key].isEnabled():
83                    # adjust individual extension menu entries
84                    self.__extensions[extensionName].showExtensionMenu(
85                        key, controlled)
86                if (not self.__extensionMenus[extensionName][key]
87                    .isEnabled() and self.__extensionMenus[extensionName][key]
88                        .isTearOffMenuVisible()):
89                    self.__extensionMenus[extensionName][key].hideTearOffMenu()
90
91    def showContextMenu(self, menu, standardItems):
92        """
93        Public slot called before the context menu is shown.
94
95        It enables/disables the VCS menu entries depending on the overall
96        VCS status and the file status.
97
98        @param menu reference to the menu to be shown
99        @param standardItems array of standard items that need
100            activation/deactivation depending on the overall VCS status
101        """
102        if self.browser.currentItem().data(1) == self.vcs.vcsName():
103            controlled = True
104            for act in self.vcsMenuActions:
105                act.setEnabled(True)
106            for act in self.vcsAddMenuActions:
107                act.setEnabled(False)
108            for act in standardItems:
109                act.setEnabled(False)
110            if not hasattr(self.browser.currentItem(), 'fileName'):
111                self.annotateAct.setEnabled(False)
112        else:
113            controlled = False
114            for act in self.vcsMenuActions:
115                act.setEnabled(False)
116            for act in self.vcsAddMenuActions:
117                act.setEnabled(True)
118            for act in standardItems:
119                act.setEnabled(True)
120        self.__showExtensionMenu("mainMenu", controlled)
121
122    def showContextMenuMulti(self, menu, standardItems):
123        """
124        Public slot called before the context menu (multiple selections) is
125        shown.
126
127        It enables/disables the VCS menu entries depending on the overall
128        VCS status and the files status.
129
130        @param menu reference to the menu to be shown
131        @param standardItems array of standard items that need
132            activation/deactivation depending on the overall VCS status
133        """
134        vcsName = self.vcs.vcsName()
135        items = self.browser.getSelectedItems()
136        vcsItems = 0
137        # determine number of selected items under VCS control
138        for itm in items:
139            if itm.data(1) == vcsName:
140                vcsItems += 1
141
142        if vcsItems > 0:
143            controlled = True
144            if vcsItems != len(items):
145                for act in self.vcsMultiMenuActions:
146                    act.setEnabled(False)
147            else:
148                for act in self.vcsMultiMenuActions:
149                    act.setEnabled(True)
150            for act in self.vcsAddMultiMenuActions:
151                act.setEnabled(False)
152            for act in standardItems:
153                act.setEnabled(False)
154        else:
155            controlled = False
156            for act in self.vcsMultiMenuActions:
157                act.setEnabled(False)
158            for act in self.vcsAddMultiMenuActions:
159                act.setEnabled(True)
160            for act in standardItems:
161                act.setEnabled(True)
162        self.__showExtensionMenu("multiMenu", controlled)
163
164    def showContextMenuDir(self, menu, standardItems):
165        """
166        Public slot called before the context menu is shown.
167
168        It enables/disables the VCS menu entries depending on the overall
169        VCS status and the directory status.
170
171        @param menu reference to the menu to be shown
172        @param standardItems array of standard items that need
173            activation/deactivation depending on the overall VCS status
174        """
175        if self.browser.currentItem().data(1) == self.vcs.vcsName():
176            controlled = True
177            for act in self.vcsDirMenuActions:
178                act.setEnabled(True)
179            for act in self.vcsAddDirMenuActions:
180                act.setEnabled(False)
181            for act in standardItems:
182                act.setEnabled(False)
183        else:
184            controlled = False
185            for act in self.vcsDirMenuActions:
186                act.setEnabled(False)
187            for act in self.vcsAddDirMenuActions:
188                act.setEnabled(True)
189            for act in standardItems:
190                act.setEnabled(True)
191        self.__showExtensionMenu("dirMenu", controlled)
192
193    def showContextMenuDirMulti(self, menu, standardItems):
194        """
195        Public slot called before the context menu is shown.
196
197        It enables/disables the VCS menu entries depending on the overall
198        VCS status and the directory status.
199
200        @param menu reference to the menu to be shown
201        @param standardItems array of standard items that need
202            activation/deactivation depending on the overall VCS status
203        """
204        vcsName = self.vcs.vcsName()
205        items = self.browser.getSelectedItems()
206        vcsItems = 0
207        # determine number of selected items under VCS control
208        for itm in items:
209            if itm.data(1) == vcsName:
210                vcsItems += 1
211
212        if vcsItems > 0:
213            controlled = True
214            if vcsItems != len(items):
215                for act in self.vcsDirMultiMenuActions:
216                    act.setEnabled(False)
217            else:
218                for act in self.vcsDirMultiMenuActions:
219                    act.setEnabled(True)
220            for act in self.vcsAddDirMultiMenuActions:
221                act.setEnabled(False)
222            for act in standardItems:
223                act.setEnabled(False)
224        else:
225            controlled = False
226            for act in self.vcsDirMultiMenuActions:
227                act.setEnabled(False)
228            for act in self.vcsAddDirMultiMenuActions:
229                act.setEnabled(True)
230            for act in standardItems:
231                act.setEnabled(True)
232        self.__showExtensionMenu("dirMultiMenu", controlled)
233
234    ###########################################################################
235    ## Private menu generation methods below
236    ###########################################################################
237
238    def __addExtensionsMenu(self, menu, key):
239        """
240        Private method to add an extension menu entry.
241
242        @param menu menu to add it to (QMenu)
243        @param key menu key (string, one of 'mainMenu', 'multiMenu',
244            'backMenu', 'dirMenu' or 'dirMultiMenu')
245        @return reference to the menu action (QAction)
246        """
247        act = None
248        if key in ['mainMenu', 'multiMenu', 'backMenu', 'dirMenu',
249                   'dirMultiMenu']:
250            extensionsMenu = QMenu(self.tr("Extensions"), menu)
251            extensionsMenu.setTearOffEnabled(True)
252            for extensionMenuTitle in sorted(self.__extensionMenuTitles):
253                extensionName = self.__extensionMenuTitles[extensionMenuTitle]
254                if key in self.__extensionMenus[extensionName]:
255                    extensionsMenu.addMenu(
256                        self.__extensionMenus[extensionName][key])
257            if not extensionsMenu.isEmpty():
258                if not menu.isEmpty():
259                    menu.addSeparator()
260                act = menu.addMenu(extensionsMenu)
261        return act
262
263    ###########################################################################
264    ## Protected menu generation methods below
265    ###########################################################################
266
267    def _addVCSMenu(self, mainMenu):
268        """
269        Protected method used to add the VCS menu to all project browsers.
270
271        @param mainMenu reference to the menu to be amended
272        """
273        self.vcsMenuActions = []
274        self.vcsAddMenuActions = []
275
276        menu = QMenu(self.tr("Version Control"))
277
278        act = menu.addAction(
279            UI.PixmapCache.getIcon(
280                os.path.join("VcsPlugins", "vcsMercurial", "icons",
281                             "mercurial.svg")),
282            self.vcs.vcsName(), self._VCSInfoDisplay)
283        font = act.font()
284        font.setBold(True)
285        act.setFont(font)
286        menu.addSeparator()
287
288        act = menu.addAction(
289            UI.PixmapCache.getIcon("vcsCommit"),
290            self.tr('Commit changes to repository...'),
291            self._VCSCommit)
292        self.vcsMenuActions.append(act)
293        self.__addExtensionsMenu(menu, 'mainMenu')
294        menu.addSeparator()
295        act = menu.addAction(
296            UI.PixmapCache.getIcon("vcsAdd"),
297            self.tr('Add to repository'),
298            self._VCSAdd)
299        self.vcsAddMenuActions.append(act)
300        act = menu.addAction(
301            UI.PixmapCache.getIcon("vcsRemove"),
302            self.tr('Remove from repository (and disk)'),
303            self._VCSRemove)
304        self.vcsMenuActions.append(act)
305        act = menu.addAction(
306            UI.PixmapCache.getIcon("vcsRemove"),
307            self.tr('Remove from repository only'),
308            self.__HgForget)
309        self.vcsMenuActions.append(act)
310        menu.addSeparator()
311        act = menu.addAction(self.tr('Copy'), self.__HgCopy)
312        self.vcsMenuActions.append(act)
313        act = menu.addAction(self.tr('Move'), self.__HgMove)
314        self.vcsMenuActions.append(act)
315        menu.addSeparator()
316        act = menu.addAction(
317            UI.PixmapCache.getIcon("vcsLog"),
318            self.tr('Show log browser'), self._VCSLogBrowser)
319        self.vcsMenuActions.append(act)
320        menu.addSeparator()
321        act = menu.addAction(
322            UI.PixmapCache.getIcon("vcsStatus"),
323            self.tr('Show status'), self._VCSStatus)
324        self.vcsMenuActions.append(act)
325        menu.addSeparator()
326        act = menu.addAction(
327            UI.PixmapCache.getIcon("vcsDiff"),
328            self.tr('Show differences'), self._VCSDiff)
329        self.vcsMenuActions.append(act)
330        act = menu.addAction(
331            UI.PixmapCache.getIcon("vcsSbsDiff"),
332            self.tr('Show differences side-by-side'), self.__HgSbsDiff)
333        self.vcsMenuActions.append(act)
334        act = menu.addAction(
335            UI.PixmapCache.getIcon("vcsDiff"),
336            self.tr('Show differences (extended)'),
337            self.__HgExtendedDiff)
338        self.vcsMenuActions.append(act)
339        act = menu.addAction(
340            UI.PixmapCache.getIcon("vcsSbsDiff"),
341            self.tr('Show differences side-by-side (extended)'),
342            self.__HgSbsExtendedDiff)
343        self.vcsMenuActions.append(act)
344        self.annotateAct = menu.addAction(
345            self.tr('Show annotated file'),
346            self.__HgAnnotate)
347        self.vcsMenuActions.append(self.annotateAct)
348        menu.addSeparator()
349        act = menu.addAction(
350            UI.PixmapCache.getIcon("vcsRevert"),
351            self.tr('Revert changes'), self.__HgRevert)
352        self.vcsMenuActions.append(act)
353        act = menu.addAction(
354            self.tr('Conflicts resolved'), self.__HgResolved)
355        self.vcsMenuActions.append(act)
356        act = menu.addAction(
357            self.tr('Conflicts unresolved'), self.__HgUnresolved)
358        self.vcsMenuActions.append(act)
359        act = menu.addAction(
360            self.tr('Re-Merge'), self.__HgReMerge)
361        self.vcsMenuActions.append(act)
362        menu.addSeparator()
363        menu.addAction(self.tr('Select all local file entries'),
364                       self.browser.selectLocalEntries)
365        menu.addAction(self.tr('Select all versioned file entries'),
366                       self.browser.selectVCSEntries)
367        menu.addAction(self.tr('Select all local directory entries'),
368                       self.browser.selectLocalDirEntries)
369        menu.addAction(self.tr('Select all versioned directory entries'),
370                       self.browser.selectVCSDirEntries)
371        menu.addSeparator()
372        menu.addAction(self.tr("Configure..."), self.__HgConfigure)
373
374        mainMenu.addSeparator()
375        mainMenu.addMenu(menu)
376        self.menu = menu
377
378    def _addVCSMenuMulti(self, mainMenu):
379        """
380        Protected method used to add the VCS menu for multi selection to all
381        project browsers.
382
383        @param mainMenu reference to the menu to be amended
384        """
385        self.vcsMultiMenuActions = []
386        self.vcsAddMultiMenuActions = []
387
388        menu = QMenu(self.tr("Version Control"))
389
390        act = menu.addAction(
391            UI.PixmapCache.getIcon(
392                os.path.join("VcsPlugins", "vcsMercurial", "icons",
393                             "mercurial.svg")),
394            self.vcs.vcsName(), self._VCSInfoDisplay)
395        font = act.font()
396        font.setBold(True)
397        act.setFont(font)
398        menu.addSeparator()
399
400        act = menu.addAction(
401            UI.PixmapCache.getIcon("vcsCommit"),
402            self.tr('Commit changes to repository...'),
403            self._VCSCommit)
404        self.vcsMultiMenuActions.append(act)
405        self.__addExtensionsMenu(menu, 'multiMenu')
406        menu.addSeparator()
407        act = menu.addAction(
408            UI.PixmapCache.getIcon("vcsAdd"),
409            self.tr('Add to repository'), self._VCSAdd)
410        self.vcsAddMultiMenuActions.append(act)
411        act = menu.addAction(
412            UI.PixmapCache.getIcon("vcsRemove"),
413            self.tr('Remove from repository (and disk)'),
414            self._VCSRemove)
415        self.vcsMultiMenuActions.append(act)
416        act = menu.addAction(
417            UI.PixmapCache.getIcon("vcsRemove"),
418            self.tr('Remove from repository only'),
419            self.__HgForget)
420        self.vcsMultiMenuActions.append(act)
421        menu.addSeparator()
422        act = menu.addAction(
423            UI.PixmapCache.getIcon("vcsStatus"),
424            self.tr('Show status'), self._VCSStatus)
425        self.vcsMultiMenuActions.append(act)
426        menu.addSeparator()
427        act = menu.addAction(
428            UI.PixmapCache.getIcon("vcsDiff"),
429            self.tr('Show differences'), self._VCSDiff)
430        self.vcsMultiMenuActions.append(act)
431        act = menu.addAction(
432            UI.PixmapCache.getIcon("vcsDiff"),
433            self.tr('Show differences (extended)'),
434            self.__HgExtendedDiff)
435        self.vcsMultiMenuActions.append(act)
436        menu.addSeparator()
437        act = menu.addAction(
438            UI.PixmapCache.getIcon("vcsRevert"),
439            self.tr('Revert changes'), self.__HgRevert)
440        self.vcsMultiMenuActions.append(act)
441        act = menu.addAction(
442            self.tr('Conflicts resolved'), self.__HgResolved)
443        self.vcsMultiMenuActions.append(act)
444        act = menu.addAction(
445            self.tr('Conflicts unresolved'), self.__HgUnresolved)
446        self.vcsMultiMenuActions.append(act)
447        act = menu.addAction(
448            self.tr('Re-Merge'), self.__HgReMerge)
449        self.vcsMultiMenuActions.append(act)
450        menu.addSeparator()
451        menu.addAction(self.tr('Select all local file entries'),
452                       self.browser.selectLocalEntries)
453        menu.addAction(self.tr('Select all versioned file entries'),
454                       self.browser.selectVCSEntries)
455        menu.addAction(self.tr('Select all local directory entries'),
456                       self.browser.selectLocalDirEntries)
457        menu.addAction(self.tr('Select all versioned directory entries'),
458                       self.browser.selectVCSDirEntries)
459        menu.addSeparator()
460        menu.addAction(self.tr("Configure..."), self.__HgConfigure)
461
462        mainMenu.addSeparator()
463        mainMenu.addMenu(menu)
464        self.menuMulti = menu
465
466    def _addVCSMenuBack(self, mainMenu):
467        """
468        Protected method used to add the VCS menu to all project browsers.
469
470        @param mainMenu reference to the menu to be amended
471        """
472        menu = QMenu(self.tr("Version Control"))
473
474        act = menu.addAction(
475            UI.PixmapCache.getIcon(
476                os.path.join("VcsPlugins", "vcsMercurial", "icons",
477                             "mercurial.svg")),
478            self.vcs.vcsName(), self._VCSInfoDisplay)
479        font = act.font()
480        font.setBold(True)
481        act.setFont(font)
482        menu.addSeparator()
483
484        menu.addAction(self.tr('Select all local file entries'),
485                       self.browser.selectLocalEntries)
486        menu.addAction(self.tr('Select all versioned file entries'),
487                       self.browser.selectVCSEntries)
488        menu.addAction(self.tr('Select all local directory entries'),
489                       self.browser.selectLocalDirEntries)
490        menu.addAction(self.tr('Select all versioned directory entries'),
491                       self.browser.selectVCSDirEntries)
492        menu.addSeparator()
493        menu.addAction(self.tr("Configure..."), self.__HgConfigure)
494
495        mainMenu.addSeparator()
496        mainMenu.addMenu(menu)
497        self.menuBack = menu
498
499    def _addVCSMenuDir(self, mainMenu):
500        """
501        Protected method used to add the VCS menu to all project browsers.
502
503        @param mainMenu reference to the menu to be amended
504        """
505        if mainMenu is None:
506            return
507
508        self.vcsDirMenuActions = []
509        self.vcsAddDirMenuActions = []
510
511        menu = QMenu(self.tr("Version Control"))
512
513        act = menu.addAction(
514            UI.PixmapCache.getIcon(
515                os.path.join("VcsPlugins", "vcsMercurial", "icons",
516                             "mercurial.svg")),
517            self.vcs.vcsName(), self._VCSInfoDisplay)
518        font = act.font()
519        font.setBold(True)
520        act.setFont(font)
521        menu.addSeparator()
522
523        act = menu.addAction(
524            UI.PixmapCache.getIcon("vcsCommit"),
525            self.tr('Commit changes to repository...'),
526            self._VCSCommit)
527        self.vcsDirMenuActions.append(act)
528        self.__addExtensionsMenu(menu, 'dirMenu')
529        menu.addSeparator()
530        act = menu.addAction(
531            UI.PixmapCache.getIcon("vcsAdd"),
532            self.tr('Add to repository'), self._VCSAdd)
533        self.vcsAddDirMenuActions.append(act)
534        act = menu.addAction(
535            UI.PixmapCache.getIcon("vcsRemove"),
536            self.tr('Remove from repository (and disk)'),
537            self._VCSRemove)
538        self.vcsDirMenuActions.append(act)
539        menu.addSeparator()
540        act = menu.addAction(self.tr('Copy'), self.__HgCopy)
541        self.vcsDirMenuActions.append(act)
542        act = menu.addAction(self.tr('Move'), self.__HgMove)
543        self.vcsDirMenuActions.append(act)
544        menu.addSeparator()
545        act = menu.addAction(
546            UI.PixmapCache.getIcon("vcsLog"),
547            self.tr('Show log browser'), self._VCSLogBrowser)
548        self.vcsDirMenuActions.append(act)
549        menu.addSeparator()
550        act = menu.addAction(
551            UI.PixmapCache.getIcon("vcsStatus"),
552            self.tr('Show status'), self._VCSStatus)
553        self.vcsDirMenuActions.append(act)
554        menu.addSeparator()
555        act = menu.addAction(
556            UI.PixmapCache.getIcon("vcsDiff"),
557            self.tr('Show differences'), self._VCSDiff)
558        self.vcsDirMenuActions.append(act)
559        act = menu.addAction(
560            UI.PixmapCache.getIcon("vcsDiff"),
561            self.tr('Show differences (extended)'),
562            self.__HgExtendedDiff)
563        self.vcsDirMenuActions.append(act)
564        menu.addSeparator()
565        act = menu.addAction(
566            UI.PixmapCache.getIcon("vcsRevert"),
567            self.tr('Revert changes'), self.__HgRevert)
568        self.vcsDirMenuActions.append(act)
569        act = menu.addAction(
570            self.tr('Conflicts resolved'), self.__HgResolved)
571        self.vcsDirMenuActions.append(act)
572        act = menu.addAction(
573            self.tr('Conflicts unresolved'), self.__HgUnresolved)
574        self.vcsDirMenuActions.append(act)
575        act = menu.addAction(
576            self.tr('Re-Merge'), self.__HgReMerge)
577        self.vcsDirMenuActions.append(act)
578        menu.addSeparator()
579        menu.addAction(self.tr('Select all local file entries'),
580                       self.browser.selectLocalEntries)
581        menu.addAction(self.tr('Select all versioned file entries'),
582                       self.browser.selectVCSEntries)
583        menu.addAction(self.tr('Select all local directory entries'),
584                       self.browser.selectLocalDirEntries)
585        menu.addAction(self.tr('Select all versioned directory entries'),
586                       self.browser.selectVCSDirEntries)
587        menu.addSeparator()
588        menu.addAction(self.tr("Configure..."), self.__HgConfigure)
589
590        mainMenu.addSeparator()
591        mainMenu.addMenu(menu)
592        self.menuDir = menu
593
594    def _addVCSMenuDirMulti(self, mainMenu):
595        """
596        Protected method used to add the VCS menu to all project browsers.
597
598        @param mainMenu reference to the menu to be amended
599        """
600        if mainMenu is None:
601            return
602
603        self.vcsDirMultiMenuActions = []
604        self.vcsAddDirMultiMenuActions = []
605
606        menu = QMenu(self.tr("Version Control"))
607
608        act = menu.addAction(
609            UI.PixmapCache.getIcon(
610                os.path.join("VcsPlugins", "vcsMercurial", "icons",
611                             "mercurial.svg")),
612            self.vcs.vcsName(), self._VCSInfoDisplay)
613        font = act.font()
614        font.setBold(True)
615        act.setFont(font)
616        menu.addSeparator()
617
618        act = menu.addAction(
619            UI.PixmapCache.getIcon("vcsCommit"),
620            self.tr('Commit changes to repository...'),
621            self._VCSCommit)
622        self.vcsDirMultiMenuActions.append(act)
623        self.__addExtensionsMenu(menu, 'dirMultiMenu')
624        menu.addSeparator()
625        act = menu.addAction(
626            UI.PixmapCache.getIcon("vcsAdd"),
627            self.tr('Add to repository'), self._VCSAdd)
628        self.vcsAddDirMultiMenuActions.append(act)
629        act = menu.addAction(
630            UI.PixmapCache.getIcon("vcsRemove"),
631            self.tr('Remove from repository (and disk)'),
632            self._VCSRemove)
633        self.vcsDirMultiMenuActions.append(act)
634        menu.addSeparator()
635        act = menu.addAction(
636            UI.PixmapCache.getIcon("vcsStatus"),
637            self.tr('Show status'), self._VCSStatus)
638        self.vcsDirMultiMenuActions.append(act)
639        menu.addSeparator()
640        act = menu.addAction(
641            UI.PixmapCache.getIcon("vcsDiff"),
642            self.tr('Show differences'), self._VCSDiff)
643        self.vcsDirMultiMenuActions.append(act)
644        act = menu.addAction(
645            UI.PixmapCache.getIcon("vcsDiff"),
646            self.tr('Show differences (extended)'),
647            self.__HgExtendedDiff)
648        self.vcsDirMultiMenuActions.append(act)
649        menu.addSeparator()
650        act = menu.addAction(
651            UI.PixmapCache.getIcon("vcsRevert"),
652            self.tr('Revert changes'), self.__HgRevert)
653        self.vcsDirMultiMenuActions.append(act)
654        act = menu.addAction(
655            self.tr('Conflicts resolved'), self.__HgResolved)
656        self.vcsDirMultiMenuActions.append(act)
657        act = menu.addAction(
658            self.tr('Conflicts unresolved'), self.__HgUnresolved)
659        self.vcsDirMultiMenuActions.append(act)
660        act = menu.addAction(
661            self.tr('Re-Merge'), self.__HgReMerge)
662        self.vcsDirMultiMenuActions.append(act)
663        menu.addSeparator()
664        menu.addAction(self.tr('Select all local file entries'),
665                       self.browser.selectLocalEntries)
666        menu.addAction(self.tr('Select all versioned file entries'),
667                       self.browser.selectVCSEntries)
668        menu.addAction(self.tr('Select all local directory entries'),
669                       self.browser.selectLocalDirEntries)
670        menu.addAction(self.tr('Select all versioned directory entries'),
671                       self.browser.selectVCSDirEntries)
672        menu.addSeparator()
673        menu.addAction(self.tr("Configure..."), self.__HgConfigure)
674
675        mainMenu.addSeparator()
676        mainMenu.addMenu(menu)
677        self.menuDirMulti = menu
678
679    ###########################################################################
680    ## Menu handling methods below
681    ###########################################################################
682
683    def __HgRevert(self):
684        """
685        Private slot called by the context menu to revert changes made.
686        """
687        names = []
688        for itm in self.browser.getSelectedItems():
689            try:
690                name = itm.fileName()
691            except AttributeError:
692                name = itm.dirName()
693            names.append(name)
694        self.vcs.hgRevert(names)
695
696    def __HgCopy(self):
697        """
698        Private slot called by the context menu to copy the selected file.
699        """
700        itm = self.browser.currentItem()
701        try:
702            fn = itm.fileName()
703        except AttributeError:
704            fn = itm.dirName()
705        self.vcs.hgCopy(fn, self.project)
706
707    def __HgMove(self):
708        """
709        Private slot called by the context menu to move the selected file.
710        """
711        itm = self.browser.currentItem()
712        try:
713            fn = itm.fileName()
714        except AttributeError:
715            fn = itm.dirName()
716        isFile = os.path.isfile(fn)
717        movefiles = self.browser.project.getFiles(fn)
718        self.browser.project.stopFileSystemMonitoring()
719        if self.vcs.vcsMove(fn, self.project):
720            if isFile:
721                self.browser.closeSourceWindow.emit(fn)
722            else:
723                for mf in movefiles:
724                    self.browser.closeSourceWindow.emit(mf)
725        self.browser.project.startFileSystemMonitoring()
726
727    def __HgExtendedDiff(self):
728        """
729        Private slot called by the context menu to show the difference of a
730        file to the repository.
731
732        This gives the chance to enter the revisions to compare.
733        """
734        names = []
735        for itm in self.browser.getSelectedItems():
736            try:
737                names.append(itm.fileName())
738            except AttributeError:
739                names.append(itm.dirName())
740        self.vcs.hgExtendedDiff(names)
741
742    def __HgSbsDiff(self):
743        """
744        Private slot called by the context menu to show the difference of a
745        file to the repository side-by-side.
746        """
747        itm = self.browser.currentItem()
748        fn = itm.fileName()
749        self.vcs.hgSbsDiff(fn)
750
751    def __HgSbsExtendedDiff(self):
752        """
753        Private slot called by the context menu to show the difference of a
754        file to the repository side-by-side.
755
756        It allows the selection of revisions to compare.
757        """
758        itm = self.browser.currentItem()
759        fn = itm.fileName()
760        self.vcs.hgSbsDiff(fn, extended=True)
761
762    def __HgAnnotate(self):
763        """
764        Private slot called by the context menu to show the annotations of a
765        file.
766        """
767        itm = self.browser.currentItem()
768        fn = itm.fileName()
769        self.vcs.hgAnnotate(fn)
770
771    def __HgResolved(self):
772        """
773        Private slot called by the context menu to mark conflicts of a file
774        as being resolved.
775        """
776        names = []
777        for itm in self.browser.getSelectedItems():
778            try:
779                names.append(itm.fileName())
780            except AttributeError:
781                names.append(itm.dirName())
782        self.vcs.hgResolved(names)
783
784    def __HgUnresolved(self):
785        """
786        Private slot called by the context menu to mark conflicts of a file
787        as being unresolved.
788        """
789        names = []
790        for itm in self.browser.getSelectedItems():
791            try:
792                names.append(itm.fileName())
793            except AttributeError:
794                names.append(itm.dirName())
795        self.vcs.hgResolved(names, unresolve=True)
796
797    def __HgReMerge(self):
798        """
799        Private slot called by the context menu to re-merge a file.
800        """
801        names = []
802        for itm in self.browser.getSelectedItems():
803            try:
804                names.append(itm.fileName())
805            except AttributeError:
806                names.append(itm.dirName())
807        self.vcs.hgReMerge(names)
808
809    def __HgForget(self):
810        """
811        Private slot called by the context menu to remove the selected file
812        from the Mercurial repository leaving a copy in the project directory.
813        """
814        from UI.DeleteFilesConfirmationDialog import (
815            DeleteFilesConfirmationDialog
816        )
817        if self.isTranslationsBrowser:
818            items = self.browser.getSelectedItems([ProjectBrowserFileItem])
819            names = [itm.fileName() for itm in items]
820
821            dlg = DeleteFilesConfirmationDialog(
822                self.parent(),
823                self.tr("Remove from repository only"),
824                self.tr(
825                    "Do you really want to remove these files"
826                    " from the repository?"),
827                names)
828        else:
829            items = self.browser.getSelectedItems()
830            names = [itm.fileName() for itm in items]
831            files = [self.browser.project.getRelativePath(name)
832                     for name in names]
833
834            dlg = DeleteFilesConfirmationDialog(
835                self.parent(),
836                self.tr("Remove from repository only"),
837                self.tr(
838                    "Do you really want to remove these files"
839                    " from the repository?"),
840                files)
841
842        if dlg.exec() == QDialog.DialogCode.Accepted:
843            self.vcs.hgForget(names)
844
845        for fn in names:
846            self._updateVCSStatus(fn)
847
848    def __HgConfigure(self):
849        """
850        Private method to open the configuration dialog.
851        """
852        e5App().getObject("UserInterface").showPreferences("zzz_mercurialPage")
853