You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1327 lines
49 KiB
1327 lines
49 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "HeadSpin.h" |
|
#include "plPythonFileComponent.h" |
|
|
|
#include "resource.h" |
|
#include "plComponent.h" |
|
#include "plComponentReg.h" |
|
#include "MaxMain/plMaxNode.h" |
|
|
|
#include "plAutoUIBlock.h" |
|
#include "plAutoUIParams.h" |
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "MaxMain/plPluginResManager.h" |
|
#include "pnMessage/plObjRefMsg.h" |
|
#include "plVolumeGadgetComponent.h" |
|
#include "plGUIComponents.h" |
|
#include "pfGUISkinComp.h" |
|
#include "plExcludeRegionComponent.h" |
|
#include "plAnimComponent.h" |
|
#include "plNotetrackAnim.h" |
|
#include "plOneShotComponent.h" |
|
#include "plMultistageBehComponent.h" |
|
#include "plInterp/plAnimEaseTypes.h" |
|
#include "plAgeDescription/plAgeDescription.h" |
|
#include "plWaterComponent.h" |
|
#include "plDrawable/plWaveSetBase.h" |
|
#include "plClusterComponent.h" |
|
#include "plDrawable/plClusterGroup.h" |
|
#include "plPhysicalComponents.h" |
|
//#include "plHavok1/plHKPhysical.h" |
|
#include "plAvatar/plSwimRegion.h" |
|
#include "plSurface/plGrassShaderMod.h" |
|
#include "plGrassComponent.h" |
|
|
|
#include "notify.h" |
|
|
|
#include <vector> |
|
#include <set> |
|
#include <map> |
|
#include <string> |
|
|
|
#include "pfPython/plPythonFileMod.h" |
|
#include "pfPython/plPythonParameter.h" |
|
|
|
// for DynamicText hack to get the plKeys (could probably be removed later) |
|
#include "plGImage/plDynamicTextMap.h" |
|
|
|
//// plCommonPythonLib /////////////////////////////////////////////////////// |
|
// Derived class for our global python fileMods, since they go in to the |
|
// BuiltIn page |
|
// 2.4.03 mcn - Added sceneObjects to the list, so that we can have an |
|
// associated sceneObject for our mod |
|
|
|
#include "MaxMain/plCommonObjLib.h" |
|
#include "plSDL/plSDL.h" |
|
|
|
class plCommonPythonLib : public plCommonObjLib |
|
{ |
|
public: |
|
virtual hsBool IsInteresting( const plKey &objectKey ) |
|
{ |
|
if( objectKey->GetUoid().GetClassType() == plPythonFileMod::Index() ) |
|
return true; |
|
if( objectKey->GetUoid().GetClassType() == plSceneObject::Index() && |
|
strcmp( objectKey->GetUoid().GetObjectName(), plSDL::kAgeSDLObjectName ) == 0 ) |
|
return true; |
|
return false; |
|
} |
|
}; |
|
|
|
static plCommonPythonLib sCommonPythonLib; |
|
|
|
|
|
|
|
static void NotifyProc(void *param, NotifyInfo *info); |
|
|
|
// Notify us on file open so we can check for bad Python components |
|
void DummyCodeIncludePythonFileFunc() |
|
{ |
|
RegisterNotification(NotifyProc, nil, NOTIFY_FILE_POST_OPEN); |
|
RegisterNotification(NotifyProc, nil, NOTIFY_FILE_PRE_SAVE); |
|
RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_POST_RESET); |
|
RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_POST_NEW); |
|
RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_SHUTDOWN2); |
|
} |
|
|
|
// Param block ID's |
|
enum |
|
{ |
|
kPythonFileType_DEAD, |
|
kPythonFilePB, |
|
kPythonVersion, |
|
kPythonFileIsGlobal |
|
}; |
|
|
|
class plPythonFileComponent : public plComponent |
|
{ |
|
public: |
|
typedef std::map<plMaxNode*, plKey> PythonKeys; |
|
|
|
// When we're auto-exporting and can't pop up an error dialog, we cache our |
|
// errors here and blab about it at export time |
|
static std::string fPythonError; |
|
|
|
protected: |
|
PythonKeys fModKeys; |
|
hsTArray<plKey> fOthersKeys; |
|
|
|
public: |
|
plPythonFileComponent(); |
|
|
|
virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); |
|
virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); |
|
virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); |
|
|
|
virtual void AddReceiverKey(plKey key, plMaxNode* node=nil) { fOthersKeys.Append(key); } |
|
|
|
// Returns false if the Python file for this component wasn't loaded, and clears |
|
// the data in the PB to prevent Max from crashing. |
|
enum Validate |
|
{ |
|
kPythonOk, |
|
kPythonNoFile, |
|
kPythonBadVer, |
|
kPythonNoVer, |
|
}; |
|
Validate ValidateFile(); |
|
|
|
const char* GetPythonName(); |
|
|
|
virtual PythonKeys& GetKeys() { return fModKeys; } |
|
|
|
virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
fModKeys.clear(); |
|
fOthersKeys.Reset(); |
|
|
|
return plComponent::DeInit(node, pErrMsg); |
|
} |
|
}; |
|
|
|
CLASS_DESC(plPythonFileComponent, gPythonFileComponentDesc, "Python File", "PythonFile", COMP_TYPE_LOGIC, PYTHON_FILE_CID); |
|
|
|
std::string plPythonFileComponent::fPythonError; |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
// Called by the Python File manager |
|
// |
|
|
|
// Storage for all the registered Python file types |
|
static std::vector<plAutoUIBlock*> gAutoUIBlocks; |
|
|
|
void PythonFile::AddAutoUIBlock(plAutoUIBlock *block) |
|
{ |
|
for (int i = 0; i < gAutoUIBlocks.size(); i++) |
|
{ |
|
if (gAutoUIBlocks[i]->GetBlockID() == block->GetBlockID()) |
|
{ |
|
char buf[256]; |
|
sprintf(buf, |
|
"Duplicate Python File ID\n\n" |
|
"%s and %s use ID %d\n\n" |
|
"%s will be ignored.", |
|
gAutoUIBlocks[i]->GetName(), |
|
block->GetName(), |
|
block->GetBlockID(), |
|
block->GetName()); |
|
hsMessageBox(buf, "Warning", hsMBoxOk); |
|
return; |
|
} |
|
} |
|
|
|
gAutoUIBlocks.push_back(block); |
|
} |
|
|
|
plComponentClassDesc *PythonFile::GetClassDesc() |
|
{ |
|
return &gPythonFileComponentDesc; |
|
} |
|
|
|
static plAutoUIBlock *FindAutoUI(IParamBlock2 *pb) |
|
{ |
|
if (!pb) |
|
return nil; |
|
|
|
for (int i = 0; i < gAutoUIBlocks.size(); i++) |
|
{ |
|
if (gAutoUIBlocks[i]->GetBlockID() == pb->ID()) |
|
return gAutoUIBlocks[i]; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
plPythonFileComponent::plPythonFileComponent() |
|
{ |
|
fClassDesc = &gPythonFileComponentDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
hsBool plPythonFileComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
if (fPythonError.length() > 0) |
|
{ |
|
pErrMsg->Set(true, "Python Error", fPythonError.c_str()).Show(); |
|
pErrMsg->Set(); |
|
fPythonError = ""; |
|
} |
|
|
|
fModKeys.clear(); |
|
return true; |
|
} |
|
|
|
hsBool plPythonFileComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); |
|
plAutoUIBlock *block = FindAutoUI(pb); |
|
|
|
if (!block) |
|
return false; |
|
|
|
// if this is a multi-modifier python file component then is this the main (or first) maxnode |
|
bool mainMultiModierNode = false; |
|
|
|
plPythonFileMod *mod = TRACKED_NEW plPythonFileMod; |
|
|
|
// create the modifier key ourselves so that we can get the name of the modifier in the name |
|
plSceneObject *obj = node->GetSceneObject(); |
|
plKey modKey; |
|
if( fCompPB->GetInt( kPythonFileIsGlobal ) ) |
|
{ |
|
// Do we already have this guy? |
|
plPythonFileMod *existingOne = plPythonFileMod::ConvertNoRef( sCommonPythonLib.FindObject( plPythonFileMod::kGlobalNameKonstant ) ); |
|
if( existingOne != nil ) |
|
{ |
|
// This component already exists, which can happen since it's in a common page. So we need |
|
// to nuke the key and its object so we can reuse it. |
|
modKey = existingOne->GetKey(); |
|
|
|
// But first detach it from its target sceneObject |
|
if( existingOne->GetTarget( 0 ) != nil ) |
|
existingOne->GetTarget( 0 )->GetKey()->Release( modKey ); |
|
|
|
if( !sCommonPythonLib.RemoveObjectAndKey( modKey ) ) |
|
{ |
|
pErrMsg->Set( true, "Python File Component Error", |
|
"The global Python File Component %s is attempting to export over an already " |
|
"existing component of the same name, and the exporter is unable to delete the " |
|
"old object to replace it. This would be a good time to call mcn for help.", GetINode()->GetName() ).Show(); |
|
pErrMsg->Set( false ); |
|
} |
|
|
|
// Pointer is invalid now anyways... |
|
existingOne = nil; |
|
} |
|
|
|
// Also make sure we have an age SDL object to attach to (currently only used for python, hence why it's safe here) |
|
obj = plSceneObject::ConvertNoRef( sCommonPythonLib.FindObject( plSDL::kAgeSDLObjectName ) ); |
|
if( obj != nil ) |
|
{ |
|
plKey foo = obj->GetKey(); |
|
if( !sCommonPythonLib.RemoveObjectAndKey( foo ) ) |
|
{ |
|
pErrMsg->Set( true, "Python File Component Error", |
|
"The global Python File Component %s is attempting to export over an already " |
|
"existing component of the same name, and the exporter is unable to delete the " |
|
"old sceneObject to replace it. This would be a good time to call mcn for help.", GetINode()->GetName() ).Show(); |
|
pErrMsg->Set( false ); |
|
} |
|
|
|
// Pointer is invalid now anyways... |
|
obj = nil; |
|
} |
|
|
|
// Create us a new sceneObject to attach to |
|
obj = TRACKED_NEW plSceneObject; |
|
|
|
const plLocation &globalLoc = plPluginResManager::ResMgr()->GetCommonPage( node->GetLocation(), plAgeDescription::kGlobal ); |
|
hsAssert( globalLoc.IsValid(), "Invalid common page location!!!" ); |
|
modKey = hsgResMgr::ResMgr()->NewKey(plPythonFileMod::kGlobalNameKonstant, mod, globalLoc ); |
|
|
|
// Make a key for our special sceneObject too |
|
plKey sdlObjectKey = hsgResMgr::ResMgr()->NewKey( plSDL::kAgeSDLObjectName, obj, globalLoc ); |
|
plPluginResManager::ResMgr()->AddLooseEnd(sdlObjectKey); |
|
} |
|
else |
|
{ |
|
if (block->IsMultiModifier()) |
|
{ |
|
// yup, then only create one modifier that will all the objects will attach to |
|
// in other words, one python module with many attached sceneobjects |
|
if ( fModKeys.empty()) |
|
{ |
|
// its empty so create the first and only one |
|
modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); |
|
mainMultiModierNode = true; |
|
} |
|
else |
|
{ |
|
// else we just take the first one (should be the only one) |
|
PythonKeys::iterator pos; |
|
pos = fModKeys.begin(); |
|
modKey = pos->second; |
|
|
|
// Guess we won't be using that modifier we new'd up there. Does that mean we should delete it? Sure. |
|
delete mod; |
|
mod = nil; |
|
} |
|
} |
|
else // else, nope... create a modifier for each object its attached to |
|
{ |
|
modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); |
|
} |
|
} |
|
hsgResMgr::ResMgr()->AddViaNotify(modKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); |
|
fModKeys[node] = modKey; |
|
|
|
// only let non-multimodifier or multimodifier then only the main node register for notifies |
|
if ( !block->IsMultiModifier() || mainMultiModierNode ) |
|
{ |
|
int nParams = block->NumParams(); |
|
for (int i = 0; i < nParams; i++) |
|
{ |
|
plAutoUIParam *param = block->GetParam(i); |
|
|
|
if (param->GetParamType() == plAutoUIParam::kTypeActivator) |
|
{ |
|
// Register the modifier to recieve notifies from this activator. |
|
// We'll let the modifier know the activator key when it's available, |
|
// in the convert pass. |
|
int numcomps = param->GetCount(pb); |
|
for (int j=0; j< numcomps; j++ ) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb,j); |
|
if (comp) |
|
comp->AddReceiverKey(modKey); |
|
} |
|
} |
|
|
|
if (param->GetParamType() == plAutoUIParam::kTypeGUIDialog) |
|
{ |
|
// Register the modifier to recieve notifies from this activator. |
|
// We'll let the modifier know the activator key when it's available, |
|
// in the convert pass. |
|
int numcomps = param->GetCount(pb); |
|
for (int j=0; j< numcomps; j++ ) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb,j); |
|
if (comp && comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID ) |
|
{ |
|
// convert the comp to a GUIDialog component, so we can talk to it |
|
plGUIDialogComponent *dialog_comp = (plGUIDialogComponent*)comp; |
|
dialog_comp->SetNotifyReceiver(modKey); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
#include "plActivatorBaseComponent.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "plResponderComponent.h" |
|
|
|
hsBool plPythonFileComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); |
|
plAutoUIBlock *block = FindAutoUI(pb); |
|
|
|
if (!block) |
|
return false; |
|
|
|
plKey modKey = fModKeys[node]; |
|
plPythonFileMod *mod = plPythonFileMod::ConvertNoRef(modKey->GetObjectPtr()); |
|
|
|
// add in all the receivers that want to be notified from the Python coded script |
|
int j; |
|
for (j = 0; j < fOthersKeys.Count(); j++) |
|
{ |
|
mod->AddToNotifyList(fOthersKeys[j]); |
|
} |
|
// remove all the Keys we know about to be re-built on the next convert |
|
fOthersKeys.Reset(); |
|
|
|
// set the name of the source file |
|
mod->SetSourceFile(block->GetName()); |
|
|
|
int nParams = block->NumParams(); |
|
for (int i = 0; i < nParams; i++) |
|
{ |
|
plAutoUIParam *param = block->GetParam(i); |
|
|
|
plPythonParameter pyParam; |
|
pyParam.fID = param->GetID(); |
|
|
|
// Get the data for the param |
|
switch (param->GetParamType()) |
|
{ |
|
case plAutoUIParam::kTypeBool: |
|
pyParam.SetTobool(param->GetBool(pb)); |
|
mod->AddParameter(pyParam); |
|
break; |
|
|
|
case plAutoUIParam::kTypeInt: |
|
pyParam.SetToInt(param->GetInt(pb)); |
|
mod->AddParameter(pyParam); |
|
break; |
|
|
|
case plAutoUIParam::kTypeFloat: |
|
pyParam.SetToFloat(param->GetFloat(pb)); |
|
mod->AddParameter(pyParam); |
|
break; |
|
|
|
case plAutoUIParam::kTypeString: |
|
pyParam.SetToString(param->GetString(pb)); |
|
mod->AddParameter(pyParam); |
|
break; |
|
|
|
case plAutoUIParam::kTypeSceneObj: |
|
{ |
|
int numKeys = param->GetCount(pb); |
|
hsBool found_atleast_one_good_one = false; |
|
for (int i = 0; i < numKeys; i++) |
|
{ |
|
plKey skey = param->GetKey(pb, i); |
|
if ( skey != nil ) |
|
{ |
|
pyParam.SetToSceneObject(skey, true); |
|
// make sure that there really was a sceneobject |
|
mod->AddParameter(pyParam); |
|
found_atleast_one_good_one = true; |
|
} |
|
} |
|
if ( !found_atleast_one_good_one ) |
|
{ |
|
char buf[512]; |
|
sprintf(buf,"The sceneobject attribute (ID=%d) that was selected in %s PythonFile, somehow does not exist!?", |
|
pyParam.fID,this->GetINode()->GetName()); |
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeComponent: |
|
{ |
|
int count = param->GetCount(pb); |
|
hsBool found_atleast_one_good_one = false; |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp) |
|
{ |
|
for (int j = 0; j < comp->NumTargets(); j++) |
|
{ |
|
plKey responderKey = Responder::GetKey(comp, comp->GetTarget(j)); |
|
if ( responderKey != nil ) |
|
{ |
|
pyParam.SetToResponder(responderKey); |
|
mod->AddParameter(pyParam); |
|
found_atleast_one_good_one = true; |
|
} |
|
} |
|
if ( !found_atleast_one_good_one ) |
|
{ |
|
char buf[512]; |
|
sprintf(buf,"The responder attribute %s that was selected in %s PythonFile, somehow does not exist!?", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeActivator: |
|
{ |
|
int count = param->GetCount(pb); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
// make sure we found a comp and then see if it is an activator type |
|
if (comp && comp->CanConvertToType(ACTIVATOR_BASE_CID)) |
|
{ |
|
plActivatorBaseComponent *activator = (plActivatorBaseComponent*)comp; |
|
const plActivatorBaseComponent::LogicKeys& logicKeys = activator->GetLogicKeys(); |
|
plActivatorBaseComponent::LogicKeys::const_iterator it; |
|
for (it = logicKeys.begin(); it != logicKeys.end(); it++) |
|
{ |
|
pyParam.SetToActivator(it->second); |
|
mod->AddParameter(pyParam); |
|
} |
|
// do special stuff for Volume sensors because they are stupid turds |
|
if (comp->ClassID() == VOLUMEGADGET_CID) |
|
{ |
|
plVolumeGadgetComponent* pClick = (plVolumeGadgetComponent*)comp; |
|
const plVolumeGadgetComponent::LogicKeys &logicKeys2 = pClick->GetLogicOutKeys(); |
|
plVolumeGadgetComponent::LogicKeys::const_iterator VGit; |
|
for (VGit = logicKeys2.begin(); VGit != logicKeys2.end(); VGit++) |
|
{ |
|
pyParam.SetToActivator(VGit->second); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
// now see if it is a PythonFile kinda activator thingy |
|
else if (comp && comp->ClassID() == PYTHON_FILE_CID) |
|
{ |
|
plPythonFileComponent *pyfact = (plPythonFileComponent*)comp; |
|
const plPythonFileComponent::PythonKeys& pythonKeys = pyfact->GetKeys(); |
|
plPythonFileComponent::PythonKeys::const_iterator it; |
|
for (it = pythonKeys.begin(); it != pythonKeys.end(); it++) |
|
{ |
|
pyParam.SetToActivator(it->second); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeDynamicText: |
|
{ |
|
int numKeys = param->GetCount(pb); |
|
for (int i = 0; i < numKeys; i++) |
|
{ |
|
plKey key = param->GetKey(pb, i); |
|
// make sure we got a key and that it is a DynamicTextMap |
|
if (key && plDynamicTextMap::ConvertNoRef(key->GetObjectPtr()) ) |
|
{ |
|
pyParam.SetToDynamicText(key); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeGUIDialog: |
|
{ |
|
int count = param->GetCount(pb); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp) |
|
{ |
|
if (comp && comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID ) |
|
{ |
|
// convert the comp to a GUIDialog component, so we can talk to it |
|
plGUIDialogComponent *dialog_comp = (plGUIDialogComponent*)comp; |
|
plKey dialogKey = dialog_comp->GetModifierKey(); |
|
pyParam.SetToGUIDialog(dialogKey); |
|
if ( pyParam.fObjectKey == nil ) |
|
{ |
|
char buf[512]; |
|
sprintf(buf,"The GUIDialog attribute %s that was selected in %s PythonFile, somehow does not exist!?", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
else |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeGUIPopUpMenu: |
|
{ |
|
int count = param->GetCount(pb); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp) |
|
{ |
|
if (comp && comp->ClassID() == GUI_MENUANCHOR_CLASSID ) |
|
{ |
|
// convert the comp to a GUIPopUpMenu component, so we can talk to it |
|
plGUIMenuComponent *guiComp = (plGUIMenuComponent*)comp; |
|
plKey key = guiComp->GetConvertedMenuKey(); |
|
pyParam.SetToGUIPopUpMenu( key ); |
|
if ( pyParam.fObjectKey == nil ) |
|
{ |
|
char buf[512]; |
|
sprintf(buf,"The GUIPopUpMenu attribute %s that was selected in %s PythonFile, somehow does not exist!?", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
else |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeGUISkin: |
|
{ |
|
int count = param->GetCount(pb); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp) |
|
{ |
|
if (comp && comp->ClassID() == GUI_SKIN_CLASSID ) |
|
{ |
|
// convert the comp to a GUISkin component, so we can talk to it |
|
plGUISkinComp *guiComp = (plGUISkinComp *)comp; |
|
plKey key = guiComp->GetConvertedSkinKey(); |
|
pyParam.SetToGUISkin( key ); |
|
if ( pyParam.fObjectKey == nil ) |
|
{ |
|
char buf[512]; |
|
sprintf(buf,"The GUISkin attribute %s that was selected in %s PythonFile, somehow does not exist!?", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
else |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeExcludeRegion: |
|
{ |
|
int count = param->GetCount(pb); |
|
int number_of_real_targets_found = 0; |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp && comp->ClassID() == XREGION_CID ) |
|
{ |
|
for (int j = 0; j < comp->NumTargets(); j++) |
|
{ |
|
plExcludeRegionComponent *excomp = (plExcludeRegionComponent*)comp; |
|
plKey exKey = excomp->GetKey((plMaxNode*)(comp->GetTarget(j))); |
|
if ( exKey != nil ) |
|
{ |
|
// only get one real target, just count the rest |
|
if ( number_of_real_targets_found == 0 ) |
|
{ |
|
pyParam.SetToExcludeRegion(exKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
number_of_real_targets_found += 1; |
|
} |
|
} |
|
if ( number_of_real_targets_found != 1 ) |
|
{ |
|
// there is zero or more than one node attached to this exclude region |
|
char buf[512]; |
|
if ( number_of_real_targets_found == 0 ) |
|
sprintf(buf,"The ExcludeRegion %s that was selected as an attribute in %s PythonFile, has no scene nodes attached.", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
else |
|
sprintf(buf,"The ExcludeRegion %s that was selected as an attribute in %s PythonFile, has more than one scene node attached (using first one found).", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
|
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeWaterComponent: |
|
{ |
|
plComponentBase* comp = param->GetComponent(pb, 0); |
|
plWaveSetBase* wsb = nil; |
|
|
|
if (comp) |
|
{ |
|
wsb = plWaterComponent::GetWaveSet(comp->GetINode()); |
|
|
|
if (wsb != nil) |
|
{ |
|
plKey waterKey = wsb->GetKey(); |
|
if ( waterKey != nil ) |
|
{ |
|
pyParam.SetToWaterComponent(waterKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeSwimCurrentInterface: |
|
{ |
|
plComponentBase* comp = param->GetComponent(pb, 0); |
|
plSwimRegionInterface* sri = nil; |
|
|
|
if (comp && comp->ClassID() == PHYS_SWIMSURFACE_CID) |
|
{ |
|
plSwim2DComponent* swimcomp = (plSwim2DComponent*)comp; |
|
std::map<plMaxNode*, plSwimRegionInterface*>::const_iterator containsNode; |
|
plMaxNode* mnode = nil; |
|
|
|
for (int i = 0; i < swimcomp->NumTargets(); i++) |
|
{ |
|
mnode = (plMaxNode*)swimcomp->GetTarget(i); |
|
containsNode = swimcomp->fSwimRegions.find(mnode); |
|
if ( containsNode != swimcomp->fSwimRegions.end() ) |
|
{ |
|
sri = swimcomp->fSwimRegions[mnode]; |
|
break; |
|
} |
|
} |
|
|
|
if (sri != nil) |
|
{ |
|
plKey swimKey = sri->GetKey(); |
|
if ( swimKey != nil ) |
|
{ |
|
pyParam.SetToSwimCurrentInterface(swimKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeClusterComponent: |
|
{ |
|
plComponentBase* comp = param->GetComponent(pb, 0); |
|
plClusterGroup* clusterGroup = nil; |
|
|
|
if (comp && comp->ClassID() == CLUSTER_COMP_CID) |
|
{ |
|
plClusterComponent* clusterComp = (plClusterComponent*)comp; |
|
int numGroups = clusterComp->GetNumGroups(); |
|
int i; |
|
for (i=0; i<numGroups; i++) |
|
{ |
|
plClusterGroup* group = clusterComp->GetGroup(i); |
|
plKey groupKey = group->GetKey(); |
|
if (groupKey != nil) |
|
{ |
|
pyParam.SetToClusterComponent(groupKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeAnimation: |
|
{ |
|
int count = param->GetCount(pb); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp && ( comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID ) ) |
|
{ |
|
plAnimComponentBase *animcomp = (plAnimComponentBase*)comp; |
|
// save out the animation name first |
|
const char *tempAnimName = animcomp->GetAnimName(); |
|
if (tempAnimName == nil) |
|
pyParam.SetToAnimationName(ENTIRE_ANIMATION_NAME); |
|
else |
|
pyParam.SetToAnimationName(tempAnimName); |
|
mod->AddParameter(pyParam); |
|
// gather up all the modkeys for all the targets attached to this animation component |
|
int j; |
|
for ( j=0; j<comp->NumTargets(); j++ ) |
|
{ |
|
pyParam.SetToAnimation(animcomp->GetModKey((plMaxNode*)(comp->GetTarget(j)))); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeBehavior: |
|
{ |
|
// The Behavior attribute is One-Shots and Multi-stage behaviors |
|
// For Python usage: we will only allow using behaviors that are |
|
// attached to one position node. In other words, Python will only |
|
// be used for special cases. |
|
int count = param->GetCount(pb); |
|
int number_of_real_targets_found = 0; |
|
for (int i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = param->GetComponent(pb, i); |
|
if (comp && comp->ClassID() == ONESHOTCLASS_ID ) |
|
{ |
|
// gather up all the modkeys for all the targets attached to this animation component |
|
int j; |
|
for ( j=0; j<comp->NumTargets(); j++ ) |
|
{ |
|
plKey behKey = OneShotComp::GetOneShotKey(comp,(plMaxNode*)(comp->GetTarget(j))); |
|
if ( behKey != nil ) |
|
{ |
|
// only get one real target, just count the rest |
|
if ( number_of_real_targets_found == 0 ) |
|
{ |
|
pyParam.SetToBehavior(behKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
number_of_real_targets_found += 1; |
|
} |
|
} |
|
} |
|
else if (comp && comp->ClassID() == MULTISTAGE_BEH_CID ) |
|
{ |
|
// gather up all the modkeys for all the targets attached to this animation component |
|
int j; |
|
for ( j=0; j<comp->NumTargets(); j++ ) |
|
{ |
|
plKey behKey = MultiStageBeh::GetMultiStageBehKey(comp,(plMaxNode*)(comp->GetTarget(j))); |
|
if ( behKey != nil ) |
|
{ |
|
// only get one real target, just count the rest |
|
if ( number_of_real_targets_found == 0 ) |
|
{ |
|
pyParam.SetToBehavior(behKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
number_of_real_targets_found += 1; |
|
} |
|
} |
|
} |
|
if ( number_of_real_targets_found != 1 ) |
|
{ |
|
// there is zero or more than one node attached to this exclude region |
|
char buf[512]; |
|
if ( number_of_real_targets_found == 0 ) |
|
sprintf(buf,"The Behavior component %s that was selected as an attribute in %s PythonFile, has no scene nodes attached.", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
else |
|
sprintf(buf,"The Behavior component %s that was selected as an attribute in %s PythonFile, has more than one scene node attached (using first one found).", |
|
comp->GetINode()->GetName(),this->GetINode()->GetName()); |
|
|
|
pErrMsg->Set(true, "PythonFile Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
|
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeMaterial: |
|
{ |
|
int numKeys = param->GetCount(pb); |
|
for (int i = 0; i < numKeys; i++) |
|
{ |
|
plKey key = param->GetKey(pb, i); |
|
// make sure we got a key and that it is a plMipmap |
|
if (key && plMipmap::ConvertNoRef(key->GetObjectPtr()) ) |
|
{ |
|
pyParam.SetToMaterial(key); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeMaterialAnimation: |
|
{ |
|
plPickMaterialAnimationButtonParam* matAnim = (plPickMaterialAnimationButtonParam*)param; |
|
matAnim->CreateKeyArray(pb); |
|
|
|
int numKeys = param->GetCount(pb); |
|
for (int i = 0; i < numKeys; i++) |
|
{ |
|
plKey key = param->GetKey(pb, i); |
|
|
|
if ( key ) |
|
{ |
|
pyParam.SetToMaterialAnimation(key); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
matAnim->DestroyKeyArray(); |
|
|
|
} |
|
break; |
|
|
|
case plAutoUIParam::kTypeDropDownList: |
|
pyParam.SetToString(param->GetString(pb)); |
|
mod->AddParameter(pyParam); |
|
break; |
|
|
|
case plAutoUIParam::kTypeGrassComponent: |
|
{ |
|
plComponentBase* comp = param->GetComponent(pb, 0); |
|
plGrassShaderMod* shader = nil; |
|
|
|
if (comp) |
|
{ |
|
shader = plGrassComponent::GetShader(comp->GetINode()); |
|
|
|
if (shader != nil) |
|
{ |
|
plKey shaderKey = shader->GetKey(); |
|
if ( shaderKey != nil ) |
|
{ |
|
pyParam.SetToGrassShaderComponent(shaderKey); |
|
mod->AddParameter(pyParam); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
plPythonFileComponent::Validate plPythonFileComponent::ValidateFile() |
|
{ |
|
// Make sure the type is in the valid range |
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); |
|
plAutoUIBlock *block = FindAutoUI(pb); |
|
if (!block || fCompPB->GetInt(kPythonVersion) > block->GetVersion()) |
|
{ |
|
// Bad type, clear out the PB so this won't blow up during a save |
|
fCompPB->SetValue(kPythonFilePB, 0, (ReferenceTarget*)nil); |
|
|
|
if (!block) |
|
return kPythonNoFile; |
|
else |
|
return kPythonBadVer; |
|
} |
|
|
|
// Got a newer version of the Python file, update our version |
|
if (fCompPB->GetInt(kPythonVersion) < block->GetVersion()) |
|
fCompPB->SetValue(kPythonVersion, 0, block->GetVersion()); |
|
|
|
if (block->GetVersion() == 0) |
|
return kPythonNoVer; |
|
|
|
return kPythonOk; |
|
} |
|
|
|
const char* plPythonFileComponent::GetPythonName() |
|
{ |
|
// Make sure the type is in the valid range |
|
IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); |
|
plAutoUIBlock *block = FindAutoUI(pb); |
|
if (block) |
|
return block->GetName(); |
|
|
|
return "(unknown)"; |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
// Verify that all Python File components in the scene are OK, and warn the user |
|
// if any aren't |
|
// |
|
|
|
class plPythonError |
|
{ |
|
public: |
|
plMaxNode* node; |
|
const char* pythonName; |
|
plPythonFileComponent::Validate error; |
|
|
|
bool operator< (const plPythonError& rhs) const { return rhs.node < node; } |
|
}; |
|
|
|
typedef std::set<plPythonError> ErrorSet; |
|
|
|
static void CheckPythonFileCompsRecur(plMaxNode *node, ErrorSet& badNodes) |
|
{ |
|
plComponentBase *comp = node->ConvertToComponent(); |
|
if (comp && comp->ClassID() == PYTHON_FILE_CID) |
|
{ |
|
plPythonFileComponent* pyComp = (plPythonFileComponent*)comp; |
|
|
|
const char* pythonName = pyComp->GetPythonName(); |
|
|
|
plPythonFileComponent::Validate valid = pyComp->ValidateFile(); |
|
if (valid != plPythonFileComponent::kPythonOk) |
|
{ |
|
plPythonError err; |
|
err.node = node; |
|
err.pythonName = pythonName; |
|
err.error = valid; |
|
badNodes.insert(err); |
|
} |
|
} |
|
|
|
for (int i = 0; i < node->NumberOfChildren(); i++) |
|
CheckPythonFileCompsRecur((plMaxNode*)node->GetChildNode(i), badNodes); |
|
} |
|
|
|
static BOOL CALLBACK WarnDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
if (msg == WM_INITDIALOG) |
|
{ |
|
HWND hList = GetDlgItem(hDlg, IDC_COMP_LIST); |
|
|
|
LVCOLUMN lvc; |
|
lvc.mask = LVCF_TEXT; |
|
lvc.pszText = "Component"; |
|
ListView_InsertColumn(hList, 0, &lvc); |
|
lvc.pszText = "Python File"; |
|
ListView_InsertColumn(hList, 1, &lvc); |
|
lvc.pszText = "Error"; |
|
ListView_InsertColumn(hList, 2, &lvc); |
|
|
|
ErrorSet *badNodes = (ErrorSet*)lParam; |
|
ErrorSet::iterator it = badNodes->begin(); |
|
for (; it != badNodes->end(); it++) |
|
{ |
|
const plPythonError err = *it; |
|
|
|
plMaxNode* node = err.node; |
|
plPythonFileComponent* comp = (plPythonFileComponent*)node->ConvertToComponent(); |
|
|
|
LVITEM lvi; |
|
lvi.mask = LVIF_TEXT; |
|
lvi.pszText = (char*)node->GetName(); |
|
lvi.iItem = 0; |
|
lvi.iSubItem = 0; |
|
int idx = ListView_InsertItem(hList, &lvi); |
|
|
|
ListView_SetItemText(hList, idx, 1, (char*)err.pythonName); |
|
|
|
switch (err.error) |
|
{ |
|
case plPythonFileComponent::Validate::kPythonBadVer: |
|
ListView_SetItemText(hList, idx, 2, "Old Version"); |
|
break; |
|
case plPythonFileComponent::Validate::kPythonNoVer: |
|
ListView_SetItemText(hList, idx, 2, "No Version"); |
|
break; |
|
case plPythonFileComponent::Validate::kPythonNoFile: |
|
ListView_SetItemText(hList, idx, 2, "No File/Python Error"); |
|
break; |
|
} |
|
} |
|
|
|
ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE); |
|
ListView_SetColumnWidth(hList, 1, LVSCW_AUTOSIZE); |
|
ListView_SetColumnWidth(hList, 2, LVSCW_AUTOSIZE); |
|
|
|
return TRUE; |
|
} |
|
else if (msg == WM_COMMAND) |
|
{ |
|
if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) |
|
{ |
|
DestroyWindow(hDlg); |
|
return TRUE; |
|
} |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
static void WriteBadPythonText(ErrorSet& badNodes) |
|
{ |
|
ErrorSet::iterator it = badNodes.begin(); |
|
for (; it != badNodes.end(); it++) |
|
{ |
|
const plPythonError err = *it; |
|
|
|
const char* compName = err.node->GetName(); |
|
const char* pythonFile = err.pythonName; |
|
const char* errorText = ""; |
|
switch (err.error) |
|
{ |
|
case plPythonFileComponent::kPythonBadVer: |
|
errorText = "Old Version"; |
|
break; |
|
case plPythonFileComponent::Validate::kPythonNoVer: |
|
errorText = "No Version"; |
|
break; |
|
case plPythonFileComponent::Validate::kPythonNoFile: |
|
errorText = "No File/Python Error"; |
|
break; |
|
} |
|
|
|
std::string& pythonError = plPythonFileComponent::fPythonError; |
|
pythonError += "Python component "; |
|
pythonError += compName; |
|
pythonError += " (file "; |
|
pythonError += pythonFile; |
|
pythonError += ") is bad. Reason: "; |
|
pythonError += errorText; |
|
pythonError += "\n"; |
|
} |
|
} |
|
|
|
static void NotifyProc(void *param, NotifyInfo *info) |
|
{ |
|
static bool gotBadPython = false; |
|
|
|
if (info->intcode == NOTIFY_FILE_POST_OPEN) |
|
{ |
|
ErrorSet badNodes; |
|
CheckPythonFileCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), badNodes); |
|
|
|
if (badNodes.size() > 0) |
|
{ |
|
gotBadPython = true; |
|
|
|
if (hsMessageBox_SuppressPrompts) |
|
WriteBadPythonText(badNodes); |
|
else |
|
CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_PYTHON_FILE_WARN), GetCOREInterface()->GetMAXHWnd(), WarnDialogProc, (LPARAM)&badNodes); |
|
} |
|
else |
|
gotBadPython = false; |
|
} |
|
else if (info->intcode == NOTIFY_FILE_PRE_SAVE) |
|
{ |
|
if (gotBadPython) |
|
{ |
|
hsMessageBox("This file has bad Python components in it, you REALLY shouldn't save it.", |
|
"Python Component Warning", |
|
hsMBoxOk); |
|
} |
|
} |
|
else if (info->intcode == NOTIFY_SYSTEM_POST_RESET || |
|
info->intcode == NOTIFY_SYSTEM_POST_NEW) |
|
{ |
|
gotBadPython = false; |
|
} |
|
// Have to do this at system shutdown 2 (really final shutdown) because it deletes the |
|
// descriptor, which Max may still try to use in between shutdown 1 and 2. |
|
else if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN2) |
|
{ |
|
int count = gAutoUIBlocks.size(); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
delete gAutoUIBlocks[i]; |
|
} |
|
gAutoUIBlocks.clear(); |
|
} |
|
} |
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
class plPythonFileComponentProc : public ParamMap2UserDlgProc |
|
{ |
|
public: |
|
plPythonFileComponentProc() : fAutoUI(nil) {} |
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
|
void DeleteThis() { DestroyAutoUI(); } |
|
|
|
protected: |
|
plAutoUIBlock *fAutoUI; |
|
|
|
void CreateAutoUI(plAutoUIBlock *autoUI, IParamBlock2 *pb); |
|
void DestroyAutoUI(); |
|
}; |
|
static plPythonFileComponentProc gPythonFileProc; |
|
|
|
ParamBlockDesc2 gPythonFileBlk |
|
( |
|
plComponent::kBlkComp, _T("PythonFile"), 0, &gPythonFileComponentDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, |
|
|
|
//rollout |
|
IDD_COMP_PYTHON_FILE, IDS_COMP_PYTHON, 0, 0, &gPythonFileProc, |
|
|
|
kPythonFilePB, _T("pb"), TYPE_REFTARG, 0, 0, |
|
end, |
|
|
|
kPythonVersion, _T("version"), TYPE_INT, 0, 0, |
|
end, |
|
|
|
kPythonFileIsGlobal, _T("isGlobal"), TYPE_BOOL, 0, 0, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_PYTHON_GLOBAL, |
|
p_default, FALSE, |
|
end, |
|
|
|
end |
|
); |
|
|
|
#define WM_LOAD_AUTO_UI WM_APP+1 |
|
|
|
BOOL plPythonFileComponentProc::DlgProc(TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
|
|
HWND hCombo = GetDlgItem(hWnd, IDC_PYTHON_FILE); |
|
ComboBox_ResetContent(hCombo); |
|
|
|
SetDlgItemText(hWnd, IDC_VER_TEXT, ""); |
|
|
|
IParamBlock2 *pythonPB = (IParamBlock2*)pb->GetReferenceTarget(kPythonFilePB); |
|
plAutoUIBlock *pythonBlock = FindAutoUI(pythonPB); |
|
|
|
int numPythonFiles = gAutoUIBlocks.size(); |
|
for (int i = 0; i < numPythonFiles; i++) |
|
{ |
|
plAutoUIBlock *block = gAutoUIBlocks[i]; |
|
const char *name = block->GetName(); |
|
|
|
int idx = ComboBox_AddString(hCombo, name); |
|
ComboBox_SetItemData(hCombo, idx, i); |
|
if (block == pythonBlock) |
|
{ |
|
ComboBox_SetCurSel(hCombo, idx); |
|
SetDlgItemInt(hWnd, IDC_VER_TEXT, block->GetVersion(), TRUE); |
|
} |
|
} |
|
|
|
// Crappy hack, see WM_LOAD_AUTO_UI |
|
PostMessage(hWnd, WM_LOAD_AUTO_UI, 0, 0); |
|
} |
|
return TRUE; |
|
|
|
// Crappy hack. If we put up the python file UI before returning from WM_INITDIALOG |
|
// it will show up ABOVE the main UI. To get around this we post a message that won't |
|
// get processed until after the main UI is put up. |
|
case WM_LOAD_AUTO_UI: |
|
{ |
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
|
|
IParamBlock2 *pythonPB = (IParamBlock2*)pb->GetReferenceTarget(kPythonFilePB); |
|
plAutoUIBlock *pythonBlock = FindAutoUI(pythonPB); |
|
|
|
if (pythonBlock && pythonPB) |
|
CreateAutoUI(pythonBlock, pythonPB); |
|
} |
|
return TRUE; |
|
|
|
case WM_COMMAND: |
|
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_PYTHON_FILE) |
|
{ |
|
HWND hCombo = (HWND)lParam; |
|
int sel = ComboBox_GetCurSel(hCombo); |
|
|
|
int type = ComboBox_GetItemData(hCombo, sel); |
|
|
|
plAutoUIBlock *block = gAutoUIBlocks[type]; |
|
IParamBlock2 *autoPB = block->CreatePB(); |
|
|
|
CreateAutoUI(block, autoPB); |
|
|
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
pb->SetValue(kPythonFilePB, 0, (ReferenceTarget*)autoPB); |
|
|
|
SetDlgItemInt(hWnd, IDC_VER_TEXT, block->GetVersion(), TRUE); |
|
|
|
return TRUE; |
|
} |
|
break; |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
void plPythonFileComponentProc::CreateAutoUI(plAutoUIBlock *autoUI, IParamBlock2 *pb) |
|
{ |
|
DestroyAutoUI(); |
|
|
|
if (autoUI && pb) |
|
{ |
|
fAutoUI = autoUI; |
|
autoUI->CreateAutoRollup(pb); |
|
} |
|
} |
|
|
|
void plPythonFileComponentProc::DestroyAutoUI() |
|
{ |
|
if (fAutoUI) |
|
{ |
|
fAutoUI->DestroyAutoRollup(); |
|
fAutoUI = nil; |
|
} |
|
}
|
|
|