#----------------------------------------------------------------------
# Name:        PropertyEditors.py
# Purpose:
#
# Author:      Riaan Booysen
#
# Created:     1999
# RCS-ID:      $Id: PropertyEditors.py,v 1.33.2.3 2005/03/15 10:21:34 riaan Exp $
# Copyright:   (c) 1999 - 2005 Riaan Booysen
# Licence:     GPL
#----------------------------------------------------------------------
"""
    Property editors provide a design time interface for the inspector to
    examine and manipulate properties of controls.

    Some properties are live and also update the design time control,
    others only update the source and changes may only be seen when the
    frame is reloaded or the control is recreated.
"""

print 'importing PropertyEditors'

# XXX Value getting setting of value between internal and sometime control value
# XXX Is still too fuzzy

from types import *
import os, string

from wxPython.wx import *
#from wxPython.utils import *

from InspectorEditorControls import *

import Utils
import Enumerations

StringTypes = [StringType]
try: StringTypes.append(UnicodeType)
except: pass

class EditorStyles:pass
class esExpandable(EditorStyles):pass
class esDialog(EditorStyles):pass
class esReadOnly(EditorStyles):pass
class esRecreateProp(EditorStyles):pass

class PropertyRegistry:
    """ Factory to return propery editors from recognisable types
        It does not return property editors for certain design-time types like
        sets, enumerations, booleans, etc.
    """

    def __init__(self):
        self.classRegistry = {}
        self.typeRegistry = {}#{type(None): None}

    def registerClasses(self, propClass, propEditors):
        for propEdit in propEditors:
            self.classRegistry[propClass.__name__] = propEdit

    def registerTypes(self, propType, propEditors):
        for propEdit in propEditors:
            self.typeRegistry[propType] = propEdit

    def factory(self, name, parent, companion, rootCompanion, propWrapper, idx, width):
        try:
            propWrapper.connect(companion.control, companion)
            value = propWrapper.getValue()
        except Exception, message:
            print 'Error on accessing Getter for', name, ':', message
            value = None

        #2.4
        if type(value) == InstanceType:
            if self.classRegistry.has_key(value.__class__.__name__):
                return self.classRegistry[value.__class__.__name__](name,
                  parent, companion, rootCompanion, propWrapper, idx, width)
            else:
                pass
##                print 'e:class', value, value.__class__.__name__, 'for', name, 'not supported'
        #2.5
        if self.typeRegistry.has_key(type(value)):
            return self.typeRegistry[type(value)](name, parent, companion,
              rootCompanion, propWrapper, idx, width)
        elif isinstance(value, object):
            if self.classRegistry.has_key(value.__class__.__name__):
                return self.classRegistry[value.__class__.__name__](name,
                  parent, companion, rootCompanion, propWrapper, idx, width)
        else:
            if type(value) == type(None):
                return None
            elif self.typeRegistry.has_key(type(value)):
                return self.typeRegistry[type(value)](name, parent, companion,
                  rootCompanion, propWrapper, idx, width)
            else:
                pass
##                print 'e:type', value, type(value), 'for', name, 'not supported'


# XXX Check IEC initialisation not from Display value but from 'valueFromIEC'
class PropertyEditor:
    """ Class associated with a design time identified type,
        it manages the behaviour of a NameValue in the Inspector
    """

    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, _1=None , _2=None):
        self.name = name
        self.parent = parent

        self.idx = idx
        self.width = width
        self.editorCtrl = None
        self.companion = companion
        self.obj = companion.control
        self.propWrapper = propWrapper
        self.propWrapper.connect(self.obj, self.companion)
        self.rootCompanion = rootCompanion
        self.root = rootCompanion.control
        self.style = []
        self.ownerPropEdit = None
        self.expanded = false
        self.initFromComponent()

    def initFromComponent(self):
        self.value = self.propWrapper.getValue()
        if self.editorCtrl:
            self.editorCtrl.setValue(self.valueToIECValue())

    def edit(self):
        pass

    def inspectorEdit(self):
        """ Start a property editing operation and opens the inplace editor """
        pass

    def refreshCompCtrl(self):
        if self.obj and hasattr(self.obj, 'Refresh'):
            self.obj.Refresh()

    def isValuesEqual(self, propVal, ctrlVal):
        if isinstance(propVal, wxFont) and isinstance(ctrlVal, wxFont):
            return fontAsExpr(propVal) == fontAsExpr(ctrlVal)
        elif isinstance(propVal, (StringType, UnicodeType)) and \
             isinstance(ctrlVal, (StringType, UnicodeType)):
            return propVal == ctrlVal
        else:
            return propVal == ctrlVal

    def validateProp(self, oldVal, newVal):
        pass

    def inspectorPost(self, closeEditor = true):
        """ Post inspector editor control, update ctrl and persist value """
        if self.editorCtrl:
            v = self.getValue()
            cv = self.getCtrlValue()
            # Only post changes
            if not self.isValuesEqual(v, cv):
                self.validateProp(v, cv)
                self.setCtrlValue(cv, v)
                self.refreshCompCtrl()
                self.persistValue(self.valueAsExpr())
                # When sub properties post, update their main properies
                if self.ownerPropEdit:
                    self.companion.updateOwnerFromObj()
                    self.ownerPropEdit.initFromComponent()

                    # XXX Font's want new objects assigned to them before
                    # XXX they update
                    if esRecreateProp in self.ownerPropEdit.getStyle():
                        v = self.companion.eval(self.ownerPropEdit.valueAsExpr())
                        self.ownerPropEdit.setCtrlValue(v, v)

                    self.ownerPropEdit.persistValue(self.ownerPropEdit.valueAsExpr())
                    self.ownerPropEdit.refreshCompCtrl()

                if self.name in self.companion.mutualDepProps:
                    for prop in self.companion.mutualDepProps:
                        if prop != self.name:
                            insp = self.companion.designer.inspector
                            insp.constructorUpdate(prop)
                            insp.propertyUpdate(prop)

            if closeEditor and self.editorCtrl:
                self.editorCtrl.destroyControl()
                self.editorCtrl = None


    def inspectorCancel(self):
        """ Cancel a property editor operation """
        if self.editorCtrl:
            self.editorCtrl.destroyControl()
            self.editorCtrl = None

    def getStyle(self):
        return self.style

    def getValue(self):
        """ Returns and initialises value for prop editor.

        If in edit mode, value should be read from the editor control

        Override if needed.
        """
        if self.editorCtrl:
            self.value = self.editorCtrl.getValue()
        return self.value

    def setValue(self, value):
        """ Initialise the prop editor and if needed the editor control """
        self.value = value
        if self.editorCtrl:
            self.editorCtrl.setValue(self.valueToIECValue())

    def getCtrlValue(self):
        """ Read current prop value from designed object """
        return self.propWrapper.getValue()

    def setCtrlValue(self, oldValue, value):
        """ Update designed object with current prop """
        # If overridden, rem to call check triggers if not calling parent method
        self.companion.checkTriggers(self.name, oldValue, value)
        self.propWrapper.setValue(value)

    def persistValue(self, value):
        funcName = self.propWrapper.getSetterName()
        self.companion.persistProp(self.name, funcName, value)

    def getDisplayValue(self):
        """ Value that should display when the prop editor is not in edit mode """
        return `self.value`

    def valueAsExpr(self):
        """ Return value as evaluatable source """
        return self.getDisplayValue()

    def getValues(self):
        """ Return list of options """
        return self.values

    def setValues(self, values):
        """ Sets list of options """
        self.values = values

    def valueToIECValue(self):
        """ Return prop value in the form that the form that the editor control expects """
        return self.value

    def setValueFromIECValue(self, value):
        """ Set value from the format that the editor control produces """
        self.value = value

    def setWidth(self, width):
        self.width = width
        if self.editorCtrl:
            self.editorCtrl.setWidth(width)

    def setIdx(self, idx):
        self.idx = idx
        if self.editorCtrl:
            self.editorCtrl.setIdx(idx)

