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.
526 lines
16 KiB
526 lines
16 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 "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); |
|
} |
|
}
|
|
|