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.
1331 lines
43 KiB
1331 lines
43 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/>. |
|
|
|
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 "plResponderComponentPriv.h" |
|
#include "resource.h" |
|
#include "plComponent.h" |
|
#include "plComponentReg.h" |
|
|
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "pnKeyedObject/hsKeyedObject.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "pnNetCommon/plSDLTypes.h" |
|
|
|
#include "plModifier/plResponderModifier.h" |
|
#include "plModifier/plLogicModifier.h" |
|
#include "plModifier/plAxisAnimModifier.h" |
|
#include "pfConditional/plActivatorConditionalObject.h" |
|
#include "pfConditional/plORConditionalObject.h" |
|
|
|
#include "pnMessage/plObjRefMsg.h" |
|
#include "pnMessage/plNotifyMsg.h" |
|
|
|
#include "hsResMgr.h" |
|
#include "MaxMain/plMaxNode.h" |
|
|
|
#include "plPickNode.h" |
|
|
|
#include "MaxMain/plPlasmaRefMsgs.h" |
|
|
|
#include "plResponderLink.h" |
|
#include "plResponderAnim.h" |
|
#include "plResponderMtl.h" |
|
#include "plResponderWait.h" |
|
|
|
#include "MaxMain/plMaxAccelerators.h" |
|
|
|
IParamBlock2 *CreateWaitBlk(); |
|
|
|
int ResponderGetActivatorCount(plComponentBase *comp) |
|
{ |
|
if (comp->ClassID() == RESPONDER_CID) |
|
return comp->GetParamBlockByID(plComponentBase::kBlkComp)->Count(kResponderActivators); |
|
|
|
return -1; |
|
} |
|
|
|
plComponentBase *ResponderGetActivator(plComponentBase *comp, int idx) |
|
{ |
|
if (comp->ClassID() == RESPONDER_CID) |
|
{ |
|
IParamBlock2 *pb = comp->GetParamBlockByID(plComponentBase::kBlkComp); |
|
plMaxNode *activatorNode = (plMaxNode*)pb->GetINode(kResponderActivators, 0, idx); |
|
return activatorNode->ConvertToComponent(); |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
plKey Responder::GetKey(plComponentBase *comp, plMaxNodeBase *node) |
|
{ |
|
if (comp->ClassID() != RESPONDER_CID) |
|
return nil; |
|
|
|
plResponderComponent *responder = (plResponderComponent*)comp; |
|
if (responder->fModKeys.find((plMaxNode*)node) != responder->fModKeys.end()) |
|
return responder->fModKeys[(plMaxNode*)node]; |
|
|
|
return nil; |
|
} |
|
|
|
CLASS_DESC(plResponderComponent, gResponderDesc, "Responder", "Responder", COMP_TYPE_LOGIC, RESPONDER_CID) |
|
|
|
class plResponderProc; |
|
extern plResponderProc gResponderComponentProc; |
|
|
|
// When one of our parameters that is a ref changes, send out the component ref |
|
// changed message. Normally, messages from component refs are ignored since |
|
// they pass along all the messages of the ref, which generates a lot of false |
|
// converts. |
|
class plResponderAccessor : public PBAccessor |
|
{ |
|
public: |
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) |
|
{ |
|
if (id == kResponderActivators || id == kResponderState) |
|
{ |
|
plResponderComponent *comp = (plResponderComponent*)owner; |
|
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); |
|
} |
|
} |
|
}; |
|
static plResponderAccessor gResponderAccessor; |
|
|
|
ParamBlockDesc2 gResponderBlock |
|
( |
|
plComponent::kBlkComp, _T("responderComp"), 0, &gResponderDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, |
|
|
|
IDD_COMP_RESPOND, IDS_COMP_RESPOND, 0, 0, &gResponderComponentProc, |
|
|
|
kResponderState, _T("state"), TYPE_REFTARG_TAB, 0, 0, 0, |
|
p_accessor, &gResponderAccessor, |
|
end, |
|
kResponderStateName, _T("stateName"), TYPE_STRING_TAB, 0, 0, 0, |
|
end, |
|
|
|
kResponderActivators, _T("activators"), TYPE_INODE_TAB, 0, 0, 0, |
|
p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, |
|
p_accessor, &gResponderAccessor, |
|
end, |
|
|
|
kResponderStateDef, _T("defState"), TYPE_INT, 0, 0, |
|
end, |
|
|
|
kResponderEnabled, _T("enabled"), TYPE_BOOL, 0, 0, |
|
p_default, TRUE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLED, |
|
end, |
|
|
|
kResponderTrigger, _T("trigger"), TYPE_BOOL, 0, 0, |
|
p_default, TRUE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_TRIGGER, |
|
end, |
|
|
|
kResponderUnTrigger, _T("unTrigger"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_UNTRIGGER, |
|
end, |
|
|
|
kResponderLocalDetect, _T("localDetect"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_DETECT_LOCAL_CHECK, |
|
end, |
|
|
|
kResponderSkipFFSound, _T("skipFFSound"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_SKIPFFSOUND, |
|
end, |
|
|
|
end |
|
); |
|
|
|
ParamBlockDesc2 gStateBlock |
|
( |
|
kResponderStateBlk, _T("responderState"), 0, &gResponderDesc, 0, |
|
|
|
kStateCmdParams, _T("cmdParam"), TYPE_REFTARG_TAB, 0, 0, 0, |
|
end, |
|
|
|
kStateCmdWait, _T("cmdWait"), TYPE_REFTARG_TAB, 0, 0, 0, |
|
end, |
|
|
|
kStateCmdSwitch, _T("cmdSwitch"), TYPE_INT, 0, 0, |
|
end, |
|
|
|
kStateCmdEnabled, _T("enabled"), TYPE_BOOL_TAB, 0, 0, 0, |
|
p_default, TRUE, |
|
end, |
|
|
|
end |
|
); |
|
|
|
std::vector<plResponderCmd*> gResponderCmds; |
|
|
|
plResponderCmd *plResponderCmd::Find(IParamBlock2 *pb) |
|
{ |
|
if (!pb) |
|
return nil; |
|
|
|
ParamBlockDesc2 *pbDesc = pb->GetDesc(); |
|
|
|
for (int i = 0; i < gResponderCmds.size(); i++) |
|
{ |
|
if (gResponderCmds[i]->GetDesc() == pbDesc) |
|
return gResponderCmds[i]; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
IParamBlock2* plResponderCmd::CreatePB(int idx) |
|
{ |
|
hsAssert(NumTypes() == 1, "Can't auto-create the pb for a cmd with multiple types"); |
|
IParamBlock2 *pb = CreateParameterBlock2(GetDesc(), nil); |
|
return pb; |
|
} |
|
|
|
void DummyCodeIncludeFuncResponder() |
|
{ |
|
gResponderCmds.push_back(&(plResponderCmdVisibility::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdLink::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdEnable::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdXRegion::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdOneShot::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdNotify::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdDetectorEnable::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdCamTransition::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdCamForce::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdAnim::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdMtl::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdDelay::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdFootSurface::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdMultistage::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdPhysEnable::Instance())); |
|
gResponderCmds.push_back(&(plResponderCmdSubWorld::Instance())); |
|
|
|
for (int i = 0; i < gResponderCmds.size(); i++) |
|
gResponderCmds[i]->GetDesc()->SetClassDesc(&gResponderDesc); |
|
|
|
ResponderWait::SetDesc(&gResponderDesc); |
|
} |
|
|
|
plResponderComponent::plResponderComponent() |
|
{ |
|
fClassDesc = &gResponderDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
hsBool plResponderComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
int numStates = fCompPB->Count(kResponderState); |
|
for (int i = 0; i < numStates; i++) |
|
{ |
|
IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, i); |
|
|
|
for (int j = 0; j < statePB->Count(kStateCmdParams); j++) |
|
{ |
|
IParamBlock2 *cmdPB = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, j); |
|
plResponderCmd *cmd = plResponderCmd::Find(cmdPB); |
|
cmd->SetupProperties(node, pErrMsg, cmdPB); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
hsBool plResponderComponent::PreConvert(plMaxNode *node,plErrorMsg *pErrMsg) |
|
{ |
|
plSceneObject* rObj = node->GetSceneObject(); |
|
plLocation loc = node->GetLocation(); |
|
|
|
// Create and register the RESPONDER's logic component |
|
plResponderModifier *responder = TRACKED_NEW plResponderModifier; |
|
plKey responderKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), responder, loc); |
|
hsgResMgr::ResMgr()->AddViaNotify(responderKey, TRACKED_NEW plObjRefMsg(rObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); |
|
|
|
// Tell all the activators to notify us |
|
for (int i = 0; i < fCompPB->Count(kResponderActivators); i++) |
|
{ |
|
plMaxNode *activatorNode = (plMaxNode*)fCompPB->GetINode(kResponderActivators, 0, i); |
|
plComponentBase *comp = activatorNode ? activatorNode->ConvertToComponent() : nil; |
|
if (comp) |
|
{ |
|
if (fCompPB->GetInt(kResponderLocalDetect)) |
|
comp->AddReceiverKey(responderKey, node); |
|
else |
|
comp->AddReceiverKey(responderKey); |
|
} |
|
} |
|
|
|
fModKeys[node] = responderKey; |
|
|
|
return true; |
|
} |
|
|
|
plResponderModifier* plResponderComponent::IGetResponderMod(plMaxNode* node) |
|
{ |
|
plKey responderKey = fModKeys[node]; |
|
return plResponderModifier::ConvertNoRef(responderKey->GetObjectPtr()); |
|
} |
|
|
|
hsBool plResponderComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
IFixOldPB(); |
|
|
|
// Create the commands for each state |
|
int numStates = fCompPB->Count(kResponderState); |
|
|
|
plResponderModifier *responder = IGetResponderMod(node); |
|
responder->fStates.SetCount(numStates); |
|
|
|
for (int i = 0; i < numStates; i++) |
|
{ |
|
CmdIdxs cmdIdxs; |
|
|
|
IConvertCmds(node, pErrMsg, i, cmdIdxs); |
|
|
|
int numCallbacks = 0; |
|
ISetupDefaultWait(node, pErrMsg, i, cmdIdxs, numCallbacks); |
|
IConvertCmdWaits(node, pErrMsg, i, cmdIdxs, numCallbacks); |
|
|
|
IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, i); |
|
responder->fStates[i].fNumCallbacks = numCallbacks; |
|
responder->fStates[i].fSwitchToState = statePB->GetInt(kStateCmdSwitch); |
|
} |
|
|
|
// Set the initial state |
|
responder->fCurState = fCompPB->GetInt(kResponderStateDef); |
|
responder->fEnabled = fCompPB->GetInt(kResponderEnabled) != 0; |
|
|
|
if (fCompPB->GetInt(kResponderTrigger)) |
|
responder->fFlags |= plResponderModifier::kDetectTrigger; |
|
if (fCompPB->GetInt(kResponderUnTrigger)) |
|
responder->fFlags |= plResponderModifier::kDetectUnTrigger; |
|
if (fCompPB->GetInt(kResponderSkipFFSound)) |
|
responder->fFlags |= plResponderModifier::kSkipFFSound; |
|
|
|
// Unless it's been overridden somewhere else, don't save our state on the server |
|
if (!node->GetOverrideHighLevelSDL()) |
|
responder->AddToSDLExcludeList(kSDLResponder); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plResponderComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
fModKeys.clear(); |
|
return true; |
|
} |
|
|
|
void plResponderComponent::IConvertCmds(plMaxNode* node, plErrorMsg* pErrMsg, int state, CmdIdxs& cmdIdxs) |
|
{ |
|
IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, state); |
|
plResponderModifier *responder = IGetResponderMod(node); |
|
|
|
// Add the messages to the logic modifier |
|
for (int i = 0; i < statePB->Count(kStateCmdParams); i++) |
|
{ |
|
plMessage *msg = nil; |
|
|
|
BOOL enabled = statePB->GetInt(kStateCmdEnabled, 0, i); |
|
if (!enabled) |
|
continue; |
|
|
|
IParamBlock2 *cmdPB = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, i); |
|
|
|
try |
|
{ |
|
plResponderCmd *cmd = plResponderCmd::Find(cmdPB); |
|
if (cmd) |
|
msg = cmd->CreateMsg(node, pErrMsg, cmdPB); |
|
} |
|
catch (char *reason) |
|
{ |
|
char buf[512]; |
|
|
|
char stateName[128]; |
|
const char *curStateName = fCompPB->GetStr(kResponderStateName, 0, state); |
|
if (curStateName && *curStateName != '\0') |
|
strcpy(stateName, fCompPB->GetStr(kResponderStateName, 0, state)); |
|
else |
|
sprintf(stateName, "State %d", state+1); |
|
|
|
sprintf(buf, |
|
"A responder command failed to export.\n\nResponder:\t%s\nState:\t\t%s\nCommand:\t%d\n\nReason: %s", |
|
GetINode()->GetName(), stateName, i+1, reason); |
|
|
|
pErrMsg->Set(true, "Responder Warning", buf).Show(); |
|
pErrMsg->Set(false); |
|
} |
|
|
|
if (msg) |
|
{ |
|
msg->SetSender(responder->GetKey()); |
|
responder->AddCommand(msg, state); |
|
int idx = responder->fStates[state].fCmds.Count()-1; |
|
cmdIdxs[i] = idx; |
|
} |
|
} |
|
} |
|
|
|
static IParamBlock2 *GetWaitBlk(IParamBlock2 *state, int idx) |
|
{ |
|
return (IParamBlock2*)state->GetReferenceTarget(kStateCmdWait, 0, idx); |
|
} |
|
|
|
void plResponderComponent::ISetupDefaultWait(plMaxNode* node, plErrorMsg* pErrMsg, |
|
int state, CmdIdxs& cmdIdxs, int &numCallbacks) |
|
{ |
|
IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, state); |
|
plResponderModifier *responder = IGetResponderMod(node); |
|
hsTArray<plResponderModifier::plResponderCmd>& cmds = responder->fStates[state].fCmds; |
|
|
|
int numCmds = cmds.Count(); |
|
for (int i = 0; i < numCmds; i++) |
|
{ |
|
IParamBlock2 *waitPB = GetWaitBlk(statePB, i); |
|
ResponderWait::FixupWaitBlock(waitPB); |
|
|
|
// If we're supposed to wait for this command, and it converted, create a callback |
|
if (ResponderWait::GetWaitOnMe(waitPB) && cmdIdxs.find(i) != cmdIdxs.end()) |
|
{ |
|
int convertedIdx = cmdIdxs[i]; |
|
|
|
ResponderWaitInfo waitInfo; |
|
waitInfo.responderName = GetINode()->GetName(); |
|
waitInfo.receiver = responder->GetKey(); |
|
waitInfo.callbackUser = numCallbacks++; |
|
waitInfo.msg = cmds[convertedIdx].fMsg; |
|
waitInfo.point = nil; |
|
|
|
IParamBlock2 *pb = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, i); |
|
plResponderCmd *cmd = plResponderCmd::Find(pb); |
|
|
|
cmd->CreateWait(node, pErrMsg, pb, waitInfo); |
|
} |
|
} |
|
} |
|
|
|
void plResponderComponent::IConvertCmdWaits(plMaxNode* node, plErrorMsg* pErrMsg, |
|
int state, CmdIdxs& cmdIdxs, int &numCallbacks) |
|
{ |
|
IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, state); |
|
plResponderModifier *responder = IGetResponderMod(node); |
|
hsTArray<plResponderModifier::plResponderCmd>& cmds = responder->fStates[state].fCmds; |
|
|
|
int numWaits = statePB->Count(kStateCmdWait); |
|
for (int i = 0; i < numWaits; i++) |
|
{ |
|
IParamBlock2 *waitPB = GetWaitBlk(statePB, i); |
|
|
|
int wait = ResponderWait::GetWaitingOn(waitPB); |
|
|
|
// If the waiter and waitee both converted, create the callback |
|
if (cmdIdxs.find(wait) != cmdIdxs.end() && cmdIdxs.find(i) != cmdIdxs.end()) |
|
{ |
|
int convertedIdx = cmdIdxs[wait]; |
|
|
|
ResponderWaitInfo waitInfo; |
|
waitInfo.responderName = GetINode()->GetName(); |
|
waitInfo.receiver = responder->GetKey(); |
|
waitInfo.callbackUser = numCallbacks++; |
|
waitInfo.msg = cmds[convertedIdx].fMsg; |
|
waitInfo.point = ResponderWait::GetWaitPoint(waitPB); |
|
|
|
responder->AddCallback(state, convertedIdx, waitInfo.callbackUser); |
|
cmds[cmdIdxs[i]].fWaitOn = waitInfo.callbackUser; |
|
|
|
IParamBlock2 *pb = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, wait); |
|
plResponderCmd *cmd = plResponderCmd::Find(pb); |
|
|
|
cmd->CreateWait(node, pErrMsg, pb, waitInfo); |
|
} |
|
} |
|
} |
|
|
|
void plResponderComponent::IFixOldPB() |
|
{ |
|
if (fCompPB) |
|
{ |
|
if (fCompPB->Count(kResponderState) == 0) |
|
{ |
|
IParamBlock2 *pb = CreateParameterBlock2(&gStateBlock, nil); |
|
int idx = fCompPB->Append(kResponderState, 1, (ReferenceTarget**)&pb); |
|
pb->SetValue(kStateCmdSwitch, 0, idx); |
|
} |
|
if (fCompPB->Count(kResponderStateName) == 0) |
|
{ |
|
char *name = ""; |
|
fCompPB->Append(kResponderStateName, 1, &name); |
|
} |
|
|
|
// Make sure there is an enabled value for each command in the state |
|
for (int i = 0; i < fCompPB->Count(kResponderState); i++) |
|
{ |
|
IParamBlock2* pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, i); |
|
if (pb->Count(kStateCmdEnabled) != pb->Count(kStateCmdParams)) |
|
pb->SetCount(kStateCmdEnabled, pb->Count(kStateCmdParams)); |
|
} |
|
} |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
#define CUSTOM_DRAW |
|
|
|
enum |
|
{ |
|
kStateName, |
|
kStateAdd, |
|
kStateRemove, |
|
kStateDefault, |
|
kStateCopy, |
|
}; |
|
|
|
class plResponderProc : public ParamMap2UserDlgProc |
|
{ |
|
protected: |
|
HWND fhDlg; |
|
IParamBlock2 *fPB; |
|
IParamBlock2 *fStatePB; |
|
int fCurState; |
|
|
|
plResponderComponent *fComp; |
|
|
|
IParamMap2 *fCmdMap; |
|
IParamMap2 *fWaitMap; |
|
|
|
int fCmdIdx; |
|
|
|
typedef std::map<int, const char*> NameID; |
|
NameID fNames; |
|
|
|
HMENU fhMenu; |
|
typedef std::pair<plResponderCmd*, int> CmdID; |
|
typedef std::map<int, CmdID> MenuCmd; |
|
MenuCmd fMenuCmds; |
|
|
|
HWND fhList; |
|
|
|
bool fIgnoreNextDrop; |
|
|
|
public: |
|
plResponderProc(); |
|
|
|
BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
|
void DeleteThis() { IRemoveCmdRollups(); } |
|
|
|
protected: |
|
void ICreateMenu(); |
|
void IAddMenuItem(HMENU hMenu, int id); |
|
|
|
void ICmdRightClick(HWND hCmdList); |
|
|
|
// Add and remove command rollups |
|
void ICreateCmdRollups(); |
|
void IRemoveCmdRollups(); |
|
IParamMap2 *ICreateMap(IParamBlock2 *pb); // Helper |
|
|
|
const char* GetCommandName(int cmdIdx); |
|
void LoadList(); |
|
|
|
BOOL DragListProc(HWND hWnd, DRAGLISTINFO *info); |
|
|
|
#ifdef CUSTOM_DRAW |
|
void IDrawComboItem(DRAWITEMSTRUCT *dis); |
|
#endif |
|
|
|
void LoadState(); |
|
|
|
void AddCommand(); |
|
void RemoveCurCommand(); |
|
void MoveCommand(int oldIdx, int newIdx); |
|
|
|
// Takes a freshly created state PB and adds it as a new state, then returns its index |
|
int AddState(IParamBlock2 *pb); |
|
}; |
|
static plResponderProc gResponderComponentProc; |
|
|
|
void plResponderProc::IAddMenuItem(HMENU hMenu, int id) |
|
{ |
|
AppendMenu(hMenu, MF_STRING, id+1, fNames[id]); |
|
} |
|
|
|
#include "hsSTLSortUtils.h" |
|
|
|
void plResponderProc::ICreateMenu() |
|
{ |
|
fhMenu = CreatePopupMenu(); |
|
|
|
std::map<const char *, HMENU, stringSorter> menus; |
|
int cmdID = 0; |
|
|
|
for (int i = 0; i < gResponderCmds.size(); i++) |
|
{ |
|
plResponderCmd *cmd = gResponderCmds[i]; |
|
for (int j = 0; j < cmd->NumTypes(); j++) |
|
{ |
|
HMENU hParent = fhMenu; |
|
|
|
const char *category = cmd->GetCategory(j); |
|
if (category) |
|
{ |
|
// Menu for this category hasn't been created yet, make one |
|
if (menus.find(category) == menus.end()) |
|
{ |
|
hParent = CreatePopupMenu(); |
|
menus[category] = hParent; |
|
InsertMenu(fhMenu, 0, MF_BYPOSITION | MF_POPUP, (UINT)hParent, category); |
|
} |
|
else |
|
hParent = menus[category]; |
|
} |
|
|
|
const char *name = cmd->GetName(j); |
|
|
|
cmdID++; |
|
fMenuCmds[cmdID] = CmdID(cmd, j); |
|
AppendMenu(hParent, MF_STRING, cmdID, name); |
|
} |
|
} |
|
} |
|
|
|
plResponderProc::plResponderProc() : fCmdMap(nil), fCmdIdx(-1), fCurState(0), fhMenu(nil), fIgnoreNextDrop(false) |
|
{ |
|
} |
|
|
|
const char* plResponderProc::GetCommandName(int cmdIdx) |
|
{ |
|
static char buf[256]; |
|
|
|
if (fStatePB->Count(kStateCmdParams) > cmdIdx) |
|
{ |
|
buf[0] = '\0'; |
|
|
|
BOOL enabled = fStatePB->GetInt(kStateCmdEnabled, 0, cmdIdx); |
|
if (!enabled) |
|
strcat(buf, "[D]"); |
|
|
|
IParamBlock2 *cmdPB = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdParams, 0, cmdIdx); |
|
plResponderCmd *cmd = plResponderCmd::Find(cmdPB); |
|
|
|
IParamBlock2 *waitPB = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdWait, 0, cmdIdx); |
|
int waitingOn = ResponderWait::GetWaitingOn(waitPB); |
|
if (waitingOn != -1) |
|
{ |
|
char num[10]; |
|
sprintf(num, "(%d)", waitingOn+1); |
|
strcat(buf, num); |
|
} |
|
|
|
strcat(buf, cmd->GetInstanceName(cmdPB)); |
|
|
|
return buf; |
|
} |
|
|
|
hsAssert(0, "Bad index to GetCommandName"); |
|
return nil; |
|
} |
|
|
|
void plResponderProc::LoadList() |
|
{ |
|
ListBox_ResetContent(fhList); |
|
|
|
for (int i = 0; i < fStatePB->Count(kStateCmdParams); i++) |
|
{ |
|
const char* name = GetCommandName(i); |
|
ListBox_AddString(fhList, name); |
|
} |
|
|
|
ListBox_SetCurSel(fhList, -1); |
|
} |
|
|
|
void plResponderProc::AddCommand() |
|
{ |
|
RECT rect; |
|
GetWindowRect(GetDlgItem(fhDlg, IDC_ADD_CMD), &rect); |
|
|
|
// Create the popup menu and get the option the user selects |
|
SetForegroundWindow(fhDlg); |
|
int type = TrackPopupMenu(fhMenu, TPM_RIGHTALIGN | TPM_NONOTIFY | TPM_RETURNCMD, rect.left, rect.top, 0, fhDlg, NULL); |
|
PostMessage(fhDlg, WM_USER, 0, 0); |
|
|
|
if (type == 0) |
|
return; |
|
|
|
CmdID& cmdID = fMenuCmds[type]; |
|
plResponderCmd *cmd = cmdID.first; |
|
int cmdIdx = cmdID.second; |
|
|
|
IParamBlock2 *cmdPB = cmd->CreatePB(cmdIdx); |
|
fStatePB->Append(kStateCmdParams, 1, (ReferenceTarget**)&cmdPB); |
|
|
|
IParamBlock2 *waitPB = ResponderWait::CreatePB(); |
|
fStatePB->Append(kStateCmdWait, 1, (ReferenceTarget**)&waitPB); |
|
|
|
BOOL enabled = TRUE; |
|
fStatePB->Append(kStateCmdEnabled, 1, &enabled); |
|
|
|
const char* name = GetCommandName(fStatePB->Count(kStateCmdParams)-1); |
|
int idx = ListBox_AddString(fhList, name); |
|
ListBox_SetCurSel(fhList, idx); |
|
|
|
ICreateCmdRollups(); |
|
} |
|
|
|
void plResponderProc::RemoveCurCommand() |
|
{ |
|
int idx = ListBox_GetCurSel(fhList); |
|
if (idx == LB_ERR) |
|
return; |
|
|
|
// Destroy the current rollup, since it's this guy |
|
IRemoveCmdRollups(); |
|
|
|
// Delete all traces of this command |
|
fStatePB->Delete(kStateCmdParams, idx, 1); |
|
fStatePB->Delete(kStateCmdWait, idx, 1); |
|
fStatePB->Delete(kStateCmdEnabled, idx, 1); |
|
ListBox_DeleteString(fhList, idx); |
|
|
|
// Patch the wait commands |
|
ResponderWait::CmdRemoved(fStatePB, idx); |
|
|
|
fCmdIdx = -1; |
|
} |
|
|
|
void plResponderProc::IRemoveCmdRollups() |
|
{ |
|
if (fCmdMap) |
|
{ |
|
DestroyCPParamMap2(fCmdMap); |
|
fCmdMap = nil; |
|
} |
|
if (fWaitMap) |
|
{ |
|
DestroyCPParamMap2(fWaitMap); |
|
fWaitMap = nil; |
|
} |
|
} |
|
|
|
IParamMap2 *plResponderProc::ICreateMap(IParamBlock2 *pb) |
|
{ |
|
ParamBlockDesc2 *pd = pb->GetDesc(); |
|
|
|
// Don't show anything if there isn't a UI |
|
if (pd->Count() < 1) |
|
{ |
|
pb->ReleaseDesc(); |
|
return nil; |
|
} |
|
|
|
// Create the rollout |
|
IParamMap2 *map = CreateCPParamMap2(0, |
|
pb, |
|
GetCOREInterface(), |
|
hInstance, |
|
MAKEINTRESOURCE(pd->dlg_template), |
|
GetString(pd->title), |
|
pd->flags, |
|
pd->dlgProc, |
|
NULL, |
|
ROLLUP_CAT_STANDARD); |
|
|
|
// Save the rollout in the paramblock |
|
pb->SetMap(map); |
|
pb->ReleaseDesc(); |
|
|
|
return map; |
|
} |
|
|
|
void plResponderProc::ICreateCmdRollups() |
|
{ |
|
// Get the index of the current command |
|
HWND hCmds = GetDlgItem(fhDlg, IDC_CMD_LIST); |
|
int cmdIdx = ListBox_GetCurSel(hCmds); |
|
|
|
if (cmdIdx != LB_ERR && cmdIdx != fCmdIdx) |
|
{ |
|
fCmdIdx = cmdIdx; |
|
fIgnoreNextDrop = true; |
|
|
|
// Save the current scroll position and reset it at the end, so the panels |
|
// won't always jerk back up to the top |
|
IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); |
|
int scrollPos = rollup->GetScrollPos(); |
|
|
|
// Destroy the last command's rollups |
|
IRemoveCmdRollups(); |
|
|
|
// Create the rollup for the current command |
|
IParamBlock2 *pb = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdParams, 0, fCmdIdx); |
|
fCmdMap = ICreateMap(pb); |
|
|
|
ResponderWait::InitDlg(fStatePB, fCmdIdx, GetDlgItem(fhDlg, IDC_CMD_LIST)); |
|
pb = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdWait, 0, fCmdIdx); |
|
fWaitMap = ICreateMap(pb); |
|
|
|
rollup->SetScrollPos(scrollPos); |
|
} |
|
} |
|
|
|
BOOL plResponderProc::DragListProc(HWND hWnd, DRAGLISTINFO *info) |
|
{ |
|
static int oldIdx = -1; |
|
|
|
int curIdx = LBItemFromPt(info->hWnd, info->ptCursor, TRUE); |
|
|
|
switch (info->uNotification) |
|
{ |
|
// Allow the drag |
|
case DL_BEGINDRAG: |
|
// When you click on an item in the listbox, the rollups are changed and Max can |
|
// shift the position of dialog you were just clicking in. Since this happens |
|
// before you let go of the mouse button, the listbox thinks you are dragging. |
|
// To get around it, we don't allow a selection change and a drag in the same click. |
|
if (fIgnoreNextDrop) |
|
{ |
|
SetWindowLong(hWnd, DWL_MSGRESULT, FALSE); |
|
} |
|
else |
|
{ |
|
oldIdx = curIdx; |
|
SetWindowLong(hWnd, DWL_MSGRESULT, TRUE); |
|
} |
|
return TRUE; |
|
|
|
case DL_DRAGGING: |
|
{ |
|
if (curIdx < oldIdx) |
|
DrawInsert(hWnd, info->hWnd, curIdx); |
|
else if (curIdx > oldIdx && ListBox_GetCount(info->hWnd) > curIdx+1) |
|
DrawInsert(hWnd, info->hWnd, curIdx+1); |
|
else |
|
DrawInsert(hWnd, info->hWnd, -1); |
|
} |
|
return TRUE; |
|
|
|
case DL_CANCELDRAG: |
|
// Clear drag arrow |
|
DrawInsert(hWnd, info->hWnd, -1); |
|
return TRUE; |
|
|
|
case DL_DROPPED: |
|
{ |
|
if (fIgnoreNextDrop) |
|
{ |
|
fIgnoreNextDrop = false; |
|
return TRUE; |
|
} |
|
|
|
// Clear drag arrow |
|
DrawInsert(hWnd, info->hWnd, -1); |
|
|
|
if (curIdx != -1 && oldIdx != -1 && curIdx != oldIdx) |
|
{ |
|
// Make sure this won't mess up any wait commands, or at least |
|
// that the user approves if it does. |
|
if (!ResponderWait::ValidateCmdMove(fStatePB, oldIdx, curIdx)) |
|
return TRUE; |
|
|
|
MoveCommand(oldIdx, curIdx); |
|
} |
|
|
|
return TRUE; |
|
} |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
#ifdef CUSTOM_DRAW |
|
void plResponderProc::IDrawComboItem(DRAWITEMSTRUCT *dis) |
|
{ |
|
if (dis->itemID == -1) // empty item |
|
return; |
|
|
|
// The colors depend on whether the item is selected. |
|
COLORREF clrForeground = SetTextColor(dis->hDC, |
|
GetSysColor(dis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); |
|
|
|
COLORREF clrBackground = SetBkColor(dis->hDC, |
|
GetSysColor(dis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); |
|
|
|
// Calculate the vertical and horizontal position. |
|
TEXTMETRIC tm; |
|
GetTextMetrics(dis->hDC, &tm); |
|
int y = (dis->rcItem.bottom + dis->rcItem.top - tm.tmHeight) / 2; |
|
int x = LOWORD(GetDialogBaseUnits()) / 4; |
|
|
|
// If this is a command, not a state, make it bold |
|
HFONT oldFont = nil; |
|
if (dis->itemData != kStateName) |
|
{ |
|
LOGFONT lf; |
|
memset(&lf, 0, sizeof(lf)); |
|
lf.lfHeight = tm.tmHeight; |
|
lf.lfWeight = FW_BOLD; |
|
GetTextFace(dis->hDC, LF_FACESIZE, lf.lfFaceName); |
|
HFONT boldFont = CreateFontIndirect(&lf); |
|
oldFont = SelectFont(dis->hDC, boldFont); |
|
} |
|
|
|
// Get and display the text for the list item. |
|
char buf[256]; |
|
ComboBox_GetLBText(dis->hwndItem, dis->itemID, buf); |
|
if (fPB->GetInt(kResponderStateDef) == dis->itemID) |
|
{ |
|
char buf2[256]; |
|
sprintf(buf2, "* %s", buf); |
|
strcpy(buf, buf2); |
|
} |
|
|
|
ExtTextOut(dis->hDC, x, y, ETO_CLIPPED | ETO_OPAQUE, &dis->rcItem, buf, strlen(buf), NULL); |
|
|
|
// Restore the previous colors. |
|
SetTextColor(dis->hDC, clrForeground); |
|
SetBkColor(dis->hDC, clrBackground); |
|
|
|
if (oldFont) |
|
DeleteFont(SelectFont(dis->hDC, oldFont)); |
|
|
|
// If the item has the focus, draw focus rectangle. |
|
if (dis->itemState & ODS_FOCUS) |
|
DrawFocusRect(dis->hDC, &dis->rcItem); |
|
} |
|
#endif |
|
|
|
void plResponderProc::LoadState() |
|
{ |
|
fStatePB = (IParamBlock2*)fPB->GetReferenceTarget(kResponderState, 0, fCurState); |
|
|
|
IRemoveCmdRollups(); |
|
LoadList(); |
|
|
|
HWND hSwitchCombo = GetDlgItem(fhDlg, IDC_SWITCH_COMBO); |
|
ComboBox_SetCurSel(hSwitchCombo, fStatePB->GetInt(kStateCmdSwitch)); |
|
} |
|
|
|
// THE MAGICAL TURDFEST. Max's default RemapDir tries to always clone your referenced |
|
// objects. So when we reference an INode it wants to make a clone of that INode |
|
// (and fails for some reason). To get around this I just check if the object to be cloned |
|
// is a paramblock, and actually clone it if it is. Otherwise, I just return the object. |
|
// |
|
// UPDATE: Looks like it's only with ResponderComponents. Probably the parentless PB's (which |
|
// exist due to another bug). Who cares, this works. |
|
class MyRemapDir : public RemapDir |
|
{ |
|
public: |
|
RefTargetHandle CloneRef(RefTargetHandle oldTarg) |
|
{ |
|
if (oldTarg == NULL) |
|
return NULL; |
|
else if (oldTarg->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID) |
|
return oldTarg->Clone(*this); |
|
else |
|
return oldTarg; |
|
} |
|
|
|
RefTargetHandle FindMapping(RefTargetHandle from) { hsAssert(0, "shit"); return NULL; } |
|
void PatchPointer(RefTargetHandle* patchThis, RefTargetHandle oldTarg) { hsAssert(0, "shit"); } |
|
void AddPostPatchProc(PostPatchProc* proc, bool toDelete) { hsAssert(0, "shit"); } |
|
void AddEntry(RefTargetHandle hfrom, RefTargetHandle hto) { hsAssert(0, "shit"); } |
|
void Backpatch() { hsAssert(0, "shit"); } |
|
bool BackpatchPending() { hsAssert(0, "shit"); return false; } |
|
void Clear() { hsAssert(0, "shit"); } |
|
void ClearBackpatch() { hsAssert(0, "shit"); } |
|
void DeleteThis() { hsAssert(0, "shit"); } |
|
|
|
}; |
|
// Even turdier - I had to define this to compile |
|
RefTargetHandle RemapDir::CloneRef(RefTargetHandle oldTarg) { return NULL; } |
|
static MyRemapDir gMyRemapDir; |
|
|
|
RefTargetHandle plResponderComponent::Clone(RemapDir &remap) |
|
{ |
|
plComponentBase *obj = (plComponentBase*)fClassDesc->Create(false); |
|
// Do the base clone |
|
BaseClone(this, obj, remap); |
|
// Copy our references |
|
obj->ReplaceReference(kRefComp, fCompPB->Clone(gMyRemapDir)); |
|
obj->ReplaceReference(kRefTargs, fTargsPB->Clone(remap)); |
|
|
|
return obj; |
|
} |
|
|
|
BOOL plResponderProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
static UINT dragListMsg = 0; |
|
|
|
if (dragListMsg != 0 && msg == dragListMsg) |
|
if (DragListProc(hWnd, (DRAGLISTINFO*)lParam)) |
|
return TRUE; |
|
|
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
if (!fhMenu) |
|
ICreateMenu(); |
|
|
|
fhDlg = hWnd; |
|
fhList = GetDlgItem(fhDlg, IDC_CMD_LIST); |
|
fCurState = 0; |
|
fCmdIdx = -1; |
|
|
|
fPB = pm->GetParamBlock(); |
|
fComp = (plResponderComponent*)fPB->GetOwner(); |
|
|
|
fComp->IFixOldPB(); |
|
|
|
LoadState(); |
|
|
|
// Make it so the user can drag commands to different positions |
|
dragListMsg = RegisterWindowMessage(DRAGLISTMSGSTRING); |
|
MakeDragList(GetDlgItem(hWnd, IDC_CMD_LIST)); |
|
|
|
// Setup the State Name combo |
|
HWND hStateName = GetDlgItem(hWnd, IDC_STATE_COMBO); |
|
ComboBox_LimitText(hStateName, 256); |
|
|
|
// I give up, Windows doesn't want to tell me the real font size |
|
#if 0//def CUSTOM_DRAW |
|
// TEMP |
|
HDC hDC = GetDC(hStateName); |
|
HFONT sysFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); |
|
HFONT oldFont = SelectFont(hDC, sysFont); |
|
|
|
TEXTMETRIC tm; |
|
GetTextMetrics(hDC, &tm); |
|
ComboBox_SetItemHeight(hStateName, 0, tm.tmHeight+2); |
|
|
|
DeleteFont(SelectFont(hDC, oldFont)); |
|
ReleaseDC(hStateName, hDC); |
|
#endif |
|
|
|
// Add the commands |
|
int idx = ComboBox_AddString(hStateName, "Add State"); |
|
ComboBox_SetItemData(hStateName, idx, kStateAdd); |
|
idx = ComboBox_AddString(hStateName, "Remove Current State"); |
|
ComboBox_SetItemData(hStateName, idx, kStateRemove); |
|
idx = ComboBox_AddString(hStateName, "Set Current as Default"); |
|
ComboBox_SetItemData(hStateName, idx, kStateDefault); |
|
idx = ComboBox_AddString(hStateName, "Copy Current State"); |
|
ComboBox_SetItemData(hStateName, idx, kStateCopy); |
|
|
|
HWND hSwitchCombo = GetDlgItem(hWnd, IDC_SWITCH_COMBO); |
|
|
|
int numStates = fPB->Count(kResponderStateName); |
|
for (int i = 0; i < numStates; i++) |
|
{ |
|
const char *stateName = fPB->GetStr(kResponderStateName, 0, i); |
|
char buf[128]; |
|
if (!stateName || *stateName == '\0') |
|
{ |
|
sprintf(buf, "State %d", i+1); |
|
stateName = buf; |
|
} |
|
ComboBox_InsertString(hStateName, i, stateName); |
|
ComboBox_AddString(hSwitchCombo, stateName); |
|
} |
|
|
|
ComboBox_SetCurSel(hStateName, fCurState); |
|
|
|
ComboBox_SetCurSel(hSwitchCombo, fStatePB->GetInt(kStateCmdSwitch)); |
|
} |
|
return TRUE; |
|
|
|
#ifdef CUSTOM_DRAW |
|
case WM_DRAWITEM: |
|
if (wParam == IDC_STATE_COMBO) |
|
{ |
|
IDrawComboItem((DRAWITEMSTRUCT*)lParam); |
|
return TRUE; |
|
} |
|
break; |
|
#endif |
|
|
|
case WM_SETCURSOR: |
|
{ |
|
if (HIWORD(lParam) == WM_RBUTTONDOWN && HWND(wParam) == GetDlgItem(hWnd, IDC_CMD_LIST)) |
|
{ |
|
ICmdRightClick(HWND(wParam)); |
|
return TRUE; |
|
} |
|
} |
|
break; |
|
|
|
case WM_COMMAND: |
|
if (HIWORD(wParam) == BN_CLICKED) |
|
{ |
|
if (LOWORD(wParam) == IDC_ADD_ACTIVATOR) |
|
{ |
|
// Adding an activator. Set it and refresh the UI to show it in our list. |
|
plPick::Activator(fPB, kResponderActivators, false); |
|
pm->Invalidate(kResponderActivators); |
|
return TRUE; |
|
} |
|
else if (LOWORD(wParam) == IDC_ADD_CMD) |
|
{ |
|
AddCommand(); |
|
return TRUE; |
|
} |
|
// Remove the currently selected condition |
|
else if (LOWORD(wParam) == IDC_REMOVE_CMD) |
|
{ |
|
RemoveCurCommand(); |
|
return TRUE; |
|
} |
|
} |
|
else if (HIWORD(wParam) == LBN_SELCHANGE && LOWORD(wParam) == IDC_CMD_LIST) |
|
{ |
|
ICreateCmdRollups(); |
|
return TRUE; |
|
} |
|
else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_SWITCH_COMBO) |
|
{ |
|
int sel = ComboBox_GetCurSel((HWND)lParam); |
|
if (sel != CB_ERR) |
|
fStatePB->SetValue(kStateCmdSwitch, 0, sel); |
|
} |
|
else if (LOWORD(wParam) == IDC_STATE_COMBO) |
|
{ |
|
HWND hCombo = (HWND)lParam; |
|
int code = HIWORD(wParam); |
|
|
|
// Disable accelerators when the combo has focus, so that new names can be typed in |
|
if (code == CBN_SETFOCUS) |
|
{ |
|
plMaxAccelerators::Disable(); |
|
return TRUE; |
|
} |
|
else if (code == CBN_KILLFOCUS) |
|
{ |
|
plMaxAccelerators::Enable(); |
|
return TRUE; |
|
} |
|
// State name changed, save it in the PB |
|
else if (code == CBN_EDITCHANGE) |
|
{ |
|
char buf[256]; |
|
ComboBox_GetText(hCombo, buf, sizeof(buf)); |
|
const char *curName = fPB->GetStr(kResponderStateName, 0, fCurState); |
|
if (!curName || strcmp(buf, curName)) |
|
{ |
|
HWND hSwitch = GetDlgItem(hWnd, IDC_SWITCH_COMBO); |
|
int sel = ComboBox_GetCurSel(hSwitch); |
|
ComboBox_DeleteString(hSwitch, fCurState); |
|
ComboBox_InsertString(hSwitch, fCurState, buf); |
|
ComboBox_SetCurSel(hSwitch, sel); |
|
|
|
fPB->SetValue(kResponderStateName, 0, buf, fCurState); |
|
ComboBox_DeleteString(hCombo, fCurState); |
|
ComboBox_InsertString(hCombo, fCurState, buf); |
|
// ComboBox_SetCurSel(hCombo, fCurState); |
|
} |
|
|
|
return TRUE; |
|
} |
|
else if (code == CBN_SELCHANGE) |
|
{ |
|
int sel = ComboBox_GetCurSel(hCombo); |
|
int type = ComboBox_GetItemData(hCombo, sel); |
|
|
|
if (type == kStateAdd) |
|
{ |
|
IParamBlock2 *pb = CreateParameterBlock2(&gStateBlock, nil); |
|
fCurState = AddState(pb); |
|
fCmdIdx = -1; |
|
} |
|
else if (type == kStateRemove) |
|
{ |
|
int count = fPB->Count(kResponderState); |
|
// Don't let the user remove the last state |
|
if (count == 1) |
|
{ |
|
hsMessageBox("You must have at least one state.", "Error", hsMessageBoxNormal); |
|
ComboBox_SetCurSel(hCombo, fCurState); |
|
return TRUE; |
|
} |
|
// Verify that the user really wants to delete the state |
|
else |
|
{ |
|
int ret = hsMessageBox("Are you sure you want to remove this state?", "Verify Remove", hsMessageBoxYesNo); |
|
if (ret == hsMBoxNo) |
|
{ |
|
ComboBox_SetCurSel(hCombo, fCurState); |
|
return TRUE; |
|
} |
|
} |
|
|
|
fPB->Delete(kResponderState, fCurState, 1); |
|
fPB->Delete(kResponderStateName, fCurState, 1); |
|
|
|
ComboBox_DeleteString(hCombo, fCurState); |
|
ComboBox_SetCurSel(hCombo, 0); |
|
|
|
HWND hSwitch = GetDlgItem(hWnd, IDC_SWITCH_COMBO); |
|
ComboBox_DeleteString(hSwitch, fCurState); |
|
|
|
// If the deleted state was the default, set the default to the first |
|
int defState = fPB->GetInt(kResponderStateDef); |
|
if (fCurState == defState) |
|
fPB->SetValue(kResponderStateDef, 0, 0); |
|
else if (fCurState < defState) |
|
fPB->SetValue(kResponderStateDef, 0, defState-1); |
|
|
|
// Patch up the switch commands |
|
for (int i = fCurState; i < fPB->Count(kResponderState); i++) |
|
{ |
|
IParamBlock2 *pb = (IParamBlock2*)fPB->GetReferenceTarget(kResponderState, 0, i); |
|
|
|
int switchState = pb->GetInt(kStateCmdSwitch); |
|
// TODO: might want to warn about this |
|
if (switchState == fCurState) |
|
pb->SetValue(kStateCmdSwitch, 0, 0); |
|
else if (switchState > fCurState) |
|
pb->SetValue(kStateCmdSwitch, 0, switchState-1); |
|
} |
|
|
|
fCurState = 0; |
|
fCmdIdx = -1; |
|
} |
|
else if (type == kStateDefault) |
|
{ |
|
// Set the current state as the default |
|
fPB->SetValue(kResponderStateDef, 0, fCurState); |
|
ComboBox_SetCurSel(hCombo, fCurState); |
|
} |
|
else if (type == kStateCopy) |
|
{ |
|
// Clone the state PB |
|
IParamBlock2 *origPB = (IParamBlock2*)fPB->GetReferenceTarget(kResponderState, 0, fCurState); |
|
IParamBlock2 *copyPB = (IParamBlock2*)origPB->Clone(gMyRemapDir); |
|
fCurState = AddState(copyPB); |
|
fCmdIdx = -1; |
|
} |
|
else |
|
{ |
|
fCurState = sel; |
|
fCmdIdx = -1; |
|
} |
|
|
|
LoadState(); |
|
|
|
return TRUE; |
|
} |
|
} |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
void plResponderProc::ICmdRightClick(HWND hCmdList) |
|
{ |
|
// Get the position of the cursor in screen and tree client coords |
|
POINT point, localPoint; |
|
GetCursorPos(&point); |
|
localPoint = point; |
|
ScreenToClient(hCmdList, &localPoint); |
|
|
|
LRESULT res = SendMessage(hCmdList, LB_ITEMFROMPOINT, 0, MAKELPARAM(localPoint.x, localPoint.y)); |
|
WORD index = LOWORD(res); |
|
if (index == WORD(LB_ERR)) |
|
return; |
|
|
|
RECT rect; |
|
SendMessage(hCmdList, LB_GETITEMRECT, index, (LPARAM)&rect); |
|
|
|
// Make sure we're actually ON an item, LB_ITEMFROMPOINT get the closest instead of exact |
|
if (localPoint.y >= rect.top && localPoint.y <= rect.bottom) |
|
{ |
|
BOOL enabled = fStatePB->GetInt(kStateCmdEnabled, 0, index); |
|
|
|
HMENU hMenu = CreatePopupMenu(); |
|
AppendMenu(hMenu, MF_STRING, 1, enabled ? "Disable" : "Enable"); |
|
|
|
SetForegroundWindow(fhDlg); |
|
int sel = TrackPopupMenu(hMenu, TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, 0, fhDlg, NULL); |
|
if (sel == 1) |
|
{ |
|
fStatePB->SetValue(kStateCmdEnabled, 0, !enabled, index); |
|
|
|
ListBox_DeleteString(hCmdList, index); |
|
ListBox_InsertString(hCmdList, index, GetCommandName(index)); |
|
} |
|
|
|
DestroyMenu(hMenu); |
|
} |
|
} |
|
|
|
int plResponderProc::AddState(IParamBlock2 *pb) |
|
{ |
|
int idx = fPB->Append(kResponderState, 1, (ReferenceTarget**)&pb); |
|
pb->SetValue(kStateCmdSwitch, 0, idx); |
|
|
|
char *name = ""; |
|
fPB->Append(kResponderStateName, 1, &name); |
|
|
|
HWND hCombo = GetDlgItem(fhDlg, IDC_STATE_COMBO); |
|
char buf[128]; |
|
sprintf(buf, "State %d", idx+1); |
|
ComboBox_InsertString(hCombo, idx, buf); |
|
ComboBox_SetCurSel(hCombo, idx); |
|
|
|
HWND hSwitch = GetDlgItem(fhDlg, IDC_SWITCH_COMBO); |
|
ComboBox_AddString(hSwitch, buf); |
|
|
|
return idx; |
|
} |
|
|
|
void plResponderProc::MoveCommand(int oldIdx, int newIdx) |
|
{ |
|
// Move data |
|
int insertIdx = (newIdx > oldIdx) ? newIdx+1 : newIdx; |
|
int deleteIdx = (newIdx < oldIdx) ? oldIdx+1 : oldIdx; |
|
|
|
ReferenceTarget *targ = fStatePB->GetReferenceTarget(kStateCmdParams, 0, oldIdx); |
|
fStatePB->Insert(kStateCmdParams, insertIdx, 1, &targ); |
|
fStatePB->Delete(kStateCmdParams, deleteIdx, 1); |
|
|
|
ReferenceTarget *wait = fStatePB->GetReferenceTarget(kStateCmdWait, 0, oldIdx); |
|
fStatePB->Insert(kStateCmdWait, insertIdx, 1, &wait); |
|
fStatePB->Delete(kStateCmdWait, deleteIdx, 1); |
|
|
|
BOOL oldEnabled = fStatePB->GetInt(kStateCmdEnabled, 0, oldIdx); |
|
BOOL newEnabled = fStatePB->GetInt(kStateCmdEnabled, 0, newIdx); |
|
fStatePB->SetValue(kStateCmdEnabled, 0, oldEnabled, newIdx); |
|
fStatePB->SetValue(kStateCmdEnabled, 0, newEnabled, oldIdx); |
|
|
|
ResponderWait::CmdMoved(fStatePB, oldIdx, newIdx); |
|
|
|
LoadList(); |
|
|
|
// Reselect item |
|
// (This doesn't send the LBN_SELCHANGE message so we do that manually) |
|
ListBox_SetCurSel(fhList, newIdx); |
|
ICreateCmdRollups(); |
|
}
|
|
|