/*==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 . 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 void IGetReferencesRecur(plMaxNode* node, INode* target, std::vector& 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 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); } }