dynamicGroup.cpp 8.6 KB

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