class FactoryPropEdit(PropertyEditor):
    pass

class LockedPropEdit(PropertyEditor):
    def getDisplayValue(self):
        return str(self.value)

#-------------------------------------------------------------------------------

class ConfPropEdit(PropertyEditor):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx,
          width, options, names):
        PropertyEditor.__init__(self, name, parent, companion, rootCompanion,
          propWrapper, idx, width)
        self.setValues(names)

    def getDisplayValue(self):
        return str(self.value)

    def initFromComponent(self):
        self.value = self.getCtrlValue()

    def persistValue(self, value):
        pass

class ContainerConfPropEdit(ConfPropEdit):
    def getStyle(self):
        return [esExpandable]
    def inspectorEdit(self):
        self.editorCtrl = BevelIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)

class StrConfPropEdit(ConfPropEdit):
    def valueToIECValue(self):
        return self.value
#        return eval(self.value)

    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
            try:
                self.value = self.editorCtrl.getValue()
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value

class PasswdStrConfPropEdit(StrConfPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width, style = wxTE_PASSWORD)
    def getDisplayValue(self):
        return '*'*len(self.value)

class EvalConfPropEdit(ConfPropEdit):
    def valueToIECValue(self):
        return `self.value`

    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, `self.value`)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
            try:
                self.value = eval(self.editorCtrl.getValue(), {})
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value

class EnumConfPropEdit(ConfPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = ChoiceIEC(self, self.getValue())
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.value)
##
##    def getValues(self):
##        return self.names

class ColourConfPropEdit(ConfPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        data = wxColourData()
        data.SetColour(self.companion.eval(self.value))
        data.SetChooseFull(true)
        dlg = wxColourDialog(self.parent, data)
        try:
            if dlg.ShowModal() == wxID_OK:
                col = dlg.GetColourData().GetColour()
                self.editorCtrl.value = \
                      'wx.Colour(%d, %d, %d)'%(col.Red(),col.Green(),col.Blue())
                self.inspectorPost(false)
        finally:
            dlg.Destroy()

class FilepathConfPropEdit(ConfPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        from FileDlg import wxFileDialog
        dlg = wxFileDialog(self.parent, 'Choose the file', '.', '', 'AllFiles', wxSAVE)
        try:
            if dlg.ShowModal() == wxID_OK:
                self.editorCtrl.setValue(`dlg.GetFilePath()`)
                self.inspectorPost(false)
            else:
                if wxMessageBox('Clear the current property value?',
                      'Clear filepath?', style=wxICON_QUESTION | wxYES_NO) == wxYES:
                    self.editorCtrl.setValue("''")
                    self.inspectorPost(false)
        finally:
            dlg.Destroy()

class DirpathConfPropEdit(ConfPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        dlg = wxDirDialog(self.parent)#, defaultPath=self.editorCtrl.value)
        try:
            dlg.SetPath(self.companion.eval(self.editorCtrl.value))
            if dlg.ShowModal() == wxID_OK:
                self.editorCtrl.setValue(`dlg.GetPath()`)
                self.inspectorPost(false)
            else:
                if wxMessageBox('Clear the current property value?',
                      'Clear dirpath?', style=wxICON_QUESTION | wxYES_NO) == wxYES:
                    self.editorCtrl.setValue("''")
                    self.inspectorPost(false)
        finally:
            dlg.Destroy()

class BoolConfPropEdit(ConfPropEdit):
    truths = ['on', 'true', '1']
    def getDisplayValue(self):
        return self.valueToIECValue()

    def valueToIECValue(self):
        return self.value.lower() in self.truths and 'True' or 'False'

    def inspectorEdit(self):
        self.editorCtrl = CheckBoxIEC(self, self.value in self.truths)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.value)

#-------------------------------------------------------------------------------

class OptionedPropEdit(PropertyEditor):
    """ Property editors initialised with options """
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options, names):
        PropertyEditor.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width)
        self.options = options
        self.names = names
        if names:
            self.revNames = Enumerations.reverseDict(names)
        else:
            self.revNames = None

class ConstrPropEditFacade:
    def initFromComponent(self):
        self.value = ''
    def getCtrlValue(self):
        return ''
    def getValue(self):
        return ''
    def setValue(self, value):
        self.value = value

class ConstrPropEdit(ConstrPropEditFacade, PropertyEditor):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx,
      width, options, names):
        PropertyEditor.__init__(self, name, parent, companion, rootCompanion,
          propWrapper, idx, width)
    def initFromComponent(self):
        self.value = self.getValue()
    def valueToIECValue(self):
        return self.value
    def getDisplayValue(self):
        return self.getValue()
    def getCtrlValue(self):
        return self.companion.textConstr.params[ \
          self.companion.constructor()[self.name]]
    def setCtrlValue(self, oldValue, value):
        self.companion.checkTriggers(self.name, oldValue, value)
        if hasattr(self.companion, 'index'):
            self.propWrapper.setValue(self.companion.eval(value), self.companion.index)
        else:
            self.propWrapper.setValue(value)

class ReadOnlyConstrPropEdit(ConstrPropEdit):
    def getValue(self):
        self.value = self.getCtrlValue()
        return self.value
    def inspectorEdit(self):
        val = self.getValue()
        self.editorCtrl = BeveledLabelIEC(self, val)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
##    def getDisplayValue(self):
##        return 'RO:`self.getValue()`
    
class ColourConstrPropEdit(ConstrPropEdit):
    def getValue(self):
        if self.editorCtrl:
            self.value = self.editorCtrl.getValue()
        else:
            self.value = self.getCtrlValue()
        return self.value

    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        data = wxColourData()
        data.SetColour(self.companion.eval(self.value))
        data.SetChooseFull(true)
        dlg = wxColourDialog(self.parent, data)
        try:
            if dlg.ShowModal() == wxID_OK:
                col = dlg.GetColourData().GetColour()
                self.value = 'wx.Colour(%d, %d, %d)'%(col.Red(),col.Green(),col.Blue())
                self.editorCtrl.setValue(self.value)
                self.inspectorPost(false)
        finally:
            dlg.Destroy()


