<reponame>itsyaboyrocket/ttleveleditor
"""
UI for object property control
"""
import wx
import os
import math

from wx.lib.embeddedimage import PyEmbeddedImage
from wx.lib.scrolledpanel import ScrolledPanel
from wx.lib.agw.cubecolourdialog import *
from direct.wxwidgets.WxSlider import *
from pandac.PandaModules import *
import ObjectGlobals as OG
import AnimGlobals as AG

#----------------------------------------------------------------------
Key = PyEmbeddedImage(
    "<KEY>"
    "<KEY>"
    "<KEY>"
    "<KEY>"
    "<KEY>"
    "SZJ8bDan06ksSyLiQmDXCfr9fp7nb8vldDp9mc9d1/1R27XaClscxzkcDlEUhcOhvt06U1uE"
    "EMaYtpbOXlu01vf5Hz/wDRuDdIDl5WtQAAAAAElFTkSuQmCC")
#----------------------------------------------------------------------

class AnimFileDrop(wx.FileDropTarget):
    def __init__(self, editor):
        wx.FileDropTarget.__init__(self)
        self.editor = editor

    def OnDropFiles(self, x, y, filenames):
        obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
        if obj is None:
            return

        objDef = obj[OG.OBJ_DEF]
        if not objDef.actor:
            return

        objNP = obj[OG.OBJ_NP]

        for filename in filenames:
            name = os.path.basename(filename)
            animName = Filename.fromOsSpecific(filename).getFullpath()
            if name.endswith('.mb') or\
               name.endswith('.ma'):
                self.editor.convertMaya(animName, self.editor.ui.protoPaletteUI.addNewItem, obj, isAnim=True)
                return

            if animName not in objDef.anims:
                objDef.anims.append(animName)

            objNP.loadAnims({name:animName})
            objNP.loop(name)
            obj[OG.OBJ_ANIM] = animName
            self.editor.ui.objectPropertyUI.updateProps(obj)
            
class ObjectPropUI(wx.Panel):
    """
    Base class for ObjectPropUIs,
    It consists of label area and ui area.
    """
    def __init__(self, parent, label):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.labelPane = wx.Panel(self)
        self.label = wx.StaticText(self.labelPane, label=label)
        self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.labelSizer.Add(self.label)
        bmpKey = Key.GetBitmap()
        self.setKeyButton = wx.BitmapButton(self.labelPane, -1, bmpKey, size = (15,15),style = wx.BU_AUTODRAW)
        self.labelSizer.Add(self.setKeyButton)
        self.labelPane.SetSizer(self.labelSizer)
        self.uiPane = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.labelPane)
        sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
        self.SetSizer(sizer)
        
        self.setKeyButton.Bind(wx.EVT_BUTTON, self.onKey)

    def onKey(self,evt):
        self.parent = wx.GetTopLevelParent(self)
        if self.parent.editor.mode == self.parent.editor.ANIM_MODE:
            obj= self.parent.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
            
            objUID = obj[OG.OBJ_UID]
            propertyName = self.label.GetLabelText()
            
            value = self.getValue()
            frame = self.parent.editor.ui.animUI.curFrame
            
            if self.parent.editor.animMgr.keyFramesInfo.has_key((objUID,propertyName)):
                for i in range(len(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])):
                    if self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i][AG.FRAME] == frame:
                        del self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i]
                self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)].append([frame, value, [], []])
                #sort keyFrameInfo list by the order of frame number
                sortKeyList = self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)]
                for i in range(0, len(sortKeyList)-1):
                    for j in range(i+1, len(sortKeyList)):
                        if sortKeyList[i][AG.FRAME]>sortKeyList[j][AG.FRAME]:
                            temp = sortKeyList[i]
                            sortKeyList[i] = sortKeyList[j]
                            sortKeyList[j] = temp
                            
                self.parent.editor.animMgr.generateSlope(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])
            else:
                self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)] = [[frame, value, [], []]]
            
            exist = False
            for keyFrame in self.parent.editor.animMgr.keyFrames:
                if frame == keyFrame:
                    exist = True
                    break
            
            if exist == False:
                self.parent.editor.animMgr.keyFrames.append(frame)
                self.parent.editor.ui.animUI.OnPropKey()

            else:
                self.parent.editor.ui.animUI.OnPropKey()
                
        else:
            evt.Skip()
        
    def setValue(self, value):
        self.ui.SetValue(value)

    def getValue(self):
        return self.ui.GetValue()

    def bindFunc(self, inFunc, outFunc, valFunc = None):
        self.ui.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.ui.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        if valFunc:
            self.ui.Bind(self.eventType, valFunc)

class ObjectPropUIEntry(ObjectPropUI):
    """ UI for string value properties """
    def __init__(self, parent, label):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.TextCtrl(self.uiPane, -1)
        self.eventType = wx.EVT_TEXT_ENTER
        self.Layout()

    def setValue(self, value):
        self.ui.SetValue(str(value))

class ObjectPropUISlider(ObjectPropUI):
    """ UI for float value properties """
    def __init__(self, parent, label, value, minValue, maxValue):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = WxSlider(self.uiPane, -1, value, minValue, maxValue,
                           pos = (0,0), size=(140, -1),
                           style=wx.SL_HORIZONTAL | wx.SL_LABELS)
        self.ui.Enable()
        self.Layout()

    def bindFunc(self, inFunc, outFunc, valFunc = None):
        self.ui.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.ui.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        self.ui.textValue.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.ui.textValue.Bind(wx.EVT_LEAVE_WINDOW, outFunc)

        if valFunc:
            self.ui.bindFunc(valFunc)


class ObjectPropUISpinner(ObjectPropUI):
    """ UI for int value properties """
    def __init__(self, parent, label, value, minValue, maxValue):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.SpinCtrl(self.uiPane, -1, "", min=minValue, max=maxValue, initial=value)
        self.eventType = wx.EVT_SPIN
        self.Layout()


