/*==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 . 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 "max.h" #include "iparamb2.h" #include "plComponentDlg.h" #include "../MaxComponent/plComponentBase.h" #include "../MaxComponent/plComponentMgr.h" #include "../MaxComponent/plComponentReg.h" #include "resource.h" #include "plMaxNode.h" #include "plComponentPanel.h" #include "plMaxAccelerators.h" #include extern HINSTANCE hInstance; plComponentDlg::plComponentDlg() : fhDlg(nil), fCompMenu(nil), fTypeMenu(nil), fCommentNode(nil) { fInterface = GetCOREInterface(); RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_OPEN); RegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_NEW); RegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_RESET); RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_MERGE); RegisterNotification(INotify, 0, NOTIFY_PRE_IMPORT); RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE); RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE_OLD); RegisterNotification(INotify, 0, NOTIFY_FILE_POST_OPEN); RegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_NEW); RegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_RESET); RegisterNotification(INotify, 0, NOTIFY_FILE_POST_MERGE); RegisterNotification(INotify, 0, NOTIFY_POST_IMPORT); RegisterNotification(INotify, 0, NOTIFY_SYSTEM_SHUTDOWN); } plComponentDlg::~plComponentDlg() { if (fhDlg) { fInterface->UnRegisterDlgWnd(fhDlg); DestroyWindow(fhDlg); } if (fCompMenu) DestroyMenu(fCompMenu); if (fTypeMenu) DestroyMenu(fTypeMenu); } plComponentDlg& plComponentDlg::Instance() { static plComponentDlg theInstance; return theInstance; } void plComponentDlg::Open() { if (!fhDlg) { fhDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_COMP_MAIN), GetCOREInterface()->GetMAXHWnd(), ForwardDlgProc); GetWindowRect(fhDlg, &fLastRect); fSmallestSize.x = fLastRect.right - fLastRect.left; fSmallestSize.y = fLastRect.bottom - fLastRect.top; RECT rect; memcpy(&rect, &fLastRect, sizeof(RECT)); rect.right = rect.left + 235; rect.bottom = rect.top + 335; IPositionControls(&rect, WMSZ_BOTTOM); SetWindowPos(fhDlg, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER); } fInterface->RegisterDlgWnd(fhDlg); ShowWindow(fhDlg, SW_SHOW); if (IsIconic(fhDlg)) ShowWindow(fhDlg, SW_RESTORE); } void plComponentDlg::IPositionControls(RECT *newRect, int edge) { // Get the new width and height int newW = newRect->right - newRect->left; int newH = newRect->bottom - newRect->top; // If an edge we don't support is being dragged, don't allow the resize. if (!(edge == WMSZ_BOTTOM || edge == WMSZ_BOTTOMRIGHT || edge == WMSZ_RIGHT)) { memcpy(newRect, &fLastRect, sizeof(RECT)); return; } // If the width or height is too small, set it to the minimum if (newW < fSmallestSize.x) newRect->right = newRect->left + fSmallestSize.x; if (newH < fSmallestSize.y) newRect->bottom = newRect->top + fSmallestSize.y; // Calculate the new width and height int hDiff = (newRect->bottom - newRect->top) - (fLastRect.bottom - fLastRect.top); int wDiff = (newRect->right - newRect->left) - (fLastRect.right - fLastRect.left); // Copy our new rect to the last rect memcpy(&fLastRect, newRect, sizeof(RECT)); // If the size has changed, reposition and resize controls if (hDiff != 0 || wDiff != 0) { IPositionControl(GetDlgItem(fhDlg, IDC_TREE), hDiff, wDiff, kResizeX | kResizeY); IPositionControl(GetDlgItem(fhDlg, IDC_COMMENT_TEXT), hDiff); IPositionControl(GetDlgItem(fhDlg, IDC_COMMENTS), hDiff, wDiff, kResizeX | kMoveY); IPositionControl(GetDlgItem(fhDlg, IDC_ATTACH), hDiff); } InvalidateRect(fhDlg, NULL, TRUE); } void plComponentDlg::IPositionControl(HWND hControl, int hDiff, int wDiff, int flags) { RECT rect; GetWindowRect(hControl, &rect); hsAssert(!((flags & kMoveX) & (flags & kResizeX)), "Moving AND resizing in X in IPositionControl"); hsAssert(!((flags & kMoveY) & (flags & kResizeY)), "Moving AND resizing in Y in IPositionControl"); if (flags & kMoveX || flags & kMoveY) { POINT pos = { rect.left, rect.top }; ScreenToClient(fhDlg, &pos); if (flags & kMoveX) pos.x += wDiff; if (flags & kMoveY) pos.y += hDiff; SetWindowPos(hControl, NULL, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW); } if (flags & kResizeX || flags & kResizeY) { int w = rect.right - rect.left; int h = rect.bottom - rect.top; if (flags & kResizeX) w += wDiff; if (flags & kResizeY) h += hDiff; SetWindowPos(hControl, NULL, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW); } } void plComponentDlg::IGetComment() { if (fCommentNode) { // Get the text from the edit and store it in the UserPropBuffer int len = GetWindowTextLength(GetDlgItem(fhDlg, IDC_COMMENTS))+1; if (len != 0) { char *buf = TRACKED_NEW char[len]; GetDlgItemText(fhDlg, IDC_COMMENTS, buf, len); fCommentNode->SetUserPropBuffer(buf); delete [] buf; } else fCommentNode->SetUserPropBuffer(""); } } BOOL plComponentDlg::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { return Instance().DlgProc(hDlg, msg, wParam, lParam); } #define MENU_ID_START 41000 BOOL plComponentDlg::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: fhDlg = hDlg; IAddComponentsRecur(GetDlgItem(hDlg, IDC_TREE), (plMaxNode*)GetCOREInterface()->GetRootNode()); ICreateMenu(); ICreateRightClickMenu(); return TRUE; case WM_SIZING: IPositionControls((RECT*)lParam, wParam); return TRUE; case WM_ACTIVATE: if (LOWORD(wParam) == WA_INACTIVE) plMaxAccelerators::Enable(); else plMaxAccelerators::Disable(); return TRUE; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { ShowWindow(hDlg, SW_HIDE); fInterface->UnRegisterDlgWnd(hDlg); return TRUE; } else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ATTACH) { IAttachTreeSelection(); return TRUE; } else if (HIWORD(wParam) == EN_KILLFOCUS && LOWORD(wParam) == IDC_COMMENTS) { IGetComment(); return TRUE; } // "Refresh" menu item else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_REFRESH) { IRefreshTree(); return TRUE; } // "Remove unused components" menu item else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_REMOVE_UNUSED) { IRemoveUnusedComps(); return TRUE; } // Item selected from 'New' menu else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) >= MENU_ID_START) { ClassDesc *desc = plComponentMgr::Inst().Get(LOWORD(wParam)-MENU_ID_START); // If this is a component type (not a category) if (desc) { // Create an object of that type and a node to reference it Object *obj = (Object*)GetCOREInterface()->CreateInstance(desc->SuperClassID(), desc->ClassID()); INode *node = GetCOREInterface()->CreateObjectNode(obj); plComponentBase *comp = (plComponentBase*)obj; node->Hide(!comp->AllowUnhide()); node->Freeze(TRUE); // Add the new component to the tree HWND hTree = GetDlgItem(hDlg, IDC_TREE); HTREEITEM item = IAddComponent(hTree, (plMaxNode*)node); TreeView_SelectItem(hTree, item); TreeView_EnsureVisible(hTree, item); } } break; case WM_NOTIFY: NMHDR *nmhdr = (NMHDR*)lParam; if (nmhdr->idFrom == IDC_TREE) { switch (nmhdr->code) { case TVN_SELCHANGED: { NMTREEVIEW *tv = (NMTREEVIEW*)lParam; IGetComment(); bool isComponent = IIsComponent(tv->itemNew.lParam); // If the new selection is a component, enable the attach button and comment field EnableWindow(GetDlgItem(hDlg, IDC_ATTACH), isComponent); SendDlgItemMessage(hDlg, IDC_COMMENTS, EM_SETREADONLY, !isComponent, 0); if (isComponent) { fCommentNode = (plMaxNode*)tv->itemNew.lParam; TSTR buf; fCommentNode->GetUserPropBuffer(buf); SetDlgItemText(hDlg, IDC_COMMENTS, buf); } else { fCommentNode = nil; SetDlgItemText(hDlg, IDC_COMMENTS, ""); } return TRUE; } break; case TVN_BEGINLABELEDIT: // If this isn't a component, don't allow the edit if (!IIsComponent(((NMTVDISPINFO*)lParam)->item.lParam)) { SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); return TRUE; } // The edit box this creates kills the focus on our window, causing // accelerators to be enabled. Add an extra disable to counteract that. plMaxAccelerators::Disable(); return TRUE; // Finishing changing the name of a component case TVN_ENDLABELEDIT: { NMTVDISPINFO *di = (NMTVDISPINFO*)lParam; char* text = di->item.pszText; // If the name was changed... if (text && *text != '\0') { // Update the name of the node plMaxNode *node = IGetTreeSelection(); node->SetName(text); // Update the name in the panel too if (plComponentUtil::Instance().IsOpen()) plComponentUtil::Instance().IUpdateNodeName(node); // Make sure Max knows the file was changed SetSaveRequiredFlag(); // Return true to keep the changes SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); } plMaxAccelerators::Enable(); } return TRUE; // User double-clicked. Select the objects the selected component is attached to. case NM_DBLCLK: ISelectTreeSelection(); return TRUE; case NM_RCLICK: IOpenRightClickMenu(); return TRUE; case TVN_KEYDOWN: // User pressed delete if (((NMTVKEYDOWN*)lParam)->wVKey == VK_DELETE) { IDeleteComponent(IGetTreeSelection()); return TRUE; } break; } } break; } return FALSE; } HTREEITEM plComponentDlg::IAddLeaf(HWND hTree, HTREEITEM hParent, const char *text, LPARAM lParam) { TVITEM tvi = {0}; tvi.mask = TVIF_TEXT | TVIF_PARAM; tvi.pszText = (char*)text; tvi.cchTextMax = strlen(text); tvi.lParam = lParam; TVINSERTSTRUCT tvins = {0}; tvins.item = tvi; tvins.hParent = hParent; tvins.hInsertAfter = TVI_SORT; return TreeView_InsertItem(hTree, &tvins); } HTREEITEM plComponentDlg::IFindTreeItem(HWND hTree, const char *name, HTREEITEM hParent) { HTREEITEM hChild = TreeView_GetChild(hTree, hParent); while (hChild) { char buf[256]; TVITEM tvi; tvi.mask = TVIF_TEXT; tvi.hItem = hChild; tvi.pszText = buf; tvi.cchTextMax = sizeof(buf); TreeView_GetItem(hTree, &tvi); if (!strcmp(name, tvi.pszText)) return hChild; hChild = TreeView_GetNextSibling(hTree, hChild); } return nil; } HTREEITEM plComponentDlg::IAddComponent(HWND hTree, plMaxNode *node) { plComponentBase *comp = node->ConvertToComponent(); // Try and find the component category in the tree const char *category = comp->GetCategory(); HTREEITEM hCat = IFindTreeItem(hTree, category, TVI_ROOT); // If it isn't there yet, add it if (!hCat) hCat = IAddLeaf(hTree, TVI_ROOT, category, 0); // Try and find the component type in the tree int idx = plComponentMgr::Inst().FindClassID(comp->ClassID()); HTREEITEM hType = ISearchTree(hTree, idx+1, hCat); if (!hType) { // If it isn't there yet, add it TSTR type; comp->GetClassName(type); if (IIsHidden(comp->ClassID())) type.Append(" (Hidden)"); hType = IAddLeaf(hTree, hCat, type, idx+1); } // Add the name of this component to this type return IAddLeaf(hTree, hType, node->GetName(), (LPARAM)node); } void plComponentDlg::IAddComponentsRecur(HWND hTree, plMaxNode *node) { if (node->IsComponent()) IAddComponent(hTree, node); for (int i = 0; i < node->NumberOfChildren(); i++) { plMaxNode *child = (plMaxNode*)node->GetChildNode(i); IAddComponentsRecur(hTree, child); } } void plComponentDlg::ICreateMenu() { // Add a refresh option to the system menu, for those rare cases where the manager gets out of sync HMENU hMenu = GetMenu(fhDlg); HMENU hNew = CreatePopupMenu(); InsertMenu(hMenu, 0, MF_POPUP | MF_STRING | MF_BYPOSITION, (UINT)hNew, "New"); const char *lastCat = nil; HMENU hCurType = nil; UInt32 count = plComponentMgr::Inst().Count(); for (UInt32 i = 0; i < count; i++) { plComponentClassDesc *desc = (plComponentClassDesc*)plComponentMgr::Inst().Get(i); // Don't put in the create menu if obsolete if (desc->IsObsolete()) continue; if (!lastCat || strcmp(lastCat, desc->Category())) { lastCat = desc->Category(); hCurType = CreatePopupMenu(); AppendMenu(hNew, MF_POPUP | MF_STRING, (UINT)hCurType, lastCat); } AppendMenu(hCurType, MF_STRING, MENU_ID_START+i, desc->ClassName()); } } // Taking advantage of the fact that the node pointers we store in the lParam // will certainly be higher than the number of component types bool plComponentDlg::IIsComponent(LPARAM lParam) { return (lParam > plComponentMgr::Inst().Count()+1); } bool plComponentDlg::IIsType(LPARAM lParam) { return (lParam > 0 && lParam <= plComponentMgr::Inst().Count()+1); } void plComponentDlg::IAttachTreeSelection() { HWND hTree = GetDlgItem(fhDlg, IDC_TREE); // Get the current selection from the tree HTREEITEM hSelected = TreeView_GetSelection(hTree); TVITEM item; item.mask = TVIF_PARAM; item.hItem = hSelected; TreeView_GetItem(hTree, &item); // If the item has a lParam it is a component if (IIsComponent(item.lParam)) { plMaxNode *node = (plMaxNode*)item.lParam; plComponentBase *comp = node->ConvertToComponent(); // Add each of the selected nodes that is not a component to the targets list int count = fInterface->GetSelNodeCount(); for (int i = 0; i < count; i++) { plMaxNode *target = (plMaxNode*)fInterface->GetSelNode(i); if (!target->IsComponent()) comp->AddTarget(target); } // Update the rollups to reflect the new component if (plComponentUtil::Instance().IsOpen()) plComponentUtil::Instance().IUpdateRollups(); } } // Wow, this INodeTab class is very thorough bool FindNodeInTab(INode *node, INodeTab& nodes) { for (int i = 0; i < nodes.Count(); i++) { if (node == nodes[i]) return true; } return false; } void plComponentDlg::SelectComponentTargs(INodeTab& nodes) { // Make an INode tab with all the targets in it INodeTab targets; for (int i = 0; i < nodes.Count(); i++) { plComponentBase *comp = ((plMaxNode*)nodes[i])->ConvertToComponent(); for (int j = 0; j < comp->NumTargets(); j++) { INode *node = comp->GetTarget(j); if (node && !FindNodeInTab(node, targets)) targets.Append(1, &node); } } // If the user is selecting a single component, make sure it is selected in the rollup too if (plComponentUtil::Instance().IsOpen() && nodes.Count() == 1) plComponentUtil::Instance().fLastComponent = ((plMaxNode*)nodes[0])->ConvertToComponent(); theHold.Begin(); fInterface->RedrawViews(fInterface->GetTime(), REDRAW_BEGIN); fInterface->ClearNodeSelection(FALSE); // Deselect current nodes // If there is at least one valid target, select it if (targets.Count() > 0) fInterface->SelectNodeTab(targets, TRUE, FALSE); fInterface->RedrawViews(fInterface->GetTime(), REDRAW_END); theHold.Accept("Select"); } void plComponentDlg::ISelectTreeSelection() { INodeTab nodes; INode *curComponent = (INode*)IGetTreeSelection(); if (curComponent) { nodes.Append(1, &curComponent); } else { HWND hTree = GetDlgItem(fhDlg, IDC_TREE); HTREEITEM hRoot = TreeView_GetSelection(hTree); IGetComponentsRecur(hTree, hRoot, nodes); } SelectComponentTargs(nodes); } void plComponentDlg::IGetComponentsRecur(HWND hTree, HTREEITEM hItem, INodeTab& nodes) { if (hItem) { INode *node = (INode*)ITreeItemToNode(hTree, hItem); if (node) nodes.Append(1, &node); else { HTREEITEM hChild = TreeView_GetChild(hTree, hItem); IGetComponentsRecur(hTree, hChild, nodes); while (hChild = TreeView_GetNextSibling(hTree, hChild)) { IGetComponentsRecur(hTree, hChild, nodes); } } } } void plComponentDlg::IDeleteComponent(plMaxNode *component) { if (!component) return; // Make sure this components interface isn't showing if (plComponentUtil::Instance().IsOpen()) plComponentUtil::Instance().IComponentPreDelete(component->ConvertToComponent()); // Delete the component from the scene theHold.Begin(); fInterface->DeleteNode(component); theHold.Accept(_T("Delete Component")); // Delete the component from the tree HWND hTree = GetDlgItem(fhDlg, IDC_TREE); HTREEITEM hItem = TreeView_GetSelection(hTree); HTREEITEM hParent = TreeView_GetParent(hTree, hItem); TreeView_DeleteItem(hTree, hItem); // If that was the only component of this type, delete the type too if (!TreeView_GetChild(hTree, hParent)) { HTREEITEM hCategory = TreeView_GetParent(hTree, hParent); TreeView_DeleteItem(hTree, hParent); // If this is the only type in this category, delete the category too! // Sadly, this is the most we can delete. if (!TreeView_GetChild(hTree, hCategory)) TreeView_DeleteItem(hTree, hCategory); } // Update the rollups in case the selected object had this component attached if (plComponentUtil::Instance().IsOpen()) plComponentUtil::Instance().IUpdateRollups(); } plMaxNode *plComponentDlg::IGetTreeSelection() { HWND hTree = GetDlgItem(fhDlg, IDC_TREE); HTREEITEM hItem = TreeView_GetSelection(hTree); return ITreeItemToNode(hTree, hItem); } plMaxNode *plComponentDlg::ITreeItemToNode(HWND hTree, HTREEITEM hItem) { if (hItem) { TVITEM item; item.mask = TVIF_PARAM; item.hItem = hItem; TreeView_GetItem(hTree, &item); if (IIsComponent(item.lParam)) return (plMaxNode*)item.lParam; } return nil; } enum { // Comp menu kMenuDelete = 1, kMenuRename, kMenuCopy, // Type menu kMenuHide }; void plComponentDlg::ICreateRightClickMenu() { fCompMenu = CreatePopupMenu(); AppendMenu(fCompMenu, MF_STRING, kMenuDelete, "Delete"); AppendMenu(fCompMenu, MF_STRING, kMenuRename, "Rename"); AppendMenu(fCompMenu, MF_STRING, kMenuCopy, "Copy"); fTypeMenu = CreatePopupMenu(); AppendMenu(fTypeMenu, MF_STRING, kMenuHide, "Hide/Show"); } void plComponentDlg::IOpenRightClickMenu() { HWND hTree = GetDlgItem(fhDlg, IDC_TREE); // Get the position of the cursor in screen and tree client coords POINT point, localPoint; GetCursorPos(&point); localPoint = point; ScreenToClient(hTree, &localPoint); // Check if there is a tree item at that point TVHITTESTINFO hitTest; hitTest.pt = localPoint; TreeView_HitTest(hTree, &hitTest); if (!(hitTest.flags & TVHT_ONITEMLABEL)) return; // Check if the tree item has an lParam (is a component) TVITEM item; item.mask = TVIF_PARAM; item.hItem = hitTest.hItem; TreeView_GetItem(hTree, &item); HMENU menu = nil; if (IIsComponent(item.lParam)) menu = fCompMenu; else if (IIsType(item.lParam)) menu = fTypeMenu; else return; // Select the item we're working with, so the user isn't confused TreeView_SelectItem(hTree, item.hItem); // Create the popup menu and get the option the user selects SetForegroundWindow(fhDlg); int sel = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, 0, fhDlg, NULL); switch(sel) { case kMenuDelete: IDeleteComponent((plMaxNode*)item.lParam); break; case kMenuRename: TreeView_EditLabel(hTree, hitTest.hItem); break; case kMenuCopy: { // Component to copy INode *node = (INode*)item.lParam; INodeTab tab; tab.Append(1, &node); // Copy INodeTab copy; // Make the copy fInterface->CloneNodes(tab, Point3(0,0,0), true, NODE_COPY, NULL, ©); // Delete the targets for the copy and add it to the tree plMaxNode *newNode = (plMaxNode*)copy[0]; newNode->ConvertToComponent()->DeleteAllTargets(); HTREEITEM hItem = IAddComponent(GetDlgItem(fhDlg, IDC_TREE), newNode); TreeView_SelectItem(GetDlgItem(fhDlg, IDC_TREE), hItem); } break; case kMenuHide: { ClassDesc *desc = plComponentMgr::Inst().Get(item.lParam-1); std::vector::iterator it; it = std::find(fHiddenComps.begin(), fHiddenComps.end(), desc->ClassID()); TSTR name = desc->ClassName(); if (it == fHiddenComps.end()) { fHiddenComps.push_back(desc->ClassID()); name.Append(" (Hidden)"); } else fHiddenComps.erase(it); item.mask = TVIF_TEXT; item.pszText = name; TreeView_SetItem(GetDlgItem(fhDlg, IDC_TREE), &item); plComponentUtil::Instance().IUpdateRollups(); } break; } PostMessage(fhDlg, WM_USER, 0, 0); } HTREEITEM plComponentDlg::ISearchTree(HWND hTree, LPARAM lParam, HTREEITEM hCur) { // Get the param for the current item TVITEM tvi; tvi.mask = TVIF_PARAM; tvi.hItem = hCur; TreeView_GetItem(hTree, &tvi); // If the lParam matches the one searching for, return the handle if (tvi.lParam == lParam) return hCur; // Do a recursive search on the items children HTREEITEM hChild = TreeView_GetChild(hTree, hCur); while (hChild) { HTREEITEM hResult = ISearchTree(hTree, lParam, hChild); if (hResult) return hResult; hChild = TreeView_GetNextSibling(hTree, hChild); } return NULL; } void plComponentDlg::IRefreshTree() { if (fhDlg) { fCommentNode = nil; HWND hTree = GetDlgItem(fhDlg, IDC_TREE); TreeView_DeleteAllItems(hTree); IAddComponentsRecur(hTree, (plMaxNode*)GetCOREInterface()->GetRootNode()); } } void plComponentDlg::INotify(void *param, NotifyInfo *info) { if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN) { UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_OPEN); UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_NEW); UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_RESET); UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_MERGE); UnRegisterNotification(INotify, 0, NOTIFY_PRE_IMPORT); UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE); UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE_OLD); UnRegisterNotification(INotify, 0, NOTIFY_FILE_POST_OPEN); UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_NEW); UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_RESET); UnRegisterNotification(INotify, 0, NOTIFY_FILE_POST_MERGE); UnRegisterNotification(INotify, 0, NOTIFY_POST_IMPORT); UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_SHUTDOWN); } // New nodes are coming in, refresh the scene component list else if (info->intcode == NOTIFY_FILE_POST_OPEN || info->intcode == NOTIFY_SYSTEM_POST_NEW || info->intcode == NOTIFY_SYSTEM_POST_RESET || info->intcode == NOTIFY_FILE_POST_MERGE || info->intcode == NOTIFY_POST_IMPORT) { Instance().IRefreshTree(); } // Nodes may be going away, save the comment now else if (info->intcode == NOTIFY_FILE_PRE_OPEN || info->intcode == NOTIFY_SYSTEM_PRE_NEW || info->intcode == NOTIFY_SYSTEM_PRE_RESET || info->intcode == NOTIFY_FILE_PRE_MERGE || info->intcode == NOTIFY_PRE_IMPORT || info->intcode == NOTIFY_FILE_PRE_SAVE || info->intcode == NOTIFY_FILE_PRE_SAVE_OLD) { // This is causing a crash, so for now if you add a comment and don't // pick another component or close the manager before closing the file, // you lose the comment -Colin // Instance().IGetComment(); } } void plComponentDlg::IUpdateNodeName(plMaxNode *node) { if (!fhDlg) return; // Update the name in the tree too HWND hTree = GetDlgItem(fhDlg, IDC_TREE); TVITEM tvi = {0}; tvi.hItem = ISearchTree(hTree, (LPARAM)node); tvi.mask = TVIF_TEXT; tvi.pszText = node->GetName(); TreeView_SetItem(hTree, &tvi); } void FindUnusedCompsRecur(plMaxNode *node, std::vector& unused) { plComponentBase *comp = node->ConvertToComponent(); if (comp) { bool isAttached = false; int num = comp->NumTargets(); for (int i = 0; i < num; i++) { if (comp->GetTarget(i)) { isAttached = true; break; } } if (!isAttached) unused.push_back(node); } for (int i = 0; i < node->NumberOfChildren(); i++) FindUnusedCompsRecur((plMaxNode*)node->GetChildNode(i), unused); } void plComponentDlg::IRemoveUnusedComps() { std::vector unused; FindUnusedCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), unused); for (int i = 0; i < unused.size(); i++) GetCOREInterface()->DeleteNode(unused[i], FALSE); IRefreshTree(); } bool plComponentDlg::IIsHidden(Class_ID& cid) { return (std::find(fHiddenComps.begin(), fHiddenComps.end(), cid) != fHiddenComps.end()); } //////////////////////////////////////////////////////////////////////////////// #include "hsUtils.h" class plCopyCompCallback : public HitByNameDlgCallback { protected: Tab fSharedComps; INodeTab fSelectedNodes; public: bool GetComponents() { fSelectedNodes.ZeroCount(); fSharedComps.ZeroCount(); Interface *ip = GetCOREInterface(); int nodeCount = ip->GetSelNodeCount(); if (nodeCount == 0) return false; // Get the components shared among the selected nodes int i; fSelectedNodes.SetCount(nodeCount); for (i = 0; i < nodeCount; i++) fSelectedNodes[i] = ip->GetSelNode(i); INodeTab sharedComps; if (plSharedComponents(fSelectedNodes, sharedComps) == 0) return false; // Put the shared components in a list fSharedComps.SetCount(sharedComps.Count()); for (i = 0; i < sharedComps.Count(); i++) fSharedComps[i] = ((plMaxNode*)sharedComps[i])->ConvertToComponent(); return true; } virtual TCHAR *dialogTitle() { return "Select Nodes"; } virtual TCHAR *buttonText() { return "Copy"; } virtual int filter(INode *node) { // Make sure this node doesn't already have the components for (int i = 0; i < fSelectedNodes.Count(); i++) { if (fSelectedNodes[i] == node) return FALSE; } return TRUE; } virtual void proc(INodeTab &nodeTab) { for (int i = 0; i < nodeTab.Count(); i++) { for (int j = 0; j < fSharedComps.Count(); j++) { fSharedComps[j]->AddTarget((plMaxNodeBase*)nodeTab[i]); } } } }; static plCopyCompCallback copyCompCallback; void CopyComponents() { if (copyCompCallback.GetComponents()) GetCOREInterface()->DoHitByNameDialog(©CompCallback); else { int count = GetCOREInterface()->GetSelNodeCount(); if (count == 0) hsMessageBox("No object(s) selected", "Component Copy", hsMessageBoxNormal); else if (count > 1) hsMessageBox("No components are shared among the selected objects", "Component Copy", hsMessageBoxNormal); else hsMessageBox("No components on the selected object", "Component Copy", hsMessageBoxNormal); } }