dynamicGroup.cpp 8.3 KB

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