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.

511 lines
13 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 "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);
}
}