home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / winfe / dropmenu.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  61.7 KB  |  2,578 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. // dropmenu.cpp
  20. // Implements a popup menu in which you can drag and drop
  21.  
  22. #include "stdafx.h"
  23. #include "dropmenu.h"
  24. #include "cxicon.h"
  25.  
  26. #define nIMG_SPACE 4
  27. #define MAX_DROPMENU_ITEM_LENGTH 45
  28.  
  29.  
  30. //////////////////////////////////////////////////////////////////////////
  31. //                    Class CDropMenuButton
  32. //////////////////////////////////////////////////////////////////////////
  33. CDropMenuButton::CDropMenuButton(void)
  34. {
  35.  
  36.     m_bButtonPressed = FALSE;
  37.     m_bMenuShowing = FALSE;
  38.     m_bFirstTime = FALSE;
  39. }
  40. //////////////////////////////////////////////////////////////////////////
  41. //                    Messages for CDropMenuButton
  42. //////////////////////////////////////////////////////////////////////////
  43.  
  44. BEGIN_MESSAGE_MAP(CDropMenuButton, CStationaryToolbarButton)
  45.     //{{AFX_MSG_MAP(CWnd)
  46.      ON_WM_LBUTTONDOWN()
  47.     ON_WM_LBUTTONUP()
  48.     ON_WM_MOUSEMOVE()
  49.     ON_MESSAGE(DM_MENUCLOSED, OnMenuClosed)
  50.  
  51.     //}}AFX_MSG_MAP
  52.  
  53. END_MESSAGE_MAP()
  54.  
  55. void CDropMenuButton::OnLButtonDown(UINT nFlags, CPoint point)
  56. {
  57.     CStationaryToolbarButton::OnLButtonDown(nFlags, point);
  58.  
  59.     m_bButtonPressed = TRUE;
  60. }
  61.  
  62. void CDropMenuButton::OnMouseMove(UINT nFlags, CPoint point)
  63. {
  64.     if(m_bMenuShowing) 
  65.     {
  66.         if(m_bFirstTime)
  67.         {
  68.             ReleaseCapture();
  69.             m_eState = eBUTTON_DOWN;
  70.             RedrawWindow();
  71.             m_bFirstTime = FALSE;
  72.         }
  73.     }
  74.     else
  75.     {
  76.         CStationaryToolbarButton::OnMouseMove(nFlags, point);
  77.     }
  78. }
  79.  
  80. void CDropMenuButton::OnLButtonUp(UINT nFlags, CPoint point)
  81. {
  82.      if(m_bButtonPressed && m_bEnabled && m_bHaveFocus)
  83.      {
  84.          OnAction();
  85.          m_bMenuShowing = TRUE;
  86.          m_bFirstTime = TRUE;
  87.      }
  88.      else
  89.      {
  90.          CStationaryToolbarButton::OnLButtonUp(nFlags, point);
  91.          m_bMenuShowing = FALSE;
  92.      }
  93.     m_bButtonPressed = FALSE;
  94. }
  95.  
  96. LRESULT CDropMenuButton::OnMenuClosed(WPARAM wParam, LPARAM lParam)
  97. {
  98.  
  99.     m_bMenuShowing = FALSE;
  100.     m_eState = eNORMAL;
  101.     RedrawWindow();
  102.  
  103.     return 1;
  104. }
  105.  
  106. //////////////////////////////////////////////////////////////////////////
  107. //                    Class CDropMenuItem
  108. //////////////////////////////////////////////////////////////////////////
  109.  
  110. CDropMenuItem::CDropMenuItem(UINT nFlags, CString szText, UINT nCommandID,
  111.                              HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, 
  112.                              BOOL bShowFeedback, void *pUserData)
  113. {
  114.     m_nIconType = BUILTIN_BITMAP;
  115.  
  116.     if(nFlags & MF_SEPARATOR)
  117.     {
  118.         m_bIsSeparator = TRUE;
  119.     }
  120.     else if(nFlags == MF_STRING)
  121.     {
  122.         m_bIsSeparator = FALSE;
  123.         char *pCutText = fe_MiddleCutString(szText.GetBuffer(szText.GetLength() + 1), MAX_DROPMENU_ITEM_LENGTH);
  124.         m_szText = pCutText;
  125.         szText.ReleaseBuffer();
  126.         free(pCutText);
  127.         m_hUnselectedBitmap = hUnselectedBitmap;
  128.  
  129.         m_hSelectedBitmap = (hSelectedBitmap == NULL) ? hUnselectedBitmap : hSelectedBitmap;
  130.  
  131.         m_nCommandID = nCommandID;
  132.         m_pSubMenu = NULL;
  133.     }
  134.     m_bIsEmpty = TRUE;
  135.     m_unselectedBitmapSize.cx = m_unselectedBitmapSize.cy = 0;
  136.     m_selectedBitmapSize.cx = m_selectedBitmapSize.cy = 0;
  137.     m_textSize.cx = m_textSize.cy = 0;
  138.     m_totalSize.cx = m_totalSize.cy = 0;
  139.     m_menuItemRect.SetRectEmpty();
  140.     m_bShowFeedback = bShowFeedback;
  141.     m_pUserData = pUserData;
  142.  
  143. }    
  144.  
  145. CDropMenuItem::CDropMenuItem(UINT nFlags, CString szText, UINT nCommandID, CDropMenu *pSubMenu, BOOL bIsEmpty,
  146.                              HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, BOOL bShowFeedback, void *pUserData)
  147. {
  148.     if(nFlags & MF_POPUP)
  149.     {
  150.         m_bIsSeparator = FALSE;
  151.         char *pCutText = fe_MiddleCutString(szText.GetBuffer(szText.GetLength() + 1), MAX_DROPMENU_ITEM_LENGTH);
  152.         m_szText = pCutText;
  153.         szText.ReleaseBuffer();
  154.  
  155.         free(pCutText);
  156.  
  157.         m_hUnselectedBitmap = hUnselectedBitmap;
  158.         m_hSelectedBitmap = (hSelectedBitmap == NULL) ? hUnselectedBitmap : hSelectedBitmap;
  159.  
  160.         m_nCommandID = nCommandID;
  161.         m_pSubMenu = pSubMenu;
  162.     }
  163.     m_bIsEmpty = bIsEmpty;
  164.     m_unselectedBitmapSize.cx = m_unselectedBitmapSize.cy = 0;
  165.     m_selectedBitmapSize.cx = m_selectedBitmapSize.cy = 0;
  166.     m_textSize.cx = m_textSize.cy = 0;
  167.     m_totalSize.cx = m_totalSize.cy = 0;
  168.     m_menuItemRect.SetRectEmpty();
  169.     m_bShowFeedback = bShowFeedback;
  170.     m_pUserData = pUserData;
  171. }
  172.  
  173. CDropMenuItem::~CDropMenuItem()
  174. {
  175.  
  176.     if(IsSubMenu())
  177.     {
  178.         CDropMenu *pSubMenu = GetSubMenu();
  179.         pSubMenu->DestroyDropMenu();
  180.         delete pSubMenu;
  181.     }
  182. }
  183. void  CDropMenuItem::GetMenuItemRect(LPRECT rect)
  184. {
  185.     rect->left = m_menuItemRect.left;
  186.     rect->right = m_menuItemRect.right;
  187.     rect->top = m_menuItemRect.top;
  188.     rect->bottom = m_menuItemRect.bottom;
  189.  
  190. }
  191.  
  192. void CDropMenuItem::SetMenuItemRect(LPRECT rect)
  193. {
  194.     m_menuItemRect.left = rect->left;
  195.     m_menuItemRect.right = rect->right;
  196.     m_menuItemRect.top = rect->top;
  197.     m_menuItemRect.bottom = rect->bottom;
  198. }
  199.  
  200.  
  201. ///////////////////////////////////////////////////////////////////////////
  202. //                            Class CDropMenuDropTarget
  203. ///////////////////////////////////////////////////////////////////////////
  204.  
  205. CDropMenuDropTarget::CDropMenuDropTarget(CWnd *pOwner)
  206. {
  207.     m_pOwner = pOwner;
  208.  
  209. }
  210.  
  211.  
  212. DROPEFFECT CDropMenuDropTarget::OnDragEnter(CWnd * pWnd,    COleDataObject * pDataObject,
  213.                                                DWORD dwKeyState, CPoint point)
  214. {
  215.     return OnDragOver(pWnd, pDataObject, dwKeyState, point);
  216.     
  217. }
  218.  
  219. DROPEFFECT CDropMenuDropTarget::OnDragOver(CWnd * pWnd, COleDataObject * pDataObject,
  220.                                               DWORD dwKeyState, CPoint point )
  221. {
  222.     CDropMenuDragData dragData(pDataObject, point);
  223.     m_pOwner->SendMessage(DT_DRAGGINGOCCURRED, (WPARAM) 0, (LPARAM)(&dragData));
  224.     
  225.     return dragData.m_DropEffect;
  226. }
  227.  
  228. BOOL CDropMenuDropTarget::OnDrop(CWnd * pWnd, COleDataObject * pDataObject,
  229.             DROPEFFECT dropEffect, CPoint point)
  230. {
  231.     CDropMenuDragData* dragData = new CDropMenuDragData(pDataObject, point);
  232.     m_pOwner->SendMessage(DT_DROPOCCURRED, (WPARAM) 0, (LPARAM)(dragData));
  233.     
  234.     return TRUE;
  235. }
  236.  
  237. // End CDropMenuDropTarget implementation
  238.  
  239. // CDropMenuColumn
  240. CDropMenuColumn::CDropMenuColumn(int nFirstItem, int nLastItem, int nWidth, int nHeight)
  241. {
  242.     m_nFirstItem = nFirstItem;
  243.     m_nLastItem = nLastItem;
  244.     m_nWidth = nWidth;
  245.     m_nHeight = nHeight;
  246.     m_bHasSubMenu = FALSE;
  247. }
  248.  
  249.  
  250. #define NOSELECTION -1
  251. #define MENU_BORDER_SIZE 3
  252.  
  253. #define SUBMENU_WIDTH 9
  254. #define SUBMENU_HEIGHT 11
  255.  
  256. #define SUBMENU_LEFT_MARGIN 3
  257. #define SUBMENU_RIGHT_MARGIN 7
  258.  
  259. #define IDT_SUBMENU_OFF    16384
  260. #define SUBMENU_DELAY_OFF_MS 400
  261.  
  262. #define IDT_SUBMENU_ON 16385
  263. #define SUBMENU_DELAY_ON_MS 400
  264.  
  265. #define IDT_HAVE_FOCUS 16386
  266. #define HAVE_FOCUS_DELAY 400
  267.  
  268. #define IDT_UNHOOK 16387
  269. #define UNHOOK_DELAY 100
  270.  
  271. #define SEPARATOR_SPACE 2
  272. #define SEPARATOR_HEIGHT 2
  273. #define SEPARATOR_WIDTH 2
  274. #define SPACE_BETWEEN_ITEMS 2
  275.  
  276. static HHOOK currentMouseHook;
  277. static HHOOK currentKeyboardHook;
  278. static CDropMenu * currentMenu = NULL;        //This refers to a submenu of the current dropmenu
  279. static CDropMenu * gLastMenu = NULL;        //This refers to the most recent complete dropmenu
  280. static LONG oldFrameProc;
  281. static UINT gUnhookTimer = 0;
  282.  
  283. CDropMenu* CDropMenu::GetLastDropMenu()
  284. {
  285.     return gLastMenu;
  286. }
  287.  
  288. void CDropMenu::SetLastDropMenu(CDropMenu* menu)
  289. {
  290.     gLastMenu = menu;
  291. }
  292.  
  293. CDropMenu::CDropMenu()
  294. {
  295.  
  296.     m_pMenuItemArray.SetSize(0, 10);
  297.  
  298.     m_WidestImage = 0;
  299.     m_WidestText = 0;
  300.  
  301.     m_nSelectedItem = -1;
  302.     m_eSelectedItemSelType = eNONE;
  303.     m_bShowing = FALSE;
  304.     m_pDropTarget = NULL;
  305.  
  306.  
  307.     m_pUnselectedItem = NULL;
  308.     m_pSelectedItem = NULL;
  309.  
  310.     m_nSelectTimer = 0;
  311.     m_nUnselectTimer = 0;
  312.  
  313.     m_pMenuParent = NULL;
  314.     m_pShowingSubmenu = NULL;
  315.  
  316.     m_bDropOccurring = FALSE;
  317.     m_bDragging = FALSE;
  318.     m_bRight = TRUE;
  319.     m_bMouseUsed = TRUE;
  320.     m_bIsOpen = FALSE;
  321.     m_pOverflowMenuItem = NULL;
  322.     m_bAboutToShow = FALSE;
  323.     m_pParentMenuItem = NULL;
  324.     m_hSubmenuUnselectedBitmap = NULL;
  325.     m_hSubmenuSelectedBitmap = NULL;
  326.     m_pUserData = NULL;
  327. }
  328.  
  329. CDropMenu::~CDropMenu()
  330. {
  331.     //  Clear out any dangling pointers.
  332.     if(currentMenu == this) {
  333.         currentMenu = NULL;
  334.     }
  335.     if(gLastMenu == this)   {
  336.         gLastMenu = NULL;
  337.     }
  338.  
  339.     int nCount = m_pMenuItemArray.GetSize();
  340.  
  341.     for(int i = 0; i < nCount; i++)
  342.     {
  343.         CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[i];
  344.         if(pItem != m_pOverflowMenuItem)
  345.             delete pItem;
  346.     }
  347.  
  348.     int nColumnCount = m_pColumnArray.GetSize();
  349.  
  350.     for(int j = 0 ; j < nColumnCount; j++)
  351.         delete (CDropMenuColumn*)m_pColumnArray[j];
  352.  
  353.     if(m_pDropTarget)
  354.         delete m_pDropTarget;
  355.  
  356.     if(m_pOverflowMenuItem != NULL)
  357.         delete m_pOverflowMenuItem;
  358.  
  359.     m_pMenuItemArray.RemoveAll();
  360.  
  361.     if(m_pMenuParent == NULL)
  362.     {
  363.         if(m_hSubmenuSelectedBitmap != NULL)
  364.         {
  365.             ::DeleteObject(m_hSubmenuSelectedBitmap);
  366.             m_hSubmenuSelectedBitmap = NULL;
  367.         }
  368.  
  369.         if(m_hSubmenuUnselectedBitmap != NULL)
  370.         {
  371.             ::DeleteObject(m_hSubmenuUnselectedBitmap);
  372.             m_hSubmenuUnselectedBitmap = NULL;
  373.         }
  374.     }
  375.  
  376.     if(m_pMenuParent != NULL && m_pMenuParent->m_pShowingSubmenu == this)
  377.         m_pMenuParent->m_pShowingSubmenu = NULL;
  378.  
  379.     m_pMenuParent = NULL;
  380.     m_bShowing = FALSE;
  381.  
  382.  
  383. }
  384.  
  385. UINT CDropMenu::GetMenuItemCount(void)
  386. {
  387.     return m_pMenuItemArray.GetSize();
  388. }
  389.  
  390. void CDropMenu::AppendMenu(UINT nFlags, UINT nIDNewItem, CString szText, BOOL bShowFeedback,
  391.                            HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, 
  392.                            void* icon, IconType iconType, void *pUserData)
  393. {
  394.  
  395.     CDropMenuItem *pMenuItem = new CDropMenuItem(nFlags, szText, nIDNewItem,
  396.                                                  hUnselectedBitmap, hSelectedBitmap, bShowFeedback, pUserData); 
  397.     
  398.     pMenuItem->SetIcon(icon, iconType);
  399.     m_pMenuItemArray.Add(pMenuItem);
  400. }
  401.  
  402. void CDropMenu::AppendMenu(UINT nFlags, UINT nIDNewItem, CDropMenu *pSubMenu, BOOL bIsEmpty, CString szText,
  403.                            BOOL bShowFeedback, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap, 
  404.                            void* icon, IconType iconType, void *pUserData)
  405. {
  406.     CDropMenuItem *pMenuItem = new CDropMenuItem(nFlags, szText, nIDNewItem, pSubMenu, bIsEmpty,
  407.                                                  hUnselectedBitmap, hSelectedBitmap, bShowFeedback, pUserData); 
  408.     pMenuItem->SetIcon(icon, iconType);
  409.     m_pMenuItemArray.Add(pMenuItem);
  410. }
  411.  
  412. void CDropMenu::DeleteMenu(UINT nPosition, UINT nFlags)
  413. {
  414.     CDropMenuItem *pItem;
  415.  
  416.     if(nFlags == MF_BYPOSITION)
  417.     {
  418.         UINT nCount = m_pMenuItemArray.GetSize();
  419.  
  420.         if(nPosition >= 0 && nPosition < nCount)
  421.         {
  422.             pItem = (CDropMenuItem *)m_pMenuItemArray[nPosition];
  423.             m_pMenuItemArray.RemoveAt(nPosition, 1);
  424.         }
  425.     }
  426.     else if(nFlags == MF_BYCOMMAND)
  427.     {
  428.         int nCommandPosition = FindCommand(nPosition);
  429.         if(nCommandPosition != -1)
  430.         {
  431.             pItem = (CDropMenuItem *)m_pMenuItemArray[nCommandPosition];
  432.             m_pMenuItemArray.RemoveAt(nCommandPosition, 1);
  433.         }
  434.     }
  435.  
  436.     if(pItem != m_pOverflowMenuItem)
  437.         delete pItem;
  438.  
  439. }
  440.  
  441. void CDropMenu::CreateOverflowMenuItem(UINT nCommand, CString szText, HBITMAP hUnselectedBitmap, HBITMAP hSelectedBitmap)
  442. {
  443.  
  444.     m_pOverflowMenuItem = new CDropMenuItem(MF_STRING, szText, nCommand, hUnselectedBitmap,
  445.                                             hSelectedBitmap, FALSE); 
  446.  
  447. }
  448.  
  449. LRESULT CALLBACK EXPORT HandleMouseEvents(int nCode, WPARAM wParam, LPARAM lParam) 
  450. {
  451.     if(nCode < 0)
  452.         return CallNextHookEx(currentMouseHook, nCode, wParam, lParam); 
  453.  
  454.     MOUSEHOOKSTRUCT *mouseHook = (MOUSEHOOKSTRUCT*)lParam;
  455.  
  456.     if(currentMenu != NULL)
  457.     {
  458.         //only want to deactivate if point is not in our menu or in our parent or children
  459.         if(nCode >= 0 && (wParam == WM_NCLBUTTONDOWN || wParam == WM_LBUTTONDOWN ||
  460.                           ((wParam == WM_LBUTTONUP || wParam == WM_NCLBUTTONUP) && currentMenu->IsDragging())))
  461.         {
  462.             
  463.             if(currentMenu != NULL && !currentMenu->PointInMenus(mouseHook->pt))
  464.             {
  465.                 currentMenu->Deactivate();
  466.             }
  467.         }
  468.     }
  469.     return CallNextHookEx(currentMouseHook, nCode, wParam, lParam); 
  470.  
  471. }
  472.  
  473. LRESULT CALLBACK EXPORT HandleKeyboardEvents(int nCode, WPARAM wParam, LPARAM lParam)
  474. {
  475.     
  476.     if(nCode < 0)
  477.     {
  478.         return CallNextHookEx(currentKeyboardHook, nCode, wParam, lParam);
  479.     }
  480.  
  481.     //only want to handle on a keydown
  482.     if(!(HIWORD(lParam) & KF_UP) && currentMenu != NULL)
  483.     {
  484.         currentMenu->HandleKeystroke(wParam);
  485.     }
  486.  
  487.     //we don't want to pass the keyboard event onwards
  488.     return 1;
  489. }
  490.  
  491. LRESULT CALLBACK EXPORT FrameProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  492. {
  493.     if(uMsg == WM_ACTIVATE)
  494.     {
  495.         if(LOWORD(wParam) == WA_INACTIVE && !currentMenu->IsShowingMenu((HWND)lParam))
  496.         {
  497.             currentMenu->Deactivate();
  498.         }
  499.  
  500.     }
  501.  
  502.     return CallWindowProc((WNDPROC)oldFrameProc, hWnd, uMsg, wParam, lParam);    
  503. }
  504.  
  505. // 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
  506. void CDropMenu::TrackDropMenu(CWnd *pParent, int x, int y, BOOL bDragging, CDropMenuDropTarget *pDropTarget, BOOL bShowFeedback,  int oppX, int oppY)
  507. {
  508.     if(!m_bShowing)
  509.     {
  510.         m_bShowFeedback = bShowFeedback;
  511.         m_bDragging = bDragging;
  512.         m_bHasSubMenu = FALSE;
  513.         m_pParent = pParent;
  514.         m_bFirstPaint =TRUE;
  515.         if(m_pMenuParent != NULL)
  516.         {
  517.             m_bRight = m_pMenuParent->m_bRight;
  518.         }
  519.  
  520.         //    m_bNotDestroyed = TRUE;
  521.         if(GetMenuItemCount() == 0)
  522.             GetTopLevelParent()->SendMessage(DM_FILLINMENU, 0, (LPARAM)this);
  523.  
  524.  
  525.  
  526.         m_bShowing = TRUE;
  527.         m_bAboutToShow = FALSE;
  528.         m_bIsOpen = TRUE;
  529.  
  530.         if(m_hSubmenuUnselectedBitmap == NULL)
  531.             m_hSubmenuUnselectedBitmap = ::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_SUBMENU1));
  532.  
  533.         if(m_hSubmenuSelectedBitmap == NULL)
  534.             m_hSubmenuSelectedBitmap = ::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_SUBMENU2));
  535.  
  536.         if(!::IsWindow(m_hWnd))
  537.         {
  538.             CWnd::CreateEx(0, AfxRegisterWndClass( CS_SAVEBITS ,
  539.                     theApp.LoadStandardCursor(IDC_ARROW), 
  540.                     (HBRUSH)(COLOR_MENU + 1), NULL),"test", WS_POPUP , 0, 0, 0,0, pParent->m_hWnd, NULL, NULL);
  541.  
  542.             SetDropMenuTarget(pDropTarget);
  543.         }
  544.         else delete pDropTarget;
  545.  
  546.         CSize size = GetMenuSize();
  547.  
  548.         CPoint startPoint = FindStartPoint(x, y, size, oppX, oppY);
  549.  
  550.         if(m_pMenuParent == NULL)
  551.         {
  552.             if(gLastMenu !=NULL)
  553.             {
  554.                 //we only want one drop menu open at a time, so close the last one if one is
  555.                 //still open. This will probably only happen on drag and drop.
  556.                 gLastMenu->Deactivate();
  557.             }
  558.  
  559.             oldFrameProc =  SetWindowLong(GetParentFrame()->m_hWnd, GWL_WNDPROC, (LONG)FrameProc);
  560. #ifdef _WIN32
  561.             currentMouseHook = SetWindowsHookEx(WH_MOUSE, HandleMouseEvents, AfxGetInstanceHandle(), GetCurrentThreadId());
  562.             currentKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, HandleKeyboardEvents, AfxGetInstanceHandle(),GetCurrentThreadId());
  563. #else
  564.             currentMouseHook = SetWindowsHookEx(WH_MOUSE, HandleMouseEvents, AfxGetInstanceHandle(), GetCurrentTask());
  565.             currentKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, HandleKeyboardEvents, AfxGetInstanceHandle(), GetCurrentTask());
  566. #endif
  567.             gLastMenu = this;
  568.         }
  569.  
  570.         SetWindowPos(NULL, startPoint.x, startPoint.y, 
  571.                      size.cx + (2*MENU_BORDER_SIZE), size.cy + (2 * MENU_BORDER_SIZE),
  572.                      SWP_NOZORDER  | SWP_NOACTIVATE);
  573.         ShowWindow(SW_SHOWNA);
  574.         //SetActiveWindow();
  575.     }
  576.     else delete pDropTarget;
  577. }
  578.  
  579. void CDropMenu::SetDropMenuTarget(CDropMenuDropTarget *pDropTarget)
  580. {
  581.  
  582.     m_pDropTarget = pDropTarget;
  583.  
  584.     DragAcceptFiles();
  585.     m_pDropTarget->Register(this);
  586.  
  587. }
  588.  
  589. BOOL CDropMenu::PointInMenus(POINT pt)
  590. {
  591.  
  592.     CRect rect;
  593.  
  594.     GetWindowRect(&rect);
  595.  
  596.     if(rect.PtInRect(pt))
  597.     {
  598.         return TRUE;
  599.     }
  600.     else
  601.     {
  602.         CDropMenu *menu = m_pMenuParent;
  603.  
  604.         while(menu != NULL && IsWindow(menu->m_hWnd))
  605.         {
  606.             menu->GetWindowRect(&rect);
  607.             if(rect.PtInRect(pt))
  608.             {
  609.                 return TRUE;
  610.             }
  611.             menu=menu->m_pMenuParent;
  612.         }
  613.  
  614.         menu = m_pShowingSubmenu;
  615.  
  616.         while(menu !=NULL && IsWindow(menu->m_hWnd))
  617.         {
  618.             menu->GetWindowRect(&rect);
  619.             if(rect.PtInRect(pt))
  620.             {
  621.                 return TRUE;
  622.             }
  623.             menu = menu->m_pShowingSubmenu;
  624.         }
  625.  
  626.     }
  627.     return FALSE;
  628. }
  629.  
  630. void CDropMenu::HandleKeystroke(WPARAM key)
  631. {
  632.     //Make all open windows know that we are in keyboard mode still
  633.     CDropMenu *pMenu = this;
  634.  
  635.     while(pMenu != NULL)
  636.     {
  637.         pMenu->m_bMouseUsed = FALSE;
  638.         pMenu = pMenu->m_pMenuParent;
  639.     }
  640.  
  641.     if(key == VK_ESCAPE || !m_bDragging)
  642.     {
  643.         switch(key)
  644.         {
  645.             case VK_ESCAPE:  currentMenu->Deactivate();
  646.                              break;
  647.             case VK_UP:         HandleUpArrow();
  648.                              break;
  649.             case VK_DOWN:     HandleDownArrow();
  650.                              break;
  651.             case VK_LEFT:     HandleLeftArrow();
  652.                              break;
  653.             case VK_RIGHT:     HandleRightArrow();
  654.                              break;
  655.             case VK_RETURN:  HandleReturnKey();
  656.         }
  657.     }
  658.  
  659. }
  660.  
  661. BOOL CDropMenu::OnCommand( WPARAM wParam, LPARAM lParam )
  662. {
  663.     ShowWindow(SW_HIDE);
  664.  
  665.     m_pParent->SendMessage(WM_COMMAND, wParam, lParam);
  666.  
  667.     return 1;
  668. }
  669.  
  670. BOOL CDropMenu::DestroyWindow(void)
  671. {
  672.     return(CWnd::DestroyWindow());
  673. }
  674.  
  675.  
  676. void CDropMenu::Deactivate(void)
  677. {
  678.         if(m_pMenuParent != NULL)
  679.         {
  680.             m_pMenuParent->SendMessage(DM_CLOSEALLMENUS, 0, 0);
  681.  
  682.         }
  683.         ShowWindow(SW_HIDE);
  684. }
  685.  
  686. BOOL CDropMenu::ShowWindow( int nCmdShow )
  687. {
  688.     if(nCmdShow == SW_HIDE && IsWindowVisible())
  689.     {
  690.         m_bIsOpen = FALSE;
  691.         if(m_pMenuParent != NULL)
  692.         {
  693.             //Turn on parent's focus timer
  694.             m_pMenuParent->m_nHaveFocusTimer = m_pMenuParent->SetTimer(IDT_HAVE_FOCUS, HAVE_FOCUS_DELAY, NULL);
  695.             // we only want the parent to be the current menu if the parent hasn't already
  696.             // brought up a new submenu.  Otherwise we have timing conflicts
  697.             if(m_pMenuParent->m_bShowing && 
  698.               (m_pMenuParent->m_pShowingSubmenu == this || m_pMenuParent->m_pShowingSubmenu == NULL))
  699.             {
  700.                 currentMenu = m_pMenuParent;
  701.             }
  702.  
  703.         }
  704.         KillTimer(m_nHaveFocusTimer);
  705.  
  706.         //if this is the top level parent then the hook needs to be destroyed
  707.         if(m_pMenuParent == NULL)
  708.         {
  709.             UnhookWindowsHookEx(currentMouseHook);
  710.             UnhookWindowsHookEx(currentKeyboardHook);
  711.             SetWindowLong(GetParentFrame()->m_hWnd, GWL_WNDPROC, (LONG)oldFrameProc);
  712.             currentMenu = NULL;
  713.             gLastMenu = NULL;
  714.         }
  715.         OnTimer(IDT_SUBMENU_OFF);
  716.         m_bShowing = FALSE;
  717.         m_nSelectedItem = -1;
  718.  
  719.         if(m_pShowingSubmenu)
  720.         {
  721.             //check to see if it's valid first because we may
  722.             //want to deactivate before it was created due to
  723.             //the timer never having been called to create it
  724.             if(IsWindow(m_pShowingSubmenu->m_hWnd))
  725.             {
  726.                 m_pShowingSubmenu->ShowWindow(SW_HIDE);
  727.             }
  728.             m_pShowingSubmenu = NULL;
  729.         }
  730.         if(m_pMenuParent == NULL)
  731.         {
  732.             m_pParent->PostMessage(DM_MENUCLOSED, 0, 0);
  733.  
  734.         }
  735.     }
  736.     else if(nCmdShow == SW_SHOWNA)
  737.     {
  738.         if(m_pMenuParent != NULL)
  739.         {
  740.             //since we are being shown, turn off parent's focus timer
  741.             m_pMenuParent->KillTimer(m_nHaveFocusTimer);
  742.         }
  743.  
  744.         m_nHaveFocusTimer = SetTimer(IDT_HAVE_FOCUS, HAVE_FOCUS_DELAY, NULL);
  745.         currentMenu = this;
  746.  
  747.     }
  748.  
  749.     return CWnd::ShowWindow(nCmdShow);
  750.  
  751.  
  752. }
  753.  
  754. void CDropMenu::PostNcDestroy(void )
  755. {
  756.     CWnd::PostNcDestroy();
  757. }
  758.  
  759. void CDropMenu::DestroyDropMenu(void)
  760. {
  761.     DestroyWindow();
  762.  
  763. }
  764. //////////////////////////////////////////////////////////////////////////
  765. //                    Messages for CDropMenu
  766. //////////////////////////////////////////////////////////////////////////
  767.  
  768. BEGIN_MESSAGE_MAP(CDropMenu, CWnd)
  769.     //{{AFX_MSG_MAP(CWnd)
  770.      ON_WM_PAINT()
  771.     ON_WM_LBUTTONDOWN()
  772.     ON_WM_LBUTTONUP()
  773.     ON_WM_RBUTTONDOWN()
  774.     ON_WM_MOUSEMOVE()
  775.     ON_WM_NCHITTEST()
  776.     ON_MESSAGE(DT_DROPOCCURRED, OnDropTargetDropOccurred)
  777.     ON_MESSAGE(DT_DRAGGINGOCCURRED, OnDropTargetDraggingOccurred)
  778.     ON_MESSAGE(DM_CLOSEALLMENUS, OnCloseAllMenus)
  779.     ON_WM_TIMER()
  780.     ON_WM_ACTIVATE()
  781.     ON_WM_DESTROY()
  782.     //}}AFX_MSG_MAP
  783.  
  784. END_MESSAGE_MAP()
  785.  
  786. void CDropMenu::OnPaint()
  787. {
  788.     CRect updateRect;
  789.  
  790.  
  791.     if(GetUpdateRect(&updateRect))
  792.     {
  793.         CPaintDC dcPaint(this);    // device context for painting
  794.  
  795.         CRect rcClient;
  796.         
  797.         GetClientRect(&rcClient);
  798.  
  799.         HDC hSrcDC = dcPaint.m_hDC;
  800.  
  801.         HDC hMemDC = ::CreateCompatibleDC(hSrcDC);
  802.         HBITMAP hbmMem = CreateCompatibleBitmap(hSrcDC,
  803.                                     rcClient.Width(),
  804.                                     rcClient.Height());
  805.  
  806.         //
  807.         // Select the bitmap into the off-screen DC.
  808.         //
  809.         COLORREF rgbOldBk = ::SetBkColor(hMemDC, sysInfo.m_clrMenu);
  810.  
  811.         HBITMAP hbmOld = (HBITMAP)::SelectObject(hMemDC, hbmMem);
  812.  
  813.         HBRUSH brFace = sysInfo.m_hbrMenu;
  814.         ::FillRect(hMemDC, rcClient, brFace);
  815.         
  816.         CRect intersectRect;
  817.         int nNumColumns = m_pColumnArray.GetSize();
  818.         int nFirstItem, nLastItem;
  819.         CDropMenuColumn *pColumn;
  820.         int nStartX = 0;
  821.  
  822.         for(int j = 0; j <=m_nLastVisibleColumn; j++)
  823.         {
  824.             int y = MENU_BORDER_SIZE;
  825.  
  826.             pColumn = (CDropMenuColumn*)m_pColumnArray[j];
  827.  
  828.             nFirstItem = pColumn->GetFirstItem();
  829.             nLastItem = pColumn->GetLastItem();
  830.  
  831.             for(int i = nFirstItem; i <= nLastItem; i++)
  832.             {
  833.                 CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[i];
  834.  
  835.                 if(m_bShowFeedback)
  836.                     y += SPACE_BETWEEN_ITEMS;
  837.  
  838.                 if(item->IsSeparator())
  839.                 {
  840.                     CRect separatorRect(nStartX + MENU_BORDER_SIZE, y, nStartX + MENU_BORDER_SIZE + pColumn->GetWidth(), y + SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT);
  841.  
  842.                     if(intersectRect.IntersectRect(separatorRect, updateRect))
  843.                     {
  844.                         DrawSeparator(hMemDC, separatorRect.left, y, separatorRect.Width() - (2 * MENU_BORDER_SIZE));
  845.                     }
  846.                     y += SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT;
  847.                 }
  848.                 else
  849.                 {
  850.                     CRect itemRect;
  851.  
  852.                     item->GetMenuItemRect(itemRect);
  853.                     if(intersectRect.IntersectRect(itemRect, updateRect))
  854.                     {
  855.                         CRect rect(nStartX + MENU_BORDER_SIZE, y ,nStartX + MENU_BORDER_SIZE + pColumn->GetWidth(), y + itemRect.Height()); 
  856.                         
  857.                         DrawItem(pColumn, item, rect, hMemDC, i == m_nSelectedItem, 
  858.                                 (i == m_nSelectedItem) ? m_eSelectedItemSelType : eNONE);
  859.                     }
  860.  
  861.                     y += itemRect.Height();
  862.                 }
  863.  
  864.             }
  865.             nStartX += pColumn->GetWidth();
  866.             if(j < m_nLastVisibleColumn)
  867.             {
  868.                 DrawVerticalSeparator(hMemDC, nStartX, MENU_BORDER_SIZE, rcClient.Height() - (2 * MENU_BORDER_SIZE));
  869.                 nStartX += SEPARATOR_WIDTH;
  870.             }
  871.         }
  872.         // need to make sure selection type shows up
  873.         if(m_nSelectedItem != NOSELECTION && m_eSelectedItemSelType != eNONE)
  874.         {
  875.             CRect itemRect;
  876.             CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[m_nSelectedItem];
  877.             
  878.             if(!item->IsSeparator())
  879.             {
  880.                 item->GetMenuItemRect(itemRect);
  881.  
  882.                 CRect rect(itemRect.left, itemRect.top ,itemRect.left + ((CDropMenuColumn*)m_pColumnArray[item->GetColumn()])->GetWidth(), itemRect.top + itemRect.Height()); 
  883.                         
  884.                 DrawItem((CDropMenuColumn*)m_pColumnArray[item->GetColumn()], item, rect, hMemDC, TRUE, m_eSelectedItemSelType );
  885.             }
  886.  
  887.         }
  888.         DrawBorders(hMemDC, rcClient);
  889.  
  890.         ::BitBlt(hSrcDC, 0, 0, rcClient.Width(), rcClient.Height(), hMemDC, 0, 0,
  891.                     SRCCOPY);
  892.  
  893.         ::SetBkColor(hMemDC, rgbOldBk);
  894.  
  895.         ::SelectObject(hMemDC, hbmOld);
  896.         ::DeleteObject(hbmMem);
  897.  
  898.         ::DeleteDC(hMemDC);
  899.     }
  900.     if(m_bFirstPaint)
  901.     {
  902.         MSG msg;
  903.  
  904.         PeekMessage(&msg, this->m_hWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE);
  905.         m_bFirstPaint = FALSE;
  906.     }
  907.  
  908. }
  909.  
  910. void CDropMenu::OnMouseMove(UINT nFlags, CPoint point)
  911. {
  912.     if(!m_bMouseUsed)
  913.     {
  914.         m_bMouseUsed = TRUE;
  915.         return;
  916.     }
  917.  
  918.     m_bMouseUsed = TRUE;
  919.     
  920.     KeepMenuAroundIfClosing();
  921.     ChangeSelection(point);
  922. }
  923.  
  924. UINT CDropMenu::OnNcHitTest( CPoint point )
  925. {
  926.     return CWnd::OnNcHitTest(point);
  927. }
  928.  
  929. void CDropMenu::OnLButtonUp(UINT nFlags, CPoint point)
  930. {
  931.     //if we haven't painted yet then the user hasn't had a chance to mouse up.
  932.     if(m_bFirstPaint || m_nSelectedItem == NOSELECTION)
  933.         return;
  934.  
  935.     MenuSelectionType eSelType;
  936.     int nSelection = FindSelection(point, eSelType);
  937.  
  938.     if(nSelection != -1)
  939.     {
  940.         CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[nSelection];
  941.         
  942.         if(!pItem->IsSubMenu())
  943.         {
  944.             SendMessage(WM_COMMAND, (WPARAM) pItem->GetCommand(), (LPARAM) m_hWnd);
  945.         }
  946.     }
  947. }
  948.  
  949. void CDropMenu::OnLButtonDown(UINT nFlags, CPoint point)
  950. {
  951.     // we want our frame to stay the active window.  Causes flicker
  952.     GetParentFrame()->SetActiveWindow();
  953. }
  954.  
  955. void CDropMenu::OnRButtonDown(UINT nFlags, CPoint point)
  956. {
  957.     // we want our frame to stay the active window.  Causes flicker
  958.     GetParentFrame()->SetActiveWindow();
  959. }
  960.  
  961.  
  962. LRESULT CDropMenu::OnDropTargetDraggingOccurred(WPARAM wParam, LPARAM lParam)
  963. {
  964.     if (wParam == 0)
  965.     {
  966.         CDropMenuDragData* pData = (CDropMenuDragData*)lParam;
  967.         KeepMenuAroundIfClosing();
  968.         ChangeSelection(pData->m_PointHit);
  969.  
  970.         // Ok, now we need to figure out what our drag feedback should be.
  971.         // note that point is already in our coordinates since we got it from our drop target
  972.         MenuSelectionType eSelType;
  973.         int nSelection = FindSelection(pData->m_PointHit, eSelType);
  974.  
  975.         if(nSelection != -1)
  976.         {
  977.             CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[nSelection];
  978.             pData->m_nCommand = pItem->GetCommand();
  979.             pData->eSelType = eSelType;
  980.             m_pParent->SendMessage(DT_DRAGGINGOCCURRED, 1, lParam);
  981.         }
  982.     }
  983.     else 
  984.     {
  985.         m_pParent->SendMessage(DT_DRAGGINGOCCURRED, 1, lParam);
  986.     }
  987.  
  988.     return 1;
  989. }
  990.  
  991. LRESULT CDropMenu::OnDropTargetDropOccurred(WPARAM wParam, LPARAM lParam)
  992. {
  993.     if (wParam == 0)
  994.     {
  995.         CDropMenuDragData* pData = (CDropMenuDragData*)lParam;
  996.         
  997.         // Ok, now we need to figure out what our drag feedback should be.
  998.         // note that point is already in our coordinates since we got it from our drop target
  999.         MenuSelectionType eSelType;
  1000.         int nSelection = FindSelection(pData->m_PointHit, eSelType);
  1001.  
  1002.         if(nSelection != -1)
  1003.         {
  1004.             CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[nSelection];
  1005.             pData->m_nCommand = pItem->GetCommand();
  1006.             pData->eSelType = eSelType;
  1007.             m_pParent->SendMessage(DT_DROPOCCURRED, 1, lParam);
  1008.         }
  1009.     }
  1010.     else 
  1011.     {
  1012.         m_pParent->SendMessage(DT_DROPOCCURRED, 1, lParam);
  1013.     }
  1014.  
  1015.     ShowWindow(SW_HIDE);
  1016.  
  1017.     return 1;
  1018. }
  1019.  
  1020. LRESULT CDropMenu::OnCloseAllMenus(WPARAM wParam, LPARAM lParam)
  1021. {
  1022.  
  1023.     if(m_pMenuParent != NULL)
  1024.     {
  1025.         m_pMenuParent->PostMessage(DM_CLOSEALLMENUS, wParam, lParam);
  1026.     }
  1027.  
  1028.     ShowWindow(SW_HIDE);
  1029.  
  1030.     return 1;
  1031. }
  1032.  
  1033.  
  1034. void CDropMenu::OnTimer( UINT  nIDEvent )
  1035. {
  1036.     if(nIDEvent == IDT_SUBMENU_OFF)
  1037.     {
  1038.         KillTimer(m_nUnselectTimer);
  1039.  
  1040.         if(m_pUnselectedItem != NULL && m_pUnselectedItem->GetSubMenu()->m_bShowing==FALSE)
  1041.         {
  1042.             m_pUnselectedItem->GetSubMenu()->ShowWindow(SW_HIDE);
  1043.         }
  1044.         m_pUnselectedItem = NULL;
  1045.         
  1046.     }
  1047.     else if(nIDEvent == IDT_SUBMENU_ON)
  1048.     {
  1049.         KillTimer(m_nSelectTimer);
  1050.  
  1051.         if(m_bShowing)
  1052.         {
  1053.             
  1054.             if(m_pSelectedItem)
  1055.             {
  1056.                 CDropMenu *pSubMenu = m_pSelectedItem->GetSubMenu();
  1057.                 if(pSubMenu && pSubMenu->m_bAboutToShow)
  1058.                     ShowSubmenu(m_pSelectedItem);
  1059.                 else
  1060.                     m_pSelectedItem = NULL;
  1061.  
  1062.             }
  1063.             m_nSelectTimer = 0;
  1064.         }
  1065.     }
  1066.     else if(nIDEvent == IDT_HAVE_FOCUS)
  1067.     {
  1068.         if(m_nSelectedItem != -1 && m_bMouseUsed)
  1069.         {
  1070.             CDropMenuItem *pItem = (CDropMenuItem*)m_pMenuItemArray[m_nSelectedItem];
  1071.             
  1072.             CPoint point;
  1073.  
  1074.             GetCursorPos(&point);
  1075.  
  1076.             ScreenToClient(&point);
  1077.  
  1078.             CRect rcClient, rect;
  1079.             GetClientRect(rcClient);
  1080.  
  1081.             if (!rcClient.PtInRect(point) && m_pShowingSubmenu == NULL )
  1082.             {
  1083.                 pItem->GetMenuItemRect(rect);
  1084.                 rect.left = 0;
  1085.                 rect.right = rcClient.right;
  1086.  
  1087.                 m_nSelectedItem = -1;
  1088.  
  1089.                 InvalidateRect(&rect);
  1090.             }
  1091.         }
  1092.     }
  1093.  
  1094. }
  1095.  
  1096. void CDropMenu::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
  1097. {
  1098.     if(nState == WA_ACTIVE)
  1099.     {
  1100.         pWndOther->SetActiveWindow();
  1101.     }
  1102. }
  1103.  
  1104. void CDropMenu::OnDestroy( )
  1105. {
  1106.     m_pDropTarget->Revoke();
  1107.     CWnd::OnDestroy();
  1108. }
  1109.   
  1110.  
  1111. //////////////////////////////////////////////////////////////////////////
  1112. //                    Helpers for CDropMenu
  1113. //////////////////////////////////////////////////////////////////////////
  1114.  
  1115. void CDropMenu::DrawSeparator(HDC hDC, int x, int y, int nWidth)
  1116. {
  1117.  
  1118.     HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  1119.     HPEN pOldPen = (HPEN)::SelectObject(hDC, pen);
  1120.  
  1121. #if defined (WIN32)
  1122.     ::MoveToEx(hDC, x, y + SEPARATOR_SPACE, NULL);
  1123. #else
  1124.     ::MoveTo(hDC, x, y + SEPARATOR_SPACE);
  1125. #endif
  1126.     ::LineTo(hDC, x + nWidth, y + SEPARATOR_SPACE);
  1127.  
  1128.     ::SelectObject(hDC, pOldPen);
  1129.     ::DeleteObject(pen);
  1130.    
  1131. #if defined(WIN32)   
  1132.     pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT));
  1133. #else
  1134.     pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  1135. #endif
  1136.  
  1137.     pOldPen = (HPEN)::SelectObject(hDC, pen);
  1138.  
  1139. #if defined(WIN32)
  1140.     ::MoveToEx(hDC, x, y + SEPARATOR_SPACE + 1, NULL);
  1141. #else
  1142.     ::MoveTo(hDC, x, y + SEPARATOR_SPACE + 1);
  1143. #endif
  1144.  
  1145.     ::LineTo(hDC, x + nWidth, y + SEPARATOR_SPACE + 1);
  1146.  
  1147.     ::SelectObject(hDC, pOldPen);
  1148.     ::DeleteObject(pen);
  1149.  
  1150. }
  1151.  
  1152. void CDropMenu::DrawVerticalSeparator(HDC hDC, int x, int y, int nHeight)
  1153. {
  1154.     HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  1155.     HPEN pOldPen = (HPEN)::SelectObject(hDC, pen);
  1156.  
  1157. #if defined (WIN32)
  1158.     ::MoveToEx(hDC, x + SEPARATOR_SPACE, y, NULL);
  1159. #else
  1160.     ::MoveTo(hDC, x + SEPARATOR_SPACE, y);
  1161. #endif
  1162.     ::LineTo(hDC, x + SEPARATOR_SPACE, y + nHeight);
  1163.  
  1164.     ::SelectObject(hDC, pOldPen);
  1165.     ::DeleteObject(pen);
  1166.    
  1167. #if defined(WIN32)   
  1168.     pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT));
  1169. #else
  1170.     pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  1171. #endif
  1172.  
  1173.     pOldPen = (HPEN)::SelectObject(hDC, pen);
  1174.  
  1175. #if defined(WIN32)
  1176.     ::MoveToEx(hDC, x + SEPARATOR_SPACE + 1, y, NULL);
  1177. #else
  1178.     ::MoveTo(hDC, x + SEPARATOR_SPACE + 1, y);
  1179. #endif
  1180.  
  1181.     ::LineTo(hDC, x + SEPARATOR_SPACE + 1, y + nHeight);
  1182.  
  1183.     ::SelectObject(hDC, pOldPen);
  1184.     ::DeleteObject(pen);
  1185. }
  1186.  
  1187. void CDropMenu::DrawBorders(HDC hDC, CRect rcClient)
  1188. {
  1189.  
  1190.     //Draw inner rect which is all BTN_FACE
  1191.     RECT rect;
  1192.     ::SetRect(&rect, rcClient.left + 2, rcClient.top + 2, rcClient.right - 2, rcClient.bottom - 2);
  1193.     ::FrameRect(hDC, &rect, sysInfo.m_hbrBtnFace);
  1194.   
  1195. #if defined(WIN32)
  1196.     HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT));
  1197. #else
  1198.     HPEN pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
  1199. #endif
  1200.  
  1201.     HPEN pOldPen = (HPEN)::SelectObject(hDC, pen);
  1202.  
  1203. #if defined(WIN32)
  1204.     ::MoveToEx(hDC, rcClient.left + 1, rcClient.bottom - 1, NULL);
  1205. #else
  1206.     ::MoveTo(hDC, rcClient.left + 1, rcClient.bottom - 1);
  1207. #endif
  1208.     ::LineTo(hDC, rcClient.left + 1, rcClient.top + 1);
  1209.     ::LineTo(hDC, rcClient.right - 1, rcClient.top + 1);
  1210.  
  1211.     ::SelectObject(hDC, pOldPen);
  1212.     ::DeleteObject(pen);
  1213.  
  1214.     pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE));
  1215.  
  1216.     pOldPen = (HPEN)::SelectObject(hDC, pen);
  1217.  
  1218. #if defined(WIN32)
  1219.     ::MoveToEx(hDC, rcClient.left, rcClient.bottom - 1, NULL);
  1220. #else
  1221.     ::MoveTo(hDC, rcClient.left, rcClient.bottom - 1);
  1222. #endif
  1223.  
  1224.     ::LineTo(hDC, rcClient.left, rcClient.top);
  1225.     ::LineTo(hDC, rcClient.right - 1, rcClient.top);
  1226.  
  1227.     ::SelectObject(hDC, pOldPen);
  1228.     ::DeleteObject(pen);
  1229.  
  1230.     pen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
  1231.  
  1232.     pOldPen = (HPEN)::SelectObject(hDC, pen);
  1233.  
  1234. #if defined(WIN32)
  1235.     ::MoveToEx(hDC, rcClient.right - 1, rcClient.top, NULL);
  1236. #else
  1237.     ::MoveTo(hDC, rcClient.right - 1, rcClient.top);
  1238. #endif
  1239.  
  1240.     ::LineTo(hDC, rcClient.right - 1 , rcClient.bottom - 1);
  1241.     ::LineTo(hDC, rcClient.left - 1, rcClient.bottom - 1);
  1242.  
  1243.     ::SelectObject(hDC, pOldPen);
  1244.     ::DeleteObject(pen);
  1245.  
  1246.     pen = ::CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
  1247.  
  1248.     pOldPen = (HPEN)::SelectObject(hDC, pen);
  1249.  
  1250. #if defined(WIN32)
  1251.     ::MoveToEx(hDC, rcClient.right - 2, rcClient.top + 1, NULL);
  1252. #else
  1253.     ::MoveTo(hDC, rcClient.right - 2, rcClient.top + 1);
  1254. #endif
  1255.     ::LineTo(hDC, rcClient.right - 2, rcClient.bottom - 2);
  1256.     ::LineTo(hDC, rcClient.left, rcClient.bottom -2);
  1257.  
  1258.     ::SelectObject(hDC, pOldPen);
  1259.     ::DeleteObject(pen);
  1260.  
  1261. }
  1262.  
  1263.  
  1264. void CDropMenu::DrawItem(CDropMenuColumn *pColumn, CDropMenuItem *pItem, CRect rc, HDC hDC, BOOL bIsSelected, MenuSelectionType eSelType)
  1265. {
  1266.     // Extract the goods from lpDI
  1267.     ASSERT(pItem != NULL);
  1268.     
  1269.     // Get proper colors and paint the background first
  1270.     COLORREF rgbTxt;
  1271.     HBRUSH brFace;
  1272.     COLORREF rgbOldBk;
  1273.  
  1274.     if (bIsSelected && (eSelType == eNONE || eSelType == eON))
  1275.     {
  1276.         rgbOldBk = ::SetBkColor(hDC, sysInfo.m_clrHighlight);
  1277.  
  1278.         brFace = sysInfo.m_hbrHighlight;
  1279.         rgbTxt = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  1280.     }
  1281.     else
  1282.     {
  1283.         rgbOldBk = ::SetBkColor(hDC, sysInfo.m_clrMenu);
  1284.  
  1285.         brFace = sysInfo.m_hbrMenu;
  1286.         rgbTxt = ::GetSysColor(COLOR_MENUTEXT);
  1287.     }
  1288.     
  1289.     ::FillRect(hDC, rc, brFace);
  1290.     
  1291.     if(eSelType == eABOVE)
  1292.         DrawSeparator(hDC, rc.left, rc.top - SEPARATOR_SPACE - 2, rc.Width() / 2);
  1293.     else if(eSelType == eBELOW)
  1294.         DrawSeparator(hDC, rc.left, rc.bottom - SEPARATOR_HEIGHT - SEPARATOR_SPACE + 2, rc.Width() / 2);
  1295.  
  1296.     CDC *pDC = CDC::FromHandle(hDC);
  1297.     // Now paint the bitmap, left side of menu
  1298.     rc.left += nIMG_SPACE;
  1299.     CSize imgSize = DrawImage(pDC, rc, pColumn, pItem, bIsSelected, eSelType);
  1300.     
  1301.     // And now the text
  1302.     HFONT MenuFont;
  1303. #if defined(WIN32)
  1304.     NONCLIENTMETRICS ncm;
  1305.     ncm.cbSize = sizeof(NONCLIENTMETRICS);
  1306.     if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0))
  1307.     {
  1308.         MenuFont = theApp.CreateAppFont( ncm.lfMenuFont );
  1309.     }
  1310.     else
  1311.     {
  1312.         MenuFont = (HFONT)::GetStockObject(SYSTEM_FONT);
  1313.     }
  1314. #else    // Win16
  1315.     MenuFont = (HFONT)::GetStockObject(SYSTEM_FONT);
  1316. #endif    // WIN32
  1317.  
  1318.     HFONT pOldFont = (HFONT)::SelectObject(hDC, MenuFont);
  1319.     int nOldBkMode = ::SetBkMode(hDC, TRANSPARENT);
  1320.     COLORREF rgbOldTxt = ::SetTextColor(hDC, rgbTxt);
  1321.  
  1322.     //Figure out where text should go in relation to bitmaps
  1323.     rc.left += imgSize.cx + nIMG_SPACE + (pColumn->GetWidestImage()-imgSize.cx);
  1324.  
  1325.     //Deal with the case where there is a tab for an accelerator
  1326.     CString label=pItem->GetText();
  1327.  
  1328.     DrawText(hDC, (const char*)label, -1, &rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE |DT_EXPANDTABS);
  1329.     
  1330.     ::SetBkMode(hDC, nOldBkMode);
  1331.     ::SetTextColor(hDC, rgbOldTxt);
  1332.     ::SelectObject(hDC, pOldFont);
  1333.     
  1334. #if defined(WIN32)
  1335.     if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0))
  1336.     {
  1337.         theApp.ReleaseAppFont(MenuFont);
  1338.     }
  1339. #endif
  1340.  
  1341.     // Draw submenu bitmap
  1342.     if(pItem->IsSubMenu() && !pItem->IsEmpty())
  1343.     {
  1344.         HBITMAP pSubmenuBitmap;
  1345.  
  1346.         if(bIsSelected && (eSelType == eNONE || eSelType == eON))
  1347.         {
  1348.             pSubmenuBitmap = m_hSubmenuSelectedBitmap;
  1349.         }
  1350.         else
  1351.         {
  1352.             pSubmenuBitmap = m_hSubmenuUnselectedBitmap;
  1353.         }
  1354.  
  1355.         // Create a scratch DC and select our bitmap into it.
  1356.         HDC pBmpDC = CreateCompatibleDC(hDC);
  1357.         HBITMAP pOldBmp = (HBITMAP)::SelectObject(pBmpDC, pSubmenuBitmap);
  1358.             
  1359.         // Center the image horizontally and vertically
  1360.         CPoint ptDst(rc.right - SUBMENU_WIDTH - SUBMENU_RIGHT_MARGIN, rc.top +
  1361.             (((rc.Height() - SUBMENU_HEIGHT) + 1) / 2));
  1362.             
  1363.         // Call the handy transparent blit function to paint the bitmap over
  1364.         // the background.
  1365.         ::FEU_TransBlt(hDC, ptDst.x, ptDst.y,SUBMENU_WIDTH, SUBMENU_HEIGHT,
  1366.                        pBmpDC, 0, 0, WFE_GetUIPalette(GetParentFrame()),RGB(255, 0, 255) );
  1367.         
  1368.         // Cleanup
  1369.         ::SelectObject(pBmpDC, pOldBmp);
  1370.         ::DeleteDC(pBmpDC);
  1371.  
  1372.     }
  1373.     ::SetBkColor(hDC, rgbOldBk);
  1374.  
  1375. } // END OF    FUNCTION CDropMenu::DrawItem()
  1376.  
  1377. /****************************************************************************
  1378. *
  1379. *    CDropMenu::DrawImage
  1380. *
  1381. *    PARAMETERS:
  1382. *        pDC        - pointer to device context to draw on
  1383. *        rect    - bounding rectangle to draw in
  1384. *        pItem    - pointer to CDropMenuItem that contains the data
  1385. *        bIsSelected - whether this item is selected
  1386. *
  1387. *    RETURNS:
  1388. *        width of the image, in pixels
  1389. *
  1390. *    DESCRIPTION:
  1391. *        Protected helper function for drawing the bitmap image in a menu
  1392. *        item. We return the size of the painted image so the caller can
  1393. *        position the menu text after the image.
  1394. *
  1395. ****************************************************************************/
  1396.  
  1397. CSize CDropMenu::DrawImage(CDC * pDC, const CRect & rect, CDropMenuColumn *pColumn, CDropMenuItem * pItem, BOOL bIsSelected,
  1398.                            MenuSelectionType eSelType)
  1399. {
  1400.     CSize sizeImg;
  1401.     
  1402.     sizeImg.cx = sizeImg.cy = 0;
  1403.  
  1404.     HBITMAP hImg = (bIsSelected && (!m_bShowFeedback || eSelType == eON || eSelType == eNONE)) ?
  1405.                       pItem->GetSelectedBitmap() : pItem->GetUnselectedBitmap();
  1406.     if (hImg != NULL)
  1407.     {
  1408.         // Get image dimensions
  1409.         BITMAP bmp;
  1410.         GetObject(hImg, sizeof(bmp), &bmp);
  1411.  
  1412.         sizeImg.cx = bmp.bmWidth;
  1413.         sizeImg.cy = bmp.bmHeight;
  1414.         
  1415.         // Create a scratch DC and select our bitmap into it.
  1416.         HDC hBmpDC = ::CreateCompatibleDC(pDC->m_hDC);
  1417.         HBITMAP hOldBmp = (HBITMAP)SelectObject(hBmpDC, hImg);
  1418.             
  1419.         // Center the image horizontally and vertically
  1420.         CPoint ptDst(rect.left + (pColumn->GetWidestImage() - sizeImg.cx)/2, rect.top +
  1421.             (((rect.Height() - sizeImg.cy) + 1) / 2));
  1422.             
  1423.         // Call the handy transparent blit function to paint the bitmap over
  1424.         // the background.
  1425.  
  1426.         if (pItem->GetIconType() == LOCAL_FILE)
  1427.         {
  1428.             HICON hIcon = pItem->GetLocalFileIcon();
  1429.             DrawIconEx(pDC->m_hDC, ptDst.x, ptDst.y, hIcon, 0, 0, 0, NULL, DI_NORMAL);
  1430.         }
  1431.         else if (pItem->GetIconType() == ARBITRARY_URL)
  1432.         {
  1433.             NSNavCenterImage* pImage = pItem->GetCustomIcon();
  1434.             HDC hDC = pDC->m_hDC;
  1435.             int left = ptDst.x;
  1436.             int top = ptDst.y;
  1437.             int imageWidth = 16;
  1438.             int imageHeight = 16;
  1439.             COLORREF bkColor = (bIsSelected && (!m_bShowFeedback || eSelType == eON || eSelType == eNONE)) ? 
  1440.                 GetSysColor(COLOR_HIGHLIGHT) : GetSysColor(COLOR_MENU);
  1441.             if (pImage && pImage->CompletelyLoaded()) 
  1442.             {
  1443.                 // Now we draw this bad boy.
  1444.                 if (pImage->m_BadImage) 
  1445.                 {         
  1446.                     // display broken icon.
  1447.                     HDC tempDC = ::CreateCompatibleDC(hDC);
  1448.                     HBITMAP hOldBmp = (HBITMAP)::SelectObject(tempDC,  NSNavCenterImage::m_hBadImageBitmap);
  1449.                     ::StretchBlt(hDC, 
  1450.                          left, top,
  1451.                         imageWidth, imageHeight, 
  1452.                         tempDC, 0, 0, 
  1453.                         imageWidth, imageHeight, SRCCOPY);
  1454.                     ::SelectObject(tempDC, hOldBmp);
  1455.                     ::DeleteDC(tempDC);
  1456.                 }
  1457.                 else if (pImage->bits ) 
  1458.                 {
  1459.                     // Center the image. 
  1460.                     long width = pImage->bmpInfo->bmiHeader.biWidth;
  1461.                     long height = pImage->bmpInfo->bmiHeader.biHeight;
  1462.                     int xoffset = (imageWidth-width)/2;
  1463.                     int yoffset = (imageHeight-height)/2;
  1464.                     if (xoffset < 0) xoffset = 0;
  1465.                     if (yoffset < 0) yoffset = 0;
  1466.                     if (width > imageWidth) width = imageWidth;
  1467.                     if (height > imageHeight) height = imageHeight;
  1468.  
  1469.                     HPALETTE hPal = WFE_GetUIPalette(NULL);
  1470.                     HPALETTE hOldPal = ::SelectPalette(hDC, hPal, TRUE);
  1471.  
  1472.                     ::RealizePalette(hDC);
  1473.                     
  1474.                     if (pImage->maskbits) 
  1475.                     {
  1476.                         WFE_StretchDIBitsWithMask(hDC, TRUE, NULL,
  1477.                             left+xoffset, top+xoffset,
  1478.                             width, height,
  1479.                             0, 0, width, height,
  1480.                             pImage->bits, pImage->bmpInfo,
  1481.                             pImage->maskbits, FALSE, bkColor);
  1482.                     }
  1483.                     else 
  1484.                     {
  1485.                         ::StretchDIBits(hDC,
  1486.                             left+xoffset, top+xoffset,
  1487.                             width, height,
  1488.                             0, 0, width, height, pImage->bits, pImage->bmpInfo, DIB_RGB_COLORS,
  1489.                             SRCCOPY);
  1490.                     }
  1491.  
  1492.                     ::SelectPalette(hDC, hOldPal, TRUE);
  1493.                 }
  1494.             }
  1495.         }
  1496.         else ::BitBlt(pDC->m_hDC, ptDst.x, ptDst.y, sizeImg.cx, sizeImg.cy, hBmpDC, 0, 0, SRCCOPY);
  1497.         
  1498.         // Cleanup
  1499.         ::SelectObject(hBmpDC, hOldBmp);
  1500.         ::DeleteDC(hBmpDC);
  1501.  
  1502.     }
  1503.  
  1504.     return(sizeImg);
  1505.     
  1506. } // END OF    FUNCTION CTreeMenu::DrawImage()
  1507.     
  1508. CDropMenuDropTarget *CDropMenu::GetDropMenuDropTarget(CWnd *pOwner)
  1509. {
  1510.  
  1511.     return new CDropMenuDropTarget(pOwner);
  1512. }
  1513.  
  1514. CSize CDropMenu::GetMenuSize(void)
  1515. {
  1516.     CSize totalSize, itemSize;
  1517.  
  1518.     int nColumnHeight = MENU_BORDER_SIZE, nColumnWidth = 0;
  1519.  
  1520.  
  1521.     CalculateItemDimensions();
  1522.     int nCount = m_pMenuItemArray.GetSize();
  1523.  
  1524.     int nScreenLeft = 0;
  1525.     int nScreenRight = GetSystemMetrics(SM_CXFULLSCREEN);
  1526.     int nScreenTop = 0;
  1527.     int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN);
  1528. #ifdef XP_WIN32
  1529.     RECT rect;
  1530.     if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0))
  1531.     {
  1532.         nScreenLeft = rect.left;
  1533.         nScreenRight = rect.right;
  1534.         nScreenTop = rect.top;
  1535.         nScreenBottom = rect.bottom;
  1536.     }
  1537. #endif
  1538.  
  1539.     int nStartX= MENU_BORDER_SIZE;
  1540.     int nOldStartX = MENU_BORDER_SIZE;
  1541.  
  1542.     int nMaxColumnHeight = 0;
  1543.  
  1544.     int nNumColumns = m_pColumnArray.GetSize();
  1545.     CSize columnSize;
  1546.     CDropMenuColumn *pColumn;
  1547.     m_nLastVisibleColumn = nNumColumns - 1;
  1548.  
  1549.     for(int i = 0; i < nNumColumns; i++)
  1550.     {
  1551.         pColumn = (CDropMenuColumn *)m_pColumnArray[i];
  1552.         if(pColumn->GetHasSubMenu())
  1553.             pColumn->SetWidth(pColumn->GetWidth() + SUBMENU_LEFT_MARGIN + SUBMENU_RIGHT_MARGIN + SUBMENU_WIDTH);
  1554.         else
  1555.             pColumn->SetWidth(pColumn->GetWidth() + SUBMENU_RIGHT_MARGIN);
  1556.  
  1557.         columnSize = CalculateColumn(pColumn, nStartX);
  1558.  
  1559.         if(nScreenLeft + nStartX + columnSize.cx >= nScreenRight)
  1560.         {
  1561.             //if we have more than one column, don't put in the last separator
  1562.             if(nNumColumns > 1)
  1563.             {
  1564.                 if(m_pOverflowMenuItem != NULL)
  1565.                 {
  1566.                     if(i  > 0)
  1567.                         nStartX = AddOverflowItem(i - 1, nOldStartX);
  1568.                     else
  1569.                         nStartX = AddOverflowItem(i, nStartX - SEPARATOR_WIDTH);
  1570.                 }
  1571.  
  1572.  
  1573.             }
  1574.             m_nLastVisibleColumn = i - 1;
  1575.             break;
  1576.         }
  1577.     
  1578.         if(columnSize.cy > nMaxColumnHeight)
  1579.             nMaxColumnHeight = columnSize.cy;
  1580.  
  1581.         nOldStartX = nStartX;
  1582.         nStartX += columnSize.cx;
  1583.         if(i + 1 < nNumColumns)
  1584.             nStartX += SEPARATOR_WIDTH;
  1585.     }
  1586.  
  1587.     totalSize.cy = nMaxColumnHeight;
  1588.     totalSize.cx = ((nNumColumns > 1) ? nStartX - SEPARATOR_WIDTH : nStartX);
  1589.     return totalSize;
  1590. }
  1591.  
  1592. CSize CDropMenu::CalculateColumn(CDropMenuColumn *pColumn, int nStartX)
  1593. {
  1594.     int nStart = pColumn->GetFirstItem();
  1595.     int nEnd = pColumn->GetLastItem();
  1596.     int nColumnWidth = pColumn->GetWidth();
  1597.     int nItemHeight;
  1598.  
  1599.     CSize totalSize(0, MENU_BORDER_SIZE);
  1600.     CSize itemSize;
  1601.     for(int i = nStart; i <= nEnd; i++)
  1602.     {
  1603.         CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[i];
  1604.  
  1605.         if(m_bShowFeedback)
  1606.             totalSize.cy += SPACE_BETWEEN_ITEMS;
  1607.  
  1608.         if(item->IsSeparator())
  1609.         {
  1610.  
  1611.             CRect rect (nStartX, totalSize.cy, nStartX + nColumnWidth, totalSize.cy + SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT);
  1612.             item->SetMenuItemRect(rect);
  1613.  
  1614.             nItemHeight = SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT;
  1615.  
  1616.         }
  1617.         else
  1618.         {
  1619.             itemSize=MeasureItem(item);
  1620.             CRect rect(nStartX, totalSize.cy, nStartX + nColumnWidth, totalSize.cy + itemSize.cy);
  1621.  
  1622.             item->SetMenuItemRect(rect);
  1623.  
  1624.             nItemHeight = itemSize.cy;
  1625.  
  1626.         }
  1627.  
  1628.         totalSize.cy += nItemHeight;
  1629.  
  1630.     }
  1631.     return CSize(pColumn->GetWidth(), totalSize.cy);
  1632. }
  1633.  
  1634. //returns the x coordinate of the right side of this last column
  1635. int CDropMenu::AddOverflowItem(int nColumn, int nStartX)
  1636. {
  1637.     int nScreenTop = 0;
  1638.     int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN);
  1639. #ifdef XP_WIN32
  1640.     RECT rect;
  1641.     if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0))
  1642.     {
  1643.         nScreenTop = rect.top;
  1644.         nScreenBottom = rect.bottom;
  1645.     }
  1646. #endif
  1647.  
  1648.     CDropMenuColumn *pColumn = (CDropMenuColumn *)m_pColumnArray[nColumn];
  1649.  
  1650.     int nPosition = pColumn->GetLastItem();
  1651.  
  1652.     CDropMenuItem *pLastItem = (CDropMenuItem*)m_pMenuItemArray[nPosition];
  1653.  
  1654.     if(pLastItem == m_pOverflowMenuItem)
  1655.         return pColumn->GetWidth();
  1656.  
  1657.     int nHeight = pColumn->GetHeight();
  1658.     CRect overflowRect;
  1659.     CRect itemRect;
  1660.     CDropMenuItem *pItem;
  1661.  
  1662.     CSize overflowSize = MeasureItem(m_pOverflowMenuItem);
  1663.     overflowSize.cx += pColumn->GetWidestImage();
  1664.  
  1665.     while(nScreenTop + nHeight + overflowSize.cy > nScreenBottom)
  1666.     {
  1667.         pItem = (CDropMenuItem*)m_pMenuItemArray[nPosition];
  1668.         pItem->GetMenuItemRect(&itemRect);
  1669.  
  1670.  
  1671.         nHeight -= itemRect.Height();
  1672.         nPosition--;
  1673.     }
  1674.  
  1675.     // add 1 since we've subtracted 1 away
  1676.     pColumn->SetLastItem(nPosition + 1);
  1677.     pColumn->SetHeight(nHeight + overflowSize.cy);
  1678.     m_pOverflowMenuItem->SetColumn(nColumn);
  1679.     if(overflowSize.cx > pColumn->GetWidth())
  1680.         pColumn->SetWidth(overflowSize.cx);
  1681.     m_pMenuItemArray.InsertAt(nPosition + 1, m_pOverflowMenuItem, 1);
  1682.     itemRect.SetRect(nStartX, nHeight, nStartX + pColumn->GetWidth(), nHeight + overflowSize.cy);
  1683.     m_pOverflowMenuItem->SetMenuItemRect(itemRect);
  1684.     return (nStartX + pColumn->GetWidth());
  1685.  
  1686. }
  1687.  
  1688. void CDropMenu::CalculateItemDimensions(void)
  1689. {
  1690.  
  1691.     int numItems = m_pMenuItemArray.GetSize();
  1692.  
  1693.     int nScreenTop = 0;
  1694.     int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN);
  1695. #ifdef XP_WIN32
  1696.     RECT rect;
  1697.     if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0))
  1698.     {
  1699.         nScreenTop = rect.top;
  1700.         nScreenBottom = rect.bottom;
  1701.     }
  1702. #endif
  1703.  
  1704.     int nNumCols = 0;
  1705.     int nColumnHeight = nScreenBottom + 1;
  1706.     int nColumnWidth = 0;
  1707.     CSize textSize, unselectedBitmapSize, selectedBitmapSize, itemSize;
  1708.     CDropMenuColumn *pColumn = NULL;
  1709.     int nMaxBitmapSize = 0, nMaxTextSize = 0;
  1710.  
  1711.     int nCount = m_pColumnArray.GetSize();
  1712.  
  1713.     //make sure column array is empty
  1714.     for(int j = 0 ; j < nCount; j++)
  1715.         delete (CDropMenuColumn*)m_pColumnArray[j];
  1716.  
  1717.     m_pColumnArray.RemoveAll();
  1718.  
  1719.     for(int i=0; i<numItems; i++)
  1720.     {
  1721.         CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[i];
  1722.  
  1723.         CalculateOneItemsDimensions(item);
  1724.  
  1725.         textSize = item->GetTextSize();
  1726.         selectedBitmapSize = item->GetSelectedBitmapSize();
  1727.         unselectedBitmapSize = item->GetUnselectedBitmapSize();
  1728.         itemSize = MeasureItem(item);
  1729.  
  1730.         if(nScreenTop + nColumnHeight + itemSize.cy > nScreenBottom)
  1731.         {
  1732.             pColumn = new CDropMenuColumn;
  1733.             pColumn->SetHasSubMenu(FALSE);
  1734.             pColumn->SetFirstItem(i);
  1735.  
  1736.             m_pColumnArray.Add(pColumn);
  1737.             nColumnHeight = nColumnWidth = 0;
  1738.             nMaxBitmapSize = 0, nMaxTextSize = 0;
  1739.             nNumCols++;
  1740.         }
  1741.  
  1742.         item->SetColumn(nNumCols - 1);
  1743.         pColumn->SetLastItem(i);
  1744.  
  1745.         if(unselectedBitmapSize.cx > nMaxBitmapSize)
  1746.             nMaxBitmapSize = unselectedBitmapSize.cx;
  1747.  
  1748.         if(selectedBitmapSize.cx > nMaxBitmapSize)
  1749.             nMaxBitmapSize= selectedBitmapSize.cx;
  1750.  
  1751.         if(textSize.cx > nMaxTextSize)
  1752.             nMaxTextSize = textSize.cx;
  1753.  
  1754.         if(item->IsSubMenu())
  1755.             pColumn->SetHasSubMenu(TRUE);
  1756.  
  1757.         nColumnHeight += itemSize.cy;
  1758.         if(m_bShowFeedback)
  1759.             nColumnHeight += SPACE_BETWEEN_ITEMS;
  1760.  
  1761.         pColumn->SetHeight(nColumnHeight);
  1762.  
  1763.         pColumn->SetWidth(nMaxBitmapSize + nMaxTextSize + ( nIMG_SPACE * 3));
  1764.         pColumn->SetWidestImage(nMaxBitmapSize);
  1765.         if(item == m_pOverflowMenuItem)
  1766.             return;
  1767.     }
  1768.  
  1769.     if(m_pOverflowMenuItem != NULL)
  1770.         CalculateOneItemsDimensions(m_pOverflowMenuItem);
  1771.  
  1772. }
  1773.  
  1774. void CDropMenu::CalculateOneItemsDimensions(CDropMenuItem* item)
  1775. {
  1776.     CSize unselectedBitmapSize, selectedBitmapSize, textSize;
  1777.     CString text;
  1778.  
  1779.     if(!item->IsSeparator())
  1780.     {
  1781.         unselectedBitmapSize = MeasureBitmap(item->GetUnselectedBitmap());
  1782.         item->SetUnselectedBitmapSize(unselectedBitmapSize);
  1783.         
  1784.         selectedBitmapSize = MeasureBitmap(item->GetSelectedBitmap());
  1785.         item->SetSelectedBitmapSize(selectedBitmapSize);
  1786.  
  1787.         if(item->IsSubMenu())
  1788.         {
  1789.             m_bHasSubMenu = TRUE;
  1790.         }
  1791.  
  1792.  
  1793.         text = item->GetText();
  1794.  
  1795.         textSize = MeasureText(text);
  1796.  
  1797.  
  1798.         item->SetTextSize(textSize);
  1799.     }
  1800.  
  1801.  
  1802. }
  1803.  
  1804. /****************************************************************************
  1805. *
  1806. *    CTreeMenu::MeasureBitmap
  1807. *
  1808. *    PARAMETERS:
  1809. *        pBitmap:  The bitmap to measure
  1810. *
  1811. *    RETURNS:
  1812. *        The size of the bitmap
  1813. *
  1814. *    DESCRIPTION:
  1815. *        Given a bitmap, this returns its dimensions
  1816. *
  1817. ****************************************************************************/
  1818.  
  1819. CSize CDropMenu::MeasureBitmap(HBITMAP hBitmap)
  1820. {
  1821.  
  1822.     CSize size;
  1823.  
  1824.     if (hBitmap != NULL)
  1825.     {
  1826.         BITMAP bmp;
  1827.         GetObject(hBitmap, sizeof(bmp), &bmp);
  1828.  
  1829.         size.cx = bmp.bmWidth;
  1830.         size.cy = bmp.bmHeight;
  1831.     }
  1832.     else
  1833.         size.cx = size.cy = 0;
  1834.  
  1835.     return size;
  1836.  
  1837.  
  1838. }
  1839.  
  1840.  
  1841. /****************************************************************************
  1842. *
  1843. *    CTreeMenu::MeasureText
  1844. *
  1845. *    PARAMETERS:
  1846. *        text:  The CString text to be measured
  1847. *
  1848. *    RETURNS:
  1849. *        Returns the size of the text in the current font
  1850. *
  1851. *    DESCRIPTION:
  1852. *        Given a CString this returns the dimensions of that text
  1853. *
  1854. ****************************************************************************/
  1855.  
  1856. CSize CDropMenu::MeasureText(CString text)
  1857. {
  1858.  
  1859.     HFONT hMenuFont;
  1860.  
  1861. #if defined(WIN32)
  1862.     NONCLIENTMETRICS ncm;
  1863.     ncm.cbSize = sizeof(NONCLIENTMETRICS);
  1864.     if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0))
  1865.     {
  1866.         hMenuFont = theApp.CreateAppFont( ncm.lfMenuFont );
  1867.     }
  1868.     else
  1869.     {
  1870.         hMenuFont = (HFONT)::GetStockObject(SYSTEM_FONT);
  1871.     }
  1872. #else    // Win16
  1873.     hMenuFont = (HFONT)::GetStockObject(SYSTEM_FONT);
  1874. #endif    // WIN32
  1875.  
  1876.     HDC hDC = ::GetDC(m_hWnd);
  1877.  
  1878.     HFONT hOldFont = (HFONT) ::SelectObject(hDC, hMenuFont);
  1879.     CSize sizeTxt = ::GetTabbedTextExtent(hDC, (LPCSTR)text, strlen(text), 0, NULL);
  1880.     ::SelectObject(hDC, hOldFont);
  1881.  
  1882. #if defined(WIN32)
  1883.     if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0))
  1884.     {
  1885.         theApp.ReleaseAppFont(hMenuFont);
  1886.     }
  1887. #endif
  1888.  
  1889.  
  1890.     ::ReleaseDC(m_hWnd, hDC);
  1891.  
  1892.     return sizeTxt;
  1893.  
  1894. }
  1895.  
  1896. /****************************************************************************
  1897. *
  1898. *    CTreeMenu::MeasureItem
  1899. *
  1900. *    PARAMETERS:
  1901. *        lpMI    - pointer to LPMEASUREITEMSTRUCT
  1902. *
  1903. *    RETURNS:
  1904. *        void
  1905. *
  1906. *    DESCRIPTION:
  1907. *        We must override this function to inform the system of our menu
  1908. *        item dimensions, since we're owner draw style.
  1909. *
  1910. ****************************************************************************/
  1911.                                 //menu item and end of accelerator
  1912.  
  1913. CSize CDropMenu::MeasureItem(CDropMenuItem *pItem)
  1914. {
  1915.     ASSERT(pItem != NULL);
  1916.     
  1917.     CSize size;
  1918.  
  1919.     if(pItem->IsSeparator())
  1920.     {
  1921.         size.cx = 0;
  1922.         size.cy = SEPARATOR_SPACE * 2 + SEPARATOR_HEIGHT;
  1923.     }
  1924.     else
  1925.     {
  1926.         // First, get all of the dimensions
  1927.         CSize unselectedSizeImg=pItem->GetUnselectedBitmapSize();
  1928.         CSize selectedSizeImg = pItem->GetSelectedBitmapSize();
  1929.         CSize sizeText=pItem->GetTextSize();
  1930.  
  1931.         size.cx = sizeText.cx + (nIMG_SPACE * 2);
  1932.  
  1933.         int maxImageHeight = max(unselectedSizeImg.cy, selectedSizeImg.cy);
  1934.  
  1935.         size.cy = max(maxImageHeight, sizeText.cy) + 4;
  1936.     }
  1937.     return size;
  1938.     
  1939. }
  1940.  
  1941. void CDropMenu::ChangeSelection(CPoint point)
  1942. {
  1943.     if(m_bShowing)
  1944.     {
  1945.         MenuSelectionType eSelType;
  1946.         int nSelection = FindSelection(point, eSelType);
  1947.  
  1948.         eSelType = m_bShowFeedback ? eSelType : eNONE;
  1949.  
  1950.         if(m_nSelectedItem != nSelection || m_eSelectedItemSelType != eSelType) 
  1951.         {
  1952.             SelectMenuItem(m_nSelectedItem, FALSE, TRUE, m_eSelectedItemSelType);
  1953.             SelectMenuItem(nSelection, TRUE, TRUE, eSelType);
  1954.         }
  1955.     }
  1956. }
  1957.  
  1958. int CDropMenu::FindSelection(CPoint point, MenuSelectionType &eSelType)
  1959. {
  1960.  
  1961.     eSelType = eNONE;
  1962.     int nNumColumns = m_pColumnArray.GetSize();
  1963.     CRect colRect;
  1964.     CDropMenuColumn *pColumn;
  1965.     int nStartX = MENU_BORDER_SIZE;
  1966.     int nColumnWidth;
  1967.  
  1968.     for(int i = 0; i < nNumColumns; i++)
  1969.     {
  1970.         
  1971.         pColumn = (CDropMenuColumn*)m_pColumnArray[i];    
  1972.         nColumnWidth = pColumn->GetWidth();
  1973.  
  1974.         colRect.SetRect(nStartX, 0, nStartX + nColumnWidth, pColumn->GetHeight());
  1975.  
  1976.         if(colRect.PtInRect(point))
  1977.         {
  1978.  
  1979.             return (FindSelectionInColumn(pColumn, point, eSelType));
  1980.         }
  1981.         else
  1982.         {
  1983.             nStartX += nColumnWidth + SEPARATOR_WIDTH;
  1984.         }
  1985.  
  1986.     }
  1987.  
  1988.     return -1;
  1989. }
  1990.  
  1991. int  CDropMenu::FindSelectionInColumn(CDropMenuColumn *pColumn, CPoint point, MenuSelectionType &eSelType)
  1992. {
  1993.  
  1994.     int y = MENU_BORDER_SIZE;
  1995.  
  1996.     if(point.y >=0 && point.y <= MENU_BORDER_SIZE)
  1997.         return 0;
  1998.     
  1999.     int nFeedbackHeight = m_bShowFeedback ? SPACE_BETWEEN_ITEMS : 0;
  2000.     CDropMenuItem *item;
  2001.     int nFirstItem = pColumn->GetFirstItem();
  2002.     int nLastItem = pColumn->GetLastItem();
  2003.  
  2004.     for(int i = nFirstItem; i <= nLastItem; i++)
  2005.     {
  2006.         item = (CDropMenuItem*)m_pMenuItemArray[i];
  2007.  
  2008.         CRect rect;
  2009.  
  2010.         item->GetMenuItemRect(rect);
  2011.  
  2012.         if( point.y >= y && point.y < y + rect.Height() + nFeedbackHeight)
  2013.         {
  2014.             if(item->GetShowFeedback())
  2015.                 eSelType = FindSelectionType(item, point.y - y, rect.Height() + nFeedbackHeight);
  2016.             return i;
  2017.         }
  2018.  
  2019.         y += rect.Height() + nFeedbackHeight;
  2020.     }
  2021.  
  2022.     //if we're in the border between the bottom and the last menu item
  2023.     if(point.y >= y && point.y <= y + 2*MENU_BORDER_SIZE)
  2024.     {
  2025.         if(item->GetShowFeedback())
  2026.             eSelType = eBELOW;
  2027.         return i - 1;
  2028.     }
  2029.  
  2030.  
  2031.  
  2032.     return -1;
  2033.  
  2034. }
  2035.  
  2036. MenuSelectionType CDropMenu::FindSelectionType(CDropMenuItem *pItem, int y, int nHeight)
  2037. {
  2038.     if(pItem->IsSubMenu())
  2039.     {
  2040.         if (y >= 0 && y <= nHeight/4.0)
  2041.             return eABOVE;
  2042.         else if (y <= 3.0*nHeight/4.0)
  2043.             return eON;
  2044.         else return eBELOW;
  2045.     }
  2046.     else
  2047.     {
  2048.         if(y >= 0 && y <= nHeight / 2.0)
  2049.             return eABOVE;
  2050.         else
  2051.             return eBELOW;
  2052.     }
  2053. }
  2054.  
  2055. void CDropMenu::SelectMenuItem(int nItemIndex, BOOL bIsSelected, BOOL bHandleSubmenus, MenuSelectionType eSelType)
  2056. {
  2057.  
  2058.     RECT rcClient;
  2059.  
  2060.     GetClientRect(&rcClient);
  2061.  
  2062.     if(nItemIndex != -1)
  2063.     {
  2064.         CDropMenuItem *item = (CDropMenuItem*)m_pMenuItemArray[nItemIndex];
  2065.  
  2066.         InvalidateMenuItemRect(item);
  2067.     
  2068.  
  2069.         if(!bIsSelected)
  2070.         {
  2071.  
  2072.             m_pShowingSubmenu = NULL;
  2073.  
  2074.             m_nSelectedItem = -1;
  2075.             m_eSelectedItemSelType = eNONE;
  2076.  
  2077.             if(bHandleSubmenus && item->IsSubMenu() && !item->IsEmpty() && item->GetSubMenu()->m_bShowing)
  2078.             {
  2079.                 if(m_pUnselectedItem == NULL)
  2080.                 {
  2081.                     item->GetSubMenu()->m_bShowing = FALSE;
  2082.                     m_pUnselectedItem = item;
  2083.                     m_nUnselectTimer = SetTimer(IDT_SUBMENU_OFF ,SUBMENU_DELAY_OFF_MS, NULL);
  2084.                 }
  2085.             }
  2086.  
  2087.         }
  2088.         else
  2089.         {
  2090.             m_nSelectedItem = nItemIndex;
  2091.             m_eSelectedItemSelType = eSelType;
  2092.             if(m_nSelectTimer != 0)
  2093.             {
  2094.                 KillTimer(m_nSelectTimer);
  2095.             }
  2096.  
  2097.             if(bHandleSubmenus && item->IsSubMenu() && !item->IsEmpty() && (eSelType == eON || eSelType == eNONE))
  2098.             {
  2099.                 m_pShowingSubmenu = item->GetSubMenu();
  2100.                 m_pSelectedItem = item;
  2101.                 m_pSelectedItem->GetSubMenu()->m_bAboutToShow = TRUE;
  2102.                 m_nSelectTimer = SetTimer(IDT_SUBMENU_ON, SUBMENU_DELAY_ON_MS, NULL);
  2103.  
  2104.             }
  2105.  
  2106.         }
  2107.     }
  2108. }
  2109.  
  2110.  
  2111. BOOL CDropMenu::NoSubmenusShowing(void)
  2112. {
  2113.     CWnd *child = GetWindow(GW_CHILD);
  2114.  
  2115.     if(child != NULL)
  2116.     {
  2117.         while(child != NULL)
  2118.         {
  2119.             if(child->IsWindowVisible())
  2120.             {
  2121.                 return FALSE;
  2122.             }
  2123.  
  2124.             child = GetWindow(GW_HWNDNEXT);
  2125.         }
  2126.         
  2127.     }
  2128.  
  2129.     return TRUE;
  2130. }
  2131.  
  2132. CDropMenu *CDropMenu::GetMenuParent(void)
  2133. {
  2134.  
  2135.     return m_pMenuParent;
  2136. }
  2137.  
  2138. BOOL CDropMenu::IsDescendant(CWnd *pDescendant)
  2139. {
  2140.     CDropMenu *pSubmenu = m_pShowingSubmenu;
  2141.  
  2142.     while(pSubmenu != NULL)
  2143.     {
  2144.         if(pSubmenu == pDescendant)
  2145.         {
  2146.             return TRUE;
  2147.         }
  2148.  
  2149.         pSubmenu = pSubmenu->m_pShowingSubmenu;
  2150.     }
  2151.  
  2152.     return FALSE;
  2153. }
  2154.  
  2155. BOOL CDropMenu::IsAncestor(CWnd *pAncestor)
  2156. {
  2157.  
  2158.     CDropMenu *pMenu = this;
  2159.  
  2160.     while(pMenu->m_pParent ==  pMenu->m_pMenuParent)
  2161.     {
  2162.         if(pMenu->m_pMenuParent == pAncestor)
  2163.         {
  2164.             return TRUE;
  2165.         }
  2166.  
  2167.  
  2168.         pMenu = pMenu->m_pMenuParent;
  2169.     }
  2170.  
  2171.     return FALSE;
  2172. }
  2173.  
  2174. //Sees if the hwnd is either this menu, an ancestor or a submenu
  2175. BOOL CDropMenu::IsShowingMenu(HWND hwnd)
  2176. {
  2177.  
  2178.     if(hwnd == m_hWnd)
  2179.     {
  2180.         return TRUE;
  2181.     }
  2182.     else
  2183.     {
  2184.         CDropMenu *menu = m_pMenuParent;
  2185.  
  2186.         while(menu != NULL)
  2187.         {
  2188.             if(hwnd == menu->m_hWnd)
  2189.             {
  2190.                 return TRUE;
  2191.             }
  2192.             menu=menu->m_pMenuParent;
  2193.         }
  2194.  
  2195.         menu = m_pShowingSubmenu;
  2196.  
  2197.         while(menu !=NULL)
  2198.         {
  2199.             if(hwnd == menu->m_hWnd)
  2200.             {
  2201.                 return TRUE;
  2202.             }
  2203.             menu = menu->m_pShowingSubmenu;
  2204.         }
  2205.  
  2206.     }
  2207.     return FALSE;
  2208. }
  2209.  
  2210. void CDropMenu::DropMenuTrackDropMenu(CDropMenu *pParent, int x, int y, BOOL bDragging, CDropMenuDropTarget *pDropTarget,
  2211.                                       BOOL bShowFeedback, int oppX, int oppY)
  2212. {
  2213.     m_pMenuParent = pParent;
  2214.  
  2215.     pParent->m_pShowingSubmenu = this;
  2216.  
  2217.     TrackDropMenu(pParent, x, y, bDragging, pDropTarget, bShowFeedback, oppX, oppY);
  2218. }
  2219.  
  2220. int CDropMenu::FindCommand(UINT nCommand)
  2221. {
  2222.  
  2223.     int nCount = m_pMenuItemArray.GetSize();
  2224.  
  2225.     for(int i = 0; i < nCount; i++)
  2226.     {
  2227.         CDropMenuItem * pItem = (CDropMenuItem*)m_pMenuItemArray[i];
  2228.         if(!pItem->IsSeparator())
  2229.         {
  2230.             if(pItem->GetCommand() == nCommand)
  2231.             {
  2232.                 return i;
  2233.             }
  2234.         }
  2235.     }
  2236.     
  2237.     return -1;
  2238.  
  2239. }
  2240.  
  2241. CPoint CDropMenu::FindStartPoint(int x, int y, CSize size, int oppX, int oppY)
  2242. {
  2243.  
  2244.     int nScreenLeft = 0;
  2245.     int nScreenRight = GetSystemMetrics(SM_CXFULLSCREEN);
  2246.     int nScreenTop = 0;
  2247.     int nScreenBottom = GetSystemMetrics(SM_CYFULLSCREEN);
  2248. #ifdef XP_WIN32
  2249.     RECT rect;
  2250.     if(SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rect,0))
  2251.     {
  2252.         nScreenLeft = rect.left;
  2253.         nScreenRight = rect.right;
  2254.         nScreenTop = rect.top;
  2255.         nScreenBottom = rect.bottom;
  2256.     }
  2257. #endif
  2258.  
  2259.  
  2260.     if(oppX == -1)
  2261.     {
  2262.         CRect parentRect;
  2263.         m_pParent->GetWindowRect(&parentRect);
  2264.  
  2265.         oppX = m_bRight ? parentRect.left : parentRect.right;
  2266.     }
  2267.  
  2268.     int nRoomToRight = nScreenRight - (m_bRight ? x : oppX);
  2269.     int nRoomToLeft = (m_bRight ? oppX : x) - nScreenLeft;
  2270.  
  2271.     // if we are opening to the right we need to make sure we don't go off the right
  2272.     // side of the screen.
  2273.     if(m_bRight)
  2274.     {
  2275.         //if we will extend past the right
  2276.         if(x + size.cx > nScreenRight)
  2277.         {
  2278.             // if we can fit to the left
  2279.             if(oppX - size.cx > nScreenLeft)
  2280.             {
  2281.                 if(m_pMenuParent != NULL)
  2282.                 {
  2283.                     x = oppX - size.cx;
  2284.                     //if it's not the top level menu we want it to overlap its parent
  2285.                     x -= 2;
  2286.                 }
  2287.                 else
  2288.                 {
  2289.                     x = oppX - size.cx;
  2290.                 }
  2291.  
  2292.                 m_bRight = FALSE;
  2293.             }
  2294.             else
  2295.             {
  2296.                 //see where we have more space
  2297.                 if(nRoomToRight >= nRoomToLeft)
  2298.                     x = nScreenRight - size.cx;
  2299.                 else
  2300.                 {
  2301.                     x = nScreenLeft;
  2302.                     m_bRight = FALSE;
  2303.                 }
  2304.             }
  2305.         }
  2306.         else if(m_pMenuParent != NULL)
  2307.         {
  2308.             // if we are a submenu we want to overlap our parent
  2309.             x -= 5;
  2310.         }
  2311.     }
  2312.     // if we are opening to the left we need to make sure we don't go off the left
  2313.     // side of the screen
  2314.     else
  2315.     {
  2316.         //if we will extend past the left
  2317.         if(x - size.cx < nScreenLeft)
  2318.         {
  2319.             // if we can fit to the right
  2320.             if(oppX + size.cx < nScreenRight)
  2321.             {
  2322.                 x = oppX;
  2323.                 //if it's not the top level menu we want it to overlap its parent
  2324.                 if(m_pMenuParent != NULL)
  2325.                 {
  2326.                     x -= 5;
  2327.                 }
  2328.                 m_bRight = TRUE;
  2329.             }
  2330.             else
  2331.             {
  2332.                 if(nRoomToRight >= nRoomToLeft)
  2333.                 {
  2334.                     x = nScreenRight - size.cx;
  2335.                     m_bRight = TRUE;
  2336.                 }
  2337.                 else
  2338.                     x = nScreenLeft;
  2339.             }
  2340.         }
  2341.         else if(m_pMenuParent != NULL)
  2342.         {
  2343.             x -= size.cx;
  2344.             // if we are a submenu we want to overlap our parent
  2345.             x -= 2;
  2346.         }
  2347.     }
  2348.  
  2349.     if(y + size.cy > nScreenBottom)
  2350.     {
  2351.         y -= ((y + size.cy) - nScreenBottom);
  2352.         
  2353.     }
  2354.  
  2355.     return CPoint(x, y);
  2356. }
  2357.  
  2358. void CDropMenu::HandleUpArrow(void)
  2359. {
  2360.     int nSelection;
  2361.  
  2362.     // need to deal with separators
  2363.     if(m_nSelectedItem == NOSELECTION || m_nSelectedItem == 0)
  2364.     {
  2365.         nSelection = GetMenuItemCount() - 1;
  2366.     }
  2367.     else
  2368.     {
  2369.         nSelection = m_nSelectedItem - 1;
  2370.     }
  2371.  
  2372.     while(((CDropMenuItem *)m_pMenuItemArray[nSelection])->IsSeparator())
  2373.     {
  2374.         nSelection = (nSelection == 0) ? GetMenuItemCount() - 1 : nSelection - 1;
  2375.     }
  2376.  
  2377.     SelectMenuItem(m_nSelectedItem, FALSE, FALSE, eNONE);
  2378.     SelectMenuItem(nSelection, TRUE, FALSE, eNONE);
  2379. }
  2380.  
  2381. void CDropMenu::HandleDownArrow(void)
  2382. {
  2383.     int nSelection;
  2384.     int nNumItems = GetMenuItemCount() - 1;
  2385.  
  2386.     // need to deal with separators
  2387.     if(m_nSelectedItem == NOSELECTION || m_nSelectedItem == nNumItems)
  2388.     {
  2389.         nSelection = 0;
  2390.     }
  2391.     else
  2392.     {
  2393.         nSelection = m_nSelectedItem + 1;
  2394.     }
  2395.  
  2396.     while(((CDropMenuItem *)m_pMenuItemArray[nSelection])->IsSeparator())
  2397.     {
  2398.         nSelection = (nSelection == nNumItems - 1) ? 0 : nSelection + 1;
  2399.     }
  2400.  
  2401.     SelectMenuItem(m_nSelectedItem, FALSE, FALSE, eNONE);
  2402.     SelectMenuItem(nSelection, TRUE, FALSE, eNONE);
  2403. }
  2404.  
  2405.  
  2406. void CDropMenu::HandleLeftArrow(void)
  2407. {
  2408.  
  2409.     if(m_pMenuParent != NULL)
  2410.     {
  2411.         m_bShowing = FALSE;
  2412.  
  2413.         m_pMenuParent->m_bMouseUsed = FALSE;
  2414.         ShowWindow(SW_HIDE);        
  2415.     }
  2416.  
  2417.  
  2418. }
  2419.  
  2420. void CDropMenu::HandleRightArrow(void)
  2421. {
  2422.     if(m_nSelectedItem != NOSELECTION)
  2423.     {
  2424.         CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[m_nSelectedItem];
  2425.         if(pItem->IsSubMenu())
  2426.         {
  2427.             m_pShowingSubmenu = pItem->GetSubMenu();
  2428.             m_pShowingSubmenu->m_nSelectedItem = 0;
  2429.             m_pShowingSubmenu->m_bMouseUsed = FALSE;
  2430.             ShowSubmenu(pItem);
  2431.         }
  2432.     }
  2433. }
  2434.  
  2435. void CDropMenu::HandleReturnKey(void)
  2436. {
  2437.     if(m_nSelectedItem != NOSELECTION)
  2438.     {
  2439.         CDropMenuItem *pItem = (CDropMenuItem *)m_pMenuItemArray[m_nSelectedItem];
  2440.  
  2441.         if(pItem->IsSubMenu())
  2442.         {
  2443.             HandleRightArrow();
  2444.         }
  2445.         else
  2446.         {
  2447. #ifdef _WIN32
  2448.             SendMessage(WM_COMMAND, MAKEWPARAM(pItem->GetCommand(), 0), 0);
  2449. #else
  2450.             SendMessage(WM_COMMAND, (WPARAM) pItem->GetCommand(), MAKELPARAM( m_hWnd, 0 ));
  2451. #endif
  2452.         }
  2453.     }
  2454. }
  2455.  
  2456. void CDropMenu::ShowSubmenu(CDropMenuItem *pItem)
  2457. {
  2458.  
  2459.     CRect rcClient, rect;
  2460.  
  2461.     GetClientRect(rcClient);
  2462.  
  2463.     pItem->GetMenuItemRect(rect);
  2464.  
  2465.     CDropMenuDropTarget *pDropTarget = GetDropMenuDropTarget(pItem->GetSubMenu());
  2466.     ClientToScreen(rect);
  2467.  
  2468.     m_pShowingSubmenu = pItem->GetSubMenu();
  2469.  
  2470.     pItem->GetSubMenu()->SetSubmenuBitmaps(m_hSubmenuSelectedBitmap, m_hSubmenuUnselectedBitmap);
  2471.  
  2472.     if(m_bRight)
  2473.     {
  2474.         pItem->GetSubMenu()->DropMenuTrackDropMenu(this, rect.right, rect.top - 2, m_bDragging, pDropTarget, m_bShowFeedback, rect.left);
  2475.     }
  2476.     else
  2477.     {
  2478.         pItem->GetSubMenu()->DropMenuTrackDropMenu(this, rect.left, rect.top - 2, m_bDragging, pDropTarget, m_bShowFeedback, rect.right);
  2479.     }
  2480.     
  2481.     pItem->GetSubMenu()->m_pParentMenuItem = pItem;
  2482.  
  2483. }
  2484.  
  2485. int    CDropMenu::FindMenuItemPosition(CDropMenuItem* pMenuItem)
  2486. {
  2487.     int nCount = m_pMenuItemArray.GetSize();
  2488.  
  2489.     for(int i = 0; i < nCount; i++)
  2490.         if(m_pMenuItemArray[i] == pMenuItem)
  2491.             return i;
  2492.  
  2493.     return -1;
  2494.  
  2495. }
  2496.  
  2497. void CDropMenu::SetSubmenuBitmaps(HBITMAP hSelectedBitmap, HBITMAP hUnselectedBitmap)
  2498. {
  2499.  
  2500.     m_hSubmenuUnselectedBitmap = hUnselectedBitmap;
  2501.     m_hSubmenuSelectedBitmap = hSelectedBitmap;
  2502.  
  2503. }
  2504.  
  2505. void CDropMenu::InvalidateMenuItemRect(CDropMenuItem *pMenuItem)
  2506. {
  2507.  
  2508.     CRect rcClient;
  2509.     CRect rect;
  2510.  
  2511.     GetClientRect(&rcClient);
  2512.  
  2513.     pMenuItem->GetMenuItemRect(rect);
  2514.     int nColumn = pMenuItem->GetColumn();
  2515.     CDropMenuColumn *pColumn= (CDropMenuColumn*)m_pColumnArray[nColumn];
  2516.  
  2517.     rect.right = rect.left + pColumn->GetWidth();
  2518.     rect.top -= 2;
  2519.     rect.bottom += 2;
  2520.     InvalidateRect(rect, TRUE);
  2521. }
  2522.  
  2523.  
  2524. void CDropMenu::KeepMenuAroundIfClosing(void)
  2525. {
  2526.     // if there is movement on this menu, then it is showing even
  2527.     // if it's been told to go away before.
  2528.     m_bShowing = TRUE;
  2529.     if(m_pMenuParent)
  2530.     {
  2531.         CDropMenuItem *pSelectedItem = m_pMenuParent->m_nSelectedItem != -1 ? (CDropMenuItem*)m_pMenuParent->m_pMenuItemArray[m_pMenuParent->m_nSelectedItem] : NULL;
  2532.  
  2533.         CDropMenu *pSubMenu = pSelectedItem && pSelectedItem->IsSubMenu() ? pSelectedItem->GetSubMenu() : NULL;
  2534.         if((pSubMenu && pSubMenu != this) || !pSubMenu)
  2535.         {
  2536.  
  2537.             KillTimer(m_pMenuParent->m_nSelectTimer);
  2538.             KillTimer(m_pMenuParent->m_nUnselectTimer);
  2539.             m_pMenuParent->m_pUnselectedItem = NULL;
  2540.             m_pMenuParent->m_nSelectTimer = 0;
  2541.             m_pMenuParent->m_pShowingSubmenu = this;
  2542.  
  2543.             //We need to make the menu item this belongs to the selected menu item.
  2544.             int nPosition = m_pMenuParent->FindMenuItemPosition(m_pParentMenuItem);
  2545.  
  2546.             if(nPosition != -1)
  2547.             {
  2548.  
  2549.                 if(pSelectedItem)
  2550.                 {
  2551.                     m_pMenuParent->InvalidateMenuItemRect(pSelectedItem);
  2552.                 }
  2553.  
  2554.                 m_pMenuParent->m_nSelectedItem = nPosition;
  2555.                 m_pMenuParent->m_eSelectedItemSelType = eON;
  2556.                 m_pMenuParent->m_pSelectedItem = m_pParentMenuItem;
  2557.  
  2558.                 m_pMenuParent->InvalidateMenuItemRect(m_pParentMenuItem);
  2559.  
  2560.             }
  2561.             
  2562.         }
  2563.     }
  2564. }
  2565.  
  2566. CWnd *CDropMenu::GetTopLevelParent(void)
  2567. {
  2568.  
  2569.     CDropMenu *pMenu = this;
  2570.  
  2571.     while(pMenu->m_pMenuParent != NULL)
  2572.     {
  2573.         pMenu = pMenu->m_pMenuParent;
  2574.     }
  2575.  
  2576.     return pMenu->m_pParent;
  2577. }
  2578.