class ObjectPropUICheck(ObjectPropUI):
    def __init__(self, parent, label, value):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.CheckBox(self.uiPane, -1, "", size=(50, 30))
        self.setValue(value)
        self.eventType = wx.EVT_CHECKBOX
        self.Layout()


class ObjectPropUIRadio(ObjectPropUI):
    def __init__(self, parent, label, value, valueList):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.RadioBox(self.uiPane, -1, "", choices=valueList, majorDimension=1, style=wx.RA_SPECIFY_COLS)
        self.setValue(value)
        self.eventType = wx.EVT_RADIOBOX
        self.Layout()

    def setValue(self, value):
        self.ui.SetStringSelection(value)

    def getValue(self):
        return self.ui.GetStringSelection()


class ObjectPropUICombo(ObjectPropUI):
    def __init__(self, parent, label, value, valueList, obj=None, callBack=None):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.Choice(self.uiPane, -1, choices=valueList)
        if callBack is not None:
            button = wx.Button(self.labelPane, -1, 'Update', size = (100, 18))
            button.Bind(wx.EVT_BUTTON, lambda p0=None, p1=obj, p2=self: callBack(p0, p1, p2))
            self.labelSizer.Add(button)
        self.setValue(value)
        self.eventType = wx.EVT_CHOICE
        self.Layout()

    def setValue(self, value):
        self.ui.SetStringSelection(value)

    def getValue(self):
        return self.ui.GetStringSelection()

    def setItems(self, valueList):
        self.ui.SetItems(valueList)

class ObjectPropUITime(wx.Panel):
    def __init__(self, parent, label, value):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.labelPane = wx.Panel(self)
        self.label = wx.StaticText(self.labelPane, label=label)
        self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.labelSizer.Add(self.label)
        self.labelPane.SetSizer(self.labelSizer)
        self.uiPane = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.labelPane)
        sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
        self.SetSizer(sizer)

        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.uiAmPm = wx.Choice(self.uiPane, -1, choices=['AM', 'PM'])
        self.uiHour = wx.Choice(self.uiPane, -1, choices=map(lambda x : str(x), range(1, 13)))
        self.uiMin = wx.Choice(self.uiPane, -1, choices=map(lambda x : str(x), range(0, 60, 15)))

        hSizer.Add(self.uiAmPm)
        hSizer.Add(self.uiHour)
        hSizer.Add(self.uiMin)
        self.uiPane.SetSizer(hSizer)

        self.setValue(value)
        self.eventType = wx.EVT_CHOICE
        self.Layout()

    def setValue(self, value):
        hourVal = int(math.floor(value))
        minVal = [0, 15, 30, 45][int((value - hourVal) * 4)]

        if hourVal > 11:
            ampmStr = 'PM'
            hourVal = hourVal - 12
        else:
            ampmStr = 'AM'

        if hourVal == 0:
            hourVal = 12

        self.uiAmPm.SetStringSelection(ampmStr)
        self.uiHour.SetStringSelection(str(hourVal))
        self.uiMin.SetStringSelection(str(minVal))

    def getValue(self):
        ampmStr = self.uiAmPm.GetStringSelection()
        hourVal = int(self.uiHour.GetStringSelection())
        if hourVal == 12:
            hourVal = 0
        if ampmStr == 'PM':
            hourVal += 12

        minVal = float(self.uiMin.GetStringSelection())
        value = float(hourVal) + minVal / 60.0
        return value

    def bindFunc(self, inFunc, outFunc, valFunc = None):
        self.uiAmPm.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.uiAmPm.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        self.uiHour.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.uiHour.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        self.uiMin.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.uiMin.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        if valFunc:
            self.uiAmPm.Bind(self.eventType, valFunc)
            self.uiHour.Bind(self.eventType, valFunc)
            self.uiMin.Bind(self.eventType, valFunc)            

class ColorPicker(CubeColourDialog):
    def __init__(self, parent, colourData=None, style=CCD_SHOW_ALPHA, alpha = 255, updateCB=None, exitCB=None):
        self.updateCB=updateCB
        CubeColourDialog.__init__(self, parent, colourData, style)
        self.okButton.Hide()
        self.cancelButton.Hide()
        self._colour.alpha = alpha
        self.alphaSpin.SetValue(self._colour.alpha)
        self.DrawAlpha()
        if exitCB:
            self.Bind(wx.EVT_CLOSE, exitCB)

    def SetPanelColours(self):
        self.oldColourPanel.RefreshColour(self._oldColour)
        self.newColourPanel.RefreshColour(self._colour)
        if self.updateCB:
            self.updateCB(self._colour.r, self._colour.g, self._colour.b, self._colour.alpha)

