guiInspector.cpp 31 KB

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