# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

from qt import *
import coral
from coral.modeler.outline.outlineeditor import ElementPick
from coral.modeler.getFQName import *
from coral.modeler.extension import PropertyEditor, ModelerTool
from coral.modeler import iconcache 
from coral.modeler.window import CoralTabWidget
from coral.modeler.editor import Editor, EditorSet
from coral.modeler.coralmodeler import CoralModeler
from coral.modeler.getFQName import *
from coral.modeler import extension,action
import os
import string
import sys
import traceback
import copy
from coral.addins.constraint.constraintTab import *
from coral import log
logger=log.getLogger("transactionList")
from coral.addins.constraint.engine import *
L = coral.lang.CONSTRAINT10
config=coral.configuration.getCoralConfiguration()
imagePath = config.dGet(["Coral","install_path"])+"/coral/addins/constraint/icons"

def metamodelName(element):
    l=element.__type__().__language__()
    return l.getName()+str(l.getVersion())+str(l.getRevision())

def getAllConstraintsFromCM(cm):
    constraints=coral.Set()
    for element in cm.constraint:
        constraints.unionWith(element.__getAllParts__().select(lambda p: p.oclIsKindOf(L.Constraint)))

    return constraints

class ConstraintTab(ModelerTool,QWidget,PropertyEditor):
    def __init__(self,parent):
        
        QWidget.__init__(self,parent)
        PropertyEditor.__init__(self,parent,manager=None)
        ModelerTool.__init__(self)
        
        self.setFocusPolicy(QWidget.StrongFocus)

        self.setIcon(iconcache.getIcon("constraint", "constraint"))
        self.setName('Constraint')
        self.setCaption(self.trUtf8("Constraint"))

        self.setMinimumSize(QApplication.globalStrut())

        self.constraintTabLayout = QHBoxLayout(self)
        self.constraintTabLayout.setSpacing(0)
        self.constraintTabLayout.setMargin(0)

        self.bFrame = QFrame(self,'bFrame')
        self.bFrame.setMinimumSize(QSize(30,30))
        self.bFrame.setFrameShape(QFrame.StyledPanel)
        self.bFrame.setFrameShadow(QFrame.Raised)
        self.bFrame.setLineWidth(0)

        self.newConstraintModelButton = QPushButton(self.bFrame, "newConstraintModelButton")
        self.newConstraintModelButton.setIconSet(QIconSet(iconcache.getIcon("newAction")))
        self.newConstraintModelButton.setGeometry(QRect(0,16,30,30))
        QToolTip.add(self.newConstraintModelButton,"Load new Constraints")
        
        self.newConstraintModelButton.show()

        self.loadConstraintModelButton = QPushButton(self.bFrame,"loadConstraintModelButton")
        self.loadConstraintModelButton.setIconSet(QIconSet(iconcache.getIcon("linkAction")))
        self.loadConstraintModelButton.setAccel(Qt.CTRL+Qt.Key_L)
        self.loadConstraintModelButton.setGeometry(QRect(0,46,30,30))
        QToolTip.add(self.loadConstraintModelButton, "View Constraints")
        self.loadConstraintModelButton.show()

        self.saveConstraintModelButton = QPushButton(self.bFrame, "saveConstraintModelButton")
        self.saveConstraintModelButton.setIconSet(QIconSet(iconcache.getIcon("filesave")))
        self.saveConstraintModelButton.setGeometry(QRect(0,76,30,30))
        QToolTip.add(self.saveConstraintModelButton, "Save")
        self.saveConstraintModelButton.show()

        self.targetButton = QPushButton(self.bFrame, "targetButton")
        self.targetButton.setIconSet(QIconSet(iconcache.getIcon("target", "constraint")))
        self.targetButton.setGeometry(QRect(0,116,30,30))
        QToolTip.add(self.targetButton, "Set Target Model")
        self.targetButton.show()

        self.activateDeactivateButton = QPushButton(self.bFrame, "activateDeactivateButton")
        self.activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("enable", "constraint")))
        self.activateDeactivateButton.setGeometry(QRect(0,146,30,30))
        QToolTip.add(self.activateDeactivateButton, "Activate/Deactivate")
        self.activateDeactivateButton.show()

        
        
        self.inProgressPicture = QPixmap(imagePath+"/trafficRed.png")
        self.offProgressPicture = QPixmap(imagePath+"/trafficGreen.png")
        self.progressLabel = QLabel(self.bFrame)
        self.progressLabel.setPixmap(self.offProgressPicture)
        self.progressLabel.setGeometry(QRect(0,176,30,70))
        self.progressLabel.show()
        QToolTip.add(self.progressLabel, "Evaluation Status")

        self.newConstraintModelButton.connect(self.newConstraintModelButton,SIGNAL("clicked()"),self.loadConstraintProject)
        self.loadConstraintModelButton.connect(self.loadConstraintModelButton,SIGNAL("clicked()"),self.pick)
        self.saveConstraintModelButton.connect(self.saveConstraintModelButton,SIGNAL("clicked()"),self.saveModel)
        self.targetButton.connect(self.targetButton,SIGNAL("clicked()"),self.targetModel)
        self.activateDeactivateButton.connect(self.activateDeactivateButton,SIGNAL("clicked()"),self.activateDeActivate)

        

        self.bFrame.show()
        self.constraintTabLayout.addWidget(self.bFrame)
        self.modelWidget = None

        self.resize(500,300)
        self.connected=0
        self.show()

    def setProgressPictureOn(self):
        self.progressLabel.setPixmap(self.inProgressPicture)
        
    def setProgressPictureOff(self):
        self.progressLabel.setPixmap(self.offProgressPicture)

    def onConstraintModelOrTargetChange(self, tr):
        """Collects the changed constraint models in a set and updates the model widget for each model. Also runs updateModelWidget() if target model has changed"""
        tm=coral.getTransactionManager()
        tr=tm.getCurrentTransaction()
        set = coral.Set()
        
        for i in xrange(tr.size()):
            c=tr[i]
            if isinstance(c,coral.ElementChangeSlotCommand):
                element=c.getSlot().getElement()
                if element.oclIsKindOf(L.ConstraintModel):
                    set.append(element)
                        
                if self.modelWidget.cm.target and element == self.modelWidget.cm.target:
                    self.modelWidget.updateCMTargetLabel()
        for element in set:
            if self.modelWidget.cm == element:
                self.modelWidget.updateModelWidget()

