Browse Source

integrated selected obj parenting option

Arkady Trestman 15 years ago
parent
commit
129f164f16

+ 359 - 349
direct/src/leveleditor/LevelEditorBase.py

@@ -1,349 +1,359 @@
-"""
-Base class for Level Editor
-
-You should write your own LevelEditor class inheriting this.
-Refer LevelEditor.py for example.
-"""
-
-from direct.showbase.DirectObject import *
-from direct.directtools.DirectUtil import *
-from direct.gui.DirectGui import *
-
-from FileMgr import *
-from ActionMgr import *
-from MayaConverter import *
-
-class LevelEditorBase(DirectObject):
-    """ Base Class for Panda3D LevelEditor """ 
-    def __init__(self):
-        #loadPrcFileData('startup', 'window-type none')
-        self.currentFile = None
-        self.fNeedToSave = False
-        self.actionEvents = []
-        #self.objectMgr = ObjectMgr(self)
-        self.fileMgr = FileMgr(self)
-        self.actionMgr = ActionMgr()
-
-        self.NPParent = render
-
-        # define your own config file in inherited class
-        self.settingsFile = None
-
-        # you can show/hide specific properties by using propertiesMask and this mode
-        self.mode = BitMask32()
-        
-    def initialize(self):
-        """ You should call this in your __init__ method of inherited LevelEditor class """
-        # specifiy what obj can be 'selected' as objects
-        base.direct.selected.addTag('OBJRoot')
-
-        self.actionEvents.extend([
-            # Node path events
-            ('DIRECT-select', self.select),
-            ('DIRECT-delete', self.handleDelete),
-            ('DIRECT-preDeselectAll', self.deselectAll),
-            ('DIRECT_deselectAll', self.deselectAllCB),
-            ('preRemoveNodePath', self.removeNodePathHook),
-            ('DIRECT_deselectedNodePath', self.deselectAllCB),
-            ('DIRECT_selectedNodePath_fMulti_fTag_fLEPane', self.selectedNodePathHook),
-            ('DIRECT_deselectAll', self.deselectAll),
-            ('LE-Undo', self.actionMgr.undo),
-            ('LE-Redo', self.actionMgr.redo),
-            ('LE-Duplicate', self.objectMgr.duplicateSelected),
-            ('DIRECT_manipulateObjectCleanup', self.cleanUpManipulating),
-            ('LE-MakeLive', self.objectMgr.makeSelectedLive),
-            ('LE-NewScene', self.ui.onNew),
-            ('LE-SaveScene', self.ui.onSave),
-            ('LE-OpenScene', self.ui.onOpen),
-            ('LE-Quit', self.ui.quit),
-            ('DIRECT-mouse3', self.handleMouse3),
-            ('DIRECT-toggleWidgetVis', self.toggleWidget),
-            ])
-
-        # Add all the action events
-        for event in self.actionEvents:
-            if len(event) == 3:
-                self.accept(event[0], event[1], event[2])
-            else:
-                self.accept(event[0], event[1])        
-
-        # editor state text display such as edit mode
-        self.statusReadout = OnscreenText(
-            pos = (-1.2, 0.9), bg=Vec4(1,1,1,1),
-            scale = 0.05, align = TextNode.ALeft,
-            mayChange = 1, font = TextNode.getDefaultFont())
-        self.statusReadout.setText("")
-        # Make sure readout is never lit or drawn in wireframe
-        useDirectRenderStyle(self.statusReadout)
-        self.statusReadout.reparentTo(hidden)
-        self.statusLines = []
-        taskMgr.doMethodLater(5, self.updateStatusReadoutTimeouts, 'updateStatus')
-
-        self.loadSettings()
-        self.reset()
-        
-    def setTitleWithFilename(self, filename=""):
-        title = self.ui.appname
-        if filename != "":
-           filenameshort = os.path.basename(filename)
-           title = title + " (%s)"%filenameshort
-        self.ui.SetLabel(title)
-
-    def removeNodePathHook(self, nodePath):
-        if nodePath is None:
-            return
-        base.direct.deselect(nodePath)
-        self.objectMgr.removeObjectByNodePath(nodePath)
-
-        if (base.direct.selected.last != None and nodePath.compareTo(base.direct.selected.last)==0):
-            # if base.direct.selected.last is refering to this
-            # removed obj, clear the reference
-            if (hasattr(__builtins__,'last')):
-                __builtins__.last = None
-            else:
-                __builtins__['last'] = None
-            base.direct.selected.last = None
-
-    def toggleWidget(self):
-        if self.objectMgr.currNodePath:
-            obj = self.objectMgr.findObjectByNodePath(self.objectMgr.currNodePath)
-            if obj and not obj[OG.OBJ_DEF].movable:
-                return
-        base.direct.toggleWidgetVis()
-
-    def handleMouse3(self, modifiers):
-        if base.direct.fAlt or modifiers == 4:
-            return
-
-        self.ui.onRightDown()
-
-    def handleDelete(self):
-        oldSelectedNPs = base.direct.selected.getSelectedAsList()
-        oldUIDs = []
-        for oldNP in oldSelectedNPs:
-            obj = self.objectMgr.findObjectByNodePath(oldNP)
-            if obj:
-                oldUIDs.append(obj[OG.OBJ_UID])
-
-        action = ActionDeleteObj(self)
-        self.actionMgr.push(action)
-        action()
-
-        for uid in oldUIDs:
-            self.ui.sceneGraphUI.delete(uid)
-
-##         reply = wx.MessageBox("Do you want to delete selected?", "Delete?",
-##                               wx.YES_NO | wx.ICON_QUESTION)
-##         if reply == wx.YES:
-##             base.direct.removeAllSelected()
-##         else:
-##             # need to reset COA
-##             dnp = base.direct.selected.last
-##             # Update camera controls coa to this point
-##             # Coa2Camera = Coa2Dnp * Dnp2Camera
-##             mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(base.direct.camera)
-##             row = mCoa2Camera.getRow(3)
-##             coa = Vec3(row[0], row[1], row[2])
-##             base.direct.cameraControl.updateCoa(coa)
-
-    def cleanUpManipulating(self, selectedNPs):
-        for np in selectedNPs:
-            obj = self.objectMgr.findObjectByNodePath(np)
-            if obj:
-                action = ActionTransformObj(self, obj[OG.OBJ_UID], Mat4(np.getMat()))
-                self.actionMgr.push(action)
-                action()                
-
-    def select(self, nodePath, fMultiSelect=0, fSelectTag=1, fResetAncestry=1, fLEPane=0, fUndo=1):
-        if fUndo:
-            # Select tagged object if present
-            if fSelectTag:
-                for tag in base.direct.selected.tagList:
-                    if nodePath.hasNetTag(tag):
-                        nodePath = nodePath.findNetTag(tag)
-                        break
-            action = ActionSelectObj(self, nodePath, fMultiSelect)
-            self.actionMgr.push(action)
-            action()
-        else:
-            base.direct.selectCB(nodePath, fMultiSelect, fSelectTag, fResetAncestry, fLEPane, fUndo)
-
-    def selectedNodePathHook(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fLEPane = 0):
-        # handle unpickable nodepath
-        if nodePath.getName() in base.direct.iRay.unpickable:
-            base.direct.deselect(nodePath)
-            return
-
-        if fMultiSelect == 0 and fLEPane == 0:
-           oldSelectedNPs = base.direct.selected.getSelectedAsList()
-           for oldNP in oldSelectedNPs:
-              obj = self.objectMgr.findObjectByNodePath(oldNP)
-              if obj:
-                 self.ui.sceneGraphUI.deSelect(obj[OG.OBJ_UID])
-        self.objectMgr.selectObject(nodePath, fLEPane)
-        self.ui.buildContextMenu(nodePath)
-        
-    def deselectAll(self, np=None):
-        if len(base.direct.selected.getSelectedAsList()) ==0:
-            return
-        action = ActionDeselectAll(self)
-        self.actionMgr.push(action)
-        action()
-
-    def deselectAllCB(self, dnp=None):
-        self.objectMgr.deselectAll()
-
-    def reset(self):
-        if self.fNeedToSave:
-            reply = wx.MessageBox("Do you want to save current scene?", "Save?",
-                               wx.YES_NO | wx.ICON_QUESTION)
-            if reply == wx.YES:
-                result = self.ui.onSave()
-                if result == False:
-                    return
-
-        base.direct.deselectAll()
-        self.ui.reset()
-        self.objectMgr.reset()
-        self.actionMgr.reset()
-        self.ui.perspView.camera.setPos(-19, -19, 19)
-        self.ui.perspView.camera.lookAt(Point3(0, 0, 0))
-        self.ui.leftView.camera.setPos(600, 0, 0)
-        self.ui.frontView.camera.setPos(0, -600, 0)
-        self.ui.topView.camera.setPos(0, 0, 600)
-        self.resetOrthoCam(self.ui.topView)
-        self.resetOrthoCam(self.ui.frontView)
-        self.resetOrthoCam(self.ui.leftView)
-        self.fNeedToSave = False
-        self.setTitleWithFilename()
-        
-    def resetOrthoCam(self, view):
-        base.direct.drList[base.camList.index(NodePath(view.camNode))].orthoFactor = 0.1
-        x = view.ClientSize.GetWidth() * 0.1
-        y = view.ClientSize.GetHeight() * 0.1
-        view.camLens.setFilmSize(x, y)
-        
-    def save(self):
-        if self.currentFile:
-            self.fileMgr.saveToFile(self.currentFile)
-
-    def saveAs(self, fileName):
-        self.fileMgr.saveToFile(fileName)
-        self.currentFile = fileName
-        
-    def load(self, fileName):
-        self.reset()
-        self.fileMgr.loadFromFile(fileName)
-        self.currentFile = fileName
-
-    def saveSettings(self):
-        if self.settingsFile is None:
-            return
-        
-        try:
-            f = open(self.settingsFile, 'w')
-            f.write('gridSize\n%f\n'%self.ui.perspView.grid.gridSize)
-            f.write('gridSpacing\n%f\n'%self.ui.perspView.grid.gridSpacing)
-            f.write('hotKey\n%s\n'%base.direct.hotKeyMap)
-            f.close()
-        except:
-            pass        
-
-    def loadSettings(self):
-        if self.settingsFile is None:
-            return
-        
-        try:
-            f = open(self.settingsFile, 'r')
-            configLines = f.readlines()
-            f.close()
-
-            gridSize = 100.0
-            gridSpacing = 5.0
-            for i in range(0, len(configLines)):
-                line = configLines[i]
-                i = i + 1
-                if line.startswith('gridSize'):
-                    gridSize = float(configLines[i])
-                elif line.startswith('gridSpacing'):
-                    gridSpacing = float(configLines[i])
-                elif line.startswith('hotKey'):
-                    customHotKeyMap = eval(configLines[i])
-                    customHotKeyDict = {}
-                    for hotKey in customHotKeyMap.keys():
-                        desc = customHotKeyMap[hotKey]
-                        customHotKeyDict[desc[1]] = hotKey
-
-                    overriddenKeys = []
-                    for key in base.direct.hotKeyMap.keys():
-                        desc = base.direct.hotKeyMap[key]
-                        if desc[1] in customHotKeyDict.keys():
-                            overriddenKeys.append(key)
-
-                    for key in overriddenKeys:
-                        del base.direct.hotKeyMap[key]
-                            
-                    base.direct.hotKeyMap.update(customHotKeyMap)
-
-            self.ui.updateGrids(gridSize, gridSpacing)
-            self.ui.updateMenu()
-        except:
-            pass
-
-    def convertMaya(self, modelname, callBack, obj=None, isAnim=False):
-        if obj and isAnim:
-            mayaConverter = MayaConverter(self.ui, self, modelname, callBack, obj, isAnim)
-        else:
-            reply = wx.MessageBox("Is it an animation file?", "Animation?",
-                              wx.YES_NO | wx.ICON_QUESTION)
-            if reply == wx.YES:
-                mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, True)
-            else:        
-                mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False)
-        mayaConverter.Show()
-
-    def convertFromMaya(self, modelname, callBack):
-        mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False)
-        mayaConverter.Show()
-
-    def updateStatusReadout(self, status, color=None):
-        if status:
-            # add new status line, first check to see if it already exists
-            alreadyExists = False
-            for currLine in self.statusLines:
-                if (status == currLine[1]):
-                    alreadyExists = True
-                    break
-            if (alreadyExists == False):
-                time = globalClock.getRealTime() + 15
-                self.statusLines.append([time,status,color])
-
-        # update display of new status lines
-        self.statusReadout.reparentTo(aspect2d)
-        statusText = ""
-        lastColor = None
-        for currLine in self.statusLines:
-            statusText += currLine[1] + '\n'
-            lastColor = currLine[2]
-        self.statusReadout.setText(statusText)
-        if (lastColor):
-            self.statusReadout.textNode.setCardColor(
-                lastColor[0], lastColor[1], lastColor[2], lastColor[3])
-            self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
-        else:
-            self.statusReadout.textNode.setCardColor(1,1,1,1)
-            self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
-            
-    def updateStatusReadoutTimeouts(self,task=None):
-        removalList = []
-        for currLine in self.statusLines:
-            if (globalClock.getRealTime() >= currLine[0]):
-                removalList.append(currLine)
-        for currRemoval in removalList:
-            self.statusLines.remove(currRemoval)
-        self.updateStatusReadout(None)
-        # perform doMethodLater again after delay
-        # This crashes when CTRL-C'ing, so this is a cheap hack.
-        #return 2
-        from direct.task import Task
-        return Task.again
+"""
+Base class for Level Editor
+
+You should write your own LevelEditor class inheriting this.
+Refer LevelEditor.py for example.
+"""
+
+from direct.showbase.DirectObject import *
+from direct.directtools.DirectUtil import *
+from direct.gui.DirectGui import *
+
+from FileMgr import *
+from ActionMgr import *
+from MayaConverter import *
+
+class LevelEditorBase(DirectObject):
+    """ Base Class for Panda3D LevelEditor """ 
+    def __init__(self):
+        #loadPrcFileData('startup', 'window-type none')
+        self.currentFile = None
+        self.fNeedToSave = False
+        self.actionEvents = []
+        #self.objectMgr = ObjectMgr(self)
+        self.fileMgr = FileMgr(self)
+        self.actionMgr = ActionMgr()
+
+        self.NPParent = render
+
+        # define your own config file in inherited class
+        self.settingsFile = None
+
+        # you can show/hide specific properties by using propertiesMask and this mode
+        self.mode = BitMask32()
+        
+    def initialize(self):
+        """ You should call this in your __init__ method of inherited LevelEditor class """
+        # specifiy what obj can be 'selected' as objects
+        base.direct.selected.addTag('OBJRoot')
+
+        self.actionEvents.extend([
+            # Node path events
+            ('DIRECT-select', self.select),
+            ('DIRECT-delete', self.handleDelete),
+            ('DIRECT-preDeselectAll', self.deselectAll),
+            ('DIRECT_deselectAll', self.deselectAllCB),
+            ('preRemoveNodePath', self.removeNodePathHook),
+            ('DIRECT_deselectedNodePath', self.deselectAllCB),
+            ('DIRECT_selectedNodePath_fMulti_fTag_fLEPane', self.selectedNodePathHook),
+            ('DIRECT_deselectAll', self.deselectAll),
+            ('LE-Undo', self.actionMgr.undo),
+            ('LE-Redo', self.actionMgr.redo),
+            ('LE-Duplicate', self.objectMgr.duplicateSelected),
+            ('DIRECT_manipulateObjectCleanup', self.cleanUpManipulating),
+            ('LE-MakeLive', self.objectMgr.makeSelectedLive),
+            ('LE-NewScene', self.ui.onNew),
+            ('LE-SaveScene', self.ui.onSave),
+            ('LE-OpenScene', self.ui.onOpen),
+            ('LE-Quit', self.ui.quit),
+            ('DIRECT-mouse3', self.handleMouse3),
+            ('DIRECT-toggleWidgetVis', self.toggleWidget),
+            ])
+
+        # Add all the action events
+        for event in self.actionEvents:
+            if len(event) == 3:
+                self.accept(event[0], event[1], event[2])
+            else:
+                self.accept(event[0], event[1])        
+
+        # editor state text display such as edit mode
+        self.statusReadout = OnscreenText(
+            pos = (-1.2, 0.9), bg=Vec4(1,1,1,1),
+            scale = 0.05, align = TextNode.ALeft,
+            mayChange = 1, font = TextNode.getDefaultFont())
+        self.statusReadout.setText("")
+        # Make sure readout is never lit or drawn in wireframe
+        useDirectRenderStyle(self.statusReadout)
+        self.statusReadout.reparentTo(hidden)
+        self.statusLines = []
+        taskMgr.doMethodLater(5, self.updateStatusReadoutTimeouts, 'updateStatus')
+
+        self.loadSettings()
+        self.reset()
+        
+    def setTitleWithFilename(self, filename=""):
+        title = self.ui.appname
+        if filename != "":
+           filenameshort = os.path.basename(filename)
+           title = title + " (%s)"%filenameshort
+        self.ui.SetLabel(title)
+
+    def removeNodePathHook(self, nodePath):
+        if nodePath is None:
+            return
+        base.direct.deselect(nodePath)
+        self.objectMgr.removeObjectByNodePath(nodePath)
+
+        if (base.direct.selected.last != None and nodePath.compareTo(base.direct.selected.last)==0):
+            # if base.direct.selected.last is refering to this
+            # removed obj, clear the reference
+            if (hasattr(__builtins__,'last')):
+                __builtins__.last = None
+            else:
+                __builtins__['last'] = None
+            base.direct.selected.last = None
+
+    def toggleWidget(self):
+        if self.objectMgr.currNodePath:
+            obj = self.objectMgr.findObjectByNodePath(self.objectMgr.currNodePath)
+            if obj and not obj[OG.OBJ_DEF].movable:
+                return
+        base.direct.toggleWidgetVis()
+
+    def handleMouse3(self, modifiers):
+        if base.direct.fAlt or modifiers == 4:
+            return
+
+        self.ui.onRightDown()
+
+    def handleDelete(self):
+        oldSelectedNPs = base.direct.selected.getSelectedAsList()
+        oldUIDs = []
+        for oldNP in oldSelectedNPs:
+            obj = self.objectMgr.findObjectByNodePath(oldNP)
+            if obj:
+                oldUIDs.append(obj[OG.OBJ_UID])
+
+        action = ActionDeleteObj(self)
+        self.actionMgr.push(action)
+        action()
+
+        for uid in oldUIDs:
+            self.ui.sceneGraphUI.delete(uid)
+
+##         reply = wx.MessageBox("Do you want to delete selected?", "Delete?",
+##                               wx.YES_NO | wx.ICON_QUESTION)
+##         if reply == wx.YES:
+##             base.direct.removeAllSelected()
+##         else:
+##             # need to reset COA
+##             dnp = base.direct.selected.last
+##             # Update camera controls coa to this point
+##             # Coa2Camera = Coa2Dnp * Dnp2Camera
+##             mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(base.direct.camera)
+##             row = mCoa2Camera.getRow(3)
+##             coa = Vec3(row[0], row[1], row[2])
+##             base.direct.cameraControl.updateCoa(coa)
+
+    def cleanUpManipulating(self, selectedNPs):
+        for np in selectedNPs:
+            obj = self.objectMgr.findObjectByNodePath(np)
+            if obj:
+                action = ActionTransformObj(self, obj[OG.OBJ_UID], Mat4(np.getMat()))
+                self.actionMgr.push(action)
+                action()                
+
+    def select(self, nodePath, fMultiSelect=0, fSelectTag=1, fResetAncestry=1, fLEPane=0, fUndo=1):
+        if fUndo:
+            # Select tagged object if present
+            if fSelectTag:
+                for tag in base.direct.selected.tagList:
+                    if nodePath.hasNetTag(tag):
+                        nodePath = nodePath.findNetTag(tag)
+                        break
+            action = ActionSelectObj(self, nodePath, fMultiSelect)
+            self.actionMgr.push(action)
+            action()
+        else:
+            base.direct.selectCB(nodePath, fMultiSelect, fSelectTag, fResetAncestry, fLEPane, fUndo)
+
+    def selectedNodePathHook(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fLEPane = 0):
+        # handle unpickable nodepath
+        if nodePath.getName() in base.direct.iRay.unpickable:
+            base.direct.deselect(nodePath)
+            return
+
+        if fMultiSelect == 0 and fLEPane == 0:
+           oldSelectedNPs = base.direct.selected.getSelectedAsList()
+           for oldNP in oldSelectedNPs:
+              obj = self.objectMgr.findObjectByNodePath(oldNP)
+              if obj:
+                 self.ui.sceneGraphUI.deSelect(obj[OG.OBJ_UID])
+        self.objectMgr.selectObject(nodePath, fLEPane)
+        self.ui.buildContextMenu(nodePath)
+        
+    def deselectAll(self, np=None):
+        if len(base.direct.selected.getSelectedAsList()) ==0:
+            return
+        action = ActionDeselectAll(self)
+        self.actionMgr.push(action)
+        action()
+
+    def deselectAllCB(self, dnp=None):
+        self.objectMgr.deselectAll()
+
+    def reset(self):
+        if self.fNeedToSave:
+            reply = wx.MessageBox("Do you want to save current scene?", "Save?",
+                               wx.YES_NO | wx.ICON_QUESTION)
+            if reply == wx.YES:
+                result = self.ui.onSave()
+                if result == False:
+                    return
+
+        base.direct.deselectAll()
+        self.ui.reset()
+        self.objectMgr.reset()
+        self.actionMgr.reset()
+        self.ui.perspView.camera.setPos(-19, -19, 19)
+        self.ui.perspView.camera.lookAt(Point3(0, 0, 0))
+        self.ui.leftView.camera.setPos(600, 0, 0)
+        self.ui.frontView.camera.setPos(0, -600, 0)
+        self.ui.topView.camera.setPos(0, 0, 600)
+        self.resetOrthoCam(self.ui.topView)
+        self.resetOrthoCam(self.ui.frontView)
+        self.resetOrthoCam(self.ui.leftView)
+        self.fNeedToSave = False
+        self.setTitleWithFilename()
+        
+    def resetOrthoCam(self, view):
+        base.direct.drList[base.camList.index(NodePath(view.camNode))].orthoFactor = 0.1
+        x = view.ClientSize.GetWidth() * 0.1
+        y = view.ClientSize.GetHeight() * 0.1
+        view.camLens.setFilmSize(x, y)
+        
+    def save(self):
+        if self.currentFile:
+            self.fileMgr.saveToFile(self.currentFile)
+
+    def saveAs(self, fileName):
+        self.fileMgr.saveToFile(fileName)
+        self.currentFile = fileName
+        
+    def load(self, fileName):
+        self.reset()
+        self.fileMgr.loadFromFile(fileName)
+        self.currentFile = fileName
+
+    def saveSettings(self):
+        if self.settingsFile is None:
+            return
+        
+        try:
+            f = open(self.settingsFile, 'w')
+            f.write('gridSize\n%f\n'%self.ui.perspView.grid.gridSize)
+            f.write('gridSpacing\n%f\n'%self.ui.perspView.grid.gridSpacing)
+            f.write('hotKey\n%s\n'%base.direct.hotKeyMap)
+            f.close()
+        except:
+            pass        
+
+    def loadSettings(self):
+        if self.settingsFile is None:
+            return
+        
+        try:
+            f = open(self.settingsFile, 'r')
+            configLines = f.readlines()
+            f.close()
+
+            gridSize = 100.0
+            gridSpacing = 5.0
+            for i in range(0, len(configLines)):
+                line = configLines[i]
+                i = i + 1
+                if line.startswith('gridSize'):
+                    gridSize = float(configLines[i])
+                elif line.startswith('gridSpacing'):
+                    gridSpacing = float(configLines[i])
+                elif line.startswith('hotKey'):
+                    customHotKeyMap = eval(configLines[i])
+                    customHotKeyDict = {}
+                    for hotKey in customHotKeyMap.keys():
+                        desc = customHotKeyMap[hotKey]
+                        customHotKeyDict[desc[1]] = hotKey
+
+                    overriddenKeys = []
+                    for key in base.direct.hotKeyMap.keys():
+                        desc = base.direct.hotKeyMap[key]
+                        if desc[1] in customHotKeyDict.keys():
+                            overriddenKeys.append(key)
+
+                    for key in overriddenKeys:
+                        del base.direct.hotKeyMap[key]
+                            
+                    base.direct.hotKeyMap.update(customHotKeyMap)
+
+            self.ui.updateGrids(gridSize, gridSpacing)
+            self.ui.updateMenu()
+        except:
+            pass
+
+    def convertMaya(self, modelname, callBack, obj=None, isAnim=False):
+        if obj and isAnim:
+            mayaConverter = MayaConverter(self.ui, self, modelname, callBack, obj, isAnim)
+        else:
+            reply = wx.MessageBox("Is it an animation file?", "Animation?",
+                              wx.YES_NO | wx.ICON_QUESTION)
+            if reply == wx.YES:
+                mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, True)
+            else:        
+                mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False)
+        mayaConverter.Show()
+
+    def convertFromMaya(self, modelname, callBack):
+        mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False)
+        mayaConverter.Show()
+
+    def updateStatusReadout(self, status, color=None):
+        if status:
+            # add new status line, first check to see if it already exists
+            alreadyExists = False
+            for currLine in self.statusLines:
+                if (status == currLine[1]):
+                    alreadyExists = True
+                    break
+            if (alreadyExists == False):
+                time = globalClock.getRealTime() + 15
+                self.statusLines.append([time,status,color])
+
+        # update display of new status lines
+        self.statusReadout.reparentTo(aspect2d)
+        statusText = ""
+        lastColor = None
+        for currLine in self.statusLines:
+            statusText += currLine[1] + '\n'
+            lastColor = currLine[2]
+        self.statusReadout.setText(statusText)
+        if (lastColor):
+            self.statusReadout.textNode.setCardColor(
+                lastColor[0], lastColor[1], lastColor[2], lastColor[3])
+            self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
+        else:
+            self.statusReadout.textNode.setCardColor(1,1,1,1)
+            self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
+            
+    def updateStatusReadoutTimeouts(self,task=None):
+        removalList = []
+        for currLine in self.statusLines:
+            if (globalClock.getRealTime() >= currLine[0]):
+                removalList.append(currLine)
+        for currRemoval in removalList:
+            self.statusLines.remove(currRemoval)
+        self.updateStatusReadout(None)
+        # perform doMethodLater again after delay
+        # This crashes when CTRL-C'ing, so this is a cheap hack.
+        #return 2
+        from direct.task import Task
+        return Task.again
+
+    def propMeetsReq(self, typeName, parentNP):
+        if self.ui.parentToSelectedMenuItem.IsChecked():
+           if base.direct.selected.last:
+              parent = base.le.objectMgr.findObjectByNodePath(base.direct.selected.last)
+              if parent:
+                 parentNP[0] = parent[OG.OBJ_NP]
+        else:
+           parentNP[0] = None
+        return True