class ObjectPropertyUI(ScrolledPanel):
    def __init__(self, parent, editor):
        self.editor = editor
        self.colorPicker = None
        self.lastColorPickerPos = None
        self.lastPropTab = None
        ScrolledPanel.__init__(self, parent)

        parentSizer = wx.BoxSizer(wx.VERTICAL)
        parentSizer.Add(self, 1, wx.EXPAND, 0)
        parent.SetSizer(parentSizer); parent.Layout()

        self.SetDropTarget(AnimFileDrop(self.editor))

    def clearPropUI(self):
        sizer = self.GetSizer()
        if sizer is not None:
            self.lastPropTab = self.nb.GetCurrentPage().GetName()
            sizer.Remove(self.propPane)
            self.propPane.Destroy()
            self.SetSizer(None)
        self.Layout()
        self.SetupScrolling(self, scroll_y = True, rate_y = 20)

    def colorPickerExitCB(self, evt=None):
        self.lastColorPickerPos = self.colorPicker.GetPosition()
        self.colorPicker.Destroy()
        self.colorPicker = None

    def colorPickerUpdateCB(self, rr, gg, bb, aa):
        r = rr / 255.0
        g = gg / 255.0
        b = bb / 255.0
        a = aa / 255.0
        self.propCR.setValue(r)
        self.propCG.setValue(g)
        self.propCB.setValue(b)
        self.propCA.setValue(a)        

        self.editor.objectMgr.updateObjectColor(r, g, b, a)

    def onColorSlider(self, evt):
        r = float(self.editor.ui.objectPropertyUI.propCR.getValue())
        g = float(self.editor.ui.objectPropertyUI.propCG.getValue())
        b = float(self.editor.ui.objectPropertyUI.propCB.getValue())
        a = float(self.editor.ui.objectPropertyUI.propCA.getValue())

        if self.colorPicker:
            evtObj = evt.GetEventObject()
            if evtObj == self.propCR.ui or\
               evtObj == self.propCR.ui.textValue:
                self.colorPicker.redSpin.SetValue(r * 255)
                self.colorPicker.AssignColourValue('r', r * 255, 255, 0)
            elif evtObj == self.propCG.ui or\
                 evtObj == self.propCG.ui.textValue:
                self.colorPicker.greenSpin.SetValue(g * 255)
                self.colorPicker.AssignColourValue('g', g * 255, 255, 0)
            elif evtObj == self.propCB.ui or\
                 evtObj == self.propCB.ui.textValue:
                self.colorPicker.blueSpin.SetValue(b * 255)
                self.colorPicker.AssignColourValue('b', b * 255, 255, 0)
            else:
                self.colorPicker._colour.alpha = a * 255
                self.colorPicker.alphaSpin.SetValue(self.colorPicker._colour.alpha)
                self.colorPicker.DrawAlpha()

        self.editor.objectMgr.updateObjectColor(r, g, b, a)
        
    def openColorPicker(self, evt, colourData, alpha):
        if self.colorPicker:
            self.lastColorPickerPos = self.colorPicker.GetPosition()
            self.colorPicker.Destroy()

        self.colorPicker = ColorPicker(self, colourData, alpha=alpha, updateCB=self.colorPickerUpdateCB, exitCB=self.colorPickerExitCB)
        self.colorPicker.GetColourData().SetChooseFull(True)
        self.colorPicker.Show()
        if self.lastColorPickerPos:
            self.colorPicker.SetPosition(self.lastColorPickerPos)
        
    def updateProps(self, obj, movable=True):
        self.clearPropUI()
        
        self.propPane = wx.Panel(self)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(self.propPane, 1, wx.EXPAND, 0)
        self.SetSizer(mainSizer)

        self.nb = wx.Notebook(self.propPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.nb, 1, wx.EXPAND)
        self.propPane.SetSizer(sizer)

        self.transformPane = wx.Panel(self.nb, -1, name='Transform')
        self.nb.AddPage(self.transformPane, 'Transform')

        self.propX = ObjectPropUIEntry(self.transformPane, 'X')
        self.propY = ObjectPropUIEntry(self.transformPane, 'Y')
        self.propZ = ObjectPropUIEntry(self.transformPane, 'Z')

        self.propH = ObjectPropUISlider(self.transformPane, 'H', 0, 0, 360)
        self.propP = ObjectPropUISlider(self.transformPane, 'P', 0, 0, 360)
        self.propR = ObjectPropUISlider(self.transformPane, 'R', 0, 0, 360)

        self.propSX = ObjectPropUIEntry(self.transformPane, 'SX')
        self.propSY = ObjectPropUIEntry(self.transformPane, 'SY')
        self.propSZ = ObjectPropUIEntry(self.transformPane, 'SZ')

        transformProps = [self.propX, self.propY, self.propZ, self.propH, self.propP, self.propR,
                       self.propSX, self.propSY, self.propSZ]

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddMany(transformProps)
        self.transformPane.SetSizer(sizer)
        for transformProp in transformProps:
            transformProp.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                                   self.editor.objectMgr.onLeaveObjectPropUI,
                                   self.editor.objectMgr.updateObjectTransform)

        if not movable:
            for transformProp in transformProps:
                transformProp.ui.Disable()

        self.lookPane = wx.Panel(self.nb, -1, name='Look')
        self.nb.AddPage(self.lookPane, 'Look')

        objNP = obj[OG.OBJ_NP]
        objRGBA = obj[OG.OBJ_RGBA]
        self.propCR = ObjectPropUISlider(self.lookPane, 'CR', objRGBA[0], 0, 1)
        self.propCG = ObjectPropUISlider(self.lookPane, 'CG', objRGBA[1], 0, 1)
        self.propCB = ObjectPropUISlider(self.lookPane, 'CB', objRGBA[2], 0, 1)        
        self.propCA = ObjectPropUISlider(self.lookPane, 'CA', objRGBA[3], 0, 1) 
        colorProps = [self.propCR, self.propCG, self.propCB, self.propCA]

        for colorProp in colorProps:
            colorProp.ui.bindFunc(self.onColorSlider)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddMany(colorProps)
        button = wx.Button(self.lookPane, -1, 'Color Picker', (0,0), (140, 20))
        _colourData = wx.ColourData()
        _colourData.SetColour(wx.Colour(objRGBA[0] * 255, objRGBA[1] * 255, objRGBA[2] * 255))
        button.Bind(wx.EVT_BUTTON, lambda p0=None, p1=_colourData, p2=objRGBA[3] * 255: self.openColorPicker(p0, p1, p2))

        sizer.Add(button)

        if self.colorPicker:
            self.openColorPicker(None, _colourData, objRGBA[3] * 255)

        objDef = obj[OG.OBJ_DEF]

        if objDef.updateModelFunction is not None or (objDef.model is not None and len(objDef.models) > 0):
            defaultModel = obj[OG.OBJ_MODEL]
            if defaultModel is None:
                defaultModel = ''

            if len(objDef.models) == 0:
                modelList = ''
            else:
                modelList = objDef.models
            propUI = ObjectPropUICombo(self.lookPane, 'model', defaultModel, modelList, obj, callBack=objDef.updateModelFunction)
            sizer.Add(propUI)            

            propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                            self.editor.objectMgr.onLeaveObjectPropUI,
                            lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModelFromUI(p0, p1))

        animList = objDef.animDict.get(obj[OG.OBJ_MODEL])
        if len(objDef.anims) > 0 or animList:
            if animList is None:
                animList = objDef.anims
                
            propUI = ObjectPropUICombo(self.lookPane, 'anim', obj[OG.OBJ_ANIM], animList)
            sizer.Add(propUI)            

            propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                            self.editor.objectMgr.onLeaveObjectPropUI,
                            lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectAnimFromUI(p0, p1))

        self.lookPane.SetSizer(sizer)

        self.propsPane = wx.Panel(self.nb, -1, name='Properties')
        self.nb.AddPage(self.propsPane, 'Properties')
        sizer = wx.BoxSizer(wx.VERTICAL)

        propNames = objDef.orderedProperties[:]
        for key in objDef.properties.keys():
            if key not in propNames:
                propNames.append(key)

        for key in propNames:
            # handling properties mask
            propMask = BitMask32()
            for modeKey in objDef.propertiesMask.keys():
                if key in objDef.propertiesMask[modeKey]:
                    propMask |= modeKey

            if not propMask.isZero():
                if (self.editor.mode & propMask).isZero():
                    continue

            propDef = objDef.properties[key]
            propType = propDef[OG.PROP_TYPE]
            propDataType = propDef[OG.PROP_DATATYPE]
            value = obj[OG.OBJ_PROP].get(key)

            if propType == OG.PROP_UI_ENTRY:
                propUI = ObjectPropUIEntry(self.propsPane, key)
                propUI.setValue(value)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_SLIDE:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]

                if value is None:
                    continue

                if propDataType != OG.PROP_FLOAT:
                    value = float(value)

                propUI = ObjectPropUISlider(self.propsPane, key, value, propRange[OG.RANGE_MIN], propRange[OG.RANGE_MAX])
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_SPIN:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]

                if value is None:
                    continue

                propUI = ObjectPropUISpinner(self.propsPane, key, value, propRange[OG.RANGE_MIN], propRange[OG.RANGE_MAX])
                sizer.Add(propUI)                

            elif propType == OG.PROP_UI_CHECK:
                if value is None:
                    continue

                propUI = ObjectPropUICheck(self.propsPane, key, value)
                sizer.Add(propUI)                  

            elif propType == OG.PROP_UI_RADIO:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]
                
                if value is None:
                    continue

                if propDataType != OG.PROP_STR:
                    for i in range(len(propRange)):
                        propRange[i] = str(propRange[i])

                    value = str(value)

                propUI = ObjectPropUIRadio(self.propsPane, key, value, propRange)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_COMBO:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]                

                if value is None:
                    continue

                if propDataType != OG.PROP_STR:
                    for i in range(len(propRange)):
                        propRange[i] = str(propRange[i])

                    value = str(value)

                propUI = ObjectPropUICombo(self.propsPane, key, value, propRange)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_COMBO_DYNAMIC:
                if len(propDef) <= OG.PROP_DYNAMIC_KEY:
                    continue
                
                propDynamicKey = propDef[OG.PROP_DYNAMIC_KEY]
                if propDynamicKey == OG.PROP_MODEL:
                    dynamicRangeKey = obj[OG.OBJ_MODEL]
                else:
                    dynamicRangeKey = obj[OG.OBJ_PROP].get(propDynamicKey)

                if dynamicRangeKey is None:
                    self.editor.objectMgr.updateObjectPropValue(obj, key, propDef[OG.PROP_DEFAULT], fUndo=False)
                    continue

                propRange = propDef[OG.PROP_RANGE].get(dynamicRangeKey)

                if propRange is None:
                    self.editor.objectMgr.updateObjectPropValue(obj, key, propDef[OG.PROP_DEFAULT], fUndo=False)
                    continue

                if value is None:
                    continue

                if propDataType != OG.PROP_STR:
                    for i in range(len(propRange)):
                        propRange[i] = str(propRange[i])

                    value = str(value)

                if value not in propRange:
                    value = propRange[0]
                    self.editor.objectMgr.updateObjectPropValue(obj, key, value, fUndo=False)
                    
                propUI = ObjectPropUICombo(self.propsPane, key, value, propRange)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_TIME:

                if value is None:
                    continue

                propUI = ObjectPropUITime(self.propsPane, key, value)
                sizer.Add(propUI)
                
            else:
                # unspported property type
                continue

            propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                            self.editor.objectMgr.onLeaveObjectPropUI,
                            lambda p0=None, p1=obj, p2=key: self.editor.objectMgr.updateObjectProperty(p0, p1, p2))


        self.propsPane.SetSizer(sizer);
        self.Layout()
        self.SetupScrolling(self, scroll_y = True, rate_y = 20)
        if self.lastPropTab == 'Transform':
            self.nb.SetSelection(0)
        elif self.lastPropTab == 'Look':
            self.nb.SetSelection(1)
        elif self.lastPropTab == 'Properties':
            self.nb.SetSelection(2)
            

