dynamicGroup.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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 "gui/buttons/guiIconButtonCtrl.h"
  23. #include "gui/editor/guiInspector.h"
  24. #include "gui/editor/inspector/dynamicGroup.h"
  25. #include "gui/editor/inspector/dynamicField.h"
  26. IMPLEMENT_CONOBJECT(GuiInspectorDynamicGroup);
  27. ConsoleDocClass( GuiInspectorDynamicGroup,
  28. "@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
  29. "of regular persistent fields.\n\n"
  30. "Editor use only.\n\n"
  31. "@internal"
  32. );
  33. //-----------------------------------------------------------------------------
  34. // GuiInspectorDynamicGroup - add custom controls
  35. //-----------------------------------------------------------------------------
  36. bool GuiInspectorDynamicGroup::createContent()
  37. {
  38. if(!Parent::createContent())
  39. return false;
  40. // encapsulate the button in a dummy control.
  41. GuiControl* shell = new GuiControl();
  42. shell->setDataField( StringTable->insert("profile"), NULL, "GuiTransparentProfile" );
  43. if( !shell->registerObject() )
  44. {
  45. delete shell;
  46. return false;
  47. }
  48. // add a button that lets us add new dynamic fields.
  49. GuiBitmapButtonCtrl* addFieldBtn = new GuiBitmapButtonCtrl();
  50. {
  51. SimObject* profilePtr = Sim::findObject("InspectorDynamicFieldButton");
  52. if( profilePtr != NULL )
  53. addFieldBtn->setControlProfile( dynamic_cast<GuiControlProfile*>(profilePtr) );
  54. // FIXME Hardcoded image
  55. addFieldBtn->setBitmap("tools/gui/images/iconAdd.png");
  56. char commandBuf[64];
  57. dSprintf(commandBuf, 64, "%d.addDynamicField();", this->getId());
  58. addFieldBtn->setField("command", commandBuf);
  59. addFieldBtn->setSizing(horizResizeLeft,vertResizeCenter);
  60. //addFieldBtn->setField("buttonMargin", "2 2");
  61. addFieldBtn->resize(Point2I(getWidth() - 20,2), Point2I(16, 16));
  62. addFieldBtn->registerObject("zAddButton");
  63. }
  64. shell->resize(Point2I(0,0), Point2I(getWidth(), 28));
  65. shell->addObject(addFieldBtn);
  66. // save off the shell control, so we can push it to the bottom of the stack in inspectGroup()
  67. mAddCtrl = shell;
  68. mStack->addObject(shell);
  69. return true;
  70. }
  71. struct FieldEntry
  72. {
  73. SimFieldDictionary::Entry* mEntry;
  74. U32 mNumTargets;
  75. };
  76. static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b)
  77. {
  78. FieldEntry& fa = *((FieldEntry *)a);
  79. FieldEntry& fb = *((FieldEntry *)b);
  80. return dStrnatcmp(fa.mEntry->slotName, fb.mEntry->slotName);
  81. }
  82. //-----------------------------------------------------------------------------
  83. // GuiInspectorDynamicGroup - inspectGroup override
  84. //-----------------------------------------------------------------------------
  85. bool GuiInspectorDynamicGroup::inspectGroup()
  86. {
  87. // clear the first responder if it's set
  88. mStack->clearFirstResponder();
  89. // Clearing the fields and recreating them will more than likely be more
  90. // efficient than looking up existent fields, updating them, and then iterating
  91. // over existent fields and making sure they still exist, if not, deleting them.
  92. clearFields();
  93. // Create a vector of the fields
  94. Vector< FieldEntry > flist;
  95. const U32 numTargets = mParent->getNumInspectObjects();
  96. for( U32 i = 0; i < numTargets; ++ i )
  97. {
  98. SimObject* target = mParent->getInspectObject( i );
  99. // Then populate with fields
  100. SimFieldDictionary * fieldDictionary = target->getFieldDictionary();
  101. for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
  102. {
  103. if( i == 0 )
  104. {
  105. flist.increment();
  106. flist.last().mEntry = *ditr;
  107. flist.last().mNumTargets = 1;
  108. }
  109. else
  110. {
  111. const U32 numFields = flist.size();
  112. for( U32 n = 0; n < numFields; ++ n )
  113. if( flist[ n ].mEntry->slotName == ( *ditr )->slotName )
  114. {
  115. flist[ n ].mNumTargets ++;
  116. break;
  117. }
  118. }
  119. }
  120. }
  121. dQsort( flist.address(), flist.size(), sizeof( FieldEntry ), compareEntries );
  122. for(U32 i = 0; i < flist.size(); i++)
  123. {
  124. if( flist[ i ].mNumTargets != numTargets )
  125. continue;
  126. SimFieldDictionary::Entry* entry = flist[i].mEntry;
  127. // Create a dynamic field inspector. Can't reuse typed GuiInspectorFields as
  128. // these rely on AbstractClassRep::Fields.
  129. GuiInspectorDynamicField *field = new GuiInspectorDynamicField( mParent, this, entry );
  130. // Register the inspector field and add it to our lists
  131. if( field->registerObject() )
  132. {
  133. mChildren.push_back( field );
  134. mStack->addObject( field );
  135. }
  136. else
  137. delete field;
  138. }
  139. mStack->pushObjectToBack(mAddCtrl);
  140. setUpdate();
  141. return true;
  142. }
  143. void GuiInspectorDynamicGroup::updateAllFields()
  144. {
  145. // We overload this to just reinspect the group.
  146. inspectGroup();
  147. }
  148. ConsoleMethod(GuiInspectorDynamicGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.")
  149. {
  150. return object->inspectGroup();
  151. }
  152. void GuiInspectorDynamicGroup::clearFields()
  153. {
  154. // save mAddCtrl
  155. Sim::getGuiGroup()->addObject(mAddCtrl);
  156. // delete everything else
  157. mStack->clear();
  158. // clear the mChildren list.
  159. mChildren.clear();
  160. // and restore.
  161. mStack->addObject(mAddCtrl);
  162. }
  163. SimFieldDictionary::Entry* GuiInspectorDynamicGroup::findDynamicFieldInDictionary( StringTableEntry fieldName )
  164. {
  165. SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary();
  166. for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
  167. {
  168. SimFieldDictionary::Entry * entry = (*ditr);
  169. if( entry->slotName == fieldName )
  170. return entry;
  171. }
  172. return NULL;
  173. }
  174. void GuiInspectorDynamicGroup::addDynamicField()
  175. {
  176. // We can't add a field without a target
  177. if( !mStack )
  178. {
  179. Con::warnf("GuiInspectorDynamicGroup::addDynamicField - no target SimObject to add a dynamic field to.");
  180. return;
  181. }
  182. // find a field name that is not in use.
  183. // But we wont try more than 100 times to find an available field.
  184. U32 uid = 1;
  185. char buf[64] = "dynamicField";
  186. SimFieldDictionary::Entry* entry = findDynamicFieldInDictionary(buf);
  187. while(entry != NULL && uid < 100)
  188. {
  189. dSprintf(buf, sizeof(buf), "dynamicField%03d", uid++);
  190. entry = findDynamicFieldInDictionary(buf);
  191. }
  192. const U32 numTargets = mParent->getNumInspectObjects();
  193. if( numTargets > 1 )
  194. Con::executef( mParent, "onBeginCompoundEdit" );
  195. for( U32 i = 0; i < numTargets; ++ i )
  196. {
  197. SimObject* target = mParent->getInspectObject( i );
  198. Con::evaluatef( "%d.dynamicField = \"defaultValue\";", target->getId(), buf );
  199. // Notify script.
  200. Con::executef( mParent, "onFieldAdded", target->getIdString(), buf );
  201. }
  202. if( numTargets > 1 )
  203. Con::executef( mParent, "onEndCompoundEdit" );
  204. // now we simply re-inspect the object, to see the new field.
  205. inspectGroup();
  206. instantExpand();
  207. }
  208. ConsoleMethod( GuiInspectorDynamicGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();" )
  209. {
  210. object->addDynamicField();
  211. }
  212. ConsoleMethod( GuiInspectorDynamicGroup, removeDynamicField, void, 3, 3, "" )
  213. {
  214. }