dynamicField.cpp 12 KB

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