guiInspector.cpp 30 KB

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