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.
527 lines
14 KiB
527 lines
14 KiB
4 years ago
|
/*==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 "plComponentPanel.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
#include "plMaxNode.h"
|
||
|
#include "../MaxComponent/plComponent.h"
|
||
|
#include "../MaxComponent/plComponentMgr.h"
|
||
|
#include "plComponentDlg.h"
|
||
|
#include "plMaxAccelerators.h"
|
||
|
|
||
|
extern TCHAR *GetString(int id);
|
||
|
|
||
|
class ComponentUtilClassDesc : public ClassDesc
|
||
|
{
|
||
|
public:
|
||
|
int IsPublic() { return TRUE; }
|
||
|
void* Create(BOOL loading) { return &plComponentUtil::Instance(); }
|
||
|
const TCHAR* ClassName() { return _T("Component Util"); }
|
||
|
SClass_ID SuperClassID() { return UTILITY_CLASS_ID; }
|
||
|
Class_ID ClassID() { return Class_ID(0xb220659, 0x31015552); }
|
||
|
const TCHAR* Category() { return _T(""); }
|
||
|
};
|
||
|
|
||
|
static ComponentUtilClassDesc theComponentUtilCD;
|
||
|
ClassDesc* GetComponentUtilDesc() { return &theComponentUtilCD; }
|
||
|
|
||
|
plComponentUtil::plComponentUtil() : fInterface(nil), fhPanel(nil), fCurComponent(nil), fLastComponent(nil)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
plComponentUtil& plComponentUtil::Instance()
|
||
|
{
|
||
|
static plComponentUtil theInstance;
|
||
|
return theInstance;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Proc for the currently selected object dialog
|
||
|
//
|
||
|
BOOL CALLBACK plComponentUtil::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
return Instance().DlgProc(hDlg, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
BOOL plComponentUtil::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_COMMAND:
|
||
|
// Switch to next or previous target
|
||
|
if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDC_BACK || LOWORD(wParam) == IDC_FORWARD))
|
||
|
{
|
||
|
INextTarget(LOWORD(wParam) == IDC_FORWARD);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_REF_BY_BUTTON)
|
||
|
{
|
||
|
IShowRefdBy();
|
||
|
return TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_NOTIFY:
|
||
|
{
|
||
|
NMHDR *nmhdr = (NMHDR*)lParam;
|
||
|
if (nmhdr->idFrom == IDC_COMPLIST)
|
||
|
{
|
||
|
switch (nmhdr->code)
|
||
|
{
|
||
|
// Stop Max from reading keypresses while the list has focus
|
||
|
case NM_SETFOCUS:
|
||
|
plMaxAccelerators::Disable();
|
||
|
return TRUE;
|
||
|
case NM_KILLFOCUS:
|
||
|
plMaxAccelerators::Enable();
|
||
|
return TRUE;
|
||
|
|
||
|
case LVN_KEYDOWN:
|
||
|
{
|
||
|
NMLVKEYDOWN *kd = (NMLVKEYDOWN*)lParam;
|
||
|
if (kd->wVKey == VK_DELETE)
|
||
|
IDeleteListSelection();
|
||
|
}
|
||
|
return TRUE;
|
||
|
|
||
|
// The edit box this creates kills the focus on the listbox,
|
||
|
// so add an extra disable to ignore it
|
||
|
case LVN_BEGINLABELEDIT:
|
||
|
plMaxAccelerators::Disable();
|
||
|
return TRUE;
|
||
|
|
||
|
// Finishing changing the name of a component
|
||
|
case LVN_ENDLABELEDIT:
|
||
|
{
|
||
|
NMLVDISPINFO *di = (NMLVDISPINFO*)lParam;
|
||
|
const char *name = di->item.pszText;
|
||
|
|
||
|
// If the name was changed...
|
||
|
if (name && *name != '\0')
|
||
|
{
|
||
|
// Update the name of the node
|
||
|
plComponentBase* comp = IGetListSelection();
|
||
|
comp->GetINode()->SetName(di->item.pszText);
|
||
|
|
||
|
// Make sure the column is wide enough
|
||
|
int width = ListView_GetStringWidth(nmhdr->hwndFrom, di->item.pszText)+10;
|
||
|
if (width > ListView_GetColumnWidth(nmhdr->hwndFrom, 0))
|
||
|
{
|
||
|
ListView_SetColumnWidth(nmhdr->hwndFrom, 0, width);
|
||
|
InvalidateRect(nmhdr->hwndFrom, NULL, FALSE);
|
||
|
}
|
||
|
|
||
|
// Update the name in the tree too
|
||
|
plComponentDlg::Instance().IUpdateNodeName((plMaxNode*)comp->GetINode());
|
||
|
|
||
|
// Return true to keep the changes
|
||
|
SetWindowLong(hDlg, DWL_MSGRESULT, TRUE);
|
||
|
}
|
||
|
|
||
|
plMaxAccelerators::Enable();
|
||
|
}
|
||
|
return TRUE;
|
||
|
|
||
|
// Selected component has changed. This notification can come
|
||
|
// more than necessary, so IAddRollups doesn't change the rollups
|
||
|
// if the "new" one is the same as the old.
|
||
|
case LVN_ITEMCHANGED:
|
||
|
{
|
||
|
plComponentBase* comp = IGetListSelection();
|
||
|
IAddRollups(comp);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::IDeleteListSelection()
|
||
|
{
|
||
|
plComponentBase* comp = IGetListSelection();
|
||
|
if (comp)
|
||
|
{
|
||
|
// Delete each of the selected nodes from this components target list
|
||
|
int count = fInterface->GetSelNodeCount();
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
plMaxNode *curNode = (plMaxNode*)fInterface->GetSelNode(i);
|
||
|
comp->DeleteTarget(curNode);
|
||
|
}
|
||
|
|
||
|
IUpdateRollups();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plComponentBase* plComponentUtil::IGetListSelection()
|
||
|
{
|
||
|
HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST);
|
||
|
|
||
|
int index = ListView_GetNextItem(hList, -1, LVNI_SELECTED);
|
||
|
if (index != -1)
|
||
|
{
|
||
|
LVITEM item;
|
||
|
item.mask = LVIF_PARAM;
|
||
|
item.iItem = index;
|
||
|
item.iSubItem = 0;
|
||
|
if (ListView_GetItem(hList, &item))
|
||
|
return (plComponentBase*)item.lParam;
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::BeginEditParams(Interface *ip, IUtil *iu)
|
||
|
{
|
||
|
fInterface = ip;
|
||
|
|
||
|
fhPanel = fInterface->AddRollupPage(hInstance, MAKEINTRESOURCE(IDD_COMP_PANEL), ForwardDlgProc, "Components (Selected Obj)");
|
||
|
|
||
|
// Add a column. We don't use it (graphically), but it has to be there.
|
||
|
HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST);
|
||
|
LVCOLUMN lvc;
|
||
|
lvc.mask = LVCF_TEXT;
|
||
|
lvc.pszText = "Description";
|
||
|
ListView_InsertColumn(hList, 0, &lvc);
|
||
|
|
||
|
IUpdateRollups();
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::EndEditParams(Interface *ip, IUtil *iu)
|
||
|
{
|
||
|
IDestroyRollups();
|
||
|
|
||
|
GetCOREInterface()->DeleteRollupPage(fhPanel);
|
||
|
fhPanel = nil;
|
||
|
fCurComponent = nil;
|
||
|
fInterface = nil;
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::SelectionSetChanged(Interface *ip, IUtil *iu)
|
||
|
{
|
||
|
IUpdateRollups();
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::IUpdateRollups()
|
||
|
{
|
||
|
if (!fhPanel)
|
||
|
return;
|
||
|
|
||
|
// Destroy any current rollups
|
||
|
IDestroyRollups();
|
||
|
|
||
|
HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST);
|
||
|
ListView_DeleteAllItems(hList);
|
||
|
|
||
|
// Check that something is selected.
|
||
|
int nodeCount = fInterface->GetSelNodeCount();
|
||
|
if (nodeCount == 0)
|
||
|
{
|
||
|
IAddRollups(nil);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Get the components shared among the selected nodes
|
||
|
int i;
|
||
|
INodeTab selNodes;
|
||
|
selNodes.SetCount(nodeCount);
|
||
|
for (i = 0; i < nodeCount; i++)
|
||
|
selNodes[i] = fInterface->GetSelNode(i);
|
||
|
|
||
|
INodeTab sharedComps;
|
||
|
plSharedComponents(selNodes, sharedComps);
|
||
|
|
||
|
// Add the shared components to the list
|
||
|
for (i = 0; i < sharedComps.Count(); i++)
|
||
|
{
|
||
|
plComponentBase *comp = ((plMaxNode*)sharedComps[i])->ConvertToComponent();
|
||
|
|
||
|
if (plComponentDlg::Instance().IIsHidden(comp->ClassID()))
|
||
|
continue;
|
||
|
|
||
|
IParamBlock2 *pb = comp->GetParamBlockByID(plComponent::kBlkComp);
|
||
|
|
||
|
LVITEM item = {0};
|
||
|
item.mask = LVIF_TEXT | LVIF_PARAM;
|
||
|
item.pszText = sharedComps[i]->GetName();
|
||
|
item.iItem = ListView_GetItemCount(hList);
|
||
|
item.lParam = (LPARAM)comp;
|
||
|
ListView_InsertItem(hList, &item);
|
||
|
}
|
||
|
|
||
|
// Make sure the column is wide enough
|
||
|
ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE);
|
||
|
|
||
|
// If there are rollups to show
|
||
|
if (ListView_GetItemCount(hList) > 0)
|
||
|
{
|
||
|
// Try and find the last used rollup
|
||
|
int idx = IFindListItem(fLastComponent);
|
||
|
|
||
|
// If last one wasn't found, just use the first
|
||
|
if (idx == -1)
|
||
|
idx = 0;
|
||
|
|
||
|
ListView_SetItemState(hList, idx, LVIS_SELECTED, LVIS_SELECTED);
|
||
|
ListView_EnsureVisible(hList, idx, FALSE);
|
||
|
}
|
||
|
else
|
||
|
IAddRollups(nil);
|
||
|
}
|
||
|
|
||
|
int plComponentUtil::IFindListItem(plComponentBase* comp)
|
||
|
{
|
||
|
LVFINDINFO fi;
|
||
|
fi.flags = LVFI_PARAM;
|
||
|
fi.lParam = (LPARAM)comp;
|
||
|
return ListView_FindItem(GetDlgItem(fhPanel, IDC_COMPLIST), -1, &fi);
|
||
|
}
|
||
|
|
||
|
#include "../MaxComponent/plAutoUIComp.h"
|
||
|
|
||
|
void plComponentUtil::IAddRollups(plComponentBase* comp)
|
||
|
{
|
||
|
if (fCurComponent == comp)
|
||
|
return;
|
||
|
|
||
|
IDestroyRollups();
|
||
|
fCurComponent = comp;
|
||
|
if (comp)
|
||
|
fLastComponent = comp;
|
||
|
|
||
|
//
|
||
|
// Update the targets dialog
|
||
|
//
|
||
|
UInt32 numTargs = 0;
|
||
|
if (fCurComponent)
|
||
|
{
|
||
|
// Only count non-nil targets
|
||
|
for (UInt32 i = 0; i < fCurComponent->NumTargets(); i++)
|
||
|
if (fCurComponent->GetTarget(i))
|
||
|
numTargs++;
|
||
|
}
|
||
|
|
||
|
// Put the number of targets in the text box
|
||
|
char buf[12];
|
||
|
itoa(numTargs, buf, 10);
|
||
|
SetWindowText(GetDlgItem(fhPanel, IDC_NUM_TARGS), buf);
|
||
|
|
||
|
// Enable the forward/back buttons if there are multiple targets
|
||
|
BOOL useButtons = (numTargs > 1);
|
||
|
EnableWindow(GetDlgItem(fhPanel, IDC_BACK), useButtons);
|
||
|
EnableWindow(GetDlgItem(fhPanel, IDC_FORWARD), useButtons);
|
||
|
|
||
|
//
|
||
|
// Add the component rollups
|
||
|
//
|
||
|
if (fCurComponent)
|
||
|
fCurComponent->CreateRollups();
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::IDestroyRollups()
|
||
|
{
|
||
|
if (fCurComponent)
|
||
|
fCurComponent->DestroyRollups();
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::INextTarget(bool forward)
|
||
|
{
|
||
|
// fCurComponent = IGetListSelection();
|
||
|
// plComponentBase *comp = fCurComponent->ConvertToComponent();
|
||
|
|
||
|
// Loop through the selected component's targets until we find the currently selected node.
|
||
|
// This gives us a starting point to find the next or previous target in this component's list.
|
||
|
plMaxNode *curNode = (plMaxNode*)GetCOREInterface()->GetSelNode(0);
|
||
|
UInt32 count = fCurComponent->NumTargets();
|
||
|
for (UInt32 i = 0; i < count; i++)
|
||
|
{
|
||
|
if (fCurComponent->GetTarget(i) == curNode)
|
||
|
{
|
||
|
// Got to loop until the target is non-nil here, so we skip over
|
||
|
// any deleted nodes.
|
||
|
UInt32 targIdx = i;
|
||
|
do
|
||
|
{
|
||
|
// Figure out which target to change to
|
||
|
if (forward)
|
||
|
{
|
||
|
if (targIdx == count-1)
|
||
|
targIdx = 0;
|
||
|
else
|
||
|
targIdx = targIdx + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (targIdx == 0)
|
||
|
targIdx = count-1;
|
||
|
else
|
||
|
targIdx = targIdx - 1;
|
||
|
}
|
||
|
} while (!fCurComponent->GetTarget(targIdx));
|
||
|
|
||
|
// Select the new target
|
||
|
theHold.Begin();
|
||
|
fInterface->RedrawViews(fInterface->GetTime(), REDRAW_BEGIN);
|
||
|
|
||
|
fInterface->SelectNode(fCurComponent->GetTarget(targIdx));
|
||
|
|
||
|
fInterface->RedrawViews(fInterface->GetTime(), REDRAW_END);
|
||
|
theHold.Accept("Select");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::IUpdateNodeName(plMaxNode *node)
|
||
|
{
|
||
|
if (!fhPanel)
|
||
|
return;
|
||
|
|
||
|
// Update the name in the list
|
||
|
int idx = IFindListItem(node->ConvertToComponent());
|
||
|
if (idx != -1)
|
||
|
{
|
||
|
HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST);
|
||
|
ListView_SetItemText(hList, idx, 0, node->GetName());
|
||
|
// Make sure the column is wide enough
|
||
|
ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::IComponentPreDelete(plComponentBase* comp)
|
||
|
{
|
||
|
if (fCurComponent == comp)
|
||
|
{
|
||
|
IDestroyRollups();
|
||
|
fCurComponent = nil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include <vector>
|
||
|
|
||
|
void IGetReferencesRecur(plMaxNode* node, INode* target, std::vector<plMaxNode*>& nodes)
|
||
|
{
|
||
|
plComponentBase* comp = node->ConvertToComponent();
|
||
|
if (comp && comp->DoReferenceNode(target))
|
||
|
{
|
||
|
const char* name = node->GetName();
|
||
|
nodes.push_back(node);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < node->NumberOfChildren(); i++)
|
||
|
{
|
||
|
IGetReferencesRecur((plMaxNode*)node->GetChildNode(i), target, nodes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL CALLBACK RefDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
if (GetCOREInterface()->GetSelNodeCount() > 0)
|
||
|
{
|
||
|
INode* node = GetCOREInterface()->GetSelNode(0);
|
||
|
|
||
|
char buf[256];
|
||
|
sprintf(buf, "%s is Ref'd By", node->GetName());
|
||
|
SetWindowText(hDlg, buf);
|
||
|
|
||
|
std::vector<plMaxNode*> nodes;
|
||
|
IGetReferencesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), node, nodes);
|
||
|
|
||
|
HWND hList = GetDlgItem(hDlg, IDC_REF_LIST);
|
||
|
for (int i = 0; i < nodes.size(); i++)
|
||
|
{
|
||
|
plMaxNode* node = nodes[i];
|
||
|
int idx = ListBox_AddString(hList, node->GetName());
|
||
|
ListBox_SetItemData(hList, idx, node);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_CLOSE)
|
||
|
{
|
||
|
EndDialog(hDlg, 0);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (HIWORD(wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_REF_LIST)
|
||
|
{
|
||
|
// If the user double clicked on a component, return it so we can
|
||
|
// select the nodes it's attached to
|
||
|
HWND hList = HWND(lParam);
|
||
|
int sel = ListBox_GetCurSel(hList);
|
||
|
LRESULT node = ListBox_GetItemData(hList, sel);
|
||
|
EndDialog(hDlg, node);
|
||
|
return TRUE;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void plComponentUtil::IShowRefdBy()
|
||
|
{
|
||
|
INode* node = (INode*)DialogBox(hInstance,
|
||
|
MAKEINTRESOURCE(IDD_REF_BY),
|
||
|
GetCOREInterface()->GetMAXHWnd(),
|
||
|
RefDlgProc);
|
||
|
if (node)
|
||
|
{
|
||
|
INodeTab nodes;
|
||
|
nodes.Append(1, &node);
|
||
|
plComponentDlg::Instance().SelectComponentTargs(nodes);
|
||
|
}
|
||
|
}
|