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