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