Release 971130
[wine] / controls / combo.c
1 /*
2  * Combo controls
3  * 
4  * Copyright 1997 Alex Korobka
5  * 
6  * FIXME: roll up in Netscape 3.01.
7  */
8
9 #include <stdio.h>
10 #include <string.h>
11
12 #include "windows.h"
13 #include "sysmetrics.h"
14 #include "syscolor.h"
15 #include "win.h"
16 #include "spy.h"
17 #include "user.h"
18 #include "graphics.h"
19 #include "heap.h"
20 #include "combo.h"
21 #include "drive.h"
22 #include "stddebug.h"
23 #include "debug.h"
24
25   /* bits in the dwKeyData */
26 #define KEYDATA_ALT             0x2000
27 #define KEYDATA_PREVSTATE       0x4000
28
29 /*
30  * Additional combo box definitions
31  */
32
33 #define CB_GETPTR( wnd )      (*(LPHEADCOMBO*)((wnd)->wExtra))
34 #define CB_NOTIFY( lphc, code ) \
35         (SendMessage32A( (lphc)->owner, WM_COMMAND, \
36                          MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
37 #define CB_GETEDITTEXTLENGTH( lphc ) \
38         (SendMessage32A( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
39
40 static HBITMAP16        hComboBmp = 0;
41 static UINT16           CBitHeight, CBitWidth;
42 static UINT16           CBitOffset = 8;
43
44 /***********************************************************************
45  *           COMBO_Init
46  *
47  * Load combo button bitmap.
48  */
49 static BOOL32 COMBO_Init()
50 {
51   HDC16         hDC;
52   
53   if( hComboBmp ) return TRUE;
54   if( (hDC = CreateCompatibleDC16(0)) )
55   {
56     BOOL32      bRet = FALSE;
57     if( (hComboBmp = LoadBitmap16(0, MAKEINTRESOURCE(OBM_COMBO))) )
58     {
59       BITMAP16      bm;
60       HBITMAP16     hPrevB;
61       RECT16        r;
62
63       GetObject16( hComboBmp, sizeof(bm), &bm );
64       CBitHeight = bm.bmHeight;
65       CBitWidth  = bm.bmWidth;
66
67       dprintf_combo(stddeb, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
68
69       hPrevB = SelectObject16( hDC, hComboBmp);
70       SetRect16( &r, 0, 0, CBitWidth, CBitHeight );
71       InvertRect16( hDC, &r );
72       SelectObject16( hDC, hPrevB );
73       bRet = TRUE;
74     }
75     DeleteDC16( hDC );
76     return bRet;
77   }
78   return FALSE;
79 }
80
81 /***********************************************************************
82  *           COMBO_NCCreate
83  */
84 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
85 {
86    LPHEADCOMBO          lphc;
87
88    if ( wnd && COMBO_Init() &&
89       (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
90    {
91         LPCREATESTRUCT32A     lpcs = (CREATESTRUCT32A*)lParam;
92        
93         memset( lphc, 0, sizeof(HEADCOMBO) );
94        *(LPHEADCOMBO*)wnd->wExtra = lphc;
95
96        /* some braindead apps do try to use scrollbar/border flags */
97
98         lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
99         wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
100
101         if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
102               lphc->dwStyle |= CBS_HASSTRINGS;
103         if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
104               lphc->wState |= CBF_NOTIFY;
105
106         dprintf_combo(stddeb, "COMBO_NCCreate: [0x%08x], style = %08x\n", 
107                                                 (UINT32)lphc, lphc->dwStyle );
108
109         return (LRESULT)(UINT32)wnd->hwndSelf; 
110     }
111     return (LRESULT)FALSE;
112 }
113
114 /***********************************************************************
115  *           COMBO_NCDestroy
116  */
117 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
118 {
119
120    if( lphc )
121    {
122        WND*             wnd = lphc->self;
123
124        dprintf_combo(stddeb,"Combo [%04x]: freeing storage\n", CB_HWND(lphc));
125
126        if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox ) 
127            DestroyWindow32( lphc->hWndLBox );
128
129        HeapFree( GetProcessHeap(), 0, lphc );
130        wnd->wExtra[0] = 0;
131    }
132    return 0;
133 }
134
135 /***********************************************************************
136  *           CBCalcPlacement
137  *
138  * Set up component coordinates given valid lphc->RectCombo.
139  */
140 static void CBCalcPlacement( LPHEADCOMBO lphc, 
141                              LPRECT16 lprEdit, LPRECT16 lprButton, LPRECT16 lprLB )
142 {
143    RECT16       rect = lphc->RectCombo;
144    SIZE16       size;
145
146    /* get combo height and width */
147
148    if( lphc->editHeight )
149        size.cy = (INT16)lphc->editHeight;
150    else
151    {
152        HDC16    hDC = GetDC16( lphc->self->hwndSelf );
153        HFONT16  hPrevFont = (HFONT16)0;
154
155        if( lphc->hFont ) hPrevFont = SelectObject16( hDC, lphc->hFont );
156    
157        GetTextExtentPoint16( hDC, "0", 1, &size);
158
159        size.cy += size.cy / 4 + 4 * SYSMETRICS_CYBORDER;
160
161        if( hPrevFont ) SelectObject16( hDC, hPrevFont );
162        ReleaseDC16( lphc->self->hwndSelf, hDC );
163    }
164    size.cx = rect.right - rect.left;
165
166    if( CB_OWNERDRAWN(lphc) )
167    {
168        UINT16   u = lphc->RectEdit.bottom - lphc->RectEdit.top;
169
170        if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
171        {
172          MEASUREITEMSTRUCT32        mi32;
173
174          lphc->wState &= ~CBF_MEASUREITEM;
175          mi32.CtlType = ODT_COMBOBOX;
176          mi32.CtlID   = lphc->self->wIDmenu;
177          mi32.itemID  = -1;
178          mi32.itemWidth  = size.cx;
179          mi32.itemHeight = size.cy - 6; /* ownerdrawn cb is taller */
180          mi32.itemData   = 0;
181          SendMessage32A(lphc->owner, WM_MEASUREITEM, 
182                                 (WPARAM32)mi32.CtlID, (LPARAM)&mi32);
183          u = 6 + (UINT16)mi32.itemHeight;
184        }
185        size.cy = u;
186    }
187
188    /* calculate text and button placement */
189
190    lprEdit->left = lprEdit->top = lprButton->top = 0;
191    if( CB_GETTYPE(lphc) == CBS_SIMPLE )         /* no button */
192        lprButton->left = lprButton->right = lprButton->bottom = 0;
193    else
194    {
195        INT32    i = size.cx - CBitWidth - 10;   /* seems ok */
196
197        lprButton->right = size.cx;
198        lprButton->left = (INT16)i;
199        lprButton->bottom = lprButton->top + size.cy;
200
201        if( i < 0 ) size.cx = 0;
202        else size.cx = (INT16)i;
203    }
204
205    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
206    {
207        size.cx -= CBitOffset;
208        if( size.cx < 0 ) size.cx = 0;
209    }
210
211    lprEdit->right = size.cx; lprEdit->bottom = size.cy;
212
213    /* listbox placement */
214
215    lprLB->left = ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
216    lprLB->top = lprEdit->bottom - SYSMETRICS_CYBORDER;
217    lprLB->right = rect.right - rect.left;
218    lprLB->bottom = rect.bottom - rect.top;
219
220    if( lphc->droppedWidth > (lprLB->right - lprLB->left) )
221        lprLB->right = lprLB->left + (INT16)lphc->droppedWidth;
222
223 dprintf_combo(stddeb,"Combo [%04x]: (%i,%i-%i,%i) placement\n\ttext\t= (%i,%i-%i,%i)\
224 \n\tbutton\t= (%i,%i-%i,%i)\n\tlbox\t= (%i,%i-%i,%i)\n", CB_HWND(lphc),
225 lphc->RectCombo.left, lphc->RectCombo.top, lphc->RectCombo.right, lphc->RectCombo.bottom,
226 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom,
227 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom, 
228 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
229
230 }
231
232 /***********************************************************************
233  *           CBGetDroppedControlRect32
234  */
235 static void CBGetDroppedControlRect32( LPHEADCOMBO lphc, LPRECT32 lpRect)
236 {
237    lpRect->left = lphc->RectCombo.left +
238                       (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
239    lpRect->top = lphc->RectCombo.top + lphc->RectEdit.bottom -
240                                              SYSMETRICS_CYBORDER;
241    lpRect->right = lphc->RectCombo.right;
242    lpRect->bottom = lphc->RectCombo.bottom - SYSMETRICS_CYBORDER;
243 }
244
245 /***********************************************************************
246  *           COMBO_Create
247  */
248 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
249 {
250   static char clbName[] = "ComboLBox";
251   static char editName[] = "Edit";
252
253   LPCREATESTRUCT32A  lpcs = (CREATESTRUCT32A*)lParam;
254   
255   if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
256   else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
257
258   lphc->self  = wnd;
259   lphc->owner = lpcs->hwndParent;
260
261   /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
262
263   if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
264   {
265       UINT32    lbeStyle;
266       RECT16    editRect, btnRect, lbRect;
267
268       GetWindowRect16( wnd->hwndSelf, &lphc->RectCombo );
269
270       lphc->wState |= CBF_MEASUREITEM;
271       CBCalcPlacement( lphc, &editRect, &btnRect, &lbRect );
272       lphc->RectButton = btnRect;
273       lphc->droppedWidth = lphc->editHeight = 0;
274
275       /* create listbox popup */
276
277       lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) | 
278                  (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
279
280       if( lphc->dwStyle & CBS_SORT )
281         lbeStyle |= LBS_SORT;
282       if( lphc->dwStyle & CBS_HASSTRINGS )
283         lbeStyle |= LBS_HASSTRINGS;
284       if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
285         lbeStyle |= LBS_NOINTEGRALHEIGHT;
286       if( lphc->dwStyle & CBS_DISABLENOSCROLL )
287         lbeStyle |= LBS_DISABLENOSCROLL;
288   
289       if( CB_GETTYPE(lphc) == CBS_SIMPLE )      /* child listbox */
290         lbeStyle |= WS_CHILD | WS_VISIBLE;
291       else                                      /* popup listbox */
292       {
293         lbeStyle |= WS_POPUP;
294         OffsetRect16( &lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
295       }
296
297      /* Dropdown ComboLBox is not a child window and we cannot pass 
298       * ID_CB_LISTBOX directly because it will be treated as a menu handle.
299       */
300
301       lphc->hWndLBox = CreateWindowEx32A( 0, clbName, NULL, lbeStyle, 
302                         lbRect.left + SYSMETRICS_CXBORDER, 
303                         lbRect.top + SYSMETRICS_CYBORDER, 
304                         lbRect.right - lbRect.left - 2 * SYSMETRICS_CXBORDER, 
305                         lbRect.bottom - lbRect.top - 2 * SYSMETRICS_CYBORDER, 
306                         lphc->self->hwndSelf, 
307                        (lphc->dwStyle & CBS_DROPDOWN)? (HMENU32)0 : (HMENU32)ID_CB_LISTBOX,
308                         lphc->self->hInstance, (LPVOID)lphc );
309       if( lphc->hWndLBox )
310       {
311           BOOL32        bEdit = TRUE;
312           lbeStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOHIDESEL | ES_LEFT;
313           if( lphc->wState & CBF_EDIT ) 
314           {
315               if( lphc->dwStyle & CBS_OEMCONVERT )
316                   lbeStyle |= ES_OEMCONVERT;
317               if( lphc->dwStyle & CBS_AUTOHSCROLL )
318                   lbeStyle |= ES_AUTOHSCROLL;
319               if( lphc->dwStyle & CBS_LOWERCASE )
320                   lbeStyle |= ES_LOWERCASE;
321               else if( lphc->dwStyle & CBS_UPPERCASE )
322                   lbeStyle |= ES_UPPERCASE;
323               lphc->hWndEdit = CreateWindowEx32A( 0, editName, NULL, lbeStyle,
324                         editRect.left, editRect.top, editRect.right - editRect.left,
325                         editRect.bottom - editRect.top, lphc->self->hwndSelf, 
326                         (HMENU32)ID_CB_EDIT, lphc->self->hInstance, NULL );
327               if( !lphc->hWndEdit ) bEdit = FALSE;
328           } 
329
330           if( bEdit )
331           {
332               lphc->RectEdit = editRect;
333               if( CB_GETTYPE(lphc) != CBS_SIMPLE )
334               {
335                 lphc->wState |= CBF_NORESIZE;
336                 SetWindowPos32( wnd->hwndSelf, 0, 0, 0, 
337                                 lphc->RectCombo.right - lphc->RectCombo.left,
338                                 lphc->RectEdit.bottom - lphc->RectEdit.top,
339                                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
340                 lphc->wState &= ~CBF_NORESIZE;
341               }
342               dprintf_combo(stddeb,"COMBO_Create: init done\n");
343               return wnd->hwndSelf;
344           }
345           dprintf_combo(stderr, "COMBO_Create: edit control failure.\n");
346       } else dprintf_combo(stderr, "COMBO_Create: listbox failure.\n");
347   } else dprintf_combo(stderr, "COMBO_Create: no owner for visible combo.\n");
348
349   /* CreateWindow() will send WM_NCDESTROY to cleanup */
350
351   return -1;
352 }
353
354 /***********************************************************************
355  *           CBPaintButton
356  *
357  * Paint combo button (normal, pressed, and disabled states).
358  */
359 static void CBPaintButton(LPHEADCOMBO lphc, HDC16 hdc)
360 {
361     RECT32      r;
362     HBRUSH32    hPrevBrush;
363     UINT32      x, y;
364     BOOL32      bBool;
365
366     if( lphc->wState & CBF_NOREDRAW )
367         return;
368
369     hPrevBrush = (HBRUSH32)SelectObject32(hdc, sysColorObjects.hbrushBtnFace);
370     CONV_RECT16TO32( &lphc->RectButton, &r );
371
372     Rectangle32(hdc, r.left, r.top, r.right, r.bottom );
373     InflateRect32( &r, -1, -1 );
374     if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
375     {
376         GRAPH_DrawReliefRect(hdc, &r, 1, 0, TRUE);
377         OffsetRect32( &r, 1, 1 );
378     } else GRAPH_DrawReliefRect(hdc, &r, 1, 2, FALSE);
379
380     x = (r.left + r.right - CBitWidth) >> 1;
381     y = (r.top + r.bottom - CBitHeight) >> 1;
382
383     InflateRect32( &r, -3, -3 );
384     if( (bBool = CB_DISABLED(lphc)) )
385     {
386         GRAPH_SelectClipMask(hdc, hComboBmp, x + 1, y + 1 );
387         FillRect32(hdc, &r, (HBRUSH32)GetStockObject32(WHITE_BRUSH));
388     }
389
390     GRAPH_SelectClipMask(hdc, hComboBmp, x, y );
391     FillRect32(hdc, &r, (HBRUSH32)GetStockObject32((bBool) ? GRAY_BRUSH : BLACK_BRUSH));
392
393     GRAPH_SelectClipMask(hdc, (HBITMAP32)0, 0, 0);
394     SelectObject32( hdc, hPrevBrush );
395 }
396
397 /***********************************************************************
398  *           CBPaintText
399  *
400  * Paint CBS_DROPDOWNLIST text field / update edit control contents.
401  */
402 static void CBPaintText(LPHEADCOMBO lphc, HDC16 hdc)
403 {
404    INT32        id, size = 0;
405    LPSTR        pText = NULL;
406
407    if( lphc->wState & CBF_NOREDRAW )
408         return;
409
410    /* follow Windows combobox that sends a bunch of text 
411     * inquiries to its listbox while processing WM_PAINT. */
412
413    if( (id = SendMessage32A(lphc->hWndLBox, LB_GETCURSEL32, 0, 0) ) != LB_ERR )
414    {
415         size = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, id, 0);
416         if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
417         {
418             SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, (WPARAM32)id, (LPARAM)pText );
419             pText[size] = '\0'; /* just in case */
420         } else return;
421    }
422
423    if( lphc->wState & CBF_EDIT )
424    {
425         if( CB_HASSTRINGS(lphc) ) SetWindowText32A( lphc->hWndEdit, pText );
426         if( lphc->wState & CBF_FOCUSED ) 
427             SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1));
428    }
429    else /* paint text field ourselves */
430    {
431         HBRUSH32 hPrevBrush = 0;
432         HDC32    hDC = hdc;
433
434         if( !hDC ) 
435         {
436             if ((hDC = GetDC32(lphc->self->hwndSelf)))
437             {
438                 HBRUSH32 hBrush = SendMessage32A( lphc->owner,
439                                                   WM_CTLCOLORLISTBOX, 
440                                                   hDC, lphc->self->hwndSelf );
441                 hPrevBrush = SelectObject32( hDC, 
442                            (hBrush) ? hBrush : GetStockObject32(WHITE_BRUSH) );
443             }
444         }
445         if( hDC )
446         {
447             RECT32      rect;
448             UINT16      itemState;
449             HFONT32     hPrevFont = (lphc->hFont) ? SelectObject32(hDC, lphc->hFont) : 0;
450
451             PatBlt32( hDC, (rect.left = lphc->RectEdit.left + SYSMETRICS_CXBORDER),
452                            (rect.top = lphc->RectEdit.top + SYSMETRICS_CYBORDER),
453                            (rect.right = lphc->RectEdit.right - SYSMETRICS_CXBORDER),
454                            (rect.bottom = lphc->RectEdit.bottom - SYSMETRICS_CYBORDER) - 1, PATCOPY );
455             InflateRect32( &rect, -1, -1 );
456
457             if( lphc->wState & CBF_FOCUSED && 
458                 !(lphc->wState & CBF_DROPPED) )
459             {
460                 /* highlight */
461
462                 FillRect32( hDC, &rect, sysColorObjects.hbrushHighlight );
463                 SetBkColor32( hDC, GetSysColor32( COLOR_HIGHLIGHT ) );
464                 SetTextColor32( hDC, GetSysColor32( COLOR_HIGHLIGHTTEXT ) );
465                 itemState = ODS_SELECTED | ODS_FOCUS;
466             } else itemState = 0;
467
468             if( CB_OWNERDRAWN(lphc) )
469             {
470                 DRAWITEMSTRUCT32 dis;
471
472                 if( lphc->self->dwStyle & WS_DISABLED ) itemState |= ODS_DISABLED;
473
474                 dis.CtlType     = ODT_COMBOBOX;
475                 dis.CtlID       = lphc->self->wIDmenu;
476                 dis.hwndItem    = lphc->self->hwndSelf;
477                 dis.itemAction  = ODA_DRAWENTIRE;
478                 dis.itemID      = id;
479                 dis.itemState   = itemState;
480                 dis.hDC         = hDC;
481                 dis.rcItem      = rect;
482                 dis.itemData    = SendMessage32A( lphc->hWndLBox, LB_GETITEMDATA32, 
483                                                                   (WPARAM32)id, 0 );
484                 SendMessage32A( lphc->owner, WM_DRAWITEM, 
485                                 lphc->self->wIDmenu, (LPARAM)&dis );
486             }
487             else
488             {
489                 ExtTextOut32A( hDC, rect.left + 1, rect.top + 1,
490                                ETO_OPAQUE | ETO_CLIPPED, &rect,
491                                (pText) ? pText : "" , size, NULL );
492                 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
493                     DrawFocusRect32( hDC, &rect );
494             }
495
496             if( hPrevFont ) SelectObject32(hDC, hPrevFont );
497             if( !hdc ) 
498             {
499                 if( hPrevBrush ) SelectObject32( hDC, hPrevBrush );
500                 ReleaseDC32( lphc->self->hwndSelf, hDC );
501             }
502         }
503    }
504    HeapFree( GetProcessHeap(), 0, pText );
505 }
506
507 /***********************************************************************
508  *           COMBO_Paint
509  */
510 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC16 hParamDC)
511 {
512   PAINTSTRUCT16 ps;
513   HDC16         hDC;
514   
515   hDC = (hParamDC) ? hParamDC
516                    : BeginPaint16( lphc->self->hwndSelf, &ps);
517   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
518   {
519       HBRUSH32  hPrevBrush, hBkgBrush;
520
521       hBkgBrush = SendMessage32A( lphc->owner, WM_CTLCOLORLISTBOX,
522                                   hDC, lphc->self->hwndSelf );
523       if( !hBkgBrush ) hBkgBrush = GetStockObject32(WHITE_BRUSH);
524
525       hPrevBrush = SelectObject32( hDC, hBkgBrush );
526       if( !IsRectEmpty16(&lphc->RectButton) )
527       {
528           /* paint everything to the right of the text field */
529
530           PatBlt32( hDC, lphc->RectEdit.right, lphc->RectEdit.top,
531                          lphc->RectButton.right - lphc->RectEdit.right,
532                          lphc->RectEdit.bottom - lphc->RectEdit.top, PATCOPY );
533           CBPaintButton( lphc, hDC );
534       }
535
536       if( !(lphc->wState & CBF_EDIT) )
537       {
538           /* paint text field */
539
540           GRAPH_DrawRectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
541                                     lphc->RectEdit.right - lphc->RectEdit.left, 
542                                     lphc->RectButton.bottom - lphc->RectButton.top,
543                                     sysColorObjects.hpenWindowFrame ); 
544           CBPaintText( lphc, hDC );
545       }
546       if( hPrevBrush ) SelectObject32( hDC, hPrevBrush );
547   }
548   if( !hParamDC ) EndPaint16(lphc->self->hwndSelf, &ps);
549   return 0;
550 }
551
552 /***********************************************************************
553  *           CBUpdateLBox
554  *
555  * Select listbox entry according to the contents of the edit control.
556  */
557 static INT32 CBUpdateLBox( LPHEADCOMBO lphc )
558 {
559    INT32        length, idx, ret;
560    LPSTR        pText = NULL;
561    
562    idx = ret = LB_ERR;
563    length = CB_GETEDITTEXTLENGTH( lphc );
564  
565    if( length > 0 ) 
566        pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
567
568    dprintf_combo(stddeb,"\tCBUpdateLBox: edit text length %i\n", length );
569
570    if( pText )
571    {
572        if( length ) GetWindowText32A( lphc->hWndEdit, pText, length + 1);
573        else pText[0] = '\0';
574        idx = SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32, 
575                              (WPARAM32)(-1), (LPARAM)pText );
576        if( idx == LB_ERR ) idx = 0;     /* select first item */
577        else ret = idx;
578        HeapFree( GetProcessHeap(), 0, pText );
579    }
580
581    /* select entry */
582
583    SendMessage32A( lphc->hWndLBox, LB_SETCURSEL32, (WPARAM32)idx, 0 );
584    
585    if( idx >= 0 )
586    {
587        SendMessage32A( lphc->hWndLBox, LB_SETTOPINDEX32, (WPARAM32)idx, 0 );
588        /* probably superfluous but Windows sends this too */
589        SendMessage32A( lphc->hWndLBox, LB_SETCARETINDEX32, (WPARAM32)idx, 0 );
590    }
591    return ret;
592 }
593
594 /***********************************************************************
595  *           CBUpdateEdit
596  *
597  * Copy a listbox entry to the edit control.
598  */
599 static void CBUpdateEdit( LPHEADCOMBO lphc , INT32 index )
600 {
601    INT32        length;
602    LPSTR        pText = NULL;
603
604    dprintf_combo(stddeb,"\tCBUpdateEdit: %i\n", index );
605
606    if( index == -1 )
607    {
608        length = CB_GETEDITTEXTLENGTH( lphc );
609        if( length )
610        {
611            if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
612            {
613                 GetWindowText32A( lphc->hWndEdit, pText, length + 1 );
614                 index = SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32,
615                                         (WPARAM32)(-1), (LPARAM)pText );
616                 HeapFree( GetProcessHeap(), 0, pText );
617            }
618        }
619    }
620
621    if( index >= 0 ) /* got an entry */
622    {
623        length = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, (WPARAM32)index, 0);
624        if( length )
625        {
626            if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
627            {
628                 SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, 
629                                 (WPARAM32)index, (LPARAM)pText );
630                 SendMessage32A( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
631                 SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1) );
632                 HeapFree( GetProcessHeap(), 0, pText );
633            }
634        }
635    }
636 }
637
638 /***********************************************************************
639  *           CBDropDown
640  * 
641  * Show listbox popup.
642  */
643 static void CBDropDown( LPHEADCOMBO lphc )
644 {
645    INT32        index;
646    RECT16       rect;
647    LPRECT16     pRect = NULL;
648
649    dprintf_combo(stddeb,"Combo [%04x]: drop down\n", CB_HWND(lphc));
650
651    CB_NOTIFY( lphc, CBN_DROPDOWN );
652
653    /* set selection */
654
655    lphc->wState |= CBF_DROPPED;
656    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
657    {
658        index = CBUpdateLBox( lphc );
659        if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
660    }
661    else
662    {
663        index = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 );
664        if( index == LB_ERR ) index = 0;
665        SendMessage32A( lphc->hWndLBox, LB_SETTOPINDEX32, (WPARAM32)index, 0 );
666        SendMessage32A( lphc->hWndLBox, LB_CARETON32, 0, 0 );
667        pRect = &lphc->RectEdit;
668    }
669
670    /* now set popup position */
671
672    GetWindowRect16( lphc->self->hwndSelf, &rect );
673    
674    rect.top += lphc->RectEdit.bottom - lphc->RectEdit.top - SYSMETRICS_CYBORDER;
675    rect.bottom = rect.top + lphc->RectCombo.bottom - 
676                             lphc->RectCombo.top - SYSMETRICS_CYBORDER;
677    rect.right = rect.left + lphc->RectCombo.right - lphc->RectCombo.left;
678    rect.left += ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
679
680    SetWindowPos32( lphc->hWndLBox, HWND_TOP, rect.left, rect.top, 
681                  rect.right - rect.left, rect.bottom - rect.top, 
682                  SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
683    if( !(lphc->wState & CBF_NOREDRAW) )
684    {
685        if( pRect )
686            RedrawWindow16( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE | 
687                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
688        ShowWindow32( lphc->hWndLBox, SW_SHOWNA );
689    }
690 }
691
692 /***********************************************************************
693  *           CBRollUp
694  *
695  * Hide listbox popup.
696  */
697 static void CBRollUp( LPHEADCOMBO lphc, BOOL32 ok, BOOL32 bButton )
698 {
699    HWND32       hWnd = lphc->self->hwndSelf;
700
701    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
702
703    if( IsWindow32( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
704    {
705
706        dprintf_combo(stddeb,"Combo [%04x]: roll up [%i]\n", CB_HWND(lphc), (INT32)ok );
707
708        /* always send WM_LBUTTONUP? */
709        SendMessage32A( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
710
711        if( lphc->wState & CBF_DROPPED ) 
712        {
713            RECT16       rect;
714
715            lphc->wState &= ~CBF_DROPPED;
716            ShowWindow32( lphc->hWndLBox, SW_HIDE );
717
718            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
719            {
720                INT32 index = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 );
721                CBUpdateEdit( lphc, index );
722                rect = lphc->RectButton;
723            }
724            else 
725            {
726                if( bButton )
727                    UnionRect16( &rect, &lphc->RectButton,
728                                        &lphc->RectEdit );
729                else
730                    rect = lphc->RectEdit;
731                bButton = TRUE;
732            }
733
734            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
735                RedrawWindow16( hWnd, &rect, 0, RDW_INVALIDATE | 
736                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
737            CB_NOTIFY( lphc, CBN_CLOSEUP );
738        }
739    }
740 }
741
742 /***********************************************************************
743  *           COMBO_FlipListbox
744  *
745  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
746  */
747 BOOL32 COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL32 bRedrawButton )
748 {
749    if( lphc->wState & CBF_DROPPED )
750    {
751        CBRollUp( lphc, TRUE, bRedrawButton );
752        return FALSE;
753    }
754
755    CBDropDown( lphc );
756    return TRUE;
757 }
758
759 /***********************************************************************
760  *           COMBO_GetLBWindow
761  *
762  * Edit control helper.
763  */
764 HWND32 COMBO_GetLBWindow( WND* pWnd )
765 {
766   LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
767   if( lphc ) return lphc->hWndLBox;
768   return 0;
769 }
770
771
772 /***********************************************************************
773  *           CBRepaintButton
774  */
775 static void CBRepaintButton( LPHEADCOMBO lphc )
776 {
777    HDC32        hDC = GetDC32( lphc->self->hwndSelf );
778
779    if( hDC )
780    {
781        CBPaintButton( lphc, (HDC16)hDC );
782        ReleaseDC32( lphc->self->hwndSelf, hDC );
783    }
784 }
785
786 /***********************************************************************
787  *           COMBO_SetFocus
788  */
789 static void COMBO_SetFocus( LPHEADCOMBO lphc )
790 {
791    if( !(lphc->wState & CBF_FOCUSED) )
792    {
793        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
794            SendMessage32A( lphc->hWndLBox, LB_CARETON32, 0, 0 );
795
796        if( lphc->wState & CBF_EDIT )
797            SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1) );
798        lphc->wState |= CBF_FOCUSED;
799        if( !(lphc->wState & CBF_EDIT) ) CBPaintText( lphc, 0 );
800
801        CB_NOTIFY( lphc, CBN_SETFOCUS );
802    }
803 }
804
805 /***********************************************************************
806  *           COMBO_KillFocus
807  */
808 static void COMBO_KillFocus( LPHEADCOMBO lphc )
809 {
810    HWND32       hWnd = lphc->self->hwndSelf;
811
812    if( lphc->wState & CBF_FOCUSED )
813    {
814        SendMessage32A( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
815
816        CBRollUp( lphc, FALSE, TRUE );
817        if( IsWindow32( hWnd ) )
818        {
819            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
820                SendMessage32A( lphc->hWndLBox, LB_CARETOFF32, 0, 0 );
821
822            lphc->wState &= ~CBF_FOCUSED;
823
824            /* redraw text */
825            if( lphc->wState & CBF_EDIT )
826                SendMessage32A( lphc->hWndEdit, EM_SETSEL32, (WPARAM32)(-1), 0 );
827            else CBPaintText( lphc, 0 );
828
829            CB_NOTIFY( lphc, CBN_KILLFOCUS );
830        }
831    }
832 }
833
834 /***********************************************************************
835  *           COMBO_Command
836  */
837 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM32 wParam, HWND32 hWnd )
838 {
839    if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
840    {
841        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
842
843        switch( HIWORD(wParam) >> 8 )
844        {   
845            case (EN_SETFOCUS >> 8):
846
847                 dprintf_combo(stddeb,"Combo [%04x]: edit [%04x] got focus\n", 
848                                      CB_HWND(lphc), (HWND16)lphc->hWndEdit );
849
850                 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
851                 break;
852
853            case (EN_KILLFOCUS >> 8):
854
855                 dprintf_combo(stddeb,"Combo [%04x]: edit [%04x] lost focus\n",
856                                       CB_HWND(lphc), (HWND16)lphc->hWndEdit );
857
858                 /* NOTE: it seems that Windows' edit control sends an
859                  * undocumented message WM_USER + 0x1B instead of this
860                  * notification (only when it happens to be a part of 
861                  * the combo). ?? - AK.
862                  */
863
864                 COMBO_KillFocus( lphc );
865                 break;
866
867
868            case (EN_CHANGE >> 8):
869                 CB_NOTIFY( lphc, CBN_EDITCHANGE );
870                 CBUpdateLBox( lphc );
871                 break;
872
873            case (EN_UPDATE >> 8):
874                 CB_NOTIFY( lphc, CBN_EDITUPDATE );
875                 break;
876
877            case (EN_ERRSPACE >> 8):
878                 CB_NOTIFY( lphc, CBN_ERRSPACE );
879        }
880    }
881    else if( lphc->hWndLBox == hWnd )
882    {
883        switch( HIWORD(wParam) )
884        {
885            case LBN_ERRSPACE:
886                 CB_NOTIFY( lphc, CBN_ERRSPACE );
887                 break;
888
889            case LBN_DBLCLK:
890                 CB_NOTIFY( lphc, CBN_DBLCLK );
891                 break;
892
893            case LBN_SELCHANGE:
894            case LBN_SELCANCEL:
895
896                 dprintf_combo(stddeb,"Combo [%04x]: lbox selection change [%04x]\n", 
897                                                       CB_HWND(lphc), lphc->wState );
898
899                 /* do not roll up if selection is being tracked 
900                  * by arrowkeys in the dropdown listbox */
901
902                 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
903                      CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
904                 else lphc->wState &= ~CBF_NOROLLUP;
905
906                 CB_NOTIFY( lphc, CBN_SELCHANGE );
907                 CBPaintText( lphc, 0 );
908                 /* fall through */
909
910            case LBN_SETFOCUS:
911            case LBN_KILLFOCUS:
912                 /* nothing to do here since ComboLBox always resets the focus to its
913                  * combo/edit counterpart */
914                  break;
915        }
916    }
917    return 0;
918 }
919
920 /***********************************************************************
921  *           COMBO_ItemOp
922  *
923  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
924  */
925 static LRESULT COMBO_ItemOp32( LPHEADCOMBO lphc, UINT32 msg, 
926                                WPARAM32 wParam, LPARAM lParam ) 
927 {
928    HWND32       hWnd = lphc->self->hwndSelf;
929
930    dprintf_combo(stddeb,"Combo [%04x]: ownerdraw op %04x\n", 
931                                        CB_HWND(lphc), (UINT16)msg );
932
933 #define lpIS    ((LPDELETEITEMSTRUCT32)lParam)
934
935    /* two first items are the same in all 4 structs */
936    lpIS->CtlType = ODT_COMBOBOX;
937    lpIS->CtlID   = lphc->self->wIDmenu;
938
939    switch( msg )        /* patch window handle */
940    {
941         case WM_DELETEITEM: 
942              lpIS->hwndItem = hWnd; 
943 #undef  lpIS
944              break;
945         case WM_DRAWITEM: 
946 #define lpIS    ((LPDRAWITEMSTRUCT32)lParam)
947              lpIS->hwndItem = hWnd; 
948 #undef  lpIS
949              break;
950         case WM_COMPAREITEM: 
951 #define lpIS    ((LPCOMPAREITEMSTRUCT32)lParam)
952              lpIS->hwndItem = hWnd; 
953 #undef  lpIS
954              break;
955    }
956
957   return SendMessage32A( lphc->owner, msg, lphc->self->wIDmenu, lParam );
958 }
959
960 /***********************************************************************
961  *           COMBO_GetText
962  */
963 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT32 N, LPSTR lpText)
964 {
965    INT32        idx;
966
967    if( lphc->wState & CBF_EDIT )
968        return SendMessage32A( lphc->hWndEdit, WM_GETTEXT, 
969                              (WPARAM32)N, (LPARAM)lpText );     
970
971    /* get it from the listbox */
972
973    idx = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 );
974    if( idx != LB_ERR )
975    {
976        LPSTR        lpBuffer;
977        INT32        length = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32,
978                                             (WPARAM32)idx, 0 );
979
980        /* 'length' is without the terminating character */
981        if( length >= N )
982            lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
983        else 
984            lpBuffer = lpText;
985
986        if( lpBuffer )
987        {
988            INT32    n = SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, 
989                                        (WPARAM32)idx, (LPARAM)lpText );
990
991            /* truncate if buffer is too short */
992
993            if( length >= N )
994            {
995                if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
996                lpText[N - 1] = '\0';
997                HeapFree( GetProcessHeap(), 0, lpBuffer );
998            }
999            return (LRESULT)n;
1000        }
1001    }
1002    return 0;
1003 }
1004
1005
1006 /***********************************************************************
1007  *           CBResetPos
1008  *
1009  * This function sets window positions according to the updated 
1010  * component placement struct.
1011  */
1012 static void CBResetPos( LPHEADCOMBO lphc, LPRECT16 lbRect, BOOL32 bRedraw )
1013 {
1014    BOOL32       bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1015
1016    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1017     * sizing messages */
1018
1019    if( lphc->wState & CBF_EDIT )
1020        SetWindowPos32( lphc->hWndEdit, 0, lphc->RectEdit.left, lphc->RectEdit.top,
1021                        lphc->RectEdit.right - lphc->RectEdit.left,
1022                        lphc->RectEdit.bottom - lphc->RectEdit.top,
1023                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1024
1025    if( bDrop )
1026        OffsetRect16( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
1027
1028    lbRect->right -= lbRect->left;       /* convert to width */
1029    lbRect->bottom -= lbRect->top;
1030    SetWindowPos32( lphc->hWndLBox, 0, lbRect->left, lbRect->top,
1031                    lbRect->right, lbRect->bottom, 
1032                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1033
1034    if( bDrop )
1035    {
1036        if( lphc->wState & CBF_DROPPED )
1037        {
1038            lphc->wState &= ~CBF_DROPPED;
1039            ShowWindow32( lphc->hWndLBox, SW_HIDE );
1040        }
1041
1042        lphc->wState |= CBF_NORESIZE;
1043        SetWindowPos32( lphc->self->hwndSelf, 0, 0, 0,
1044                        lphc->RectCombo.right - lphc->RectCombo.left,
1045                        lphc->RectEdit.bottom - lphc->RectEdit.top,
1046                        SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
1047        lphc->wState &= ~CBF_NORESIZE;
1048        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1049            RedrawWindow32( lphc->self->hwndSelf, NULL, 0,
1050                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1051    }
1052 }
1053
1054
1055 /***********************************************************************
1056  *           COMBO_Size
1057  */
1058 static void COMBO_Size( LPHEADCOMBO lphc )
1059 {
1060   RECT16        rect;
1061   INT16         w, h;
1062
1063   GetWindowRect16( lphc->self->hwndSelf, &rect );
1064   w = rect.right - rect.left; h = rect.bottom - rect.top;
1065
1066   dprintf_combo(stddeb,"COMBO_Size: w = %i, h = %i\n", w, h );
1067
1068   /* CreateWindow() may send a bogus WM_SIZE, ignore it */
1069
1070   if( w == (lphc->RectCombo.right - lphc->RectCombo.left) )
1071       if( (CB_GETTYPE(lphc) == CBS_SIMPLE) &&
1072           (h == (lphc->RectCombo.bottom - lphc->RectCombo.top)) )
1073           return;
1074       else if( (lphc->dwStyle & CBS_DROPDOWN) &&
1075                (h == (lphc->RectEdit.bottom - lphc->RectEdit.top))  )
1076                return;
1077   
1078   lphc->RectCombo = rect;
1079   CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect );
1080   CBResetPos( lphc, &rect, TRUE );
1081 }
1082
1083
1084 /***********************************************************************
1085  *           COMBO_Font
1086  */
1087 static void COMBO_Font( LPHEADCOMBO lphc, HFONT16 hFont, BOOL32 bRedraw )
1088 {
1089   RECT16        rect;
1090
1091   lphc->hFont = hFont;
1092
1093   if( lphc->wState & CBF_EDIT )
1094       SendMessage32A( lphc->hWndEdit, WM_SETFONT, (WPARAM32)hFont, bRedraw );
1095   SendMessage32A( lphc->hWndLBox, WM_SETFONT, (WPARAM32)hFont, bRedraw );
1096
1097   GetWindowRect16( lphc->self->hwndSelf, &rect );
1098   OffsetRect16( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1099                                   rect.top - lphc->RectCombo.top );
1100   CBCalcPlacement( lphc, &lphc->RectEdit,
1101                          &lphc->RectButton, &rect );
1102   CBResetPos( lphc, &rect, bRedraw );
1103 }
1104
1105
1106 /***********************************************************************
1107  *           COMBO_SetItemHeight
1108  */
1109 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT32 index, INT32 height )
1110 {
1111    LRESULT      lRet = CB_ERR;
1112
1113    if( index == -1 ) /* set text field height */
1114    {
1115        if( height < 32768 )
1116        {
1117            RECT16       rect;
1118
1119            lphc->editHeight = height;
1120            GetWindowRect16( lphc->self->hwndSelf, &rect );
1121            OffsetRect16( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1122                                            rect.top - lphc->RectCombo.top );
1123            CBCalcPlacement( lphc, &lphc->RectEdit,
1124                                   &lphc->RectButton, &rect );
1125            CBResetPos( lphc, &rect, TRUE );
1126            lRet = height;
1127        }
1128    } 
1129    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1130         lRet = SendMessage32A( lphc->hWndLBox, LB_SETITEMHEIGHT32, 
1131                               (WPARAM32)index, (LPARAM)height );
1132    return lRet;
1133 }
1134
1135 /***********************************************************************
1136  *           COMBO_SelectString
1137  */
1138 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT32 start, LPCSTR pText )
1139 {
1140    INT32 index = SendMessage32A( lphc->hWndLBox, LB_SELECTSTRING32, 
1141                                  (WPARAM32)start, (LPARAM)pText );
1142    if( index >= 0 )
1143         if( lphc->wState & CBF_EDIT )
1144             CBUpdateEdit( lphc, index );
1145         else
1146             CBPaintText( lphc, 0 );
1147    return (LRESULT)index;
1148 }
1149
1150 /***********************************************************************
1151  *           COMBO_LButtonDown
1152  */
1153 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1154 {
1155    BOOL32      bButton = PtInRect16(&lphc->RectButton, MAKEPOINT16(lParam));
1156    HWND32      hWnd = lphc->self->hwndSelf;
1157
1158    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1159        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1160    {
1161        lphc->wState |= CBF_BUTTONDOWN;
1162        if( lphc->wState & CBF_DROPPED )
1163        {
1164            /* got a click to cancel selection */
1165
1166            CBRollUp( lphc, TRUE, FALSE );
1167            if( !IsWindow32( hWnd ) ) return;
1168
1169            if( lphc->wState & CBF_CAPTURE )
1170            {
1171                lphc->wState &= ~CBF_CAPTURE;
1172                ReleaseCapture();
1173            }
1174            lphc->wState &= ~CBF_BUTTONDOWN;
1175        }
1176        else
1177        {
1178            /* drop down the listbox and start tracking */
1179
1180            lphc->wState |= CBF_CAPTURE;
1181            CBDropDown( lphc );
1182            SetCapture32( hWnd );
1183        }
1184        if( bButton ) CBRepaintButton( lphc );
1185    }
1186 }
1187
1188 /***********************************************************************
1189  *           COMBO_LButtonUp
1190  *
1191  * Release capture and stop tracking if needed.
1192  */
1193 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1194 {
1195    if( lphc->wState & CBF_CAPTURE )
1196    {
1197        lphc->wState &= ~CBF_CAPTURE;
1198        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1199        {
1200            INT32 index = CBUpdateLBox( lphc );
1201            CBUpdateEdit( lphc, index );
1202        }
1203        ReleaseCapture();
1204    }
1205
1206    if( lphc->wState & CBF_BUTTONDOWN )
1207    {
1208        lphc->wState &= ~CBF_BUTTONDOWN;
1209        CBRepaintButton( lphc );
1210    }
1211 }
1212
1213 /***********************************************************************
1214  *           COMBO_MouseMove
1215  *
1216  * Two things to do - track combo button and release capture when
1217  * pointer goes into the listbox.
1218  */
1219 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM32 wParam, LPARAM lParam )
1220 {
1221    RECT16       lbRect;
1222
1223    if( lphc->wState & CBF_BUTTONDOWN )
1224    {
1225        BOOL32   bButton = PtInRect16(&lphc->RectButton, MAKEPOINT16(lParam));
1226
1227        if( !bButton )
1228        {
1229            lphc->wState &= ~CBF_BUTTONDOWN;
1230            CBRepaintButton( lphc );
1231        }
1232    }
1233
1234    GetClientRect16( lphc->hWndLBox, &lbRect );
1235    MapWindowPoints16( lphc->self->hwndSelf, 
1236                       lphc->hWndLBox, (LPPOINT16)&lParam, 1 );
1237    if( PtInRect16(&lbRect, MAKEPOINT16(lParam)) )
1238    {
1239        lphc->wState &= ~CBF_CAPTURE;
1240        ReleaseCapture();
1241        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1242
1243        /* hand over pointer tracking */
1244        SendMessage32A( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1245    }
1246 }
1247
1248
1249 /***********************************************************************
1250  *           ComboWndProc
1251  *
1252  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1253  */
1254 LRESULT WINAPI ComboWndProc( HWND32 hwnd, UINT32 message,
1255                              WPARAM32 wParam, LPARAM lParam )
1256 {
1257     WND*        pWnd = WIN_FindWndPtr(hwnd);
1258    
1259     if( pWnd )
1260     {
1261       LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
1262
1263       dprintf_combo( stddeb, "Combo [%04x]: msg %s wp %08x lp %08lx\n",
1264                      pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1265
1266       if( lphc || message == WM_NCCREATE )
1267       switch(message) 
1268       { 
1269
1270         /* System messages */
1271
1272         case WM_NCCREATE: 
1273                 return COMBO_NCCreate(pWnd, lParam);
1274
1275         case WM_NCDESTROY: 
1276                 COMBO_NCDestroy(lphc);
1277                 break;
1278
1279         case WM_CREATE: 
1280                 return COMBO_Create(lphc, pWnd, lParam);
1281
1282         case WM_PAINT:
1283                 /* wParam may contain a valid HDC! */
1284                 return COMBO_Paint(lphc, (HDC16)wParam);
1285
1286         case WM_ERASEBKGND:
1287                 return TRUE;
1288
1289         case WM_GETDLGCODE: 
1290                 return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1291
1292         case WM_SIZE:
1293                 if( lphc->hWndLBox && 
1294                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1295                 return TRUE;
1296
1297         case WM_SETFONT:
1298                 COMBO_Font( lphc, (HFONT16)wParam, (BOOL32)lParam );
1299                 return TRUE;
1300
1301         case WM_GETFONT:
1302                 return (LRESULT)lphc->hFont;
1303
1304         case WM_SETFOCUS:
1305                 if( lphc->wState & CBF_EDIT )
1306                     SetFocus32( lphc->hWndEdit );
1307                 else
1308                     COMBO_SetFocus( lphc );
1309                 return TRUE;
1310
1311         case WM_KILLFOCUS:
1312 #define hwndFocus ((HWND16)wParam)
1313                 if( !hwndFocus ||
1314                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1315                     COMBO_KillFocus( lphc );
1316 #undef hwndFocus
1317                 return TRUE;
1318
1319         case WM_COMMAND:
1320                 return COMBO_Command( lphc, wParam, (HWND32)lParam );
1321
1322         case WM_GETTEXT:
1323                 return COMBO_GetText( lphc, (UINT32)wParam, (LPSTR)lParam );
1324
1325         case WM_SETTEXT:
1326         case WM_GETTEXTLENGTH:
1327         case WM_CLEAR:
1328         case WM_CUT:
1329         case WM_PASTE:
1330         case WM_COPY:
1331                 if( lphc->wState & CBF_EDIT )
1332                     return SendMessage32A( lphc->hWndEdit, message, wParam, lParam );
1333                 return CB_ERR;
1334
1335         case WM_DRAWITEM:
1336         case WM_DELETEITEM:
1337         case WM_COMPAREITEM:
1338         case WM_MEASUREITEM:
1339                 return COMBO_ItemOp32( lphc, message, wParam, lParam );
1340
1341         case WM_ENABLE:
1342                 if( lphc->wState & CBF_EDIT )
1343                     EnableWindow32( lphc->hWndEdit, (BOOL32)wParam ); 
1344                 EnableWindow32( lphc->hWndLBox, (BOOL32)wParam );
1345                 return TRUE;
1346
1347         case WM_SETREDRAW:
1348                 if( wParam )
1349                     lphc->wState &= ~CBF_NOREDRAW;
1350                 else
1351                     lphc->wState |= CBF_NOREDRAW;
1352
1353                 if( lphc->wState & CBF_EDIT )
1354                     SendMessage32A( lphc->hWndEdit, message, wParam, lParam );
1355                 SendMessage32A( lphc->hWndLBox, message, wParam, lParam );
1356                 break;
1357                 
1358         case WM_SYSKEYDOWN:
1359                 if( KEYDATA_ALT & HIWORD(lParam) )
1360                     if( wParam == VK_UP || wParam == VK_DOWN )
1361                         COMBO_FlipListbox( lphc, TRUE );
1362                 break;
1363
1364         case WM_CHAR:
1365         case WM_KEYDOWN:
1366                 if( lphc->wState & CBF_EDIT )
1367                     return SendMessage32A( lphc->hWndEdit, message, wParam, lParam );
1368                 else
1369                     return SendMessage32A( lphc->hWndLBox, message, wParam, lParam );
1370
1371         case WM_LBUTTONDOWN: 
1372                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus32( lphc->self->hwndSelf );
1373                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1374                 return TRUE;
1375
1376         case WM_LBUTTONUP:
1377                 COMBO_LButtonUp( lphc, lParam );
1378                 return TRUE;
1379
1380         case WM_MOUSEMOVE: 
1381                 if( lphc->wState & CBF_CAPTURE ) 
1382                     COMBO_MouseMove( lphc, wParam, lParam );
1383                 return TRUE;
1384
1385         /* Combo messages */
1386
1387         case CB_ADDSTRING16:
1388                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1389         case CB_ADDSTRING32:
1390                 return SendMessage32A( lphc->hWndLBox, LB_ADDSTRING32, 0, lParam);
1391
1392         case CB_INSERTSTRING16:
1393                 wParam = (INT32)(INT16)wParam;
1394                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1395         case CB_INSERTSTRING32:
1396                 return SendMessage32A( lphc->hWndLBox, LB_INSERTSTRING32, wParam, lParam);
1397
1398         case CB_DELETESTRING16:
1399         case CB_DELETESTRING32:
1400                 return SendMessage32A( lphc->hWndLBox, LB_DELETESTRING32, wParam, 0);
1401
1402         case CB_SELECTSTRING16:
1403                 wParam = (INT32)(INT16)wParam;
1404                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1405         case CB_SELECTSTRING32:
1406                 return COMBO_SelectString( lphc, (INT32)wParam, (LPSTR)lParam );
1407
1408         case CB_FINDSTRING16:
1409                 wParam = (INT32)(INT16)wParam;
1410                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1411         case CB_FINDSTRING32:
1412                 return SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32, wParam, lParam);
1413
1414         case CB_FINDSTRINGEXACT16:
1415                 wParam = (INT32)(INT16)wParam;
1416                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1417         case CB_FINDSTRINGEXACT32:
1418                 return SendMessage32A( lphc->hWndLBox, LB_FINDSTRINGEXACT32, 
1419                                                        wParam, lParam );
1420         case CB_SETITEMHEIGHT16:
1421                 wParam = (INT32)(INT16)wParam;
1422         case CB_SETITEMHEIGHT32:
1423                 return COMBO_SetItemHeight( lphc, (INT32)wParam, (INT32)lParam);
1424
1425         case CB_RESETCONTENT16: 
1426         case CB_RESETCONTENT32:
1427                 SendMessage32A( lphc->hWndLBox, LB_RESETCONTENT32, 0, 0 );
1428                 CBPaintText( lphc, 0 );
1429                 return TRUE;
1430
1431         case CB_INITSTORAGE32:
1432                 return SendMessage32A( lphc->hWndLBox, LB_INITSTORAGE32, wParam, lParam);
1433
1434         case CB_GETHORIZONTALEXTENT32:
1435                 return SendMessage32A( lphc->hWndLBox, LB_GETHORIZONTALEXTENT32, 0, 0);
1436
1437         case CB_SETHORIZONTALEXTENT32:
1438                 return SendMessage32A( lphc->hWndLBox, LB_SETHORIZONTALEXTENT32, wParam, 0);
1439
1440         case CB_GETTOPINDEX32:
1441                 return SendMessage32A( lphc->hWndLBox, LB_GETTOPINDEX32, 0, 0);
1442
1443         case CB_GETLOCALE32:
1444                 return SendMessage32A( lphc->hWndLBox, LB_GETLOCALE32, 0, 0);
1445
1446         case CB_SETLOCALE32:
1447                 return SendMessage32A( lphc->hWndLBox, LB_SETLOCALE32, wParam, 0);
1448
1449         case CB_GETDROPPEDWIDTH32:
1450                 if( lphc->droppedWidth )
1451                     return lphc->droppedWidth;
1452                 return lphc->RectCombo.right - lphc->RectCombo.left - 
1453                            (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
1454
1455         case CB_SETDROPPEDWIDTH32:
1456                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1457                     (INT32)wParam < 32768 ) lphc->droppedWidth = (INT32)wParam;
1458                 return CB_ERR;
1459
1460         case CB_GETDROPPEDCONTROLRECT16:
1461                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1462                 if( lParam ) 
1463                 {
1464                     RECT32      r;
1465                     CBGetDroppedControlRect32( lphc, &r );
1466                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
1467                 }
1468                 return CB_OKAY;
1469
1470         case CB_GETDROPPEDCONTROLRECT32:
1471                 if( lParam ) CBGetDroppedControlRect32(lphc, (LPRECT32)lParam );
1472                 return CB_OKAY;
1473
1474         case CB_GETDROPPEDSTATE16:
1475         case CB_GETDROPPEDSTATE32:
1476                 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1477
1478         case CB_DIR16: 
1479                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1480                 /* fall through */
1481         case CB_DIR32:
1482                 return COMBO_Directory( lphc, (UINT32)wParam, 
1483                                        (LPSTR)lParam, (message == CB_DIR32));
1484         case CB_SHOWDROPDOWN16:
1485         case CB_SHOWDROPDOWN32:
1486                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1487                     if( wParam )
1488                     {
1489                         if( !(lphc->wState & CBF_DROPPED) )
1490                             CBDropDown( lphc );
1491                     }
1492                     else 
1493                         if( lphc->wState & CBF_DROPPED ) 
1494                             CBRollUp( lphc, FALSE, TRUE );
1495                 return TRUE;
1496
1497         case CB_GETCOUNT16: 
1498         case CB_GETCOUNT32:
1499                 return SendMessage32A( lphc->hWndLBox, LB_GETCOUNT32, 0, 0);
1500
1501         case CB_GETCURSEL16: 
1502         case CB_GETCURSEL32:
1503                 return SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0);
1504
1505         case CB_SETCURSEL16:
1506                 wParam = (INT32)(INT16)wParam;
1507         case CB_SETCURSEL32:
1508                 return SendMessage32A( lphc->hWndLBox, LB_SETCURSEL32, wParam, 0);
1509
1510         case CB_GETLBTEXT16: 
1511                 wParam = (INT32)(INT16)wParam;
1512                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1513         case CB_GETLBTEXT32:
1514                 return SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, wParam, lParam);
1515
1516         case CB_GETLBTEXTLEN16: 
1517                 wParam = (INT32)(INT16)wParam;
1518         case CB_GETLBTEXTLEN32:
1519                 return SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, wParam, 0);
1520
1521         case CB_GETITEMDATA16:
1522                 wParam = (INT32)(INT16)wParam;
1523         case CB_GETITEMDATA32:
1524                 return SendMessage32A( lphc->hWndLBox, LB_GETITEMDATA32, wParam, 0);
1525
1526         case CB_SETITEMDATA16:
1527                 wParam = (INT32)(INT16)wParam;
1528         case CB_SETITEMDATA32:
1529                 return SendMessage32A( lphc->hWndLBox, LB_SETITEMDATA32, wParam, lParam);
1530
1531         case CB_GETEDITSEL16: 
1532                 wParam = lParam = 0;   /* just in case */
1533         case CB_GETEDITSEL32:
1534                 if( lphc->wState & CBF_EDIT )
1535                 {
1536                     INT32       a, b;
1537
1538                     return SendMessage32A( lphc->hWndEdit, EM_GETSEL32,
1539                                            (wParam) ? wParam : (WPARAM32)&a,
1540                                            (lParam) ? lParam : (LPARAM)&b );
1541                 }
1542                 return CB_ERR;
1543
1544         case CB_SETEDITSEL16: 
1545         case CB_SETEDITSEL32:
1546                 if( lphc->wState & CBF_EDIT ) 
1547                     return SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 
1548                           (INT32)(INT16)LOWORD(lParam), (INT32)(INT16)HIWORD(lParam) );
1549                 return CB_ERR;
1550
1551         case CB_SETEXTENDEDUI16:
1552         case CB_SETEXTENDEDUI32:
1553                 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) return CB_ERR;
1554
1555                 if( wParam )
1556                     lphc->wState |= CBF_EUI;
1557                 else lphc->wState &= ~CBF_EUI;
1558                 return CB_OKAY;
1559
1560         case CB_GETEXTENDEDUI16:
1561         case CB_GETEXTENDEDUI32:
1562                 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
1563
1564         case (WM_USER + 0x1B):
1565                 dprintf_combo(stddeb,"Combo [%04x]: undocumented msg!\n", (HWND16)hwnd );
1566     }
1567     return DefWindowProc32A(hwnd, message, wParam, lParam);
1568   }
1569   return CB_ERR;
1570 }
1571