~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/user32/combo.c

Version: ~ [ wine-1.5.4 ] ~ [ wine-1.5.3 ] ~ [ wine-1.5.2 ] ~ [ wine-1.5.1 ] ~ [ wine-1.5.0 ] ~ [ wine-1.4 ] ~ [ wine-1.4-rc6 ] ~ [ wine-1.4-rc5 ] ~ [ wine-1.4-rc4 ] ~ [ wine-1.4-rc3 ] ~ [ wine-1.4-rc2 ] ~ [ wine-1.4-rc1 ] ~ [ wine-1.3.37 ] ~ [ wine-1.3.36 ] ~ [ wine-1.3.35 ] ~ [ wine-1.3.34 ] ~ [ wine-1.3.33 ] ~ [ wine-1.3.32 ] ~ [ wine-1.3.31 ] ~ [ wine-1.3.30 ] ~ [ wine-1.3.29 ] ~ [ wine-1.3.28 ] ~ [ wine-1.3.27 ] ~ [ wine-1.3.26 ] ~ [ wine-1.3.25 ] ~ [ wine-1.3.24 ] ~ [ wine-1.3.23 ] ~ [ wine-1.3.22 ] ~ [ wine-1.3.21 ] ~ [ wine-1.3.20 ] ~ [ wine-1.3.19 ] ~ [ wine-1.3.18 ] ~ [ wine-1.2.3 ] ~ [ wine-1.3.17 ] ~ [ wine-1.3.16 ] ~ [ wine-1.3.15 ] ~ [ wine-1.3.14 ] ~ [ wine-1.3.13 ] ~ [ wine-1.3.12 ] ~ [ wine-1.3.11 ] ~ [ wine-1.3.10 ] ~ [ wine-1.3.9 ] ~ [ wine-1.2.2 ] ~ [ wine-1.3.8 ] ~ [ wine-1.3.7 ] ~ [ wine-1.3.6 ] ~ [ wine-1.3.5 ] ~ [ wine-1.2.1 ] ~ [ wine-1.3.4 ] ~ [ wine-1.3.3 ] ~ [ wine-1.3.2 ] ~ [ wine-1.3.1 ] ~ [ wine-1.3.0 ] ~ [ wine-1.2 ] ~ [ wine-1.2-rc7 ] ~ [ wine-1.2-rc6 ] ~ [ wine-1.2-rc5 ] ~ [ wine-1.2-rc4 ] ~ [ wine-1.2-rc3 ] ~ [ wine-1.2-rc2 ] ~ [ wine-1.2-rc1 ] ~ [ wine-1.1.44 ] ~ [ wine-1.1.43 ] ~ [ wine-1.1.42 ] ~ [ wine-1.1.41 ] ~ [ wine-1.1.40 ] ~ [ wine-1.1.39 ] ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Combo controls
  3  *
  4  * Copyright 1997 Alex Korobka
  5  *
  6  * This library is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU Lesser General Public
  8  * License as published by the Free Software Foundation; either
  9  * version 2.1 of the License, or (at your option) any later version.
 10  *
 11  * This library is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  * Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public
 17  * License along with this library; if not, write to the Free Software
 18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 19  *
 20  * NOTES
 21  *
 22  * This code was audited for completeness against the documented features
 23  * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
 24  * 
 25  * Unless otherwise noted, we believe this code to be complete, as per
 26  * the specification mentioned above.
 27  * If you discover missing features, or bugs, please note them below.
 28  * 
 29  * TODO:
 30  *   - ComboBox_[GS]etMinVisible()
 31  *   - CB_GETMINVISIBLE, CB_SETMINVISIBLE
 32  *   - CB_SETTOPINDEX
 33  */
 34 
 35 #include <stdarg.h>
 36 #include <string.h>
 37 
 38 #define OEMRESOURCE
 39 
 40 #include "windef.h"
 41 #include "winbase.h"
 42 #include "wingdi.h"
 43 #include "winuser.h"
 44 #include "wine/unicode.h"
 45 #include "user_private.h"
 46 #include "win.h"
 47 #include "controls.h"
 48 #include "winternl.h"
 49 #include "wine/debug.h"
 50 
 51 WINE_DEFAULT_DEBUG_CHANNEL(combo);
 52 
 53   /* bits in the dwKeyData */
 54 #define KEYDATA_ALT             0x2000
 55 #define KEYDATA_PREVSTATE       0x4000
 56 
 57 /*
 58  * Additional combo box definitions
 59  */
 60 
 61 #define CB_NOTIFY( lphc, code ) \
 62     (SendMessageW((lphc)->owner, WM_COMMAND, \
 63                   MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
 64 
 65 #define CB_DISABLED( lphc )   (!IsWindowEnabled((lphc)->self))
 66 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
 67 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
 68 #define CB_HWND( lphc )       ((lphc)->self)
 69 #define CB_GETTYPE( lphc )    ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
 70 
 71 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
 72 
 73 /*
 74  * Drawing globals
 75  */
 76 static HBITMAP  hComboBmp = 0;
 77 static UINT     CBitHeight, CBitWidth;
 78 
 79 /*
 80  * Look and feel dependent "constants"
 81  */
 82 
 83 #define COMBO_YBORDERGAP         5
 84 #define COMBO_XBORDERSIZE()      2
 85 #define COMBO_YBORDERSIZE()      2
 86 #define COMBO_EDITBUTTONSPACE()  0
 87 #define EDIT_CONTROL_PADDING()   1
 88 
 89 /*********************************************************************
 90  * combo class descriptor
 91  */
 92 static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
 93 const struct builtin_class_descr COMBO_builtin_class =
 94 {
 95     comboboxW,            /* name */
 96     CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style  */
 97     WINPROC_COMBO,        /* proc */
 98     sizeof(HEADCOMBO *),  /* extra */
 99     IDC_ARROW,            /* cursor */
100     0                     /* brush */
101 };
102 
103 
104 /***********************************************************************
105  *           COMBO_Init
106  *
107  * Load combo button bitmap.
108  */
109 static BOOL COMBO_Init(void)
110 {
111   HDC           hDC;
112 
113   if( hComboBmp ) return TRUE;
114   if( (hDC = CreateCompatibleDC(0)) )
115   {
116     BOOL        bRet = FALSE;
117     if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
118     {
119       BITMAP      bm;
120       HBITMAP     hPrevB;
121       RECT        r;
122 
123       GetObjectW( hComboBmp, sizeof(bm), &bm );
124       CBitHeight = bm.bmHeight;
125       CBitWidth  = bm.bmWidth;
126 
127       TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
128 
129       hPrevB = SelectObject( hDC, hComboBmp);
130       SetRect( &r, 0, 0, CBitWidth, CBitHeight );
131       InvertRect( hDC, &r );
132       SelectObject( hDC, hPrevB );
133       bRet = TRUE;
134     }
135     DeleteDC( hDC );
136     return bRet;
137   }
138   return FALSE;
139 }
140 
141 /***********************************************************************
142  *           COMBO_NCCreate
143  */
144 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
145 {
146     LPHEADCOMBO lphc;
147 
148     if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
149     {
150         lphc->self = hwnd;
151         SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
152 
153        /* some braindead apps do try to use scrollbar/border flags */
154 
155         lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
156         SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
157 
158         /*
159          * We also have to remove the client edge style to make sure
160          * we don't end-up with a non client area.
161          */
162         SetWindowLongW( hwnd, GWL_EXSTYLE,
163                         GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
164 
165         if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
166               lphc->dwStyle |= CBS_HASSTRINGS;
167         if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
168               lphc->wState |= CBF_NOTIFY;
169 
170         TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
171         return TRUE;
172     }
173     return FALSE;
174 }
175 
176 /***********************************************************************
177  *           COMBO_NCDestroy
178  */
179 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
180 {
181 
182    if( lphc )
183    {
184        TRACE("[%p]: freeing storage\n", lphc->self);
185 
186        if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
187            DestroyWindow( lphc->hWndLBox );
188 
189        SetWindowLongPtrW( lphc->self, 0, 0 );
190        HeapFree( GetProcessHeap(), 0, lphc );
191    }
192    return 0;
193 }
194 
195 /***********************************************************************
196  *           CBGetTextAreaHeight
197  *
198  * This method will calculate the height of the text area of the
199  * combobox.
200  * The height of the text area is set in two ways.
201  * It can be set explicitly through a combobox message or through a
202  * WM_MEASUREITEM callback.
203  * If this is not the case, the height is set to font height + 4px
204  * This height was determined through experimentation.
205  * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
206  */
207 static INT CBGetTextAreaHeight(
208   HWND        hwnd,
209   LPHEADCOMBO lphc)
210 {
211   INT iTextItemHeight;
212 
213   if( lphc->editHeight ) /* explicitly set height */
214   {
215     iTextItemHeight = lphc->editHeight;
216   }
217   else
218   {
219     TEXTMETRICW tm;
220     HDC         hDC       = GetDC(hwnd);
221     HFONT       hPrevFont = 0;
222     INT         baseUnitY;
223 
224     if (lphc->hFont)
225       hPrevFont = SelectObject( hDC, lphc->hFont );
226 
227     GetTextMetricsW(hDC, &tm);
228 
229     baseUnitY = tm.tmHeight;
230 
231     if( hPrevFont )
232       SelectObject( hDC, hPrevFont );
233 
234     ReleaseDC(hwnd, hDC);
235 
236     iTextItemHeight = baseUnitY + 4;
237   }
238 
239   /*
240    * Check the ownerdraw case if we haven't asked the parent the size
241    * of the item yet.
242    */
243   if ( CB_OWNERDRAWN(lphc) &&
244        (lphc->wState & CBF_MEASUREITEM) )
245   {
246     MEASUREITEMSTRUCT measureItem;
247     RECT              clientRect;
248     INT               originalItemHeight = iTextItemHeight;
249     UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
250 
251     /*
252      * We use the client rect for the width of the item.
253      */
254     GetClientRect(hwnd, &clientRect);
255 
256     lphc->wState &= ~CBF_MEASUREITEM;
257 
258     /*
259      * Send a first one to measure the size of the text area
260      */
261     measureItem.CtlType    = ODT_COMBOBOX;
262     measureItem.CtlID      = id;
263     measureItem.itemID     = -1;
264     measureItem.itemWidth  = clientRect.right;
265     measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
266     measureItem.itemData   = 0;
267     SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
268     iTextItemHeight = 6 + measureItem.itemHeight;
269 
270     /*
271      * Send a second one in the case of a fixed ownerdraw list to calculate the
272      * size of the list items. (we basically do this on behalf of the listbox)
273      */
274     if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
275     {
276       measureItem.CtlType    = ODT_COMBOBOX;
277       measureItem.CtlID      = id;
278       measureItem.itemID     = 0;
279       measureItem.itemWidth  = clientRect.right;
280       measureItem.itemHeight = originalItemHeight;
281       measureItem.itemData   = 0;
282       SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
283       lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
284     }
285 
286     /*
287      * Keep the size for the next time
288      */
289     lphc->editHeight = iTextItemHeight;
290   }
291 
292   return iTextItemHeight;
293 }
294 
295 /***********************************************************************
296  *           CBForceDummyResize
297  *
298  * The dummy resize is used for listboxes that have a popup to trigger
299  * a re-arranging of the contents of the combobox and the recalculation
300  * of the size of the "real" control window.
301  */
302 static void CBForceDummyResize(
303   LPHEADCOMBO lphc)
304 {
305   RECT windowRect;
306   int newComboHeight;
307 
308   newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
309 
310   GetWindowRect(lphc->self, &windowRect);
311 
312   /*
313    * We have to be careful, resizing a combobox also has the meaning that the
314    * dropped rect will be resized. In this case, we want to trigger a resize
315    * to recalculate layout but we don't want to change the dropped rectangle
316    * So, we pass the height of text area of control as the height.
317    * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
318    * message.
319    */
320   SetWindowPos( lphc->self,
321                 NULL,
322                 0, 0,
323                 windowRect.right  - windowRect.left,
324                 newComboHeight,
325                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
326 }
327 
328 /***********************************************************************
329  *           CBCalcPlacement
330  *
331  * Set up component coordinates given valid lphc->RectCombo.
332  */
333 static void CBCalcPlacement(
334   HWND        hwnd,
335   LPHEADCOMBO lphc,
336   LPRECT      lprEdit,
337   LPRECT      lprButton,
338   LPRECT      lprLB)
339 {
340   /*
341    * Again, start with the client rectangle.
342    */
343   GetClientRect(hwnd, lprEdit);
344 
345   /*
346    * Remove the borders
347    */
348   InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
349 
350   /*
351    * Chop off the bottom part to fit with the height of the text area.
352    */
353   lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
354 
355   /*
356    * The button starts the same vertical position as the text area.
357    */
358   CopyRect(lprButton, lprEdit);
359 
360   /*
361    * If the combobox is "simple" there is no button.
362    */
363   if( CB_GETTYPE(lphc) == CBS_SIMPLE )
364     lprButton->left = lprButton->right = lprButton->bottom = 0;
365   else
366   {
367     /*
368      * Let's assume the combobox button is the same width as the
369      * scrollbar button.
370      * size the button horizontally and cut-off the text area.
371      */
372     lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
373     lprEdit->right  = lprButton->left;
374   }
375 
376   /*
377    * In the case of a dropdown, there is an additional spacing between the
378    * text area and the button.
379    */
380   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
381   {
382     lprEdit->right -= COMBO_EDITBUTTONSPACE();
383   }
384 
385   /*
386    * If we have an edit control, we space it away from the borders slightly.
387    */
388   if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
389   {
390     InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
391   }
392 
393   /*
394    * Adjust the size of the listbox popup.
395    */
396   if( CB_GETTYPE(lphc) == CBS_SIMPLE )
397   {
398     /*
399      * Use the client rectangle to initialize the listbox rectangle
400      */
401     GetClientRect(hwnd, lprLB);
402 
403     /*
404      * Then, chop-off the top part.
405      */
406     lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
407   }
408   else
409   {
410     /*
411      * Make sure the dropped width is as large as the combobox itself.
412      */
413     if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
414     {
415       lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
416 
417       /*
418        * In the case of a dropdown, the popup listbox is offset to the right.
419        * so, we want to make sure it's flush with the right side of the
420        * combobox
421        */
422       if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
423         lprLB->right -= COMBO_EDITBUTTONSPACE();
424     }
425     else
426        lprLB->right = lprLB->left + lphc->droppedWidth;
427   }
428 
429   /* don't allow negative window width */
430   if (lprEdit->right < lprEdit->left)
431     lprEdit->right = lprEdit->left;
432 
433   TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
434 
435   TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
436 
437   TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
438 }
439 
440 /***********************************************************************
441  *           CBGetDroppedControlRect
442  */
443 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
444 {
445     /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
446      of the combo box and the lower right corner of the listbox */
447 
448     GetWindowRect(lphc->self, lpRect);
449 
450     lpRect->right =  lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
451     lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
452 
453 }
454 
455 /***********************************************************************
456  *           COMBO_WindowPosChanging
457  */
458 static LRESULT COMBO_WindowPosChanging(
459   HWND        hwnd,
460   LPHEADCOMBO lphc,
461   WINDOWPOS*  posChanging)
462 {
463   /*
464    * We need to override the WM_WINDOWPOSCHANGING method to handle all
465    * the non-simple comboboxes. The problem is that those controls are
466    * always the same height. We have to make sure they are not resized
467    * to another value.
468    */
469   if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
470        ((posChanging->flags & SWP_NOSIZE) == 0) )
471   {
472     int newComboHeight;
473 
474     newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
475                       2*COMBO_YBORDERSIZE();
476 
477     /*
478      * Resizing a combobox has another side effect, it resizes the dropped
479      * rectangle as well. However, it does it only if the new height for the
480      * combobox is more than the height it should have. In other words,
481      * if the application resizing the combobox only had the intention to resize
482      * the actual control, for example, to do the layout of a dialog that is
483      * resized, the height of the dropdown is not changed.
484      */
485     if (posChanging->cy > newComboHeight)
486     {
487         TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
488               posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
489               lphc->droppedRect.top);
490       lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
491 
492     }
493     posChanging->cy = newComboHeight;
494   }
495 
496   return 0;
497 }
498 
499 /***********************************************************************
500  *           COMBO_Create
501  */
502 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
503                              BOOL unicode )
504 {
505   static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
506   static const WCHAR editName[] = {'E','d','i','t',0};
507 
508   if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
509   if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
510 
511   lphc->owner = hwndParent;
512 
513   /*
514    * The item height and dropped width are not set when the control
515    * is created.
516    */
517   lphc->droppedWidth = lphc->editHeight = 0;
518 
519   /*
520    * The first time we go through, we want to measure the ownerdraw item
521    */
522   lphc->wState |= CBF_MEASUREITEM;
523 
524   /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
525 
526   if( lphc->owner || !(style & WS_VISIBLE) )
527   {
528       UINT lbeStyle   = 0;
529       UINT lbeExStyle = 0;
530 
531       /*
532        * Initialize the dropped rect to the size of the client area of the
533        * control and then, force all the areas of the combobox to be
534        * recalculated.
535        */
536       GetClientRect( hwnd, &lphc->droppedRect );
537       CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
538 
539       /*
540        * Adjust the position of the popup listbox if it's necessary
541        */
542       if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
543       {
544         lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE();
545 
546         /*
547          * If it's a dropdown, the listbox is offset
548          */
549         if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
550           lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
551 
552         if (lphc->droppedRect.bottom < lphc->droppedRect.top)
553             lphc->droppedRect.bottom = lphc->droppedRect.top;
554         if (lphc->droppedRect.right < lphc->droppedRect.left)
555             lphc->droppedRect.right = lphc->droppedRect.left;
556         MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
557       }
558 
559       /* create listbox popup */
560 
561       lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
562                  (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
563 
564       if( lphc->dwStyle & CBS_SORT )
565         lbeStyle |= LBS_SORT;
566       if( lphc->dwStyle & CBS_HASSTRINGS )
567         lbeStyle |= LBS_HASSTRINGS;
568       if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
569         lbeStyle |= LBS_NOINTEGRALHEIGHT;
570       if( lphc->dwStyle & CBS_DISABLENOSCROLL )
571         lbeStyle |= LBS_DISABLENOSCROLL;
572 
573       if( CB_GETTYPE(lphc) == CBS_SIMPLE )      /* child listbox */
574       {
575         lbeStyle |= WS_VISIBLE;
576 
577         /*
578          * In win 95 look n feel, the listbox in the simple combobox has
579          * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
580          */
581         lbeStyle   &= ~WS_BORDER;
582         lbeExStyle |= WS_EX_CLIENTEDGE;
583       }
584       else
585       {
586         lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
587       }
588 
589       if (unicode)
590           lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
591                                            lphc->droppedRect.left,
592                                            lphc->droppedRect.top,
593                                            lphc->droppedRect.right - lphc->droppedRect.left,
594                                            lphc->droppedRect.bottom - lphc->droppedRect.top,
595                                            hwnd, (HMENU)ID_CB_LISTBOX,
596                                            (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
597       else
598           lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
599                                            lphc->droppedRect.left,
600                                            lphc->droppedRect.top,
601                                            lphc->droppedRect.right - lphc->droppedRect.left,
602                                            lphc->droppedRect.bottom - lphc->droppedRect.top,
603                                            hwnd, (HMENU)ID_CB_LISTBOX,
604                                            (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
605 
606       if( lphc->hWndLBox )
607       {
608           BOOL  bEdit = TRUE;
609           lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
610 
611           if( lphc->wState & CBF_EDIT )
612           {
613               if( lphc->dwStyle & CBS_OEMCONVERT )
614                   lbeStyle |= ES_OEMCONVERT;
615               if( lphc->dwStyle & CBS_AUTOHSCROLL )
616                   lbeStyle |= ES_AUTOHSCROLL;
617               if( lphc->dwStyle & CBS_LOWERCASE )
618                   lbeStyle |= ES_LOWERCASE;
619               else if( lphc->dwStyle & CBS_UPPERCASE )
620                   lbeStyle |= ES_UPPERCASE;
621 
622               if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
623 
624               if (unicode)
625                   lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
626                                                    lphc->textRect.left, lphc->textRect.top,
627                                                    lphc->textRect.right - lphc->textRect.left,
628                                                    lphc->textRect.bottom - lphc->textRect.top,
629                                                    hwnd, (HMENU)ID_CB_EDIT,
630                                                    (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
631               else
632                   lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
633                                                    lphc->textRect.left, lphc->textRect.top,
634                                                    lphc->textRect.right - lphc->textRect.left,
635                                                    lphc->textRect.bottom - lphc->textRect.top,
636                                                    hwnd, (HMENU)ID_CB_EDIT,
637                                                    (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
638 
639               if( !lphc->hWndEdit )
640                 bEdit = FALSE;
641           }
642 
643           if( bEdit )
644           {
645             if( CB_GETTYPE(lphc) != CBS_SIMPLE )
646             {
647               /* Now do the trick with parent */
648               SetParent(lphc->hWndLBox, HWND_DESKTOP);
649               /*
650                * If the combo is a dropdown, we must resize the control
651                * to fit only the text area and button. To do this,
652                * we send a dummy resize and the WM_WINDOWPOSCHANGING message
653                * will take care of setting the height for us.
654                */
655               CBForceDummyResize(lphc);
656             }
657 
658             TRACE("init done\n");
659             return 0;
660           }
661           ERR("edit control failure.\n");
662       } else ERR("listbox failure.\n");
663   } else ERR("no owner for visible combo.\n");
664 
665   /* CreateWindow() will send WM_NCDESTROY to cleanup */
666 
667   return -1;
668 }
669 
670 /***********************************************************************
671  *           CBPaintButton
672  *
673  * Paint combo button (normal, pressed, and disabled states).
674  */
675 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
676 {
677     UINT buttonState = DFCS_SCROLLCOMBOBOX;
678 
679     if( lphc->wState & CBF_NOREDRAW )
680       return;
681 
682 
683     if (lphc->wState & CBF_BUTTONDOWN)
684         buttonState |= DFCS_PUSHED;
685 
686     if (CB_DISABLED(lphc))
687         buttonState |= DFCS_INACTIVE;
688 
689     DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
690 }
691 
692 /***********************************************************************
693  *           CBPaintText
694  *
695  * Paint CBS_DROPDOWNLIST text field / update edit control contents.
696  */
697 static void CBPaintText(
698   LPHEADCOMBO lphc,
699   HDC         hdc,
700   RECT        rectEdit)
701 {
702    INT  id, size = 0;
703    LPWSTR pText = NULL;
704 
705    if( lphc->wState & CBF_NOREDRAW ) return;
706 
707    TRACE("\n");
708 
709    /* follow Windows combobox that sends a bunch of text
710     * inquiries to its listbox while processing WM_PAINT. */
711 
712    if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
713    {
714         size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
715         if (size == LB_ERR)
716           FIXME("LB_ERR probably not handled yet\n");
717         if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
718         {
719             /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
720            size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
721             pText[size] = '\0'; /* just in case */
722         } else return;
723    }
724    else
725        if( !CB_OWNERDRAWN(lphc) )
726            return;
727 
728    if( lphc->wState & CBF_EDIT )
729    {
730         static const WCHAR empty_stringW[] = { 0 };
731         if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
732         if( lphc->wState & CBF_FOCUSED )
733            SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
734    }
735    else /* paint text field ourselves */
736    {
737      UINT       itemState = ODS_COMBOBOXEDIT;
738      HFONT      hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
739 
740      /*
741       * Give ourselves some space.
742       */
743      InflateRect( &rectEdit, -1, -1 );
744 
745      if( CB_OWNERDRAWN(lphc) )
746      {
747        DRAWITEMSTRUCT dis;
748        HRGN           clipRegion;
749        UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
750 
751        /* setup state for DRAWITEM message. Owner will highlight */
752        if ( (lphc->wState & CBF_FOCUSED) &&
753             !(lphc->wState & CBF_DROPPED) )
754            itemState |= ODS_SELECTED | ODS_FOCUS;
755 
756        if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
757 
758        dis.CtlType      = ODT_COMBOBOX;
759        dis.CtlID        = ctlid;
760        dis.hwndItem     = lphc->self;
761        dis.itemAction   = ODA_DRAWENTIRE;
762        dis.itemID       = id;
763        dis.itemState    = itemState;
764        dis.hDC          = hdc;
765        dis.rcItem       = rectEdit;
766        dis.itemData     = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
767 
768        /*
769         * Clip the DC and have the parent draw the item.
770         */
771        clipRegion = set_control_clipping( hdc, &rectEdit );
772 
773        SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
774 
775        SelectClipRgn( hdc, clipRegion );
776        if (clipRegion) DeleteObject( clipRegion );
777      }
778      else
779      {
780        static const WCHAR empty_stringW[] = { 0 };
781 
782        if ( (lphc->wState & CBF_FOCUSED) &&
783             !(lphc->wState & CBF_DROPPED) ) {
784 
785            /* highlight */
786            FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
787            SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
788            SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
789        }
790 
791        ExtTextOutW( hdc,
792                     rectEdit.left + 1,
793                     rectEdit.top + 1,
794                     ETO_OPAQUE | ETO_CLIPPED,
795                     &rectEdit,
796                     pText ? pText : empty_stringW , size, NULL );
797 
798        if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
799          DrawFocusRect( hdc, &rectEdit );
800      }
801 
802      if( hPrevFont )
803        SelectObject(hdc, hPrevFont );
804    }
805    HeapFree( GetProcessHeap(), 0, pText );
806 }
807 
808 /***********************************************************************
809  *           CBPaintBorder
810  */
811 static void CBPaintBorder(
812   HWND            hwnd,
813   const HEADCOMBO *lphc,
814   HDC             hdc)
815 {
816   RECT clientRect;
817 
818   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
819   {
820     GetClientRect(hwnd, &clientRect);
821   }
822   else
823   {
824     CopyRect(&clientRect, &lphc->textRect);
825 
826     InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
827     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
828   }
829 
830   DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
831 }
832 
833 /***********************************************************************
834  *           COMBO_PrepareColors
835  *
836  * This method will sent the appropriate WM_CTLCOLOR message to
837  * prepare and setup the colors for the combo's DC.
838  *
839  * It also returns the brush to use for the background.
840  */
841 static HBRUSH COMBO_PrepareColors(
842   LPHEADCOMBO lphc,
843   HDC         hDC)
844 {
845   HBRUSH  hBkgBrush;
846 
847   /*
848    * Get the background brush for this control.
849    */
850   if (CB_DISABLED(lphc))
851   {
852     hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
853                                      (WPARAM)hDC, (LPARAM)lphc->self );
854 
855     /*
856      * We have to change the text color since WM_CTLCOLORSTATIC will
857      * set it to the "enabled" color. This is the same behavior as the
858      * edit control
859      */
860     SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
861   }
862   else
863   {
864       /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
865       hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
866                                        (WPARAM)hDC, (LPARAM)lphc->self );
867   }
868 
869   /*
870    * Catch errors.
871    */
872   if( !hBkgBrush )
873     hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
874 
875   return hBkgBrush;
876 }
877 
878 
879 /***********************************************************************
880  *           COMBO_Paint
881  */
882 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
883 {
884   PAINTSTRUCT ps;
885   HDC   hDC;
886 
887   hDC = (hParamDC) ? hParamDC
888                    : BeginPaint( lphc->self, &ps);
889 
890   TRACE("hdc=%p\n", hDC);
891 
892   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
893   {
894       HBRUSH    hPrevBrush, hBkgBrush;
895 
896       /*
897        * Retrieve the background brush and select it in the
898        * DC.
899        */
900       hBkgBrush = COMBO_PrepareColors(lphc, hDC);
901 
902       hPrevBrush = SelectObject( hDC, hBkgBrush );
903       if (!(lphc->wState & CBF_EDIT))
904         FillRect(hDC, &lphc->textRect, hBkgBrush);
905 
906       /*
907        * In non 3.1 look, there is a sunken border on the combobox
908        */
909       CBPaintBorder(lphc->self, lphc, hDC);
910 
911       if( !IsRectEmpty(&lphc->buttonRect) )
912       {
913         CBPaintButton(lphc, hDC, lphc->buttonRect);
914       }
915 
916       /* paint the edit control padding area */
917       if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
918       {
919           RECT rPadEdit = lphc->textRect;
920 
921           InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
922 
923           FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
924       }
925 
926       if( !(lphc->wState & CBF_EDIT) )
927         CBPaintText( lphc, hDC, lphc->textRect);
928 
929       if( hPrevBrush )
930         SelectObject( hDC, hPrevBrush );
931   }
932 
933   if( !hParamDC )
934     EndPaint(lphc->self, &ps);
935 
936   return 0;
937 }
938 
939 /***********************************************************************
940  *           CBUpdateLBox
941  *
942  * Select listbox entry according to the contents of the edit control.
943  */
944 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
945 {
946    INT  length, idx;
947    LPWSTR pText = NULL;
948 
949    idx = LB_ERR;
950    length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
951 
952    if( length > 0 )
953        pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
954 
955    TRACE("\t edit text length %i\n", length );
956 
957    if( pText )
958    {
959        GetWindowTextW( lphc->hWndEdit, pText, length + 1);
960        idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
961        HeapFree( GetProcessHeap(), 0, pText );
962    }
963 
964    SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
965 
966    /* probably superfluous but Windows sends this too */
967    SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
968    SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
969 
970    return idx;
971 }
972 
973 /***********************************************************************
974  *           CBUpdateEdit
975  *
976  * Copy a listbox entry to the edit control.
977  */
978 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
979 {
980    INT  length;
981    LPWSTR pText = NULL;
982    static const WCHAR empty_stringW[] = { 0 };
983 
984    TRACE("\t %i\n", index );
985 
986    if( index >= 0 ) /* got an entry */
987    {
988        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
989        if( length != LB_ERR)
990        {
991            if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
992            {
993                SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
994            }
995        }
996    }
997 
998    if( CB_HASSTRINGS(lphc) )
999    {
1000       lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1001       SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1002       lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1003    }
1004 
1005    if( lphc->wState & CBF_FOCUSED )
1006       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1007 
1008    HeapFree( GetProcessHeap(), 0, pText );
1009 }
1010 
1011 /***********************************************************************
1012  *           CBDropDown
1013  *
1014  * Show listbox popup.
1015  */
1016 static void CBDropDown( LPHEADCOMBO lphc )
1017 {
1018     HMONITOR monitor;
1019     MONITORINFO mon_info;
1020    RECT rect,r;
1021    int nItems = 0;
1022    int nDroppedHeight;
1023 
1024    TRACE("[%p]: drop down\n", lphc->self);
1025 
1026    CB_NOTIFY( lphc, CBN_DROPDOWN );
1027 
1028    /* set selection */
1029 
1030    lphc->wState |= CBF_DROPPED;
1031    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1032    {
1033        lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1034 
1035        /* Update edit only if item is in the list */
1036        if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1037          CBUpdateEdit( lphc, lphc->droppedIndex );
1038    }
1039    else
1040    {
1041        lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1042 
1043        SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1044                     lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
1045        SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1046    }
1047 
1048    /* now set popup position */
1049    GetWindowRect( lphc->self, &rect );
1050 
1051    /*
1052     * If it's a dropdown, the listbox is offset
1053     */
1054    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1055      rect.left += COMBO_EDITBUTTONSPACE();
1056 
1057   /* if the dropped height is greater than the total height of the dropped
1058      items list, then force the drop down list height to be the total height
1059      of the items in the dropped list */
1060 
1061   /* And Remove any extra space (Best Fit) */
1062    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1063   /* if listbox length has been set directly by its handle */
1064    GetWindowRect(lphc->hWndLBox, &r);
1065    if (nDroppedHeight < r.bottom - r.top)
1066        nDroppedHeight = r.bottom - r.top;
1067    nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1068 
1069    if (nItems > 0)
1070    {
1071       int nHeight;
1072       int nIHeight;
1073 
1074       nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1075 
1076       nHeight = nIHeight*nItems;
1077 
1078       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1079          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1080 
1081       if (nDroppedHeight < nHeight)
1082       {
1083             if (nItems < 5)
1084                 nDroppedHeight = (nItems+1)*nIHeight;
1085             else if (nDroppedHeight < 6*nIHeight)
1086                 nDroppedHeight = 6*nIHeight;
1087       }
1088    }
1089 
1090    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1091    monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1092    mon_info.cbSize = sizeof(mon_info);
1093    GetMonitorInfoW( monitor, &mon_info );
1094 
1095    if( (rect.bottom + nDroppedHeight) >= mon_info.rcWork.bottom )
1096       rect.bottom = rect.top - nDroppedHeight;
1097 
1098    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1099                  lphc->droppedRect.right - lphc->droppedRect.left,
1100                  nDroppedHeight,
1101                  SWP_NOACTIVATE | SWP_SHOWWINDOW);
1102 
1103 
1104    if( !(lphc->wState & CBF_NOREDRAW) )
1105      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1106                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1107 
1108    EnableWindow( lphc->hWndLBox, TRUE );
1109    if (GetCapture() != lphc->self)
1110       SetCapture(lphc->hWndLBox);
1111 }
1112 
1113 /***********************************************************************
1114  *           CBRollUp
1115  *
1116  * Hide listbox popup.
1117  */
1118 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1119 {
1120    HWND hWnd = lphc->self;
1121 
1122    TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1123          lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1124 
1125    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1126 
1127    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1128    {
1129 
1130        if( lphc->wState & CBF_DROPPED )
1131        {
1132            RECT rect;
1133 
1134            lphc->wState &= ~CBF_DROPPED;
1135            ShowWindow( lphc->hWndLBox, SW_HIDE );
1136 
1137            if(GetCapture() == lphc->hWndLBox)
1138            {
1139                ReleaseCapture();
1140            }
1141 
1142            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1143            {
1144                rect = lphc->buttonRect;
1145            }
1146            else
1147            {
1148                if( bButton )
1149                {
1150                  UnionRect( &rect,
1151                             &lphc->buttonRect,
1152                             &lphc->textRect);
1153                }
1154                else
1155                  rect = lphc->textRect;
1156 
1157                bButton = TRUE;
1158            }
1159 
1160            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1161                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1162                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1163            CB_NOTIFY( lphc, CBN_CLOSEUP );
1164        }
1165    }
1166 }
1167 
1168 /***********************************************************************
1169  *           COMBO_FlipListbox
1170  *
1171  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1172  */
1173 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1174 {
1175    if( lphc->wState & CBF_DROPPED )
1176    {
1177        CBRollUp( lphc, ok, bRedrawButton );
1178        return FALSE;
1179    }
1180 
1181    CBDropDown( lphc );
1182    return TRUE;
1183 }
1184 
1185 /***********************************************************************
1186  *           CBRepaintButton
1187  */
1188 static void CBRepaintButton( LPHEADCOMBO lphc )
1189    {
1190   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1191   UpdateWindow(lphc->self);
1192 }
1193 
1194 /***********************************************************************
1195  *           COMBO_SetFocus
1196  */
1197 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1198 {
1199    if( !(lphc->wState & CBF_FOCUSED) )
1200    {
1201        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1202            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1203 
1204        /* This is wrong. Message sequences seem to indicate that this
1205           is set *after* the notify. */
1206        /* lphc->wState |= CBF_FOCUSED;  */
1207 
1208        if( !(lphc->wState & CBF_EDIT) )
1209          InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1210 
1211        CB_NOTIFY( lphc, CBN_SETFOCUS );
1212        lphc->wState |= CBF_FOCUSED;
1213    }
1214 }
1215 
1216 /***********************************************************************
1217  *           COMBO_KillFocus
1218  */
1219 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1220 {
1221    HWND hWnd = lphc->self;
1222 
1223    if( lphc->wState & CBF_FOCUSED )
1224    {
1225        CBRollUp( lphc, FALSE, TRUE );
1226        if( IsWindow( hWnd ) )
1227        {
1228            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1229                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1230 
1231            lphc->wState &= ~CBF_FOCUSED;
1232 
1233            /* redraw text */
1234            if( !(lphc->wState & CBF_EDIT) )
1235              InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1236 
1237            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1238        }
1239    }
1240 }
1241 
1242 /***********************************************************************
1243  *           COMBO_Command
1244  */
1245 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1246 {
1247    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1248    {
1249        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1250 
1251        switch( HIWORD(wParam) >> 8 )
1252        {
1253            case (EN_SETFOCUS >> 8):
1254 
1255                TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1256 
1257                 COMBO_SetFocus( lphc );
1258                 break;
1259 
1260            case (EN_KILLFOCUS >> 8):
1261 
1262                TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1263 
1264                 /* NOTE: it seems that Windows' edit control sends an
1265                  * undocumented message WM_USER + 0x1B instead of this
1266                  * notification (only when it happens to be a part of
1267                  * the combo). ?? - AK.
1268                  */
1269 
1270                 COMBO_KillFocus( lphc );
1271                 break;
1272 
1273 
1274            case (EN_CHANGE >> 8):
1275                /*
1276                 * In some circumstances (when the selection of the combobox
1277                 * is changed for example) we don't want the EN_CHANGE notification
1278                 * to be forwarded to the parent of the combobox. This code
1279                 * checks a flag that is set in these occasions and ignores the
1280                 * notification.
1281                 */
1282                 if (lphc->wState & CBF_NOLBSELECT)
1283                 {
1284                   lphc->wState &= ~CBF_NOLBSELECT;
1285                 }
1286                 else
1287                 {
1288                   CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1289                 }
1290 
1291                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1292                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1293                 break;
1294 
1295            case (EN_UPDATE >> 8):
1296                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1297                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1298                 break;
1299 
1300            case (EN_ERRSPACE >> 8):
1301                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1302        }
1303    }
1304    else if( lphc->hWndLBox == hWnd )
1305    {
1306        switch( (short)HIWORD(wParam) )
1307        {
1308            case LBN_ERRSPACE:
1309                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1310                 break;
1311 
1312            case LBN_DBLCLK:
1313                 CB_NOTIFY( lphc, CBN_DBLCLK );
1314                 break;
1315 
1316            case LBN_SELCHANGE:
1317            case LBN_SELCANCEL:
1318 
1319                 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1320 
1321                 /* do not roll up if selection is being tracked
1322                  * by arrow keys in the dropdown listbox */
1323                 if (!(lphc->wState & CBF_NOROLLUP))
1324                 {
1325                     CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1326                 }
1327                 else lphc->wState &= ~CBF_NOROLLUP;
1328 
1329                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1330 
1331                 if( HIWORD(wParam) == LBN_SELCHANGE)
1332                 {
1333                    if( lphc->wState & CBF_EDIT )
1334                    {
1335                        INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1336                        lphc->wState |= CBF_NOLBSELECT;
1337                        CBUpdateEdit( lphc, index );
1338                        /* select text in edit, as Windows does */
1339                       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1340                    }
1341                    else
1342                    {
1343                        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1344                        UpdateWindow(lphc->self);
1345                    }
1346                 }
1347                 break;
1348 
1349            case LBN_SETFOCUS:
1350            case LBN_KILLFOCUS:
1351                 /* nothing to do here since ComboLBox always resets the focus to its
1352                  * combo/edit counterpart */
1353                  break;
1354        }
1355    }
1356    return 0;
1357 }
1358 
1359 /***********************************************************************
1360  *           COMBO_ItemOp
1361  *
1362  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1363  */
1364 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1365 {
1366    HWND hWnd = lphc->self;
1367    UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1368 
1369    TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1370 
1371    switch( msg )
1372    {
1373    case WM_DELETEITEM:
1374        {
1375            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1376            lpIS->CtlType  = ODT_COMBOBOX;
1377            lpIS->CtlID    = id;
1378            lpIS->hwndItem = hWnd;
1379            break;
1380        }
1381    case WM_DRAWITEM:
1382        {
1383            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1384            lpIS->CtlType  = ODT_COMBOBOX;
1385            lpIS->CtlID    = id;
1386            lpIS->hwndItem = hWnd;
1387            break;
1388        }
1389    case WM_COMPAREITEM:
1390        {
1391            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1392            lpIS->CtlType  = ODT_COMBOBOX;
1393            lpIS->CtlID    = id;
1394            lpIS->hwndItem = hWnd;
1395            break;
1396        }
1397    case WM_MEASUREITEM:
1398        {
1399            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1400            lpIS->CtlType  = ODT_COMBOBOX;
1401            lpIS->CtlID    = id;
1402            break;
1403        }
1404    }
1405    return SendMessageW(lphc->owner, msg, id, lParam);
1406 }
1407 
1408 
1409 /***********************************************************************
1410  *           COMBO_GetTextW
1411  */
1412 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1413 {
1414     INT length;
1415 
1416     if( lphc->wState & CBF_EDIT )
1417         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1418 
1419     /* get it from the listbox */
1420 
1421     if (!count || !buf) return 0;
1422     if( lphc->hWndLBox )
1423     {
1424         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1425         if (idx == LB_ERR) goto error;
1426         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1427         if (length == LB_ERR) goto error;
1428 
1429         /* 'length' is without the terminating character */
1430         if (length >= count)
1431         {
1432             LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1433             if (!lpBuffer) goto error;
1434             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1435 
1436             /* truncate if buffer is too short */
1437             if (length != LB_ERR)
1438             {
1439                 lstrcpynW( buf, lpBuffer, count );
1440                 length = count;
1441             }
1442             HeapFree( GetProcessHeap(), 0, lpBuffer );
1443         }
1444         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1445 
1446         if (length == LB_ERR) return 0;
1447         return length;
1448     }
1449 
1450  error:  /* error - truncate string, return zero */
1451     buf[0] = 0;
1452     return 0;
1453 }
1454 
1455 
1456 /***********************************************************************
1457  *           COMBO_GetTextA
1458  *
1459  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1460  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1461  */
1462 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1463 {
1464     INT length;
1465 
1466     if( lphc->wState & CBF_EDIT )
1467         return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1468 
1469     /* get it from the listbox */
1470 
1471     if (!count || !buf) return 0;
1472     if( lphc->hWndLBox )
1473     {
1474         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1475         if (idx == LB_ERR) goto error;
1476         length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1477         if (length == LB_ERR) goto error;
1478 
1479         /* 'length' is without the terminating character */
1480         if (length >= count)
1481         {
1482             LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1483             if (!lpBuffer) goto error;
1484             length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1485 
1486             /* truncate if buffer is too short */
1487             if (length != LB_ERR)
1488             {
1489                 lstrcpynA( buf, lpBuffer, count );
1490                 length = count;
1491             }
1492             HeapFree( GetProcessHeap(), 0, lpBuffer );
1493         }
1494         else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1495 
1496         if (length == LB_ERR) return 0;
1497         return length;
1498     }
1499 
1500  error:  /* error - truncate string, return zero */
1501     buf[0] = 0;
1502     return 0;
1503 }
1504 
1505 
1506 /***********************************************************************
1507  *           CBResetPos
1508  *
1509  * This function sets window positions according to the updated
1510  * component placement struct.
1511  */
1512 static void CBResetPos(
1513   LPHEADCOMBO lphc,
1514   const RECT  *rectEdit,
1515   const RECT  *rectLB,
1516   BOOL        bRedraw)
1517 {
1518    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1519 
1520    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1521     * sizing messages */
1522 
1523    if( lphc->wState & CBF_EDIT )
1524      SetWindowPos( lphc->hWndEdit, 0,
1525                    rectEdit->left, rectEdit->top,
1526                    rectEdit->right - rectEdit->left,
1527                    rectEdit->bottom - rectEdit->top,
1528                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1529 
1530    SetWindowPos( lphc->hWndLBox, 0,
1531                  rectLB->left, rectLB->top,
1532                  rectLB->right - rectLB->left,
1533                  rectLB->bottom - rectLB->top,
1534                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1535 
1536    if( bDrop )
1537    {
1538        if( lphc->wState & CBF_DROPPED )
1539        {
1540            lphc->wState &= ~CBF_DROPPED;
1541            ShowWindow( lphc->hWndLBox, SW_HIDE );
1542        }
1543 
1544        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1545            RedrawWindow( lphc->self, NULL, 0,
1546                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1547    }
1548 }
1549 
1550 
1551 /***********************************************************************
1552  *           COMBO_Size
1553  */
1554 static void COMBO_Size( LPHEADCOMBO lphc, BOOL bRedraw )
1555   {
1556   CBCalcPlacement(lphc->self,
1557                   lphc,
1558                   &lphc->textRect,
1559                   &lphc->buttonRect,
1560                   &lphc->droppedRect);
1561 
1562   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, bRedraw );
1563 }
1564 
1565 
1566 /***********************************************************************
1567  *           COMBO_Font
1568  */
1569 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1570 {
1571   /*
1572    * Set the font
1573    */
1574   lphc->hFont = hFont;
1575 
1576   /*
1577    * Propagate to owned windows.
1578    */
1579   if( lphc->wState & CBF_EDIT )
1580       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1581   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1582 
1583   /*
1584    * Redo the layout of the control.
1585    */
1586   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1587   {
1588     CBCalcPlacement(lphc->self,
1589                     lphc,
1590                     &lphc->textRect,
1591                     &lphc->buttonRect,
1592                     &lphc->droppedRect);
1593 
1594     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1595   }
1596   else
1597   {
1598     CBForceDummyResize(lphc);
1599   }
1600 }
1601 
1602 
1603 /***********************************************************************
1604  *           COMBO_SetItemHeight
1605  */
1606 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1607 {
1608    LRESULT      lRet = CB_ERR;
1609 
1610    if( index == -1 ) /* set text field height */
1611    {
1612        if( height < 32768 )
1613        {
1614            lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1615 
1616          /*
1617           * Redo the layout of the control.
1618           */
1619          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1620          {
1621            CBCalcPlacement(lphc->self,
1622                            lphc,
1623                            &lphc->textRect,
1624                            &lphc->buttonRect,
1625                            &lphc->droppedRect);
1626 
1627            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1628          }
1629          else
1630          {
1631            CBForceDummyResize(lphc);
1632          }
1633 
1634            lRet = height;
1635        }
1636    }
1637    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1638        lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1639    return lRet;
1640 }
1641 
1642 /***********************************************************************
1643  *           COMBO_SelectString
1644  */
1645 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1646 {
1647    INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
1648                          SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1649    if( index >= 0 )
1650    {
1651      if( lphc->wState & CBF_EDIT )
1652        CBUpdateEdit( lphc, index );
1653      else
1654      {
1655        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1656      }
1657    }
1658    return (LRESULT)index;
1659 }
1660 
1661 /***********************************************************************
1662  *           COMBO_LButtonDown
1663  */
1664 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1665 {
1666    POINT     pt;
1667    BOOL      bButton;
1668    HWND      hWnd = lphc->self;
1669 
1670    pt.x = (short)LOWORD(lParam);
1671    pt.y = (short)HIWORD(lParam);
1672    bButton = PtInRect(&lphc->buttonRect, pt);
1673 
1674    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1675        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1676    {
1677        lphc->wState |= CBF_BUTTONDOWN;
1678        if( lphc->wState & CBF_DROPPED )
1679        {
1680            /* got a click to cancel selection */
1681 
1682            lphc->wState &= ~CBF_BUTTONDOWN;
1683            CBRollUp( lphc, TRUE, FALSE );
1684            if( !IsWindow( hWnd ) ) return;
1685 
1686            if( lphc->wState & CBF_CAPTURE )
1687            {
1688                lphc->wState &= ~CBF_CAPTURE;
1689                ReleaseCapture();
1690            }
1691        }
1692        else
1693        {
1694            /* drop down the listbox and start tracking */
1695 
1696            lphc->wState |= CBF_CAPTURE;
1697            SetCapture( hWnd );
1698            CBDropDown( lphc );
1699        }
1700        if( bButton ) CBRepaintButton( lphc );
1701    }
1702 }
1703 
1704 /***********************************************************************
1705  *           COMBO_LButtonUp
1706  *
1707  * Release capture and stop tracking if needed.
1708  */
1709 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1710 {
1711    if( lphc->wState & CBF_CAPTURE )
1712    {
1713        lphc->wState &= ~CBF_CAPTURE;
1714        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1715        {
1716            INT index = CBUpdateLBox( lphc, TRUE );
1717            /* Update edit only if item is in the list */
1718            if(index >= 0)
1719            {
1720                lphc->wState |= CBF_NOLBSELECT;
1721                CBUpdateEdit( lphc, index );
1722                lphc->wState &= ~CBF_NOLBSELECT;
1723            }
1724        }
1725        ReleaseCapture();
1726        SetCapture(lphc->hWndLBox);
1727    }
1728 
1729    if( lphc->wState & CBF_BUTTONDOWN )
1730    {
1731        lphc->wState &= ~CBF_BUTTONDOWN;
1732        CBRepaintButton( lphc );
1733    }
1734 }
1735 
1736 /***********************************************************************
1737  *           COMBO_MouseMove
1738  *
1739  * Two things to do - track combo button and release capture when
1740  * pointer goes into the listbox.
1741  */
1742 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1743 {
1744    POINT  pt;
1745    RECT   lbRect;
1746 
1747    pt.x = (short)LOWORD(lParam);
1748    pt.y = (short)HIWORD(lParam);
1749 
1750    if( lphc->wState & CBF_BUTTONDOWN )
1751    {
1752      BOOL bButton;
1753 
1754      bButton = PtInRect(&lphc->buttonRect, pt);
1755 
1756      if( !bButton )
1757      {
1758        lphc->wState &= ~CBF_BUTTONDOWN;
1759        CBRepaintButton( lphc );
1760      }
1761    }
1762 
1763    GetClientRect( lphc->hWndLBox, &lbRect );
1764    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1765    if( PtInRect(&lbRect, pt) )
1766    {
1767        lphc->wState &= ~CBF_CAPTURE;
1768        ReleaseCapture();
1769        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1770 
1771        /* hand over pointer tracking */
1772        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1773    }
1774 }
1775 
1776 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1777 {
1778     if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1779         return FALSE;
1780 
1781     pcbi->rcItem = lphc->textRect;
1782     pcbi->rcButton = lphc->buttonRect;
1783     pcbi->stateButton = 0;
1784     if (lphc->wState & CBF_BUTTONDOWN)
1785         pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1786     if (IsRectEmpty(&lphc->buttonRect))
1787         pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1788     pcbi->hwndCombo = lphc->self;
1789     pcbi->hwndItem = lphc->hWndEdit;
1790     pcbi->hwndList = lphc->hWndLBox;
1791     return TRUE;
1792 }
1793 
1794 static char *strdupA(LPCSTR str)
1795 {
1796     char *ret;
1797     DWORD len;
1798 
1799     if(!str) return NULL;
1800 
1801     len = strlen(str);
1802     ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1803     memcpy(ret, str, len + 1);
1804     return ret;
1805 }
1806 
1807 /***********************************************************************
1808  *           ComboWndProc_common
1809  */
1810 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1811 {
1812       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1813 
1814       TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1815             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1816 
1817       if (!IsWindow(hwnd)) return 0;
1818 
1819       if( lphc || message == WM_NCCREATE )
1820       switch(message)
1821       {
1822 
1823         /* System messages */
1824 
1825         case WM_NCCREATE:
1826         {
1827                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1828                                        ((LPCREATESTRUCTA)lParam)->style;
1829                 return COMBO_NCCreate(hwnd, style);
1830         }
1831         case WM_NCDESTROY:
1832                 COMBO_NCDestroy(lphc);
1833                 break;/* -> DefWindowProc */
1834 
1835         case WM_CREATE:
1836         {
1837                 HWND hwndParent;
1838                 LONG style;
1839                 if(unicode)
1840                 {
1841                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1842                     style = ((LPCREATESTRUCTW)lParam)->style;
1843                 }
1844                 else
1845                 {
1846                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1847                     style = ((LPCREATESTRUCTA)lParam)->style;
1848                 }
1849                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1850         }
1851 
1852         case WM_PRINTCLIENT:
1853                 /* Fallthrough */
1854         case WM_PAINT:
1855                 /* wParam may contain a valid HDC! */
1856                 return  COMBO_Paint(lphc, (HDC)wParam);
1857 
1858         case WM_ERASEBKGND:
1859                 /* do all painting in WM_PAINT like Windows does */
1860                 return 1;
1861 
1862         case WM_GETDLGCODE:
1863         {
1864                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1865                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1866                 {
1867                    int vk = (int)((LPMSG)lParam)->wParam;
1868 
1869                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1870                        result |= DLGC_WANTMESSAGE;
1871                 }
1872                 return  result;
1873         }
1874         case WM_WINDOWPOSCHANGING:
1875                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1876     case WM_WINDOWPOSCHANGED:
1877         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1878          * In that case, the Combobox itself will not be resized, so we won't
1879          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1880          * do it here.
1881          */
1882         /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1883          * Z-order based painting.
1884          */
1885         /* fall through */
1886         case WM_SIZE:
1887                 if( lphc->hWndLBox &&
1888                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
1889                 return  TRUE;
1890         case WM_SETFONT:
1891                 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1892                 return  TRUE;
1893         case WM_GETFONT:
1894                 return  (LRESULT)lphc->hFont;
1895         case WM_SETFOCUS:
1896                if( lphc->wState & CBF_EDIT ) {
1897                    SetFocus( lphc->hWndEdit );
1898                    /* The first time focus is received, select all the text */
1899                    if( !(lphc->wState & CBF_BEENFOCUSED) ) {
1900                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1901                        lphc->wState |= CBF_BEENFOCUSED;
1902                    }
1903                }
1904                 else
1905                     COMBO_SetFocus( lphc );
1906                 return  TRUE;
1907         case WM_KILLFOCUS:
1908             {
1909                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1910                 if( !hwndFocus ||
1911                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1912                     COMBO_KillFocus( lphc );
1913                 return  TRUE;
1914             }
1915         case WM_COMMAND:
1916                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1917         case WM_GETTEXT:
1918             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1919                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1920         case WM_SETTEXT:
1921         case WM_GETTEXTLENGTH:
1922         case WM_CLEAR:
1923                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1924                 {
1925                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1926                     if (j == -1) return 0;
1927                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1928                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1929                 }
1930                 else if( lphc->wState & CBF_EDIT )
1931                 {
1932                     LRESULT ret;
1933                     lphc->wState |= CBF_NOEDITNOTIFY;
1934                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1935                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1936                     lphc->wState &= ~CBF_NOEDITNOTIFY;
1937                     return ret;
1938                 }
1939                 else return CB_ERR;
1940         case WM_CUT:
1941         case WM_PASTE:
1942         case WM_COPY:
1943                 if( lphc->wState & CBF_EDIT )
1944                 {
1945                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1946                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1947                 }
1948                 else return  CB_ERR;
1949 
1950         case WM_DRAWITEM:
1951         case WM_DELETEITEM:
1952         case WM_COMPAREITEM:
1953         case WM_MEASUREITEM:
1954                 return COMBO_ItemOp(lphc, message, lParam);
1955         case WM_ENABLE:
1956                 if( lphc->wState & CBF_EDIT )
1957                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1958                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1959 
1960                 /* Force the control to repaint when the enabled state changes. */
1961                 InvalidateRect(lphc->self, NULL, TRUE);
1962                 return  TRUE;
1963         case WM_SETREDRAW:
1964                 if( wParam )
1965                     lphc->wState &= ~CBF_NOREDRAW;
1966                 else
1967                     lphc->wState |= CBF_NOREDRAW;
1968 
1969                 if( lphc->wState & CBF_EDIT )
1970                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1971                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1972                 return  0;
1973         case WM_SYSKEYDOWN:
1974                 if( KEYDATA_ALT & HIWORD(lParam) )
1975                     if( wParam == VK_UP || wParam == VK_DOWN )
1976                         COMBO_FlipListbox( lphc, FALSE, FALSE );
1977                 return  0;
1978 
1979         case WM_KEYDOWN:
1980                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1981                      (lphc->wState & CBF_DROPPED))
1982                 {
1983                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1984                    return TRUE;
1985                 }
1986                else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1987                {
1988                   COMBO_FlipListbox( lphc, FALSE, FALSE );
1989                   return TRUE;
1990                }
1991                /* fall through */
1992         case WM_CHAR:
1993         case WM_IME_CHAR:
1994         {
1995                 HWND hwndTarget;
1996 
1997                 if( lphc->wState & CBF_EDIT )
1998                     hwndTarget = lphc->hWndEdit;
1999                 else
2000                     hwndTarget = lphc->hWndLBox;
2001 
2002                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2003                                  SendMessageA(hwndTarget, message, wParam, lParam);
2004         }
2005         case WM_LBUTTONDOWN:
2006                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2007                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2008                 return  TRUE;
2009         case WM_LBUTTONUP:
2010                 COMBO_LButtonUp( lphc );
2011                 return  TRUE;
2012         case WM_MOUSEMOVE:
2013                 if( lphc->wState & CBF_CAPTURE )
2014                     COMBO_MouseMove( lphc, wParam, lParam );
2015                 return  TRUE;
2016 
2017         case WM_MOUSEWHEEL:
2018                 if (wParam & (MK_SHIFT | MK_CONTROL))
2019                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2020                                      DefWindowProcA(hwnd, message, wParam, lParam);
2021 
2022                 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2023                 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2024                 return TRUE;
2025 
2026         /* Combo messages */
2027 
2028         case CB_ADDSTRING:
2029                 if( unicode )
2030                 {
2031                     if( lphc->dwStyle & CBS_LOWERCASE )
2032                         CharLowerW((LPWSTR)lParam);
2033                     else if( lphc->dwStyle & CBS_UPPERCASE )
2034                         CharUpperW((LPWSTR)lParam);
2035                     return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2036                 }
2037                 else /* unlike the unicode version, the ansi version does not overwrite
2038                         the string if converting case */
2039                 {
2040                     char *string = NULL;
2041                     LRESULT ret;
2042                     if( lphc->dwStyle & CBS_LOWERCASE )
2043                     {
2044                         string = strdupA((LPSTR)lParam);
2045                         CharLowerA(string);
2046                     }
2047 
2048                     else if( lphc->dwStyle & CBS_UPPERCASE )
2049                     {
2050                         string = strdupA((LPSTR)lParam);
2051                         CharUpperA(string);
2052                     }
2053 
2054                     ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2055                     HeapFree(GetProcessHeap(), 0, string);
2056                     return ret;
2057                 }
2058         case CB_INSERTSTRING:
2059                 if( unicode )
2060                 {
2061                     if( lphc->dwStyle & CBS_LOWERCASE )
2062                         CharLowerW((LPWSTR)lParam);
2063                     else if( lphc->dwStyle & CBS_UPPERCASE )
2064                         CharUpperW((LPWSTR)lParam);
2065                     return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2066                 }
2067                 else
2068                 {
2069                     if( lphc->dwStyle & CBS_LOWERCASE )
2070                         CharLowerA((LPSTR)lParam);
2071                     else if( lphc->dwStyle & CBS_UPPERCASE )
2072                         CharUpperA((LPSTR)lParam);
2073 
2074                     return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2075                 }
2076         case CB_DELETESTRING:
2077                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2078                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2079         case CB_SELECTSTRING:
2080                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2081         case CB_FINDSTRING:
2082                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2083                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2084         case CB_FINDSTRINGEXACT:
2085                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2086                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2087         case CB_SETITEMHEIGHT:
2088                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2089         case CB_GETITEMHEIGHT:
2090                 if( (INT)wParam >= 0 )  /* listbox item */
2091                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2092                 return  CBGetTextAreaHeight(hwnd, lphc);
2093         case CB_RESETCONTENT:
2094                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2095                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2096                 {
2097                     static const WCHAR empty_stringW[] = { 0 };
2098                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2099                 }
2100                 else
2101                     InvalidateRect(lphc->self, NULL, TRUE);
2102                 return  TRUE;
2103         case CB_INITSTORAGE:
2104                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2105         case CB_GETHORIZONTALEXTENT:
2106                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2107         case CB_SETHORIZONTALEXTENT:
2108                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2109         case CB_GETTOPINDEX:
2110                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2111         case CB_GETLOCALE:
2112                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2113         case CB_SETLOCALE:
2114                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2115         case CB_SETDROPPEDWIDTH:
2116                 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
2117                     (INT)wParam >= 32768 )
2118                     return CB_ERR;
2119                 /* new value must be higher than combobox width */
2120                 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2121                     lphc->droppedWidth = wParam;
2122                 else if(wParam)
2123                     lphc->droppedWidth = 0;
2124 
2125                 /* recalculate the combobox area */
2126                 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
2127 
2128                 /* fall through */
2129         case CB_GETDROPPEDWIDTH:
2130                 if( lphc->droppedWidth )
2131                     return  lphc->droppedWidth;
2132                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2133         case CB_GETDROPPEDCONTROLRECT:
2134                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2135                 return  CB_OKAY;
2136         case CB_GETDROPPEDSTATE:
2137                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2138         case CB_DIR:
2139                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2140                                  SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2141 
2142         case CB_SHOWDROPDOWN:
2143                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2144                 {
2145                     if( wParam )
2146                     {
2147                         if( !(lphc->wState & CBF_DROPPED) )
2148                             CBDropDown( lphc );
2149                     }
2150                     else
2151                         if( lphc->wState & CBF_DROPPED )
2152                             CBRollUp( lphc, FALSE, TRUE );
2153                 }
2154                 return  TRUE;
2155         case CB_GETCOUNT:
2156                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2157         case CB_GETCURSEL:
2158                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2159         case CB_SETCURSEL:
2160                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2161                 if( lParam >= 0 )
2162                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2163 
2164                 /* no LBN_SELCHANGE in this case, update manually */
2165                 if( lphc->wState & CBF_EDIT )
2166                     CBUpdateEdit( lphc, (INT)wParam );
2167                 else
2168                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2169                 lphc->wState &= ~CBF_SELCHANGE;
2170                 return  lParam;
2171         case CB_GETLBTEXT:
2172                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2173                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2174         case CB_GETLBTEXTLEN:
2175                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2176                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2177         case CB_GETITEMDATA:
2178                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2179         case CB_SETITEMDATA:
2180                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2181         case CB_GETEDITSEL:
2182                 /* Edit checks passed parameters itself */
2183                 if( lphc->wState & CBF_EDIT )
2184                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2185                 return  CB_ERR;
2186         case CB_SETEDITSEL:
2187                 if( lphc->wState & CBF_EDIT )
2188                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2189                           (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2190                 return  CB_ERR;
2191         case CB_SETEXTENDEDUI:
2192                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2193                     return  CB_ERR;
2194                 if( wParam )
2195                     lphc->wState |= CBF_EUI;
2196                 else lphc->wState &= ~CBF_EUI;
2197                 return  CB_OKAY;
2198         case CB_GETEXTENDEDUI:
2199                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2200         case CB_GETCOMBOBOXINFO:
2201                 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2202         case CB_LIMITTEXT:
2203                 if( lphc->wState & CBF_EDIT )
2204                         return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2205                 return  TRUE;
2206         default:
2207                 if (message >= WM_USER)
2208                     WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2209                         message - WM_USER, wParam, lParam );
2210                 break;
2211       }
2212       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2213                        DefWindowProcA(hwnd, message, wParam, lParam);
2214 }
2215 
2216 /*************************************************************************
2217  *           GetComboBoxInfo   (USER32.@)
2218  */
2219 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
2220                             PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2221 {
2222     TRACE("(%p, %p)\n", hwndCombo, pcbi);
2223     return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
2224 }
2225 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.