[91ef65]: MenuTipper.cpp Maximize Restore History

Download this file

MenuTipper.cpp    166 lines (149 with data), 4.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
* Copyright (c) 2003-2007 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license.php
*/
////////////////////////////////////////////////////////////////
// Based on MSDN Magazine -- November 2003
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio .NET on Windows XP. Tab size=3.
//
#include "stdafx.h"
#include "ThisMfcApp.h"
#include "resource.h"
#include "resource2.h" // Menu, Toolbar & Accelerator resources
#include "resource3.h" // String resources
#include "MenuTipper.h"
#include <afxpriv.h> // for AfxLoadString
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////
// Override CSubclassWnd::WindowProc to hook messages on behalf of
// main window.
//
LRESULT CMenuTipManager::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_MENUSELECT) {
OnMenuSelect(LOWORD(wp), HIWORD(wp), (HMENU)lp);
} else if (msg==WM_ENTERIDLE) {
OnEnterIdle(wp, (HWND)lp);
}
return CSubclassWnd::WindowProc(msg, wp, lp);
}
//////////////////
// Got WM_MENUSELECT: show tip.
//
void CMenuTipManager::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hMenu)
{
// Only for MRU entries to display full path
if (nItemID < ID_FILE_MRU_ENTRY1 || nItemID > ID_FILE_MRU_ENTRYMAX) {
m_wndTip.Cancel(); // cancel/hide tip
m_bMouseSelect = FALSE;
m_bSticky = FALSE;
return;
}
if (!m_wndTip.m_hWnd) {
m_wndTip.Create(CPoint(0,0), CWnd::FromHandle(m_hWnd));
m_wndTip.m_szMargins = CSize(4,0);
}
if ((nFlags & 0xFFFF) == 0xFFFF) {
m_wndTip.Cancel(); // cancel/hide tip
m_bMouseSelect = FALSE;
m_bSticky = FALSE;
} else if (nFlags & MF_POPUP) {
m_wndTip.Cancel(); // new popup: cancel
m_bSticky = FALSE;
} else if (nFlags & MF_SEPARATOR) {
// separator: hide tip but remember sticky state
m_bSticky = m_wndTip.IsWindowVisible();
m_wndTip.Cancel();
} else if (nItemID && hMenu) {
// if tips already displayed, keep displayed
m_bSticky = m_wndTip.IsWindowVisible() || m_bSticky;
// remember if mouse used to invoke menu
m_bMouseSelect = (nFlags & MF_MOUSESELECT)!=0;
// get prompt and display tip (with or without timeout)
CString prompt = (*app.GetMRU())[nItemID - ID_FILE_MRU_ENTRY1];
if (prompt.IsEmpty())
m_wndTip.Cancel(); // no prompt: cancel tip
else {
prompt.Replace(_T("&"), _T("&&"));
prompt = _T(" ") + prompt + _T(" ");
CRect rc = GetMenuTipRect(hMenu, nItemID);
m_wndTip.SetWindowPos(&CWnd::wndTopMost, rc.left, rc.top,
rc.Width(), rc.Height(), SWP_NOACTIVATE);
m_wndTip.SetWindowText(prompt);
m_wndTip.ShowDelayed(m_bSticky ? 0 : m_iDelay);
}
}
}
//////////////////
// Calculate position of tip: next to menu item.
//
CRect CMenuTipManager::GetMenuTipRect(HMENU hmenu, UINT nID)
{
CWnd* pWndMenu = GetRunningMenuWnd(); //CWnd::WindowFromPoint(pt);
ASSERT(pWndMenu);
CRect rcMenu;
pWndMenu->GetWindowRect(rcMenu); // whole menu rect
// add heights of menu items until i reach nID
int count = ::GetMenuItemCount(hmenu);
int cy = rcMenu.top + GetSystemMetrics(SM_CYEDGE)+1;
for (int i=0; i<count; i++) {
CRect rc;
::GetMenuItemRect(m_hWnd, hmenu, i, &rc);
if (::GetMenuItemID(hmenu,i)==nID) {
// found menu item: adjust rectangle to right and down
rc += CPoint(rcMenu.right - rc.left, cy - rc.top);
return rc;
}
cy += rc.Height(); // add height
}
return CRect(0,0,0,0);
}
//////////////////
// Note that windows are enumerated in top-down Z-order, so the menu
// window should always be the first one found.
//
static BOOL CALLBACK MyEnumProc(HWND hwnd, LPARAM lParam)
{
TCHAR buf[16];
GetClassName(hwnd, buf, sizeof(buf)/sizeof(buf[0]));
if (_tcscmp(buf, _T("#32768")) == 0) { // special classname for menus
*((HWND*)lParam) = hwnd; // found it
return FALSE;
}
return TRUE;
}
//////////////////
// Get running menu window.
//
CWnd* CMenuTipManager::GetRunningMenuWnd()
{
HWND hwnd = NULL;
EnumWindows(MyEnumProc,(LPARAM)&hwnd);
return CWnd::FromHandle(hwnd);
}
//////////////////
// Need to handle WM_ENTERIDLE to cancel the tip if the user moved
// the mouse off the popup menu. For main menus, Windows will send a
// WM_MENUSELECT message for the parent menu when this happens, but for
// context menus there's no other way to know the user moved the mouse
// off the menu.
//
void CMenuTipManager::OnEnterIdle(WPARAM nWhy, HWND hwndWho)
{
if (m_bMouseSelect && nWhy == MSGF_MENU) {
CPoint pt;
GetCursorPos(&pt);
if (hwndWho != ::WindowFromPoint(pt)) {
m_wndTip.Cancel();
}
}
}