/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ // dropmenu.cpp // Implements a popup menu in which you can drag and drop #include "stdafx.h" #include "dropmenu.h" #define nIMG_SPACE 4 #define MAX_DROPMENU_ITEM_LENGTH 45 ////////////////////////////////////////////////////////////////////////// // Class CDropMenuButton ////////////////////////////////////////////////////////////////////////// CDropMenuButton::CDropMenuButton(void) { m_bButtonPressed = FALSE; m_bMenuShowing = FALSE; m_bFirstTime = FALSE; } ////////////////////////////////////////////////////////////////////////// // Messages for CDropMenuButton ////////////////////////////////////////////////////////////////////////// BEGIN_MESSAGE_MAP(CDropMenuButton, CStationaryToolbarButton) //{{AFX_MSG_MAP(CWnd) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_MESSAGE(DM_MENUCLOSED, OnMenuClosed) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CDropMenuButton::OnLButtonDown(UINT nFlags, CPoint point) { CStationaryToolbarButton::OnLButtonDown(nFlags, point); m_bButtonPressed = TRUE; } void CDropMenuButton::OnMouseMove(UINT nFlags, CPoint point) { if(m_bMenuShowing) { if(m_bFirstTime) { ReleaseCapture(); m_eState = eBUTTON_DOWN; RedrawWindow(); m_bFirstTime = FALSE; } } else { CStationaryToolbarButton::OnMouseMove(nFlags, point); } } void CDropMenuButton::OnLButtonUp(UINT nFlags, CPoint point) { if(m_bButtonPressed && m_bEnabled && m_bHaveFocus) { OnAction(); m_bMenuShowing = TRUE; m_bFirstTime = TRUE; } else { CStationaryToolbarButton::OnLButtonUp(nFlags, point); m_bMenuShowing = FALSE; } m_bButtonPressed = FALSE; } LRESULT CDropMenuButton::OnMenuClosed(WPARAM wParam, LPARAM lParam) { m_bMenuShowing = FALSE; m_eState = eNORMAL; RedrawWindow(); return 1; } ////////////////////////////////////////////////////////////////////////// // Class CDropMenuItem ////////////////////////////////////////////////////////////////////////// CDropMenuItem::CDropMenuItem(UINT nFlags, CString szText, UINT nCommandID, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, BOOL bShowFeedback, void *pUserData) { m_nIconType = BUILTIN_BITMAP; if(nFlags & MF_SEPARATOR) { m_bIsSeparator = TRUE; } else if(nFlags == MF_STRING) { m_bIsSeparator = FALSE; char *pCutText = fe_MiddleCutString(szText.GetBuffer(szText.GetLength() + 1), MAX_DROPMENU_ITEM_LENGTH); m_szText = pCutText; szText.ReleaseBuffer(); free(pCutText); m_hUnselectedBitmap = hUnselectedBitmap; m_hSelectedBitmap = (hSelectedBitmap == NULL) ? hUnselectedBitmap : hSelectedBitmap; m_nCommandID = nCommandID; m_pSubMenu = NULL; } m_bIsEmpty = TRUE; m_unselectedBitmapSize.cx = m_unselectedBitmapSize.cy = 0; m_selectedBitmapSize.cx = m_selectedBitmapSize.cy = 0; m_textSize.cx = m_textSize.cy = 0; m_totalSize.cx = m_totalSize.cy = 0; m_menuItemRect.SetRectEmpty(); m_bShowFeedback = bShowFeedback; m_pUserData = pUserData; } CDropMenuItem::CDropMenuItem(UINT nFlags, CString szText, UINT nCommandID, CDropMenu *pSubMenu, BOOL bIsEmpty, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, BOOL bShowFeedback, void *pUserData) { if(nFlags & MF_POPUP) { m_bIsSeparator = FALSE; char *pCutText = fe_MiddleCutString(szText.GetBuffer(szText.GetLength() + 1), MAX_DROPMENU_ITEM_LENGTH); m_szText = pCutText; szText.ReleaseBuffer(); free(pCutText); m_hUnselectedBitmap = hUnselectedBitmap; m_hSelectedBitmap = (hSelectedBitmap == NULL) ? hUnselectedBitmap : hSelectedBitmap; m_nCommandID = nCommandID; m_pSubMenu = pSubMenu; } m_bIsEmpty = bIsEmpty; m_unselectedBitmapSize.cx = m_unselectedBitmapSize.cy = 0; m_selectedBitmapSize.cx = m_selectedBitmapSize.cy = 0; m_textSize.cx = m_textSize.cy = 0; m_totalSize.cx = m_totalSize.cy = 0; m_menuItemRect.SetRectEmpty(); m_bShowFeedback = bShowFeedback; m_pUserData = pUserData; } CDropMenuItem::~CDropMenuItem() { if(IsSubMenu()) { CDropMenu *pSubMenu = GetSubMenu(); pSubMenu->DestroyDropMenu(); delete pSubMenu; } } void CDropMenuItem::GetMenuItemRect(LPRECT rect) { rect->left = m_menuItemRect.left; rect->right = m_menuItemRect.right; rect->top = m_menuItemRect.top; rect->bottom = m_menuItemRect.bottom; } void CDropMenuItem::SetMenuItemRect(LPRECT rect) { m_menuItemRect.left = rect->left; m_menuItemRect.right = rect->right; m_menuItemRect.top = rect->top; m_menuItemRect.bottom = rect->bottom; } /////////////////////////////////////////////////////////////////////////// // Class CDropMenuDropTarget /////////////////////////////////////////////////////////////////////////// CDropMenuDropTarget::CDropMenuDropTarget(CWnd *pOwner) { m_pOwner = pOwner; } DROPEFFECT CDropMenuDropTarget::OnDragEnter(CWnd * pWnd, COleDataObject * pDataObject, DWORD dwKeyState, CPoint point) { return OnDragOver(pWnd, pDataObject, dwKeyState, point); } DROPEFFECT CDropMenuDropTarget::OnDragOver(CWnd * pWnd, COleDataObject * pDataObject, DWORD dwKeyState, CPoint point ) { CDropMenuDragData dragData(pDataObject, point); m_pOwner->SendMessage(DT_DRAGGINGOCCURRED, (WPARAM) 0, (LPARAM)(&dragData)); return dragData.m_DropEffect; } BOOL CDropMenuDropTarget::OnDrop(CWnd * pWnd, COleDataObject * pDataObject, DROPEFFECT dropEffect, CPoint point) { CDropMenuDragData* dragData = new CDropMenuDragData(pDataObject, point); m_pOwner->SendMessage(DT_DROPOCCURRED, (WPARAM) 0, (LPARAM)(dragData)); return TRUE; } // End CDropMenuDropTarget implementation // CDropMenuColumn CDropMenuColumn::CDropMenuColumn(int nFirstItem, int nLastItem, int nWidth, int nHeight) { m_nFirstItem = nFirstItem; m_nLastItem = nLastItem; m_nWidth = nWidth; m_nHeight = nHeight; m_bHasSubMenu = FALSE; } #define NOSELECTION -1 #define MENU_BORDER_SIZE 3 #define SUBMENU_WIDTH 9 #define SUBMENU_HEIGHT 11 #define SUBMENU_LEFT_MARGIN 3 #define SUBMENU_RIGHT_MARGIN 7 #define IDT_SUBMENU_OFF 16384 #define SUBMENU_DELAY_OFF_MS 400 #define IDT_SUBMENU_ON 16385 #define SUBMENU_DELAY_ON_MS 400 #define IDT_HAVE_FOCUS 16386 #define HAVE_FOCUS_DELAY 400 #define IDT_UNHOOK 16387 #define UNHOOK_DELAY 100 #define SEPARATOR_SPACE 2 #define SEPARATOR_HEIGHT 2 #define SEPARATOR_WIDTH 2 #define SPACE_BETWEEN_ITEMS 2 static HHOOK currentMouseHook; static HHOOK currentKeyboardHook; static CDropMenu * currentMenu = NULL; //This refers to a submenu of the current dropmenu static CDropMenu * gLastMenu = NULL; //This refers to the most recent complete dropmenu static LONG oldFrameProc; static UINT gUnhookTimer = 0; CDropMenu* CDropMenu::GetLastDropMenu() { return gLastMenu; } void CDropMenu::SetLastDropMenu(CDropMenu* menu) { gLastMenu = menu; } CDropMenu::CDropMenu() { m_pMenuItemArray.SetSize(0, 10); m_WidestImage = 0; m_WidestText = 0; m_nSelectedItem = -1; m_eSelectedItemSelType = eNONE; m_bShowing = FALSE; m_pDropTarget = NULL; m_pUnselectedItem = NULL; m_pSelectedItem = NULL; m_nSelectTimer = 0; m_nUnselectTimer = 0; m_pMenuParent = NULL; m_pShowingSubmenu = NULL; m_bDropOccurring = FALSE; m_bDragging = FALSE; m_bRight = TRUE; m_bMouseUsed = TRUE; m_bIsOpen = FALSE; m_pOverflowMenuItem = NULL; m_bAboutToShow = FALSE; m_pParentMenuItem = NULL; m_hSubmenuUnselectedBitmap = NULL; m_hSubmenuSelectedBitmap = NULL; m_pUserData = NULL; } CDropMenu::~CDropMenu() { // Clear out any dangling pointers. if(currentMenu == this) { currentMenu = NULL; } if(gLastMenu == this) { gLastMenu = NULL; } int nCount = m_pMenuItemArray.GetSize(); for(int i = 0; i < nCount; i++) { CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[i]; if(pItem != m_pOverflowMenuItem) delete pItem; } int nColumnCount = m_pColumnArray.GetSize(); for(int j = 0 ; j < nColumnCount; j++) delete (CDropMenuColumn*)m_pColumnArray[j]; if(m_pDropTarget) delete m_pDropTarget; if(m_pOverflowMenuItem != NULL) delete m_pOverflowMenuItem; m_pMenuItemArray.RemoveAll(); if(m_pMenuParent == NULL) { if(m_hSubmenuSelectedBitmap != NULL) { ::DeleteObject(m_hSubmenuSelectedBitmap); m_hSubmenuSelectedBitmap = NULL; } if(m_hSubmenuUnselectedBitmap != NULL) { ::DeleteObject(m_hSubmenuUnselectedBitmap); m_hSubmenuUnselectedBitmap = NULL; } } if(m_pMenuParent != NULL && m_pMenuParent->m_pShowingSubmenu == this) m_pMenuParent->m_pShowingSubmenu = NULL; m_pMenuParent = NULL; m_bShowing = FALSE; } UINT CDropMenu::GetMenuItemCount(void) { return m_pMenuItemArray.GetSize(); } void CDropMenu::AppendMenu(UINT nFlags, UINT nIDNewItem, CString szText, BOOL bShowFeedback, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, void* icon, IconType iconType, void *pUserData) { CDropMenuItem *pMenuItem = new CDropMenuItem(nFlags, szText, nIDNewItem, hUnselectedBitmap, hSelectedBitmap, bShowFeedback, pUserData); pMenuItem->SetIcon(icon, iconType); m_pMenuItemArray.Add(pMenuItem); } void CDropMenu::AppendMenu(UINT nFlags, UINT nIDNewItem, CDropMenu *pSubMenu, BOOL bIsEmpty, CString szText, BOOL bShowFeedback, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, void* icon, IconType iconType, void *pUserData) { CDropMenuItem *pMenuItem = new CDropMenuItem(nFlags, szText, nIDNewItem, pSubMenu, bIsEmpty, hUnselectedBitmap, hSelectedBitmap, bShowFeedback, pUserData); pMenuItem->SetIcon(icon, iconType); m_pMenuItemArray.Add(pMenuItem); } void CDropMenu::DeleteMenu(UINT nPosition, UINT nFlags) { CDropMenuItem *pItem; if(nFlags == MF_BYPOSITION) { UINT nCount = m_pMenuItemArray.GetSize(); if(nPosition >= 0 && nPosition < nCount) { pItem = (CDropMenuItem *)m_pMenuItemArray[nPosition]; m_pMenuItemArray.RemoveAt(nPosition, 1); } } else if(nFlags == MF_BYCOMMAND) { int nCommandPosition = FindCommand(nPosition); if(nCommandPosition != -1) { pItem = (CDropMenuItem *)m_pMenuItemArray[nCommandPosition]; m_pMenuItemArray.RemoveAt(nCommandPosition, 1); } } if(pItem != m_pOverflowMenuItem) delete pItem; } void CDropMenu::CreateOverflowMenuItem(UINT nCommand, CString szText, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap) { m_pOverflowMenuItem = new CDropMenuItem(MF_STRING, szText, nCommand, hUnselectedBitmap, hSelectedBitmap, FALSE); } LRESULT CALLBACK EXPORT HandleMouseEvents(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode < 0) return CallNextHookEx(currentMouseHook, nCode, wParam, lParam); MOUSEHOOKSTRUCT *mouseHook = (MOUSEHOOKSTRUCT*)lParam; if(currentMenu != NULL) { //only want to deactivate if point is not in our menu or in our parent or children if(nCode >= 0 && (wParam == WM_NCLBUTTONDOWN || wParam == WM_LBUTTONDOWN || ((wParam == WM_LBUTTONUP || wParam == WM_NCLBUTTONUP) && currentMenu->IsDragging()))) { if(currentMenu != NULL && !currentMenu->PointInMenus(mouseHook->pt)) { currentMenu->Deactivate(); } } } return CallNextHookEx(currentMouseHook, nCode, wParam, lParam); } LRESULT CALLBACK EXPORT HandleKeyboardEvents(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode < 0) { return CallNextHookEx(currentKeyboardHook, nCode, wParam, lParam); } //only want to handle on a keydown if(!(HIWORD(lParam) & KF_UP) && currentMenu != NULL) { currentMenu->HandleKeystroke(wParam); } //we don't want to pass the keyboard event onwards return 1; } LRESULT CALLBACK EXPORT FrameProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg == WM_ACTIVATE) { if(LOWORD(wParam) == WA_INACTIVE && !currentMenu->IsShowingMenu((HWND)lParam)) { currentMenu->Deactivate(); } } return CallWindowProc((WNDPROC)oldFrameProc, hWnd, uMsg, wParam, lParam); } // oppX and oppY refer to the x and y coordinates if the menu has to open in the opposite direction. Default is to not use them void CDropMenu::TrackDropMenu(CWnd *pParent, int x, int y, BOOL bDragging, CDropMenuDropTarget *pDropTarget, BOOL bShowFeedback, int oppX, int oppY) { if(!m_bShowing) { m_bShowFeedback = bShowFeedback; m_bDragging = bDragging; m_bHasSubMenu = FALSE; m_pParent = pParent; m_bFirstPaint =TRUE; if(m_pMenuParent != NULL) { m_bRight = m_pMenuParent->m_bRight; } // m_bNotDestroyed = TRUE; if(GetMenuItemCount() == 0) GetTopLevelParent()->SendMessage(DM_FILLINMENU, 0, (LPARAM)this); m_bShowing = TRUE; m_bAboutToShow = FALSE; m_bIsOpen = TRUE; if(m_hSubmenuUnselectedBitmap == NULL) m_hSubmenuUnselectedBitmap = ::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_SUBMENU1)); if(m_hSubmenuSelectedBitmap == NULL) m_hSubmenuSelectedBitmap = ::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_SUBMENU2)); if(!::IsWindow(m_hWnd)) { CWnd::CreateEx(0, AfxRegisterWndClass( CS_SAVEBITS , theApp.LoadStandardCursor(IDC_ARROW), (HBRUSH)(COLOR_MENU + 1), NULL),"test", WS_POPUP , 0, 0, 0,0, pParent->m_hWnd, NULL, NULL); SetDropMenuTarget(pDropTarget); } else delete pDropTarget; CSize size = GetMenuSize(); CPoint startPoint = FindStartPoint(x, y, size, oppX, oppY); if(m_pMenuParent == NULL) { if(gLastMenu !=NULL) { //we only want one drop menu open at a time, so close the last one if one is //still open. This will probably only happen on drag and drop. gLastMenu->Deactivate(); } oldFrameProc = SetWindowLong(GetParentFrame()->m_hWnd, GWL_WNDPROC, (LONG)FrameProc); #ifdef _WIN32 currentMouseHook = SetWindowsHookEx(WH_MOUSE, HandleMouseEvents, AfxGetInstanceHandle(), GetCurrentThreadId()); currentKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, HandleKeyboardEvents, AfxGetInstanceHandle(),GetCurrentThreadId()); #else currentMouseHook = SetWindowsHookEx(WH_MOUSE, HandleMouseEvents, AfxGetInstanceHandle(), GetCurrentTask()); currentKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, HandleKeyboardEvents, AfxGetInstanceHandle(), GetCurrentTask()); #endif gLastMenu = this; } SetWindowPos(NULL, startPoint.x, startPoint.y, size.cx + (2*MENU_BORDER_SIZE), size.cy + (2 * MENU_BORDER_SIZE), SWP_NOZORDER | SWP_NOACTIVATE); ShowWindow(SW_SHOWNA); //SetActiveWindow(); } else delete pDropTarget; } void CDropMenu::SetDropMenuTarget(CDropMenuDropTarget *pDropTarget) { m_pDropTarget = pDropTarget; DragAcceptFiles(); m_pDropTarget->Register(this); } BOOL CDropMenu::PointInMenus(POINT pt) { CRect rect; GetWindowRect(&rect); if(rect.PtInRect(pt)) { return TRUE; } else { CDropMenu *menu = m_pMenuParent; while(menu != NULL && IsWindow(menu->m_hWnd)) { menu->GetWindowRect(&rect); if(rect.PtInRect(pt)) { return TRUE; } menu=menu->m_pMenuParent; } menu = m_pShowingSubmenu; while(menu !=NULL && IsWindow(menu->m_hWnd)) { menu->GetWindowRect(&rect); if(rect.PtInRect(pt)) { return TRUE; } menu = menu->m_pShowingSubmenu; } } return FALSE; } void CDropMenu::HandleKeystroke(WPARAM key) { //Make all open windows know that we are in keyboard mode still CDropMenu *pMenu = this; while(pMenu != NULL) { pMenu->m_bMouseUsed = FALSE; pMenu = pMenu->m_pMenuParent; } if(key == VK_ESCAPE || !m_bDragging) { switch(key) { case VK_ESCAPE: currentMenu->Deactivate(); break; case VK_UP: HandleUpArrow(); break; case VK_DOWN: HandleDownArrow(); break; case VK_LEFT: HandleLeftArrow(); break; case VK_RIGHT: HandleRightArrow(); break; case VK_RETURN: HandleReturnKey(); } } } BOOL CDropMenu::OnCommand( WPARAM wParam, LPARAM lParam ) { ShowWindow(SW_HIDE); m_pParent->SendMessage(WM_COMMAND, wParam, lParam); return 1; } BOOL CDropMenu::DestroyWindow(void) { return(CWnd::DestroyWindow()); } void CDropMenu::Deactivate(void) { if(m_pMenuParent != NULL) { m_pMenuParent->SendMessage(DM_CLOSEALLMENUS, 0, 0); } ShowWindow(SW_HIDE); } BOOL CDropMenu::ShowWindow( int nCmdShow ) { if(nCmdShow == SW_HIDE && IsWindowVisible()) { m_bIsOpen = FALSE; if(m_pMenuParent != NULL) { //Turn on parent's focus timer m_pMenuParent->m_nHaveFocusTimer = m_pMenuParent->SetTimer(IDT_HAVE_FOCUS, HAVE_FOCUS_DELAY, NULL); // we only want the parent to be the current menu if the parent hasn't already // brought up a new submenu. Otherwise we have timing conflicts if(m_pMenuParent->m_bShowing && (m_pMenuParent->m_pShowingSubmenu == this || m_pMenuParent->m_pShowingSubmenu == NULL)) { currentMenu = m_pMenuParent; } } KillTimer(m_nHaveFocusTimer); //if this is the top level parent then the hook needs to be destroyed if(m_pMenuParent == NULL) { UnhookWindowsHookEx(currentMouseHook); UnhookWindowsHookEx(currentKeyboardHook); SetWindowLong(GetParentFrame()->m_hWnd, GWL_WNDPROC, (LONG)oldFrameProc); currentMenu = NULL; gLastMenu = NULL; } OnTimer(IDT_SUBMENU_OFF); m_bShowing = FALSE; m_nSelectedItem = -1; if(m_pShowingSubmenu) { //check to see if it's valid first because we may //want to deactivate before it was created due to //the timer never having been called to create it if(IsWindow(m_pShowingSubmenu->m_hWnd)) { m_pShowingSubmenu->ShowWindow(SW_HIDE); } m_pShowingSubmenu = NULL; } if(m_pMenuParent == NULL) { m_pParent->PostMessage(DM_MENUCLOSED, 0, 0); } } else if(nCmdShow == SW_SHOWNA) { if(m_pMenuParent != NULL) { //since we are being shown, turn off parent's focus timer m_pMenuParent->KillTimer(m_nHaveFocusTimer); } m_nHaveFocusTimer = SetTimer(IDT_HAVE_FOCUS, HAVE_FOCUS_DELAY, NULL); currentMenu = this; } return CWnd::ShowWindow(nCmdShow); } void CDropMenu::PostNcDestroy(void ) { CWnd::PostNcDestroy(); } void CDropMenu::DestroyDropMenu(void) { DestroyWindow(); } ////////////////////////////////////////////////////////////////////////// // Messages for CDropMenu ////////////////////////////////////////////////////////////////////////// BEGIN_MESSAGE_MAP(CDropMenu, CWnd) //{{AFX_MSG_MAP(CWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_RBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_NCHITTEST() ON_MESSAGE(DT_DROPOCCURRED, OnDropTargetDropOccurred) ON_MESSAGE(DT_DRAGGINGOCCURRED, OnDropTargetDraggingOccurred) ON_MESSAGE(DM_CLOSEALLMENUS, OnCloseAllMenus) ON_WM_TIMER() ON_WM_ACTIVATE() ON_WM_DESTROY() //}}AFX_MSG_MAP END_MESSAGE_MAP() void CDropMenu::OnPaint() { CRect updateRect; if(GetUpdateRect(&updateRect)) { CPaintDC dcPaint(this); // device context for painting CRect rcClient; GetClientRect(&rcClient); HDC hSrcDC = dcPaint.m_hDC; HDC hMemDC = ::CreateCompatibleDC(hSrcDC); HBITMAP hbmMem = CreateCompatibleBitmap(hSrcDC, rcClient.Width(), rcClient.Height()); // // Select the bitmap into the off-screen DC. // COLORREF rgbOldBk = ::SetBkColor(hMemDC, sysInfo.m_clrMenu); HBITMAP hbmOld = (HBITMAP)::SelectObject(hMemDC, hbmMem); HBRUSH brFace = sysInfo.m_hbrMenu; ::FillRect(hMemDC, rcClient, brFace); CRect intersectRect; int nNumColumns = m_pColumnArray.GetSize(); int nFirstItem, nLastItem; CDropMenuColumn *pColumn; int nStartX = 0; for(int j = 0; j <=m_nLastVisibleColumn; j++) { int y = MENU_BORDER_SIZE; pColumn = (CDropMenuColumn*)m_pColumnArray[j]; nFirstItem = pColumn->GetFirstItem(); nLastItem = pColumn->GetLastItem(); for(int i = nFirstItem; i <= nLastItem; i++) { CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[i]; if(m_bShowFeedback) y += SPACE_BETWEEN_ITEMS; if(item->IsSeparator()) { CRect separatorRect(nStartX + MENU_BORDER_SIZE, y, nStartX + MENU_BORDER_SIZE + pColumn->GetWidth(), y + SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT); if(intersectRect.IntersectRect(separatorRect, updateRect)) { DrawSeparator(hMemDC, separatorRect.left, y, separatorRect.Width() - (2 * MENU_BORDER_SIZE)); } y += SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT; } else { CRect itemRect; item->GetMenuItemRect(itemRect); if(intersectRect.IntersectRect(itemRect, updateRect)) { CRect rect(nStartX + MENU_BORDER_SIZE, y ,nStartX + MENU_BORDER_SIZE + pColumn->GetWidth(), y + itemRect.Height()); DrawItem(pColumn, item, rect, hMemDC, i == m_nSelectedItem, (i == m_nSelectedItem) ? m_eSelectedItemSelType : eNONE); } y += itemRect.Height(); } } nStartX += pColumn->GetWidth(); if(j < m_nLastVisibleColumn) { DrawVerticalSeparator(hMemDC, nStartX, MENU_BORDER_SIZE, rcClient.Height() - (2 * MENU_BORDER_SIZE)); nStartX += SEPARATOR_WIDTH; } } // need to make sure selection type shows up if(m_nSelectedItem != NOSELECTION && m_eSelectedItemSelType != eNONE) { CRect itemRect; CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[m_nSelectedItem]; if(!item->IsSeparator()) { item->GetMenuItemRect(itemRect); CRect rect(itemRect.left, itemRect.top ,itemRect.left + ((CDropMenuColumn*)m_pColumnArray[item->GetColumn()])->GetWidth(), itemRect.top + itemRect.Height()); DrawItem((CDropMenuColumn*)m_pColumnArray[item->GetColumn()], item, rect, hMemDC, TRUE, m_eSelectedItemSelType ); } } DrawBorders(hMemDC, rcClient); ::BitBlt(hSrcDC, 0, 0, rcClient.Width(), rcClient.Height(), hMemDC, 0, 0, SRCCOPY); ::SetBkColor(hMemDC, rgbOldBk); ::SelectObject(hMemDC, hbmOld); ::DeleteObject(hbmMem); ::DeleteDC(hMemDC); } if(m_bFirstPaint) { MSG msg; PeekMessage(&msg, this->m_hWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE); m_bFirstPaint = FALSE; } } void CDropMenu::OnMouseMove(UINT nFlags, CPoint point) { if(!m_bMouseUsed) { m_bMouseUsed = TRUE; return; } m_bMouseUsed = TRUE; KeepMenuAroundIfClosing(); ChangeSelection(point); } UINT CDropMenu::OnNcHitTest( CPoint point ) { return CWnd::OnNcHitTest(point); } void CDropMenu::OnLButtonUp(UINT nFlags, CPoint point) { //if we haven't painted yet then the user hasn't had a chance to mouse up. if(m_bFirstPaint || m_nSelectedItem == NOSELECTION) return; MenuSelectionType eSelType; int nSelection = FindSelection(point, eSelType); if(nSelection != -1) { CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[nSelection]; if(!pItem->IsSubMenu()) { SendMessage(WM_COMMAND, (WPARAM) pItem->GetCommand(), (LPARAM) m_hWnd); } } } void CDropMenu::OnLButtonDown(UINT nFlags, CPoint point) { // we want our frame to stay the active window. Causes flicker GetParentFrame()->SetActiveWindow(); } void CDropMenu::OnRButtonDown(UINT nFlags, CPoint point) { // we want our frame to stay the active window. Causes flicker GetParentFrame()->SetActiveWindow(); } LRESULT CDropMenu::OnDropTargetDraggingOccurred(WPARAM wParam, LPARAM lParam) { if (wParam == 0) { CDropMenuDragData* pData = (CDropMenuDragData*)lParam; KeepMenuAroundIfClosing(); ChangeSelection(pData->m_PointHit); // Ok, now we need to figure out what our drag feedback should be. // note that point is already in our coordinates since we got it from our drop target MenuSelectionType eSelType; int nSelection = FindSelection(pData->m_PointHit, eSelType); if(nSelection != -1) { CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[nSelection]; pData->m_nCommand = pItem->GetCommand(); pData->eSelType = eSelType; m_pParent->SendMessage(DT_DRAGGINGOCCURRED, 1, lParam); } } else { m_pParent->SendMessage(DT_DRAGGINGOCCURRED, 1, lParam); } return 1; } LRESULT CDropMenu::OnDropTargetDropOccurred(WPARAM wParam, LPARAM lParam) { if (wParam == 0) { CDropMenuDragData* pData = (CDropMenuDragData*)lParam; // Ok, now we need to figure out what our drag feedback should be. // note that point is already in our coordinates since we got it from our drop target MenuSelectionType eSelType; int nSelection = FindSelection(pData->m_PointHit, eSelType); if(nSelection != -1) { CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[nSelection]; pData->m_nCommand = pItem->GetCommand(); pData->eSelType = eSelType; m_pParent->SendMessage(DT_DROPOCCURRED, 1, lParam); } } else { m_pParent->SendMessage(DT_DROPOCCURRED, 1, lParam); } ShowWindow(SW_HIDE); return 1; } LRESULT CDropMenu::OnCloseAllMenus(WPARAM wParam, LPARAM lParam) { if(m_pMenuParent != NULL) { m_pMenuParent->PostMessage(DM_CLOSEALLMENUS, wParam, lParam); } ShowWindow(SW_HIDE); return 1; } void CDropMenu::OnTimer( UINT nIDEvent ) { if(nIDEvent == IDT_SUBMENU_OFF) { KillTimer(m_nUnselectTimer); if(m_pUnselectedItem != NULL && m_pUnselectedItem->GetSubMenu()->m_bShowing==FALSE) { m_pUnselectedItem->GetSubMenu()->ShowWindow(SW_HIDE); } m_pUnselectedItem = NULL; } else if(nIDEvent == IDT_SUBMENU_ON) { KillTimer(m_nSelectTimer); if(m_bShowing) { if(m_pSelectedItem) { CDropMenu *pSubMenu = m_pSelectedItem->GetSubMenu(); if(pSubMenu && pSubMenu->m_bAboutToShow) ShowSubmenu(m_pSelectedItem); else m_pSelectedItem = NULL; } m_nSelectTimer = 0; } } else if(nIDEvent == IDT_HAVE_FOCUS) { if(m_nSelectedItem != -1 && m_bMouseUsed) { CDropMenuItem *pItem = (CDropMenuItem*)m_pMenuItemArray[m_nSelectedItem]; CPoint point; GetCursorPos(&point); ScreenToClient(&point); CRect rcClient, rect; GetClientRect(rcClient); if (!rcClient.PtInRect(point) && m_pShowingSubmenu == NULL ) { pItem->GetMenuItemRect(rect); rect.left = 0; rect.right = rcClient.right; m_nSelectedItem = -1; InvalidateRect(&rect); } } } } void CDropMenu::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized ) { if(nState == WA_ACTIVE) { pWndOther->SetActiveWindow(); } } void CDropMenu::OnDestroy( ) { m_pDropTarget->Revoke(); CWnd::OnDestroy(); } ////////////////////////////////////////////////////////////////////////// // Helpers for CDropMenu ////////////////////////////////////////////////////////////////////////// void CDropMenu::DrawSeparator(HDC hDC, int x, int y, int nWidth) { HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); HPEN pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined (WIN32) ::MoveToEx(hDC, x, y + SEPARATOR_SPACE, NULL); #else ::MoveTo(hDC, x, y + SEPARATOR_SPACE); #endif ::LineTo(hDC, x + nWidth, y + SEPARATOR_SPACE); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); #if defined(WIN32) pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT)); #else pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); #endif pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined(WIN32) ::MoveToEx(hDC, x, y + SEPARATOR_SPACE + 1, NULL); #else ::MoveTo(hDC, x, y + SEPARATOR_SPACE + 1); #endif ::LineTo(hDC, x + nWidth, y + SEPARATOR_SPACE + 1); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); } void CDropMenu::DrawVerticalSeparator(HDC hDC, int x, int y, int nHeight) { HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); HPEN pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined (WIN32) ::MoveToEx(hDC, x + SEPARATOR_SPACE, y, NULL); #else ::MoveTo(hDC, x + SEPARATOR_SPACE, y); #endif ::LineTo(hDC, x + SEPARATOR_SPACE, y + nHeight); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); #if defined(WIN32) pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT)); #else pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); #endif pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined(WIN32) ::MoveToEx(hDC, x + SEPARATOR_SPACE + 1, y, NULL); #else ::MoveTo(hDC, x + SEPARATOR_SPACE + 1, y); #endif ::LineTo(hDC, x + SEPARATOR_SPACE + 1, y + nHeight); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); } void CDropMenu::DrawBorders(HDC hDC, CRect rcClient) { //Draw inner rect which is all BTN_FACE RECT rect; ::SetRect(&rect, rcClient.left + 2, rcClient.top + 2, rcClient.right - 2, rcClient.bottom - 2); ::FrameRect(hDC, &rect, sysInfo.m_hbrBtnFace); #if defined(WIN32) HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT)); #else HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); #endif HPEN pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined(WIN32) ::MoveToEx(hDC, rcClient.left + 1, rcClient.bottom - 1, NULL); #else ::MoveTo(hDC, rcClient.left + 1, rcClient.bottom - 1); #endif ::LineTo(hDC, rcClient.left + 1, rcClient.top + 1); ::LineTo(hDC, rcClient.right - 1, rcClient.top + 1); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE)); pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined(WIN32) ::MoveToEx(hDC, rcClient.left, rcClient.bottom - 1, NULL); #else ::MoveTo(hDC, rcClient.left, rcClient.bottom - 1); #endif ::LineTo(hDC, rcClient.left, rcClient.top); ::LineTo(hDC, rcClient.right - 1, rcClient.top); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); pen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined(WIN32) ::MoveToEx(hDC, rcClient.right - 1, rcClient.top, NULL); #else ::MoveTo(hDC, rcClient.right - 1, rcClient.top); #endif ::LineTo(hDC, rcClient.right - 1 , rcClient.bottom - 1); ::LineTo(hDC, rcClient.left - 1, rcClient.bottom - 1); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); pOldPen = (HPEN)::SelectObject(hDC, pen); #if defined(WIN32) ::MoveToEx(hDC, rcClient.right - 2, rcClient.top + 1, NULL); #else ::MoveTo(hDC, rcClient.right - 2, rcClient.top + 1); #endif ::LineTo(hDC, rcClient.right - 2, rcClient.bottom - 2); ::LineTo(hDC, rcClient.left, rcClient.bottom -2); ::SelectObject(hDC, pOldPen); ::DeleteObject(pen); } void CDropMenu::DrawItem(CDropMenuColumn *pColumn, CDropMenuItem *pItem, CRect rc, HDC hDC, BOOL bIsSelected, MenuSelectionType eSelType) { // Extract the goods from lpDI ASSERT(pItem != NULL); // Get proper colors and paint the background first COLORREF rgbTxt; HBRUSH brFace; COLORREF rgbOldBk; if (bIsSelected && (eSelType == eNONE || eSelType == eON)) { rgbOldBk = ::SetBkColor(hDC, sysInfo.m_clrHighlight); brFace = sysInfo.m_hbrHighlight; rgbTxt = ::GetSysColor(COLOR_HIGHLIGHTTEXT); } else { rgbOldBk = ::SetBkColor(hDC, sysInfo.m_clrMenu); brFace = sysInfo.m_hbrMenu; rgbTxt = ::GetSysColor(COLOR_MENUTEXT); } ::FillRect(hDC, rc, brFace); if(eSelType == eABOVE) DrawSeparator(hDC, rc.left, rc.top - SEPARATOR_SPACE - 2, rc.Width() / 2); else if(eSelType == eBELOW) DrawSeparator(hDC, rc.left, rc.bottom - SEPARATOR_HEIGHT - SEPARATOR_SPACE + 2, rc.Width() / 2); CDC *pDC = CDC::FromHandle(hDC); // Now paint the bitmap, left side of menu rc.left += nIMG_SPACE; CSize imgSize = DrawImage(pDC, rc, pColumn, pItem, bIsSelected, eSelType); // And now the text HFONT MenuFont; #if defined(WIN32) NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(NONCLIENTMETRICS); if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) { MenuFont = theApp.CreateAppFont( ncm.lfMenuFont ); } else { MenuFont = (HFONT)::GetStockObject(SYSTEM_FONT); } #else // Win16 MenuFont = (HFONT)::GetStockObject(SYSTEM_FONT); #endif // WIN32 HFONT pOldFont = (HFONT)::SelectObject(hDC, MenuFont); int nOldBkMode = ::SetBkMode(hDC, TRANSPARENT); COLORREF rgbOldTxt = ::SetTextColor(hDC, rgbTxt); //Figure out where text should go in relation to bitmaps rc.left += imgSize.cx + nIMG_SPACE + (pColumn->GetWidestImage()-imgSize.cx); //Deal with the case where there is a tab for an accelerator CString label=pItem->GetText(); DrawText(hDC, (const char*)label, -1, &rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE |DT_EXPANDTABS); ::SetBkMode(hDC, nOldBkMode); ::SetTextColor(hDC, rgbOldTxt); ::SelectObject(hDC, pOldFont); #if defined(WIN32) if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) { theApp.ReleaseAppFont(MenuFont); } #endif // Draw submenu bitmap if(pItem->IsSubMenu() && !pItem->IsEmpty()) { HBITMAP pSubmenuBitmap; if(bIsSelected && (eSelType == eNONE || eSelType == eON)) { pSubmenuBitmap = m_hSubmenuSelectedBitmap; } else { pSubmenuBitmap = m_hSubmenuUnselectedBitmap; } // Create a scratch DC and select our bitmap into it. HDC pBmpDC = CreateCompatibleDC(hDC); HBITMAP pOldBmp = (HBITMAP)::SelectObject(pBmpDC, pSubmenuBitmap); // Center the image horizontally and vertically CPoint ptDst(rc.right - SUBMENU_WIDTH - SUBMENU_RIGHT_MARGIN, rc.top + (((rc.Height() - SUBMENU_HEIGHT) + 1) / 2)); // Call the handy transparent blit function to paint the bitmap over // the background. ::FEU_TransBlt(hDC, ptDst.x, ptDst.y,SUBMENU_WIDTH, SUBMENU_HEIGHT, pBmpDC, 0, 0, WFE_GetUIPalette(GetParentFrame()),RGB(255, 0, 255) ); // Cleanup ::SelectObject(pBmpDC, pOldBmp); ::DeleteDC(pBmpDC); } ::SetBkColor(hDC, rgbOldBk); } // END OF FUNCTION CDropMenu::DrawItem() /**************************************************************************** * * CDropMenu::DrawImage * * PARAMETERS: * pDC - pointer to device context to draw on * rect - bounding rectangle to draw in * pItem - pointer to CDropMenuItem that contains the data * bIsSelected - whether this item is selected * * RETURNS: * width of the image, in pixels * * DESCRIPTION: * Protected helper function for drawing the bitmap image in a menu * item. We return the size of the painted image so the caller can * position the menu text after the image. * ****************************************************************************/ CSize CDropMenu::DrawImage(CDC * pDC, const CRect & rect, CDropMenuColumn *pColumn, CDropMenuItem * pItem, BOOL bIsSelected, MenuSelectionType eSelType) { CSize sizeImg; sizeImg.cx = sizeImg.cy = 0; HBITMAP hImg = (bIsSelected && (!m_bShowFeedback || eSelType == eON || eSelType == eNONE)) ? pItem->GetSelectedBitmap() : pItem->GetUnselectedBitmap(); if (hImg != NULL) { // Get image dimensions BITMAP bmp; GetObject(hImg, sizeof(bmp), &bmp); sizeImg.cx = bmp.bmWidth; sizeImg.cy = bmp.bmHeight; // Create a scratch DC and select our bitmap into it. HDC hBmpDC = ::CreateCompatibleDC(pDC->m_hDC); HBITMAP hOldBmp = (HBITMAP)SelectObject(hBmpDC, hImg); // Center the image horizontally and vertically CPoint ptDst(rect.left + (pColumn->GetWidestImage() - sizeImg.cx)/2, rect.top + (((rect.Height() - sizeImg.cy) + 1) / 2)); // Call the handy transparent blit function to paint the bitmap over // the background. if (pItem->GetIconType() == LOCAL_FILE) { HICON hIcon = pItem->GetLocalFileIcon(); DrawIconEx(pDC->m_hDC, ptDst.x, ptDst.y, hIcon, 0, 0, 0, NULL, DI_NORMAL); } else if (pItem->GetIconType() == ARBITRARY_URL) { CRDFImage* pImage = pItem->GetCustomIcon(); HDC hDC = pDC->m_hDC; int left = ptDst.x; int top = ptDst.y; int imageWidth = 16; int imageHeight = 16; COLORREF bkColor = (bIsSelected && (!m_bShowFeedback || eSelType == eON || eSelType == eNONE)) ? GetSysColor(COLOR_HIGHLIGHT) : GetSysColor(COLOR_MENU); if (pImage && pImage->FrameLoaded()) { // Now we draw this bad boy. if (pImage->m_BadImage) { // display broken icon. HDC tempDC = ::CreateCompatibleDC(hDC); HBITMAP hOldBmp = (HBITMAP)::SelectObject(tempDC, CRDFImage::m_hBadImageBitmap); ::StretchBlt(hDC, left, top, imageWidth, imageHeight, tempDC, 0, 0, imageWidth, imageHeight, SRCCOPY); ::SelectObject(tempDC, hOldBmp); ::DeleteDC(tempDC); } else if (pImage->bits ) { // Center the image. long width = pImage->bmpInfo->bmiHeader.biWidth; long height = pImage->bmpInfo->bmiHeader.biHeight; int xoffset = (imageWidth-width)/2; int yoffset = (imageHeight-height)/2; if (xoffset < 0) xoffset = 0; if (yoffset < 0) yoffset = 0; if (width > imageWidth) width = imageWidth; if (height > imageHeight) height = imageHeight; HPALETTE hPal = WFE_GetUIPalette(NULL); HPALETTE hOldPal = ::SelectPalette(hDC, hPal, TRUE); ::RealizePalette(hDC); if (pImage->maskbits) { WFE_StretchDIBitsWithMask(hDC, TRUE, NULL, left+xoffset, top+xoffset, width, height, 0, 0, width, height, pImage->bits, pImage->bmpInfo, pImage->maskbits, FALSE, bkColor); } else { ::StretchDIBits(hDC, left+xoffset, top+xoffset, width, height, 0, 0, width, height, pImage->bits, pImage->bmpInfo, DIB_RGB_COLORS, SRCCOPY); } ::SelectPalette(hDC, hOldPal, TRUE); } } } else ::BitBlt(pDC->m_hDC, ptDst.x, ptDst.y, sizeImg.cx, sizeImg.cy, hBmpDC, 0, 0, SRCCOPY); // Cleanup ::SelectObject(hBmpDC, hOldBmp); ::DeleteDC(hBmpDC); } return(sizeImg); } // END OF FUNCTION CTreeMenu::DrawImage() CDropMenuDropTarget *CDropMenu::GetDropMenuDropTarget(CWnd *pOwner) { return new CDropMenuDropTarget(pOwner); } CSize CDropMenu::GetMenuSize(void) { CSize totalSize, itemSize; int nColumnHeight = MENU_BORDER_SIZE, nColumnWidth = 0; CalculateItemDimensions(); int nCount = m_pMenuItemArray.GetSize(); int nScreenLeft = 0; int nScreenRight = GetSystemMetrics(SM_CXFULLSCREEN); int nScreenTop = 0; int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN); #ifdef XP_WIN32 RECT rect; if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0)) { nScreenLeft = rect.left; nScreenRight = rect.right; nScreenTop = rect.top; nScreenBottom = rect.bottom; } #endif int nStartX= MENU_BORDER_SIZE; int nOldStartX = MENU_BORDER_SIZE; int nMaxColumnHeight = 0; int nNumColumns = m_pColumnArray.GetSize(); CSize columnSize; CDropMenuColumn *pColumn; m_nLastVisibleColumn = nNumColumns - 1; for(int i = 0; i < nNumColumns; i++) { pColumn = (CDropMenuColumn *)m_pColumnArray[i]; if(pColumn->GetHasSubMenu()) pColumn->SetWidth(pColumn->GetWidth() + SUBMENU_LEFT_MARGIN + SUBMENU_RIGHT_MARGIN + SUBMENU_WIDTH); else pColumn->SetWidth(pColumn->GetWidth() + SUBMENU_RIGHT_MARGIN); columnSize = CalculateColumn(pColumn, nStartX); if(nScreenLeft + nStartX + columnSize.cx >= nScreenRight) { //if we have more than one column, don't put in the last separator if(nNumColumns > 1) { if(m_pOverflowMenuItem != NULL) { if(i > 0) nStartX = AddOverflowItem(i - 1, nOldStartX); else nStartX = AddOverflowItem(i, nStartX - SEPARATOR_WIDTH); } } m_nLastVisibleColumn = i - 1; break; } if(columnSize.cy > nMaxColumnHeight) nMaxColumnHeight = columnSize.cy; nOldStartX = nStartX; nStartX += columnSize.cx; if(i + 1 < nNumColumns) nStartX += SEPARATOR_WIDTH; } totalSize.cy = nMaxColumnHeight; totalSize.cx = ((nNumColumns > 1) ? nStartX - SEPARATOR_WIDTH : nStartX); return totalSize; } CSize CDropMenu::CalculateColumn(CDropMenuColumn *pColumn, int nStartX) { int nStart = pColumn->GetFirstItem(); int nEnd = pColumn->GetLastItem(); int nColumnWidth = pColumn->GetWidth(); int nItemHeight; CSize totalSize(0, MENU_BORDER_SIZE); CSize itemSize; for(int i = nStart; i <= nEnd; i++) { CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[i]; if(m_bShowFeedback) totalSize.cy += SPACE_BETWEEN_ITEMS; if(item->IsSeparator()) { CRect rect (nStartX, totalSize.cy, nStartX + nColumnWidth, totalSize.cy + SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT); item->SetMenuItemRect(rect); nItemHeight = SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT; } else { itemSize=MeasureItem(item); CRect rect(nStartX, totalSize.cy, nStartX + nColumnWidth, totalSize.cy + itemSize.cy); item->SetMenuItemRect(rect); nItemHeight = itemSize.cy; } totalSize.cy += nItemHeight; } return CSize(pColumn->GetWidth(), totalSize.cy); } //returns the x coordinate of the right side of this last column int CDropMenu::AddOverflowItem(int nColumn, int nStartX) { int nScreenTop = 0; int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN); #ifdef XP_WIN32 RECT rect; if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0)) { nScreenTop = rect.top; nScreenBottom = rect.bottom; } #endif CDropMenuColumn *pColumn = (CDropMenuColumn *)m_pColumnArray[nColumn]; int nPosition = pColumn->GetLastItem(); CDropMenuItem *pLastItem = (CDropMenuItem*)m_pMenuItemArray[nPosition]; if(pLastItem == m_pOverflowMenuItem) return pColumn->GetWidth(); int nHeight = pColumn->GetHeight(); CRect overflowRect; CRect itemRect; CDropMenuItem *pItem; CSize overflowSize = MeasureItem(m_pOverflowMenuItem); overflowSize.cx += pColumn->GetWidestImage(); while(nScreenTop + nHeight + overflowSize.cy > nScreenBottom) { pItem = (CDropMenuItem*)m_pMenuItemArray[nPosition]; pItem->GetMenuItemRect(&itemRect); nHeight -= itemRect.Height(); nPosition--; } // add 1 since we've subtracted 1 away pColumn->SetLastItem(nPosition + 1); pColumn->SetHeight(nHeight + overflowSize.cy); m_pOverflowMenuItem->SetColumn(nColumn); if(overflowSize.cx > pColumn->GetWidth()) pColumn->SetWidth(overflowSize.cx); m_pMenuItemArray.InsertAt(nPosition + 1, m_pOverflowMenuItem, 1); itemRect.SetRect(nStartX, nHeight, nStartX + pColumn->GetWidth(), nHeight + overflowSize.cy); m_pOverflowMenuItem->SetMenuItemRect(itemRect); return (nStartX + pColumn->GetWidth()); } void CDropMenu::CalculateItemDimensions(void) { int numItems = m_pMenuItemArray.GetSize(); int nScreenTop = 0; int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN); #ifdef XP_WIN32 RECT rect; if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0)) { nScreenTop = rect.top; nScreenBottom = rect.bottom; } #endif int nNumCols = 0; int nColumnHeight = nScreenBottom + 1; int nColumnWidth = 0; CSize textSize, unselectedBitmapSize, selectedBitmapSize, itemSize; CDropMenuColumn *pColumn = NULL; int nMaxBitmapSize = 0, nMaxTextSize = 0; int nCount = m_pColumnArray.GetSize(); //make sure column array is empty for(int j = 0 ; j < nCount; j++) delete (CDropMenuColumn*)m_pColumnArray[j]; m_pColumnArray.RemoveAll(); for(int i=0; iGetTextSize(); selectedBitmapSize = item->GetSelectedBitmapSize(); unselectedBitmapSize = item->GetUnselectedBitmapSize(); itemSize = MeasureItem(item); if(nScreenTop + nColumnHeight + itemSize.cy > nScreenBottom) { pColumn = new CDropMenuColumn; pColumn->SetHasSubMenu(FALSE); pColumn->SetFirstItem(i); m_pColumnArray.Add(pColumn); nColumnHeight = nColumnWidth = 0; nMaxBitmapSize = 0, nMaxTextSize = 0; nNumCols++; } item->SetColumn(nNumCols - 1); pColumn->SetLastItem(i); if(unselectedBitmapSize.cx > nMaxBitmapSize) nMaxBitmapSize = unselectedBitmapSize.cx; if(selectedBitmapSize.cx > nMaxBitmapSize) nMaxBitmapSize= selectedBitmapSize.cx; if(textSize.cx > nMaxTextSize) nMaxTextSize = textSize.cx; if(item->IsSubMenu()) pColumn->SetHasSubMenu(TRUE); nColumnHeight += itemSize.cy; if(m_bShowFeedback) nColumnHeight += SPACE_BETWEEN_ITEMS; pColumn->SetHeight(nColumnHeight); pColumn->SetWidth(nMaxBitmapSize + nMaxTextSize + ( nIMG_SPACE * 3)); pColumn->SetWidestImage(nMaxBitmapSize); if(item == m_pOverflowMenuItem) return; } if(m_pOverflowMenuItem != NULL) CalculateOneItemsDimensions(m_pOverflowMenuItem); } void CDropMenu::CalculateOneItemsDimensions(CDropMenuItem* item) { CSize unselectedBitmapSize, selectedBitmapSize, textSize; CString text; if(!item->IsSeparator()) { unselectedBitmapSize = MeasureBitmap(item->GetUnselectedBitmap()); item->SetUnselectedBitmapSize(unselectedBitmapSize); selectedBitmapSize = MeasureBitmap(item->GetSelectedBitmap()); item->SetSelectedBitmapSize(selectedBitmapSize); if(item->IsSubMenu()) { m_bHasSubMenu = TRUE; } text = item->GetText(); textSize = MeasureText(text); item->SetTextSize(textSize); } } /**************************************************************************** * * CTreeMenu::MeasureBitmap * * PARAMETERS: * pBitmap: The bitmap to measure * * RETURNS: * The size of the bitmap * * DESCRIPTION: * Given a bitmap, this returns its dimensions * ****************************************************************************/ CSize CDropMenu::MeasureBitmap(HBITMAP hBitmap) { CSize size; if (hBitmap != NULL) { BITMAP bmp; GetObject(hBitmap, sizeof(bmp), &bmp); size.cx = bmp.bmWidth; size.cy = bmp.bmHeight; } else size.cx = size.cy = 0; return size; } /**************************************************************************** * * CTreeMenu::MeasureText * * PARAMETERS: * text: The CString text to be measured * * RETURNS: * Returns the size of the text in the current font * * DESCRIPTION: * Given a CString this returns the dimensions of that text * ****************************************************************************/ CSize CDropMenu::MeasureText(CString text) { HFONT hMenuFont; #if defined(WIN32) NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(NONCLIENTMETRICS); if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) { hMenuFont = theApp.CreateAppFont( ncm.lfMenuFont ); } else { hMenuFont = (HFONT)::GetStockObject(SYSTEM_FONT); } #else // Win16 hMenuFont = (HFONT)::GetStockObject(SYSTEM_FONT); #endif // WIN32 HDC hDC = ::GetDC(m_hWnd); HFONT hOldFont = (HFONT) ::SelectObject(hDC, hMenuFont); CSize sizeTxt = ::GetTabbedTextExtent(hDC, (LPCSTR)text, strlen(text), 0, NULL); ::SelectObject(hDC, hOldFont); #if defined(WIN32) if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) { theApp.ReleaseAppFont(hMenuFont); } #endif ::ReleaseDC(m_hWnd, hDC); return sizeTxt; } /**************************************************************************** * * CTreeMenu::MeasureItem * * PARAMETERS: * lpMI - pointer to LPMEASUREITEMSTRUCT * * RETURNS: * void * * DESCRIPTION: * We must override this function to inform the system of our menu * item dimensions, since we're owner draw style. * ****************************************************************************/ //menu item and end of accelerator CSize CDropMenu::MeasureItem(CDropMenuItem *pItem) { ASSERT(pItem != NULL); CSize size; if(pItem->IsSeparator()) { size.cx = 0; size.cy = SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT; } else { // First, get all of the dimensions CSize unselectedSizeImg=pItem->GetUnselectedBitmapSize(); CSize selectedSizeImg = pItem->GetSelectedBitmapSize(); CSize sizeText=pItem->GetTextSize(); size.cx = sizeText.cx + (nIMG_SPACE * 2); int maxImageHeight = max(unselectedSizeImg.cy, selectedSizeImg.cy); size.cy = max(maxImageHeight, sizeText.cy) + 4; } return size; } void CDropMenu::ChangeSelection(CPoint point) { if(m_bShowing) { MenuSelectionType eSelType; int nSelection = FindSelection(point, eSelType); eSelType = m_bShowFeedback ? eSelType : eNONE; if(m_nSelectedItem != nSelection || m_eSelectedItemSelType != eSelType) { SelectMenuItem(m_nSelectedItem, FALSE, TRUE, m_eSelectedItemSelType); SelectMenuItem(nSelection, TRUE, TRUE, eSelType); } } } int CDropMenu::FindSelection(CPoint point, MenuSelectionType &eSelType) { eSelType = eNONE; int nNumColumns = m_pColumnArray.GetSize(); CRect colRect; CDropMenuColumn *pColumn; int nStartX = MENU_BORDER_SIZE; int nColumnWidth; for(int i = 0; i < nNumColumns; i++) { pColumn = (CDropMenuColumn*)m_pColumnArray[i]; nColumnWidth = pColumn->GetWidth(); colRect.SetRect(nStartX, 0, nStartX + nColumnWidth, pColumn->GetHeight()); if(colRect.PtInRect(point)) { return (FindSelectionInColumn(pColumn, point, eSelType)); } else { nStartX += nColumnWidth + SEPARATOR_WIDTH; } } return -1; } int CDropMenu::FindSelectionInColumn(CDropMenuColumn *pColumn, CPoint point, MenuSelectionType &eSelType) { int y = MENU_BORDER_SIZE; if(point.y >=0 && point.y <= MENU_BORDER_SIZE) return 0; int nFeedbackHeight = m_bShowFeedback ? SPACE_BETWEEN_ITEMS : 0; CDropMenuItem *item; int nFirstItem = pColumn->GetFirstItem(); int nLastItem = pColumn->GetLastItem(); for(int i = nFirstItem; i <= nLastItem; i++) { item = (CDropMenuItem*)m_pMenuItemArray[i]; CRect rect; item->GetMenuItemRect(rect); if( point.y >= y && point.y < y + rect.Height() + nFeedbackHeight) { if(item->GetShowFeedback()) eSelType = FindSelectionType(item, point.y - y, rect.Height() + nFeedbackHeight); return i; } y += rect.Height() + nFeedbackHeight; } //if we're in the border between the bottom and the last menu item if(point.y >= y && point.y <= y + 2*MENU_BORDER_SIZE) { if(item->GetShowFeedback()) eSelType = eBELOW; return i - 1; } return -1; } MenuSelectionType CDropMenu::FindSelectionType(CDropMenuItem *pItem, int y, int nHeight) { if(pItem->IsSubMenu()) { if (y >= 0 && y <= nHeight/4.0) return eABOVE; else if (y <= 3.0*nHeight/4.0) return eON; else return eBELOW; } else { if(y >= 0 && y <= nHeight / 2.0) return eABOVE; else return eBELOW; } } void CDropMenu::SelectMenuItem(int nItemIndex, BOOL bIsSelected, BOOL bHandleSubmenus, MenuSelectionType eSelType) { RECT rcClient; GetClientRect(&rcClient); if(nItemIndex != -1) { CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[nItemIndex]; InvalidateMenuItemRect(item); if(!bIsSelected) { m_pShowingSubmenu = NULL; m_nSelectedItem = -1; m_eSelectedItemSelType = eNONE; if(bHandleSubmenus && item->IsSubMenu() && !item->IsEmpty() && item->GetSubMenu()->m_bShowing) { if(m_pUnselectedItem == NULL) { item->GetSubMenu()->m_bShowing = FALSE; m_pUnselectedItem = item; m_nUnselectTimer = SetTimer(IDT_SUBMENU_OFF ,SUBMENU_DELAY_OFF_MS, NULL); } } } else { m_nSelectedItem = nItemIndex; m_eSelectedItemSelType = eSelType; if(m_nSelectTimer != 0) { KillTimer(m_nSelectTimer); } if(bHandleSubmenus && item->IsSubMenu() && !item->IsEmpty() && (eSelType == eON || eSelType == eNONE)) { m_pShowingSubmenu = item->GetSubMenu(); m_pSelectedItem = item; m_pSelectedItem->GetSubMenu()->m_bAboutToShow = TRUE; m_nSelectTimer = SetTimer(IDT_SUBMENU_ON, SUBMENU_DELAY_ON_MS, NULL); } } } } BOOL CDropMenu::NoSubmenusShowing(void) { CWnd *child = GetWindow(GW_CHILD); if(child != NULL) { while(child != NULL) { if(child->IsWindowVisible()) { return FALSE; } child = GetWindow(GW_HWNDNEXT); } } return TRUE; } CDropMenu *CDropMenu::GetMenuParent(void) { return m_pMenuParent; } BOOL CDropMenu::IsDescendant(CWnd *pDescendant) { CDropMenu *pSubmenu = m_pShowingSubmenu; while(pSubmenu != NULL) { if(pSubmenu == pDescendant) { return TRUE; } pSubmenu = pSubmenu->m_pShowingSubmenu; } return FALSE; } BOOL CDropMenu::IsAncestor(CWnd *pAncestor) { CDropMenu *pMenu = this; while(pMenu->m_pParent == pMenu->m_pMenuParent) { if(pMenu->m_pMenuParent == pAncestor) { return TRUE; } pMenu = pMenu->m_pMenuParent; } return FALSE; } //Sees if the hwnd is either this menu, an ancestor or a submenu BOOL CDropMenu::IsShowingMenu(HWND hwnd) { if(hwnd == m_hWnd) { return TRUE; } else { CDropMenu *menu = m_pMenuParent; while(menu != NULL) { if(hwnd == menu->m_hWnd) { return TRUE; } menu=menu->m_pMenuParent; } menu = m_pShowingSubmenu; while(menu !=NULL) { if(hwnd == menu->m_hWnd) { return TRUE; } menu = menu->m_pShowingSubmenu; } } return FALSE; } void CDropMenu::DropMenuTrackDropMenu(CDropMenu *pParent, int x, int y, BOOL bDragging, CDropMenuDropTarget *pDropTarget, BOOL bShowFeedback, int oppX, int oppY) { m_pMenuParent = pParent; pParent->m_pShowingSubmenu = this; TrackDropMenu(pParent, x, y, bDragging, pDropTarget, bShowFeedback, oppX, oppY); } int CDropMenu::FindCommand(UINT nCommand) { int nCount = m_pMenuItemArray.GetSize(); for(int i = 0; i < nCount; i++) { CDropMenuItem * pItem = (CDropMenuItem*)m_pMenuItemArray[i]; if(!pItem->IsSeparator()) { if(pItem->GetCommand() == nCommand) { return i; } } } return -1; } CPoint CDropMenu::FindStartPoint(int x, int y, CSize size, int oppX, int oppY) { int nScreenLeft = 0; int nScreenRight = GetSystemMetrics(SM_CXFULLSCREEN); int nScreenTop = 0; int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN); #ifdef XP_WIN32 RECT rect; if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0)) { nScreenLeft = rect.left; nScreenRight = rect.right; nScreenTop = rect.top; nScreenBottom = rect.bottom; } #endif if(oppX == -1) { CRect parentRect; m_pParent->GetWindowRect(&parentRect); oppX = m_bRight ? parentRect.left : parentRect.right; } int nRoomToRight = nScreenRight - (m_bRight ? x : oppX); int nRoomToLeft = (m_bRight ? oppX : x) - nScreenLeft; // if we are opening to the right we need to make sure we don't go off the right // side of the screen. if(m_bRight) { //if we will extend past the right if(x + size.cx > nScreenRight) { // if we can fit to the left if(oppX - size.cx > nScreenLeft) { if(m_pMenuParent != NULL) { x = oppX - size.cx; //if it's not the top level menu we want it to overlap its parent x -= 2; } else { x = oppX - size.cx; } m_bRight = FALSE; } else { //see where we have more space if(nRoomToRight >= nRoomToLeft) x = nScreenRight - size.cx; else { x = nScreenLeft; m_bRight = FALSE; } } } else if(m_pMenuParent != NULL) { // if we are a submenu we want to overlap our parent x -= 5; } } // if we are opening to the left we need to make sure we don't go off the left // side of the screen else { //if we will extend past the left if(x - size.cx < nScreenLeft) { // if we can fit to the right if(oppX + size.cx < nScreenRight) { x = oppX; //if it's not the top level menu we want it to overlap its parent if(m_pMenuParent != NULL) { x -= 5; } m_bRight = TRUE; } else { if(nRoomToRight >= nRoomToLeft) { x = nScreenRight - size.cx; m_bRight = TRUE; } else x = nScreenLeft; } } else if(m_pMenuParent != NULL) { x -= size.cx; // if we are a submenu we want to overlap our parent x -= 2; } } if(y + size.cy > nScreenBottom) { y -= ((y + size.cy) - nScreenBottom); } return CPoint(x, y); } void CDropMenu::HandleUpArrow(void) { int nSelection; // need to deal with separators if(m_nSelectedItem == NOSELECTION || m_nSelectedItem == 0) { nSelection = GetMenuItemCount() - 1; } else { nSelection = m_nSelectedItem - 1; } while(((CDropMenuItem *)m_pMenuItemArray[nSelection])->IsSeparator()) { nSelection = (nSelection == 0) ? GetMenuItemCount() - 1 : nSelection - 1; } SelectMenuItem(m_nSelectedItem, FALSE, FALSE, eNONE); SelectMenuItem(nSelection, TRUE, FALSE, eNONE); } void CDropMenu::HandleDownArrow(void) { int nSelection; int nNumItems = GetMenuItemCount() - 1; // need to deal with separators if(m_nSelectedItem == NOSELECTION || m_nSelectedItem == nNumItems) { nSelection = 0; } else { nSelection = m_nSelectedItem + 1; } while(((CDropMenuItem *)m_pMenuItemArray[nSelection])->IsSeparator()) { nSelection = (nSelection == nNumItems - 1) ? 0 : nSelection + 1; } SelectMenuItem(m_nSelectedItem, FALSE, FALSE, eNONE); SelectMenuItem(nSelection, TRUE, FALSE, eNONE); } void CDropMenu::HandleLeftArrow(void) { if(m_pMenuParent != NULL) { m_bShowing = FALSE; m_pMenuParent->m_bMouseUsed = FALSE; ShowWindow(SW_HIDE); } } void CDropMenu::HandleRightArrow(void) { if(m_nSelectedItem != NOSELECTION) { CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[m_nSelectedItem]; if(pItem->IsSubMenu()) { m_pShowingSubmenu = pItem->GetSubMenu(); m_pShowingSubmenu->m_nSelectedItem = 0; m_pShowingSubmenu->m_bMouseUsed = FALSE; ShowSubmenu(pItem); } } } void CDropMenu::HandleReturnKey(void) { if(m_nSelectedItem != NOSELECTION) { CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[m_nSelectedItem]; if(pItem->IsSubMenu()) { HandleRightArrow(); } else { #ifdef _WIN32 SendMessage(WM_COMMAND, MAKEWPARAM(pItem->GetCommand(), 0), 0); #else SendMessage(WM_COMMAND, (WPARAM) pItem->GetCommand(), MAKELPARAM( m_hWnd, 0 )); #endif } } } void CDropMenu::ShowSubmenu(CDropMenuItem *pItem) { CRect rcClient, rect; GetClientRect(rcClient); pItem->GetMenuItemRect(rect); CDropMenuDropTarget *pDropTarget = GetDropMenuDropTarget(pItem->GetSubMenu()); ClientToScreen(rect); m_pShowingSubmenu = pItem->GetSubMenu(); pItem->GetSubMenu()->SetSubmenuBitmaps(m_hSubmenuSelectedBitmap, m_hSubmenuUnselectedBitmap); if(m_bRight) { pItem->GetSubMenu()->DropMenuTrackDropMenu(this, rect.right, rect.top - 2, m_bDragging, pDropTarget, m_bShowFeedback, rect.left); } else { pItem->GetSubMenu()->DropMenuTrackDropMenu(this, rect.left, rect.top - 2, m_bDragging, pDropTarget, m_bShowFeedback, rect.right); } pItem->GetSubMenu()->m_pParentMenuItem = pItem; } int CDropMenu::FindMenuItemPosition(CDropMenuItem* pMenuItem) { int nCount = m_pMenuItemArray.GetSize(); for(int i = 0; i < nCount; i++) if(m_pMenuItemArray[i] == pMenuItem) return i; return -1; } void CDropMenu::SetSubmenuBitmaps(HBITMAP hSelectedBitmap, HBITMAP hUnselectedBitmap) { m_hSubmenuUnselectedBitmap = hUnselectedBitmap; m_hSubmenuSelectedBitmap = hSelectedBitmap; } void CDropMenu::InvalidateMenuItemRect(CDropMenuItem *pMenuItem) { CRect rcClient; CRect rect; GetClientRect(&rcClient); pMenuItem->GetMenuItemRect(rect); int nColumn = pMenuItem->GetColumn(); CDropMenuColumn *pColumn= (CDropMenuColumn*)m_pColumnArray[nColumn]; rect.right = rect.left + pColumn->GetWidth(); rect.top -= 2; rect.bottom += 2; InvalidateRect(rect, TRUE); } void CDropMenu::InvalidateMenuItemIconRect(CDropMenuItem *pMenuItem) { CRect rcClient; CRect rect; GetClientRect(&rcClient); pMenuItem->GetMenuItemRect(rect); int nColumn = pMenuItem->GetColumn(); CDropMenuColumn *pColumn= (CDropMenuColumn*)m_pColumnArray[nColumn]; rect.left += nIMG_SPACE; rect.right = rect.left + 16; InvalidateRect(rect, TRUE); } void CDropMenu::KeepMenuAroundIfClosing(void) { // if there is movement on this menu, then it is showing even // if it's been told to go away before. m_bShowing = TRUE; if(m_pMenuParent) { CDropMenuItem *pSelectedItem = m_pMenuParent->m_nSelectedItem != -1 ? (CDropMenuItem*)m_pMenuParent->m_pMenuItemArray[m_pMenuParent->m_nSelectedItem] : NULL; CDropMenu *pSubMenu = pSelectedItem && pSelectedItem->IsSubMenu() ? pSelectedItem->GetSubMenu() : NULL; if((pSubMenu && pSubMenu != this) || !pSubMenu) { KillTimer(m_pMenuParent->m_nSelectTimer); KillTimer(m_pMenuParent->m_nUnselectTimer); m_pMenuParent->m_pUnselectedItem = NULL; m_pMenuParent->m_nSelectTimer = 0; m_pMenuParent->m_pShowingSubmenu = this; //We need to make the menu item this belongs to the selected menu item. int nPosition = m_pMenuParent->FindMenuItemPosition(m_pParentMenuItem); if(nPosition != -1) { if(pSelectedItem) { m_pMenuParent->InvalidateMenuItemRect(pSelectedItem); } m_pMenuParent->m_nSelectedItem = nPosition; m_pMenuParent->m_eSelectedItemSelType = eON; m_pMenuParent->m_pSelectedItem = m_pParentMenuItem; m_pMenuParent->InvalidateMenuItemRect(m_pParentMenuItem); } } } } CWnd *CDropMenu::GetTopLevelParent(void) { CDropMenu *pMenu = this; while(pMenu->m_pMenuParent != NULL) { pMenu = pMenu->m_pMenuParent; } return pMenu->m_pParent; } void CDropMenu::LoadComplete(HT_Resource r) { // Need to invalidate the line that corresponds to this particular resource. HT_Resource parent = HT_GetParent(r); if (parent) { // The offset of this node from its direct parent determines the line that should be // invalidated. int childIndex = HT_GetNodeIndex(HT_GetView(r), r); int parentIndex = HT_GetNodeIndex(HT_GetView(parent), parent); if (parent == HT_TopNode(HT_GetView(r))) parentIndex = -1; // Need to count up from the parent index to the child index and only include nodes // with indentation equal to the child index. int indentation = HT_GetItemIndentation(r); int offset = 0; for (int i = parentIndex + 1; i < childIndex; i++) { int otherIndentation = HT_GetItemIndentation(HT_GetNthItem(HT_GetView(r), i)); if (otherIndentation == indentation) offset++; } CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[offset]; InvalidateMenuItemIconRect(item); } }