combo.c 62.8 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
 * TODO:
 *   - CB_SETTOPINDEX
Alexandre Julliard's avatar
Alexandre Julliard committed
22
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
23

24 25
#define OEMRESOURCE

26
#include <stdlib.h>
27
#include "user_private.h"
28
#include "controls.h"
29
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
30

31
WINE_DEFAULT_DEBUG_CHANNEL(combo);
32

Alexandre Julliard's avatar
Alexandre Julliard committed
33 34 35
  /* bits in the dwKeyData */
#define KEYDATA_ALT             0x2000
#define KEYDATA_PREVSTATE       0x4000
Alexandre Julliard's avatar
Alexandre Julliard committed
36

Alexandre Julliard's avatar
Alexandre Julliard committed
37 38 39
/*
 * Additional combo box definitions
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
40

Alexandre Julliard's avatar
Alexandre Julliard committed
41
#define CB_NOTIFY( lphc, code ) \
42
    (SendMessageW((lphc)->owner, WM_COMMAND, \
43
                  MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
44 45 46 47 48

#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)
49
#define CB_GETTYPE( lphc )    ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
Alexandre Julliard's avatar
Alexandre Julliard committed
50

51 52
#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)

53 54 55
/*
 * Drawing globals
 */
56
static HBITMAP 	hComboBmp = 0;
57 58 59
static UINT	CBitHeight, CBitWidth;

/*
60
 * Look and feel dependent "constants"
61
 */
62 63

#define COMBO_YBORDERGAP         5
64 65 66 67
#define COMBO_XBORDERSIZE()      2
#define COMBO_YBORDERSIZE()      2
#define COMBO_EDITBUTTONSPACE()  0
#define EDIT_CONTROL_PADDING()   1
Alexandre Julliard's avatar
Alexandre Julliard committed
68

69 70 71
static void CBCalcPlacement(HEADCOMBO *combo);
static void CBResetPos(HEADCOMBO *combo, BOOL redraw);

Alexandre Julliard's avatar
Alexandre Julliard committed
72 73 74 75 76
/***********************************************************************
 *           COMBO_Init
 *
 * Load combo button bitmap.
 */
