guiInspector.cpp 27 KB

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