| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2013 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/editor/guiInspector.h"
- #include "gui/buttons/guiIconButtonCtrl.h"
- #include "memory/frameAllocator.h"
- //////////////////////////////////////////////////////////////////////////
- // GuiInspector
- //////////////////////////////////////////////////////////////////////////
- // The GuiInspector Control houses the body of the inspector.
- // It is not exposed as a conobject because it merely does the grunt work
- // and is only meant to be used when housed by a scroll control. Therefore
- // the GuiInspector control is a scroll control that creates it's own
- // content. That content being of course, the GuiInspector control.
- IMPLEMENT_CONOBJECT(GuiInspector);
- GuiInspector::GuiInspector()
- {
- mGroups.clear();
- mTarget = NULL;
- mPadding = 1;
- }
- GuiInspector::~GuiInspector()
- {
- clearGroups();
- }
- bool GuiInspector::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
- return true;
- }
- //////////////////////////////////////////////////////////////////////////
- // Handle Parent Sizing (We constrain ourself to our parents width)
- //////////////////////////////////////////////////////////////////////////
- void GuiInspector::parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent)
- {
- GuiControl *parent = getParent();
- if( parent && dynamic_cast<GuiScrollCtrl*>(parent) != NULL )
- {
- GuiScrollCtrl *scroll = dynamic_cast<GuiScrollCtrl*>(parent);
- setWidth( ( newParentExtent.x - ( scroll->scrollBarThickness() + 4 ) ) );
- }
- else
- Parent::parentResized(oldParentExtent,newParentExtent);
- }
- bool GuiInspector::findExistentGroup( StringTableEntry groupName )
- {
- // If we have no groups, it couldn't possibly exist
- if( mGroups.empty() )
- return false;
- // Attempt to find it in the group list
- Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
- for( ; i != mGroups.end(); i++ )
- {
- if( dStricmp( (*i)->getGroupName(), groupName ) == 0 )
- return true;
- }
- return false;
- }
- void GuiInspector::clearGroups()
- {
- // If we're clearing the groups, we want to clear our target too.
- mTarget = NULL;
- // If we have no groups, there's nothing to clear!
- if( mGroups.empty() )
- return;
- // Attempt to find it in the group list
- Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
- for( ; i != mGroups.end(); i++ )
- if( (*i)->isProperlyAdded() )
- (*i)->deleteObject();
- mGroups.clear();
- }
- void GuiInspector::inspectObject( SimObject *object )
- {
- GuiCanvas *guiCanvas = getRoot();
- if( !guiCanvas )
- return;
- SimObjectPtr<GuiControl> currResponder = guiCanvas->getFirstResponder();
- // If our target is the same as our current target, just update the groups.
- if( mTarget == object )
- {
- Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
- for ( ; i != mGroups.end(); i++ )
- (*i)->inspectGroup();
- // Don't steal first responder
- if( !currResponder.isNull() )
- guiCanvas->setFirstResponder( currResponder );
- return;
- }
- // Clear our current groups
- clearGroups();
- // Set Target
- mTarget = object;
- // Always create the 'general' group (for un-grouped fields)
- GuiInspectorGroup* general = new GuiInspectorGroup( mTarget, "General", this );
- if( general != NULL )
- {
- general->registerObject();
- mGroups.push_back( general );
- addObject( general );
- }
- // Grab this objects field list
- AbstractClassRep::FieldList &fieldList = mTarget->getModifiableFieldList();
- AbstractClassRep::FieldList::iterator itr;
- // Iterate through, identifying the groups and create necessary GuiInspectorGroups
- for(itr = fieldList.begin(); itr != fieldList.end(); itr++)
- {
- if(itr->type == AbstractClassRep::StartGroupFieldType && !findExistentGroup( itr->pGroupname ) )
- {
- GuiInspectorGroup *group = new GuiInspectorGroup( mTarget, itr->pGroupname, this );
- if( group != NULL )
- {
- group->registerObject();
- mGroups.push_back( group );
- addObject( group );
- }
- }
- }
- // Deal with dynamic fields
- GuiInspectorGroup *dynGroup = new GuiInspectorDynamicGroup( mTarget, "Dynamic Fields", this);
- if( dynGroup != NULL )
- {
- dynGroup->registerObject();
- mGroups.push_back( dynGroup );
- addObject( dynGroup );
- }
- // If the general group is still empty at this point, kill it.
- for(S32 i=0; i<mGroups.size(); i++)
- {
- if(mGroups[i] == general && general->mStack->size() == 0)
- {
- mGroups.erase(i);
- general->deleteObject();
- updatePanes();
- }
- }
- // Don't steal first responder
- if( !currResponder.isNull() )
- guiCanvas->setFirstResponder( currResponder );
- }
- ConsoleMethod( GuiInspector, inspect, void, 3, 3, "(obj) Goes through the object's fields and autogenerates editor boxes\n"
- "@return No return value.")
- {
- SimObject * target = Sim::findObject(argv[2]);
- if(!target)
- {
- if(dAtoi(argv[2]) > 0)
- Con::warnf("%s::inspect(): invalid object: %s", argv[0], argv[2]);
-
- object->clearGroups();
- return;
- }
- object->inspectObject(target);
- }
- ConsoleMethod( GuiInspector, getInspectObject, const char*, 2, 2, "() - Returns currently inspected object\n"
- "@return The Object's ID as a string.")
- {
- SimObject *pSimObject = object->getInspectObject();
- if( pSimObject != NULL )
- return pSimObject->getIdString();
-
- return "";
- }
- void GuiInspector::setName( const char* newName )
- {
- if( mTarget == NULL )
- return;
- // Only assign a new name if we provide one
- mTarget->assignName(newName);
- }
- ConsoleMethod( GuiInspector, setName, void, 3, 3, "(NewObjectName) Set object name.\n"
- "@return No return value.")
- {
- object->setName(argv[2]);
- }
- //////////////////////////////////////////////////////////////////////////
- // GuiInspectorField
- //////////////////////////////////////////////////////////////////////////
- // The GuiInspectorField control is a representation of a single abstract
- // field for a given ConsoleObject derived object. It handles creation
- // getting and setting of it's fields data and editing control.
- //
- // Creation of custom edit controls is done through this class and is
- // dependent upon the dynamic console type, which may be defined to be
- // custom for different types.
- //
- // Note : GuiInspectorField controls must have a GuiInspectorGroup as their
- // parent.
- IMPLEMENT_CONOBJECT(GuiInspectorField);
- // Caption width is in percentage of total width
- S32 GuiInspectorField::smCaptionWidth = 50;
- GuiInspectorField::GuiInspectorField( GuiInspectorGroup* parent, SimObjectPtr<SimObject> target, AbstractClassRep::Field* field )
- {
- if( field != NULL )
- mCaption = StringTable->insert( field->pFieldname );
- else
- mCaption = StringTable->EmptyString;
- mParent = parent;
- mTarget = target;
- mField = field;
- mCanSave = false;
- mFieldArrayIndex = NULL;
- mBounds.set(0,0,100,18);
- }
- GuiInspectorField::GuiInspectorField()
- {
- mCaption = StringTable->EmptyString;
- mParent = NULL;
- mTarget = NULL;
- mField = NULL;
- mFieldArrayIndex = NULL;
- mBounds.set(0,0,100,18);
- mCanSave = false;
- }
- GuiInspectorField::~GuiInspectorField()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- // Get/Set Data Functions
- //////////////////////////////////////////////////////////////////////////
- void GuiInspectorField::setData( const char* data )
- {
- if( mField == NULL || mTarget == NULL )
- return;
- mTarget->inspectPreApply();
- mTarget->setDataField( mField->pFieldname, mFieldArrayIndex, data );
- // Force our edit to update
- updateValue( data );
- mTarget->inspectPostApply();
- }
- const char* GuiInspectorField::getData()
- {
- if( mField == NULL || mTarget == NULL )
- return "";
- return mTarget->getDataField( mField->pFieldname, mFieldArrayIndex );
- }
- void GuiInspectorField::setInspectorField( AbstractClassRep::Field *field, const char*arrayIndex )
- {
- mField = field;
- if( arrayIndex != NULL )
- {
- mFieldArrayIndex = StringTable->insert( arrayIndex );
- S32 frameTempSize = dStrlen( field->pFieldname ) + 32;
- FrameTemp<char> valCopy( frameTempSize );
- dSprintf( (char *)valCopy, frameTempSize, "%s%s", field->pFieldname, arrayIndex );
- mCaption = StringTable->insert( valCopy );
- }
- else
- mCaption = StringTable->insert( field->pFieldname );
- }
- StringTableEntry GuiInspectorField::getFieldName()
- {
- // Sanity
- if ( mField == NULL )
- return StringTable->EmptyString;
- // Array element?
- if( mFieldArrayIndex != NULL )
- {
- S32 frameTempSize = dStrlen( mField->pFieldname ) + 32;
- FrameTemp<char> valCopy( frameTempSize );
- dSprintf( (char *)valCopy, frameTempSize, "%s%s", mField->pFieldname, mFieldArrayIndex );
- // Return formatted element
- return StringTable->insert( valCopy );
- }
- // Plain ole field name.
- return mField->pFieldname;
- };
- //////////////////////////////////////////////////////////////////////////
- // Overrideables for custom edit fields
- //////////////////////////////////////////////////////////////////////////
- GuiControl* GuiInspectorField::constructEditControl()
- {
- GuiControl* retCtrl = new GuiTextEditCtrl();
-
- // If we couldn't construct the control, bail!
- if( retCtrl == NULL )
- return retCtrl;
- // Let's make it look pretty.
- retCtrl->setField( "profile", "GuiInspectorTextEditProfile" );
- // Don't forget to register ourselves
- registerEditControl( retCtrl );
- char szBuffer[512];
- dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() );
- retCtrl->setField("AltCommand", szBuffer );
- retCtrl->setField("Validate", szBuffer );
- return retCtrl;
- }
- void GuiInspectorField::registerEditControl( GuiControl *ctrl )
- {
- if(!mTarget)
- return;
-
- char szName[512];
- dSprintf( szName, 512, "IE_%s_%d_%s_Field", ctrl->getClassName(), mTarget->getId(),mCaption);
- // Register the object
- ctrl->registerObject( szName );
- }
- void GuiInspectorField::onRender(Point2I offset, const RectI &updateRect)
- {
- if(mCaption && mCaption[0])
- {
- // Calculate Caption Rect
- RectI captionRect( offset , Point2I((S32) mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0f ) ), (S32)mBounds.extent.y ) );
- // Calculate Y Offset to center vertically the caption
- U32 captionYOffset = (U32)mFloor( (F32)( captionRect.extent.y - mProfile->mFont->getHeight() ) / 2 );
- RectI clipRect = dglGetClipRect();
- if( clipRect.intersect( captionRect ) )
- {
- // Backup Bitmap Modulation
- ColorI currColor;
- dglGetBitmapModulation( &currColor );
- dglSetBitmapModulation( mProfile->mFontColor );
- dglSetClipRect( RectI( clipRect.point, Point2I( captionRect.extent.x, clipRect.extent.y ) ));
- // Draw Caption ( Vertically Centered )
- U32 textY = captionRect.point.y + captionYOffset;
- U32 textX = captionRect.point.x + captionRect.extent.x - mProfile->mFont->getStrWidth(mCaption) - 6;
- Point2I textPT(textX, textY);
- dglDrawText( mProfile->mFont, textPT, mCaption, &mProfile->mFontColor );
- dglSetBitmapModulation( currColor );
- dglSetClipRect( clipRect );
- }
- }
- Parent::onRender( offset, updateRect );
- }
- bool GuiInspectorField::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
- if( !mTarget )
- return false;
- mEdit = constructEditControl();
- if( mEdit == NULL )
- return false;
- // Add our edit as a child
- addObject( mEdit );
- // Calculate Caption Rect
- RectI captionRect( mBounds.point , Point2I( (S32)mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0 ) ), (S32)mBounds.extent.y ) );
- // Calculate Edit Field Rect
- RectI editFieldRect( Point2I( captionRect.extent.x + 1, 1 ) , Point2I( mBounds.extent.x - ( captionRect.extent.x + 5 ) , mBounds.extent.y - 1) );
- // Resize to fit properly in allotted space
- mEdit->resize( editFieldRect.point, editFieldRect.extent );
- // Prefer GuiInspectorFieldProfile
- setField( "profile", "GuiInspectorFieldProfile" );
- // Force our editField to set it's value
- updateValue( getData() );
- return true;
- }
- void GuiInspectorField::updateValue( const char* newValue )
- {
- GuiTextEditCtrl *ctrl = dynamic_cast<GuiTextEditCtrl*>( mEdit );
- if( ctrl != NULL )
- ctrl->setText( newValue );
- }
- ConsoleMethod( GuiInspectorField, apply, void, 3,3, "(newValue) Applies the given value to the field\n"
- "@return No return value." )
- {
- object->setData( argv[2] );
- }
- void GuiInspectorField::resize( const Point2I &newPosition, const Point2I &newExtent )
- {
- Parent::resize( newPosition, newExtent );
- if( mEdit != NULL )
- {
- // Calculate Caption Rect
- RectI captionRect( mBounds.point , Point2I( (S32)mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0f ) ), (S32)mBounds.extent.y ) );
- // Calculate Edit Field Rect
- RectI editFieldRect( Point2I( captionRect.extent.x + 1, 1 ) , Point2I( mBounds.extent.x - ( captionRect.extent.x + 5 ) , mBounds.extent.y - 1) );
- mEdit->resize( editFieldRect.point, editFieldRect.extent );
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // GuiInspectorGroup
- //////////////////////////////////////////////////////////////////////////
- //
- // The GuiInspectorGroup control is a helper control that the inspector
- // makes use of which houses a collapsible pane type control for separating
- // inspected objects fields into groups. The content of the inspector is
- // made up of zero or more GuiInspectorGroup controls inside of a GuiStackControl
- //
- //
- //
- IMPLEMENT_CONOBJECT(GuiInspectorGroup);
- GuiInspectorGroup::GuiInspectorGroup()
- {
- mBounds.set(0,0,200,20);
- mChildren.clear();
- mTarget = NULL;
- mParent = NULL;
- mCanSave = false;
- // Make sure we receive our ticks.
- setProcessTicks();
- }
- GuiInspectorGroup::GuiInspectorGroup( SimObjectPtr<SimObject> target, StringTableEntry groupName, SimObjectPtr<GuiInspector> parent )
- {
- mBounds.set(0,0,200,20);
- mChildren.clear();
- mCaption = StringTable->insert(groupName);
- mTarget = target;
- mParent = parent;
- mCanSave = false;
- }
- GuiInspectorGroup::~GuiInspectorGroup()
- {
- if( !mChildren.empty() )
- {
- Vector<GuiInspectorField*>::iterator i = mChildren.begin();
- for( ; i != mChildren.end(); i++ );
-
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Scene Events
- //////////////////////////////////////////////////////////////////////////
- bool GuiInspectorGroup::onAdd()
- {
- setField( "profile", "GuiInspectorGroupProfile" );
- if( !Parent::onAdd() )
- return false;
- // Create our inner controls. Allow subclasses to provide other content.
- if(!createContent())
- return false;
- inspectGroup();
- return true;
- }
- bool GuiInspectorGroup::createContent()
- {
- // Create our field stack control
- mStack = new GuiStackControl();
- if( !mStack )
- return false;
- // Prefer GuiTransperantProfile for the stack.
- mStack->setField( "profile", "GuiTransparentProfile" );
- mStack->registerObject();
- addObject( mStack );
- mStack->setField( "padding", "0" );
- return true;
- }
- //////////////////////////////////////////////////////////////////////////
- // Control Sizing Animation Functions
- //////////////////////////////////////////////////////////////////////////
- void GuiInspectorGroup::animateToContents()
- {
- calculateHeights();
- if(size() > 0)
- animateTo( mExpanded.extent.y );
- else
- animateTo( mHeader.extent.y );
- }
- GuiInspectorField* GuiInspectorGroup::constructField( S32 fieldType )
- {
- ConsoleBaseType *cbt = ConsoleBaseType::getType(fieldType);
- AssertFatal(cbt, "GuiInspectorGroup::constructField - could not resolve field type!");
- // Alright, is it a datablock?
- if(cbt->isDatablock())
- {
- // This is fairly straightforward to deal with.
- GuiInspectorDatablockField *dbFieldClass = new GuiInspectorDatablockField( cbt->getTypeClassName() );
- if( dbFieldClass != NULL )
- {
- // return our new datablock field with correct datablock type enumeration info
- return dbFieldClass;
- }
- }
- // Nope, not a datablock. So maybe it has a valid inspector field override we can use?
- if(!cbt->getInspectorFieldType())
- // Nothing, so bail.
- return NULL;
- // Otherwise try to make it!
- ConsoleObject *co = create(cbt->getInspectorFieldType());
- GuiInspectorField *gif = dynamic_cast<GuiInspectorField*>(co);
- if(!gif)
- {
- // Wasn't appropriate type, bail.
- delete co;
- return NULL;
- }
- return gif;
- }
- GuiInspectorField *GuiInspectorGroup::findField( StringTableEntry fieldName )
- {
- // If we don't have any field children we can't very well find one then can we?
- if( mChildren.empty() )
- return NULL;
- Vector<GuiInspectorField*>::iterator i = mChildren.begin();
- for( ; i != mChildren.end(); i++ )
- {
- if( (*i)->getFieldName() != NULL && dStricmp( (*i)->getFieldName(), fieldName ) == 0 )
- return (*i);
- }
- return NULL;
- }
- bool GuiInspectorGroup::inspectGroup()
- {
- // We can't inspect a group without a target!
- if( !mTarget )
- 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;
- AbstractClassRep::FieldList &fieldList = mTarget->getModifiableFieldList();
- AbstractClassRep::FieldList::iterator itr;
- bool bGrabItems = false;
- bool bNewItems = false;
- for(itr = fieldList.begin(); itr != fieldList.end(); itr++)
- {
- if( itr->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( itr->pGroupname != NULL && dStricmp( itr->pGroupname, mCaption ) == 0 )
- bGrabItems = true;
- continue;
- }
- else if ( itr->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( itr->pGroupname != NULL && dStricmp( itr->pGroupname, mCaption ) == 0 )
- bGrabItems = false;
- continue;
- }
- if( ( bGrabItems == true || ( bNoGroup == true && bGrabItems == false ) ) && itr->type != AbstractClassRep::DepricatedFieldType )
- {
- if( bNoGroup == true && bGrabItems == true )
- continue;
- // This is weird, but it should work for now. - JDD
- // We are going to check to see if this item is an array
- // if so, we're going to construct a field for each array element
- if( itr->elementCount > 1 )
- {
- for(S32 nI = 0; nI < itr->elementCount; nI++)
- {
- FrameTemp<char> intToStr( 64 );
- dSprintf( intToStr, 64, "%d", nI );
- const char *val = mTarget->getDataField( itr->pFieldname, intToStr );
- if (!val)
- val = StringTable->EmptyString;
- // Copy Val and construct proper ValueName[nI] format
- // which is "ValueName0" for index 0, etc.
- S32 frameTempSize = dStrlen( val ) + 32;
- FrameTemp<char> valCopy( frameTempSize );
- dSprintf( (char *)valCopy, frameTempSize, "%s%d", itr->pFieldname, nI );
- // If the field already exists, just update it
- GuiInspectorField *field = findField( valCopy );
- if( field != NULL )
- {
- field->updateValue( field->getData() );
- continue;
- }
- bNewItems = true;
- field = constructField( itr->type );
- if( field == NULL )
- {
- field = new GuiInspectorField( this, mTarget, itr );
- field->setInspectorField( itr, intToStr );
- }
- else
- {
- field->setTarget( mTarget );
- field->setParent( this );
- field->setInspectorField( itr, intToStr );
- }
- field->registerObject();
- mChildren.push_back( field );
- mStack->addObject( field );
- }
- }
- else
- {
- // If the field already exists, just update it
- GuiInspectorField *field = findField( itr->pFieldname );
- if( field != NULL )
- {
- field->updateValue( field->getData() );
- continue;
- }
- bNewItems = true;
- field = constructField( itr->type );
- if( field == NULL )
- field = new GuiInspectorField( this, mTarget, itr );
- else
- {
- field->setTarget( mTarget );
- field->setParent( this );
- field->setInspectorField( itr );
- }
- field->registerObject();
- mChildren.push_back( field );
- mStack->addObject( field );
- }
- }
- }
- 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;
- }
- IMPLEMENT_CONOBJECT(GuiInspectorDynamicGroup);
- //////////////////////////////////////////////////////////////////////////
- // GuiInspectorDynamicGroup - add custom controls
- //////////////////////////////////////////////////////////////////////////
- bool GuiInspectorDynamicGroup::createContent()
- {
- if(!Parent::createContent())
- return false;
- // add a button that lets us add new dynamic fields.
- GuiIconButtonCtrl* addFieldBtn = new GuiIconButtonCtrl();
- {
- addFieldBtn->setBitmap("tools/gui/images/iconAdd");
- SimObject* profilePtr = Sim::findObject("EditorButton");
- if( profilePtr != NULL )
- addFieldBtn->setControlProfile( dynamic_cast<GuiControlProfile*>(profilePtr) );
- char commandBuf[64];
- dSprintf(commandBuf, 64, "%d.addDynamicField();", this->getId());
- addFieldBtn->setField("command", commandBuf);
- addFieldBtn->setSizing(horizResizeLeft,vertResizeCenter);
- //addFieldBtn->setField("buttonMargin", "2 2");
- addFieldBtn->resize(Point2I(mBounds.extent.x - 20,2), Point2I(16, 16));
- addFieldBtn->registerObject("zAddButton");
- }
- // encapsulate the button in a dummy control.
- GuiControl* shell = new GuiControl();
- shell->setField( "profile", "GuiTransparentProfile" );
- shell->registerObject();
- shell->resize(Point2I(0,0), Point2I(mBounds.extent.x, 28));
- shell->addObject(addFieldBtn);
- // save off the shell control, so we can push it to the bottom of the stack in inspectGroup()
- mAddCtrl = shell;
- mStack->addObject(shell);
- return true;
- }
- static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b)
- {
- SimFieldDictionary::Entry *fa = *((SimFieldDictionary::Entry **)a);
- SimFieldDictionary::Entry *fb = *((SimFieldDictionary::Entry **)b);
- return dStricmp(fa->slotName, fb->slotName);
- }
- //////////////////////////////////////////////////////////////////////////
- // GuiInspectorDynamicGroup - inspectGroup override
- //////////////////////////////////////////////////////////////////////////
- bool GuiInspectorDynamicGroup::inspectGroup()
- {
- // We can't inspect a group without a target!
- if( !mTarget )
- return false;
- // Clearing the fields and recreating them will more than likely be more
- // efficient than looking up existent fields, updating them, and then iterating
- // over existent fields and making sure they still exist, if not, deleting them.
- clearFields();
-
- // Create a vector of the fields
- Vector<SimFieldDictionary::Entry *> flist;
- // Then populate with fields
- SimFieldDictionary * fieldDictionary = mTarget->getFieldDictionary();
- for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
- {
- flist.push_back(*ditr);
- }
- dQsort(flist.address(),flist.size(),sizeof(SimFieldDictionary::Entry *),compareEntries);
-
- for(U32 i = 0; i < (U32)flist.size(); i++)
- {
- SimFieldDictionary::Entry * entry = flist[i];
- GuiInspectorField *field = new GuiInspectorDynamicField( this, mTarget, entry );
- if( field != NULL )
- {
- field->registerObject();
- mChildren.push_back( field );
- mStack->addObject( field );
- }
- }
-
- mStack->pushObjectToBack(mAddCtrl);
- setUpdate();
- return true;
- }
- ConsoleMethod(GuiInspectorDynamicGroup, inspectGroup, bool, 2, 2, "() Refreshes the dynamic fields in the inspector.\n"
- "@return Returns true on success.")
- {
- return object->inspectGroup();
- }
- void GuiInspectorDynamicGroup::clearFields()
- {
- // save mAddCtrl
- Sim::getGuiGroup()->addObject(mAddCtrl);
- // delete everything else
- mStack->clear();
- // clear the mChildren list.
- mChildren.clear();
- // and restore.
- mStack->addObject(mAddCtrl);
- }
- SimFieldDictionary::Entry* GuiInspectorDynamicGroup::findDynamicFieldInDictionary( StringTableEntry fieldName )
- {
- if( !mTarget )
- return NULL;
- SimFieldDictionary * fieldDictionary = mTarget->getFieldDictionary();
- for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
- {
- SimFieldDictionary::Entry * entry = (*ditr);
-
- if( dStricmp( entry->slotName, fieldName ) == 0 )
- return entry;
- }
- return NULL;
- }
- void GuiInspectorDynamicGroup::addDynamicField()
- {
- // We can't add a field without a target
- if( !mTarget || !mStack )
- {
- Con::warnf("GuiInspectorDynamicGroup::addDynamicField - no target SimObject to add a dynamic field to.");
- return;
- }
- // find a field name that is not in use.
- // But we wont try more than 100 times to find an available field.
- U32 uid = 1;
- char buf[64] = "dynamicField";
- SimFieldDictionary::Entry* entry = findDynamicFieldInDictionary(buf);
- while(entry != NULL && uid < 100)
- {
- dSprintf(buf, sizeof(buf), "dynamicField%03d", uid++);
- entry = findDynamicFieldInDictionary(buf);
- }
- //Con::evaluatef( "%d.%s = \"defaultValue\";", mTarget->getId(), buf );
- mTarget->setDataField(StringTable->insert(buf), NULL, "defaultValue");
- // now we simply re-inspect the object, to see the new field.
- this->inspectGroup();
- animateToContents();
- }
- ConsoleMethod( GuiInspectorDynamicGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();" )
- {
- object->addDynamicField();
- }
- //////////////////////////////////////////////////////////////////////////
- // GuiInspectorDynamicField - Child class of GuiInspectorField
- //////////////////////////////////////////////////////////////////////////
- IMPLEMENT_CONOBJECT(GuiInspectorDynamicField);
- GuiInspectorDynamicField::GuiInspectorDynamicField( GuiInspectorGroup* parent, SimObjectPtr<SimObject> target, SimFieldDictionary::Entry* field )
- {
- mCaption = NULL;
- mParent = parent;
- mTarget = target;
- mDynField = field;
- mBounds.set(0,0,100,20);
- mRenameCtrl = NULL;
- }
- void GuiInspectorDynamicField::setData( const char* data )
- {
- if( mTarget == NULL || mDynField == NULL )
- return;
- char buf[1024];
- const char * newValue = mEdit->getScriptValue();
- dStrcpy( buf, newValue ? newValue : "" );
- collapseEscape(buf);
- mTarget->getFieldDictionary()->setFieldValue(mDynField->slotName, buf);
- // Force our edit to update
- updateValue( data );
- }
- const char* GuiInspectorDynamicField::getData()
- {
- if( mTarget == NULL || mDynField == NULL )
- return "";
- return mTarget->getFieldDictionary()->getFieldValue( mDynField->slotName );
- }
- void GuiInspectorDynamicField::renameField( StringTableEntry newFieldName )
- {
- if( mTarget == NULL || mDynField == NULL || mParent == NULL || mEdit == NULL )
- {
- Con::warnf("GuiInspectorDynamicField::renameField - No target object or dynamic field data found!" );
- return;
- }
- if( !newFieldName )
- {
- Con::warnf("GuiInspectorDynamicField::renameField - Invalid field name specified!" );
- return;
- }
- // Only proceed if the name has changed
- if( dStricmp( newFieldName, getFieldName() ) == 0 )
- return;
- // Grab a pointer to our parent and cast it to GuiInspectorDynamicGroup
- GuiInspectorDynamicGroup *group = dynamic_cast<GuiInspectorDynamicGroup*>(mParent);
- if( group == NULL )
- {
- Con::warnf("GuiInspectorDynamicField::renameField - Unable to locate GuiInspectorDynamicGroup parent!" );
- return;
- }
- // Grab our current dynamic field value
- const char* currentValue = getData();
- // Create our new field with the value of our old field and the new fields name!
- mTarget->setDataField( newFieldName, NULL, currentValue );
- // Configure our field to grab data from the new dynamic field
- SimFieldDictionary::Entry *newEntry = group->findDynamicFieldInDictionary( newFieldName );
- if( newEntry == NULL )
- {
- Con::warnf("GuiInspectorDynamicField::renameField - Unable to find new field!" );
- return;
- }
- // Set our old fields data to "" (which will effectively erase the field)
- mTarget->setDataField( getFieldName(), NULL, "" );
-
- // Assign our dynamic field pointer (where we retrieve field information from) to our new field pointer
- mDynField = newEntry;
- // Lastly we need to reassign our Command and AltCommand fields for our value edit control
- char szBuffer[512];
- dSprintf( szBuffer, 512, "%d.%s = %d.getText();",mTarget->getId(), getFieldName(), mEdit->getId() );
- mEdit->setField("AltCommand", szBuffer );
- mEdit->setField("Validate", szBuffer );
- }
- ConsoleMethod( GuiInspectorDynamicField, renameField, void, 3,3, "field.renameField(newDynamicFieldName);" )
- {
- object->renameField( StringTable->insert(argv[2]) );
- }
- bool GuiInspectorDynamicField::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
- mRenameCtrl = constructRenameControl();
- pushObjectToBack(mEdit);
- return true;
- }
- GuiControl* GuiInspectorDynamicField::constructRenameControl()
- {
- // Create our renaming field
- GuiControl* retCtrl = new GuiTextEditCtrl();
- // If we couldn't construct the control, bail!
- if( retCtrl == NULL )
- return retCtrl;
- // Let's make it look pretty.
- retCtrl->setField( "profile", "GuiInspectorTextEditRightProfile" );
- // Don't forget to register ourselves
- char szName[512];
- dSprintf( szName, 512, "IE_%s_%d_%s_Rename", retCtrl->getClassName(), mTarget->getId(), getFieldName() );
- retCtrl->registerObject( szName );
- // Our command will evaluate to :
- //
- // if( (editCtrl).getText() !$= "" )
- // (field).renameField((editCtrl).getText());
- //
- char szBuffer[512];
- dSprintf( szBuffer, 512, "if( %d.getText() !$= \"\" ) %d.renameField(%d.getText());",retCtrl->getId(), getId(), retCtrl->getId() );
- dynamic_cast<GuiTextEditCtrl*>(retCtrl)->setText( getFieldName() );
- retCtrl->setField("AltCommand", szBuffer );
- retCtrl->setField("Validate", szBuffer );
- // Calculate Caption Rect (Adjust for 16 pixel wide delete button)
- RectI captionRect( Point2I(mBounds.point.x,0) , Point2I( (S32)mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0f ) ), (S32)mBounds.extent.y ) );
- RectI valueRect(mEdit->mBounds.point, mEdit->mBounds.extent - Point2I(20, 0));
- RectI deleteRect( Point2I( mBounds.point.x + mBounds.extent.x - 20,2), Point2I( 16, mBounds.extent.y - 4));
- addObject( retCtrl );
- // Resize the name control to fit in our caption rect (tricksy!)
- retCtrl->resize( captionRect.point, captionRect.extent );
- // resize the value control to leave space for the delete button
- mEdit->resize(valueRect.point, valueRect.extent);
- // Finally, add a delete button for this field
- GuiIconButtonCtrl * delButt = new GuiIconButtonCtrl();
- if( delButt != NULL )
- {
- dSprintf(szBuffer, 512, "%d.%s = \"\";%d.inspectGroup();", mTarget->getId(), getFieldName(), mParent->getId());
- delButt->setField("Bitmap", "^modules/gui/images/iconDelete");
- delButt->setField("Text", "X");
- delButt->setField("Command", szBuffer);
- delButt->setSizing(horizResizeLeft,vertResizeCenter);
- delButt->registerObject();
- delButt->resize( deleteRect.point,deleteRect.extent);
- addObject(delButt);
- }
- return retCtrl;
- }
- void GuiInspectorDynamicField::resize( const Point2I &newPosition, const Point2I &newExtent )
- {
- Parent::resize( newPosition, newExtent );
- // If we don't have a field rename control, bail!
- if( mRenameCtrl == NULL )
- return;
- // Calculate Caption Rect
- RectI captionRect( Point2I(mBounds.point.x,0) , Point2I( (S32)mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0f ) ), (S32)mBounds.extent.y ) );
- RectI valueRect(mEdit->mBounds.point, mEdit->mBounds.extent - Point2I(20, 0));
- // Resize the edit control to fit in our caption rect (tricksy!)
- mRenameCtrl->resize( captionRect.point, captionRect.extent );
- mEdit->resize( valueRect.point, valueRect.extent);
- }
- //////////////////////////////////////////////////////////////////////////
- // GuiInspectorDatablockField
- // Field construction for datablock types
- //////////////////////////////////////////////////////////////////////////
- IMPLEMENT_CONOBJECT(GuiInspectorDatablockField);
- static S32 QSORT_CALLBACK stringCompare(const void *a,const void *b)
- {
- StringTableEntry sa = *(StringTableEntry*)a;
- StringTableEntry sb = *(StringTableEntry*)b;
- return(dStricmp(sb, sa));
- }
- GuiInspectorDatablockField::GuiInspectorDatablockField( StringTableEntry className )
- {
- setClassName(className);
- };
- void GuiInspectorDatablockField::setClassName( StringTableEntry className )
- {
- // Walk the ACR list and find a matching class if any.
- AbstractClassRep *walk = AbstractClassRep::getClassList();
- while(walk)
- {
- if(!dStricmp(walk->getClassName(), className))
- {
- // Match!
- mDesiredClass = walk;
- return;
- }
- walk = walk->getNextClass();
- }
- // No dice.
- Con::warnf("GuiInspectorDatablockField::setClassName - no class '%s' found!", className);
- return;
- }
- GuiControl* GuiInspectorDatablockField::constructEditControl()
- {
- 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->setField( "profile", "InspectorTypeEnumProfile" );
- menu->setField("text", getData());
- registerEditControl( retCtrl );
- // Configure it to update our value when the popup is closed
- char szBuffer[512];
- dSprintf( szBuffer, 512, "%d.%s = %d.getText();%d.inspect(%d);",mTarget->getId(), mField->pFieldname, menu->getId(), mParent->mParent->getId(), mTarget->getId() );
- menu->setField("Command", szBuffer );
- Vector<StringTableEntry> entries;
- SimDataBlockGroup * grp = Sim::getDataBlockGroup();
- for(SimDataBlockGroup::iterator i = grp->begin(); i != grp->end(); i++)
- {
- SimDataBlock * datablock = dynamic_cast<SimDataBlock*>(*i);
- // Skip non-datablocks if we somehow encounter them.
- if(!datablock)
- continue;
- // Ok, now we have to figure inheritance info.
- if( datablock && datablock->getClassRep()->isClass(mDesiredClass) )
- entries.push_back(datablock->getName());
- }
- // sort the entries
- dQsort(entries.address(), entries.size(), sizeof(StringTableEntry), stringCompare);
- // add them to our enum
- for(U32 j = 0; j < (U32)entries.size(); j++)
- menu->addEntry(entries[j], 0);
- return retCtrl;
- }
|