Browse Source

*** empty log message ***

Mark Mine 24 years ago
parent
commit
ff9b9bcf4c

+ 6 - 3
direct/src/directtools/DirectCameraControl.py

@@ -21,8 +21,8 @@ class DirectCameraControl(PandaObject):
 	self.camManipRef = direct.group.attachNewNode('camManipRef')
         t = CAM_MOVE_DURATION
         self.actionEvents = [
-            ['handleMouse2', self.mouseFlyStart],
-            ['handleMouse2Up', self.mouseFlyStop],
+            ['DIRECT_mouse2', self.mouseFlyStart],
+            ['DIRECT_mouse2Up', self.mouseFlyStop],
             ['c', self.centerCamIn, 0.5],
             ['f', self.fitOnWidget],
             ['h', self.homeCam],
@@ -259,7 +259,10 @@ class DirectCameraControl(PandaObject):
         angle = getCrankAngle(state.coaCenter)
         deltaAngle = angle - state.lastAngle
         state.lastAngle = angle
-        self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle)
+        if base.config.GetBool('temp-hpr-fix',0):
+            self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
+        else:
+            self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle)
         direct.camera.setMat(self.camManipRef, wrtMat)
         return Task.cont
 

+ 3 - 1
direct/src/directtools/DirectLights.py

@@ -89,7 +89,7 @@ class DirectLights(NodePath):
         # Turn it on as a default
         self.setOn(directLight)
         # Send an event to all watching objects
-        messenger.send('DirectLights_addLight', [directLight])
+        messenger.send('DIRECT_addLight', [directLight])
         # Return the new light
         return directLight
 
@@ -101,6 +101,8 @@ class DirectLights(NodePath):
         """ Turn on all DIRECT lights """
         base.initialState.setAttribute(LightTransition.getClassType(),
                                        self.la)
+        # Make sure there is a default material
+        render.setMaterial(Material())
 
     def allOff(self):
         """ Turn off all DIRECT lights """

+ 13 - 7
direct/src/directtools/DirectManipulation.py

@@ -24,8 +24,8 @@ class DirectManipulationControl(PandaObject):
         self.fScaling = 0
         self.mode = None
         self.actionEvents = [
-            ['handleMouse1', self.manipulationStart],
-            ['handleMouse1Up', self.manipulationStop],
+            ['DIRECT_mouse1', self.manipulationStart],
+            ['DIRECT_mouse1Up', self.manipulationStop],
             ['tab', self.toggleObjectHandlesMode],
             ['.', self.objectHandles.multiplyScalingFactorBy, 2.0],
             ['>', self.objectHandles.multiplyScalingFactorBy, 2.0],
@@ -111,7 +111,7 @@ class DirectManipulationControl(PandaObject):
         self.objectHandles.hideGuides()
         # Restart followSelectedNodePath task
         self.spawnFollowSelectedNodePathTask()
-        messenger.send('manipulateObjectCleanup')
+        messenger.send('DIRECT_manipulateObjectCleanup')
 
     def spawnFollowSelectedNodePathTask(self):
         # If nothing selected, just return
@@ -177,7 +177,7 @@ class DirectManipulationControl(PandaObject):
             # hide the bbox of the selected objects during interaction
             direct.selected.dehighlightAll()
             # Send event to signal start of manipulation
-            messenger.send('manipulateObjectStart')
+            messenger.send('DIRECT_manipulateObjectStart')
             # Manipulate the real object with the constraint
             # The constraint is passed as the name of the node 
             self.spawnManipulateObjectTask()
@@ -304,7 +304,10 @@ class DirectManipulationControl(PandaObject):
         elif self.rotateAxis == 'y':
             direct.widget.setR(direct.widget, -deltaAngle)
         elif self.rotateAxis == 'z':
-            direct.widget.setH(direct.widget, deltaAngle)
+            if base.config.GetBool('temp-hpr-fix',0):
+                direct.widget.setH(direct.widget, -deltaAngle)
+            else:
+                direct.widget.setH(direct.widget, deltaAngle)
         # Record crank angle for next time around
         self.lastCrankAngle = newAngle
 
@@ -416,7 +419,10 @@ class DirectManipulationControl(PandaObject):
         deltaAngle = angle - state.lastAngle
         state.lastAngle = angle
         # Mouse motion edge to edge of display region results in one full turn
-        relHpr(direct.widget, direct.camera, 0, 0, deltaAngle)
+        if base.config.GetBool('temp-hpr-fix',0):
+            relHpr(direct.widget, direct.camera, 0, 0, -deltaAngle)
+        else:
+            relHpr(direct.widget, direct.camera, 0, 0, deltaAngle)
 
     def scale3D(self, state):
         # Scale the selected node based upon up down mouse motion
@@ -461,7 +467,7 @@ class DirectManipulationControl(PandaObject):
             # Move the objects with the widget
             direct.selected.moveWrtWidgetAll()
             # Let everyone know that something was moved
-            messenger.send('manipulateObjectCleanup')
+            messenger.send('DIRECT_manipulateObjectCleanup')
 
 class ObjectHandles(NodePath,PandaObject):
     def __init__(self):

+ 1 - 1
direct/src/directtools/DirectSelection.py

@@ -107,7 +107,7 @@ class SelectedNodePaths(PandaObject):
             # And keep track of it in the deselected dictionary
             self.deselectedDict[id] = dnp
             # Send a message
-            messenger.send('deselectedNodePath', [dnp])
+            messenger.send('DIRECT_deselectedNodePath', [dnp])
         return dnp
 
     def getSelectedAsList(self):

+ 54 - 25
direct/src/directtools/DirectSession.py

@@ -7,7 +7,9 @@ from DirectGrid import *
 from DirectGeometry import *
 from DirectLights import *
 from DirectSessionPanel import *
+from tkSimpleDialog import askstring
 import Placer
+import EntryScale
 import OnscreenText
 import types
 import __builtin__
@@ -44,6 +46,7 @@ class DirectSession(PandaObject):
         # Ancestry of currently selected object
         self.ancestry = []
         self.ancestryIndex = 0
+        self.activeParent = None
 
         self.readout = OnscreenText.OnscreenText( pos = (0.1, -0.95), bg=Vec4(1,1,1,1))
         # Make sure readout is never lit or drawn in wireframe
@@ -90,15 +93,19 @@ class DirectSession(PandaObject):
             ['highlightAll', self.selected.highlightAll],
             ['preRemoveNodePath', self.deselect],
             # Scene graph explorer functions
-            ['SGENodePath_Select', self.select],
-            ['SGENodePath_Deselect', self.deselect],
-            ['SGENodePath_Flash', self.flash],
-            ['SGENodePath_Isolate', self.isolate],
-            ['SGENodePath_Toggle Vis', self.toggleVis],
-            ['SGENodePath_Show All', self.showAllDescendants],
-            ['SGENodePath_Fit', self.fitOnNodePath],
-            ['SGENodePath_Place', Placer.place],
-            ['SGENodePath_Delete', self.removeNodePath],
+            ['SGE_Select', self.select],
+            ['SGE_Deselect', self.deselect],
+            ['SGE_Set Parent', self.setActiveParent],
+            ['SGE_Reparent', self.reparent],
+            ['SGE_Flash', self.flash],
+            ['SGE_Isolate', self.isolate],
+            ['SGE_Toggle Vis', self.toggleVis],
+            ['SGE_Show All', self.showAllDescendants],
+            ['SGE_Fit', self.fitOnNodePath],
+            ['SGE_Place', Placer.place],
+            ['SGE_Set Color', EntryScale.rgbPanel],
+            ['SGE_Delete', self.removeNodePath],
+            ['SGE_Set Name', self.getAndSetName],
             ]
         self.keyEvents = ['escape', 'delete', 'control', 'control-up',
                           'shift', 'shift-up', 'alt', 'alt-up',
@@ -195,17 +202,17 @@ class DirectSession(PandaObject):
     def inputHandler(self, input):
         # Deal with keyboard and mouse input
         if input == 'mouse1':
-            messenger.send('handleMouse1')
+            messenger.send('DIRECT_mouse1')
         elif input == 'mouse1-up':
-            messenger.send('handleMouse1Up')
+            messenger.send('DIRECT_mouse1Up')
         elif input == 'mouse2': 
-            messenger.send('handleMouse2')
+            messenger.send('DIRECT_mouse2')
         elif input == 'mouse2-up':
-            messenger.send('handleMouse2Up')
+            messenger.send('DIRECT_mouse2Up')
         elif input == 'mouse3': 
-            messenger.send('handleMouse3')
+            messenger.send('DIRECT_mouse3')
         elif input == 'mouse3-up':
-            messenger.send('handleMouse3Up')
+            messenger.send('DIRECT_mouse3Up')
         elif input == 'shift':
             self.fShift = 1
         elif input == 'shift-up':
@@ -249,7 +256,7 @@ class DirectSession(PandaObject):
     def select(self, nodePath, fMultiSelect = 0, fResetAncestry = 1):
         dnp = self.selected.select(nodePath, fMultiSelect)
         if dnp:
-            messenger.send('preSelectNodePath', [dnp])
+            messenger.send('DIRECT_preSelectNodePath', [dnp])
             if fResetAncestry:
                 # Update ancestry
                 self.ancestry = dnp.getAncestry()
@@ -276,7 +283,7 @@ class DirectSession(PandaObject):
             t.dnp = dnp
             taskMgr.spawnTaskNamed(t, 'followSelectedNodePath')
             # Send an message marking the event
-            messenger.send('selectedNodePath', [dnp])
+            messenger.send('DIRECT_selectedNodePath', [dnp])
 
     def followSelectedNodePathTask(self, state):
         mCoa2Render = state.dnp.mCoa2Dnp * state.dnp.getMat(render)
@@ -296,7 +303,7 @@ class DirectSession(PandaObject):
             taskMgr.removeTasksNamed('followSelectedNodePath')
             self.ancestry = []
             # Send an message marking the event
-            messenger.send('deselectedNodePath', [dnp])
+            messenger.send('DIRECT_deselectedNodePath', [dnp])
 
     def deselectAll(self):
         self.selected.deselectAll()
@@ -306,6 +313,20 @@ class DirectSession(PandaObject):
         self.readout.setText(' ')
         taskMgr.removeTasksNamed('followSelectedNodePath')
 
+    def setActiveParent(self, nodePath = None):
+        # Record new parent
+        self.activeParent = nodePath
+        # Alert everyone else
+        messenger.send('DIRECT_activeParent', [self.activeParent])
+        
+    def reparent(self, nodePath = None):
+        if nodePath and self.activeParent:
+            oldParent = nodePath.getParent()
+            nodePath.reparentTo(self.activeParent)
+            # Alert everyone else
+            messenger.send('DIRECT_reparent',
+                           [nodePath, oldParent, self.activeParent])
+        
     def flash(self, nodePath = 'None Given'):
         """ Highlight an object by setting it red for a few seconds """
         # Clean up any existing task
@@ -421,6 +442,14 @@ class DirectSession(PandaObject):
                     self.select(np, 0, 0)
                     self.flash(np)
 
+    def getAndSetName(self, nodePath):
+        """ Prompt user for new node path name """
+        newName = askstring('Node Path: ' + nodePath.getName(),
+                            'Enter new name:')
+        if newName:
+            nodePath.setName(newName)
+            messenger.send('DIRECT_nodePathSetName', [nodePath, newName])
+
     # UNDO REDO FUNCTIONS
     def pushUndo(self, nodePathList, fResetRedo = 1):
         # Assemble group of changes
@@ -435,10 +464,10 @@ class DirectSession(PandaObject):
         # Truncate list
         self.undoList = self.undoList[-25:]
         # Alert anyone who cares
-        messenger.send('pushUndo')
+        messenger.send('DIRECT_pushUndo')
         if fResetRedo and (nodePathList != []):
             self.redoList = []
-            messenger.send('redoListEmpty')
+            messenger.send('DIRECT_redoListEmpty')
 
     def popUndoGroup(self):
         # Get last item
@@ -447,7 +476,7 @@ class DirectSession(PandaObject):
         self.undoList = self.undoList[:-1]
         # Update state of undo button
         if not self.undoList:
-            messenger.send('undoListEmpty')
+            messenger.send('DIRECT_undoListEmpty')
         # Return last item
         return undoGroup
         
@@ -464,7 +493,7 @@ class DirectSession(PandaObject):
         # Truncate list
         self.redoList = self.redoList[-25:]
         # Alert anyone who cares
-        messenger.send('pushRedo')
+        messenger.send('DIRECT_pushRedo')
 
     def popRedoGroup(self):
         # Get last item
@@ -473,7 +502,7 @@ class DirectSession(PandaObject):
         self.redoList = self.redoList[:-1]
         # Update state of redo button
         if not self.redoList:
-            messenger.send('redoListEmpty')
+            messenger.send('DIRECT_redoListEmpty')
         # Return last item
         return redoGroup
         
@@ -489,7 +518,7 @@ class DirectSession(PandaObject):
                 # Undo xform
                 pose[0].setPosHprScale(pose[1], pose[2], pose[3])
             # Alert anyone who cares
-            messenger.send('undo')
+            messenger.send('DIRECT_undo')
 
     def redo(self):
         if self.redoList:
@@ -502,7 +531,7 @@ class DirectSession(PandaObject):
             for pose in redoGroup:
                 pose[0].setPosHprScale(pose[1], pose[2], pose[3])
             # Alert anyone who cares
-            messenger.send('redo')
+            messenger.send('DIRECT_redo')
 
     # UTILITY FUNCTIONS
     def useObjectHandles(self):

+ 0 - 12
direct/src/directtools/DirectSessionGlobal.py

@@ -1,12 +0,0 @@
-from ShowBaseGlobal import *
-import __builtin__
-
-# If specified in the user's Configrc, create the direct session
-if base.wantDIRECT:
-    from DirectSession import *
-    __builtin__.direct = base.direct = DirectSession()
-    direct.enable()
-else:
-    # Otherwise set the values to None
-    __builtin__.direct = base.direct = None
-

+ 7 - 0
direct/src/directtools/DirectUtil.py

@@ -1,4 +1,11 @@
 from PandaObject import *
+from EntryScale import EntryScale
+
+def adjust(command = None, min = 0.0, max = 1.0, text = 'Adjust'):
+    tl = Toplevel()
+    tl.title('Parameter Adjust')
+    es = EntryScale(tl, command = command, min = min, max = max, text = text)
+    es.pack(expand = 1, fill = X)
 
 ## Background Color ##
 def setBackgroundColor(r,g,b):

+ 31 - 2
direct/src/extensions/NodePath-extensions.py

@@ -776,11 +776,40 @@
     def place(self):
         base.wantDIRECT = 1
         base.wantTk = 1
-        from TkGlobal import *
-        from DirectSessionGlobal import *
+        from ShowBaseGlobal import *
+        import TkGlobal
         import Placer
         Placer.place(self)
 
+    def explore(self):
+        base.wantDIRECT = 1
+        base.wantTk = 1
+        from ShowBaseGlobal import *
+        import TkGlobal
+        import SceneGraphExplorer
+        SceneGraphExplorer.explore(self)
+
+    def rgbPanel(self, cb = None):
+        base.wantDIRECT = 1
+        base.wantTk = 1
+        from ShowBaseGlobal import *
+        import TkGlobal
+        import EntryScale
+        EntryScale.rgbPanel(self, cb)
+
+    def select(self):
+        base.wantDIRECT = 1
+        base.wantTk = 1
+        from ShowBaseGlobal import *
+        import TkGlobal
+        direct.select(self)
+
+    def deselect(self):
+        base.wantDIRECT = 1
+        base.wantTk = 1
+        from ShowBaseGlobal import *
+        import TkGlobal
+        direct.deselect(self)
 
 
 

+ 1 - 1
direct/src/interval/IntervalTest.py

@@ -1,5 +1,5 @@
 from PandaModules import *
-from DirectSessionGlobal import *
+from ShowBaseGlobal import *
 from IntervalGlobal import *
 from Actor import *
 

+ 147 - 175
direct/src/leveleditor/LevelEditor.py

@@ -20,14 +20,13 @@ else:
 
 print "Loading LevelEditor for hoods: ", hoods
 
-from DirectSessionGlobal import *
+from ShowBaseGlobal import *
 from PandaObject import *
 from PieMenu import *
 from GuiGlobals import *
 from Tkinter import *
 from DirectGeometry import *
 from SceneGraphExplorer import *
-from tkSimpleDialog import askstring
 from tkMessageBox import showinfo
 from tkFileDialog import *
 from whrandom import *
@@ -39,7 +38,6 @@ import os
 import __builtin__
 import whrandom
 import Floater
-import EntryScale
 
 # Colors used by all color menus
 DEFAULT_COLORS = [
@@ -418,19 +416,19 @@ class LevelEditor(NodePath, PandaObject):
         
         # The list of events the level editor responds to
         self.actionEvents = [
-            # Actions in response to DIRECT operations
-            ('selectedNodePath', self.selectedNodePathHook),
-            ('deselectedNodePath', self.deselectedNodePathHook),
+            # Node path events
             ('preRemoveNodePath', self.removeNodePathHook),
-            ('manipulateObjectCleanup', self.updateSelectedPose),
-            
+            # Actions in response to DIRECT operations
+            ('DIRECT_selectedNodePath', self.selectedNodePathHook),
+            ('DIRECT_deselectedNodePath', self.deselectedNodePathHook),
+            ('DIRECT_manipulateObjectCleanup', self.updateSelectedPose),
+            ('DIRECT_nodePathSetName', self.setName),
+            ('DIRECT_activeParent', self.setActiveParent),
+            ('DIRECT_reparent', self.reparent),
+            ('RGBPanel_setColor', self.setColor),
             # Actions in response to Level Editor Panel operations
-            ('SGENodePath_Set Name', self.getAndSetName),
-            ('SGENodePath_Set Parent', self.setActiveParent),
-            ('SGENodePath_Reparent', self.reparent),
-            ('SGENodePath_Add Group', self.addGroup),
-            ('SGENodePath_Add Vis Group', self.addVisGroup),
-            ('SGENodePath_Set Color', self.setNPColor),
+            ('SGE_Add Group', self.addGroup),
+            ('SGE_Add Vis Group', self.addVisGroup),
             # Actions in response to Pie Menu interaction
             ('select_building_style', self.setBuildingStyle),
             ('select_building_type', self.setBuildingType),
@@ -515,6 +513,7 @@ class LevelEditor(NodePath, PandaObject):
 
         # BATTLE CELLS
         self.battleCellMarker = loader.loadModel('models/misc/sphere')
+        self.battleCellMarker.setName('battleCellMarker')
         self.battleCellMarker.setScale(1)
         self.currentBattleCellType = "20w 20l"
 
@@ -676,19 +675,22 @@ class LevelEditor(NodePath, PandaObject):
         # Turn off player camera control
         base.disableMouse()
         # Handle mouse events for pie menus
-        self.accept('handleMouse3',self.levelHandleMouse3)
-        self.accept('handleMouse3Up',self.levelHandleMouse3Up)
+        self.accept('DIRECT_mouse3',self.levelHandleMouse3)
+        self.accept('DIRECT_mouse3Up',self.levelHandleMouse3Up)
 
     def disableMouse(self):
         """ Disable Pie Menu interaction """
         # Disable handling of mouse events
-        self.ignore('handleMouse3')
-        self.ignore('handleMouse3Up')
+        self.ignore('DIRECT_mouse3')
+        self.ignore('DIRECT_mouse3Up')
 
     # LEVEL OBJECT MANAGEMENT FUNCTIONS
     def findDNANode(self, nodePath):
         """ Find node path's DNA Object in DNAStorage (if any) """
-        return DNASTORE.findDNAGroup(nodePath.id())
+        if nodePath:
+            return DNASTORE.findDNAGroup(nodePath.id())
+        else:
+            return None
 
     def replaceSelected(self):
         if self.replaceSelectedEnabled:
@@ -735,40 +737,35 @@ class LevelEditor(NodePath, PandaObject):
             nodePath.removeNode()
 
     def removeNodePathHook(self, nodePath):
-        if nodePath:
-            dnaNode = self.findDNANode(nodePath)
-            # Does the node path correspond to a DNA Object
-            if dnaNode:
-                for i in self.removeHookList:
-                    i(dnaNode, nodePath)
-                # Get DNANode's parent
-                parentDNANode = dnaNode.getParent()
-                if parentDNANode:
-                    # Remove DNANode from its parent
-                    parentDNANode.remove(dnaNode)
-                # Delete DNA and associated Node Relations from DNA Store
-                DNASTORE.removeDNAGroup(dnaNode)
-
-    def reparent(self, nodePath):
+        dnaNode = self.findDNANode(nodePath)
+        # Does the node path correspond to a DNA Object
+        if dnaNode:
+            for i in self.removeHookList:
+                i(dnaNode, nodePath)
+            # Get DNANode's parent
+            parentDNANode = dnaNode.getParent()
+            if parentDNANode:
+                # Remove DNANode from its parent
+                parentDNANode.remove(dnaNode)
+            # Delete DNA and associated Node Relations from DNA Store
+            DNASTORE.removeDNAGroup(dnaNode)
+
+    def reparent(self, nodePath, oldParent, newParent):
         """ Move node path (and its DNA) to active parent """
-        # Do we have a node path?
-        if nodePath:
-            dnaNode = self.findDNANode(nodePath)
-            # Does the node path correspond to a DNA Object
-            if dnaNode:
-                # Yes, get its parent and see if it has a DNA Object
-                parent = nodePath.getParent()
-                parentDNAObject = self.findDNANode(parent)
-                if parentDNAObject:
-                    # Yes it does, move node path (and DNA)
-                    # to new parent (if active parent set)
-                    if ((self.NPParent != None) and
-                        (self.DNAParent != None)):
-                        nodePath.reparentTo(self.NPParent)
-                        parentDNAObject.remove(dnaNode)
-                        self.DNAParent.add(dnaNode)
-                        # Update scene graph explorer to reflect change
-                        # self.panel.sceneGraphExplorer.update()
+        # Does the node path correspond to a DNA Object
+        dnaNode = self.findDNANode(nodePath)
+        if dnaNode:
+            # Find old parent DNA
+            oldParentDNANode = self.findDNANode(oldParent)
+            # Remove DNA from old parent
+            if oldParentDNANode:
+                oldParentDNANode.remove(dnaNode)
+            if newParent:
+                # Update active parent just to be safe
+                self.setActiveParent(newParent)
+                # Move DNA to new parent (if active parent set)
+                if self.DNAParent != None:
+                    self.DNAParent.add(dnaNode)
 
     def reparentSelected(self):
         for nodePath in direct.selected:
@@ -776,9 +773,6 @@ class LevelEditor(NodePath, PandaObject):
 
     def setActiveParent(self, nodePath = None):
         """ Set NPParent and DNAParent to node path and its DNA """
-        # If nothing passed in, try currently selected node path
-        if not nodePath:
-            nodePath = direct.selected.last
         # If we've got a valid node path
         if nodePath:
             # See if this is in the DNA database
@@ -791,17 +785,9 @@ class LevelEditor(NodePath, PandaObject):
         else:
             print 'LevelEditor.setActiveParent: nodePath == None'
 
-    def getAndSetName(self, nodePath):
-        """ Prompt user for new node path name """
-        newName = askstring('Node Path', 'Enter new name:')
-        if newName:
-            self.setName(nodePath, newName)
-
     def setName(self, nodePath, newName):
-        """ Set name of a node path and its DNA (if it exists) """
-        # First, set name of the node path
-        nodePath.setName(newName)
-        # Now find the DNA that corresponds to this node path
+        """ Set name of nodePath's DNA (if it exists) """
+        # Find the DNA that corresponds to this node path
         dnaNode = self.findDNANode(nodePath)
         if dnaNode:
             # If it exists, set the name of the DNA Node
@@ -834,30 +820,34 @@ class LevelEditor(NodePath, PandaObject):
                     # Update DNA
                     self.updatePose(dnaObject, selectedNode)
             else:
-                possiblePoint = self.findSuitPointMarker(selectedNode)
-                if possiblePoint:
-                    point = self.getSuitPointFromNodePath(possiblePoint)
-                    if point:
-                        print "Found suit point!", point
-                        # First snap selected node path to grid
-                        pos = selectedNode.getPos(direct.grid)
-                        snapPos = direct.grid.computeSnapPoint(pos)
-                        if self.panel.fPlaneSnap.get():
-                            zheight = 0
-                        else:
-                            zheight = snapPos[2]
-                        selectedNode.setPos(direct.grid,
-                                            snapPos[0], snapPos[1], zheight)
-                        newPos = selectedNode.getPos(self.NPToplevel)
-                        point.setPos(newPos)
+                pointOrCell, type = self.findPointOrCell(selectedNode)
+                if pointOrCell and type:
+                    # First snap selected node path to grid
+                    pos = selectedNode.getPos(direct.grid)
+                    snapPos = direct.grid.computeSnapPoint(pos)
+                    if self.panel.fPlaneSnap.get():
+                        zheight = 0
+                    else:
+                        zheight = snapPos[2]
+                    selectedNode.setPos(
+                        direct.grid,
+                        snapPos[0], snapPos[1], zheight)
+                    newPos = selectedNode.getPos(self.NPToplevel)
+                    # Update DNA
+                    pointOrCell.setPos(newPos)
+                    if (type == 'suitPointMarker'):
+                        print "Found suit point!", pointOrCell
                         # Ok, now update all the lines into that node
-                        for edge in self.point2edgeDict[point]:
+                        for edge in self.point2edgeDict[pointOrCell]:
                             oldEdgeLine = self.edgeDict[edge]
                             del self.edgeDict[edge]
                             oldEdgeLine.reset()
                             del oldEdgeLine
-                            newEdgeLine = self.drawSuitEdge(edge, self.NPParent)
+                            newEdgeLine = self.drawSuitEdge(
+                                edge, self.NPParent)
                             self.edgeDict[edge] = newEdgeLine
+                    elif (type == 'battleCellMarker'):
+                        print "Found battle cell!", pointOrCell
 
     def updatePose(self, dnaObject, nodePath):
         """
@@ -1013,14 +1003,14 @@ class LevelEditor(NodePath, PandaObject):
     def addGroup(self, nodePath):
         """ Add a new DNA Node Group to the specified Node Path """
         # Set the node path as the current parent
-        self.setActiveParent(nodePath)
+        direct.setActiveParent(nodePath)
         # Add a new group to the selected parent
         self.createNewGroup()
 
     def addVisGroup(self, nodePath):
         """ Add a new DNA Group to the specified Node Path """
         # Set the node path as the current parent
-        self.setActiveParent(nodePath)
+        direct.setActiveParent(nodePath)
         # Add a new group to the selected parent
         self.createNewGroup(type = 'vis')
 
@@ -1473,13 +1463,23 @@ class LevelEditor(NodePath, PandaObject):
                 self.DNATarget.getHeight())
             self.replaceSelected()
     
-    def setNPColor(self, nodePath):
+    def setColor(self, nodePath, r,g,b,a):
         """ This is used to set color of dnaNode subparts """
-        def updateDNANodeColor(color, s = self, np = nodePath):
-            # Update node color in DNASTORE here
-            # dnaNode = s.findDNANode(np)
-            pass
-        esg = EntryScale.setColor(nodePath, updateDNANodeColor)
+        dnaNode = self.findDNANode(nodePath)
+        if dnaNode:
+            objClass = DNAGetClassType(dnaNode)
+            if ((objClass.eq(DNA_WALL)) or
+                (objClass.eq(DNA_WINDOWS)) or
+                (objClass.eq(DNA_DOOR)) or
+                (objClass.eq(DNA_CORNICE)) or
+                (objClass.eq(DNA_PROP)) or
+                (objClass.eq(DNA_SIGN)) or
+                (objClass.eq(DNA_SIGN_BASELINE)) or
+                (objClass.eq(DNA_SIGN_TEXT)) or
+                (objClass.eq(DNA_SIGN_GRAPHIC))
+                ):
+                # Update dna information
+                dnaNode.setColor(VBase4(r/255.0, g/255.0, b/255.0, a/255.0))
     
     # SELECTION FUNCTIONS
     def selectedNodePathHook(self, nodePath):
@@ -1527,15 +1527,12 @@ class LevelEditor(NodePath, PandaObject):
             if DNAClassEqual(dnaNode, DNA_STREET):
                 self.snapList = OBJECT_SNAP_POINTS[dnaNode.getCode()]
         else:
-            possiblePoint = self.findSuitPointMarker(nodePath)
-            if possiblePoint:
-                point = self.getSuitPointFromNodePath(possiblePoint)
-                if point:
-                    print "Found suit point!", point
-                else:
-                    pass
-            else:
-                pass
+            pointOrCell, type = self.findPointOrCell(nodePath)
+            if pointOrCell and (type == 'suitPointMarker'):
+                print "Found suit point!", pointOrCell
+            if pointOrCell and (type == 'battleCellMarker'):
+                print "Found battle cell!", pointOrCell
+
         # Let others know that something new may be selected:
         for i in self.selectedNodePathHookHooks:
                 i()
@@ -1577,20 +1574,29 @@ class LevelEditor(NodePath, PandaObject):
                 return self.findDNARoot(nodePath.getParent())
 
 
-    def findSuitPointMarker(self, nodePath):
-        """ Walk up a node path's ancestry looking for its DNA Root """
+    def findPointOrCell(self, nodePath):
+        """
+        Walk up a node path's ancestry to see if its a suit point marker
+        or a battle cell marker
+        """
         # Check current node's name for root marker
         if (nodePath.getName() == 'suitPointMarker'):
-            # Its a root!
-            return nodePath
+            # Its a suit point marker!
+            # See if point is in pointDict
+            point = self.getSuitPointFromNodePath(nodePath)
+            return point, 'suitPointMarker'
+        elif (nodePath.getName() == 'battleCellMarker'):
+            # Its a battle cell marker
+            # See if cell is in cell Dict
+            cell = self.getBattleCellFromNodePath(nodePath)
+            return cell, 'battleCellMarker'
         else:
             # If reached the top: fail
             if not nodePath.hasParent():
-                return None
+                return None, None
             else:
                 # Try parent
-                return self.findSuitPointMarker(nodePath.getParent())
-
+                return self.findPointOrCell(nodePath.getParent())
 
     # MANIPULATION FUNCTIONS
     def keyboardRotateSelected(self, arrowDirection):
@@ -2252,15 +2258,17 @@ class LevelEditor(NodePath, PandaObject):
 
     def placeSuitPoint(self):
         v = self.getGridSnapIntersectionPoint()
-        # get the absolute pos relative to the top level. That is what gets stored in the point
+        # get the absolute pos relative to the top level.
+        # That is what gets stored in the point
         mat = direct.grid.getMat(self.NPToplevel)
         absPos = Point3(mat.xformPoint(v))
         print 'Suit point: ' + `absPos`
-        # Store the point in the DNA. If this point is already in there, it returns
-        # the existing point
+        # Store the point in the DNA. If this point is already in there,
+        # it returns the existing point
         suitPoint = DNASTORE.storeSuitPoint(self.currentSuitPointType, absPos)
         if not self.pointDict.has_key(suitPoint):
-            marker = self.drawSuitPoint(absPos, self.currentSuitPointType, self.NPToplevel)
+            marker = self.drawSuitPoint(
+                absPos, self.currentSuitPointType, self.NPToplevel)
             self.pointDict[suitPoint] = marker
         self.currentSuitPointIndex = suitPoint.getIndex()
 
@@ -2269,9 +2277,10 @@ class LevelEditor(NodePath, PandaObject):
             # Make a new dna edge
             if DNAClassEqual(self.DNAParent, DNA_VIS_GROUP):
                 zoneId = self.DNAParent.getName()
-                suitEdge = DNASuitEdge(self.startSuitPoint, self.endSuitPoint, zoneId)
+                suitEdge = DNASuitEdge(
+                    self.startSuitPoint, self.endSuitPoint, zoneId)
                 DNASTORE.storeSuitEdge(suitEdge)
-                # Add the edge to the current vis group so it can be written out
+                # Add edge to the current vis group so it can be written out
                 self.DNAParent.addSuitEdge(suitEdge)
                 # Draw a line to represent the edge
                 edgeLine = self.drawSuitEdge(suitEdge, self.NPParent)
@@ -2338,7 +2347,8 @@ class LevelEditor(NodePath, PandaObject):
         numPoints = DNASTORE.getNumSuitPoints()
         for i in range(numPoints):
             point = DNASTORE.getSuitPoint(i)
-            marker = self.drawSuitPoint(point.getPos(), point.getPointType(), self.NPToplevel)
+            marker = self.drawSuitPoint(
+                point.getPos(), point.getPointType(), self.NPToplevel)
             self.pointDict[point] = marker
 
         # Edges
@@ -2359,7 +2369,6 @@ class LevelEditor(NodePath, PandaObject):
                     else:
                         self.point2edgeDict[point] = [edge]
 
-
     def getSuitPointFromNodePath(self, nodePath):
         """
         Given a node path, attempt to find the point,nodePath pair
@@ -2421,6 +2430,18 @@ class LevelEditor(NodePath, PandaObject):
         for cell, marker in self.cellDict.items():
             marker.show()
     
+    def getBattleCellFromNodePath(self, nodePath):
+        """
+        Given a node path, attempt to find the battle cell, nodePath pair
+        in the cellDict. If it is there, return the cell. If we
+        cannot find it, return None.
+        TODO: a reverse lookup cellDict would speed this up quite a bit
+        """
+        for cell, marker in self.cellDict.items():
+            if marker.eq(nodePath):
+                return cell
+        return None
+
     def colorZones(self):
         # Give each zone a random color to see them better
         visGroups = self.getDNAVisGroups(self.NPToplevel)
@@ -3728,13 +3749,14 @@ class LevelEditorPanel(Pmw.MegaToplevel):
         self.fUpdateSelected = 1
         # Handle to the toplevels hull
         hull = self.component('hull')
-
+        hull.geometry('400x575')
+        
         balloon = self.balloon = Pmw.Balloon(hull)
         # Start with balloon help disabled
         self.balloon.configure(state = 'none')
         
         menuFrame = Frame(hull, relief = GROOVE, bd = 2)
-        menuFrame.pack(fill = X, expand = 1)
+        menuFrame.pack(fill = X)
 
         menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon)
         menuBar.pack(side = LEFT, expand = 1, fill = X)
@@ -3809,7 +3831,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
 
         self.editMenu = Pmw.ComboBox(
             menuFrame, labelpos = W,
-            label_text = 'Edit Mode:', entry_width = 12,
+            label_text = 'Edit Mode:', entry_width = 18,
             selectioncommand = self.levelEditor.setEditMode, history = 0,
             scrolledlist_items = NEIGHBORHOODS)
         self.editMenu.selectitem(NEIGHBORHOODS[0])
@@ -4181,50 +4203,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
                                       variable = self.fGrid,
                                       command = self.toggleGrid)
         direct.gridButton.pack(side = 'left', expand = 1, fill = 'x')
-
-        buttonFrame.pack(expand = 1, fill = 'x')
-
-        buttonFrame2 = Frame(hull)
-        self.groupButton = Button(
-            buttonFrame2,
-            text = 'New Group',
-            command = self.levelEditor.createNewGroup)
-        self.groupButton.pack(side = 'left', expand = 1, fill = 'x')
-        
-        self.visGroupButton = Button(
-            buttonFrame2,
-            text = 'New Vis Group',
-            command = self.createNewVisGroup)
-        self.visGroupButton.pack(side = 'left', expand = 1, fill = 'x')
-        
-        self.setParentButton = Button(
-            buttonFrame2,
-            text = 'Set Parent',
-            command = self.levelEditor.setActiveParent)
-        self.setParentButton.pack(side = 'left', expand = 1, fill = 'x')
-
-        self.reparentButton = Button(
-            buttonFrame2,
-            text = 'Re Parent',
-            command = self.levelEditor.reparentSelected)
-        self.reparentButton.pack(side = 'left', expand = 1, fill = 'x')
-
-        buttonFrame2.pack(fill = 'x')
-
-        buttonFrame3 = Frame(hull)
-        self.isolateButton = Button(
-            buttonFrame3,
-            text = 'Isolate Selected',
-            command = direct.isolate)
-        self.isolateButton.pack(side = 'left', expand = 1, fill = 'x')
-
-        self.showAllButton = Button(
-            buttonFrame3,
-            text = 'Show All',
-            command = self.showAll)
-        self.showAllButton.pack(side = 'left', expand = 1, fill = 'x')
-
-        buttonFrame3.pack(fill = 'x')
+        buttonFrame.pack(fill = X)
 
         buttonFrame4 = Frame(hull)
         self.driveMode = IntVar()
@@ -4243,14 +4222,13 @@ class LevelEditorPanel(Pmw.MegaToplevel):
             variable = self.driveMode,
             command = self.levelEditor.useDirectFly)
         directModeButton.pack(side = 'left', expand = 1, fill = 'x')
-        buttonFrame4.pack(fill = 'x')
+        buttonFrame4.pack(fill = X)
 
         self.sceneGraphExplorer = SceneGraphExplorer(
             parent = sceneGraphPage,
-            menuItems = ['Set Parent', 'Reparent', 'Add Group',
-                         'Add Vis Group', 'Set Color',
-                         'Set Name'])
-        self.sceneGraphExplorer.pack(expand = 1, fill = 'both')
+            nodePath = self.levelEditor,
+            menuItems = ['Add Group', 'Add Vis Group', 'Set Color'])
+        self.sceneGraphExplorer.pack(expand = 1, fill = BOTH)
         
         # Make sure input variables processed 
         self.initialiseoptions(LevelEditorPanel)
@@ -4286,12 +4264,6 @@ class LevelEditorPanel(Pmw.MegaToplevel):
     def toggleMapVis(self):
         self.levelEditor.toggleMapVis(self.fMapVis.get())
 
-    def showAll(self):
-        direct.showAll(self.levelEditor.NPToplevel)
-
-    def createNewVisGroup(self):
-        self.levelEditor.createNewGroup(type = 'vis')
-        
     def setStreetModuleType(self,name):
         self.streetModuleType = 'street_' + name
         self.levelEditor.setCurrent('street_texture', self.streetModuleType)
@@ -4699,8 +4671,8 @@ class LevelEditorPanel(Pmw.MegaToplevel):
                     (objClass.eq(DNA_PROP)) or
                     (objClass.eq(DNA_SIGN)) or
                     (objClass.eq(DNA_SIGN_BASELINE)) or
-                    (objClass.eq(DNA_SIGN_BASELINE_TEXT)) or
-                    (objClass.eq(DNA_SIGN_BASELINE_GRAPHIC))
+                    (objClass.eq(DNA_SIGN_TEXT)) or
+                    (objClass.eq(DNA_SIGN_GRAPHIC))
                     ):
                     self.levelEditor.setDNATargetColor(
                         VBase4((color[0]/255.0),

+ 1 - 1
direct/src/leveleditor/LevelEditorStart.py

@@ -1,3 +1,3 @@
-from DirectSessionGlobal import *
+from ShowBaseGlobal import *
 import LevelEditor
 l = LevelEditor.LevelEditor()

+ 1 - 1
direct/src/particles/ParticleTest.py

@@ -1,4 +1,4 @@
-from DirectSessionGlobal import *
+from ShowBaseGlobal import *
 
 import ParticleEffect
 import ParticlePanel

+ 3 - 0
direct/src/particles/Particles.py

@@ -179,6 +179,9 @@ class Particles(ParticleSystem.ParticleSystem):
 	else:
 	    self.removeAngularForce(force)
 
+    def setRenderNodePath(self, nodePath):
+        self.setRenderParent(nodePath.node())
+
     ## Getters ##
     def getName(self):
         """getName()"""

+ 9 - 0
direct/src/showbase/ShowBaseGlobal.py

@@ -20,6 +20,15 @@ __builtin__.messenger = base.messenger
 __builtin__.config = base.config
 __builtin__.directNotify = directNotify
 
+# Initialize DIRECT
+if base.wantDIRECT:
+    from DirectSession import *
+    __builtin__.direct = base.direct = DirectSession()
+    direct.enable()
+else:
+    # Otherwise set the values to None
+    __builtin__.direct = base.direct = None
+
 # Set direct notify categories now that we have config
 directNotify.setDconfigLevels()
 

+ 20 - 18
direct/src/tkpanels/DirectSessionPanel.py

@@ -22,7 +22,7 @@ taskMgr page
 class DirectSessionPanel(AppShell):
     # Override class variables here
     appname = 'Direct Session Panel'
-    frameWidth      = 550
+    frameWidth      = 600
     frameHeight     = 502
     usecommandarea = 1
     usestatusarea  = 0
@@ -67,15 +67,16 @@ class DirectSessionPanel(AppShell):
         self.jbNodePathNames = ['camera', 'selected', 'none']
 
         # Set up event hooks
-        self.actionEvents = [('undo', self.undoHook),
-                             ('pushUndo', self.pushUndoHook),
-                             ('undoListEmpty', self.undoListEmptyHook),
-                             ('redo', self.redoHook),
-                             ('pushRedo', self.pushRedoHook),
-                             ('redoListEmpty', self.redoListEmptyHook),
-                             ('selectedNodePath', self.selectedNodePathHook),
-                             ('DirectLights_addLight', self.addLight),
-                             ]
+        self.actionEvents = [
+            ('DIRECT_undo', self.undoHook),
+            ('DIRECT_pushUndo', self.pushUndoHook),
+            ('DIRECT_undoListEmpty', self.undoListEmptyHook),
+            ('DIRECT_redo', self.redoHook),
+            ('DIRECT_pushRedo', self.pushRedoHook),
+            ('DIRECT_redoListEmpty', self.redoListEmptyHook),
+            ('DIRECT_selectedNodePath',self.selectedNodePathHook),
+            ('DIRECT_addLight', self.addLight),
+            ]
         for event, method in self.actionEvents:
             self.accept(event, method)
 
@@ -139,20 +140,21 @@ class DirectSessionPanel(AppShell):
         # The master frame for the dials
 	mainFrame = Frame(interior)
 
+        # Paned widget for dividing two halves
+        framePane = Pmw.PanedWidget(mainFrame, orient = HORIZONTAL)
+        sgeFrame = framePane.add('left', min = 250)
+        notebookFrame = framePane.add('right', min = 300)
+
         # Scene Graph Explorer
-        sgeFrame = Frame(mainFrame)
-        self.sgeUpdate = Button(sgeFrame, text = 'Update Explorer')
-        self.sgeUpdate.pack(fill = X, expand = 0)
         self.SGE = SceneGraphExplorer.SceneGraphExplorer(
             sgeFrame, nodePath = render,
-            scrolledCanvas_hull_width = 200,
+            scrolledCanvas_hull_width = 250,
             scrolledCanvas_hull_height = 400)
         self.SGE.pack(fill = BOTH, expand = 0)
-        self.sgeUpdate['command'] = self.SGE.update
         sgeFrame.pack(side = LEFT, fill = 'both', expand = 0)
 
         # Create the notebook pages
-        notebook = Pmw.NoteBook(mainFrame)
+        notebook = Pmw.NoteBook(notebookFrame)
         notebook.pack(fill = BOTH, expand = 1)
         envPage = notebook.add('Environment')
         lightsPage = notebook.add('Lights')
@@ -562,9 +564,9 @@ class DirectSessionPanel(AppShell):
             self.jbHprSF.pack(fill = X, expand = 0)
             self.bind(self.jbHprSF, 'Set joybox HPR speed multiplier')
 
-
         notebook.setnaturalsize()
-        
+
+        framePane.pack(expand = 1, fill = BOTH)
         mainFrame.pack(fill = 'both', expand = 1)
 
         # Create some buttons in the bottom tray

+ 11 - 10
direct/src/tkpanels/MopathRecorder.py

@@ -153,16 +153,17 @@ class MopathRecorder(AppShell, PandaObject):
         self.iRay = SelectionRay(self.iRayCS)
         # Set up event hooks
         self.actionEvents = [
-            ('undo', self.undoHook),
-            ('pushUndo', self.pushUndoHook),
-            ('undoListEmpty', self.undoListEmptyHook),
-            ('redo', self.redoHook),
-            ('pushRedo', self.pushRedoHook),
-            ('redoListEmpty', self.redoListEmptyHook),
-            ('selectedNodePath', self.selectedNodePathHook),
-            ('deselectedNodePath', self.deselectedNodePathHook),
-            ('manipulateObjectStart', self.manipulateObjectStartHook),
-            ('manipulateObjectCleanup', self.manipulateObjectCleanupHook),
+            ('DIRECT_undo', self.undoHook),
+            ('DIRECT_pushUndo', self.pushUndoHook),
+            ('DIRECT_undoListEmpty', self.undoListEmptyHook),
+            ('DIRECT_redo', self.redoHook),
+            ('DIRECT_pushRedo', self.pushRedoHook),
+            ('DIRECT_redoListEmpty', self.redoListEmptyHook),
+            ('DIRECT_selectedNodePath', self.selectedNodePathHook),
+            ('DIRECT_deselectedNodePath', self.deselectedNodePathHook),
+            ('DIRECT_manipulateObjectStart', self.manipulateObjectStartHook),
+            ('DIRECT_manipulateObjectCleanup',
+             self.manipulateObjectCleanupHook),
             ]
         for event, method in self.actionEvents:
             self.accept(event, method)

+ 7 - 7
direct/src/tkpanels/Placer.py

@@ -67,12 +67,12 @@ class Placer(AppShell):
         self.posOffset = Vec3(0)
 
         # Set up event hooks
-        self.undoEvents = [('undo', self.undoHook),
-                           ('pushUndo', self.pushUndoHook),
-                           ('undoListEmpty', self.undoListEmptyHook),
-                           ('redo', self.redoHook),
-                           ('pushRedo', self.pushRedoHook),
-                           ('redoListEmpty', self.redoListEmptyHook)]
+        self.undoEvents = [('DIRECT_undo', self.undoHook),
+                           ('DIRECT_pushUndo', self.pushUndoHook),
+                           ('DIRECT_undoListEmpty', self.undoListEmptyHook),
+                           ('DIRECT_redo', self.redoHook),
+                           ('DIRECT_pushRedo', self.pushRedoHook),
+                           ('DIRECT_redoListEmpty', self.redoListEmptyHook)]
         for event, method in self.undoEvents:
             self.accept(event, method)
 
@@ -610,7 +610,7 @@ class Placer(AppShell):
         
     def xformStop(self, data):
         # Throw event to signal manipulation done
-        messenger.send('manipulateObjectCleanup')
+        messenger.send('DIRECT_manipulateObjectCleanup')
         # Update placer to reflect new state
         self.updatePlacer()
         # If moving widget restart follow task

+ 81 - 23
direct/src/tkwidgets/EntryScale.py

@@ -22,7 +22,7 @@ class EntryScale(Pmw.MegaWidget):
             ('initialValue',        0.0,           Pmw.INITOPT),
             ('resolution',          0.001,         None),
             ('command',             None,          None),
-            ('callbackData',        [],       None),
+            ('callbackData',        [],            None),
             ('min',                 0.0,           self._updateValidate),
             ('max',                 100.0,         self._updateValidate),
             ('text',                'EntryScale',  self._updateLabelText),
@@ -328,6 +328,12 @@ class EntryScaleGroup(Pmw.MegaToplevel):
                 text = self['labels'][index])
             # Do this separately so command doesn't get executed during construction
             f['command'] = lambda val, s=self, i=index: s._entryScaleSetAt(i, val)
+            f['callbackData'] = [self]
+            # Callbacks
+            f.onReturn = self.__onReturn
+            f.onReturnRelease = self.__onReturnRelease
+            f.onPress = self.__onPress
+            f.onRelease = self.__onRelease
             f.pack(side = self['side'], expand = 1, fill = X)
             self.entryScaleList.append(f)
 
@@ -376,15 +382,80 @@ class EntryScaleGroup(Pmw.MegaToplevel):
     def reset(self):
         self.set(self['initialValue'])
 
+    def __onReturn(self, esg):
+        # Execute onReturn callback
+        apply(self.onReturn, esg.get())
 
-def setColor(nodePath, callback = None):
+    def onReturn(self, *args):
+        """ User redefinable callback executed on button press """
+        pass
+
+    def __onReturnRelease(self, esg):
+        # Execute onReturnRelease callback
+        apply(self.onReturnRelease, esg.get())
+
+    def onReturnRelease(self, *args):
+        """ User redefinable callback executed on button press """
+        pass
+
+    def __onPress(self, esg):
+        # Execute onPress callback
+        apply(self.onPress, esg.get())
+
+    def onPress(self, *args):
+        """ User redefinable callback executed on button press """
+        pass
+
+    def __onRelease(self, esg):
+        # Execute onRelease callback
+        apply(self.onRelease, esg.get())
+
+    def onRelease(self, *args):
+        """ User redefinable callback executed on button release """
+        pass
+
+def rgbPanel(nodePath, callback = None):
     def setNodePathColor(color, np = nodePath, cb = callback):
         np.setColor(color[0]/255.0, color[1]/255.0,
                     color[2]/255.0, color[3]/255.0)
         # Execute callback to pass along color info
         if cb:
             cb(color)
-    def popupColorPicker():
+    # Check init color
+    if nodePath.hasColor():
+        initColor = nodePath.getColor() * 255.0
+    else:
+        initColor = Vec4(255)
+    # Create entry scale group
+    esg = EntryScaleGroup(title = 'RGBA Panel: ' + nodePath.getName(),
+                          dim = 4,
+                          labels = ['R','G','B','A'],
+                          initialValue = [int(initColor[0]),
+                                          int(initColor[1]),
+                                          int(initColor[2]),
+                                          int(initColor[3])],
+                          Valuator_max = 255,
+                          Valuator_resolution = 1,
+                          # Destroy not withdraw panel on dismiss
+                          fDestroy = 1,
+                          command = setNodePathColor)
+    # Update menu button
+    esg.component('menubar').component('EntryScale Group-button')['text'] = (
+        'RGBA Panel')
+    # Update menu
+    menu = esg.component('menubar').component('EntryScale Group-menu')
+    # Some helper functions
+    # Clear color
+    menu.insert_command(index = 1, label = 'Clear Color',
+                        command = lambda np = nodePath: np.clearColor())
+    # Set Clear Transparency
+    menu.insert_command(index = 2, label = 'Set Transparency',
+                        command = lambda np = nodePath: np.setTransparency(1))
+    menu.insert_command(
+        index = 3, label = 'Clear Transparency',
+        command = lambda np = nodePath: np.clearTransparency())
+    # System color picker
+    def popupColorPicker(esg = esg):
         # Can pass in current color with: color = (255, 0, 0)
         color = tkColorChooser.askcolor(
             parent = esg.interior(),
@@ -392,26 +463,13 @@ def setColor(nodePath, callback = None):
             initialcolor = tuple(esg.get()[:3]))[0]
         if color:
             esg.set((color[0], color[1], color[2], esg.getAt(3)))
-    if isinstance(nodePath, NodePath):
-        if nodePath.hasColor():
-            initColor = nodePath.getColor() * 255.0
-        else:
-            initColor = Vec4(255)
-        esg = EntryScaleGroup(title = 'RGBA Panel',
-                              dim = 4,
-                              labels = ['R','G','B','A'],
-                              initialValue = [int(initColor[0]),
-                                              int(initColor[1]),
-                                              int(initColor[2]),
-                                              int(initColor[3])],
-                              Valuator_max = 255,
-                              Valuator_resolution = 1,
-                              # Destroy not withdraw panel on dismiss
-                              fDestroy = 1,
-                              command = setNodePathColor)
-        menu = esg.component('menubar').component('EntryScale Group-menu')
-        menu.insert_command(index = 1, label = 'Popup Color Picker',
-                            command = popupColorPicker)
+    menu.insert_command(index = 4, label = 'Popup Color Picker',
+                        command = popupColorPicker)
+    
+    # Set callback
+    def onRelease(r,g,b,a, nodePath = nodePath):
+        messenger.send('RGBPanel_setColor', [nodePath, r,g,b,a])
+    esg.onRelease = onRelease
     return esg
 
 ## SAMPLE CODE

+ 33 - 7
direct/src/tkwidgets/SceneGraphExplorer.py

@@ -3,11 +3,19 @@ from Tkinter import *
 from Tree import *
 import Pmw
 
-DEFAULT_MENU_ITEMS = ['Select', 'Deselect', 'Flash', 
-                      'Isolate', 'Toggle Vis', 'Show All',
-                      'Fit', 'Place', 'Delete']
-
-class SceneGraphExplorer(Pmw.MegaWidget):
+DEFAULT_MENU_ITEMS = [
+    'Update Explorer',
+    'Separator',
+    'Select', 'Deselect', 'Set Parent', 'Reparent', 'Set Name',
+    'Separator',
+    'Delete',
+    'Separator',
+    'Fit', 'Flash', 'Isolate', 'Toggle Vis', 'Show All',
+    'Separator',
+    'Place', 'Set Color',
+    'Separator']
+
+class SceneGraphExplorer(Pmw.MegaWidget, PandaObject):
     "Graphical display of a scene graph"
     def __init__(self, parent = None, nodePath = render, **kw):
         # Define the megawidget options.
@@ -53,6 +61,24 @@ class SceneGraphExplorer(Pmw.MegaWidget):
                               DEFAULT_MENU_ITEMS + self['menuItems'])
         self._node.expand()
 
+        self._parentFrame = Frame(interior)
+        self._label = self.createcomponent(
+            'parentLabel',
+            (), None,
+            Label, (interior,),
+            text = 'Active Parent: ',
+            anchor = W, justify = LEFT)
+        self._label.pack(expand = 1, fill = X)
+
+        # Add update parent label
+        def updateLabel(nodePath = None, s = self):
+            s._label['text'] = 'Active Parent: ' + nodePath.getName()
+        self.accept('DIRECT_activeParent', updateLabel)
+
+        # Add update hook
+        self.accept('SGE_Update Explorer',
+                    lambda np, s = self: s.update())
+
         # Check keywords and initialise options based on input values.
         self.initialiseoptions(SceneGraphExplorer)
 
@@ -118,10 +144,10 @@ class SceneGraphExplorerItem(TreeItem):
         return sublist
 
     def OnSelect(self):
-        messenger.send('SGENodePath_Flash', [self.nodePath])
+        messenger.send('SGE_Flash', [self.nodePath])
 
     def MenuCommand(self, command):
-        messenger.send('SGENodePath_' + command, [self.nodePath])
+        messenger.send('SGE_' + command, [self.nodePath])
 
 
 def explore(nodePath = render):

+ 17 - 9
direct/src/tkwidgets/Tree.py

@@ -45,16 +45,21 @@ class TreeNode:
         self.menuVar.set(0)
         self._popupMenu = None
         if self.menuList:
+            if self.menuList[-1] == 'Separator':
+                self.menuList = self.menuList[:-1]
             self._popupMenu = Menu(self.canvas, tearoff = 0)
             for i in range(len(self.menuList)):
                 item = self.menuList[i]
-                self._popupMenu.add_radiobutton(
-                    label = item,
-                    variable = self.menuVar,
-                    value = i,
-                    indicatoron = 0,
-                    command = self.popupMenuCommand)
-
+                if item == 'Separator':
+                    self._popupMenu.add_separator()
+                else:
+                    self._popupMenu.add_radiobutton(
+                        label = item,
+                        variable = self.menuVar,
+                        value = i,
+                        indicatoron = 0,
+                        command = self.popupMenuCommand)
+                    
     def destroy(self):
         for key in self.kidKeys:
             c = self.children[key]
@@ -120,8 +125,11 @@ class TreeNode:
             return "break"
 
     def popupMenuCommand(self):
-        self.item.MenuCommand(self.menuList[self.menuVar.get()])
-        self.parent.update()
+        command = self.menuList[self.menuVar.get()]
+        self.item.MenuCommand(command)
+        if self.parent and (command != 'Update Explorer'):
+            # Update parent to try to keep explorer up to date
+            self.parent.update()
 
     def expand(self, event=None):
         if not self.item.IsExpandable():