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

import copy
import string
import types
import traceback

from qt import *
try:
    from qttable import *
except:
    pass

from coral import log
from coral.modeler import iconcache
from coral.modeler.editor import *
from coral import modeler
from coral import configuration
from coral.modeler.getFQName import *
from coral.modeler.dlg.PropertyEditorManagerDlg import PropertyEditorManagerDlg
from coral.modeler.dlg.TextInputDlg import TextInputDlg
logger=log.getLogger("PropertyEditor")
from coral.modeler.window import CoralActionHandler
import coral
from coral.modeler.coralmodeler import CoralModeler
from coral.modeler.extension import PropertyEditor
import os

from coral.log import getLogger
_log=getLogger("coral.modeler.property.propertyeditor")

from coral.util.text_editor import TextualEditor, CouldNotEdit

class SlotEditor(TextualEditor):
    """Runs external editor for editing a slot value."""
    def __init__(self, element, property, editors):
        self.__element = element
        self.__property = property
        TextualEditor.__init__(self, editors)

    def _get(self):
        slot = getattr(self.__element, self.__property.getName())
        ls = []
        for i in slot:
            ls.append(i)
        return ls

    def _set(self, ls):
        lst = []
        for line in ls:
            s = coral.String.newAtom()
            s.__setString__(line)
            lst.append(s)
        setattr(self.__element, self.__property.getName(), lst)

def private_property_sorter(x, y):
    return cmp(x.getName(), y.getName())


class PropertyEditorManager(Editor,CoralActionHandler,PropertyEditorManagerDlg):
    """
  
    """
    
    def __init__(self,parent,subEditors=None):
        self.follow=1
        
        self.lastTab=None

        self.focus=None
        self.pages=[]
        self.subEditors=[]

        self.edited=0

        PropertyEditorManagerDlg.__init__(self,parent)
        CoralActionHandler.__init__(self)
        Editor.__init__(self)

        self.setIcon(iconcache.getIcon("properties"))
        if self.editorTab.currentPage():
            self.editorTab.removePage(self.editorTab.currentPage())
        self.connect(self.editorTab,SIGNAL('currentChanged(QWidget *)'),self.currentChanged)
        self.setupSubEditors(subEditors)

        coral.getTransactionManager().addObserver(self.onModelChanged)

    def setEdited(self,edit=1):
        self.edited=edit
        if isinstance(self.parent(),CompositePropertyEditor):
            self.parent().setEdited(edit)
        

    def onModelChanged(self,x):
        _log.debug("Begin PropertyEditorManagr.onModelChanged")

        # This always updates the property editor
        if not self.focus:
            return
        r = self.focus.__getRootsProject__()
        if r:
            self.focusOn(self.focus)
            if isinstance(self.parent(), CompositePropertyEditor):
                self.parent().updateValues()
        else:
            self.focusOn(None)
        return

    def followSelection(self,follow=-1):
        if follow==-1:
            follow=not self.follow
        if follow:        
            self.bFollow.setState(QButton.On)
        else:
            self.bFollow.setState(QButton.Off)
        self.follow=follow

        
    def canEdit(self,e):
        return 1

     
    def setupSubEditors(self,subEditors):
        if subEditors:
            for e in subEditors:
                se=e(self.tab,self)
                se.hide()               
                self.subEditors.append(se)
            
        else:
            self.subEditors=[]
            self.subEditors.append(ElementPE(parent=self.tab,manager=self))

            for e in CoralModeler.extensionRegistry.getRegisteredExtensionsByType("PropertyEditor"):
                #_log.debug("Extension: "+unicode(e[1]))
                se=e[1](parent=self.tab,manager=self)
                se.hide()
                self.subEditors.append(se)

    def __name_sorter(self, x, y):
        if isinstance(x, ElementPE):
            return -1
        if isinstance(y, ElementPE):
            return 1
        return cmp(x.name(), y.name())

            
    def focusOn(self,element):

        if not self.follow and not element==self.focus:
            return
        #Editor.focusOn(self,element)
        
        tempTab=self.lastTab

      
        exclude=[]
        
        # apply the changes to the last tab
        if self.lastTab and not coral.getTransactionManager().isUndoOrRedo():
            self.lastTab.apply()

        Editor.focusOn(self,element)
        self.focus=element

        if self.focus:
            # find subEditors to be excluded
            possible = []
            for p in self.subEditors:
                if p.canEdit(element):
                    exclude=exclude+p.supercedes()
                    possible.append(p)

            # Exclude them
            for c in exclude:
                for p in copy.copy(possible):
                    if isinstance(p, c):
                        possible.remove(p)

            # Remove subEditors we don't need
            for p in copy.copy(self.pages):
                if p in possible:
                    continue
                p.hide()
                self.editorTab.removePage(p)
                self.pages.remove(p)

            # insert suitable subEditors
            possible.sort(self.__name_sorter)

            i = 0
            for p in possible:
                # This is a bit clumsy, we duplicate some code...
    
                if len(self.pages) > i and self.pages[i] == p:
                    # Found, continue
                    try:
                        self.currentChanged(p)
                    except:
                        logger.exception("Exception while focusOn event, tab:"+str(p)+". The pane will not be shown")
                        p.hide()
                        self.pages.remove(p)
                        self.editorTab.removePage(p)
                    else:
                        i += 1
                    continue
                else:
                    # Wrong order, so we remove it wherever it is
                    if p in self.pages:
                        self.pages.remove(p)
                        self.editorTab.removePage(p)
                    try:
                        p.focusOn(element)
                        self.editorTab.insertTab(p, p.name(), i)
                        self.editorTab.showPage(p)
                        tempTab=p
                    except:
                        logger.exception("Exception while focusOn event, tab:"+str(p)+". The pane will not be shown")
                    else:
                        self.pages.insert(i, p)
                        i += 1
            

            if self.pages:
                if tempTab in self.pages:
                    self.lastTab=tempTab
                else:
                    moreImportant=self.pages[0]
                    for p in self.pages:
                        if p.name()==element.__type__().getName():
                            moreImportant=p
                    for p in self.pages:
                        if p.importance()>moreImportant.importance():
                            moreImportant=p
                    #print "more important ",moreImportant
                    self.editorTab.showPage(moreImportant)
                    self.lastTab=moreImportant

        else:
            for p in self.pages:
                p.hide()
                self.editorTab.removePage(p)
            self.pages = []

        if isinstance(self.parent(),CompositePropertyEditor) and self.focus:
            self.parent().currentChanged(self.parent().itable.currentRow(),0,recursive=0)

    def currentChanged(self,p):
        if self.lastTab and not coral.getTransactionManager().isUndoOrRedo() and self.focus:
            self.lastTab.apply()
        if not isinstance(p,PropertyEditor):
            return
        p.focusOn(self.focus)
        self.lastTab=p