"""
UI for object property control
"""
import wx
import os
import math

from wx.lib.embeddedimage import PyEmbeddedImage
from wx.lib.scrolledpanel import ScrolledPanel
from wx.lib.agw.cubecolourdialog import *
from direct.wxwidgets.WxSlider import *
from pandac.PandaModules import *
import ObjectGlobals as OG
import AnimGlobals as AG

#----------------------------------------------------------------------
Key = PyEmbeddedImage(
    "<KEY>"
    "<KEY>"
    "<KEY>"
    "<KEY>"
    "<KEY>"
    "SZJ8bDan06ksSyLiQmDXCfr9fp7nb8vldDp9mc9d1/1R27XaClscxzkcDlEUhcOhvt06U1uE"
    "EMaYtpbOXlu01vf5Hz/wDRuDdIDl5WtQAAAAAElFTkSuQmCC")
#----------------------------------------------------------------------

class AnimFileDrop(wx.FileDropTarget):
    def __init__(self, editor):
        wx.FileDropTarget.__init__(self)
        self.editor = editor

    def OnDropFiles(self, x, y, filenames):
        obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
        if obj is None:
            return

        objDef = obj[OG.OBJ_DEF]
        if not objDef.actor:
            return

        objNP = obj[OG.OBJ_NP]

        for filename in filenames:
            name = os.path.basename(filename)
            animName = Filename.fromOsSpecific(filename).getFullpath()
            if name.endswith('.mb') or\
               name.endswith('.ma'):
                self.editor.convertMaya(animName, self.editor.ui.protoPaletteUI.addNewItem, obj, isAnim=True)
                return

            if animName not in objDef.anims:
                objDef.anims.append(animName)

            objNP.loadAnims({name:animName})
            objNP.loop(name)
            obj[OG.OBJ_ANIM] = animName
            self.editor.ui.objectPropertyUI.updateProps(obj)
            
