Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
719 lines
19 KiB
C++
719 lines
19 KiB
C++
/**********************************************************************
|
|
*<
|
|
FILE: linkctrl.cpp
|
|
|
|
DESCRIPTION: An animated link controller
|
|
|
|
CREATED BY: Rolf Berteig
|
|
|
|
HISTORY: 1/25/97
|
|
|
|
*> Copyright (c) 1996, All Rights Reserved.
|
|
**********************************************************************/
|
|
#include "ctrl.h"
|
|
#include "units.h"
|
|
#include "istdplug.h"
|
|
|
|
#define LINKCTRL_NAME GetString(IDS_RB_LINKCTRL)
|
|
|
|
class LinkCtrl;
|
|
|
|
class PickLinkMode :
|
|
public PickModeCallback,
|
|
public PickNodeCallback {
|
|
public:
|
|
LinkCtrl *cont;
|
|
|
|
PickLinkMode(LinkCtrl *c) {cont=c;}
|
|
BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);
|
|
BOOL Pick(IObjParam *ip,ViewExp *vpt);
|
|
void EnterMode(IObjParam *ip);
|
|
void ExitMode(IObjParam *ip);
|
|
BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;}
|
|
BOOL Filter(INode *node);
|
|
PickNodeCallback *GetFilter() {return this;}
|
|
};
|
|
|
|
class LinkCtrl : public ILinkCtrl {
|
|
public:
|
|
Control *tmControl; // ref 0
|
|
Tab<INode*> nodes; // ref 1-n
|
|
Tab<TimeValue> times;
|
|
|
|
static HWND hWnd;
|
|
static IObjParam *ip;
|
|
static LinkCtrl *editCont;
|
|
static ISpinnerControl *iTime;
|
|
static ICustButton *iPickOb, *iDelOb;
|
|
static PickLinkMode *pickLinkMode;
|
|
static BOOL valid;
|
|
|
|
LinkCtrl(BOOL loading=FALSE);
|
|
|
|
// Animatable methods
|
|
void DeleteThis() {delete this;}
|
|
int GetNumTargets() {return 0;}
|
|
Class_ID ClassID() {return LINKCTRL_CLASSID;}
|
|
SClass_ID SuperClassID() {return CTRL_MATRIX3_CLASS_ID;}
|
|
void GetClassName(TSTR& s) {s = LINKCTRL_NAME;}
|
|
void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev);
|
|
void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next);
|
|
int NumSubs() {return 1;}
|
|
Animatable* SubAnim(int i) {return tmControl;}
|
|
TSTR SubAnimName(int i) {return GetString(IDS_RB_TRANSFORM);}
|
|
|
|
// Reference methods
|
|
RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage);
|
|
IOResult Save(ISave *isave);
|
|
IOResult Load(ILoad *iload);
|
|
RefTargetHandle Clone(RemapDir &remap = NoRemap());
|
|
int NumRefs() {return 1+nodes.Count();}
|
|
RefTargetHandle GetReference(int i);
|
|
void SetReference(int i, RefTargetHandle rtarg);
|
|
BOOL AssignController(Animatable *control,int subAnim);
|
|
|
|
// Control methods
|
|
void Copy(Control *from);
|
|
BOOL IsLeaf() {return FALSE;}
|
|
void CommitValue(TimeValue t) {}
|
|
void RestoreValue(TimeValue t) {}
|
|
void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
|
|
void SetValue(TimeValue t, void *val, int commit=1, GetSetMethod method=CTRL_ABSOLUTE);
|
|
BOOL InheritsParentTransform() {return FALSE;}
|
|
BOOL CanInstanceController() {return FALSE;}
|
|
|
|
// Animatable methods
|
|
void CopyKeysFromTime(TimeValue src,TimeValue dst,DWORD flags) {tmControl->CopyKeysFromTime(src,dst,flags);}
|
|
BOOL IsKeyAtTime(TimeValue t,DWORD flags) {return tmControl->IsKeyAtTime(t,flags);}
|
|
BOOL GetNextKeyTime(TimeValue t,DWORD flags,TimeValue &nt) {return tmControl->GetNextKeyTime(t,flags,nt);}
|
|
int GetKeyTimes(Tab<TimeValue> ×,Interval range,DWORD flags) {return tmControl->GetKeyTimes(times,range,flags);}
|
|
int GetKeySelState(BitArray &sel,Interval range,DWORD flags) {return tmControl->GetKeySelState(sel,range,flags);}
|
|
Control *GetPositionController() {return tmControl->GetPositionController();}
|
|
Control *GetRotationController() {return tmControl->GetRotationController();}
|
|
Control *GetScaleController() {return tmControl->GetScaleController();}
|
|
BOOL SetPositionController(Control *c) {return tmControl->SetPositionController(c);}
|
|
BOOL SetRotationController(Control *c) {return tmControl->SetRotationController(c);}
|
|
BOOL SetScaleController(Control *c) {return tmControl->SetScaleController(c);}
|
|
void MapKeys(TimeMap *map,DWORD flags);
|
|
|
|
// From ILinkCtrl
|
|
int GetParentCount() {return nodes.Count();}
|
|
TimeValue GetLinkTime(int i) {return times[i];}
|
|
void SetLinkTime(int i,TimeValue t) {SetTime(t,i);}
|
|
void LinkTimeChanged() {SortNodes(-1);}
|
|
|
|
BOOL SetFrameNumber(int targetNumber, int frameNumber) {return TRUE;}
|
|
BOOL SetTarget(INode *target, int targetNumber) {return TRUE;}
|
|
BOOL AddTarget(INode *target, int frameNo) {return TRUE;}
|
|
INode* GetNode(int targetNumber) {return NULL;}
|
|
|
|
|
|
BOOL DeleteTarget(int selection) {return TRUE;}
|
|
int GetFrameNumber(int targetNumber) {return 0;}
|
|
|
|
// Local methods
|
|
Matrix3 GetParentTM(TimeValue t,Interval *valid=NULL);
|
|
Matrix3 CompTM(TimeValue t, int i);
|
|
void AddNewLink(INode *node,TimeValue t);
|
|
void DeleteLink(int i);
|
|
void SetTime(TimeValue t,int i);
|
|
void SortNodes(int sel);
|
|
|
|
void SetupDialog(HWND hWnd);
|
|
void DestroyDialog();
|
|
void SetupList(int sel=-1);
|
|
void ListSelChanged();
|
|
void Invalidate();
|
|
void Update();
|
|
};
|
|
|
|
|
|
class LinksRestore : public RestoreObj {
|
|
public:
|
|
LinkCtrl *cont;
|
|
Tab<INode*> unodes;
|
|
Tab<INode*> rnodes;
|
|
Tab<TimeValue> utimes;
|
|
Tab<TimeValue> rtimes;
|
|
|
|
LinksRestore(LinkCtrl *c) {
|
|
cont = c;
|
|
unodes = cont->nodes;
|
|
utimes = cont->times;
|
|
}
|
|
void Restore(int isUndo) {
|
|
if (isUndo) {
|
|
rnodes = cont->nodes;
|
|
rtimes = cont->times;
|
|
}
|
|
cont->nodes = unodes;
|
|
cont->times = utimes;
|
|
cont->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
cont->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
cont->Invalidate();
|
|
}
|
|
void Redo() {
|
|
cont->nodes = rnodes;
|
|
cont->times = rtimes;
|
|
cont->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
cont->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
cont->Invalidate();
|
|
}
|
|
void EndHold() {cont->ClearAFlag(A_HELD);}
|
|
};
|
|
|
|
|
|
|
|
//--- Class Descriptor ----------------------------------------------
|
|
|
|
HWND LinkCtrl::hWnd = NULL;
|
|
IObjParam *LinkCtrl::ip = NULL;
|
|
LinkCtrl *LinkCtrl::editCont = NULL;
|
|
ISpinnerControl *LinkCtrl::iTime = NULL;
|
|
ICustButton *LinkCtrl::iPickOb = NULL;
|
|
ICustButton *LinkCtrl::iDelOb = NULL;
|
|
PickLinkMode *LinkCtrl::pickLinkMode = NULL;
|
|
BOOL LinkCtrl::valid = FALSE;
|
|
|
|
class LinkCtrlClassDesc:public ClassDesc {
|
|
public:
|
|
int IsPublic() {return 1;}
|
|
void * Create(BOOL loading) {return new LinkCtrl(loading);}
|
|
const TCHAR * ClassName() {return LINKCTRL_NAME;}
|
|
SClass_ID SuperClassID() {return CTRL_MATRIX3_CLASS_ID; }
|
|
Class_ID ClassID() {return LINKCTRL_CLASSID;}
|
|
const TCHAR* Category() {return _T("");}
|
|
};
|
|
static LinkCtrlClassDesc linkCtrlCD;
|
|
ClassDesc* GetLinkCtrlDesc() {return &linkCtrlCD;}
|
|
|
|
BOOL CALLBACK LinkParamDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
|
|
//--- LinkCtrl methods -----------------------------------------------
|
|
|
|
LinkCtrl::LinkCtrl(BOOL loading)
|
|
{
|
|
tmControl = NULL;
|
|
if (!loading) {
|
|
Control *cont;
|
|
ClassDesc *desc = GetDefaultController(CTRL_MATRIX3_CLASS_ID);
|
|
if (desc && desc->ClassID()==ClassID()) {
|
|
cont = (Control*)CreateInstance(CTRL_MATRIX3_CLASS_ID, Class_ID(PRS_CONTROL_CLASS_ID,0));
|
|
} else {
|
|
cont = NewDefaultMatrix3Controller();
|
|
}
|
|
ReplaceReference(0,cont);
|
|
}
|
|
}
|
|
|
|
void LinkCtrl::BeginEditParams(
|
|
IObjParam *ip, ULONG flags,Animatable *prev)
|
|
{
|
|
if (flags&BEGIN_EDIT_MOTION) {
|
|
this->ip = ip;
|
|
editCont = this;
|
|
hWnd = ip->AddRollupPage(
|
|
hInstance,
|
|
MAKEINTRESOURCE(IDD_LINK_PARAMS),
|
|
LinkParamDialogProc,
|
|
GetString(IDS_RB_LINKPARAMS),
|
|
(LPARAM)this);
|
|
pickLinkMode = new PickLinkMode(this);
|
|
tmControl->BeginEditParams(ip,flags,NULL);
|
|
}
|
|
}
|
|
|
|
void LinkCtrl::EndEditParams(
|
|
IObjParam *ip, ULONG flags,Animatable *next)
|
|
{
|
|
if (editCont) {
|
|
ip->ClearPickMode();
|
|
tmControl->EndEditParams(ip,flags,NULL);
|
|
ip->DeleteRollupPage(hWnd);
|
|
hWnd = NULL;
|
|
ip = NULL;
|
|
editCont = NULL;
|
|
delete pickLinkMode;
|
|
pickLinkMode = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
RefResult LinkCtrl::NotifyRefChanged(
|
|
Interval iv, RefTargetHandle hTarg,
|
|
PartID& partID, RefMessage msg)
|
|
{
|
|
switch (msg) {
|
|
case REFMSG_TARGET_DELETED: {
|
|
for (int i=nodes.Count()-1; i>=0; i--) {
|
|
if (nodes[i]==hTarg) DeleteLink(i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return REF_SUCCEED;
|
|
}
|
|
|
|
#define LINKCOUNT_CHUNK 0x0100
|
|
#define TIMES_CHUNK 0x0110
|
|
|
|
IOResult LinkCtrl::Save(ISave *isave)
|
|
{
|
|
ULONG nb;
|
|
int ct = nodes.Count();
|
|
|
|
isave->BeginChunk(LINKCOUNT_CHUNK);
|
|
isave->Write(&ct,sizeof(ct),&nb);
|
|
isave->EndChunk();
|
|
|
|
if (ct) {
|
|
isave->BeginChunk(TIMES_CHUNK);
|
|
isave->Write(times.Addr(0),sizeof(TimeValue)*ct,&nb);
|
|
isave->EndChunk();
|
|
}
|
|
|
|
return IO_OK;
|
|
}
|
|
|
|
IOResult LinkCtrl::Load(ILoad *iload)
|
|
{
|
|
ULONG nb;
|
|
int ct;
|
|
IOResult res;
|
|
|
|
while (IO_OK==(res=iload->OpenChunk())) {
|
|
switch (iload->CurChunkID()) {
|
|
case LINKCOUNT_CHUNK: {
|
|
iload->Read(&ct,sizeof(ct),&nb);
|
|
nodes.SetCount(ct);
|
|
times.SetCount(ct);
|
|
for (int i=0; i<ct; i++) nodes[i] = NULL;
|
|
break;
|
|
}
|
|
|
|
case TIMES_CHUNK:
|
|
iload->Read(times.Addr(0),sizeof(TimeValue)*ct,&nb);
|
|
break;
|
|
}
|
|
iload->CloseChunk();
|
|
if (res!=IO_OK) return res;
|
|
}
|
|
|
|
return IO_OK;
|
|
}
|
|
|
|
|
|
RefTargetHandle LinkCtrl::Clone(RemapDir &remap)
|
|
{
|
|
LinkCtrl *ctrl = new LinkCtrl();
|
|
ctrl->ReplaceReference(0,remap.CloneRef(tmControl));
|
|
ctrl->nodes.SetCount(nodes.Count());
|
|
ctrl->times.SetCount(times.Count());
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
ctrl->nodes[i] = NULL;
|
|
ctrl->ReplaceReference(i+1,nodes[i]);
|
|
ctrl->times[i] = times[i];
|
|
}
|
|
return ctrl;
|
|
}
|
|
|
|
RefTargetHandle LinkCtrl::GetReference(int i)
|
|
{
|
|
if (i==0) return tmControl;
|
|
return nodes[i-1];
|
|
}
|
|
|
|
void LinkCtrl::SetReference(int i, RefTargetHandle rtarg)
|
|
{
|
|
if (i==0) tmControl = (Control*)rtarg;
|
|
else nodes[i-1] = (INode*)rtarg;
|
|
}
|
|
|
|
BOOL LinkCtrl::AssignController(Animatable *control,int subAnim)
|
|
{
|
|
if (control->ClassID()==ClassID()) return FALSE;
|
|
ReplaceReference(0,(ReferenceTarget*)control);
|
|
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
return TRUE;
|
|
}
|
|
|
|
void LinkCtrl::Copy(Control *from)
|
|
{
|
|
if (from->ClassID()==ClassID()) {
|
|
LinkCtrl *lc = (LinkCtrl*)from;
|
|
ReplaceReference(0,(ReferenceTarget*)lc->tmControl->Clone());
|
|
} else {
|
|
ReplaceReference(0,(ReferenceTarget*)from->Clone());
|
|
}
|
|
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
}
|
|
|
|
void LinkCtrl::GetValue(
|
|
TimeValue t, void *val, Interval &valid,
|
|
GetSetMethod method)
|
|
{
|
|
Matrix3 *ptm = (Matrix3*)val;
|
|
*ptm = GetParentTM(t,&valid);
|
|
tmControl->GetValue(t,val,valid,method);
|
|
}
|
|
|
|
void LinkCtrl::SetValue(
|
|
TimeValue t, void *val, int commit,
|
|
GetSetMethod method)
|
|
{
|
|
SetXFormPacket *pckt = (SetXFormPacket*)val;
|
|
pckt->tmParent = GetParentTM(t);
|
|
tmControl->SetValue(t,val,commit,method);
|
|
}
|
|
|
|
|
|
Matrix3 LinkCtrl::CompTM(TimeValue t, int i)
|
|
{
|
|
Matrix3 rtm(1);
|
|
if (i) {
|
|
// Evaluate the tm of the previous node the instant of the switch
|
|
Matrix3 pptm = CompTM(times[i],i-1);
|
|
|
|
// Evaluate the tm of this node the instant of the switch
|
|
Matrix3 ptm = nodes[i]->GetNodeTM(times[i]);
|
|
|
|
rtm = pptm*Inverse(ptm);
|
|
}
|
|
return rtm*nodes[i]->GetNodeTM(t);
|
|
}
|
|
|
|
Matrix3 LinkCtrl::GetParentTM(TimeValue t,Interval *valid)
|
|
{
|
|
if (!nodes.Count()) return Matrix3(1);
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
if (times[i]>t) {
|
|
if (i) i--;
|
|
break;
|
|
}
|
|
}
|
|
if (i>nodes.Count()-1) i = nodes.Count()-1;
|
|
|
|
// Compute a matrix that adjusts the transitions between links so
|
|
// as to maintain continuity
|
|
Matrix3 rtm(1);
|
|
if (i) {
|
|
// Evaluate (recursively) the tm of the previous node the instant of the switch
|
|
//Matrix3 pptm = nodes[i-1]->GetNodeTM(times[i]);
|
|
Matrix3 pptm = CompTM(times[i],i-1);
|
|
|
|
// Evaluate the tm of this node the instant of the switch
|
|
Matrix3 ptm = nodes[i]->GetNodeTM(times[i]);
|
|
|
|
rtm = pptm*Inverse(ptm);
|
|
}
|
|
|
|
// Validity is limited to the duration of this link
|
|
if (valid) {
|
|
Interval iv = FOREVER;
|
|
if (i) iv.SetStart(times[i]);
|
|
if (i<times.Count()-1) iv.SetEnd(times[i+1]);
|
|
*valid &= iv;
|
|
}
|
|
|
|
return rtm*nodes[i]->GetNodeTM(t,valid);
|
|
}
|
|
|
|
|
|
void LinkCtrl::AddNewLink(INode *node,TimeValue t)
|
|
{
|
|
if (theHold.Holding() && !TestAFlag(A_HELD))
|
|
theHold.Put(new LinksRestore(this));
|
|
|
|
if (!nodes.Count()) {
|
|
Matrix3 tm(1), ntm = node->GetNodeTM(t), itm(1);
|
|
tmControl->GetValue(t,&tm,FOREVER,CTRL_RELATIVE);
|
|
tmControl->ChangeParents(t,itm,ntm,tm);
|
|
}
|
|
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
if (times[i]>t) break;
|
|
}
|
|
nodes.Insert(i,1,&node);
|
|
times.Insert(i,1,&t);
|
|
MakeRefByID(FOREVER,i+1,node);
|
|
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
SetupList(i);
|
|
}
|
|
|
|
void LinkCtrl::DeleteLink(int i)
|
|
{
|
|
if (nodes.Count()==1) {
|
|
TimeValue t = GetCOREInterface()->GetTime();
|
|
Matrix3 ptm = GetParentTM(t);
|
|
Matrix3 itm(1), tm = ptm;
|
|
tmControl->GetValue(t,&tm,FOREVER,CTRL_RELATIVE);
|
|
tmControl->ChangeParents(t,ptm,itm,tm);
|
|
}
|
|
|
|
DeleteReference(i+1);
|
|
if (theHold.Holding() && !TestAFlag(A_HELD))
|
|
theHold.Put(new LinksRestore(this));
|
|
nodes.Delete(i,1);
|
|
times.Delete(i,1);
|
|
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
if (hWnd && editCont==this) SetupList();
|
|
}
|
|
|
|
void LinkCtrl::SetTime(TimeValue t,int i)
|
|
{
|
|
if (theHold.Holding() && !TestAFlag(A_HELD)) {
|
|
theHold.Put(new LinksRestore(this));
|
|
SetAFlag(A_HELD);
|
|
}
|
|
times[i] = t;
|
|
SortNodes(i);
|
|
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
}
|
|
|
|
void LinkCtrl::MapKeys(TimeMap *map,DWORD flags)
|
|
{
|
|
if (theHold.Holding() && !TestAFlag(A_HELD)) {
|
|
theHold.Put(new LinksRestore(this));
|
|
SetAFlag(A_HELD);
|
|
}
|
|
|
|
for (int i=0; i<times.Count(); i++) {
|
|
times[i] = map->map(times[i]);
|
|
}
|
|
|
|
if (hWnd && editCont==this) SetupList();
|
|
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
}
|
|
|
|
static LinkCtrl *theSortCtrl = NULL;
|
|
static int __cdecl CompareNodes(const void *a, const void *b)
|
|
{
|
|
int *aa = (int*)a;
|
|
int *bb = (int*)b;
|
|
assert(theSortCtrl);
|
|
return theSortCtrl->times[*aa] - theSortCtrl->times[*bb];
|
|
}
|
|
|
|
void LinkCtrl::SortNodes(int sel)
|
|
{
|
|
// Setup a map for a shell sort
|
|
Tab<int> map;
|
|
map.SetCount(nodes.Count());
|
|
for (int i=0; i<map.Count(); i++) map[i] = i;
|
|
|
|
// Sort
|
|
theSortCtrl = this;
|
|
map.Sort(CompareNodes);
|
|
|
|
// Put everything in the right place
|
|
Tab<INode*> onodes = nodes;
|
|
Tab<TimeValue> otimes = times;
|
|
for (i=0; i<nodes.Count(); i++) {
|
|
nodes[i] = onodes[map[i]];
|
|
times[i] = otimes[map[i]];
|
|
}
|
|
|
|
// Redo the list selecting the node in its new place
|
|
if (sel>=0) {
|
|
for (i=0; i<map.Count(); i++) {
|
|
if (map[i]==sel) {
|
|
SetupList(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--- UI ------------------------------------------------------------
|
|
|
|
|
|
static BOOL CALLBACK LinkParamDialogProc(
|
|
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LinkCtrl *cont = (LinkCtrl*)GetWindowLong(hWnd,GWL_USERDATA);
|
|
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
cont = (LinkCtrl*)lParam;
|
|
SetWindowLong(hWnd,GWL_USERDATA,lParam);
|
|
cont->SetupDialog(hWnd);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_LINK_PICK:
|
|
cont->ip->SetPickMode(cont->pickLinkMode);
|
|
break;
|
|
|
|
case IDC_LINK_DELETE: {
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
|
|
if (sel!=LB_ERR) {
|
|
theHold.Begin();
|
|
cont->DeleteLink(sel);
|
|
theHold.Accept(GetString(IDS_RB_DELETELINK));
|
|
cont->ip->RedrawViews(cont->ip->GetTime());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_LINK_LIST:
|
|
if (HIWORD(wParam)==LBN_SELCHANGE) {
|
|
cont->ListSelChanged();
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CC_SPINNER_BUTTONDOWN:
|
|
theHold.Begin();
|
|
break;
|
|
|
|
case CC_SPINNER_CHANGE: {
|
|
ISpinnerControl *spin = (ISpinnerControl*)lParam;
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
|
|
if (sel!=LB_ERR) cont->SetTime(spin->GetIVal(),sel);
|
|
cont->ip->RedrawViews(cont->ip->GetTime());
|
|
break;
|
|
}
|
|
|
|
case CC_SPINNER_BUTTONUP:
|
|
if (HIWORD(wParam)) {
|
|
theHold.Accept(GetString(IDS_RB_SETLINKTIME));
|
|
} else {
|
|
theHold.Cancel();
|
|
}
|
|
cont->ip->RedrawViews(cont->ip->GetTime());
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
cont->Update();
|
|
return FALSE;
|
|
|
|
case WM_DESTROY:
|
|
cont->DestroyDialog();
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void LinkCtrl::SetupDialog(HWND hWnd)
|
|
{
|
|
this->hWnd = hWnd;
|
|
|
|
iTime = GetISpinner(GetDlgItem(hWnd,IDC_LINK_TIMESPIN));
|
|
iTime->SetLimits(TIME_NegInfinity,TIME_PosInfinity,FALSE);
|
|
iTime->SetScale(10.0f);
|
|
iTime->LinkToEdit(GetDlgItem(hWnd,IDC_LINK_TIME),EDITTYPE_TIME);
|
|
|
|
iPickOb = GetICustButton(GetDlgItem(hWnd,IDC_LINK_PICK));
|
|
iDelOb = GetICustButton(GetDlgItem(hWnd,IDC_LINK_DELETE));
|
|
|
|
iPickOb->SetType(CBT_CHECK);
|
|
iPickOb->SetHighlightColor(GREEN_WASH);
|
|
|
|
SetupList();
|
|
}
|
|
|
|
void LinkCtrl::DestroyDialog()
|
|
{
|
|
ReleaseISpinner(iTime);
|
|
ReleaseICustButton(iPickOb);
|
|
ReleaseICustButton(iDelOb);
|
|
iTime = NULL;
|
|
iPickOb = NULL;
|
|
iDelOb = NULL;
|
|
}
|
|
|
|
void LinkCtrl::SetupList(int sel)
|
|
{
|
|
if (!hWnd || editCont!=this) return;
|
|
int csel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
|
|
if (sel>=0) csel = sel;
|
|
SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_RESETCONTENT,0,0);
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_ADDSTRING,0,
|
|
(LPARAM)nodes[i]->GetName());
|
|
}
|
|
if (csel!=LB_ERR && csel<nodes.Count()) {
|
|
SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_SETCURSEL,csel,0);
|
|
}
|
|
ListSelChanged();
|
|
}
|
|
|
|
void LinkCtrl::ListSelChanged()
|
|
{
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
|
|
if (sel!=LB_ERR) {
|
|
iDelOb->Enable();
|
|
iTime->Enable();
|
|
iTime->SetValue(times[sel],FALSE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LINK_TIMELABEL),TRUE);
|
|
} else {
|
|
iDelOb->Disable();
|
|
iTime->Disable();
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LINK_TIMELABEL),FALSE);
|
|
}
|
|
}
|
|
|
|
void LinkCtrl::Invalidate()
|
|
{
|
|
if (hWnd && editCont==this) {
|
|
valid = FALSE;
|
|
InvalidateRect(hWnd,NULL,FALSE);
|
|
}
|
|
}
|
|
|
|
void LinkCtrl::Update()
|
|
{
|
|
if (!valid) {
|
|
valid = TRUE;
|
|
SetupList();
|
|
}
|
|
}
|
|
|
|
|
|
//--- PickLinkMode ------------------------------------------------
|
|
|
|
BOOL PickLinkMode::Filter(INode *node)
|
|
{
|
|
if (node) {
|
|
return (node->TestForLoop(FOREVER,cont)==REF_SUCCEED);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL PickLinkMode::HitTest(
|
|
IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags)
|
|
{
|
|
INode *node = cont->ip->PickNode(hWnd,m);
|
|
return node?TRUE:FALSE;
|
|
}
|
|
|
|
BOOL PickLinkMode::Pick(IObjParam *ip,ViewExp *vpt)
|
|
{
|
|
INode *node = vpt->GetClosestHit();
|
|
if (node) {
|
|
if (node->TestForLoop(FOREVER,cont)==REF_SUCCEED) {
|
|
theHold.Begin();
|
|
cont->AddNewLink(node,ip->GetTime());
|
|
theHold.Accept(GetString(IDS_RB_ADDLINK));
|
|
cont->ip->RedrawViews(cont->ip->GetTime());
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void PickLinkMode::EnterMode(IObjParam *ip)
|
|
{cont->iPickOb->SetCheck(TRUE);}
|
|
|
|
void PickLinkMode::ExitMode(IObjParam *ip)
|
|
{cont->iPickOb->SetCheck(FALSE);}
|
|
|