dynamicField.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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/editor/inspector/dynamicField.h"
  23. #include "gui/editor/inspector/dynamicGroup.h"
  24. #include "gui/editor/guiInspector.h"
  25. #include "gui/buttons/guiIconButtonCtrl.h"
  26. //-----------------------------------------------------------------------------
  27. // GuiInspectorDynamicField - Child class of GuiInspectorField
  28. //-----------------------------------------------------------------------------
  29. IMPLEMENT_CONOBJECT( GuiInspectorDynamicField );
  30. ConsoleDocClass( GuiInspectorDynamicField,
  31. "@brief Custom field type for dynamic variable modification on SimObjects.\n\n"
  32. "Editor use only.\n\n"
  33. "@internal"
  34. );
  35. GuiInspectorDynamicField::GuiInspectorDynamicField( GuiInspector *inspector,
  36. GuiInspectorGroup* parent,
  37. SimFieldDictionary::Entry* field )
  38. : mRenameCtrl( NULL ),
  39. mDeleteButton( NULL )
  40. {
  41. mInspector = inspector;
  42. mParent = parent;
  43. mDynField = field;
  44. setBounds(0,0,100,20);
  45. }
  46. void GuiInspectorDynamicField::setData( const char* data, bool callbacks )
  47. {
  48. if ( mDynField == NULL )
  49. return;
  50. const U32 numTargets = mInspector->getNumInspectObjects();
  51. if( callbacks && numTargets > 1 )
  52. Con::executef( mInspector, "beginCompoundUndo" );
  53. // Setting an empty string will kill the field.
  54. const bool isRemoval = !data[ 0 ];
  55. for( U32 i = 0; i < numTargets; ++ i )
  56. {
  57. SimObject* target = mInspector->getInspectObject( i );
  58. // Callback on the inspector when the field is modified
  59. // to allow creation of undo/redo actions.
  60. const char *oldData = target->getDataField( mDynField->slotName, NULL );
  61. if ( !oldData )
  62. oldData = "";
  63. if ( dStrcmp( oldData, data ) != 0 )
  64. {
  65. target->inspectPreApply();
  66. if( callbacks )
  67. {
  68. if( isRemoval )
  69. Con::executef( mInspector, "onFieldRemoved", target->getIdString(), mDynField->slotName );
  70. else
  71. Con::executef( mInspector, "onInspectorFieldModified", target->getIdString(), mDynField->slotName, oldData, data );
  72. }
  73. target->setDataField( mDynField->slotName, NULL, data );
  74. // give the target a chance to validate
  75. target->inspectPostApply();
  76. }
  77. }
  78. if( callbacks && numTargets > 1 )
  79. Con::executef( mInspector, "endCompoundUndo" );
  80. // Force our edit to update
  81. updateValue();
  82. }
  83. const char* GuiInspectorDynamicField::getData( U32 inspectObjectIndex )
  84. {
  85. if( mDynField == NULL )
  86. return "";
  87. return mInspector->getInspectObject( inspectObjectIndex )->getDataField( mDynField->slotName, NULL );
  88. }
  89. void GuiInspectorDynamicField::renameField( const char* newFieldName )
  90. {
  91. newFieldName = StringTable->insert( newFieldName );
  92. if ( mDynField == NULL || mParent == NULL || mEdit == NULL )
  93. {
  94. Con::warnf("GuiInspectorDynamicField::renameField - No target object or dynamic field data found!" );
  95. return;
  96. }
  97. if ( !newFieldName )
  98. {
  99. Con::warnf("GuiInspectorDynamicField::renameField - Invalid field name specified!" );
  100. return;
  101. }
  102. // Only proceed if the name has changed
  103. if ( dStricmp( newFieldName, getFieldName() ) == 0 )
  104. return;
  105. // Grab a pointer to our parent and cast it to GuiInspectorDynamicGroup
  106. GuiInspectorDynamicGroup *group = dynamic_cast<GuiInspectorDynamicGroup*>(mParent);
  107. if ( group == NULL )
  108. {
  109. Con::warnf("GuiInspectorDynamicField::renameField - Unable to locate GuiInspectorDynamicGroup parent!" );
  110. return;
  111. }
  112. const U32 numTargets = mInspector->getNumInspectObjects();
  113. if( numTargets > 1 )
  114. Con::executef( mInspector, "onBeginCompoundEdit" );
  115. const char* oldFieldName = getFieldName();
  116. SimFieldDictionary::Entry* newEntry = NULL;
  117. for( U32 i = 0; i < numTargets; ++ i )
  118. {
  119. SimObject* target = mInspector->getInspectObject( i );
  120. // Make sure the new field is not already defined as a static field
  121. // on the object.
  122. if( target->isField( newFieldName ) )
  123. {
  124. // New field is already defined. If we can, let the scripts handle
  125. // the error. Otherwise, just emit an error on the console and proceed.
  126. if( numTargets == 1 && mInspector->isMethod( "onFieldRenameAlreadyDefined" ) )
  127. Con::executef( mInspector, "onFieldRenameAlreadyDefined", target->getIdString(), oldFieldName, newFieldName );
  128. else
  129. Con::errorf( "GuiInspectorDynamicField::renameField - field '%s' is already defined on %i:%s (%s)",
  130. newFieldName, target->getId(), target->getClassName(), target->getName() );
  131. // Reset the text entry.
  132. if( mRenameCtrl )
  133. mRenameCtrl->setText( oldFieldName );
  134. continue;
  135. }
  136. char currentValue[1024] = {0};
  137. // Grab our current dynamic field value (we use a temporary buffer as this gets corrupted upon Con::eval)
  138. dSprintf( currentValue, sizeof( currentValue ), "%s", target->getDataField( oldFieldName, NULL ) );
  139. // Unset the old field and set the new field.
  140. target->setDataField( oldFieldName, NULL, "" );
  141. target->setDataField( newFieldName, NULL, currentValue );
  142. // Notify script.
  143. Con::executef( mInspector, "onFieldRenamed", target->getIdString(), oldFieldName, newFieldName );
  144. // Look up the new SimFieldDictionary entry.
  145. if( !newEntry )
  146. {
  147. newEntry = target->getFieldDictionary()->findDynamicField( newFieldName );
  148. if( !newEntry )
  149. {
  150. Con::warnf( "GuiInspectorDynamicField::renameField - could not find new field '%s' on object %i:%s (%s)",
  151. newFieldName, target->getId(), target->getClassName(), target->getName() );
  152. }
  153. mDynField = newEntry;
  154. }
  155. }
  156. if( numTargets > 1 )
  157. Con::executef( mInspector, "onEndCompoundEdit" );
  158. // Lastly we need to reassign our validate field for our value edit control
  159. char szBuffer[1024];
  160. dSprintf( szBuffer, sizeof( szBuffer ), "%d.apply(%d.getText());", getId(), mEdit->getId() );
  161. mEdit->setField("validate", szBuffer );
  162. if( mDeleteButton )
  163. {
  164. dSprintf(szBuffer, sizeof( szBuffer ), "%d.apply("");%d.inspectGroup();", getId(), newFieldName, group->getId());
  165. mDeleteButton->setField("Command", szBuffer);
  166. }
  167. }
  168. bool GuiInspectorDynamicField::onAdd()
  169. {
  170. if( !Parent::onAdd() )
  171. return false;
  172. //pushObjectToBack(mEdit);
  173. // Create our renaming field
  174. mRenameCtrl = new GuiTextEditCtrl();
  175. mRenameCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorDynamicFieldProfile" );
  176. char szName[512];
  177. dSprintf( szName, 512, "IE_%s_%d_%s_Rename", mRenameCtrl->getClassName(), mInspector->getInspectObject()->getId(), getFieldName() );
  178. mRenameCtrl->registerObject( szName );
  179. // Our command will evaluate to :
  180. //
  181. // if( (editCtrl).getText() !$= "" )
  182. // (field).renameField((editCtrl).getText());
  183. //
  184. char szBuffer[1024];
  185. dSprintf( szBuffer, sizeof( szBuffer ), "if( %d.getText() !$= \"\" ) %d.renameField(%d.getText());", mRenameCtrl->getId(), getId(), mRenameCtrl->getId() );
  186. mRenameCtrl->setText( getFieldName() );
  187. mRenameCtrl->setField("Validate", szBuffer );
  188. addObject( mRenameCtrl );
  189. // Resize the name control to fit in our caption rect
  190. mRenameCtrl->resize( mCaptionRect.point, mCaptionRect.extent );
  191. // Resize the value control to leave space for the delete button
  192. mEdit->resize( mValueRect.point, mValueRect.extent);
  193. // Clear out any caption set from Parent::onAdd
  194. // since we are rendering the fieldname with our 'rename' control.
  195. mCaption = StringTable->insert( "" );
  196. // Create delete button control
  197. mDeleteButton = new GuiBitmapButtonCtrl();
  198. SimObject* profilePtr = Sim::findObject("InspectorDynamicFieldButton");
  199. if( profilePtr != NULL )
  200. mDeleteButton->setControlProfile( dynamic_cast<GuiControlProfile*>(profilePtr) );
  201. dSprintf( szBuffer, sizeof( szBuffer ),
  202. "%d.apply(\"\");%d.schedule(1,\"inspectGroup\");",
  203. getId(),
  204. mParent->getId() );
  205. // FIXME Hardcoded image
  206. mDeleteButton->setField( "Bitmap", "tools/gui/images/iconDelete" );
  207. mDeleteButton->setField( "Text", "X" );
  208. mDeleteButton->setField( "Command", szBuffer );
  209. mDeleteButton->setSizing( horizResizeLeft, vertResizeCenter );
  210. mDeleteButton->resize(Point2I(getWidth() - 20,2), Point2I(16, 16));
  211. mDeleteButton->registerObject();
  212. addObject(mDeleteButton);
  213. return true;
  214. }
  215. bool GuiInspectorDynamicField::updateRects()
  216. {
  217. Point2I fieldExtent = getExtent();
  218. S32 dividerPos, dividerMargin;
  219. mInspector->getDivider( dividerPos, dividerMargin );
  220. S32 editWidth = dividerPos - dividerMargin;
  221. mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, editWidth, fieldExtent.y - 1 );
  222. mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y );
  223. mValueRect.set( mEditCtrlRect.point, mEditCtrlRect.extent - Point2I( 20, 0 ) );
  224. mDeleteRect.set( fieldExtent.x - 20, 2, 16, fieldExtent.y - 4 );
  225. // This is probably being called during Parent::onAdd
  226. // so our special controls haven't been created yet but are just about to
  227. // so we just need to calculate the extents.
  228. if ( mRenameCtrl == NULL )
  229. return false;
  230. bool sized0 = mRenameCtrl->resize( mCaptionRect.point, mCaptionRect.extent );
  231. bool sized1 = mEdit->resize( mValueRect.point, mValueRect.extent );
  232. bool sized2 = mDeleteButton->resize(Point2I(getWidth() - 20,2), Point2I(16, 16));
  233. return ( sized0 || sized1 || sized2 );
  234. }
  235. void GuiInspectorDynamicField::setInspectorField( AbstractClassRep::Field *field,
  236. StringTableEntry caption,
  237. const char*arrayIndex )
  238. {
  239. // Override the base just to be sure it doesn't get called.
  240. // We don't use an AbstractClassRep::Field...
  241. // mField = field;
  242. // mCaption = StringTable->EmptyString();
  243. // mRenameCtrl->setText( getFieldName() );
  244. }
  245. void GuiInspectorDynamicField::_executeSelectedCallback()
  246. {
  247. ConsoleBaseType* type = mDynField->type;
  248. if ( type )
  249. Con::executef( mInspector, "onFieldSelected", mDynField->slotName, type->getTypeName() );
  250. else
  251. Con::executef( mInspector, "onFieldSelected", mDynField->slotName, "TypeDynamicField" );
  252. }
  253. ConsoleMethod( GuiInspectorDynamicField, renameField, void, 3,3, "field.renameField(newDynamicFieldName);" )
  254. {
  255. object->renameField( argv[ 2 ] );
  256. }