dynamicGroup.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. if( !mParent )
  88. return false;
  89. // clear the first responder if it's set
  90. mStack->clearFirstResponder();
  91. // Clearing the fields and recreating them will more than likely be more
  92. // efficient than looking up existent fields, updating them, and then iterating
  93. // over existent fields and making sure they still exist, if not, deleting them.
  94. clearFields();
  95. // Create a vector of the fields
  96. Vector< FieldEntry > flist;
  97. const U32 numTargets = mParent->getNumInspectObjects();
  98. for( U32 i = 0; i < numTargets; ++ i )
  99. {
  100. SimObject* target = mParent->getInspectObject( i );
  101. // Then populate with fields
  102. SimFieldDictionary * fieldDictionary = target->getFieldDictionary();
  103. for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
  104. {
  105. if( i == 0 )
  106. {
  107. flist.increment();
  108. flist.last().mEntry = *ditr;
  109. flist.last().mNumTargets = 1;
  110. }
  111. else
  112. {
  113. const U32 numFields = flist.size();
  114. for( U32 n = 0; n < numFields; ++ n )
  115. if( flist[ n ].mEntry->slotName == ( *ditr )->slotName )
  116. {
  117. flist[ n ].mNumTargets ++;
  118. break;
  119. }
  120. }
  121. }
  122. }
  123. dQsort( flist.address(), flist.size(), sizeof( FieldEntry ), compareEntries );
  124. for(U32 i = 0; i < flist.size(); i++)
  125. {
  126. if( flist[ i ].mNumTargets != numTargets )
  127. continue;
  128. SimFieldDictionary::Entry* entry = flist[i].mEntry;
  129. // Create a dynamic field inspector. Can't reuse typed GuiInspectorFields as
  130. // these rely on AbstractClassRep::Fields.
  131. GuiInspectorDynamicField *field = new GuiInspectorDynamicField( mParent, this, entry );
  132. // Register the inspector field and add it to our lists
  133. if( field->registerObject() )
  134. {
  135. mChildren.push_back( field );
  136. mStack->addObject( field );
  137. }
  138. else
  139. delete field;
  140. }
  141. mStack->pushObjectToBack(mAddCtrl);
  142. setUpdate();
  143. return true;
  144. }
  145. void GuiInspectorDynamicGroup::updateAllFields()
  146. {
  147. // We overload this to just reinspect the group.
  148. inspectGroup();
  149. }
  150. ConsoleMethod(GuiInspectorDynamicGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.")
  151. {
  152. return object->inspectGroup();
  153. }
  154. void GuiInspectorDynamicGroup::clearFields()
  155. {
  156. // save mAddCtrl
  157. Sim::getGuiGroup()->addObject(mAddCtrl);
  158. // delete everything else
  159. mStack->clear();
  160. // clear the mChildren list.
  161. mChildren.clear();
  162. // and restore.
  163. mStack->addObject(mAddCtrl);
  164. }
  165. SimFieldDictionary::Entry* GuiInspectorDynamicGroup::findDynamicFieldInDictionary( StringTableEntry fieldName )
  166. {
  167. SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary();
  168. for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
  169. {
  170. SimFieldDictionary::Entry * entry = (*ditr);
  171. if( entry->slotName == fieldName )
  172. return entry;
  173. }
  174. return NULL;
  175. }
  176. void GuiInspectorDynamicGroup::addDynamicField()
  177. {
  178. // We can't add a field without a target
  179. if( !mStack )
  180. {
  181. Con::warnf("GuiInspectorDynamicGroup::addDynamicField - no target SimObject to add a dynamic field to.");
  182. return;
  183. }
  184. // find a field name that is not in use.
  185. // But we wont try more than 100 times to find an available field.
  186. U32 uid = 1;
  187. char buf[64] = "dynamicField";
  188. SimFieldDictionary::Entry* entry = findDynamicFieldInDictionary(buf);
  189. while(entry != NULL && uid < 100)
  190. {
  191. dSprintf(buf, sizeof(buf), "dynamicField%03d", uid++);
  192. entry = findDynamicFieldInDictionary(buf);
  193. }
  194. const U32 numTargets = mParent->getNumInspectObjects();
  195. if( numTargets > 1 )
  196. Con::executef( mParent, "onBeginCompoundEdit" );
  197. for( U32 i = 0; i < numTargets; ++ i )
  198. {
  199. SimObject* target = mParent->getInspectObject( i );
  200. Con::evaluatef( "%d.dynamicField = \"defaultValue\";", target->getId(), buf );
  201. // Notify script.
  202. Con::executef( mParent, "onFieldAdded", target->getIdString(), buf );
  203. }
  204. if( numTargets > 1 )
  205. Con::executef( mParent, "onEndCompoundEdit" );
  206. // now we simply re-inspect the object, to see the new field.
  207. inspectGroup();
  208. instantExpand();
  209. }
  210. ConsoleMethod( GuiInspectorDynamicGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();" )
  211. {
  212. object->addDynamicField();
  213. }
  214. ConsoleMethod( GuiInspectorDynamicGroup, removeDynamicField, void, 3, 3, "" )
  215. {
  216. }