combo.c 68 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 * Combo controls
3
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4
 * Copyright 1997 Alex Korobka
5
 *
6 7 8 9 10 11 12 13 14 15 16 17
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 21 22 23 24 25 26 27 28 29 30 31 32
 * NOTES
 *
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features, or bugs, please note them below.
 * 
 * TODO:
 *   - ComboBox_[GS]etMinVisible()
 *   - CB_GETMINVISIBLE, CB_SETMINVISIBLE
 *   - CB_SETTOPINDEX
Alexandre Julliard's avatar
Alexandre Julliard committed
33
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
34

35
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
36
#include <string.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
37

38
#include "windef.h"
39
#include "winbase.h"
40
#include "wingdi.h"
41
#include "winuser.h"
42
#include "wine/winuser16.h"
43
#include "wine/unicode.h"
44
#include "user_private.h"
45
#include "win.h"
46
#include "controls.h"
47 48
#include "winreg.h"
#include "winternl.h"
49
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
50

51
WINE_DEFAULT_DEBUG_CHANNEL(combo);
52

Alexandre Julliard's avatar
Alexandre Julliard committed
53 54 55
  /* bits in the dwKeyData */
#define KEYDATA_ALT             0x2000
#define KEYDATA_PREVSTATE       0x4000
Alexandre Julliard's avatar
Alexandre Julliard committed
56

Alexandre Julliard's avatar
Alexandre Julliard committed
57 58 59
/*
 * Additional combo box definitions
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
60

Alexandre Julliard's avatar
Alexandre Julliard committed
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 66 67 68

#define CB_DISABLED( lphc )   (!IsWindowEnabled((lphc)->self))
#define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
#define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
#define CB_HWND( lphc )       ((lphc)->self)
69
#define CB_GETTYPE( lphc )    ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
Alexandre Julliard's avatar
Alexandre Julliard committed
70

71 72
#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)

73 74 75
/*
 * Drawing globals
 */
76
static HBITMAP 	hComboBmp = 0;
77 78 79
static UINT	CBitHeight, CBitWidth;

/*
80
 * Look and feel dependent "constants"
81
 */
82 83

#define COMBO_YBORDERGAP         5
84 85 86 87
#define COMBO_XBORDERSIZE()      2
#define COMBO_YBORDERSIZE()      2
#define COMBO_EDITBUTTONSPACE()  0
#define EDIT_CONTROL_PADDING()   1
Alexandre Julliard's avatar
Alexandre Julliard committed
88

89
static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
90
static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
91 92 93 94 95 96 97

/*********************************************************************
 * combo class descriptor
 */
const struct builtin_class_descr COMBO_builtin_class =
{
    "ComboBox",           /* name */
98
    CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style  */
99
    ComboWndProcA,        /* procA */
100
    ComboWndProcW,        /* procW */
101
    sizeof(HEADCOMBO *),  /* extra */
102
    IDC_ARROW,            /* cursor */
103 104 105 106
    0                     /* brush */
};


Alexandre Julliard's avatar
Alexandre Julliard committed
107 108 109 110 111
/***********************************************************************
 *           COMBO_Init
 *
 * Load combo button bitmap.
 */