class ListWidget(QWidget,PropertyEditor):
    etype_string=0
    etype_object=1

    def __init__(self,parent,allowLink,allowNew,manager):
        QWidget.__init__(self,parent, "", Qt.WType_Dialog+Qt.WShowModal)
        PropertyEditor.__init__(self,parent,manager)
                
        self.edited=0
        self.multiplicity=0
        self.etype=0

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

        self.f1 = QFrame(self,'f1')
        self.f1.setProperty('frameShape',QVariant(QFrame.StyledPanel))
        self.f1.setProperty('frameShadow',QVariant(QFrame.Raised))
        f1Layout = QHBoxLayout(self.f1)
        f1Layout.setSpacing(3)
        f1Layout.setMargin(5)

        self.itable = QTable(0,len(self.getHeader()),self.f1,'itable2')
        self.itable.setReadOnly(1)
                
        self.connect(self.itable,SIGNAL('doubleClicked(int,int,int,const QPoint &)'),self.doubleClicked)
        self.itable.setColumnStretchable(self.itable.numCols()-1,1)
        f1Layout.addWidget(self.itable)
        Form2Layout.addWidget(self.f1)

        self.f2 = QFrame(self,'f2')
        self.f2.setProperty('frameShape',QVariant(QFrame.StyledPanel))
        self.f2.setProperty('frameShadow',QVariant(QFrame.Raised))
        f2Layout = QVBoxLayout(self.f2)
        f2Layout.setSpacing(3)
        f2Layout.setMargin(5)

        self.addB = QToolButton(self.f2,'addB')
        f2Layout.addWidget(self.addB)

        if not allowNew:            
            self.addB.hide()

        self.linkB = QToolButton(self.f2,'alinkB')
        f2Layout.addWidget(self.linkB)
        
        if not allowLink:
            self.linkB.hide()
            
          

        self.delB = QToolButton(self.f2,'delB')
        f2Layout.addWidget(self.delB)

        self.upB = QToolButton(self.f2,'upB')
        f2Layout.addWidget(self.upB)

        self.downB = QToolButton(self.f2,'downB')
        f2Layout.addWidget(self.downB)

        
        spacer = QSpacerItem(20,20,QSizePolicy.Minimum,QSizePolicy.Expanding)        
        f2Layout.addItem(spacer)
        Form2Layout.addWidget(self.f2)

        # Some window managers do not have an explicit
        # close button, so we add one here.
        self.closeB = QToolButton(self.f2, 'closeB')
        f2Layout.addWidget(self.closeB)

        i=0
        for h in self.getHeader():         
            self.itable.horizontalHeader().setLabel(i,h)
            i=i+1

        self.addB.setTextLabel("New element")
        self.addB.setPixmap(iconcache.getIcon("newAction"))
        self.connect(self.addB,SIGNAL("clicked()"),self.add)

        self.linkB.setTextLabel("Link")
        self.linkB.setPixmap(iconcache.getIcon("linkAction"))
        self.connect(self.linkB,SIGNAL("clicked()"),self.link)
        

        self.upB.setTextLabel("Move element up")
        self.upB.setPixmap(iconcache.getIcon("arrowUp"))
        self.connect(self.upB,SIGNAL("clicked()"),self.moveUp)
        
        self.downB.setTextLabel("Move element down")
        self.downB.setPixmap(iconcache.getIcon("arrowDown"))
        self.connect(self.downB,SIGNAL("clicked()"),self.moveDown)

        self.delB.setTextLabel("Delete selection")
        self.delB.setPixmap(iconcache.getIcon("deleteAction"))
        self.connect(self.delB,SIGNAL("clicked()"),self.deleteSelection)
        self.connect(self.itable,SIGNAL("valueChanged(int,int)"),self.setEdited)

        self.closeB.setTextLabel("Close Window")
        # self.closeB.setPixmap()
        self.closeB.setUsesTextLabel(True)
        self.connect(self.closeB, SIGNAL("clicked()"), self.close)

    def setEdited(self,c,r):
        self.edited=1
                
    def add(self,item=None):
        if self.multiplicity or not self.itable.numRows():
            coral.getTransactionManager().beginModifyCmd()
            try:
                if not item:
                    item=self.elementFactory()
                if item:
                    self.itable.setNumRows(self.itable.numRows()+1)
                    n = self.getListName()
                    if self.multiplicity:
                        getattr(self.focus, n).append(item)
                    else:
                        setattr(self.focus, n, item)
                    
                    self.load(self.itable.numRows()-1, item)
                    self.edited=1
                    self.focusOn(self.focus)
            finally:
                coral.getTransactionManager().endModifyCmd()


    def moveUp(self):
        i=1
        selectedRows=[]
        coral.getTransactionManager().beginModifyCmd()
        try:
            for x in range(self.itable.numRows()):
                if self.itable.isRowSelected(x,1):
                    selectedRows.append(x)
            a = getattr(self.focus,self.getListName())
            while i<self.itable.numRows():
                if self.itable.isRowSelected(i,1):
                    item = a[i-1]
                    a.drop_index(i-1)
                    a.insert(item, i)
                    self.load(i-1, a[i-1])
                    self.load(i, a[i])
                i += 1
        finally:
            coral.getTransactionManager().endModifyCmd()

        self.itable.clearSelection()
        for sel in selectedRows:
            if sel>0:
                sel=sel-1
            cellSelect=QTableSelection()
            cellSelect.init(sel,0)
            cellSelect.expandTo(sel,0)
            self.itable.addSelection(cellSelect)
            self.itable.setCurrentCell(sel,0)

        return

    def moveDown(self):
        i=self.itable.numRows()-2
        selectedRows=[]
        coral.getTransactionManager().beginModifyCmd()
        try:
            for x in range(self.itable.numRows()):
                if self.itable.isRowSelected(x,1):
                    selectedRows.append(x)
            a = getattr(self.focus,self.getListName())
            while i >= 0:
                if self.itable.isRowSelected(i,1):
                    item = a[i]
                    a.drop_index(i)
                    a.insert(item, i+1)
                    self.load(i, a[i])
                    self.load(i+1, a[i+1])
                i -= 1
        finally:
            coral.getTransactionManager().endModifyCmd()

        self.itable.clearSelection()
        for sel in selectedRows:
            if sel<self.itable.numRows()-1:
                sel=sel+1
            cellSelect=QTableSelection()
            cellSelect.init(sel,0)
            cellSelect.expandTo(sel,0)
            self.itable.addSelection(cellSelect)
            self.itable.setCurrentCell(sel,0)

        return

        
    def deleteAll(self):
        coral.getTransactionManager().beginModifyCmd()
        try:
            if self.itable.numRows():
                while self.itable.numRows():
                    self.itable.removeRow(0)
                self.edited=1
            if self.multiplicity:
                setattr(self.focus,self.getListName(),[])
            else:
                setattr(self.focus,self.getListName(),None)
        finally:
            coral.getTransactionManager().endModifyCmd()
            
    def deleteSelection(self):
        # IPP try to find a better way
        # This assumes that it is text!
        os=self.itable.numRows()
        offset=0
        i=0
        coral.getTransactionManager().beginModifyCmd()
        try:
            while i<self.itable.numRows():
                if self.itable.isRowSelected(i,1):
                    self.itable.removeRow(i)
                    if self.multiplicity:
                        getattr(self.focus,self.getListName()).remove(
                            getattr(self.focus,self.getListName())[i])
                    else:
                        setattr(self.focus,self.getListName(),None)
                else:
                    i=i+1
        finally:
            coral.getTransactionManager().endModifyCmd()

    def focusOn(self,element):
        if self.focus!=element:
            self.itable.setNumRows(0)
        self.focus=element

        try:
            values=getattr(self.focus,self.getListName())
            llist=len(values)
            self.multiplicity=1
        except:
            #it was not a list but a sclar, no problem
            val=getattr(self.focus,self.getListName())
            if val:
                llist=1
                values=[val]
            else:
                llist=0
                values=[]            
            self.multiplicity=0
            
        if element:
            self.itable.setNumRows(llist)
            r=0
            for c in values:
                self.load(r,c)
                r=r+1
        self.edited=0
        self.itable.adjustColumn(0)

        
    def apply(self):

        self.edited=0
        return
    
    
    def load(self,row,e):
        pass

    def save(self,row):
        assert(0)

    def doubleClicked(self,r,c,b,point):
        if self.multiplicity:
            value=getattr(self.focus,self.getListName())[r]            
        else:
            value=getattr(self.focus,self.getListName())

        #self.manager.editorSet.notifyFocusOn(value,exclude=self.manager)
        
