Category : Files from Magazines
Archive   : WDAPR92.ZIP
Filename : 3N04052A

 
Output of file : 3N04052A contained in archive : WDAPR92.ZIP
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=Begin Listing4-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
/*****************************************************/
/* listmenu.c */
/* -- Implements a popup menu with a scrollable */
/* ListBox. */
/*****************************************************/

/*****************************************************/
/* Header files. */
/*****************************************************/
#include
#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-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


  3 Responses to “Category : Files from Magazines
Archive   : WDAPR92.ZIP
Filename : 3N04052A

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/