##    def deactivateModelWidget(self,mw):
##        """Removes the widget from the active list"""
##        if mw in self.activeModelWidgets.values():
##            self.activeModelWidgets.pop(mw.cm)
        
##    def focusOn(self, element):
##        """Gets the element that is in focus. Delegates the update of the focus element to its children"""
##        if element in self.activeModelWidgets:
##            self.activeModelWidgets[element].focusOn(element)
##            self.constraintModelTab.showPage(self.activeModelWidgets[element])

##        self.focus = element
        

    def pick(self):
        r=ElementPick().pick(None, L.ConstraintModel)
        if r:
            self.loadConstraintModel(r)
        
    def loadConstraintModel(self, r):
        """Loads a constraint model into an own tab, only if it isn't already loaded"""
        if not self.modelWidget:
            coral.getTransactionManager().addObserver(self.onConstraintModelOrTargetChange)
            self.modelWidget=ModelWidget(self,r)
            self.modelWidget.show()
            self.constraintTabLayout.addWidget(self.modelWidget)
        elif r == self.modelWidget.cm:
            return
        else:
            if self.modelWidget.cm.target:
                r.target = self.modelWidget.cm.target
            self.modelWidget.cm = r
            self.modelWidget.updateModelWidget()

        
        if not isActive(r):
            #Uses loadConstraintModel from engine.py so the CM is added to inActiveConstraintModels, the CM is already loaded into the tab.
            loadConstraintModel(r)
        else:
            #if the CM is already active when loaded into the tab the button should indicate this
            self.activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("disable", "constraint")))
        

    def loadConstraintProject(self):
        """Loads a new project with constraints to be used"""
            
        project = None
        project = action["openProject"].__call__()

        if not project:
            return
        
        if not self.modelWidget:
            coral.getTransactionManager().addObserver(self.onConstraintModelOrTargetChange)
            self.newModel(project)
        else:
            foundEmptyTab = False
            for e in action.getCurrentHandler("modeler").editorSet.getEditors():
                if isinstance(e,coral.addins.constraint.constraintTab.ConstraintTab):
                    if not e.modelWidget:
                        foundEmptyTab = True
                        action.getCurrentHandler("modeler").tabs[(1,1)].showPage(e)
                        coral.getTransactionManager().addObserver(e.onConstraintModelOrTargetChange)
                        e.newModel(project)
                        break

            if not foundEmptyTab:
                action["openConstraint"].__call__()
                for e in action.getCurrentHandler("modeler").editorSet.getEditors():
                    if isinstance(e,coral.addins.constraint.constraintTab.ConstraintTab):
                        if not e.modelWidget:
                            action.getCurrentHandler("modeler").tabs[(1,1)].showPage(e)
                            coral.getTransactionManager().addObserver(e.onConstraintModelOrTargetChange)
                            e.newModel(project)
                            break
                        

    def newModel(self, project):
        """Creates a new constraint model in the current project. If thera are no project selected it will
        create a new one."""
        # Gets the current focus from outline editor, so the user doesn't have to
        # click on some element just to set focus in the Constraint Tab
        if self.focus == None:
            for e in action.getCurrentHandler("modeler").editorSet.getEditors():
                if isinstance(e,coral.modeler.outline.outlineeditor.OutlineEditor):
                    self.focus= e.focus
                    break

        if not project:
            return

        if len(project.contents())>1:
               cPackage = None
               cPackage=ElementPick().pick(project.contents()[0], L.Package)
        else:
            cPackage = project.contents()[0]

        if not cPackage.oclIsKindOf(L.Package):
            return
        
        coral.getTransactionManager().beginModifyCmd()
        try:
            p = L.Package()
            p.name = "Constraints"

            cm = L.ConstraintModel()
            p.element.append(cm)

            set = coral.Set()
            set.append(cPackage)
            cp = coral.ModelCopy(set,coral.MODELCOPY_DONT_ATTACH_TO_PARENTS)

            cm.name = unicode(cp[0].name)

            
            if not cp[0] in cm.constraint:
                cm.constraint.append(cp[0])


            if self.focus == None or self.focus.__getRootsProject__() == None:
                prj = coral.Project()
                p.__setProject__(prj)
            else:
                p.__setProject__(self.focus.__getRootsProject__())
                                
            if not cp[0] in p.element:
                p.element.append(cp[0])

            project.invalidate()
            
            self.notifyFocusOn(p)
            self.focus = p
        
            self.modelWidget=ModelWidget(self,cm)
            self.modelWidget.show()
            self.constraintTabLayout.addWidget(self.modelWidget)
            loadConstraintModel(cm)

        finally:
            coral.getTransactionManager().endModifyCmd()

                        

    def saveModel(self):
        """Saves the project in focus"""
        if self.focus:
            action["saveProject"].__call__()

    def targetModel(self):
        """Selects and sets a target model for the constraint model object"""
        # Gets the current focus from outline editor, so the user doesn't have to
        # click on some element just to set focus in the Constraint Tab
        if self.focus == None:
            for e in action.getCurrentHandler("modeler").editorSet.getEditors():
                if isinstance(e,coral.modeler.outline.outlineeditor.OutlineEditor):
                    self.focus= e.focus
                    break
        if not self.modelWidget:
            return
        cm = self.modelWidget.cm
                
        ed=cm.__type__().getPropertyByName("target").getTarget()
        target=ElementPick().pick(cm.target, ed)
        if target == None:
            return
        
        coral.getTransactionManager().beginModifyCmd()
        try:
            cm.target = target
            cm.result.resetSlot()
        finally:
            coral.getTransactionManager().endModifyCmd()
            
            if isActive(self.modelWidget.cm):
                deActivateCM(self.modelWidget.cm)
                self.modelWidget.updateIcons()
                self.activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("enable", "constraint")))
                
            #self.setProgressPictureOn()
            #self.progressLabel.repaint()
            #reEvaluateAll(cm)
            #self.setProgressPictureOff()

    def activateDeActivate(self):
        if self.modelWidget:
            if isActive(self.modelWidget.cm):
                deActivateCM(self.modelWidget.cm)
                self.modelWidget.updateIcons()
            else:
                self.setProgressPictureOn()
                self.progressLabel.repaint()
                activateCM(self.modelWidget.cm)
                self.setProgressPictureOff()
                self.modelWidget.updateIcons()
            if isActive(self.modelWidget.cm):
                self.activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("disable", "constraint")))
            else:
                self.activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("enable", "constraint")))
            
    def updateIcons(self, cm):
        if cm == self.modelWidget.cm:
            self.modelWidget.updateIcons()


