wine/controls/combo.c
Pavel Roskin 6d40d1b05f Changed the button state to "up" as soon as it is known that the
selection has been cancelled, so the button is redrawn in the "up"
state.
1999-03-12 17:00:13 +00:00

1621 lines
45 KiB
C

/*
* Combo controls
*
* Copyright 1997 Alex Korobka
*
* FIXME: roll up in Netscape 3.01.
*/
#include <string.h>
#include "winuser.h"
#include "wine/winuser16.h"
#include "sysmetrics.h"
#include "win.h"
#include "spy.h"
#include "user.h"
#include "heap.h"
#include "combo.h"
#include "drive.h"
#include "debug.h"
/* bits in the dwKeyData */
#define KEYDATA_ALT 0x2000
#define KEYDATA_PREVSTATE 0x4000
/*
* Additional combo box definitions
*/
#define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
#define CB_NOTIFY( lphc, code ) \
(SendMessageA( (lphc)->owner, WM_COMMAND, \
MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
#define CB_GETEDITTEXTLENGTH( lphc ) \
(SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
static HBITMAP hComboBmp = 0;
static UINT CBitHeight, CBitWidth;
static UINT CBitOffset = 8;
/***********************************************************************
* COMBO_Init
*
* Load combo button bitmap.
*/
static BOOL COMBO_Init()
{
HDC hDC;
if( hComboBmp ) return TRUE;
if( (hDC = CreateCompatibleDC(0)) )
{
BOOL bRet = FALSE;
if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
{
BITMAP bm;
HBITMAP hPrevB;
RECT r;
GetObjectA( hComboBmp, sizeof(bm), &bm );
CBitHeight = bm.bmHeight;
CBitWidth = bm.bmWidth;
TRACE(combo, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
hPrevB = SelectObject16( hDC, hComboBmp);
SetRect( &r, 0, 0, CBitWidth, CBitHeight );
InvertRect( hDC, &r );
SelectObject( hDC, hPrevB );
bRet = TRUE;
}
DeleteDC( hDC );
return bRet;
}
return FALSE;
}
/***********************************************************************
* COMBO_NCCreate
*/
static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
{
LPHEADCOMBO lphc;
if ( wnd && COMBO_Init() &&
(lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
{
LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
memset( lphc, 0, sizeof(HEADCOMBO) );
*(LPHEADCOMBO*)wnd->wExtra = lphc;
/* some braindead apps do try to use scrollbar/border flags */
lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
lphc->dwStyle |= CBS_HASSTRINGS;
if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
lphc->wState |= CBF_NOTIFY;
TRACE(combo, "[0x%08x], style = %08x\n",
(UINT)lphc, lphc->dwStyle );
return (LRESULT)(UINT)wnd->hwndSelf;
}
return (LRESULT)FALSE;
}
/***********************************************************************
* COMBO_NCDestroy
*/
static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
{
if( lphc )
{
WND* wnd = lphc->self;
TRACE(combo,"[%04x]: freeing storage\n", CB_HWND(lphc));
if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
DestroyWindow( lphc->hWndLBox );
HeapFree( GetProcessHeap(), 0, lphc );
wnd->wExtra[0] = 0;
}
return 0;
}
/***********************************************************************
* CBGetDefaultTextHeight
*/
static void CBGetDefaultTextHeight( LPHEADCOMBO lphc, LPSIZE lpSize )
{
if( lphc->editHeight ) /* explicitly set height */
lpSize->cy = lphc->editHeight;
else
{
HDC hDC = GetDC( lphc->self->hwndSelf );
HFONT hPrevFont = 0;
if( lphc->hFont ) hPrevFont = SelectObject( hDC, lphc->hFont );
GetTextExtentPoint32A( hDC, "0", 1, lpSize);
lpSize->cy += lpSize->cy / 4 + 4 * SYSMETRICS_CYBORDER;
if( hPrevFont ) SelectObject( hDC, hPrevFont );
ReleaseDC( lphc->self->hwndSelf, hDC );
}
lpSize->cx = lphc->RectCombo.right - lphc->RectCombo.left;
}
/***********************************************************************
* CBCalcPlacement
*
* Set up component coordinates given valid lphc->RectCombo.
*/
static void CBCalcPlacement( LPHEADCOMBO lphc, LPRECT lprEdit,
LPRECT lprButton, LPRECT lprLB )
{
RECT rect = lphc->RectCombo;
SIZE size;
/* get combo height and width */
if( CB_OWNERDRAWN(lphc) )
{
UINT u = lphc->RectEdit.bottom - lphc->RectEdit.top;
if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
{
MEASUREITEMSTRUCT mi32;
/* calculate defaults before sending WM_MEASUREITEM */
CBGetDefaultTextHeight( lphc, &size );
lphc->wState &= ~CBF_MEASUREITEM;
mi32.CtlType = ODT_COMBOBOX;
mi32.CtlID = lphc->self->wIDmenu;
mi32.itemID = -1;
mi32.itemWidth = size.cx;
mi32.itemHeight = size.cy - 6; /* ownerdrawn cb is taller */
mi32.itemData = 0;
SendMessageA(lphc->owner, WM_MEASUREITEM,
(WPARAM)mi32.CtlID, (LPARAM)&mi32);
u = 6 + (UINT16)mi32.itemHeight;
}
else
size.cx = rect.right - rect.left;
size.cy = u;
}
else
CBGetDefaultTextHeight( lphc, &size );
/* calculate text and button placement */
lprEdit->left = lprEdit->top = lprButton->top = 0;
if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* no button */
lprButton->left = lprButton->right = lprButton->bottom = 0;
else
{
INT i = size.cx - CBitWidth - 10; /* seems ok */
lprButton->right = size.cx;
lprButton->left = (INT16)i;
lprButton->bottom = lprButton->top + size.cy;
if( i < 0 ) size.cx = 0;
else size.cx = i;
}
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
size.cx -= CBitOffset;
if( size.cx < 0 ) size.cx = 0;
}
lprEdit->right = size.cx; lprEdit->bottom = size.cy;
/* listbox placement */
lprLB->left = ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
lprLB->top = lprEdit->bottom - SYSMETRICS_CYBORDER;
lprLB->right = rect.right - rect.left;
lprLB->bottom = rect.bottom - rect.top;
if( lphc->droppedWidth > (lprLB->right - lprLB->left) )
lprLB->right = lprLB->left + lphc->droppedWidth;
TRACE(combo,"[%04x]: (%i,%i-%i,%i) placement\n",
CB_HWND(lphc), lphc->RectCombo.left, lphc->RectCombo.top,
lphc->RectCombo.right, lphc->RectCombo.bottom);
TRACE(combo,"\ttext\t= (%i,%i-%i,%i)\n",
lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
TRACE(combo,"\tbutton\t= (%i,%i-%i,%i)\n",
lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
TRACE(combo,"\tlbox\t= (%i,%i-%i,%i)\n",
lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
}
/***********************************************************************
* CBGetDroppedControlRect32
*/
static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
{
lpRect->left = lphc->RectCombo.left +
(lphc->wState & CBF_EDIT) ? CBitOffset : 0;
lpRect->top = lphc->RectCombo.top + lphc->RectEdit.bottom -
SYSMETRICS_CYBORDER;
lpRect->right = lphc->RectCombo.right;
lpRect->bottom = lphc->RectCombo.bottom - SYSMETRICS_CYBORDER;
}
/***********************************************************************
* COMBO_Create
*/
static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
{
static char clbName[] = "ComboLBox";
static char editName[] = "Edit";
LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
lphc->self = wnd;
lphc->owner = lpcs->hwndParent;
/* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
{
UINT lbeStyle;
RECT editRect, btnRect, lbRect;
GetWindowRect( wnd->hwndSelf, &lphc->RectCombo );
lphc->wState |= CBF_MEASUREITEM;
CBCalcPlacement( lphc, &editRect, &btnRect, &lbRect );
lphc->RectButton = btnRect;
lphc->droppedWidth = lphc->editHeight = 0;
/* create listbox popup */
lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
(lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
if( lphc->dwStyle & CBS_SORT )
lbeStyle |= LBS_SORT;
if( lphc->dwStyle & CBS_HASSTRINGS )
lbeStyle |= LBS_HASSTRINGS;
if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
lbeStyle |= LBS_NOINTEGRALHEIGHT;
if( lphc->dwStyle & CBS_DISABLENOSCROLL )
lbeStyle |= LBS_DISABLENOSCROLL;
if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
lbeStyle |= WS_CHILD | WS_VISIBLE;
else /* popup listbox */
{
lbeStyle |= WS_POPUP;
OffsetRect( &lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
}
/* Dropdown ComboLBox is not a child window and we cannot pass
* ID_CB_LISTBOX directly because it will be treated as a menu handle.
*/
lphc->hWndLBox = CreateWindowExA( 0, clbName, NULL, lbeStyle,
lbRect.left + SYSMETRICS_CXBORDER,
lbRect.top + SYSMETRICS_CYBORDER,
lbRect.right - lbRect.left - 2 * SYSMETRICS_CXBORDER,
lbRect.bottom - lbRect.top - 2 * SYSMETRICS_CYBORDER,
lphc->self->hwndSelf,
(lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
lphc->self->hInstance, (LPVOID)lphc );
if( lphc->hWndLBox )
{
BOOL bEdit = TRUE;
lbeStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOHIDESEL | ES_LEFT;
if( lphc->wState & CBF_EDIT )
{
if( lphc->dwStyle & CBS_OEMCONVERT )
lbeStyle |= ES_OEMCONVERT;
if( lphc->dwStyle & CBS_AUTOHSCROLL )
lbeStyle |= ES_AUTOHSCROLL;
if( lphc->dwStyle & CBS_LOWERCASE )
lbeStyle |= ES_LOWERCASE;
else if( lphc->dwStyle & CBS_UPPERCASE )
lbeStyle |= ES_UPPERCASE;
lphc->hWndEdit = CreateWindowExA( 0, editName, NULL, lbeStyle,
editRect.left, editRect.top, editRect.right - editRect.left,
editRect.bottom - editRect.top, lphc->self->hwndSelf,
(HMENU)ID_CB_EDIT, lphc->self->hInstance, NULL );
if( !lphc->hWndEdit ) bEdit = FALSE;
}
if( bEdit )
{
lphc->RectEdit = editRect;
if( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
lphc->wState |= CBF_NORESIZE;
SetWindowPos( wnd->hwndSelf, 0, 0, 0,
lphc->RectCombo.right - lphc->RectCombo.left,
lphc->RectEdit.bottom - lphc->RectEdit.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
lphc->wState &= ~CBF_NORESIZE;
}
TRACE(combo,"init done\n");
return wnd->hwndSelf;
}
ERR(combo, "edit control failure.\n");
} else ERR(combo, "listbox failure.\n");
} else ERR(combo, "no owner for visible combo.\n");
/* CreateWindow() will send WM_NCDESTROY to cleanup */
return -1;
}
/***********************************************************************
* CBPaintButton
*
* Paint combo button (normal, pressed, and disabled states).
*/
static void CBPaintButton(LPHEADCOMBO lphc, HDC hdc)
{
RECT r;
UINT x, y;
BOOL bBool;
HDC hMemDC;
HBRUSH hPrevBrush;
COLORREF oldTextColor, oldBkColor;
if( lphc->wState & CBF_NOREDRAW ) return;
hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
CONV_RECT16TO32( &lphc->RectButton, &r );
Rectangle(hdc, r.left, r.top, r.right, r.bottom );
if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
{
DrawEdge( hdc, &r, EDGE_SUNKEN, BF_RECT );
OffsetRect( &r, 1, 1 );
} else {
r.top++, r.left++;
DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
r.top--, r.left--;
}
InflateRect( &r, -1, -1 );
x = (r.left + r.right - CBitWidth) >> 1;
y = (r.top + r.bottom - CBitHeight) >> 1;
InflateRect( &r, -3, -3 );
hMemDC = CreateCompatibleDC( hdc );
SelectObject( hMemDC, hComboBmp );
oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
RGB(0,0,0) );
BitBlt( hdc, x, y, 8, 8, hMemDC, 0, 0, SRCCOPY );
SetBkColor( hdc, oldBkColor );
SetTextColor( hdc, oldTextColor );
DeleteDC( hMemDC );
SelectObject( hdc, hPrevBrush );
}
/***********************************************************************
* CBPaintText
*
* Paint CBS_DROPDOWNLIST text field / update edit control contents.
*/
static void CBPaintText(LPHEADCOMBO lphc, HDC hdc)
{
INT id, size = 0;
LPSTR pText = NULL;
if( lphc->wState & CBF_NOREDRAW ) return;
/* follow Windows combobox that sends a bunch of text
* inquiries to its listbox while processing WM_PAINT. */
if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
{
size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
{
SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
pText[size] = '\0'; /* just in case */
} else return;
}
if( lphc->wState & CBF_EDIT )
{
if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
if( lphc->wState & CBF_FOCUSED )
SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
}
else /* paint text field ourselves */
{
HBRUSH hPrevBrush = 0;
HDC hDC = hdc;
if( !hDC )
{
if ((hDC = GetDC(lphc->self->hwndSelf)))
{
HBRUSH hBrush = SendMessageA( lphc->owner,
WM_CTLCOLORLISTBOX,
hDC, lphc->self->hwndSelf );
hPrevBrush = SelectObject( hDC,
(hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
}
}
if( hDC )
{
RECT rect;
UINT itemState;
HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
PatBlt( hDC, (rect.left = lphc->RectEdit.left + SYSMETRICS_CXBORDER),
(rect.top = lphc->RectEdit.top + SYSMETRICS_CYBORDER),
(rect.right = lphc->RectEdit.right - SYSMETRICS_CXBORDER),
(rect.bottom = lphc->RectEdit.bottom - SYSMETRICS_CYBORDER) - 1, PATCOPY );
InflateRect( &rect, -1, -1 );
if( lphc->wState & CBF_FOCUSED &&
!(lphc->wState & CBF_DROPPED) )
{
/* highlight */
FillRect( hDC, &rect, GetSysColorBrush(COLOR_HIGHLIGHT) );
SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
itemState = ODS_SELECTED | ODS_FOCUS;
} else itemState = 0;
if( CB_OWNERDRAWN(lphc) )
{
DRAWITEMSTRUCT dis;
if( lphc->self->dwStyle & WS_DISABLED ) itemState |= ODS_DISABLED;
dis.CtlType = ODT_COMBOBOX;
dis.CtlID = lphc->self->wIDmenu;
dis.hwndItem = lphc->self->hwndSelf;
dis.itemAction = ODA_DRAWENTIRE;
dis.itemID = id;
dis.itemState = itemState;
dis.hDC = hDC;
dis.rcItem = rect;
dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
(WPARAM)id, 0 );
SendMessageA( lphc->owner, WM_DRAWITEM,
lphc->self->wIDmenu, (LPARAM)&dis );
}
else
{
ExtTextOutA( hDC, rect.left + 1, rect.top + 1,
ETO_OPAQUE | ETO_CLIPPED, &rect,
pText ? pText : "" , size, NULL );
if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
DrawFocusRect( hDC, &rect );
}
if( hPrevFont ) SelectObject(hDC, hPrevFont );
if( !hdc )
{
if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
ReleaseDC( lphc->self->hwndSelf, hDC );
}
}
}
if (pText)
HeapFree( GetProcessHeap(), 0, pText );
}
/***********************************************************************
* COMBO_Paint
*/
static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
{
PAINTSTRUCT ps;
HDC hDC;
hDC = (hParamDC) ? hParamDC
: BeginPaint( lphc->self->hwndSelf, &ps);
if( hDC && !(lphc->wState & CBF_NOREDRAW) )
{
HBRUSH hPrevBrush, hBkgBrush;
hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
hDC, lphc->self->hwndSelf );
if( !hBkgBrush ) hBkgBrush = GetStockObject(WHITE_BRUSH);
hPrevBrush = SelectObject( hDC, hBkgBrush );
if( !IsRectEmpty(&lphc->RectButton) )
{
/* paint everything to the right of the text field */
PatBlt( hDC, lphc->RectEdit.right, lphc->RectEdit.top,
lphc->RectButton.right - lphc->RectEdit.right,
lphc->RectEdit.bottom - lphc->RectEdit.top, PATCOPY );
CBPaintButton( lphc, hDC );
}
if( !(lphc->wState & CBF_EDIT) )
{
/* paint text field */
HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(
COLOR_WINDOWFRAME) );
Rectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
lphc->RectEdit.right, lphc->RectButton.bottom );
SelectObject( hDC, hPrevPen );
CBPaintText( lphc, hDC );
}
if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
}
if( !hParamDC ) EndPaint(lphc->self->hwndSelf, &ps);
return 0;
}
/***********************************************************************
* CBUpdateLBox
*
* Select listbox entry according to the contents of the edit control.
*/
static INT CBUpdateLBox( LPHEADCOMBO lphc )
{
INT length, idx, ret;
LPSTR pText = NULL;
idx = ret = LB_ERR;
length = CB_GETEDITTEXTLENGTH( lphc );
if( length > 0 )
pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
TRACE(combo,"\t edit text length %i\n", length );
if( pText )
{
if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
else pText[0] = '\0';
idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
(WPARAM)(-1), (LPARAM)pText );
if( idx == LB_ERR ) idx = 0; /* select first item */
else ret = idx;
HeapFree( GetProcessHeap(), 0, pText );
}
/* select entry */
SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
if( idx >= 0 )
{
SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
/* probably superfluous but Windows sends this too */
SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
}
return ret;
}
/***********************************************************************
* CBUpdateEdit
*
* Copy a listbox entry to the edit control.
*/
static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
{
INT length;
LPSTR pText = NULL;
TRACE(combo,"\t %i\n", index );
if( index == -1 )
{
length = CB_GETEDITTEXTLENGTH( lphc );
if( length )
{
if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
{
GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
(WPARAM)(-1), (LPARAM)pText );
HeapFree( GetProcessHeap(), 0, pText );
}
}
}
if( index >= 0 ) /* got an entry */
{
length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
if( length )
{
if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
{
SendMessageA( lphc->hWndLBox, LB_GETTEXT,
(WPARAM)index, (LPARAM)pText );
SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
HeapFree( GetProcessHeap(), 0, pText );
}
}
}
}
/***********************************************************************
* CBDropDown
*
* Show listbox popup.
*/
static void CBDropDown( LPHEADCOMBO lphc )
{
INT index;
RECT rect;
LPRECT pRect = NULL;
TRACE(combo,"[%04x]: drop down\n", CB_HWND(lphc));
CB_NOTIFY( lphc, CBN_DROPDOWN );
/* set selection */
lphc->wState |= CBF_DROPPED;
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
index = CBUpdateLBox( lphc );
if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
}
else
{
index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
if( index == LB_ERR ) index = 0;
SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
pRect = &lphc->RectEdit;
}
/* now set popup position */
GetWindowRect( lphc->self->hwndSelf, &rect );
rect.top += lphc->RectEdit.bottom - lphc->RectEdit.top - SYSMETRICS_CYBORDER;
rect.bottom = rect.top + lphc->RectCombo.bottom -
lphc->RectCombo.top - SYSMETRICS_CYBORDER;
rect.right = rect.left + lphc->RectCombo.right - lphc->RectCombo.left;
rect.left += ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
if( !(lphc->wState & CBF_NOREDRAW) )
if( pRect )
RedrawWindow( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE |
RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
ShowWindow( lphc->hWndLBox, SW_SHOWNA );
}
/***********************************************************************
* CBRollUp
*
* Hide listbox popup.
*/
static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
{
HWND hWnd = lphc->self->hwndSelf;
CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
{
TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
/* always send WM_LBUTTONUP? */
SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
if( lphc->wState & CBF_DROPPED )
{
RECT rect;
lphc->wState &= ~CBF_DROPPED;
ShowWindow( lphc->hWndLBox, SW_HIDE );
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
CBUpdateEdit( lphc, index );
rect = lphc->RectButton;
}
else
{
if( bButton )
UnionRect( &rect, &lphc->RectButton,
&lphc->RectEdit );
else
rect = lphc->RectEdit;
bButton = TRUE;
}
if( bButton && !(lphc->wState & CBF_NOREDRAW) )
RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
CB_NOTIFY( lphc, CBN_CLOSEUP );
}
}
}
/***********************************************************************
* COMBO_FlipListbox
*
* Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
*/
BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
{
if( lphc->wState & CBF_DROPPED )
{
CBRollUp( lphc, TRUE, bRedrawButton );
return FALSE;
}
CBDropDown( lphc );
return TRUE;
}
/***********************************************************************
* COMBO_GetLBWindow
*
* Edit control helper.
*/
HWND COMBO_GetLBWindow( WND* pWnd )
{
LPHEADCOMBO lphc = CB_GETPTR(pWnd);
if( lphc ) return lphc->hWndLBox;
return 0;
}
/***********************************************************************
* CBRepaintButton
*/
static void CBRepaintButton( LPHEADCOMBO lphc )
{
HDC hDC = GetDC( lphc->self->hwndSelf );
if( hDC )
{
CBPaintButton( lphc, hDC );
ReleaseDC( lphc->self->hwndSelf, hDC );
}
}
/***********************************************************************
* COMBO_SetFocus
*/
static void COMBO_SetFocus( LPHEADCOMBO lphc )
{
if( !(lphc->wState & CBF_FOCUSED) )
{
if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
if( lphc->wState & CBF_EDIT )
SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
lphc->wState |= CBF_FOCUSED;
if( !(lphc->wState & CBF_EDIT) ) CBPaintText( lphc, 0 );
CB_NOTIFY( lphc, CBN_SETFOCUS );
}
}
/***********************************************************************
* COMBO_KillFocus
*/
static void COMBO_KillFocus( LPHEADCOMBO lphc )
{
HWND hWnd = lphc->self->hwndSelf;
if( lphc->wState & CBF_FOCUSED )
{
SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
CBRollUp( lphc, FALSE, TRUE );
if( IsWindow( hWnd ) )
{
if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
lphc->wState &= ~CBF_FOCUSED;
/* redraw text */
if( lphc->wState & CBF_EDIT )
SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
else CBPaintText( lphc, 0 );
CB_NOTIFY( lphc, CBN_KILLFOCUS );
}
}
}
/***********************************************************************
* COMBO_Command
*/
static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
{
if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
{
/* ">> 8" makes gcc generate jump-table instead of cmp ladder */
switch( HIWORD(wParam) >> 8 )
{
case (EN_SETFOCUS >> 8):
TRACE(combo,"[%04x]: edit [%04x] got focus\n",
CB_HWND(lphc), lphc->hWndEdit );
if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
break;
case (EN_KILLFOCUS >> 8):
TRACE(combo,"[%04x]: edit [%04x] lost focus\n",
CB_HWND(lphc), lphc->hWndEdit );
/* NOTE: it seems that Windows' edit control sends an
* undocumented message WM_USER + 0x1B instead of this
* notification (only when it happens to be a part of
* the combo). ?? - AK.
*/
COMBO_KillFocus( lphc );
break;
case (EN_CHANGE >> 8):
CB_NOTIFY( lphc, CBN_EDITCHANGE );
CBUpdateLBox( lphc );
break;
case (EN_UPDATE >> 8):
CB_NOTIFY( lphc, CBN_EDITUPDATE );
break;
case (EN_ERRSPACE >> 8):
CB_NOTIFY( lphc, CBN_ERRSPACE );
}
}
else if( lphc->hWndLBox == hWnd )
{
switch( HIWORD(wParam) )
{
case LBN_ERRSPACE:
CB_NOTIFY( lphc, CBN_ERRSPACE );
break;
case LBN_DBLCLK:
CB_NOTIFY( lphc, CBN_DBLCLK );
break;
case LBN_SELCHANGE:
case LBN_SELCANCEL:
TRACE(combo,"[%04x]: lbox selection change [%04x]\n",
CB_HWND(lphc), lphc->wState );
/* do not roll up if selection is being tracked
* by arrowkeys in the dropdown listbox */
if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
else lphc->wState &= ~CBF_NOROLLUP;
CB_NOTIFY( lphc, CBN_SELCHANGE );
CBPaintText( lphc, 0 );
/* fall through */
case LBN_SETFOCUS:
case LBN_KILLFOCUS:
/* nothing to do here since ComboLBox always resets the focus to its
* combo/edit counterpart */
break;
}
}
return 0;
}
/***********************************************************************
* COMBO_ItemOp
*
* Fixup an ownerdrawn item operation and pass it up to the combobox owner.
*/
static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
WPARAM wParam, LPARAM lParam )
{
HWND hWnd = lphc->self->hwndSelf;
TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
#define lpIS ((LPDELETEITEMSTRUCT)lParam)
/* two first items are the same in all 4 structs */
lpIS->CtlType = ODT_COMBOBOX;
lpIS->CtlID = lphc->self->wIDmenu;
switch( msg ) /* patch window handle */
{
case WM_DELETEITEM:
lpIS->hwndItem = hWnd;
#undef lpIS
break;
case WM_DRAWITEM:
#define lpIS ((LPDRAWITEMSTRUCT)lParam)
lpIS->hwndItem = hWnd;
#undef lpIS
break;
case WM_COMPAREITEM:
#define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
lpIS->hwndItem = hWnd;
#undef lpIS
break;
}
return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
}
/***********************************************************************
* COMBO_GetText
*/
static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
{
if( lphc->wState & CBF_EDIT )
return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
(WPARAM)N, (LPARAM)lpText );
/* get it from the listbox */
if( lphc->hWndLBox )
{
INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
if( idx != LB_ERR )
{
LPSTR lpBuffer;
INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
(WPARAM)idx, 0 );
/* 'length' is without the terminating character */
if( length >= N )
lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
else
lpBuffer = lpText;
if( lpBuffer )
{
INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
(WPARAM)idx, (LPARAM)lpBuffer );
/* truncate if buffer is too short */
if( length >= N )
{
if (N && lpText) {
if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
lpText[N - 1] = '\0';
}
HeapFree( GetProcessHeap(), 0, lpBuffer );
}
return (LRESULT)n;
}
}
}
return 0;
}
/***********************************************************************
* CBResetPos
*
* This function sets window positions according to the updated
* component placement struct.
*/
static void CBResetPos( LPHEADCOMBO lphc, LPRECT lbRect, BOOL bRedraw )
{
BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
/* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
* sizing messages */
if( lphc->wState & CBF_EDIT )
SetWindowPos( lphc->hWndEdit, 0, lphc->RectEdit.left, lphc->RectEdit.top,
lphc->RectEdit.right - lphc->RectEdit.left,
lphc->RectEdit.bottom - lphc->RectEdit.top,
SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
if( bDrop )
OffsetRect( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
lbRect->right -= lbRect->left; /* convert to width */
lbRect->bottom -= lbRect->top;
SetWindowPos( lphc->hWndLBox, 0, lbRect->left, lbRect->top,
lbRect->right, lbRect->bottom,
SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
if( bDrop )
{
if( lphc->wState & CBF_DROPPED )
{
lphc->wState &= ~CBF_DROPPED;
ShowWindow( lphc->hWndLBox, SW_HIDE );
}
lphc->wState |= CBF_NORESIZE;
SetWindowPos( lphc->self->hwndSelf, 0, 0, 0,
lphc->RectCombo.right - lphc->RectCombo.left,
lphc->RectEdit.bottom - lphc->RectEdit.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
lphc->wState &= ~CBF_NORESIZE;
if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
RedrawWindow( lphc->self->hwndSelf, NULL, 0,
RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
}
}
/***********************************************************************
* COMBO_Size
*/
static void COMBO_Size( LPHEADCOMBO lphc )
{
RECT rect;
INT w, h;
GetWindowRect( lphc->self->hwndSelf, &rect );
w = rect.right - rect.left; h = rect.bottom - rect.top;
TRACE(combo,"w = %i, h = %i\n", w, h );
/* CreateWindow() may send a bogus WM_SIZE, ignore it */
if( w == (lphc->RectCombo.right - lphc->RectCombo.left) )
{
if( (CB_GETTYPE(lphc) == CBS_SIMPLE) &&
(h == (lphc->RectCombo.bottom - lphc->RectCombo.top)) )
return;
else if( (lphc->dwStyle & CBS_DROPDOWN) &&
(h == (lphc->RectEdit.bottom - lphc->RectEdit.top)) )
return;
}
lphc->RectCombo = rect;
CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect );
CBResetPos( lphc, &rect, TRUE );
}
/***********************************************************************
* COMBO_Font
*/
static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
{
RECT rect;
lphc->hFont = hFont;
if( lphc->wState & CBF_EDIT )
SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
GetWindowRect( lphc->self->hwndSelf, &rect );
OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
rect.top - lphc->RectCombo.top );
CBCalcPlacement( lphc, &lphc->RectEdit,
&lphc->RectButton, &rect );
CBResetPos( lphc, &rect, bRedraw );
}
/***********************************************************************
* COMBO_SetItemHeight
*/
static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
{
LRESULT lRet = CB_ERR;
if( index == -1 ) /* set text field height */
{
if( height < 32768 )
{
RECT rect;
lphc->editHeight = height;
GetWindowRect( lphc->self->hwndSelf, &rect );
OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
rect.top - lphc->RectCombo.top );
CBCalcPlacement( lphc, &lphc->RectEdit,
&lphc->RectButton, &rect );
CBResetPos( lphc, &rect, TRUE );
lRet = height;
}
}
else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
(WPARAM)index, (LPARAM)height );
return lRet;
}
/***********************************************************************
* COMBO_SelectString
*/
static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
{
INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
(WPARAM)start, (LPARAM)pText );
if( index >= 0 )
{
if( lphc->wState & CBF_EDIT )
CBUpdateEdit( lphc, index );
else
CBPaintText( lphc, 0 );
}
return (LRESULT)index;
}
/***********************************************************************
* COMBO_LButtonDown
*/
static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
{
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
BOOL bButton = PtInRect(&lphc->RectButton, pt);
HWND hWnd = lphc->self->hwndSelf;
if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
(bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
{
lphc->wState |= CBF_BUTTONDOWN;
if( lphc->wState & CBF_DROPPED )
{
/* got a click to cancel selection */
lphc->wState &= ~CBF_BUTTONDOWN;
CBRollUp( lphc, TRUE, FALSE );
if( !IsWindow( hWnd ) ) return;
if( lphc->wState & CBF_CAPTURE )
{
lphc->wState &= ~CBF_CAPTURE;
ReleaseCapture();
}
}
else
{
/* drop down the listbox and start tracking */
lphc->wState |= CBF_CAPTURE;
CBDropDown( lphc );
SetCapture( hWnd );
}
if( bButton ) CBRepaintButton( lphc );
}
}
/***********************************************************************
* COMBO_LButtonUp
*
* Release capture and stop tracking if needed.
*/
static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
{
if( lphc->wState & CBF_CAPTURE )
{
lphc->wState &= ~CBF_CAPTURE;
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
INT index = CBUpdateLBox( lphc );
CBUpdateEdit( lphc, index );
}
ReleaseCapture();
}
if( lphc->wState & CBF_BUTTONDOWN )
{
lphc->wState &= ~CBF_BUTTONDOWN;
CBRepaintButton( lphc );
}
}
/***********************************************************************
* COMBO_MouseMove
*
* Two things to do - track combo button and release capture when
* pointer goes into the listbox.
*/
static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
{
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
RECT lbRect;
if( lphc->wState & CBF_BUTTONDOWN )
{
BOOL bButton = PtInRect(&lphc->RectButton, pt);
if( !bButton )
{
lphc->wState &= ~CBF_BUTTONDOWN;
CBRepaintButton( lphc );
}
}
GetClientRect( lphc->hWndLBox, &lbRect );
MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
if( PtInRect(&lbRect, pt) )
{
lphc->wState &= ~CBF_CAPTURE;
ReleaseCapture();
if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
/* hand over pointer tracking */
SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
}
}
/***********************************************************************
* ComboWndProc
*
* http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
*/
LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam )
{
WND* pWnd = WIN_FindWndPtr(hwnd);
if( pWnd )
{
LPHEADCOMBO lphc = CB_GETPTR(pWnd);
TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
if( lphc || message == WM_NCCREATE )
switch(message)
{
/* System messages */
case WM_NCCREATE:
return COMBO_NCCreate(pWnd, lParam);
case WM_NCDESTROY:
COMBO_NCDestroy(lphc);
break;
case WM_CREATE:
return COMBO_Create(lphc, pWnd, lParam);
case WM_PAINT:
/* wParam may contain a valid HDC! */
return COMBO_Paint(lphc, wParam);
case WM_ERASEBKGND:
return TRUE;
case WM_GETDLGCODE:
return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
case WM_SIZE:
if( lphc->hWndLBox &&
!(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
return TRUE;
case WM_SETFONT:
COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
return TRUE;
case WM_GETFONT:
return (LRESULT)lphc->hFont;
case WM_SETFOCUS:
if( lphc->wState & CBF_EDIT )
SetFocus( lphc->hWndEdit );
else
COMBO_SetFocus( lphc );
return TRUE;
case WM_KILLFOCUS:
#define hwndFocus ((HWND16)wParam)
if( !hwndFocus ||
(hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
COMBO_KillFocus( lphc );
#undef hwndFocus
return TRUE;
case WM_COMMAND:
return COMBO_Command( lphc, wParam, (HWND)lParam );
case WM_GETTEXT:
return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
case WM_SETTEXT:
case WM_GETTEXTLENGTH:
case WM_CLEAR:
case WM_CUT:
case WM_PASTE:
case WM_COPY:
if( lphc->wState & CBF_EDIT )
return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
return CB_ERR;
case WM_DRAWITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
case WM_MEASUREITEM:
return COMBO_ItemOp( lphc, message, wParam, lParam );
case WM_ENABLE:
if( lphc->wState & CBF_EDIT )
EnableWindow( lphc->hWndEdit, (BOOL)wParam );
EnableWindow( lphc->hWndLBox, (BOOL)wParam );
return TRUE;
case WM_SETREDRAW:
if( wParam )
lphc->wState &= ~CBF_NOREDRAW;
else
lphc->wState |= CBF_NOREDRAW;
if( lphc->wState & CBF_EDIT )
SendMessageA( lphc->hWndEdit, message, wParam, lParam );
SendMessageA( lphc->hWndLBox, message, wParam, lParam );
return 0;
case WM_SYSKEYDOWN:
if( KEYDATA_ALT & HIWORD(lParam) )
if( wParam == VK_UP || wParam == VK_DOWN )
COMBO_FlipListbox( lphc, TRUE );
break;
case WM_CHAR:
case WM_KEYDOWN:
if( lphc->wState & CBF_EDIT )
return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
else
return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
case WM_LBUTTONDOWN:
if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
return TRUE;
case WM_LBUTTONUP:
COMBO_LButtonUp( lphc, lParam );
return TRUE;
case WM_MOUSEMOVE:
if( lphc->wState & CBF_CAPTURE )
COMBO_MouseMove( lphc, wParam, lParam );
return TRUE;
/* Combo messages */
case CB_ADDSTRING16:
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_ADDSTRING:
return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
case CB_INSERTSTRING16:
wParam = (INT)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_INSERTSTRING:
return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
case CB_DELETESTRING16:
case CB_DELETESTRING:
return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
case CB_SELECTSTRING16:
wParam = (INT)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_SELECTSTRING:
return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
case CB_FINDSTRING16:
wParam = (INT)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_FINDSTRING:
return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
case CB_FINDSTRINGEXACT16:
wParam = (INT)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_FINDSTRINGEXACT:
return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
wParam, lParam );
case CB_SETITEMHEIGHT16:
wParam = (INT)(INT16)wParam; /* signed integer */
case CB_SETITEMHEIGHT:
return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
case CB_GETITEMHEIGHT16:
wParam = (INT)(INT16)wParam;
case CB_GETITEMHEIGHT:
if( (INT)wParam >= 0 ) /* listbox item */
return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
return (lphc->RectEdit.bottom - lphc->RectEdit.top);
case CB_RESETCONTENT16:
case CB_RESETCONTENT:
SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
CBPaintText( lphc, 0 );
return TRUE;
case CB_INITSTORAGE:
return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
case CB_GETHORIZONTALEXTENT:
return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
case CB_SETHORIZONTALEXTENT:
return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
case CB_GETTOPINDEX:
return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
case CB_GETLOCALE:
return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
case CB_SETLOCALE:
return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
case CB_GETDROPPEDWIDTH:
if( lphc->droppedWidth )
return lphc->droppedWidth;
return lphc->RectCombo.right - lphc->RectCombo.left -
(lphc->wState & CBF_EDIT) ? CBitOffset : 0;
case CB_SETDROPPEDWIDTH:
if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
(INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
return CB_ERR;
case CB_GETDROPPEDCONTROLRECT16:
lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
if( lParam )
{
RECT r;
CBGetDroppedControlRect( lphc, &r );
CONV_RECT32TO16( &r, (LPRECT16)lParam );
}
return CB_OKAY;
case CB_GETDROPPEDCONTROLRECT:
if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
return CB_OKAY;
case CB_GETDROPPEDSTATE16:
case CB_GETDROPPEDSTATE:
return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
case CB_DIR16:
lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
/* fall through */
case CB_DIR:
return COMBO_Directory( lphc, (UINT)wParam,
(LPSTR)lParam, (message == CB_DIR));
case CB_SHOWDROPDOWN16:
case CB_SHOWDROPDOWN:
if( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
if( wParam )
{
if( !(lphc->wState & CBF_DROPPED) )
CBDropDown( lphc );
}
else
if( lphc->wState & CBF_DROPPED )
CBRollUp( lphc, FALSE, TRUE );
}
return TRUE;
case CB_GETCOUNT16:
case CB_GETCOUNT:
return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
case CB_GETCURSEL16:
case CB_GETCURSEL:
return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
case CB_SETCURSEL16:
wParam = (INT)(INT16)wParam;
case CB_SETCURSEL:
lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
if( lphc->wState & CBF_SELCHANGE )
{
/* no LBN_SELCHANGE in this case, update manually */
CBPaintText( lphc, 0 );
lphc->wState &= ~CBF_SELCHANGE;
}
return lParam;
case CB_GETLBTEXT16:
wParam = (INT)(INT16)wParam;
lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_GETLBTEXT:
return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
case CB_GETLBTEXTLEN16:
wParam = (INT)(INT16)wParam;
case CB_GETLBTEXTLEN:
return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
case CB_GETITEMDATA16:
wParam = (INT)(INT16)wParam;
case CB_GETITEMDATA:
return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
case CB_SETITEMDATA16:
wParam = (INT)(INT16)wParam;
case CB_SETITEMDATA:
return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
case CB_GETEDITSEL16:
wParam = lParam = 0; /* just in case */
case CB_GETEDITSEL:
if( lphc->wState & CBF_EDIT )
{
INT a, b;
return SendMessageA( lphc->hWndEdit, EM_GETSEL,
(wParam) ? wParam : (WPARAM)&a,
(lParam) ? lParam : (LPARAM)&b );
}
return CB_ERR;
case CB_SETEDITSEL16:
case CB_SETEDITSEL:
if( lphc->wState & CBF_EDIT )
return SendMessageA( lphc->hWndEdit, EM_SETSEL,
(INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
return CB_ERR;
case CB_SETEXTENDEDUI16:
case CB_SETEXTENDEDUI:
if( CB_GETTYPE(lphc) == CBS_SIMPLE ) return CB_ERR;
if( wParam )
lphc->wState |= CBF_EUI;
else lphc->wState &= ~CBF_EUI;
return CB_OKAY;
case CB_GETEXTENDEDUI16:
case CB_GETEXTENDEDUI:
return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
case (WM_USER + 0x1B):
WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
}
return DefWindowProcA(hwnd, message, wParam, lParam);
}
return CB_ERR;
}