112
static BOOL COMBO_Init(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
113
{
114
  HDC		hDC;
115

Alexandre Julliard's avatar
Alexandre Julliard committed
116
  if( hComboBmp ) return TRUE;
117
  if( (hDC = CreateCompatibleDC(0)) )
Alexandre Julliard's avatar
Alexandre Julliard committed
118
  {
119
    BOOL	bRet = FALSE;
120
    if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
Alexandre Julliard's avatar
Alexandre Julliard committed
121
    {
122 123 124
      BITMAP      bm;
      HBITMAP     hPrevB;
      RECT        r;
Alexandre Julliard's avatar
Alexandre Julliard committed
125

126
      GetObjectW( hComboBmp, sizeof(bm), &bm );
Alexandre Julliard's avatar
Alexandre Julliard committed
127 128
      CBitHeight = bm.bmHeight;
      CBitWidth  = bm.bmWidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
129

130
      TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
Alexandre Julliard's avatar
Alexandre Julliard committed
131

132
      hPrevB = SelectObject( hDC, hComboBmp);
133 134 135
      SetRect( &r, 0, 0, CBitWidth, CBitHeight );
      InvertRect( hDC, &r );
      SelectObject( hDC, hPrevB );
Alexandre Julliard's avatar
Alexandre Julliard committed
136 137
      bRet = TRUE;
    }
138
    DeleteDC( hDC );
Alexandre Julliard's avatar
Alexandre Julliard committed
139 140 141
    return bRet;
  }
  return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
142 143 144
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
145
 *           COMBO_NCCreate
Alexandre Julliard's avatar
Alexandre Julliard committed
146
 */
147
static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
Alexandre Julliard's avatar
Alexandre Julliard committed
148
{
149
    LPHEADCOMBO lphc;
Alexandre Julliard's avatar
Alexandre Julliard committed
150

151 152 153
    if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
    {
        lphc->self = hwnd;
154
        SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
155 156

       /* some braindead apps do try to use scrollbar/border flags */
Alexandre Julliard's avatar
Alexandre Julliard committed
157

158
	lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
159
        SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
Alexandre Julliard's avatar
Alexandre Julliard committed
160

161 162 163 164
	/*
	 * We also have to remove the client edge style to make sure
	 * we don't end-up with a non client area.
	 */
165 166
        SetWindowLongW( hwnd, GWL_EXSTYLE,
                        GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
167

168
	if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
Alexandre Julliard's avatar
Alexandre Julliard committed
169
              lphc->dwStyle |= CBS_HASSTRINGS;
170
	if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
Alexandre Julliard's avatar
Alexandre Julliard committed
171
	      lphc->wState |= CBF_NOTIFY;
Alexandre Julliard's avatar
Alexandre Julliard committed
172

173
        TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
174
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
175
    }
176
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
177 178 179
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
180
 *           COMBO_NCDestroy
Alexandre Julliard's avatar
Alexandre Julliard committed
181
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
182
static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
183
{
Alexandre Julliard's avatar
Alexandre Julliard committed
184

Alexandre Julliard's avatar
Alexandre Julliard committed
185 186
   if( lphc )
   {
187
       TRACE("[%p]: freeing storage\n", lphc->self);
Alexandre Julliard's avatar
Alexandre Julliard committed
188

189
       if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
190
   	   DestroyWindow( lphc->hWndLBox );
Alexandre Julliard's avatar
Alexandre Julliard committed
191

192
       SetWindowLongPtrW( lphc->self, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
193 194
       HeapFree( GetProcessHeap(), 0, lphc );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
195
   return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
196 197
}

198 199 200
/***********************************************************************
 *           CBGetTextAreaHeight
 *
201
 * This method will calculate the height of the text area of the
202
 * combobox.
203
 * The height of the text area is set in two ways.
204
 * It can be set explicitly through a combobox message or through a
205 206 207 208 209 210 211 212 213
 * WM_MEASUREITEM callback.
 * If this is not the case, the height is set to 13 dialog units.
 * This height was determined through experimentation.
 */
static INT CBGetTextAreaHeight(
  HWND        hwnd,
  LPHEADCOMBO lphc)
{
  INT iTextItemHeight;
214

215 216 217 218 219 220
  if( lphc->editHeight ) /* explicitly set height */
  {
    iTextItemHeight = lphc->editHeight;
  }
  else
  {
221
    TEXTMETRICW tm;
222 223 224
    HDC         hDC       = GetDC(hwnd);
    HFONT       hPrevFont = 0;
    INT         baseUnitY;
225

226 227
    if (lphc->hFont)
      hPrevFont = SelectObject( hDC, lphc->hFont );
228

229
    GetTextMetricsW(hDC, &tm);
230

231
    baseUnitY = tm.tmHeight;
232

233 234
    if( hPrevFont )
      SelectObject( hDC, hPrevFont );
235

236
    ReleaseDC(hwnd, hDC);
237

238 239 240 241 242 243 244 245 246
    iTextItemHeight = ((13 * baseUnitY) / 8);

    /*
     * This "formula" calculates the height of the complete control.
     * To calculate the height of the text area, we have to remove the
     * borders.
     */
    iTextItemHeight -= 2*COMBO_YBORDERSIZE();
  }
247

248 249 250 251 252 253 254 255 256 257
  /*
   * Check the ownerdraw case if we haven't asked the parent the size
   * of the item yet.
   */
  if ( CB_OWNERDRAWN(lphc) &&
       (lphc->wState & CBF_MEASUREITEM) )
  {
    MEASUREITEMSTRUCT measureItem;
    RECT              clientRect;
    INT               originalItemHeight = iTextItemHeight;
258
    UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
259

260 261 262 263
    /*
     * We use the client rect for the width of the item.
     */
    GetClientRect(hwnd, &clientRect);
264

265
    lphc->wState &= ~CBF_MEASUREITEM;
266

267 268 269 270
    /*
     * Send a first one to measure the size of the text area
     */
    measureItem.CtlType    = ODT_COMBOBOX;
271
    measureItem.CtlID      = id;
272 273 274 275
    measureItem.itemID     = -1;
    measureItem.itemWidth  = clientRect.right;
    measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
    measureItem.itemData   = 0;
276
    SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
277 278 279 280 281 282 283 284 285
    iTextItemHeight = 6 + measureItem.itemHeight;

    /*
     * Send a second one in the case of a fixed ownerdraw list to calculate the
     * size of the list items. (we basically do this on behalf of the listbox)
     */
    if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
    {
      measureItem.CtlType    = ODT_COMBOBOX;
286
      measureItem.CtlID      = id;
287 288 289 290
      measureItem.itemID     = 0;
      measureItem.itemWidth  = clientRect.right;
      measureItem.itemHeight = originalItemHeight;
      measureItem.itemData   = 0;
291
      SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
292 293
      lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
    }
294

295
    /*
296
     * Keep the size for the next time
297 298 299
     */
    lphc->editHeight = iTextItemHeight;
  }
300

301
  return iTextItemHeight;
302 303
}

304 305 306 307 308 309 310 311 312 313 314 315 316
/***********************************************************************
 *           CBForceDummyResize
 *
 * The dummy resize is used for listboxes that have a popup to trigger
 * a re-arranging of the contents of the combobox and the recalculation
 * of the size of the "real" control window.
 */
static void CBForceDummyResize(
  LPHEADCOMBO lphc)
{
  RECT windowRect;
  int newComboHeight;

317
  newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
318

319
  GetWindowRect(lphc->self, &windowRect);
320 321 322 323 324 325 326 327 328

  /*
   * We have to be careful, resizing a combobox also has the meaning that the
   * dropped rect will be resized. In this case, we want to trigger a resize
   * to recalculate layout but we don't want to change the dropped rectangle
   * So, we pass the height of text area of control as the height.
   * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
   * message.
   */
329
  SetWindowPos( lphc->self,
330
		NULL,
331 332 333 334 335
		0, 0,
		windowRect.right  - windowRect.left,
		newComboHeight,
		SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
}
Alexandre Julliard's avatar
Alexandre Julliard committed
336

Alexandre Julliard's avatar
Alexandre Julliard committed
337
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
338 339 340
 *           CBCalcPlacement
 *
 * Set up component coordinates given valid lphc->RectCombo.
Alexandre Julliard's avatar
Alexandre Julliard committed
341
 */
342 343 344
static void CBCalcPlacement(
  HWND        hwnd,
  LPHEADCOMBO lphc,
345 346
  LPRECT      lprEdit,
  LPRECT      lprButton,
347
  LPRECT      lprLB)
Alexandre Julliard's avatar
Alexandre Julliard committed
348
{
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
  /*
   * Again, start with the client rectangle.
   */
  GetClientRect(hwnd, lprEdit);

  /*
   * Remove the borders
   */
  InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());

  /*
   * Chop off the bottom part to fit with the height of the text area.
   */
  lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);

  /*
   * The button starts the same vertical position as the text area.
   */
  CopyRect(lprButton, lprEdit);

  /*
   * If the combobox is "simple" there is no button.
   */
372
  if( CB_GETTYPE(lphc) == CBS_SIMPLE )
373 374 375 376 377 378 379 380 381 382 383
    lprButton->left = lprButton->right = lprButton->bottom = 0;
  else
  {
    /*
     * Let's assume the combobox button is the same width as the
     * scrollbar button.
     * size the button horizontally and cut-off the text area.
     */
    lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
    lprEdit->right  = lprButton->left;
  }
384

385 386 387 388 389 390 391 392
  /*
   * In the case of a dropdown, there is an additional spacing between the
   * text area and the button.
   */
  if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
  {
    lprEdit->right -= COMBO_EDITBUTTONSPACE();
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
393

394 395 396 397 398 399 400
  /*
   * If we have an edit control, we space it away from the borders slightly.
   */
  if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
  {
    InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
  }
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
  /*
   * Adjust the size of the listbox popup.
   */
  if( CB_GETTYPE(lphc) == CBS_SIMPLE )
  {
    /*
     * Use the client rectangle to initialize the listbox rectangle
     */
    GetClientRect(hwnd, lprLB);

    /*
     * Then, chop-off the top part.
     */
    lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
  }
  else
  {
    /*
     * Make sure the dropped width is as large as the combobox itself.
     */
    if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
    {
      lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());

      /*
       * In the case of a dropdown, the popup listbox is offset to the right.
428
       * so, we want to make sure it's flush with the right side of the
429 430 431 432 433 434
       * combobox
       */
      if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
	lprLB->right -= COMBO_EDITBUTTONSPACE();
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
435
       lprLB->right = lprLB->left + lphc->droppedWidth;
436
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
437

438 439 440 441
  /* don't allow negative window width */
  if (lprEdit->right < lprEdit->left)
    lprEdit->right = lprEdit->left;

442
  TRACE("\ttext\t= (%ld,%ld-%ld,%ld)\n",
443
	lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
444

445
  TRACE("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
446
	lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
447

448
  TRACE("\tlbox\t= (%ld,%ld-%ld,%ld)\n",
449
	lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
450 451 452
}

/***********************************************************************
453
 *           CBGetDroppedControlRect
Alexandre Julliard's avatar
Alexandre Julliard committed
454
 */
455
static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
Alexandre Julliard's avatar
Alexandre Julliard committed
456
{
457 458
    /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
     of the combo box and the lower right corner of the listbox */
459

460
    GetWindowRect(lphc->self, lpRect);
461 462 463 464

    lpRect->right =  lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
    lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;

465 466 467
}

/***********************************************************************
468
 *           COMBO_WindowPosChanging
469 470 471 472 473 474 475 476 477 478 479 480
 */
static LRESULT COMBO_WindowPosChanging(
  HWND        hwnd,
  LPHEADCOMBO lphc,
  WINDOWPOS*  posChanging)
{
  /*
   * We need to override the WM_WINDOWPOSCHANGING method to handle all
   * the non-simple comboboxes. The problem is that those controls are
   * always the same height. We have to make sure they are not resized
   * to another value.
   */
481
  if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
482
       ((posChanging->flags & SWP_NOSIZE) == 0) )
483
  {
484 485 486
    int newComboHeight;

    newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
487
                      2*COMBO_YBORDERSIZE();
488 489 490 491

    /*
     * Resizing a combobox has another side effect, it resizes the dropped
     * rectangle as well. However, it does it only if the new height for the
492
     * combobox is different from the height it should have. In other words,
493 494 495 496 497 498
     * if the application resizing the combobox only had the intention to resize
     * the actual control, for example, to do the layout of a dialog that is
     * resized, the height of the dropdown is not changed.
     */
    if (posChanging->cy != newComboHeight)
    {
499
	TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%ld, oldtop=%ld\n",
500 501
	      posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
	      lphc->droppedRect.top);
502 503 504 505
      lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;

      posChanging->cy = newComboHeight;
    }
506 507 508
  }

  return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
509 510 511
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
512
 *           COMBO_Create
Alexandre Julliard's avatar
Alexandre Julliard committed
513
 */
514 515
static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
                             BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
516
{
517 518
  static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
  static const WCHAR editName[] = {'E','d','i','t',0};
Alexandre Julliard's avatar
Alexandre Julliard committed
519

Alexandre Julliard's avatar
Alexandre Julliard committed
520
  if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
521
  if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
522

523
  lphc->owner = hwndParent;
Alexandre Julliard's avatar
Alexandre Julliard committed
524

525 526 527 528 529 530 531 532 533 534 535
  /*
   * The item height and dropped width are not set when the control
   * is created.
   */
  lphc->droppedWidth = lphc->editHeight = 0;

  /*
   * The first time we go through, we want to measure the ownerdraw item
   */
  lphc->wState |= CBF_MEASUREITEM;

Alexandre Julliard's avatar
Alexandre Julliard committed
536
  /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
Alexandre Julliard's avatar
Alexandre Julliard committed
537

538
  if( lphc->owner || !(style & WS_VISIBLE) )
Alexandre Julliard's avatar
Alexandre Julliard committed
539
  {
540 541
      UINT lbeStyle   = 0;
      UINT lbeExStyle = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
542

543 544 545 546 547
      /*
       * Initialize the dropped rect to the size of the client area of the
       * control and then, force all the areas of the combobox to be
       * recalculated.
       */
548 549
      GetClientRect( hwnd, &lphc->droppedRect );
      CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
550 551 552 553 554 555 556

      /*
       * Adjust the position of the popup listbox if it's necessary
       */
      if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
      {
	lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE();
Alexandre Julliard's avatar
Alexandre Julliard committed
557

558 559 560 561 562 563
	/*
	 * If it's a dropdown, the listbox is offset
	 */
	if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
	  lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();

564 565 566 567 568
        if (lphc->droppedRect.bottom < lphc->droppedRect.top)
            lphc->droppedRect.bottom = lphc->droppedRect.top;
        if (lphc->droppedRect.right < lphc->droppedRect.left)
            lphc->droppedRect.right = lphc->droppedRect.left;
        MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
569
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
570 571 572

      /* create listbox popup */

573
      lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
574
                 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
Alexandre Julliard's avatar
Alexandre Julliard committed
575 576 577 578 579 580 581 582 583

      if( lphc->dwStyle & CBS_SORT )
	lbeStyle |= LBS_SORT;
      if( lphc->dwStyle & CBS_HASSTRINGS )
	lbeStyle |= LBS_HASSTRINGS;
      if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
	lbeStyle |= LBS_NOINTEGRALHEIGHT;
      if( lphc->dwStyle & CBS_DISABLENOSCROLL )
	lbeStyle |= LBS_DISABLENOSCROLL;
584

Alexandre Julliard's avatar
Alexandre Julliard committed
585
      if( CB_GETTYPE(lphc) == CBS_SIMPLE ) 	/* child listbox */
586
      {
587
	lbeStyle |= WS_VISIBLE;
588 589 590 591 592

	/*
	 * In win 95 look n feel, the listbox in the simple combobox has
	 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
	 */
593 594
	lbeStyle   &= ~WS_BORDER;
	lbeExStyle |= WS_EX_CLIENTEDGE;
595
      }
596 597 598 599
      else
      {
        lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
600

601 602 603 604 605 606 607
      if (unicode)
          lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
                                           lphc->droppedRect.left,
                                           lphc->droppedRect.top,
                                           lphc->droppedRect.right - lphc->droppedRect.left,
                                           lphc->droppedRect.bottom - lphc->droppedRect.top,
                                           hwnd, (HMENU)ID_CB_LISTBOX,
608
                                           (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
609 610 611 612 613 614 615
      else
          lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
                                           lphc->droppedRect.left,
                                           lphc->droppedRect.top,
                                           lphc->droppedRect.right - lphc->droppedRect.left,
                                           lphc->droppedRect.bottom - lphc->droppedRect.top,
                                           hwnd, (HMENU)ID_CB_LISTBOX,
616
                                           (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
617

Alexandre Julliard's avatar
Alexandre Julliard committed
618 619
      if( lphc->hWndLBox )
      {
620
	  BOOL	bEdit = TRUE;
621
	  lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
622

623
	  if( lphc->wState & CBF_EDIT )
Alexandre Julliard's avatar
Alexandre Julliard committed
624 625 626 627 628 629 630 631 632
	  {
	      if( lphc->dwStyle & CBS_OEMCONVERT )
		  lbeStyle |= ES_OEMCONVERT;
	      if( lphc->dwStyle & CBS_AUTOHSCROLL )
		  lbeStyle |= ES_AUTOHSCROLL;
	      if( lphc->dwStyle & CBS_LOWERCASE )
		  lbeStyle |= ES_LOWERCASE;
	      else if( lphc->dwStyle & CBS_UPPERCASE )
		  lbeStyle |= ES_UPPERCASE;
633

634
              if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
635

636 637 638 639 640 641
              if (unicode)
                  lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
                                                   lphc->textRect.left, lphc->textRect.top,
                                                   lphc->textRect.right - lphc->textRect.left,
                                                   lphc->textRect.bottom - lphc->textRect.top,
                                                   hwnd, (HMENU)ID_CB_EDIT,
642
                                                   (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
643 644 645 646 647 648
              else
                  lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
                                                   lphc->textRect.left, lphc->textRect.top,
                                                   lphc->textRect.right - lphc->textRect.left,
                                                   lphc->textRect.bottom - lphc->textRect.top,
                                                   hwnd, (HMENU)ID_CB_EDIT,
649
                                                   (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
650 651 652

	      if( !lphc->hWndEdit )
		bEdit = FALSE;
653
	  }
Alexandre Julliard's avatar
Alexandre Julliard committed
654 655 656

          if( bEdit )
	  {
657 658
	    if( CB_GETTYPE(lphc) != CBS_SIMPLE )
	    {
659 660
              /* Now do the trick with parent */
	      SetParent(lphc->hWndLBox, HWND_DESKTOP);
661
              /*
662 663 664 665
               * If the combo is a dropdown, we must resize the control
	       * to fit only the text area and button. To do this,
	       * we send a dummy resize and the WM_WINDOWPOSCHANGING message
	       * will take care of setting the height for us.
666
               */
667
	      CBForceDummyResize(lphc);
668
	    }
669

670
	    TRACE("init done\n");
671
	    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
672
	  }
673 674 675
	  ERR("edit control failure.\n");
      } else ERR("listbox failure.\n");
  } else ERR("no owner for visible combo.\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
676 677 678 679

  /* CreateWindow() will send WM_NCDESTROY to cleanup */

  return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
680 681 682
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
683 684 685
 *           CBPaintButton
 *
 * Paint combo button (normal, pressed, and disabled states).
Alexandre Julliard's avatar
Alexandre Julliard committed
686
 */
687
static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
Alexandre Julliard's avatar
Alexandre Julliard committed
688
{
689 690
    UINT buttonState = DFCS_SCROLLCOMBOBOX;

691
    if( lphc->wState & CBF_NOREDRAW )
692
      return;
Alexandre Julliard's avatar
Alexandre Julliard committed
693

694

695 696
    if (lphc->wState & CBF_BUTTONDOWN)
	buttonState |= DFCS_PUSHED;
697

698 699
    if (CB_DISABLED(lphc))
	buttonState |= DFCS_INACTIVE;
700

701
    DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
Alexandre Julliard's avatar
Alexandre Julliard committed
702
}
Alexandre Julliard's avatar
Alexandre Julliard committed
703

Alexandre Julliard's avatar
Alexandre Julliard committed
704
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
705 706 707
 *           CBPaintText
 *
 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
Alexandre Julliard's avatar
Alexandre Julliard committed
708
 */
709
static void CBPaintText(
710
  LPHEADCOMBO lphc,
711 712
  HDC         hdc,
  RECT        rectEdit)
Alexandre Julliard's avatar
Alexandre Julliard committed
713
{
714
   INT	id, size = 0;
715
   LPWSTR pText = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
716

Alexandre Julliard's avatar
Alexandre Julliard committed
717
   if( lphc->wState & CBF_NOREDRAW ) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
718

719 720
   TRACE("\n");

721
   /* follow Windows combobox that sends a bunch of text
Alexandre Julliard's avatar
Alexandre Julliard committed
722
    * inquiries to its listbox while processing WM_PAINT. */
Alexandre Julliard's avatar
Alexandre Julliard committed
723

724
   if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
Alexandre Julliard's avatar
Alexandre Julliard committed
725
   {
726
        size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
727 728
	if (size == LB_ERR)
	  FIXME("LB_ERR probably not handled yet\n");
729
        if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
Alexandre Julliard's avatar
Alexandre Julliard committed
730
	{
731 732
            /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
	    size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
Alexandre Julliard's avatar
Alexandre Julliard committed
733 734 735
	    pText[size] = '\0';	/* just in case */
	} else return;
   }
736
   else
737 738
       if( !CB_OWNERDRAWN(lphc) )
	   return;
Alexandre Julliard's avatar
Alexandre Julliard committed
739

Alexandre Julliard's avatar
Alexandre Julliard committed
740 741
   if( lphc->wState & CBF_EDIT )
   {
742 743
        static const WCHAR empty_stringW[] = { 0 };
	if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
744
	if( lphc->wState & CBF_FOCUSED )
745
	    SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
Alexandre Julliard's avatar
Alexandre Julliard committed
746 747 748
   }
   else /* paint text field ourselves */
   {
749
     UINT	itemState = ODS_COMBOBOXEDIT;
750
     HFONT	hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
751

752 753 754 755
     /*
      * Give ourselves some space.
      */
     InflateRect( &rectEdit, -1, -1 );
756

757 758 759 760
     if( CB_OWNERDRAWN(lphc) )
     {
       DRAWITEMSTRUCT dis;
       HRGN           clipRegion;
761
       UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
762

763
       /* setup state for DRAWITEM message. Owner will highlight */
764
       if ( (lphc->wState & CBF_FOCUSED) &&
765 766 767
	    !(lphc->wState & CBF_DROPPED) )
	   itemState |= ODS_SELECTED | ODS_FOCUS;

768 769 770 771 772 773
       /*
	* Save the current clip region.
	* To retrieve the clip region, we need to create one "dummy"
	* clip region.
	*/
       clipRegion = CreateRectRgnIndirect(&rectEdit);
774

775 776 777
       if (GetClipRgn(hdc, clipRegion)!=1)
       {
	 DeleteObject(clipRegion);
778
	 clipRegion=NULL;
779
       }
780 781 782

       if (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED;

783
       dis.CtlType	= ODT_COMBOBOX;
784 785
       dis.CtlID	= ctlid;
       dis.hwndItem	= lphc->self;
786 787 788 789 790
       dis.itemAction	= ODA_DRAWENTIRE;
       dis.itemID	= id;
       dis.itemState	= itemState;
       dis.hDC		= hdc;
       dis.rcItem	= rectEdit;
791
       dis.itemData	= SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
792
					(WPARAM)id, 0 );
793

794 795 796 797 798 799
       /*
	* Clip the DC and have the parent draw the item.
	*/
       IntersectClipRect(hdc,
			 rectEdit.left,  rectEdit.top,
			 rectEdit.right, rectEdit.bottom);
800 801 802

       SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );

803 804 805
       /*
	* Reset the clipping region.
	*/
806
       SelectClipRgn(hdc, clipRegion);
807 808 809
     }
     else
     {
810 811
       static const WCHAR empty_stringW[] = { 0 };

812
       if ( (lphc->wState & CBF_FOCUSED) &&
813 814 815 816 817 818
	    !(lphc->wState & CBF_DROPPED) ) {

	   /* highlight */
	   FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
	   SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
	   SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
819 820 821 822
       }

       ExtTextOutW( hdc,
		    rectEdit.left + 1,
823
		    rectEdit.top + 1,
824
		    ETO_OPAQUE | ETO_CLIPPED,
825
		    &rectEdit,
826
		    pText ? pText : empty_stringW , size, NULL );
827

828 829 830
       if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
	 DrawFocusRect( hdc, &rectEdit );
     }
831 832

     if( hPrevFont )
833
       SelectObject(hdc, hPrevFont );
Alexandre Julliard's avatar
Alexandre Julliard committed
834
   }
835
   HeapFree( GetProcessHeap(), 0, pText );
Alexandre Julliard's avatar
Alexandre Julliard committed
836 837
}

838 839 840 841 842
/***********************************************************************
 *           CBPaintBorder
 */
static void CBPaintBorder(
  HWND        hwnd,
843
  LPHEADCOMBO lphc,
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
  HDC         hdc)
{
  RECT clientRect;

  if (CB_GETTYPE(lphc) != CBS_SIMPLE)
  {
    GetClientRect(hwnd, &clientRect);
  }
  else
  {
    CopyRect(&clientRect, &lphc->textRect);

    InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
    InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
  }

  DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
}

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
/***********************************************************************
 *           COMBO_PrepareColors
 *
 * This method will sent the appropriate WM_CTLCOLOR message to
 * prepare and setup the colors for the combo's DC.
 *
 * It also returns the brush to use for the background.
 */
static HBRUSH COMBO_PrepareColors(
  LPHEADCOMBO lphc,
  HDC         hDC)
{
  HBRUSH  hBkgBrush;

  /*
   * Get the background brush for this control.
   */
  if (CB_DISABLED(lphc))
  {
882 883
    hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
				     (WPARAM)hDC, (LPARAM)lphc->self );
884

885 886 887 888 889 890 891 892 893 894 895
    /*
     * We have to change the text color since WM_CTLCOLORSTATIC will
     * set it to the "enabled" color. This is the same behavior as the
     * edit control
     */
    SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
  }
  else
  {
    if (lphc->wState & CBF_EDIT)
    {
896 897
      hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
				       (WPARAM)hDC, (LPARAM)lphc->self );
898 899 900
    }
    else
    {
901 902
      hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX,
				       (WPARAM)hDC, (LPARAM)lphc->self );
903 904 905 906 907 908
    }
  }

  /*
   * Catch errors.
   */
909
  if( !hBkgBrush )
910 911 912 913 914
    hBkgBrush = GetSysColorBrush(COLOR_WINDOW);

  return hBkgBrush;
}

915 916 917 918
/***********************************************************************
 *           COMBO_EraseBackground
 */
static LRESULT COMBO_EraseBackground(
919
  HWND        hwnd,
920 921 922 923 924
  LPHEADCOMBO lphc,
  HDC         hParamDC)
{
  HBRUSH  hBkgBrush;
  HDC 	  hDC;
925 926 927

  if(lphc->wState & CBF_EDIT)
      return TRUE;
928

929 930
  hDC = (hParamDC) ? hParamDC
		   : GetDC(hwnd);
931 932 933
  /*
   * Retrieve the background brush
   */
934
  hBkgBrush = COMBO_PrepareColors(lphc, hDC);
935

936
  FillRect(hDC, &lphc->textRect, hBkgBrush);
937 938 939 940 941 942 943

  if (!hParamDC)
    ReleaseDC(hwnd, hDC);

  return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
944
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
945
 *           COMBO_Paint
Alexandre Julliard's avatar
Alexandre Julliard committed
946
 */
947
static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
Alexandre Julliard's avatar
Alexandre Julliard committed
948
{
949 950
  PAINTSTRUCT ps;
  HDC 	hDC;
951

Alexandre Julliard's avatar
Alexandre Julliard committed
952
  hDC = (hParamDC) ? hParamDC
953
		   : BeginPaint( lphc->self, &ps);
954

955
  TRACE("hdc=%p\n", hDC);
956

Alexandre Julliard's avatar
Alexandre Julliard committed
957
  if( hDC && !(lphc->wState & CBF_NOREDRAW) )
Alexandre Julliard's avatar
Alexandre Julliard committed
958
  {
959
      HBRUSH	hPrevBrush, hBkgBrush;
Alexandre Julliard's avatar
Alexandre Julliard committed
960

961 962 963 964
      /*
       * Retrieve the background brush and select it in the
       * DC.
       */
965
      hBkgBrush = COMBO_PrepareColors(lphc, hDC);
Alexandre Julliard's avatar
Alexandre Julliard committed
966

967
      hPrevBrush = SelectObject( hDC, hBkgBrush );
968 969 970 971

      /*
       * In non 3.1 look, there is a sunken border on the combobox
       */
972
      CBPaintBorder(lphc->self, lphc, hDC);
Alexandre Julliard's avatar
Alexandre Julliard committed
973

974 975 976
      if( !IsRectEmpty(&lphc->buttonRect) )
      {
	CBPaintButton(lphc, hDC, lphc->buttonRect);
Alexandre Julliard's avatar
Alexandre Julliard committed
977 978
      }

979 980 981 982 983 984 985
      /* paint the edit control padding area */
      if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
      {
          RECT rPadEdit = lphc->textRect;

          InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());

986
          FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
987
      }
988

Alexandre Julliard's avatar
Alexandre Julliard committed
989
      if( !(lphc->wState & CBF_EDIT) )
990 991 992 993
	CBPaintText( lphc, hDC, lphc->textRect);

      if( hPrevBrush )
	SelectObject( hDC, hPrevBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
994
  }
995

996
  if( !hParamDC )
997
    EndPaint(lphc->self, &ps);
998

Alexandre Julliard's avatar
Alexandre Julliard committed
999 1000 1001 1002
  return 0;
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1003 1004 1005
 *           CBUpdateLBox
 *
 * Select listbox entry according to the contents of the edit control.
Alexandre Julliard's avatar
Alexandre Julliard committed
1006
 */
1007
static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
Alexandre Julliard's avatar
Alexandre Julliard committed
1008
{
1009
   INT	length, idx;
1010
   LPWSTR pText = NULL;
1011

1012
   idx = LB_ERR;
1013 1014
   length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );

1015
   if( length > 0 )
1016
       pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
Alexandre Julliard's avatar
Alexandre Julliard committed
1017

1018
   TRACE("\t edit text length %i\n", length );
Alexandre Julliard's avatar
Alexandre Julliard committed
1019 1020 1021

   if( pText )
   {
1022
       if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1023
       else pText[0] = '\0';
1024
       idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1025
			     (WPARAM)(-1), (LPARAM)pText );
Alexandre Julliard's avatar
Alexandre Julliard committed
1026 1027
       HeapFree( GetProcessHeap(), 0, pText );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1028

1029 1030 1031 1032 1033
   SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);

   /* probably superfluous but Windows sends this too */
   SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
   SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1034

1035
   return idx;
Alexandre Julliard's avatar
Alexandre Julliard committed
1036 1037 1038
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1039 1040 1041
 *           CBUpdateEdit
 *
 * Copy a listbox entry to the edit control.
Alexandre Julliard's avatar
Alexandre Julliard committed
1042
 */
1043
static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
Alexandre Julliard's avatar
Alexandre Julliard committed
1044
{
1045
   INT	length;
1046
   LPWSTR pText = NULL;
1047
   static const WCHAR empty_stringW[] = { 0 };
Alexandre Julliard's avatar
Alexandre Julliard committed
1048

1049
   TRACE("\t %i\n", index );
Alexandre Julliard's avatar
Alexandre Julliard committed
1050

Alexandre Julliard's avatar
Alexandre Julliard committed
1051 1052
   if( index >= 0 ) /* got an entry */
   {
1053
       length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1054
       if( length != LB_ERR)
Alexandre Julliard's avatar
Alexandre Julliard committed
1055
       {
1056
	   if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1057
	   {
1058
		SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1059
				(WPARAM)index, (LPARAM)pText );
Alexandre Julliard's avatar
Alexandre Julliard committed
1060 1061
	   }
       }
1062
   }
1063 1064 1065 1066

   lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
   SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
   lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1067

1068
   if( lphc->wState & CBF_FOCUSED )
1069
      SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1070

1071
   HeapFree( GetProcessHeap(), 0, pText );
Alexandre Julliard's avatar
Alexandre Julliard committed
1072 1073 1074
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1075
 *           CBDropDown
1076
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1077
 * Show listbox popup.
Alexandre Julliard's avatar
Alexandre Julliard committed
1078
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1079
static void CBDropDown( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1080
{
1081
   RECT rect,r;
1082
   int nItems = 0;
1083
   int nDroppedHeight;
Alexandre Julliard's avatar
Alexandre Julliard committed
1084

1085
   TRACE("[%p]: drop down\n", lphc->self);
Alexandre Julliard's avatar
Alexandre Julliard committed
1086 1087 1088 1089 1090 1091 1092 1093

   CB_NOTIFY( lphc, CBN_DROPDOWN );

   /* set selection */

   lphc->wState |= CBF_DROPPED;
   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
   {
1094
       lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1095

1096 1097
       /* Update edit only if item is in the list */
       if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1098
	 CBUpdateEdit( lphc, lphc->droppedIndex );
Alexandre Julliard's avatar
Alexandre Julliard committed
1099 1100 1101
   }
   else
   {
1102
       lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1103

1104
       SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1105
                     (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1106
       SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1107
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1108

Alexandre Julliard's avatar
Alexandre Julliard committed
1109
   /* now set popup position */
1110
   GetWindowRect( lphc->self, &rect );
1111

1112 1113 1114 1115 1116 1117
   /*
    * If it's a dropdown, the listbox is offset
    */
   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
     rect.left += COMBO_EDITBUTTONSPACE();

1118 1119 1120 1121
  /* if the dropped height is greater than the total height of the dropped
     items list, then force the drop down list height to be the total height
     of the items in the dropped list */

1122
  /* And Remove any extra space (Best Fit) */
1123
   nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1124 1125 1126 1127
  /* if listbox length has been set directly by its handle */
   GetWindowRect(lphc->hWndLBox, &r);
   if (nDroppedHeight < r.bottom - r.top)
       nDroppedHeight = r.bottom - r.top;
1128
   nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1129 1130

   if (nItems > 0)
1131
   {
1132
      int nHeight;
1133
      int nIHeight;
1134

1135 1136 1137
      nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);

      nHeight = nIHeight*nItems;
1138 1139 1140

      if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
         nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1141 1142 1143 1144

      if (nDroppedHeight < nIHeight)
      {
            if (nItems < 5)
1145
                nDroppedHeight = (nItems+1)*nIHeight;
1146
            else
1147
                nDroppedHeight = 6*nIHeight;
1148
      }
1149
   }
1150

1151 1152 1153
   /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
   if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
      rect.bottom = rect.top - nDroppedHeight;
1154

1155
   SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1156
		 lphc->droppedRect.right - lphc->droppedRect.left,
1157 1158 1159
		 nDroppedHeight,
		 SWP_NOACTIVATE | SWP_SHOWWINDOW);

Alexandre Julliard's avatar
Alexandre Julliard committed
1160

Alexandre Julliard's avatar
Alexandre Julliard committed
1161
   if( !(lphc->wState & CBF_NOREDRAW) )
1162
     RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
Alexandre Julliard's avatar
Alexandre Julliard committed
1163
			   RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1164

Alexandre Julliard's avatar
Alexandre Julliard committed
1165
   EnableWindow( lphc->hWndLBox, TRUE );
1166
   if (GetCapture() != lphc->self)
1167
      SetCapture(lphc->hWndLBox);
Alexandre Julliard's avatar
Alexandre Julliard committed
1168 1169 1170
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1171 1172 1173
 *           CBRollUp
 *
 * Hide listbox popup.
Alexandre Julliard's avatar
Alexandre Julliard committed
1174
 */
1175
static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
Alexandre Julliard's avatar
Alexandre Julliard committed
1176
{
1177
   HWND	hWnd = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
1178

1179
   TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1180
	 lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1181

Alexandre Julliard's avatar
Alexandre Julliard committed
1182 1183
   CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );

1184
   if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
Alexandre Julliard's avatar
Alexandre Julliard committed
1185 1186
   {

1187
       if( lphc->wState & CBF_DROPPED )
Alexandre Julliard's avatar
Alexandre Julliard committed
1188
       {
1189
	   RECT	rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1190 1191

	   lphc->wState &= ~CBF_DROPPED;
1192
	   ShowWindow( lphc->hWndLBox, SW_HIDE );
1193

1194 1195 1196 1197 1198
           if(GetCapture() == lphc->hWndLBox)
           {
               ReleaseCapture();
           }

Alexandre Julliard's avatar
Alexandre Julliard committed
1199 1200
	   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
	   {
1201
	       rect = lphc->buttonRect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1202
	   }
1203
	   else
Alexandre Julliard's avatar
Alexandre Julliard committed
1204 1205
           {
	       if( bButton )
1206 1207 1208 1209 1210
	       {
		 UnionRect( &rect,
			    &lphc->buttonRect,
			    &lphc->textRect);
	       }
Alexandre Julliard's avatar
Alexandre Julliard committed
1211
	       else
1212 1213
		 rect = lphc->textRect;

Alexandre Julliard's avatar
Alexandre Julliard committed
1214 1215 1216
	       bButton = TRUE;
	   }

Alexandre Julliard's avatar
Alexandre Julliard committed
1217
	   if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1218
	       RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
Alexandre Julliard's avatar
Alexandre Julliard committed
1219 1220 1221 1222
			       RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
	   CB_NOTIFY( lphc, CBN_CLOSEUP );
       }
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1223 1224 1225
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1226 1227 1228
 *           COMBO_FlipListbox
 *
 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
Alexandre Julliard's avatar
Alexandre Julliard committed
1229
 */
1230
BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
Alexandre Julliard's avatar
Alexandre Julliard committed
1231
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1232 1233
   if( lphc->wState & CBF_DROPPED )
   {
1234
       CBRollUp( lphc, ok, bRedrawButton );
Alexandre Julliard's avatar
Alexandre Julliard committed
1235 1236 1237 1238 1239
       return FALSE;
   }

   CBDropDown( lphc );
   return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1240 1241 1242
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1243
 *           CBRepaintButton
Alexandre Julliard's avatar
Alexandre Julliard committed
1244
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1245 1246
static void CBRepaintButton( LPHEADCOMBO lphc )
   {
1247 1248
  InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
  UpdateWindow(lphc->self);
Alexandre Julliard's avatar
Alexandre Julliard committed
1249 1250 1251
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1252
 *           COMBO_SetFocus
Alexandre Julliard's avatar
Alexandre Julliard committed
1253
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1254
static void COMBO_SetFocus( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1255
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1256 1257 1258
   if( !(lphc->wState & CBF_FOCUSED) )
   {
       if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1259
           SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1260

1261 1262 1263
       /* This is wrong. Message sequences seem to indicate that this
          is set *after* the notify. */
       /* lphc->wState |= CBF_FOCUSED;  */
1264

1265
       if( !(lphc->wState & CBF_EDIT) )
1266
	 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1267

Alexandre Julliard's avatar
Alexandre Julliard committed
1268
       CB_NOTIFY( lphc, CBN_SETFOCUS );
1269
       lphc->wState |= CBF_FOCUSED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1270
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1271 1272 1273
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1274
 *           COMBO_KillFocus
Alexandre Julliard's avatar
Alexandre Julliard committed
1275
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1276
static void COMBO_KillFocus( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1277
{
1278
   HWND	hWnd = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
1279

Alexandre Julliard's avatar
Alexandre Julliard committed
1280 1281 1282
   if( lphc->wState & CBF_FOCUSED )
   {
       CBRollUp( lphc, FALSE, TRUE );
1283
       if( IsWindow( hWnd ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1284 1285
       {
           if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1286
               SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1287

Alexandre Julliard's avatar
Alexandre Julliard committed
1288 1289 1290
 	   lphc->wState &= ~CBF_FOCUSED;

           /* redraw text */
1291
	   if( !(lphc->wState & CBF_EDIT) )
1292
	     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1293

Alexandre Julliard's avatar
Alexandre Julliard committed
1294 1295 1296
           CB_NOTIFY( lphc, CBN_KILLFOCUS );
       }
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1297 1298
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1299
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1300
 *           COMBO_Command
Alexandre Julliard's avatar
Alexandre Julliard committed
1301
 */
1302
static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
1303
{
1304
   if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
1305
   {
Alexandre Julliard's avatar
Alexandre Julliard committed
1306
       /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
Alexandre Julliard's avatar
Alexandre Julliard committed
1307

Alexandre Julliard's avatar
Alexandre Julliard committed
1308
       switch( HIWORD(wParam) >> 8 )
1309
       {
Alexandre Julliard's avatar
Alexandre Julliard committed
1310
	   case (EN_SETFOCUS >> 8):
Alexandre Julliard's avatar
Alexandre Julliard committed
1311

1312
               TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
Alexandre Julliard's avatar
Alexandre Julliard committed
1313

1314
		COMBO_SetFocus( lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1315
	        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1316

Alexandre Julliard's avatar
Alexandre Julliard committed
1317
	   case (EN_KILLFOCUS >> 8):
Alexandre Julliard's avatar
Alexandre Julliard committed
1318

1319
               TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
Alexandre Julliard's avatar
Alexandre Julliard committed
1320

Alexandre Julliard's avatar
Alexandre Julliard committed
1321 1322
		/* NOTE: it seems that Windows' edit control sends an
		 * undocumented message WM_USER + 0x1B instead of this
1323
		 * notification (only when it happens to be a part of
Alexandre Julliard's avatar
Alexandre Julliard committed
1324 1325 1326 1327 1328
		 * the combo). ?? - AK.
		 */

		COMBO_KillFocus( lphc );
		break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1329

Alexandre Julliard's avatar
Alexandre Julliard committed
1330

Alexandre Julliard's avatar
Alexandre Julliard committed
1331
	   case (EN_CHANGE >> 8):
1332 1333
	       /*
	        * In some circumstances (when the selection of the combobox
1334
		* is changed for example) we don't want the EN_CHANGE notification
1335
		* to be forwarded to the parent of the combobox. This code
1336
		* checks a flag that is set in these occasions and ignores the
1337 1338
		* notification.
	        */
1339 1340 1341 1342 1343 1344
		if (lphc->wState & CBF_NOLBSELECT)
		{
		  lphc->wState &= ~CBF_NOLBSELECT;
		}
		else
		{
1345
		  CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1346
		}
1347 1348 1349

	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
		  CB_NOTIFY( lphc, CBN_EDITCHANGE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1350 1351 1352
		break;

	   case (EN_UPDATE >> 8):
1353 1354
	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
		  CB_NOTIFY( lphc, CBN_EDITUPDATE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1355 1356 1357 1358 1359 1360 1361 1362
		break;

	   case (EN_ERRSPACE >> 8):
		CB_NOTIFY( lphc, CBN_ERRSPACE );
       }
   }
   else if( lphc->hWndLBox == hWnd )
   {
1363
       switch( (short)HIWORD(wParam) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
       {
	   case LBN_ERRSPACE:
		CB_NOTIFY( lphc, CBN_ERRSPACE );
		break;

	   case LBN_DBLCLK:
		CB_NOTIFY( lphc, CBN_DBLCLK );
		break;

	   case LBN_SELCHANGE:
	   case LBN_SELCANCEL:

1376
               TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
Alexandre Julliard's avatar
Alexandre Julliard committed
1377

1378
		if( HIWORD(wParam) == LBN_SELCHANGE)
1379
		{
1380 1381
		   if( lphc->wState & CBF_EDIT )
		   {
1382
		       INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1383 1384 1385
		       lphc->wState |= CBF_NOLBSELECT;
		       CBUpdateEdit( lphc, index );
		       /* select text in edit, as Windows does */
1386
		       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1387 1388
		   }
		   else
1389
		       InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1390 1391
		}

1392
		/* do not roll up if selection is being tracked
1393
		 * by arrowkeys in the dropdown listbox */
1394
                if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1395 1396 1397 1398 1399 1400 1401
                {
                   CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
                }
		else lphc->wState &= ~CBF_NOROLLUP;

		CB_NOTIFY( lphc, CBN_SELCHANGE );

Alexandre Julliard's avatar
Alexandre Julliard committed
1402 1403 1404 1405 1406 1407
		/* fall through */

	   case LBN_SETFOCUS:
	   case LBN_KILLFOCUS:
		/* nothing to do here since ComboLBox always resets the focus to its
		 * combo/edit counterpart */
Alexandre Julliard's avatar
Alexandre Julliard committed
1408
		 break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1409 1410 1411
       }
   }
   return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1412
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1413

Alexandre Julliard's avatar
Alexandre Julliard committed
1414
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1415 1416 1417
 *           COMBO_ItemOp
 *
 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
Alexandre Julliard's avatar
Alexandre Julliard committed
1418
 */
1419
static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1420
{
1421
   HWND hWnd = lphc->self;
1422
   UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
1423

1424
   TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
Alexandre Julliard's avatar
Alexandre Julliard committed
1425

1426
   switch( msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
1427
   {
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
   case WM_DELETEITEM:
       {
           DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
           lpIS->CtlType  = ODT_COMBOBOX;
           lpIS->CtlID    = id;
           lpIS->hwndItem = hWnd;
           break;
       }
   case WM_DRAWITEM:
       {
           DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
           lpIS->CtlType  = ODT_COMBOBOX;
           lpIS->CtlID    = id;
           lpIS->hwndItem = hWnd;
           break;
       }
   case WM_COMPAREITEM:
       {
           COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
           lpIS->CtlType  = ODT_COMBOBOX;
           lpIS->CtlID    = id;
           lpIS->hwndItem = hWnd;
           break;
       }
   case WM_MEASUREITEM:
       {
           MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
           lpIS->CtlType  = ODT_COMBOBOX;
           lpIS->CtlID    = id;
           break;
       }
Alexandre Julliard's avatar
Alexandre Julliard committed
1459
   }
1460
   return SendMessageW(lphc->owner, msg, id, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1461 1462
}

1463

Alexandre Julliard's avatar
Alexandre Julliard committed
1464
/***********************************************************************
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
 *           COMBO_GetTextW
 */
static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
{
    INT length;

    if( lphc->wState & CBF_EDIT )
        return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );

    /* get it from the listbox */

    if (!count || !buf) return 0;
    if( lphc->hWndLBox )
    {
        INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
        if (idx == LB_ERR) goto error;
        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
        if (length == LB_ERR) goto error;

        /* 'length' is without the terminating character */
        if (length >= count)
        {
            LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
            if (!lpBuffer) goto error;
            length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);

            /* truncate if buffer is too short */
            if (length != LB_ERR)
            {
                lstrcpynW( buf, lpBuffer, count );
                length = count;
            }
            HeapFree( GetProcessHeap(), 0, lpBuffer );
        }
        else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);

        if (length == LB_ERR) return 0;
        return length;
    }

 error:  /* error - truncate string, return zero */
    buf[0] = 0;
    return 0;
}


/***********************************************************************
 *           COMBO_GetTextA
1513 1514 1515
 *
 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
 *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
Alexandre Julliard's avatar
Alexandre Julliard committed
1516
 */
1517
static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
Alexandre Julliard's avatar
Alexandre Julliard committed
1518
{
1519
    INT length;
Alexandre Julliard's avatar
Alexandre Julliard committed
1520

1521 1522
    if( lphc->wState & CBF_EDIT )
        return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
Alexandre Julliard's avatar
Alexandre Julliard committed
1523

1524
    /* get it from the listbox */
Alexandre Julliard's avatar
Alexandre Julliard committed
1525

1526 1527 1528
    if (!count || !buf) return 0;
    if( lphc->hWndLBox )
    {
1529
        INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549
        if (idx == LB_ERR) goto error;
        length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
        if (length == LB_ERR) goto error;

        /* 'length' is without the terminating character */
        if (length >= count)
        {
            LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
            if (!lpBuffer) goto error;
            length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);

            /* truncate if buffer is too short */
            if (length != LB_ERR)
            {
                lstrcpynA( buf, lpBuffer, count );
                length = count;
            }
            HeapFree( GetProcessHeap(), 0, lpBuffer );
        }
        else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1550

1551 1552 1553
        if (length == LB_ERR) return 0;
        return length;
    }
1554

1555 1556 1557
 error:  /* error - truncate string, return zero */
    buf[0] = 0;
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1558 1559
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1560

Alexandre Julliard's avatar
Alexandre Julliard committed
1561
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1562 1563
 *           CBResetPos
 *
1564
 * This function sets window positions according to the updated
Alexandre Julliard's avatar
Alexandre Julliard committed
1565
 * component placement struct.
Alexandre Julliard's avatar
Alexandre Julliard committed
1566
 */
1567
static void CBResetPos(
1568
  LPHEADCOMBO lphc,
1569 1570 1571
  LPRECT      rectEdit,
  LPRECT      rectLB,
  BOOL        bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1572
{
1573
   BOOL	bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1574 1575 1576 1577 1578

   /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
    * sizing messages */

   if( lphc->wState & CBF_EDIT )
1579
     SetWindowPos( lphc->hWndEdit, 0,
1580 1581 1582
		   rectEdit->left, rectEdit->top,
		   rectEdit->right - rectEdit->left,
		   rectEdit->bottom - rectEdit->top,
Alexandre Julliard's avatar
Alexandre Julliard committed
1583 1584
                       SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );

1585 1586
   SetWindowPos( lphc->hWndLBox, 0,
		 rectLB->left, rectLB->top,
1587 1588
                 rectLB->right - rectLB->left,
		 rectLB->bottom - rectLB->top,
Alexandre Julliard's avatar
Alexandre Julliard committed
1589 1590 1591 1592 1593 1594 1595
		   SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );

   if( bDrop )
   {
       if( lphc->wState & CBF_DROPPED )
       {
           lphc->wState &= ~CBF_DROPPED;
1596
           ShowWindow( lphc->hWndLBox, SW_HIDE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1597 1598
       }

Alexandre Julliard's avatar
Alexandre Julliard committed
1599
       if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1600
           RedrawWindow( lphc->self, NULL, 0,
Alexandre Julliard's avatar
Alexandre Julliard committed
1601 1602
                           RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1603 1604
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1605

Alexandre Julliard's avatar
Alexandre Julliard committed
1606
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1607
 *           COMBO_Size
Alexandre Julliard's avatar
Alexandre Julliard committed
1608
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1609
static void COMBO_Size( LPHEADCOMBO lphc )
1610
  {
1611
  CBCalcPlacement(lphc->self,
1612 1613 1614
		  lphc,
		  &lphc->textRect,
		  &lphc->buttonRect,
1615
		  &lphc->droppedRect);
1616

1617
  CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1618 1619
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1620

Alexandre Julliard's avatar
Alexandre Julliard committed
1621
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1622
 *           COMBO_Font
Alexandre Julliard's avatar
Alexandre Julliard committed
1623
 */
1624
static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
Alexandre Julliard's avatar
Alexandre Julliard committed
1625
{
1626 1627 1628
  /*
   * Set the font
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1629 1630
  lphc->hFont = hFont;

1631 1632 1633
  /*
   * Propagate to owned windows.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1634
  if( lphc->wState & CBF_EDIT )
1635 1636
      SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
  SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
Alexandre Julliard's avatar
Alexandre Julliard committed
1637

1638 1639 1640 1641 1642
  /*
   * Redo the layout of the control.
   */
  if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
  {
1643
    CBCalcPlacement(lphc->self,
1644 1645 1646
		    lphc,
		    &lphc->textRect,
		    &lphc->buttonRect,
1647
		    &lphc->droppedRect);
1648

1649 1650 1651 1652
    CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
  }
  else
  {
1653
    CBForceDummyResize(lphc);
1654
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1655 1656
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1657

Alexandre Julliard's avatar
Alexandre Julliard committed
1658
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1659
 *           COMBO_SetItemHeight
Alexandre Julliard's avatar
Alexandre Julliard committed
1660
 */
1661
static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
Alexandre Julliard's avatar
Alexandre Julliard committed
1662
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1663 1664 1665 1666 1667 1668 1669
   LRESULT	lRet = CB_ERR;

   if( index == -1 ) /* set text field height */
   {
       if( height < 32768 )
       {
           lphc->editHeight = height;
1670 1671 1672 1673 1674 1675

	 /*
	  * Redo the layout of the control.
	  */
	 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
	 {
1676
	   CBCalcPlacement(lphc->self,
1677 1678 1679
			   lphc,
			   &lphc->textRect,
			   &lphc->buttonRect,
1680
			   &lphc->droppedRect);
1681

1682 1683 1684 1685
	   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
	 }
	 else
	 {
1686
	   CBForceDummyResize(lphc);
1687
	 }
1688

Alexandre Julliard's avatar
Alexandre Julliard committed
1689 1690
	   lRet = height;
       }
1691
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1692
   else if ( CB_OWNERDRAWN(lphc) )	/* set listbox item height */
1693
	lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1694
			      (WPARAM)index, (LPARAM)height );
Alexandre Julliard's avatar
Alexandre Julliard committed
1695
   return lRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
1696 1697 1698
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1699
 *           COMBO_SelectString
Alexandre Julliard's avatar
Alexandre Julliard committed
1700
 */
1701
static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1702
{
1703 1704
   INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
                         SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1705 1706
   if( index >= 0 )
   {
1707 1708 1709 1710
     if( lphc->wState & CBF_EDIT )
       CBUpdateEdit( lphc, index );
     else
     {
1711
       InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1712
     }
Alexandre Julliard's avatar
Alexandre Julliard committed
1713
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1714
   return (LRESULT)index;
Alexandre Julliard's avatar
Alexandre Julliard committed
1715 1716 1717
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1718
 *           COMBO_LButtonDown
Alexandre Julliard's avatar
Alexandre Julliard committed
1719
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1720
static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1721
{
1722
   POINT     pt;
1723
   BOOL      bButton;
1724
   HWND      hWnd = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
1725

1726 1727
   pt.x = LOWORD(lParam);
   pt.y = HIWORD(lParam);
1728 1729
   bButton = PtInRect(&lphc->buttonRect, pt);

Alexandre Julliard's avatar
Alexandre Julliard committed
1730 1731 1732 1733 1734 1735 1736 1737
   if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
       (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
   {
       lphc->wState |= CBF_BUTTONDOWN;
       if( lphc->wState & CBF_DROPPED )
       {
	   /* got a click to cancel selection */

1738
           lphc->wState &= ~CBF_BUTTONDOWN;
Alexandre Julliard's avatar
Alexandre Julliard committed
1739
           CBRollUp( lphc, TRUE, FALSE );
1740
	   if( !IsWindow( hWnd ) ) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752

           if( lphc->wState & CBF_CAPTURE )
           {
               lphc->wState &= ~CBF_CAPTURE;
               ReleaseCapture();
           }
       }
       else
       {
	   /* drop down the listbox and start tracking */

           lphc->wState |= CBF_CAPTURE;
1753
           SetCapture( hWnd );
1754
           CBDropDown( lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1755 1756 1757
       }
       if( bButton ) CBRepaintButton( lphc );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1758 1759 1760
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1761 1762 1763
 *           COMBO_LButtonUp
 *
 * Release capture and stop tracking if needed.
Alexandre Julliard's avatar
Alexandre Julliard committed
1764
 */
1765
static void COMBO_LButtonUp( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1766
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1767 1768 1769 1770 1771
   if( lphc->wState & CBF_CAPTURE )
   {
       lphc->wState &= ~CBF_CAPTURE;
       if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
       {
1772
	   INT index = CBUpdateLBox( lphc, TRUE );
1773 1774 1775 1776 1777 1778 1779
	   /* Update edit only if item is in the list */
	   if(index >= 0)
	   {
	       lphc->wState |= CBF_NOLBSELECT;
	       CBUpdateEdit( lphc, index );
	       lphc->wState &= ~CBF_NOLBSELECT;
	   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1780 1781
       }
       ReleaseCapture();
1782
       SetCapture(lphc->hWndLBox);
Alexandre Julliard's avatar
Alexandre Julliard committed
1783
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1784

Alexandre Julliard's avatar
Alexandre Julliard committed
1785 1786 1787 1788 1789
   if( lphc->wState & CBF_BUTTONDOWN )
   {
       lphc->wState &= ~CBF_BUTTONDOWN;
       CBRepaintButton( lphc );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1790 1791 1792
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1793 1794 1795 1796
 *           COMBO_MouseMove
 *
 * Two things to do - track combo button and release capture when
 * pointer goes into the listbox.
Alexandre Julliard's avatar
Alexandre Julliard committed
1797
 */
1798
static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1799
{
1800
   POINT  pt;
1801
   RECT   lbRect;
1802

1803 1804
   pt.x = LOWORD(lParam);
   pt.y = HIWORD(lParam);
1805

Alexandre Julliard's avatar
Alexandre Julliard committed
1806 1807
   if( lphc->wState & CBF_BUTTONDOWN )
   {
1808
     BOOL bButton;
Alexandre Julliard's avatar
Alexandre Julliard committed
1809

1810 1811 1812 1813 1814 1815 1816
     bButton = PtInRect(&lphc->buttonRect, pt);

     if( !bButton )
     {
       lphc->wState &= ~CBF_BUTTONDOWN;
       CBRepaintButton( lphc );
     }
Alexandre Julliard's avatar
Alexandre Julliard committed
1817
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1818

1819
   GetClientRect( lphc->hWndLBox, &lbRect );
1820
   MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1821
   if( PtInRect(&lbRect, pt) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1822 1823 1824
   {
       lphc->wState &= ~CBF_CAPTURE;
       ReleaseCapture();
1825
       if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1826

Alexandre Julliard's avatar
Alexandre Julliard committed
1827
       /* hand over pointer tracking */
1828
       SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1829
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1830 1831
}

1832 1833 1834 1835 1836 1837 1838 1839
static LRESULT COMBO_GetComboBoxInfo(LPHEADCOMBO lphc, COMBOBOXINFO *pcbi)
{
    if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
        return FALSE;

    pcbi->rcItem = lphc->textRect;
    pcbi->rcButton = lphc->buttonRect;
    pcbi->stateButton = 0;
1840
    if (lphc->wState & CBF_BUTTONDOWN)
1841 1842 1843 1844 1845 1846 1847 1848 1849
        pcbi->stateButton |= STATE_SYSTEM_PRESSED;
    if (IsRectEmpty(&lphc->buttonRect))
        pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
    pcbi->hwndCombo = lphc->self;
    pcbi->hwndItem = lphc->hWndEdit;
    pcbi->hwndList = lphc->hWndLBox;
    return TRUE;
}

1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861
static char *strdupA(LPCSTR str)
{
    char *ret;
    DWORD len;

    if(!str) return NULL;

    len = strlen(str);
    ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
    memcpy(ret, str, len + 1);
    return ret;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1862 1863

/***********************************************************************
1864
 *           ComboWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1865
 *
1866
 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/comboboxes/comboboxes.asp
Alexandre Julliard's avatar
Alexandre Julliard committed
1867
 */
1868
static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1869
                                    WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1870
{
1871
      LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1872

1873
      TRACE("[%p]: msg %s wp %08x lp %08lx\n",
1874
            hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1875 1876

      if( lphc || message == WM_NCCREATE )
1877 1878
      switch(message)
      {
Alexandre Julliard's avatar
Alexandre Julliard committed
1879 1880 1881

	/* System messages */

1882
     	case WM_NCCREATE:
1883 1884 1885
	{
		LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
				       ((LPCREATESTRUCTA)lParam)->style;
1886
                return COMBO_NCCreate(hwnd, style);
1887
	}
1888
     	case WM_NCDESTROY:
Alexandre Julliard's avatar
Alexandre Julliard committed
1889
		COMBO_NCDestroy(lphc);
1890
		break;/* -> DefWindowProc */
Alexandre Julliard's avatar
Alexandre Julliard committed
1891

1892 1893
     	case WM_CREATE:
	{
1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905
		HWND hwndParent;
		LONG style;
		if(unicode)
		{
		    hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
		    style = ((LPCREATESTRUCTW)lParam)->style;
		}
		else
		{
		    hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
		    style = ((LPCREATESTRUCTA)lParam)->style;
		}
1906
                return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1907
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1908

1909 1910
        case WM_PRINTCLIENT:
	        if (lParam & PRF_ERASEBKGND)
1911
		  COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1912 1913

		/* Fallthrough */
Alexandre Julliard's avatar
Alexandre Julliard committed
1914 1915
     	case WM_PAINT:
		/* wParam may contain a valid HDC! */
1916
		return  COMBO_Paint(lphc, (HDC)wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1917
	case WM_ERASEBKGND:
1918
		return  COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
	case WM_GETDLGCODE:
	{
		LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
		if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
		{
		   int vk = (int)((LPMSG)lParam)->wParam;

		   if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
		       result |= DLGC_WANTMESSAGE;
		}
		return  result;
	}
	case WM_WINDOWPOSCHANGING:
1932
	        return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1933 1934 1935 1936 1937 1938 1939
    case WM_WINDOWPOSCHANGED:
        /* SetWindowPos can be called on a Combobox to resize its Listbox.
         * In that case, the Combobox itself will not be resized, so we won't
         * get a WM_SIZE. Since we still want to update the Listbox, we have to
         * do it here.
         */
        /* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
1940
	case WM_SIZE:
1941
	        if( lphc->hWndLBox &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1942
		  !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1943
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1944
	case WM_SETFONT:
1945
		COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1946
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1947
	case WM_GETFONT:
1948
		return  (LRESULT)lphc->hFont;
Alexandre Julliard's avatar
Alexandre Julliard committed
1949 1950
	case WM_SETFOCUS:
		if( lphc->wState & CBF_EDIT )
1951
		    SetFocus( lphc->hWndEdit );
Alexandre Julliard's avatar
Alexandre Julliard committed
1952 1953
		else
		    COMBO_SetFocus( lphc );
1954
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1955
	case WM_KILLFOCUS:
1956 1957
            {
                HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1958 1959 1960
		if( !hwndFocus ||
		    (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
		    COMBO_KillFocus( lphc );
1961
		return  TRUE;
1962
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1963
	case WM_COMMAND:
1964
		return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1965
	case WM_GETTEXT:
1966 1967
            return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
                           : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1968 1969 1970
	case WM_SETTEXT:
	case WM_GETTEXTLENGTH:
	case WM_CLEAR:
1971 1972
                if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
                {
1973 1974 1975 1976
                    int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
                    if (j == -1) return 0;
                    return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
                                     SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1977
                }
1978
		else if( lphc->wState & CBF_EDIT )
1979
		{
1980
		    LRESULT ret;
1981
		    lphc->wState |= CBF_NOEDITNOTIFY;
1982 1983
		    ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
				    SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1984 1985
		    lphc->wState &= ~CBF_NOEDITNOTIFY;
		    return ret;
1986
		}
1987
		else return CB_ERR;
1988 1989 1990
	case WM_CUT:
        case WM_PASTE:
	case WM_COPY:
1991
		if( lphc->wState & CBF_EDIT )
1992 1993 1994 1995
		{
		    return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
				     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
		}
1996
		else return  CB_ERR;
1997

Alexandre Julliard's avatar
Alexandre Julliard committed
1998 1999 2000 2001
	case WM_DRAWITEM:
	case WM_DELETEITEM:
	case WM_COMPAREITEM:
	case WM_MEASUREITEM:
2002
		return COMBO_ItemOp(lphc, message, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
2003 2004
	case WM_ENABLE:
		if( lphc->wState & CBF_EDIT )
2005
		    EnableWindow( lphc->hWndEdit, (BOOL)wParam );
2006
		EnableWindow( lphc->hWndLBox, (BOOL)wParam );
2007 2008

		/* Force the control to repaint when the enabled state changes. */
2009
		InvalidateRect(lphc->self, NULL, TRUE);
2010
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2011
	case WM_SETREDRAW:
Alexandre Julliard's avatar
Alexandre Julliard committed
2012 2013 2014 2015 2016
		if( wParam )
		    lphc->wState &= ~CBF_NOREDRAW;
		else
		    lphc->wState |= CBF_NOREDRAW;

Alexandre Julliard's avatar
Alexandre Julliard committed
2017
		if( lphc->wState & CBF_EDIT )
2018 2019
		    SendMessageW(lphc->hWndEdit, message, wParam, lParam);
		SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2020
		return  0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2021 2022 2023
	case WM_SYSKEYDOWN:
		if( KEYDATA_ALT & HIWORD(lParam) )
		    if( wParam == VK_UP || wParam == VK_DOWN )
2024 2025
			COMBO_FlipListbox( lphc, FALSE, FALSE );
                return  0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2026 2027

	case WM_CHAR:
2028
	case WM_IME_CHAR:
Alexandre Julliard's avatar
Alexandre Julliard committed
2029
	case WM_KEYDOWN:
2030 2031 2032 2033
	{
		HWND hwndTarget;

		if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2034 2035
		     (lphc->wState & CBF_DROPPED))
		{
2036
		   CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2037 2038
		   return TRUE;
		}
2039 2040 2041 2042 2043
               else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
               {
                  COMBO_FlipListbox( lphc, FALSE, FALSE );
                  return TRUE;
               }
2044

Alexandre Julliard's avatar
Alexandre Julliard committed
2045
		if( lphc->wState & CBF_EDIT )
2046
		    hwndTarget = lphc->hWndEdit;
Alexandre Julliard's avatar
Alexandre Julliard committed
2047
		else
2048 2049 2050 2051 2052
		    hwndTarget = lphc->hWndLBox;

		return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
				 SendMessageA(hwndTarget, message, wParam, lParam);
	}
2053
	case WM_LBUTTONDOWN:
2054
		if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
Alexandre Julliard's avatar
Alexandre Julliard committed
2055
		if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2056
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2057
	case WM_LBUTTONUP:
2058
		COMBO_LButtonUp( lphc );
2059
		return  TRUE;
2060 2061
	case WM_MOUSEMOVE:
		if( lphc->wState & CBF_CAPTURE )
Alexandre Julliard's avatar
Alexandre Julliard committed
2062
		    COMBO_MouseMove( lphc, wParam, lParam );
2063
		return  TRUE;
2064 2065 2066

        case WM_MOUSEWHEEL:
                if (wParam & (MK_SHIFT | MK_CONTROL))
2067 2068
                    return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
				     DefWindowProcA(hwnd, message, wParam, lParam);
2069 2070 2071

                if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
                if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2072 2073
                return TRUE;

Alexandre Julliard's avatar
Alexandre Julliard committed
2074 2075 2076
	/* Combo messages */

	case CB_ADDSTRING16:
2077
		if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2078
		/* fall through */
2079
	case CB_ADDSTRING:
2080 2081 2082
		if( unicode )
                {
                    if( lphc->dwStyle & CBS_LOWERCASE )
2083
                        CharLowerW((LPWSTR)lParam);
2084
                    else if( lphc->dwStyle & CBS_UPPERCASE )
2085
                        CharUpperW((LPWSTR)lParam);
2086 2087
                    return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
                }
2088 2089
                else /* unlike the unicode version, the ansi version does not overwrite
                        the string if converting case */
2090
                {
2091
                    char *string = NULL;
2092
                    LRESULT ret;
2093
                    if( lphc->dwStyle & CBS_LOWERCASE )
2094 2095
                    {
                        string = strdupA((LPSTR)lParam);
2096
                        CharLowerA(string);
2097
                    }
2098

2099
                    else if( lphc->dwStyle & CBS_UPPERCASE )
2100 2101
                    {
                        string = strdupA((LPSTR)lParam);
2102
                        CharUpperA(string);
2103
                    }
2104

2105 2106 2107
                    ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
                    HeapFree(GetProcessHeap(), 0, string);
                    return ret;
2108
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
2109
	case CB_INSERTSTRING16:
2110
		wParam = (INT)(INT16)wParam;
2111
		if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2112
		/* fall through */
2113
	case CB_INSERTSTRING:
2114 2115 2116
		if( unicode )
                {
                    if( lphc->dwStyle & CBS_LOWERCASE )
2117
                        CharLowerW((LPWSTR)lParam);
2118
                    else if( lphc->dwStyle & CBS_UPPERCASE )
2119
                        CharUpperW((LPWSTR)lParam);
2120 2121 2122 2123 2124
                    return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
                }
                else
                {
                    if( lphc->dwStyle & CBS_LOWERCASE )
2125
                        CharLowerA((LPSTR)lParam);
2126
                    else if( lphc->dwStyle & CBS_UPPERCASE )
2127 2128
                        CharUpperA((LPSTR)lParam);

2129 2130
                    return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
2131
	case CB_DELETESTRING16:
2132
	case CB_DELETESTRING:
2133 2134
		return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
				 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2135
	case CB_SELECTSTRING16:
2136
		wParam = (INT)(INT16)wParam;
2137
		if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2138
		/* fall through */
2139
	case CB_SELECTSTRING:
2140
		return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
Alexandre Julliard's avatar
Alexandre Julliard committed
2141
	case CB_FINDSTRING16:
2142
		wParam = (INT)(INT16)wParam;
2143
		if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2144
		/* fall through */
2145
	case CB_FINDSTRING:
2146 2147
		return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
2148
	case CB_FINDSTRINGEXACT16:
2149
		wParam = (INT)(INT16)wParam;
2150
		if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2151
		/* fall through */
2152
	case CB_FINDSTRINGEXACT:
2153 2154
		return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
2155
	case CB_SETITEMHEIGHT16:
2156
		wParam = (INT)(INT16)wParam;	/* signed integer */
2157
		/* fall through */
2158
	case CB_SETITEMHEIGHT:
2159
		return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
2160
	case CB_GETITEMHEIGHT16:
2161
		wParam = (INT)(INT16)wParam;
2162
		/* fall through */
2163 2164
	case CB_GETITEMHEIGHT:
		if( (INT)wParam >= 0 )	/* listbox item */
2165
                    return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2166
                return  CBGetTextAreaHeight(hwnd, lphc);
2167
	case CB_RESETCONTENT16:
2168
	case CB_RESETCONTENT:
2169
		SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2170
                if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2171 2172 2173 2174
		{
		    static const WCHAR empty_stringW[] = { 0 };
                    SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
		}
2175
                else
2176
                    InvalidateRect(lphc->self, NULL, TRUE);
2177
		return  TRUE;
2178
	case CB_INITSTORAGE:
2179
		return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2180
	case CB_GETHORIZONTALEXTENT:
2181
		return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2182
	case CB_SETHORIZONTALEXTENT:
2183
		return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2184
	case CB_GETTOPINDEX:
2185
		return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2186
	case CB_GETLOCALE:
2187
		return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2188
	case CB_SETLOCALE:
2189
		return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2190
	case CB_GETDROPPEDWIDTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
2191
		if( lphc->droppedWidth )
2192 2193
                    return  lphc->droppedWidth;
		return  lphc->droppedRect.right - lphc->droppedRect.left;
2194
	case CB_SETDROPPEDWIDTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
2195
		if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2196
		    (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2197
		return  CB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
2198
	case CB_GETDROPPEDCONTROLRECT16:
2199
		lParam = (LPARAM)MapSL(lParam);
2200
		if( lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
2201
		{
2202 2203 2204 2205 2206 2207 2208
                    RECT r;
                    RECT16 *r16 = (RECT16 *)lParam;
                    CBGetDroppedControlRect( lphc, &r );
                    r16->left   = r.left;
                    r16->top    = r.top;
                    r16->right  = r.right;
                    r16->bottom = r.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
2209
		}
2210
		return  CB_OKAY;
2211 2212
	case CB_GETDROPPEDCONTROLRECT:
		if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2213
		return  CB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2214
	case CB_GETDROPPEDSTATE16:
2215
	case CB_GETDROPPEDSTATE:
2216
		return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2217
	case CB_DIR16:
Carlos Lozano's avatar
Carlos Lozano committed
2218
		return SendMessageA(lphc->hWndLBox, LB_DIR16, wParam, lParam);
2219
	case CB_DIR:
Carlos Lozano's avatar
Carlos Lozano committed
2220 2221
		return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2222

Alexandre Julliard's avatar
Alexandre Julliard committed
2223
	case CB_SHOWDROPDOWN16:
2224
	case CB_SHOWDROPDOWN:
2225 2226 2227 2228
		if( CB_GETTYPE(lphc) != CBS_SIMPLE )
		{
		    if( wParam )
		    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2229 2230
			if( !(lphc->wState & CBF_DROPPED) )
			    CBDropDown( lphc );
2231
		    }
2232 2233
		    else
			if( lphc->wState & CBF_DROPPED )
Alexandre Julliard's avatar
Alexandre Julliard committed
2234
		            CBRollUp( lphc, FALSE, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2235
		}
2236
		return  TRUE;
2237
	case CB_GETCOUNT16:
2238
	case CB_GETCOUNT:
2239
		return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2240
	case CB_GETCURSEL16:
2241
	case CB_GETCURSEL:
2242
		return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2243
	case CB_SETCURSEL16:
2244
		wParam = (INT)(INT16)wParam;
2245
		/* fall through */
2246
	case CB_SETCURSEL:
2247
		lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2248
	        if( lParam >= 0 )
2249
	            SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2250 2251 2252 2253 2254

		/* no LBN_SELCHANGE in this case, update manually */
		if( lphc->wState & CBF_EDIT )
		    CBUpdateEdit( lphc, (INT)wParam );
		else
2255
		    InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2256
		lphc->wState &= ~CBF_SELCHANGE;
2257
	        return  lParam;
2258
	case CB_GETLBTEXT16:
2259
		wParam = (INT)(INT16)wParam;
2260
		lParam = (LPARAM)MapSL(lParam);
2261
		/* fall through */
2262
	case CB_GETLBTEXT:
2263 2264
		return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2265
	case CB_GETLBTEXTLEN16:
2266
		wParam = (INT)(INT16)wParam;
2267
		/* fall through */
2268
	case CB_GETLBTEXTLEN:
2269 2270
                return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
                                 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2271
	case CB_GETITEMDATA16:
2272
		wParam = (INT)(INT16)wParam;
2273
		/* fall through */
2274
	case CB_GETITEMDATA:
2275
		return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2276
	case CB_SETITEMDATA16:
2277
		wParam = (INT)(INT16)wParam;
2278
		/* fall through */
2279
	case CB_SETITEMDATA:
2280
		return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2281
	case CB_GETEDITSEL16:
Alexandre Julliard's avatar
Alexandre Julliard committed
2282
		wParam = lParam = 0;   /* just in case */
2283
		/* fall through */
2284
	case CB_GETEDITSEL:
2285
		/* Edit checks passed parameters itself */
Alexandre Julliard's avatar
Alexandre Julliard committed
2286
		if( lphc->wState & CBF_EDIT )
2287
		    return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2288
		return  CB_ERR;
2289
	case CB_SETEDITSEL16:
2290
	case CB_SETEDITSEL:
2291
		if( lphc->wState & CBF_EDIT )
2292
                    return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2293
			  (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2294
		return  CB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
2295
	case CB_SETEXTENDEDUI16:
2296
	case CB_SETEXTENDEDUI:
2297
                if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2298
                    return  CB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
2299 2300 2301
		if( wParam )
		    lphc->wState |= CBF_EUI;
		else lphc->wState &= ~CBF_EUI;
2302
		return  CB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2303
	case CB_GETEXTENDEDUI16:
2304
	case CB_GETEXTENDEDUI:
2305
		return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2306 2307
	case CB_GETCOMBOBOXINFO:
		return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2308 2309 2310
	case CB_LIMITTEXT:
		if( lphc->wState & CBF_EDIT )
			return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2311 2312 2313 2314 2315
	default:
		if (message >= WM_USER)
		    WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
			message - WM_USER, wParam, lParam );
		break;
2316 2317 2318
      }
      return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
                       DefWindowProcA(hwnd, message, wParam, lParam);
2319 2320 2321
}

/***********************************************************************
2322
 *           ComboWndProcA
2323 2324 2325 2326
 *
 * This is just a wrapper for the real ComboWndProc which locks/unlocks
 * window structs.
 */
2327
static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2328
{
2329 2330
    if (!IsWindow(hwnd)) return 0;
    return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2331 2332
}

2333 2334 2335 2336 2337
/***********************************************************************
 *           ComboWndProcW
 */
static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
2338 2339
    if (!IsWindow(hwnd)) return 0;
    return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2340
}
2341 2342 2343 2344

/*************************************************************************
 *           GetComboBoxInfo   (USER32.@)
 */
2345 2346
BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
			    PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2347
{
2348 2349
    TRACE("(%p, %p)\n", hwndCombo, pcbi);
    return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
2350
}