1
0
Fork 0
mirror of synced 2025-03-07 03:53:26 +01:00
wine/controls/combo.c
Alexandre Julliard d30dfd24d6 Release 980927
Sun Sep 27 14:25:38 1998  Petter Reinholdtsen <pere@td.org.uit.no>

	* [files/drive.c]
	Make sure GetDriveType32A() handles param NULL.  Added some
	doc on function.

Sun Sep 27 14:07:26 1998  Huw D M Davies <daviesh@abacus.physics.ox.ac.uk>

	* [controls/edit.c] [windows/win.c]
	Don't call SetWindowLong() in EDIT_WM_NCREATE.
	Fix SetWindowLong(GWL_[EX]STYLE) to work for 16bit windows. Remove
	UpdateWindow() call. 

Sun Sep 27 13:41:22 1998  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [scheduler/*.c] [server/event.c] [server/mutex.c]
	  [server/semaphore.c]
	Implemented server-side synchronisation objects.

Sun Sep 27 01:13:35 1998  Alex Priem <alexp@sci.kun.nl>

	* [dlls/comctl32/treeview.c] [include/treeview.h] [include/comctl.h]
	Treeview implementation.

	* [dlls/comctl32/trackbar.c] [include/trackbar.h] 
	Trackbar implementation.

Sat Sep 26 20:49:13 1998  Ulrich Weigand <weigand@informatik.uni-erlangen.de>

	* [if1632/thunk.c] [tools/build.c] [win32/kernel32.c]
	Bugfix: several problems with flat thunks fixed.

	* [memory/selector.c]
	Bugfix: IsBad...Ptr16 didn't work for limit_in_pages segments.

	* [scheduler/thread.c]
	Bugfix: CreateThread: Allow id parameter == NULL.

	* [objects/gdiobj.c]
	Bugfix: IsGDIObject: Return correct object type for stock objects.

	* [msdos/dpmi.c]
	Bugfix: fixed typo in INT_DoRealModeInt.

	* [msdos/int21.c]
	Bugfix: int21 READ *must* use WIN16_hread, not _hread16.

	* [if1632/kernel.spec] [if1632/dummy.c] [if1632/thunk.c]
	  [loader/ne/module.c] [scheduler/event.c] [scheduler/synchro.c]
	  [scheduler/thread.c] [win32/kernel32.c] [win32/ordinals.c]
	Added names/stubs for all undocumented KERNEL routines (Win95).
	Added the following undoc. 16-bit equivalents to Win32 routines:
	KERNEL.441-443,449-453,456-462,471-476,479-486,488.
	Added stubs for some other KERNEL routines.

	* [memory/heap.c] [memory/global.c] [include/global.h]
	Implemented Local32... 32-bit local heap routines (KERNEL.208-215, 229).

	* [miscemu/instr.c] [loader/module.c] [include/module.h]
	Implemented __GP fault handling and HasGPHandler (KERNEL.338).

	* [misc/error.c]
	Implemented LogParamErrorRegs (KERNEL.327).

	* [loader/task.c] [include/windows.h]
	Implemented GetCodeInfo (KERNEL.104).

	* [loader/task.c] [scheduler/thread.c] [include/thread.h]
	Implemented [GS]etThreadQueue and [GS]etFastQueue (KERNEL.463/4, 624/5).

	* [if1632/gdi.spec] [objects/dc.c] [objects/dib.c]
	  [objects/bitmap.c] [include/windows.h]
	Bugfix: fixed wrong parameter for CreateDIBSection16.
	Added [GS]etDIBColorTable16, stub for GetBoundsRect16.
	Partially implemented BITMAP_GetObject16 for DIBs.

	* [if1632/gdi.spec] [relay32/gdi32.spec] [objects/palette.c]
	Added some GDI stubs.

	* [if1632/Makefile.in] [if1632/display.spec] [if1632/mouse.spec]
	  [if1632/keyboard.spec] [if1632/builtin.c] [windows/keyboard.c]
	Added some stubs for Win16 drivers: KEYBOARD, MOUSE, DISPLAY.

	* [if1632/wprocs.spec] [msdos/vxd.c]
	Added some stubs for VxDs: VMM, ConfigMG, TimerAPI.

	* [msdos/int2f.c]
	Added some stubs for real-mode network drivers.

Sat Sep 26 18:18:18 1998  Marcus Meissner <marcus@jet.franken.de>

	* [configure.in]
	Merged in some more of the FreeBSD ports/emulators/wine patches. 
	(Maintainer(s) of this port: You can just submit these
	patches to Alexandre directly.)

	 * [loader/pe_image.c]
	Check filesize of image against size derived from header
	to spot truncated executeables without crashing.

	* [files/directory.c]
	Set envvar "COMSPEC". One win32(!!) program crashes without it.

	* [multimedia/mmio.c]
	Added mmioSetInfo32.

	* [include/file.h]
	Return STD_ERROR_HANDLE for AUX and PRT dos handles.

	* [loader/module.c]
	Handle executeables with spaces in their names a bit better in
	CreateProcess.

	* [relay32/msvfw32.spec][if1632/msvideo.spec][multimedia/msvideo.c][include/vfw.h]
	Started on MS Video support (can load Win32 ICMs).

	* [tools/testrun]
	A bit smarter use of ps.

	* [memory/virtual.c]
	Report PAGE_GUARDed pages as PAGE_PROTECTED (AutoCAD LT R17 fails
	without that check (since Win95 doesn't know about PAGE_GUARD)).

Sat Sep 26 15:04:05 1998  Ove Kaaven <ovek@arcticnet.no>

	* [include/miscemu.h] [if1632/builtin.c] [loader/task.c]
	  [miscemu/instr.c] [msdos/dpmi.c] [msdos/int21.c]
	  [msdos/interrupts.c] [windows/user.c]
	INT_[S|G]etHandler was renamed to INT_[S|G]etPMHandler.
	Added handlers to deal with real-mode interrupts; DOS
	programs are now able to hook real-mode interrupts.

	* [loader/dos/module.c] [msdos/dosmem.c] [msdos/int21.c]
	Moved real-mode interrupt table initialization to
	msdos/dosmem.c, and made new V86 tasks get a full copy
	of the existing "system memory" instead of almost empty
	space. Misc fixes.

	* [include/dosexe.h] [loader/dos/module.c] [msdos/dpmi.c]
	  [msdos/int2f.c]
	First shot at letting DOS programs start up DPMI (but DPMI
	is still disabled for DOS programs, for pkunzip's sake).

	* [include/debugger.h] [debugger/break.c] [debugger/dbg.y]
	  [debugger/registers.c] [debugger/memory.c] [debugger/info.c]
	  [loader/dos/dosvm.c]
	First shot at making Wine's debugger work for DOS programs.
	The -debug flag works, as do "nexti" and "stepi".

Sat Sep 26 13:13:13 1998  Juergen Schmied <juergen.schmied@metronet.de>

	* [dlls/shell32/dataobject.c]
	New classes IEnumFORMATETC implemented, IDataObject stubs.
	
	* [dlls/shell32/*.*][relay32/shell32.spec]
	Bugfixes.
	New: ICM_InsertItem(), ILCreateFromPath().
	Implemented: ILCloneFirst().
	Stubs: ILIsEqual(), ILFindChild(), SHLogILFromFSIL(),
	  PathMatchSpec(), PathIsExe().
	Changed: ILGetSize(), _ILIsDesktop(), PathCombine().

	* [include/shlobj.h]
	New SHLGUID's
	New structures: DVTARGETDEVICE32, STGMEDIUM32, FORMATETC32,
	CLIPFORMAT32.
	New interfaces: IEnumFORMATETC, IDataObject, ICommDlgBrowser
	IDockingWindowFrame, IServiceProvider.

	* [dlls/shell32/folders.c]
	Stubs for IShellLink.

	* [loader/resource.c]
	Small fixes.

	* [misc/crtdll.c][relay32/crtdll.spec]
	New __dllonexit().

	* [windows/message.c]
	SendNotifyMessageA, SendMessageCallBack32A half implemented.

	* [controls/edit.c]
	EDIT_WM_SetText set EF_UPDATE flag not for ES_MULTILINE.

	* [files/file.c]
	Handling of fileposition fixed.

Fri Sep 25 18:13:30 1998  Patrik Stridvall <ps@leissner.se>

	* [include/windows.h] [include/wintypes.h]
	  [ole/ole2nls.h] [relay32/kernel32.spec]
	Implemented EnumDateFormats and EnumTimeFormats.
	Only adds US English support.

	* [Makefile.in] [configure.in] 
	  [dlls/Makefile.in] [dlls/psapi/Makefile.in] 
	  [dlls/psapi/psapi_main.c] 
	New files to implement stubs for PSAPI.DLL (NT only).

	* [relay32/Makefile.in] [relay32/builtin32.c] 
	  [relay32/psapi.spec]
	New spec file for PSAPI.DLL (NT only).

	* [scheduler/handle.c]
	HANDLE_GetObjPtr should only interpret the pseudo handles as the
	current thread or the current process if a thread or a process is
	requested.

	* [include/winversion.h] [misc/version.c]
	Adds the global function VERSION_GetVersion() so functions can
	have different behavior depending on the -winver flag.

	* [include/oledlg.h] [ole/oledlg.c]
	Minor fixes. 

	* [windows/winproc.c]
	Minor changes.

	* [include/imm.h] [misc/imm.c]
	Now returns correct values under both Windows 95 and NT 4.0.

Thu Sep 24 22:11:44 1998  Kristian Nielsen  <kristian.nielsen@risoe.dk>

	* [configure.in] [include/acconfig.h] [include/thread.h]
	  [scheduler/sysdeps.c]
	Autoconfig test for non-reentrant libc.

Wed Sep 23 19:52:12 1998  Matthew Becker <mbecker@glasscity.net>

	* [*/*.c]
	Miscellaneous documentation updates and debugging output 
	standardizations.

	* [objects/clipping.c]
	Added ExtSelectClipRgn.

