256 lines
7.3 KiB
C
256 lines
7.3 KiB
C
/*****************************************************************************
|
|
*
|
|
* TallMenu.c
|
|
*
|
|
* Copyright (c) 1997-2002 Microsoft Corporation.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This source code is only intended as a supplement to
|
|
* Development Tools and/or SDK documentation.
|
|
* See these sources for detailed information regarding the
|
|
* Microsoft samples programs.
|
|
*
|
|
* THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
|
|
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
* PARTICULAR PURPOSE.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Sample program to demonstrate how a program can display a
|
|
* tall menu without the menu going off the top or bottom of
|
|
* the work area.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "tallmenu.h"
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Overview
|
|
*
|
|
* To split a tall menu into multiple columns, add the
|
|
* MF_MENUBREAK attribute on each item that should start
|
|
* in a new column.
|
|
*
|
|
* To apply this dynamically to a menu, walk through the items
|
|
* on the menu, tallying their cumulative heights. When the
|
|
* height exceeds the height of the screen, place the next
|
|
* item in a new column.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define COMPILE_MULTIMON_STUBS
|
|
#include <multimon.h>
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* GetWorkArea
|
|
*
|
|
* This function takes a window handle and returns the dimensions
|
|
* of the work area associated with that window.
|
|
*
|
|
* Ask the system which monitor the window is on and use the
|
|
* work area of that monitor.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void
|
|
GetWorkArea(HWND hwnd, LPRECT prcWork)
|
|
{
|
|
HMONITOR hmon;
|
|
MONITORINFO moni;
|
|
|
|
hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
moni.cbSize = sizeof(moni);
|
|
GetMonitorInfo(hmon, &moni);
|
|
|
|
*prcWork = moni.rcWork;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* AdjustTallMenu
|
|
*
|
|
* The function takes a tall menu and adds/removes MF_MENUBREAK
|
|
* attributes so that the menu is not taller than the screen.
|
|
*
|
|
* The job is made trickier by multiple monitor support. Each
|
|
* monitor may have a different height, and we must make sure
|
|
* to use the height appropriate to the monitor associated with
|
|
* the menu.
|
|
*
|
|
* hwnd - The window associated with the menu
|
|
* hmenuPopup - The popup menu to adjust
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void
|
|
AdjustTallMenu(HWND hwnd, HMENU hmenuPopup)
|
|
{
|
|
int cyScreen, cyItem, cy;
|
|
int iItem, cItem;
|
|
RECT rc;
|
|
|
|
/*
|
|
* Early-out: If the menu is empty, then there is nothing to adjust.
|
|
* This saves us from boundary conditions in the rest of the code.
|
|
*/
|
|
cItem = GetMenuItemCount(hmenuPopup);
|
|
if (cItem == 0) return;
|
|
|
|
GetWorkArea(hwnd, &rc);
|
|
|
|
/*
|
|
* Convert rectangle boundaries to height.
|
|
*/
|
|
cyScreen = rc.bottom - rc.top;
|
|
|
|
/*
|
|
* Subtract off some extra to account for the border around the menu.
|
|
* Note that this doesn't need to be exact, since this entire process
|
|
* of adjusting a tall menu is a heuristic anyway.
|
|
*/
|
|
|
|
cyScreen -= 2 * 3 * GetSystemMetrics(SM_CYEDGE);
|
|
|
|
/*
|
|
* Warning! We assume that the menu is not owner-draw!
|
|
*
|
|
* Extending this code to support owner-draw menu items is left
|
|
* as an exercise for the reader. (Hint: WM_MEASUREITEM is your
|
|
* friend.)
|
|
*/
|
|
GetMenuItemRect(hwnd, hmenuPopup, 0, &rc);
|
|
cyItem = rc.bottom - rc.top;
|
|
|
|
/*
|
|
* cyItem is zero if the menu item has never been shown, in which
|
|
* case we cheat and use the menu bar height, which is a reasonable
|
|
* approximation to the menu item height.
|
|
*/
|
|
if (cyItem == 0) {
|
|
cyItem = GetSystemMetrics(SM_CYMENU);
|
|
}
|
|
|
|
/*
|
|
* Now walk through the menu tallying up the height of each item.
|
|
* When it gets too tall, insert a break.
|
|
*
|
|
* An annoying feature of SetMenuItemInfo is that you can't change
|
|
* the MFT_MENUBARBREAK flag without resetting the text, so we
|
|
* have to get the text, flip the flag, and set the text back
|
|
* with the new flag.
|
|
*/
|
|
|
|
cy = 0;
|
|
for (iItem = 0; iItem < cItem; iItem++) {
|
|
MENUITEMINFO mii;
|
|
TCHAR buf[80];
|
|
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_TYPE | MIIM_DATA;
|
|
mii.dwTypeData = buf;
|
|
mii.cch = 80;
|
|
|
|
if (GetMenuItemInfo(hmenuPopup, iItem, MF_BYPOSITION, &mii)) {
|
|
if (cy > cyScreen) {
|
|
cy = 0;
|
|
mii.fType |= MFT_MENUBARBREAK;
|
|
} else {
|
|
mii.fType &= ~MFT_MENUBARBREAK;
|
|
}
|
|
|
|
SetMenuItemInfo(hmenuPopup, iItem, MF_BYPOSITION, &mii);
|
|
}
|
|
cy += cyItem;
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TallMenu_WndProc
|
|
*
|
|
* Window procedure for tall menu demo.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LRESULT CALLBACK
|
|
TallMenu_WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uiMsg) {
|
|
|
|
/*
|
|
* If a popup menu is about to be displayed, first make sure
|
|
* it isn't too tall for the screen.
|
|
*/
|
|
case WM_INITMENUPOPUP:
|
|
AdjustTallMenu(hwnd, (HMENU)wParam);
|
|
break;
|
|
|
|
/*
|
|
* The app exits when this window is destroyed.
|
|
*/
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, uiMsg, wParam, lParam);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinMain
|
|
*
|
|
* Program entry point.
|
|
*
|
|
* Demonstrate tall menus and how to prevent them from going off
|
|
* the screen.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int WINAPI
|
|
WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszCmdLine, int nCmdShow)
|
|
{
|
|
WNDCLASS wc;
|
|
MSG msg;
|
|
HWND hwnd;
|
|
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = TallMenu_WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hinst;
|
|
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wc.lpszMenuName = TEXT("TallMenu");
|
|
wc.lpszClassName = TEXT("TallMenu");
|
|
|
|
RegisterClass(&wc);
|
|
|
|
hwnd = CreateWindow(
|
|
TEXT("TallMenu"), /* Class Name */
|
|
TEXT("Tall Menu Sample"), /* Title */
|
|
WS_OVERLAPPEDWINDOW, /* Style */
|
|
CW_USEDEFAULT, CW_USEDEFAULT, /* Position */
|
|
CW_USEDEFAULT, CW_USEDEFAULT, /* Size */
|
|
NULL, /* Parent */
|
|
NULL, /* Use class menu */
|
|
hinst, /* Instance */
|
|
0); /* No special parameters */
|
|
|
|
ShowWindow(hwnd, nCmdShow);
|
|
|
|
while (GetMessage(&msg, NULL, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return 0;
|
|
}
|