Project/ProjectBrowserModel.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Project/ProjectBrowserModel.py	Mon Dec 28 16:03:33 2009 +0000
@@ -0,0 +1,779 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2006 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the browser model.
+"""
+
+import sys
+import os
+import re
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from UI.BrowserModel import *
+
+import UI.PixmapCache
+import Preferences
+import Utilities
+
+import Utilities.ModuleParser
+
+ProjectBrowserItemSimpleDirectory  = 100
+ProjectBrowserItemDirectory        = 101
+ProjectBrowserItemFile             = 102
+
+ProjectBrowserNoType          = 0
+ProjectBrowserSourceType      = 1
+ProjectBrowserFormType        = 2
+ProjectBrowserInterfaceType   = 3
+ProjectBrowserTranslationType = 4
+ProjectBrowserOthersType      = 5
+ProjectBrowserResourceType    = 6
+
+class ProjectBrowserItemMixin(object):
+    """
+    Class implementing common methods of project browser items.
+    
+    It is meant to be used as a mixin class.
+    """
+    def __init__(self, type_, bold = False):
+        """
+        Constructor
+        
+        @param type_ type of file/directory in the project
+        @param bold flag indicating a highlighted font
+        """
+        self._projectTypes = [type_]
+        self.bold = bold
+        self.vcsState = " "
+    
+    def getTextColor(self):
+        """
+        Public method to get the items text color.
+        
+        @return text color (QVariant(QColor))
+        """
+        if self.bold:
+            return QVariant(Preferences.getProjectBrowserColour("Highlighted"))
+        else:
+            return QVariant()
+    
+    def setVcsState(self, state):
+        """
+        Public method to set the items VCS state.
+        
+        @param state VCS state (one of A, C, M, U or " ") (string)
+        """
+        self.vcsState = state
+    
+    def addVcsStatus(self, vcsStatus):
+        """
+        Public method to add the VCS status.
+        
+        @param vcsStatus VCS status text (string)
+        """
+        self.itemData.append(vcsStatus)
+    
+    def setVcsStatus(self, vcsStatus):
+        """
+        Public method to set the VCS status.
+        
+        @param vcsStatus VCS status text (string)
+        """
+        self.itemData[1] = vcsStatus
+    
+    def getProjectTypes(self):
+        """
+        Public method to get the project type.
+        
+        @return project type
+        """
+        return self._projectTypes[:]
+    
+    def addProjectType(self, type_):
+        """
+        Public method to add a type to the list.
+        
+        @param type_ type to add to the list
+        """
+        self._projectTypes.append(type_)
+
+class ProjectBrowserSimpleDirectoryItem(BrowserItem, ProjectBrowserItemMixin):
+    """
+    Class implementing the data structure for project browser simple directory items.
+    """
+    def __init__(self, parent, projectType, text, path = ""):
+        """
+        Constructor
+        
+        @param parent parent item
+        @param projectType type of file/directory in the project
+        @param text text to be displayed (string)
+        @param path path of the directory (string)
+        """
+        BrowserItem.__init__(self, parent, text)
+        ProjectBrowserItemMixin.__init__(self, projectType)
+        
+        self._dirName = path
+        if not os.path.isdir(self._dirName):
+            self._dirName = os.path.dirname(self._dirName)
+        
+        self.type_ = ProjectBrowserItemSimpleDirectory
+        self.icon = UI.PixmapCache.getIcon("dirClosed.png")
+    
+    def dirName(self):
+        """
+        Public method returning the directory name.
+        
+        @return directory name (string)
+        """
+        return self._dirName
+    
+    def lessThan(self, other, column, order):
+        """
+        Public method to check, if the item is less than the other one.
+        
+        @param other reference to item to compare against (BrowserItem)
+        @param column column number to use for the comparison (integer)
+        @param order sort order (Qt.SortOrder) (for special sorting)
+        @return true, if this item is less than other (boolean)
+        """
+        if issubclass(other.__class__, BrowserFileItem):
+            if Preferences.getUI("BrowsersListFoldersFirst"):
+                return order == Qt.AscendingOrder
+        
+        return BrowserItem.lessThan(self, other, column, order)
+
+class ProjectBrowserDirectoryItem(BrowserDirectoryItem, ProjectBrowserItemMixin):
+    """
+    Class implementing the data structure for project browser directory items.
+    """
+    def __init__(self, parent, dinfo, projectType, full = True, bold = False):
+        """
+        Constructor
+        
+        @param parent parent item
+        @param dinfo dinfo is the string for the directory (string)
+        @param projectType type of file/directory in the project
+        @param full flag indicating full pathname should be displayed (boolean)
+        @param bold flag indicating a highlighted font (boolean)
+        """
+        BrowserDirectoryItem.__init__(self, parent, dinfo, full)
+        ProjectBrowserItemMixin.__init__(self, projectType, bold)
+        
+        self.type_ = ProjectBrowserItemDirectory
+
+class ProjectBrowserFileItem(BrowserFileItem, ProjectBrowserItemMixin):
+    """
+    Class implementing the data structure for project browser file items.
+    """
+    def __init__(self, parent, finfo, projectType, full = True, bold = False,
+                 sourceLanguage = ""):
+        """
+        Constructor
+        
+        @param parent parent item
+        @param finfo the string for the file (string)
+        @param projectType type of file/directory in the project
+        @param full flag indicating full pathname should be displayed (boolean)
+        @param bold flag indicating a highlighted font (boolean)
+        @param sourceLanguage source code language of the project (string)
+        """
+        BrowserFileItem.__init__(self, parent, finfo, full, sourceLanguage)
+        ProjectBrowserItemMixin.__init__(self, projectType, bold)
+        
+        self.type_ = ProjectBrowserItemFile
+
+class ProjectBrowserModel(BrowserModel):
+    """
+    Class implementing the project browser model.
+    
+    @signal vcsStateChanged(QString) emitted after the VCS state has changed
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent reference to parent object (Project.Project)
+        """
+        QAbstractItemModel.__init__(self, parent)
+        
+        rootData = QVariant(self.trUtf8("Name"))
+        self.rootItem = BrowserItem(None, rootData)
+        self.rootItem.itemData.append(QVariant(self.trUtf8("VCS Status")))
+        
+        self.progDir = None
+        self.project = parent
+        
+        self.inRefresh = False
+        
+        self.projectBrowserTypes = {
+            "SOURCES"      : ProjectBrowserSourceType,
+            "FORMS"        : ProjectBrowserFormType,
+            "RESOURCES"    : ProjectBrowserResourceType,
+            "INTERFACES"   : ProjectBrowserInterfaceType,
+            "TRANSLATIONS" : ProjectBrowserTranslationType,
+            "OTHERS"       : ProjectBrowserOthersType,
+        }
+        
+        self.colorNames = {
+            "A" : "VcsAdded",
+            "M" : "VcsModified",
+            "R" : "VcsReplaced", 
+            "U" : "VcsUpdate",
+            "Z" : "VcsConflict",
+        }
+        self.itemBackgroundColors = {
+            " " : QColor(),
+            "A" : Preferences.getProjectBrowserColour(self.colorNames["A"]),
+            "M" : Preferences.getProjectBrowserColour(self.colorNames["M"]),
+            "R" : Preferences.getProjectBrowserColour(self.colorNames["R"]),
+            "U" : Preferences.getProjectBrowserColour(self.colorNames["U"]),
+            "Z" : Preferences.getProjectBrowserColour(self.colorNames["Z"]),
+        }
+        
+        self.highLightColor = Preferences.getProjectBrowserColour("Highlighted")
+        # needed by preferencesChanged()
+        
+        self.vcsStatusReport = {}
+    
+    def data(self, index, role):
+        """
+        Public method to get data of an item.
+        
+        @param index index of the data to retrieve (QModelIndex)
+        @param role role of data (Qt.ItemDataRole)
+        @return requested data (QVariant)
+        """
+        if not index.isValid():
+            return QVariant()
+        
+        if role == Qt.TextColorRole:
+            if index.column() == 0:
+                try:
+                    return index.internalPointer().getTextColor()
+                except AttributeError:
+                    return QVariant()
+        elif role == Qt.BackgroundColorRole:
+            try:
+                col = self.itemBackgroundColors[index.internalPointer().vcsState]
+                if col.isValid():
+                    return QVariant(col)
+                else:
+                    return QVariant()
+            except AttributeError:
+                return QVariant()
+            except KeyError:
+                return QVariant()
+        
+        return BrowserModel.data(self, index, role)
+    
+    def populateItem(self, parentItem, repopulate = False):
+        """
+        Public method to populate an item's subtree.
+        
+        @param parentItem reference to the item to be populated
+        @param repopulate flag indicating a repopulation (boolean)
+        """
+        if parentItem.type() == ProjectBrowserItemSimpleDirectory:
+            return  # nothing to do
+        elif parentItem.type() == ProjectBrowserItemDirectory:
+            self.populateProjectDirectoryItem(parentItem, repopulate)
+        elif parentItem.type() == ProjectBrowserItemFile:
+            self.populateFileItem(parentItem, repopulate)
+        else:
+            BrowserModel.populateItem(self, parentItem, repopulate)
+
+    def populateProjectDirectoryItem(self, parentItem, repopulate = False):
+        """
+        Public method to populate a directory item's subtree.
+        
+        @param parentItem reference to the directory item to be populated
+        @param repopulate flag indicating a repopulation (boolean)
+        """
+        qdir = QDir(parentItem.dirName())
+        
+        entryInfoList = \
+            qdir.entryInfoList(QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot))
+        
+        if len(entryInfoList) > 0:
+            if repopulate:
+                self.beginInsertRows(self.createIndex(parentItem.row(), 0, parentItem),
+                    0, len(entryInfoList) - 1)
+            states = {}
+            if self.project.vcs is not None:
+                for f in entryInfoList:
+                    fname = f.absoluteFilePath()
+                    states[os.path.normcase(fname)] = 0
+                dname = parentItem.dirName()
+                self.project.vcs.clearStatusCache()
+                states = self.project.vcs.vcsAllRegisteredStates(states, dname)
+            
+            for f in entryInfoList:
+                if f.isDir():
+                    node = ProjectBrowserDirectoryItem(parentItem,
+                        Utilities.toNativeSeparators(f.absoluteFilePath()), 
+                        parentItem.getProjectTypes()[0], False)
+                else:
+                    node = ProjectBrowserFileItem(parentItem,
+                        Utilities.toNativeSeparators(f.absoluteFilePath()),
+                        parentItem.getProjectTypes()[0])
+                if self.project.vcs is not None:
+                    fname = f.absoluteFilePath()
+                    if states[os.path.normcase(fname)] == self.project.vcs.canBeCommitted:
+                        node.addVcsStatus(self.project.vcs.vcsName())
+                        self.project.clearStatusMonitorCachedState(f.absoluteFilePath())
+                    else:
+                        node.addVcsStatus(self.trUtf8("local"))
+                self._addItem(node, parentItem)
+            if repopulate:
+                self.endInsertRows()
+
+    def projectClosed(self):
+        """
+        Public method called after a project has been closed.
+        """
+        self.__vcsStatus = {}
+        
+        self.rootItem.removeChildren()
+        self.reset()
+        
+        # reset the module parser cache
+        Utilities.ModuleParser.resetParsedModules()
+        
+    def projectOpened(self):
+        """
+        Public method used to populate the model after a project has been opened.
+        """
+        self.__vcsStatus = {}
+        states = {}
+        keys = self.projectBrowserTypes.keys()[:]
+        
+        if self.project.vcs is not None:
+            for key in keys:
+                for fn in self.project.pdata[key]:
+                    states[os.path.normcase(os.path.join(self.project.ppath, fn))] = 0
+            
+            self.project.vcs.clearStatusCache()
+            for dir in self.project.subdirs:
+                states = self.project.vcs.vcsAllRegisteredStates(states, 
+                    os.path.join(self.project.ppath, dir))
+            
+            for dir in self.project.otherssubdirs:
+                if not os.path.isabs(dir):
+                    dir = os.path.join(self.project.ppath, dir)
+                states = self.project.vcs.vcsAllRegisteredStates(states, dir)
+            
+            if self.project.pdata["TRANSLATIONPATTERN"]:
+                dir = os.path.join(self.project.ppath, 
+                                   self.project.pdata["TRANSLATIONPATTERN"][0])\
+                      .split("%language%")[0]
+                if not os.path.isdir(dir):
+                    dir = os.path.dirname(dir)
+                states = self.project.vcs.vcsAllRegisteredStates(states, dir)
+        
+        self.inRefresh = True
+        for key in keys:
+            # Show the entry in bold in the others browser to make it more distinguishable
+            if key == "OTHERS":
+                bold = True
+            else:
+                bold = False
+            
+            if key == "SOURCES":
+                sourceLanguage = self.project.pdata["PROGLANGUAGE"][0]
+            else:
+                sourceLanguage = ""
+            
+            for fn in self.project.pdata[key]:
+                fname = os.path.join(self.project.ppath, fn)
+                parentItem, dt = \
+                    self.findParentItemByName(self.projectBrowserTypes[key], fn)
+                if os.path.isdir(fname):
+                    itm = ProjectBrowserDirectoryItem(parentItem, fname,
+                        self.projectBrowserTypes[key], False, bold)
+                else:
+                    itm = ProjectBrowserFileItem(parentItem, fname,
+                        self.projectBrowserTypes[key], False, bold,
+                        sourceLanguage = sourceLanguage)
+                self._addItem(itm, parentItem)
+                if self.project.vcs is not None:
+                    if states[os.path.normcase(fname)] == self.project.vcs.canBeCommitted:
+                        itm.addVcsStatus(self.project.vcs.vcsName())
+                    else:
+                        itm.addVcsStatus(self.trUtf8("local"))
+                else:
+                    itm.addVcsStatus("")
+        self.inRefresh = False
+        self.reset()
+
+    def findParentItemByName(self, type_, name, dontSplit = False):
+        """
+        Public method to find an item given it's name.
+        
+        <b>Note</b>: This method creates all necessary parent items, if they
+        don't exist.
+        
+        @param type_ type of the item
+        @param name name of the item (string)
+        @param dontSplit flag indicating the name should not be split (boolean)
+        @return reference to the item found and the new display name (string)
+        """
+        if dontSplit:
+            pathlist = []
+            pathlist.append(name)
+            pathlist.append("ignore_me")
+        else:
+            pathlist = re.split(r'/|\\', name)
+        
+        if len(pathlist) > 1:
+            olditem = self.rootItem
+            path = self.project.ppath
+            for p in pathlist[:-1]:
+                itm = self.findChildItem(p, 0, olditem)
+                path = os.path.join(path, p)
+                if itm is None:
+                    itm = ProjectBrowserSimpleDirectoryItem(olditem, type_, p, path)
+                    self.__addVCSStatus(itm, path)
+                    if self.inRefresh:
+                        self._addItem(itm, olditem)
+                    else:
+                        if olditem == self.rootItem:
+                            oldindex = QModelIndex()
+                        else:
+                            oldindex = self.createIndex(olditem.row(), 0, olditem)
+                        self.addItem(itm, oldindex)
+                else:
+                    if type_ and type_ not in itm.getProjectTypes():
+                        itm.addProjectType(type_)
+                        index = self.createIndex(itm.row(), 0, itm)
+                        self.emit(SIGNAL(\
+                            "dataChanged(const QModelIndex &, const QModelIndex &)"),
+                            index, index)
+                olditem = itm
+            return (itm, pathlist[-1])
+        else:
+            return (self.rootItem, name)
+    
+    def findChildItem(self, text, column, parentItem = None):
+        """
+        Public method to find a child item given some text.
+        
+        @param text text to search for (string)
+        @param column column to search in (integer)
+        @param parentItem reference to parent item
+        @return reference to the item found
+        """
+        if parentItem is None:
+            parentItem = self.rootItem
+        
+        for itm in parentItem.children():
+            if itm.data(column) == text:
+                return itm
+        
+        return None
+        
+    def addNewItem(self, typeString, name, additionalTypeStrings = []):
+        """
+        Public method to add a new item to the model.
+        
+        @param typeString string denoting the type of the new item (string)
+        @param name name of the new item (string)
+        @param additionalTypeStrings names of additional types (list of string)
+        """
+        # Show the entry in bold in the others browser to make it more distinguishable
+        if typeString == "OTHERS":
+            bold = True
+        else:
+            bold = False
+        
+        fname = os.path.join(self.project.ppath, name)
+        parentItem, dt = \
+            self.findParentItemByName(self.projectBrowserTypes[typeString], name)
+        if parentItem == self.rootItem:
+            parentIndex = QModelIndex()
+        else:
+            parentIndex = self.createIndex(parentItem.row(), 0, parentItem)
+        if os.path.isdir(fname):
+            itm = ProjectBrowserDirectoryItem(parentItem, fname,
+                self.projectBrowserTypes[typeString], False, bold)
+        else:
+            if typeString == "SOURCES":
+                sourceLanguage = self.project.pdata["PROGLANGUAGE"][0]
+            else:
+                sourceLanguage = ""
+            itm = ProjectBrowserFileItem(parentItem, fname,
+                self.projectBrowserTypes[typeString], False, bold,
+                sourceLanguage = sourceLanguage)
+        self.__addVCSStatus(itm, fname)
+        if additionalTypeStrings:
+            for additionalTypeString in additionalTypeStrings:
+                type_ = self.projectBrowserTypes[additionalTypeString]
+                itm.addProjectType(type_)
+        self.addItem(itm, parentIndex)
+    
+    def renameItem(self, name, newFilename):
+        """
+        Public method to rename an item.
+        
+        @param name the old display name (string)
+        @param newFilename new filename of the item (string)
+        """
+        itm = self.findItem(name)
+        if itm is None:
+            return
+        
+        index = self.createIndex(itm.row(), 0, itm)
+        itm.setName(newFilename)
+        self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
+                  index, index)
+        self.repopulateItem(newFilename)
+    
+    def findItem(self, name):
+        """
+        Public method to find an item given it's name.
+        
+        @param name name of the item (string)
+        @return reference to the item found
+        """
+        if QDir.isAbsolutePath(name):
+            name.replace(self.project.ppath + os.sep, "")
+        pathlist = re.split(r'/|\\', name)
+        if len(pathlist) > 0:
+            olditem = self.rootItem
+            for p in pathlist:
+                itm = self.findChildItem(p, 0, olditem)
+                if itm is None:
+                    return None
+                olditem = itm
+            return itm
+        else:
+            return None
+    
+    def itemIndexByName(self, name):
+        """
+        Public method to find an item's index given it's name.
+        
+        @param name name of the item (string)
+        @return index of the item found (QModelIndex)
+        """
+        itm = self.findItem(name)
+        if itm is None:
+            index =  QModelIndex()
+        else:
+            index =  self.createIndex(itm.row(), 0, itm)
+        return index
+    
+    def __addVCSStatus(self, item, name):
+        """
+        Private method used to set the vcs status of a node.
+        
+        @param item item to work on
+        @param name filename belonging to this item (string)
+        """
+        if self.project.vcs is not None:
+            state = self.project.vcs.vcsRegisteredState(name)
+            if state == self.project.vcs.canBeCommitted:
+                item.addVcsStatus(self.project.vcs.vcsName())
+            else:
+                item.addVcsStatus(self.trUtf8("local"))
+        else:
+            item.addVcsStatus("")
+    
+    def __updateVCSStatus(self, item, name, recursive = True):
+        """
+        Private method used to update the vcs status of a node.
+        
+        @param item item to work on
+        @param name filename belonging to this item (string)
+        @param recursive flag indicating a recursive update (boolean)
+        """
+        if self.project.vcs is not None:
+            self.project.vcs.clearStatusCache()
+            state = self.project.vcs.vcsRegisteredState(name)
+            if state == self.project.vcs.canBeCommitted:
+                item.setVcsStatus(self.project.vcs.vcsName())
+            else:
+                item.setVcsStatus(self.trUtf8("local"))
+            if recursive:
+                name = os.path.dirname(name)
+                parentItem = item.parent()
+                if name and parentItem is not self.rootItem:
+                    self.__updateVCSStatus(parentItem, name, recursive)
+        else:
+            item.setVcsStatus("")
+        
+        index = self.createIndex(item.row(), 0, item)
+        self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
+                  index, index)
+    
+    def updateVCSStatus(self, name, recursive = True):
+        """
+        Public method used to update the vcs status of a node.
+        
+        @param name filename belonging to this item (string)
+        @param recursive flag indicating a recursive update (boolean)
+        """
+        item = self.findItem(name)
+        if item:
+            self.__updateVCSStatus(item, name, recursive)
+    
+    def removeItem(self, name):
+        """
+        Public method to remove a named item.
+        
+        @param name file or directory name of the item (string).
+        """
+        fname = os.path.basename(name)
+        parentItem = self.findParentItemByName(0, name)[0]
+        if parentItem == self.rootItem:
+            parentIndex = QModelIndex()
+        else:
+            parentIndex = self.createIndex(parentItem.row(), 0, parentItem)
+        childItem = self.findChildItem(fname, 0, parentItem)
+        if childItem is not None:
+            self.beginRemoveRows(parentIndex, childItem.row(), childItem.row())
+            parentItem.removeChild(childItem)
+            self.endRemoveRows()
+    
+    def repopulateItem(self, name):
+        """
+        Public method to repopulate an item.
+        
+        @param name name of the file relative to the project root (string)
+        """
+        itm = self.findItem(name)
+        if itm is None:
+            return
+        
+        if itm.isLazyPopulated() and not itm.isPopulated():
+            # item is not populated yet, nothing to do
+            return
+        
+        if itm.childCount():
+            index = self.createIndex(itm.row(), 0, itm)
+            self.beginRemoveRows(index, 0, itm.childCount() - 1)
+            itm.removeChildren()
+            self.endRemoveRows()
+            Utilities.ModuleParser.resetParsedModule(\
+                os.path.join(self.project.ppath, name))
+            
+            self.populateItem(itm, True)
+    
+    def projectPropertiesChanged(self):
+        """
+        Public method to react on a change of the project properties.
+        """
+        # nothing to do for now
+        return
+
+    def changeVCSStates(self, statesList):
+        """
+        Public slot to record the (non normal) VCS states.
+        
+        @param statesList list of VCS state entries (list of strings) giving the 
+            states in the first column and the path relative to the project 
+            directory starting with the third column. The allowed status flags 
+            are:
+            <ul>
+                <li>"A" path was added but not yet comitted</li>
+                <li>"M" path has local changes</li>
+                <li>"R" path was deleted and then re-added</li>
+                <li>"U" path needs an update</li>
+                <li>"Z" path contains a conflict</li>
+                <li>" " path is back at normal</li>
+            </ul>
+        """
+        statesList.sort()
+        lastHead = ""
+        itemCache = {}
+        if len(statesList) == 1 and statesList[0] == '--RESET--':
+            statesList = []
+            for name in self.__vcsStatus.keys():
+                statesList.append(" %s" % name)
+        
+        for name in statesList:
+            state = name[0]
+            name = name[1:].strip()
+            if state == ' ':
+                if name in self.__vcsStatus:
+                    del self.__vcsStatus[name]
+            else:
+                self.__vcsStatus[name] = state
+            
+            try:
+                itm = itemCache[name]
+            except KeyError:
+                itm = self.findItem(name)
+                if itm:
+                    itemCache[name] = itm
+            if itm:
+                itm.setVcsState(state)
+                index1 = self.createIndex(itm.row(), 0, itm)
+                index2 = self.createIndex(itm.row(), 
+                    self.rootItem.columnCount(), itm)
+                self.emit(\
+                    SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
+                    index1, index2)
+            
+            head, tail = os.path.split(name)
+            if head != lastHead:
+                if lastHead:
+                    self.__changeParentsVCSState(lastHead, itemCache)
+                lastHead = head
+        if lastHead:
+            self.__changeParentsVCSState(lastHead, itemCache)
+        try:
+            globalVcsStatus = sorted(self.__vcsStatus.values())[-1]
+        except IndexError:
+            globalVcsStatus = ' '
+        self.emit(SIGNAL("vcsStateChanged"), globalVcsStatus)
+
+    def __changeParentsVCSState(self, path, itemCache):
+        """
+        Private method to recursively change the parents VCS state.
+        
+        @param path pathname of parent item (string)
+        @param itemCache reference to the item cache used to store
+            references to named items
+        """
+        while path:
+            try:
+                itm = itemCache[path]
+            except KeyError:
+                itm = self.findItem(path)
+                if itm:
+                    itemCache[path] = itm
+            if itm:
+                state = " "
+                for id_ in itm.children():
+                    if state < id_.vcsState:
+                        state = id_.vcsState
+                if state != itm.vcsState:
+                    itm.setVcsState(state)
+                    index1 = self.createIndex(itm.row(), 0, itm)
+                    index2 = self.createIndex(itm.row(), 
+                        self.rootItem.columnCount(), itm)
+                    self.emit(\
+                        SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
+                        index1, index2)
+            path, tail = os.path.split(path)
+    
+    def preferencesChanged(self):
+        """
+        Public method used to handle a change in preferences.
+        """
+        for code in self.colorNames.keys():
+            color = Preferences.getProjectBrowserColour(self.colorNames[code])
+            if color.name() == self.itemBackgroundColors[code].name():
+                continue
+            
+            self.itemBackgroundColors[code] = color
+        
+        color = Preferences.getProjectBrowserColour("Highlighted")
+        if self.highLightColor.name() != color.name():
+            self.highLightColor = color

eric ide

mercurial