77
static BOOL COMBO_Init(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
78
{
79
  HDC		hDC;
80

Alexandre Julliard's avatar
Alexandre Julliard committed
81
  if( hComboBmp ) return TRUE;
82
  if( (hDC = CreateCompatibleDC(0)) )
Alexandre Julliard's avatar
Alexandre Julliard committed
83
  {
84
    BOOL	bRet = FALSE;
85
    if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
Alexandre Julliard's avatar
Alexandre Julliard committed
86
    {
87 88 89
      BITMAP      bm;
      HBITMAP     hPrevB;
      RECT        r;
Alexandre Julliard's avatar
Alexandre Julliard committed
90

91
      GetObjectW( hComboBmp, sizeof(bm), &bm );
Alexandre Julliard's avatar
Alexandre Julliard committed
92 93
      CBitHeight = bm.bmHeight;
      CBitWidth  = bm.bmWidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
94

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

97
      hPrevB = SelectObject( hDC, hComboBmp);
98 99 100
      SetRect( &r, 0, 0, CBitWidth, CBitHeight );
      InvertRect( hDC, &r );
      SelectObject( hDC, hPrevB );
Alexandre Julliard's avatar
Alexandre Julliard committed
101 102
      bRet = TRUE;
    }
103
    DeleteDC( hDC );
Alexandre Julliard's avatar
Alexandre Julliard committed
104 105 106
    return bRet;
  }
  return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
107 108 109
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
110
 *           COMBO_NCCreate
Alexandre Julliard's avatar
Alexandre Julliard committed
111
 */
112
static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
Alexandre Julliard's avatar
Alexandre Julliard committed
113
{
114
    LPHEADCOMBO lphc;
Alexandre Julliard's avatar
Alexandre Julliard committed
115

116
    if( COMBO_Init() && (lphc = calloc( 1, sizeof(HEADCOMBO) )) )
117 118
    {
        lphc->self = hwnd;
119
        SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
120 121

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

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

126 127 128 129
	/*
	 * We also have to remove the client edge style to make sure
	 * we don't end-up with a non client area.
	 */
130 131
        SetWindowLongW( hwnd, GWL_EXSTYLE,
                        GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
132

133
	if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
Alexandre Julliard's avatar
Alexandre Julliard committed
134
              lphc->dwStyle |= CBS_HASSTRINGS;
135
	if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
Alexandre Julliard's avatar
Alexandre Julliard committed
136
	      lphc->wState |= CBF_NOTIFY;
Alexandre Julliard's avatar
Alexandre Julliard committed
137

138
        TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
139
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
140
    }
141
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
142 143 144
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
145
 *           COMBO_NCDestroy
Alexandre Julliard's avatar
Alexandre Julliard committed
146
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
147
static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
148
{
Alexandre Julliard's avatar
Alexandre Julliard committed
149

Alexandre Julliard's avatar
Alexandre Julliard committed
150 151
   if( lphc )
   {
152
       TRACE("[%p]: freeing storage\n", lphc->self);
Alexandre Julliard's avatar
Alexandre Julliard committed
153

154
       if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
155
           NtUserDestroyWindow( lphc->hWndLBox );
Alexandre Julliard's avatar
Alexandre Julliard committed
156

157
       SetWindowLongPtrW( lphc->self, 0, 0 );
158
       free( lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
159
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
160
   return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
161 162
}

163 164
static INT combo_get_text_height(const HEADCOMBO *combo)
{
165
    HDC hdc = NtUserGetDC(combo->self);
166 167 168 169 170 171 172 173 174 175 176
    HFONT prev_font = 0;
    TEXTMETRICW tm;

    if (combo->hFont)
        prev_font = SelectObject(hdc, combo->hFont);

    GetTextMetricsW(hdc, &tm);

    if (prev_font)
        SelectObject(hdc, prev_font);

177
    NtUserReleaseDC( combo->self, hdc );
178 179 180 181

    return tm.tmHeight + 4;
}

182 183 184
/***********************************************************************
 *           CBGetTextAreaHeight
 *
185
 * This method will calculate the height of the text area of the
186
 * combobox.
187
 * The height of the text area is set in two ways.
188
 * It can be set explicitly through a combobox message or through a
189
 * WM_MEASUREITEM callback.
190
 * If this is not the case, the height is set to font height + 4px
191
 * This height was determined through experimentation.
192
 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
193
 */
194
static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
195
{
196
  INT item_height, text_height;
197

198
  if (clip_item_height && !CB_OWNERDRAWN(lphc))
199
  {
200 201 202
      text_height = combo_get_text_height(lphc);
      if (lphc->item_height < text_height)
          lphc->item_height = text_height;
203
  }
204

205
  item_height = lphc->item_height;
206

207 208 209 210 211 212 213 214 215
  /*
   * 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;
216
    INT               originalItemHeight = item_height;
217
    UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
218

219 220 221
    /*
     * We use the client rect for the width of the item.
     */
222
    GetClientRect(lphc->self, &clientRect);
223

224
    lphc->wState &= ~CBF_MEASUREITEM;
225

226 227 228 229
    /*
     * Send a first one to measure the size of the text area
     */
    measureItem.CtlType    = ODT_COMBOBOX;
230
    measureItem.CtlID      = id;
231 232
    measureItem.itemID     = -1;
    measureItem.itemWidth  = clientRect.right;
233
    measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
234
    measureItem.itemData   = 0;
235
    SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
236
    item_height = 6 + measureItem.itemHeight;
237 238 239 240 241 242 243 244

    /*
     * 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;
245
      measureItem.CtlID      = id;
246 247 248 249
      measureItem.itemID     = 0;
      measureItem.itemWidth  = clientRect.right;
      measureItem.itemHeight = originalItemHeight;
      measureItem.itemData   = 0;
250
      SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
251 252
      lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
    }
253

254
    /*
255
     * Keep the size for the next time
256
     */
257
    lphc->item_height = item_height;
258
  }
259

260
  return item_height;
261 262
}

263 264 265 266 267 268 269
/***********************************************************************
 *           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.
 */
270
static void CBForceDummyResize(LPHEADCOMBO lphc)
271 272 273 274
{
  RECT windowRect;
  int newComboHeight;

275
  newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
276

277
  GetWindowRect(lphc->self, &windowRect);
278 279 280 281 282 283 284 285 286

  /*
   * 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.
   */
287
  lphc->wState |= CBF_NORESIZE;
288 289 290 291 292 293
  NtUserSetWindowPos( lphc->self,
                      NULL,
                      0, 0,
                      windowRect.right  - windowRect.left,
                      newComboHeight,
                      SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
294 295 296 297
  lphc->wState &= ~CBF_NORESIZE;

  CBCalcPlacement(lphc);
  CBResetPos(lphc, FALSE);
298
}
Alexandre Julliard's avatar
Alexandre Julliard committed
299

Alexandre Julliard's avatar
Alexandre Julliard committed
300
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
301 302 303
 *           CBCalcPlacement
 *
 * Set up component coordinates given valid lphc->RectCombo.
Alexandre Julliard's avatar
Alexandre Julliard committed
304
 */
305
static void CBCalcPlacement(HEADCOMBO *combo)
Alexandre Julliard's avatar
Alexandre Julliard committed
306
{
307 308
    /* Start with the client rectangle. */
    GetClientRect(combo->self, &combo->textRect);
309

310 311
    /* Remove the borders */
    InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
312

313
    /* Chop off the bottom part to fit with the height of the text area. */
314
    combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
315

316 317
    /* The button starts the same vertical position as the text area. */
    combo->buttonRect = combo->textRect;
318

319 320 321 322 323 324 325 326 327 328 329 330 331
    /* If the combobox is "simple" there is no button. */
    if (CB_GETTYPE(combo) == CBS_SIMPLE)
        combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.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.
         */
        combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
        combo->textRect.right = combo->buttonRect.left;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
332

333 334 335
    /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
    if (CB_GETTYPE(combo) == CBS_DROPDOWN)
        combo->textRect.right -= COMBO_EDITBUTTONSPACE();
336

337 338 339
    /* If we have an edit control, we space it away from the borders slightly. */
    if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
        InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
340

341 342
    /* Adjust the size of the listbox popup. */
    if (CB_GETTYPE(combo) == CBS_SIMPLE)
343
    {
344 345
        GetClientRect(combo->self, &combo->droppedRect);
        combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
346 347
    }
    else
348 349 350 351 352
    {
        /* Make sure the dropped width is as large as the combobox itself. */
        if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
        {
            combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
353

354 355 356 357 358 359 360 361
            /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
               with the right side of the combobox. */
            if (CB_GETTYPE(combo) == CBS_DROPDOWN)
                combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
        }
        else
            combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
    }
362

363 364 365
    /* Disallow negative window width */
    if (combo->textRect.right < combo->textRect.left)
        combo->textRect.right = combo->textRect.left;
366

367 368
    TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
            wine_dbgstr_rect(&combo->droppedRect));
Alexandre Julliard's avatar
Alexandre Julliard committed
369 370 371
}

/***********************************************************************
372
 *           CBGetDroppedControlRect
Alexandre Julliard's avatar
Alexandre Julliard committed
373
 */
374
static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
Alexandre Julliard's avatar
Alexandre Julliard committed
375
{
376 377
    /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
     of the combo box and the lower right corner of the listbox */
378

379
    GetWindowRect(lphc->self, lpRect);
380 381 382 383

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

384 385
}

Alexandre Julliard's avatar
Alexandre Julliard committed
386
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
387
 *           COMBO_Create
Alexandre Julliard's avatar
Alexandre Julliard committed
388
 */
389 390
static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
                             BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
391
{
Alexandre Julliard's avatar
Alexandre Julliard committed
392
  if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
393
  if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
394

395
  lphc->owner = hwndParent;
Alexandre Julliard's avatar
Alexandre Julliard committed
396

397 398 399
  lphc->droppedWidth = 0;

  lphc->item_height = combo_get_text_height(lphc);
400 401 402 403 404 405

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

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

408
  if( lphc->owner || !(style & WS_VISIBLE) )
Alexandre Julliard's avatar
Alexandre Julliard committed
409
  {
410 411
      UINT lbeStyle   = 0;
      UINT lbeExStyle = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
412

413 414 415 416 417
      /*
       * 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.
       */
418
      GetClientRect( hwnd, &lphc->droppedRect );
419
      CBCalcPlacement(lphc);
420 421 422 423 424 425 426

      /*
       * 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
427

428 429 430 431 432 433
	/*
	 * If it's a dropdown, the listbox is offset
	 */
	if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
	  lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();

434 435 436 437 438
        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 );
439
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
440 441 442

      /* create listbox popup */

443
      lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
444
                 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
Alexandre Julliard's avatar
Alexandre Julliard committed
445 446 447 448 449 450 451 452 453

      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;
454

Alexandre Julliard's avatar
Alexandre Julliard committed
455
      if( CB_GETTYPE(lphc) == CBS_SIMPLE ) 	/* child listbox */
456
      {
457
	lbeStyle |= WS_VISIBLE;
458 459 460 461 462

	/*
	 * In win 95 look n feel, the listbox in the simple combobox has
	 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
	 */
463 464
	lbeStyle   &= ~WS_BORDER;
	lbeExStyle |= WS_EX_CLIENTEDGE;
465
      }
466 467 468 469
      else
      {
        lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
470

471
      if (unicode)
472
          lphc->hWndLBox = CreateWindowExW(lbeExStyle, L"ComboLBox", NULL, lbeStyle,
473 474 475 476 477
                                           lphc->droppedRect.left,
                                           lphc->droppedRect.top,
                                           lphc->droppedRect.right - lphc->droppedRect.left,
                                           lphc->droppedRect.bottom - lphc->droppedRect.top,
                                           hwnd, (HMENU)ID_CB_LISTBOX,
478
                                           (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
479 480 481 482 483 484 485
      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,
486
                                           (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
487

Alexandre Julliard's avatar
Alexandre Julliard committed
488 489
      if( lphc->hWndLBox )
      {
490
	  BOOL	bEdit = TRUE;
491
	  lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
492

493
	  if( lphc->wState & CBF_EDIT )
Alexandre Julliard's avatar
Alexandre Julliard committed
494 495 496 497 498 499 500 501 502
	  {
	      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;
503

504
              if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
505

506
              if (unicode)
507
                  lphc->hWndEdit = CreateWindowExW(0, L"Edit", NULL, lbeStyle,
508 509 510 511
                                                   lphc->textRect.left, lphc->textRect.top,
                                                   lphc->textRect.right - lphc->textRect.left,
                                                   lphc->textRect.bottom - lphc->textRect.top,
                                                   hwnd, (HMENU)ID_CB_EDIT,
512
                                                   (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
513 514 515 516 517 518
              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,
519
                                                   (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
520 521 522

	      if( !lphc->hWndEdit )
		bEdit = FALSE;
523
	  }
Alexandre Julliard's avatar
Alexandre Julliard committed
524 525 526

          if( bEdit )
	  {
527 528
	    if( CB_GETTYPE(lphc) != CBS_SIMPLE )
	    {
529
              /* Now do the trick with parent */
530
              NtUserSetParent( lphc->hWndLBox, HWND_DESKTOP );
531
              /*
532 533 534 535
               * 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.
536
               */
537
	      CBForceDummyResize(lphc);
538
	    }
539

540
	    TRACE("init done\n");
541
	    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
542
	  }
543 544 545
	  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
546 547 548 549

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

  return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
550 551 552
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
553 554 555
 *           CBPaintButton
 *
 * Paint combo button (normal, pressed, and disabled states).
Alexandre Julliard's avatar
Alexandre Julliard committed
556
 */
557
static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
558
{
559 560
    UINT buttonState = DFCS_SCROLLCOMBOBOX;

561 562 563
    if (IsRectEmpty(&lphc->buttonRect))
        return;

564
    if( lphc->wState & CBF_NOREDRAW )
565
      return;
Alexandre Julliard's avatar
Alexandre Julliard committed
566

567

568 569
    if (lphc->wState & CBF_BUTTONDOWN)
	buttonState |= DFCS_PUSHED;
570

571 572
    if (CB_DISABLED(lphc))
	buttonState |= DFCS_INACTIVE;
573

574
    DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
Alexandre Julliard's avatar
Alexandre Julliard committed
575
}
Alexandre Julliard's avatar
Alexandre Julliard committed
576

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
/***********************************************************************
 *           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))
    {
        hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
                (WPARAM)hDC, (LPARAM)lphc->self );

        /*
         * 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
    {
        /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
        hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
                (WPARAM)hDC, (LPARAM)lphc->self );
    }

    /*
     * Catch errors.
     */
    if( !hBkgBrush )
        hBkgBrush = GetSysColorBrush(COLOR_WINDOW);

    return hBkgBrush;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
622
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
623 624 625
 *           CBPaintText
 *
 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
Alexandre Julliard's avatar
Alexandre Julliard committed
626
 */
627
static void CBPaintText(
628
  LPHEADCOMBO lphc,
629
  HDC         hdc_paint)
Alexandre Julliard's avatar
Alexandre Julliard committed
630
{
631
   RECT rectEdit = lphc->textRect;
632
   INT	id, size = 0;
633
   LPWSTR pText = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
634

635 636
   TRACE("\n");

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

640
   if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
Alexandre Julliard's avatar
Alexandre Julliard committed
641
   {
642
        size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
643 644
	if (size == LB_ERR)
	  FIXME("LB_ERR probably not handled yet\n");
645
        if( (pText = malloc((size + 1) * sizeof(WCHAR))) )
Alexandre Julliard's avatar
Alexandre Julliard committed
646
	{
647
            /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
648
           size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
Alexandre Julliard's avatar
Alexandre Julliard committed
649 650 651
	    pText[size] = '\0';	/* just in case */
	} else return;
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
652

Alexandre Julliard's avatar
Alexandre Julliard committed
653 654
   if( lphc->wState & CBF_EDIT )
   {
655
	if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : L"" );
656
	if( lphc->wState & CBF_FOCUSED )
657
           SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
Alexandre Julliard's avatar
Alexandre Julliard committed
658
   }
659
   else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
Alexandre Julliard's avatar
Alexandre Julliard committed
660
   {
661
     /* paint text field ourselves */
662
     HDC hdc = hdc_paint ? hdc_paint : NtUserGetDC(lphc->self);
663 664 665
     UINT itemState = ODS_COMBOBOXEDIT;
     HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
     HBRUSH hPrevBrush, hBkgBrush;
666

667 668 669 670
     /*
      * Give ourselves some space.
      */
     InflateRect( &rectEdit, -1, -1 );
671

672 673 674 675
     hBkgBrush = COMBO_PrepareColors( lphc, hdc );
     hPrevBrush = SelectObject( hdc, hBkgBrush );
     FillRect( hdc, &rectEdit, hBkgBrush );

676 677 678 679
     if( CB_OWNERDRAWN(lphc) )
     {
       DRAWITEMSTRUCT dis;
       HRGN           clipRegion;
680
       UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
681

682
       /* setup state for DRAWITEM message. Owner will highlight */
683
       if ( (lphc->wState & CBF_FOCUSED) &&
684 685 686
	    !(lphc->wState & CBF_DROPPED) )
	   itemState |= ODS_SELECTED | ODS_FOCUS;

Andrew Talbot's avatar
Andrew Talbot committed
687
       if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
688

689
       dis.CtlType	= ODT_COMBOBOX;
690 691
       dis.CtlID	= ctlid;
       dis.hwndItem	= lphc->self;
692 693 694 695 696
       dis.itemAction	= ODA_DRAWENTIRE;
       dis.itemID	= id;
       dis.itemState	= itemState;
       dis.hDC		= hdc;
       dis.rcItem	= rectEdit;
697
       dis.itemData     = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
698

699 700 701
       /*
	* Clip the DC and have the parent draw the item.
	*/
702
       clipRegion = set_control_clipping( hdc, &rectEdit );
703 704 705

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

706 707
       SelectClipRgn( hdc, clipRegion );
       if (clipRegion) DeleteObject( clipRegion );
708 709 710
     }
     else
     {
711
       if ( (lphc->wState & CBF_FOCUSED) &&
712 713 714 715 716 717
	    !(lphc->wState & CBF_DROPPED) ) {

	   /* highlight */
	   FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
	   SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
	   SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
718 719 720 721
       }

       ExtTextOutW( hdc,
		    rectEdit.left + 1,
722
		    rectEdit.top + 1,
723
		    ETO_OPAQUE | ETO_CLIPPED,
724
		    &rectEdit,
725
		    pText ? pText : L"" , size, NULL );
726

727 728 729
       if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
	 DrawFocusRect( hdc, &rectEdit );
     }
730 731

     if( hPrevFont )
732
       SelectObject(hdc, hPrevFont );
733 734 735 736

    if( hPrevBrush )
        SelectObject( hdc, hPrevBrush );

737 738
     if (!hdc_paint)
       NtUserReleaseDC( lphc->self, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
739
   }
740
   free(pText);
Alexandre Julliard's avatar
Alexandre Julliard committed
741 742
}

743 744 745
/***********************************************************************
 *           CBPaintBorder
 */
746
static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
747 748 749 750 751
{
  RECT clientRect;

  if (CB_GETTYPE(lphc) != CBS_SIMPLE)
  {
752
    GetClientRect(lphc->self, &clientRect);
753 754 755
  }
  else
  {
756
    clientRect = lphc->textRect;
757 758 759 760 761 762 763 764

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
765
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
766
 *           COMBO_Paint
Alexandre Julliard's avatar
Alexandre Julliard committed
767
 */
768
static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
Alexandre Julliard's avatar
Alexandre Julliard committed
769
{
770 771
  PAINTSTRUCT ps;
  HDC 	hDC;
772

773
  hDC = hParamDC ? hParamDC : NtUserBeginPaint( lphc->self, &ps );
774

775
  TRACE("hdc=%p\n", hDC);
776

Alexandre Julliard's avatar
Alexandre Julliard committed
777
  if( hDC && !(lphc->wState & CBF_NOREDRAW) )
Alexandre Julliard's avatar
Alexandre Julliard committed
778
  {
779
      HBRUSH	hPrevBrush, hBkgBrush;
Alexandre Julliard's avatar
Alexandre Julliard committed
780

781 782 783 784
      /*
       * Retrieve the background brush and select it in the
       * DC.
       */
785
      hBkgBrush = COMBO_PrepareColors(lphc, hDC);
Alexandre Julliard's avatar
Alexandre Julliard committed
786

787
      hPrevBrush = SelectObject( hDC, hBkgBrush );
788 789
      if (!(lphc->wState & CBF_EDIT))
        FillRect(hDC, &lphc->textRect, hBkgBrush);
790 791 792 793

      /*
       * In non 3.1 look, there is a sunken border on the combobox
       */
794
      CBPaintBorder(lphc, hDC);
795
      CBPaintButton(lphc, hDC);
Alexandre Julliard's avatar
Alexandre Julliard committed
796

797 798 799 800 801 802 803
      /* paint the edit control padding area */
      if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
      {
          RECT rPadEdit = lphc->textRect;

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

804
          FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
805
      }
806

Alexandre Julliard's avatar
Alexandre Julliard committed
807
      if( !(lphc->wState & CBF_EDIT) )
808
	CBPaintText( lphc, hDC );
809 810 811

      if( hPrevBrush )
	SelectObject( hDC, hPrevBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
812
  }
813

814 815
  if (!hParamDC)
    NtUserEndPaint( lphc->self, &ps );
816

Alexandre Julliard's avatar
Alexandre Julliard committed
817 818 819 820
  return 0;
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
821 822 823
 *           CBUpdateLBox
 *
 * Select listbox entry according to the contents of the edit control.
Alexandre Julliard's avatar
Alexandre Julliard committed
824
 */
825
static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
Alexandre Julliard's avatar
Alexandre Julliard committed
826
{
827
   INT	length, idx;
828
   LPWSTR pText = NULL;
829

830
   idx = LB_ERR;
831 832
   length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );

833
   if( length > 0 )
834
       pText = malloc((length + 1) * sizeof(WCHAR));
Alexandre Julliard's avatar
Alexandre Julliard committed
835

836
   TRACE("\t edit text length %i\n", length );
Alexandre Julliard's avatar
Alexandre Julliard committed
837 838 839

   if( pText )
   {
840
       GetWindowTextW( lphc->hWndEdit, pText, length + 1);
841
       idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
842
       free(pText);
Alexandre Julliard's avatar
Alexandre Julliard committed
843
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
844

845
   SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
846 847

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

851
   return idx;
Alexandre Julliard's avatar
Alexandre Julliard committed
852 853 854
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
855 856 857
 *           CBUpdateEdit
 *
 * Copy a listbox entry to the edit control.
Alexandre Julliard's avatar
Alexandre Julliard committed
858
 */
859
static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
Alexandre Julliard's avatar
Alexandre Julliard committed
860
{
861
   INT	length;
862
   LPWSTR pText = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
863

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

Alexandre Julliard's avatar
Alexandre Julliard committed
866 867
   if( index >= 0 ) /* got an entry */
   {
868
       length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
869
       if( length != LB_ERR)
Alexandre Julliard's avatar
Alexandre Julliard committed
870
       {
871
           if( (pText = malloc((length + 1) * sizeof(WCHAR))) )
Alexandre Julliard's avatar
Alexandre Julliard committed
872
	   {
873
               SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
Alexandre Julliard's avatar
Alexandre Julliard committed
874 875
	   }
       }
876
   }
877

878 879 880
   if( CB_HASSTRINGS(lphc) )
   {
      lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
881
      SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
882 883
      lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
   }
884

885
   if( lphc->wState & CBF_FOCUSED )
886
      SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
887

888
   free(pText);
Alexandre Julliard's avatar
Alexandre Julliard committed
889 890 891
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
892
 *           CBDropDown
893
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
894
 * Show listbox popup.
Alexandre Julliard's avatar
Alexandre Julliard committed
895
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
896
static void CBDropDown( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
897
{
898 899
    HMONITOR monitor;
    MONITORINFO mon_info;
900
   RECT rect,r;
901
   int nItems;
902
   int nDroppedHeight;
Alexandre Julliard's avatar
Alexandre Julliard committed
903

904
   TRACE("[%p]: drop down\n", lphc->self);
Alexandre Julliard's avatar
Alexandre Julliard committed
905 906 907 908 909 910 911 912

   CB_NOTIFY( lphc, CBN_DROPDOWN );

   /* set selection */

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

915 916
       /* Update edit only if item is in the list */
       if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
917
	 CBUpdateEdit( lphc, lphc->droppedIndex );
Alexandre Julliard's avatar
Alexandre Julliard committed
918 919 920
   }
   else
   {
921
       lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
922

923
       SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
924
                    lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
925
       SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
926
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
927

Alexandre Julliard's avatar
Alexandre Julliard committed
928
   /* now set popup position */
929
   GetWindowRect( lphc->self, &rect );
930

931 932 933 934 935 936
   /*
    * If it's a dropdown, the listbox is offset
    */
   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
     rect.left += COMBO_EDITBUTTONSPACE();

937 938 939 940
  /* 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 */

941
  /* And Remove any extra space (Best Fit) */
942
   nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
943 944 945 946
  /* 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;
947
   nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
948 949

   if (nItems > 0)
950
   {
951
      int nHeight;
952
      int nIHeight;
953

954 955 956
      nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);

      nHeight = nIHeight*nItems;
957 958 959

      if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
         nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
960
   }
961

962 963 964 965 966
   r.left = rect.left;
   r.top = rect.bottom;
   r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
   r.bottom = r.top + nDroppedHeight;

967
   /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
968 969 970 971
   monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
   mon_info.cbSize = sizeof(mon_info);
   GetMonitorInfoW( monitor, &mon_info );

972 973 974 975 976
   if (r.bottom > mon_info.rcWork.bottom)
   {
       r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
       r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
   }
977

978 979
   NtUserSetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
                       SWP_NOACTIVATE | SWP_SHOWWINDOW );
980

Alexandre Julliard's avatar
Alexandre Julliard committed
981

Alexandre Julliard's avatar
Alexandre Julliard committed
982
   if( !(lphc->wState & CBF_NOREDRAW) )
983
     NtUserRedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
984

Alexandre Julliard's avatar
Alexandre Julliard committed
985
   EnableWindow( lphc->hWndLBox, TRUE );
986
   if (GetCapture() != lphc->self)
987
      NtUserSetCapture(lphc->hWndLBox);
Alexandre Julliard's avatar
Alexandre Julliard committed
988 989 990
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
991 992 993
 *           CBRollUp
 *
 * Hide listbox popup.
Alexandre Julliard's avatar
Alexandre Julliard committed
994
 */
995
static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
Alexandre Julliard's avatar
Alexandre Julliard committed
996
{
997
   HWND	hWnd = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
998

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

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

1004
   if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
Alexandre Julliard's avatar
Alexandre Julliard committed
1005 1006
   {

1007
       if( lphc->wState & CBF_DROPPED )
Alexandre Julliard's avatar
Alexandre Julliard committed
1008
       {
1009
	   RECT	rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1010 1011

	   lphc->wState &= ~CBF_DROPPED;
1012
	   NtUserShowWindow( lphc->hWndLBox, SW_HIDE );
1013

1014 1015 1016 1017 1018
           if(GetCapture() == lphc->hWndLBox)
           {
               ReleaseCapture();
           }

Alexandre Julliard's avatar
Alexandre Julliard committed
1019 1020
	   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
	   {
1021
	       rect = lphc->buttonRect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1022
	   }
1023
	   else
Alexandre Julliard's avatar
Alexandre Julliard committed
1024 1025
           {
	       if( bButton )
1026 1027 1028 1029 1030
	       {
		 UnionRect( &rect,
			    &lphc->buttonRect,
			    &lphc->textRect);
	       }
Alexandre Julliard's avatar
Alexandre Julliard committed
1031
	       else
1032 1033
		 rect = lphc->textRect;

Alexandre Julliard's avatar
Alexandre Julliard committed
1034 1035 1036
	       bButton = TRUE;
	   }

Alexandre Julliard's avatar
Alexandre Julliard committed
1037
	   if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1038 1039
	       NtUserRedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
                                   RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1040 1041 1042
	   CB_NOTIFY( lphc, CBN_CLOSEUP );
       }
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1043 1044 1045
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1046 1047 1048
 *           COMBO_FlipListbox
 *
 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
Alexandre Julliard's avatar
Alexandre Julliard committed
1049
 */
1050
BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
Alexandre Julliard's avatar
Alexandre Julliard committed
1051
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1052 1053
   if( lphc->wState & CBF_DROPPED )
   {
1054
       CBRollUp( lphc, ok, bRedrawButton );
Alexandre Julliard's avatar
Alexandre Julliard committed
1055 1056 1057 1058 1059
       return FALSE;
   }

   CBDropDown( lphc );
   return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1060 1061 1062
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1063
 *           CBRepaintButton
Alexandre Julliard's avatar
Alexandre Julliard committed
1064
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1065
static void CBRepaintButton( LPHEADCOMBO lphc )
1066 1067 1068
{
    NtUserInvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
    UpdateWindow(lphc->self);
Alexandre Julliard's avatar
Alexandre Julliard committed
1069 1070 1071
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1072
 *           COMBO_SetFocus
Alexandre Julliard's avatar
Alexandre Julliard committed
1073
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1074
static void COMBO_SetFocus( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1075
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1076 1077 1078
   if( !(lphc->wState & CBF_FOCUSED) )
   {
       if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1079
           SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1080

1081 1082 1083
       /* This is wrong. Message sequences seem to indicate that this
          is set *after* the notify. */
       /* lphc->wState |= CBF_FOCUSED;  */
1084

1085
       if( !(lphc->wState & CBF_EDIT) )
1086
           NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1087

Alexandre Julliard's avatar
Alexandre Julliard committed
1088
       CB_NOTIFY( lphc, CBN_SETFOCUS );
1089
       lphc->wState |= CBF_FOCUSED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1090
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1091 1092 1093
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1094
 *           COMBO_KillFocus
Alexandre Julliard's avatar
Alexandre Julliard committed
1095
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1096
static void COMBO_KillFocus( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1097
{
1098
   HWND	hWnd = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
1099

Alexandre Julliard's avatar
Alexandre Julliard committed
1100 1101 1102
   if( lphc->wState & CBF_FOCUSED )
   {
       CBRollUp( lphc, FALSE, TRUE );
1103
       if( IsWindow( hWnd ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1104 1105
       {
           if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1106
               SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1107

Alexandre Julliard's avatar
Alexandre Julliard committed
1108 1109 1110
 	   lphc->wState &= ~CBF_FOCUSED;

           /* redraw text */
1111
	   if( !(lphc->wState & CBF_EDIT) )
1112
               NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1113

Alexandre Julliard's avatar
Alexandre Julliard committed
1114 1115 1116
           CB_NOTIFY( lphc, CBN_KILLFOCUS );
       }
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1117 1118
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1119
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1120
 *           COMBO_Command
Alexandre Julliard's avatar
Alexandre Julliard committed
1121
 */
1122
static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
1123
{
1124
   if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
1125
   {
Alexandre Julliard's avatar
Alexandre Julliard committed
1126
       /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
Alexandre Julliard's avatar
Alexandre Julliard committed
1127

Alexandre Julliard's avatar
Alexandre Julliard committed
1128
       switch( HIWORD(wParam) >> 8 )
1129
       {
Alexandre Julliard's avatar
Alexandre Julliard committed
1130
	   case (EN_SETFOCUS >> 8):
Alexandre Julliard's avatar
Alexandre Julliard committed
1131

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

1134
		COMBO_SetFocus( lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1135
	        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1136

Alexandre Julliard's avatar
Alexandre Julliard committed
1137
	   case (EN_KILLFOCUS >> 8):
Alexandre Julliard's avatar
Alexandre Julliard committed
1138

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1141 1142
		/* NOTE: it seems that Windows' edit control sends an
		 * undocumented message WM_USER + 0x1B instead of this
1143
		 * notification (only when it happens to be a part of
Alexandre Julliard's avatar
Alexandre Julliard committed
1144 1145 1146 1147 1148
		 * the combo). ?? - AK.
		 */

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1150

Alexandre Julliard's avatar
Alexandre Julliard committed
1151
	   case (EN_CHANGE >> 8):
1152 1153
	       /*
	        * In some circumstances (when the selection of the combobox
1154
		* is changed for example) we don't want the EN_CHANGE notification
1155
		* to be forwarded to the parent of the combobox. This code
1156
		* checks a flag that is set in these occasions and ignores the
1157 1158
		* notification.
	        */
1159 1160 1161 1162 1163 1164
		if (lphc->wState & CBF_NOLBSELECT)
		{
		  lphc->wState &= ~CBF_NOLBSELECT;
		}
		else
		{
1165
		  CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1166
		}
1167 1168 1169

	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
		  CB_NOTIFY( lphc, CBN_EDITCHANGE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1170 1171 1172
		break;

	   case (EN_UPDATE >> 8):
1173 1174
	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
		  CB_NOTIFY( lphc, CBN_EDITUPDATE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1175 1176 1177 1178 1179 1180 1181 1182
		break;

	   case (EN_ERRSPACE >> 8):
		CB_NOTIFY( lphc, CBN_ERRSPACE );
       }
   }
   else if( lphc->hWndLBox == hWnd )
   {
1183
       switch( (short)HIWORD(wParam) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
       {
	   case LBN_ERRSPACE:
		CB_NOTIFY( lphc, CBN_ERRSPACE );
		break;

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

	   case LBN_SELCHANGE:
	   case LBN_SELCANCEL:

1196 1197
                TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );

1198
                /* do not roll up if selection is being tracked
Austin English's avatar
Austin English committed
1199
                 * by arrow keys in the dropdown listbox */
1200 1201 1202 1203 1204 1205
                if (!(lphc->wState & CBF_NOROLLUP))
                {
                    CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
                }
                else lphc->wState &= ~CBF_NOROLLUP;

1206
		CB_NOTIFY( lphc, CBN_SELCHANGE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1207

1208
		if( HIWORD(wParam) == LBN_SELCHANGE)
1209
		{
1210 1211
		   if( lphc->wState & CBF_EDIT )
		       lphc->wState |= CBF_NOLBSELECT;
1212
		   CBPaintText( lphc, NULL );
1213
		}
1214
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1215 1216 1217 1218 1219

	   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
1220
		 break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1221 1222 1223
       }
   }
   return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1224
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1225

Alexandre Julliard's avatar
Alexandre Julliard committed
1226
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1227 1228 1229
 *           COMBO_ItemOp
 *
 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
Alexandre Julliard's avatar
Alexandre Julliard committed
1230
 */
1231
static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1232
{
1233
   HWND hWnd = lphc->self;
1234
   UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
1235

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

1238
   switch( msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
1239
   {
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
   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
1271
   }
1272
   return SendMessageW(lphc->owner, msg, id, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1273 1274
}

1275

Alexandre Julliard's avatar
Alexandre Julliard committed
1276
/***********************************************************************
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
 *           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)
        {
1299
            WCHAR *lpBuffer = malloc((length + 1) * sizeof(WCHAR));
1300 1301 1302 1303 1304 1305 1306 1307 1308
            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;
            }
1309
            free(lpBuffer);
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
        }
        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
1325 1326 1327
 *
 * 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
1328
 */
1329
static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
Alexandre Julliard's avatar
Alexandre Julliard committed
1330
{
1331
    INT length;
Alexandre Julliard's avatar
Alexandre Julliard committed
1332

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

1336
    /* get it from the listbox */
Alexandre Julliard's avatar
Alexandre Julliard committed
1337

1338 1339 1340
    if (!count || !buf) return 0;
    if( lphc->hWndLBox )
    {
1341
        INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1342 1343 1344 1345 1346 1347 1348
        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)
        {
1349
            char *lpBuffer = malloc(length + 1);
1350 1351 1352 1353 1354 1355 1356 1357 1358
            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;
            }
1359
            free(lpBuffer);
1360 1361
        }
        else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1362

1363 1364 1365
        if (length == LB_ERR) return 0;
        return length;
    }
1366

1367 1368 1369
 error:  /* error - truncate string, return zero */
    buf[0] = 0;
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1370 1371
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1372

Alexandre Julliard's avatar
Alexandre Julliard committed
1373
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1374 1375
 *           CBResetPos
 *
1376
 * This function sets window positions according to the updated
Alexandre Julliard's avatar
Alexandre Julliard committed
1377
 * component placement struct.
Alexandre Julliard's avatar
Alexandre Julliard committed
1378
 */
1379
static void CBResetPos(HEADCOMBO *combo, BOOL redraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1380
{
1381 1382 1383 1384 1385
    BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;

    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
     * sizing messages */
    if (combo->wState & CBF_EDIT)
1386 1387 1388 1389
        NtUserSetWindowPos( combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
                            combo->textRect.right - combo->textRect.left,
                            combo->textRect.bottom - combo->textRect.top,
                            SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0) );
1390

1391 1392 1393 1394
    NtUserSetWindowPos( combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
                        combo->droppedRect.right - combo->droppedRect.left,
                        combo->droppedRect.bottom - combo->droppedRect.top,
                        SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0) );
1395 1396 1397 1398 1399 1400

    if (drop)
    {
        if (combo->wState & CBF_DROPPED)
        {
           combo->wState &= ~CBF_DROPPED;
1401
           NtUserShowWindow( combo->hWndLBox, SW_HIDE );
1402
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1403

1404
        if (redraw && !(combo->wState & CBF_NOREDRAW))
1405
            NtUserRedrawWindow( combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1406
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1407 1408 1409
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1410
 *           COMBO_Size
Alexandre Julliard's avatar
Alexandre Julliard committed
1411
 */
1412
static void COMBO_Size( HEADCOMBO *lphc )
1413
{
1414 1415 1416
    if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
        return;

1417 1418 1419 1420 1421
  /*
   * Those controls are always the same height. So we have to make sure
   * they are not resized to another value.
   */
  if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1422
  {
1423 1424
    int newComboHeight, curComboHeight, curComboWidth;
    RECT rc;
1425

1426 1427 1428
    GetWindowRect(lphc->self, &rc);
    curComboHeight = rc.bottom - rc.top;
    curComboWidth = rc.right - rc.left;
1429
    newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1430 1431 1432 1433 1434 1435 1436 1437 1438

    /*
     * 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
     * combobox is more than the height it should have. In other words,
     * 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.
     */
1439
    if( curComboHeight > newComboHeight )
1440
    {
1441
      TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%ld, oldDropTop=%ld\n",
1442
            curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1443
            lphc->droppedRect.top);
1444
      lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1445 1446 1447 1448
    }
    /*
     * Restore original height
     */
1449 1450 1451
    if (curComboHeight != newComboHeight)
    {
        lphc->wState |= CBF_NORESIZE;
1452 1453
        NtUserSetWindowPos( lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
                            SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW );
1454 1455
        lphc->wState &= ~CBF_NORESIZE;
    }
1456 1457
  }

1458
  CBCalcPlacement(lphc);
1459

1460
  CBResetPos(lphc, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1461 1462
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1463

Alexandre Julliard's avatar
Alexandre Julliard committed
1464
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1465
 *           COMBO_Font
Alexandre Julliard's avatar
Alexandre Julliard committed
1466
 */
1467
static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
Alexandre Julliard's avatar
Alexandre Julliard committed
1468
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1469
  lphc->hFont = hFont;
1470
  lphc->item_height = combo_get_text_height(lphc);
Alexandre Julliard's avatar
Alexandre Julliard committed
1471

1472 1473 1474
  /*
   * Propagate to owned windows.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1475
  if( lphc->wState & CBF_EDIT )
1476 1477
      SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
  SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
Alexandre Julliard's avatar
Alexandre Julliard committed
1478

1479 1480 1481 1482 1483
  /*
   * Redo the layout of the control.
   */
  if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
  {
1484
    CBCalcPlacement(lphc);
1485

1486
    CBResetPos(lphc, TRUE);
1487 1488 1489
  }
  else
  {
1490
    CBForceDummyResize(lphc);
1491
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1492 1493
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1494

Alexandre Julliard's avatar
Alexandre Julliard committed
1495
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1496
 *           COMBO_SetItemHeight
Alexandre Julliard's avatar
Alexandre Julliard committed
1497
 */
1498
static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
Alexandre Julliard's avatar
Alexandre Julliard committed
1499
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1500 1501 1502 1503 1504 1505
   LRESULT	lRet = CB_ERR;

   if( index == -1 ) /* set text field height */
   {
       if( height < 32768 )
       {
1506
           lphc->item_height = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1507 1508 1509 1510 1511 1512

	 /*
	  * Redo the layout of the control.
	  */
	 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
	 {
1513
	   CBCalcPlacement(lphc);
1514

1515
	   CBResetPos(lphc, TRUE);
1516 1517 1518
	 }
	 else
	 {
1519
	   CBForceDummyResize(lphc);
1520
	 }
1521

Alexandre Julliard's avatar
Alexandre Julliard committed
1522 1523
	   lRet = height;
       }
1524
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1525
   else if ( CB_OWNERDRAWN(lphc) )	/* set listbox item height */
1526
       lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
Alexandre Julliard's avatar
Alexandre Julliard committed
1527
   return lRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
1528 1529 1530
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1531
 *           COMBO_SelectString
Alexandre Julliard's avatar
Alexandre Julliard committed
1532
 */
1533
static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1534
{
1535 1536
   INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
                         SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1537 1538
   if( index >= 0 )
   {
1539 1540 1541 1542
     if( lphc->wState & CBF_EDIT )
       CBUpdateEdit( lphc, index );
     else
     {
1543
       NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
1544
     }
Alexandre Julliard's avatar
Alexandre Julliard committed
1545
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1546
   return (LRESULT)index;
Alexandre Julliard's avatar
Alexandre Julliard committed
1547 1548 1549
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1550
 *           COMBO_LButtonDown
Alexandre Julliard's avatar
Alexandre Julliard committed
1551
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1552
static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1553
{
1554
   POINT     pt;
1555
   BOOL      bButton;
1556
   HWND      hWnd = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
1557

1558 1559
   pt.x = (short)LOWORD(lParam);
   pt.y = (short)HIWORD(lParam);
1560 1561
   bButton = PtInRect(&lphc->buttonRect, pt);

Alexandre Julliard's avatar
Alexandre Julliard committed
1562 1563 1564 1565 1566 1567 1568 1569
   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 */

1570
           lphc->wState &= ~CBF_BUTTONDOWN;
Alexandre Julliard's avatar
Alexandre Julliard committed
1571
           CBRollUp( lphc, TRUE, FALSE );
1572
	   if( !IsWindow( hWnd ) ) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584

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

           lphc->wState |= CBF_CAPTURE;
1585
           NtUserSetCapture( hWnd );
1586
           CBDropDown( lphc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1587 1588 1589
       }
       if( bButton ) CBRepaintButton( lphc );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1590 1591 1592
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1593 1594 1595
 *           COMBO_LButtonUp
 *
 * Release capture and stop tracking if needed.
Alexandre Julliard's avatar
Alexandre Julliard committed
1596
 */
1597
static void COMBO_LButtonUp( LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1598
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1599 1600 1601 1602 1603
   if( lphc->wState & CBF_CAPTURE )
   {
       lphc->wState &= ~CBF_CAPTURE;
       if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
       {
1604
	   INT index = CBUpdateLBox( lphc, TRUE );
1605 1606 1607 1608 1609 1610 1611
	   /* 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
1612 1613
       }
       ReleaseCapture();
1614
       NtUserSetCapture(lphc->hWndLBox);
Alexandre Julliard's avatar
Alexandre Julliard committed
1615
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1616

Alexandre Julliard's avatar
Alexandre Julliard committed
1617 1618 1619 1620 1621
   if( lphc->wState & CBF_BUTTONDOWN )
   {
       lphc->wState &= ~CBF_BUTTONDOWN;
       CBRepaintButton( lphc );
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1622 1623 1624
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1625 1626 1627 1628
 *           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
1629
 */
1630
static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1631
{
1632
   POINT  pt;
1633
   RECT   lbRect;
1634

1635 1636
   pt.x = (short)LOWORD(lParam);
   pt.y = (short)HIWORD(lParam);
1637

Alexandre Julliard's avatar
Alexandre Julliard committed
1638 1639
   if( lphc->wState & CBF_BUTTONDOWN )
   {
1640
     BOOL bButton;
Alexandre Julliard's avatar
Alexandre Julliard committed
1641

1642 1643 1644 1645 1646 1647 1648
     bButton = PtInRect(&lphc->buttonRect, pt);

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

1651
   GetClientRect( lphc->hWndLBox, &lbRect );
1652
   MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1653
   if( PtInRect(&lbRect, pt) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1654 1655 1656
   {
       lphc->wState &= ~CBF_CAPTURE;
       ReleaseCapture();
1657
       if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1658

Alexandre Julliard's avatar
Alexandre Julliard committed
1659
       /* hand over pointer tracking */
1660
       SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1661
   }
Alexandre Julliard's avatar
Alexandre Julliard committed
1662 1663
}

1664
static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1665 1666 1667 1668 1669 1670 1671
{
    if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
        return FALSE;

    pcbi->rcItem = lphc->textRect;
    pcbi->rcButton = lphc->buttonRect;
    pcbi->stateButton = 0;
1672
    if (lphc->wState & CBF_BUTTONDOWN)
1673 1674 1675 1676 1677 1678 1679 1680 1681
        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;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1682
/***********************************************************************
1683
 *           ComboWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1684
 */
1685
LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1686
{
1687
      LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1688

1689
      TRACE("[%p]: msg %s wp %08Ix lp %08Ix\n",
1690
            hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1691

1692 1693
      if (!IsWindow(hwnd)) return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1694
      if( lphc || message == WM_NCCREATE )
1695 1696
      switch(message)
      {
Alexandre Julliard's avatar
Alexandre Julliard committed
1697 1698 1699

	/* System messages */

1700
     	case WM_NCCREATE:
1701 1702 1703
	{
		LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
				       ((LPCREATESTRUCTA)lParam)->style;
1704
                return COMBO_NCCreate(hwnd, style);
1705
	}
1706
     	case WM_NCDESTROY:
Alexandre Julliard's avatar
Alexandre Julliard committed
1707
		COMBO_NCDestroy(lphc);
1708
		break;/* -> DefWindowProc */
Alexandre Julliard's avatar
Alexandre Julliard committed
1709

1710 1711
     	case WM_CREATE:
	{
1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723
		HWND hwndParent;
		LONG style;
		if(unicode)
		{
		    hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
		    style = ((LPCREATESTRUCTW)lParam)->style;
		}
		else
		{
		    hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
		    style = ((LPCREATESTRUCTA)lParam)->style;
		}
1724
                return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1725
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1726

1727 1728
        case WM_PRINTCLIENT:
		/* Fallthrough */
Alexandre Julliard's avatar
Alexandre Julliard committed
1729 1730
     	case WM_PAINT:
		/* wParam may contain a valid HDC! */
1731
		return  COMBO_Paint(lphc, (HDC)wParam);
1732

Alexandre Julliard's avatar
Alexandre Julliard committed
1733
	case WM_ERASEBKGND:
1734 1735 1736
                /* do all painting in WM_PAINT like Windows does */
                return 1;

1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748
	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;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1749
	case WM_SIZE:
1750
	        COMBO_Size( lphc );
1751
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1752
	case WM_SETFONT:
1753
		COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1754
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1755
	case WM_GETFONT:
1756
		return  (LRESULT)lphc->hFont;
Alexandre Julliard's avatar
Alexandre Julliard committed
1757
	case WM_SETFOCUS:
1758
               if( lphc->wState & CBF_EDIT ) {
1759
                   NtUserSetFocus( lphc->hWndEdit );
1760 1761 1762 1763 1764 1765
                   /* The first time focus is received, select all the text */
                   if( !(lphc->wState & CBF_BEENFOCUSED) ) {
                       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
                       lphc->wState |= CBF_BEENFOCUSED;
                   }
               }
Alexandre Julliard's avatar
Alexandre Julliard committed
1766 1767
		else
		    COMBO_SetFocus( lphc );
1768
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1769
	case WM_KILLFOCUS:
1770 1771
            {
                HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1772 1773 1774
		if( !hwndFocus ||
		    (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
		    COMBO_KillFocus( lphc );
1775
		return  TRUE;
1776
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1777
	case WM_COMMAND:
1778
		return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1779
	case WM_GETTEXT:
1780 1781
            return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
                           : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1782 1783 1784
	case WM_SETTEXT:
	case WM_GETTEXTLENGTH:
	case WM_CLEAR:
1785 1786
                if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
                {
1787 1788 1789 1790
                    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);
1791
                }
1792
		else if( lphc->wState & CBF_EDIT )
1793
		{
1794
		    LRESULT ret;
1795
		    lphc->wState |= CBF_NOEDITNOTIFY;
1796 1797
		    ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
				    SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1798 1799
		    lphc->wState &= ~CBF_NOEDITNOTIFY;
		    return ret;
1800
		}
1801
		else return CB_ERR;
1802 1803 1804
	case WM_CUT:
        case WM_PASTE:
	case WM_COPY:
1805
		if( lphc->wState & CBF_EDIT )
1806 1807 1808 1809
		{
		    return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
				     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
		}
1810
		else return  CB_ERR;
1811

Alexandre Julliard's avatar
Alexandre Julliard committed
1812 1813 1814 1815
	case WM_DRAWITEM:
	case WM_DELETEITEM:
	case WM_COMPAREITEM:
	case WM_MEASUREITEM:
1816
		return COMBO_ItemOp(lphc, message, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1817 1818
	case WM_ENABLE:
		if( lphc->wState & CBF_EDIT )
1819
		    EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1820
		EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1821 1822

		/* Force the control to repaint when the enabled state changes. */
1823
		NtUserInvalidateRect(lphc->self, NULL, TRUE);
1824
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1825
	case WM_SETREDRAW:
Alexandre Julliard's avatar
Alexandre Julliard committed
1826 1827 1828 1829 1830
		if( wParam )
		    lphc->wState &= ~CBF_NOREDRAW;
		else
		    lphc->wState |= CBF_NOREDRAW;

Alexandre Julliard's avatar
Alexandre Julliard committed
1831
		if( lphc->wState & CBF_EDIT )
1832 1833
		    SendMessageW(lphc->hWndEdit, message, wParam, lParam);
		SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1834
		return  0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1835 1836 1837
	case WM_SYSKEYDOWN:
		if( KEYDATA_ALT & HIWORD(lParam) )
		    if( wParam == VK_UP || wParam == VK_DOWN )
1838 1839
			COMBO_FlipListbox( lphc, FALSE, FALSE );
                return  0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1840 1841

	case WM_KEYDOWN:
1842
		if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1843 1844
		     (lphc->wState & CBF_DROPPED))
		{
1845
		   CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1846 1847
		   return TRUE;
		}
1848 1849 1850 1851 1852
               else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
               {
                  COMBO_FlipListbox( lphc, FALSE, FALSE );
                  return TRUE;
               }
1853 1854 1855 1856 1857
               /* fall through */
	case WM_CHAR:
	case WM_IME_CHAR:
	{
		HWND hwndTarget;
1858

Alexandre Julliard's avatar
Alexandre Julliard committed
1859
		if( lphc->wState & CBF_EDIT )
1860
		    hwndTarget = lphc->hWndEdit;
Alexandre Julliard's avatar
Alexandre Julliard committed
1861
		else
1862 1863 1864 1865 1866
		    hwndTarget = lphc->hWndLBox;

		return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
				 SendMessageA(hwndTarget, message, wParam, lParam);
	}
1867
	case WM_LBUTTONDOWN:
1868 1869
		if (!(lphc->wState & CBF_FOCUSED)) NtUserSetFocus( lphc->self );
		if (lphc->wState & CBF_FOCUSED) COMBO_LButtonDown( lphc, lParam );
1870
		return  TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1871
	case WM_LBUTTONUP:
1872
		COMBO_LButtonUp( lphc );
1873
		return  TRUE;
1874 1875
	case WM_MOUSEMOVE:
		if( lphc->wState & CBF_CAPTURE )
Alexandre Julliard's avatar
Alexandre Julliard committed
1876
		    COMBO_MouseMove( lphc, wParam, lParam );
1877
		return  TRUE;
1878 1879 1880

        case WM_MOUSEWHEEL:
                if (wParam & (MK_SHIFT | MK_CONTROL))
1881 1882
                    return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
				     DefWindowProcA(hwnd, message, wParam, lParam);
1883 1884 1885

                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);
1886 1887
                return TRUE;

1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
        case WM_CTLCOLOR:
        case WM_CTLCOLORMSGBOX:
        case WM_CTLCOLOREDIT:
        case WM_CTLCOLORLISTBOX:
        case WM_CTLCOLORBTN:
        case WM_CTLCOLORDLG:
        case WM_CTLCOLORSCROLLBAR:
        case WM_CTLCOLORSTATIC:
            if (lphc->owner)
                return SendMessageW(lphc->owner, message, wParam, lParam);
            break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1900 1901
	/* Combo messages */

1902
	case CB_ADDSTRING:
1903 1904 1905
		if( unicode )
                {
                    if( lphc->dwStyle & CBS_LOWERCASE )
1906
                        CharLowerW((LPWSTR)lParam);
1907
                    else if( lphc->dwStyle & CBS_UPPERCASE )
1908
                        CharUpperW((LPWSTR)lParam);
1909 1910
                    return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
                }
1911 1912
                else /* unlike the unicode version, the ansi version does not overwrite
                        the string if converting case */
1913
                {
1914
                    char *string = NULL;
1915
                    LRESULT ret;
1916
                    if( lphc->dwStyle & CBS_LOWERCASE )
1917
                    {
1918
                        string = strdup((char *)lParam);
1919
                        CharLowerA(string);
1920
                    }
1921

1922
                    else if( lphc->dwStyle & CBS_UPPERCASE )
1923
                    {
1924
                        string = strdup((char *)lParam);
1925
                        CharUpperA(string);
1926
                    }
1927

1928
                    ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
1929
                    free(string);
1930
                    return ret;
1931
                }
1932
	case CB_INSERTSTRING:
1933 1934 1935
		if( unicode )
                {
                    if( lphc->dwStyle & CBS_LOWERCASE )
1936
                        CharLowerW((LPWSTR)lParam);
1937
                    else if( lphc->dwStyle & CBS_UPPERCASE )
1938
                        CharUpperW((LPWSTR)lParam);
1939 1940 1941 1942 1943
                    return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
                }
                else
                {
                    if( lphc->dwStyle & CBS_LOWERCASE )
1944
                        CharLowerA((LPSTR)lParam);
1945
                    else if( lphc->dwStyle & CBS_UPPERCASE )
1946 1947
                        CharUpperA((LPSTR)lParam);

1948 1949
                    return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
                }
1950
	case CB_DELETESTRING:
1951 1952
		return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
				 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1953
	case CB_SELECTSTRING:
1954
		return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
1955
	case CB_FINDSTRING:
1956 1957
		return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1958
	case CB_FINDSTRINGEXACT:
1959 1960
		return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1961
	case CB_SETITEMHEIGHT:
1962
		return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1963 1964
	case CB_GETITEMHEIGHT:
		if( (INT)wParam >= 0 )	/* listbox item */
1965
                    return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1966
                return CBGetTextAreaHeight(lphc, FALSE);
1967
	case CB_RESETCONTENT:
1968
		SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
1969
                if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
1970
                    SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
1971
                else
1972
                    NtUserInvalidateRect(lphc->self, NULL, TRUE);
1973
		return  TRUE;
1974
	case CB_INITSTORAGE:
1975
		return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1976
	case CB_GETHORIZONTALEXTENT:
1977
		return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1978
	case CB_SETHORIZONTALEXTENT:
1979
		return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1980
	case CB_GETTOPINDEX:
1981
		return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1982
	case CB_GETLOCALE:
1983
		return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1984
	case CB_SETLOCALE:
1985
		return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996
	case CB_SETDROPPEDWIDTH:
		if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
		    (INT)wParam >= 32768 )
		    return CB_ERR;
		/* new value must be higher than combobox width */
		if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
		    lphc->droppedWidth = wParam;
		else if(wParam)
		    lphc->droppedWidth = 0;

		/* recalculate the combobox area */
1997
		CBCalcPlacement(lphc);
1998 1999

		/* fall through */
2000
	case CB_GETDROPPEDWIDTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
2001
		if( lphc->droppedWidth )
2002 2003
                    return  lphc->droppedWidth;
		return  lphc->droppedRect.right - lphc->droppedRect.left;
2004 2005
	case CB_GETDROPPEDCONTROLRECT:
		if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2006
		return  CB_OKAY;
2007
	case CB_GETDROPPEDSTATE:
2008
		return (lphc->wState & CBF_DROPPED) != 0;
2009
	case CB_DIR:
Carlos Lozano's avatar
Carlos Lozano committed
2010 2011
		return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2012

2013
	case CB_SHOWDROPDOWN:
2014 2015 2016 2017
		if( CB_GETTYPE(lphc) != CBS_SIMPLE )
		{
		    if( wParam )
		    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2018 2019
			if( !(lphc->wState & CBF_DROPPED) )
			    CBDropDown( lphc );
2020
		    }
2021 2022
		    else
			if( lphc->wState & CBF_DROPPED )
Alexandre Julliard's avatar
Alexandre Julliard committed
2023
		            CBRollUp( lphc, FALSE, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2024
		}
2025
		return  TRUE;
2026
	case CB_GETCOUNT:
2027
		return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2028
	case CB_GETCURSEL:
2029
		return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2030
	case CB_SETCURSEL:
2031
		lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2032
	        if( lParam >= 0 )
2033
	            SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2034 2035

		/* no LBN_SELCHANGE in this case, update manually */
2036
                CBPaintText( lphc, NULL );
2037
		lphc->wState &= ~CBF_SELCHANGE;
2038
	        return  lParam;
2039
	case CB_GETLBTEXT:
2040 2041
		return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
				 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2042
	case CB_GETLBTEXTLEN:
2043 2044
                return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
                                 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2045
	case CB_GETITEMDATA:
2046
		return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2047
	case CB_SETITEMDATA:
2048
		return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2049
	case CB_GETEDITSEL:
2050
		/* Edit checks passed parameters itself */
Alexandre Julliard's avatar
Alexandre Julliard committed
2051
		if( lphc->wState & CBF_EDIT )
2052
		    return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2053
		return  CB_ERR;
2054
	case CB_SETEDITSEL:
2055
		if( lphc->wState & CBF_EDIT )
2056
                    return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2057
			  (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2058
		return  CB_ERR;
2059
	case CB_SETEXTENDEDUI:
2060
                if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2061
                    return  CB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
2062 2063 2064
		if( wParam )
		    lphc->wState |= CBF_EUI;
		else lphc->wState &= ~CBF_EUI;
2065
		return  CB_OKAY;
2066
	case CB_GETEXTENDEDUI:
2067
		return (lphc->wState & CBF_EUI) != 0;
2068 2069
	case CB_GETCOMBOBOXINFO:
		return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2070 2071 2072
	case CB_LIMITTEXT:
		if( lphc->wState & CBF_EDIT )
			return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2073
		return  TRUE;
2074 2075
	default:
		if (message >= WM_USER)
2076
		    WARN("unknown msg WM_USER+%04x wp=%04Ix lp=%08Ix\n",
2077 2078
			message - WM_USER, wParam, lParam );
		break;
2079 2080 2081
      }
      return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
                       DefWindowProcA(hwnd, message, wParam, lParam);
2082 2083
}

2084 2085 2086
/*************************************************************************
 *           GetComboBoxInfo   (USER32.@)
 */
2087 2088
BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
			    PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2089
{
2090 2091
    TRACE("(%p, %p)\n", hwndCombo, pcbi);
    return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
2092
}