# Collection id name
class ItemIdConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        val = self.valueToIECValue()
        self.editorCtrl = TextCtrlIEC(self, val)
        self.editorCtrl.createControl(self.parent, val, self.idx, self.width)

    def valueToIECValue(self):
        return self.getDisplayValue()

    def getDisplayValue(self):
        base = self.companion.newWinId('')
        return self.getValue()[len(base):]

    def fixupName(self, name):
        newname = []
        for c in name:
            if c == ' ': c = '_'
            if c in string.digits + string.letters + '_':
                newname.append(c)

        return ''.join(newname).upper()

    def getValue(self):
        if self.editorCtrl and self.editorCtrl.getValue():
            base = self.companion.newWinId('')
            self.value = base + self.fixupName(self.editorCtrl.getValue())
        else:
            self.value = self.getCtrlValue()
        return self.value

    def valueAsExpr(self):
        return self.getValue()

    def setCtrlValue(self, oldValue, value):
        self.companion.checkTriggers(self.name, oldValue, value)
        self.propWrapper.setValue(value, self.companion.index)

class IntConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = SpinCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
#             and self.editorCtrl.getValue():
            self.value = str(self.editorCtrl.getValue())
        else:
            self.value = self.getCtrlValue()
        return self.value

class SBFWidthConstrPropEdit(IntConstrPropEdit):
    def getCtrlValue(self):
        return self.companion.GetWidth()

    def setCtrlValue(self, oldValue, value):
        self.companion.SetWidth(value)

    def persistValue(self, value):
        pass

class ClassLinkConstrPropEdit(IntConstrPropEdit): pass

class BitmapPropEditMix:
    extTypeMap = {'.bmp': 'wx.BITMAP_TYPE_BMP',
                  '.gif': 'wx.BITMAP_TYPE_GIF',
                  '.jpg': 'wx.BITMAP_TYPE_JPEG',
                  '.png': 'wx.BITMAP_TYPE_PNG',
                  '.py':  'ResourceModule'}

    srcClass = 'wx.Bitmap'
    ctrlClass = wxBitmap
    nullClass = 'wx.NullBitmap'

    onlyIcons = false

    def showImgDlg(self, dir, name, tpe='Bitmap'):
        model = self.companion.designer.model
        selImg = ''
        if tpe == 'Bitmap' and not os.path.isdir(dir):
            wxMessageBox('The given directory is invalid, using current '
                         'directory.\n(%s)'%dir, 'Warning', 
                         wxOK | wxICON_EXCLAMATION)
            dir = '.'
        elif tpe == 'ResourceModule':
            selImg = name
            
        from FileDlg import wxFileDialog
        
        keepShowing = true
        while keepShowing:
            keepShowing = false
            if tpe == 'ResourceModule':
                mod = model.resources[dir]
                ext = '.py'
                pth = abspth = os.path.splitext(mod.__file__)[0]+ext
            else:
                dlg = wxFileDialog(self.parent, 'Choose an image', dir, name,
                      'ImageFiles', wxOPEN)
                try:
                    if dlg.ShowModal() != wxID_OK:
                        return '', '', ''
                    pth = abspth = dlg.GetFilePath().replace('\\', '/')
                finally:
                    dlg.Destroy()
    
                if not Preferences.cgAbsoluteImagePaths:
                    pth = Utils.pathRelativeToModel(pth, model)
            
                ext = os.path.splitext(pth)[-1].lower()    

            if ext == '.py':
                if os.path.isabs(pth):
                    pth = Utils.pathRelativeToModel(pth, model)

                resInfo = self.handleResourceFiles(abspth, pth, selImg)
                if resInfo:
                    if resInfo == -1:
                        return '', '', ''
                    if resInfo == 'FileDlg':
                        dir, name = os.path.split(abspth)
                        keepShowing = true
                        tpe = 'Bitmap'
                        continue
                    return resInfo[0], resInfo[1], 'ResourceModule'
            try:
                ext = self.extTypeMap[ext]
            except KeyError, err:
                raise Exception('Files of type %s not allowed for this '
                                'property editor.\n The following types '
                                'are allowed %s'%(str(err), 
                                ', '.join(self.extTypeMap.keys())))
            else:
                return abspth, pth, ext

    def extractPathFromSrc(self, src):
        if src:
            if src.startswith(self.nullClass):
                return os.path.join('.', self.nullClass, 'Bitmap'), '.', '', 'Bitmap'
            elif src.startswith('wx.Bitmap(') or src.startswith('wx.Icon('):
                if src.startswith('wx.Bitmap('):
                    filename = src[len('wx.Bitmap(')+1:]
                else:
                    filename = src[len('wx.Icon(')+1:]
                    
                pth = filename[:filename.rfind(',')-1]
                if not os.path.isabs(pth):
                    mbd = Utils.getModelBaseDir(self.companion.designer.model)
                    if mbd: mbd = mbd[7:]
                    pth = os.path.normpath(os.path.join(mbd, pth)).replace('\\', 
                          '/')
                dir, name = os.path.split(pth)
                if not dir: dir = '.'
                return os.path.join(pth, 'Bitmap'), dir, name, 'Bitmap'
            else:
                import moduleparse
                m = moduleparse.is_resource_bitmap.search(src)
                if m:
                    importName, imageName = m.group('imppath'), m.group('imgname')
                    src = os.path.join(importName, imageName, 'ResourceModule')
                    return (src, importName, imageName, 'ResourceModule')

        return './Unknown/Unknown', '.', '', 'Unknown'

    def handleResourceFiles(self, resourceFilename, relResourceFilename, image=''):
        from Models import Controllers

        Model, main = Controllers.identifyFile(resourceFilename)
        for ResourceClass in Controllers.resourceClasses:
            if issubclass(Model, ResourceClass):
                break
        else:
            return # not a resource module
                
        idx, imageName = self.showResourceDlg(resourceFilename, image)
        if idx is None: return 'FileDlg'
        if idx == -1:   return -1

        importName = os.path.splitext(relResourceFilename)[0].replace('\\', 
              '/').replace('/', '.')
        if os.path.isabs(relResourceFilename):
            importName = os.path.splitdrive(importName)[1][1:]
            impNameDlg = wxTextEntryDialog(self.parent, 'Correct the module '
                  'name that must be imported', 'Resource module', importName)
            try:
                if impNameDlg.ShowModal() != wxID_OK:
                    return
                importName = impNameDlg.GetValue()
                # XXX test with imp?
            finally:
                impNameDlg.Destroy()
        else:
            if relResourceFilename.startswith('../'):
                while importName.startswith('../'):
                    importName = importName[3:]
            importName = importName.replace('/', '.')
        
        return importName, imageName

    def getSrcForResPath(self, importName, imageName):
        if self.onlyIcons: cls = 'Icon'
        else:              cls = 'Bitmap'
        return '%s.get%s%s()'%(importName, imageName, cls)

    def showResourceDlg(self, filename, image=''):
        from Models import ResourceSupport
        resDlg = ResourceSupport.ResourceSelectDlg(self.parent, 
          self.companion.designer.model.editor, filename, image, self.onlyIcons)
        try:
            result = resDlg.ShowModal()
            if result == wxID_CANCEL:
                return -1, ''
            if result == wxID_YES:
                return None, None
            if result == wxID_OK:
                idx = resDlg.resources.selected
                return idx, resDlg.resources.imageSrcInfo[idx][0]
            return -1, ''
        finally:
            resDlg.Destroy()

    def assureResourceLoaded(self, importName, imageName):
        model = self.companion.designer.model
        if model.assureResourceLoaded(importName, model.resources,
              specialAttrs=model.specialAttrs):
            src = self.getSrcForResPath(importName, imageName)
            value = self.companion.eval(src)
            bmpPath = os.path.join(importName, imageName, 'ResourceModule')
            self.companion.registerResourceModule(importName)
            
            return src, value, bmpPath
        else:
            raise Exception, '%s could not be loaded as a Resource Module'%importName
            #wxLogError('%s could not be loaded as a Resource Module'%importName)
            #return '', None, ''