class ObjectPropUI(wx.Panel):
    """
    Base class for ObjectPropUIs,
    It consists of label area and ui area.
    """
    def __init__(self, parent, label):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.labelPane = wx.Panel(self)
        self.label = wx.StaticText(self.labelPane, label=label)
        self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.labelSizer.Add(self.label)
        bmpKey = Key.GetBitmap()
        self.setKeyButton = wx.BitmapButton(self.labelPane, -1, bmpKey, size = (15,15),style = wx.BU_AUTODRAW)
        self.labelSizer.Add(self.setKeyButton)
        self.labelPane.SetSizer(self.labelSizer)
        self.uiPane = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.labelPane)
        sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
        self.SetSizer(sizer)
        
        self.setKeyButton.Bind(wx.EVT_BUTTON, self.onKey)

    def onKey(self,evt):
        self.parent = wx.GetTopLevelParent(self)
        if self.parent.editor.mode == self.parent.editor.ANIM_MODE:
            obj= self.parent.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
            
            objUID = obj[OG.OBJ_UID]
            propertyName = self.label.GetLabelText()
            
            value = self.getValue()
            frame = self.parent.editor.ui.animUI.curFrame
            
            if self.parent.editor.animMgr.keyFramesInfo.has_key((objUID,propertyName)):
                for i in range(len(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])):
                    if self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i][AG.FRAME] == frame:
                        del self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i]
                self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)].append([frame, value, [], []])
                #sort keyFrameInfo list by the order of frame number
                sortKeyList = self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)]
                for i in range(0, len(sortKeyList)-1):
                    for j in range(i+1, len(sortKeyList)):
                        if sortKeyList[i][AG.FRAME]>sortKeyList[j][AG.FRAME]:
                            temp = sortKeyList[i]
                            sortKeyList[i] = sortKeyList[j]
                            sortKeyList[j] = temp
                            
                self.parent.editor.animMgr.generateSlope(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])
            else:
                self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)] = [[frame, value, [], []]]
            
            exist = False
            for keyFrame in self.parent.editor.animMgr.keyFrames:
                if frame == keyFrame:
                    exist = True
                    break
            
            if exist == False:
                self.parent.editor.animMgr.keyFrames.append(frame)
                self.parent.editor.ui.animUI.OnPropKey()

            else:
                self.parent.editor.ui.animUI.OnPropKey()
                
        else:
            evt.Skip()
        
    def setValue(self, value):
        self.ui.SetValue(value)

    def getValue(self):
        return self.ui.GetValue()

    def bindFunc(self, inFunc, outFunc, valFunc = None):
        self.ui.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.ui.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        if valFunc:
            self.ui.Bind(self.eventType, valFunc)

class ObjectPropUIEntry(ObjectPropUI):
    """ UI for string value properties """
    def __init__(self, parent, label):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.TextCtrl(self.uiPane, -1)
        self.eventType = wx.EVT_TEXT_ENTER
        self.Layout()

    def setValue(self, value):
        self.ui.SetValue(str(value))

class ObjectPropUISlider(ObjectPropUI):
    """ UI for float value properties """
    def __init__(self, parent, label, value, minValue, maxValue):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = WxSlider(self.uiPane, -1, value, minValue, maxValue,
                           pos = (0,0), size=(140, -1),
                           style=wx.SL_HORIZONTAL | wx.SL_LABELS)
        self.ui.Enable()
        self.Layout()

    def bindFunc(self, inFunc, outFunc, valFunc = None):
        self.ui.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.ui.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        self.ui.textValue.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.ui.textValue.Bind(wx.EVT_LEAVE_WINDOW, outFunc)

        if valFunc:
            self.ui.bindFunc(valFunc)


class ObjectPropUISpinner(ObjectPropUI):
    """ UI for int value properties """
    def __init__(self, parent, label, value, minValue, maxValue):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.SpinCtrl(self.uiPane, -1, "", min=minValue, max=maxValue, initial=value)
        self.eventType = wx.EVT_SPIN
        self.Layout()


class ObjectPropUICheck(ObjectPropUI):
    def __init__(self, parent, label, value):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.CheckBox(self.uiPane, -1, "", size=(50, 30))
        self.setValue(value)
        self.eventType = wx.EVT_CHECKBOX
        self.Layout()