+ 8 - 1
direct/src/leveleditor/LevelEditorUIBase.py

@@ -23,7 +23,10 @@ class PandaTextDropTarget(wx.TextDropTarget):
 
     def OnDropText(self, x, y, text):
         # create new object
-        action = ActionAddNewObj(self.editor, text)
+        parentNPRef = [None]
+        if not self.editor.propMeetsReq(text, parentNPRef):
+            return
+        action = ActionAddNewObj(self.editor, text, parent=parentNPRef[0])
         self.editor.actionMgr.push(action)
         newobj = action()
         if newobj is None:
@@ -112,6 +115,7 @@ ID_GRID_SIZE = 302
 ID_GRID_SNAP = 303
 ID_SHOW_PANDA_OBJECT = 304
 ID_HOT_KEYS = 305
+ID_PARENT_TO_SELECTED = 306
 
 class LevelEditorUIBase(WxPandaShell):
     """ Class for Panda3D LevelEditor """ 
@@ -131,6 +135,7 @@ class LevelEditorUIBase(WxPandaShell):
             ID_GRID_SNAP : ("Grid S&nap", None),
             ID_SHOW_PANDA_OBJECT : ("Show &Panda Objects", None),
             ID_HOT_KEYS : ("&Hot Keys", None),
+            ID_PARENT_TO_SELECTED : ("&Parent To Selected", None)
             })
 
         self.editor = editor