class ModelWidget(QWidget):
    def __init__(self,parent,cm):
        QWidget.__init__(self,parent)

        self.cm=cm

        self.cmTargetLabel=QLabel(self)
        self.cmTargetLabel.show()

        Form2Layout = QVBoxLayout(self)
        Form2Layout.setSpacing(0)
        Form2Layout.setMargin(0)
        Form2Layout.addWidget(self.cmTargetLabel)
        Form1Layout = QHBoxLayout(Form2Layout)
        Form1Layout.setSpacing(0)
        Form1Layout.setMargin(0)
        
        self.cr = ConstraintResult(self,cm)
        self.cr.show()
        self.cr.setColumnWidth(0,240)
        self.ol = OffendingList(self)
        self.ol.show()
        self.ol.setColumnWidth(0,220)
        self.te = QTextEdit(self)
        self.te.setReadOnly(True)
        self.te.setMaximumSize(QSize(32767,80))
        self.te.show()
        self.label=QLabel(self)
        self.label.setText("Message")
        self.label.show()
        Form1Layout.addWidget(self.cr)
        Form1Layout.addWidget(self.ol)
        Form2Layout.addWidget(self.label)
        Form2Layout.addWidget(self.te)
        self.updateModelWidget()

    def updateIcons(self):
        if isActive(self.cm):
            for e in self.cr.qlvItems.values():
                if e.hasChildren:
                    icon=iconcache.getIcon("stop", "constraint")
                    if icon:
                        e.setPixmap(0,icon)
                else:
                    icon=iconcache.getIcon("apply", "constraint")
                    if icon:
                        e.setPixmap(0,icon)
        else:
            for e in self.cr.qlvItems.values():
                if e.hasChildren:
                    icon=iconcache.getIcon("stop_disabled", "constraint")
                    if icon:
                        e.setPixmap(0,icon)
                else:
                    #only constraints should have this icon, not an issue in the above case, since only constraints can have children
                    if e.subject.oclIsKindOf(L.Constraint):
                        icon=iconcache.getIcon("apply_disabled", "constraint")
                        if icon:
                            e.setPixmap(0,icon)
        
    def activate(self):
        """Activates the checking in the engine for this constraint model.
        The icons set when the items are updated due to changes in the model"""
        loadConstraintModel(self.cm)
        #activateCM(self.cm)

    def deActivate(self):
        """Deactivates checking in the engine and set icons to gray disabled icons.
        Used when the model tab is closed"""
        deActivateCM(self.cm)
        for e in self.cr.qlvItems.values():
            if e.hasChildren:
                icon=iconcache.getIcon("stop_disabled", "constraint")
                if icon:
                    e.setPixmap(0,icon)
            else:
                icon=iconcache.getIcon("apply_disabled", "constraint")
                if icon:
                    e.setPixmap(0,icon)
                

    def updateModelWidget(self):
        """Updates all the child widgets if a changed in the constraint model occure."""
        self.constraint2result={}
        for constraint in getAllConstraintsFromCM(self.cm):
            self.constraint2result[constraint]=[]
        for result in self.cm.result:
            if result.constraint:
                self.constraint2result[result.constraint].append(result)
            else:
                self.constraint2result[result.constraint]=[result]

        self.cr.updateModel(self.constraint2result)
        self.ol.update(None)
        self.te.setText("")

        self.updateCMTargetLabel()

        if isActive(self.cm):
            self.parent().activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("disable", "constraint")))
        else:
            self.parent().activateDeactivateButton.setIconSet(QIconSet(iconcache.getIcon("enable", "constraint")))


    def updateCMTargetLabel(self):
        if not self.cm.target:
            if not self.cm.name == "":
                self.cmTargetLabel.setText("ConstraintModel: "+self.cm.name+" Target:")
            else:
                self.cmTargetLabel.setText("ConstraintModel: (noname)  Target:")
        else:
            if not self.cm.name == "" and not self.cm.target.name == "":
                self.cmTargetLabel.setText("ConstraintModel: "+self.cm.name+" Target: "+self.cm.target.name)
            elif self.cm.name == "" and not self.cm.target.name == "":
                self.cmTargetLabel.setText("ConstraintModel: (noname)  Target: "+self.cm.target.name)
            elif not self.cm.name == "" and self.cm.target.name == "":
                self.cmTargetLabel.setText("ConstraintModel: "+self.cm.name+" Target: (noname)")
            else:
                self.cmTargetLabel.setText("ConstraintModel: (noname)  Target: (noname)")

        

    def clickedItem(self,element):
        self.ol.update(element)
        if element.oclIsKindOf(L.Result):
            self.te.setText(str(element.message))
            self.te.show()
        elif element.oclIsKindOf(L.Constraint):
            self.te.setText(str(element.description))
            self.te.show()
        else:
            self.te.setText("")

    def focusOn(self, element):
        self.cr.focusOn(element)
        if element.oclIsKindOf(L.Constraint) or element.oclIsKindOf(L.Result):
            self.ol.update(element)


