home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
xwphescr.zip
/
XWPH0208.ZIP
/
src
/
helpers
/
winh.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-08-11
|
184KB
|
5,149 lines
/*
*@@sourcefile winh.c:
* contains Presentation Manager helper functions that are
* independent of a single application, i.e. these can be
* used w/out the rest of the XWorkplace source in any PM
* program.
*
* Usage: All PM programs.
*
* Function prefixes (new with V0.81):
* -- winh* Win (Presentation Manager) helper functions
*
* Note: Version numbering in this file relates to XWorkplace version
* numbering.
*
*@@header "helpers\winh.h"
*/
/*
* Copyright (C) 1997-2002 Ulrich Möller.
* This file is part of the "XWorkplace helpers" source package.
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, in version 2 as it comes in the
* "COPYING" file of the XWorkplace main distribution.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define OS2EMX_PLAIN_CHAR
// this is needed for "os2emx.h"; if this is defined,
// emx will define PSZ as _signed_ char, otherwise
// as unsigned char
#define INCL_DOSPROCESS
#define INCL_DOSMODULEMGR
#define INCL_DOSSEMAPHORES
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_DOSSESMGR
#define INCL_DOSERRORS
#define INCL_WINWINDOWMGR
#define INCL_WINMESSAGEMGR
#define INCL_WINFRAMEMGR
#define INCL_WININPUT
#define INCL_WINDIALOGS
#define INCL_WINPOINTERS
#define INCL_WINRECTANGLES
#define INCL_WINSHELLDATA
#define INCL_WINTIMER
#define INCL_WINSYS
#define INCL_WINHELP
#define INCL_WINPROGRAMLIST
#define INCL_WINSWITCHLIST
#define INCL_WINBUTTONS
#define INCL_WINSTATICS
#define INCL_WINMENUS
#define INCL_WINENTRYFIELDS
#define INCL_WINSCROLLBARS
#define INCL_WINLISTBOXES
#define INCL_WINSTDSPIN
#define INCL_WINSTDSLIDER
#define INCL_WINCIRCULARSLIDER
#define INCL_WINSTDFILE
#define INCL_SPL
#define INCL_SPLDOSPRINT
#define INCL_SPLERRORS
#define INCL_GPIBITMAPS
#define INCL_GPIPRIMITIVES
#include <os2.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "setup.h" // code generation and debugging options
#include "helpers\dosh.h"
#include "helpers\winh.h"
#include "helpers\prfh.h"
#include "helpers\gpih.h"
#include "helpers\standards.h"
#include "helpers\stringh.h"
#include "helpers\undoc.h"
#include "helpers\xstring.h" // extended string helpers
/*
*@@category: Helpers\PM helpers\Wrappers
*/
/* ******************************************************************
*
* Wrappers
*
********************************************************************/
#ifdef WINH_STANDARDWRAPPERS
/*
*@@ winhSendMsg:
* wrapper for WinSendMsg.
*
* If WINH_STANDARDWRAPPERS is #defined before
* including win.h, all WinSendMsg calls are
* redefined to use this wrapper instead. This
* reduces the amount of external fixups required
* for loading the module.
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
MRESULT winhSendMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
// put the call in brackets so the macro won't apply here
return ((WinSendMsg)(hwnd, msg, mp1, mp2));
}
/*
*@@ winhSendDlgItemMsg:
* wrapper for WinSendDlgItemMsg.
*
* If WINH_STANDARDWRAPPERS is #defined before
* including win.h, all WinSendMsg calls are
* redefined to use this wrapper instead. This
* reduces the amount of external fixups required
* for loading the module.
*
*@@added V0.9.13 (2001-06-27) [umoeller]
*/
MRESULT winhSendDlgItemMsg(HWND hwnd, ULONG id, ULONG msg, MPARAM mp1, MPARAM mp2)
{
return ((WinSendDlgItemMsg)(hwnd, id, msg, mp1, mp2));
}
/*
*@@ winhPostMsg:
* wrapper for WinPostMsg.
*
* If WINH_STANDARDWRAPPERS is #defined before
* including win.h, all WinSendMsg calls are
* redefined to use this wrapper instead. This
* reduces the amount of external fixups required
* for loading the module.
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
BOOL winhPostMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
// put the call in brackets so the macro won't apply here
return ((WinPostMsg)(hwnd, msg, mp1, mp2));
}
/*
*@@ winhWindowFromID:
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
HWND winhWindowFromID(HWND hwnd, ULONG id)
{
// put the call in brackets so the macro won't apply here
return ((WinWindowFromID)(hwnd, id));
}
/*
*@@ winhQueryWindow:
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
HWND winhQueryWindow(HWND hwnd, LONG lCode)
{
// put the call in brackets so the macro won't apply here
return ((WinQueryWindow)(hwnd, lCode));
}
/*
*@@ winhQueryWindowPtr:
*
*@@added V0.9.13 (2001-06-21) [umoeller]
*/
PVOID winhQueryWindowPtr(HWND hwnd, LONG index)
{
// put the call in brackets so the macro won't apply here
return ((WinQueryWindowPtr)(hwnd, index));
}
/*
*@@ winhSetWindowText2:
*
*@@added V0.9.13 (2001-06-21) [umoeller]
*/
BOOL winhSetWindowText2(HWND hwnd, const char *pcsz)
{
// put the call in brackets so the macro won't apply here
return (WinSetWindowText)(hwnd, (PSZ)pcsz);
}
/*
*@@ winhSetDlgItemText:
*
*@@added V0.9.13 (2001-06-21) [umoeller]
*/
BOOL winhSetDlgItemText(HWND hwnd, ULONG id, const char *pcsz)
{
// put the call in brackets so the macro won't apply here
return (WinSetDlgItemText)(hwnd, id, (PSZ)pcsz);
}
/*
*@@ winhRequestMutexSem:
*
*@@added V0.9.16 (2002-01-26) [umoeller]
*/
APIRET winhRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
{
// put the call in brackets so the macro won't apply here
return (WinRequestMutexSem)(hmtx, ulTimeout);
}
#endif // WINH_STANDARDWRAPPERS
/*
*@@category: Helpers\PM helpers\Rectangle helpers
*/
/* ******************************************************************
*
* Rectangle helpers
*
********************************************************************/
/*
*@@ winhOffsetRect:
* like WinOffsetRect, but doesn't require
* an anchor block to be passed in. Why
* the original would need an anchor block
* for this awfully complicated task is
* a mystery to me anyway.
*
*@@added V0.9.9 (2001-03-13) [umoeller]
*/
VOID winhOffsetRect(PRECTL prcl,
LONG lx,
LONG ly)
{
prcl->xLeft += lx;
prcl->xRight += lx;
prcl->yBottom += ly;
prcl->yTop += ly;
}
/*
*@@category: Helpers\PM helpers\Generics
*/
/* ******************************************************************
*
* Generics
*
********************************************************************/
/*
*@@ winhQueryWindowStyle:
*
*@@added V0.9.13 (2001-07-02) [umoeller]
*/
ULONG winhQueryWindowStyle(HWND hwnd)
{
return (WinQueryWindowULong(hwnd, QWL_STYLE));
}
/*
*@@ winhEnableDlgItem:
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
BOOL winhEnableDlgItem(HWND hwndDlg,
SHORT id,
BOOL fEnable)
{
return (WinEnableWindow(WinWindowFromID(hwndDlg, id), fEnable));
}
/*
*@@ winhIsDlgItemEnabled:
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
BOOL winhIsDlgItemEnabled(HWND hwndDlg,
SHORT id)
{
return (WinIsWindowEnabled(WinWindowFromID(hwndDlg, id)));
}
/*
*@@category: Helpers\PM helpers\Menu helpers
*/
/* ******************************************************************
*
* Menu helpers
*
********************************************************************/
/*
*@@ winhQueryMenuItem:
* wrapper around MM_QUERYITEM.
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
BOOL winhQueryMenuItem(HWND hwndMenu,
USHORT usItemID,
BOOL fSearchSubmenus,
PMENUITEM pmi) // out: MENUITEM data
{
return ((BOOL)WinSendMsg(hwndMenu,
MM_QUERYITEM,
MPFROM2SHORT(usItemID, fSearchSubmenus),
(MPARAM)pmi));
}
/*
*@@ winhQuerySubmenu:
* tests whether sID specifies a submenu in
* hMenu and returns the submenu window handle
* if so.
*
*@@added V0.9.20 (2002-08-10) [umoeller]
*/
HWND winhQuerySubmenu(HWND hMenu,
SHORT sID)
{
MENUITEM mi = {0};
if ( (WinSendMsg(hMenu,
MM_QUERYITEM,
MPFROM2SHORT(sID,
FALSE),
(MPARAM)&mi))
&& (mi.afStyle & MIS_SUBMENU)
)
return mi.hwndSubMenu;
return NULLHANDLE;
}
/*
*@@ winhInsertMenuItem:
* this inserts one one menu item into a given menu.
*
* Returns the return value of the MM_INSERTITEM msg:
* -- MIT_MEMERROR: space allocation for menu item failed
* -- MIT_ERROR: other error
* -- other: zero-based index of new item in menu.
*/
SHORT winhInsertMenuItem(HWND hwndMenu, // in: menu to insert item into
SHORT iPosition, // in: zero-based index of where to
// insert or MIT_END
SHORT sItemId, // in: ID of new menu item
const char *pcszItemTitle, // in: title of new menu item
SHORT afStyle,
// in: MIS_* style flags.
// Valid menu item styles are:
// -- MIS_SUBMENU
// -- MIS_SEPARATOR
// -- MIS_BITMAP: the display object is a bit map.
// -- MIS_TEXT: the display object is a text string.
// -- MIS_BUTTONSEPARATOR:
// The item is a menu button. Any menu can have zero,
// one, or two items of this type. These are the last
// items in a menu and are automatically displayed after
// a separator bar. The user cannot move the cursor to
// these items, but can select them with the pointing
// device or with the appropriate key.
// -- MIS_BREAK: the item begins a new row or column.
// -- MIS_BREAKSEPARATOR:
// Same as MIS_BREAK, except that it draws a separator
// between rows or columns of a pull-down menu.
// This style can only be used within a submenu.
// -- MIS_SYSCOMMAND:
// menu posts a WM_SYSCOMMAND message rather than a
// WM_COMMAND message.
// -- MIS_OWNERDRAW:
// WM_DRAWITEM and WM_MEASUREITEM notification messages
// are sent to the owner to draw the item or determine its size.
// -- MIS_HELP:
// menu posts a WM_HELP message rather than a
// WM_COMMAND message.
// -- MIS_STATIC
// This type of item exists for information purposes only.
// It cannot be selected with the pointing device or
// keyboard.
SHORT afAttr)
// in: MIA_* attribute flags
// Valid menu item attributes (afAttr) are:
// -- MIA_HILITED: if and only if, the item is selected.
// -- MIA_CHECKED: a check mark appears next to the item (submenu only).
// -- MIA_DISABLED: item is disabled and cannot be selected.
// The item is drawn in a disabled state (gray).
// -- MIA_FRAMED: a frame is drawn around the item (top-level menu only).
// -- MIA_NODISMISS:
// if the item is selected, the submenu remains down. A menu
// with this attribute is not hidden until the application
// or user explicitly does so, for example by selecting either
// another menu on the action bar or by pressing the escape key.
{
MENUITEM mi;
SHORT src = MIT_ERROR;
mi.iPosition = iPosition;
mi.afStyle = afStyle;
mi.afAttribute = afAttr;
mi.id = sItemId;
mi.hwndSubMenu = 0;
mi.hItem = 0;
src = SHORT1FROMMR(WinSendMsg(hwndMenu,
MM_INSERTITEM,
(MPARAM)&mi,
(MPARAM)pcszItemTitle));
return (src);
}
/*
*@@ winhInsertSubmenu:
* this inserts a submenu into a given menu and, if
* sItemId != 0, inserts one item into this new submenu also.
*
* See winhInsertMenuItem for valid menu item styles and
* attributes.
*
* Returns the HWND of the new submenu.
*/
HWND winhInsertSubmenu(HWND hwndMenu, // in: menu to add submenu to
ULONG iPosition, // in: index where to add submenu or MIT_END
SHORT sMenuId, // in: menu ID of new submenu
const char *pcszSubmenuTitle, // in: title of new submenu
USHORT afMenuStyle, // in: MIS* style flags for submenu;
// MIS_SUBMENU will always be added
SHORT sItemId, // in: ID of first item to add to submenu;
// if 0, no first item is inserted
const char *pcszItemTitle, // in: title of this item
// (if sItemID != 0)
USHORT afItemStyle, // in: style flags for this item, e.g. MIS_TEXT
// (this is ignored if sItemID == 0)
USHORT afAttribute) // in: attributes for this item, e.g. MIA_DISABLED
// (this is ignored if sItemID == 0)
{
MENUITEM mi;
SHORT src = MIT_ERROR;
HWND hwndNewMenu;
// create new, empty menu
hwndNewMenu = WinCreateMenu(hwndMenu,
NULL); // no menu template
if (hwndNewMenu)
{
// add "submenu item" to this empty menu;
// for some reason, PM always needs submenus
// to be a menu item
mi.iPosition = iPosition;
mi.afStyle = afMenuStyle | MIS_SUBMENU;
mi.afAttribute = 0;
mi.id = sMenuId;
mi.hwndSubMenu = hwndNewMenu;
mi.hItem = 0;
src = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_INSERTITEM, (MPARAM)&mi, (MPARAM)pcszSubmenuTitle));
if ( (src != MIT_MEMERROR)
&& (src != MIT_ERROR)
)
{
// set the new menu's ID to the same as the
// submenu item
WinSetWindowUShort(hwndNewMenu, QWS_ID, sMenuId);
if (sItemId)
{
// item id given: insert first menu item also
mi.iPosition = 0;
mi.afStyle = afItemStyle;
mi.afAttribute = afAttribute;
mi.id = sItemId;
mi.hwndSubMenu = 0;
mi.hItem = 0;
WinSendMsg(hwndNewMenu,
MM_INSERTITEM,
(MPARAM)&mi,
(MPARAM)pcszItemTitle);
}
}
}
return (hwndNewMenu);
}
/*
*@@ winhSetMenuCondCascade:
* sets the "conditional cascade" style
* on the specified submenu.
*
* This style must always be enabled manually
* because the resource compiler won't handle it.
*
* Note: Pass in the _submenu_ window handle,
* not the one of the parent. With lDefaultItem,
* specify the item ID in the submenu which is
* to be checked as the default item.
*
*@@added V0.9.12 (2001-05-22) [umoeller]
*@@changed V0.9.20 (2002-08-10) [umoeller]: now supporting calling this more than once
*/
BOOL winhSetMenuCondCascade(HWND hwndMenu, // in: submenu handle
LONG lDefaultItem) // in: item ID of new default item
{
BOOL brc;
ULONG ulStyle = WinQueryWindowULong(hwndMenu, QWL_STYLE);
LONG lOldDefault = -1;
if (ulStyle & MS_CONDITIONALCASCADE)
{
// menu is already conditional cascade:
lOldDefault = (LONG)WinSendMsg(hwndMenu,
MM_QUERYDEFAULTITEMID,
0,
0);
}
else
{
ulStyle |= MS_CONDITIONALCASCADE;
WinSetWindowULong(hwndMenu, QWL_STYLE, ulStyle);
}
// make the first item in the subfolder
// the default of cascading submenu
brc = (BOOL)WinSendMsg(hwndMenu,
MM_SETDEFAULTITEMID,
(MPARAM)lDefaultItem,
0);
if ( (lOldDefault != -1)
&& (lOldDefault != lDefaultItem)
)
{
// unset the "checked" attribute of the old one
// or we'll have two in the menu
WinSendMsg(hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT(lOldDefault,
FALSE),
MPFROM2SHORT(MIA_CHECKED, 0));
}
return brc;
}
/*
*@@ winhInsertMenuSeparator:
* this inserts a separator into a given menu at
* the given position (which may be MIT_END);
* returns the position at which the item was
* inserted.
*/
SHORT winhInsertMenuSeparator(HWND hMenu, // in: menu to add separator to
SHORT iPosition, // in: index where to add separator or MIT_END
SHORT sId) // in: separator menu ID (doesn't really matter)
{
MENUITEM mi;
mi.iPosition = iPosition;
mi.afStyle = MIS_SEPARATOR; // append separator
mi.afAttribute = 0;
mi.id = sId;
mi.hwndSubMenu = 0;
mi.hItem = 0;
return (SHORT1FROMMR(WinSendMsg(hMenu,
MM_INSERTITEM,
(MPARAM)&mi,
(MPARAM)"")));
}
/*
*@@ winhCopyMenuItem2:
* copies a menu item from hmenuSource to hmenuTarget.
*
* This creates a full duplicate. If usID specifies
* a submenu, the entire submenu is copied as well
* (this will then recurse).
*
* fl can be any combination of:
*
* -- COPYFL_STRIPTABS: strip off \t and everything
* that follows, if present.
*
* NOTE: Copying submenus will work only if each item
* in the submenu has a unique menu ID. This is due
* to the dumb implementation of menus in PM where
* it is impossible to query menu items without
* knowing their ID.
*
*@@added V0.9.9 (2001-03-09) [umoeller]
*@@changed V0.9.20 (2002-08-10) [umoeller]: renamed, added fl
*/
BOOL winhCopyMenuItem2(HWND hmenuTarget,
HWND hmenuSource,
USHORT usID,
SHORT sTargetPosition, // in: position to insert at or MIT_END
ULONG fl) // in: COPYFL_* flags
{
BOOL brc = FALSE;
MENUITEM mi = {0};
if (WinSendMsg(hmenuSource,
MM_QUERYITEM,
MPFROM2SHORT(usID,
FALSE),
(MPARAM)&mi))
{
// found in source:
// is it a separator?
if (mi.afStyle & MIS_SEPARATOR)
winhInsertMenuSeparator(hmenuTarget,
sTargetPosition,
usID);
else
{
// no separator:
// get item text
PSZ pszSource;
if (pszSource = winhQueryMenuItemText(hmenuSource,
usID))
{
PSZ p;
// remove the hotkey description
// V0.9.20 (2002-08-10) [umoeller]
if ( (fl & COPYFL_STRIPTABS)
&& (p = strchr(pszSource, '\t'))
)
*p = '\0';
if ( (mi.afStyle & MIS_SUBMENU)
&& (mi.hwndSubMenu)
)
{
// this is the top of a submenu:
HWND hwndSubMenu;
if (hwndSubMenu = winhInsertSubmenu(hmenuTarget,
sTargetPosition,
mi.id,
pszSource,
mi.afStyle,
0,
NULL,
0,
0))
{
// now copy all the items in the submenu
SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
MM_QUERYITEMCOUNT,
0,
0));
// loop through all entries in the original submenu
ULONG i;
for (i = 0;
i < cMenuItems;
i++)
{
SHORT id = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
MM_ITEMIDFROMPOSITION,
MPFROMSHORT(i),
0));
// recurse
winhCopyMenuItem2(hwndSubMenu,
mi.hwndSubMenu,
id,
MIT_END,
fl);
}
// now check... was the original submenu
// "conditional cascade"?
if (WinQueryWindowULong(mi.hwndSubMenu,
QWL_STYLE)
& MS_CONDITIONALCASCADE)
// yes:
{
// get the original default item
SHORT sDefID = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
MM_QUERYDEFAULTITEMID,
0,
0));
// set "conditional cascade style" on target too
winhSetMenuCondCascade(hwndSubMenu, sDefID);
}
} // end if (hwndSubmenu)
} // end if ( (mi.afStyle & MIS_SUBMENU)
else
{
// no submenu:
// just copy that item
SHORT s;
mi.iPosition = sTargetPosition;
s = SHORT1FROMMR(WinSendMsg(hmenuTarget,
MM_INSERTITEM,
MPFROMP(&mi),
MPFROMP(pszSource)));
if ( (s != MIT_MEMERROR)
&& (s != MIT_ERROR)
)
brc = TRUE;
}
free(pszSource);
} // end if (pszSource)
} // end else if (mi.afStyle & MIS_SEPARATOR)
} // end if (WinSendMsg(hmenuSource, MM_QUERYITEM,...
return brc;
}
/*
*@@ winhCopyMenuItem:
* wrapper for winhCopyMenuItem2 because it was
* exported.
*
*@@added V0.9.20 (2002-08-10) [umoeller]
*/
BOOL winhCopyMenuItem(HWND hmenuTarget,
HWND hmenuSource,
USHORT usID,
SHORT sTargetPosition) // in: position to insert at or MIT_END
{
return winhCopyMenuItem2(hmenuTarget, hmenuSource, usID, sTargetPosition, 0);
}
/*
*@@ winhMergeIntoSubMenu:
* creates a new submenu in hmenuTarget with the
* specified title at the specified position
* and copies the entire contents of hmenuSource
* into that.
*
* Returns the window handle of the new submenu
* or NULLHANDLE on errors.
*
* NOTE: Copying submenus will work only if each item
* in the submenu has a unique menu ID. This is due
* to the dumb implementation of menus in PM where
* it is impossible to query menu items without
* knowing their ID.
*
*@@added V0.9.9 (2001-03-09) [umoeller]
*/
HWND winhMergeIntoSubMenu(HWND hmenuTarget, // in: menu where to create submenu
SHORT sTargetPosition, // in: position to insert at or MIT_END
const char *pcszTitle, // in: title of new submenu or NULL
SHORT sID, // in: ID of new submenu
HWND hmenuSource) // in: menu to merge
{
HWND hwndNewSubmenu;
if (hwndNewSubmenu = WinCreateMenu(hmenuTarget, NULL))
{
MENUITEM mi = {0};
SHORT src = 0;
// SHORT s = 0;
mi.iPosition = MIT_END;
mi.afStyle = MIS_TEXT | MIS_SUBMENU;
mi.id = 2000;
mi.hwndSubMenu = hwndNewSubmenu;
WinSetWindowUShort(hwndNewSubmenu, QWS_ID, sID);
// insert new submenu into hmenuTarget
src = SHORT1FROMMR(WinSendMsg(hmenuTarget,
MM_INSERTITEM,
(MPARAM)&mi,
(MPARAM)pcszTitle));
if ( (src != MIT_MEMERROR)
&& (src != MIT_ERROR)
)
{
int i;
SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(hmenuSource,
MM_QUERYITEMCOUNT,
0, 0));
// loop through all entries in the original menu
for (i = 0; i < cMenuItems; i++)
{
SHORT id = SHORT1FROMMR(WinSendMsg(hmenuSource,
MM_ITEMIDFROMPOSITION,
MPFROMSHORT(i),
0));
winhCopyMenuItem(hwndNewSubmenu,
hmenuSource,
id,
MIT_END);
}
}
else
{
// error:
WinDestroyWindow(hwndNewSubmenu);
hwndNewSubmenu = NULLHANDLE;
}
}
return (hwndNewSubmenu);
}
/*
*@@ winhMergeIntoSubMenu:
* copies all items from hmenuSource into hmenuTarget,
* starting at the given position.
*
* Returns the no. of items that were copied.
*
* NOTE: Copying submenus will work only if each item
* in the submenu has a unique menu ID. This is due
* to the dumb implementation of menus in PM where
* it is impossible to query menu items without
* knowing their ID.
*
*@@added V0.9.20 (2002-08-10) [umoeller]
*/
ULONG winhMergeMenus(HWND hmenuTarget, // in: menu to copy items to
SHORT sTargetPosition, // in: position to insert at or MIT_END
HWND hmenuSource, // in: menu to merge
ULONG fl) // in: COPYFL_* flags for winhCopyMenuItem2
{
SHORT sTarget = MIT_END;
int i;
SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(hmenuSource,
MM_QUERYITEMCOUNT,
0, 0));
// loop through all entries in the original menu
for (i = 0; i < cMenuItems; i++)
{
SHORT id = SHORT1FROMMR(WinSendMsg(hmenuSource,
MM_ITEMIDFROMPOSITION,
MPFROMSHORT(i),
0));
winhCopyMenuItem2(hmenuTarget,
hmenuSource,
id,
MIT_END,
fl);
}
return i;
}
/*
*@@ winhQueryMenuItemText:
* this returns a menu item text as a PSZ
* to a newly allocated buffer or NULL if
* not found.
*
* Returns NULL on error. Use free()
* to free the return value.
*
* This uses MM_QUERYITEMTEXT internally.
* PMREF doesn't say anything about this,
* but from my testing this always recurses
* into submenus.
*
* Use the WinSetMenuItemText macro to
* set the menu item text.
*/
PSZ winhQueryMenuItemText(HWND hwndMenu,
USHORT usItemID) // in: menu item ID (not index)
{
PSZ prc = NULL;
SHORT sLength;
if (sLength = SHORT1FROMMR(WinSendMsg(hwndMenu,
MM_QUERYITEMTEXTLENGTH,
(MPARAM)(ULONG)usItemID,
(MPARAM)NULL)))
{
prc = (PSZ)malloc(sLength + 1);
WinSendMsg(hwndMenu,
MM_QUERYITEMTEXT,
MPFROM2SHORT(usItemID, sLength + 1),
(MPARAM)prc);
}
return (prc);
}
/*
*@@ winhAppend2MenuItemText:
*
*@@added V0.9.2 (2000-03-08) [umoeller]
*/
BOOL winhAppend2MenuItemText(HWND hwndMenu,
USHORT usItemID, // in: menu item ID (not index)
const char *pcszAppend, // in: text to append
BOOL fTab) // in: if TRUE, add \t before pcszAppend
{
BOOL brc = FALSE;
CHAR szItemText[400];
if (WinSendMsg(hwndMenu,
MM_QUERYITEMTEXT,
MPFROM2SHORT(usItemID,
sizeof(szItemText)),
(MPARAM)szItemText))
{
// text copied:
if (fTab)
{
if (strchr(szItemText, '\t'))
// we already have a tab:
strcat(szItemText, " ");
else
strcat(szItemText, "\t");
}
strcat(szItemText, pcszAppend);
brc = (BOOL)WinSendMsg(hwndMenu,
MM_SETITEMTEXT,
MPFROMSHORT(usItemID),
(MPARAM)szItemText);
}
return brc;
}
/*
*@@ winhMenuRemoveEllipse:
* removes a "..." substring from a menu item
* title, if found. This is useful if confirmations
* have been turned off for a certain menu item, which
* should be reflected in the menu.
*/
VOID winhMenuRemoveEllipse(HWND hwndMenu,
USHORT usItemId) // in: item to remove "..." from
{
CHAR szBuf[255];
CHAR *p;
WinSendMsg(hwndMenu,
MM_QUERYITEMTEXT,
MPFROM2SHORT(usItemId, sizeof(szBuf)-1),
(MPARAM)&szBuf);
if ((p = strstr(szBuf, "...")))
strcpy(p, p+3);
WinSendMsg(hwndMenu,
MM_SETITEMTEXT,
MPFROMSHORT(usItemId),
(MPARAM)&szBuf);
}
/*
*@@ winhQueryItemUnderMouse:
* this queries the menu item which corresponds
* to the given mouse coordinates.
* Returns the ID of the menu item and stores its
* rectangle in *prtlItem; returns (-1) upon errors.
*/
SHORT winhQueryItemUnderMouse(HWND hwndMenu, // in: menu handle
POINTL *pptlMouse, // in: mouse coordinates
RECTL *prtlItem) // out: rectangle of menu item
{
SHORT s, sItemId, sItemCount;
HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
sItemCount = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_QUERYITEMCOUNT, MPNULL, MPNULL));
for (s = 0;
s <= sItemCount;
s++)
{
sItemId = SHORT1FROMMR(WinSendMsg(hwndMenu,
MM_ITEMIDFROMPOSITION,
(MPARAM)(ULONG)s, MPNULL));
WinSendMsg(hwndMenu,
MM_QUERYITEMRECT,
MPFROM2SHORT(sItemId, FALSE),
(MPARAM)prtlItem);
if (WinPtInRect(habDesktop, prtlItem, pptlMouse))
return (sItemId);
}
/* sItemId = (SHORT)WinSendMsg(hwndMenu, MM_ITEMIDFROMPOSITION, (MPARAM)(sItemCount-1), MPNULL);
return (sItemId); */
return (-1); // error: no valid menu item
}
/*
*@@category: Helpers\PM helpers\Slider helpers
*/
/* ******************************************************************
*
* Slider helpers
*
********************************************************************/
/*
*@@ winhReplaceWithLinearSlider:
* this destroys the control with the ID ulID in hwndDlg
* and creates a linear slider at the same position with the
* same ID (effectively replacing it).
*
* This is needed because the IBM dialog editor (DLGEDIT.EXE)
* keeps crashing when creating sliders. So the way to do
* this easily is to create some other control with DLGEDIT
* where the slider should be later and call this function
* on that control when the dialog is initialized.
*
* You need to specify _one_ of the following with ulSliderStyle:
* -- SLS_HORIZONTAL: horizontal slider (default)
* -- SLS_VERTICAL: vertical slider
*
* plus _one_ additional common slider style for positioning:
* -- for horizontal sliders: SLS_BOTTOM, SLS_CENTER, or SLS_TOP
* -- for vertical sliders: SLS_LEFT, SLS_CENTER, or SLS_RIGHT
*
* Additional common slider styles are:
* -- SLS_PRIMARYSCALE1: determines the location of the scale
* on the slider shaft by using increment
* and spacing specified for scale 1 as
* the incremental value for positioning
* the slider arm. Scale 1 is displayed
* above the slider shaft of a horizontal
* slider and to the right of the slider
* shaft of a vertical slider. This is
* the default for a slider.
* -- SLS_PRIMARYSCALE2: not supported by this function
* -- SLS_READONLY: creates a read-only slider, which
* presents information to the user but
* allows no interaction with the user.
* -- SLS_RIBBONSTRIP: fills, as the slider arm moves, the
* slider shaft between the home position
* and the slider arm with a color value
* different from slider shaft color,
* similar to mercury in a thermometer.
* -- SLS_OWNERDRAW: notifies the application whenever the
* slider shaft, the ribbon strip, the
* slider arm, and the slider background
* are to be drawn.
* -- SLS_SNAPTOINCREMENT: causes the slider arm, when positioned
* between two values, to be positioned
* to the nearest value and redrawn at
* that position.
*
* Additionally, for horizontal sliders:
* -- SLS_BUTTONSLEFT: specifies that the optional slider
* buttons are to be used and places them
* to the left of the slider shaft. The
* buttons move the slider arm by one
* position, left or right, in the
* direction selected.
* -- SLS_BUTTONSRIGHT: specifies that the optional slider
* buttons are to be used and places them
* to the right of the slider shaft. The
* buttons move the slider arm by one
* position, left or right, in the
* direction selected.
* -- SLS_HOMELEFT: specifies the slider arm's home
* position. The left edge is used as the
* base value for incrementing (default).
* -- SLS_HOMERIGHT: specifies the slider arm's home
* position. The right edge is used as
* the base value for incrementing.
*
* Instead, for vertical sliders:
* -- SLS_BUTTONSBOTTOM: specifies that the optional slider
* buttons are to be used and places them
* at the bottom of the slider shaft. The
* buttons move the slider arm by one
* position, up or down, in the direction
* selected.
* -- SLS_BUTTONSTOP: specifies that the optional slider
* buttons are to be used and places them
* at the top of the slider shaft. The
* buttons move the slider arm by one
* position, up or down, in the direction
* selected.
* -- SLS_HOMEBOTTOM: specifies the slider arm's home
* position. The bottom of the slider is
* used as the base value for
* incrementing.
* -- SLS_HOMETOP: specifies the slider arm's home
* position. The top of the slider is
* used as the base value for
* incrementing.
*
* Notes: This function automatically adds WS_PARENTCLIP,
* WS_TABSTOP, and WS_SYNCPAINT to the specified styles.
* For the WS_TABSTOP style, hwndInsertAfter is important.
* If you specify HWND_TOP, your window will be the first
* in the tab stop list.
*
* It also shows the slider after having done all the
* processing in here by calling WinShowWindow.
*
* Also, we only provide support for scale 1 here, so
* do not specify SLS_PRIMARYSCALE2 with ulSliderStyle,
* and we have the slider calculate all the spacings.
*
* This returns the HWND of the slider or NULLHANDLE upon
* errors.
*
*@@added V0.9.0 [umoeller]
*/
HWND winhReplaceWithLinearSlider(HWND hwndParent, // in: parent of old control and slider
HWND hwndOwner, // in: owner of old control and slider
HWND hwndInsertAfter, // in: the control after which the slider should
// come up, or HWND_TOP, or HWND_BOTTOM
ULONG ulID, // in: ID of old control and slider
ULONG ulSliderStyle, // in: SLS_* styles
ULONG ulTickCount) // in: number of ticks (scale 1)
{
HWND hwndSlider = NULLHANDLE;
HWND hwndKill = WinWindowFromID(hwndParent, ulID);
if (hwndKill)
{
SWP swpControl;
if (WinQueryWindowPos(hwndKill, &swpControl))
{
SLDCDATA slcd;
// destroy the old control
WinDestroyWindow(hwndKill);
// initialize slider control data
slcd.cbSize = sizeof(SLDCDATA);
slcd.usScale1Increments = ulTickCount;
slcd.usScale1Spacing = 0; // have slider calculate it
slcd.usScale2Increments = 0;
slcd.usScale2Spacing = 0;
// create a slider with the same ID at the same
// position
hwndSlider = WinCreateWindow(hwndParent,
WC_SLIDER,
NULL, // no window text
ulSliderStyle
| WS_PARENTCLIP
| WS_SYNCPAINT
| WS_TABSTOP,
swpControl.x,
swpControl.y,
swpControl.cx,
swpControl.cy,
hwndOwner,
hwndInsertAfter,
ulID, // same ID as destroyed control
&slcd, // slider control data
NULL); // presparams
WinSendMsg(hwndSlider,
SLM_SETTICKSIZE,
MPFROM2SHORT(SMA_SETALLTICKS,
6), // 15 pixels high
NULL);
WinShowWindow(hwndSlider, TRUE);
}
}
return (hwndSlider);
}
/*
*@@ winhSetSliderTicks:
* this adds ticks to the given linear slider,
* which are ulPixels pixels high. A useful
* value for this is 4.
*
* This queries the slider for the primary
* scale values. Only the primary scale is
* supported.
*
* This function goes sets the ticks twice,
* once with mpEveryOther1 and ulPixels1,
* and then a second time with mpEveryOther2
* and ulPixels2. This allows you to quickly
* give, say, every tenth item a taller tick.
*
* For every set, if mpEveryOther is 0, this sets
* all ticks on the primary slider scale.
*
* If mpEveryOther is != 0, SHORT1FROMMP
* specifies the first tick to set, and
* SHORT2FROMMP specifies every other tick
* to set from there. For example:
*
+ MPFROM2SHORT(9, 10)
*
* would set tick 9, 19, 29, and so forth.
*
* If both mpEveryOther and ulPixels are -1,
* that set is skipped.
*
* Example: Considering a slider with a
* primary scale from 0 to 30, using
*
+ winhSetSliderTicks(hwndSlider,
+ 0, // every tick
+ 3, // to three pixels
+ MPFROM2SHORT(9, 10) // then every tenth
+ 6); // to six pixels.
*
* Returns FALSE upon errors.
*
*@@added V0.9.1 (99-12-04) [umoeller]
*@@changed V0.9.7 (2001-01-18) [umoeller]: added second set
*/
BOOL winhSetSliderTicks(HWND hwndSlider, // in: linear slider
MPARAM mpEveryOther1, // in: set 1
ULONG ulPixels1,
MPARAM mpEveryOther2, // in: set 2
ULONG ulPixels2)
{
BOOL brc = FALSE;
ULONG ulSet;
MPARAM mpEveryOther = mpEveryOther1;
ULONG ulPixels = ulPixels1;
// do this twice
for (ulSet = 0;
ulSet < 2;
ulSet++)
{
if (mpEveryOther == 0)
{
// set all ticks:
brc = (BOOL)WinSendMsg(hwndSlider,
SLM_SETTICKSIZE,
MPFROM2SHORT(SMA_SETALLTICKS,
ulPixels),
NULL);
}
else if ( (mpEveryOther != (MPARAM)-1) && (ulPixels != -1) )
{
SLDCDATA slcd;
WNDPARAMS wp;
memset(&wp, 0, sizeof(WNDPARAMS));
wp.fsStatus = WPM_CTLDATA;
wp.cbCtlData = sizeof(slcd);
wp.pCtlData = &slcd;
// get primary scale data from the slider
if (WinSendMsg(hwndSlider,
WM_QUERYWINDOWPARAMS,
(MPARAM)&wp,
0))
{
USHORT usStart = SHORT1FROMMP(mpEveryOther),
usEveryOther = SHORT2FROMMP(mpEveryOther);
USHORT usScale1Max = slcd.usScale1Increments,
us;
brc = TRUE;
for (us = usStart; us < usScale1Max; us += usEveryOther)
{
if (!(BOOL)WinSendMsg(hwndSlider,
SLM_SETTICKSIZE,
MPFROM2SHORT(us,
ulPixels),
NULL))
{
brc = FALSE;
break;
}
}
}
}
// for the second loop, use second value set
mpEveryOther = mpEveryOther2;
ulPixels = ulPixels2;
// we only loop twice
} // end for (ulSet = 0; ulSet < 2;
return brc;
}
/*
*@@ winhReplaceWithCircularSlider:
* this destroys the control with the ID ulID in hwndDlg
* and creates a linear slider at the same position with the
* same ID (effectively replacing it).
*
* This is needed because the IBM dialog editor (DLGEDIT.EXE)
* cannot create circular sliders. So the way to do this
* easily is to create some other control with DLGEDIT
* where the slider should be later and call this function
* on that control when the dialog is initialized.
*
* You need to specify the following with ulSliderStyle:
* -- CSS_CIRCULARVALUE: draws a circular thumb, rather than a line,
* for the value indicator.
* -- CSS_MIDPOINT: makes the mid-point tick mark larger.
* -- CSS_NOBUTTON: does not display value buttons. Per default, the
* slider displays "-" and "+" buttons to the bottom left
* and bottom right of the knob. (BTW, these bitmaps can be
* changed using CSM_SETBITMAPDATA.)
* -- CSS_NONUMBER: does not display the value on the dial.
* -- CSS_NOTEXT: does not display title text under the dial.
* Otherwise, the text in the pszTitle parameter
* will be used.
* -- CSS_NOTICKS (only listed in pmstddlg.h, not in PMREF):
* obviously, this prevents tick marks from being drawn.
* -- CSS_POINTSELECT: permits the values on the circular slider
* to change immediately when dragged.
* Direct manipulation is performed by using a mouse to
* click on and drag the circular slider. There are two
* modes of direct manipulation for the circular slider:
* <BR><B>1)</B> The default direct manipulation mode is to scroll to
* the value indicated by the position of the mouse.
* This could be important if you used a circular slider
* for a volume control, for example. Increasing the volume
* from 0% to 100% too quickly could result in damage to
* both the user's ears and the equipment.
* <BR><B>2)</B>The other mode of direct manipulation permits
* the value on the circular slider to change immediately when dragged.
* This mode is enabled using the CSS_POINTSELECT style bit. When this
* style is used, the value of the dial can be changed by tracking
* the value with the mouse, which changes values quickly.
* -- CSS_PROPORTIONALTICKS: allow the length of the tick marks to be calculated
* as a percentage of the radius (for small sliders).
* -- CSS_360: permits the scroll range to extend 360 degrees.
* CSS_360 forces the CSS_NONUMBER style on. This is necessary
* to keep the value indicator from corrupting the number value.
*
* FYI: The most commonly known circular slider in OS/2, the one in the
* default "Sound" object, has a style of 0x9002018a, meaning
* CSS_NOTEXT | CSS_POINTSELECT | CSS_NOTICKS.
*
* Notes: This function automatically adds WS_PARENTCLIP,
* WS_TABSTOP, and WS_SYNCPAINT to the specified styles.
* For the WS_TABSTOP style, hwndInsertAfter is important.
* If you specify HWND_TOP, your window will be the first
* in the tab stop list.
*
* It also shows the slider after having done all the
* processing in here by calling WinShowWindow.
*
* This returns the HWND of the slider or NULLHANDLE upon
* errors.
*
*@@added V0.9.0 [umoeller]
*/
HWND winhReplaceWithCircularSlider(HWND hwndParent, // in: parent of old control and slider
HWND hwndOwner, // in: owner of old control and slider
HWND hwndInsertAfter, // in: the control after which the slider should
// come up, or HWND_TOP, or HWND_BOTTOM
ULONG ulID, // in: ID of old control and slider
ULONG ulSliderStyle, // in: SLS_* styles
SHORT sMin, // in: minimum value (e.g. 0)
SHORT sMax, // in: maximum value (e.g. 100)
USHORT usIncrement, // in: minimum increment (e.g. 1)
USHORT usTicksEvery) // in: ticks ever x values (e.g. 20)
{
HWND hwndSlider = NULLHANDLE;
HWND hwndKill = WinWindowFromID(hwndParent, ulID);
if (hwndKill)
{
SWP swpControl;
if (WinQueryWindowPos(hwndKill, &swpControl))
{
// destroy the old control
WinDestroyWindow(hwndKill);
// WinRegisterCircularSlider();
// create a slider with the same ID at the same
// position
hwndSlider = WinCreateWindow(hwndParent,
WC_CIRCULARSLIDER,
"dummy", // no window text
ulSliderStyle
// | WS_PARENTCLIP
// | WS_SYNCPAINT
| WS_TABSTOP,
swpControl.x,
swpControl.y,
swpControl.cx,
swpControl.cy,
hwndOwner,
hwndInsertAfter,
ulID, // same ID as destroyed control
NULL, // control data
NULL); // presparams
if (hwndSlider)
{
// set slider range
WinSendMsg(hwndSlider,
CSM_SETRANGE,
(MPARAM)(ULONG)sMin,
(MPARAM)(ULONG)sMax);
// set slider increments
WinSendMsg(hwndSlider,
CSM_SETINCREMENT,
(MPARAM)(ULONG)usIncrement,
(MPARAM)(ULONG)usTicksEvery);
// set slider value
WinSendMsg(hwndSlider,
CSM_SETVALUE,
(MPARAM)0,
(MPARAM)0);
// for some reason, the slider always has
// WS_CLIPSIBLINGS set, even though we don't
// set this; we must unset this now, or
// the slider won't draw itself (%&$&%"$&%!!!)
WinSetWindowBits(hwndSlider,
QWL_STYLE,
0, // unset bit
WS_CLIPSIBLINGS);
WinShowWindow(hwndSlider, TRUE);
}
}
}
return (hwndSlider);
}
/*
*@@category: Helpers\PM helpers\Spin button helpers
*/
/* ******************************************************************
*
* Spin button helpers
*
********************************************************************/
/*
*@@ winhSetDlgItemSpinData:
* sets a spin button's limits and data within a dialog window.
* This only works for decimal spin buttons.
*/
VOID winhSetDlgItemSpinData(HWND hwndDlg, // in: dlg window
ULONG idSpinButton, // in: item ID of spin button
ULONG min, // in: minimum allowed value
ULONG max, // in: maximum allowed value
ULONG current) // in: new current value
{
HWND hwndSpinButton = WinWindowFromID(hwndDlg, idSpinButton);
if (hwndSpinButton)
{
WinSendMsg(hwndSpinButton,
SPBM_SETLIMITS, // Set limits message
(MPARAM)max, // Spin Button maximum setting
(MPARAM)min); // Spin Button minimum setting
WinSendMsg(hwndSpinButton,
SPBM_SETCURRENTVALUE, // Set current value message
(MPARAM)current,
(MPARAM)NULL);
}
}
/*
*@@ winhAdjustDlgItemSpinData:
* this can be called on a spin button control to
* have its current data snap to a grid. This only
* works for LONG integer values.
*
* For example, if you specify 100 for the grid and call
* this func after you have received SPBN_UP/DOWNARROW,
* the spin button's value will always in/decrease
* so that the spin button's value is a multiple of 100.
*
* By contrast, if (lGrid < 0), this will not really
* snap the value to a multiple of -lGrid, but instead
* in/decrease the value by -lGrid. The value will not
* necessarily be a multiple of the grid. (0.9.14)
*
* This returns the "snapped" value to which the spin
* button was set.
*
* If you specify lGrid == 0, this returns the spin
* button's value only without snapping (V0.9.0).
*
*@@changed V0.9.0 [umoeller]: added check for lGrid == 0 (caused division by zero previously)
*@@changed V0.9.14 (2001-08-03) [umoeller]: added fixes for age-old problems with wrap around
*@@changed V0.9.14 (2001-08-03) [umoeller]: added lGrid < 0 mode
*/
LONG winhAdjustDlgItemSpinData(HWND hwndDlg, // in: dlg window
USHORT usItemID, // in: item ID of spin button
LONG lGrid, // in: grid
USHORT usNotifyCode) // in: SPBN_UP* or *DOWNARROW of WM_CONTROL message
{
HWND hwndSpin = WinWindowFromID(hwndDlg, usItemID);
LONG lBottom, lTop, lValue;
// get value, which has already increased /
// decreased by 1
WinSendMsg(hwndSpin,
SPBM_QUERYVALUE,
(MPARAM)&lValue,
MPFROM2SHORT(0, SPBQ_ALWAYSUPDATE));
if ((lGrid)
&& ( (usNotifyCode == SPBN_UPARROW)
|| (usNotifyCode == SPBN_DOWNARROW)
)
)
{
// only if the up/down buttons were pressed,
// snap to the nearest grid; if the user
// manually enters something (SPBN_CHANGE),
// we'll accept that value
LONG lChanged = (usNotifyCode == SPBN_UPARROW)
// if the spin button went up, subtract 1
? -1
: +1;
LONG lPrev = lValue + lChanged;
// if grid is negative, it is assumed to
// not be a "real" grid but jump in those
// steps only
if (lGrid < 0)
{
// add /subtract grid
if (usNotifyCode == SPBN_UPARROW)
lValue = lPrev - lGrid;
else
lValue = lPrev + lGrid;
// lValue = (lValue / lGrid) * lGrid;
}
else
{
// add /subtract grid
if (usNotifyCode == SPBN_UPARROW)
lValue = lPrev + lGrid;
else
lValue = lPrev - lGrid;
lValue = (lValue / lGrid) * lGrid;
}
// balance with spin button limits
WinSendMsg(hwndSpin,
SPBM_QUERYLIMITS,
(MPARAM)&lTop,
(MPARAM)&lBottom);
if (lValue < lBottom)
lValue = lTop;
else if (lValue > lTop)
lValue = lBottom;
WinSendMsg(hwndSpin,
SPBM_SETCURRENTVALUE,
(MPARAM)(lValue),
MPNULL);
}
return (lValue);
}
/*
*@@category: Helpers\PM helpers\List box helpers
*/
/* ******************************************************************
*
* List box helpers
*
********************************************************************/
/*
*@@ winhQueryLboxItemText:
* returns the text of the specified
* list box item in a newly allocated
* buffer.
*
* Returns NULL on error. Use fre()
* to free the return value.
*
*@@added V0.9.1 (99-12-14) [umoeller]
*/
PSZ winhQueryLboxItemText(HWND hwndListbox,
SHORT sIndex)
{
PSZ pszReturn = 0;
SHORT cbText = SHORT1FROMMR(WinSendMsg(hwndListbox,
LM_QUERYITEMTEXTLENGTH,
(MPARAM)(ULONG)sIndex,
0));
if ((cbText) && (cbText != LIT_ERROR))
{
pszReturn = (PSZ)malloc(cbText + 1); // add zero terminator
WinSendMsg(hwndListbox,
LM_QUERYITEMTEXT,
MPFROM2SHORT(sIndex,
cbText + 1),
(MPARAM)pszReturn);
}
return (pszReturn);
}
/*
*@@ winhMoveLboxItem:
* this moves one list box item from one
* list box to another, including the
* item text and the item "handle"
* (see LM_QUERYITEMHANDLE).
*
* sTargetIndex can either be a regular
* item index or one of the following
* (as in LM_INSERTITEM):
* -- LIT_END
* -- LIT_SORTASCENDING
* -- LIT_SORTDESCENDING
*
* If (fSelectTarget == TRUE), the new
* item is also selected in the target
* list box.
*
* Returns FALSE if moving failed. In
* that case, the list boxes are unchanged.
*
*@@added V0.9.1 (99-12-14) [umoeller]
*/
BOOL winhMoveLboxItem(HWND hwndSource,
SHORT sSourceIndex,
HWND hwndTarget,
SHORT sTargetIndex,
BOOL fSelectTarget)
{
BOOL brc = FALSE;
PSZ pszItemText = winhQueryLboxItemText(hwndSource, sSourceIndex);
if (pszItemText)
{
ULONG ulItemHandle = winhQueryLboxItemHandle(hwndSource,
sSourceIndex);
// probably 0, if not used
LONG lTargetIndex = WinInsertLboxItem(hwndTarget,
sTargetIndex,
pszItemText);
if ( (lTargetIndex != LIT_ERROR)
&& (lTargetIndex != LIT_MEMERROR)
)
{
// successfully inserted:
winhSetLboxItemHandle(hwndTarget, lTargetIndex, ulItemHandle);
if (fSelectTarget)
winhSetLboxSelectedItem(hwndTarget, lTargetIndex, TRUE);
// remove source
WinDeleteLboxItem(hwndSource,
sSourceIndex);
brc = TRUE;
}
free(pszItemText);
}
return brc;
}
/*
*@@ winhLboxSelectAll:
* this selects or deselects all items in the
* given list box, depending on fSelect.
*
* Returns the number of items in the list box.
*/
ULONG winhLboxSelectAll(HWND hwndListBox, // in: list box
BOOL fSelect) // in: TRUE = select, FALSE = deselect
{
LONG lItemCount = WinQueryLboxCount(hwndListBox);
ULONG ul;
for (ul = 0; ul < lItemCount; ul++)
{
WinSendMsg(hwndListBox,
LM_SELECTITEM,
(MPARAM)ul, // index
(MPARAM)fSelect);
}
return (lItemCount);
}
/*
*@@ winhLboxFindItemFromHandle:
* finds the list box item with the specified
* handle.
*
* Of course this only makes sense if each item
* has a unique handle indeed.
*
* Returns the index of the item found or -1.
*
*@@added V0.9.12 (2001-05-18) [umoeller]
*/
ULONG winhLboxFindItemFromHandle(HWND hwndListBox,
ULONG ulHandle)
{
LONG cItems;
if (cItems = WinQueryLboxCount(hwndListBox))
{
ULONG ul;
for (ul = 0;
ul < cItems;
ul++)
{
if (ulHandle == winhQueryLboxItemHandle(hwndListBox,
ul))
return (ul);
}
}
return (-1);
}
/*
*@@category: Helpers\PM helpers\Scroll bar helpers
*/
/* ******************************************************************
*
* Scroll bar helpers
*
********************************************************************/
/*
*@@ winhUpdateScrollBar:
* updates the given scroll bar according to the given
* values. This updates the scroll bar's thumb size,
* extension, and position, all in one shot.
*
* This function usually gets called when the window is
* created and later when the window is resized.
*
* This simplifies the typical functionality of a scroll
* bar in a client window which is to be scrolled. I am
* wondering why IBM never included such a function, since
* it is so damn basic and still writing it cost me a whole
* day.
*
* Terminology:
*
* -- "window": the actual window with scroll bars which displays
* a subrectangle of the available data. With a typical PM
* application, this will be your client window.
*
* The width or height of this must be passed in ulWinPels.
*
* -- "viewport": the entire data to be displayed, of which the
* "window" can only display a subrectangle, if the viewport
* is larger than the window.
*
* The width or height of this must be passed in ulViewportPels.
* This can be smaller than ulWinPels (if the window is larger
* than the data) or the same or larger than ulWinPels
* (if the window is too small to show all the data).
*
* -- "window offset": the offset of the current window within
* the viewport.
*
* For horizontal scroll bars, this is the X coordinate,
* counting from the left of the window (0 means leftmost).
*
* For vertical scroll bars, this is counted from the _top_
* of the viewport (0 means topmost, as opposed to OS/2
* window coordinates!). This is because for vertical scroll
* bars controls, higher values move the thumb _down_. Yes
* indeed, this conflicts with PM's coordinate system.
*
* The window offset is therefore always positive.
*
* The scroll bar gets disabled if the entire viewport is visible,
* that is, if ulViewportPels <= ulWinPels. In that case
* FALSE is returned. If (fAutoHide == TRUE), the scroll
* bar is not only disabled, but also hidden from the display.
* In that case, you will need to reformat your output because
* your viewport becomes larger without the scroll bar.
*
* This function will set the range of the scroll bar to 0 up
* to a value depending on the viewport size. For vertical scroll
* bars, 0 means topmost (which is kinda sick with the OS/2
* coordinate system), for horizontal scroll bars, 0 means leftmost.
*
* The maximum value of the scroll bar will be
*
+ (ulViewportPels - ulWinPels) / usScrollUnitPels
*
* The thumb size of the scroll bar will also be adjusted
* based on the viewport and window size, as it should be.
*
*@@added V0.9.1 (2000-02-14) [umoeller]
*@@changed V0.9.3 (2000-04-30) [umoeller]: fixed pels/unit confusion
*@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
*/
BOOL winhUpdateScrollBar(HWND hwndScrollBar, // in: scroll bar (vertical or horizontal)
ULONG ulWinPels, // in: vertical or horizontal dimension of
// visible window part (in pixels),
// excluding the scroll bar!
ULONG ulViewportPels, // in: dimension of total data part, of
// which ulWinPels is a sub-dimension
// (in pixels);
// if <= ulWinPels, the scrollbar will be
// disabled
ULONG ulCurPelsOfs, // in: current offset of visible part
// (in pixels)
BOOL fAutoHide) // in: hide scroll bar if disabled
{
BOOL brc = FALSE;
// _Pmpf(("Entering winhUpdateScrollBar"));
// for large viewports, adjust scroll bar units
USHORT usScrollUnitPels = 1;
if (ulViewportPels > 10000)
usScrollUnitPels = 100;
if (ulViewportPels > ulWinPels)
{
// scrollbar needed:
USHORT usThumbDivisorUnits = usScrollUnitPels;
USHORT lMaxAllowedUnitOfs;
// _Pmpf(("winhUpdateScrollBar: ulViewportPels > ulWinPels, enabling scroller"));
// divisor for thumb size (below)
if (ulViewportPels > 10000)
// for very large viewports, we need to
// raise the divisor, because we only
// have a USHORT
usThumbDivisorUnits = usScrollUnitPels * 100;
// viewport is larger than window:
WinEnableWindow(hwndScrollBar, TRUE);
if (fAutoHide)
WinShowWindow(hwndScrollBar, TRUE);
// calculate limit
lMaxAllowedUnitOfs = ((ulViewportPels - ulWinPels + usScrollUnitPels)
// scroll unit is 10
/ usScrollUnitPels);
// _Pmpf((" usCurUnitOfs: %d", ulCurUnitOfs));
// _Pmpf((" usMaxUnits: %d", lMaxAllowedUnitOfs));
// set thumb position and limit
WinSendMsg(hwndScrollBar,
SBM_SETSCROLLBAR,
(MPARAM)(ulCurPelsOfs), // / usThumbDivisorUnits), // position: 0 means top
MPFROM2SHORT(0, // minimum
lMaxAllowedUnitOfs)); // maximum
// set thumb size based on ulWinPels and
// ulViewportPels
WinSendMsg(hwndScrollBar,
SBM_SETTHUMBSIZE,
MPFROM2SHORT( ulWinPels / usThumbDivisorUnits, // visible
ulViewportPels / usThumbDivisorUnits), // total
0);
brc = TRUE;
}
else
{
// _Pmpf(("winhUpdateScrollBar: ulViewportPels <= ulWinPels"));
// entire viewport is visible:
WinEnableWindow(hwndScrollBar, FALSE);
if (fAutoHide)
WinShowWindow(hwndScrollBar, FALSE);
}
// _Pmpf(("End of winhUpdateScrollBar"));
return brc;
}
/*
*@@ winhHandleScrollMsg:
* this helper handles a WM_VSCROLL or WM_HSCROLL
* message posted to a client window when the user
* has worked on a client scroll bar. Calling this
* function is ALL you need to do to handle those
* two messages.
*
* This is most useful in conjunction with winhUpdateScrollBar.
* See that function for the terminology also.
*
* This function calculates the new scrollbar position
* (from the mp2 value, which can be line up/down,
* page up/down, or slider track) and calls WinScrollWindow
* accordingly. The window part which became invalid
* because of the scrolling is automatically invalidated
* (using WinInvalidateRect), so expect a WM_PAINT after
* calling this function.
*
* This function assumes that the scrollbar operates
* on values starting from zero. The maximum value
* of the scroll bar is:
*
+ ulViewportPels - (prcl2Scroll->yTop - prcl2Scroll->yBottom)
*
* This function also automatically changes the scroll bar
* units, should you have a viewport size which doesn't fit
* into the SHORT's that the scroll bar uses internally. As
* a result, this function handles a the complete range of
* a ULONG for the viewport.
*
* Replace "bottom" and "top" with "right" and "left" for
* horizontal scrollbars in the above formula.
*
*@@added V0.9.1 (2000-02-13) [umoeller]
*@@changed V0.9.3 (2000-04-30) [umoeller]: changed prototype, fixed pels/unit confusion
*@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
*@@changed V0.9.7 (2001-01-17) [umoeller]: changed PLONG to PULONG
*/
BOOL winhHandleScrollMsg(HWND hwnd2Scroll, // in: client window to scroll
HWND hwndScrollBar, // in: vertical or horizontal scroll bar window
PULONG pulCurPelsOfs, // in/out: current viewport offset;
// this is updated with the proper scroll units
PRECTL prcl2Scroll, // in: hwnd2Scroll rectangle to scroll
// (in window coordinates);
// this is passed to WinScrollWindow,
// which considers this inclusive!
LONG ulViewportPels, // in: total viewport dimension,
// into which *pulCurPelsOfs is an offset
USHORT usLineStepPels, // in: pixels to scroll line-wise
// (scroll bar buttons pressed)
ULONG msg, // in: either WM_VSCROLL or WM_HSCROLL
MPARAM mp2) // in: complete mp2 of WM_VSCROLL/WM_HSCROLL;
// this has two SHORT's (usPos and usCmd),
// see PMREF for details
{
ULONG ulOldPelsOfs = *pulCurPelsOfs;
USHORT usPosUnits = SHORT1FROMMP(mp2), // in scroll units
usCmd = SHORT2FROMMP(mp2);
LONG lMaxAllowedUnitOfs;
ULONG ulWinPels;
// for large viewports, adjust scroll bar units
USHORT usScrollUnitPels = 1;
if (ulViewportPels > 10000)
usScrollUnitPels = 100;
// calculate window size (vertical or horizontal)
if (msg == WM_VSCROLL)
ulWinPels = (prcl2Scroll->yTop - prcl2Scroll->yBottom);
else
ulWinPels = (prcl2Scroll->xRight - prcl2Scroll->xLeft);
lMaxAllowedUnitOfs = ((LONG)ulViewportPels - ulWinPels) / usScrollUnitPels;
// _Pmpf(("Entering winhHandleScrollMsg"));
switch (usCmd)
{
case SB_LINEUP:
if (*pulCurPelsOfs > usLineStepPels)
*pulCurPelsOfs -= usLineStepPels; // * usScrollUnitPels);
else
*pulCurPelsOfs = 0;
break;
case SB_LINEDOWN:
*pulCurPelsOfs += usLineStepPels; // * usScrollUnitPels);
break;
case SB_PAGEUP:
if (*pulCurPelsOfs > ulWinPels)
*pulCurPelsOfs -= ulWinPels; // convert to units
else
*pulCurPelsOfs = 0;
break;
case SB_PAGEDOWN:
*pulCurPelsOfs += ulWinPels; // convert to units
break;
case SB_SLIDERTRACK:
*pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
// _Pmpf((" SB_SLIDERTRACK: usUnits = %d", usPosUnits));
break;
case SB_SLIDERPOSITION:
*pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
break;
}
// are we close to the lower limit?
/* if (*plCurUnitOfs < usLineStepUnits) // usScrollUnit)
*plCurUnitOfs = 0;
// are we close to the upper limit?
else if (*plCurUnitOfs + usLineStepUnits > lMaxUnitOfs)
{
_Pmpf((" !!! limiting: %d to %d", *plCurUnitOfs, lMaxUnitOfs));
*plCurUnitOfs = lMaxUnitOfs;
} */
/* if (*plCurPelsOfs < 0)
*plCurPelsOfs = 0; */ // checked above
if (*pulCurPelsOfs > (lMaxAllowedUnitOfs * usScrollUnitPels))
{
*pulCurPelsOfs = (lMaxAllowedUnitOfs * usScrollUnitPels);
}
if ( (*pulCurPelsOfs != ulOldPelsOfs)
|| (*pulCurPelsOfs == 0)
|| (*pulCurPelsOfs == (lMaxAllowedUnitOfs * usScrollUnitPels))
)
{
RECTL rcl2Scroll,
rcl2Update;
// changed:
WinSendMsg(hwndScrollBar,
SBM_SETPOS,
(MPARAM)(*pulCurPelsOfs / usScrollUnitPels), // / usScrollUnit),
0);
// scroll window rectangle:
rcl2Scroll.xLeft = prcl2Scroll->xLeft;
rcl2Scroll.xRight = prcl2Scroll->xRight;
rcl2Scroll.yBottom = prcl2Scroll->yBottom;
rcl2Scroll.yTop = prcl2Scroll->yTop;
if (msg == WM_VSCROLL)
WinScrollWindow(hwnd2Scroll,
0,
(*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
, // * usScrollUnitPels, // convert to pels
&rcl2Scroll, // rcl to scroll
prcl2Scroll, // clipping rect
NULLHANDLE, // no region
&rcl2Update,
0);
else
WinScrollWindow(hwnd2Scroll,
-(LONG)(*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
, // * usScrollUnitPels,
0,
&rcl2Scroll, // rcl to scroll
prcl2Scroll, // clipping rect
NULLHANDLE, // no region
&rcl2Update,
0);
// WinScrollWindow has stored the invalid window
// rectangle which needs to be repainted in rcl2Update:
WinInvalidateRect(hwnd2Scroll, &rcl2Update, FALSE);
}
// _Pmpf(("End of winhHandleScrollMsg"));
return (TRUE);
}
/*
*@@ winhProcessScrollChars:
* helper for processing WM_CHAR messages for
* client windows with scroll bars.
*
* If your window has scroll bars, you normally
* need to process a number of keystrokes to be
* able to scroll the window contents. This is
* tiresome to code, so here is a helper.
*
* When receiving WM_CHAR, call this function.
* If this returns TRUE, the keystroke has been
* a scroll keystroke, and the window has been
* updated (by sending WM_VSCROLL or WM_HSCROLL
* to hwndClient). Otherwise, you should process
* the keystroke as usual because it's not a
* scroll keystroke.
*
* The following keystrokes are processed here:
*
* -- "cursor up, down, right, left": scroll one
* line in the proper direction.
* -- "page up, down": scroll one page up or down.
* -- "Home": scroll leftmost.
* -- "Ctrl+ Home": scroll topmost.
* -- "End": scroll rightmost.
* -- "Ctrl+ End": scroll bottommost.
* -- "Ctrl + page up, down": scroll one screen left or right.
*
* This is CUA behavior.
*
* Returns TRUE if the message has been
* processed.
*
*@@added V0.9.3 (2000-04-29) [umoeller]
*@@changed V0.9.9 (2001-02-01) [lafaix]: Ctrl+PgUp/Dn now do one screen left/right
*/
BOOL winhProcessScrollChars(HWND hwndClient, // in: client window
HWND hwndVScroll, // in: vertical scroll bar
HWND hwndHScroll, // in: horizontal scroll bar
MPARAM mp1, // in: WM_CHAR mp1
MPARAM mp2, // in: WM_CHAR mp2
ULONG ulVertMax, // in: maximum viewport cy
ULONG ulHorzMax) // in: maximum viewport cx
{
BOOL fProcessed = FALSE;
USHORT usFlags = SHORT1FROMMP(mp1);
// USHORT usch = SHORT1FROMMP(mp2);
USHORT usvk = SHORT2FROMMP(mp2);
// _Pmpf(("Entering winhProcessScrollChars"));
if (usFlags & KC_VIRTUALKEY)
{
ULONG ulMsg = 0;
SHORT sPos = 0;
SHORT usCmd = 0;
fProcessed = TRUE;
switch (usvk)
{
case VK_UP:
ulMsg = WM_VSCROLL;
usCmd = SB_LINEUP;
break;
case VK_DOWN:
ulMsg = WM_VSCROLL;
usCmd = SB_LINEDOWN;
break;
case VK_RIGHT:
ulMsg = WM_HSCROLL;
usCmd = SB_LINERIGHT;
break;
case VK_LEFT:
ulMsg = WM_HSCROLL;
usCmd = SB_LINELEFT;
break;
case VK_PAGEUP:
if (usFlags & KC_CTRL)
ulMsg = WM_HSCROLL;
else
ulMsg = WM_VSCROLL;
usCmd = SB_PAGEUP;
break;
case VK_PAGEDOWN:
if (usFlags & KC_CTRL)
ulMsg = WM_HSCROLL;
else
ulMsg = WM_VSCROLL;
usCmd = SB_PAGEDOWN;
break;
case VK_HOME:
if (usFlags & KC_CTRL)
// vertical:
ulMsg = WM_VSCROLL;
else
ulMsg = WM_HSCROLL;
sPos = 0;
usCmd = SB_SLIDERPOSITION;
break;
case VK_END:
if (usFlags & KC_CTRL)
{
// vertical:
ulMsg = WM_VSCROLL;
sPos = ulVertMax;
}
else
{
ulMsg = WM_HSCROLL;
sPos = ulHorzMax;
}
usCmd = SB_SLIDERPOSITION;
break;
default:
// other:
fProcessed = FALSE;
}
if ( ((usFlags & KC_KEYUP) == 0)
&& (ulMsg)
)
{
HWND hwndScrollBar = ((ulMsg == WM_VSCROLL)
? hwndVScroll
: hwndHScroll);
if (WinIsWindowEnabled(hwndScrollBar))
{
USHORT usID = WinQueryWindowUShort(hwndScrollBar,
QWS_ID);
WinSendMsg(hwndClient,
ulMsg,
MPFROMSHORT(usID),
MPFROM2SHORT(sPos,
usCmd));
}
}
}
// _Pmpf(("End of winhProcessScrollChars"));
return (fProcessed);
}
/*
*@@category: Helpers\PM helpers\Window positioning
*/
/* ******************************************************************
*
* Window positioning helpers
*
********************************************************************/
/*
*@@ winhSaveWindowPos:
* saves the position of a certain window. As opposed
* to the barely documented WinStoreWindowPos API, this
* one only saves one regular SWP structure for the given
* window, as returned by WinQueryWindowPos for hwnd.
*
* If the window is currently maximized or minimized,
* we won't store the current window size and position
* (which wouldn't make much sense), but retrieve the
* "restored" window position from the window words
* instead.
*
* The window should still be visible on the screen
* when calling this function. Do not call it in WM_DESTROY,
* because then the SWP data is no longer valid.
*
* This returns TRUE if saving was successful.
*
*@@changed V0.9.1 (99-12-19) [umoeller]: added minimize/maximize support
*/
BOOL winhSaveWindowPos(HWND hwnd, // in: window to save
HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
const char *pcszApp, // in: INI application name
const char *pcszKey) // in: INI key name
{
BOOL brc = FALSE;
SWP swp;
if (WinQueryWindowPos(hwnd, &swp))
{
if (swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE))
{
// window currently maximized or minimized:
// retrieve "restore" position from window words
swp.x = WinQueryWindowUShort(hwnd, QWS_XRESTORE);
swp.y = WinQueryWindowUShort(hwnd, QWS_YRESTORE);
swp.cx = WinQueryWindowUShort(hwnd, QWS_CXRESTORE);
swp.cy = WinQueryWindowUShort(hwnd, QWS_CYRESTORE);
}
brc = PrfWriteProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, sizeof(swp));
}
return brc;
}
/*
*@@ winhRestoreWindowPos:
* this will retrieve a window position which was
* previously stored using winhSaveWindowPos.
*
* The window should not be visible to avoid flickering.
* "fl" must contain the SWP_flags as in WinSetWindowPos.
*
* Note that only the following may be used:
* -- SWP_MOVE reposition the window
* -- SWP_SIZE also resize the window to
* the stored position; this might
* lead to problems with different
* video resolutions, so be careful.
* -- SWP_SHOW make window visible too
* -- SWP_NOREDRAW changes are not redrawn
* -- SWP_NOADJUST do not send a WM_ADJUSTWINDOWPOS message
* before moving or sizing
* -- SWP_ACTIVATE activate window (make topmost)
* -- SWP_DEACTIVATE deactivate window (make bottommost)
*
* Do not specify any other SWP_* flags.
*
* If SWP_SIZE is not set, the window will be moved only.
*
* This returns TRUE if INI data was found.
*
* This function automatically checks for whether the
* window would be positioned outside the visible screen
* area and will adjust coordinates accordingly. This can
* happen when changing video resolutions.
*
*@@changed V0.9.7 (2000-12-20) [umoeller]: fixed invalid params if INI key not found
*/
BOOL winhRestoreWindowPos(HWND hwnd, // in: window to restore
HINI hIni, // in: INI file (or HINI_USER/SYSTEM)
const char *pcszApp, // in: INI application name
const char *pcszKey, // in: INI key name
ULONG fl) // in: "fl" parameter for WinSetWindowPos
{
BOOL brc = FALSE;
SWP swp;
ULONG cbswp = sizeof(swp);
ULONG fl2 = (fl & ~SWP_ZORDER);
if (PrfQueryProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, &cbswp))
{
ULONG ulScreenCX = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
ULONG ulScreenCY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
brc = TRUE;
if ((fl & SWP_SIZE) == 0)
{
// if no resize, we need to get the current position
SWP swpNow;
brc = WinQueryWindowPos(hwnd, &swpNow);
swp.cx = swpNow.cx;
swp.cy = swpNow.cy;
}
if (brc)
{
// check for full visibility
if ( (swp.x + swp.cx) > ulScreenCX)
swp.x = ulScreenCX - swp.cx;
if ( (swp.y + swp.cy) > ulScreenCY)
swp.y = ulScreenCY - swp.cy;
}
brc = TRUE;
}
else
{
// window pos not found in INI: unset SWP_MOVE etc.
WinQueryWindowPos(hwnd, &swp);
fl2 &= ~(SWP_MOVE | SWP_SIZE);
}
WinSetWindowPos(hwnd,
NULLHANDLE, // insert-behind window
swp.x,
swp.y,
swp.cx,
swp.cy,
fl2); // SWP_* flags
return brc;
}
/*
*@@ winhAdjustControls:
* helper function for dynamically adjusting a window's
* controls when the window is resized.
*
* This is most useful with dialogs loaded from resources
* which should be sizeable. Normally, when the dialog
* frame is resized, the controls stick to their positions,
* and for dialogs with many controls, programming the
* changes can be tiresome.
*
* Enter this function. ;-) Basically, this takes a
* static array of MPARAM's as input (plus one dynamic
* storage area for the window positions).
*
* This function must get called in three contexts:
* during WM_INITDLG, during WM_WINDOWPOSCHANGED, and
* during WM_DESTROY, with varying parameters.
*
* In detail, there are four things you need to do to make
* this work:
*
* 1) Set up a static array (as a global variable) of
* MPARAM's, one for each control in your array.
* Each MPARAM will have the control's ID and the
* XAC_* flags (winh.h) how the control shall be moved.
* Use MPFROM2SHORT to easily create this. Example:
*
+ MPARAM ampControlFlags[] =
+ { MPFROM2SHORT(ID_CONTROL_1, XAC_MOVEX),
+ MPFROM2SHORT(ID_CONTROL_2, XAC_SIZEY),
+ ...
+ }
*
* This can safely be declared as a global variable
* because this data will only be read and never
* changed by this function.
*
* 2) In WM_INITDLG of your dialog function, set up
* an XADJUSTCTRLS structure, preferrably in your
* window words (QWL_USER).
*
* ZERO THAT STRUCTURE (memset(&xac, 0, sizeof(XADJUSTCTRLS),
* or this func will not work (because it will intialize
* things on the first WM_WINDOWPOSCHANGED).
*
* 3) Intercept WM_WINDOWPOSCHANGED:
*
+ case WM_WINDOWPOSCHANGED:
+ {
+ // this msg is passed two SWP structs:
+ // one for the old, one for the new data
+ // (from PM docs)
+ PSWP pswpNew = PVOIDFROMMP(mp1);
+ PSWP pswpOld = pswpNew + 1;
+
+ // resizing?
+ if (pswpNew->fl & SWP_SIZE)
+ {
+ PXADJUSTCTRLS pxac = ... // get it from your window words
+
+ winhAdjustControls(hwndDlg, // dialog
+ ampControlFlags, // MPARAMs array
+ sizeof(ampControlFlags) / sizeof(MPARAM),
+ // items count
+ pswpNew, // mp1
+ pxac); // storage area
+ }
+ mrc = WinDefDlgProc(hwnd, msg, mp1, mp2); ...
*
* 4) In WM_DESTROY, call this function again with pmpFlags,
* pswpNew, and pswpNew set to NULL. This will clean up the
* data which has been allocated internally (pointed to from
* the XADJUSTCTRLS structure).
* Don't forget to free your storage for XADJUSTCTLRS
* _itself_, that's the job of the caller.
*
* This might sound complicated, but it's a lot easier than
* having to write dozens of WinSetWindowPos calls oneself.
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.19 (2002-04-13) [umoeller]: added correlation for entry field repositioning, this was always off
*/
BOOL winhAdjustControls(HWND hwndDlg, // in: dialog (req.)
const MPARAM *pmpFlags, // in: init flags or NULL for cleanup
ULONG ulCount, // in: item count (req.)
PSWP pswpNew, // in: pswpNew from WM_WINDOWPOSCHANGED or NULL for cleanup
PXADJUSTCTRLS pxac) // in: adjust-controls storage area (req.)
{
BOOL brc = FALSE;
ULONG ul = 0;
/* if (!WinIsWindowVisible(hwndDlg))
return (FALSE); */
if ((pmpFlags) && (pxac))
{
PSWP pswpThis;
const MPARAM *pmpThis;
LONG ldcx, ldcy;
ULONG cWindows = 0;
// V0.9.19 (2002-04-13) [umoeller]
LONG cxMarginEF = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
LONG cyMarginEF = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
// setup mode:
if (pxac->fInitialized == FALSE)
{
// first call: get all the SWP's
WinQueryWindowPos(hwndDlg, &pxac->swpMain);
// _Pmpf(("winhAdjustControls: queried main cx = %d, cy = %d",
// pxac->swpMain.cx, pxac->swpMain.cy));
pxac->paswp = (PSWP)malloc(sizeof(SWP) * ulCount);
pswpThis = pxac->paswp;
pmpThis = pmpFlags;
for (ul = 0;
ul < ulCount;
ul++)
{
HWND hwndThis;
CHAR szClass[10];
if (hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis)))
{
WinQueryWindowPos(hwndThis, pswpThis);
// correlate the stupid repositioning of entry fields
// V0.9.19 (2002-04-13) [umoeller]
if ( (WinQueryClassName(hwndThis, sizeof(szClass), szClass)
&& (!strcmp(szClass, "#6"))
&& (WinQueryWindowULong(hwndThis, QWL_STYLE) & ES_MARGIN))
)
{
pswpThis->x += cxMarginEF;
pswpThis->y += cyMarginEF;
pswpThis->cx -= 2 * cxMarginEF;
pswpThis->cy -= 2 * cyMarginEF;
}
cWindows++;
}
pswpThis++;
pmpThis++;
}
pxac->fInitialized = TRUE;
// _Pmpf(("winhAdjustControls: queried %d controls", cWindows));
}
if (pswpNew)
{
// compute width and height delta
ldcx = (pswpNew->cx - pxac->swpMain.cx);
ldcy = (pswpNew->cy - pxac->swpMain.cy);
// _Pmpf(("winhAdjustControls: new cx = %d, cy = %d",
// pswpNew->cx, pswpNew->cy));
// now adjust the controls
cWindows = 0;
pswpThis = pxac->paswp;
pmpThis = pmpFlags;
for (ul = 0;
ul < ulCount;
ul++)
{
HWND hwndThis;
if (hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis)))
{
LONG x = pswpThis->x,
y = pswpThis->y,
cx = pswpThis->cx,
cy = pswpThis->cy;
ULONG ulSwpFlags = 0;
// get flags for this control
USHORT usFlags = SHORT2FROMMP(*pmpThis);
if (usFlags & XAC_MOVEX)
{
x += ldcx;
ulSwpFlags |= SWP_MOVE;
}
if (usFlags & XAC_MOVEY)
{
y += ldcy;
ulSwpFlags |= SWP_MOVE;
}
if (usFlags & XAC_SIZEX)
{
cx += ldcx;
ulSwpFlags |= SWP_SIZE;
}
if (usFlags & XAC_SIZEY)
{
cy += ldcy;
ulSwpFlags |= SWP_SIZE;
}
if (ulSwpFlags)
{
WinSetWindowPos(hwndThis,
NULLHANDLE, // hwndInsertBehind
x, y, cx, cy,
ulSwpFlags);
cWindows++;
brc = TRUE;
}
}
pswpThis++;
pmpThis++;
}
// _Pmpf(("winhAdjustControls: set %d windows", cWindows));
}
}
else
{
// pxac == NULL:
// cleanup mode
if (pxac->paswp)
free(pxac->paswp);
}
return brc;
}
/*
*@@ winhCenterWindow:
* centers a window within its parent window. If that's
* the PM desktop, it will be centered according to the
* whole screen.
* For dialog boxes, use WinCenteredDlgBox as a one-shot
* function.
*
* Note: When calling this function, the window should
* not be visible to avoid flickering.
* This func does not show the window either, so call
* WinShowWindow afterwards.
*/
void winhCenterWindow(HWND hwnd)
{
RECTL rclParent;
RECTL rclWindow;
WinQueryWindowRect(hwnd, &rclWindow);
WinQueryWindowRect(WinQueryWindow(hwnd, QW_PARENT), &rclParent);
rclWindow.xLeft = (rclParent.xRight - rclWindow.xRight) / 2;
rclWindow.yBottom = (rclParent.yTop - rclWindow.yTop ) / 2;
WinSetWindowPos(hwnd, NULLHANDLE, rclWindow.xLeft, rclWindow.yBottom,
0, 0, SWP_MOVE);
}
/*
*@@ winhCenteredDlgBox:
* just like WinDlgBox, but the dlg box is centered on the screen;
* you should mark the dlg template as not visible in the dlg
* editor, or display will flicker.
* As opposed to winhCenterWindow, this _does_ show the window.
*/
ULONG winhCenteredDlgBox(HWND hwndParent,
HWND hwndOwner,
PFNWP pfnDlgProc,
HMODULE hmod,
ULONG idDlg,
PVOID pCreateParams)
{
ULONG ulReply;
HWND hwndDlg = WinLoadDlg(hwndParent,
hwndOwner,
pfnDlgProc,
hmod,
idDlg,
pCreateParams);
winhCenterWindow(hwndDlg);
ulReply = WinProcessDlg(hwndDlg);
WinDestroyWindow(hwndDlg);
return (ulReply);
}
/*
*@@ winhPlaceBesides:
* attempts to place hwnd somewhere besides
* hwndRelative.
*
* fl is presently ignored, but should be
* PLF_SMART for future extensions.
*
* Works only if hwnd is a desktop child.
*
*@@added V0.9.19 (2002-04-17) [umoeller]
*/
BOOL winhPlaceBesides(HWND hwnd,
HWND hwndRelative,
ULONG fl)
{
BOOL brc = FALSE;
SWP swpRel,
swpThis;
LONG xNew, yNew;
if ( (WinQueryWindowPos(hwndRelative, &swpRel))
&& (WinQueryWindowPos(hwnd, &swpThis))
)
{
HWND hwndRelParent,
hwndThisParent;
POINTL ptlRel = {swpRel.x, swpRel.y};
if ( (hwndRelParent = WinQueryWindow(hwndRelative, QW_PARENT))
&& (hwndThisParent = WinQueryWindow(hwnd, QW_PARENT))
&& (hwndRelParent != hwndThisParent)
)
{
WinMapWindowPoints(hwndRelParent,
hwndThisParent,
&ptlRel,
1);
}
// place right first
xNew = ptlRel.x + swpRel.cx;
// center vertically
yNew = ptlRel.y + ((swpRel.cy - swpThis.cy) / 2);
if (xNew + swpThis.cy > WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN))
{
// place left then
xNew = ptlRel.x - swpThis.cx;
if (xNew < 0)
{
// center then
winhCenterWindow(hwnd);
brc = TRUE;
}
}
if (!brc)
brc = WinSetWindowPos(hwnd,
0,
xNew,
yNew,
0,
0,
SWP_MOVE);
}
return brc;
}
/*
*@@ winhFindWindowBelow:
* finds the window with the same parent
* which sits right below hwndFind in the
* window Z-order.
*
*@@added V0.9.7 (2000-12-04) [umoeller]
*/
HWND winhFindWindowBelow(HWND hwndFind)
{
HWND hwnd = NULLHANDLE,
hwndParent = WinQueryWindow(hwndFind, QW_PARENT);
if (hwndParent)
{
HENUM henum = WinBeginEnumWindows(hwndParent);
HWND hwndThis;
while (hwndThis = WinGetNextWindow(henum))
{
SWP swp;
WinQueryWindowPos(hwndThis, &swp);
if (swp.hwndInsertBehind == hwndFind)
{
hwnd = hwndThis;
break;
}
}
WinEndEnumWindows(henum);
}
return (hwnd);
}
/*
*@@category: Helpers\PM helpers\Presentation parameters
*/
/* ******************************************************************
*
* Presparams helpers
*
********************************************************************/
/*
*@@ winhQueryWindowFont:
* returns the window font presentation parameter
* in a newly allocated buffer.
*
* Returns NULL on error. Use free()
* to free the return value.
*
*@@added V0.9.1 (2000-02-14) [umoeller]
*/
PSZ winhQueryWindowFont(HWND hwnd)
{
CHAR szNewFont[100] = "";
WinQueryPresParam(hwnd,
PP_FONTNAMESIZE,
0,
NULL,
(ULONG)sizeof(szNewFont),
(PVOID)&szNewFont,
QPF_NOINHERIT);
if (szNewFont[0] != 0)
return (strdup(szNewFont));
return NULL;
}
/*
*@@ winhSetWindowFont:
* this sets a window's font by invoking
* WinSetPresParam on it.
*
* If (pszFont == NULL), a default font will be set
* (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
*
* winh.h also defines the winhSetDlgItemFont macro.
*
* Returns TRUE if successful or FALSE otherwise.
*
*@@added V0.9.0 [umoeller]
*/
BOOL winhSetWindowFont(HWND hwnd,
const char *pcszFont)
{
CHAR szFont[256];
if (pcszFont == NULL)
{
if (doshIsWarp4())
strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
else
strhncpy0(szFont, "8.Helv", sizeof(szFont));
}
else
strhncpy0(szFont, pcszFont, sizeof(szFont));
return (WinSetPresParam(hwnd,
PP_FONTNAMESIZE,
strlen(szFont)+1,
szFont));
}
/*
*@@ winhSetControlsFont:
* this sets the font for all the controls of hwndDlg
* which have a control ID in the range of usIDMin to
* usIDMax. "Unused" IDs (i.e. -1) will also be set.
*
* If (pszFont == NULL), a default font will be set
* (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
*
* Returns the no. of controls set.
*
*@@added V0.9.0 [umoeller]
*/
ULONG winhSetControlsFont(HWND hwndDlg, // in: dlg to set
SHORT usIDMin, // in: minimum control ID to be set (inclusive)
SHORT usIDMax, // in: maximum control ID to be set (inclusive)
const char *pcszFont) // in: font to use (e.g. "9.WarpSans") or NULL
{
ULONG ulrc = 0;
HENUM henum;
HWND hwndItem;
CHAR szFont[256];
ULONG cbFont;
if (pcszFont == NULL)
{
if (doshIsWarp4())
strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
else
strhncpy0(szFont, "8.Helv", sizeof(szFont));
}
else
strhncpy0(szFont, pcszFont, sizeof(szFont));
cbFont = strlen(szFont)+1;
// set font for all the dialog controls
henum = WinBeginEnumWindows(hwndDlg);
while ((hwndItem = WinGetNextWindow(henum)))
{
SHORT sID = WinQueryWindowUShort(hwndItem, QWS_ID);
if ( (sID == -1)
|| ((sID >= usIDMin) && (sID <= usIDMax))
)
if (WinSetPresParam(hwndItem,
PP_FONTNAMESIZE,
cbFont,
szFont))
// successful:
ulrc++;
}
WinEndEnumWindows(henum);
return (ulrc);
}
/*
*@@ winhStorePresParam:
* this appends a new presentation parameter to an
* array of presentation parameters which can be
* passed to WinCreateWindow. This is preferred
* over setting the presparams using WinSetPresParams,
* because that call will cause a lot of messages.
*
* On the first call, pppp _must_ be NULL. This
* will allocate memory for storing the given
* data as necessary and modify *pppp to point
* to the new array.
*
* On subsequent calls with the same pppp, memory
* will be reallocated, the old data will be copied,
* and the new given data will be appended.
*
* Use free() on your PPRESPARAMS pointer (whose
* address was passed) after WinCreateWindow.
*
* See winhQueryPresColor for typical presparams
* used in OS/2.
*
* Example:
*
+ PPRESPARAMS ppp = NULL;
+ CHAR szFont[] = "9.WarpSans";
+ LONG lColor = CLR_WHITE;
+ winhStorePresParam(&ppp, PP_FONTNAMESIZE, sizeof(szFont), szFont);
+ winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
+ WinCreateWindow(...., ppp);
+ free(ppp);
*
*@@added V0.9.0 [umoeller]
*/
BOOL winhStorePresParam(PPRESPARAMS *pppp, // in: data pointer (modified)
ULONG ulAttrType, // in: PP_* index
ULONG cbData, // in: sizeof(*pData), e.g. sizeof(LONG)
PVOID pData) // in: presparam data (e.g. a PLONG to a color)
{
BOOL brc = FALSE;
if (pppp)
{
ULONG cbOld = 0,
cbNew;
PBYTE pbTemp = 0;
PPRESPARAMS pppTemp = 0;
PPARAM pppCopyTo = 0;
if (*pppp != NULL)
// subsequent calls:
cbOld = (**pppp).cb;
cbNew = sizeof(ULONG) // PRESPARAMS.cb
+ cbOld // old count, which does not include PRESPARAMS.cb
+ sizeof(ULONG) // PRESPARAMS.aparam[0].id
+ sizeof(ULONG) // PRESPARAMS.aparam[0].cb
+ cbData; // PRESPARAMS.aparam[0].ab[]
pbTemp = (PBYTE)malloc(cbNew);
if (pbTemp)
{
pppTemp = (PPRESPARAMS)pbTemp;
if (*pppp != NULL)
{
// copy old data
memcpy(pbTemp, *pppp, cbOld + sizeof(ULONG)); // including PRESPARAMS.cb
pppCopyTo = (PPARAM)(pbTemp // new buffer
+ sizeof(ULONG) // skipping PRESPARAMS.cb
+ cbOld); // old PARAM array
}
else
// first call:
pppCopyTo = pppTemp->aparam;
pppTemp->cb = cbNew - sizeof(ULONG); // excluding PRESPARAMS.cb
pppCopyTo->id = ulAttrType;
pppCopyTo->cb = cbData; // byte count of PARAM.ab[]
memcpy(pppCopyTo->ab, pData, cbData);
free(*pppp);
*pppp = pppTemp;
brc = TRUE;
}
}
return brc;
}
/*
*@@ winhQueryPresColor2:
* returns the specified color. This is queried in the
* following order:
*
* 1) hwnd's pres params are searched for ulPP
* (which should be a PP_* index);
* 2) if (fInherit == TRUE), the parent windows
* are searched also;
* 3) if this fails or (fInherit == FALSE), WinQuerySysColor
* is called to get lSysColor (which should be a SYSCLR_*
* index), if lSysColor != -1;
* 4) if (lSysColor == -1), -1 is returned.
*
* The return value is always an RGB LONG, _not_ a color index.
* This is even true for the returned system colors, which are
* converted to RGB.
*
* If you do any painting with this value, you should switch
* the HPS you're using to RGB mode (use gpihSwitchToRGB for that).
*
* Some useful ulPP / lSysColor pairs
* (default values as in PMREF):
*
+ -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT (for most controls also)
+ SYSCLR_WINDOWSTATICTEXT (for static controls)
+ Foreground color (default: black)
+ -- PP_BACKGROUNDCOLOR SYSCLR_BACKGROUND
+ SYSCLR_DIALOGBACKGROUND
+ SYSCLR_FIELDBACKGROUND (for disabled scrollbars)
+ SYSCLR_WINDOW (application surface -- empty clients)
+ Background color (default: light gray)
+ -- PP_ACTIVETEXTFGNDCOLOR
+ -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
+ Highlighted foreground color, for example for selected menu
+ (def.: white)
+ -- PP_ACTIVETEXTBGNDCOLOR
+ -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
+ Highlighted background color (def.: dark gray)
+ -- PP_INACTIVETEXTFGNDCOLOR
+ -- PP_DISABLEDFOREGROUNDCOLOR SYSCLR_MENUDISABLEDTEXT
+ Disabled foreground color (dark gray)
+ -- PP_INACTIVETEXTBGNDCOLOR
+ -- PP_DISABLEDBACKGROUNDCOLOR
+ Disabled background color
+ -- PP_BORDERCOLOR SYSCLR_WINDOWFRAME
+ SYSCLR_INACTIVEBORDER
+ Border color (around pushbuttons, in addition to
+ the 3D colors)
+ -- PP_ACTIVECOLOR SYSCLR_ACTIVETITLE
+ Active color
+ -- PP_INACTIVECOLOR SYSCLR_INACTIVETITLE
+ Inactive color
*
* For menus:
+ -- PP_MENUBACKGROUNDCOLOR SYSCLR_MENU
+ -- PP_MENUFOREGROUNDCOLOR SYSCLR_MENUTEXT
+ -- PP_MENUHILITEBGNDCOLOR SYSCLR_MENUHILITEBGND
+ -- PP_MENUHILITEFGNDCOLOR SYSCLR_MENUHILITE
+ -- ?? SYSCLR_MENUDISABLEDTEXT
+
* For containers (according to the API ref. at EDM/2):
+ -- PP_FOREGROUNDCOLOR SYSCLR_WINDOWTEXT
+ -- PP_BACKGROUNDCOLOR SYSCLR_WINDOW
+ -- PP_HILITEFOREGROUNDCOLOR SYSCLR_HILITEFOREGROUND
+ -- PP_HILITEBACKGROUNDCOLOR SYSCLR_HILITEBACKGROUND
+ -- PP_BORDERCOLOR
+ (used for separator lines, eg. in Details view)
+ -- PP_ICONTEXTBACKGROUNDCOLOR
+ (column titles in Details view?!?)
+
* For listboxes / entryfields / MLE's:
+ -- PP_BACKGROUNDCOLOR SYSCLR_ENTRYFIELD
*
* PMREF has more of these.
*
*@@changed V0.9.0 [umoeller]: removed INI key query, using SYSCLR_* instead; function prototype changed
*@@changed V0.9.0 [umoeller]: added fInherit parameter
*@@changed V0.9.7 (2000-12-02) [umoeller]: added lSysColor == -1 support
*@@changed V0.9.20 (2002-08-04) [umoeller]: added ulPPIndex, renamed func
*/
LONG winhQueryPresColor2(HWND hwnd, // in: window to query
ULONG ulppRGB, // in: PP_* index for RGB color
ULONG ulppIndex, // in: PP_* index for color _index_ (can be null)
BOOL fInherit, // in: search parent windows too?
LONG lSysColor) // in: SYSCLR_* index or -1
{
ULONG ul,
attrFound;
LONG lColorFound;
if (ulppRGB != (ULONG)-1)
{
ULONG fl = 0;
if (!fInherit)
fl = QPF_NOINHERIT;
if (ulppIndex)
fl |= QPF_ID2COLORINDEX; // convert indexed color 2 to RGB V0.9.20 (2002-08-04) [umoeller]
if ((ul = WinQueryPresParam(hwnd,
ulppRGB,
ulppIndex,
&attrFound,
sizeof(lColorFound),
&lColorFound,
fl)))
return lColorFound;
}
// not found: get system color
if (lSysColor != -1)
return WinQuerySysColor(HWND_DESKTOP, lSysColor, 0);
return -1;
}
/*
*@@ winhQueryPresColor:
* compatibility function because this one was
* exported.
*
*@@added V0.9.20 (2002-08-04) [umoeller]
*/
LONG XWPENTRY winhQueryPresColor(HWND hwnd,
ULONG ulPP,
BOOL fInherit,
LONG lSysColor)
{
return winhQueryPresColor2(hwnd,
ulPP,
0,
fInherit,
lSysColor);
}
/*
*@@ winhSetPresColor:
* sets a color presparam. ulIndex specifies
* the presparam to be set and would normally
* be either PP_BACKGROUNDCOLOR or PP_FOREGROUNDCOLOR.
*
*@@added V0.9.16 (2001-10-15) [umoeller]
*/
BOOL winhSetPresColor(HWND hwnd,
ULONG ulIndex,
LONG lColor)
{
return (WinSetPresParam(hwnd,
ulIndex,
sizeof(LONG),
&lColor));
}
/*
*@@category: Helpers\PM helpers\Help (IPF)
*/
/* ******************************************************************
*
* Help instance helpers
*
********************************************************************/
/*
*@@ winhCreateHelp:
* creates a help instance and connects it with the
* given frame window.
*
* If (pszFileName == NULL), we'll retrieve the
* executable's fully qualified file name and
* replace the extension with .HLP simply. This
* avoids the typical "Help not found" errors if
* the program isn't started in its own directory.
*
* If you have created a help table in memory, specify it
* with pHelpTable. To load a help table from the resources,
* specify hmod (or NULLHANDLE) and set pHelpTable to the
* following:
+
+ (PHELPTABLE)MAKELONG(usTableID, 0xffff)
*
* Returns the help window handle or NULLHANDLE on errors.
*
* Based on an EDM/2 code snippet.
*
*@@added V0.9.4 (2000-07-03) [umoeller]
*/
HWND winhCreateHelp(HWND hwndFrame, // in: app's frame window handle; can be NULLHANDLE
const char *pcszFileName, // in: help file name or NULL
HMODULE hmod, // in: module with help table or NULLHANDLE (current)
PHELPTABLE pHelpTable, // in: help table or resource ID
const char *pcszWindowTitle) // in: help window title or NULL
{
HELPINIT hi;
PSZ pszExt;
CHAR szName[CCHMAXPATH];
HWND hwndHelp;
if (pcszFileName == NULL)
{
PPIB ppib;
PTIB ptib;
DosGetInfoBlocks(&ptib, &ppib);
DosQueryModuleName(ppib->pib_hmte, sizeof(szName), szName);
pszExt = strrchr(szName, '.');
if (pszExt)
strcpy(pszExt, ".hlp");
else
strcat(szName, ".hlp");
pcszFileName = szName;
}
hi.cb = sizeof(HELPINIT);
hi.ulReturnCode = 0;
hi.pszTutorialName = NULL;
hi.phtHelpTable = pHelpTable;
hi.hmodHelpTableModule = hmod;
hi.hmodAccelActionBarModule = NULLHANDLE;
hi.idAccelTable = 0;
hi.idActionBar = 0;
hi.pszHelpWindowTitle = (PSZ)pcszWindowTitle;
hi.fShowPanelId = CMIC_HIDE_PANEL_ID;
hi.pszHelpLibraryName = (PSZ)pcszFileName;
hwndHelp = WinCreateHelpInstance(WinQueryAnchorBlock(hwndFrame),
&hi);
if ((hwndFrame) && (hwndHelp))
{
WinAssociateHelpInstance(hwndHelp, hwndFrame);
}
return (hwndHelp);
}
/*
*@@ winhDisplayHelpPanel:
* displays the specified help panel ID.
*
* If (ulHelpPanel == 0), this displays the
* standard OS/2 "Using help" panel.
*
* Returns zero on success or one of the
* help manager error codes on failure.
* See HM_ERROR for those.
*
*@@added V0.9.7 (2001-01-21) [umoeller]
*/
ULONG winhDisplayHelpPanel(HWND hwndHelpInstance, // in: from winhCreateHelp
ULONG ulHelpPanel) // in: help panel ID
{
return (ULONG)(WinSendMsg(hwndHelpInstance,
HM_DISPLAY_HELP,
(MPARAM)ulHelpPanel,
(MPARAM)( (ulHelpPanel != 0)
? HM_RESOURCEID
: 0)));
}
/*
*@@ winhDestroyHelp:
* destroys the help instance created by winhCreateHelp.
*
* Based on an EDM/2 code snippet.
*
*@@added V0.9.4 (2000-07-03) [umoeller]
*/
void winhDestroyHelp(HWND hwndHelp,
HWND hwndFrame) // can be NULLHANDLE if not used with winhCreateHelp
{
if (hwndHelp)
{
if (hwndFrame)
WinAssociateHelpInstance(NULLHANDLE, hwndFrame);
WinDestroyHelpInstance(hwndHelp);
}
}
/*
*@@category: Helpers\PM helpers\Application control
*/
/* ******************************************************************
*
* Application control
*
********************************************************************/
/*
*@@ winhAnotherInstance:
* this tests whether another instance of the same
* application is already running.
*
* To identify instances of the same application, the
* application must call this function during startup
* with the unique name of an OS/2 semaphore. As with
* all OS/2 semaphores, the semaphore name must begin
* with "\\SEM32\\". The semaphore isn't really used
* except for testing for its existence, since that
* name is unique among all processes.
*
* If another instance is found, TRUE is returned. If
* (fSwitch == TRUE), that instance is switched to,
* using the tasklist.
*
* If no other instance is found, FALSE is returned only.
*
* Based on an EDM/2 code snippet.
*
*@@added V0.9.0 (99-10-22) [umoeller]
*/
BOOL winhAnotherInstance(const char *pcszSemName, // in: semaphore ID
BOOL fSwitch) // in: if TRUE, switch to first instance if running
{
HMTX hmtx;
if (DosCreateMutexSem((PSZ)pcszSemName,
&hmtx,
DC_SEM_SHARED,
TRUE)
== NO_ERROR)
// semapore created: this doesn't happen if the semaphore
// exists already, so no other instance is running
return (FALSE);
// else: instance running
hmtx = NULLHANDLE;
// switch to other instance?
if (fSwitch)
{
// yes: query mutex creator
if (DosOpenMutexSem((PSZ)pcszSemName,
&hmtx)
== NO_ERROR)
{
PID pid = 0;
TID tid = 0; // unused
ULONG ulCount; // unused
if (DosQueryMutexSem(hmtx, &pid, &tid, &ulCount) == NO_ERROR)
{
HSWITCH hswitch = WinQuerySwitchHandle(NULLHANDLE, pid);
if (hswitch != NULLHANDLE)
WinSwitchToProgram(hswitch);
}
DosCloseMutexSem(hmtx);
}
}
return (TRUE); // another instance exists
}
/*
*@@ winhAddToTasklist:
* this adds the specified window to the tasklist
* with hIcon as its program icon (which is also
* set for the main window). This is useful for
* the old "dialog as main window" trick.
*
* Returns the HSWITCH of the added entry.
*/
HSWITCH winhAddToTasklist(HWND hwnd, // in: window to add
HPOINTER hIcon) // in: icon for main window
{
SWCNTRL swctl;
HSWITCH hswitch = 0;
swctl.hwnd = hwnd; // window handle
swctl.hwndIcon = hIcon; // icon handle
swctl.hprog = NULLHANDLE; // program handle (use default)
WinQueryWindowProcess(hwnd, &(swctl.idProcess), NULL);
// process identifier
swctl.idSession = 0; // session identifier ?
swctl.uchVisibility = SWL_VISIBLE; // visibility
swctl.fbJump = SWL_JUMPABLE; // jump indicator
// get window title from window titlebar
if (hwnd)
WinQueryWindowText(hwnd, sizeof(swctl.szSwtitle), swctl.szSwtitle);
swctl.bProgType = PROG_DEFAULT; // program type
hswitch = WinAddSwitchEntry(&swctl);
// give the main window the icon
if ((hwnd) && (hIcon))
WinSendMsg(hwnd,
WM_SETICON,
(MPARAM)hIcon,
NULL);
return (hswitch);
}
/*
*@@category: Helpers\PM helpers\Miscellaneous
*/
/* ******************************************************************
*
* Miscellaneous
*
********************************************************************/
/*
*@@ winhMyAnchorBlock:
* returns the proper anchor block (HAB)
* for the calling thread.
*
* Many Win* functions require an HAB to be
* passed in. While many of them will work
* when passing in NULLHANDLE, some (such as
* WinGetMsg) won't. If you don't know the
* anchor block of the calling thread, use
* this function.
*
* This creates a temporary object window to
* find out the anchor block. This is quite
* expensive so only use this if there's no
* other way to find out.
*
*@@added V0.9.11 (2001-04-20) [umoeller]
*/
HAB winhMyAnchorBlock(VOID)
{
HAB hab = NULLHANDLE;
HWND hwnd;
if (hwnd = winhCreateObjectWindow(WC_BUTTON, NULL))
{
hab = WinQueryAnchorBlock(hwnd);
WinDestroyWindow(hwnd);
}
return (hab);
}
/*
*@@ winhFree:
* frees a block of memory allocated by the
* winh* functions.
*
* Since the winh* functions use malloc(),
* you can also use free() directly on such
* blocks. However, you must use winhFree
* if the winh* functions are in a module
* with a different C runtime.
*
*@@added V0.9.7 (2000-12-06) [umoeller]
*/
VOID winhFree(PVOID p)
{
if (p)
free(p);
}
/*
*@@ winhSleep:
* sleeps at least the specified amount of time,
* without blocking the message queue.
*
* NOTE: This function is a bit expensive because
* it creates a temporary object window. If you
* need to sleep several times, you should rather
* use a private timer.
*
*@@added V0.9.4 (2000-07-11) [umoeller]
*@@changed V0.9.9 (2001-03-11) [umoeller]: rewritten
*/
VOID winhSleep(ULONG ulSleep) // in: sleep time in milliseconds
{
HWND hwnd;
if (hwnd = winhCreateObjectWindow(WC_STATIC, NULL))
{
QMSG qmsg;
HAB hab;
if ( (hab = WinQueryAnchorBlock(hwnd))
&& (WinStartTimer(hab,
hwnd,
1,
ulSleep))
)
{
while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
{
if ( (qmsg.hwnd == hwnd)
&& (qmsg.msg == WM_TIMER)
&& (qmsg.mp1 == (MPARAM)1) // timer ID
)
break;
WinDispatchMsg(hab, &qmsg);
}
WinStopTimer(hab,
hwnd,
1);
}
else
// timer creation failed:
DosSleep(ulSleep);
WinDestroyWindow(hwnd);
}
else
DosSleep(ulSleep);
}
/*
*@@ winhFileDlg:
* one-short function for opening an "Open" file
* dialog.
*
* On input, pszFile specifies the directory and
* file specification (e.g. "F:\*.txt").
*
* Returns TRUE if the user pressed OK. In that
* case, the fully qualified filename is written
* into pszFile again.
*
* Returns FALSE if the user pressed Cancel.
*
* Notes about flFlags:
*
* -- WINH_FOD_SAVEDLG: display a "Save As" dialog.
* Otherwise an "Open" dialog is displayed.
*
* -- WINH_FOD_INILOADDIR: load a directory from the
* specified INI key and switch the dlg to it.
* In that case, on input, pszFile must only
* contain the file filter without any path
* specification, because that is loaded from
* the INI key. If the INI key does not exist,
* the current process directory will be used.
*
* -- WINH_FOD_INISAVEDIR: if the user presses OK,
* the directory of the selected file is written
* to the specified INI key so that it can be
* reused later. This flag is independent of
* WINH_FOD_INISAVEDIR: you can specify none,
* one, or both of them.
*
*@@added V0.9.3 (2000-04-29) [umoeller]
*@@changed V0.9.12 (2001-05-21) [umoeller]: this failed if INI data had root dir, fixed
*/
BOOL winhFileDlg(HWND hwndOwner, // in: owner for file dlg
PSZ pszFile, // in: file mask; out: fully q'd filename
// (should be CCHMAXPATH in size)
ULONG flFlags, // in: any combination of the following:
// -- WINH_FOD_SAVEDLG: save dlg; else open dlg
// -- WINH_FOD_INILOADDIR: load FOD path from INI
// -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
HINI hini, // in: INI file to load/store last path from (can be HINI_USER)
const char *pcszApplication, // in: INI application to load/store last path from
const char *pcszKey) // in: INI key to load/store last path from
{
FILEDLG fd;
FILESTATUS3 fs3;
memset(&fd, 0, sizeof(FILEDLG));
fd.cbSize = sizeof(FILEDLG);
fd.fl = FDS_CENTER;
if (flFlags & WINH_FOD_SAVEDLG)
fd.fl |= FDS_SAVEAS_DIALOG;
else
fd.fl |= FDS_OPEN_DIALOG;
if ( (hini)
&& (flFlags & WINH_FOD_INILOADDIR)
&& (PrfQueryProfileString(hini,
(PSZ)pcszApplication,
(PSZ)pcszKey,
"", // default string V0.9.9 (2001-02-10) [umoeller]
fd.szFullFile,
sizeof(fd.szFullFile)-10)
> 2)
// added these checks V0.9.12 (2001-05-21) [umoeller]
&& (!DosQueryPathInfo(fd.szFullFile,
FIL_STANDARD,
&fs3,
sizeof(fs3)))
&& (fs3.attrFile & FILE_DIRECTORY)
)
{
// found: append "\*"
strcat(fd.szFullFile, "\\");
strcat(fd.szFullFile, pszFile);
}
else
// default: copy pszFile
strcpy(fd.szFullFile, pszFile);
// fixed V0.9.12 (2001-05-21) [umoeller]
if ( WinFileDlg(HWND_DESKTOP, // parent
hwndOwner, // owner
&fd)
&& (fd.lReturn == DID_OK)
)
{
// save path back?
if ( (hini)
&& (flFlags & WINH_FOD_INISAVEDIR)
)
{
// get the directory that was used
PSZ p = strrchr(fd.szFullFile, '\\');
if (p)
{
// contains directory:
// copy to OS2.INI
PSZ pszDir = strhSubstr(fd.szFullFile, p);
if (pszDir)
{
PrfWriteProfileString(hini,
(PSZ)pcszApplication,
(PSZ)pcszKey,
pszDir);
free(pszDir);
}
}
}
strcpy(pszFile, fd.szFullFile);
return (TRUE);
}
return (FALSE);
}
/*
*@@ winhSetWaitPointer:
* this sets the mouse pointer to "Wait".
* Returns the previous pointer (HPOINTER),
* which should be stored somewhere to be
* restored later. Example:
+ HPOINTER hptrOld = winhSetWaitPointer();
+ ...
+ WinSetPointer(HWND_DESKTOP, hptrOld);
*/
HPOINTER winhSetWaitPointer(VOID)
{
HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
WinSetPointer(HWND_DESKTOP,
WinQuerySysPointer(HWND_DESKTOP,
SPTR_WAIT,
FALSE)); // no copy
return (hptr);
}
/*
*@@ winhQueryWindowText:
* this returns the window text of the specified
* HWND in a newly allocated buffer.
*
* Returns NULL on error. Use free()
* to free the return value.
*/
PSZ winhQueryWindowText(HWND hwnd)
{
PSZ pszText = NULL;
ULONG cbText = WinQueryWindowTextLength(hwnd);
// additional null character
if (cbText)
{
if (pszText = (PSZ)malloc(cbText + 1))
WinQueryWindowText(hwnd,
cbText + 1,
pszText);
}
return (pszText);
}
/*
*@@ winhSetWindowText:
* like WinSetWindowText, but this one accepts
* printf-like arguments.
*
* Note that the total string is limited to
* 1000 characters.
*
*@@added V0.9.16 (2001-10-08) [umoeller]
*/
BOOL winhSetWindowText(HWND hwnd,
const char *pcszFormat,
...)
{
CHAR szBuf[1000];
va_list args;
int i;
va_start(args, pcszFormat);
i = vsprintf(szBuf, pcszFormat, args);
va_end(args);
return (WinSetWindowText(hwnd,
szBuf));
}
/*
*@@ winhReplaceWindowText:
* this is a combination of winhQueryWindowText
* and strhFindReplace to replace substrings in a window.
*
* This is useful for filling in placeholders
* a la "%1" in control windows, e.g. static
* texts.
*
* This replaces only the first occurence of
* pszSearch.
*
* Returns TRUE only if the window exists and
* the search string was replaced.
*
*@@added V0.9.0 [umoeller]
*/
BOOL winhReplaceWindowText(HWND hwnd, // in: window whose text is to be modified
const char *pcszSearch, // in: search string (e.g. "%1")
const char *pcszReplaceWith) // in: replacement string for pszSearch
{
BOOL brc = FALSE;
PSZ pszText = winhQueryWindowText(hwnd);
if (pszText)
{
ULONG ulOfs = 0;
if (strhFindReplace(&pszText, &ulOfs, pcszSearch, pcszReplaceWith) > 0)
{
WinSetWindowText(hwnd, pszText);
brc = TRUE;
}
free(pszText);
}
return brc;
}
/*
*@@ winhEnableDlgItems:
* this enables/disables a whole range of controls
* in a window by enumerating the child windows
* until usIDFirst is found. If so, that subwindow
* is enabled/disabled and all the following windows
* in the enumeration also, until usIDLast is found.
*
* Note that this affects _all_ controls following
* the usIDFirst window, no matter what ID they have
* (even if "-1"), until usIDLast is found.
*
* Returns the no. of controls which were enabled/disabled
* (null if none).
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.1 (99-12-20) [umoeller]: renamed from winhEnableDlgItems
*/
ULONG winhEnableControls(HWND hwndDlg, // in: dialog window
USHORT usIDFirst, // in: first affected control ID
USHORT usIDLast, // in: last affected control ID (inclusive)
BOOL fEnable) // in: enable or disable?
{
HENUM henum1 = NULLHANDLE;
HWND hwndThis = NULLHANDLE;
ULONG ulCount = 0;
henum1 = WinBeginEnumWindows(hwndDlg);
while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
{
USHORT usIDCheckFirst = WinQueryWindowUShort(hwndThis, QWS_ID),
usIDCheckLast;
if (usIDCheckFirst == usIDFirst)
{
WinEnableWindow(hwndThis, fEnable);
ulCount++;
while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
{
WinEnableWindow(hwndThis, fEnable);
ulCount++;
usIDCheckLast = WinQueryWindowUShort(hwndThis, QWS_ID);
if (usIDCheckLast == usIDLast)
break;
}
break; // outer loop
}
}
WinEndEnumWindows(henum1);
return (ulCount);
}
/*
*@@ winhEnableControls2:
* like winhEnableControls, but instead this
* takes an array of ULONGs as input, which
* is assumed to contain the dialog IDs of
* the controls to be enabled/disabled.
*
*@@added V0.9.19 (2002-05-28) [umoeller]
*/
ULONG winhEnableControls2(HWND hwndDlg, // in: dialog window
const ULONG *paulIDs, // in: array of dialog IDs
ULONG cIDs, // in: array item count (NOT array size)
BOOL fEnable) // in: enable or disable?
{
ULONG ul,
ulrc = 0;
for (ul = 0;
ul < cIDs;
++ul)
{
if (WinEnableControl(hwndDlg, paulIDs[ul], fEnable))
++ulrc;
}
return ulrc;
}
/*
*@@ winhCreateStdWindow:
* much like WinCreateStdWindow, but this one
* allows you to have the standard window
* positioned automatically, using a given
* SWP structure (*pswpFrame).
*
* The frame is created with the specified parent
* (usually HWND_DESKTOP), but no owner.
*
* The client window is created with the frame as
* its parent and owner and gets an ID of FID_CLIENT.
*
* Alternatively, you can set pswpFrame to NULL
* and specify FCF_SHELLPOSITION with flFrameCreateFlags.
* If you want the window to be shown, specify
* SWP_SHOW (and maybe SWP_ACTIVATE) in *pswpFrame.
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.5 (2000-08-13) [umoeller]: flStyleClient never worked, fixed
*@@changed V0.9.7 (2000-12-08) [umoeller]: fixed client calc for invisible window
*/
HWND winhCreateStdWindow(HWND hwndFrameParent, // in: normally HWND_DESKTOP
PSWP pswpFrame, // in: frame wnd pos (ptr can be NULL)
ULONG flFrameCreateFlags, // in: FCF_* flags
ULONG ulFrameStyle, // in: WS_* flags (e.g. WS_VISIBLE, WS_ANIMATE)
const char *pcszFrameTitle, // in: frame title (title bar)
ULONG ulResourcesID, // in: according to FCF_* flags
const char *pcszClassClient, // in: client class name
ULONG flStyleClient, // in: client style
ULONG ulID, // in: frame window ID
PVOID pClientCtlData, // in: pCtlData structure pointer for client
PHWND phwndClient) // out: created client wnd
{
FRAMECDATA fcdata;
HWND hwndFrame;
RECTL rclClient;
fcdata.cb = sizeof(FRAMECDATA);
fcdata.flCreateFlags = flFrameCreateFlags;
fcdata.hmodResources = (HMODULE)NULL;
fcdata.idResources = ulResourcesID;
/* Create the frame and client windows. */
hwndFrame = WinCreateWindow(hwndFrameParent,
WC_FRAME,
(PSZ)pcszFrameTitle,
ulFrameStyle,
0,0,0,0, // size and position = 0
NULLHANDLE, // no owner
HWND_TOP, // z-order
ulID, // frame window ID
&fcdata, // frame class data
NULL); // no presparams
if (hwndFrame)
{
*phwndClient = WinCreateWindow(hwndFrame, // parent
(PSZ)pcszClassClient, // class
NULL, // no title
flStyleClient, // style
0,0,0,0, // size and position = 0
hwndFrame, // owner
HWND_BOTTOM, // bottom z-order
FID_CLIENT, // frame window ID
pClientCtlData, // class data
NULL); // no presparams
if (*phwndClient)
{
if (pswpFrame)
{
// position frame
WinSetWindowPos(hwndFrame,
pswpFrame->hwndInsertBehind,
pswpFrame->x,
pswpFrame->y,
pswpFrame->cx,
pswpFrame->cy,
pswpFrame->fl);
// position client
// WinQueryWindowRect(hwndFrame, &rclClient);
// doesn't work because it might be invisible V0.9.7 (2000-12-08) [umoeller]
rclClient.xLeft = 0;
rclClient.yBottom = 0;
rclClient.xRight = pswpFrame->cx;
rclClient.yTop = pswpFrame->cy;
WinCalcFrameRect(hwndFrame,
&rclClient,
TRUE); // calc client from frame
WinSetWindowPos(*phwndClient,
HWND_TOP,
rclClient.xLeft,
rclClient.yBottom,
rclClient.xRight - rclClient.xLeft,
rclClient.yTop - rclClient.yBottom,
SWP_MOVE | SWP_SIZE | SWP_SHOW);
}
}
}
return (hwndFrame);
}
/*
*@@ winhCreateObjectWindow:
* creates an object window of the specified
* window class, which you should have registered
* before calling this. pvCreateParam will be
* given to the window on WM_CREATE.
*
* Returns the HWND of the object window or
* NULLHANDLE on errors.
*
*@@added V0.9.3 (2000-04-17) [umoeller]
*@@changed V0.9.7 (2001-01-17) [umoeller]: made this a function from a macro
*/
HWND winhCreateObjectWindow(const char *pcszWindowClass, // in: PM window class name
PVOID pvCreateParam) // in: create param
{
return (WinCreateWindow(HWND_OBJECT,
(PSZ)pcszWindowClass,
(PSZ)"",
0,
0,0,0,0,
0,
HWND_BOTTOM,
0,
pvCreateParam,
NULL));
}
/*
*@@ winhCreateControl:
* creates a control with a size and position of 0.
*
*@@added V0.9.9 (2001-03-13) [umoeller]
*/
HWND winhCreateControl(HWND hwndParentAndOwner, // in: owner and parent window
const char *pcszClass, // in: window class (e.g. WC_BUTTON)
const char *pcszText, // in: window title
ULONG ulStyle, // in: control style
ULONG ulID) // in: control ID
{
return (WinCreateWindow(hwndParentAndOwner,
(PSZ)pcszClass,
(PSZ)pcszText,
ulStyle,
0, 0, 0, 0,
hwndParentAndOwner,
HWND_TOP,
ulID,
NULL,
NULL));
}
/*
*@@ winhRepaintWindows:
* this repaints all children of hwndParent.
* If this is passed as HWND_DESKTOP, the
* whole screen is repainted.
*
*@@changed V0.9.7 (2000-12-13) [umoeller]: hwndParent was never respected, fixed
*/
VOID winhRepaintWindows(HWND hwndParent)
{
HWND hwndTop;
HENUM henum = WinBeginEnumWindows(hwndParent);
while ((hwndTop = WinGetNextWindow(henum)))
if (WinIsWindowShowing(hwndTop))
WinInvalidateRect(hwndTop, NULL, TRUE);
WinEndEnumWindows(henum);
}
/*
*@@ winhFindMsgQueue:
* returns the message queue which matches
* the given process and thread IDs. Since,
* per IBM definition, every thread may only
* have one MQ, this should be unique.
*
*@@added V0.9.2 (2000-03-08) [umoeller]
*/
HMQ winhFindMsgQueue(PID pid, // in: process ID
TID tid, // in: thread ID
HAB* phab) // out: anchor block
{
HWND hwndThis = 0,
rc = 0;
HENUM henum = WinBeginEnumWindows(HWND_OBJECT);
while ((hwndThis = WinGetNextWindow(henum)))
{
CHAR szClass[200];
if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
{
if (strcmp(szClass, "#32767") == 0)
{
// message queue window:
PID pidWin = 0;
TID tidWin = 0;
WinQueryWindowProcess(hwndThis,
&pidWin,
&tidWin);
if ( (pidWin == pid)
&& (tidWin == tid)
)
{
// get HMQ from window words
rc = WinQueryWindowULong(hwndThis, QWL_HMQ);
if (rc)
if (phab)
*phab = WinQueryAnchorBlock(hwndThis);
break;
}
}
}
}
WinEndEnumWindows(henum);
return (rc);
}
/*
*@@ winhFindHardErrorWindow:
* this searches all children of HWND_OBJECT
* for the PM hard error windows, which are
* invisible most of the time. When a hard
* error occurs, that window is made a child
* of HWND_DESKTOP instead.
*
* Stolen from ProgramCommander/2 (C) Roman Stangl.
*
*@@added V0.9.3 (2000-04-27) [umoeller]
*/
VOID winhFindPMErrorWindows(HWND *phwndHardError, // out: hard error window
HWND *phwndSysError) // out: system error window
{
PID pidObject; // HWND_OBJECT's process and thread id
TID tidObject;
PID pidObjectChild; // HWND_OBJECT's child window process and thread id
TID tidObjectChild;
HENUM henumObject; // HWND_OBJECT enumeration handle
HWND hwndObjectChild; // Window handle of current HWND_OBJECT child
UCHAR ucClassName[32]; // Window class e.g. #1 for WC_FRAME
CLASSINFO classinfoWindow; // Class info of current HWND_OBJECT child
*phwndHardError = NULLHANDLE;
*phwndSysError = NULLHANDLE;
// query HWND_OBJECT's window process
WinQueryWindowProcess(WinQueryObjectWindow(HWND_DESKTOP), &pidObject, &tidObject);
// enumerate all child windows of HWND_OBJECT
henumObject = WinBeginEnumWindows(HWND_OBJECT);
while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
{
// see if the current HWND_OBJECT child window runs in the
// process of HWND_OBJECT (PM)
WinQueryWindowProcess(hwndObjectChild, &pidObjectChild, &tidObjectChild);
if (pidObject == pidObjectChild)
{
// get the child window's data
WinQueryClassName(hwndObjectChild,
sizeof(ucClassName),
(PCH)ucClassName);
WinQueryClassInfo(WinQueryAnchorBlock(hwndObjectChild),
(PSZ)ucClassName,
&classinfoWindow);
if ( (!strcmp((PSZ)ucClassName, "#1")
|| (classinfoWindow.flClassStyle & CS_FRAME))
)
{
// if the child window is a frame window and running in
// HWND_OBJECT's (PM's) window process, it must be the
// PM Hard Error or System Error window
WinQueryClassName(WinWindowFromID(hwndObjectChild,
FID_CLIENT),
sizeof(ucClassName),
(PSZ)ucClassName);
if (!strcmp((PSZ)ucClassName, "PM Hard Error"))
{
*phwndHardError = hwndObjectChild;
if (*phwndSysError)
// we found the other one already:
// stop searching, we got both
break;
}
else
{
printf("Utility: Found System Error %08X\n", (int)hwndObjectChild);
*phwndSysError = hwndObjectChild;
if (*phwndHardError)
// we found the other one already:
// stop searching, we got both
break;
}
}
} // end if (pidObject == pidObjectChild)
} // end while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
WinEndEnumWindows(henumObject);
}
/*
*@@ winhCreateFakeDesktop:
* this routine creates and displays a frameless window over
* the whole screen in the color of PM's Desktop to fool the
* user that all windows have been closed (which in fact might
* not be the case).
*
* This window's background color is set to the Desktop's
* (PM's one, not the WPS's one).
*
* Returns the HWND of this window.
*/
HWND winhCreateFakeDesktop(HWND hwndSibling)
{
// presparam for background
typedef struct _BACKGROUND
{
ULONG cb; // length of the aparam parameter, in bytes
ULONG id; // attribute type identity
ULONG cb2; // byte count of the ab parameter
RGB rgb; // attribute value
} BACKGROUND;
BACKGROUND background;
LONG lDesktopColor;
// create fake desktop window = empty window with
// the size of full screen
lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
SYSCLR_BACKGROUND,
0);
background.cb = sizeof(background.id)
+ sizeof(background.cb)
+ sizeof(background.rgb);
background.id = PP_BACKGROUNDCOLOR;
background.cb2 = sizeof(RGB);
background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
background.rgb.bRed = (CHAR3FROMMP(lDesktopColor));
return (WinCreateWindow(HWND_DESKTOP, // parent window
WC_FRAME, // class name
"", // window text
WS_VISIBLE, // window style
0, 0, // position and size
WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN),
WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN),
NULLHANDLE, // owner window
hwndSibling, // sibling window
1, // window id
NULL, // control data
&background)); // presentation parms
}
/*
*@@ winhAssertWarp4Notebook:
* this takes hwndDlg as a notebook dialog page and
* goes thru all its controls. If a control with an
* ID <= udIdThreshold is found, this is assumed to
* be a button which is to be given the BS_NOTEBOOKBUTTON
* style. You should therefore give all your button
* controls which should be moved such an ID.
*
* Note that this function will now automatically
* find out the lowest y coordinate that was used
* for a non-notebook button and move all controls
* down accordingly. As a result, ulDownUnit must
* no longer be specified (V0.9.19).
*
* This function is useful if you wish to create
* notebook pages using dlgedit.exe which are compatible
* with both Warp 3 and Warp 4. This should be executed
* in WM_INITDLG of the notebook dlg function if the app
* has determined that it is running on Warp 4.
*
*@@changed V0.9.16 (2002-02-02) [umoeller]: fixed entry fields
*@@changed V0.9.19 (2002-04-24) [umoeller]: removed ulDownUnits
*@@changed V0.9.19 (2002-05-02) [umoeller]: fix for combobox
*/
BOOL winhAssertWarp4Notebook(HWND hwndDlg,
USHORT usIdThreshold) // in: ID threshold
{
BOOL brc = FALSE;
if (doshIsWarp4())
{
LONG yLowest = 10000;
HWND hwndItem;
HENUM henum = 0;
PSWP paswp,
pswpThis;
ULONG cWindows = 0,
ul;
BOOL fIsVisible;
if (fIsVisible = WinIsWindowVisible(hwndDlg))
// avoid flicker
WinEnableWindowUpdate(hwndDlg, FALSE);
if (paswp = (PSWP)malloc(sizeof(SWP) * 100))
{
pswpThis = paswp;
// loop 1: set notebook buttons, find lowest y used
henum = WinBeginEnumWindows(hwndDlg);
while ((hwndItem = WinGetNextWindow(henum)))
{
USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
// _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
if (usId <= usIdThreshold)
{
// pushbutton to change:
WinSetWindowBits(hwndItem,
QWL_STYLE,
BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
brc = TRUE;
}
else
{
// no pushbutton to change:
CHAR szClass[10];
WinQueryWindowPos(hwndItem, pswpThis);
// special handling for entry fields
// V0.9.16 (2002-02-02) [umoeller]
WinQueryClassName(hwndItem, sizeof(szClass), szClass);
if (!strcmp(szClass, "#6"))
{
pswpThis->x += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
pswpThis->y += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
}
// check lowest y
if ( (pswpThis->y < yLowest)
// ignore combobox, this will distort everything
// AGAIN ... sigh V0.9.19 (2002-05-02) [umoeller]
&& (strcmp(szClass, "#2"))
)
yLowest = pswpThis->y ;
++pswpThis;
if (++cWindows == 100)
break;
}
} // end while ((hwndItem = WinGetNextWindow(henum)))
WinEndEnumWindows(henum);
// now adjust window positions
pswpThis = paswp;
for (ul = 0;
ul < cWindows;
++ul, ++pswpThis)
{
pswpThis->y -= (yLowest - 8);
// 8 is magic to match the lower border of the
// standard WPS notebook pages V0.9.19 (2002-04-24) [umoeller]
pswpThis->fl = SWP_MOVE;
}
WinSetMultWindowPos(WinQueryAnchorBlock(hwndDlg),
paswp,
cWindows);
free(paswp);
}
if (fIsVisible)
WinShowWindow(hwndDlg, TRUE);
}
return brc;
}
/*
*@@ winhDrawFormattedText:
* this func takes a rectangle and draws pszText into
* it, breaking the words as neccessary. The line spacing
* is determined from the font currently selected in hps.
*
* As opposed to WinDrawText, this can draw several lines
* at once, and format the _complete_ text according to the
* flCmd parameter, which is like with WinDrawText.
*
* After this function returns, *prcl is modified like this:
*
* -- yTop and yBottom contain the upper and lower boundaries
* which were needed to draw the text. This depends on
* whether DT_TOP etc. were specified.
* To get the height of the rectangle used, calculate the
* delta between yTop and yBottom.
*
* -- xLeft and xRight are modified to contain the outmost
* left and right coordinates which were needed to draw
* the text. This will be set to the longest line which
* was encountered.
*
* You can specify DT_QUERYEXTENT with flDraw to only have
* these text boundaries calculated without actually drawing.
*
* This returns the number of lines drawn.
*
* Note that this calls WinDrawText with DT_TEXTATTRS set,
* that is, the current text primitive attributes will be
* used (fonts and colors).
*
*@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
*/
ULONG winhDrawFormattedText(HPS hps, // in: presentation space; its settings
// are used, but not altered
PRECTL prcl, // in/out: rectangle to use for drawing
// (modified)
const char *pcszText, // in: text to draw (zero-terminated)
ULONG flCmd) // in: flags like in WinDrawText; I have
// only tested DT_TOP and DT_LEFT though.
// DT_WORDBREAK | DT_TEXTATTRS are always
// set.
// You can specify DT_QUERYEXTENT to only
// have prcl calculated without drawing.
{
PSZ p = (PSZ)pcszText;
LONG lDrawn = 1,
lTotalDrawn = 0,
lLineCount = 0,
lOrigYTop = prcl->yTop;
ULONG ulTextLen = strlen(pcszText),
ulCharHeight,
flCmd2,
xLeftmost = prcl->xRight,
xRightmost = prcl->xLeft;
RECTL rcl2;
flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
ulCharHeight = gpihQueryLineSpacing(hps);
while ( (lDrawn)
&& (lTotalDrawn < ulTextLen)
)
{
memcpy(&rcl2, prcl, sizeof(rcl2));
lDrawn = WinDrawText(hps,
ulTextLen-lTotalDrawn,
p,
&rcl2,
0, 0, // colors
flCmd2);
// update char counters
p += lDrawn;
lTotalDrawn += lDrawn;
// update x extents
if (rcl2.xLeft < xLeftmost)
xLeftmost = rcl2.xLeft;
if (rcl2.xRight > xRightmost)
xRightmost = rcl2.xRight;
// update y for next line
prcl->yTop -= ulCharHeight;
// increase line count
lLineCount++;
}
prcl->xLeft = xLeftmost;
prcl->xRight = xRightmost;
prcl->yBottom = prcl->yTop;
prcl->yTop = lOrigYTop;
return (lLineCount);
}
/*
*@@ winhQuerySwitchList:
* returns the switch list in a newly
* allocated buffer. This does the
* regular double WinQuerySwitchList
* call to first get the no. of items
* and then get the items.
*
* The no. of items can be found in
* the returned SWBLOCK.cwsentry.
*
* Returns NULL on errors. Use
* free() to free the return value.
*
*@@added V0.9.7 (2000-12-06) [umoeller]
*/
PSWBLOCK winhQuerySwitchList(HAB hab)
{
ULONG cItems = WinQuerySwitchList(hab, NULL, 0);
ULONG ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
PSWBLOCK pSwBlock = (PSWBLOCK)malloc(ulBufSize);
if (pSwBlock)
{
cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize);
if (!cItems)
{
free(pSwBlock);
pSwBlock = NULL;
}
}
return (pSwBlock);
}
typedef HSWITCH APIENTRY WINHSWITCHFROMHAPP(HAPP happ);
/*
*@@ winhHSWITCHfromHAPP:
* resolves and calls the undocumented
* WinHSWITCHfromHAPP API.
*
*@@added V0.9.19 (2002-04-17) [umoeller]
*/
HSWITCH winhHSWITCHfromHAPP(HAPP happ)
{
static WINHSWITCHFROMHAPP *pWinHSWITCHfromHAPP = NULL;
if (!pWinHSWITCHfromHAPP)
// first call: import WinHSWITCHfromHAPP
// WinHSWITCHfromHAPP PMMERGE.5199
doshQueryProcAddr("PMMERGE",
5199,
(PFN*)&pWinHSWITCHfromHAPP);
if (pWinHSWITCHfromHAPP)
return pWinHSWITCHfromHAPP(happ);
return NULLHANDLE;
}
/*
*@@ winhQueryTasklistWindow:
* returns the window handle of the PM task list.
*
*@@added V0.9.7 (2000-12-07) [umoeller]
*/
HWND winhQueryTasklistWindow(VOID)
{
SWBLOCK swblock;
// HWND hwndTasklist = winhQueryTasklistWindow();
// the tasklist has entry #0 in the SWBLOCK
WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
return (swblock.aswentry[0].swctl.hwnd);
}
/*
*@@ winhKillTasklist:
* this will destroy the Tasklist (window list) window.
* Note: you will only be able to get it back after a
* reboot, not a WPS restart. Only for use at shutdown and such.
* This trick by Uri J. Stern at
* http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
*/
VOID winhKillTasklist(VOID)
{
HWND hwndTasklist = winhQueryTasklistWindow();
WinPostMsg(hwndTasklist,
0x0454, // undocumented msg for killing tasklist
NULL, NULL);
}
// the following must be added for EMX (99-10-22) [umoeller]
#ifndef NERR_BufTooSmall
#define NERR_BASE 2100
#define NERR_BufTooSmall (NERR_BASE+23)
// the API return buffer is too small
#endif
/*
*@@ winhQueryPendingSpoolJobs:
* returns the number of pending print jobs in the spooler
* or 0 if none. Useful for testing before shutdown.
*/
ULONG winhQueryPendingSpoolJobs(VOID)
{
// BOOL rcPending = FALSE;
ULONG ulTotalJobCount = 0;
SPLERR splerr;
USHORT jobCount;
ULONG cbBuf;
ULONG cTotal;
ULONG cReturned;
ULONG cbNeeded;
ULONG ulLevel;
ULONG i,j;
PSZ pszComputerName;
PBYTE pBuf = NULL;
PPRQINFO3 prq;
PPRJINFO2 prj2;
ulLevel = 4L;
pszComputerName = (PSZ)NULL;
splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
&cReturned, &cTotal,
&cbNeeded, NULL);
if ( (splerr == ERROR_MORE_DATA)
|| (splerr == NERR_BufTooSmall)
)
{
if (!DosAllocMem((PPVOID)&pBuf,
cbNeeded,
PAG_READ | PAG_WRITE | PAG_COMMIT))
{
cbBuf = cbNeeded;
splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
&cReturned, &cTotal,
&cbNeeded, NULL);
if (splerr == NO_ERROR)
{
// set pointer to point to the beginning of the buffer
prq = (PPRQINFO3)pBuf;
// cReturned has the count of the number of PRQINFO3 structures
for (i = 0;
i < cReturned;
i++)
{
// save the count of jobs; there are this many PRJINFO2
// structures following the PRQINFO3 structure
jobCount = prq->cJobs;
// _Pmpf(( "Job count in this queue is %d",jobCount ));
// increment the pointer past the PRQINFO3 structure
prq++;
// set a pointer to point to the first PRJINFO2 structure
prj2=(PPRJINFO2)prq;
for (j = 0;
j < jobCount;
j++)
{
// increment the pointer to point to the next structure
prj2++;
// increase the job count, which we'll return
ulTotalJobCount++;
} // endfor jobCount
// after doing all the job structures, prj2 points to the next
// queue structure; set the pointer for a PRQINFO3 structure
prq = (PPRQINFO3)prj2;
} //endfor cReturned
} // endif NO_ERROR
DosFreeMem(pBuf);
}
} // end if Q level given
return (ulTotalJobCount);
}
/*
*@@ winhSetNumLock:
* this sets the NumLock key on or off, depending
* on fState.
*
* Based on code from WarpEnhancer, (C) Achim Hasenmüller.
*
*@@added V0.9.1 (99-12-18) [umoeller]
*/
VOID winhSetNumLock(BOOL fState)
{
// BOOL fRestoreKBD = FALSE; // Assume we're not going to close Kbd
BYTE KeyStateTable[256];
ULONG ulActionTaken; // Used by DosOpen
HFILE hKbd;
// read keyboard state table
if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
FALSE))
{
// first set the PM state
if (fState)
KeyStateTable[VK_NUMLOCK] |= 0x01; // Turn numlock on
else
KeyStateTable[VK_NUMLOCK] &= 0xFE; // Turn numlock off
// set keyboard state table with new state values
WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
}
// now set the OS/2 keyboard state
// try to open OS/2 keyboard driver
if (!DosOpen("KBD$",
&hKbd, &ulActionTaken,
0, // cbFile
FILE_NORMAL,
OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
NULL))
{
SHIFTSTATE ShiftState;
ULONG DataLen = sizeof(SHIFTSTATE);
memset(&ShiftState, '\0', DataLen);
DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
NULL, 0L, NULL,
&ShiftState, DataLen, &DataLen);
if (fState)
ShiftState.fsState |= 0x0020; // turn NumLock on
else
ShiftState.fsState &= 0xFFDF; // turn NumLock off
DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
&ShiftState, DataLen, &DataLen,
NULL, 0L, NULL);
// now close OS/2 keyboard driver
DosClose(hKbd);
}
return;
}
/*
*@@category: Helpers\PM helpers\Extended frame windows
*/
/* ******************************************************************
*
* Extended frame
*
********************************************************************/
/*
*@@ fnwpSubclExtFrame:
* subclassed frame window proc.
*
*@@added V0.9.16 (2001-09-29) [umoeller]
*/
MRESULT EXPENTRY fnwpSubclExtFrame(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
PEXTFRAMEDATA pData = (PEXTFRAMEDATA)WinQueryWindowPtr(hwndFrame, QWL_USER);
switch (msg)
{
case WM_QUERYFRAMECTLCOUNT:
{
// query the standard frame controls count
ULONG ulrc = (ULONG)pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
// if we have a status bar, increment the count
ulrc++;
mrc = (MPARAM)ulrc;
}
break;
case WM_FORMATFRAME:
{
// query the number of standard frame controls
ULONG ulCount = (ULONG)pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
// we have a status bar:
// format the frame
ULONG ul;
PSWP swpArr = (PSWP)mp1;
for (ul = 0; ul < ulCount; ul++)
{
if (WinQueryWindowUShort( swpArr[ul].hwnd, QWS_ID ) == 0x8008 )
// FID_CLIENT
{
POINTL ptlBorderSizes;
ULONG ulStatusBarHeight = 20;
WinSendMsg(hwndFrame,
WM_QUERYBORDERSIZE,
(MPARAM)&ptlBorderSizes,
0);
// first initialize the _new_ SWP for the status bar.
// Since the SWP array for the std frame controls is
// zero-based, and the standard frame controls occupy
// indices 0 thru ulCount-1 (where ulCount is the total
// count), we use ulCount for our static text control.
swpArr[ulCount].fl = SWP_MOVE | SWP_SIZE | SWP_NOADJUST | SWP_ZORDER;
swpArr[ulCount].x = ptlBorderSizes.x;
swpArr[ulCount].y = ptlBorderSizes.y;
swpArr[ulCount].cx = swpArr[ul].cx; // same as cnr's width
swpArr[ulCount].cy = ulStatusBarHeight;
swpArr[ulCount].hwndInsertBehind = HWND_BOTTOM; // HWND_TOP;
swpArr[ulCount].hwnd = WinWindowFromID(hwndFrame, FID_STATUSBAR);
// adjust the origin and height of the container to
// accomodate our static text control
swpArr[ul].y += swpArr[ulCount].cy;
swpArr[ul].cy -= swpArr[ulCount].cy;
}
}
// increment the number of frame controls
// to include our status bar
mrc = (MRESULT)(ulCount + 1);
}
break;
case WM_CALCFRAMERECT:
{
mrc = pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
// we have a status bar: calculate its rectangle
// CalcFrameRect(mp1, mp2);
}
break;
case WM_DESTROY:
WinSubclassWindow(hwndFrame, pData->pfnwpOrig);
free(pData);
WinSetWindowPtr(hwndFrame, QWL_USER, NULL);
break;
default:
mrc = pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
}
return mrc;
}
/*
*@@ winhCreateStatusBar:
* creates a status bar for a frame window.
*
* Normally there's no need to call this manually,
* this gets called by winhCreateExtStdWindow
* automatically.
*
*@@added V0.9.16 (2001-09-29) [umoeller]
*/
HWND winhCreateStatusBar(HWND hwndFrame,
HWND hwndOwner,
const char *pcszText, // in: initial status bar text
const char *pcszFont, // in: font to use for status bar
LONG lColor) // in: foreground color for status bar
{
// create status bar
HWND hwndReturn = NULLHANDLE;
PPRESPARAMS ppp = NULL;
winhStorePresParam(&ppp, PP_FONTNAMESIZE, strlen(pcszFont)+1, (PVOID)pcszFont);
lColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0);
winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
lColor = CLR_BLACK;
winhStorePresParam(&ppp, PP_FOREGROUNDCOLOR, sizeof(lColor), &lColor);
hwndReturn = WinCreateWindow(hwndFrame,
WC_STATIC,
(PSZ)pcszText,
SS_TEXT | DT_VCENTER | WS_VISIBLE,
0, 0, 0, 0,
hwndOwner,
HWND_TOP,
FID_STATUSBAR,
NULL,
ppp);
free(ppp);
return (hwndReturn);
}
/*
*@@ winhCreateExtStdWindow:
* creates an extended frame window.
*
* pData must point to an EXTFRAMECDATA structure
* which contains a copy of the parameters to be
* passed to winhCreateStdWindow. In addition,
* this contains the flExtFlags field, which allows
* you to automatically create a status bar for
* the window.
*
* Note that we subclass the frame here and require
* QWL_USER for that. The frame's QWL_USER points
* to an EXTFRAMEDATA structure whose pUser parameter
* you may use for additional data, if you want to
* do further subclassing.
*
*@@added V0.9.16 (2001-09-29) [umoeller]
*/
HWND winhCreateExtStdWindow(PEXTFRAMECDATA pData, // in: extended frame data
PHWND phwndClient) // out: created client wnd
{
HWND hwndFrame;
if (hwndFrame = winhCreateStdWindow(HWND_DESKTOP,
pData->pswpFrame,
pData->flFrameCreateFlags,
pData->ulFrameStyle,
pData->pcszFrameTitle,
pData->ulResourcesID,
pData->pcszClassClient,
pData->flStyleClient,
pData->ulID,
pData->pClientCtlData,
phwndClient))
{
if (pData->flExtFlags & XFCF_STATUSBAR)
{
// create status bar as child of the frame
HWND hwndStatusBar = winhCreateStatusBar(hwndFrame,
hwndFrame,
"",
"9.WarpSans",
CLR_BLACK);
// subclass frame for supporting status bar and msgs
PEXTFRAMEDATA pFrameData;
if (pFrameData = NEW(EXTFRAMEDATA))
{
ZERO(pFrameData),
memcpy(&pFrameData->CData, pData, sizeof(pFrameData->CData));
if (pFrameData->pfnwpOrig = WinSubclassWindow(hwndFrame,
fnwpSubclExtFrame))
{
WinSetWindowPtr(hwndFrame, QWL_USER, pFrameData);
}
else
free(pFrameData);
}
}
}
return (hwndFrame);
}
/*
*@@category: Helpers\PM helpers\Workplace Shell\WPS class list
*/
/* ******************************************************************
*
* WPS Class List helpers
*
********************************************************************/
/*
*@@ winhQueryWPSClassList:
* this returns the WPS class list in a newly
* allocated buffer. This is just a shortcut to
* the usual double WinEnumObjectClasses call.
*
* The return value is actually of the POBJCLASS type,
* so you better cast this manually. We declare this
* this as PBYTE though because POBJCLASS requires
* INCL_WINWORKPLACE.
* See WinEnumObjectClasses() for details.
*
* Returns NULL on error. Use free()
* to free the return value.
*
*@@added V0.9.0 [umoeller]
*/
PBYTE winhQueryWPSClassList(VOID)
{
ULONG ulSize;
POBJCLASS pObjClass = 0;
// get WPS class list size
if (WinEnumObjectClasses(NULL, &ulSize))
{
// allocate buffer
pObjClass = (POBJCLASS)malloc(ulSize+1);
// and load the classes into it
WinEnumObjectClasses(pObjClass, &ulSize);
}
return ((PBYTE)pObjClass);
}
/*
*@@ winhQueryWPSClass:
* this returns the POBJCLASS item if pszClass is registered
* with the WPS or NULL if the class could not be found.
*
* The return value is actually of the POBJCLASS type,
* so you better cast this manually. We declare this
* this as PBYTE though because POBJCLASS requires
* INCL_WINWORKPLACE.
*
* This takes as input the return value of winhQueryWPSClassList,
* which you must call first.
*
* <B>Usage:</B>
+ PBYTE pClassList = winhQueryWPSClassList(),
+ pWPFolder;
+ if (pClassList)
+ {
+ if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
+ ...
+ free(pClassList);
+ }
*
*@@added V0.9.0 [umoeller]
*/
PBYTE winhQueryWPSClass(PBYTE pObjClass, // in: buffer returned by
// winhQueryWPSClassList
const char *pszClass) // in: class name to query
{
PBYTE pbReturn = 0;
POBJCLASS pocThis = (POBJCLASS)pObjClass;
// now go thru the WPS class list
while (pocThis)
{
if (strcmp(pocThis->pszClassName, pszClass) == 0)
{
pbReturn = (PBYTE)pocThis;
break;
}
// next class
pocThis = pocThis->pNext;
} // end while (pocThis)
return (pbReturn);
}
/*
*@@ winhRegisterClass:
* this works just like WinRegisterObjectClass,
* except that it returns a more meaningful
* error code than just FALSE in case registering
* fails.
*
* This returns NO_ERROR if the class was successfully
* registered (WinRegisterObjectClass returned TRUE).
*
* Otherwise, we do a DosLoadModule if maybe the DLL
* couldn't be loaded in the first place. If DosLoadModule
* did not return NO_ERROR, this function returns that
* return code, which can be:
*
* -- 2 ERROR_FILE_NOT_FOUND: pcszModule does not exist
* -- 2 ERROR_FILE_NOT_FOUND
* -- 3 ERROR_PATH_NOT_FOUND
* -- 4 ERROR_TOO_MANY_OPEN_FILES
* -- 5 ERROR_ACCESS_DENIED
* -- 8 ERROR_NOT_ENOUGH_MEMORY
* -- 11 ERROR_BAD_FORMAT
* -- 26 ERROR_NOT_DOS_DISK (unknown media type)
* -- 32 ERROR_SHARING_VIOLATION
* -- 33 ERROR_LOCK_VIOLATION
* -- 36 ERROR_SHARING_BUFFER_EXCEEDED
* -- 95 ERROR_INTERRUPT (interrupted system call)
* -- 108 ERROR_DRIVE_LOCKED (by another process)
* -- 123 ERROR_INVALID_NAME (illegal character or FS name not valid)
* -- 127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
* -- 180 ERROR_INVALID_SEGMENT_NUMBER
* -- 182 ERROR_INVALID_ORDINAL
* -- 190 ERROR_INVALID_MODULETYPE (probably an application)
* -- 191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
* -- 192 ERROR_EXE_MARKED_INVALID (by linker)
* -- 194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
* -- 195 ERROR_INVALID_MINALLOCSIZE
* -- 196 ERROR_DYNLINK_FROM_INVALID_RING
* -- 198 ERROR_INVALID_SEGDPL
* -- 199 ERROR_AUTODATASEG_EXCEEDS_64K
* -- 201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
* -- 206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
* -- 295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
*
* In all these cases, pszBuf may contain a meaningful
* error message from DosLoadModule, especially if an import
* could not be resolved.
*
* Still worse, if DosLoadModule returned NO_ERROR, we
* probably have some SOM internal error. A probable
* reason is that the parent class of pcszClassName
* is not installed, but that's WPS/SOM internal
* and cannot be queried from outside the WPS context.
*
* In that case, ERROR_OPEN_FAILED (110) is returned.
* That one sounded good to me. ;-)
*/
APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
const char* pcszModule, // in: e.g. "C:\XFOLDER\XFLDR.DLL"
PSZ pszBuf, // out: error message from DosLoadModule
ULONG cbBuf) // in: sizeof(*pszBuf), passed to DosLoadModule
{
APIRET arc = NO_ERROR;
if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
{
// failed: do more error checking then, try DosLoadModule
HMODULE hmod = NULLHANDLE;
arc = DosLoadModule(pszBuf, cbBuf,
(PSZ)pcszModule,
&hmod);
if (arc == NO_ERROR)
{
// DosLoadModule succeeded:
// some SOM error then
DosFreeModule(hmod);
arc = ERROR_OPEN_FAILED;
}
}
// else: ulrc still 0 (== no error)
return arc;
}
/*
*@@ winhIsClassRegistered:
* quick one-shot function which checks if
* a class is currently registered. Calls
* winhQueryWPSClassList and winhQueryWPSClass
* in turn.
*
*@@added V0.9.2 (2000-02-26) [umoeller]
*/
BOOL winhIsClassRegistered(const char *pcszClass)
{
BOOL brc = FALSE;
PBYTE pClassList = winhQueryWPSClassList();
if (pClassList)
{
if (winhQueryWPSClass(pClassList, pcszClass))
brc = TRUE;
free(pClassList);
}
return brc;
}
/*
*@@category: Helpers\PM helpers\Workplace Shell
*/
/*
*@@ winhResetWPS:
* restarts the WPS using PrfReset. Returns
* one of the following:
*
* -- 0: no error.
* -- 1: PrfReset failed.
* -- 2 or 4: PrfQueryProfile failed.
* -- 3: malloc() failed.
*
*@@added V0.9.4 (2000-07-01) [umoeller]
*/
ULONG winhResetWPS(HAB hab)
{
ULONG ulrc = 0;
// find out current profile names
PRFPROFILE Profiles;
Profiles.cchUserName = Profiles.cchSysName = 0;
// first query their file name lengths
if (PrfQueryProfile(hab, &Profiles))
{
// allocate memory for filenames
Profiles.pszUserName = (PSZ)malloc(Profiles.cchUserName);
Profiles.pszSysName = (PSZ)malloc(Profiles.cchSysName);
if (Profiles.pszSysName)
{
// get filenames
if (PrfQueryProfile(hab, &Profiles))
{
// "change" INIs to these filenames:
// THIS WILL RESET THE WPS
if (PrfReset(hab, &Profiles) == FALSE)
ulrc = 1;
free(Profiles.pszSysName);
free(Profiles.pszUserName);
}
else
ulrc = 2;
}
else
ulrc = 3;
}
else
ulrc = 4;
return (ulrc);
}