class ElementTable(QTable):
    def cut(self):
        self.parent().cut()
    def copy(self):
        self.parent().copy()
    def paste(self):
        self.parent().paste()
    def deleteSelection(self):
        self.parent().deleteSelection()
        
class CompositePropertyEditor(QWidget,PropertyEditor):
    attach=0

    def __init__(self,parent,manager):
        
        PropertyEditor.__init__(self,parent,manager)
        QWidget.__init__(self,parent)
        
        self.setName(self.getName())
        self.focus=None
        self.edited=0
        self.list=[]
    
        self.currentItem=None
        
        x=0
        y=0
        sx=200
        sy=200

        
        self.itable = ElementTable(0,1,self,'table')
        self.itable.setSelectionMode(QTable.Single)
        self.itable.setColumnStretchable(0,1)
        self.itable.setMaximumSize(QSize(200,30000))
        self.itable.setRowMovingEnabled(1)
        self.itable.verticalHeader().hide()
        self.itable.setLeftMargin(0) 
        self.Form1Layout = QGridLayout(self,1,1,0,0,"Form1Layout")
        
        self.addB = QToolButton(self,"addB")

        self.Form1Layout.addWidget(self.addB,0,1)

        self.delB = QToolButton(self,"delB")

        self.Form1Layout.addWidget(self.delB,1,1)

        self.selectB = QToolButton(self,"selectB")

        self.Form1Layout.addWidget(self.selectB,2,1)
        
        self.upB = QToolButton(self,"upB")

        self.Form1Layout.addWidget(self.upB,3,1)
        
        self.downB = QToolButton(self,"downB")

        self.Form1Layout.addWidget(self.downB,4,1)

        self.Form1Layout.addMultiCellWidget(self.itable,0,5,0,0)
        spacer_3 = QSpacerItem(200,1,QSizePolicy.Fixed,QSizePolicy.Fixed)
        self.Form1Layout.addItem(spacer_3,6,0)

        self.editor = PropertyEditorManager(self,subEditors=self.getSubEditors())

        self.Form1Layout.addMultiCellWidget(self.editor,1,5,2,2)
        spacer = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
        self.Form1Layout.addItem(spacer,0,2)
        spacer_2 = QSpacerItem(20,20,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.Form1Layout.addItem(spacer_2,5,1)

        self.namelabel = QLabel(self,"namelabel")
        self.Form1Layout.addWidget(self.namelabel,0,2)
        
        i=0
        for h in [self.name()]:         
            self.itable.horizontalHeader().setLabel(i,h)
            i=i+1

        self.addB.setTextLabel("New element")
        self.addB.setPixmap(iconcache.getIcon("newAction"))
        self.connect(self.addB,SIGNAL("clicked()"),self.add)
                     
        self.delB.setTextLabel("Delete selection")
        self.delB.setPixmap(iconcache.getIcon("deleteAction"))
        self.connect(self.delB,SIGNAL("clicked()"),self.deleteSelection)

        self.selectB.setTextLabel("Select element")
        self.selectB.setPixmap(iconcache.getIcon("linkAction"))
        self.connect(self.selectB,SIGNAL("clicked()"),self.select)

        self.upB.setTextLabel("Move element up")
        self.upB.setPixmap(iconcache.getIcon("arrowUp"))
        self.connect(self.upB,SIGNAL("clicked()"),self.moveUp)
        
        self.downB.setTextLabel("Move element down")
        self.downB.setPixmap(iconcache.getIcon("arrowDown"))
        self.connect(self.downB,SIGNAL("clicked()"),self.moveDown)

        self.connect(self.itable,SIGNAL("currentChanged(int,int)"),self.currentChanged)
        self.connect(self.itable,SIGNAL('doubleClicked(int,int,int,const QPoint &)'),self.doubleClicked)

      
    def getSubEditors(self):
        return []
    
    def setEdited(self,edit=1):
        self.edited=edit

    def currentChanged(self,r,c,recursive=1):
        if not coral.getTransactionManager().isUndoOrRedo():
            self.apply()
            
        if self.list and r>=0:            
            for i in range(self.itable.numRows()):
                item = self.itable.item(i, 1)
                if item:
                    item.setText(self.getDescriptionOfElement(self.list[i]))

            if r>=self.itable.numRows():
                r=r-1
            if recursive:
                self.editor.focusOn(self.list[r])
                if self.editor.isHidden():
                    self.editor.show()
            
            self.currentItem=self.itable.item(r, 1)

            self.itable.setCurrentCell(r,c)
            self.itable.updateCell(r,c)
            
        else:
            if recursive:
                self.editor.focusOn(None)


    def updateValues(self):
        if self.list:
            for i in range(self.itable.numRows()):
                self.itable.setText(i,0,self.getDescriptionOfElement(self.list[i]))
 
    def copy(self):
        toCopy=[]
        for x in range(self.itable.numRows()):
            if self.itable.isRowSelected(x,1):
                toCopy.append(self.list[x])
        copied=MetaMM.modelcopy(toCopy,1)
        for c in copied:
            getattr(self.focus,self.getAssociationName()).remove(c)
        #self.project.mainWindow.clipboard.set(copied)

    def cut(self):
        coral.getTransactionManager().beginModifyCmd()
        try:
            self.copy()
            self.deleteSelection()
        finally:
            coral.getTransactionManager().endModifyCmd()
        
    def paste(self):
        coral.getTransactionManager().beginModifyCmd()
        try:
            toPaste=self.project.mainWindow.clipboard.getItems()[0]
            if not toPaste:
                return
            elements=MetaMM.modelcopy(toPaste,1)
            for e in elements:
                self.add(e)
        finally:
            coral.getTransactionManager().endModifyCmd()
        
    def add(self,element=None):
        coral.getTransactionManager().beginModifyCmd()
        try:
            if self.list and self.editor.isEnabled():
                if self.editor.lastTab:
                    self.editor.lastTab.apply()
                if self.editor.focus and self.currentItem:
                    self.currentItem.setText(
                        self.getDescriptionOfElement(self.editor.focus))
                self.itable.update()
                self.editor.setEnabled(1)
            self.itable.setNumRows(self.itable.numRows()+1)
    
            new=QTableItem(self.itable,QTableItem.Never)
            self.itable.setItem(self.itable.numRows()-1,0, new)
            self.edited=1
            if not element:
                e=self.elementFactory()
                if not e:
                    self.itable.setNumRows(self.itable.numRows()-1)
                    return
            else:
                e=element

            if self.focus.__getSlotByName__(self.getAssociationName()).getProperty().getMultUpper()==1:
                setattr(self.focus,self.getAssociationName(), e)
            else:
                getattr(self.focus,self.getAssociationName()).append(e)
                
            assert(self.filterAssociation(e))
            r = self.itable.numRows()-1

            if not self.focus.__getSlotByName__(self.getAssociationName()).getProperty().getMultUpper()==1:
                for i in xrange(len(getattr(self.focus,self.getAssociationName()))):
                    if getattr(self.focus,self.getAssociationName())[i]==e:
                        r=i
            # The new elements should be allowed in the list
            self.list.append(e)
            self.itable.setCurrentCell(r,0)
                
            if len(self.list)==1:
                self.currentChanged(0,0)

            new.setText(
                self.getDescriptionOfElement(e))

        finally:
            coral.getTransactionManager().endModifyCmd()

        self.itable.clearSelection()
        cellSelect=QTableSelection()
        cellSelect.init(r,0)
        cellSelect.expandTo(r,0)
        self.itable.addSelection(cellSelect) 

            
    def select(self):
        from coral.modeler.outline.outlineeditor import ElementPick
        attr = self.getAssociationName()
        r=ElementPick().pick(None, self.focus.__type__().getPropertyByName(attr).getTarget())
        if r:
            self.add(r)


    def deleteSelection(self,toDelete=None):
        r=self.itable.numRows()-1
        if not toDelete:
            toDelete=[]
            _log.debug("todelete "+unicode(self.itable.currentSelection()))
            for x in range(self.itable.numRows()):
                if self.itable.isRowSelected(x,1):
                    r=x
                    toDelete.append(x)
        
        toDelete.reverse()
        for i in toDelete:            
            self.itable.removeRow(i)
            self.edited=1
            self.list=self.list[:i]+self.list[i+1:]
            
        if self.editor and toDelete:
            self.editor.setEnabled(0)
            self.editor.hide()
            self.currentItem=None
            
        if r >= self.itable.numRows():
            r=self.itable.numRows()-1
            
        self.itable.setCurrentCell(r,0)
        self.apply()
        self.currentChanged(r,0)
        self.itable.clearSelection()
        cellSelect=QTableSelection()
        cellSelect.init(r,0)
        cellSelect.expandTo(r,0)
        self.itable.addSelection(cellSelect)
        

    def moveUp(self):

        if self.list and self.itable.numSelections()==1 and self.itable.currentRow()>0:

            theRow=self.itable.currentRow()

            self.itable.swapRows(theRow,theRow-1)
        
            self.list[theRow],self.list[theRow-1]=self.list[theRow-1],self.list[theRow]
            coral.getTransactionManager().beginModifyCmd()
            try:
                first=getattr(self.focus,self.getAssociationName()).remove(self.list[theRow])
                second=getattr(self.focus,self.getAssociationName()).remove(self.list[theRow-1])
                getattr(self.focus,self.getAssociationName()).insert(self.list[theRow],second)
                getattr(self.focus,self.getAssociationName()).insert(self.list[theRow-1],first)
                #setattr(self.focus,self.getAssociationName(),self.list)
            finally:
                coral.getTransactionManager().endModifyCmd()
                
            self.itable.clearSelection()
            self.itable.setCurrentCell(theRow-1,0)
            cellSelect=QTableSelection()
            cellSelect.init(theRow-1,0)
            cellSelect.expandTo(theRow-1,0)
            self.itable.addSelection(cellSelect)

            self.edited=1


    def moveDown(self):
        
        if self.list and self.itable.numSelections()==1 and self.itable.currentRow()<self.itable.numRows()-1:
            theRow=self.itable.currentRow()
            self.itable.swapRows(theRow,theRow+1)

            self.list[theRow],self.list[theRow+1]=self.list[theRow+1],self.list[theRow]
            coral.getTransactionManager().beginModifyCmd()
            try:
                # Move the elements in the abstract model
                first=getattr(self.focus,self.getAssociationName()).remove(self.list[theRow])
                second=getattr(self.focus,self.getAssociationName()).remove(self.list[theRow+1])
                getattr(self.focus,self.getAssociationName()).insert(self.list[theRow],second)
                getattr(self.focus,self.getAssociationName()).insert(self.list[theRow+1],first)
            finally:
                coral.getTransactionManager().endModifyCmd()
            self.itable.clearSelection()
            self.itable.setCurrentCell(theRow+1,0)
            cellSelect=QTableSelection()
            cellSelect.init(theRow+1,0)
            cellSelect.expandTo(theRow+1,0)
            self.itable.addSelection(cellSelect)
            self.edited=1

                
    
    def focusOn(self,element):
        if self.focus!=element:
            self.itable.setNumRows(0)
        self.focus=element
        self.list=[]
        self.editor.setEnabled(1)
        attr = self.getAssociationName()
        if element:
            prop = element.__type__().getPropertyByName(attr)
            if not prop:
                _log.warn("Some kind of weird (but harmless) error in the property manager:\n Element %s Property %s" % (element.__type__().getName(), attr))
                self.editor.focusOn(None)
                return
            
            if not prop.isOrdered():
                self.upB.hide()
                self.downB.hide()
            if not prop.isAssociation():
                self.selectB.hide()
            if not self.focus.__getSlotByName__(attr).getProperty().getMultUpper()==1:
                for o in getattr(self.focus, attr):
                    if self.filterAssociation(o):
                        self.list.append(o)
            else:
                v = getattr(self.focus, attr)
                if self.filterAssociation(v):
                    self.list.append(v)
                
            self.itable.setNumRows(len(self.list))
            r=0
            #Caption
            self.namelabel.setText(" Editing %s of %s %s" % (attr, self.focus.__type__().getName(), getFQName(self.focus,1)))

            for c in self.list:
                self.currentItem=QTableItem(self.itable,QTableItem.Never,
                                            QString("new"))
                self.currentItem.setText(self.getDescriptionOfElement(c))
                self.itable.setItem(r,0,self.currentItem)
                
                r += 1

            if self.itable.numRows()==0:
                self.editor.focusOn(None) 
            else:
                if self.itable.currentRow()>=0:
                    self.currentChanged(self.itable.currentRow(),0)
                else:
                    self.currentChanged(0,0)

        else:
            self.editor.focusOn(None)


    def apply(self):
        # Start by applying changes in all subeditors
        for pe in self.editor.subEditors:
            pe.apply()
        
        if not self.edited:
            return
        #Caption
        self.namelabel.setText(" Editing %s of %s %s"%(self.getAssociationName(), self.focus.__type__().getName(), getFQName(self.focus,1)))

        if self.isEnabled():
            coral.getTransactionManager().beginModifyCmd()
            try:
                if not self.focus.__getSlotByName__(self.getAssociationName()).getProperty().getMultUpper()==1:
                    originalList=getattr(self.focus,self.getAssociationName())
                    # This works without a copy of the list since
                    # Coral Collections are guaranteed to work anyway, iff you
                    # modify only the current element. Ahem. If this changes
                    # you'll have to copy the collection (using .clone())
                    t = {}
                    for i in self.list: t[i] = 1
                    for c in originalList:
                        if self.filterAssociation(c):
                            if not t.has_key(c):
                                originalList.drop(c)

            finally:
                coral.getTransactionManager().endModifyCmd()
                
            for g in range(self.itable.numRows()):
                self.itable.updateCell(g,0)
  
            self.itable.update()
            self.editor.setEnabled(1)
            self.edited=0
            

            
    def filterAssociation(self,e):
        return 1
     
    def elementFactory(self):
        """ElementFactory is supposed to return an element of the
        appropriate type and connect it to the model. It is called when you
        add a new element. i.e by clicking the add button"""
        
        raise "not implemented"

    def getDescriptionOfElement(self,e):
        return getFQName(e,1)
    
    def getHeader(self):
        return []
    
    def doubleClicked(self,r,c,b,point):
        self.apply()
        if r>=0:
            if self.list[r]:
                self.manager.editorSet.notifyFocusOn(self.list[r])

    
def Label(parent,geometry,text):
    q=QLabel(parent)
    q.setGeometry(QRect(geometry))
    q.setText(q.tr(text))
    return q


class ElementPE(QWidget,PropertyEditor):
    
    def __init__(self,parent=None,manager=None):

        QWidget.__init__(self,parent)
        PropertyEditor.__init__(self,parent,manager)
        self.setName('Element')
        self.manager=manager
        Form2Layout = QVBoxLayout(self)
        Form2Layout.setSpacing(0)
        Form2Layout.setMargin(0)

        self.tframe = QFrame(self,'tframe')
        self.tframe.setProperty('minimumSize',QVariant(QSize(0,45)))

        self.lName = QLabel(self.tframe,'lName')
        self.lUUID = QLabel(self.tframe,'lUUID')
        self.lName.setGeometry(QRect(20,3,1500,20))
        self.lUUID.setGeometry(QRect(20,23,1500,20))
        
        self.lName.setProperty('text',QVariant(self.tr('')))
        self.lUUID.setProperty('text',QVariant(self.tr('')))
        Form2Layout.addWidget(self.tframe)

        self.bframe = QFrame(self,'bframe')
        bframeLayout = QVBoxLayout(self.bframe)
        bframeLayout.setSpacing(0)
        bframeLayout.setMargin(0)

        self.itable = QTable(0,2,self.bframe,'itable')
        self.itable.setSelectionMode(QTable.Single)
        self.itable.oldRow=-1
        self.itable.oldHeight=0
        
        self.itable.setColumnStretchable(1,1)
        bframeLayout.addWidget(self.itable)
        Form2Layout.addWidget(self.bframe)
        
        self.edited=0        

        self.previous_focus_type = None
        self.itemsL=[]
        self.itemsW=[]

        i=0
        for h in ["Name","Value"]:         
            self.itable.horizontalHeader().setLabel(i,h)
            i=i+1

                
    def focusOn(self,element):
        if self.focus==element:
            for i in self.itemsW:
                i.setValue()
            return

        self.itemsW=[]

        self.focus=element
        self.edited=0
        if not self.focus:
            self.lName.setText('')
            return
        t = element.__type__()
        self.lName.setText(getFQName(element)+": "+getFQTypeName(t))
        self.lUUID.setText("UUID: "+unicode(element.__XMIid__()))
        # Debugging
        if 0 >= log.getCurrentLogLevel():
            properties = [p for p in self.focus.__properties__() ]
        else:
            properties = [p for p in self.focus.__properties__() if not p.isAnonymous() ]

        properties.sort(private_property_sorter)
        
        self.itable.setNumRows(len(properties))

        # cached_L is true when we switch from an element of type T
        # to another element of the same type
        # Thus, self.itemsL can be kept.
        t = element.__type__()
        cached_L = (t == self.previous_focus_type)
        if not cached_L:
            self.itemsL = []
        self.previous_focus_type = t
        
        row=0
        for p in properties:
            if not cached_L:
                self.itemsL.append(QTableItem(self.itable,QTableItem.Never,QString(
p.getName()+":"+p.getTarget().getName())))
                self.itable.setItem(row, 0, self.itemsL[-1])
            self.itemsW.append(ElementTableItem(self.itable,QTableItem.WhenCurrent,self.focus,p,self.manager))
            self.itable.setItem(row, 1, self.itemsW[-1])
            row += 1
        self.itable.adjustColumn(0)


    def apply(self):
        for eti in self.itemsW:
            if eti.editor and eti.edited:
                eti.setContentFromEditor(eti.editor)
        

    def canEdit(self,e):
        "The universal editor can edit everything"
        return 1


class ElementTableItem(QTableItem):

    def __init__(self,table,edit,focus,property,manager):
        self.focus=focus
        self.manager=manager
        self.property = property
        self.atype= property.getTarget()
        self.editor=None
        self.edited=0
        QTableItem.__init__(self,table,edit, "")
        self.setValue()
        # This check has to match with createEditor's
        # Has some bugs, e.g. multiple EnumerationDefinition
        if not self.atype.isDatatypeDefinition():
            self.table().connect(self.table(), SIGNAL("clicked(int,int,int,const QPoint &)"), self.itemClicked)

    def itemClicked(self,r,c,b,p):
        if r==self.row() and c==self.col():
            self.editor=AssociationListE(self.table().viewport(),
                                         self.property,
                                         self.focus,
                                         self.manager)
            self.editor.show()
            self.setContentFromEditor(self.editor)
        
    def setValue(self):
        v = getattr(self.focus, self.property.getName())
        if v is None:
            txt = ""
        elif isinstance(v, coral.Slot):
            txt = [ getFQName(x, 1) for x in v ]
            txt = "[ " + ", ".join(txt) + " ]"
        elif isinstance(v, unicode):
            txt = v
        else:
            txt = getFQName(v, 1)
        self.setText(txt)
        self.table().updateCell(self.row(),self.col())


    def alignment(self):
        return Qt.AlignLeft

    def setEdited(self):
        self.edited=1

    def createEditor(self):
        """Overridden QT method. This gets called when the user clicks
        on an item and wants to change it. It should return an appropriate
        (invisibile) editor for QT."""
        #
        # This whole junk is wrapped inside a try..except
        # because QT bombs if there's an error here. --msa
        #
        try:
            value = getattr(self.focus, self.property.getName())
            self.editor=None
            self.edited=-1
    
            if self.atype == coral.coralPyObj:
                self.editor = None
    
            elif isinstance(self.atype, coral.EnumerationDefinition):
                self.editor=QComboBox(0,None,'enumerationEditor')
                for i in range(self.atype.size()):
                    e=self.atype.getDescription(i)                
                    self.editor.insertItem(QString(unicode(e)))
                self.editor.setCurrentItem(0)
                self.editor.connect(self.editor,SIGNAL('activated(int)'),self.setEdited)
                
            elif self.atype.isDatatypeDefinition():
                if self.property.isAtMostOne():
                    self.editor=QLineEdit(None,"nameEditor")
                    self.editor.setText(unicode(value))
                    self.editor.connect(self.editor,SIGNAL('textChanged(const QString &)'),self.setEdited)
                else:
                    try:
                        SlotEditor(self.focus,
                                   self.property,
                                   None)
                    except CouldNotEdit:
                        self.editor = \
                                    PrimitiveTypeEditor(self.table().viewport(),
                                                        self.focus,
                                                        self.property)
                    # BUG update view here
            else:
                # This should be handled by itemClicked
                pass
        except:
            print traceback.print_exc()
            return None
        return self.editor

        
    def setContentFromEditor(self,editor):
        """This pairs with createEditor(). It's supposed to
        set the value from the editor."""

        if not self.edited:
            return
        
        self.edited=0
            
        if self.atype == coral.coralPyObj:
            pass

        elif isinstance(self.atype, coral.EnumerationDefinition):
            value = editor.currentItem()
            coral.getTransactionManager().beginModifyCmd()
            try:
                setattr(self.focus, self.property.getName(), value)
            finally:
                coral.getTransactionManager().endModifyCmd()
            editor.close()

        elif self.atype.isDatatypeDefinition():
            coral.getTransactionManager().beginModifyCmd()
            try:
                if self.property.isAtMostOne():
                    if self.atype.isSubclass(coral.UnlimitedInteger):
                        setattr(self.focus, self.property.getName(), int(str(editor.text())))
                    elif self.atype.isSubclass(coral.String):
                        setattr(self.focus, self.property.getName(), unicode(editor.text()))
                    elif self.atype.isSubclass(coral.Double):
                        setattr(self.focus, self.property.getName(), float(str(editor.text())))
                    else:
                        _log.debug(unicode(self.atype)+" type not handled by propertyeditor")     
                    
                         
                else:
                    self.editor.setSlotFromEditor()
            finally:
                coral.getTransactionManager().endModifyCmd()
        else:
            # BUG here self.editor, and AssociationListE should be used
            # to set the value. But it sets the value by itself,
            # so we have nothing to do.
            # This is bad behaviour since it's harder to cancel stuff.
            pass
        
        self.setValue()
                                
                

class PrimitiveTypeEditor(TextInputDlg):
    def __init__(self,parent,element=None,property=None):
        self.element=element
        TextInputDlg.__init__(self,parent,fl=Qt.WType_Dialog+Qt.WShowModal)
        
        self.edited=0
        self.textEdit1.setWordWrap(QTextEdit.NoWrap)
        self.property = property
        a = getattr(self.element, self.property.getName())
        s = ""
        if self.property.isAtMostOne():
            s = unicode(a)
        else:
            for i in a:
                s += unicode(i)
                s += "\n"
        self.textEdit1.setText(s)
        self.connect(self.textEdit1,SIGNAL('textChanged()'),self.changed)
        self.connect(self.OKButton,SIGNAL('clicked()'), self.setSlotFromEditor)
        self.connect(self.CancelButton,SIGNAL('clicked()'),self.cancel)
        self.setCaption("Edit " + self.property.getName())

    def cancel(self):
        self.edited=0
        self.close()
      
    def changed(self):
        self.edited=1
        
    def setSlotFromEditor(self):
        if self.edited:
            t = unicode(self.textEdit1.text())
            if self.property.isAtMostOne():
                setattr(self.element, self.property.getName(), t)
            else:
                a = getattr(self.element, self.property.getName())
                a.resetSlot()
                targettype = a.getProperty().getTarget()
                for i in t.rstrip().split("\n"):
                    s = targettype.newAtom()
                    # BUG we would be generic for any datatype
                    # if this was fixed...
                    s.__setString__(i)
                    a.append(s)
        self.cancel()
        
class AssociationListE(ListWidget):
    def __init__(self,parent,property,element,manager):
        self.property=property
        self.focus=element
        assert self.focus
        self.manager=manager

        if self.property.isComposition():
            allowNew=1
            allowLink=0
        else:
            allowNew=0
            allowLink=1

        ListWidget.__init__(self,parent,allowLink,allowNew,manager)

        if not self.property.isOrdered():
            self.upB.hide()
            self.downB.hide()

        #self.setWFlags(Qt.WType_Dialog+Qt.WShowModal+Qt.WDestructiveClose)
        #self.setAcceptDrops(1)
        self.focusOn(element)
        self.resize(QSize(300,150).expandedTo(self.minimumSizeHint()))
        self.setCaption("Edit " + self.property.getName())
        
    def getHeader(self):
        ed=self.property.getTarget()
        mult=str(self.property.getMultLower())+".."
        if self.property.getMultUpper()==-1:
            mult+="*"
        else:
            mult+=str(self.property.getMultUpper())
        return [self.property.getName() + ":" + ed.getName() + " " + mult]
    
    def getListName(self):
        return self.property.getName()
    
    def load(self,row,e):
        self.itable.setText(row, 0, getFQName(e, 1))

    def link(self,item=None):
        from coral.modeler.outline.outlineeditor import ElementPick

        root=self.focus.__getRoot__()
            
        new=ElementPick(self).pick(root,self.property.getTarget())

        if new:
            coral.getTransactionManager().beginModifyCmd()
            try:
                if not self.multiplicity:
                    self.deleteAll()
                self.add(item=new)
            finally:
                coral.getTransactionManager().endModifyCmd()
            self.edited=1
            

    def elementFactory(self):
        candidates = coral.inheritanceTree(self.property.getTarget())
        if len(candidates)>1:
            sorted_candidates = [i for i in candidates ]
            # Doesn't take package names into account...
            sorted_candidates.sort(lambda x,y: cmp(x.getName(), y.getName()))
            dlg=NewElementDlg(candidates=sorted_candidates,modal=1)
            if dlg.exec_loop():
                newClass=sorted_candidates[dlg.ComboBox1.currentItem()]
            else:
                newClass=None
        else:
            newClass=candidates[0]
        if newClass:
            o=newClass()
            if o.__type__().getPropertyByName("name"):
                setattr(o,"name","(noname)")
            return o
        else:
            return None

    def dragEnterEvent(self,e):
        assert(0)
        
    def dropEvent(self,e):
        assert(0)

    def doubleClicked(self,r,c,b,point):
        value=None
        if self.multiplicity:
            value=getattr(self.focus,self.getListName())[r]            
        else:
            value=getattr(self.focus,self.getListName())

        if value:
            self.manager.editorSet.notifyFocusOn(value)
            #self.close(1)
            #qApp.quit()

            
class NewElementDlg(QDialog):
    def __init__(self,parent = None,name = None,modal = 0,fl = 0,candidates=None):
        QDialog.__init__(self,parent,name,modal,fl)

        if name is None:
            self.setName('NewElementDlg')

        self.resize(332,100)
        self.setProperty('caption',QVariant(self.tr('New Element')))
        #self.setProperty('sizeGripEnabled',QVariant(1,0))

        self.label = QLabel(self,'label')
        self.label.setGeometry(QRect(10,10,200,20))
        self.label.setProperty('text',QVariant(self.tr('Select the type of new element:')))

        LayoutWidget = QWidget(self,'Layout5')
        LayoutWidget.setGeometry(QRect(240,10,82,80))
        Layout5 = QVBoxLayout(LayoutWidget)
        Layout5.setSpacing(6)
        Layout5.setMargin(0)

        self.buttonOk = QPushButton(LayoutWidget,'buttonOk')
        self.buttonOk.setProperty('text',QVariant(self.tr('&OK')))
        self.buttonOk.setProperty('autoDefault',QVariant(1,0))
        self.buttonOk.setProperty('default',QVariant(1,0))
        Layout5.addWidget(self.buttonOk)

        self.buttonCancel = QPushButton(LayoutWidget,'buttonCancel')
        self.buttonCancel.setProperty('text',QVariant(self.tr('&Cancel')))
        self.buttonCancel.setProperty('autoDefault',QVariant(1,0))
        Layout5.addWidget(self.buttonCancel)

        self.ComboBox1 = QComboBox(0,self,'ComboBox1')
        self.ComboBox1.setEditable(1)
        self.ComboBox1.setAutoCompletion(1)
        self.ComboBox1.setDuplicatesEnabled(0)
        if candidates:
            for i in candidates:
                self.ComboBox1.insertItem(i.getName())
            
        self.ComboBox1.setGeometry(QRect(10,40,220,22))

        self.connect(self.buttonOk,SIGNAL('clicked()'),self,SLOT('accept()'))
        self.connect(self.buttonCancel,SIGNAL('clicked()'),self,SLOT('reject()'))

class ReferenceLineEdit(QLineEdit):
    def __init__(self,parent,name):
        QLineEdit.__init__(self,parent,name)
        self.parent=parent
  
    def mouseDoubleClickEvent(self,e):
        self.parent.doubleClicked()