class ConstraintResult(Editor,QListView):
    """List widget representing the tree"""
    def __init__(self,parent,cm,excludeCriteria=None,includeCriteria=None):
        Editor.__init__(self)
        QListView.__init__(self,parent)
        self.cm=cm
        self.setCaption("Model Outline")
        self.pickMode=0
        self.selectedConstraints = {}

        self.setSelectionMode(QListView.Extended)
        
        
        self.setRootIsDecorated(1)
        self.setTreeStepSize(12)
        self.addColumn("Constraint")
        
        self.qlvItems={}
        self.connect(self,SIGNAL("clicked(QListViewItem *)"),
                     self.clicked)
        self.connect(self,SIGNAL("rightButtonClicked(QListViewItem *,const QPoint &, int)"),
                     self.rightButtonClicked)
        self.tm=coral.getTransactionManager()     
        self.result=None

        self.contextMenuExtensions=[]

        constraint_set=coral.Set()

        self.constraint2result={}
        for constraint in getAllConstraintsFromCM(self.cm):
            self.constraint2result[constraint]=[]
        for result in self.cm.result:
            if result.constraint:
                self.constraint2result[result.constraint].append(result)
            else:
                self.constraint2result[result.constraint] = [result]
        for element in cm.constraint:
            constraint_set.unionWith(element.__getAllParts__().select(lambda p: p.oclIsKindOf(L.Constraint)))

        if not self.cm.target == None: 
            for c in constraint_set:
                self.addConstraint(c)

        for r in cm.result.select(lambda l:l.constraint == None):
            self.addResult(r)

        
    
    def updateModel(self,constraint2result):
        self.constraint2result=constraint2result
        temp = self.qlvItems.copy()
        for i in temp:
            self.removeItem(temp[i])
            
        self.qlvItems={}
        
        if not self.cm.target == None: 
            for constraint,results in constraint2result.items():
                self.addConstraint(constraint)
                
        for r in self.cm.result.select(lambda l:l.constraint == None):
            self.addResult(r)

        
                    
    def focusOn(self, element):
        self.focus=element
        self.clearSelection()

        if not self.qlvItems.has_key(element):
            return

        p = self.qlvItems[element]
        self.setSelected(p,1)
        self.ensureItemVisible(p)

        
    def addConstraint(self,constraint):
        """Adds constraints to the list"""
        item = ConstraintListItem(self, constraint)
        self.__setExpandMark(item)
        self.qlvItems[constraint] = item
        assert self.qlvItems.has_key(constraint)
        return item

    def addResult(self,result):
        """Adds results to the list"""
        if self.qlvItems.has_key(result):
            return self.qlvItems[result]
        item = ElementListItem(self, result)
        self.qlvItems[result] = item
        assert self.qlvItems.has_key(result)
        return item
    
    def __setExpandMark(self, item):
        b = item.checkExpandMark(self.cm)
        if b or ( not b and item.isExpandable() ):
            item.setExpandable(b)
            
    def addElementItem(self,element,parent,recurse=0):
        """Add a new ListItem for an element to parent, if it passes
        the matching criteria."""
        
        if self.qlvItems.has_key(element):
            return self.qlvItems[element]
        item = ElementListItem(parent, element)
        self.__setExpandMark(item)
        self.qlvItems[element] = item
        return item

    def __removeItem(self, item):
        i = item.firstChild()
        while i:
            next = i.nextSibling()
            self.__removeItem(i)
            i = next
        if self.qlvItems.has_key(item.subject):
            del self.qlvItems[item.subject]
            
        # Qt oddity, can't call parent() on top-most elements
        p = item.parent()
        if not p:
            p = item.listView()
        if p is not None:
            p.takeItem(item)
        
    def removeItem(self, item):
        """Removes an item from a ListView. The given parameter
        cannot be used after this. Especially note how this means
        you have to save item.nextSibling() before calling this method."""
        self.__removeItem(item)
        
    def clicked(self,item):
        if not item:
            return

        if item.subject.oclIsKindOf(L.Result):
            self.parent().parent().notifyFocusOn(item.subject)
        item.updateChildren(recurse=1)
        
        self.parent().clickedItem(item.subject)

    def rightButtonClicked(self,item,qp,col):
        if not item or not item.subject.oclIsKindOf(L.Constraint):
            return
        self.item=item
        reEvaluateMenu=QPopupMenu(self,"reeval")

        reEvaluateMenu.insertItem("Evaluate constraint(s)",self.reEvaluateConstraint)
        reEvaluateMenu.insertItem("View constraint definition",self.viewDefinition)
        reEvaluateMenu.popup(QPoint(qp.x(),qp.y()),0)

        self.selectedConstraints.clear()
        for p in self.qlvItems.keys():
            if self.qlvItems[p].isSelected() and p.oclIsKindOf(L.Constraint):
                self.selectedConstraints[p] = self.qlvItems[p]

    def reEvaluateConstraint(self):
        self.parent().parent().setProgressPictureOn()
        self.parent().parent().progressLabel.repaint()
        self.tm.beginModifyCmd()
        resultList = []
        try:
            for r in self.parent().cm.result[:]:
                for k in self.selectedConstraints.keys():
                    if r.name == k.name:
                        self.parent().cm.result.remove(r)

            for k in self.selectedConstraints.keys():
                for r in checkSingleConstraint(k, self.parent().cm.target):
                    resultList.append(r)
            for r in resultList:
                addResult(r, self.parent().cm)
        finally:
            self.tm.endModifyCmd()
            self.parent().parent().setProgressPictureOff()

    def viewDefinition(self):
        self.parent().parent().notifyFocusOn(self.item.subject)
        self.window = action.getCurrentHandler("modeler")
        for e in self.window.editorSet.getEditors():
            if isinstance(e,coral.modeler.property.propertyeditor.PropertyEditorManager):
                action.getCurrentHandler("modeler").tabs[(1,1)].showPage(e)
    
    
