1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2005 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the VCS project browser helper for subversion.
8"""
9
10import os
11
12import pysvn
13
14from PyQt5.QtWidgets import QMenu
15
16from E5Gui.E5Application import e5App
17
18from VCS.ProjectBrowserHelper import VcsProjectBrowserHelper
19
20from Project.ProjectBrowserModel import ProjectBrowserFileItem
21
22import UI.PixmapCache
23
24
25class SvnProjectBrowserHelper(VcsProjectBrowserHelper):
26    """
27    Class implementing the VCS project browser helper for subversion.
28    """
29    def __init__(self, vcsObject, browserObject, projectObject,
30                 isTranslationsBrowser, parent=None, name=None):
31        """
32        Constructor
33
34        @param vcsObject reference to the vcs object
35        @param browserObject reference to the project browser object
36        @param projectObject reference to the project object
37        @param isTranslationsBrowser flag indicating, the helper is requested
38            for the translations browser (this needs some special treatment)
39        @param parent parent widget (QWidget)
40        @param name name of this object (string)
41        """
42        VcsProjectBrowserHelper.__init__(self, vcsObject, browserObject,
43                                         projectObject, isTranslationsBrowser,
44                                         parent, name)
45
46    def showContextMenu(self, menu, standardItems):
47        """
48        Public slot called before the context menu is shown.
49
50        It enables/disables the VCS menu entries depending on the overall
51        VCS status and the file status.
52
53        @param menu reference to the menu to be shown
54        @param standardItems array of standard items that need
55            activation/deactivation depending on the overall VCS status
56        """
57        if self.browser.currentItem().data(1) == self.vcs.vcsName():
58            for act in self.vcsMenuActions:
59                act.setEnabled(True)
60            for act in self.vcsAddMenuActions:
61                act.setEnabled(False)
62            for act in standardItems:
63                act.setEnabled(False)
64            if not hasattr(self.browser.currentItem(), 'fileName'):
65                self.blameAct.setEnabled(False)
66        else:
67            for act in self.vcsMenuActions:
68                act.setEnabled(False)
69            for act in self.vcsAddMenuActions:
70                act.setEnabled(True)
71            if 1 in self.browser.specialMenuEntries:
72                try:
73                    name = self.browser.currentItem().fileName()
74                except AttributeError:
75                    name = self.browser.currentItem().dirName()
76                if not os.path.isdir(name):
77                    self.vcsMenuAddTree.setEnabled(False)
78            for act in standardItems:
79                act.setEnabled(True)
80
81    def showContextMenuMulti(self, menu, standardItems):
82        """
83        Public slot called before the context menu (multiple selections) is
84        shown.
85
86        It enables/disables the VCS menu entries depending on the overall
87        VCS status and the files status.
88
89        @param menu reference to the menu to be shown
90        @param standardItems array of standard items that need
91            activation/deactivation depending on the overall VCS status
92        """
93        vcsName = self.vcs.vcsName()
94        items = self.browser.getSelectedItems()
95        vcsItems = 0
96        # determine number of selected items under VCS control
97        for itm in items:
98            if itm.data(1) == vcsName:
99                vcsItems += 1
100
101        if vcsItems > 0:
102            if vcsItems != len(items):
103                for act in self.vcsMultiMenuActions:
104                    act.setEnabled(False)
105            else:
106                for act in self.vcsMultiMenuActions:
107                    act.setEnabled(True)
108            for act in self.vcsAddMultiMenuActions:
109                act.setEnabled(False)
110            for act in standardItems:
111                act.setEnabled(False)
112        else:
113            for act in self.vcsMultiMenuActions:
114                act.setEnabled(False)
115            for act in self.vcsAddMultiMenuActions:
116                act.setEnabled(True)
117            if (
118                1 in self.browser.specialMenuEntries and
119                self.__itemsHaveFiles(items)
120            ):
121                self.vcsMultiMenuAddTree.setEnabled(False)
122            for act in standardItems:
123                act.setEnabled(True)
124
125    def showContextMenuDir(self, menu, standardItems):
126        """
127        Public slot called before the context menu is shown.
128
129        It enables/disables the VCS menu entries depending on the overall
130        VCS status and the directory status.
131
132        @param menu reference to the menu to be shown
133        @param standardItems array of standard items that need
134            activation/deactivation depending on the overall VCS status
135        """
136        if self.browser.currentItem().data(1) == self.vcs.vcsName():
137            for act in self.vcsDirMenuActions:
138                act.setEnabled(True)
139            for act in self.vcsAddDirMenuActions:
140                act.setEnabled(False)
141            for act in standardItems:
142                act.setEnabled(False)
143        else:
144            for act in self.vcsDirMenuActions:
145                act.setEnabled(False)
146            for act in self.vcsAddDirMenuActions:
147                act.setEnabled(True)
148            for act in standardItems:
149                act.setEnabled(True)
150
151    def showContextMenuDirMulti(self, menu, standardItems):
152        """
153        Public slot called before the context menu is shown.
154
155        It enables/disables the VCS menu entries depending on the overall
156        VCS status and the directory status.
157
158        @param menu reference to the menu to be shown
159        @param standardItems array of standard items that need
160            activation/deactivation depending on the overall VCS status
161        """
162        vcsName = self.vcs.vcsName()
163        items = self.browser.getSelectedItems()
164        vcsItems = 0
165        # determine number of selected items under VCS control
166        for itm in items:
167            if itm.data(1) == vcsName:
168                vcsItems += 1
169
170        if vcsItems > 0:
171            if vcsItems != len(items):
172                for act in self.vcsDirMultiMenuActions:
173                    act.setEnabled(False)
174            else:
175                for act in self.vcsDirMultiMenuActions:
176                    act.setEnabled(True)
177            for act in self.vcsAddDirMultiMenuActions:
178                act.setEnabled(False)
179            for act in standardItems:
180                act.setEnabled(False)
181        else:
182            for act in self.vcsDirMultiMenuActions:
183                act.setEnabled(False)
184            for act in self.vcsAddDirMultiMenuActions:
185                act.setEnabled(True)
186            for act in standardItems:
187                act.setEnabled(True)
188
189    ###########################################################################
190    # Protected menu generation methods below
191    ###########################################################################
192
193    def _addVCSMenu(self, mainMenu):
194        """
195        Protected method used to add the VCS menu to all project browsers.
196
197        @param mainMenu reference to the menu to be amended
198        """
199        self.vcsMenuActions = []
200        self.vcsAddMenuActions = []
201
202        menu = QMenu(self.tr("Version Control"))
203
204        act = menu.addAction(
205            UI.PixmapCache.getIcon(
206                os.path.join("VcsPlugins", "vcsPySvn", "icons", "pysvn.svg")),
207            self.vcs.vcsName(), self._VCSInfoDisplay)
208        font = act.font()
209        font.setBold(True)
210        act.setFont(font)
211        menu.addSeparator()
212
213        act = menu.addAction(
214            UI.PixmapCache.getIcon("vcsUpdate"),
215            self.tr('Update from repository'), self._VCSUpdate)
216        self.vcsMenuActions.append(act)
217        act = menu.addAction(
218            UI.PixmapCache.getIcon("vcsCommit"),
219            self.tr('Commit changes to repository...'),
220            self._VCSCommit)
221        self.vcsMenuActions.append(act)
222        menu.addSeparator()
223        act = menu.addAction(
224            UI.PixmapCache.getIcon("vcsAdd"),
225            self.tr('Add to repository'),
226            self._VCSAdd)
227        self.vcsAddMenuActions.append(act)
228        if 1 in self.browser.specialMenuEntries:
229            self.vcsMenuAddTree = menu.addAction(
230                UI.PixmapCache.getIcon("vcsAdd"),
231                self.tr('Add tree to repository'),
232                self._VCSAddTree)
233            self.vcsAddMenuActions.append(self.vcsMenuAddTree)
234        act = menu.addAction(
235            UI.PixmapCache.getIcon("vcsRemove"),
236            self.tr('Remove from repository (and disk)'),
237            self._VCSRemove)
238        self.vcsMenuActions.append(act)
239        menu.addSeparator()
240        act = menu.addAction(
241            self.tr('Copy'), self.__SVNCopy)
242        self.vcsMenuActions.append(act)
243        act = menu.addAction(self.tr('Move'), self.__SVNMove)
244        self.vcsMenuActions.append(act)
245        if pysvn.svn_version >= (1, 5, 0) and pysvn.version >= (1, 6, 0):
246            menu.addSeparator()
247            act = menu.addAction(
248                self.tr("Add to Changelist"),
249                self.__SVNAddToChangelist)
250            self.vcsMenuActions.append(act)
251            act = menu.addAction(
252                self.tr("Remove from Changelist"),
253                self.__SVNRemoveFromChangelist)
254            self.vcsMenuActions.append(act)
255        menu.addSeparator()
256        act = menu.addAction(
257            UI.PixmapCache.getIcon("vcsLog"),
258            self.tr('Show log browser'), self._VCSLogBrowser)
259        self.vcsMenuActions.append(act)
260        menu.addSeparator()
261        act = menu.addAction(
262            UI.PixmapCache.getIcon("vcsStatus"),
263            self.tr('Show status'), self._VCSStatus)
264        self.vcsMenuActions.append(act)
265        act = menu.addAction(
266            UI.PixmapCache.getIcon("vcsRepo"),
267            self.tr('Show repository info'), self.__SVNInfo)
268        self.vcsMenuActions.append(act)
269        menu.addSeparator()
270        act = menu.addAction(
271            UI.PixmapCache.getIcon("vcsDiff"),
272            self.tr('Show differences'), self._VCSDiff)
273        self.vcsMenuActions.append(act)
274        act = menu.addAction(
275            UI.PixmapCache.getIcon("vcsSbsDiff"),
276            self.tr('Show differences side-by-side'), self.__SVNSbsDiff)
277        self.vcsMenuActions.append(act)
278        act = menu.addAction(
279            UI.PixmapCache.getIcon("vcsDiff"),
280            self.tr('Show differences (extended)'),
281            self.__SVNExtendedDiff)
282        self.vcsMenuActions.append(act)
283        act = menu.addAction(
284            UI.PixmapCache.getIcon("vcsSbsDiff"),
285            self.tr('Show differences side-by-side (extended)'),
286            self.__SVNSbsExtendedDiff)
287        self.vcsMenuActions.append(act)
288        act = menu.addAction(
289            UI.PixmapCache.getIcon("vcsDiff"),
290            self.tr('Show differences (URLs)'),
291            self.__SVNUrlDiff)
292        self.vcsMenuActions.append(act)
293        self.blameAct = menu.addAction(
294            self.tr('Show annotated file'),
295            self.__SVNBlame)
296        self.vcsMenuActions.append(self.blameAct)
297        menu.addSeparator()
298        act = menu.addAction(
299            UI.PixmapCache.getIcon("vcsRevert"),
300            self.tr('Revert changes'), self._VCSRevert)
301        self.vcsMenuActions.append(act)
302        act = menu.addAction(
303            UI.PixmapCache.getIcon("vcsMerge"),
304            self.tr('Merge changes'), self._VCSMerge)
305        self.vcsMenuActions.append(act)
306        act = menu.addAction(
307            self.tr('Conflicts resolved'), self.__SVNResolve)
308        self.vcsMenuActions.append(act)
309        menu.addSeparator()
310        act = menu.addAction(
311            UI.PixmapCache.getIcon("vcsLock"),
312            self.tr('Lock'), self.__SVNLock)
313        self.vcsMenuActions.append(act)
314        act = menu.addAction(
315            UI.PixmapCache.getIcon("vcsUnlock"),
316            self.tr('Unlock'), self.__SVNUnlock)
317        self.vcsMenuActions.append(act)
318        act = menu.addAction(
319            UI.PixmapCache.getIcon("vcsUnlock"),
320            self.tr('Break Lock'), self.__SVNBreakLock)
321        self.vcsMenuActions.append(act)
322        act = menu.addAction(
323            UI.PixmapCache.getIcon("vcsUnlock"),
324            self.tr('Steal Lock'), self.__SVNStealLock)
325        self.vcsMenuActions.append(act)
326        menu.addSeparator()
327        act = menu.addAction(self.tr('Set Property'), self.__SVNSetProp)
328        self.vcsMenuActions.append(act)
329        act = menu.addAction(
330            self.tr('List Properties'), self.__SVNListProps)
331        self.vcsMenuActions.append(act)
332        act = menu.addAction(self.tr('Delete Property'), self.__SVNDelProp)
333        self.vcsMenuActions.append(act)
334        menu.addSeparator()
335        menu.addAction(self.tr('Select all local file entries'),
336                       self.browser.selectLocalEntries)
337        menu.addAction(self.tr('Select all versioned file entries'),
338                       self.browser.selectVCSEntries)
339        menu.addAction(self.tr('Select all local directory entries'),
340                       self.browser.selectLocalDirEntries)
341        menu.addAction(self.tr('Select all versioned directory entries'),
342                       self.browser.selectVCSDirEntries)
343        menu.addSeparator()
344        menu.addAction(self.tr("Configure..."), self.__SVNConfigure)
345
346        mainMenu.addSeparator()
347        mainMenu.addMenu(menu)
348        self.menu = menu
349
350    def _addVCSMenuMulti(self, mainMenu):
351        """
352        Protected method used to add the VCS menu for multi selection to all
353        project browsers.
354
355        @param mainMenu reference to the menu to be amended
356        """
357        self.vcsMultiMenuActions = []
358        self.vcsAddMultiMenuActions = []
359
360        menu = QMenu(self.tr("Version Control"))
361
362        act = menu.addAction(
363            UI.PixmapCache.getIcon(
364                os.path.join("VcsPlugins", "vcsPySvn", "icons", "pysvn.svg")),
365            self.vcs.vcsName(), self._VCSInfoDisplay)
366        font = act.font()
367        font.setBold(True)
368        act.setFont(font)
369        menu.addSeparator()
370
371        act = menu.addAction(
372            UI.PixmapCache.getIcon("vcsUpdate"),
373            self.tr('Update from repository'), self._VCSUpdate)
374        self.vcsMultiMenuActions.append(act)
375        act = menu.addAction(
376            UI.PixmapCache.getIcon("vcsCommit"),
377            self.tr('Commit changes to repository...'),
378            self._VCSCommit)
379        self.vcsMultiMenuActions.append(act)
380        menu.addSeparator()
381        act = menu.addAction(
382            UI.PixmapCache.getIcon("vcsAdd"),
383            self.tr('Add to repository'), self._VCSAdd)
384        self.vcsAddMultiMenuActions.append(act)
385        if 1 in self.browser.specialMenuEntries:
386            self.vcsMultiMenuAddTree = menu.addAction(
387                UI.PixmapCache.getIcon("vcsAdd"),
388                self.tr('Add tree to repository'), self._VCSAddTree)
389            self.vcsAddMultiMenuActions.append(self.vcsMultiMenuAddTree)
390        act = menu.addAction(
391            UI.PixmapCache.getIcon("vcsRemove"),
392            self.tr('Remove from repository (and disk)'),
393            self._VCSRemove)
394        self.vcsMultiMenuActions.append(act)
395        if pysvn.svn_version >= (1, 5, 0) and pysvn.version >= (1, 6, 0):
396            menu.addSeparator()
397            act = menu.addAction(
398                self.tr("Add to Changelist"),
399                self.__SVNAddToChangelist)
400            self.vcsMenuActions.append(act)
401            act = menu.addAction(
402                self.tr("Remove from Changelist"),
403                self.__SVNRemoveFromChangelist)
404            self.vcsMenuActions.append(act)
405        menu.addSeparator()
406        act = menu.addAction(
407            UI.PixmapCache.getIcon("vcsStatus"),
408            self.tr('Show status'), self._VCSStatus)
409        self.vcsMultiMenuActions.append(act)
410        menu.addSeparator()
411        act = menu.addAction(
412            UI.PixmapCache.getIcon("vcsDiff"),
413            self.tr('Show differences'), self._VCSDiff)
414        self.vcsMultiMenuActions.append(act)
415        act = menu.addAction(
416            UI.PixmapCache.getIcon("vcsDiff"),
417            self.tr('Show differences (extended)'),
418            self.__SVNExtendedDiff)
419        self.vcsMultiMenuActions.append(act)
420        act = menu.addAction(
421            UI.PixmapCache.getIcon("vcsDiff"),
422            self.tr('Show differences (URLs)'),
423            self.__SVNUrlDiff)
424        self.vcsMultiMenuActions.append(act)
425        menu.addSeparator()
426        act = menu.addAction(
427            UI.PixmapCache.getIcon("vcsRevert"),
428            self.tr('Revert changes'), self._VCSRevert)
429        self.vcsMultiMenuActions.append(act)
430        act = menu.addAction(
431            self.tr('Conflicts resolved'), self.__SVNResolve)
432        self.vcsMultiMenuActions.append(act)
433        menu.addSeparator()
434        act = menu.addAction(
435            UI.PixmapCache.getIcon("vcsLock"),
436            self.tr('Lock'), self.__SVNLock)
437        self.vcsMultiMenuActions.append(act)
438        act = menu.addAction(
439            UI.PixmapCache.getIcon("vcsUnlock"),
440            self.tr('Unlock'), self.__SVNUnlock)
441        self.vcsMultiMenuActions.append(act)
442        act = menu.addAction(
443            UI.PixmapCache.getIcon("vcsUnlock"),
444            self.tr('Break Lock'), self.__SVNBreakLock)
445        self.vcsMultiMenuActions.append(act)
446        act = menu.addAction(
447            UI.PixmapCache.getIcon("vcsUnlock"),
448            self.tr('Steal Lock'), self.__SVNStealLock)
449        self.vcsMultiMenuActions.append(act)
450        menu.addSeparator()
451        act = menu.addAction(self.tr('Set Property'), self.__SVNSetProp)
452        self.vcsMultiMenuActions.append(act)
453        act = menu.addAction(
454            self.tr('List Properties'), self.__SVNListProps)
455        self.vcsMultiMenuActions.append(act)
456        act = menu.addAction(self.tr('Delete Property'), self.__SVNDelProp)
457        self.vcsMultiMenuActions.append(act)
458        menu.addSeparator()
459        menu.addAction(self.tr('Select all local file entries'),
460                       self.browser.selectLocalEntries)
461        menu.addAction(self.tr('Select all versioned file entries'),
462                       self.browser.selectVCSEntries)
463        menu.addAction(self.tr('Select all local directory entries'),
464                       self.browser.selectLocalDirEntries)
465        menu.addAction(self.tr('Select all versioned directory entries'),
466                       self.browser.selectVCSDirEntries)
467        menu.addSeparator()
468        menu.addAction(self.tr("Configure..."), self.__SVNConfigure)
469
470        mainMenu.addSeparator()
471        mainMenu.addMenu(menu)
472        self.menuMulti = menu
473
474    def _addVCSMenuBack(self, mainMenu):
475        """
476        Protected method used to add the VCS menu to all project browsers.
477
478        @param mainMenu reference to the menu to be amended
479        """
480        menu = QMenu(self.tr("Version Control"))
481
482        act = menu.addAction(
483            UI.PixmapCache.getIcon(
484                os.path.join("VcsPlugins", "vcsPySvn", "icons", "pysvn.svg")),
485            self.vcs.vcsName(), self._VCSInfoDisplay)
486        font = act.font()
487        font.setBold(True)
488        act.setFont(font)
489        menu.addSeparator()
490
491        menu.addAction(self.tr('Select all local file entries'),
492                       self.browser.selectLocalEntries)
493        menu.addAction(self.tr('Select all versioned file entries'),
494                       self.browser.selectVCSEntries)
495        menu.addAction(self.tr('Select all local directory entries'),
496                       self.browser.selectLocalDirEntries)
497        menu.addAction(self.tr('Select all versioned directory entries'),
498                       self.browser.selectVCSDirEntries)
499        menu.addSeparator()
500        menu.addAction(self.tr("Configure..."), self.__SVNConfigure)
501
502        mainMenu.addSeparator()
503        mainMenu.addMenu(menu)
504        self.menuBack = menu
505
506    def _addVCSMenuDir(self, mainMenu):
507        """
508        Protected method used to add the VCS menu to all project browsers.
509
510        @param mainMenu reference to the menu to be amended
511        """
512        if mainMenu is None:
513            return
514
515        self.vcsDirMenuActions = []
516        self.vcsAddDirMenuActions = []
517
518        menu = QMenu(self.tr("Version Control"))
519
520        act = menu.addAction(
521            UI.PixmapCache.getIcon(
522                os.path.join("VcsPlugins", "vcsPySvn", "icons", "pysvn.svg")),
523            self.vcs.vcsName(), self._VCSInfoDisplay)
524        font = act.font()
525        font.setBold(True)
526        act.setFont(font)
527        menu.addSeparator()
528
529        act = menu.addAction(
530            UI.PixmapCache.getIcon("vcsUpdate"),
531            self.tr('Update from repository'), self._VCSUpdate)
532        self.vcsDirMenuActions.append(act)
533        act = menu.addAction(
534            UI.PixmapCache.getIcon("vcsCommit"),
535            self.tr('Commit changes to repository...'),
536            self._VCSCommit)
537        self.vcsDirMenuActions.append(act)
538        menu.addSeparator()
539        act = menu.addAction(
540            UI.PixmapCache.getIcon("vcsAdd"),
541            self.tr('Add to repository'), self._VCSAdd)
542        self.vcsAddDirMenuActions.append(act)
543        act = menu.addAction(
544            UI.PixmapCache.getIcon("vcsRemove"),
545            self.tr('Remove from repository (and disk)'),
546            self._VCSRemove)
547        self.vcsDirMenuActions.append(act)
548        menu.addSeparator()
549        act = menu.addAction(self.tr('Copy'), self.__SVNCopy)
550        self.vcsDirMenuActions.append(act)
551        act = menu.addAction(self.tr('Move'), self.__SVNMove)
552        self.vcsDirMenuActions.append(act)
553        if pysvn.svn_version >= (1, 5, 0) and pysvn.version >= (1, 6, 0):
554            menu.addSeparator()
555            act = menu.addAction(
556                self.tr("Add to Changelist"),
557                self.__SVNAddToChangelist)
558            self.vcsMenuActions.append(act)
559            act = menu.addAction(
560                self.tr("Remove from Changelist"),
561                self.__SVNRemoveFromChangelist)
562            self.vcsMenuActions.append(act)
563        menu.addSeparator()
564        act = menu.addAction(
565            UI.PixmapCache.getIcon("vcsLog"),
566            self.tr('Show log browser'), self._VCSLogBrowser)
567        self.vcsDirMenuActions.append(act)
568        menu.addSeparator()
569        act = menu.addAction(
570            UI.PixmapCache.getIcon("vcsStatus"),
571            self.tr('Show status'), self._VCSStatus)
572        self.vcsDirMenuActions.append(act)
573        act = menu.addAction(
574            UI.PixmapCache.getIcon("vcsRepo"),
575            self.tr('Show repository info'), self.__SVNInfo)
576        self.vcsDirMenuActions.append(act)
577        menu.addSeparator()
578        act = menu.addAction(
579            UI.PixmapCache.getIcon("vcsDiff"),
580            self.tr('Show differences'), self._VCSDiff)
581        self.vcsDirMenuActions.append(act)
582        act = menu.addAction(
583            UI.PixmapCache.getIcon("vcsDiff"),
584            self.tr('Show differences (extended)'),
585            self.__SVNExtendedDiff)
586        self.vcsDirMenuActions.append(act)
587        act = menu.addAction(
588            UI.PixmapCache.getIcon("vcsDiff"),
589            self.tr('Show differences (URLs)'),
590            self.__SVNUrlDiff)
591        self.vcsDirMenuActions.append(act)
592        menu.addSeparator()
593        act = menu.addAction(
594            UI.PixmapCache.getIcon("vcsRevert"),
595            self.tr('Revert changes'), self._VCSRevert)
596        self.vcsDirMenuActions.append(act)
597        act = menu.addAction(
598            UI.PixmapCache.getIcon("vcsMerge"),
599            self.tr('Merge changes'), self._VCSMerge)
600        self.vcsDirMenuActions.append(act)
601        act = menu.addAction(
602            self.tr('Conflicts resolved'), self.__SVNResolve)
603        self.vcsDirMenuActions.append(act)
604        menu.addSeparator()
605        act = menu.addAction(self.tr('Set Property'), self.__SVNSetProp)
606        self.vcsDirMenuActions.append(act)
607        act = menu.addAction(
608            self.tr('List Properties'), self.__SVNListProps)
609        self.vcsDirMenuActions.append(act)
610        act = menu.addAction(self.tr('Delete Property'), self.__SVNDelProp)
611        self.vcsDirMenuActions.append(act)
612        menu.addSeparator()
613        menu.addAction(self.tr('Select all local file entries'),
614                       self.browser.selectLocalEntries)
615        menu.addAction(self.tr('Select all versioned file entries'),
616                       self.browser.selectVCSEntries)
617        menu.addAction(self.tr('Select all local directory entries'),
618                       self.browser.selectLocalDirEntries)
619        menu.addAction(self.tr('Select all versioned directory entries'),
620                       self.browser.selectVCSDirEntries)
621        menu.addSeparator()
622        menu.addAction(self.tr("Configure..."), self.__SVNConfigure)
623
624        mainMenu.addSeparator()
625        mainMenu.addMenu(menu)
626        self.menuDir = menu
627
628    def _addVCSMenuDirMulti(self, mainMenu):
629        """
630        Protected method used to add the VCS menu to all project browsers.
631
632        @param mainMenu reference to the menu to be amended
633        """
634        if mainMenu is None:
635            return
636
637        self.vcsDirMultiMenuActions = []
638        self.vcsAddDirMultiMenuActions = []
639
640        menu = QMenu(self.tr("Version Control"))
641
642        act = menu.addAction(
643            UI.PixmapCache.getIcon(
644                os.path.join("VcsPlugins", "vcsPySvn", "icons", "pysvn.svg")),
645            self.vcs.vcsName(), self._VCSInfoDisplay)
646        font = act.font()
647        font.setBold(True)
648        act.setFont(font)
649        menu.addSeparator()
650
651        act = menu.addAction(
652            UI.PixmapCache.getIcon("vcsUpdate"),
653            self.tr('Update from repository'), self._VCSUpdate)
654        self.vcsDirMultiMenuActions.append(act)
655        act = menu.addAction(
656            UI.PixmapCache.getIcon("vcsCommit"),
657            self.tr('Commit changes to repository...'),
658            self._VCSCommit)
659        self.vcsDirMultiMenuActions.append(act)
660        menu.addSeparator()
661        act = menu.addAction(
662            UI.PixmapCache.getIcon("vcsAdd"),
663            self.tr('Add to repository'), self._VCSAdd)
664        self.vcsAddDirMultiMenuActions.append(act)
665        act = menu.addAction(
666            UI.PixmapCache.getIcon("vcsRemove"),
667            self.tr('Remove from repository (and disk)'),
668            self._VCSRemove)
669        self.vcsDirMultiMenuActions.append(act)
670        if pysvn.svn_version >= (1, 5, 0) and pysvn.version >= (1, 6, 0):
671            menu.addSeparator()
672            act = menu.addAction(
673                self.tr("Add to Changelist"),
674                self.__SVNAddToChangelist)
675            self.vcsMenuActions.append(act)
676            act = menu.addAction(
677                self.tr("Remove from Changelist"),
678                self.__SVNRemoveFromChangelist)
679            self.vcsMenuActions.append(act)
680        menu.addSeparator()
681        act = menu.addAction(
682            UI.PixmapCache.getIcon("vcsStatus"),
683            self.tr('Show status'), self._VCSStatus)
684        self.vcsDirMultiMenuActions.append(act)
685        menu.addSeparator()
686        act = menu.addAction(
687            UI.PixmapCache.getIcon("vcsDiff"),
688            self.tr('Show differences'), self._VCSDiff)
689        self.vcsDirMultiMenuActions.append(act)
690        act = menu.addAction(
691            UI.PixmapCache.getIcon("vcsDiff"),
692            self.tr('Show differences (extended)'),
693            self.__SVNExtendedDiff)
694        self.vcsDirMultiMenuActions.append(act)
695        act = menu.addAction(
696            UI.PixmapCache.getIcon("vcsDiff"),
697            self.tr('Show differences (URLs)'),
698            self.__SVNUrlDiff)
699        self.vcsDirMultiMenuActions.append(act)
700        menu.addSeparator()
701        act = menu.addAction(
702            UI.PixmapCache.getIcon("vcsRevert"),
703            self.tr('Revert changes'), self._VCSRevert)
704        self.vcsDirMultiMenuActions.append(act)
705        act = menu.addAction(
706            UI.PixmapCache.getIcon("vcsMerge"),
707            self.tr('Merge changes'), self._VCSMerge)
708        self.vcsDirMultiMenuActions.append(act)
709        act = menu.addAction(
710            self.tr('Conflicts resolved'), self.__SVNResolve)
711        self.vcsDirMultiMenuActions.append(act)
712        menu.addSeparator()
713        act = menu.addAction(self.tr('Set Property'), self.__SVNSetProp)
714        self.vcsDirMultiMenuActions.append(act)
715        act = menu.addAction(
716            self.tr('List Properties'), self.__SVNListProps)
717        self.vcsDirMultiMenuActions.append(act)
718        act = menu.addAction(self.tr('Delete Property'), self.__SVNDelProp)
719        self.vcsDirMultiMenuActions.append(act)
720        menu.addSeparator()
721        menu.addAction(self.tr('Select all local file entries'),
722                       self.browser.selectLocalEntries)
723        menu.addAction(self.tr('Select all versioned file entries'),
724                       self.browser.selectVCSEntries)
725        menu.addAction(self.tr('Select all local directory entries'),
726                       self.browser.selectLocalDirEntries)
727        menu.addAction(self.tr('Select all versioned directory entries'),
728                       self.browser.selectVCSDirEntries)
729        menu.addSeparator()
730        menu.addAction(self.tr("Configure..."), self.__SVNConfigure)
731
732        mainMenu.addSeparator()
733        mainMenu.addMenu(menu)
734        self.menuDirMulti = menu
735
736    ###########################################################################
737    # Menu handling methods below
738    ###########################################################################
739
740    def __SVNCopy(self):
741        """
742        Private slot called by the context menu to copy the selected file.
743        """
744        itm = self.browser.currentItem()
745        try:
746            fn = itm.fileName()
747        except AttributeError:
748            fn = itm.dirName()
749        self.vcs.svnCopy(fn, self.project)
750
751    def __SVNMove(self):
752        """
753        Private slot called by the context menu to move the selected file.
754        """
755        itm = self.browser.currentItem()
756        try:
757            fn = itm.fileName()
758        except AttributeError:
759            fn = itm.dirName()
760        isFile = os.path.isfile(fn)
761        movefiles = self.browser.project.getFiles(fn)
762        self.browser.project.stopFileSystemMonitoring()
763        if self.vcs.vcsMove(fn, self.project):
764            if isFile:
765                self.browser.closeSourceWindow.emit(fn)
766            else:
767                for mf in movefiles:
768                    self.browser.closeSourceWindow.emit(mf)
769        self.browser.project.startFileSystemMonitoring()
770
771    def __SVNResolve(self):
772        """
773        Private slot called by the context menu to resolve conflicts of a file.
774        """
775        names = []
776        for itm in self.browser.getSelectedItems():
777            try:
778                names.append(itm.fileName())
779            except AttributeError:
780                names.append(itm.dirName())
781        self.vcs.svnResolve(names)
782
783    def __SVNListProps(self):
784        """
785        Private slot called by the context menu to list the subversion
786        properties of a file.
787        """
788        names = []
789        for itm in self.browser.getSelectedItems():
790            try:
791                names.append(itm.fileName())
792            except AttributeError:
793                names.append(itm.dirName())
794        self.vcs.svnListProps(names)
795
796    def __SVNSetProp(self):
797        """
798        Private slot called by the context menu to set a subversion
799        property of 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.svnSetProp(names)
808
809    def __SVNDelProp(self):
810        """
811        Private slot called by the context menu to delete a subversion
812        property of a file.
813        """
814        names = []
815        for itm in self.browser.getSelectedItems():
816            try:
817                names.append(itm.fileName())
818            except AttributeError:
819                names.append(itm.dirName())
820        self.vcs.svnDelProp(names)
821
822    def __SVNExtendedDiff(self):
823        """
824        Private slot called by the context menu to show the difference of a
825        file to the repository.
826
827        This gives the chance to enter the revisions to compare.
828        """
829        names = []
830        for itm in self.browser.getSelectedItems():
831            try:
832                names.append(itm.fileName())
833            except AttributeError:
834                names.append(itm.dirName())
835        self.vcs.svnExtendedDiff(names)
836
837    def __SVNUrlDiff(self):
838        """
839        Private slot called by the context menu to show the difference of a
840        file of two repository URLs.
841
842        This gives the chance to enter the repository URLs to compare.
843        """
844        names = []
845        for itm in self.browser.getSelectedItems():
846            try:
847                names.append(itm.fileName())
848            except AttributeError:
849                names.append(itm.dirName())
850        self.vcs.svnUrlDiff(names)
851
852    def __SVNSbsDiff(self):
853        """
854        Private slot called by the context menu to show the difference of a
855        file to the repository side-by-side.
856        """
857        itm = self.browser.currentItem()
858        fn = itm.fileName()
859        self.vcs.svnSbsDiff(fn)
860
861    def __SVNSbsExtendedDiff(self):
862        """
863        Private slot called by the context menu to show the difference of a
864        file to the repository side-by-side.
865
866        It allows the selection of revisions to compare.
867        """
868        itm = self.browser.currentItem()
869        fn = itm.fileName()
870        self.vcs.svnSbsDiff(fn, extended=True)
871
872    def __SVNBlame(self):
873        """
874        Private slot called by the context menu to show the blame of a file.
875        """
876        itm = self.browser.currentItem()
877        fn = itm.fileName()
878        self.vcs.svnBlame(fn)
879
880    def __SVNLock(self):
881        """
882        Private slot called by the context menu to lock files in the
883        repository.
884        """
885        names = []
886        for itm in self.browser.getSelectedItems():
887            try:
888                names.append(itm.fileName())
889            except AttributeError:
890                names.append(itm.dirName())
891        self.vcs.svnLock(names)
892
893    def __SVNUnlock(self):
894        """
895        Private slot called by the context menu to unlock files in the
896        repository.
897        """
898        names = []
899        for itm in self.browser.getSelectedItems():
900            try:
901                names.append(itm.fileName())
902            except AttributeError:
903                names.append(itm.dirName())
904        self.vcs.svnUnlock(names)
905
906    def __SVNBreakLock(self):
907        """
908        Private slot called by the context menu to break lock files in the
909        repository.
910        """
911        names = []
912        for itm in self.browser.getSelectedItems():
913            try:
914                names.append(itm.fileName())
915            except AttributeError:
916                names.append(itm.dirName())
917        self.vcs.svnUnlock(names, breakIt=True)
918
919    def __SVNStealLock(self):
920        """
921        Private slot called by the context menu to steal lock files in the
922        repository.
923        """
924        names = []
925        for itm in self.browser.getSelectedItems():
926            try:
927                names.append(itm.fileName())
928            except AttributeError:
929                names.append(itm.dirName())
930        self.vcs.svnLock(names, stealIt=True)
931
932    def __SVNInfo(self):
933        """
934        Private slot called by the context menu to show repository information
935        of a file or directory.
936        """
937        try:
938            name = self.browser.currentItem().fileName()
939        except AttributeError:
940            name = self.browser.currentItem().dirName()
941        name = self.project.getRelativePath(name)
942        self.vcs.svnInfo(self.project.ppath, name)
943
944    def __SVNConfigure(self):
945        """
946        Private method to open the configuration dialog.
947        """
948        e5App().getObject("UserInterface").showPreferences(
949            "zzz_subversionPage")
950
951    def __SVNAddToChangelist(self):
952        """
953        Private slot called by the context menu to add files to a changelist.
954        """
955        names = []
956        for itm in self.browser.getSelectedItems():
957            try:
958                names.append(itm.fileName())
959            except AttributeError:
960                names.append(itm.dirName())
961        self.vcs.svnAddToChangelist(names)
962
963    def __SVNRemoveFromChangelist(self):
964        """
965        Private slot called by the context menu to remove files from their
966        changelist.
967        """
968        names = []
969        for itm in self.browser.getSelectedItems():
970            try:
971                names.append(itm.fileName())
972            except AttributeError:
973                names.append(itm.dirName())
974        self.vcs.svnRemoveFromChangelist(names)
975
976    ###########################################################################
977    # Some private utility methods below
978    ###########################################################################
979
980    def __itemsHaveFiles(self, items):
981        """
982        Private method to check, if items contain file type items.
983
984        @param items items to check (list of QTreeWidgetItems)
985        @return flag indicating items contain file type items (boolean)
986        """
987        return any(isinstance(itm, ProjectBrowserFileItem) for itm in items)
988