class ObjectPropUIRadio(ObjectPropUI):
    def __init__(self, parent, label, value, valueList):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.RadioBox(self.uiPane, -1, "", choices=valueList, majorDimension=1, style=wx.RA_SPECIFY_COLS)
        self.setValue(value)
        self.eventType = wx.EVT_RADIOBOX
        self.Layout()

    def setValue(self, value):
        self.ui.SetStringSelection(value)

    def getValue(self):
        return self.ui.GetStringSelection()


class ObjectPropUICombo(ObjectPropUI):
    def __init__(self, parent, label, value, valueList, obj=None, callBack=None):
        ObjectPropUI.__init__(self, parent, label)
        self.ui = wx.Choice(self.uiPane, -1, choices=valueList)
        if callBack is not None:
            button = wx.Button(self.labelPane, -1, 'Update', size = (100, 18))
            button.Bind(wx.EVT_BUTTON, lambda p0=None, p1=obj, p2=self: callBack(p0, p1, p2))
            self.labelSizer.Add(button)
        self.setValue(value)
        self.eventType = wx.EVT_CHOICE
        self.Layout()

    def setValue(self, value):
        self.ui.SetStringSelection(value)

    def getValue(self):
        return self.ui.GetStringSelection()

    def setItems(self, valueList):
        self.ui.SetItems(valueList)

class ObjectPropUITime(wx.Panel):
    def __init__(self, parent, label, value):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.labelPane = wx.Panel(self)
        self.label = wx.StaticText(self.labelPane, label=label)
        self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.labelSizer.Add(self.label)
        self.labelPane.SetSizer(self.labelSizer)
        self.uiPane = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.labelPane)
        sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
        self.SetSizer(sizer)

        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.uiAmPm = wx.Choice(self.uiPane, -1, choices=['AM', 'PM'])
        self.uiHour = wx.Choice(self.uiPane, -1, choices=map(lambda x : str(x), range(1, 13)))
        self.uiMin = wx.Choice(self.uiPane, -1, choices=map(lambda x : str(x), range(0, 60, 15)))

        hSizer.Add(self.uiAmPm)
        hSizer.Add(self.uiHour)
        hSizer.Add(self.uiMin)
        self.uiPane.SetSizer(hSizer)

        self.setValue(value)
        self.eventType = wx.EVT_CHOICE
        self.Layout()

    def setValue(self, value):
        hourVal = int(math.floor(value))
        minVal = [0, 15, 30, 45][int((value - hourVal) * 4)]

        if hourVal > 11:
            ampmStr = 'PM'
            hourVal = hourVal - 12
        else:
            ampmStr = 'AM'

        if hourVal == 0:
            hourVal = 12

        self.uiAmPm.SetStringSelection(ampmStr)
        self.uiHour.SetStringSelection(str(hourVal))
        self.uiMin.SetStringSelection(str(minVal))

    def getValue(self):
        ampmStr = self.uiAmPm.GetStringSelection()
        hourVal = int(self.uiHour.GetStringSelection())
        if hourVal == 12:
            hourVal = 0
        if ampmStr == 'PM':
            hourVal += 12

        minVal = float(self.uiMin.GetStringSelection())
        value = float(hourVal) + minVal / 60.0
        return value

    def bindFunc(self, inFunc, outFunc, valFunc = None):
        self.uiAmPm.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.uiAmPm.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        self.uiHour.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.uiHour.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        self.uiMin.Bind(wx.EVT_ENTER_WINDOW, inFunc)
        self.uiMin.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
        if valFunc:
            self.uiAmPm.Bind(self.eventType, valFunc)
            self.uiHour.Bind(self.eventType, valFunc)
            self.uiMin.Bind(self.eventType, valFunc)            

class ColorPicker(CubeColourDialog):
    def __init__(self, parent, colourData=None, style=CCD_SHOW_ALPHA, alpha = 255, updateCB=None, exitCB=None):
        self.updateCB=updateCB
        CubeColourDialog.__init__(self, parent, colourData, style)
        self.okButton.Hide()
        self.cancelButton.Hide()
        self._colour.alpha = alpha
        self.alphaSpin.SetValue(self._colour.alpha)
        self.DrawAlpha()
        if exitCB:
            self.Bind(wx.EVT_CLOSE, exitCB)

    def SetPanelColours(self):
        self.oldColourPanel.RefreshColour(self._oldColour)
        self.newColourPanel.RefreshColour(self._colour)
        if self.updateCB:
            self.updateCB(self._colour.r, self._colour.g, self._colour.b, self._colour.alpha)