class OffendingList(QListView):
    def __init__(self,parent):

        QListView.__init__(self,parent)
        self.olItems={}
        self.addColumn("Offending")
        self.connect(self,SIGNAL("clicked(QListViewItem *)"),
                     self.clicked)

    def clicked(self,item):
        if not item:
            return
        self.parent().parent().notifyFocusOn(item.subject)
        
    def update(self,element):

        self.clear()
        self.hide()
        if not element:
            self.show()
            return
        temp = self.olItems.copy()
        for i in temp:
            del self.olItems[i]
        self.olItems = {}
        
        if not element.oclIsKindOf(L.Result):
            self.show()
            return
        for o in element.offending:            
            it=ResultItem(self,o)
            self.olItems[o]=it
        self.show()


class ResultItem(QListViewItem):
    def __init__(self,parent,subject):
        if subject.__hasSlotByName__("name"):
            self.name=subject.name+" : "+subject.__type__().getName()
        else:
            self.name=" : "+subject.__type__().getName()
        QListViewItem.__init__(self, parent, str(self.name))
        self.subject=subject



### Classes that representate the qt list items for the constraints and the results ### 
class ListItem(QListViewItem):
    def __init__(self, parent, subject):
        QListViewItem.__init__(self, parent)
        self.subject = subject
        self.onElementChanged(0)
        self.setOpen(0)
        
    def setOpen(self, b):
        """This is called when the node is first expanded.
        We use it to populate ourselves."""
        if b:
            self.updateChildren(recurse=1)
        QListViewItem.setOpen(self, b)

    def updateChildren(self,recurse):
        pass