Wed Sep 23 00:03:28 EDT 1998  Pete Ratzlaff <pratzlaff@cfa.harvard.edu>

	* [include/windows.h] [if1632/user.spec] [relay32/user32.spec]
	  [windows/keyboard.c]
	Added, marginally implemented, GetKeyboardLayoutName().
	Only returns US English keyboard name.

Tue Sep 22 16:32:41 1998  Marcel Baur <mbaur@iiic.ethz.ch>

	* [programs/control/*]
	New Winelib application.

Mon Sep 21 00:29:18 1998  Peter Hunnisett <hunnise@nortel.ca>

	* [include/dplay.h][multimedia/dplay.c][ole/compobj.c]
	Added all DirectPlayLobby interfaces and enhanced DirectPlay
	and DirectPlayLobby support. Still not all that much. Useful
	enough if you just need to start a program, don't try any
	real dplay/lobby stuff.

	* [documentation/status/directplay]
	Added a very little bit.

	* [graphics/ddraw.c]
	- Call to SetWindowLong32A wasn't working because there was no
	  memory set aside when the window class was registered.
	- Fixed some xlib reference counting and change the behaviour
	  of DirectDrawSurface3_SetPalette to mimic observed behaviour
	  (palette is associated will all backbuffers)
	- Also stored all palette colour fields and spit back our saved
	  colour fields rather than query X for them.
	- Added plenty of AddRef and Release traces.
	- Added Xlib support for using -desktop option.
	- Fixed Xlib message handling. Messages weren't being passed to
	  the application. Fixes mouse movements in some xlib DDraw games.
	- Added a few stubs.

	* [windows/win.c][include/winerror.h]
	Fixed up some error handling in WIN_SetWindowLong. SetLastError
	wasn't being used. Could cause problems with 0 return codes.
	Added new error in winerror (1400).

	* [AUTHORS] [include/authors.h]
	Added myself as a Wine author.

Sun Sep 20 21:22:44 1998  Alexander Larsson  <alla@lysator.liu.se>

	* [loader/module.c]
	Changed GetModuleFileName32A so that is returns the
	long version of the filename. Note that just the name
	is long, not the directories.

Sat Sep 19 20:05:30 1998 Per �ngstr�m <pang@mind.nu> 

	* [controls/menu.c]
	Made a couple of fixes to make life easier for applications that alter
	their menus at runtime.

	* [windows/defdlg.c]
	Removed the cast of the return value from dialog procedures to a 16-bit
	bool. The return value needs to retain all its 32 bits, since it is not 
	always a bool, such as when responding to the WM_NCHITTEST message.

Fri Sep 18 11:30:38 1998  Sergey Turchanov <turchanov@usa.net>

	* [loader/resource.c]
	Fixed very funny bug (though gravely affecting further excecution)
	with FindResource[Ex]32 functions.

	* [include/multimon.h] [windows/multimon.c] [relay32/user32.spec]
	  [include/windows.h] [windows/sysmetrics.c]
	Default implementation for Multimonitor API.

	* [include/windows.h] [windows/winpos.c]
	Fixed incorrect declaration (and behaviour) of GetWindowRect32.

Wed Sep 16 10:21:15 1998  Gerard Patel <G.Patel@Wanadoo.fr>

	* [controls/edit.c]
	Fixed EDIT_EM_GetLine to use correctly length of lines.

Tue Sep 15 20:40:16 1998  Eric Kohl <ekohl@abo.rhein-zeitung.de>

	* [misc/tweak.c][include/tweak.h][controls/menu.c]
	Replaced the tweak graphic routines by calls to DrawEdge32().

	* [misc/tweak.c][include/tweak.h][documentation/win95look]
	  [wine.ini][*/*]
	Changed "look and feel" selection. Allows Win3.1, Win95 and
	Win98 (no GUI code implemented) look and feel.

	* [dlls/comctl32/header.c][include/header.h][include/commctrl.h]
	Started callback item support and did some minor improvements.

	* [dlls/comctl32/imagelist.c]
	Fixed bug in transparent image display.
	ImageList_GetIcon is still buggy :-(

	* [dlls/comctl32/toolbar.c]
	Fixed button drawing (partial hack).

	* [dlls/comctl32/commctrl.c]
	Fixed MenuHelp().

	* [controls/button.c]
	Added 3d effect for groupbox.

	* [windows/msgbox.c]
	Added font support for message boxes.

	* [windows/nonclient.c]
	Fixed window moving bug.

	* [dlls/comctl32/*.c]
	Various improvements.

	* [dlls/comctl32/listview.c][dlls/comctl32/rebar.c]
	  [include/commctrl.h]
	More messages.

	* [windows/syscolor.c][include/windows.h]
	Introduced new Win98 system colors.

Tue Sep 15 18:29:45 1998 Wesley Filardo <eightknots@aol.com>

	* [files/profile.c]
	Added support in PROFILE_LoadWineIni for -config option

	* [misc/main.c] [include/options.h]
	Added -config option.

Tue Sep 15 18:22:26 1998  Petter Reinholdtsen <pere@td.org.uit.no>

	* [documentation/Makefile.in]
	Make sure directory exists before installing into it.

Tue Sep 15 01:47:33 1998  Pablo Saratxaga <pablo.sarachaga@ping.be>

	* [ole/nls/*] [ole/ole2nls.c] [include/winnls.h]
	Fixed a few errors and completed some NLS files.

Mon Sep 14 01:23:45 1998  Joseph Pranevich <knight@baltimore.wwaves.com>

	* [include/miscemu.h] [msdos/interrupts.c]
	Removed a compilation warning, added INT 25 to the list of interrupts
	callable from DOS applications, added a debug message when unsupported
	interrupts are used.

Sun Sep 13 19:55:22 1998  Lawson Whitney <lawson_whitney@juno.com>

	* [if1632/relay.c]
	CallProcEx32W should not reverse arguments.

Sun Aug 17 21:18:12 1998  Eric Pouech  <eric.pouech@lemel.fr>

	* [multimedia/midi.c] [multimedia/init.c] [multimedia/mmsys.c] 
	  [include/multimedia.h] [include/mmsystem.h] 
	  [multimedia/Makefile.in] [multimedia/midipatch.c]
	  [if1632/multimedia.spec]
	Made MIDI input and output functional on OSS capable systems.

	* [multimedia/timer.c]
	Changes to trigger callbacks at the accurate pace even when
	fake timers are used.
1998-09-27 18:28:36 +00:00

1584 lines
45 KiB
C

/*
* Combo controls
*
* Copyright 1997 Alex Korobka
*
* FIXME: roll up in Netscape 3.01.
*/
#include <string.h>
#include "windows.h"
#include "sysmetrics.h"
#include "win.h"
#include "spy.h"
#include "user.h"
#include "graphics.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 ) \
(SendMessage32A( (lphc)->owner, WM_COMMAND, \
MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
#define CB_GETEDITTEXTLENGTH( lphc ) \
(SendMessage32A( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
static HBITMAP32 hComboBmp = 0;
static UINT32 CBitHeight, CBitWidth;
static UINT32 CBitOffset = 8;
/***********************************************************************
* COMBO_Init
*
* Load combo button bitmap.
*/
static BOOL32 COMBO_Init()
{
HDC32 hDC;
if( hComboBmp ) return TRUE;
if( (hDC = CreateCompatibleDC32(0)) )
{
BOOL32 bRet = FALSE;
if( (hComboBmp = LoadBitmap32A(0, MAKEINTRESOURCE32A(OBM_COMBO))) )
{
BITMAP32 bm;
HBITMAP32 hPrevB;
RECT32 r;
GetObject32A( hComboBmp, sizeof(bm), &bm );
CBitHeight = bm.bmHeight;
CBitWidth = bm.bmWidth;
TRACE(combo, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
hPrevB = SelectObject16( hDC, hComboBmp);
SetRect32( &r, 0, 0, CBitWidth, CBitHeight );
InvertRect32( hDC, &r );
SelectObject32( hDC, hPrevB );
bRet = TRUE;
}
DeleteDC32( 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))) )
{
LPCREATESTRUCT32A lpcs = (CREATESTRUCT32A*)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",
(UINT32)lphc, lphc->dwStyle );
return (LRESULT)(UINT32)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 )
DestroyWindow32( lphc->hWndLBox );
HeapFree( GetProcessHeap(), 0, lphc );
wnd->wExtra[0] = 0;
}
return 0;
}
/***********************************************************************
* CBCalcPlacement
*
* Set up component coordinates given valid lphc->RectCombo.
*/
static void CBCalcPlacement( LPHEADCOMBO lphc, LPRECT32 lprEdit,
LPRECT32 lprButton, LPRECT32 lprLB )
{
RECT32 rect = lphc->RectCombo;
SIZE32 size;
/* get combo height and width */
size.cx = rect.right - rect.left;
if( CB_OWNERDRAWN(lphc) )
{
UINT32 u = lphc->RectEdit.bottom - lphc->RectEdit.top;
if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
{
MEASUREITEMSTRUCT32 mi32;
lphc->wState &= ~CBF_MEASUREITEM;
mi32.CtlType = ODT_COMBOBOX;
mi32.CtlID = lphc->self->wIDmenu;
mi32.itemID = -1;
mi32.itemWidth = size.cx;
mi32.itemHeight = u - 6; /* ownerdrawn cb is taller */
mi32.itemData = 0;
SendMessage32A(lphc->owner, WM_MEASUREITEM,
(WPARAM32)mi32.CtlID, (LPARAM)&mi32);
u = 6 + (UINT16)mi32.itemHeight;
}
size.cy = u;
}
else if( lphc->editHeight ) /* explicitly set height */
size.cy = lphc->editHeight;
else
{
HDC32 hDC = GetDC32( lphc->self->hwndSelf );
HFONT32 hPrevFont = 0;
SIZE32 font_size;
if( lphc->hFont ) hPrevFont = SelectObject32( hDC, lphc->hFont );
GetTextExtentPoint32A( hDC, "0", 1, &font_size);
size.cy = font_size.cy + font_size.cy / 4 + 4 * SYSMETRICS_CYBORDER;
if( hPrevFont ) SelectObject32( hDC, hPrevFont );
ReleaseDC32( lphc->self->hwndSelf, hDC );
}
/* 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
{
INT32 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 CBGetDroppedControlRect32( LPHEADCOMBO lphc, LPRECT32 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";
LPCREATESTRUCT32A lpcs = (CREATESTRUCT32A*)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) )
{
UINT32 lbeStyle;
RECT32 editRect, btnRect, lbRect;
GetWindowRect32( 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;
OffsetRect32( &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 = CreateWindowEx32A( 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)? (HMENU32)0 : (HMENU32)ID_CB_LISTBOX,
lphc->self->hInstance, (LPVOID)lphc );
if( lphc->hWndLBox )
{
BOOL32 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 = CreateWindowEx32A( 0, editName, NULL, lbeStyle,
editRect.left, editRect.top, editRect.right - editRect.left,
editRect.bottom - editRect.top, lphc->self->hwndSelf,
(HMENU32)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;
SetWindowPos32( 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, HDC32 hdc)
{
RECT32 r;
HBRUSH32 hPrevBrush;
UINT32 x, y;
BOOL32 bBool;
if( lphc->wState & CBF_NOREDRAW ) return;
hPrevBrush = (HBRUSH32)SelectObject32(hdc, GetSysColorBrush32(COLOR_BTNFACE));
CONV_RECT16TO32( &lphc->RectButton, &r );
Rectangle32(hdc, r.left, r.top, r.right, r.bottom );
InflateRect32( &r, -1, -1 );
if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
{
GRAPH_DrawReliefRect(hdc, &r, 1, 0, TRUE);
OffsetRect32( &r, 1, 1 );
} else GRAPH_DrawReliefRect(hdc, &r, 1, 2, FALSE);
x = (r.left + r.right - CBitWidth) >> 1;
y = (r.top + r.bottom - CBitHeight) >> 1;
InflateRect32( &r, -3, -3 );
if( (bBool = CB_DISABLED(lphc)) )
{
GRAPH_SelectClipMask(hdc, hComboBmp, x + 1, y + 1 );
FillRect32(hdc, &r, (HBRUSH32)GetStockObject32(WHITE_BRUSH));
}
GRAPH_SelectClipMask(hdc, hComboBmp, x, y );
FillRect32(hdc, &r, (HBRUSH32)GetStockObject32((bBool) ? GRAY_BRUSH : BLACK_BRUSH));
GRAPH_SelectClipMask(hdc, (HBITMAP32)0, 0, 0);
SelectObject32( hdc, hPrevBrush );
}
/***********************************************************************
* CBPaintText
*
* Paint CBS_DROPDOWNLIST text field / update edit control contents.
*/
static void CBPaintText(LPHEADCOMBO lphc, HDC32 hdc)
{
INT32 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 = SendMessage32A(lphc->hWndLBox, LB_GETCURSEL32, 0, 0) ) != LB_ERR )
{
size = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, id, 0);
if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
{
SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, (WPARAM32)id, (LPARAM)pText );
pText[size] = '\0'; /* just in case */
} else return;
}
if( lphc->wState & CBF_EDIT )
{
if( CB_HASSTRINGS(lphc) ) SetWindowText32A( lphc->hWndEdit, pText );
if( lphc->wState & CBF_FOCUSED )
SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1));
}
else /* paint text field ourselves */
{
HBRUSH32 hPrevBrush = 0;
HDC32 hDC = hdc;
if( !hDC )
{
if ((hDC = GetDC32(lphc->self->hwndSelf)))
{
HBRUSH32 hBrush = SendMessage32A( lphc->owner,
WM_CTLCOLORLISTBOX,
hDC, lphc->self->hwndSelf );
hPrevBrush = SelectObject32( hDC,
(hBrush) ? hBrush : GetStockObject32(WHITE_BRUSH) );
}
}
if( hDC )
{
RECT32 rect;
UINT32 itemState;
HFONT32 hPrevFont = (lphc->hFont) ? SelectObject32(hDC, lphc->hFont) : 0;
PatBlt32( 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 );
InflateRect32( &rect, -1, -1 );
if( lphc->wState & CBF_FOCUSED &&
!(lphc->wState & CBF_DROPPED) )
{
/* highlight */
FillRect32( hDC, &rect, GetSysColorBrush32(COLOR_HIGHLIGHT) );
SetBkColor32( hDC, GetSysColor32( COLOR_HIGHLIGHT ) );
SetTextColor32( hDC, GetSysColor32( COLOR_HIGHLIGHTTEXT ) );
itemState = ODS_SELECTED | ODS_FOCUS;
} else itemState = 0;
if( CB_OWNERDRAWN(lphc) )
{
DRAWITEMSTRUCT32 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 = SendMessage32A( lphc->hWndLBox, LB_GETITEMDATA32,
(WPARAM32)id, 0 );
SendMessage32A( lphc->owner, WM_DRAWITEM,
lphc->self->wIDmenu, (LPARAM)&dis );
}
else
{
ExtTextOut32A( 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))
DrawFocusRect32( hDC, &rect );
}
if( hPrevFont ) SelectObject32(hDC, hPrevFont );
if( !hdc )
{
if( hPrevBrush ) SelectObject32( hDC, hPrevBrush );
ReleaseDC32( lphc->self->hwndSelf, hDC );
}
}
}
HeapFree( GetProcessHeap(), 0, pText );
}
/***********************************************************************
* COMBO_Paint
*/
static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC32 hParamDC)
{
PAINTSTRUCT32 ps;
HDC32 hDC;
hDC = (hParamDC) ? hParamDC
: BeginPaint32( lphc->self->hwndSelf, &ps);
if( hDC && !(lphc->wState & CBF_NOREDRAW) )
{
HBRUSH32 hPrevBrush, hBkgBrush;
hBkgBrush = SendMessage32A( lphc->owner, WM_CTLCOLORLISTBOX,
hDC, lphc->self->hwndSelf );
if( !hBkgBrush ) hBkgBrush = GetStockObject32(WHITE_BRUSH);
hPrevBrush = SelectObject32( hDC, hBkgBrush );
if( !IsRectEmpty32(&lphc->RectButton) )
{
/* paint everything to the right of the text field */
PatBlt32( 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 */
GRAPH_DrawRectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
lphc->RectEdit.right - lphc->RectEdit.left,
lphc->RectButton.bottom - lphc->RectButton.top,
GetSysColorPen32(COLOR_WINDOWFRAME) );
CBPaintText( lphc, hDC );
}
if( hPrevBrush ) SelectObject32( hDC, hPrevBrush );
}
if( !hParamDC ) EndPaint32(lphc->self->hwndSelf, &ps);
return 0;
}
/***********************************************************************
* CBUpdateLBox
*
* Select listbox entry according to the contents of the edit control.
*/
static INT32 CBUpdateLBox( LPHEADCOMBO lphc )
{
INT32 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 ) GetWindowText32A( lphc->hWndEdit, pText, length + 1);
else pText[0] = '\0';
idx = SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32,
(WPARAM32)(-1), (LPARAM)pText );
if( idx == LB_ERR ) idx = 0; /* select first item */
else ret = idx;
HeapFree( GetProcessHeap(), 0, pText );
}
/* select entry */
SendMessage32A( lphc->hWndLBox, LB_SETCURSEL32, (WPARAM32)idx, 0 );
if( idx >= 0 )
{
SendMessage32A( lphc->hWndLBox, LB_SETTOPINDEX32, (WPARAM32)idx, 0 );
/* probably superfluous but Windows sends this too */
SendMessage32A( lphc->hWndLBox, LB_SETCARETINDEX32, (WPARAM32)idx, 0 );
}
return ret;
}
/***********************************************************************
* CBUpdateEdit
*
* Copy a listbox entry to the edit control.
*/
static void CBUpdateEdit( LPHEADCOMBO lphc , INT32 index )
{
INT32 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)) )
{
GetWindowText32A( lphc->hWndEdit, pText, length + 1 );
index = SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32,
(WPARAM32)(-1), (LPARAM)pText );
HeapFree( GetProcessHeap(), 0, pText );
}
}
}
if( index >= 0 ) /* got an entry */
{
length = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, (WPARAM32)index, 0);
if( length )
{
if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
{
SendMessage32A( lphc->hWndLBox, LB_GETTEXT32,
(WPARAM32)index, (LPARAM)pText );
SendMessage32A( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1) );
HeapFree( GetProcessHeap(), 0, pText );
}
}
}
}
/***********************************************************************
* CBDropDown
*
* Show listbox popup.
*/
static void CBDropDown( LPHEADCOMBO lphc )
{
INT32 index;
RECT32 rect;
LPRECT32 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 = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 );
if( index == LB_ERR ) index = 0;
SendMessage32A( lphc->hWndLBox, LB_SETTOPINDEX32, (WPARAM32)index, 0 );
SendMessage32A( lphc->hWndLBox, LB_CARETON32, 0, 0 );
pRect = &lphc->RectEdit;
}
/* now set popup position */
GetWindowRect32( 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;
SetWindowPos32( 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 )
RedrawWindow32( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE |
RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
ShowWindow32( lphc->hWndLBox, SW_SHOWNA );
}
/***********************************************************************
* CBRollUp
*
* Hide listbox popup.
*/
static void CBRollUp( LPHEADCOMBO lphc, BOOL32 ok, BOOL32 bButton )
{
HWND32 hWnd = lphc->self->hwndSelf;
CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
if( IsWindow32( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
{
TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT32)ok );
/* always send WM_LBUTTONUP? */
SendMessage32A( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
if( lphc->wState & CBF_DROPPED )
{
RECT32 rect;
lphc->wState &= ~CBF_DROPPED;
ShowWindow32( lphc->hWndLBox, SW_HIDE );
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
INT32 index = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 );
CBUpdateEdit( lphc, index );
rect = lphc->RectButton;
}
else
{
if( bButton )
UnionRect32( &rect, &lphc->RectButton,
&lphc->RectEdit );
else
rect = lphc->RectEdit;
bButton = TRUE;
}
if( bButton && !(lphc->wState & CBF_NOREDRAW) )
RedrawWindow32( 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...
*/
BOOL32 COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL32 bRedrawButton )
{
if( lphc->wState & CBF_DROPPED )
{
CBRollUp( lphc, TRUE, bRedrawButton );
return FALSE;
}
CBDropDown( lphc );
return TRUE;
}
/***********************************************************************
* COMBO_GetLBWindow
*
* Edit control helper.
*/
HWND32 COMBO_GetLBWindow( WND* pWnd )
{
LPHEADCOMBO lphc = CB_GETPTR(pWnd);
if( lphc ) return lphc->hWndLBox;
return 0;
}
/***********************************************************************
* CBRepaintButton
*/
static void CBRepaintButton( LPHEADCOMBO lphc )
{
HDC32 hDC = GetDC32( lphc->self->hwndSelf );
if( hDC )
{
CBPaintButton( lphc, hDC );
ReleaseDC32( lphc->self->hwndSelf, hDC );
}
}
/***********************************************************************
* COMBO_SetFocus
*/
static void COMBO_SetFocus( LPHEADCOMBO lphc )
{
if( !(lphc->wState & CBF_FOCUSED) )
{
if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
SendMessage32A( lphc->hWndLBox, LB_CARETON32, 0, 0 );
if( lphc->wState & CBF_EDIT )
SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 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 )
{
HWND32 hWnd = lphc->self->hwndSelf;
if( lphc->wState & CBF_FOCUSED )
{
SendMessage32A( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
CBRollUp( lphc, FALSE, TRUE );
if( IsWindow32( hWnd ) )
{
if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
SendMessage32A( lphc->hWndLBox, LB_CARETOFF32, 0, 0 );
lphc->wState &= ~CBF_FOCUSED;
/* redraw text */
if( lphc->wState & CBF_EDIT )
SendMessage32A( lphc->hWndEdit, EM_SETSEL32, (WPARAM32)(-1), 0 );
else CBPaintText( lphc, 0 );
CB_NOTIFY( lphc, CBN_KILLFOCUS );
}
}
}
/***********************************************************************
* COMBO_Command
*/
static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM32 wParam, HWND32 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_ItemOp32( LPHEADCOMBO lphc, UINT32 msg,
WPARAM32 wParam, LPARAM lParam )
{
HWND32 hWnd = lphc->self->hwndSelf;
TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
#define lpIS ((LPDELETEITEMSTRUCT32)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 ((LPDRAWITEMSTRUCT32)lParam)
lpIS->hwndItem = hWnd;
#undef lpIS
break;
case WM_COMPAREITEM:
#define lpIS ((LPCOMPAREITEMSTRUCT32)lParam)
lpIS->hwndItem = hWnd;
#undef lpIS
break;
}
return SendMessage32A( lphc->owner, msg, lphc->self->wIDmenu, lParam );
}
/***********************************************************************
* COMBO_GetText
*/
static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT32 N, LPSTR lpText)
{
if( lphc->wState & CBF_EDIT )
return SendMessage32A( lphc->hWndEdit, WM_GETTEXT,
(WPARAM32)N, (LPARAM)lpText );
/* get it from the listbox */
if( lphc->hWndLBox )
{
INT32 idx = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 );
if( idx != LB_ERR )
{
LPSTR lpBuffer;
INT32 length = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32,
(WPARAM32)idx, 0 );
/* 'length' is without the terminating character */
if( length >= N )
lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
else
lpBuffer = lpText;
if( lpBuffer )
{
INT32 n = SendMessage32A( lphc->hWndLBox, LB_GETTEXT32,
(WPARAM32)idx, (LPARAM)lpText );
/* truncate if buffer is too short */
if( length >= N )
{
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, LPRECT32 lbRect, BOOL32 bRedraw )
{
BOOL32 bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
/* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
* sizing messages */
if( lphc->wState & CBF_EDIT )
SetWindowPos32( 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 )
OffsetRect32( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
lbRect->right -= lbRect->left; /* convert to width */
lbRect->bottom -= lbRect->top;
SetWindowPos32( 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;
ShowWindow32( lphc->hWndLBox, SW_HIDE );
}
lphc->wState |= CBF_NORESIZE;
SetWindowPos32( 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) )
RedrawWindow32( lphc->self->hwndSelf, NULL, 0,
RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
}
}
/***********************************************************************
* COMBO_Size
*/
static void COMBO_Size( LPHEADCOMBO lphc )
{
RECT32 rect;
INT32 w, h;
GetWindowRect32( 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, HFONT32 hFont, BOOL32 bRedraw )
{
RECT32 rect;
lphc->hFont = hFont;
if( lphc->wState & CBF_EDIT )
SendMessage32A( lphc->hWndEdit, WM_SETFONT, (WPARAM32)hFont, bRedraw );
SendMessage32A( lphc->hWndLBox, WM_SETFONT, (WPARAM32)hFont, bRedraw );
GetWindowRect32( lphc->self->hwndSelf, &rect );
OffsetRect32( &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, INT32 index, INT32 height )
{
LRESULT lRet = CB_ERR;
if( index == -1 ) /* set text field height */
{
if( height < 32768 )
{
RECT32 rect;
lphc->editHeight = height;
GetWindowRect32( lphc->self->hwndSelf, &rect );
OffsetRect32( &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 = SendMessage32A( lphc->hWndLBox, LB_SETITEMHEIGHT32,
(WPARAM32)index, (LPARAM)height );
return lRet;
}
/***********************************************************************
* COMBO_SelectString
*/
static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT32 start, LPCSTR pText )
{
INT32 index = SendMessage32A( lphc->hWndLBox, LB_SELECTSTRING32,
(WPARAM32)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 )
{
POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
BOOL32 bButton = PtInRect32(&lphc->RectButton, pt);
HWND32 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 */
CBRollUp( lphc, TRUE, FALSE );
if( !IsWindow32( hWnd ) ) return;
if( lphc->wState & CBF_CAPTURE )
{
lphc->wState &= ~CBF_CAPTURE;
ReleaseCapture();
}
lphc->wState &= ~CBF_BUTTONDOWN;
}
else
{
/* drop down the listbox and start tracking */
lphc->wState |= CBF_CAPTURE;
CBDropDown( lphc );
SetCapture32( 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 )
{
INT32 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, WPARAM32 wParam, LPARAM lParam )
{
POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
RECT32 lbRect;
if( lphc->wState & CBF_BUTTONDOWN )
{
BOOL32 bButton = PtInRect32(&lphc->RectButton, pt);
if( !bButton )
{
lphc->wState &= ~CBF_BUTTONDOWN;
CBRepaintButton( lphc );
}
}
GetClientRect32( lphc->hWndLBox, &lbRect );
MapWindowPoints32( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
if( PtInRect32(&lbRect, pt) )
{
lphc->wState &= ~CBF_CAPTURE;
ReleaseCapture();
if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
/* hand over pointer tracking */
SendMessage32A( 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( HWND32 hwnd, UINT32 message,
WPARAM32 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, (BOOL32)lParam );
return TRUE;
case WM_GETFONT:
return (LRESULT)lphc->hFont;
case WM_SETFOCUS:
if( lphc->wState & CBF_EDIT )
SetFocus32( 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, (HWND32)lParam );
case WM_GETTEXT:
return COMBO_GetText( lphc, (UINT32)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 SendMessage32A( lphc->hWndEdit, message, wParam, lParam );
return CB_ERR;
case WM_DRAWITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
case WM_MEASUREITEM:
return COMBO_ItemOp32( lphc, message, wParam, lParam );
case WM_ENABLE:
if( lphc->wState & CBF_EDIT )
EnableWindow32( lphc->hWndEdit, (BOOL32)wParam );
EnableWindow32( lphc->hWndLBox, (BOOL32)wParam );
return TRUE;
case WM_SETREDRAW:
if( wParam )
lphc->wState &= ~CBF_NOREDRAW;
else
lphc->wState |= CBF_NOREDRAW;
if( lphc->wState & CBF_EDIT )
SendMessage32A( lphc->hWndEdit, message, wParam, lParam );
SendMessage32A( 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 SendMessage32A( lphc->hWndEdit, message, wParam, lParam );
else
return SendMessage32A( lphc->hWndLBox, message, wParam, lParam );
case WM_LBUTTONDOWN:
if( !(lphc->wState & CBF_FOCUSED) ) SetFocus32( 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_ADDSTRING32:
return SendMessage32A( lphc->hWndLBox, LB_ADDSTRING32, 0, lParam);
case CB_INSERTSTRING16:
wParam = (INT32)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_INSERTSTRING32:
return SendMessage32A( lphc->hWndLBox, LB_INSERTSTRING32, wParam, lParam);
case CB_DELETESTRING16:
case CB_DELETESTRING32:
return SendMessage32A( lphc->hWndLBox, LB_DELETESTRING32, wParam, 0);
case CB_SELECTSTRING16:
wParam = (INT32)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_SELECTSTRING32:
return COMBO_SelectString( lphc, (INT32)wParam, (LPSTR)lParam );
case CB_FINDSTRING16:
wParam = (INT32)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_FINDSTRING32:
return SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32, wParam, lParam);
case CB_FINDSTRINGEXACT16:
wParam = (INT32)(INT16)wParam;
if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_FINDSTRINGEXACT32:
return SendMessage32A( lphc->hWndLBox, LB_FINDSTRINGEXACT32,
wParam, lParam );
case CB_SETITEMHEIGHT16:
wParam = (INT32)(INT16)wParam; /* signed integer */
case CB_SETITEMHEIGHT32:
return COMBO_SetItemHeight( lphc, (INT32)wParam, (INT32)lParam);
case CB_GETITEMHEIGHT16:
wParam = (INT32)(INT16)wParam;
case CB_GETITEMHEIGHT32:
if( (INT32)wParam >= 0 )
return SendMessage32A( lphc->hWndLBox, LB_GETITEMHEIGHT32, wParam, 0);
return (lphc->RectEdit.bottom - lphc->RectEdit.top);
case CB_RESETCONTENT16:
case CB_RESETCONTENT32:
SendMessage32A( lphc->hWndLBox, LB_RESETCONTENT32, 0, 0 );
CBPaintText( lphc, 0 );
return TRUE;
case CB_INITSTORAGE32:
return SendMessage32A( lphc->hWndLBox, LB_INITSTORAGE32, wParam, lParam);
case CB_GETHORIZONTALEXTENT32:
return SendMessage32A( lphc->hWndLBox, LB_GETHORIZONTALEXTENT32, 0, 0);
case CB_SETHORIZONTALEXTENT32:
return SendMessage32A( lphc->hWndLBox, LB_SETHORIZONTALEXTENT32, wParam, 0);
case CB_GETTOPINDEX32:
return SendMessage32A( lphc->hWndLBox, LB_GETTOPINDEX32, 0, 0);
case CB_GETLOCALE32:
return SendMessage32A( lphc->hWndLBox, LB_GETLOCALE32, 0, 0);
case CB_SETLOCALE32:
return SendMessage32A( lphc->hWndLBox, LB_SETLOCALE32, wParam, 0);
case CB_GETDROPPEDWIDTH32:
if( lphc->droppedWidth )
return lphc->droppedWidth;
return lphc->RectCombo.right - lphc->RectCombo.left -
(lphc->wState & CBF_EDIT) ? CBitOffset : 0;
case CB_SETDROPPEDWIDTH32:
if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
(INT32)wParam < 32768 ) lphc->droppedWidth = (INT32)wParam;
return CB_ERR;
case CB_GETDROPPEDCONTROLRECT16:
lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
if( lParam )
{
RECT32 r;
CBGetDroppedControlRect32( lphc, &r );
CONV_RECT32TO16( &r, (LPRECT16)lParam );
}
return CB_OKAY;
case CB_GETDROPPEDCONTROLRECT32:
if( lParam ) CBGetDroppedControlRect32(lphc, (LPRECT32)lParam );
return CB_OKAY;
case CB_GETDROPPEDSTATE16:
case CB_GETDROPPEDSTATE32:
return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
case CB_DIR16:
lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
/* fall through */
case CB_DIR32:
return COMBO_Directory( lphc, (UINT32)wParam,
(LPSTR)lParam, (message == CB_DIR32));
case CB_SHOWDROPDOWN16:
case CB_SHOWDROPDOWN32:
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_GETCOUNT32:
return SendMessage32A( lphc->hWndLBox, LB_GETCOUNT32, 0, 0);
case CB_GETCURSEL16:
case CB_GETCURSEL32:
return SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0);
case CB_SETCURSEL16:
wParam = (INT32)(INT16)wParam;
case CB_SETCURSEL32:
return SendMessage32A( lphc->hWndLBox, LB_SETCURSEL32, wParam, 0);
case CB_GETLBTEXT16:
wParam = (INT32)(INT16)wParam;
lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
case CB_GETLBTEXT32:
return SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, wParam, lParam);
case CB_GETLBTEXTLEN16:
wParam = (INT32)(INT16)wParam;
case CB_GETLBTEXTLEN32:
return SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, wParam, 0);
case CB_GETITEMDATA16:
wParam = (INT32)(INT16)wParam;
case CB_GETITEMDATA32:
return SendMessage32A( lphc->hWndLBox, LB_GETITEMDATA32, wParam, 0);
case CB_SETITEMDATA16:
wParam = (INT32)(INT16)wParam;
case CB_SETITEMDATA32:
return SendMessage32A( lphc->hWndLBox, LB_SETITEMDATA32, wParam, lParam);
case CB_GETEDITSEL16:
wParam = lParam = 0; /* just in case */
case CB_GETEDITSEL32:
if( lphc->wState & CBF_EDIT )
{
INT32 a, b;
return SendMessage32A( lphc->hWndEdit, EM_GETSEL32,
(wParam) ? wParam : (WPARAM32)&a,
(lParam) ? lParam : (LPARAM)&b );
}
return CB_ERR;
case CB_SETEDITSEL16:
case CB_SETEDITSEL32:
if( lphc->wState & CBF_EDIT )
return SendMessage32A( lphc->hWndEdit, EM_SETSEL32,
(INT32)(INT16)LOWORD(lParam), (INT32)(INT16)HIWORD(lParam) );
return CB_ERR;
case CB_SETEXTENDEDUI16:
case CB_SETEXTENDEDUI32:
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_GETEXTENDEDUI32:
return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
case (WM_USER + 0x1B):
WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
}
return DefWindowProc32A(hwnd, message, wParam, lParam);
}
return CB_ERR;
}