dynamicGroup.cpp 8.7 KB

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