class ElementListItem(ListItem):
    def __init__(self, parent, subject):
        ListItem.__init__(self, parent, subject)
        self.hasChildren = False

        typename = self.subject.__type__().getName().lower()
        icon=iconcache.getIcon(typename + "lv", metamodelName(self.subject) )
        if icon:
            self.setPixmap(0,icon)

    def onElementChanged(self,recurse=1):
        if not self.subject:
            return

        if not self.subject.__getRootsProject__():
            self.__kill__()
            return

        editor = self.listView()
        if not editor:
            return

        # Explicitly call it so that our view of our children is updated
        # but @recurse can still stop the potentially length recursion
        self.updateChildren(recurse)

        name = getFQName(self.subject, 1)
        if not name:
            name="(noname)"
        else:
            # Must flatten name because QT can't show multi-line strings
            # in ListViews
            name = re.sub("\n", " ", name)

        # Prepend the name of the property owning the element, if exists
        if self.subject.__parentSlot__() and self.subject.__parentSlot__():
            name=str(self.subject.__parentSlot__().__type__().getOppositeProperty().getName())+" "+name
            
        self.setText(0,QString(name+":"+self.subject.__type__().getName()))

        if editor.pickMode and editor.includeCriteria:
            selectable=0
            for c in editor.includeCriteria:
                if c(self.subject):
                    selectable=1
                    break
            self.setSelectable(selectable)
                
    def checkExpandMark(self, outliner):
        """ Return always False since our result item can not have cildren"""
        return self.hasChildren
            
    def __kill__(self):
        logger.info("__kill__"+unicode(self)+" "+unicode(self.parent()))
        p = self.parent()
        if not p:
            p = self.listView()
        if p:
            p.takeItem(self)

