home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_03_04
/
3n04052a
< prev
next >
Wrap
Text File
|
1992-02-11
|
19KB
|
630 lines
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=Begin Listing4-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
/*****************************************************/
/* listmenu.c */
/* -- Implements a popup menu with a scrollable */
/* ListBox. */
/*****************************************************/
/*****************************************************/
/* Header files. */
/*****************************************************/
#include <windows.h>
#include "listmenu.h"
/*****************************************************/
/* Constants. */
/*****************************************************/
#define imdsNil -1 /* Undefined menu index. */
#define cbMenuMax 256 /* Max. length of MenuItem. */
#define szMenuClass "#32768" /* Menu classname. */
#define szListClass "ListBox" /* LBox classname. */
#define szFilter "LFilter" /* Filter classname. */
/*****************************************************/
/* Types. */
/*****************************************************/
typedef struct
{
int cidm; /* # items in menu. */
int dx, dy; /* Size of menu. */
int idm; /* Index in MenuBar. */
HMENU hmnu; /* Original popup menu. */
HWND hwnd; /* ListBox window. */
WORD rgidm[1]; /* MenuItem id array */
} MDS; /* Menu DeScriptor. */
typedef MDS FAR * LPMDS;
typedef HANDLE HMDS;
typedef HMDS FAR * LRGHMDS;
/*****************************************************/
/* Globals. */
/*****************************************************/
HANDLE hrghmds; /* Handle to array of menus. */
int cmds; /* Number of menus. */
int imdsDown; /* Dropped popup menu. */
HWND hwndMenu; /* Popup menu window. */
HWND hwndMain; /* App's main window. */
HMENU hmnuTop; /* App's MenuBar. */
FARPROC lpfnMenuFilter; /* Popup menu subclasser. */
FARPROC lpfnMenu; /* Popup menu window proc. */
FARPROC lpfnMain; /* App's main window proc. */
FARPROC lpfnMainFilter; /* Subclasser for above. */
FARPROC lpfnListFilter; /* Subclasser for ListBox. */
FARPROC lpfnList;
BOOL fButtonDown; /* Buttoned down in list? */
/*****************************************************/
/* Private Prototypes. */
/*****************************************************/
void CloseListMenu(void);
void DestroyMenuImds(int);
BOOL FAR PASCAL FEnumWnd(HWND, LONG);
void GetMdsImds(MDS *, int);
WORD IdmFromIidmImds(int, int);
int ImdsFromIdm(WORD);
LONG FAR PASCAL ListFilter(HWND, WORD, WORD, LONG);
LONG FAR PASCAL MainFilter(HWND, WORD, WORD, LONG);
LONG FAR PASCAL MenuFilter(HWND, WORD, WORD, LONG);
void RemoveListMenu(BOOL);
/*****************************************************/
/* Routines. */
/*****************************************************/
BOOL
FInitListMenu(BOOL fFirst, HWND hwnd)
/*****************************************************/
/* -- Initialize the list menu module. */
/* -- hwnd : Window containing MenuBar. */
/* -- szMenuList : Name of menu list of popups. */
/*****************************************************/
{
FARPROC lpfn; /* EnumWindows() callback. */
HANDLE hins; /* App's instance. */
WNDCLASS wcs; /* Our own ListBox class. */
hwndMain = hwnd;
if ((hmnuTop = GetMenu(hwnd)) == NULL)
return FALSE;
/* Get the popup menu handle so we can subclass */
/* it at will. */
hins = GetWindowWord(hwnd, GWW_HINSTANCE);
if ((lpfn = MakeProcInstance(FEnumWnd, hins)) ==
NULL)
return FALSE;
EnumWindows(lpfn, 0L);
FreeProcInstance(lpfn);
if (hwndMenu == NULL)
return FALSE;
lpfnMenu =
(FARPROC)GetWindowLong(hwndMenu, GWL_WNDPROC);
if ((lpfnMenuFilter = MakeProcInstance(
(FARPROC)MenuFilter, hins)) == NULL)
return FALSE;
/* Subclass the app's main window. */
lpfnMain =
(FARPROC)GetWindowLong(hwndMain, GWL_WNDPROC);
if ((lpfnMainFilter = MakeProcInstance(
(FARPROC)MainFilter, hins)) == NULL)
return FALSE;
SetWindowLong(hwndMain, GWL_WNDPROC,
(LONG)lpfnMainFilter);
/* Create a our version of the ListBox class. */
if (!GetClassInfo(NULL, szListClass, &wcs))
return FALSE;
lpfnList = (FARPROC)wcs.lpfnWndProc;
if (fFirst)
{
wcs.lpfnWndProc = ListFilter;
wcs.hInstance = hins;
wcs.lpszClassName = szFilter;
if (!RegisterClass(&wcs))
return FALSE;
}
imdsDown = imdsNil;
return TRUE;
}
BOOL FAR PASCAL
FEnumWnd(HWND hwnd, LONG lwp)
/*****************************************************/
/* -- EnumWindows() callback to get popup menu */
/* window handle. */
/*****************************************************/
{
char szBuf[40];
GetClassName(hwnd, szBuf, sizeof szBuf);
if (!lstrcmp(szBuf, szMenuClass))
{
hwndMenu = hwnd;
return FALSE;
}
return TRUE;
}
void
CloseListMenu(void)
/*****************************************************/
/* -- Close the list menu module. */
/*****************************************************/
{
int imds;
if (cmds == 0)
return; /* Nothing to do. */
/* Destroy all list menus. */
for (imds = 0; imds < cmds; imds++)
DestroyMenuImds(imds);
/* Remove filters. */
if (lpfnMenu != NULL)
{
if (IsWindow(hwndMenu))
SetWindowLong(hwndMenu, GWL_WNDPROC,
(LONG)lpfnMenu);
lpfnMenu = NULL;
}
if (lpfnMenuFilter != NULL)
{
FreeProcInstance(lpfnMenuFilter);
lpfnMenuFilter = NULL;
}
if (lpfnMain != NULL && IsWindow(hwndMain))
SetWindowLong(hwndMain, GWL_WNDPROC,
(LONG)lpfnMain);
if (lpfnMainFilter != NULL)
{
FreeProcInstance(lpfnMainFilter);
lpfnMainFilter = NULL;
}
/* Restore state and free menu table array. */
hmnuTop = NULL;
hwndMenu = hwndMain = NULL;
GlobalFree(hrghmds);
hrghmds = NULL;
cmds = 0;
}
BOOL
FAssignMenu(WORD idm, int dy)
/*****************************************************/
/* -- Assign a list menu to the given MenuItem. */
/* -- The strings and command values for the list */
/* menu are extracted from the given popup menu. */
/* -- Return TRUE for success, FALSE for failure. */
/* -- idm : MenuItem id. */
/* -- dy : Height of the menu. */
/*****************************************************/
{
int imds;
HMENU hmnu;
HMDS hmds = NULL;
LPMDS lpmds = NULL;
HDC hdc = NULL;
int iidm, cidm;
BOOL fVal = FALSE;
HANDLE hins;
/* See if a list menu for this popup already */
/* exists. If not, grow (or allocate if for the */
/* first time) the list menu table array. If */
/* a list menu already exists, destroy it, but */
/* reuse its slot in the array. */
if ((imds = ImdsFromIdm(idm)) == imdsNil)
{
/* Grow menu array. */
if (cmds == 0)
{
if ((hrghmds = GlobalAlloc(
GMEM_MOVEABLE | GMEM_ZEROINIT,
sizeof(HMDS))) == NULL)
goto FAssignMenuError;
cmds = 1;
imds = 0;
}
else
{
HANDLE hrghmdsNew;
imds = cmds++;
if ((hrghmdsNew = GlobalReAlloc(hrghmds,
cmds * sizeof(HMDS),
GMEM_MOVEABLE | GMEM_ZEROINIT))
== NULL)
goto FAssignMenuError;
hrghmds = hrghmdsNew;
}
}
else
{
DestroyMenuImds(imds);
}
/* Create a new menu. Clone a ListBox with the */
/* same MenuItems. */
hins = GetWindowWord(hwndMain, GWW_HINSTANCE);
if ((hmnu = GetSubMenu(hmnuTop, idm)) == NULL)
goto FAssignMenuError;
if ((cidm = GetMenuItemCount(hmnu)) < 0)
goto FAssignMenuError;
if ((hmds = GlobalAlloc(GMEM_MOVEABLE,
sizeof(MDS) + (cidm - 1) * sizeof(WORD))) ==
NULL)
goto FAssignMenuError;
lpmds = (LPMDS)GlobalLock(hmds);
lpmds->cidm = cidm;
lpmds->hmnu = hmnu;
lpmds->idm = idm;
if ((lpmds->hwnd = CreateWindow(szFilter, NULL,
WS_CHILD | WS_BORDER | WS_VSCROLL | WS_VISIBLE,
0, 0, 0, 0, hwndMain, idm, hins, NULL)) == NULL)
goto FAssignMenuError;
/* Fill the ListBox with MenuItems. Get width */
/* of widest item. */
lpmds->dx = -1;
if ((hdc = GetDC(lpmds->hwnd)) == NULL)
goto FAssignMenuError;
for (iidm = 0; iidm < cidm; iidm++)
{
char sz[cbMenuMax];
int dx;
if ((lpmds->rgidm[iidm] =
GetMenuItemID(hmnu, iidm)) == -1)
goto FAssignMenuError;
if (GetMenuString(hmnu, iidm, sz, sizeof sz,
MF_BYPOSITION) <= 0)
goto FAssignMenuError;
if (SendMessage(lpmds->hwnd, LB_ADDSTRING, 0,
(LONG)(LPSTR)sz) == LB_ERR)
goto FAssignMenuError;
dx = LOWORD(GetTextExtent(hdc, sz,
lstrlen(sz)));
if (dx > lpmds->dx)
lpmds->dx = dx;
}
/* Allow for scroll bar and extra whitespace. */
lpmds->dx += 2 * GetSystemMetrics(SM_CXVSCROLL);
lpmds->dy = dy;
((LRGHMDS)GlobalLock(hrghmds))[imds] = hmds;
GlobalUnlock(hrghmds);
fVal = TRUE;
goto FAssignMenuExit;
FAssignMenuError: /* Clean up in case of error. */
if (lpmds != NULL && lpmds->hwnd != NULL)
DestroyWindow(lpmds->hwnd);
if (hmds != NULL)
GlobalFree(hmds);
FAssignMenuExit: /* Normal cleanup. */
if (hdc != NULL)
ReleaseDC(lpmds->hwnd, hdc);
if (lpmds != NULL)
GlobalUnlock(hmds);
return fVal;
}
int
ImdsFromIdm(WORD idm)
/*****************************************************/
/* -- Given a top-level MenuItem id, return the */
/* associated list menu. */
/* -- Return -1 if no such menu. */
/*****************************************************/
{
int imds;
MDS mds;
if (cmds == 0)
return imdsNil;
for (imds = 0; imds < cmds; imds++)
{
GetMdsImds(&mds, imds);
if (mds.hwnd != NULL &&
GetWindowWord(mds.hwnd, GWW_ID) == idm)
break;
}
return imds == cmds ? imdsNil : imds;
}
void
DestroyMenuImds(int imds)
/*****************************************************/
/* -- Destroy the given list menu. */
/* -- imds : Index of menu. */
/*****************************************************/
{
LRGHMDS lrghmds = (LRGHMDS)GlobalLock(hrghmds);
HMDS hmds = lrghmds[imds];
LPMDS lpmds = (LPMDS)GlobalLock(hmds);
if (IsWindow(lpmds->hwnd))
DestroyWindow(lpmds->hwnd);
GlobalUnlock(hmds);
GlobalFree(hmds);
lrghmds[imds] = NULL;
GlobalUnlock(hrghmds);
}
LONG FAR PASCAL
MenuFilter(HWND hwnd, WORD wm, WORD wp, LONG lwp)
/*****************************************************/
/* -- Subclasser for menu popup window. */
/*****************************************************/
{
switch (wm)
{
default:
break;
case WM_PAINT:
{
PAINTSTRUCT wps;
RECT rect;
MDS mds;
/* Eat the paint message and remove the */
/* popup menu and subclasser. */
BeginPaint(hwnd, &wps);
EndPaint(hwnd, &wps);
ShowWindow(hwnd, SW_HIDE);
SetWindowLong(hwndMenu, GWL_WNDPROC,
(LONG)lpfnMenu);
/* Position and display the list menu. */
GetMdsImds(&mds, imdsDown);
GetWindowRect(hwnd, &rect);
ScreenToClient(hwndMain, (LPPOINT)&rect);
MoveWindow(mds.hwnd,
rect.left, rect.top, mds.dx, mds.dy, TRUE);
SendMessage(mds.hwnd, LB_SETCURSEL, 0, 0L);
SetFocus(mds.hwnd);
/* Get out of menu mode. */
PostMessage(hwnd, WM_KEYDOWN, VK_ESCAPE, 0L);
PostMessage(hwndMain, WM_KEYDOWN, VK_ESCAPE,
0L);
/* But highlight the top-level MenuItem to */
/* look like we are still in it. Have to do */
/* this after the menu manager has removed */
/* it, though!. So post a message to */
/* ourself that will arrive after. */
PostMessage(hwndMain, WM_USER, 0, 0L);
}
return 0L;
case WM_ERASEBKGND:
return 1L; /* Eat this message too. */
} /* End switch wm. */
return CallWindowProc(lpfnMenu, hwnd, wm, wp, lwp);
}
LONG FAR PASCAL
MainFilter(HWND hwnd, WORD wm, WORD wp, LONG lwp)
/*****************************************************/
/* -- Subclasser for app's main window. */
/*****************************************************/
{
switch (wm)
{
default:
break;
case WM_INITMENUPOPUP:
/* Get rid of any list menu already visible. */
RemoveListMenu(FALSE);
/* Install the menu filter to start the ball */
/* rolling. */
if (HIWORD(lwp) == 0 &&
(imdsDown = ImdsFromIdm(LOWORD(lwp))) !=
imdsNil)
SetWindowLong(hwndMenu, GWL_WNDPROC,
(LONG)lpfnMenuFilter);
break;
case WM_COMMAND:
/* Eat notifications from the menu */
/* ListBoxes. */
if (ImdsFromIdm(wp) != imdsNil)
return 0L;
case WM_ACTIVATE:
case WM_LBUTTONDOWN:
case WM_NCLBUTTONDOWN:
/* If the user button's down elsewhere in */
/* the app, or activates another app, remove */
/* any visisble list menu. */
RemoveListMenu(FALSE);
break;
case WM_USER:
if (imdsDown != imdsNil)
{
MDS mds;
GetMdsImds(&mds, imdsDown);
HiliteMenuItem(hwnd, hmnuTop, mds.idm,
MF_BYPOSITION | MF_HILITE);
}
break;
case WM_DESTROY:
CloseListMenu(); /* Clean up. */
break;
} /* End switch wm. */
return CallWindowProc(lpfnMain, hwnd, wm, wp, lwp);
}
void
RemoveListMenu(BOOL fCommand)
/*****************************************************/
/* -- If a list menu is visible, remove it. */
/* -- fCommand : Generate a menu-style WM_COMMAND */
/* message if set. */
/*****************************************************/
{
MDS mds;
int imdsSav;
fButtonDown = FALSE;
if (imdsDown == imdsNil)
return;
GetMdsImds(&mds, imdsDown);
HiliteMenuItem(hwndMain, hmnuTop, mds.idm,
MF_BYPOSITION | MF_UNHILITE);
imdsSav = imdsDown;
imdsDown = imdsNil;
/* Remove focus first, so there isn't a ghost */
/* caret floating around until the parent */
/* windows gets a change to repaint itself. */
if (GetFocus() == mds.hwnd)
SetFocus(hwndMain);
/* Hide the list menu. */
MoveWindow(mds.hwnd, 0, 0, 0, 0, TRUE);
if (fCommand) /* Notify the app. */
{
int iidm;
iidm = (int)SendMessage(mds.hwnd, LB_GETCURSEL,
0, 0L);
if (iidm != LB_ERR)
PostMessage(hwndMain, WM_COMMAND,
IdmFromIidmImds(iidm, imdsSav), 0L);
}
}
LONG FAR PASCAL
ListFilter(HWND hwnd, WORD wm, WORD wp, LONG lwp)
/*****************************************************/
/* -- Subclasser for ListBox control. */
/*****************************************************/
{
switch (wm)
{
default:
break;
case WM_LBUTTONDOWN:
fButtonDown = TRUE;
break;
case WM_LBUTTONUP:
if (fButtonDown)
{
LONG pt = GetMessagePos();
RECT rect;
/* Do not generate a command if the user */
/* releases the mouse outside the bounds */
/* of the ListBox. */
GetClientRect(hwnd, &rect);
ScreenToClient(hwnd, (LPPOINT)&pt);
RemoveListMenu(
PtInRect(&rect, *(POINT *)&pt));
}
break;
case WM_SIZE:
{
LONG lVal;
/* Windows will put a bogus horizontal */
/* scroll bar style in the window in */
/* response to this message! So stomp it */
/* out. */
lVal =
CallWindowProc(lpfnList, hwnd, wm, wp, lwp);
SetWindowLong(hwnd, GWL_STYLE,
GetWindowLong(hwnd, GWL_STYLE) & ~WS_HSCROLL);
return lVal;
}
case WM_KEYDOWN:
switch (wp)
{
default:
break;
case VK_RETURN:
RemoveListMenu(TRUE);
return 0L;
case VK_ESCAPE:
RemoveListMenu(FALSE);
return 0L;
} /* End switch wp. */
break; /* End case WM_KEYDOWN. */
} /* End switch wm. */
return CallWindowProc(lpfnList, hwnd, wm, wp, lwp);
}
void
GetMdsImds(MDS * pmds, int imds)
/*****************************************************/
/* -- Given a list menu id, return its mds data. */
/* -- pmds : Fill this struct. */
/* -- imds : Index of list menu in array. */
/*****************************************************/
{
LRGHMDS lrghmds = (LRGHMDS)GlobalLock(hrghmds);
*pmds = *(LPMDS)GlobalLock(lrghmds[imds]);
GlobalUnlock(lrghmds[imds]);
GlobalUnlock(hrghmds);
}
WORD
IdmFromIidmImds(int iidm, int imds)
/*****************************************************/
/* -- Return the requested MenuItem id. */
/* -- iidm : Index of MenuItem id in arrar of them. */
/* -- imds : Index of list menu in array of them. */
/*****************************************************/
{
LRGHMDS lrghmds = (LRGHMDS)GlobalLock(hrghmds);
WORD idm;
idm =
((LPMDS)GlobalLock(lrghmds[imds]))->rgidm[iidm];
GlobalUnlock(lrghmds[imds]);
GlobalUnlock(hrghmds);
return idm;
}
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=End Listing4-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=