//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "gui/editor/inspector/dynamicField.h" #include "gui/editor/inspector/dynamicGroup.h" #include "gui/editor/guiInspector.h" #include "gui/buttons/guiIconButtonCtrl.h" #include "console/engineAPI.h" //----------------------------------------------------------------------------- // GuiInspectorDynamicField - Child class of GuiInspectorField //----------------------------------------------------------------------------- IMPLEMENT_CONOBJECT( GuiInspectorDynamicField ); ConsoleDocClass( GuiInspectorDynamicField, "@brief Custom field type for dynamic variable modification on SimObjects.\n\n" "Editor use only.\n\n" "@internal" ); GuiInspectorDynamicField::GuiInspectorDynamicField( GuiInspector *inspector, GuiInspectorGroup* parent, SimFieldDictionary::Entry* field ) : mRenameCtrl( NULL ), mDeleteButton( NULL ) { mInspector = inspector; mParent = parent; mDynField = field; setBounds(0,0,100,20); } void GuiInspectorDynamicField::setData( const char* data, bool callbacks ) { if ( mDynField == NULL ) return; const U32 numTargets = mInspector->getNumInspectObjects(); if( callbacks && numTargets > 1 ) Con::executef( mInspector, "beginCompoundUndo" ); // Setting an empty string will kill the field. const bool isRemoval = !data[ 0 ]; for( U32 i = 0; i < numTargets; ++ i ) { SimObject* target = mInspector->getInspectObject( i ); // Callback on the inspector when the field is modified // to allow creation of undo/redo actions. const char *oldData = target->getDataField( mDynField->slotName, NULL ); if ( !oldData ) oldData = ""; if ( String::compare( oldData, data ) != 0 ) { target->inspectPreApply(); if( callbacks ) { if( isRemoval ) Con::executef( mInspector, "onFieldRemoved", target->getIdString(), mDynField->slotName ); else Con::executef( mInspector, "onInspectorFieldModified", target->getIdString(), mDynField->slotName, oldData, data ); } target->setDataField( mDynField->slotName, NULL, data ); // give the target a chance to validate target->inspectPostApply(); } } if( callbacks && numTargets > 1 ) Con::executef( mInspector, "endCompoundUndo" ); // Force our edit to update updateValue(); } const char* GuiInspectorDynamicField::getData( U32 inspectObjectIndex ) { if( mDynField == NULL ) return ""; return mInspector->getInspectObject( inspectObjectIndex )->getDataField( mDynField->slotName, NULL ); } void GuiInspectorDynamicField::renameField( const char* newFieldName ) { newFieldName = StringTable->insert( newFieldName ); if ( mDynField == NULL || mParent == NULL || mEdit == NULL ) { Con::warnf("GuiInspectorDynamicField::renameField - No target object or dynamic field data found!" ); return; } if ( !newFieldName ) { Con::warnf("GuiInspectorDynamicField::renameField - Invalid field name specified!" ); return; } // Only proceed if the name has changed if ( dStricmp( newFieldName, getFieldName() ) == 0 ) return; // Grab a pointer to our parent and cast it to GuiInspectorDynamicGroup GuiInspectorDynamicGroup *group = dynamic_cast(mParent); if ( group == NULL ) { Con::warnf("GuiInspectorDynamicField::renameField - Unable to locate GuiInspectorDynamicGroup parent!" ); return; } const U32 numTargets = mInspector->getNumInspectObjects(); if( numTargets > 1 ) Con::executef( mInspector, "onBeginCompoundEdit" ); const char* oldFieldName = getFieldName(); SimFieldDictionary::Entry* newEntry = NULL; for( U32 i = 0; i < numTargets; ++ i ) { SimObject* target = mInspector->getInspectObject( i ); // Make sure the new field is not already defined as a static field // on the object. if( target->isField( newFieldName ) ) { // New field is already defined. If we can, let the scripts handle // the error. Otherwise, just emit an error on the console and proceed. if( numTargets == 1 && mInspector->isMethod( "onFieldRenameAlreadyDefined" ) ) Con::executef( mInspector, "onFieldRenameAlreadyDefined", target->getIdString(), oldFieldName, newFieldName ); else Con::errorf( "GuiInspectorDynamicField::renameField - field '%s' is already defined on %i:%s (%s)", newFieldName, target->getId(), target->getClassName(), target->getName() ); // Reset the text entry. if( mRenameCtrl ) mRenameCtrl->setText( oldFieldName ); continue; } char currentValue[1024] = {0}; // Grab our current dynamic field value (we use a temporary buffer as this gets corrupted upon Con::eval) dSprintf( currentValue, sizeof( currentValue ), "%s", target->getDataField( oldFieldName, NULL ) ); // Unset the old field and set the new field. target->setDataField( oldFieldName, NULL, "" ); target->setDataField( newFieldName, NULL, currentValue ); // Notify script. Con::executef( mInspector, "onFieldRenamed", target->getIdString(), oldFieldName, newFieldName ); // Look up the new SimFieldDictionary entry. if( !newEntry ) { newEntry = target->getFieldDictionary()->findDynamicField( newFieldName ); if( !newEntry ) { Con::warnf( "GuiInspectorDynamicField::renameField - could not find new field '%s' on object %i:%s (%s)", newFieldName, target->getId(), target->getClassName(), target->getName() ); } mDynField = newEntry; } } if( numTargets > 1 ) Con::executef( mInspector, "onEndCompoundEdit" ); // Lastly we need to reassign our validate field for our value edit control char szBuffer[1024]; dSprintf( szBuffer, sizeof( szBuffer ), "%d.apply(%d.getText());", getId(), mEdit->getId() ); mEdit->setField("validate", szBuffer ); if( mDeleteButton ) { dSprintf(szBuffer, sizeof( szBuffer ), "%d.apply("");%d.inspectGroup();", getId(), newFieldName, group->getId()); mDeleteButton->setField("Command", szBuffer); } } bool GuiInspectorDynamicField::onAdd() { if( !Parent::onAdd() ) return false; //pushObjectToBack(mEdit); // Create our renaming field mRenameCtrl = new GuiTextEditCtrl(); mRenameCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorDynamicFieldProfile" ); char szName[512]; dSprintf( szName, 512, "IE_%s_%d_%s_Rename", mRenameCtrl->getClassName(), mInspector->getInspectObject()->getId(), getFieldName() ); mRenameCtrl->registerObject( szName ); // Our command will evaluate to : // // if( (editCtrl).getText() !$= "" ) // (field).renameField((editCtrl).getText()); // char szBuffer[1024]; dSprintf( szBuffer, sizeof( szBuffer ), "if( %d.getText() !$= \"\" ) %d.renameField(%d.getText());", mRenameCtrl->getId(), getId(), mRenameCtrl->getId() ); mRenameCtrl->setText( getFieldName() ); mRenameCtrl->setField("Validate", szBuffer ); addObject( mRenameCtrl ); // Resize the name control to fit in our caption rect mRenameCtrl->resize( mCaptionRect.point, mCaptionRect.extent ); // Resize the value control to leave space for the delete button mEdit->resize( mValueRect.point, mValueRect.extent); // Clear out any caption set from Parent::onAdd // since we are rendering the fieldname with our 'rename' control. mCaption = StringTable->insert( "" ); // Create delete button control mDeleteButton = new GuiBitmapButtonCtrl(); SimObject* profilePtr = Sim::findObject("InspectorDynamicFieldButton"); if( profilePtr != NULL ) mDeleteButton->setControlProfile( dynamic_cast(profilePtr) ); dSprintf( szBuffer, sizeof( szBuffer ), "%d.apply(\"\");%d.schedule(1,\"inspectGroup\");", getId(), mParent->getId() ); // FIXME Hardcoded image mDeleteButton->setField( "Bitmap", "ToolsModule:iconDelete_image" ); mDeleteButton->setField( "Text", "X" ); mDeleteButton->setField( "Command", szBuffer ); mDeleteButton->setSizing( horizResizeLeft, vertResizeCenter ); mDeleteButton->resize(Point2I(getWidth() - 20,2), Point2I(16, 16)); mDeleteButton->registerObject(); addObject(mDeleteButton); return true; } bool GuiInspectorDynamicField::updateRects() { Point2I fieldExtent = getExtent(); S32 dividerPos, dividerMargin; mInspector->getDivider( dividerPos, dividerMargin ); S32 editWidth = dividerPos - dividerMargin; mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, editWidth, fieldExtent.y - 1 ); mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); mValueRect.set( mEditCtrlRect.point, mEditCtrlRect.extent - Point2I( 20, 0 ) ); mDeleteRect.set( fieldExtent.x - 20, 2, 16, fieldExtent.y - 4 ); // This is probably being called during Parent::onAdd // so our special controls haven't been created yet but are just about to // so we just need to calculate the extents. if ( mRenameCtrl == NULL ) return false; bool sized0 = mRenameCtrl->resize( mCaptionRect.point, mCaptionRect.extent ); bool sized1 = mEdit->resize( mValueRect.point, mValueRect.extent ); bool sized2 = mDeleteButton->resize(Point2I(getWidth() - 20,2), Point2I(16, 16)); return ( sized0 || sized1 || sized2 ); } void GuiInspectorDynamicField::setInspectorField( AbstractClassRep::Field *field, StringTableEntry caption, const char*arrayIndex ) { // Override the base just to be sure it doesn't get called. // We don't use an AbstractClassRep::Field... // mField = field; // mCaption = StringTable->EmptyString(); // mRenameCtrl->setText( getFieldName() ); } void GuiInspectorDynamicField::_executeSelectedCallback() { ConsoleBaseType* type = mDynField->type; if ( type ) Con::executef( mInspector, "onFieldSelected", mDynField->slotName, type->getTypeName() ); else Con::executef( mInspector, "onFieldSelected", mDynField->slotName, "TypeDynamicField" ); } DefineEngineMethod( GuiInspectorDynamicField, renameField, void, (const char* newDynamicFieldName),, "field.renameField(newDynamicFieldName);" ) { object->renameField( newDynamicFieldName ); }