COMBO_Size: maintain the height of the RectCombo when the window is
[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         (SendMessageA( (lphc)->owner, WM_COMMAND, \
33                          MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
34 #define CB_GETEDITTEXTLENGTH( lphc ) \
35         (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
36
37 static HBITMAP  hComboBmp = 0;
38 static UINT             CBitHeight, CBitWidth;
39 static UINT             CBitOffset = 8;
40
41 /***********************************************************************
42  *           COMBO_Init
43  *
44  * Load combo button bitmap.
45  */
46 static BOOL COMBO_Init()
47 {
48   HDC           hDC;
49   
50   if( hComboBmp ) return TRUE;
51   if( (hDC = CreateCompatibleDC(0)) )
52   {
53     BOOL        bRet = FALSE;
54     if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
55     {
56       BITMAP      bm;
57       HBITMAP     hPrevB;
58       RECT        r;
59
60       GetObjectA( 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       SetRect( &r, 0, 0, CBitWidth, CBitHeight );
68       InvertRect( hDC, &r );
69       SelectObject( hDC, hPrevB );
70       bRet = TRUE;
71     }
72     DeleteDC( 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         LPCREATESTRUCTA     lpcs = (CREATESTRUCTA*)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                      (UINT)lphc, lphc->dwStyle );
105
106         return (LRESULT)(UINT)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            DestroyWindow( 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, LPSIZE lpSize )
136 {
137    if( lphc->editHeight ) /* explicitly set height */
138        lpSize->cy = lphc->editHeight;
139    else
140    {
141        HDC    hDC = GetDC( lphc->self->hwndSelf );
142        HFONT  hPrevFont = 0;
143
144        if( lphc->hFont ) hPrevFont = SelectObject( hDC, lphc->hFont );
145
146        GetTextExtentPoint32A( hDC, "0", 1, lpSize);
147
148        lpSize->cy += lpSize->cy / 4 + 4 * SYSMETRICS_CYBORDER;
149
150        if( hPrevFont ) SelectObject( hDC, hPrevFont );
151        ReleaseDC( 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, LPRECT lprEdit, 
163                              LPRECT lprButton, LPRECT lprLB )
164 {
165    RECT rect = lphc->RectCombo;
166    SIZE size;
167
168    /* get combo height and width */
169
170    if( CB_OWNERDRAWN(lphc) )
171    {
172        UINT     u = lphc->RectEdit.bottom - lphc->RectEdit.top;
173
174        if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
175        {
176            MEASUREITEMSTRUCT        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            SendMessageA(lphc->owner, WM_MEASUREITEM, 
191                                 (WPARAM)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        INT      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 CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT 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   LPCREATESTRUCTA  lpcs = (CREATESTRUCTA*)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       UINT      lbeStyle;
284       RECT      editRect, btnRect, lbRect;
285
286       GetWindowRect( 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         OffsetRect( &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 = CreateWindowExA( 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)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
326                         lphc->self->hInstance, (LPVOID)lphc );
327       if( lphc->hWndLBox )
328       {
329           BOOL  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 = CreateWindowExA( 0, editName, NULL, lbeStyle,
342                         editRect.left, editRect.top, editRect.right - editRect.left,
343                         editRect.bottom - editRect.top, lphc->self->hwndSelf, 
344                         (HMENU)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                 SetWindowPos( 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, HDC hdc)
378 {
379     RECT        r;
380     UINT        x, y;
381     BOOL        bBool;
382     HDC       hMemDC;
383     HBRUSH    hPrevBrush;
384     COLORREF    oldTextColor, oldBkColor;
385
386     if( lphc->wState & CBF_NOREDRAW ) return;
387
388     hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
389     CONV_RECT16TO32( &lphc->RectButton, &r );
390
391     Rectangle(hdc, r.left, r.top, r.right, r.bottom );
392     if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
393     {
394         DrawEdge( hdc, &r, EDGE_SUNKEN, BF_RECT );
395         OffsetRect( &r, 1, 1 );
396     } else {
397         r.top++, r.left++;
398         DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
399         r.top--, r.left--;
400     }
401
402     InflateRect( &r, -1, -1 );  
403
404     x = (r.left + r.right - CBitWidth) >> 1;
405     y = (r.top + r.bottom - CBitHeight) >> 1;
406
407     InflateRect( &r, -3, -3 );
408
409     hMemDC = CreateCompatibleDC( hdc );
410     SelectObject( hMemDC, hComboBmp );
411     oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
412     oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
413                                RGB(0,0,0) );
414     BitBlt( hdc, x, y, 8, 8, hMemDC, 0, 0, SRCCOPY );
415     SetBkColor( hdc, oldBkColor );
416     SetTextColor( hdc, oldTextColor );
417     DeleteDC( hMemDC );
418     SelectObject( 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, HDC hdc)
427 {
428    INT  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 = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
437    {
438         size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
439         if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
440         {
441             SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)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) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
449         if( lphc->wState & CBF_FOCUSED ) 
450             SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
451    }
452    else /* paint text field ourselves */
453    {
454         HBRUSH hPrevBrush = 0;
455         HDC      hDC = hdc;
456
457         if( !hDC ) 
458         {
459             if ((hDC = GetDC(lphc->self->hwndSelf)))
460             {
461                 HBRUSH hBrush = SendMessageA( lphc->owner,
462                                                   WM_CTLCOLORLISTBOX, 
463                                                   hDC, lphc->self->hwndSelf );
464                 hPrevBrush = SelectObject( hDC, 
465                            (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
466             }
467         }
468         if( hDC )
469         {
470             RECT        rect;
471             UINT        itemState;
472             HFONT       hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
473
474             PatBlt( 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             InflateRect( &rect, -1, -1 );
479
480             if( lphc->wState & CBF_FOCUSED && 
481                 !(lphc->wState & CBF_DROPPED) )
482             {
483                 /* highlight */
484
485                 FillRect( hDC, &rect, GetSysColorBrush(COLOR_HIGHLIGHT) );
486                 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
487                 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
488                 itemState = ODS_SELECTED | ODS_FOCUS;
489             } else itemState = 0;
490
491             if( CB_OWNERDRAWN(lphc) )
492             {
493                 DRAWITEMSTRUCT 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    = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, 
506                                                                   (WPARAM)id, 0 );
507                 SendMessageA( lphc->owner, WM_DRAWITEM, 
508                                 lphc->self->wIDmenu, (LPARAM)&dis );
509             }
510             else
511             {
512                 ExtTextOutA( 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                     DrawFocusRect( hDC, &rect );
517             }
518
519             if( hPrevFont ) SelectObject(hDC, hPrevFont );
520             if( !hdc ) 
521             {
522                 if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
523                 ReleaseDC( 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, HDC hParamDC)
535 {
536   PAINTSTRUCT ps;
537   HDC   hDC;
538   
539   hDC = (hParamDC) ? hParamDC
540                    : BeginPaint( lphc->self->hwndSelf, &ps);
541   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
542   {
543       HBRUSH    hPrevBrush, hBkgBrush;
544
545       hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
546                                   hDC, lphc->self->hwndSelf );
547       if( !hBkgBrush ) hBkgBrush = GetStockObject(WHITE_BRUSH);
548
549       hPrevBrush = SelectObject( hDC, hBkgBrush );
550       if( !IsRectEmpty(&lphc->RectButton) )
551       {
552           /* paint everything to the right of the text field */
553
554           PatBlt( 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           HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(
565                                                           COLOR_WINDOWFRAME) );
566
567           Rectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
568                        lphc->RectEdit.right, lphc->RectButton.bottom );
569           SelectObject( hDC, hPrevPen );
570           CBPaintText( lphc, hDC );
571       }
572       if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
573   }
574   if( !hParamDC ) EndPaint(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 INT CBUpdateLBox( LPHEADCOMBO lphc )
584 {
585    INT  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 ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
599        else pText[0] = '\0';
600        idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, 
601                              (WPARAM)(-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    SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
610    
611    if( idx >= 0 )
612    {
613        SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
614        /* probably superfluous but Windows sends this too */
615        SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)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 , INT index )
626 {
627    INT  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                 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
640                 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
641                                         (WPARAM)(-1), (LPARAM)pText );
642                 HeapFree( GetProcessHeap(), 0, pText );
643            }
644        }
645    }
646
647    if( index >= 0 ) /* got an entry */
648    {
649        length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
650        if( length )
651        {
652            if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
653            {
654                 SendMessageA( lphc->hWndLBox, LB_GETTEXT, 
655                                 (WPARAM)index, (LPARAM)pText );
656                 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
657                 SendMessageA( lphc->hWndEdit, EM_SETSEL, 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    INT  index;
672    RECT rect;
673    LPRECT       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 = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
690        if( index == LB_ERR ) index = 0;
691        SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
692        SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
693        pRect = &lphc->RectEdit;
694    }
695
696    /* now set popup position */
697
698    GetWindowRect( 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    SetWindowPos( 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            RedrawWindow( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE | 
713                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
714    ShowWindow( lphc->hWndLBox, SW_SHOWNA );
715 }
716
717 /***********************************************************************
718  *           CBRollUp
719  *
720  * Hide listbox popup.
721  */
722 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
723 {
724    HWND hWnd = lphc->self->hwndSelf;
725
726    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
727
728    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
729    {
730
731        TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
732
733        /* always send WM_LBUTTONUP? */
734        SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
735
736        if( lphc->wState & CBF_DROPPED ) 
737        {
738            RECT rect;
739
740            lphc->wState &= ~CBF_DROPPED;
741            ShowWindow( lphc->hWndLBox, SW_HIDE );
742
743            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
744            {
745                INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
746                CBUpdateEdit( lphc, index );
747                rect = lphc->RectButton;
748            }
749            else 
750            {
751                if( bButton )
752                    UnionRect( &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                RedrawWindow( 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 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL 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 HWND 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    HDC        hDC = GetDC( lphc->self->hwndSelf );
803
804    if( hDC )
805    {
806        CBPaintButton( lphc, hDC );
807        ReleaseDC( 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            SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
820
821        if( lphc->wState & CBF_EDIT )
822            SendMessageA( lphc->hWndEdit, EM_SETSEL, 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    HWND hWnd = lphc->self->hwndSelf;
836
837    if( lphc->wState & CBF_FOCUSED )
838    {
839        SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
840
841        CBRollUp( lphc, FALSE, TRUE );
842        if( IsWindow( hWnd ) )
843        {
844            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
845                SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
846
847            lphc->wState &= ~CBF_FOCUSED;
848
849            /* redraw text */
850            if( lphc->wState & CBF_EDIT )
851                SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-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, WPARAM wParam, HWND 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_ItemOp( LPHEADCOMBO lphc, UINT msg, 
951                                WPARAM wParam, LPARAM lParam ) 
952 {
953    HWND hWnd = lphc->self->hwndSelf;
954
955    TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
956
957 #define lpIS    ((LPDELETEITEMSTRUCT)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    ((LPDRAWITEMSTRUCT)lParam)
971              lpIS->hwndItem = hWnd; 
972 #undef  lpIS
973              break;
974         case WM_COMPAREITEM: 
975 #define lpIS    ((LPCOMPAREITEMSTRUCT)lParam)
976              lpIS->hwndItem = hWnd; 
977 #undef  lpIS
978              break;
979    }
980
981    return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
982 }
983
984 /***********************************************************************
985  *           COMBO_GetText
986  */
987 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
988 {
989    if( lphc->wState & CBF_EDIT )
990        return SendMessageA( lphc->hWndEdit, WM_GETTEXT, 
991                              (WPARAM)N, (LPARAM)lpText );     
992
993    /* get it from the listbox */
994
995    if( lphc->hWndLBox )
996    {
997        INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
998        if( idx != LB_ERR )
999        {
1000            LPSTR        lpBuffer;
1001            INT  length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1002                                                 (WPARAM)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                INT    n = SendMessageA( lphc->hWndLBox, LB_GETTEXT, 
1013                                            (WPARAM)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, LPRECT lbRect, BOOL bRedraw )
1040 {
1041    BOOL 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        SetWindowPos( 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        OffsetRect( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
1054
1055    lbRect->right -= lbRect->left;       /* convert to width */
1056    lbRect->bottom -= lbRect->top;
1057    SetWindowPos( 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            ShowWindow( lphc->hWndLBox, SW_HIDE );
1067        }
1068
1069        lphc->wState |= CBF_NORESIZE;
1070        SetWindowPos( 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            RedrawWindow( 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   RECT  rect;
1089   INT           w, h;
1090
1091   GetWindowRect( 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
1108   //Maintain the RectCombo height
1109   h = lphc->RectCombo.bottom - lphc->RectCombo.top;
1110
1111   lphc->RectCombo = rect;
1112   lphc->RectCombo.bottom = lphc->RectCombo.top + h;
1113
1114   CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect );
1115   CBResetPos( lphc, &rect, TRUE );
1116 }
1117
1118
1119 /***********************************************************************
1120  *           COMBO_Font
1121  */
1122 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1123 {
1124   RECT        rect;
1125
1126   lphc->hFont = hFont;
1127
1128   if( lphc->wState & CBF_EDIT )
1129       SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1130   SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1131
1132   GetWindowRect( lphc->self->hwndSelf, &rect );
1133   OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1134                                   rect.top - lphc->RectCombo.top );
1135   CBCalcPlacement( lphc, &lphc->RectEdit,
1136                          &lphc->RectButton, &rect );
1137   CBResetPos( lphc, &rect, bRedraw );
1138 }
1139
1140
1141 /***********************************************************************
1142  *           COMBO_SetItemHeight
1143  */
1144 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1145 {
1146    LRESULT      lRet = CB_ERR;
1147
1148    if( index == -1 ) /* set text field height */
1149    {
1150        if( height < 32768 )
1151        {
1152            RECT rect;
1153
1154            lphc->editHeight = height;
1155            GetWindowRect( lphc->self->hwndSelf, &rect );
1156            OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1157                                            rect.top - lphc->RectCombo.top );
1158            CBCalcPlacement( lphc, &lphc->RectEdit,
1159                                   &lphc->RectButton, &rect );
1160            CBResetPos( lphc, &rect, TRUE );
1161            lRet = height;
1162        }
1163    } 
1164    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1165         lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT, 
1166                               (WPARAM)index, (LPARAM)height );
1167    return lRet;
1168 }
1169
1170 /***********************************************************************
1171  *           COMBO_SelectString
1172  */
1173 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1174 {
1175    INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING, 
1176                                  (WPARAM)start, (LPARAM)pText );
1177    if( index >= 0 )
1178    {
1179         if( lphc->wState & CBF_EDIT )
1180             CBUpdateEdit( lphc, index );
1181         else
1182             CBPaintText( lphc, 0 );
1183    }
1184    return (LRESULT)index;
1185 }
1186
1187 /***********************************************************************
1188  *           COMBO_LButtonDown
1189  */
1190 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1191 {
1192    POINT     pt = { LOWORD(lParam), HIWORD(lParam) };
1193    BOOL      bButton = PtInRect(&lphc->RectButton, pt);
1194    HWND      hWnd = lphc->self->hwndSelf;
1195
1196    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1197        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1198    {
1199        lphc->wState |= CBF_BUTTONDOWN;
1200        if( lphc->wState & CBF_DROPPED )
1201        {
1202            /* got a click to cancel selection */
1203
1204            lphc->wState &= ~CBF_BUTTONDOWN;
1205            CBRollUp( lphc, TRUE, FALSE );
1206            if( !IsWindow( hWnd ) ) return;
1207
1208            if( lphc->wState & CBF_CAPTURE )
1209            {
1210                lphc->wState &= ~CBF_CAPTURE;
1211                ReleaseCapture();
1212            }
1213        }
1214        else
1215        {
1216            /* drop down the listbox and start tracking */
1217
1218            lphc->wState |= CBF_CAPTURE;
1219            CBDropDown( lphc );
1220            SetCapture( hWnd );
1221        }
1222        if( bButton ) CBRepaintButton( lphc );
1223    }
1224 }
1225
1226 /***********************************************************************
1227  *           COMBO_LButtonUp
1228  *
1229  * Release capture and stop tracking if needed.
1230  */
1231 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1232 {
1233    if( lphc->wState & CBF_CAPTURE )
1234    {
1235        lphc->wState &= ~CBF_CAPTURE;
1236        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1237        {
1238            INT index = CBUpdateLBox( lphc );
1239            CBUpdateEdit( lphc, index );
1240        }
1241        ReleaseCapture();
1242    }
1243
1244    if( lphc->wState & CBF_BUTTONDOWN )
1245    {
1246        lphc->wState &= ~CBF_BUTTONDOWN;
1247        CBRepaintButton( lphc );
1248    }
1249 }
1250
1251 /***********************************************************************
1252  *           COMBO_MouseMove
1253  *
1254  * Two things to do - track combo button and release capture when
1255  * pointer goes into the listbox.
1256  */
1257 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1258 {
1259    POINT  pt = { LOWORD(lParam), HIWORD(lParam) };
1260    RECT   lbRect;
1261
1262    if( lphc->wState & CBF_BUTTONDOWN )
1263    {
1264        BOOL     bButton = PtInRect(&lphc->RectButton, pt);
1265
1266        if( !bButton )
1267        {
1268            lphc->wState &= ~CBF_BUTTONDOWN;
1269            CBRepaintButton( lphc );
1270        }
1271    }
1272
1273    GetClientRect( lphc->hWndLBox, &lbRect );
1274    MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1275    if( PtInRect(&lbRect, pt) )
1276    {
1277        lphc->wState &= ~CBF_CAPTURE;
1278        ReleaseCapture();
1279        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1280
1281        /* hand over pointer tracking */
1282        SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1283    }
1284 }
1285
1286
1287 /***********************************************************************
1288  *           ComboWndProc
1289  *
1290  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1291  */
1292 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
1293                              WPARAM wParam, LPARAM lParam )
1294 {
1295     LRESULT retvalue;
1296     WND*        pWnd = WIN_FindWndPtr(hwnd);
1297    
1298     if( pWnd )
1299     {
1300       LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
1301
1302       TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
1303                    pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1304
1305       if( lphc || message == WM_NCCREATE )
1306       switch(message) 
1307       { 
1308
1309         /* System messages */
1310
1311         case WM_NCCREATE: 
1312                 retvalue = COMBO_NCCreate(pWnd, lParam);
1313                 goto END;
1314         case WM_NCDESTROY: 
1315                 COMBO_NCDestroy(lphc);
1316                 break;
1317
1318         case WM_CREATE: 
1319                 retvalue = COMBO_Create(lphc, pWnd, lParam);
1320                 goto END;
1321
1322         case WM_PAINT:
1323                 /* wParam may contain a valid HDC! */
1324                 retvalue = COMBO_Paint(lphc, wParam);
1325                 goto END;
1326         case WM_ERASEBKGND:
1327                 retvalue = TRUE;
1328                 goto END;
1329         case WM_GETDLGCODE: 
1330                 retvalue = (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1331                 goto END;
1332         case WM_SIZE:
1333                 if( lphc->hWndLBox && 
1334                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1335                 retvalue = TRUE;
1336                 goto END;
1337         case WM_SETFONT:
1338                 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1339                 retvalue = TRUE;
1340                 goto END;
1341         case WM_GETFONT:
1342                 retvalue = (LRESULT)lphc->hFont;
1343                 goto END;
1344         case WM_SETFOCUS:
1345                 if( lphc->wState & CBF_EDIT )
1346                     SetFocus( lphc->hWndEdit );
1347                 else
1348                     COMBO_SetFocus( lphc );
1349                 retvalue = TRUE;
1350                 goto END;
1351         case WM_KILLFOCUS:
1352 #define hwndFocus ((HWND16)wParam)
1353                 if( !hwndFocus ||
1354                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1355                     COMBO_KillFocus( lphc );
1356 #undef hwndFocus
1357                 retvalue = TRUE;
1358                 goto END;
1359         case WM_COMMAND:
1360                 retvalue = COMBO_Command( lphc, wParam, (HWND)lParam );
1361                 goto END;
1362         case WM_GETTEXT:
1363                 retvalue = COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1364                 goto END;
1365         case WM_SETTEXT:
1366         case WM_GETTEXTLENGTH:
1367         case WM_CLEAR:
1368         case WM_CUT:
1369         case WM_PASTE:
1370         case WM_COPY:
1371                 if( lphc->wState & CBF_EDIT )
1372                 {
1373                     retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1374                     goto END;
1375                 }
1376                 retvalue = CB_ERR;
1377                 goto END;
1378         case WM_DRAWITEM:
1379         case WM_DELETEITEM:
1380         case WM_COMPAREITEM:
1381         case WM_MEASUREITEM:
1382                 retvalue = COMBO_ItemOp( lphc, message, wParam, lParam );
1383                 goto END;
1384         case WM_ENABLE:
1385                 if( lphc->wState & CBF_EDIT )
1386                     EnableWindow( lphc->hWndEdit, (BOOL)wParam ); 
1387                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1388                 retvalue = TRUE;
1389                 goto END;
1390         case WM_SETREDRAW:
1391                 if( wParam )
1392                     lphc->wState &= ~CBF_NOREDRAW;
1393                 else
1394                     lphc->wState |= CBF_NOREDRAW;
1395
1396                 if( lphc->wState & CBF_EDIT )
1397                     SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1398                 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1399                 retvalue = 0;
1400                 goto END;
1401         case WM_SYSKEYDOWN:
1402                 if( KEYDATA_ALT & HIWORD(lParam) )
1403                     if( wParam == VK_UP || wParam == VK_DOWN )
1404                         COMBO_FlipListbox( lphc, TRUE );
1405                 break;
1406
1407         case WM_CHAR:
1408         case WM_KEYDOWN:
1409                 if( lphc->wState & CBF_EDIT )
1410                     retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1411                 else
1412                     retvalue = SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1413
1414         case WM_LBUTTONDOWN: 
1415                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1416                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1417                 retvalue = TRUE;
1418                 goto END;
1419         case WM_LBUTTONUP:
1420                 COMBO_LButtonUp( lphc, lParam );
1421                 retvalue = TRUE;
1422                 goto END;
1423         case WM_MOUSEMOVE: 
1424                 if( lphc->wState & CBF_CAPTURE ) 
1425                     COMBO_MouseMove( lphc, wParam, lParam );
1426                 retvalue = TRUE;
1427                 goto END;
1428         /* Combo messages */
1429
1430         case CB_ADDSTRING16:
1431                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1432         case CB_ADDSTRING:
1433                 retvalue = SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1434                 goto END;
1435         case CB_INSERTSTRING16:
1436                 wParam = (INT)(INT16)wParam;
1437                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1438         case CB_INSERTSTRING:
1439                 retvalue = SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1440                 goto END;
1441         case CB_DELETESTRING16:
1442         case CB_DELETESTRING:
1443                 retvalue = SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1444                 goto END;
1445         case CB_SELECTSTRING16:
1446                 wParam = (INT)(INT16)wParam;
1447                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1448         case CB_SELECTSTRING:
1449                 retvalue = COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1450                 goto END;
1451         case CB_FINDSTRING16:
1452                 wParam = (INT)(INT16)wParam;
1453                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1454         case CB_FINDSTRING:
1455                 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1456                 goto END;
1457         case CB_FINDSTRINGEXACT16:
1458                 wParam = (INT)(INT16)wParam;
1459                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1460         case CB_FINDSTRINGEXACT:
1461                 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT, 
1462                                                        wParam, lParam );
1463                 goto END;
1464         case CB_SETITEMHEIGHT16:
1465                 wParam = (INT)(INT16)wParam;    /* signed integer */
1466         case CB_SETITEMHEIGHT:
1467                 retvalue = COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1468                 goto END;
1469         case CB_GETITEMHEIGHT16:
1470                 wParam = (INT)(INT16)wParam;
1471         case CB_GETITEMHEIGHT:
1472                 if( (INT)wParam >= 0 )  /* listbox item */
1473                 {
1474                     retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1475                     goto END;
1476                 }
1477                 retvalue = (lphc->RectEdit.bottom - lphc->RectEdit.top);
1478                 goto END;
1479         case CB_RESETCONTENT16: 
1480         case CB_RESETCONTENT:
1481                 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1482                 CBPaintText( lphc, 0 );
1483                 retvalue = TRUE;
1484                 goto END;
1485         case CB_INITSTORAGE:
1486                 retvalue = SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1487                 goto END;
1488         case CB_GETHORIZONTALEXTENT:
1489                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1490                 goto END;
1491         case CB_SETHORIZONTALEXTENT:
1492                 retvalue = SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1493                 goto END;
1494         case CB_GETTOPINDEX:
1495                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1496                 goto END;
1497         case CB_GETLOCALE:
1498                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1499                 goto END;
1500         case CB_SETLOCALE:
1501                 retvalue = SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1502                 goto END;
1503         case CB_GETDROPPEDWIDTH:
1504                 if( lphc->droppedWidth )
1505                 {
1506                     retvalue = lphc->droppedWidth;
1507                     goto END;
1508                 }
1509                 retvalue = lphc->RectCombo.right - lphc->RectCombo.left - 
1510                            (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
1511                 goto END;
1512         case CB_SETDROPPEDWIDTH:
1513                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1514                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1515                 retvalue = CB_ERR;
1516                 goto END;
1517         case CB_GETDROPPEDCONTROLRECT16:
1518                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1519                 if( lParam ) 
1520                 {
1521                     RECT        r;
1522                     CBGetDroppedControlRect( lphc, &r );
1523                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
1524                 }
1525                 retvalue = CB_OKAY;
1526                 goto END;
1527         case CB_GETDROPPEDCONTROLRECT:
1528                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1529                 retvalue = CB_OKAY;
1530                 goto END;
1531         case CB_GETDROPPEDSTATE16:
1532         case CB_GETDROPPEDSTATE:
1533                 retvalue = (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1534                 goto END;
1535         case CB_DIR16: 
1536                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1537                 /* fall through */
1538         case CB_DIR:
1539                 retvalue = COMBO_Directory( lphc, (UINT)wParam, 
1540                                        (LPSTR)lParam, (message == CB_DIR));
1541                 goto END;
1542         case CB_SHOWDROPDOWN16:
1543         case CB_SHOWDROPDOWN:
1544                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1545                 {
1546                     if( wParam )
1547                     {
1548                         if( !(lphc->wState & CBF_DROPPED) )
1549                             CBDropDown( lphc );
1550                     }
1551                     else 
1552                         if( lphc->wState & CBF_DROPPED ) 
1553                             CBRollUp( lphc, FALSE, TRUE );
1554                 }
1555                 retvalue = TRUE;
1556                 goto END;
1557         case CB_GETCOUNT16: 
1558         case CB_GETCOUNT:
1559                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1560                 goto END;
1561         case CB_GETCURSEL16: 
1562         case CB_GETCURSEL:
1563                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1564                 goto END;
1565         case CB_SETCURSEL16:
1566                 wParam = (INT)(INT16)wParam;
1567         case CB_SETCURSEL:
1568                 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1569                 if( lphc->wState & CBF_SELCHANGE )
1570                 {
1571                     /* no LBN_SELCHANGE in this case, update manually */
1572                    
1573                     CBPaintText( lphc, 0 );
1574                     lphc->wState &= ~CBF_SELCHANGE;
1575                 }
1576                 retvalue = lParam;
1577                 goto END;
1578         case CB_GETLBTEXT16: 
1579                 wParam = (INT)(INT16)wParam;
1580                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1581         case CB_GETLBTEXT:
1582                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1583                 goto END;
1584         case CB_GETLBTEXTLEN16: 
1585                 wParam = (INT)(INT16)wParam;
1586         case CB_GETLBTEXTLEN:
1587                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1588                 goto END;
1589         case CB_GETITEMDATA16:
1590                 wParam = (INT)(INT16)wParam;
1591         case CB_GETITEMDATA:
1592                 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1593                 goto END;
1594         case CB_SETITEMDATA16:
1595                 wParam = (INT)(INT16)wParam;
1596         case CB_SETITEMDATA:
1597                 retvalue = SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1598                 goto END;
1599         case CB_GETEDITSEL16: 
1600                 wParam = lParam = 0;   /* just in case */
1601         case CB_GETEDITSEL:
1602                 if( lphc->wState & CBF_EDIT )
1603                 {
1604                     INT a, b;
1605
1606                     retvalue = SendMessageA( lphc->hWndEdit, EM_GETSEL,
1607                                            (wParam) ? wParam : (WPARAM)&a,
1608                                            (lParam) ? lParam : (LPARAM)&b );
1609                     goto END;
1610                 }
1611                 retvalue = CB_ERR;
1612                 goto END;
1613         case CB_SETEDITSEL16: 
1614         case CB_SETEDITSEL:
1615                 if( lphc->wState & CBF_EDIT ) 
1616                 {
1617                     retvalue = SendMessageA( lphc->hWndEdit, EM_SETSEL,
1618                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
1619                     goto END;
1620                 }
1621                 retvalue = CB_ERR;
1622                 goto END;
1623         case CB_SETEXTENDEDUI16:
1624         case CB_SETEXTENDEDUI:
1625                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
1626                 {
1627                     retvalue = CB_ERR;
1628                     goto END;
1629                 }
1630                 if( wParam )
1631                     lphc->wState |= CBF_EUI;
1632                 else lphc->wState &= ~CBF_EUI;
1633                 retvalue = CB_OKAY;
1634                 goto END;
1635         case CB_GETEXTENDEDUI16:
1636         case CB_GETEXTENDEDUI:
1637                 retvalue = (lphc->wState & CBF_EUI) ? TRUE : FALSE;
1638                 goto END;
1639         case (WM_USER + 0x1B):
1640                 WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
1641     }
1642     retvalue = DefWindowProcA(hwnd, message, wParam, lParam);
1643     goto END;
1644   }
1645     retvalue = CB_ERR;
1646 END:
1647     WIN_ReleaseWndPtr(pWnd);
1648     return retvalue;
1649 }
1650