dynamicField.cpp 12 KB

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