guiInspector.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "console/engineAPI.h"
  23. #include "gui/editor/guiInspector.h"
  24. #include "gui/editor/inspector/field.h"
  25. #include "gui/editor/inspector/group.h"
  26. #include "gui/buttons/guiIconButtonCtrl.h"
  27. #include "gui/editor/inspector/dynamicGroup.h"
  28. #include "gui/containers/guiScrollCtrl.h"
  29. #include "gui/editor/inspector/customField.h"
  30. #include "gui/editor/inspector/entityGroup.h"
  31. #include "gui/editor/inspector/mountingGroup.h"
  32. #include "gui/editor/inspector/componentGroup.h"
  33. IMPLEMENT_CONOBJECT(GuiInspector);
  34. ConsoleDocClass( GuiInspector,
  35. "@brief A control that allows to edit the properties of one or more SimObjects.\n\n"
  36. "Editor use only.\n\n"
  37. "@internal"
  38. );
  39. //#define DEBUG_SPEW
  40. //-----------------------------------------------------------------------------
  41. GuiInspector::GuiInspector()
  42. : mDividerPos( 0.35f ),
  43. mDividerMargin( 5 ),
  44. mOverDivider( false ),
  45. mMovingDivider( false ),
  46. mHLField( NULL ),
  47. mShowCustomFields( true )
  48. {
  49. mPadding = 1;
  50. }
  51. //-----------------------------------------------------------------------------
  52. GuiInspector::~GuiInspector()
  53. {
  54. clearGroups();
  55. }
  56. //-----------------------------------------------------------------------------
  57. void GuiInspector::initPersistFields()
  58. {
  59. addGroup( "Inspector" );
  60. addField( "dividerMargin", TypeS32, Offset( mDividerMargin, GuiInspector ) );
  61. addField( "groupFilters", TypeRealString, Offset( mGroupFilters, GuiInspector ),
  62. "Specify groups that should be shown or not. Specifying 'shown' implicitly does 'not show' all other groups. Example string: +name -otherName" );
  63. addField( "showCustomFields", TypeBool, Offset( mShowCustomFields, GuiInspector ),
  64. "If false the custom fields Name, Id, and Source Class will not be shown." );
  65. endGroup( "Inspector" );
  66. Parent::initPersistFields();
  67. }
  68. //-----------------------------------------------------------------------------
  69. void GuiInspector::onRemove()
  70. {
  71. clearGroups();
  72. Parent::onRemove();
  73. }
  74. //-----------------------------------------------------------------------------
  75. void GuiInspector::onDeleteNotify( SimObject *object )
  76. {
  77. Parent::onDeleteNotify( object );
  78. if( isInspectingObject( object ) )
  79. removeInspectObject( object );
  80. }
  81. //-----------------------------------------------------------------------------
  82. void GuiInspector::parentResized(const RectI &oldParentRect, const RectI &newParentRect)
  83. {
  84. GuiControl *parent = getParent();
  85. if ( parent && dynamic_cast<GuiScrollCtrl*>(parent) != NULL )
  86. {
  87. GuiScrollCtrl *scroll = dynamic_cast<GuiScrollCtrl*>(parent);
  88. setWidth( ( newParentRect.extent.x - ( scroll->scrollBarThickness() + 4 ) ) );
  89. }
  90. else
  91. Parent::parentResized(oldParentRect,newParentRect);
  92. }
  93. //-----------------------------------------------------------------------------
  94. bool GuiInspector::resize( const Point2I &newPosition, const Point2I &newExtent )
  95. {
  96. //F32 dividerPerc = (F32)getWidth() / (F32)mDividerPos;
  97. bool result = Parent::resize( newPosition, newExtent );
  98. //mDividerPos = (F32)getWidth() * dividerPerc;
  99. updateDivider();
  100. return result;
  101. }
  102. //-----------------------------------------------------------------------------
  103. GuiControl* GuiInspector::findHitControl( const Point2I &pt, S32 initialLayer )
  104. {
  105. if ( mOverDivider || mMovingDivider )
  106. return this;
  107. return Parent::findHitControl( pt, initialLayer );
  108. }
  109. //-----------------------------------------------------------------------------
  110. void GuiInspector::getCursor( GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent )
  111. {
  112. GuiCanvas *pRoot = getRoot();
  113. if( !pRoot )
  114. return;
  115. S32 desiredCursor = mOverDivider ? PlatformCursorController::curResizeVert : PlatformCursorController::curArrow;
  116. // Bail if we're already at the desired cursor
  117. if ( pRoot->mCursorChanged == desiredCursor )
  118. return;
  119. PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
  120. AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible.");
  121. PlatformCursorController *pController = pWindow->getCursorController();
  122. AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
  123. // Now change the cursor shape
  124. pController->popCursor();
  125. pController->pushCursor(desiredCursor);
  126. pRoot->mCursorChanged = desiredCursor;
  127. }
  128. //-----------------------------------------------------------------------------
  129. void GuiInspector::onMouseMove(const GuiEvent &event)
  130. {
  131. if ( collideDivider( globalToLocalCoord( event.mousePoint ) ) )
  132. mOverDivider = true;
  133. else
  134. mOverDivider = false;
  135. }
  136. //-----------------------------------------------------------------------------
  137. void GuiInspector::onMouseDown(const GuiEvent &event)
  138. {
  139. if ( mOverDivider )
  140. {
  141. mMovingDivider = true;
  142. }
  143. }
  144. //-----------------------------------------------------------------------------
  145. void GuiInspector::onMouseUp(const GuiEvent &event)
  146. {
  147. mMovingDivider = false;
  148. }
  149. //-----------------------------------------------------------------------------
  150. void GuiInspector::onMouseDragged(const GuiEvent &event)
  151. {
  152. if ( !mMovingDivider )
  153. return;
  154. Point2I localPnt = globalToLocalCoord( event.mousePoint );
  155. S32 inspectorWidth = getWidth();
  156. // Distance from mouse/divider position in local space
  157. // to the right edge of the inspector
  158. mDividerPos = inspectorWidth - localPnt.x;
  159. mDividerPos = mClamp( mDividerPos, 0, inspectorWidth );
  160. // Divide that by the inspectorWidth to get a percentage
  161. mDividerPos /= inspectorWidth;
  162. updateDivider();
  163. }
  164. //-----------------------------------------------------------------------------
  165. GuiInspectorGroup* GuiInspector::findExistentGroup( StringTableEntry groupName )
  166. {
  167. // If we have no groups, it couldn't possibly exist
  168. if( mGroups.empty() )
  169. return NULL;
  170. // Attempt to find it in the group list
  171. Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
  172. for( ; i != mGroups.end(); i++ )
  173. {
  174. if( dStricmp( (*i)->getGroupName(), groupName ) == 0 )
  175. return *i;
  176. }
  177. return NULL;
  178. }
  179. //-----------------------------------------------------------------------------
  180. void GuiInspector::updateFieldValue( StringTableEntry fieldName, StringTableEntry arrayIdx )
  181. {
  182. // We don't know which group contains the field of this name,
  183. // so ask each group in turn, and break when a group returns true
  184. // signifying it contained and updated that field.
  185. Vector<GuiInspectorGroup*>::iterator groupIter = mGroups.begin();
  186. for( ; groupIter != mGroups.end(); groupIter++ )
  187. {
  188. if ( (*groupIter)->updateFieldValue( fieldName, arrayIdx ) )
  189. break;
  190. }
  191. }
  192. //-----------------------------------------------------------------------------
  193. void GuiInspector::clearGroups()
  194. {
  195. #ifdef DEBUG_SPEW
  196. Platform::outputDebugString( "[GuiInspector] Clearing %i (%s)", getId(), getName() );
  197. #endif
  198. // If we have no groups, there's nothing to clear!
  199. if( mGroups.empty() )
  200. return;
  201. mHLField = NULL;
  202. if( isMethod( "onClear" ) )
  203. Con::executef( this, "onClear" );
  204. Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
  205. freeze(true);
  206. // Delete Groups
  207. for( ; i != mGroups.end(); i++ )
  208. {
  209. if((*i) && (*i)->isProperlyAdded())
  210. (*i)->deleteObject();
  211. }
  212. mGroups.clear();
  213. freeze(false);
  214. updatePanes();
  215. }
  216. //-----------------------------------------------------------------------------
  217. bool GuiInspector::isInspectingObject( SimObject* object )
  218. {
  219. const U32 numTargets = mTargets.size();
  220. for( U32 i = 0; i < numTargets; ++ i )
  221. if( mTargets[ i ] == object )
  222. return true;
  223. return false;
  224. }
  225. //-----------------------------------------------------------------------------
  226. void GuiInspector::inspectObject( SimObject *object )
  227. {
  228. if( mTargets.size() > 1 || !isInspectingObject( object ) )
  229. clearInspectObjects();
  230. addInspectObject( object );
  231. }
  232. //-----------------------------------------------------------------------------
  233. void GuiInspector::clearInspectObjects()
  234. {
  235. const U32 numTargets = mTargets.size();
  236. for( U32 i = 0; i < numTargets; ++ i )
  237. clearNotify( mTargets[ i ] );
  238. clearGroups();
  239. mTargets.clear();
  240. }
  241. //-----------------------------------------------------------------------------
  242. void GuiInspector::addInspectObject( SimObject* object, bool autoSync )
  243. {
  244. // If we are already inspecting the object, just update the groups.
  245. if( isInspectingObject( object ) )
  246. {
  247. #ifdef DEBUG_SPEW
  248. Platform::outputDebugString( "[GuiInspector] Refreshing view of %i:%s (%s)",
  249. object->getId(), object->getClassName(), object->getName() );
  250. #endif
  251. Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
  252. for ( ; i != mGroups.end(); i++ )
  253. (*i)->updateAllFields();
  254. return;
  255. }
  256. #ifdef DEBUG_SPEW
  257. Platform::outputDebugString( "[GuiInspector] Adding %i:%s (%s) to inspect set",
  258. object->getId(), object->getClassName(), object->getName() );
  259. #endif
  260. // Give users a chance to customize fields on this object
  261. if( object->isMethod("onDefineFieldTypes") )
  262. Con::executef( object, "onDefineFieldTypes" );
  263. // Set Target
  264. mTargets.push_back( object );
  265. deleteNotify( object );
  266. if( autoSync )
  267. refresh();
  268. }
  269. //-----------------------------------------------------------------------------
  270. void GuiInspector::removeInspectObject( SimObject* object )
  271. {
  272. const U32 numTargets = mTargets.size();
  273. for( U32 i = 0; i < numTargets; ++ i )
  274. if( mTargets[ i ] == object )
  275. {
  276. // Delete all inspector data *before* removing the target so that apply calls
  277. // triggered by edit controls losing focus will not find the inspect object
  278. // gone.
  279. clearGroups();
  280. #ifdef DEBUG_SPEW
  281. Platform::outputDebugString( "[GuiInspector] Removing %i:%s (%s) from inspect set",
  282. object->getId(), object->getClassName(), object->getName() );
  283. #endif
  284. mTargets.erase( i );
  285. clearNotify( object );
  286. // Refresh the inspector except if the system is going down.
  287. if( !Sim::isShuttingDown() )
  288. refresh();
  289. return;
  290. }
  291. }
  292. //-----------------------------------------------------------------------------
  293. void GuiInspector::setName( StringTableEntry newName )
  294. {
  295. if( mTargets.size() != 1 )
  296. return;
  297. StringTableEntry name = StringTable->insert(newName);
  298. // Only assign a new name if we provide one
  299. mTargets[ 0 ]->assignName(name);
  300. }
  301. //-----------------------------------------------------------------------------
  302. bool GuiInspector::collideDivider( const Point2I &localPnt )
  303. {
  304. RectI divisorRect( getWidth() - getWidth() * mDividerPos - mDividerMargin, 0, mDividerMargin * 2, getHeight() );
  305. if ( divisorRect.pointInRect( localPnt ) )
  306. return true;
  307. return false;
  308. }
  309. //-----------------------------------------------------------------------------
  310. void GuiInspector::updateDivider()
  311. {
  312. for ( U32 i = 0; i < mGroups.size(); i++ )
  313. for ( U32 j = 0; j < mGroups[i]->mChildren.size(); j++ )
  314. mGroups[i]->mChildren[j]->updateRects();
  315. //setUpdate();
  316. }
  317. //-----------------------------------------------------------------------------
  318. void GuiInspector::getDivider( S32 &pos, S32 &margin )
  319. {
  320. pos = (F32)getWidth() * mDividerPos;
  321. margin = mDividerMargin;
  322. }
  323. //-----------------------------------------------------------------------------
  324. void GuiInspector::setHighlightField( GuiInspectorField *field )
  325. {
  326. if ( mHLField == field )
  327. return;
  328. if ( mHLField.isValid() )
  329. mHLField->setHLEnabled( false );
  330. mHLField = field;
  331. // We could have been passed a null field, meaning, set no field highlighted.
  332. if ( mHLField.isNull() )
  333. return;
  334. mHLField->setHLEnabled( true );
  335. }
  336. //-----------------------------------------------------------------------------
  337. bool GuiInspector::isGroupFiltered( const char *groupName ) const
  338. {
  339. // Internal and Ungrouped always filtered, we never show them.
  340. if ( dStricmp( groupName, "Internal" ) == 0 ||
  341. dStricmp( groupName, "Ungrouped" ) == 0 ||
  342. dStricmp( groupName, "AdvCoordManipulation" ) == 0)
  343. return true;
  344. // Normal case, determine if filtered by looking at the mGroupFilters string.
  345. String searchStr;
  346. // Is this group explicitly show? Does it immediately follow a + char.
  347. searchStr = String::ToString( "+%s", groupName );
  348. if ( mGroupFilters.find( searchStr ) != String::NPos )
  349. return false;
  350. // Were there any other + characters, if so, we are implicitly hidden.
  351. if ( mGroupFilters.find( "+" ) != String::NPos )
  352. return true;
  353. // Is this group explicitly hidden? Does it immediately follow a - char.
  354. searchStr = String::ToString( "-%s", groupName );
  355. if ( mGroupFilters.find( searchStr ) != String::NPos )
  356. return true;
  357. return false;
  358. }
  359. //-----------------------------------------------------------------------------
  360. bool GuiInspector::isGroupExplicitlyFiltered( const char *groupName ) const
  361. {
  362. String searchStr;
  363. searchStr = String::ToString( "-%s", groupName );
  364. if ( mGroupFilters.find( searchStr ) != String::NPos )
  365. return true;
  366. return false;
  367. }
  368. //-----------------------------------------------------------------------------
  369. void GuiInspector::setObjectField( const char *fieldName, const char *data )
  370. {
  371. GuiInspectorField *field;
  372. for ( S32 i = 0; i < mGroups.size(); i++ )
  373. {
  374. field = mGroups[i]->findField( fieldName );
  375. if( field )
  376. {
  377. field->setData( data );
  378. return;
  379. }
  380. }
  381. }
  382. //-----------------------------------------------------------------------------
  383. static SimObject *sgKeyObj = NULL;
  384. bool findInspectors( GuiInspector *obj )
  385. {
  386. if ( obj->isAwake() && obj->isInspectingObject( sgKeyObj ) )
  387. return true;
  388. return false;
  389. }
  390. //-----------------------------------------------------------------------------
  391. GuiInspector* GuiInspector::findByObject( SimObject *obj )
  392. {
  393. sgKeyObj = obj;
  394. Vector< GuiInspector* > found;
  395. Sim::getGuiGroup()->findObjectByCallback( findInspectors, found );
  396. if ( found.empty() )
  397. return NULL;
  398. return found.first();
  399. }
  400. //-----------------------------------------------------------------------------
  401. void GuiInspector::refresh()
  402. {
  403. clearGroups();
  404. // Remove any inspect object that happened to have
  405. // already been killed.
  406. for( U32 i = 0; i < mTargets.size(); ++ i )
  407. if( !mTargets[ i ] )
  408. {
  409. mTargets.erase( i );
  410. -- i;
  411. }
  412. if( !mTargets.size() )
  413. return;
  414. // Special group for fields which should appear at the top of the
  415. // list outside of a rollout control.
  416. GuiInspectorGroup *ungroup = NULL;
  417. if( mTargets.size() == 1 )
  418. {
  419. ungroup = new GuiInspectorGroup( "Ungrouped", this );
  420. ungroup->setHeaderHidden( true );
  421. ungroup->setCanCollapse( false );
  422. ungroup->registerObject();
  423. mGroups.push_back( ungroup );
  424. addObject( ungroup );
  425. }
  426. // Put the 'transform' group first
  427. GuiInspectorGroup *transform = new GuiInspectorGroup( "Transform", this );
  428. transform->registerObject();
  429. mGroups.push_back(transform);
  430. addObject(transform);
  431. // Always create the 'general' group (for fields without a group)
  432. GuiInspectorGroup *general = new GuiInspectorGroup( "General", this );
  433. general->registerObject();
  434. mGroups.push_back(general);
  435. addObject(general);
  436. //Entity inspector group
  437. if (mTargets.first()->getClassRep()->isSubclassOf("Entity"))
  438. {
  439. GuiInspectorEntityGroup *components = new GuiInspectorEntityGroup("Components", this);
  440. if (components != NULL)
  441. {
  442. components->registerObject();
  443. mGroups.push_back(components);
  444. addObject(components);
  445. }
  446. //Mounting group override
  447. GuiInspectorGroup *mounting = new GuiInspectorMountingGroup("Mounting", this);
  448. if (mounting != NULL)
  449. {
  450. mounting->registerObject();
  451. mGroups.push_back(mounting);
  452. addObject(mounting);
  453. }
  454. }
  455. if (mTargets.first()->getClassRep()->isSubclassOf("Component"))
  456. {
  457. //Build the component field groups as the component describes it
  458. Component* comp = dynamic_cast<Component*>(mTargets.first().getPointer());
  459. if (comp->getComponentFieldCount() > 0)
  460. {
  461. GuiInspectorComponentGroup *compGroup = new GuiInspectorComponentGroup("Component Fields", this);
  462. compGroup->registerObject();
  463. mGroups.push_back(compGroup);
  464. addObject(compGroup);
  465. }
  466. }
  467. // Create the inspector groups for static fields.
  468. for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter )
  469. {
  470. AbstractClassRep::FieldList &fieldList = ( *iter )->getModifiableFieldList();
  471. // Iterate through, identifying the groups and create necessary GuiInspectorGroups
  472. for( AbstractClassRep::FieldList::iterator itr = fieldList.begin(); itr != fieldList.end(); itr++ )
  473. {
  474. if ( itr->type == AbstractClassRep::StartGroupFieldType )
  475. {
  476. GuiInspectorGroup* group = findExistentGroup( itr->pGroupname );
  477. if( !group && !isGroupFiltered( itr->pGroupname ) )
  478. {
  479. GuiInspectorGroup *group = new GuiInspectorGroup( itr->pGroupname, this );
  480. group->registerObject();
  481. if( !group->getNumFields() )
  482. {
  483. #ifdef DEBUG_SPEW
  484. Platform::outputDebugString( "[GuiInspector] Removing empty group '%s'",
  485. group->getCaption().c_str() );
  486. #endif
  487. // The group ended up having no fields. Remove it.
  488. group->deleteObject();
  489. }
  490. else
  491. {
  492. mGroups.push_back( group );
  493. addObject( group );
  494. }
  495. }
  496. }
  497. }
  498. }
  499. // Deal with dynamic fields
  500. if ( !isGroupFiltered( "Dynamic Fields" ) )
  501. {
  502. GuiInspectorGroup *dynGroup = new GuiInspectorDynamicGroup( "Dynamic Fields", this);
  503. dynGroup->registerObject();
  504. mGroups.push_back( dynGroup );
  505. addObject( dynGroup );
  506. }
  507. if( mShowCustomFields && mTargets.size() == 1 )
  508. {
  509. SimObject* object = mTargets.first();
  510. // Add the SimObjectID field to the ungrouped group.
  511. GuiInspectorCustomField* field = new GuiInspectorCustomField();
  512. field->init( this, ungroup );
  513. if( field->registerObject() )
  514. {
  515. ungroup->mChildren.push_back( field );
  516. ungroup->mStack->addObject( field );
  517. static StringTableEntry sId = StringTable->insert( "id" );
  518. field->setCaption( sId );
  519. field->setData( object->getIdString() );
  520. field->setDoc( "SimObjectId of this object. [Read Only]" );
  521. }
  522. else
  523. delete field;
  524. // Add the Source Class field to the ungrouped group.
  525. field = new GuiInspectorCustomField();
  526. field->init( this, ungroup );
  527. if( field->registerObject() )
  528. {
  529. ungroup->mChildren.push_back( field );
  530. ungroup->mStack->addObject( field );
  531. StringTableEntry sSourceClass = StringTable->insert( "Source Class", true );
  532. field->setCaption( sSourceClass );
  533. field->setData( object->getClassName() );
  534. Namespace* ns = object->getClassRep()->getNameSpace();
  535. field->setToolTip( Con::getNamespaceList( ns ) );
  536. field->setDoc( "Native class of this object. [Read Only]" );
  537. }
  538. else
  539. delete field;
  540. }
  541. // If the general group is still empty at this point ( or filtered ), kill it.
  542. if ( isGroupFiltered( "General" ) || general->mStack->size() == 0 )
  543. {
  544. for(S32 i=0; i<mGroups.size(); i++)
  545. {
  546. if ( mGroups[i] == general )
  547. {
  548. mGroups.erase(i);
  549. general->deleteObject();
  550. updatePanes();
  551. break;
  552. }
  553. }
  554. }
  555. // If transform turns out to be empty or filtered, remove it
  556. if( isGroupFiltered( "Transform" ) || transform->mStack->size() == 0 )
  557. {
  558. for(S32 i=0; i<mGroups.size(); i++)
  559. {
  560. if ( mGroups[i] == transform )
  561. {
  562. mGroups.erase(i);
  563. transform->deleteObject();
  564. updatePanes();
  565. break;
  566. }
  567. }
  568. }
  569. // If ungrouped is empty or explicitly filtered, remove it.
  570. if( ungroup && ( isGroupExplicitlyFiltered( "Ungrouped" ) || ungroup->getNumFields() == 0 ) )
  571. {
  572. for(S32 i=0; i<mGroups.size(); i++)
  573. {
  574. if ( mGroups[i] == ungroup )
  575. {
  576. mGroups.erase(i);
  577. ungroup->deleteObject();
  578. updatePanes();
  579. break;
  580. }
  581. }
  582. }
  583. // If the object cannot be renamed, deactivate the name field if we have it.
  584. if( ungroup && getNumInspectObjects() == 1 && !getInspectObject()->isNameChangeAllowed() )
  585. {
  586. GuiInspectorField* nameField = ungroup->findField( "name" );
  587. if( nameField )
  588. nameField->setActive( false );
  589. }
  590. }
  591. //-----------------------------------------------------------------------------
  592. void GuiInspector::sendInspectPreApply()
  593. {
  594. const U32 numObjects = getNumInspectObjects();
  595. for( U32 i = 0; i < numObjects; ++ i )
  596. getInspectObject( i )->inspectPreApply();
  597. }
  598. //-----------------------------------------------------------------------------
  599. void GuiInspector::sendInspectPostApply()
  600. {
  601. const U32 numObjects = getNumInspectObjects();
  602. for( U32 i = 0; i < numObjects; ++ i )
  603. getInspectObject( i )->inspectPostApply();
  604. }
  605. //=============================================================================
  606. // Console Methods.
  607. //=============================================================================
  608. // MARK: ---- Console Methods ----
  609. //-----------------------------------------------------------------------------
  610. DefineEngineMethod( GuiInspector, inspect, void, (const char* simObject), (""),
  611. "Inspect the given object.\n"
  612. "@param simObject Object to inspect.")
  613. {
  614. SimObject * target = Sim::findObject(simObject);
  615. if(!target)
  616. {
  617. if(dAtoi(simObject) > 0)
  618. Con::warnf("%s::inspect(): invalid object: %s", object->getClassName(), simObject);
  619. object->clearInspectObjects();
  620. return;
  621. }
  622. object->inspectObject(target);
  623. }
  624. //-----------------------------------------------------------------------------
  625. DefineEngineMethod( GuiInspector, addInspect, void, (const char* simObject, bool autoSync), (true),
  626. "Add the object to the list of objects being inspected.\n"
  627. "@param simObject Object to add to the inspection."
  628. "@param autoSync Auto sync the values when they change.")
  629. {
  630. SimObject* obj;
  631. if( !Sim::findObject( simObject, obj ) )
  632. {
  633. Con::errorf( "%s::addInspect(): invalid object: %s", object->getClassName(), simObject );
  634. return;
  635. }
  636. object->addInspectObject( obj, autoSync );
  637. }
  638. //-----------------------------------------------------------------------------
  639. DefineEngineMethod( GuiInspector, removeInspect, void, (const char* simObject), ,
  640. "Remove the object from the list of objects being inspected.\n"
  641. "@param simObject Object to remove from the inspection.")
  642. {
  643. SimObject* obj;
  644. if( !Sim::findObject( simObject, obj ) )
  645. {
  646. Con::errorf( "%s::removeInspect(): invalid object: %s", object->getClassName(), simObject );
  647. return;
  648. }
  649. object->removeInspectObject( obj );
  650. }
  651. //-----------------------------------------------------------------------------
  652. DefineEngineMethod( GuiInspector, refresh, void, (), ,
  653. "Re-inspect the currently selected object.\n")
  654. {
  655. if ( object->getNumInspectObjects() == 0 )
  656. return;
  657. SimObject *target = object->getInspectObject();
  658. if ( target )
  659. object->inspectObject( target );
  660. }
  661. //-----------------------------------------------------------------------------
  662. DefineEngineMethod( GuiInspector, getInspectObject, const char*, (S32 index), (0),
  663. "Returns currently inspected object.\n"
  664. "@param index Index of object in inspection list you want to get."
  665. "@return object being inspected.")
  666. {
  667. if( index < 0 || index >= object->getNumInspectObjects() )
  668. {
  669. Con::errorf( "GuiInspector::getInspectObject() - index out of range: %i", index );
  670. return "";
  671. }
  672. return object->getInspectObject( index )->getIdString();
  673. }
  674. //-----------------------------------------------------------------------------
  675. DefineEngineMethod( GuiInspector, getNumInspectObjects, S32, (), ,
  676. "Return the number of objects currently being inspected.\n"
  677. "@return number of objects currently being inspected.")
  678. {
  679. return object->getNumInspectObjects();
  680. }
  681. //-----------------------------------------------------------------------------
  682. DefineEngineMethod( GuiInspector, setName, void, (const char* newObjectName), ,
  683. "Rename the object being inspected (first object in inspect list).\n"
  684. "@param newObjectName new name for object being inspected.")
  685. {
  686. object->setName(newObjectName);
  687. }
  688. //-----------------------------------------------------------------------------
  689. DefineEngineMethod( GuiInspector, apply, void, (), ,
  690. "Force application of inspected object's attributes.\n")
  691. {
  692. object->sendInspectPostApply();
  693. }
  694. //-----------------------------------------------------------------------------
  695. DefineEngineMethod( GuiInspector, setObjectField, void, (const char* fieldname, const char* data ), ,
  696. "Set a named fields value on the inspected object if it exists. This triggers all the usual callbacks that would occur if the field had been changed through the gui..\n"
  697. "@param fieldname Field name on object we are inspecting we want to change."
  698. "@param data New Value for the given field.")
  699. {
  700. object->setObjectField( fieldname, data );
  701. }
  702. //-----------------------------------------------------------------------------
  703. DefineEngineMethod( GuiInspector, findByObject, S32, (SimObject* obj), ,
  704. "Returns the id of an awake inspector that is inspecting the passed object if one exists\n"
  705. "@param object Object to find away inspector for."
  706. "@return id of an awake inspector that is inspecting the passed object if one exists, else NULL or 0.")
  707. {
  708. if ( !obj)
  709. return NULL;
  710. SimObject *inspector = GuiInspector::findByObject(obj);
  711. if ( !inspector )
  712. return NULL;
  713. return inspector->getId();
  714. }