@@ -191,6 +196,8 @@ class LevelEditorUIBase(WxPandaShell):
         self.showPandaObjectsMenuItem = self.menuOptions.Append(ID_SHOW_PANDA_OBJECT, self.MENU_TEXTS[ID_SHOW_PANDA_OBJECT][0], kind = wx.ITEM_CHECK)
         self.Bind(wx.EVT_MENU, self.onShowPandaObjects, self.showPandaObjectsMenuItem)
 
+        self.parentToSelectedMenuItem = self.menuOptions.Append(ID_PARENT_TO_SELECTED, self.MENU_TEXTS[ID_PARENT_TO_SELECTED][0], kind = wx.ITEM_CHECK)
+
         self.hotKeysMenuItem = self.menuOptions.Append(ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0])
         self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem)
 

+ 3 - 1
direct/src/leveleditor/ObjectMgrBase.py

@@ -180,7 +180,7 @@ class ObjectMgrBase:
             if self.editor:
                 if fSelectObject:
                     self.editor.select(newobj, fUndo=0)
-                self.editor.ui.sceneGraphUI.add(newobj)
+                self.editor.ui.sceneGraphUI.add(newobj, parent)
                 self.editor.fNeedToSave = True
         return newobj
 
@@ -771,3 +771,5 @@ class ObjectMgrBase:
     def flatten(self, newobjModel, objDef, uid):
         # override this to flatten models
         pass
+
+

+ 4 - 4
direct/src/leveleditor/SceneGraphUIBase.py

@@ -112,7 +112,7 @@ class SceneGraphUIBase(wx.Panel):
            item, cookie = self.tree.GetNextChild(parent, cookie)
            self.removePandaObjectChildren(itemToRemove)
 
-    def add(self, item):
+    def add(self, item, parentNP = None):
         #import pdb;pdb.set_trace()
         if item is None:
            return
@@ -120,10 +120,10 @@ class SceneGraphUIBase(wx.Panel):
         if obj is None:
            return
 
-        parentNodePath = obj[OG.OBJ_NP].getParent()
-        parentObj = self.editor.objectMgr.findObjectByNodePath(parentNodePath)
+        if parentNP is None :
+           parentNP = obj[OG.OBJ_NP].getParent()
+        parentObj = self.editor.objectMgr.findObjectByNodePath(parentNP)
 
-        #import pdb;pdb.set_trace()
         if parentObj is None:
             parent = self.root
         else: