浏览代码

Integration of Entities into the inspector/editor so they can call back into the tool scripts for custom handling.

Areloch 9 年之前
父节点
当前提交
827e70d674

+ 57 - 1
Engine/source/gui/controls/guiTreeViewCtrl.cpp

@@ -36,7 +36,7 @@
    #include "gui/editor/editorFunctions.h"
 #endif
 #include "console/engineAPI.h"
-
+#include "T3D/Entity.h"
 
 IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
 
@@ -486,6 +486,14 @@ void GuiTreeViewCtrl::Item::getDisplayText(U32 bufLen, char *buf)
 {
    FrameAllocatorMarker txtAlloc;
 
+   //if we're doing the special case of forcing the item text, just skip the rest of this junk
+   if (mState.test(ForceItemName))
+   {
+      StringTableEntry text = (mScriptInfo.mText) ? mScriptInfo.mText : StringTable->EmptyString();
+      dStrncpy(buf, text, bufLen);
+      return;
+   }
+
    if( mState.test( InspectorData ) )
    {
       SimObject *pObject = getObject();
@@ -637,6 +645,18 @@ void GuiTreeViewCtrl::Item::getTooltipText(U32 bufLen, char *buf)
 
 bool GuiTreeViewCtrl::Item::isParent() const
 {
+   //We might have a special case with entities
+   //So if our entity either has children, or has some component with the EditorInspect interface, we return true
+   if (mInspectorInfo.mObject)
+   {
+      Entity* e = dynamic_cast<Entity*>(mInspectorInfo.mObject.getObject());
+      if (e)
+      {
+         if (e->size() > 0 || e->getComponentCount() != 0)
+            return true;
+      }
+   }
+
    if(mState.test(VirtualParent))
    {
       if( !isInspectorData() )
@@ -1518,6 +1538,11 @@ bool GuiTreeViewCtrl::isValidDragTarget( Item* item )
 {
    bool isValid = true;
    
+   // First, check if we're just going to override this from manually setting the ForceAllowDrag flag
+   // If that's set, we're assuming special circumstances and will just let it go on it's way
+   if (item->isDragTargetAllowed())
+      return true;
+   
    // If this is inspector data, first make sure the item accepts all
    // selected objects as children.  This prevents bad surprises when
    // certain SimSet subclasses reject children and start shoving them
@@ -3462,6 +3487,11 @@ void GuiTreeViewCtrl::onMouseDragged(const GuiEvent &event)
    if (mSelectedItems.size() == 0)
       return;
       
+   //Check through to make sure all attempted dragged items even allow it
+   for (U32 i = 0; i < mSelectedItems.size(); i++)
+      if (!mSelectedItems[i]->isDragAllowed())
+         return;
+      
    // Give us a little delta before we actually start a mouse drag so that
    // if the user moves the mouse a little while clicking, he/she does not
    // accidentally trigger a drag.
@@ -3756,6 +3786,23 @@ void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event)
       if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
          onVirtualParentExpand(item);
       
+      //Slightly hacky, but I'm not sure of a better setup until we get major update to the editors
+      //We check if our object is an entity, and if it is, we call a 'onInspect' function.
+      //This function is pretty much a special notifier to the entity so if it has any behaviors that do special
+      //stuff in the editor, it can fire that up
+      Entity* e = dynamic_cast<Entity*>(item->getObject());
+      if (item->mScriptInfo.mText != StringTable->insert("Components"))
+      {
+         Entity* e = dynamic_cast<Entity*>(item->getObject());
+         if (e)
+         {
+            if (item->isExpanded())
+               e->onInspect();
+            else
+               e->onEndInspect();
+         }
+      }
+      
       mFlags.set( RebuildVisible );
       scrollVisible(item);
    }
@@ -4490,6 +4537,11 @@ bool GuiTreeViewCtrl::objectSearch( const SimObject *object, Item **item )
 		Item *pItem = mItems[i];
 
       if ( !pItem )
+         continue;
+
+      //A bit hackish, but we make a special exception here for items that are named 'Components', as they're merely
+      //virtual parents to act as a container to an Entity's components
+      if (pItem->mScriptInfo.mText == StringTable->insert("Components"))
          continue;
 
 		SimObject *pObj = pItem->getObject();
@@ -4555,6 +4607,10 @@ bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate)
    
    // Go through our items and purge those that have disappeared from
    // the set.
+
+   //Entities will be a special case here, if we're an entity, skip this step
+   if (dynamic_cast<Entity*>(srcObj))
+      return true;
    
    for( Item* ptr = item->mChild; ptr != NULL; )
    {

+ 12 - 1
Engine/source/gui/controls/guiTreeViewCtrl.h

@@ -75,7 +75,10 @@ class GuiTreeViewCtrl : public GuiArrayCtrl
                ShowClassName     = BIT( 11 ),
                ShowObjectName    = BIT( 12 ),
                ShowInternalName  = BIT( 13 ),
-               ShowClassNameForUnnamed = BIT( 14 )
+               ShowClassNameForUnnamed = BIT( 14 ),
+               ForceItemName = BIT(15),
+               ForceDragTarget = BIT(16),
+               DenyDrag = BIT(17),
             };
 
             GuiTreeViewCtrl* mParentControl;
@@ -169,6 +172,14 @@ class GuiTreeViewCtrl : public GuiArrayCtrl
             /// or false if it's just an item.
             bool isInspectorData() const { return mState.test(InspectorData); };
 
+            /// Returns true if we've been manually set to allow dragging overrides.
+            /// As it's a manually set flag, by default it is false.
+            bool isDragTargetAllowed() const { return mState.test(ForceDragTarget); };
+
+            /// Returns true if we've been manually set to allow dragging overrides.
+            /// As it's a manually set flag, by default it is false.
+            bool isDragAllowed() const { return !mState.test(DenyDrag); };
+
             /// Returns true if we should show the expand art
             /// and make the item interact with the mouse as if
             /// it were a parent.

+ 23 - 1
Engine/source/gui/editor/guiInspector.cpp

@@ -28,7 +28,8 @@
 #include "gui/editor/inspector/dynamicGroup.h"
 #include "gui/containers/guiScrollCtrl.h"
 #include "gui/editor/inspector/customField.h"
-
+#include "gui/editor/inspector/entityGroup.h"
+#include "gui/editor/inspector/mountingGroup.h"
 
 IMPLEMENT_CONOBJECT(GuiInspector);
 
@@ -584,6 +585,27 @@ void GuiInspector::refresh()
    mGroups.push_back(general);
    addObject(general);
 
+   //Behavior inspector group
+   if (mTargets.first()->getClassRep()->isSubclassOf("Entity"))
+   {
+      GuiInspectorEntityGroup *components = new GuiInspectorEntityGroup("Components", this);
+      if (components != NULL)
+      {
+         components->registerObject();
+         mGroups.push_back(components);
+         addObject(components);
+      }
+
+      //Mounting group override
+      GuiInspectorGroup *mounting = new GuiInspectorMountingGroup("Mounting", this);
+      if (mounting != NULL)
+      {
+         mounting->registerObject();
+         mGroups.push_back(mounting);
+         addObject(mounting);
+      }
+   }
+
    // Create the inspector groups for static fields.
 
    for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter )

+ 135 - 0
Engine/source/gui/editor/inspector/entityGroup.cpp

@@ -0,0 +1,135 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "gui/buttons/guiIconButtonCtrl.h"
+#include "gui/editor/guiInspector.h"
+#include "gui/editor/inspector/entityGroup.h"
+#include "core/strings/stringUnit.h"
+#include "T3D/Components/Component.h"
+
+#include "console/engineAPI.h"
+
+IMPLEMENT_CONOBJECT(GuiInspectorEntityGroup);
+
+ConsoleDocClass(GuiInspectorEntityGroup,
+   "@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
+   "of regular persistent fields.\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+   );
+
+bool GuiInspectorEntityGroup::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+}
+
+//-----------------------------------------------------------------------------
+// GuiInspectorEntityGroup - add custom controls
+//-----------------------------------------------------------------------------
+bool GuiInspectorEntityGroup::createContent()
+{
+   if(!Parent::createContent())
+      return false;
+
+   Con::evaluatef("%d.stack = %d;", this->getId(), mStack->getId());
+
+   Con::executef(this, "createContent");
+
+   return true;
+}
+
+//-----------------------------------------------------------------------------
+// GuiInspectorEntityGroup - inspectGroup override
+//-----------------------------------------------------------------------------
+bool GuiInspectorEntityGroup::inspectGroup()
+{
+   const U32 numTargets = mParent->getNumInspectObjects();
+   if (numTargets == 1)
+   {
+      Entity* target = dynamic_cast<Entity*>(mParent->getInspectObject(0));
+
+      Con::executef(this, "inspectObject", target->getIdString());
+   }
+
+   return true;
+}
+
+void GuiInspectorEntityGroup::updateAllFields()
+{
+   // We overload this to just reinspect the group.
+   inspectGroup();
+}
+
+void GuiInspectorEntityGroup::onMouseMove(const GuiEvent &event)
+{
+   //mParent->mOverDivider = false;
+}
+ConsoleMethod(GuiInspectorEntityGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.")
+{
+   return object->inspectGroup();
+}
+
+void GuiInspectorEntityGroup::clearFields()
+{
+}
+
+SimFieldDictionary::Entry* GuiInspectorEntityGroup::findDynamicFieldInDictionary(StringTableEntry fieldName)
+{
+   SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary();
+
+   for (SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
+   {
+      SimFieldDictionary::Entry * entry = (*ditr);
+
+      if (entry->slotName == fieldName)
+         return entry;
+   }
+
+   return NULL;
+}
+
+void GuiInspectorEntityGroup::addDynamicField()
+{
+}
+
+AbstractClassRep::Field* GuiInspectorEntityGroup::findObjectBehaviorField(Component* target, String fieldName)
+{
+   AbstractClassRep::FieldList& fieldList = target->getClassRep()->mFieldList;
+   for (AbstractClassRep::FieldList::iterator itr = fieldList.begin();
+      itr != fieldList.end(); ++itr)
+   {
+      AbstractClassRep::Field* field = &(*itr);
+      String fldNm(field->pFieldname);
+      if (fldNm == fieldName)
+         return field;
+   }
+   return NULL;
+}
+ConsoleMethod(GuiInspectorEntityGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();")
+{
+   object->addDynamicField();
+}
+
+ConsoleMethod(GuiInspectorEntityGroup, removeDynamicField, void, 3, 3, "")
+{
+}

+ 72 - 0
Engine/source/gui/editor/inspector/entityGroup.h

@@ -0,0 +1,72 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef GUI_INSPECTOR_ENTITY_GROUP_H
+#define GUI_INSPECTOR_ENTITY_GROUP_H
+
+#include "gui/editor/inspector/group.h"
+#include "console/simFieldDictionary.h"
+#include "T3D/Components/Component.h"
+#include "gui/controls/guiPopUpCtrlEx.h"
+
+class GuiInspectorEntityGroup : public GuiInspectorGroup
+{
+private:
+   typedef GuiInspectorGroup Parent;
+   GuiControl* mAddCtrl;
+
+   GuiPopUpMenuCtrlEx* mAddBhvrList;
+
+public:
+   DECLARE_CONOBJECT(GuiInspectorEntityGroup);
+   GuiInspectorEntityGroup() { /*mNeedScroll=false;*/ };
+   GuiInspectorEntityGroup(StringTableEntry groupName, SimObjectPtr<GuiInspector> parent)
+      : GuiInspectorGroup(groupName, parent) { /*mNeedScroll=false;*/
+   };
+
+   //-----------------------------------------------------------------------------
+   // inspectGroup is overridden in GuiInspectorEntityGroup to inspect an 
+   // objects FieldDictionary (dynamic fields) instead of regular persistent
+   // fields.
+   virtual bool onAdd();
+   bool inspectGroup();
+   virtual void updateAllFields();
+
+   void onMouseMove(const GuiEvent &event);
+
+   // For scriptable dynamic field additions
+   void addDynamicField();
+
+   // Clear our fields (delete them)
+   void clearFields();
+
+   // Find an already existent field by name in the dictionary
+   virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary(StringTableEntry fieldName);
+
+   AbstractClassRep::Field* findObjectBehaviorField(Component* target, String fieldName);
+protected:
+   // create our inner controls when we add
+   virtual bool createContent();
+
+};
+
+#endif

+ 507 - 0
Engine/source/gui/editor/inspector/mountingGroup.cpp

@@ -0,0 +1,507 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "gui/buttons/guiIconButtonCtrl.h"
+#include "gui/editor/guiInspector.h"
+#include "gui/editor/inspector/mountingGroup.h"
+#include "core/strings/stringUnit.h"
+#include "T3D/Entity.h"
+#include "T3D/Components/Component.h"
+
+//Need this to get node lists
+#include "T3D/Components/render/renderComponentInterface.h"
+
+IMPLEMENT_CONOBJECT(GuiInspectorMountingGroup);
+
+ConsoleDocClass( GuiInspectorMountingGroup,
+   "@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
+   "of regular persistent fields.\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+);
+
+//-----------------------------------------------------------------------------
+// GuiInspectorMountingGroup - add custom controls
+//-----------------------------------------------------------------------------
+GuiInspectorMountingGroup::GuiInspectorMountingGroup( StringTableEntry groupName, SimObjectPtr<GuiInspector> parent )
+      : GuiInspectorGroup( groupName, parent) 
+{ 
+	mParentInspector = parent;
+
+	targetMountCtrl = NULL;
+	mountCtrl = NULL;
+};
+
+bool GuiInspectorMountingGroup::createContent()
+{
+   if(!Parent::createContent())
+      return false;
+
+   //give the necessary padding for the nested controls so it looks nice.
+   setMargin(RectI(4,0,4,4));
+
+   return true;
+}
+
+GuiControl* GuiInspectorMountingGroup::buildMenuCtrl()
+{
+	GuiControl* retCtrl = new GuiPopUpMenuCtrl();
+
+   // If we couldn't construct the control, bail!
+   if( retCtrl == NULL )
+      return retCtrl;
+
+   GuiPopUpMenuCtrl *menu = dynamic_cast<GuiPopUpMenuCtrl*>(retCtrl);
+
+   // Let's make it look pretty.
+   retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiPopUpMenuProfile" );
+   //GuiInspectorTypeMenuBase::_registerEditControl( retCtrl );
+
+	char szName[512];
+   dSprintf( szName, 512, "IE_%s_%d_%s_Field", retCtrl->getClassName(), mParentInspector->getInspectObject()->getId(), mCaption);
+
+   // Register the object
+   retCtrl->registerObject( szName );
+
+   // Configure it to update our value when the popup is closed
+   char szBuffer[512];
+   dSprintf( szBuffer, 512, "%d.apply( %d.getText() );", getId(), menu->getId() );
+   menu->setField("Command", szBuffer );
+
+	return menu;
+}
+
+bool GuiInspectorMountingGroup::buildList(Entity* ent, GuiPopUpMenuCtrl* menu)
+{
+   RenderComponentInterface* renderInterface = ent->getComponent<RenderComponentInterface>();
+
+   if (renderInterface)
+	{
+      TSShape* shape = renderInterface->getShape();
+      S32 nodeCount = shape ? shape->nodes.size() : 0;
+
+		for(U32 i=0; i < nodeCount; i++)
+		{
+         menu->addEntry(shape->names[i], i);
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// GuiInspectorMountingGroup - inspectGroup override
+//-----------------------------------------------------------------------------
+bool GuiInspectorMountingGroup::inspectGroup()
+{
+   // We can't inspect a group without a target!
+   if( !mParent->getNumInspectObjects() )
+      return false;
+
+   // to prevent crazy resizing, we'll just freeze our stack for a sec..
+   mStack->freeze(true);
+
+   bool bNoGroup = false;
+
+   // Un-grouped fields are all sorted into the 'general' group
+   if ( dStricmp( mCaption, "General" ) == 0 )
+      bNoGroup = true;
+      
+   // Just delete all fields and recreate them (like the dynamicGroup)
+   // because that makes creating controls for array fields a lot easier
+   clearFields();
+   
+   bool bNewItems = false;
+   bool bMakingArray = false;
+   GuiStackControl *pArrayStack = NULL;
+   GuiRolloutCtrl *pArrayRollout = NULL;
+   bool bGrabItems = false;
+
+   AbstractClassRep* commonAncestorClass = findCommonAncestorClass();
+   AbstractClassRep::FieldList& fieldList = commonAncestorClass->mFieldList;
+   for( AbstractClassRep::FieldList::iterator itr = fieldList.begin();
+        itr != fieldList.end(); ++ itr )
+   {
+      AbstractClassRep::Field* field = &( *itr );
+      if( field->type == AbstractClassRep::StartGroupFieldType )
+      {
+         // If we're dealing with general fields, always set grabItems to true (to skip them)
+         if( bNoGroup == true )
+            bGrabItems = true;
+         else if( dStricmp( field->pGroupname, mCaption ) == 0 )
+            bGrabItems = true;
+         continue;
+      }
+      else if ( field->type == AbstractClassRep::EndGroupFieldType )
+      {
+         // If we're dealing with general fields, always set grabItems to false (to grab them)
+         if( bNoGroup == true )
+            bGrabItems = false;
+         else if( dStricmp( field->pGroupname, mCaption ) == 0 )
+            bGrabItems = false;
+         continue;
+      }
+      
+      // Skip field if it has the HideInInspectors flag set.
+      
+      if( field->flag.test( AbstractClassRep::FIELD_HideInInspectors ) )
+         continue;
+
+      if( ( bGrabItems == true || ( bNoGroup == true && bGrabItems == false ) ) && itr->type != AbstractClassRep::DeprecatedFieldType )
+      {
+         if( bNoGroup == true && bGrabItems == true )
+            continue;
+
+         // If the field already exists, just update it
+			GuiInspectorField *fieldGui = findField( field->pFieldname );
+			if ( fieldGui != NULL )
+			{
+				fieldGui->updateValue();
+				continue;
+			}
+            
+			bNewItems = true;
+            
+			if(field->pFieldname == StringTable->insert("mountNode"))
+			{
+				fieldGui = new GuiInspectorNodeListField();
+
+				Entity* e = dynamic_cast<Entity*>(mParent->getInspectObject(0));
+				if(e)
+					(dynamic_cast<GuiInspectorNodeListField*>(fieldGui))->setTargetEntity(e);
+			}
+			else
+			{
+				fieldGui = constructField( field->type );
+				if ( fieldGui == NULL )
+					fieldGui = new GuiInspectorField();
+			}
+
+			fieldGui->init( mParent, this );            
+			fieldGui->setInspectorField( field );
+                     
+			if( fieldGui->registerObject() )
+			{
+				#ifdef DEBUG_SPEW
+				Platform::outputDebugString( "[GuiInspectorGroup] Adding field '%s'",
+					field->pFieldname );
+				#endif
+
+				mChildren.push_back( fieldGui );
+				mStack->addObject( fieldGui );
+			}
+			else
+			{
+				SAFE_DELETE( fieldGui );
+			}
+      }
+   }
+   mStack->freeze(false);
+   mStack->updatePanes();
+
+   // If we've no new items, there's no need to resize anything!
+   if( bNewItems == false && !mChildren.empty() )
+      return true;
+
+   sizeToContents();
+
+   setUpdate();
+
+   return true;
+}
+
+void GuiInspectorMountingGroup::updateAllFields()
+{
+   // We overload this to just reinspect the group.
+   inspectGroup();
+}
+
+void GuiInspectorMountingGroup::onMouseMove(const GuiEvent &event)
+{
+	//mParent->mOverDivider = false;
+	bool test = false;
+}
+ConsoleMethod(GuiInspectorMountingGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.")
+{
+   return object->inspectGroup();
+}
+
+void GuiInspectorMountingGroup::clearFields()
+{
+}
+
+bool GuiInspectorMountingGroup::resize( const Point2I &newPosition, const Point2I &newExtent )
+{
+   if ( !Parent::resize( newPosition, newExtent ) )
+      return false;
+
+	//check if we're set up yet
+	if(!targetMountCtrl || !mountCtrl)
+		//no? bail
+		return false;
+
+	targetMountCtrl->setExtent(newExtent.x, 18);
+	mountCtrl->setExtent(newExtent.x, 18);
+
+	S32 dividerPos, dividerMargin;
+   mParentInspector->getDivider( dividerPos, dividerMargin );   
+
+   Point2I fieldExtent = Point2I(newExtent.x, 18);
+   Point2I fieldPos = Point2I(newExtent.x, 18);
+
+   S32 editWidth = dividerPos - dividerMargin;
+
+	targetMountText->setPosition(0,0);
+	targetMountText->setExtent(fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
+
+	targetMountNode->setPosition(fieldExtent.x - dividerPos + dividerMargin, 1);
+	targetMountNode->setExtent(editWidth, fieldExtent.y - 1);
+
+	mountText->setPosition(0,0);
+	mountText->setExtent(fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
+
+	mountNode->setPosition(fieldExtent.x - dividerPos + dividerMargin, 1);
+	mountNode->setExtent(editWidth, fieldExtent.y - 1);
+
+   return true;
+}
+
+SimFieldDictionary::Entry* GuiInspectorMountingGroup::findDynamicFieldInDictionary( StringTableEntry fieldName )
+{
+   SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary();
+
+   for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
+   {
+      SimFieldDictionary::Entry * entry = (*ditr);
+
+      if( entry->slotName == fieldName )
+         return entry;
+   }
+
+   return NULL;
+}
+
+void GuiInspectorMountingGroup::addDynamicField()
+{
+}
+
+AbstractClassRep::Field* GuiInspectorMountingGroup::findObjectComponentField(Component* target, String fieldName)
+{
+   AbstractClassRep::FieldList& fieldList = target->getClassRep()->mFieldList;
+   for( AbstractClassRep::FieldList::iterator itr = fieldList.begin();
+		itr != fieldList.end(); ++ itr )
+   {
+	  AbstractClassRep::Field* field = &( *itr );
+	  String fldNm(field->pFieldname);
+	  if(fldNm == fieldName)
+		  return field;
+   }
+   return NULL;
+}
+ConsoleMethod( GuiInspectorMountingGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();" )
+{
+   object->addDynamicField();
+}
+
+ConsoleMethod( GuiInspectorMountingGroup, removeDynamicField, void, 3, 3, "" )
+{
+}
+
+//
+IMPLEMENT_CONOBJECT( GuiInspectorNodeListField );
+
+ConsoleDocClass( GuiInspectorNodeListField,
+   "@brief A control that allows to edit the custom properties (text) of one or more SimObjects.\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+);
+
+GuiInspectorNodeListField::GuiInspectorNodeListField( GuiInspector *inspector,
+                                                    GuiInspectorGroup* parent, 
+                                                    SimFieldDictionary::Entry* field,
+																	 SimObjectPtr<Entity> target )
+{
+   mInspector = inspector;
+   mParent = parent;
+   setBounds(0,0,100,20);   
+	mTargetEntity = target;
+}
+
+GuiInspectorNodeListField::GuiInspectorNodeListField()
+{
+   mInspector = NULL;
+   mParent = NULL;
+}
+
+void GuiInspectorNodeListField::setData( const char* data, bool callbacks )
+{
+   mCustomValue = data;
+
+	//We aren't updating any mounting info if we're not mounted already
+	if(mTargetEntity.getObject())
+	{
+		Entity* target = dynamic_cast<Entity*>(mTargetEntity->getObjectMount());
+		if(target)
+		{
+         RenderComponentInterface* renderInterface = target->getComponent<RenderComponentInterface>();
+         if (renderInterface)
+			{
+            if (renderInterface->getShape())
+				{
+               S32 nodeIdx = renderInterface->getShape()->findNode(data);
+				
+					target->mountObject(mTargetEntity, nodeIdx, MatrixF::Identity);
+					mTargetEntity->setMaskBits(Entity::MountedMask);
+				}
+			}
+		}
+	}
+
+   // Force our edit to update
+   updateValue();
+}
+
+const char* GuiInspectorNodeListField::getData( U32 inspectObjectIndex )
+{
+   return mCustomValue;
+}
+
+void GuiInspectorNodeListField::updateValue()
+{
+	mMenu->clear();
+	//mMenu->addEntry("Origin");
+
+	//if(mCustomValue.isEmpty())
+	if(mTargetEntity.getObject())
+	{
+		Entity* target = dynamic_cast<Entity*>(mTargetEntity->getObjectMount());
+		if(target)
+		{
+			mMenu->addEntry("Origin");
+			mMenu->setActive(true);
+
+         RenderComponentInterface* renderInterface = target->getComponent<RenderComponentInterface>();
+
+         if (renderInterface)
+			{
+            TSShape* shape = renderInterface->getShape();
+
+            S32 nodeCount = shape ? shape->nodes.size() : 0;
+
+				for(U32 i=0; i < nodeCount; i++)
+				{
+               mMenu->addEntry(shape->names[i], i);
+				}
+
+				S32 targetNode = mTargetEntity->getMountNode();
+				if(targetNode != -1)
+				{
+               String name = shape->names[targetNode];
+					mCustomValue = name;
+				}
+				else
+				{
+					mCustomValue = String("Origin");
+				}
+
+				setValue( mCustomValue );
+				return;
+			}
+		}
+	}
+
+	setValue("Not Mounted");
+	mMenu->setActive(false);
+}
+
+void GuiInspectorNodeListField::setDoc( const char* doc )
+{
+   mDoc = StringTable->insert( doc, true );
+}
+
+void GuiInspectorNodeListField::setToolTip( StringTableEntry data )
+{
+   static StringTableEntry sTooltipProfile = StringTable->insert( "tooltipProfile" );
+   static StringTableEntry sHoverTime = StringTable->insert( "hovertime" );
+   static StringTableEntry sTooltip = StringTable->insert( "tooltip" );
+   
+   mEdit->setDataField( sTooltipProfile, NULL, "GuiToolTipProfile" );
+   mEdit->setDataField( sHoverTime, NULL, "1000" );
+   mEdit->setDataField( sTooltip, NULL, data );
+}
+
+bool GuiInspectorNodeListField::onAdd()
+{
+   if( !Parent::onAdd() )
+      return false;
+
+   return true;
+}
+
+void GuiInspectorNodeListField::setInspectorField( AbstractClassRep::Field *field, 
+                                                  StringTableEntry caption, 
+                                                  const char*arrayIndex ) 
+{
+   // Override the base just to be sure it doesn't get called.
+   // We don't use an AbstractClassRep::Field...
+
+	mField = field;
+	mCaption = field->pFieldname;
+	mDoc = field->pFieldDocs;
+}
+
+GuiControl* GuiInspectorNodeListField::constructEditControl()
+{
+   GuiControl* retCtrl = new GuiPopUpMenuCtrl();
+
+	mMenu = dynamic_cast<GuiPopUpMenuCtrl*>(retCtrl);
+
+   static StringTableEntry sProfile = StringTable->insert( "profile" );
+   retCtrl->setDataField( sProfile, NULL, "ToolsGuiPopUpMenuEditProfile" );
+
+   // Register the object
+   retCtrl->registerObject();
+
+	char szBuffer[512];
+	dSprintf( szBuffer, 512, "%d.apply( %d.getText() );", getId(), mMenu->getId() );
+	mMenu->setField("Command", szBuffer );
+
+   return retCtrl;
+}
+
+void GuiInspectorNodeListField::setValue( const char* newValue )
+{
+   GuiPopUpMenuCtrl *ctrl = dynamic_cast<GuiPopUpMenuCtrl*>( mEdit );
+   if( ctrl != NULL )
+      ctrl->setText( newValue );
+}
+
+void GuiInspectorNodeListField::_executeSelectedCallback()
+{
+}
+
+void GuiInspectorNodeListField::setTargetEntity(SimObjectPtr<Entity> target)
+{
+	mTargetEntity = target;
+}

+ 152 - 0
Engine/source/gui/editor/inspector/mountingGroup.h

@@ -0,0 +1,152 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef GUI_INSPECTOR_MOUNTINGGROUP_H
+#define GUI_INSPECTOR_MOUNTINGGROUP_H
+
+#include "gui/editor/inspector/group.h"
+#include "console/simFieldDictionary.h"
+#include "T3D/Components/Component.h"
+#include "gui/controls/guiPopUpCtrlEx.h"
+
+#ifndef _GUI_INSPECTOR_TYPES_H_
+#include "gui/editor/guiInspectorTypes.h"
+#endif
+
+#ifndef _ENTITY_H_
+#include "T3D/Entity.h"
+#endif
+
+class GuiInspectorMountingGroup;
+
+class GuiInspectorNodeListField : public GuiInspectorField
+{
+   typedef GuiInspectorField Parent;
+	friend class GuiInspectorMountingGroup;
+
+public:
+
+   GuiInspectorNodeListField( GuiInspector *inspector, GuiInspectorGroup* parent, SimFieldDictionary::Entry* field,
+																	 SimObjectPtr<Entity> target  );
+   GuiInspectorNodeListField();
+   ~GuiInspectorNodeListField() {};
+
+   DECLARE_CONOBJECT( GuiInspectorNodeListField );
+
+   virtual void             setData( const char* data, bool callbacks = true );
+   virtual const char*      getData( U32 inspectObjectIndex = 0 );
+   virtual void             updateValue();
+   virtual StringTableEntry getFieldName() { return StringTable->EmptyString(); }
+
+   virtual void setDoc( const char* doc );
+   virtual void setToolTip( StringTableEntry data );
+
+   virtual bool onAdd();
+
+   virtual void setInspectorField( AbstractClassRep::Field *field, 
+                                   StringTableEntry caption = NULL,
+                                   const char *arrayIndex = NULL );
+   
+   virtual GuiControl* constructEditControl();
+
+   virtual void setValue( const char* newValue );
+
+	void setTargetEntity(SimObjectPtr<Entity> target);
+
+protected:
+
+   virtual void _executeSelectedCallback();
+
+protected:
+
+   String mCustomValue;
+   StringTableEntry mDoc;
+
+	GuiPopUpMenuCtrl	*mMenu;
+
+	SimObjectPtr<Entity> mTargetEntity;
+};
+
+class GuiInspectorMountingGroup : public GuiInspectorGroup
+{
+private:
+   typedef GuiInspectorGroup Parent;
+   GuiControl* mAddCtrl;
+
+   GuiPopUpMenuCtrlEx* mAddBhvrList;
+
+	GuiTextCtrl		*persistText;
+	GuiButtonCtrl *reloadFile;
+	GuiButtonCtrl *saveFile;
+	GuiButtonCtrl *overwriteFile;
+	GuiButtonCtrl *mBrowseButton;
+	GuiControl    *filePath;
+
+	GuiControl			*targetMountCtrl;
+	GuiTextCtrl			*targetMountText;
+	GuiPopUpMenuCtrl	*targetMountNode;
+
+	GuiControl			*mountCtrl;
+	GuiTextCtrl			*mountText;
+	GuiPopUpMenuCtrl	*mountNode;
+
+	GuiInspectorNodeListField* mountNodeList;
+	GuiInspectorNodeListField* targetMountNodeList;
+
+	SimObjectPtr<GuiInspector>   mParentInspector;
+
+public:
+   DECLARE_CONOBJECT(GuiInspectorMountingGroup);
+   GuiInspectorMountingGroup() { /*mNeedScroll=false;*/ };
+   GuiInspectorMountingGroup( StringTableEntry groupName, SimObjectPtr<GuiInspector> parent );
+
+   //-----------------------------------------------------------------------------
+   // inspectGroup is overridden in GuiInspectorMountingGroup to inspect an 
+   // objects FieldDictionary (dynamic fields) instead of regular persistent
+   // fields.
+   bool inspectGroup();
+   virtual void updateAllFields();
+
+   void onMouseMove(const GuiEvent &event);
+
+   // For scriptable dynamic field additions
+   void addDynamicField();
+
+   // Clear our fields (delete them)
+   void clearFields();
+
+	virtual bool resize( const Point2I &newPosition, const Point2I &newExtent );
+
+   // Find an already existent field by name in the dictionary
+   virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary( StringTableEntry fieldName );
+
+   AbstractClassRep::Field* findObjectComponentField(Component* target, String fieldName);
+protected:
+   // create our inner controls when we add
+   virtual bool createContent();
+
+	GuiControl* buildMenuCtrl();
+
+	bool buildList(Entity* ent, GuiPopUpMenuCtrl* menu);
+};
+
+#endif

+ 2 - 2
Engine/source/gui/worldEditor/editor.cpp

@@ -122,9 +122,9 @@ void EditManager::editorDisabled()
 static GameBase * getControlObj()
 {
    GameConnection * connection = GameConnection::getLocalClientConnection();
-   ShapeBase* control = 0;
+   GameBase* control = 0;
    if(connection)
-      control = dynamic_cast<ShapeBase*>(connection->getControlObject());
+      control = connection->getControlObject();
    return(control);
 }