class ObjectPropertyUI(ScrolledPanel):
    def __init__(self, parent, editor):
        self.editor = editor
        self.colorPicker = None
        self.lastColorPickerPos = None
        self.lastPropTab = None
        ScrolledPanel.__init__(self, parent)

        parentSizer = wx.BoxSizer(wx.VERTICAL)
        parentSizer.Add(self, 1, wx.EXPAND, 0)
        parent.SetSizer(parentSizer); parent.Layout()

        self.SetDropTarget(AnimFileDrop(self.editor))

    def clearPropUI(self):
        sizer = self.GetSizer()
        if sizer is not None:
            self.lastPropTab = self.nb.GetCurrentPage().GetName()
            sizer.Remove(self.propPane)
            self.propPane.Destroy()
            self.SetSizer(None)
        self.Layout()
        self.SetupScrolling(self, scroll_y = True, rate_y = 20)

    def colorPickerExitCB(self, evt=None):
        self.lastColorPickerPos = self.colorPicker.GetPosition()
        self.colorPicker.Destroy()
        self.colorPicker = None

    def colorPickerUpdateCB(self, rr, gg, bb, aa):
        r = rr / 255.0
        g = gg / 255.0
        b = bb / 255.0
        a = aa / 255.0
        self.propCR.setValue(r)
        self.propCG.setValue(g)
        self.propCB.setValue(b)
        self.propCA.setValue(a)        

        self.editor.objectMgr.updateObjectColor(r, g, b, a)

    def onColorSlider(self, evt):
        r = float(self.editor.ui.objectPropertyUI.propCR.getValue())
        g = float(self.editor.ui.objectPropertyUI.propCG.getValue())
        b = float(self.editor.ui.objectPropertyUI.propCB.getValue())
        a = float(self.editor.ui.objectPropertyUI.propCA.getValue())

        if self.colorPicker:
            evtObj = evt.GetEventObject()
            if evtObj == self.propCR.ui or\
               evtObj == self.propCR.ui.textValue:
                self.colorPicker.redSpin.SetValue(r * 255)
                self.colorPicker.AssignColourValue('r', r * 255, 255, 0)
            elif evtObj == self.propCG.ui or\
                 evtObj == self.propCG.ui.textValue:
                self.colorPicker.greenSpin.SetValue(g * 255)
                self.colorPicker.AssignColourValue('g', g * 255, 255, 0)
            elif evtObj == self.propCB.ui or\
                 evtObj == self.propCB.ui.textValue:
                self.colorPicker.blueSpin.SetValue(b * 255)
                self.colorPicker.AssignColourValue('b', b * 255, 255, 0)
            else:
                self.colorPicker._colour.alpha = a * 255
                self.colorPicker.alphaSpin.SetValue(self.colorPicker._colour.alpha)
                self.colorPicker.DrawAlpha()

        self.editor.objectMgr.updateObjectColor(r, g, b, a)
        
    def openColorPicker(self, evt, colourData, alpha):
        if self.colorPicker:
            self.lastColorPickerPos = self.colorPicker.GetPosition()
            self.colorPicker.Destroy()

        self.colorPicker = ColorPicker(self, colourData, alpha=alpha, updateCB=self.colorPickerUpdateCB, exitCB=self.colorPickerExitCB)
        self.colorPicker.GetColourData().SetChooseFull(True)
        self.colorPicker.Show()
        if self.lastColorPickerPos:
            self.colorPicker.SetPosition(self.lastColorPickerPos)
        
    def updateProps(self, obj, movable=True):
        self.clearPropUI()
        
        self.propPane = wx.Panel(self)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(self.propPane, 1, wx.EXPAND, 0)
        self.SetSizer(mainSizer)

        self.nb = wx.Notebook(self.propPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.nb, 1, wx.EXPAND)
        self.propPane.SetSizer(sizer)

        self.transformPane = wx.Panel(self.nb, -1, name='Transform')
        self.nb.AddPage(self.transformPane, 'Transform')

        self.propX = ObjectPropUIEntry(self.transformPane, 'X')
        self.propY = ObjectPropUIEntry(self.transformPane, 'Y')
        self.propZ = ObjectPropUIEntry(self.transformPane, 'Z')

        self.propH = ObjectPropUISlider(self.transformPane, 'H', 0, 0, 360)
        self.propP = ObjectPropUISlider(self.transformPane, 'P', 0, 0, 360)
        self.propR = ObjectPropUISlider(self.transformPane, 'R', 0, 0, 360)

        self.propSX = ObjectPropUIEntry(self.transformPane, 'SX')
        self.propSY = ObjectPropUIEntry(self.transformPane, 'SY')
        self.propSZ = ObjectPropUIEntry(self.transformPane, 'SZ')

        transformProps = [self.propX, self.propY, self.propZ, self.propH, self.propP, self.propR,
                       self.propSX, self.propSY, self.propSZ]

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddMany(transformProps)
        self.transformPane.SetSizer(sizer)
        for transformProp in transformProps:
            transformProp.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                                   self.editor.objectMgr.onLeaveObjectPropUI,
                                   self.editor.objectMgr.updateObjectTransform)

        if not movable:
            for transformProp in transformProps:
                transformProp.ui.Disable()

        self.lookPane = wx.Panel(self.nb, -1, name='Look')
        self.nb.AddPage(self.lookPane, 'Look')

        objNP = obj[OG.OBJ_NP]
        objRGBA = obj[OG.OBJ_RGBA]
        self.propCR = ObjectPropUISlider(self.lookPane, 'CR', objRGBA[0], 0, 1)
        self.propCG = ObjectPropUISlider(self.lookPane, 'CG', objRGBA[1], 0, 1)
        self.propCB = ObjectPropUISlider(self.lookPane, 'CB', objRGBA[2], 0, 1)        
        self.propCA = ObjectPropUISlider(self.lookPane, 'CA', objRGBA[3], 0, 1) 
        colorProps = [self.propCR, self.propCG, self.propCB, self.propCA]

        for colorProp in colorProps:
            colorProp.ui.bindFunc(self.onColorSlider)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddMany(colorProps)
        button = wx.Button(self.lookPane, -1, 'Color Picker', (0,0), (140, 20))
        _colourData = wx.ColourData()
        _colourData.SetColour(wx.Colour(objRGBA[0] * 255, objRGBA[1] * 255, objRGBA[2] * 255))
        button.Bind(wx.EVT_BUTTON, lambda p0=None, p1=_colourData, p2=objRGBA[3] * 255: self.openColorPicker(p0, p1, p2))

        sizer.Add(button)

        if self.colorPicker:
            self.openColorPicker(None, _colourData, objRGBA[3] * 255)

        objDef = obj[OG.OBJ_DEF]

        if objDef.updateModelFunction is not None or (objDef.model is not None and len(objDef.models) > 0):
            defaultModel = obj[OG.OBJ_MODEL]
            if defaultModel is None:
                defaultModel = ''

            if len(objDef.models) == 0:
                modelList = ''
            else:
                modelList = objDef.models
            propUI = ObjectPropUICombo(self.lookPane, 'model', defaultModel, modelList, obj, callBack=objDef.updateModelFunction)
            sizer.Add(propUI)            

            propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                            self.editor.objectMgr.onLeaveObjectPropUI,
                            lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModelFromUI(p0, p1))

        animList = objDef.animDict.get(obj[OG.OBJ_MODEL])
        if len(objDef.anims) > 0 or animList:
            if animList is None:
                animList = objDef.anims
                
            propUI = ObjectPropUICombo(self.lookPane, 'anim', obj[OG.OBJ_ANIM], animList)
            sizer.Add(propUI)            

            propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                            self.editor.objectMgr.onLeaveObjectPropUI,
                            lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectAnimFromUI(p0, p1))

        self.lookPane.SetSizer(sizer)

        self.propsPane = wx.Panel(self.nb, -1, name='Properties')
        self.nb.AddPage(self.propsPane, 'Properties')
        sizer = wx.BoxSizer(wx.VERTICAL)

        propNames = objDef.orderedProperties[:]
        for key in objDef.properties.keys():
            if key not in propNames:
                propNames.append(key)

        for key in propNames:
            # handling properties mask
            propMask = BitMask32()
            for modeKey in objDef.propertiesMask.keys():
                if key in objDef.propertiesMask[modeKey]:
                    propMask |= modeKey

            if not propMask.isZero():
                if (self.editor.mode & propMask).isZero():
                    continue

            propDef = objDef.properties[key]
            propType = propDef[OG.PROP_TYPE]
            propDataType = propDef[OG.PROP_DATATYPE]
            value = obj[OG.OBJ_PROP].get(key)

            if propType == OG.PROP_UI_ENTRY:
                propUI = ObjectPropUIEntry(self.propsPane, key)
                propUI.setValue(value)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_SLIDE:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]

                if value is None:
                    continue

                if propDataType != OG.PROP_FLOAT:
                    value = float(value)

                propUI = ObjectPropUISlider(self.propsPane, key, value, propRange[OG.RANGE_MIN], propRange[OG.RANGE_MAX])
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_SPIN:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]

                if value is None:
                    continue

                propUI = ObjectPropUISpinner(self.propsPane, key, value, propRange[OG.RANGE_MIN], propRange[OG.RANGE_MAX])
                sizer.Add(propUI)                

            elif propType == OG.PROP_UI_CHECK:
                if value is None:
                    continue

                propUI = ObjectPropUICheck(self.propsPane, key, value)
                sizer.Add(propUI)                  

            elif propType == OG.PROP_UI_RADIO:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]
                
                if value is None:
                    continue

                if propDataType != OG.PROP_STR:
                    for i in range(len(propRange)):
                        propRange[i] = str(propRange[i])

                    value = str(value)

                propUI = ObjectPropUIRadio(self.propsPane, key, value, propRange)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_COMBO:
                if len(propDef) <= OG.PROP_RANGE:
                    continue
                propRange = propDef[OG.PROP_RANGE]                

                if value is None:
                    continue

                if propDataType != OG.PROP_STR:
                    for i in range(len(propRange)):
                        propRange[i] = str(propRange[i])

                    value = str(value)

                propUI = ObjectPropUICombo(self.propsPane, key, value, propRange)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_COMBO_DYNAMIC:
                if len(propDef) <= OG.PROP_DYNAMIC_KEY:
                    continue
                
                propDynamicKey = propDef[OG.PROP_DYNAMIC_KEY]
                if propDynamicKey == OG.PROP_MODEL:
                    dynamicRangeKey = obj[OG.OBJ_MODEL]
                else:
                    dynamicRangeKey = obj[OG.OBJ_PROP].get(propDynamicKey)

                if dynamicRangeKey is None:
                    self.editor.objectMgr.updateObjectPropValue(obj, key, propDef[OG.PROP_DEFAULT], fUndo=False)
                    continue

                propRange = propDef[OG.PROP_RANGE].get(dynamicRangeKey)

                if propRange is None:
                    self.editor.objectMgr.updateObjectPropValue(obj, key, propDef[OG.PROP_DEFAULT], fUndo=False)
                    continue

                if value is None:
                    continue

                if propDataType != OG.PROP_STR:
                    for i in range(len(propRange)):
                        propRange[i] = str(propRange[i])

                    value = str(value)

                if value not in propRange:
                    value = propRange[0]
                    self.editor.objectMgr.updateObjectPropValue(obj, key, value, fUndo=False)
                    
                propUI = ObjectPropUICombo(self.propsPane, key, value, propRange)
                sizer.Add(propUI)

            elif propType == OG.PROP_UI_TIME:

                if value is None:
                    continue

                propUI = ObjectPropUITime(self.propsPane, key, value)
                sizer.Add(propUI)
                
            else:
                # unspported property type
                continue

            propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
                            self.editor.objectMgr.onLeaveObjectPropUI,
                            lambda p0=None, p1=obj, p2=key: self.editor.objectMgr.updateObjectProperty(p0, p1, p2))


        self.propsPane.SetSizer(sizer);
        self.Layout()
        self.SetupScrolling(self, scroll_y = True, rate_y = 20)
        if self.lastPropTab == 'Transform':
            self.nb.SetSelection(0)
        elif self.lastPropTab == 'Look':
            self.nb.SetSelection(1)
        elif self.lastPropTab == 'Properties':
            self.nb.SetSelection(2)
UI for object property control #---------------------------------------------------------------------- #---------------------------------------------------------------------- Base class for ObjectPropUIs,     It consists of label area and ui area. #sort keyFrameInfo list by the order of frame number UI for string value properties UI for float value properties UI for int value properties # handling properties mask # unspported property type