guiInspector.cc 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 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 "gui/editor/guiInspector.h"
  23. #include "gui/buttons/guiButtonCtrl.h"
  24. #include "memory/frameAllocator.h"
  25. #pragma region GuiInspector
  26. //////////////////////////////////////////////////////////////////////////
  27. // GuiInspector
  28. //////////////////////////////////////////////////////////////////////////
  29. IMPLEMENT_CONOBJECT(GuiInspector);
  30. GuiInspector::GuiInspector()
  31. {
  32. mGroups.clear();
  33. mTarget = NULL;
  34. mGroupPanelProfile = NULL;
  35. setField("GroupPanelProfile", "GuiPanelProfile");
  36. mGroupGridProfile = NULL;
  37. setField("GroupGridProfile", "GuiDefaultProfile");
  38. mLabelProfile = NULL;
  39. setField("LabelProfile", "GuiDefaultProfile");
  40. mTextEditProfile = NULL;
  41. setField("TextEditProfile", "GuiTextEditProfile");
  42. mDropDownProfile = NULL;
  43. setField("DropDownProfile", "GuiDropDownProfile");
  44. mDropDownItemProfile = NULL;
  45. setField("DropDownItemProfile", "GuiListBoxProfile");
  46. mScrollProfile = NULL;
  47. setField("ScrollProfile", "GuiScrollProfile");
  48. mBackgroundProfile = NULL;
  49. setField("BackgroundProfile", "GuiDefaultProfile");
  50. mThumbProfile = NULL;
  51. setField("ThumbProfile", "GuiScrollThumbProfile");
  52. mArrowProfile = NULL;
  53. setField("ArrowProfile", "GuiScrollArrowProfile");
  54. mTrackProfile = NULL;
  55. setField("TrackProfile", "GuiScrollTrackProfile");
  56. mCheckboxProfile = NULL;
  57. setField("CheckboxProfile", "GuiCheckboxProfile");
  58. mButtonProfile = NULL;
  59. setField("ButtonProfile", "GuiButtonProfile");
  60. mUseConstantHeightThumb = false;
  61. mScrollBarThickness = 12;
  62. mShowArrowButtons = true;
  63. mFieldCellSize.set(300, 30);
  64. mControlOffset.set(10, 16);
  65. }
  66. GuiInspector::~GuiInspector()
  67. {
  68. clearGroups();
  69. }
  70. void GuiInspector::initPersistFields()
  71. {
  72. Parent::initPersistFields();
  73. addField("GroupPanelProfile", TypeGuiProfile, Offset(mGroupPanelProfile, GuiInspector));
  74. addField("GroupGridProfile", TypeGuiProfile, Offset(mGroupGridProfile, GuiInspector));
  75. addField("LabelProfile", TypeGuiProfile, Offset(mLabelProfile, GuiInspector));
  76. addField("TextEditProfile", TypeGuiProfile, Offset(mTextEditProfile, GuiInspector));
  77. addField("DropDownProfile", TypeGuiProfile, Offset(mDropDownProfile, GuiInspector));
  78. addField("DropDownItemProfile", TypeGuiProfile, Offset(mDropDownItemProfile, GuiInspector));
  79. addField("ScrollProfile", TypeGuiProfile, Offset(mScrollProfile, GuiInspector));
  80. addField("backgroundProfile", TypeGuiProfile, Offset(mBackgroundProfile, GuiInspector));
  81. addField("thumbProfile", TypeGuiProfile, Offset(mThumbProfile, GuiInspector));
  82. addField("trackProfile", TypeGuiProfile, Offset(mTrackProfile, GuiInspector));
  83. addField("arrowProfile", TypeGuiProfile, Offset(mArrowProfile, GuiInspector));
  84. addField("CheckboxProfile", TypeGuiProfile, Offset(mCheckboxProfile, GuiInspector));
  85. addField("ButtonProfile", TypeGuiProfile, Offset(mButtonProfile, GuiInspector));
  86. addField("constantThumbHeight", TypeBool, Offset(mUseConstantHeightThumb, GuiInspector));
  87. addField("scrollBarThickness", TypeS32, Offset(mScrollBarThickness, GuiInspector));
  88. addField("showArrowButtons", TypeBool, Offset(mShowArrowButtons, GuiInspector));
  89. addField("FieldCellSize", TypePoint2I, Offset(mFieldCellSize, GuiInspector));
  90. addField("ControlOffset", TypePoint2I, Offset(mControlOffset, GuiInspector));
  91. }
  92. bool GuiInspector::onWake()
  93. {
  94. if (!Parent::onWake())
  95. return false;
  96. if (mGroupPanelProfile != NULL)
  97. mGroupPanelProfile->incRefCount();
  98. if (mGroupGridProfile != NULL)
  99. mGroupGridProfile->incRefCount();
  100. if (mLabelProfile != NULL)
  101. mLabelProfile->incRefCount();
  102. if (mTextEditProfile != NULL)
  103. mTextEditProfile->incRefCount();
  104. if (mDropDownProfile != NULL)
  105. mDropDownProfile->incRefCount();
  106. if (mDropDownItemProfile != NULL)
  107. mDropDownItemProfile->incRefCount();
  108. if (mScrollProfile != NULL)
  109. mScrollProfile->incRefCount();
  110. if (mBackgroundProfile != NULL)
  111. mBackgroundProfile->incRefCount();
  112. if (mThumbProfile != NULL)
  113. mThumbProfile->incRefCount();
  114. if (mTrackProfile != NULL)
  115. mTrackProfile->incRefCount();
  116. if (mArrowProfile != NULL)
  117. mArrowProfile->incRefCount();
  118. if (mCheckboxProfile != NULL)
  119. mCheckboxProfile->incRefCount();
  120. if (mButtonProfile != NULL)
  121. mButtonProfile->incRefCount();
  122. return true;
  123. }
  124. void GuiInspector::onSleep()
  125. {
  126. Parent::onSleep();
  127. if (mGroupPanelProfile != NULL)
  128. mGroupPanelProfile->decRefCount();
  129. if (mGroupGridProfile != NULL)
  130. mGroupGridProfile->decRefCount();
  131. if (mLabelProfile != NULL)
  132. mLabelProfile->decRefCount();
  133. if (mTextEditProfile != NULL)
  134. mTextEditProfile->decRefCount();
  135. if (mDropDownProfile != NULL)
  136. mDropDownProfile->decRefCount();
  137. if (mDropDownItemProfile != NULL)
  138. mDropDownItemProfile->decRefCount();
  139. if (mScrollProfile != NULL)
  140. mScrollProfile->decRefCount();
  141. if (mBackgroundProfile != NULL)
  142. mBackgroundProfile->decRefCount();
  143. if (mThumbProfile != NULL)
  144. mThumbProfile->decRefCount();
  145. if (mTrackProfile != NULL)
  146. mTrackProfile->decRefCount();
  147. if (mArrowProfile != NULL)
  148. mArrowProfile->decRefCount();
  149. if (mCheckboxProfile != NULL)
  150. mCheckboxProfile->decRefCount();
  151. if (mButtonProfile != NULL)
  152. mButtonProfile->decRefCount();
  153. }
  154. void GuiInspector::inspectPostApply()
  155. {
  156. Parent::inspectPostApply();
  157. if (mTarget)
  158. {
  159. SimObjectPtr<SimObject> oldTarget = mTarget;
  160. mTarget = NULL;
  161. inspectObject(oldTarget);
  162. }
  163. }
  164. void GuiInspector::resize(const Point2I &newPosition, const Point2I &newExtent)
  165. {
  166. Parent::resize(Point2I(0, 0), newExtent);
  167. }
  168. void GuiInspector::parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent)
  169. {
  170. GuiControl *parent = getParent();
  171. if( parent && dynamic_cast<GuiScrollCtrl*>(parent) != NULL )
  172. {
  173. // Handle Parent Sizing (We constrain ourself to our parent's width)
  174. GuiScrollCtrl *scroll = dynamic_cast<GuiScrollCtrl*>(parent);
  175. setWidth(newParentExtent.x - scroll->scrollBarThickness());
  176. }
  177. else
  178. Parent::parentResized(oldParentExtent,newParentExtent);
  179. }
  180. bool GuiInspector::findExistentGroup( StringTableEntry groupName )
  181. {
  182. // If we have no groups, it couldn't possibly exist
  183. if( mGroups.empty() )
  184. return false;
  185. // Attempt to find it in the group list
  186. Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
  187. for( ; i != mGroups.end(); i++ )
  188. {
  189. if( dStricmp( (*i)->getGroupName(), groupName ) == 0 )
  190. return true;
  191. }
  192. return false;
  193. }
  194. void GuiInspector::clearGroups()
  195. {
  196. // If we're clearing the groups, we want to clear our target too.
  197. mTarget = NULL;
  198. // If we have no groups, there's nothing to clear!
  199. if( mGroups.empty() )
  200. return;
  201. // Attempt to find it in the group list
  202. Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
  203. for( ; i != mGroups.end(); i++ )
  204. if( (*i)->isProperlyAdded() )
  205. (*i)->deleteObject();
  206. mGroups.clear();
  207. }
  208. void GuiInspector::inspectObject( SimObject *object )
  209. {
  210. GuiCanvas *guiCanvas = getRoot();
  211. if( !guiCanvas )
  212. return;
  213. SimObjectPtr<GuiControl> currResponder = guiCanvas->getFirstResponder();
  214. // If our target is the same as our current target, just update the groups.
  215. if( mTarget == object )
  216. {
  217. Vector<GuiInspectorGroup*>::iterator i = mGroups.begin();
  218. for ( ; i != mGroups.end(); i++ )
  219. (*i)->inspectGroup();
  220. // Don't steal first responder
  221. if( !currResponder.isNull() )
  222. guiCanvas->setFirstResponder( currResponder );
  223. return;
  224. }
  225. // Clear our current groups
  226. clearGroups();
  227. // Set Target
  228. mTarget = object;
  229. // Always create the 'general' group (for un-grouped fields)
  230. GuiInspectorGroup* general = new GuiInspectorGroup( mTarget, "General", this );
  231. if( general != NULL )
  232. {
  233. general->registerObject();
  234. mGroups.push_back( general );
  235. addObject( general );
  236. }
  237. // Grab this objects field list
  238. AbstractClassRep::FieldList &fieldList = mTarget->getModifiableFieldList();
  239. AbstractClassRep::FieldList::iterator itr;
  240. // Iterate through, identifying the groups and create necessary GuiInspectorGroups
  241. for(itr = fieldList.begin(); itr != fieldList.end(); itr++)
  242. {
  243. if(itr->type == AbstractClassRep::StartGroupFieldType && !findExistentGroup( itr->pGroupname ) )
  244. {
  245. GuiInspectorGroup *group = new GuiInspectorGroup( mTarget, itr->pGroupname, this );
  246. if( group != NULL )
  247. {
  248. group->registerObject();
  249. mGroups.push_back( group );
  250. addObject( group );
  251. }
  252. }
  253. }
  254. // Deal with dynamic fields
  255. GuiInspectorGroup *dynGroup = new GuiInspectorDynamicGroup( mTarget, "Dynamic Fields", this);
  256. if( dynGroup != NULL )
  257. {
  258. dynGroup->registerObject();
  259. mGroups.push_back( dynGroup );
  260. addObject( dynGroup );
  261. }
  262. // If any group is still empty at this point, kill it.
  263. for(S32 i=0; i<mGroups.size(); i++)
  264. {
  265. if(mGroups[i]->mGrid->size() == 0)
  266. {
  267. mGroups[i]->deleteObject();
  268. mGroups.erase(i);
  269. i--;
  270. }
  271. }
  272. resize(getPosition(), getExtent());
  273. // Don't steal first responder
  274. if( !currResponder.isNull() )
  275. guiCanvas->setFirstResponder( currResponder );
  276. }
  277. ConsoleMethod( GuiInspector, inspect, void, 3, 3, "(obj) Goes through the object's fields and autogenerates editor boxes\n"
  278. "@return No return value.")
  279. {
  280. SimObject * target = Sim::findObject(argv[2]);
  281. if(!target)
  282. {
  283. if(dAtoi(argv[2]) > 0)
  284. Con::warnf("%s::inspect(): invalid object: %s", argv[0], argv[2]);
  285. object->clearGroups();
  286. return;
  287. }
  288. object->inspectObject(target);
  289. }
  290. ConsoleMethod( GuiInspector, getInspectObject, const char*, 2, 2, "() - Returns currently inspected object\n"
  291. "@return The Object's ID as a string.")
  292. {
  293. SimObject *pSimObject = object->getInspectObject();
  294. if( pSimObject != NULL )
  295. return pSimObject->getIdString();
  296. return "";
  297. }
  298. void GuiInspector::setName( const char* newName )
  299. {
  300. if( mTarget == NULL )
  301. return;
  302. // Only assign a new name if we provide one
  303. mTarget->assignName(newName);
  304. }
  305. ConsoleMethod( GuiInspector, setName, void, 3, 3, "(NewObjectName) Set object name.\n"
  306. "@return No return value.")
  307. {
  308. object->setName(argv[2]);
  309. }
  310. ConsoleMethod(GuiInspector, clearHiddenFields, void, 2, 2, "() Clears the list of hidden fields\n"
  311. "@return No return value.")
  312. {
  313. object->clearHiddenField();
  314. }
  315. ConsoleMethod(GuiInspector, addHiddenField, void, 3, 3, "() Adds a new field to the list of hidden fields\n"
  316. "@return No return value.")
  317. {
  318. object->addHiddenField(argv[2]);
  319. }
  320. #pragma endregion
  321. #pragma region GuiInspectorField
  322. //////////////////////////////////////////////////////////////////////////
  323. // GuiInspectorField
  324. //////////////////////////////////////////////////////////////////////////
  325. // The GuiInspectorField control is a representation of a single abstract
  326. // field for a given ConsoleObject derived object. It handles creation
  327. // getting and setting of it's fields data and editing control.
  328. //
  329. // Creation of custom edit controls is done through this class and is
  330. // dependent upon the dynamic console type, which may be defined to be
  331. // custom for different types.
  332. //
  333. // Note : GuiInspectorField controls must have a GuiInspectorGroup as their
  334. // parent.
  335. IMPLEMENT_CONOBJECT(GuiInspectorField);
  336. GuiInspectorField::GuiInspectorField( GuiInspectorGroup* parent, SimObjectPtr<SimObject> target, AbstractClassRep::Field* field )
  337. {
  338. if( field != NULL )
  339. mText = StringTable->insert( field->pFieldname );
  340. else
  341. mText = StringTable->EmptyString;
  342. mGroup = parent;
  343. mTarget = target;
  344. mField = field;
  345. mCanSave = false;
  346. mFieldArrayIndex = NULL;
  347. }
  348. GuiInspectorField::GuiInspectorField()
  349. {
  350. mText = StringTable->EmptyString;
  351. mGroup = NULL;
  352. mTarget = NULL;
  353. mField = NULL;
  354. mFieldArrayIndex = NULL;
  355. mCanSave = false;
  356. }
  357. GuiInspectorField::~GuiInspectorField()
  358. {
  359. }
  360. //////////////////////////////////////////////////////////////////////////
  361. // Get/Set Data Functions
  362. //////////////////////////////////////////////////////////////////////////
  363. void GuiInspectorField::setData( const char* data )
  364. {
  365. if( mField == NULL || mTarget == NULL )
  366. return;
  367. mTarget->inspectPreApply();
  368. mTarget->setDataField( mField->pFieldname, mFieldArrayIndex, data );
  369. // Force our edit to update
  370. updateValue( data );
  371. mTarget->inspectPostApply();
  372. }
  373. const char* GuiInspectorField::getData()
  374. {
  375. if( mField == NULL || mTarget == NULL )
  376. return "";
  377. return mTarget->getDataField( mField->pFieldname, mFieldArrayIndex );
  378. }
  379. void GuiInspectorField::setInspectorField( AbstractClassRep::Field *field, const char*arrayIndex )
  380. {
  381. mField = field;
  382. if( arrayIndex != NULL )
  383. {
  384. mFieldArrayIndex = StringTable->insert( arrayIndex );
  385. S32 frameTempSize = dStrlen( field->pFieldname ) + 32;
  386. FrameTemp<char> valCopy( frameTempSize );
  387. dSprintf( (char *)valCopy, frameTempSize, "%s%s", field->pFieldname, arrayIndex );
  388. mText = StringTable->insert( valCopy );
  389. }
  390. else
  391. mText = StringTable->insert( field->pFieldname );
  392. }
  393. StringTableEntry GuiInspectorField::getFieldName()
  394. {
  395. // Sanity
  396. if ( mField == NULL )
  397. return StringTable->EmptyString;
  398. // Array element?
  399. if( mFieldArrayIndex != NULL )
  400. {
  401. S32 frameTempSize = dStrlen( mField->pFieldname ) + 32;
  402. FrameTemp<char> valCopy( frameTempSize );
  403. dSprintf( (char *)valCopy, frameTempSize, "%s%s:", mField->pFieldname, mFieldArrayIndex );
  404. // Return formatted element
  405. return StringTable->insert( valCopy );
  406. }
  407. // Plain ole field name.
  408. return mField->pFieldname;
  409. };
  410. //////////////////////////////////////////////////////////////////////////
  411. // Overrideables for custom edit fields
  412. //////////////////////////////////////////////////////////////////////////
  413. GuiControl* GuiInspectorField::constructEditControl(S32 width)
  414. {
  415. GuiControl* retCtrl = new GuiTextEditCtrl();
  416. // If we couldn't construct the control, bail!
  417. if( retCtrl == NULL )
  418. return retCtrl;
  419. // Let's make it look pretty.
  420. retCtrl->setControlProfile(mGroup->mInspector->mTextEditProfile);
  421. // Don't forget to register ourselves
  422. registerEditControl( retCtrl );
  423. char szBuffer[512];
  424. dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() );
  425. retCtrl->setField("AltCommand", szBuffer );
  426. retCtrl->mBounds.set(mGroup->mInspector->mControlOffset, Point2I(width - mGroup->mInspector->mControlOffset.x, 30));
  427. return retCtrl;
  428. }
  429. void GuiInspectorField::registerEditControl( GuiControl *ctrl )
  430. {
  431. if(!mTarget)
  432. return;
  433. char szName[512];
  434. dSprintf( szName, 512, "IE_%s_%d_%s_Field", ctrl->getClassName(), mTarget->getId(),mText);
  435. // Register the object
  436. ctrl->registerObject( szName );
  437. }
  438. bool GuiInspectorField::onAdd()
  439. {
  440. if( !Parent::onAdd() )
  441. return false;
  442. if( !mTarget )
  443. return false;
  444. setControlProfile(mGroup->mInspector->mLabelProfile);
  445. //Find the target width
  446. RectI innerRect = getInnerRect(Point2I(0,0), mGroup->mInspector->mFieldCellSize, NormalState, mProfile);
  447. mEdit = constructEditControl(innerRect.extent.x);
  448. if( mEdit == NULL )
  449. return false;
  450. innerRect.extent.y = mGroup->mInspector->mControlOffset.y + mEdit->getExtent().y;
  451. Point2I outerExt = getOuterExtent(innerRect.extent, NormalState, mProfile);
  452. mBounds.extent.y = outerExt.y;
  453. //Set the tool tip if possible
  454. if (mField != NULL && mField->pFieldDocs != NULL)
  455. {
  456. mEdit->setField("tooltip", mField->pFieldDocs);
  457. if (mGroup->mInspector->mTooltipProfile != NULL)
  458. {
  459. char buffer[256];
  460. dSprintf(buffer, sizeof(buffer), "%d", mGroup->mInspector->mTooltipProfile->getId());
  461. mEdit->setField("tooltipProfile", buffer);
  462. }
  463. }
  464. addObject( mEdit );
  465. // Force our editField to set it's value
  466. updateValue( getData() );
  467. return true;
  468. }
  469. void GuiInspectorField::updateValue( const char* newValue )
  470. {
  471. GuiTextEditCtrl *ctrl = dynamic_cast<GuiTextEditCtrl*>( mEdit );
  472. if( ctrl != NULL )
  473. ctrl->setText( newValue );
  474. }
  475. ConsoleMethod( GuiInspectorField, apply, void, 3,3, "(newValue) Applies the given value to the field\n"
  476. "@return No return value." )
  477. {
  478. object->setData( argv[2] );
  479. }
  480. #pragma endregion
  481. #pragma region GuiInspectorGroup
  482. //////////////////////////////////////////////////////////////////////////
  483. // GuiInspectorGroup
  484. //////////////////////////////////////////////////////////////////////////
  485. //
  486. // The GuiInspectorGroup control is a helper control that the inspector
  487. // makes use of which houses a collapsible panel type control for separating
  488. // inspected objects fields into groups. The content of the inspector is
  489. // made up of zero or more GuiInspectorGroup controls inside of a GuiChainControl
  490. //
  491. //
  492. //
  493. IMPLEMENT_CONOBJECT(GuiInspectorGroup);
  494. GuiInspectorGroup::GuiInspectorGroup()
  495. {
  496. mChildren.clear();
  497. mTarget = NULL;
  498. mInspector = NULL;
  499. mCanSave = false;
  500. mHorizSizing = horizSizingOptions::horizResizeWidth;
  501. mVertSizing = vertSizingOptions::vertResizeTop;
  502. mBounds.set(10, 0, (mInspector->getExtent().x - 20), 24);
  503. }
  504. GuiInspectorGroup::GuiInspectorGroup( SimObjectPtr<SimObject> target, StringTableEntry groupName, SimObjectPtr<GuiInspector> inspector )
  505. {
  506. mChildren.clear();
  507. mText = StringTable->insert(groupName);
  508. mTarget = target;
  509. mInspector = inspector;
  510. mCanSave = false;
  511. mHorizSizing = horizSizingOptions::horizResizeWidth;
  512. mVertSizing = vertSizingOptions::vertResizeTop;
  513. mBounds.set(10, 0, (mInspector->getExtent().x - 20), 24);
  514. }
  515. bool GuiInspectorGroup::onAdd()
  516. {
  517. if( !Parent::onAdd() )
  518. return false;
  519. setControlProfile(mInspector->mGroupPanelProfile);
  520. // Create our inner controls. Allow subclasses to provide other content.
  521. if(!createContent())
  522. return false;
  523. inspectGroup();
  524. return true;
  525. }
  526. bool GuiInspectorGroup::createContent()
  527. {
  528. // Create our field stack control
  529. mGrid = new GuiGridCtrl();
  530. if( !mGrid)
  531. return false;
  532. mGrid->setControlProfile(mInspector->mGroupGridProfile);
  533. mGrid->setCellSize(mInspector->mFieldCellSize.x, mInspector->mFieldCellSize.y);
  534. mGrid->setCellSpacing(0,0);
  535. mGrid->setCellModeX(GuiGridCtrl::CellMode::Variable);
  536. mGrid->setCellModeY(GuiGridCtrl::CellMode::Variable);
  537. mGrid->setMaxColCount(0);
  538. mGrid->mIsExtentDynamic = true;
  539. mGrid->mOrderMode = GuiGridCtrl::OrderMode::LRTB;
  540. mGrid->setWidth(getExtent().x - 20);
  541. mGrid->setPosition(Point2I(10, getExtent().y));
  542. mGrid->setField("horizSizing", "width");
  543. mGrid->setField("vertSizing", "bottom");
  544. mGrid->registerObject();
  545. addObject( mGrid );
  546. return true;
  547. }
  548. GuiInspectorField* GuiInspectorGroup::constructField( S32 fieldType )
  549. {
  550. ConsoleBaseType *cbt = ConsoleBaseType::getType(fieldType);
  551. AssertFatal(cbt, "GuiInspectorGroup::constructField - could not resolve field type!");
  552. // Alright, is it a datablock?
  553. if(cbt->isDatablock())
  554. {
  555. // This is fairly straightforward to deal with.
  556. GuiInspectorDatablockField *dbFieldClass = new GuiInspectorDatablockField( cbt->getTypeClassName() );
  557. if( dbFieldClass != NULL )
  558. {
  559. // return our new datablock field with correct datablock type enumeration info
  560. return dbFieldClass;
  561. }
  562. }
  563. // Nope, not a datablock. So maybe it has a valid inspector field override we can use?
  564. if(!cbt->getInspectorFieldType())
  565. // Nothing, so bail.
  566. return NULL;
  567. // Otherwise try to make it!
  568. ConsoleObject *co = create(cbt->getInspectorFieldType());
  569. GuiInspectorField *gif = dynamic_cast<GuiInspectorField*>(co);
  570. if(!gif)
  571. {
  572. // Wasn't appropriate type, bail.
  573. delete co;
  574. return NULL;
  575. }
  576. return gif;
  577. }
  578. GuiInspectorField *GuiInspectorGroup::findField( StringTableEntry fieldName )
  579. {
  580. // If we don't have any field children we can't very well find one then can we?
  581. if( mChildren.empty() )
  582. return NULL;
  583. Vector<GuiInspectorField*>::iterator i = mChildren.begin();
  584. for( ; i != mChildren.end(); i++ )
  585. {
  586. if( (*i)->getFieldName() != NULL && dStricmp( (*i)->getFieldName(), fieldName ) == 0 )
  587. return (*i);
  588. }
  589. return NULL;
  590. }
  591. bool GuiInspectorGroup::inspectGroup()
  592. {
  593. // We can't inspect a group without a target!
  594. if( !mTarget )
  595. return false;
  596. // to prevent crazy resizing, we'll just freeze our stack for a sec..
  597. //mStack->freeze(true);
  598. bool bNoGroup = false;
  599. // Un-grouped fields are all sorted into the 'general' group
  600. if ( dStricmp( mText, "General" ) == 0 )
  601. bNoGroup = true;
  602. AbstractClassRep::FieldList &fieldList = mTarget->getModifiableFieldList();
  603. AbstractClassRep::FieldList::iterator itr;
  604. bool bGrabItems = false;
  605. bool bNewItems = false;
  606. for(itr = fieldList.begin(); itr != fieldList.end(); itr++)
  607. {
  608. if( itr->type == AbstractClassRep::StartGroupFieldType )
  609. {
  610. // If we're dealing with general fields, always set grabItems to true (to skip them)
  611. if( bNoGroup == true || ( itr->pGroupname != NULL && dStricmp( itr->pGroupname, mText ) == 0 ))
  612. bGrabItems = true;
  613. continue;
  614. }
  615. else if ( itr->type == AbstractClassRep::EndGroupFieldType )
  616. {
  617. // If we're dealing with general fields, always set grabItems to false (to grab them)
  618. if( bNoGroup == true || ( itr->pGroupname != NULL && dStricmp( itr->pGroupname, mText ) == 0 ))
  619. bGrabItems = false;
  620. continue;
  621. }
  622. if((bGrabItems == true || (bNoGroup == true && bGrabItems == false)) && itr->type != AbstractClassRep::DepricatedFieldType )
  623. {
  624. //We are inside a group and looking for items that don't have a group. Move on.
  625. if( bNoGroup == true && bGrabItems == true )
  626. continue;
  627. // We are going to check to see if this item is an array
  628. // if so, we're going to construct a field for each array element
  629. if( itr->elementCount > 1 )
  630. {
  631. for(S32 nI = 0; nI < itr->elementCount; nI++)
  632. {
  633. FrameTemp<char> intToStr( 64 );
  634. dSprintf( intToStr, 64, "%d", nI );
  635. const char *val = mTarget->getDataField( itr->pFieldname, intToStr );
  636. if (!val)
  637. val = StringTable->EmptyString;
  638. // Copy Val and construct proper ValueName[nI] format
  639. // which is "ValueName0" for index 0, etc.
  640. S32 frameTempSize = dStrlen( val ) + 32;
  641. FrameTemp<char> valCopy( frameTempSize );
  642. dSprintf( (char *)valCopy, frameTempSize, "%s%d", itr->pFieldname, nI );
  643. // If the field already exists, just update it
  644. GuiInspectorField *field = findField( valCopy );
  645. if( field != NULL )
  646. {
  647. field->updateValue( field->getData() );
  648. continue;
  649. }
  650. bNewItems = true;
  651. field = constructField( itr->type );
  652. if( field == NULL )
  653. {
  654. field = new GuiInspectorField( this, mTarget, itr );
  655. field->setExtent(Point2I(this->getExtent().x,30));
  656. field->setInspectorField( itr, intToStr );
  657. }
  658. else
  659. {
  660. field->setExtent(Point2I(this->getExtent().x, 30));
  661. field->setTarget( mTarget );
  662. field->setInspectorGroup( this );
  663. field->setInspectorField( itr, intToStr );
  664. }
  665. field->registerObject();
  666. mChildren.push_back( field );
  667. mGrid->addObject( field );
  668. }
  669. }
  670. else
  671. {
  672. // If the field already exists, just update it
  673. GuiInspectorField *field = findField( itr->pFieldname );
  674. if( field != NULL )
  675. {
  676. field->updateValue( field->getData() );
  677. continue;
  678. }
  679. bNewItems = true;
  680. //check the hidden field list
  681. if (mInspector->hideField(itr->pFieldname))
  682. {
  683. continue;
  684. }
  685. //Time to create a new field
  686. field = constructField( itr->type );
  687. if( field == NULL )
  688. field = new GuiInspectorField( this, mTarget, itr );
  689. else
  690. {
  691. field->setTarget( mTarget );
  692. field->setInspectorGroup( this );
  693. field->setInspectorField( itr );
  694. }
  695. field->setExtent(Point2I(this->getExtent().x, 30));
  696. field->registerObject();
  697. mChildren.push_back( field );
  698. mGrid->addObject( field );
  699. }
  700. }
  701. }
  702. // If we've no new items, there's no need to resize anything!
  703. if( bNewItems == false && !mChildren.empty() )
  704. return true;
  705. //sizeToContents();
  706. setUpdate();
  707. return true;
  708. }
  709. #pragma endregion
  710. #pragma region GuiInspectorDynamicGroup
  711. IMPLEMENT_CONOBJECT(GuiInspectorDynamicGroup);
  712. //////////////////////////////////////////////////////////////////////////
  713. // GuiInspectorDynamicGroup - add custom controls
  714. //////////////////////////////////////////////////////////////////////////
  715. bool GuiInspectorDynamicGroup::createContent()
  716. {
  717. if(!Parent::createContent())
  718. return false;
  719. // add a button that lets us add new dynamic fields.
  720. GuiButtonCtrl* addFieldBtn = new GuiButtonCtrl();
  721. {
  722. SimObject* profilePtr = Sim::findObject("EditorButton");
  723. if( profilePtr != NULL )
  724. addFieldBtn->setControlProfile( dynamic_cast<GuiControlProfile*>(profilePtr) );
  725. char commandBuf[64];
  726. dSprintf(commandBuf, 64, "%d.addDynamicField();", this->getId());
  727. addFieldBtn->setField("profile", "GuiButtonDynProfile");
  728. addFieldBtn->setField("command", commandBuf);
  729. addFieldBtn->setField("text", "+");
  730. addFieldBtn->setExtent(Point2I(30, 30));
  731. addFieldBtn->registerObject("zAddButton");
  732. }
  733. // encapsulate the button in a dummy control.
  734. GuiControl* shell = new GuiControl();
  735. shell->setField( "profile", "GuiTextProfile" );
  736. shell->registerObject();
  737. shell->setField("text", "Add Field");
  738. shell->setExtent(Point2I(getExtent().x - 30,30 + 10));
  739. addFieldBtn->setPosition(Point2I((shell->getExtent().x) - 30, 0));
  740. shell->addObject(addFieldBtn);
  741. // save off the shell control, so we can push it to the bottom of the stack in inspectGroup()
  742. mAddCtrl = shell;
  743. mGrid->addObject(shell);
  744. return true;
  745. }
  746. static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b)
  747. {
  748. SimFieldDictionary::Entry *fa = *((SimFieldDictionary::Entry **)a);
  749. SimFieldDictionary::Entry *fb = *((SimFieldDictionary::Entry **)b);
  750. return dStricmp(fa->slotName, fb->slotName);
  751. }
  752. //////////////////////////////////////////////////////////////////////////
  753. // GuiInspectorDynamicGroup - inspectGroup override
  754. //////////////////////////////////////////////////////////////////////////
  755. bool GuiInspectorDynamicGroup::inspectGroup()
  756. {
  757. // We can't inspect a group without a target!
  758. if( !mTarget )
  759. return false;
  760. // Clearing the fields and recreating them will more than likely be more
  761. // efficient than looking up existent fields, updating them, and then iterating
  762. // over existent fields and making sure they still exist, if not, deleting them.
  763. clearFields();
  764. // Create a vector of the fields
  765. Vector<SimFieldDictionary::Entry *> flist;
  766. // Then populate with fields
  767. SimFieldDictionary * fieldDictionary = mTarget->getFieldDictionary();
  768. for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
  769. {
  770. flist.push_back(*ditr);
  771. }
  772. dQsort(flist.address(),flist.size(),sizeof(SimFieldDictionary::Entry *),compareEntries);
  773. for(U32 i = 0; i < (U32)flist.size(); i++)
  774. {
  775. SimFieldDictionary::Entry * entry = flist[i];
  776. GuiInspectorField *field = new GuiInspectorDynamicField( this, mTarget, entry );
  777. if( field != NULL )
  778. {
  779. field->setExtent(Point2I(this->getExtent().x, 30));
  780. field->registerObject();
  781. mChildren.push_back( field );
  782. mGrid->addObject( field );
  783. }
  784. }
  785. mGrid->pushObjectToBack(mAddCtrl);
  786. setUpdate();
  787. return true;
  788. }
  789. ConsoleMethod(GuiInspectorDynamicGroup, inspectGroup, bool, 2, 2, "() Refreshes the dynamic fields in the inspector.\n"
  790. "@return Returns true on success.")
  791. {
  792. return object->inspectGroup();
  793. }
  794. void GuiInspectorDynamicGroup::clearFields()
  795. {
  796. // save mAddCtrl
  797. Sim::getGuiGroup()->addObject(mAddCtrl);
  798. // delete everything else
  799. mGrid->clear();
  800. // clear the mChildren list.
  801. mChildren.clear();
  802. // and restore.
  803. mGrid->addObject(mAddCtrl);
  804. }
  805. SimFieldDictionary::Entry* GuiInspectorDynamicGroup::findDynamicFieldInDictionary( StringTableEntry fieldName )
  806. {
  807. if( !mTarget )
  808. return NULL;
  809. SimFieldDictionary * fieldDictionary = mTarget->getFieldDictionary();
  810. for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
  811. {
  812. SimFieldDictionary::Entry * entry = (*ditr);
  813. if( dStricmp( entry->slotName, fieldName ) == 0 )
  814. return entry;
  815. }
  816. return NULL;
  817. }
  818. void GuiInspectorDynamicGroup::addDynamicField()
  819. {
  820. // We can't add a field without a target
  821. if( !mTarget || !mGrid)
  822. {
  823. Con::warnf("GuiInspectorDynamicGroup::addDynamicField - no target SimObject to add a dynamic field to.");
  824. return;
  825. }
  826. // find a field name that is not in use.
  827. // But we wont try more than 100 times to find an available field.
  828. U32 uid = 1;
  829. char buf[64] = "dynamicField";
  830. SimFieldDictionary::Entry* entry = findDynamicFieldInDictionary(buf);
  831. while(entry != NULL && uid < 100)
  832. {
  833. dSprintf(buf, sizeof(buf), "dynamicField%03d", uid++);
  834. entry = findDynamicFieldInDictionary(buf);
  835. }
  836. //Con::evaluatef( "%d.%s = \"defaultValue\";", mTarget->getId(), buf );
  837. mTarget->setDataField(StringTable->insert(buf), NULL, "defaultValue");
  838. // now we simply re-inspect the object, to see the new field.
  839. this->inspectGroup();
  840. //animateToContents();
  841. }
  842. ConsoleMethod( GuiInspectorDynamicGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();" )
  843. {
  844. object->addDynamicField();
  845. }
  846. #pragma endregion
  847. #pragma region GuiInspectorDynamicField
  848. //////////////////////////////////////////////////////////////////////////
  849. // GuiInspectorDynamicField - Child class of GuiInspectorField
  850. //////////////////////////////////////////////////////////////////////////
  851. IMPLEMENT_CONOBJECT(GuiInspectorDynamicField);
  852. GuiInspectorDynamicField::GuiInspectorDynamicField( GuiInspectorGroup* parent, SimObjectPtr<SimObject> target, SimFieldDictionary::Entry* field )
  853. {
  854. mGroup = parent;
  855. mTarget = target;
  856. mDynField = field;
  857. mRenameCtrl = NULL;
  858. }
  859. void GuiInspectorDynamicField::setData( const char* data )
  860. {
  861. if( mTarget == NULL || mDynField == NULL )
  862. return;
  863. char buf[1024];
  864. const char * newValue = mEdit->getScriptValue();
  865. dStrcpy( buf, newValue ? newValue : "" );
  866. collapseEscape(buf);
  867. mTarget->getFieldDictionary()->setFieldValue(mDynField->slotName, buf);
  868. // Force our edit to update
  869. updateValue( data );
  870. }
  871. const char* GuiInspectorDynamicField::getData()
  872. {
  873. if( mTarget == NULL || mDynField == NULL )
  874. return "";
  875. return mTarget->getFieldDictionary()->getFieldValue( mDynField->slotName );
  876. }
  877. void GuiInspectorDynamicField::renameField( StringTableEntry newFieldName )
  878. {
  879. if( mTarget == NULL || mDynField == NULL || mGroup == NULL || mEdit == NULL )
  880. {
  881. Con::warnf("GuiInspectorDynamicField::renameField - No target object or dynamic field data found!" );
  882. return;
  883. }
  884. if( !newFieldName )
  885. {
  886. Con::warnf("GuiInspectorDynamicField::renameField - Invalid field name specified!" );
  887. return;
  888. }
  889. // Only proceed if the name has changed
  890. if( dStricmp( newFieldName, getFieldName() ) == 0 )
  891. return;
  892. // Grab a pointer to our parent and cast it to GuiInspectorDynamicGroup
  893. GuiInspectorDynamicGroup *group = dynamic_cast<GuiInspectorDynamicGroup*>(mGroup);
  894. if( group == NULL )
  895. {
  896. Con::warnf("GuiInspectorDynamicField::renameField - Unable to locate GuiInspectorDynamicGroup parent!" );
  897. return;
  898. }
  899. // Grab our current dynamic field value
  900. const char* currentValue = getData();
  901. // Create our new field with the value of our old field and the new fields name!
  902. mTarget->setDataField( newFieldName, NULL, currentValue );
  903. // Configure our field to grab data from the new dynamic field
  904. SimFieldDictionary::Entry *newEntry = group->findDynamicFieldInDictionary( newFieldName );
  905. if( newEntry == NULL )
  906. {
  907. Con::warnf("GuiInspectorDynamicField::renameField - Unable to find new field!" );
  908. return;
  909. }
  910. // Set our old fields data to "" (which will effectively erase the field)
  911. mTarget->setDataField( getFieldName(), NULL, "" );
  912. // Assign our dynamic field pointer (where we retrieve field information from) to our new field pointer
  913. mDynField = newEntry;
  914. // Lastly we need to reassign our AltCommand fields for our value edit control
  915. char szBuffer[512];
  916. dSprintf( szBuffer, 512, "%d.%s = %d.getText();",mTarget->getId(), getFieldName(), mEdit->getId() );
  917. mEdit->setExtent(Point2I((getExtent().x / 2) - 20, 30));
  918. mEdit->setField("AltCommand", szBuffer );
  919. }
  920. ConsoleMethod( GuiInspectorDynamicField, renameField, void, 3,3, "field.renameField(newDynamicFieldName);" )
  921. {
  922. object->renameField( StringTable->insert(argv[2]) );
  923. }
  924. bool GuiInspectorDynamicField::onAdd()
  925. {
  926. if( !Parent::onAdd() )
  927. return false;
  928. mRenameCtrl = constructRenameControl();
  929. pushObjectToBack(mEdit);
  930. return true;
  931. }
  932. GuiControl* GuiInspectorDynamicField::constructRenameControl()
  933. {
  934. // Create our renaming field
  935. GuiControl* retCtrl = new GuiTextEditCtrl();
  936. // If we couldn't construct the control, bail!
  937. if( retCtrl == NULL )
  938. return retCtrl;
  939. // Let's make it look pretty.
  940. retCtrl->setField( "profile", "GuiTextEditProfile" );
  941. // Don't forget to register ourselves
  942. char szName[512];
  943. dSprintf( szName, 512, "IE_%s_%d_%s_Rename", retCtrl->getClassName(), mTarget->getId(), getFieldName() );
  944. retCtrl->registerObject( szName );
  945. // Our command will evaluate to :
  946. //
  947. // if( (editCtrl).getText() !$= "" )
  948. // (field).renameField((editCtrl).getText());
  949. //
  950. char szBuffer[512];
  951. dSprintf( szBuffer, 512, "if( %d.getText() !$= \"\" ) %d.renameField(%d.getText());",retCtrl->getId(), getId(), retCtrl->getId() );
  952. dynamic_cast<GuiTextEditCtrl*>(retCtrl)->setText( getFieldName() );
  953. retCtrl->setExtent(Point2I((getExtent().x / 2) - 20, 30));
  954. retCtrl->setField("AltCommand", szBuffer );
  955. addObject( retCtrl );
  956. // Finally, add a delete button for this field
  957. GuiButtonCtrl * delButt = new GuiButtonCtrl();
  958. if( delButt != NULL )
  959. {
  960. dSprintf(szBuffer, 512, "%d.%s = \"\";%d.inspectGroup();", mTarget->getId(), getFieldName(), mGroup->getId());
  961. delButt->setField("profile", "GuiButtonDynProfile");
  962. delButt->setField("Text", "X");
  963. delButt->setPosition(Point2I((getExtent().x - 40), 0));
  964. delButt->setField("extent", "30 30");
  965. delButt->setField("Command", szBuffer);
  966. delButt->registerObject();
  967. addObject(delButt);
  968. }
  969. return retCtrl;
  970. }
  971. void GuiInspectorDynamicField::resize( const Point2I &newPosition, const Point2I &newExtent )
  972. {
  973. Parent::resize( newPosition, newExtent );
  974. }
  975. #pragma endregion
  976. #pragma region GuiInspectorDatablockField
  977. //////////////////////////////////////////////////////////////////////////
  978. // GuiInspectorDatablockField
  979. // Field construction for datablock types
  980. //////////////////////////////////////////////////////////////////////////
  981. IMPLEMENT_CONOBJECT(GuiInspectorDatablockField);
  982. static S32 QSORT_CALLBACK stringCompare(const void *a,const void *b)
  983. {
  984. StringTableEntry sa = *(StringTableEntry*)a;
  985. StringTableEntry sb = *(StringTableEntry*)b;
  986. return(dStricmp(sb, sa));
  987. }
  988. GuiInspectorDatablockField::GuiInspectorDatablockField( StringTableEntry className )
  989. {
  990. setClassName(className);
  991. };
  992. void GuiInspectorDatablockField::setClassName( StringTableEntry className )
  993. {
  994. // Walk the ACR list and find a matching class if any.
  995. AbstractClassRep *walk = AbstractClassRep::getClassList();
  996. while(walk)
  997. {
  998. if(!dStricmp(walk->getClassName(), className))
  999. {
  1000. // Match!
  1001. mDesiredClass = walk;
  1002. return;
  1003. }
  1004. walk = walk->getNextClass();
  1005. }
  1006. // No dice.
  1007. Con::warnf("GuiInspectorDatablockField::setClassName - no class '%s' found!", className);
  1008. return;
  1009. }
  1010. GuiControl* GuiInspectorDatablockField::constructEditControl()
  1011. {
  1012. GuiControl* retCtrl = new GuiDropDownCtrl();
  1013. // If we couldn't construct the control, bail!
  1014. if( retCtrl == NULL )
  1015. return retCtrl;
  1016. GuiDropDownCtrl *menu = dynamic_cast<GuiDropDownCtrl*>(retCtrl);
  1017. menu->setField("text", getData());
  1018. registerEditControl( retCtrl );
  1019. // Configure it to update our value when the dropdown is closed
  1020. char szBuffer[512];
  1021. dSprintf( szBuffer, 512, "%d.%s = %d.getText();%d.inspect(%d);",mTarget->getId(), mField->pFieldname, menu->getId(), mGroup->mInspector->getId(), mTarget->getId() );
  1022. menu->setField("Command", szBuffer );
  1023. Vector<StringTableEntry> entries;
  1024. SimDataBlockGroup * grp = Sim::getDataBlockGroup();
  1025. for(SimDataBlockGroup::iterator i = grp->begin(); i != grp->end(); i++)
  1026. {
  1027. SimDataBlock * datablock = dynamic_cast<SimDataBlock*>(*i);
  1028. // Skip non-datablocks if we somehow encounter them.
  1029. if(!datablock)
  1030. continue;
  1031. // Ok, now we have to figure inheritance info.
  1032. if( datablock && datablock->getClassRep()->isClass(mDesiredClass) )
  1033. entries.push_back(datablock->getName());
  1034. }
  1035. // sort the entries
  1036. dQsort(entries.address(), entries.size(), sizeof(StringTableEntry), stringCompare);
  1037. // add them to our enum
  1038. for(U32 j = 0; j < (U32)entries.size(); j++)
  1039. menu->getList()->addItem(entries[j]);
  1040. return retCtrl;
  1041. }
  1042. #pragma endregion