home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
xwphescr.zip
/
XWPH0208.ZIP
/
src
/
helpers
/
cctl_combo.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-06-02
|
24KB
|
725 lines
/*
*@@sourcefile cctl_combo.c:
* super combo box control, which is, essentially, a
* subclassed entry field with a list box attached to
* it.
*
* Compared to the standard PM combo box, this one has a
* number of advantages:
*
* -- It doesn't require the parent window to have the
* WS_CLIPCHILDREN style bit cleared.
*
* -- It has proper window positioning; the size of the
* enhanced combo is the size of the entry field, while
* the PM combo wants to have the size of the expanded
* list box too, which isn't easy to handle.
*
* See ctlComboFromEntryField for details.
*
* Note: Version numbering in this file relates to XWorkplace version
* numbering.
*
*@@header "helpers\comctl.h"
*@@added V0.9.16 (2002-01-01) [umoeller]
*/
/*
* Copyright (C) 2001-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_DOSEXCEPTIONS
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSERRORS
#define INCL_WINWINDOWMGR
#define INCL_WINFRAMEMGR
#define INCL_WININPUT
#define INCL_WINPOINTERS
#define INCL_WINBUTTONS
#define INCL_WINENTRYFIELDS
#define INCL_WINLISTBOXES
#define INCL_GPILOGCOLORTABLE
#define INCL_GPIBITMAPS
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <setjmp.h> // needed for except.h
#include <assert.h> // needed for except.h
#include "setup.h" // code generation and debugging options
#include "helpers\except.h" // exception handling
#include "helpers\winh.h"
#include "helpers\comctl.h"
#pragma hdrstop
/*
*@@category: Helpers\PM helpers\Window classes\Super combo box
* See cctl_combo.c.
*/
/* ******************************************************************
*
* Super Combination Box control
*
********************************************************************/
#define COMBO_BUTTON_WIDTH 20
#define ID_COMBO_BUTTON 1001
#define ID_COMBO_LISTBOX 1002
/*
*@@ COMBODATA:
*
*@@added V0.9.9 (2001-03-17) [umoeller]
*/
typedef struct _COMBODATA
{
PFNWP pfnwpOrigEntryField,
pfnwpOrigButton;
ULONG flStyle;
// position of entire combo
LONG x,
y,
cx,
cy;
HWND hwndButton,
hwndListbox;
HBITMAP hbmButton;
SIZEL szlButton; // bitmap dimensions
} COMBODATA, *PCOMBODATA;
/*
*@@ PaintButtonBitmap:
*
*@@added V0.9.9 (2001-03-17) [umoeller]
*/
static VOID PaintButtonBitmap(HWND hwnd,
PCOMBODATA pcd)
{
HPS hps;
RECTL rcl;
POINTL ptlDest;
hps = WinGetPS(hwnd);
WinQueryWindowRect(hwnd, &rcl);
ptlDest.x = (rcl.xRight - pcd->szlButton.cx) / 2;
ptlDest.y = (rcl.yTop - pcd->szlButton.cy) / 2;
WinDrawBitmap(hps,
pcd->hbmButton,
NULL,
&ptlDest,
0, 0,
DBM_NORMAL);
WinReleasePS(hps);
}
/*
*@@ fnwpSubclassedComboButton:
* window proc the combobox's button is subclassed with.
* This is only for WM_PAINT because BN_PAINT is really
* not that great for painting a button that looks like
* a standard button.
*
*@@added V0.9.9 (2001-03-17) [umoeller]
*/
static MRESULT EXPENTRY fnwpSubclassedComboButton(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
PCOMBODATA pcd;
switch (msg)
{
case WM_PAINT:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
mrc = pcd->pfnwpOrigButton(hwnd, msg, mp1, mp2);
PaintButtonBitmap(hwnd, pcd);
}
break;
/*
* default:
*
*/
default:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
mrc = pcd->pfnwpOrigButton(hwnd, msg, mp1, mp2);
break;
}
return mrc;
}
/*
*@@ ShowListbox:
*
*@@added V0.9.9 (2001-03-17) [umoeller]
*/
static VOID ShowListbox(HWND hwnd, // in: subclassed entry field
PCOMBODATA pcd,
BOOL fShow) // in: TRUE == show, FALSE == hide
{
BOOL fHilite = FALSE;
if (fShow)
{
// list box is invisible:
SWP swp;
POINTL ptl;
WinQueryWindowPos(hwnd, &swp);
_Pmpf(("showing lb"));
// convert to desktop
ptl.x = swp.x;
ptl.y = swp.y;
WinMapWindowPoints(WinQueryWindow(hwnd, QW_PARENT), // from
HWND_DESKTOP, // to
&ptl,
// SWP.y comes before SWP.x
1);
WinSetWindowPos(pcd->hwndListbox,
HWND_TOP,
ptl.x + COMBO_BUTTON_WIDTH,
ptl.y - 100,
swp.cx,
100,
SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_NOREDRAW);
WinSetParent(pcd->hwndListbox,
HWND_DESKTOP,
TRUE); // redraw
// set focus to subclassed entry field in any case;
// we never let the listbox get the focus
WinSetFocus(HWND_DESKTOP, hwnd);
fHilite = TRUE;
}
else
{
// list box is showing:
HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
_Pmpf(("hiding listbox"));
WinSetParent(pcd->hwndListbox,
HWND_OBJECT,
TRUE); // redraw now
// give focus back to entry field
if (hwndFocus == pcd->hwndListbox)
WinSetFocus(HWND_DESKTOP, hwnd);
}
WinSendMsg(pcd->hwndButton,
BM_SETHILITE,
(MPARAM)fHilite,
0);
PaintButtonBitmap(pcd->hwndButton, pcd);
}
/*
*@@ fnwpComboSubclass:
*
*@@added V0.9.9 (2001-03-17) [umoeller]
*/
static MRESULT EXPENTRY fnwpComboSubclass(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
PCOMBODATA pcd;
switch (msg)
{
/*
* WM_ADJUSTWINDOWPOS:
*
*/
case WM_ADJUSTWINDOWPOS:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
PSWP pswp = (PSWP)mp1;
if (pswp->fl & SWP_SIZE)
// if we're being sized, make us smaller so that
// there's room for the button
pswp->cx -= COMBO_BUTTON_WIDTH;
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
}
break;
/*
* WM_WINDOWPOSCHANGED:
*
*/
case WM_WINDOWPOSCHANGED:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
PSWP pswpNew = (PSWP)mp1;
if (pswpNew->fl & (SWP_SIZE | SWP_MOVE))
{
// moved or sized:
SWP swp;
WinQueryWindowPos(hwnd, &swp);
WinSetWindowPos(pcd->hwndButton,
0,
pswpNew->x + pswpNew->cx, // has already been truncated!
pswpNew->y,
COMBO_BUTTON_WIDTH,
pswpNew->cy,
SWP_MOVE | SWP_SIZE);
}
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
}
break;
/*
* WM_SETFOCUS:
* hide listbox if focus is going away from us
*/
case WM_SETFOCUS:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
if (!mp2)
// we're losing focus:
// is listbox currently showing?
ShowListbox(hwnd,
pcd,
FALSE);
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
}
break;
/*
* WM_COMMAND:
* show/hide listbox if the button gets pressed.
*/
case WM_COMMAND:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
if (SHORT1FROMMP(mp1) == ID_COMBO_BUTTON)
{
// button clicked:
ShowListbox(hwnd,
pcd,
// check state of list box
(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
== WinQueryObjectWindow(HWND_DESKTOP)));
// do not call parent
break;
} // end if ((SHORT)mp1 == ID_COMBO_BUTTON)
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
}
break;
/*
* WM_CONTROL:
* handle notifications from listbox.
*/
case WM_CONTROL:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
USHORT usid = SHORT1FROMMP(mp1),
uscode = SHORT2FROMMP(mp1);
if (usid == ID_COMBO_LISTBOX)
{
switch (uscode)
{
case LN_ENTER:
break;
case LN_SELECT:
{
SHORT sSelected = winhQueryLboxSelectedItem(pcd->hwndListbox,
LIT_FIRST);
PSZ psz = NULL;
if (sSelected != LIT_NONE)
{
psz = winhQueryLboxItemText(pcd->hwndListbox,
sSelected);
}
WinSetWindowText(hwnd, psz);
if (psz)
{
WinPostMsg(hwnd,
EM_SETSEL,
MPFROM2SHORT(0, strlen(psz)),
0);
free(psz);
}
break; }
case LN_SETFOCUS:
// when the list box gets the focus, always
// set focus to ourselves
WinSetFocus(HWND_DESKTOP, hwnd);
break;
}
// forward list box notifications to
// our own owner, but replace the id
// with the combo box id
WinPostMsg(WinQueryWindow(hwnd, QW_OWNER),
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwnd, QWS_ID),
uscode),
mp2);
// do not call parent
break;
} // end if (usid == ID_COMBO_LISTBOX)
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
}
break;
/*
* WM_CHAR:
*
*/
case WM_CHAR:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
USHORT usFlags = SHORT1FROMMP(mp1);
// USHORT usch = SHORT1FROMMP(mp2);
USHORT usvk = SHORT2FROMMP(mp2);
if ((usFlags & KC_KEYUP) == 0)
{
if (usFlags & KC_VIRTUALKEY)
{
switch (usvk)
{
case VK_DOWN:
case VK_UP:
// if alt is pressed with these, show/hide listbox
if (usFlags & KC_ALT)
WinPostMsg(hwnd,
CBM_SHOWLIST,
(MPARAM)(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
== WinQueryObjectWindow(HWND_DESKTOP)),
0);
else
{
// just up or down, no alt:
// select next or previous item in list box
SHORT sSelected = winhQueryLboxSelectedItem(pcd->hwndListbox,
LIT_FIRST),
sNew = 0;
if (usvk == VK_DOWN)
{
if (sSelected != LIT_NONE)
{
if (sSelected < WinQueryLboxCount(pcd->hwndListbox))
sNew = sSelected + 1;
}
// else: sNew still 0
}
else
{
// up:
if ( (sSelected != LIT_NONE)
&& (sSelected > 0)
)
sNew = sSelected - 1;
}
winhSetLboxSelectedItem(pcd->hwndListbox,
sNew,
TRUE);
}
break;
}
}
}
// call parent only if this is not a drop-down list
if ((pcd->flStyle & CBS_DROPDOWNLIST) == 0)
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
else
// forward to owner
WinSendMsg(WinQueryWindow(hwnd, QW_OWNER),
msg,
mp1,
mp2);
}
break;
/*
* CBM_ISLISTSHOWING:
* implementation of the original combobox msg.
*/
case CBM_ISLISTSHOWING:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
mrc = (MPARAM)(WinQueryWindow(pcd->hwndListbox, QW_PARENT)
== WinQueryObjectWindow(HWND_DESKTOP));
}
break;
/*
* CBM_SHOWLIST:
* implementation of the original combobox msg.
*/
case CBM_SHOWLIST:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
ShowListbox(hwnd,
pcd,
(BOOL)mp1);
}
break;
/*
* list box messages:
* forward all these to the listbox and
* return the listbox return value.
*/
case LM_INSERTITEM:
case LM_SETTOPINDEX:
case LM_QUERYTOPINDEX:
case LM_DELETEITEM:
case LM_SELECTITEM:
case LM_QUERYSELECTION:
case LM_SETITEMTEXT:
case LM_QUERYITEMTEXT:
case LM_SEARCHSTRING:
case LM_DELETEALL:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
mrc = WinSendMsg(pcd->hwndListbox, msg, mp1, mp2);
break;
/*
* WM_DESTROY:
*
*/
case WM_DESTROY:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
{
WinDestroyWindow(pcd->hwndButton);
WinDestroyWindow(pcd->hwndListbox);
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
free(pcd);
}
break;
/*
* default:
*
*/
default:
if (pcd = (PCOMBODATA)WinQueryWindowPtr(hwnd, QWL_USER))
mrc = pcd->pfnwpOrigEntryField(hwnd, msg, mp1, mp2);
break;
}
return mrc;
}
/*
*@@ ctlComboFromEntryField:
* turns a standard entry field control into an
* XComboBox.
*
* The XComboBox is intended to work like a standard
* combobox, but it doesn't have the silly limitation
* that the size of the combobox is assumed to be
* the size of the dropped-down combobox. This limitation
* makes it impossible to use standard comboboxes in
* windows which have the WS_CLIPCHILDREN style because
* the entire combo area will always be clipped out.
*
* This is not a full reimplementation. Only drop-down
* and drop-down list comboboxes are supported. Besides,
* the XComboBox is essentially a subclassed entryfield,
* so there might be limitations.
*
* On input to this function, with flStyle, specify
* either CBS_DROPDOWN or CBS_DROPDOWNLIST. CBS_SIMPLE
* is not supported.
*
* Supported messages to the XComboBox after this funcion
* has been called:
*
* -- CBM_ISLISTSHOWING
*
* -- CBM_SHOWLIST
*
* -- LM_QUERYITEMCOUNT
*
* -- LM_INSERTITEM
*
* -- LM_SETTOPINDEX
*
* -- LM_QUERYTOPINDEX
*
* -- LM_DELETEITEM
*
* -- LM_SELECTITEM
*
* -- LM_QUERYSELECTION
*
* -- LM_SETITEMTEXT
*
* -- LM_QUERYITEMTEXT
*
* -- LM_SEARCHSTRING
*
* -- LM_DELETEALL
*
* NOTE: This occupies QWL_USER of the entryfield.
*
*@@added V0.9.9 (2001-03-17) [umoeller]
*/
BOOL ctlComboFromEntryField(HWND hwnd, // in: entry field to be converted
ULONG flStyle) // in: combo box styles
{
BOOL brc = FALSE;
PFNWP pfnwpOrig;
if (pfnwpOrig = WinSubclassWindow(hwnd,
fnwpComboSubclass))
{
PCOMBODATA pcd;
if (pcd = (PCOMBODATA)malloc(sizeof(*pcd)))
{
SWP swp;
BITMAPINFOHEADER2 bmih2;
memset(pcd, 0, sizeof(*pcd));
pcd->pfnwpOrigEntryField = pfnwpOrig;
pcd->flStyle = flStyle;
WinSetWindowPtr(hwnd, QWL_USER, pcd);
WinQueryWindowPos(hwnd, &swp);
pcd->x = swp.x;
pcd->y = swp.y;
pcd->cx = swp.cx;
pcd->cy = swp.cy;
swp.cx -= COMBO_BUTTON_WIDTH;
WinSetWindowPos(hwnd,
0,
0, 0,
swp.cx, swp.cy,
SWP_SIZE | SWP_NOADJUST); // circumvent subclassing
pcd->hbmButton = WinGetSysBitmap(HWND_DESKTOP,
SBMP_COMBODOWN);
bmih2.cbFix = sizeof(bmih2);
GpiQueryBitmapInfoHeader(pcd->hbmButton,
&bmih2);
pcd->szlButton.cx = bmih2.cx;
pcd->szlButton.cy = bmih2.cy;
pcd->hwndButton = WinCreateWindow(WinQueryWindow(hwnd, QW_PARENT),
WC_BUTTON,
"",
WS_VISIBLE
| BS_PUSHBUTTON | BS_NOPOINTERFOCUS,
swp.x + swp.cx - COMBO_BUTTON_WIDTH,
swp.y,
COMBO_BUTTON_WIDTH,
swp.cy,
hwnd, // owner == entry field!
hwnd, // insert behind entry field
ID_COMBO_BUTTON,
NULL,
NULL);
WinSetWindowPtr(pcd->hwndButton, QWL_USER, pcd);
pcd->pfnwpOrigButton = WinSubclassWindow(pcd->hwndButton,
fnwpSubclassedComboButton);
pcd->hwndListbox = WinCreateWindow(HWND_OBJECT, // parent, for now
WC_LISTBOX,
"?",
WS_VISIBLE | WS_SAVEBITS | WS_CLIPSIBLINGS
| LS_NOADJUSTPOS,
0,
0,
0,
0,
hwnd, // owner == entry field!
HWND_TOP, // insert behind entry field
ID_COMBO_LISTBOX,
NULL,
NULL);
// finally, set style of entry field... we force
// these flags no matter what the original style
// was
/* WinSetWindowBits(hwnd,
QWL_STYLE,
// bits to set:
(flStyle & CBS_DROPDOWNLIST)
? ES_READONLY
: 0,
// mask:
ES_READONLY); */
}
}
return brc;
}