# -*- coding: utf-8 -*-

# Copyright (c) 2005 - 2007 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the VCS project browser helper for CVS.
"""

import os

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from VCS.ProjectBrowserHelper import VcsProjectBrowserHelper

from Project.ProjectBrowserModel import ProjectBrowserFileItem, \
    ProjectBrowserDirectoryItem

class CvsProjectBrowserHelper(VcsProjectBrowserHelper):
    """
    Class implementing the VCS project browser helper for CVS.
    """
    def __init__(self, vcsObject, browserObject, projectObject, isTranslationsBrowser,
        parent = None, name = None):
        """
        Constructor
        
        @param vcsObject reference to the vcs object
        @param browserObject reference to the project browser object
        @param projectObject reference to the project object
        @param isTranslationsBrowser flag indicating, the helper is requested for the
            translations browser (this needs some special treatment)
        @param parent parent widget (QWidget)
        @param name name of this object (string or QString)
        """
        VcsProjectBrowserHelper.__init__(self, vcsObject, browserObject, projectObject,
            isTranslationsBrowser, parent, name)
    
    def showPopupMenu(self, menu, standardItems):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the file status.
        
        @param menu reference to the menu to be shown
        @param standardItems array of standard items that need activation/deactivation
            depending on the overall VCS status
        """
        if unicode(self.browser.currentItem().data(1)) == self.vcs.vcsName():
            for act in self.vcsMenuActions:
                act.setEnabled(True)
            for act in self.vcsAddMenuActions:
                act.setEnabled(False)
            for act in standardItems:
                act.setEnabled(False)
            try:
                name = unicode(self.browser.currentItem().fileName())
            except AttributeError:
                name = unicode(self.browser.currentItem().dirName())
            if os.path.isdir(name):
                self.cvsMenuEdit.setEnabled(False)
                self.cvsMenuUnedit.setEnabled(False)
        else:
            for act in self.vcsMenuActions:
                act.setEnabled(False)
            for act in self.vcsAddMenuActions:
                act.setEnabled(True)
            if 1 in self.browser.specialMenuEntries:
                try:
                    name = unicode(self.browser.currentItem().fileName())
                except AttributeError:
                    name = unicode(self.browser.currentItem().dirName())
                if not os.path.isdir(name):
                    self.vcsMenuAddTree.setEnabled(False)
            for act in standardItems:
                act.setEnabled(True)
    
    def showPopupMenuMulti(self, menu, standardItems):
        """
        Slot called before the context menu (multiple selections) is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the files status.
        
        @param menu reference to the menu to be shown
        @param standardItems array of standard items that need activation/deactivation
            depending on the overall VCS status
        """
        vcsName = self.vcs.vcsName()
        items = self.browser.getSelectedItems()
        vcsItems = 0
        # determine number of selected items under VCS control
        for itm in items:
            if unicode(itm.data(1)) == vcsName:
                vcsItems += 1
        
        if vcsItems > 0:
            if vcsItems != len(items):
                for act in self.vcsMultiMenuActions:
                    act.setEnabled(False)
            else:
                for act in self.vcsMultiMenuActions:
                    act.setEnabled(True)
            for act in self.vcsAddMultiMenuActions:
                act.setEnabled(False)
            for act in standardItems:
                act.setEnabled(False)
            if self.__itemsHaveDirectories(items):
                self.cvsMultiMenuEdit.setEnabled(False)
                self.cvsMultiMenuUnedit.setEnabled(False)
        else:
            for act in self.vcsMultiMenuActions:
                act.setEnabled(False)
            for act in self.vcsAddMultiMenuActions:
                act.setEnabled(True)
            if 1 in self.browser.specialMenuEntries and \
               self.__itemsHaveFiles(items):
                self.vcsMultiMenuAddTree.setEnabled(False)
            for act in standardItems:
                act.setEnabled(True)
    
    def showPopupMenuDir(self, menu, standardItems):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the directory status.
        
        @param menu reference to the menu to be shown
        @param standardItems array of standard items that need activation/deactivation
            depending on the overall VCS status
        """
        if unicode(self.browser.currentItem().data(1)) == self.vcs.vcsName():
            for act in self.vcsDirMenuActions:
                act.setEnabled(True)
            for act in self.vcsAddDirMenuActions:
                act.setEnabled(False)
            for act in standardItems:
                act.setEnabled(False)
        else:
            for act in self.vcsDirMenuActions:
                act.setEnabled(False)
            for act in self.vcsAddDirMenuActions:
                act.setEnabled(True)
            for act in standardItems:
                act.setEnabled(True)
    
    def showPopupMenuDirMulti(self, menu, standardItems):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the directory status.
        
        @param menu reference to the menu to be shown
        @param standardItems array of standard items that need activation/deactivation
            depending on the overall VCS status
        """
        vcsName = self.vcs.vcsName()
        items = self.browser.getSelectedItems()
        vcsItems = 0
        # determine number of selected items under VCS control
        for itm in items:
            if unicode(itm.data(1)) == vcsName:
                vcsItems += 1
        
        if vcsItems > 0:
            if vcsItems != len(items):
                for act in self.vcsDirMultiMenuActions:
                    act.setEnabled(False)
            else:
                for act in self.vcsDirMultiMenuActions:
                    act.setEnabled(True)
            for act in self.vcsAddDirMultiMenuActions:
                act.setEnabled(False)
            for act in standardItems:
                act.setEnabled(False)
        else:
            for act in self.vcsDirMultiMenuActions:
                act.setEnabled(False)
            for act in self.vcsAddDirMultiMenuActions:
                act.setEnabled(True)
            for act in standardItems:
                act.setEnabled(True)

    ############################################################################
    # Protected menu generation methods below
    ############################################################################

    def _addVCSMenu(self, mainMenu):
        """
        Protected method used to add the VCS menu to all project browsers.
        
        @param mainMenu reference to the menu to be amended
        """
        self.vcsMenuActions = []
        self.vcsAddMenuActions = []
        
        menu = QMenu(self.trUtf8("Version Control"))
        
        act = menu.addAction(self.vcs.vcsName(), self._VCSInfoDisplay)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()
        
        act = menu.addAction(self.trUtf8('Update from repository'), self._VCSUpdate)
        self.vcsMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Commit changes to repository...'), 
            self._VCSCommit)
        self.vcsMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Add to repository'), self._VCSAdd)
        self.vcsAddMenuActions.append(act)
        if 1 in self.browser.specialMenuEntries:
            act = menu.addAction(self.trUtf8('Add to repository (binary)'), 
                self.__CVSAddBinary)
            self.vcsAddMenuActions.append(act)
            self.vcsMenuAddTree = menu.addAction(self.trUtf8('Add tree to repository'), 
                self._VCSAddTree)
            self.vcsAddMenuActions.append(self.vcsMenuAddTree)
        act = menu.addAction(self.trUtf8('Remove from repository (and disk)'), 
            self._VCSRemove)
        self.vcsMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Edit'), self.__CVSEdit)
        self.vcsMenuActions.append(act)
        self.cvsMenuEdit = act
        act = menu.addAction(self.trUtf8('Unedit'), self.__CVSUnedit)
        self.vcsMenuActions.append(act)
        self.cvsMenuUnedit = act
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Show log'), self._VCSLog)
        self.vcsMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show status'), self._VCSStatus)
        self.vcsMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show difference'), self._VCSDiff)
        self.vcsMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show history'), self.__CVSHistory)
        self.vcsMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Revert changes'), self._VCSRevert)
        self.vcsMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Merge changes'), self._VCSMerge)
        self.vcsMenuActions.append(act)
        menu.addSeparator()
        menu.addAction(self.trUtf8('Select all local file entries'),
                        self.browser.selectLocalEntries)
        menu.addAction(self.trUtf8('Select all versioned file entries'),
                        self.browser.selectVCSEntries)
        menu.addAction(self.trUtf8('Select all local directory entries'),
                        self.browser.selectLocalDirEntries)
        menu.addAction(self.trUtf8('Select all versioned directory entries'),
                        self.browser.selectVCSDirEntries)
        
        mainMenu.addSeparator()
        mainMenu.addMenu(menu)
        self.menu = menu
        
    def _addVCSMenuMulti(self, mainMenu):
        """
        Protected method used to add the VCS menu for multi selection to all 
        project browsers.
        
        @param mainMenu reference to the menu to be amended
        """
        self.vcsMultiMenuActions = []
        self.vcsAddMultiMenuActions = []
        
        menu = QMenu(self.trUtf8("Version Control"))
        
        act = menu.addAction(self.vcs.vcsName(), self._VCSInfoDisplay)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()
        
        act = menu.addAction(self.trUtf8('Update from repository'), self._VCSUpdate)
        self.vcsMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Commit changes to repository...'), 
            self._VCSCommit)
        self.vcsMultiMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Add to repository'), self._VCSAdd)
        self.vcsAddMultiMenuActions.append(act)
        if 1 in self.browser.specialMenuEntries:
            act = menu.addAction(self.trUtf8('Add to repository (binary)'), 
                self.__CVSAddBinary)
            self.vcsAddMultiMenuActions.append(act)
            self.vcsMultiMenuAddTree = \
                menu.addAction(self.trUtf8('Add tree to repository'), self._VCSAddTree)
            self.vcsAddMultiMenuActions.append(self.vcsMultiMenuAddTree)
        act = menu.addAction(self.trUtf8('Remove from repository (and disk)'), 
            self._VCSRemove)
        self.vcsMultiMenuActions.append(act)
        self.vcsRemoveMultiMenuItem = act
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Edit'), self.__CVSEdit)
        self.vcsMultiMenuActions.append(act)
        self.cvsMultiMenuEdit = act
        act = menu.addAction(self.trUtf8('Unedit'), self.__CVSUnedit)
        self.vcsMultiMenuActions.append(act)
        self.cvsMultiMenuUnedit = act
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Show status'), self._VCSStatus)
        self.vcsMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show difference'), self._VCSDiff)
        self.vcsMultiMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Revert changes'), self._VCSRevert)
        self.vcsMultiMenuActions.append(act)
        menu.addSeparator()
        menu.addAction(self.trUtf8('Select all local file entries'),
                        self.browser.selectLocalEntries)
        menu.addAction(self.trUtf8('Select all versioned file entries'),
                        self.browser.selectVCSEntries)
        menu.addAction(self.trUtf8('Select all local directory entries'),
                        self.browser.selectLocalDirEntries)
        menu.addAction(self.trUtf8('Select all versioned directory entries'),
                        self.browser.selectVCSDirEntries)
        
        mainMenu.addSeparator()
        mainMenu.addMenu(menu)
        self.menuMulti = menu
        
    def _addVCSMenuBack(self, mainMenu):
        """
        Protected method used to add the VCS menu to all project browsers.
        
        @param mainMenu reference to the menu to be amended
        """
        menu = QMenu(self.trUtf8("Version Control"))
        
        act = menu.addAction(self.vcs.vcsName(), self._VCSInfoDisplay)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()
        
        menu.addAction(self.trUtf8('Select all local file entries'),
                        self.browser.selectLocalEntries)
        menu.addAction(self.trUtf8('Select all versioned file entries'),
                        self.browser.selectVCSEntries)
        menu.addAction(self.trUtf8('Select all local directory entries'),
                        self.browser.selectLocalDirEntries)
        menu.addAction(self.trUtf8('Select all versioned directory entries'),
                        self.browser.selectVCSDirEntries)
        
        mainMenu.addSeparator()
        mainMenu.addMenu(menu)
        self.menuBack = menu
        
    def _addVCSMenuDir(self, mainMenu):
        """
        Protected method used to add the VCS menu to all project browsers.
        
        @param mainMenu reference to the menu to be amended
        """
        if mainMenu is None:
            return
        
        self.vcsDirMenuActions = []
        self.vcsAddDirMenuActions = []
        
        menu = QMenu(self.trUtf8("Version Control"))
        
        act = menu.addAction(self.vcs.vcsName(), self._VCSInfoDisplay)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()
        
        act = menu.addAction(self.trUtf8('Update from repository'), self._VCSUpdate)
        self.vcsDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Commit changes to repository...'), 
            self._VCSCommit)
        self.vcsDirMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Add to repository'), self._VCSAdd)
        self.vcsAddDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Remove from repository (and disk)'), 
            self._VCSRemove)
        self.vcsDirMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Show log'), self._VCSLog)
        self.vcsDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show status'), self._VCSStatus)
        self.vcsDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show difference'), self._VCSDiff)
        self.vcsDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show history'), self.__CVSHistory)
        self.vcsDirMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Revert changes'), self._VCSRevert)
        self.vcsDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Merge changes'), self._VCSMerge)
        self.vcsDirMenuActions.append(act)
        menu.addSeparator()
        menu.addAction(self.trUtf8('Select all local file entries'),
                        self.browser.selectLocalEntries)
        menu.addAction(self.trUtf8('Select all versioned file entries'),
                        self.browser.selectVCSEntries)
        menu.addAction(self.trUtf8('Select all local directory entries'),
                        self.browser.selectLocalDirEntries)
        menu.addAction(self.trUtf8('Select all versioned directory entries'),
                        self.browser.selectVCSDirEntries)
        
        mainMenu.addSeparator()
        mainMenu.addMenu(menu)
        self.menuDir = menu
        
    def _addVCSMenuDirMulti(self, mainMenu):
        """
        Protected method used to add the VCS menu to all project browsers.
        
        @param mainMenu reference to the menu to be amended
        """
        if mainMenu is None:
            return
        
        self.vcsDirMultiMenuActions = []
        self.vcsAddDirMultiMenuActions = []
        
        menu = QMenu(self.trUtf8("Version Control"))
        
        act = menu.addAction(self.vcs.vcsName(), self._VCSInfoDisplay)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()
        
        act = menu.addAction(self.trUtf8('Update from repository'), self._VCSUpdate)
        self.vcsDirMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Commit changes to repository...'), 
            self._VCSCommit)
        self.vcsDirMultiMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Add to repository'), self._VCSAdd)
        self.vcsAddDirMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Remove from repository (and disk)'), 
            self._VCSRemove)
        self.vcsDirMultiMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Show log'), self._VCSLog)
        self.vcsDirMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show status'), self._VCSStatus)
        self.vcsDirMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show difference'), self._VCSDiff)
        self.vcsDirMultiMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Show history'), self.__CVSHistory)
        self.vcsDirMultiMenuActions.append(act)
        menu.addSeparator()
        act = menu.addAction(self.trUtf8('Revert changes'), self._VCSRevert)
        self.vcsDirMenuActions.append(act)
        act = menu.addAction(self.trUtf8('Merge changes'), self._VCSMerge)
        self.vcsDirMenuActions.append(act)
        menu.addSeparator()
        menu.addAction(self.trUtf8('Select all local file entries'),
                        self.browser.selectLocalEntries)
        menu.addAction(self.trUtf8('Select all versioned file entries'),
                        self.browser.selectVCSEntries)
        menu.addAction(self.trUtf8('Select all local directory entries'),
                        self.browser.selectLocalDirEntries)
        menu.addAction(self.trUtf8('Select all versioned directory entries'),
                        self.browser.selectVCSDirEntries)
        
        mainMenu.addSeparator()
        mainMenu.addMenu(menu)
        self.menuDirMulti = menu
    
    ############################################################################
    # Menu handling methods below
    ############################################################################
    
    def __CVSAddBinary(self):
        """
        Private slot called by the context menu.
        
        It is used to add the selected
        file/directory in binary mode to the VCS repository.
        """
        names = []
        for itm in self.browser.getSelectedItems():
            try:
                names.append(unicode(itm.fileName()))
            except AttributeError:
                names.append(unicode(itm.dirName()))
        if len(names) == 1:
            self.vcs.vcsAddBinary(names[0], os.path.isdir(names[0]))
        else:
            self.vcs.vcsAddBinary(names)
        for fn in names:
            self._updateVCSStatus(fn)
        
    def __CVSHistory(self):
        """
        Private slot called by the context menu to show the history of a file.
        """
        itm = self.browser.currentItem()
        try:
            fn = unicode(itm.fileName())
        except AttributeError:
            fn = unicode(itm.dirName())
        self.vcs.vcsHistory(fn)
        
    def __CVSEdit(self):
        """
        Private slot called by the context menu to edit a file (CVS).
        """
        names = [unicode(itm.fileName()) for itm in self.browser.getSelectedItems()]
        if self.isTranslationsBrowser:
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.vcs.cvsEdit(names)
        
    def __CVSUnedit(self):
        """
        Private slot called by the context menu to unedit a file (CVS).
        """
        names = []
        for itm in self.browser.getSelectedItems():
            try:
                names.append(unicode(itm.fileName()))
            except AttributeError:
                names.append(unicode(itm.dirName()))
        self.vcs.cvsUnedit(names)

    ############################################################################
    # Some private utility methods below
    ############################################################################
    
    def __itemsHaveFiles(self, items):
        """
        Private method to check, if items contain file type items.
        
        @param items items to check (list of QTreeWidgetItems)
        @return flag indicating items contain file type items (boolean)
        """
        for itm in items:
            if isinstance(itm, ProjectBrowserFileItem):
                return True
        return False

    def __itemsHaveDirectories(self, items):
        """
        Private method to check, if items contain directory type items.
        
        @param items items to check (list of QTreeWidgetItems)
        @return flag indicating items contain directory type items (boolean)
        """
        for itm in items:
            if isinstance(itm, ProjectBrowserDirectoryItem):
                return True
        return False