class ConstraintListItem(ListItem):
    def __init__(self, parent, subject):
        ListItem.__init__(self,parent, subject)
        self.__parent=parent
        self.hasChildren = False
        self.editor = self.listView()
        
    def updateChildren(self,recurse):
        children={}
        for i in self.__parent.constraint2result[self.subject]:
        # for i in self.subject.__getSlotByOppositeName__(L.ConstraintModel, "constraint").result.select(lambda l:l.constraint==self.subject):
            editor = self.listView()
            if editor.qlvItems.has_key(i):
                item = editor.qlvItems[i]
                self.insertItem(item)
                children[item] = 1
            else:
                if recurse and isinstance(i,coral.Element):
                    an_item = editor.addElementItem(i,self,recurse-1)
                    if not an_item:
                        continue 
                    children[an_item] = 1

        # remove the ListItem children that should not be there
        i = self.firstChild()
        while i:
            next = i.nextSibling()
            if not children.has_key(i):
                i.__kill__()
            i = next

    def onElementChanged(self,recurse=1):
        if not self.subject:
            return

        if recurse:
            self.updateChildren(recurse)
       
        name = self.subject.name
            
        if not name:
            name="(noname)"
        self.setText(0,QString(name+":"+self.subject.__type__().getName()))

        self.editor = self.listView()
        self.setSelectable(1)

                                    
    def checkExpandMark(self, outliner):
        """ Return True if we have even one child which we can
        show as a child"""
        if not self.subject or self.subject.oclIsKindOf(L.Result):
            return False

        constraint_models=self.subject.__getSlotByOppositeName__(L.ConstraintModel, "constraint")
        constraints = constraint_models.select(lambda l:l == self.listView().cm)

                
        for element in self.__parent.constraint2result[self.subject]:
            if element.constraint == self.subject and len(self.__parent.constraint2result[self.subject]):
                if isActive(self.editor.cm):
                    icon=iconcache.getIcon("stop", "constraint" )
                else:
                    icon=iconcache.getIcon("stop_disabled", "constraint" )
                if icon:
                    self.setPixmap(0,icon)
                self.hasChildren = True
                return self.hasChildren
            
        if isActive(self.editor.cm):
            icon=iconcache.getIcon("apply", "constraint" )
        else:
            icon=iconcache.getIcon("apply_disabled", "constraint" )
        if icon:
            self.setPixmap(0,icon)

        self.hasChildren = False
        return self.hasChildren

        
    def __kill__(self):
        self.listView().removeItem(self)

    def compare(self, compareItem, column, ascending):
        if not self.subject or not compareItem.subject:
            return 0
        if self.subject.oclIsKindOf(L.Constraint) and compareItem.subject.oclIsKindOf(L.Constraint):
            if self.hasChildren and not compareItem.hasChildren:
                return -1
            elif not self.hasChildren and compareItem.hasChildren:
                return 1
            elif (self.hasChildren and compareItem.hasChildren) or (not self.hasChildren and not compareItem.hasChildren):
                return QString.compare(str(self.subject.name), str(compareItem.subject.name))
        return 0
        