class BitmapConstrPropEdit(IntConstrPropEdit, BitmapPropEditMix):
    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        model = self.companion.designer.model
        dummy, dir, name, tpe = self.extractPathFromSrc(self.value)
        abspth, pth, tpe = self.showImgDlg(dir, name, tpe)
        if not tpe:
            return
        elif tpe == 'ResourceModule':
            self.value, ctrlVal, bmpPath = self.assureResourceLoaded(abspth, pth)
        elif abspth:
            self.value = 'wx.Bitmap(%s, %s)'%(`pth`, tpe)
            ctrlVal = wxBitmap(abspth, self.companion.eval(tpe))

        self.persistValue(self.value)
        self.propWrapper.setValue(ctrlVal, self.companion.index)
        self.refreshCompCtrl()

    def getValue(self):
        if self.editorCtrl:
            return self.value
        else:
            return self.getCtrlValue()

class BitmapPropEdit(PropertyEditor, BitmapPropEditMix):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options = None, names = None):
        PropertyEditor.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width)

##    def getStyle(self):
##        return ClassPropEdit.getStyle(self) + [esDialog, esReadOnly]

    def getDisplayValue(self):
        return '(%s)'%self.srcClass

    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)
        constrs = self.companion.constructor()
        if constrs.has_key(self.name):
            constr = self.companion.textConstr.params[constrs[self.name]]
        else:
            constr = self.companion.persistedPropVal(self.name, 
                  self.propWrapper.getSetterName())
            if constr is not None:
                constr = constr[0]

        self.bmpPath = self.extractPathFromSrc(constr)[0]

    def edit(self, event):
        if self.bmpPath:
            path, tpe = os.path.split(self.bmpPath)
            dir, name = os.path.split(path)
        else:
            dir, name, tpe = '.', '', 'Bitmap'

        abspth, pth, tpe = self.showImgDlg(dir, name, tpe)
        if not tpe or not abspth:
            return
        if tpe == 'ResourceModule':
            self.value, self.bmpPath = self.assureResourceLoaded(abspth, pth)[1:]
        else:
            self.value = self.ctrlClass(abspth, self.companion.eval(tpe))
            self.bmpPath = os.path.join(pth, 'Bitmap')
        self.inspectorPost(false)

    def getValue(self):
        return self.value

    def valueAsExpr(self):
        if self.bmpPath:
            path, tpe = os.path.split(self.bmpPath)
            dir, name = os.path.split(path)
            if tpe == 'Bitmap':
                # XXX path and name both?
                if path == self.nullClass:
                    return self.nullClass
                elif name == self.nullClass:
                    return self.nullClass
                else:
                    return '%s(%s, %s)'%(self.srcClass, `path`,
                           self.extTypeMap[os.path.splitext(path)[-1].lower()])
            elif tpe == 'ResourceModule':
                return self.getSrcForResPath(dir, name)
            elif tpe == 'Unknown':
                return self.nullClass
            else:
                raise Exception, 'Unhandled image handling type: %s'%tpe
        else:
            return self.nullClass

class IconPropEdit(BitmapPropEdit):
    srcClass = 'wx.Icon'
    ctrlClass = wxIcon
    nullClass = 'wx.NullIcon'
    onlyIcons = true

    extTypeMap = {'.ico': 'wx.BITMAP_TYPE_ICO',
                  '.py':  'ResourceModule'}


class EnumConstrPropEdit(IntConstrPropEdit):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options, names):
        IntConstrPropEdit.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options, names)
        self.names = names
    def valueToIECValue(self):
        return self.getValue()
    def inspectorEdit(self):
        value = self.getValue()
        self.editorCtrl = ChoiceIEC(self, value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(value)
        
    def getDisplayValue(self):
        return self.valueToIECValue()
    def getValues(self):
        return self.names

class ClassConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        val = self.getValue()
        if self.companion.designer.model.customClasses.has_key(val):
            self.editorCtrl = ChoiceIEC(self, val)
            self.editorCtrl.createControl(self.parent, self.idx, self.width)
            self.editorCtrl.setValue(val)
        else:
            self.editorCtrl = BeveledLabelIEC(self, val)
            self.editorCtrl.createControl(self.parent, self.idx, self.width)

    def setCtrlValue(self, oldValue, value):
        #self.companion.checkTriggers(self.name, oldValue, value)
        self.propWrapper.setValue(value)
    def getCtrlValue(self):
        return self.propWrapper.getValue()

    def getValue(self):
        if self.editorCtrl:
            self.value = self.editorCtrl.getValue()
        else:
            self.value = self.getCtrlValue()
        return self.value

    def getValues(self):
        custClss = self.companion.designer.model.customClasses
        MyCls = custClss[self.value]
        vals = []
        for name, Cls in custClss.items():
            if MyCls == Cls:
                vals.append(name)
        vals.remove(Utils.getWxPyNameForClass(MyCls))
        vals.insert(0, Utils.getWxPyNameForClass(MyCls))
        return vals




##    def getDisplayValue(self):
##        dv = EnumConstrPropEdit.getDisplayValue(self)
##        print dv
##        return dv

class BoolConstrPropEdit(EnumConstrPropEdit):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, 
                 width, options, names):
        EnumConstrPropEdit.__init__(self, name, parent, companion, rootCompanion, 
                 propWrapper, idx, width, options, ['True', 'False'])

    def inspectorEdit(self):
        self.editorCtrl = CheckBoxIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.value)

class LCCEdgeConstrPropEdit(EnumConstrPropEdit):
    def getCtrlValue(self):
        return self.companion.GetEdge()

    def getValue(self):
        if self.editorCtrl:
            try:
                if self.editorCtrl.getValue():
                    self.value = self.editorCtrl.getValue()
                else:
                    self.value = self.getCtrlValue()
            except Exception, message:
                self.value = self.getCtrlValue()
        else:
            self.value = self.getCtrlValue()
        return self.value

    def setCtrlValue(self, oldValue, value):
        self.companion.SetEdge(value)

    def persistValue(self, value):
        pass

    def getValues(self):
        objName = self.companion.__class__.sourceObjName
        return [self.getCtrlValue()] + \
          map(lambda a, objName=objName: '%s.%s'%(objName, a),
          self.companion.availableItems())

class ObjEnumConstrPropEdit(EnumConstrPropEdit):
    def getValue(self):
        if self.editorCtrl:
            try:
                if self.editorCtrl.getValue():
                    self.value = self.editorCtrl.getValue()
                else:
                    self.value = self.getCtrlValue()
            except Exception, message:
                self.value = self.getCtrlValue()
        else:
            self.value = self.getCtrlValue()
        return self.value

    def persistValue(self, value):
        pass

    def getObjects(self):
        return self.companion.designer.getAllObjects().keys()

    def getValues(self):
        vals = self.getObjects()
        try:
            val = self.getValue()
            if val == 'self': vals.remove('self')
            else: vals.remove('self.'+val)
        except: pass
        return vals

