mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2595 lines
79 KiB
2595 lines
79 KiB
/*--------------------------------------------------------------------------*
|
|
*
|
|
* Microsoft Windows
|
|
* Copyright (C) Microsoft Corporation, 1992 - 1999
|
|
*
|
|
* File: menubar.cpp
|
|
*
|
|
* Contents: Implementation file for CMenuBar
|
|
*
|
|
* History: 14-Nov-97 JeffRo Created
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "menubar.h"
|
|
#include "mdiuisim.h"
|
|
#include "amc.h"
|
|
#include "amcdoc.h"
|
|
#include "mainfrm.h"
|
|
#include "tbtrack.h"
|
|
#include "mnemonic.h"
|
|
#include "childfrm.h"
|
|
#include "menubtns.h"
|
|
#include <algorithm>
|
|
#include <mmsystem.h>
|
|
#include <oleacc.h>
|
|
#include "amcview.h"
|
|
#include "favorite.h"
|
|
#include "util.h"
|
|
|
|
/*
|
|
* if we're supporting old platforms, we need to get MSAA definitions
|
|
* from somewhere other than winuser.h
|
|
*/
|
|
#if (_WINNT_WIN32 < 0x0500)
|
|
#include <winable.h>
|
|
#endif
|
|
|
|
/*--------*/
|
|
/* SAccel */
|
|
/*--------*/
|
|
struct SAccel : public ACCEL
|
|
{
|
|
SAccel (WORD key_, WORD cmd_, BYTE fVirt_)
|
|
{
|
|
ZeroMemory (this, sizeof (*this));
|
|
key = key_;
|
|
cmd = cmd_;
|
|
fVirt = fVirt_;
|
|
}
|
|
};
|
|
|
|
|
|
/*--------------------*/
|
|
/* CPopupTrackContext */
|
|
/*--------------------*/
|
|
class CPopupTrackContext
|
|
{
|
|
public:
|
|
CPopupTrackContext (CMenuBar* pMenuBar, int nCurrentPopupIndex);
|
|
~CPopupTrackContext ();
|
|
|
|
// control over monitoring
|
|
void StartMonitoring();
|
|
bool WasAnotherPopupRequested(int& iNewIdx);
|
|
|
|
private:
|
|
typedef std::vector<int> BoundaryCollection;
|
|
typedef BoundaryCollection::iterator BoundIt;
|
|
typedef BoundaryCollection::const_iterator BoundConstIt;
|
|
|
|
BoundaryCollection m_ButtonBoundaries;
|
|
CMenuBar* const m_pMenuBar;
|
|
HHOOK m_hhkMouse;
|
|
HHOOK m_hhkKeyboard;
|
|
HHOOK m_hhkCallWnd;
|
|
CRect m_rectAllButtons;
|
|
CPoint m_ptLastMouseMove;
|
|
int m_nCurrentPopupIndex;
|
|
|
|
CWnd* m_pMaxedMDIChild;
|
|
const CPoint m_ptLButtonDown;
|
|
const UINT m_dwLButtonDownTime;
|
|
const int m_cButtons;
|
|
int m_cCascadedPopups;
|
|
bool m_fCurrentlyOnPopupItem;
|
|
bool m_bPopupMonitorHooksActive;
|
|
int m_iRequestForNewPopup;
|
|
|
|
LRESULT MouseProc (int nCode, UINT msg, LPMOUSEHOOKSTRUCT pmhs);
|
|
LRESULT KeyboardProc (int nCode, int vkey, int cRepeat, bool fDown, LPARAM lParam);
|
|
LRESULT CallWndProc (int nCode, BOOL bCurrentThread, LPCWPSTRUCT lpCWP);
|
|
|
|
int HitTest (CPoint pt) const;
|
|
int MapBoundaryIteratorToButtonIndex (BoundConstIt it) const;
|
|
void MaybeCloseMDIChild (CPoint pt);
|
|
void DismissCurrentPopup (bool fTrackingComplete);
|
|
void NotifyNewPopup (int nNewPopupIndex);
|
|
|
|
void SetPopupMonitorHooks();
|
|
void RemovePopupMonitorHooks();
|
|
static LRESULT CALLBACK MouseProc (int nCode, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT CALLBACK KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT CALLBACK CallWndProc (int nCode, WPARAM wParam, LPARAM lParam);
|
|
static CPopupTrackContext* s_pTrackContext;
|
|
};
|
|
|
|
CPopupTrackContext* CPopupTrackContext::s_pTrackContext = NULL;
|
|
|
|
|
|
|
|
const TCHAR chChildSysMenuMnemonic = _T('-');
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* AddMnemonic
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
template<class OutputIterator>
|
|
static void AddMnemonic (
|
|
TCHAR chMnemonic,
|
|
int cmd,
|
|
BYTE fVirt,
|
|
OutputIterator it)
|
|
{
|
|
ASSERT (chMnemonic != _T('\0'));
|
|
|
|
TCHAR chLower = (TCHAR) tolower (chMnemonic);
|
|
TCHAR chUpper = (TCHAR) toupper (chMnemonic);
|
|
|
|
/*
|
|
* add the lowercase mnemonic
|
|
*/
|
|
*it++ = SAccel (chLower, (WORD) cmd, fVirt);
|
|
|
|
/*
|
|
* if the uppercase mnemonic character is different from the
|
|
* lowercase character, add the uppercase mnemonic as well
|
|
*/
|
|
if (chUpper != chLower)
|
|
*it++ = SAccel (chUpper, (WORD) cmd, fVirt);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* PrepBandInfo
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
static void PrepBandInfo (LPREBARBANDINFO prbi, UINT fMask)
|
|
{
|
|
ZeroMemory (prbi, sizeof (REBARBANDINFO));
|
|
prbi->cbSize = sizeof (REBARBANDINFO);
|
|
prbi->fMask = fMask;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CMenuBar
|
|
|
|
BEGIN_MESSAGE_MAP(CMenuBar, CMMCToolBarCtrlEx)
|
|
//{{AFX_MSG_MAP(CMenuBar)
|
|
ON_NOTIFY_REFLECT(TBN_DROPDOWN, OnDropDown)
|
|
ON_NOTIFY_REFLECT(TBN_GETDISPINFO, OnGetDispInfo)
|
|
ON_NOTIFY_REFLECT(TBN_HOTITEMCHANGE, OnHotItemChange)
|
|
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
|
|
ON_WM_SYSCOMMAND()
|
|
ON_WM_SETTINGCHANGE()
|
|
ON_WM_DESTROY()
|
|
ON_COMMAND(ID_MTB_ACTIVATE_CURRENT_POPUP, OnActivateCurrentPopup)
|
|
//}}AFX_MSG_MAP
|
|
|
|
ON_MESSAGE(WM_POPUP_ASYNC, OnPopupAsync)
|
|
ON_COMMAND(CMMCToolBarCtrlEx::ID_MTBX_PRESS_HOT_BUTTON, OnActivateCurrentPopup)
|
|
ON_COMMAND_RANGE(ID_MTB_MENU_FIRST, ID_MTB_MENU_LAST, OnAccelPopup)
|
|
ON_UPDATE_COMMAND_UI_RANGE(ID_MTB_MENU_FIRST, ID_MTB_MENU_LAST, OnUpdateAllCmdUI)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::GetMenuUISimAccel
|
|
*
|
|
* Manages the accelerator table singleton for CMenuBar
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
const CAccel& CMenuBar::GetMenuUISimAccel ()
|
|
{
|
|
static ACCEL aaclTrack[] = {
|
|
{ FVIRTKEY, VK_RETURN, CMenuBar::ID_MTB_ACTIVATE_CURRENT_POPUP },
|
|
{ FVIRTKEY, VK_UP, CMenuBar::ID_MTB_ACTIVATE_CURRENT_POPUP },
|
|
{ FVIRTKEY, VK_DOWN, CMenuBar::ID_MTB_ACTIVATE_CURRENT_POPUP },
|
|
};
|
|
|
|
static const CAccel MenuUISimAccel (aaclTrack, countof (aaclTrack));
|
|
return (MenuUISimAccel);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::CMenuBar
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
CMenuBar::CMenuBar ()
|
|
{
|
|
m_pMDIFrame = NULL;
|
|
m_pwndLastActive = NULL;
|
|
m_pRebar = NULL;
|
|
m_hMenuLast = NULL;
|
|
m_hMaxedChildIcon = NULL;
|
|
m_fDestroyChildIcon = false;
|
|
m_fDecorationsShowing = false;
|
|
m_fMaxedChildIconIsInvalid = false;
|
|
m_CommandIDUnUsed.clear();
|
|
m_vMenuAccels.clear();
|
|
m_vTrackingAccels.clear();
|
|
m_bInProgressDisplayingPopup = false;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::~CMenuBar
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
CMenuBar::~CMenuBar ()
|
|
{
|
|
DeleteMaxedChildIcon();
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::Create
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
BOOL CMenuBar::Create (
|
|
CFrameWnd * pwndFrame,
|
|
CRebarDockWindow* pParentRebar,
|
|
DWORD dwStyle,
|
|
UINT idWindow)
|
|
{
|
|
ASSERT_VALID (pwndFrame);
|
|
ASSERT_VALID (pParentRebar);
|
|
|
|
// create the window
|
|
if (!CMMCToolBarCtrlEx::Create (NULL, dwStyle | TBSTYLE_LIST,
|
|
g_rectEmpty, pParentRebar, idWindow))
|
|
return (FALSE);
|
|
|
|
// initialize to hidded accelerator state
|
|
SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
|
|
|
|
// insert a hidden button for the maximized child's system menu
|
|
InsertButton (0, (LPCTSTR) NULL, ID_MTB_MENU_SYSMENU, NULL, 0, 0);
|
|
|
|
TBBUTTONINFO btni;
|
|
btni.cbSize = sizeof (btni);
|
|
btni.dwMask = TBIF_STATE | TBIF_SIZE;
|
|
btni.fsState = TBSTATE_HIDDEN;
|
|
btni.cx = static_cast<WORD>(GetSystemMetrics (SM_CXSMICON));
|
|
|
|
SetButtonInfo (ID_MTB_MENU_SYSMENU, &btni);
|
|
|
|
#ifdef SHRINK_PADDING
|
|
CSize sizePad = GetPadding();
|
|
sizePad.cx = 3;
|
|
SetPadding (sizePad);
|
|
#endif // SHRINK_PADDING
|
|
|
|
SetMenuFont ();
|
|
m_pRebar = pParentRebar->GetRebar();
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::Create
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
BOOL CMenuBar::Create (
|
|
CMDIFrameWnd * pwndFrame,
|
|
CRebarDockWindow* pParentRebar,
|
|
DWORD dwStyle,
|
|
UINT idWindow)
|
|
{
|
|
if (!Create ((CFrameWnd*) pwndFrame, pParentRebar, dwStyle, idWindow))
|
|
return (FALSE);
|
|
|
|
m_pMDIFrame = pwndFrame;
|
|
|
|
// this is a menu for an MDI frame window; create MDI decorations
|
|
m_pMDIDec = std::auto_ptr<CMDIMenuDecoration>(new CMDIMenuDecoration);
|
|
m_pMDIDec->Create (NULL, NULL,
|
|
WS_CHILD | MMDS_MINIMIZE |
|
|
MMDS_RESTORE | MMDS_CLOSE | MMDS_AUTOSIZE,
|
|
g_rectEmpty, this, ID_MDIDECORATION);
|
|
|
|
// the rebar will re-parent the decoration, make sure we remain the owner
|
|
m_pMDIDec->SetOwner (this);
|
|
|
|
// insert the MDI decoration band
|
|
REBARBANDINFO rbi;
|
|
PrepBandInfo (&rbi, RBBIM_CHILD | RBBIM_STYLE | RBBIM_ID);
|
|
|
|
rbi.fStyle = RBBS_FIXEDSIZE | RBBS_HIDDEN;
|
|
rbi.hwndChild = m_pMDIDec->m_hWnd;
|
|
rbi.wID = ID_MDIDECORATION;
|
|
|
|
ASSERT (m_pRebar != NULL);
|
|
m_pRebar->InsertBand (&rbi);
|
|
|
|
// resize the decoration window, *after* inserting the band
|
|
SizeDecoration ();
|
|
|
|
// there's bug in rebar which will show a band's
|
|
// child, even with RBBS_HIDDEN, work around it
|
|
m_pMDIDec->ShowWindow (SW_HIDE);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::PreTranslateMessage
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
BOOL CMenuBar::PreTranslateMessage(MSG* pMsg)
|
|
{
|
|
// show accelerators, since user indicated he wants to control using the keyboard
|
|
if ( (pMsg->message == WM_SYSKEYDOWN ) &&
|
|
(!(pMsg->lParam & 0x40000000)/*not repeating*/) )
|
|
{
|
|
SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS));
|
|
}
|
|
|
|
if (CMMCToolBarCtrlEx::PreTranslateMessage (pMsg))
|
|
return (TRUE);
|
|
|
|
if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST))
|
|
{
|
|
CMainFrame* pFrame = AMCGetMainWnd();
|
|
if ((pFrame == NULL) || !pFrame->IsMenuVisible())
|
|
return (FALSE);
|
|
|
|
// if we're in menu mode, check menu mode-only accelerators
|
|
if (IsTrackingToolBar ())
|
|
{
|
|
const CAccel& MenuUISimAccel = GetMenuUISimAccel();
|
|
|
|
ASSERT (MenuUISimAccel != NULL);
|
|
if (MenuUISimAccel.TranslateAccelerator (m_hWnd, pMsg))
|
|
return (TRUE);
|
|
|
|
ASSERT (m_TrackingAccel != NULL);
|
|
if (m_TrackingAccel.TranslateAccelerator (m_hWnd, pMsg))
|
|
return (TRUE);
|
|
}
|
|
|
|
if ((m_MenuAccel != NULL) && m_MenuAccel.TranslateAccelerator (m_hWnd, pMsg))
|
|
return (TRUE);
|
|
|
|
// handle Alt+- when child is maximized
|
|
if (m_fDecorationsShowing &&
|
|
(pMsg->message == WM_SYSCHAR) &&
|
|
(pMsg->wParam == chChildSysMenuMnemonic))
|
|
{
|
|
SendMessage (WM_COMMAND, ID_MTB_MENU_SYSMENU);
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::SetMenu
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::SetMenu (CMenu* pMenu)
|
|
{
|
|
HMENU hMenu = pMenu->GetSafeHmenu();
|
|
|
|
// don't need to do anything if we're setting the same menu as last time
|
|
if (hMenu == m_hMenuLast)
|
|
return;
|
|
|
|
// remember this menu for optimization next time
|
|
m_hMenuLast = hMenu;
|
|
|
|
// delete all but the first existing buttons from the toolbar
|
|
while (DeleteButton(1))
|
|
{
|
|
}
|
|
|
|
// delete the previous dynamic accelerator table
|
|
m_MenuAccel.DestroyAcceleratorTable ();
|
|
m_strAccelerators.Empty ();
|
|
|
|
// this should have been done in CMenuBar::Create
|
|
ASSERT (GetBitmapSize().cx == 0);
|
|
|
|
if (pMenu != NULL)
|
|
{
|
|
CString strMenuText;
|
|
|
|
// Clear the accels before calling InsertButton
|
|
// which adds accels for each button.
|
|
m_vMenuAccels.clear();
|
|
m_vTrackingAccels.clear();
|
|
|
|
int cMenuItems = pMenu->GetMenuItemCount();
|
|
|
|
// Initialize Unused command IDs pool.
|
|
m_CommandIDUnUsed.clear();
|
|
for (INT idCommand = ID_MTB_FIRST_COMMANDID;
|
|
idCommand < ID_MTB_MENU_LAST;
|
|
idCommand++)
|
|
{
|
|
m_CommandIDUnUsed.insert(idCommand);
|
|
}
|
|
|
|
for (int i = 0; i < cMenuItems; i++)
|
|
{
|
|
// get the menu text and add it to the toolbar
|
|
pMenu->GetMenuString (i, strMenuText, MF_BYPOSITION);
|
|
|
|
// sometimes empty items will be appended to the menu, ignore them
|
|
if (strMenuText.IsEmpty ())
|
|
continue;
|
|
|
|
if (m_CommandIDUnUsed.empty())
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// See if this top level menu has sub menus, if so when menu is clicked
|
|
// it is popped up else it is Action or View or Favorites menu
|
|
// for which submenu is dynamic and has to be constructured.
|
|
// So we set the TBBUTTON.dwData member with submenu (for static menus)
|
|
// or NULL for for dynamic menus like Action, View, Favorites.
|
|
CMenu* pSubMenu = pMenu->GetSubMenu(i);
|
|
DWORD_PTR dwMenuData = NULL;
|
|
BYTE byState = 0;
|
|
|
|
if (pSubMenu)
|
|
{
|
|
// Get an unused command id for this button.
|
|
CommandIDPool::iterator itCommandID = m_CommandIDUnUsed.begin();
|
|
idCommand = *itCommandID;
|
|
m_CommandIDUnUsed.erase(itCommandID);
|
|
dwMenuData = reinterpret_cast<DWORD_PTR>(pSubMenu->GetSafeHmenu());
|
|
}
|
|
else
|
|
{
|
|
UINT uMenuID = pMenu->GetMenuItemID(i);
|
|
switch (uMenuID)
|
|
{
|
|
case ID_ACTION_MENU:
|
|
idCommand = ID_MTB_MENU_ACTION;
|
|
break;
|
|
|
|
case ID_VIEW_MENU:
|
|
idCommand = ID_MTB_MENU_VIEW;
|
|
break;
|
|
|
|
case ID_FAVORITES_MENU:
|
|
idCommand = ID_MTB_MENU_FAVORITES;
|
|
break;
|
|
|
|
case ID_SNAPIN_MENU_PLACEHOLDER:
|
|
/*
|
|
* We add a hidden menu as a marker. Later when snapin
|
|
* calls to insert a menu button we find the position
|
|
* of this menu and add snapin menu before it.
|
|
*/
|
|
idCommand = ID_MTB_MENU_SNAPIN_PLACEHOLDER;
|
|
byState |= TBSTATE_HIDDEN; // Add this as hidden.
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
return;
|
|
break;
|
|
}
|
|
|
|
bool bShow = IsStandardMenuAllowed(uMenuID);
|
|
if (! bShow)
|
|
byState |= TBSTATE_HIDDEN;
|
|
|
|
}
|
|
|
|
|
|
// append this button to the end of the toolbar
|
|
InsertButton (-1, strMenuText, idCommand, dwMenuData, byState, TBSTYLE_AUTOSIZE);
|
|
}
|
|
|
|
// add the accelerator for the child system menu
|
|
std::back_insert_iterator<AccelVector>
|
|
itTrackingInserter = std::back_inserter (m_vTrackingAccels);
|
|
|
|
AddMnemonic (chChildSysMenuMnemonic, ID_MTB_MENU_SYSMENU, 0, itTrackingInserter);
|
|
}
|
|
|
|
|
|
UpdateToolbarSize ();
|
|
AutoSize ();
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: InsertButton
|
|
//
|
|
// Synopsis: Insert a menu (button) to the main menu and then
|
|
// if there is a mnemonic char add it to accelerator
|
|
// and re-load the accelerators.
|
|
//
|
|
// Arguments: [nIndex] - index after which to insert.
|
|
// [strText] - menu text.
|
|
// [idCommand] - ID of command to notify with.
|
|
// [dwMenuData] - either submenu to be displayed (for static menus)
|
|
// or NULL for Action, View & Favorites.
|
|
// [fsState] - additional button states.
|
|
// [fsStyle] - additional button styles.
|
|
//
|
|
// Returns: BOOL
|
|
//
|
|
// Note: dwMenuData is handle to submenu for File, Window, Help menus whose
|
|
// sub-menu is static. But for top level menus like Action, View, Favorites,
|
|
// and Snapin menus it is NULL.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
BOOL CMenuBar::InsertButton (
|
|
int nIndex,
|
|
const CString& strText,
|
|
int idCommand,
|
|
DWORD_PTR dwMenuData,
|
|
BYTE fsState,
|
|
BYTE fsStyle)
|
|
{
|
|
TBBUTTON btn;
|
|
|
|
btn.iBitmap = nIndex;
|
|
btn.idCommand = idCommand;
|
|
|
|
if (fsState & TBSTATE_HIDDEN)
|
|
btn.fsState = fsState;
|
|
else
|
|
btn.fsState = TBSTATE_ENABLED | fsState;
|
|
|
|
btn.fsStyle = TBSTYLE_DROPDOWN | fsStyle;
|
|
btn.dwData = dwMenuData;
|
|
btn.iString = AddString (strText);
|
|
|
|
ASSERT(GetButtonCount() <= cMaxTopLevelMenuItems);
|
|
ASSERT (btn.idCommand <= ID_MTB_MENU_LAST);
|
|
|
|
BOOL bRet = CMMCToolBarCtrlEx::InsertButton (nIndex, &btn);
|
|
if (bRet == FALSE)
|
|
return bRet;
|
|
|
|
// Successfully added the menu button. Now add
|
|
// the accelerator for this item to our dynamic tables
|
|
TCHAR chMnemonic = GetMnemonicChar (static_cast<LPCTSTR>(strText));
|
|
|
|
if (chMnemonic != _T('\0'))
|
|
{
|
|
|
|
std::back_insert_iterator<AccelVector>
|
|
itMenuInserter = std::back_inserter (m_vMenuAccels);
|
|
std::back_insert_iterator<AccelVector>
|
|
itTrackingInserter = std::back_inserter (m_vTrackingAccels);
|
|
|
|
// add the Alt+<mnemonic> accelerator for use all the time
|
|
AddMnemonic (chMnemonic, idCommand, FALT, itMenuInserter);
|
|
|
|
// add the <mnemonic> accelerator for use when we're in menu mode
|
|
AddMnemonic (chMnemonic, idCommand, 0, itTrackingInserter);
|
|
|
|
m_strAccelerators += (TCHAR) tolower (chMnemonic);
|
|
m_strAccelerators += (TCHAR) toupper (chMnemonic);
|
|
}
|
|
|
|
// Re-load the accelerators
|
|
LoadAccels();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: LoadAccels
|
|
//
|
|
// Synopsis: Load the accelerators. (This destorys old accel table
|
|
// and creates accel table).
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: void
|
|
//
|
|
//--------------------------------------------------------------------
|
|
void CMenuBar::LoadAccels()
|
|
{
|
|
// create the accelerator tables for the menu
|
|
m_MenuAccel .CreateAcceleratorTable (m_vMenuAccels.begin (),
|
|
m_vMenuAccels.size ());
|
|
m_TrackingAccel.CreateAcceleratorTable (m_vTrackingAccels.begin (),
|
|
m_vTrackingAccels.size ());
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: InsertMenuButton
|
|
//
|
|
// Synopsis: Insert a menu button to the main menu, called by
|
|
// CMenuButtonsMgr to add any snapin menus.
|
|
//
|
|
// Arguments: [lpszButtonText] - menu text.
|
|
// [bHidden] - Is this menu to be inserted hidden or not.
|
|
// [iPreferredPos] - The preferred position of this button.
|
|
//
|
|
// Returns: LONG, the command id of the inserted button.
|
|
// -1 if failed.
|
|
//
|
|
// Note: The snapin added menus should be added before the Window menu.
|
|
// For this a hidden menu is added (in SetMenu) which tells the
|
|
// position where snapin menu is to be added. If iPreferredPos is -1
|
|
// then find the position of this menu and add the menu before it.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
LONG CMenuBar::InsertMenuButton(LPCTSTR lpszButtonText,
|
|
BOOL bHidden,
|
|
int iPreferredPos)
|
|
{
|
|
AFX_MANAGE_STATE (AfxGetAppModuleState());
|
|
|
|
if (m_CommandIDUnUsed.size() == 0)
|
|
return -1;
|
|
|
|
// Get a command id for this button from the pool
|
|
CommandIDPool::iterator itCommandID = m_CommandIDUnUsed.begin();
|
|
int idCommand = *itCommandID;
|
|
m_CommandIDUnUsed.erase(itCommandID);
|
|
|
|
CString str = lpszButtonText;
|
|
|
|
// Add snapin menus before the SNAPIN_MENU_PLACE holder.
|
|
// See CMenubar::SetMenu.
|
|
if (-1 == iPreferredPos)
|
|
iPreferredPos = CommandToIndex(ID_MTB_MENU_SNAPIN_PLACEHOLDER);
|
|
|
|
BOOL bSuccess = InsertButton(iPreferredPos,
|
|
str, idCommand,
|
|
NULL,
|
|
bHidden ? TBSTATE_HIDDEN : 0,
|
|
TBSTYLE_AUTOSIZE);
|
|
|
|
if (bSuccess)
|
|
{
|
|
UpdateToolbarSize ();
|
|
AutoSize ();
|
|
return idCommand;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: DeleteMenuButton
|
|
//
|
|
// Synopsis: Delete a menu button from the main menu, called by
|
|
// CMenuButtonsMgr
|
|
//
|
|
// Arguments: [nCommandID] - Command ID of the menu.
|
|
//
|
|
// Returns: BOOL
|
|
//
|
|
// Note: Delete should also delete the accelerator.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
BOOL CMenuBar::DeleteMenuButton(INT nCommandID)
|
|
{
|
|
AFX_MANAGE_STATE (AfxGetAppModuleState());
|
|
|
|
int iIndex = CommandToIndex(nCommandID);
|
|
|
|
// We need to delete the mnemonics. So let us get the old
|
|
// mnemonic before deleting the button.
|
|
|
|
// 1. Get the menu text (string) index.
|
|
TBBUTTON tbbi;
|
|
ZeroMemory(&tbbi, sizeof(tbbi));
|
|
BOOL bSuccess = GetButton(iIndex, &tbbi);
|
|
if (FALSE == bSuccess)
|
|
return bSuccess;
|
|
|
|
// 2. Delete the button.
|
|
bSuccess = DeleteButton(iIndex);
|
|
ASSERT(bSuccess);
|
|
if (FALSE == bSuccess)
|
|
return bSuccess;
|
|
|
|
// Add the command id to the unused pool.
|
|
m_CommandIDUnUsed.insert(nCommandID);
|
|
|
|
// Get the mnemonic and Delete it.
|
|
ASSERT(m_ToolbarStringPool.size() > tbbi.iString);
|
|
CString strText = m_ToolbarStringPool[tbbi.iString];
|
|
for (AccelVector::iterator itAccel = m_vMenuAccels.begin();
|
|
itAccel != m_vMenuAccels.end();
|
|
itAccel++)
|
|
{
|
|
if (itAccel->cmd == (WORD)nCommandID)
|
|
{
|
|
m_vMenuAccels.erase(itAccel);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (AccelVector::iterator itTrack = m_vTrackingAccels.begin();
|
|
itTrack != m_vTrackingAccels.end();
|
|
itTrack++)
|
|
{
|
|
if (itTrack->cmd == (WORD)nCommandID)
|
|
{
|
|
m_vTrackingAccels.erase(itTrack);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Delete the mnemonic from m_strAccelerators.
|
|
TCHAR chMnemonicOld = GetMnemonicChar (static_cast<LPCTSTR>(strText));
|
|
if (chMnemonicOld != _T('\0'))
|
|
{
|
|
// CString::Remove cannot be used as it is for VC6. We use the tstring
|
|
// to remove chars from the mnemonic string.
|
|
tstring tstrAccels = m_strAccelerators;
|
|
|
|
tstring::iterator itNewEnd = std::remove(tstrAccels.begin(), tstrAccels.end(), toupper(chMnemonicOld));
|
|
tstrAccels.erase(itNewEnd, tstrAccels.end());
|
|
itNewEnd = std::remove(tstrAccels.begin(), tstrAccels.end(), tolower(chMnemonicOld));
|
|
tstrAccels.erase(itNewEnd, tstrAccels.end());
|
|
m_strAccelerators = tstrAccels.data();
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: SetMenuButton
|
|
//
|
|
// Synopsis: Modify menu button text, called by CMenuButtonsMgr
|
|
//
|
|
// Arguments: [nCommandID] - Command ID.
|
|
// [lpszButtonText] - New text.
|
|
//
|
|
// Returns: LONG, command id of the menu (-1 if failed to change).
|
|
//
|
|
// Note: We delete the old button and add a new button with this
|
|
// name and button id.
|
|
// The reason for not calling SetButtonInfo is it does not allow us
|
|
// to change the string index (iString in TBBUTTON).
|
|
//
|
|
//--------------------------------------------------------------------
|
|
LONG CMenuBar::SetMenuButton(INT nCommandID, LPCTSTR lpszButtonText)
|
|
{
|
|
AFX_MANAGE_STATE (AfxGetAppModuleState());
|
|
|
|
// Note the index and hidden state.
|
|
int iIndex = CommandToIndex(nCommandID);
|
|
bool bHidden = IsButtonHidden(nCommandID);
|
|
|
|
// Please see the note above about why we do Delete and Insert
|
|
// instead of Set.
|
|
BOOL bSuccess = DeleteMenuButton(nCommandID);
|
|
|
|
if (bSuccess)
|
|
return InsertMenuButton(lpszButtonText, bHidden, iIndex);
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::SetMenuFont
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::SetMenuFont ()
|
|
{
|
|
// delete the old font
|
|
m_MenuFont.DeleteObject ();
|
|
|
|
// query the system for the current menu font
|
|
NONCLIENTMETRICS ncm;
|
|
ncm.cbSize = sizeof (ncm);
|
|
SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
|
|
|
|
// use it here, too
|
|
m_MenuFont.CreateFontIndirect (&ncm.lfMenuFont);
|
|
SetFont (&m_MenuFont, FALSE);
|
|
|
|
/*
|
|
* get the metrics for the menu text, so we can use it's height
|
|
*/
|
|
TEXTMETRIC tm;
|
|
CWindowDC dc(this);
|
|
CFont* pOldFont = dc.SelectObject (&m_MenuFont);
|
|
dc.GetTextMetrics (&tm);
|
|
dc.SelectObject (pOldFont);
|
|
|
|
/*
|
|
* Menu item buttons will contain only text (no bitmaps), so set the
|
|
* bitmap width to 0 so we don't have unwanted whitespace.
|
|
*
|
|
* We need to reserve height for the bitmap, though. If we don't do
|
|
* this, then the toolbar will calculate its own height to be too
|
|
* small when there aren't any buttons with text. (This occurs in MDI
|
|
* user mode when the active child is maximized. In that case, the
|
|
* system menu button is visible, but we have no menu items.)
|
|
*/
|
|
SetBitmapSize (CSize (0, std::_MAX ((int) tm.tmHeight,
|
|
GetSystemMetrics (SM_CXSMICON))));
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::OnActivateCurrentPopup
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnActivateCurrentPopup ()
|
|
{
|
|
PopupMenu (GetHotItem(), false /*bHighlightFirstItem*/);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::OnDestroy
|
|
*
|
|
* WM_DESTROY handler for CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnDestroy()
|
|
{
|
|
CMMCToolBarCtrlEx::OnDestroy();
|
|
GetMaxedChildIcon (NULL);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::OnSysCommand
|
|
*
|
|
* WM_SYSCOMMAND handler for CMenuBar.
|
|
*
|
|
* If we want to get the right sound effects for the action, we'll need to
|
|
* let DefWindowProc handle the message.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnSysCommand(UINT nID, LPARAM lParam)
|
|
{
|
|
ASSERT (m_pMDIFrame != NULL);
|
|
|
|
BOOL bMaximized;
|
|
CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive (&bMaximized);
|
|
|
|
// if the user has quick fingers he may succeed in issuing the command
|
|
// while the document is being closed - thus there may not be any
|
|
// child windows at all. We ignore the command in such case
|
|
// see bug #119775: MMC Crashes when snapin delays in closing down.
|
|
if (pwndActive == NULL)
|
|
return;
|
|
|
|
switch (nID & 0xFFF0)
|
|
{
|
|
case SC_MINIMIZE: pwndActive->ShowWindow (SW_MINIMIZE); break;
|
|
case SC_MAXIMIZE: pwndActive->ShowWindow (SW_MAXIMIZE); break;
|
|
case SC_RESTORE: pwndActive->ShowWindow (SW_RESTORE); break;
|
|
case SC_CLOSE: pwndActive->SendMessage (WM_CLOSE); break;
|
|
default:
|
|
CMMCToolBarCtrlEx::OnSysCommand (nID, lParam);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::OnSettingChange
|
|
*
|
|
* WM_SETTINGCHANGE handler for CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
|
|
{
|
|
if (uFlags == SPI_SETNONCLIENTMETRICS)
|
|
{
|
|
// the system menu font may have changed; update it now
|
|
SetMenuFont ();
|
|
|
|
// resize the decoration window
|
|
SizeDecoration ();
|
|
|
|
// update the size of the system menu button
|
|
TBBUTTONINFO btni;
|
|
btni.cbSize = sizeof (btni);
|
|
btni.dwMask = TBIF_SIZE;
|
|
btni.cx = static_cast<WORD>(GetSystemMetrics (SM_CXSMICON));
|
|
SetButtonInfo (ID_MTB_MENU_SYSMENU, &btni);
|
|
|
|
m_fMaxedChildIconIsInvalid = true;
|
|
|
|
// auto-size the toolbar
|
|
UpdateToolbarSize ();
|
|
AutoSize ();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::SizeDecoration
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::SizeDecoration ()
|
|
{
|
|
if (m_pMDIDec.get() == NULL)
|
|
return;
|
|
|
|
// jiggle the window's size so it will auto-size...
|
|
m_pMDIDec->SetWindowPos (NULL, 0, 0, 10, 10,
|
|
SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
|
|
|
|
CRect rect;
|
|
m_pMDIDec->GetClientRect (rect);
|
|
|
|
// ...and update its band to accomodate it
|
|
REBARBANDINFO rbi;
|
|
PrepBandInfo (&rbi, RBBIM_SIZE | RBBIM_CHILDSIZE);
|
|
rbi.cx = rect.Width();
|
|
rbi.cxMinChild = rect.Width();
|
|
rbi.cyMinChild = rect.Height();
|
|
|
|
m_pRebar->SetBandInfo (GetDecorationBandIndex (), &rbi);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* void CMenuBar::OnDropDown
|
|
*
|
|
* Reflected TBN_DROPDOWN handler for void CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
afx_msg void CMenuBar::OnDropDown (
|
|
NMHDR * pHdr,
|
|
LRESULT * pResult)
|
|
{
|
|
ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
|
|
|
|
// pop up the menu. Use async method, because toolbar will
|
|
// keeb the button hilited until this function returns
|
|
PopupMenuAsync (CommandToIndex (((LPNMTOOLBAR) pHdr)->iItem));
|
|
|
|
// drop down notification handled here
|
|
*pResult = TBDDRET_DEFAULT;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* void CMenuBar::OnGetDispInfo
|
|
*
|
|
* Reflected TBN_GETDISPINFO handler for void CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
afx_msg void CMenuBar::OnGetDispInfo (
|
|
NMHDR * pHdr,
|
|
LRESULT * pResult)
|
|
{
|
|
ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
|
|
|
|
if (m_fDecorationsShowing)
|
|
{
|
|
NMTBDISPINFO* ptbdi = reinterpret_cast<NMTBDISPINFO *>(pHdr);
|
|
|
|
if ((ptbdi->dwMask & TBNF_IMAGE) &&
|
|
(ptbdi->idCommand != ID_MTB_MENU_SYSMENU))
|
|
{
|
|
ptbdi->iImage = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* void CMenuBar::OnCustomDraw
|
|
*
|
|
* Reflected NM_CUSTOMDRAW handler for void CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
afx_msg void CMenuBar::OnCustomDraw (
|
|
NMHDR * pHdr,
|
|
LRESULT * pResult)
|
|
{
|
|
ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
|
|
LPNMCUSTOMDRAW pnmcd = reinterpret_cast<LPNMCUSTOMDRAW>(pHdr);
|
|
|
|
switch (pnmcd->dwDrawStage)
|
|
{
|
|
case CDDS_PREPAINT:
|
|
// notify for individual buttons
|
|
*pResult = CDRF_NOTIFYITEMDRAW;
|
|
break;
|
|
|
|
case CDDS_ITEMPREPAINT:
|
|
// draw the system menu button manually
|
|
if (pnmcd->dwItemSpec == ID_MTB_MENU_SYSMENU)
|
|
{
|
|
if (m_fMaxedChildIconIsInvalid)
|
|
GetMaxedChildIcon (m_pMDIFrame->MDIGetActive());
|
|
|
|
if (m_hMaxedChildIcon != NULL)
|
|
{
|
|
/*
|
|
* compute the location at which we'll draw,
|
|
* biasing down and to the left
|
|
*/
|
|
CRect rect = pnmcd->rc;
|
|
int dx = (rect.Width() - GetSystemMetrics(SM_CXSMICON) ) / 2;
|
|
int dy = (rect.Height() - GetSystemMetrics(SM_CYSMICON) + 1) / 2;
|
|
|
|
|
|
|
|
/*
|
|
* Preserve icon shape when BitBlitting it to a
|
|
* mirrored DC.
|
|
*/
|
|
DWORD dwLayout=0L;
|
|
if ((dwLayout=GetLayout(pnmcd->hdc)) & LAYOUT_RTL)
|
|
{
|
|
SetLayout(pnmcd->hdc, dwLayout|LAYOUT_BITMAPORIENTATIONPRESERVED);
|
|
}
|
|
|
|
DrawIconEx (pnmcd->hdc,
|
|
rect.left + dx,
|
|
rect.top + dy,
|
|
m_hMaxedChildIcon, 0, 0, 0,
|
|
NULL, DI_NORMAL);
|
|
|
|
|
|
/*
|
|
* Restore the DC to its previous layout state.
|
|
*/
|
|
if (dwLayout & LAYOUT_RTL)
|
|
{
|
|
SetLayout(pnmcd->hdc, dwLayout);
|
|
}
|
|
}
|
|
|
|
// skip the default drawing
|
|
*pResult = CDRF_SKIPDEFAULT;
|
|
}
|
|
else
|
|
{
|
|
// do the default drawing
|
|
*pResult = CDRF_DODEFAULT;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::PopupMenuAsync
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::PopupMenuAsync (int nItemIdex)
|
|
{
|
|
PostMessage (WM_POPUP_ASYNC, nItemIdex);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* void CMenuBar::OnPopupAsync
|
|
*
|
|
* WM_POPUP_ASYNC handler for void CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
afx_msg LRESULT CMenuBar::OnPopupAsync (WPARAM wParam, LPARAM)
|
|
{
|
|
PopupMenu (wParam, false /*bHighlightFirstItem*/);
|
|
return (0);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CMenuBar::OnHotItemChange
|
|
*
|
|
* PURPOSE: Called when item hiliting changes. Used here to detect when menu
|
|
* is dissmissed to reset the UI
|
|
*
|
|
* PARAMETERS:
|
|
* NMHDR* pNMHDR
|
|
* LRESULT* pResult
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
afx_msg void CMenuBar::OnHotItemChange(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CMenuBar::OnHotItemChange"));
|
|
|
|
// parameter chack
|
|
sc = ScCheckPointers(pNMHDR, pResult);
|
|
if (sc)
|
|
return;
|
|
|
|
// init out parameter
|
|
*pResult = 0;
|
|
|
|
// let the base class do it's job
|
|
CMMCToolBarCtrlEx::OnHotItemChange(pNMHDR, pResult);
|
|
|
|
// if menu is dismissed and not because of popup being displayed,
|
|
// we need to revert to initial state by hiding accelerators
|
|
LPNMTBHOTITEM lpNmTbHotItem = (LPNMTBHOTITEM)pNMHDR;
|
|
if ( (*pResult == 0) &&
|
|
(lpNmTbHotItem->dwFlags & HICF_LEAVING) &&
|
|
!m_bInProgressDisplayingPopup )
|
|
{
|
|
SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CMenuBar::PopupMenu
|
|
*
|
|
* PURPOSE: Displays the popup menu specified by the index.
|
|
*
|
|
* PARAMETERS:
|
|
* int nItemIndex :
|
|
* bool bHighlightFirstItem : true to automatically highlight the first item
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void
|
|
CMenuBar::PopupMenu (int nItemIndex, bool bHighlightFirstItem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CMenuBar::PopupMenu"));
|
|
|
|
// get the rectangle that we don't want the popup to overlap
|
|
CRect rectExclude;
|
|
CPopupTrackContext popupMonitor(this, nItemIndex);
|
|
|
|
/*
|
|
* OnIdle updates various member variables such as
|
|
* m_fDecorationsShowing. It must be called before
|
|
* popup menu is created to ensure accurate information
|
|
*/
|
|
OnIdle();
|
|
|
|
// there is high possibility, that the code in following block
|
|
// adds nothing to the current functionality. It is left as it was
|
|
// in previos implementation, since it isn't obvios if removing it
|
|
// does not break anything.
|
|
// Even if the block is removed, call to EndTracking() at the end of method
|
|
// must still be present to exit tracking if it was put by entering menu
|
|
// via pressing the ALT key
|
|
{
|
|
/*
|
|
* make sure the frame's tracking manager is in tracking mode
|
|
*/
|
|
CMainFrame* pFrame = AMCGetMainWnd();
|
|
if (pFrame != NULL)
|
|
{
|
|
CToolbarTracker* pTracker = pFrame->GetToolbarTracker();
|
|
ASSERT (pTracker != NULL);
|
|
|
|
if (!pTracker->IsTracking())
|
|
pTracker->BeginTracking();
|
|
}
|
|
|
|
BeginTracking ();
|
|
}
|
|
|
|
// following is to indicate that the change in menu (if one does occurr)
|
|
// is because of attempt to switch to another submenu, and should not be
|
|
// treated as dismissing the menu. thus accelerator state should not be changed
|
|
m_bInProgressDisplayingPopup = true;
|
|
|
|
// There are two ways the popup menu is dismissed. it either:
|
|
// - request to display the new popup (left & right arrows, mouse on other item, etc)
|
|
// - or request to close the menu ( command is selected, ESC is pressed )
|
|
// following loop will continue displaying menus until request to close the menu is received
|
|
do {
|
|
|
|
GetItemRect (nItemIndex, rectExclude);
|
|
MapWindowPoints (NULL, rectExclude);
|
|
|
|
// get the information for this button
|
|
TBBUTTON btn;
|
|
GetButton (nItemIndex, &btn);
|
|
|
|
// if the requested button is ignorable, punt
|
|
if (::IsIgnorableButton (btn))
|
|
break;
|
|
|
|
// get the popup menu to display
|
|
HMENU hPopupMenu = (HMENU) btn.dwData;
|
|
|
|
// For system menu hPopupMenu will be NULL.
|
|
// If system menu is requested get it now.
|
|
if (ID_MTB_MENU_SYSMENU == btn.idCommand)
|
|
{
|
|
ASSERT (m_fDecorationsShowing);
|
|
ASSERT (m_pMDIFrame != NULL);
|
|
|
|
CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive();
|
|
ASSERT (pwndActive != NULL);
|
|
if (pwndActive == NULL)
|
|
break;
|
|
|
|
hPopupMenu = pwndActive->GetSystemMenu(FALSE)->GetSafeHmenu();
|
|
}
|
|
|
|
// display the button's popup menu
|
|
TPMPARAMS tpm;
|
|
tpm.cbSize = sizeof(TPMPARAMS);
|
|
tpm.rcExclude = rectExclude;
|
|
|
|
SetHotItem (-1);
|
|
PressButton (btn.idCommand, TRUE);
|
|
|
|
// Get the point where the menu should be popped up.
|
|
bool fLayoutRTL = (GetExStyle() & WS_EX_LAYOUTRTL);
|
|
POINT pt;
|
|
pt.y = rectExclude.bottom;
|
|
pt.x = (fLayoutRTL) ? rectExclude.right : rectExclude.left;
|
|
|
|
/*
|
|
* Bug 17342: TrackPopupMenuEx doesn't place the menu well if the
|
|
* x-coordinate of its origin is off-screen to the left, so prevent
|
|
* this from occurring. TrackPopupMenuEx *does* work fine when the
|
|
* x-coordinate is off-screen to the right or if the y-coordinate is
|
|
* off-screen to the bottom, so we don't need to account for those
|
|
* cases. (The system prevents placing the window such that the
|
|
* y-coordinate would be off-screen to the top.)
|
|
*
|
|
* Bug 173543: We can't assume that an x-coordinate less than 0 is
|
|
* off the screen. For multimon systems with the primary monitor
|
|
* on the right, the left monitor will have negative x coordinates.
|
|
* Our left-most position will be the left edge of the monitor nearest
|
|
* where the menu will be displayed.
|
|
*/
|
|
int xMin = 0;
|
|
HMONITOR hmonMenu = MonitorFromPoint (pt, MONITOR_DEFAULTTONEAREST);
|
|
|
|
if (hmonMenu != NULL)
|
|
{
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
|
|
if (GetMonitorInfo (hmonMenu, &mi))
|
|
xMin = (fLayoutRTL) ? mi.rcWork.right : mi.rcWork.left;
|
|
}
|
|
|
|
if ((!fLayoutRTL && (pt.x < xMin)) || (fLayoutRTL && (pt.x > xMin)))
|
|
pt.x = xMin;
|
|
|
|
// HACK: post a bogus down arrow so the first menu item will be selected
|
|
if(bHighlightFirstItem)
|
|
{
|
|
CWnd* pwndFocus = GetFocus();
|
|
|
|
if (pwndFocus != NULL)
|
|
pwndFocus->PostMessage (WM_KEYDOWN, VK_DOWN, 1);
|
|
}
|
|
|
|
// monitor what's the popup menu fate
|
|
popupMonitor.StartMonitoring();
|
|
|
|
// Child window wont exist if there is no views, so check this ptr before using it.
|
|
CChildFrame* pChildFrame = dynamic_cast<CChildFrame*>(m_pMDIFrame->MDIGetActive());
|
|
|
|
// hPopupMenu exists only if the sub-menus were added thro resource like File, Window
|
|
// and Help menus. The Action, View, Favorites and any snapin added menu's sub-menus
|
|
// are not added thro resources so for them hPopupmenu is null.
|
|
if (! hPopupMenu)
|
|
{
|
|
sc = ScCheckPointers(pChildFrame, E_UNEXPECTED);
|
|
if (sc)
|
|
{
|
|
sc.TraceAndClear();
|
|
break;
|
|
}
|
|
|
|
CAMCView *pAMCView = pChildFrame->GetAMCView();
|
|
sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
|
|
if (sc)
|
|
{
|
|
sc.TraceAndClear();
|
|
break;
|
|
}
|
|
|
|
switch(btn.idCommand)
|
|
{
|
|
case ID_MTB_MENU_ACTION:
|
|
pAMCView->OnActionMenu(pt, rectExclude);
|
|
break;
|
|
|
|
case ID_MTB_MENU_VIEW:
|
|
pAMCView->OnViewMenu(pt, rectExclude);
|
|
break;
|
|
|
|
case ID_MTB_MENU_FAVORITES:
|
|
pAMCView->OnFavoritesMenu(pt, rectExclude);
|
|
break;
|
|
|
|
// Assumption if none of the above then it is snapin added menu.
|
|
// We try to forward this to snapin else we get an error.
|
|
default:
|
|
{
|
|
// If this is one of the MenuButtons inserted by
|
|
// the CMenuButtonsMgrImpl, notify CMenuButtonsMgrImpl
|
|
// to do TrackPopupMenu.
|
|
|
|
// Get the CMenuButtonsMgrImpl from the ChildFrame.
|
|
CMenuButtonsMgrImpl* pMenuBtnsMgr = pChildFrame->GetMenuButtonsMgr();
|
|
sc = ScCheckPointers(pMenuBtnsMgr, E_UNEXPECTED);
|
|
if (sc)
|
|
break;
|
|
|
|
// Notify CMenuButtonsMgr to popup a menu.
|
|
sc = pMenuBtnsMgr->ScNotifyMenuClick(btn.idCommand, pt);
|
|
if (sc)
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (sc)
|
|
{
|
|
sc.TraceAndClear();
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ASSERT (::IsMenu (hPopupMenu));
|
|
|
|
HWND hwndMenuOwner = AfxGetMainWnd()->GetSafeHwnd();
|
|
|
|
TrackPopupMenuEx (hPopupMenu,
|
|
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
|
|
pt.x, pt.y, hwndMenuOwner, &tpm);
|
|
}
|
|
|
|
// Clear the status bar.
|
|
if (pChildFrame)
|
|
sc = pChildFrame->ScSetStatusText(TEXT(""));
|
|
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
PressButton (btn.idCommand, FALSE);
|
|
SetHotItem (-1);
|
|
|
|
// the loop will continue if it was requested to display the new popup menu.
|
|
// if it was requested to simply close the menu, execution will exit the loop
|
|
}while ( popupMonitor.WasAnotherPopupRequested(nItemIndex) );
|
|
|
|
m_bInProgressDisplayingPopup = false;
|
|
//reset the UI by hiding the accelerators (since we are done)
|
|
SendMessage( WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS));
|
|
|
|
EndTracking();
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::OnAccelPopup
|
|
*
|
|
* Keyboard accelerator handler for CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnAccelPopup (UINT cmd)
|
|
{
|
|
PopupMenu (CommandToIndex (cmd), true /*bHighlightFirstItem*/);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* void CMenuBar::OnUpdateAllCmdUI
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnUpdateAllCmdUI (CCmdUI* pCmdUI)
|
|
{
|
|
pCmdUI->Enable ();
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::AddString
|
|
*
|
|
* The toolbar control doesn't provide a way to delete strings once they've
|
|
* been added, so we'll check our cache of strings that we've already added
|
|
* to the toolbar so we don't add wasteful duplicate strings.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMenuBar::AddString (const CString& strAdd)
|
|
{
|
|
// -1 for empty strings
|
|
if (strAdd.IsEmpty())
|
|
return (-1);
|
|
|
|
// check the cache
|
|
ToolbarStringPool::iterator it = std::find (m_ToolbarStringPool.begin(),
|
|
m_ToolbarStringPool.end(),
|
|
strAdd);
|
|
|
|
// if we found a hit in the cache, return the cached index
|
|
if (it != m_ToolbarStringPool.end())
|
|
return (it - m_ToolbarStringPool.begin());
|
|
|
|
|
|
// new string, add it to the cache...
|
|
m_ToolbarStringPool.push_back (strAdd);
|
|
|
|
|
|
// ...and to the toolbar, including a double-NULL
|
|
int cchAdd = strAdd.GetLength() + 1;
|
|
LPTSTR pszAdd = (LPTSTR) _alloca ((cchAdd + 1) * sizeof (TCHAR));
|
|
_tcscpy (pszAdd, strAdd);
|
|
pszAdd[cchAdd] = 0;
|
|
|
|
int nIndex = AddStrings (pszAdd);
|
|
|
|
// make sure the toolbar's string index matches the cache's string index
|
|
ASSERT (nIndex == m_ToolbarStringPool.end()-m_ToolbarStringPool.begin()-1);
|
|
|
|
return (nIndex);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::GetMenuBandIndex
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMenuBar::GetMenuBandIndex () const
|
|
{
|
|
return (m_pRebar->IdToIndex (GetDlgCtrlID ()));
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::GetDecorationBandIndex
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMenuBar::GetDecorationBandIndex () const
|
|
{
|
|
return (m_pRebar->IdToIndex (ID_MDIDECORATION));
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::GetFirstButtonIndex
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMenuBar::GetFirstButtonIndex ()
|
|
{
|
|
// make sure the system menu isn't the first one activated
|
|
return (GetNextButtonIndex (0));
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::OnIdle
|
|
*
|
|
* WM_IDLE handler for CMenuBar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::OnIdle ()
|
|
{
|
|
/*----------------------------------------------------------*/
|
|
/* If there's no MDI frame, that means this menu is serving */
|
|
/* an SDI window. We don't have to do any special stuff to */
|
|
/* simulate the MDI menu UI, so bail now. */
|
|
/*----------------------------------------------------------*/
|
|
if (m_pMDIFrame == NULL)
|
|
return;
|
|
|
|
ProgramMode eMode = AMCGetApp()->GetMode();
|
|
|
|
// if we're in SDI User mode, bail now as well
|
|
if (eMode == eMode_User_SDI)
|
|
{
|
|
#ifdef DBG
|
|
// the decorations should be hidden
|
|
REBARBANDINFO rbi;
|
|
PrepBandInfo (&rbi, RBBIM_STYLE);
|
|
m_pRebar->GetBandInfo (GetDecorationBandIndex(), &rbi);
|
|
ASSERT (rbi.fStyle & RBBS_HIDDEN);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/*---------------------------------------------------------------*/
|
|
/* We should be able to use MDIGetActive(&fMaximized) to tell */
|
|
/* whether the active window is maximized, instead of calling */
|
|
/* pwndActive->IsZoomed(). However, fMaximized doesn't always */
|
|
/* get initialized correctly in certain low memory/slow machine */
|
|
/* situations. This was the cause of Bug 133179, logged by SQL. */
|
|
/*---------------------------------------------------------------*/
|
|
CMDIChildWnd* pwndActive = m_pMDIFrame->MDIGetActive ();
|
|
bool fShow = (pwndActive != NULL) && pwndActive->IsZoomed();
|
|
|
|
REBARBANDINFO rbi;
|
|
PrepBandInfo (&rbi, RBBIM_STYLE);
|
|
m_pRebar->GetBandInfo (GetMenuBandIndex(), &rbi);
|
|
|
|
// if the menu bar is hidden, the decorations must be hidden as well
|
|
if (rbi.fStyle & RBBS_HIDDEN)
|
|
fShow = false;
|
|
|
|
if (fShow != m_fDecorationsShowing)
|
|
{
|
|
// show/hide the MDI decorations
|
|
m_pRebar->ShowBand (GetDecorationBandIndex(), fShow);
|
|
|
|
GetMaxedChildIcon (pwndActive);
|
|
HideButton (ID_MTB_MENU_SYSMENU, !fShow);
|
|
UpdateToolbarSize ();
|
|
|
|
// remember the new setting
|
|
m_fDecorationsShowing = fShow;
|
|
}
|
|
|
|
// otherwise, see if a window was maximized before but a different one is maxed now
|
|
else if (fShow && (m_pwndLastActive != pwndActive))
|
|
{
|
|
// get the new active window's icon
|
|
GetMaxedChildIcon (pwndActive);
|
|
|
|
// repaint the menu and MDI decorations
|
|
InvalidateRect (NULL);
|
|
m_pMDIDec->InvalidateRect (NULL);
|
|
}
|
|
|
|
// remember the currently active window
|
|
m_pwndLastActive = pwndActive;
|
|
|
|
// insure that it's safe to keep this pointer around
|
|
ASSERT ((m_pwndLastActive == NULL) ||
|
|
(CWnd::FromHandlePermanent (m_pwndLastActive->m_hWnd) != NULL));
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::DeleteMaxedChildIcon
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
void
|
|
CMenuBar::DeleteMaxedChildIcon()
|
|
{
|
|
// destroy the previous icon, if we need to
|
|
if (m_fDestroyChildIcon)
|
|
{
|
|
ASSERT (m_hMaxedChildIcon != NULL);
|
|
DestroyIcon (m_hMaxedChildIcon);
|
|
m_hMaxedChildIcon = NULL;
|
|
m_fDestroyChildIcon = false;
|
|
}
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CMenuBar::GetMaxedChildIcon
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::GetMaxedChildIcon (CWnd* pwnd)
|
|
{
|
|
DeleteMaxedChildIcon();
|
|
|
|
// get the small icon for the given window
|
|
if (IsWindow (pwnd->GetSafeHwnd()))
|
|
{
|
|
HICON hOriginalIcon = pwnd->GetIcon (false /*bBigIcon*/);
|
|
|
|
m_hMaxedChildIcon = (HICON) CopyImage (
|
|
hOriginalIcon, IMAGE_ICON,
|
|
GetSystemMetrics (SM_CXSMICON),
|
|
GetSystemMetrics (SM_CYSMICON),
|
|
LR_COPYFROMRESOURCE | LR_COPYRETURNORG);
|
|
|
|
// if the system had to create a new icon, we'll have to destroy it later
|
|
if ((m_hMaxedChildIcon != NULL) && (m_hMaxedChildIcon != hOriginalIcon))
|
|
m_fDestroyChildIcon = true;
|
|
}
|
|
|
|
m_fMaxedChildIconIsInvalid = false;
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMenuBar::GetAccelerators
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::GetAccelerators (int cchBuffer, LPTSTR lpBuffer) const
|
|
{
|
|
lstrcpyn (lpBuffer, m_strAccelerators, cchBuffer);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CMenuBar::IsStandardMenuAllowed
|
|
//
|
|
// Synopsis: Is this standard MMC menu allowed or not.
|
|
//
|
|
// Arguments: uMenuID - The menu ID.
|
|
//
|
|
// Returns: bool
|
|
//
|
|
//--------------------------------------------------------------------
|
|
bool CMenuBar::IsStandardMenuAllowed(UINT uMenuID)
|
|
{
|
|
DECLARE_SC(sc, _T("CMenuBar::IsStandardMenuAllowed"));
|
|
|
|
/*
|
|
* We add a hidden menu as a marker. Later when snapin
|
|
* calls to insert a menu button we find the position
|
|
* of this menu and add snapin menu before it.
|
|
* So this acts as Std menu which is always allowed.
|
|
*/
|
|
if (uMenuID == ID_SNAPIN_MENU_PLACEHOLDER)
|
|
return true;
|
|
|
|
// First make sure it is one of the std menus.
|
|
if ( (uMenuID != ID_FAVORITES_MENU) &&
|
|
(uMenuID != ID_ACTION_MENU) &&
|
|
(uMenuID != ID_VIEW_MENU))
|
|
{
|
|
sc = E_INVALIDARG;
|
|
return true;
|
|
}
|
|
|
|
// Ask view data if std menus are allowed.
|
|
|
|
CMainFrame* pMainFrame = AMCGetMainWnd();
|
|
sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
|
|
if (sc)
|
|
return false;
|
|
|
|
CAMCView *pAMCView = pMainFrame->GetActiveAMCView();
|
|
sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
|
|
if (sc)
|
|
return false;
|
|
|
|
SViewData* pViewData = pAMCView->GetViewData();
|
|
sc = ScCheckPointers(pViewData, E_UNEXPECTED);
|
|
if (sc)
|
|
return false;
|
|
|
|
if (! pViewData->IsStandardMenusAllowed())
|
|
return false;
|
|
|
|
if (uMenuID != ID_FAVORITES_MENU)
|
|
return true;
|
|
|
|
/*
|
|
* Display the Favorites menu button if we are in author mode, or if
|
|
* we're in user mode and we have at least one favorite. If we're in
|
|
* user mode and no favorites are defined, hide the Favorites button.
|
|
*/
|
|
bool fShowFavorites = true;
|
|
CAMCApp* pApp = AMCGetApp();
|
|
|
|
if (pApp != NULL)
|
|
{
|
|
/*
|
|
* show favorites in author mode
|
|
*/
|
|
fShowFavorites = (pApp->GetMode() == eMode_Author);
|
|
|
|
/*
|
|
* not author mode? see if we have any favorites
|
|
*/
|
|
if (!fShowFavorites)
|
|
{
|
|
CAMCDoc* pDoc = CAMCDoc::GetDocument ();
|
|
|
|
if (pDoc != NULL)
|
|
{
|
|
CFavorites* pFavorites = pDoc->GetFavorites();
|
|
|
|
if (pFavorites != NULL)
|
|
fShowFavorites = !pFavorites->IsEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
return fShowFavorites;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CMenuBar::ScShowMMCMenus
|
|
//
|
|
// Synopsis: Show or Hide the MMC menus (Action, View & Favorites).
|
|
// Called from customize view.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: SC
|
|
//
|
|
// Note: As this is called from Customize view no need to look
|
|
// at viewdata::IsStandardMenusAllowed.
|
|
// Also Favorites button is not added in first place
|
|
// if it is not allowed. So no need for Favorites menu
|
|
// special case.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SC CMenuBar::ScShowMMCMenus (bool bShow)
|
|
{
|
|
DECLARE_SC(sc, _T("CMenuBar::ScShowMMCMenus"));
|
|
|
|
// Go through the menu buttons & find Action, View & Favorites.
|
|
|
|
TBBUTTON btn;
|
|
int cButtons = GetButtonCount();
|
|
|
|
for (int i = 0; i < cButtons; ++i)
|
|
{
|
|
GetButton (i, &btn);
|
|
|
|
// Skip if button is not action/view/favs.
|
|
if ( (btn.idCommand != ID_MTB_MENU_FAVORITES) &&
|
|
(btn.idCommand != ID_MTB_MENU_ACTION) &&
|
|
(btn.idCommand != ID_MTB_MENU_VIEW) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// For favorites menu see if it is appropriate to un-hide it.
|
|
// In non-author mode if there is no favorites then this menu
|
|
// is hidden.
|
|
if ( bShow &&
|
|
(btn.idCommand == ID_MTB_MENU_FAVORITES) &&
|
|
(! IsStandardMenuAllowed(ID_FAVORITES_MENU)) )
|
|
continue;
|
|
|
|
HideButton(btn.idCommand, !bShow);
|
|
}
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMenuBar::ScInsertAccPropIDs
|
|
*
|
|
* Inserts the IDs of the accessibility properties supported by CMenuBar
|
|
* (see ScGetPropValue).
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMenuBar::ScInsertAccPropIDs (PropIDCollection& v)
|
|
{
|
|
DECLARE_SC (sc, _T("CMenuBar::ScInsertAccPropIDs"));
|
|
|
|
/*
|
|
* let the base class do its thing
|
|
*/
|
|
sc = CMMCToolBarCtrlEx::ScInsertAccPropIDs (v);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
/*
|
|
* add our own properties
|
|
*/
|
|
v.push_back (PROPID_ACC_ROLE);
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMenuBar::ScGetPropValue
|
|
*
|
|
* Returns accessibility properties supported by CMenuBar.
|
|
*
|
|
* If a property is returned, fGotProp is set to true. If it is not
|
|
* returned, the value of fGotProp is unchanged, since the property might
|
|
* have been provided by a base/derived class.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMenuBar::ScGetPropValue (
|
|
HWND hwnd, // I:accessible window
|
|
DWORD idObject, // I:accessible object
|
|
DWORD idChild, // I:accessible child object
|
|
const MSAAPROPID& idProp, // I:property requested
|
|
VARIANT& varValue, // O:returned property value
|
|
BOOL& fGotProp) // O:was a property returned?
|
|
{
|
|
DECLARE_SC (sc, _T("CMenuBar::ScGetPropValue"));
|
|
|
|
/*
|
|
* call the base class
|
|
*/
|
|
sc = CMMCToolBarCtrlEx::ScGetPropValue (hwnd, idObject, idChild, idProp, varValue, fGotProp);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
/*
|
|
* now handle requests for properties we support...role first
|
|
*/
|
|
if (idProp == PROPID_ACC_ROLE)
|
|
{
|
|
/*
|
|
* don't override the property for child elements,
|
|
* don't return a property
|
|
*/
|
|
if (idChild != CHILDID_SELF)
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: no role for child %d"), idChild);
|
|
return (sc);
|
|
}
|
|
|
|
/*
|
|
* the control itself has a role of menubar
|
|
*/
|
|
V_VT(&varValue) = VT_I4;
|
|
V_I4(&varValue) = ROLE_SYSTEM_MENUBAR;
|
|
fGotProp = true;
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
|
|
}
|
|
else if (idProp == PROPID_ACC_STATE)
|
|
{
|
|
/*
|
|
* Bug 148132: if the base class returned a property, append
|
|
* STATE_SYSTEM_HASPOPUP so Narrator et al will announce "has a
|
|
* submenu" when the menu item is highlighted
|
|
*/
|
|
if (fGotProp)
|
|
{
|
|
ASSERT (V_VT(&varValue) == VT_I4);
|
|
V_I4(&varValue) |= STATE_SYSTEM_HASPOPUP;
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: Appending STATE_SYSTEM_HASPOPUP, Returning 0x%08x"), V_I4(&varValue));
|
|
}
|
|
else
|
|
{
|
|
V_VT(&varValue) = VT_I4;
|
|
V_I4(&varValue) = STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_HASPOPUP;
|
|
fGotProp = true;
|
|
|
|
if (IsTrackingToolBar() && (GetHotItem() == (idChild-1) /*0-based*/))
|
|
V_I4(&varValue) |= STATE_SYSTEM_FOCUSED | STATE_SYSTEM_HOTTRACKED;
|
|
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
|
|
}
|
|
}
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMenuBar::BeginTracking2
|
|
*
|
|
* Fires EVENT_SYSTEM_MENUSTART event accessibility event, then call base
|
|
* class.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::BeginTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
|
|
{
|
|
NotifyWinEvent (EVENT_SYSTEM_MENUSTART, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
|
|
CMMCToolBarCtrlEx::BeginTracking2 (pAuxWnd);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMenuBar::EndTracking2
|
|
*
|
|
* Fires EVENT_SYSTEM_MENUEND event accessibility event, then call base
|
|
* class.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMenuBar::EndTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
|
|
{
|
|
NotifyWinEvent (EVENT_SYSTEM_MENUEND, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
|
|
CMMCToolBarCtrlEx::EndTracking2 (pAuxWnd);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPopupTrackContext
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::CPopupTrackContext
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
CPopupTrackContext::CPopupTrackContext (
|
|
CMenuBar* pMenuBar,
|
|
int nCurrentPopupIndex)
|
|
:
|
|
m_pMenuBar (pMenuBar),
|
|
m_cButtons (pMenuBar->GetButtonCount()),
|
|
m_ptLastMouseMove (GetMessagePos()),
|
|
m_ptLButtonDown (GetMessagePos()),
|
|
m_dwLButtonDownTime (GetMessageTime()),
|
|
m_bPopupMonitorHooksActive(false),
|
|
m_iRequestForNewPopup(-1)
|
|
{
|
|
ASSERT_VALID (pMenuBar);
|
|
ASSERT (s_pTrackContext == NULL);
|
|
|
|
m_nCurrentPopupIndex = nCurrentPopupIndex;
|
|
m_fCurrentlyOnPopupItem = false;
|
|
m_cCascadedPopups = 0;
|
|
|
|
ASSERT (m_nCurrentPopupIndex < m_cButtons);
|
|
ASSERT (m_nCurrentPopupIndex >= 0);
|
|
|
|
// build the vector of button boundaries
|
|
m_ButtonBoundaries.resize (m_cButtons + 1, 0);
|
|
ASSERT (m_ButtonBoundaries.size() == m_cButtons + 1);
|
|
ASSERT (m_ButtonBoundaries.size() >= 2);
|
|
|
|
CRect rectButton (0, 0, 0, 0);
|
|
POINT ptTopLeft = rectButton.TopLeft();
|
|
|
|
for (int i = 0; i < m_cButtons; i++)
|
|
{
|
|
// GetItemRect will fail (acceptably) for hidden buttons,
|
|
// but should otherwise succeed.
|
|
VERIFY (pMenuBar->GetItemRect(i, rectButton) ||
|
|
pMenuBar->IsButtonHidden(pMenuBar->IndexToCommand(i)) );
|
|
|
|
// Do not map rectButton from Client To Screen.
|
|
// Map a copy of it (in ptTopLeft). So if a hidden
|
|
// button follows, it can use the rectButton.TopLeft()
|
|
// value and map it.
|
|
ptTopLeft = rectButton.TopLeft();
|
|
pMenuBar->ClientToScreen (&ptTopLeft);
|
|
m_ButtonBoundaries[i] = ptTopLeft.x;
|
|
|
|
// make m_rectAllButtons as a union of all buttons
|
|
if (i == 0)
|
|
m_rectAllButtons = rectButton;
|
|
else
|
|
m_rectAllButtons |= rectButton;
|
|
}
|
|
|
|
ptTopLeft = rectButton.BottomRight();
|
|
pMenuBar->ClientToScreen (&ptTopLeft);
|
|
m_ButtonBoundaries[m_cButtons] = ptTopLeft.x;
|
|
|
|
pMenuBar->ClientToScreen (&m_rectAllButtons);
|
|
// decrease m_rectAllButtons slightly
|
|
m_rectAllButtons.left = m_rectAllButtons.left + 1;
|
|
m_rectAllButtons.right = m_rectAllButtons.right - 1;
|
|
|
|
#ifdef DBG
|
|
{
|
|
// the button boundaries should naturally fall in ascending(for LTR) order
|
|
for (int j = 0; j < m_ButtonBoundaries.size()-1; j++)
|
|
{
|
|
if (0 == (m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL))
|
|
ASSERT (m_ButtonBoundaries[j] <= m_ButtonBoundaries[j+1]);
|
|
else
|
|
ASSERT (m_ButtonBoundaries[j] >= m_ButtonBoundaries[j+1]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* see if we might need to simulate a double-click on the system menu */
|
|
/*--------------------------------------------------------------------*/
|
|
m_pMaxedMDIChild = NULL;
|
|
|
|
// only deal with the system menu if the MDI decorations are showing
|
|
if (m_pMenuBar->m_fDecorationsShowing)
|
|
{
|
|
ASSERT (m_pMenuBar->m_pMDIFrame != NULL);
|
|
CWnd* pMDIChild = m_pMenuBar->m_pMDIFrame->MDIGetActive();
|
|
|
|
// nothing to do if child is already gone
|
|
if ( pMDIChild == NULL )
|
|
return;
|
|
|
|
ASSERT (pMDIChild->IsZoomed());
|
|
|
|
// if the mouse is over the system menu, remember the maximized child
|
|
// (non-NULL m_pMaxedMDIChild is the key later on in MaybeCloseMDIChild)
|
|
if (HitTest (m_ptLButtonDown) == 0)
|
|
m_pMaxedMDIChild = pMDIChild;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::~CPopupTrackContext
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
CPopupTrackContext::~CPopupTrackContext ()
|
|
{
|
|
// release the mouse and keyboard hooks
|
|
RemovePopupMonitorHooks();
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::RemovePopupMonitorHooks
|
|
*
|
|
* Unhooks from the system and stops watching the events
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CPopupTrackContext::RemovePopupMonitorHooks ()
|
|
{
|
|
// ignore if not monitoring yet
|
|
if (m_bPopupMonitorHooksActive)
|
|
{
|
|
// we MUST be the active monitor if we came here
|
|
if (s_pTrackContext != this)
|
|
{
|
|
ASSERT(FALSE && "Popup monitor uninstalled from outside");
|
|
return;
|
|
}
|
|
// release the mouse and keyboard hooks
|
|
UnhookWindowsHookEx (m_hhkMouse);
|
|
UnhookWindowsHookEx (m_hhkKeyboard);
|
|
UnhookWindowsHookEx (m_hhkCallWnd);
|
|
|
|
m_bPopupMonitorHooksActive = false;
|
|
// uninstall itself as hook monitor
|
|
s_pTrackContext = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::SetPopupMonitorHooks
|
|
*
|
|
* Hooks into the system and begins watching the events
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CPopupTrackContext::SetPopupMonitorHooks ()
|
|
{
|
|
// ignore if already set
|
|
if (!m_bPopupMonitorHooksActive)
|
|
{
|
|
// there is only one active menu per app. There is no place for anybody else
|
|
if (s_pTrackContext)
|
|
{
|
|
ASSERT(FALSE && "Popup menu overrun");
|
|
return;
|
|
}
|
|
// install itself as hook monitor
|
|
s_pTrackContext = this;
|
|
|
|
DWORD idCurrentThread = ::GetCurrentThreadId();
|
|
|
|
// hook the mouse for hot tracking
|
|
m_hhkMouse = SetWindowsHookEx (WH_MOUSE, MouseProc, NULL, idCurrentThread);
|
|
|
|
// hook the keyboard for navigation
|
|
m_hhkKeyboard = SetWindowsHookEx (WH_KEYBOARD, KeyboardProc, NULL, idCurrentThread);
|
|
|
|
// hook send messages for Menu_Is_Closed detection
|
|
m_hhkCallWnd = SetWindowsHookEx (WH_CALLWNDPROC, CallWndProc, NULL, idCurrentThread);
|
|
|
|
m_bPopupMonitorHooksActive = true;
|
|
}
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::StartMonitoring
|
|
*
|
|
* Public method to start popup monitoring
|
|
* Hooks into the system and begins watching the events
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CPopupTrackContext::StartMonitoring()
|
|
{
|
|
// reset the request to activate another popup upon finish
|
|
m_iRequestForNewPopup = -1;
|
|
// setup hooks and watch...
|
|
SetPopupMonitorHooks();
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::WasAnotherPopupRequested
|
|
*
|
|
* Used to retrieve the cause the menu was dismissed.
|
|
* If new popupu is requested, button index is returned
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
bool CPopupTrackContext::WasAnotherPopupRequested(int& iNewIdx)
|
|
{
|
|
if (m_iRequestForNewPopup >= 0)
|
|
{
|
|
iNewIdx = m_iRequestForNewPopup;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::MouseProc
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CALLBACK CPopupTrackContext::MouseProc (int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CPopupTrackContext* this_ = s_pTrackContext;
|
|
ASSERT (this_ != NULL);
|
|
|
|
if (nCode < 0)
|
|
return (CallNextHookEx (this_->m_hhkMouse, nCode, wParam, lParam));
|
|
|
|
return (this_->MouseProc (nCode, wParam, (LPMOUSEHOOKSTRUCT) lParam));
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::MouseProc
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CPopupTrackContext::MouseProc (int nCode, UINT msg, LPMOUSEHOOKSTRUCT pmhs)
|
|
{
|
|
// if this is a mouse message within the menu bar...
|
|
if (m_rectAllButtons.PtInRect (pmhs->pt))
|
|
{
|
|
// eat the button down so we don't get into a TBN_DROPDOWN loop
|
|
if (msg == WM_LBUTTONDOWN)
|
|
{
|
|
DismissCurrentPopup (true);
|
|
MaybeCloseMDIChild (pmhs->pt);
|
|
return (1);
|
|
}
|
|
|
|
// for (non-duplicate) mouse moves, follow the mouse with the active menu
|
|
if ((msg == WM_MOUSEMOVE) &&
|
|
(nCode == HC_ACTION) &&
|
|
(m_ptLastMouseMove != pmhs->pt))
|
|
{
|
|
// determine which button is being tracked over
|
|
m_ptLastMouseMove = pmhs->pt;
|
|
int nNewPopupIndex = HitTest (m_ptLastMouseMove);
|
|
ASSERT (nNewPopupIndex != -1);
|
|
|
|
// if we're not over the same button we were last
|
|
// time, display the popup for the new button
|
|
if (nNewPopupIndex != m_nCurrentPopupIndex)
|
|
NotifyNewPopup (m_nCurrentPopupIndex = nNewPopupIndex);
|
|
}
|
|
}
|
|
|
|
return (CallNextHookEx (m_hhkMouse, nCode, msg, (LPARAM) pmhs));
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::KeyboardProc
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CALLBACK CPopupTrackContext::KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CPopupTrackContext* this_ = s_pTrackContext;
|
|
ASSERT (this_ != NULL);
|
|
|
|
if (nCode < 0)
|
|
return (CallNextHookEx (this_->m_hhkKeyboard, nCode, wParam, lParam));
|
|
|
|
int cRepeat = LOWORD (lParam);
|
|
bool fDown = (lParam & (1 << 31)) == 0;
|
|
|
|
return (this_->KeyboardProc (nCode, wParam, cRepeat, fDown, lParam));
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::KeyboardProc
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CPopupTrackContext::KeyboardProc (
|
|
int nCode,
|
|
int vkey,
|
|
int cRepeat,
|
|
bool fDown,
|
|
LPARAM lParam)
|
|
{
|
|
// if this isn't a real message, ignore it
|
|
if (nCode != HC_ACTION)
|
|
return (CallNextHookEx (m_hhkKeyboard, nCode, vkey, lParam));
|
|
|
|
// if this is a left or right message...
|
|
if ((vkey == VK_LEFT) || (vkey == VK_RIGHT))
|
|
{
|
|
// eat the key release, but don't do anything with it
|
|
if (!fDown)
|
|
return (1);
|
|
|
|
/*
|
|
* let the menu code handle cascaded popups
|
|
*/
|
|
// need to do everything in opposite direction on RTL layout
|
|
// see bug #402620 ntbug9 05/23/2001
|
|
const bool fNext = ( (m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL) ? (vkey != VK_RIGHT) : (vkey == VK_RIGHT) ) ;
|
|
|
|
if (m_fCurrentlyOnPopupItem && fNext)
|
|
m_cCascadedPopups++;
|
|
|
|
else if ((m_cCascadedPopups > 0) && !fNext)
|
|
m_cCascadedPopups--;
|
|
|
|
/*
|
|
* not right on a popup item, or left on a popped-up menu
|
|
*/
|
|
else
|
|
{
|
|
m_cCascadedPopups = 0;
|
|
|
|
// figure out the next button
|
|
int nNewPopupIndex = fNext
|
|
? m_pMenuBar->GetNextButtonIndex (m_nCurrentPopupIndex, cRepeat)
|
|
: m_pMenuBar->GetPrevButtonIndex (m_nCurrentPopupIndex, cRepeat);
|
|
|
|
// activate the new button's popup, if it's different from the current one
|
|
if (nNewPopupIndex != m_nCurrentPopupIndex)
|
|
NotifyNewPopup (m_nCurrentPopupIndex = nNewPopupIndex);
|
|
|
|
// eat the key press
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
return (CallNextHookEx (m_hhkKeyboard, nCode, vkey, lParam));
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::CallWndProc
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CALLBACK CPopupTrackContext::CallWndProc(
|
|
int nCode, // hook code
|
|
WPARAM wParam, // current-process flag
|
|
LPARAM lParam // address of structure with message data
|
|
)
|
|
{
|
|
// get the active monitor
|
|
CPopupTrackContext* this_ = s_pTrackContext;
|
|
ASSERT (this_ != NULL);
|
|
|
|
// ignore special cases
|
|
if (nCode < 0)
|
|
return (CallNextHookEx (this_->m_hhkCallWnd, nCode, wParam, lParam));
|
|
|
|
BOOL bCurrentThread = wParam;
|
|
LPCWPSTRUCT lpCWP = reinterpret_cast<LPCWPSTRUCT>(lParam);
|
|
|
|
// forward the request to monitor
|
|
return (this_->CallWndProc (nCode, bCurrentThread, lpCWP));
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::CallWndProc
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CPopupTrackContext::CallWndProc (int nCode, BOOL bCurrentThread, LPCWPSTRUCT lpCWP)
|
|
{
|
|
ASSERT(lpCWP != NULL);
|
|
if (lpCWP)
|
|
{
|
|
// watch for message
|
|
if (lpCWP->message == WM_MENUSELECT)
|
|
{
|
|
// decode params
|
|
const UINT fuFlags = (UINT) HIWORD(lpCWP->wParam); // menu flags
|
|
const HMENU hmenu = (HMENU) lpCWP->lParam; // handle to menu clicked
|
|
|
|
if (fuFlags == 0xFFFF && hmenu == NULL)
|
|
{
|
|
// menu is closed! no more hooking needed
|
|
RemovePopupMonitorHooks ();
|
|
}
|
|
else
|
|
{
|
|
// we stepped on the popup (will use the info when arrows are pressed)
|
|
m_fCurrentlyOnPopupItem = (fuFlags & MF_POPUP);
|
|
}
|
|
}
|
|
}
|
|
// done
|
|
return (CallNextHookEx (m_hhkCallWnd, nCode, bCurrentThread, (LPARAM)lpCWP));
|
|
}
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::DismissCurrentPopup
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CPopupTrackContext::DismissCurrentPopup (bool fTrackingComplete)
|
|
{
|
|
// If the snapin does TrackPopupMenu with a window other than
|
|
// MainFrame as parent then that window should be asked to
|
|
// close the menu by sending WM_CANCELMODE. The window
|
|
// is found by calling GetCapture().
|
|
CWnd* pwndMode = CWnd::GetCapture();
|
|
|
|
if (pwndMode == NULL)
|
|
pwndMode = AfxGetMainWnd();
|
|
|
|
pwndMode->SendMessage (WM_CANCELMODE);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::NotifyNewPopup
|
|
*
|
|
* Notify the menu toolbar that it needs to display a new popup menu.
|
|
* Note that this must be accomplished asynchronously so that we can
|
|
* allow CMenuBar::PopupMenu to unwind after the WM_CANCELMODE from
|
|
* DismissCurrentPopup.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CPopupTrackContext::NotifyNewPopup (int nNewPopupIndex)
|
|
{
|
|
// dismiss the existing popup
|
|
DismissCurrentPopup (false);
|
|
// ask to activate the new popup after this one is closed
|
|
m_iRequestForNewPopup = nNewPopupIndex;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::HitTest
|
|
*
|
|
* Returns the index of the button under the given point, -1 if none.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CPopupTrackContext::HitTest (CPoint pt) const
|
|
{
|
|
/*----------------------------------------------------------------*/
|
|
/* Find the range of "hit" buttons. The range will span more */
|
|
/* than one button only if there are hidden buttons in the range, */
|
|
/* and in that case, there will be exactly one non-hidden button */
|
|
/* in the range. */
|
|
/*----------------------------------------------------------------*/
|
|
std::pair<BoundConstIt, BoundConstIt> range;
|
|
|
|
if ( m_pMenuBar->GetExStyle() & WS_EX_LAYOUTRTL )
|
|
{
|
|
range = std::equal_range (m_ButtonBoundaries.begin(),
|
|
m_ButtonBoundaries.end(), pt.x,
|
|
std::greater<BoundaryCollection::value_type>() );
|
|
}
|
|
else
|
|
{
|
|
range = std::equal_range (m_ButtonBoundaries.begin(),
|
|
m_ButtonBoundaries.end(), pt.x);
|
|
}
|
|
|
|
int nLowerHitIndex = MapBoundaryIteratorToButtonIndex (range.first);
|
|
int nUpperHitIndex = MapBoundaryIteratorToButtonIndex (range.second);
|
|
|
|
/*
|
|
* equal_range returns values that are less_than and greater_than
|
|
* given value. The m_ButtonBoundaries has duplicate values (due
|
|
* to hidden buttons). So if the less_than value is one of duplicate
|
|
* values (not unique) then equal_range returns the iterator to
|
|
* last dup item, not first dup item.
|
|
*
|
|
* Below we try to find the first dup item.
|
|
*/
|
|
|
|
// Find the first item with value m_ButtonBoundaries[nLowerHitIndex].
|
|
for (int iIndex = 0; iIndex < nLowerHitIndex; ++iIndex)
|
|
{
|
|
if (m_ButtonBoundaries[iIndex] == m_ButtonBoundaries[nLowerHitIndex])
|
|
{
|
|
// Found first item.
|
|
nLowerHitIndex = iIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT (nLowerHitIndex <= nUpperHitIndex);
|
|
|
|
int nHitIndex;
|
|
|
|
// lower equal upper? no hidden buttons
|
|
if (nLowerHitIndex == nUpperHitIndex)
|
|
nHitIndex = nLowerHitIndex;
|
|
|
|
// otherwise we have some hidden buttons, or we are precisely on a button border
|
|
else
|
|
{
|
|
nHitIndex = -1;
|
|
|
|
if (nUpperHitIndex == -1)
|
|
nUpperHitIndex = m_cButtons;
|
|
|
|
for (int i = nLowerHitIndex;
|
|
i <= nUpperHitIndex; // We should check till we hit nUpperHitIndex? AnandhaG
|
|
++i)
|
|
{
|
|
// See if this button is not hidden.
|
|
if (!m_pMenuBar->IsButtonHidden (m_pMenuBar->IndexToCommand(i)))
|
|
{
|
|
nHitIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// we should have found a visible button
|
|
ASSERT (nHitIndex != -1);
|
|
}
|
|
|
|
ASSERT (nHitIndex >= -1);
|
|
ASSERT (nHitIndex < m_cButtons);
|
|
return (nHitIndex);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::MapBoundaryIteratorToButtonIndex
|
|
*
|
|
* Returns the button index corresponding to the input m_ButtonBoundaries
|
|
* index, -1 for not found.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CPopupTrackContext::MapBoundaryIteratorToButtonIndex (BoundConstIt it) const
|
|
{
|
|
return ((it != m_ButtonBoundaries.end())
|
|
? it - m_ButtonBoundaries.begin() - 1
|
|
: -1);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* CPopupTrackContext::MaybeCloseMDIChild
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CPopupTrackContext::MaybeCloseMDIChild (CPoint pt)
|
|
{
|
|
// if we didn't find a maxed MDI child when this all started, punt
|
|
if (m_pMaxedMDIChild == NULL)
|
|
return;
|
|
|
|
// if the click didn't happen on the system menu toolbar button, punt
|
|
if (HitTest (pt) != 0)
|
|
return;
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* if the double-click time has elapsed, punt */
|
|
/* */
|
|
/* Note: this is called from a mouse hook, which means the value */
|
|
/* returned by GetMessageTime hasn't been updated for this message, */
|
|
/* so it really reflects the time of the *previous* message (most */
|
|
/* likely WM_LBUTTONUP). */
|
|
/* */
|
|
/* GetTickCount returns a close enough approximation to */
|
|
/* GetMessageTime, except when we're debugging through this routine, */
|
|
/* in which case the tick count will continue to spin (and this test */
|
|
/* will always fail) but the message time would have remained fixed. */
|
|
/*-------------------------------------------------------------------*/
|
|
// if ((GetMessageTime() - m_dwLButtonDownTime) > GetDoubleClickTime())
|
|
if ((GetTickCount() - m_dwLButtonDownTime) > GetDoubleClickTime())
|
|
return;
|
|
|
|
// if the second click occurred outside the double-click space, punt
|
|
if ((abs (m_ptLButtonDown.x - pt.x) > GetSystemMetrics (SM_CXDOUBLECLK)) ||
|
|
(abs (m_ptLButtonDown.y - pt.y) > GetSystemMetrics (SM_CYDOUBLECLK)))
|
|
return;
|
|
|
|
// if the window doesn't have a system menu, or its Close item is disabled, punt
|
|
CMenu* pSysMenu = m_pMaxedMDIChild->GetSystemMenu (FALSE);
|
|
|
|
if (pSysMenu == NULL)
|
|
return;
|
|
|
|
UINT nCloseState = pSysMenu->GetMenuState (SC_CLOSE, MF_BYCOMMAND);
|
|
|
|
if ((nCloseState == 0xFFFFFFFF) ||
|
|
(nCloseState & (MF_GRAYED | MF_DISABLED)))
|
|
return;
|
|
|
|
// here: we've identified a double-click on a maximized child's
|
|
// system menu; close it
|
|
m_pMaxedMDIChild->PostMessage (WM_SYSCOMMAND, SC_CLOSE);
|
|
}
|