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