class WinEnumConstrPropEdit(ObjEnumConstrPropEdit):
    def getObjects(self):
        return ['None'] + self.companion.designer.getObjectsOfClassWithParent(
                                wxWindow, self.companion.name).keys()
    def getCtrlValue(self):
        return self.companion.GetOtherWin()
    def setCtrlValue(self, oldValue, value):
        self.companion.SetOtherWin(value)

class MenuEnumConstrPropEdit(ObjEnumConstrPropEdit):
    def getValues(self):
        return ['wx.Menu()'] + ObjEnumConstrPropEdit.getValues(self)
    def getObjects(self):
        menus = self.companion.designer.getObjectsOfClass(wxMenu).keys()
        if isinstance(self.companion.control, wxMenu):
            menus.remove(Utils.srcRefFromCtrlName(self.companion.name))
        return menus
    def setCtrlValue(self, oldValue, value):
        self.companion.SetMenu(value)
    def getCtrlValue(self):
        return self.companion.GetMenu()

##class ControlEnumConstrPropEdit(ObjEnumConstrPropEdit):
####    def getValues(self):
####        return ['wxMenu()'] + ObjEnumConstrPropEdit.getValues(self)
##    def getObjects(self):
##        return self.companion.designer.getObjectsOfClass(wxControl).keys()
##    def setCtrlValue(self, oldValue, value):
##        self.companion.SetControl(value)
##    def getCtrlValue(self):
##        return self.companion.GetControl()

class SizerEnumConstrPropEdit(ObjEnumConstrPropEdit):
##    def getValues(self):
##        return ['wxMenu()'] + ObjEnumConstrPropEdit.getValues(self)
    def getObjects(self):
        return self.companion.designer.getObjectsOfClass(wxBoxSizer).keys()
##    def setCtrlValue(self, oldValue, value):
##        self.companion.SetMenu(value)
##    def getCtrlValue(self):
##        return self.companion.GetMenu()


class BaseFlagsConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getStyle(self):
        return [esExpandable]

    def getValue(self):
        """ For efficiency override the entire getValue"""
        if self.editorCtrl:
            try:
                anInt = self.companion.eval(self.editorCtrl.getValue())
                if type(anInt) is IntType:
                    self.value = ' | '.join(map(string.strip,
                        self.editorCtrl.getValue().split('|')))
                else:
                    self.value = self.getCtrlValue()
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value

class StyleConstrPropEdit(BaseFlagsConstrPropEdit):
    def getSubCompanion(self):
        from Companions.Companions import WindowStyleDTC
        return WindowStyleDTC

class FlagsConstrPropEdit(BaseFlagsConstrPropEdit):
    def getSubCompanion(self):
        from Companions.Companions import FlagsDTC
        return FlagsDTC

class StrConstrPropEdit(ConstrPropEdit):
    def valueToIECValue(self):
        return self.companion.eval(self.value)

    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
            try:
                aStr = self.editorCtrl.getValue()
                if type(aStr) in StringTypes:
                    self.value = `aStr`
                else:
                    self.value = self.getCtrlValue()
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value

class SizeConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
            try:
                self.value = self.editorCtrl.getValue()
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value


# XXX Check for name conflicts
class NameConstrPropEdit(StrConstrPropEdit):
    def getValue(self):
        if self.editorCtrl:
            value = self.editorCtrl.getValue()
            if type(value) in StringTypes:
                value = `self.editorCtrl.getValue()`
            else:
                value = self.getCtrlValue()

            if value != self.value:
                strVal = self.companion.eval(value)
                if not strVal:
                    message = 'Invalid name for Python object'
                    wxLogError(message)
                    return self.value

                for c in strVal:
                    if c not in string.letters+string.digits+'_':#"\'':
                        message = 'Invalid name for Python object'
                        wxLogError(message)
                        return self.value
                        #raise message

                if self.companion.designer.objects.has_key(value):
                    wxLogError('Name already used by another control.')
                    return self.value
                    #raise 'Name already used by another control.'
            self.value = value
        else:
            self.value = self.getCtrlValue()
        return self.value


    def getCtrlValue(self):
        return `self.companion.name`

    def setCtrlValue(self, oldValue, newValue):
        self.companion.checkTriggers(self.name, 
              self.companion.eval(oldValue), 
              self.companion.eval(newValue))

    def persistValue(self, value):
        pass

class ChoicesConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
            try:
                aList = self.companion.eval(self.editorCtrl.getValue())
                if type(aList) is ListType:
                    self.value = self.editorCtrl.getValue()
                else:
                    self.value = self.getCtrlValue()
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value

class MajorDimensionConstrPropEdit(ConstrPropEdit):
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx,
          self.width)

    def getValue(self):
        if self.editorCtrl:
            try:
                anInt = self.companion.eval(self.editorCtrl.getValue())
                if type(anInt) is IntType:
                    self.value = self.editorCtrl.getValue()
                else:
                    self.value = self.getCtrlValue()
            except Exception, message:
                self.value = self.getCtrlValue()
                print 'invalid constr prop value', message
        else:
            self.value = self.getCtrlValue()
        return self.value

class EventPropEdit(OptionedPropEdit):
    """ Property editor to handle design time definition of events """
    def initFromComponent(self):
        # unlike other propedit getter setters these are methods not funcs
        self.value = self.propWrapper.getValue(self.name)
    def valueToIECValue(self):
        v = self.value
        return v

    def inspectorEdit(self):
        self.editorCtrl = ChoiceIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.value)

    def setCtrlValue(self, oldValue, value):
        self.propWrapper.setValue(value, self.name)

    def getDisplayValue(self):
        return self.valueToIECValue()

    extraOpts = ['(delete)', '(rename)']
    scopeOpts = {'(show all)': 'all',
                 '(show own)': 'own'}
    def getValues(self):
        """ Build event list based on currently selected scope for the event """
        # XXX Should ideally do this one day:
        # XXX   Show event's of similar types, e.g. mouse events, cmd events.
        # XXX   Also show events from the code not bound to the frame
        vals = []
        showScope = 'own'
        if self.companion:
            for evt in self.companion.textEventList:
                if evt.event_name == self.name:
                    showScope = evt.show_scope

                if evt.trigger_meth not in self.extraOpts:
                    try: vals.index(evt.trigger_meth)
                    except ValueError: vals.append(evt.trigger_meth)

            if showScope != 'own':
                # Add evts from other scopes
                # XXX Collection items' events aren't handled correctly
                # XXX designer != CollEditorView
                for comp, ctrl, prnt in self.companion.designer.objects.values():
                    if comp != self.companion and showScope == 'all':
                        #or  showScope == 'same' and comp.__class__ == self.companion.__class__):
                        for evt in comp.textEventList:
                            if evt.trigger_meth not in self.extraOpts:
                                try: vals.index(evt.trigger_meth)
                                except ValueError: vals.append(evt.trigger_meth)

        scopeChoices = self.scopeOpts.keys()
        del scopeChoices[self.scopeOpts.values().index(showScope)]

        vals.extend(self.extraOpts + scopeChoices)
        return vals

    def _repopulateChoice(self, value):
        self.editorCtrl.repopulate()
        self.editorCtrl.setValue(value)

    def getValue(self):
        """ Return current value, or if a special (*) value is selected,
            process it, and return previous 'current value' """
        if self.editorCtrl:
            oldVal = defVal = self.value
            value = self.editorCtrl.getValue()
            # Event rename
            if value == '(rename)':
                if oldVal == '(delete)':
                    for evt in self.companion.textEventList:
                        if evt.trigger_meth == oldVal:
                            defVal = evt.prev_trigger_meth
                            break

                ted = wxTextEntryDialog(self.parent, 'Enter a new method name:',
                      'Rename event method', defVal)
                try:
                    if ted.ShowModal() == wxID_OK:
                        self.value = ted.GetValue()

                        # XXX All references should be renamed !!!!
                        # XXX Add as method on designer
                        for evt in self.companion.textEventList:
                            if evt.trigger_meth == oldVal:
                                if not evt.prev_trigger_meth:
                                    evt.prev_trigger_meth = oldVal
                                evt.trigger_meth = self.value
                                break
                        self._repopulateChoice(self.value)
                finally:
                    ted.Destroy()
            # Event deletion
            elif value == '(delete)':
                for evt in self.companion.textEventList:
                    if evt.trigger_meth == oldVal:
                        if not evt.prev_trigger_meth:
                            evt.prev_trigger_meth = oldVal
                        break
                self.value = self.editorCtrl.getValue()
            # Event scope change
            elif value in self.scopeOpts.keys():
                self.value = oldVal
                for evt in self.companion.textEventList:
                    if evt.event_name == self.name:
                        evt.show_scope = self.scopeOpts[value]
                        self._repopulateChoice(oldVal)
                        break
            # Normal event selection
            else:
                self.value = self.editorCtrl.getValue()

        return self.value

    def persistValue(self, value):
        self.companion.persistEvt(self.name, value)

class BITPropEditor(FactoryPropEdit):
    """ Editors for Built-in Python Types """
    def valueToIECValue(self):
        return `self.value`
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx, self.width)
    def getValue(self):
        if self.editorCtrl:
            try:
                value = self.companion.eval(self.editorCtrl.getValue())
            except Exception, mess:
                wxLogError('Invalid value: %s' % str(mess))
                raise
            self.value = value
        return self.value

class IntPropEdit(BITPropEditor):
    def inspectorEdit(self):
        self.editorCtrl = SpinCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx, self.width)

    def getValue(self):
        if self.editorCtrl:
            self.value = self.editorCtrl.getValue()
        return self.value

class StrPropEdit(BITPropEditor):
    def valueToIECValue(self):
        return self.value
    def getValue(self):
        return FactoryPropEdit.getValue(self)

# The following is a work in progress for having a string editor dlg that
# also handles gettext formatted strings
#
# The current problem is that for normal string properties, the property
# refers to a string object, not to the source reference, iow _() is not a string
##    def inspectorEdit(self):
##        self.editorCtrl = TextCtrlButtonIEC(self, self.value)
##        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)
##    def edit(self, event):
##        import StringPropEditorDlg
##        dlg = StringPropEditorDlg.create(self.parent, repr(self.value))
##        try:
##            if dlg.ShowModal() == wxID_OK:
##                self.inspectorPost(false)
##                pass
##        finally:
##            dlg.Destroy()


class NamePropEdit(StrPropEdit):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options, names):
        StrPropEdit.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width)

    identifier = string.letters+string.digits+'_'

    def getValue(self):
        # XXX Currently returning the old value in case of error because
        # XXX an exception here cannot be gracefully handled yet.
        # XXX Specifically closing the frame with the focus on the
        if self.editorCtrl:
            value = self.editorCtrl.getValue()
            if value != self.value:
                if self.companion.designer.objects.has_key(value):
                    wxLogError('Name already used by another control.')
                    return self.value

                if not value:
                    message = 'Invalid name for Python object'
                    wxLogError(message)
                    return self.value

                for c in value:
                    if c not in self.identifier:
                        message = 'Invalid name for Python object'
                        wxLogError(message)
                        return self.value
            self.value = value
        return self.value

class TuplePropEdit(BITPropEditor):
    pass

class BoolPropEdit(OptionedPropEdit):
    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options=None, names=None):
        OptionedPropEdit.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, options, names)

    def valueToIECValue(self):
        v = self.value
        if type(v) == IntType:
            return self.getValues()[v]
        else: return `v`
    def inspectorEdit(self):
        self.editorCtrl = CheckBoxIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.getValues()[self.value])
    def getDisplayValue(self):
        return self.valueToIECValue()
    def getValues(self):
        return ['False', 'True']
    def getValue(self):
        if self.editorCtrl:
            # trick to convert boolean string to integer
            v = self.editorCtrl.getValue()
            self.value = self.getValues().index(self.editorCtrl.getValue())
            #self.value = self.companion.eval(self.editorCtrl.getValue())
        return self.value

class EnumPropEdit(OptionedPropEdit):
    def valueToIECValue(self):
        if self.revNames:
            try:
                return self.revNames[self.value]
            except KeyError:
                return `self.value`

        else: OptionedPropEdit.getDisplayValue(self)
    def inspectorEdit(self):
        self.editorCtrl = ChoiceIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.setValue(self.value)
    def getDisplayValue(self):
        return self.valueToIECValue()
    def getValues(self):
        vals = self.names.keys()
        try:
            name = self.revNames[self.value]
        except KeyError:
            name = `self.value`
        if name not in vals:
            vals.append(name)
        # XXX !
        vals.sort()
        return vals
    def setValue(self, value):
        self.value = value
        if self.editorCtrl:
            try:
                self.editorCtrl.setValue(self.revNames[value])
            except KeyError:
                self.editorCtrl.setValue(`value`)
    def getValue(self):
        if self.editorCtrl:
            strVal = self.editorCtrl.getValue()
            try:
                self.value = self.names[strVal]
            except KeyError:
                self.value = self.companion.eval(strVal)

        return self.value

class StringEnumPropEdit(EnumPropEdit):
    def getDisplayValue(self):
        return `self.value`

# Property editors for classes
class ClassPropEdit(FactoryPropEdit):
    def getDisplayValue(self):
        return '('+self.value.__class__.__name__+')'
    def getStyle(self):
        return [esExpandable]

class ClassLinkPropEdit(OptionedPropEdit):
    defaults = {'None': None}
    linkClass = None
    def getStyle(self):
        return []
    def valueToIECValue(self):
        return self.getNameForValue(self.value, self.linkClass)
    
    def getNameForValue(self, value, LinkClass):
        for k, v in self.defaults.items():
            if value == v:
                return k
        objs = self.companion.designer.getObjectsOfClass(LinkClass)
        for objName in objs.keys():
            if objs[objName] and value and objs[objName] == value:
                return objName
        return `None`
    
    def inspectorEdit(self):
        self.editorCtrl = ChoiceIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.setValue(self.value)
    def getDisplayValue(self):
        return self.valueToIECValue()
    def getValues(self):
        defs = self.defaults.keys()
        defs.sort()
        return defs + self.companion.designer.getObjectsOfClass(self.linkClass).keys()
    def setValue(self, value):
        self.value = value
        if self.editorCtrl:
            self.editorCtrl.setValue(self.valueToIECValue())
    def getValue(self):
        if self.editorCtrl:
            strVal = self.editorCtrl.getValue()
            if self.defaults.has_key(strVal):
                self.value = self.defaults[strVal]
            else:
                objs = self.companion.designer.getObjectsOfClass(self.linkClass)
                self.value = objs[strVal]

        return self.value

class WindowClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxWindow

class WindowClassLinkWithParentPropEdit(WindowClassLinkPropEdit):
    def getValues(self):
        return ['None'] + self.companion.designer.getObjectsOfClassWithParent(
               self.linkClass, self.companion.name).keys()

class StatusBarClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxStatusBar

class ToolBarClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxToolBarBase

class MenuBarClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxMenuBar

class ImageListClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxImageList

class ButtonClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxButton

class CursorClassLinkPropEdit(ClassLinkPropEdit):
    defaults = {'None': wxNullCursor, 'wx.STANDARD_CURSOR': wxSTANDARD_CURSOR,
                'wx.HOURGLASS_CURSOR': wxHOURGLASS_CURSOR,
                'wx.CROSS_CURSOR': wxCROSS_CURSOR}
    linkClass = wxCursor

class ListCtrlImageListClassLinkPropEdit(ImageListClassLinkPropEdit):
    listTypeMap = {wxIMAGE_LIST_SMALL : 'wx.IMAGE_LIST_SMALL',
                   wxIMAGE_LIST_NORMAL: 'wx.IMAGE_LIST_NORMAL'}
    def valueToIECValue(self):
        if self.value[0] is None: return `None`
        objs = self.companion.designer.getObjectsOfClass(self.linkClass)
        for objName in objs.keys():
            if objs[objName] and self.value[0] and objs[objName] == self.value[0]:
                return objName
        return `None`

    def inspectorEdit(self):
        self.editorCtrl = ChoiceIEC(self, self.valueToIECValue())
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.valueToIECValue())

    def getValue(self):
        if self.editorCtrl:
            strVal = self.editorCtrl.getValue()
            if strVal == `None`:
                self.value = (None, self.value[1])
            else:
                objs = self.companion.designer.getObjectsOfClass(self.linkClass)
                self.value = (objs[strVal], self.value[1])
        return self.value

    def valueAsExpr(self):
        return '%s, %s'%(self.valueToIECValue(), self.listTypeMap[self.value[1]])

class SplitterWindowLinkPropEdit(WindowClassLinkPropEdit):
    def getValues(self):
        children = self.companion.designer.getObjectsOfClassWithParent(
               self.linkClass, self.companion.name).keys()
        otherWin = self.getOtherWindow()
        if otherWin:
            otherWinName = 'self.%s'%otherWin.GetName()
            if otherWinName in children:
                children.remove(otherWinName)

        return ['None'] + children

    def getOtherWindow(self): return None

class SplitterWindow1LinkPropEdit(SplitterWindowLinkPropEdit):
    def getOtherWindow(self):
        return self.companion.GetWindow2(None)

class SplitterWindow2LinkPropEdit(SplitterWindowLinkPropEdit):
    def getOtherWindow(self): 
        return self.companion.GetWindow1(None)

def getValidSizers(parent, designer, value):
    # build a list of nested parent sizers
    sizerParents = [parent]
    while hasattr(parent, '_sub_sizer'):
        parent = parent._sub_sizer
        sizerParents.append(parent)

    sizers = designer.getObjectsOfClass(wxSizer)
    # remove invalid sizers from the list
    for n, s in sizers.items():
        if s in sizerParents or \
              hasattr(s, '_sub_sizer') or hasattr(s, '_has_control'):
            del sizers[n]

    sizerNames = sizers.keys()
    sizerNames.sort()

    res = ['None'] + sizerNames
    if value != 'None':
        res.insert(1, value)
    return res
    


class SizerEnumConstrPropEdit(ObjEnumConstrPropEdit):
    def getObjects(self):
        return getValidSizers(self.companion.parentCompanion.control, 
                              self.companion.designer, self.value)

    def getCtrlValue(self):
        return self.companion.GetSizer()
    def setCtrlValue(self, oldValue, value):
        self.companion.SetSizer(value)

class SizerClassLinkPropEdit(ClassLinkPropEdit):
    linkClass = wxSizer
    def getValues(self):
        if self.value is None:
            value = 'None'
        else:
            value = self.getNameForValue(self.value, self.linkClass)
            
        return getValidSizers(self.companion.control, 
                              self.companion.designer, value)
    
    
class ColPropEdit(ClassPropEdit):
    def getStyle(self):
        return [esExpandable]

    def getSubCompanion(self):
        from Companions.Companions import ColourDTC
        return ColourDTC

    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        data = wxColourData()
        data.SetColour(self.value)
        data.SetChooseFull(true)
        dlg = wxColourDialog(self.parent, data)
        try:
            if dlg.ShowModal() == wxID_OK:
                self.value = dlg.GetColourData().GetColour()
                self.inspectorPost(false)
                self.editorCtrl.setValue(self.value)
                #self.propWrapper.setValue(self.value)
                #self.obj.Refresh()
        finally:
            dlg.Destroy()

    def getValue(self):
        return self.value#wxColour(self.value.Red(), self.value.Green(), self.value.Blue())

    def valueAsExpr(self):
        return 'wx.Colour(%d, %d, %d)'%(self.value.Red(), self.value.Green(), self.value.Blue())

class SizePropEdit(ClassPropEdit):
    def getDisplayValue(self):
        return self.valueToIECValue()
    def valueToIECValue(self):
        return `self.value`
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.valueToIECValue())
        self.editorCtrl.createControl(self.parent, self.valueToIECValue(), self.idx, self.width)
    def getValue(self):
        if self.editorCtrl:
            try:
                tuplePos = self.companion.eval(self.editorCtrl.getValue())
            except Exception, mess:
                Utils.ShowErrorMessage(self.parent, 'Invalid value', mess)
                raise
            self.value = wxSize(tuplePos[0], tuplePos[1])
        return self.value
    def valueAsExpr(self):
        return 'wx.Size(%d, %d)'%(self.value.x, self.value.y)
    def getSubCompanion(self):
        from Companions.Companions import SizeDTC
        return SizeDTC

class PosPropEdit(ClassPropEdit):
    def getDisplayValue(self):
        return self.valueToIECValue()
    def valueToIECValue(self):
        return `self.value`
    def inspectorEdit(self):
        self.editorCtrl = TextCtrlIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.value, self.idx, self.width)
    def getValue(self):
        if self.editorCtrl:
            try:
                tuplePos = self.companion.eval(self.editorCtrl.getValue())
            except Exception, mess:
                Utils.ShowErrorMessage(self.parent, 'Invalid value', mess)
                raise
            self.value = wxPoint(tuplePos[0], tuplePos[1])
        return self.value
    def valueAsExpr(self):
        return 'wx.Point(%d, %d)'%(self.value.x, self.value.y)
    def getSubCompanion(self):
        from Companions.Companions import PosDTC
        return PosDTC

class FontPropEdit(ClassPropEdit):
#    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, _1=None , _2=None):
#        ClassPropEdit.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width)

    def getStyle(self):
        return ClassPropEdit.getStyle(self) + [esDialog, esReadOnly, esRecreateProp]

    def getSubCompanion(self):
        from Companions.Companions import FontDTC
        return FontDTC

    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def getValue(self):
        return self.value

    def edit(self, event):
        data = wxFontData()
        dlg = wxFontDialog(self.parent, data)
        dlg.GetFontData().SetInitialFont(self.value)
        try:
            if dlg.ShowModal() == wxID_OK:
                self.value = dlg.GetFontData().GetChosenFont()
                self.inspectorPost(false)
        finally:
            dlg.Destroy()

    def valueAsExpr(self):
        fnt = self.value
        return fontAsExpr(fnt)

def fontAsExpr(fnt):
    fontFamily = Enumerations.reverseDict(Enumerations.fontFamilyNames)
    fontStyle = Enumerations.reverseDict(Enumerations.fontStyleNames)
    fontWeight = Enumerations.reverseDict(Enumerations.fontWeightNames)
    
    family = fontFamily.get(fnt.GetFamily(), fnt.GetFamily())
    style = fontStyle.get(fnt.GetStyle(), fnt.GetStyle())
    weight = fontWeight.get(fnt.GetWeight(), fnt.GetWeight())

    return 'wx.Font(%d, %s, %s, %s, %s, %s)'%(
        fnt.GetPointSize(), family, style, weight, 
        fnt.GetUnderlined() and 'True' or 'False',
        `fnt.GetFaceName()`)


class AnchorPropEdit(OptionedPropEdit):
    def getStyle(self):
        return [esExpandable]

    def getSubCompanion(self):
        from Companions.Companions import AnchorsDTC
        return AnchorsDTC

    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def edit(self, event):
        if self.expanded:
            wxMessageBox('Anchors can not be reset while the property is expanded',
                  'Anchors')
        else:
            if self.companion.anchorSettings:
                message = 'Remove anchors?'
            else:
                message = 'Define default Anchors?'

            dlg = wxMessageDialog(self.parent, message,
                              'Anchors', wxYES_NO | wxICON_QUESTION)
            try:
                if dlg.ShowModal() == wxID_YES:
                    if self.companion.anchorSettings:
                        self.companion.removeAnchors()
                        self.propWrapper.setValue(self.getValue())
                    else:
                        self.companion.defaultAnchors()
                        self.inspectorPost(false)
            finally:
                dlg.Destroy()

    def getValue(self):
        return self.companion.GetAnchors(self.companion)

    def getDisplayValue(self):
        if self.companion.anchorSettings:
            l, t, r, b = self.companion.anchorSettings
            set = []
            if l: set.append('left')
            if t: set.append('top')
            if r: set.append('right')
            if b: set.append('bottom')
            return '('+', '.join(set)+')'
        else:
            return 'None'

    def valueAsExpr(self):
        if self.companion.anchorSettings:
            l, t, r, b = self.companion.anchorSettings
            return 'LayoutAnchors(self.%s, %s, %s, %s, %s)'%(self.companion.name,
                l and 'True' or 'False', t and 'True' or 'False',
                r and 'True' or 'False', b and 'True' or 'False')
        else:
            return 'None'


class SashVisiblePropEdit(BoolPropEdit):
    sashEdgeMap = {wxSASH_LEFT: 'wx.SASH_LEFT', wxSASH_TOP: 'wx.SASH_TOP',
                   wxSASH_RIGHT: 'wx.SASH_RIGHT', wxSASH_BOTTOM: 'wx.SASH_BOTTOM'}
    def valueToIECValue(self):
        v = self.value[1]
        if type(v) == IntType:
            return self.getValues()[v]
        else: return `v`
    def inspectorEdit(self):
        self.editorCtrl = CheckBoxIEC(self, self.value[1])
        self.editorCtrl.createControl(self.parent, self.idx, self.width)
        self.editorCtrl.setValue(self.getValues()[self.value[1]])
##    def getDisplayValue(self):
##        return self.valueToIECValue()
##    def getValues(self):
##        return ['false', 'true']
    def getValue(self):
        if self.editorCtrl:
            # trick to convert boolean string to integer
            v = self.editorCtrl.getValue()
            self.value = (self.value[0], self.getValues().index(self.editorCtrl.getValue()))
        return self.value
    def valueAsExpr(self):
        return '%s, %s'%(self.sashEdgeMap[self.value[0]],
                         self.value[1] and 'True' or 'False')

class CollectionPropEdit(PropertyEditor):
    """ Class associated with a design time identified type,
        it manages the behaviour of a NameValue in the Inspector
    """

    def __init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width, names, options):
        PropertyEditor.__init__(self, name, parent, companion, rootCompanion, propWrapper, idx, width)

    def inspectorEdit(self):
        self.editorCtrl = ButtonIEC(self, self.value)
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)

    def inspectorPost(self, closeEditor = true):
        """ Code persistance taken over by companion because collection
            transactions live longer than properties
        """
        if self.editorCtrl and closeEditor:
            self.editorCtrl.destroyControl()
            self.editorCtrl = None
            self.refreshCompCtrl()

    def getDisplayValue(self):
        return '(%s)'%self.name

    def valueAsExpr(self):
        return self.getDisplayValue()

    def edit(self, event):
        self.companion.designer.showCollectionEditor(\
          self.companion.name, self.name)

class ListColumnsColPropEdit(CollectionPropEdit): pass
class AcceleratorEntriesColPropEdit(CollectionPropEdit): pass
class MenuBarColPropEdit(CollectionPropEdit): pass
class MenuColPropEdit(CollectionPropEdit): pass
class ImagesColPropEdit(CollectionPropEdit): pass
class NotebookPagesColPropEdit(CollectionPropEdit): pass

# Property editor registration

def registerEditors(reg):
    for theType, theClass, editors in registeredTypes:
        if theType == 'Type':
            reg.registerTypes(theClass, editors)
        elif theType == 'Class':
            reg.registerClasses(theClass, editors)

registeredTypes = [
    ('Type', IntType, [IntPropEdit]),
    ('Type', StringType, [StrPropEdit]),
    ('Type', UnicodeType, [StrPropEdit]),
    ('Type', TupleType, [TuplePropEdit]),
    ('Class', wxSize, [SizePropEdit]),
    ('Class', wxSizePtr, [SizePropEdit]),
    ('Class', wxPoint, [PosPropEdit]),
    ('Class', wxPointPtr, [PosPropEdit]),
    ('Class', wxFontPtr, [FontPropEdit]),
    ('Class', wxFont, [FontPropEdit]),
    ('Class', wxColourPtr, [ColPropEdit]),
    ('Class', wxColour, [ColPropEdit]),
    ('Class', wxBitmapPtr, [BitmapPropEdit]),
    ('Class', wxBitmap, [BitmapPropEdit]),
    ('Class', wxIconPtr, [IconPropEdit]),
    ('Class', wxIcon, [IconPropEdit]),
    ('Class', wxValidator, [ClassLinkPropEdit]),
]

try:
    registeredTypes.append( ('Type', BooleanType, [BoolPropEdit]) )
except NameError: # 2.2
    pass 
