1 /* 2 * ComboBoxEx control 3 * 4 * Copyright 1998, 1999 Eric Kohl 5 * Copyright 2000, 2001, 2002 Guy Albertelli <galberte@neo.lrun.com> 6 * Copyright 2002 Dimitrie O. Paun 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include <stdarg.h> 24 #include <string.h> 25 #include "windef.h" 26 #include "winbase.h" 27 #include "wingdi.h" 28 #include "winuser.h" 29 #include "winnls.h" 30 #include "commctrl.h" 31 #include "comctl32.h" 32 #include "wine/debug.h" 33 #include "wine/unicode.h" 34 35 WINE_DEFAULT_DEBUG_CHANNEL(comboex); 36 37 /* Item structure */ 38 typedef struct _CBE_ITEMDATA 39 { 40 struct _CBE_ITEMDATA *next; 41 UINT mask; 42 LPWSTR pszText; 43 LPWSTR pszTemp; 44 int cchTextMax; 45 int iImage; 46 int iSelectedImage; 47 int iOverlay; 48 int iIndent; 49 LPARAM lParam; 50 } CBE_ITEMDATA; 51 52 /* ComboBoxEx structure */ 53 typedef struct 54 { 55 HIMAGELIST himl; 56 HWND hwndSelf; /* my own hwnd */ 57 HWND hwndNotify; /* my parent hwnd */ 58 HWND hwndCombo; 59 HWND hwndEdit; 60 DWORD dwExtStyle; 61 INT selected; /* index of selected item */ 62 DWORD flags; /* WINE internal flags */ 63 HFONT defaultFont; 64 HFONT font; 65 INT nb_items; /* Number of items */ 66 BOOL unicode; /* TRUE if this window is Unicode */ 67 BOOL NtfUnicode; /* TRUE if parent wants notify in Unicode */ 68 CBE_ITEMDATA edit; /* item data for edit item */ 69 CBE_ITEMDATA *items; /* Array of items */ 70 } COMBOEX_INFO; 71 72 /* internal flags in the COMBOEX_INFO structure */ 73 #define WCBE_ACTEDIT 0x00000001 /* Edit active i.e. 74 * CBEN_BEGINEDIT issued 75 * but CBEN_ENDEDIT{A|W} 76 * not yet issued. */ 77 #define WCBE_EDITCHG 0x00000002 /* Edit issued EN_CHANGE */ 78 #define WCBE_EDITHASCHANGED (WCBE_ACTEDIT | WCBE_EDITCHG) 79 #define WCBE_EDITFOCUSED 0x00000004 /* Edit control has focus */ 80 #define WCBE_MOUSECAPTURED 0x00000008 /* Combo has captured mouse */ 81 #define WCBE_MOUSEDRAGGED 0x00000010 /* User has dragged in combo */ 82 83 #define ID_CB_EDIT 1001 84 85 86 /* 87 * Special flag set in DRAWITEMSTRUCT itemState field. It is set by 88 * the ComboEx version of the Combo Window Proc so that when the 89 * WM_DRAWITEM message is then passed to ComboEx, we know that this 90 * particular WM_DRAWITEM message is for listbox only items. Any message 91 * without this flag is then for the Edit control field. 92 * 93 * We really cannot use the ODS_COMBOBOXEDIT flag because MSDN states that 94 * only version 4.0 applications will have ODS_COMBOBOXEDIT set. 95 */ 96 #define ODS_COMBOEXLBOX 0x4000 97 98 99 100 /* Height in pixels of control over the amount of the selected font */ 101 #define CBE_EXTRA 3 102 103 /* Indent amount per MS documentation */ 104 #define CBE_INDENT 10 105 106 /* Offset in pixels from left side for start of image or text */ 107 #define CBE_STARTOFFSET 6 108 109 /* Offset between image and text */ 110 #define CBE_SEP 4 111 112 #define COMBO_SUBCLASSID 1 113 #define EDIT_SUBCLASSID 2 114 115 #define COMBOEX_GetInfoPtr(hwnd) ((COMBOEX_INFO *)GetWindowLongPtrW (hwnd, 0)) 116 117 static LRESULT CALLBACK COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 118 UINT_PTR uId, DWORD_PTR ref_data); 119 static LRESULT CALLBACK COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 120 UINT_PTR uId, DWORD_PTR ref_data); 121 static LRESULT COMBOEX_Destroy (COMBOEX_INFO *infoPtr); 122 typedef INT (WINAPI *cmp_func_t)(LPCWSTR, LPCWSTR); 123 124 static inline BOOL is_textW(LPCWSTR str) 125 { 126 return str && str != LPSTR_TEXTCALLBACKW; 127 } 128 129 static inline BOOL is_textA(LPCSTR str) 130 { 131 return str && str != LPSTR_TEXTCALLBACKA; 132 } 133 134 static inline LPCSTR debugstr_txt(LPCWSTR str) 135 { 136 if (str == LPSTR_TEXTCALLBACKW) return "(callback)"; 137 return debugstr_w(str); 138 } 139 140 static void COMBOEX_DumpItem (CBE_ITEMDATA const *item) 141 { 142 TRACE("item %p - mask=%08x, pszText=%p, cchTM=%d, iImage=%d\n", 143 item, item->mask, item->pszText, item->cchTextMax, item->iImage); 144 TRACE("item %p - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%08lx\n", 145 item, item->iSelectedImage, item->iOverlay, item->iIndent, item->lParam); 146 if (item->mask & CBEIF_TEXT) 147 TRACE("item %p - pszText=%s\n", item, debugstr_txt(item->pszText)); 148 } 149 150 151 static void COMBOEX_DumpInput (COMBOBOXEXITEMW const *input) 152 { 153 TRACE("input - mask=%08x, iItem=%ld, pszText=%p, cchTM=%d, iImage=%d\n", 154 input->mask, input->iItem, input->pszText, input->cchTextMax, 155 input->iImage); 156 if (input->mask & CBEIF_TEXT) 157 TRACE("input - pszText=<%s>\n", debugstr_txt(input->pszText)); 158 TRACE("input - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%08lx\n", 159 input->iSelectedImage, input->iOverlay, input->iIndent, input->lParam); 160 } 161 162 163 static inline CBE_ITEMDATA *get_item_data(const COMBOEX_INFO *infoPtr, INT index) 164 { 165 return (CBE_ITEMDATA *)SendMessageW (infoPtr->hwndCombo, CB_GETITEMDATA, 166 index, 0); 167 } 168 169 static inline cmp_func_t get_cmp_func(COMBOEX_INFO const *infoPtr) 170 { 171 return infoPtr->dwExtStyle & CBES_EX_CASESENSITIVE ? lstrcmpW : lstrcmpiW; 172 } 173 174 static INT COMBOEX_Notify (const COMBOEX_INFO *infoPtr, INT code, NMHDR *hdr) 175 { 176 hdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf); 177 hdr->hwndFrom = infoPtr->hwndSelf; 178 hdr->code = code; 179 if (infoPtr->NtfUnicode) 180 return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)hdr); 181 else 182 return SendMessageA (infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)hdr); 183 } 184 185 186 static INT 187 COMBOEX_NotifyItem (const COMBOEX_INFO *infoPtr, UINT code, NMCOMBOBOXEXW *hdr) 188 { 189 /* Change the Text item from Unicode to ANSI if necessary for NOTIFY */ 190 if (infoPtr->NtfUnicode) 191 return COMBOEX_Notify (infoPtr, code, &hdr->hdr); 192 else { 193 LPWSTR wstr = hdr->ceItem.pszText; 194 LPSTR astr = 0; 195 INT ret, len = 0; 196 197 if ((hdr->ceItem.mask & CBEIF_TEXT) && is_textW(wstr)) { 198 len = WideCharToMultiByte (CP_ACP, 0, wstr, -1, 0, 0, NULL, NULL); 199 if (len > 0) { 200 astr = Alloc ((len + 1)*sizeof(CHAR)); 201 if (!astr) return 0; 202 WideCharToMultiByte (CP_ACP, 0, wstr, -1, astr, len, 0, 0); 203 hdr->ceItem.pszText = (LPWSTR)astr; 204 } 205 } 206 207 if (code == CBEN_ENDEDITW) code = CBEN_ENDEDITA; 208 else if (code == CBEN_GETDISPINFOW) code = CBEN_GETDISPINFOA; 209 else if (code == CBEN_DRAGBEGINW) code = CBEN_DRAGBEGINA; 210 211 ret = COMBOEX_Notify (infoPtr, code, (NMHDR *)hdr); 212 213 if (astr && hdr->ceItem.pszText == (LPWSTR)astr) 214 hdr->ceItem.pszText = wstr; 215 216 Free(astr); 217 218 return ret; 219 } 220 } 221 222 223 static INT COMBOEX_NotifyEndEdit (const COMBOEX_INFO *infoPtr, NMCBEENDEDITW *neew, LPCWSTR wstr) 224 { 225 /* Change the Text item from Unicode to ANSI if necessary for NOTIFY */ 226 if (infoPtr->NtfUnicode) { 227 lstrcpynW(neew->szText, wstr, CBEMAXSTRLEN); 228 return COMBOEX_Notify (infoPtr, CBEN_ENDEDITW, &neew->hdr); 229 } else { 230 NMCBEENDEDITA neea; 231 232 neea.hdr = neew->hdr; 233 neea.fChanged = neew->fChanged; 234 neea.iNewSelection = neew->iNewSelection; 235 WideCharToMultiByte (CP_ACP, 0, wstr, -1, neea.szText, CBEMAXSTRLEN, 0, 0); 236 neea.iWhy = neew->iWhy; 237 238 return COMBOEX_Notify (infoPtr, CBEN_ENDEDITA, &neea.hdr); 239 } 240 } 241 242 243 static void COMBOEX_NotifyDragBegin(const COMBOEX_INFO *infoPtr, LPCWSTR wstr) 244 { 245 /* Change the Text item from Unicode to ANSI if necessary for NOTIFY */ 246 if (infoPtr->NtfUnicode) { 247 NMCBEDRAGBEGINW ndbw; 248 249 ndbw.iItemid = -1; 250 lstrcpynW(ndbw.szText, wstr, CBEMAXSTRLEN); 251 COMBOEX_Notify (infoPtr, CBEN_DRAGBEGINW, &ndbw.hdr); 252 } else { 253 NMCBEDRAGBEGINA ndba; 254 255 ndba.iItemid = -1; 256 WideCharToMultiByte (CP_ACP, 0, wstr, -1, ndba.szText, CBEMAXSTRLEN, 0, 0); 257 258 COMBOEX_Notify (infoPtr, CBEN_DRAGBEGINA, &ndba.hdr); 259 } 260 } 261 262 263 static void COMBOEX_FreeText (CBE_ITEMDATA *item) 264 { 265 if (is_textW(item->pszText)) Free(item->pszText); 266 item->pszText = NULL; 267 Free(item->pszTemp); 268 item->pszTemp = NULL; 269 } 270 271 272 static INT COMBOEX_GetIndex(COMBOEX_INFO const *infoPtr, CBE_ITEMDATA const *item) 273 { 274 CBE_ITEMDATA const *moving; 275 INT index; 276 277 moving = infoPtr->items; 278 index = infoPtr->nb_items - 1; 279 280 while (moving && (moving != item)) { 281 moving = moving->next; 282 index--; 283 } 284 if (!moving || (index < 0)) { 285 ERR("COMBOBOXEX item structures broken. Please report!\n"); 286 return -1; 287 } 288 return index; 289 } 290 291 292 static LPCWSTR COMBOEX_GetText(const COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item) 293 { 294 NMCOMBOBOXEXW nmce; 295 LPWSTR text, buf; 296 INT len; 297 298 if (item->pszText != LPSTR_TEXTCALLBACKW) 299 return item->pszText; 300 301 ZeroMemory(&nmce, sizeof(nmce)); 302 nmce.ceItem.mask = CBEIF_TEXT; 303 nmce.ceItem.lParam = item->lParam; 304 nmce.ceItem.iItem = COMBOEX_GetIndex(infoPtr, item); 305 COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce); 306 307 if (is_textW(nmce.ceItem.pszText)) { 308 len = MultiByteToWideChar (CP_ACP, 0, (LPSTR)nmce.ceItem.pszText, -1, NULL, 0); 309 buf = Alloc ((len + 1)*sizeof(WCHAR)); 310 if (buf) 311 MultiByteToWideChar (CP_ACP, 0, (LPSTR)nmce.ceItem.pszText, -1, buf, len); 312 if (nmce.ceItem.mask & CBEIF_DI_SETITEM) { 313 COMBOEX_FreeText(item); 314 item->pszText = buf; 315 } else { 316 Free(item->pszTemp); 317 item->pszTemp = buf; 318 } 319 text = buf; 320 } else 321 text = nmce.ceItem.pszText; 322 323 if (nmce.ceItem.mask & CBEIF_DI_SETITEM) 324 item->pszText = text; 325 return text; 326 } 327 328 329 static void COMBOEX_GetComboFontSize (const COMBOEX_INFO *infoPtr, SIZE *size) 330 { 331 static const WCHAR strA[] = { 'A', 0 }; 332 HFONT nfont, ofont; 333 HDC mydc; 334 335 mydc = GetDC (0); /* why the entire screen???? */ 336 nfont = (HFONT)SendMessageW (infoPtr->hwndCombo, WM_GETFONT, 0, 0); 337 ofont = SelectObject (mydc, nfont); 338 GetTextExtentPointW (mydc, strA, 1, size); 339 SelectObject (mydc, ofont); 340 ReleaseDC (0, mydc); 341 TRACE("selected font hwnd=%p, height=%d\n", nfont, size->cy); 342 } 343 344 345 static void COMBOEX_CopyItem (const CBE_ITEMDATA *item, COMBOBOXEXITEMW *cit) 346 { 347 if (cit->mask & CBEIF_TEXT) { 348 /* 349 * when given a text buffer actually use that buffer 350 */ 351 if (cit->pszText) { 352 if (is_textW(item->pszText)) 353 lstrcpynW(cit->pszText, item->pszText, cit->cchTextMax); 354 else 355 cit->pszText[0] = 0; 356 } else { 357 cit->pszText = item->pszText; 358 cit->cchTextMax = item->cchTextMax; 359 } 360 } 361 if (cit->mask & CBEIF_IMAGE) 362 cit->iImage = item->iImage; 363 if (cit->mask & CBEIF_SELECTEDIMAGE) 364 cit->iSelectedImage = item->iSelectedImage; 365 if (cit->mask & CBEIF_OVERLAY) 366 cit->iOverlay = item->iOverlay; 367 if (cit->mask & CBEIF_INDENT) 368 cit->iIndent = item->iIndent; 369 if (cit->mask & CBEIF_LPARAM) 370 cit->lParam = item->lParam; 371 } 372 373 374 static void COMBOEX_AdjustEditPos (const COMBOEX_INFO *infoPtr) 375 { 376 SIZE mysize; 377 INT x, y, w, h, xioff; 378 RECT rect; 379 380 if (!infoPtr->hwndEdit) return; 381 382 if (infoPtr->himl && !(infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGEINDENT)) { 383 IMAGEINFO iinfo; 384 iinfo.rcImage.left = iinfo.rcImage.right = 0; 385 ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo); 386 xioff = iinfo.rcImage.right - iinfo.rcImage.left + CBE_SEP; 387 } else xioff = 0; 388 389 GetClientRect (infoPtr->hwndCombo, &rect); 390 InflateRect (&rect, -2, -2); 391 InvalidateRect (infoPtr->hwndCombo, &rect, TRUE); 392 393 /* reposition the Edit control based on whether icon exists */ 394 COMBOEX_GetComboFontSize (infoPtr, &mysize); 395 TRACE("Combo font x=%d, y=%d\n", mysize.cx, mysize.cy); 396 x = xioff + CBE_STARTOFFSET + 1; 397 w = rect.right-rect.left - x - GetSystemMetrics(SM_CXVSCROLL) - 1; 398 h = mysize.cy + 1; 399 y = rect.bottom - h - 1; 400 401 TRACE("Combo client (%s), setting Edit to (%d,%d)-(%d,%d)\n", 402 wine_dbgstr_rect(&rect), x, y, x + w, y + h); 403 SetWindowPos(infoPtr->hwndEdit, HWND_TOP, x, y, w, h, 404 SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER); 405 } 406 407 408 static void COMBOEX_ReSize (const COMBOEX_INFO *infoPtr) 409 { 410 SIZE mysize; 411 LONG cy; 412 IMAGEINFO iinfo; 413 414 COMBOEX_GetComboFontSize (infoPtr, &mysize); 415 cy = mysize.cy + CBE_EXTRA; 416 if (infoPtr->himl && ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo)) { 417 cy = max (iinfo.rcImage.bottom - iinfo.rcImage.top, cy); 418 TRACE("upgraded height due to image: height=%d\n", cy); 419 } 420 SendMessageW (infoPtr->hwndSelf, CB_SETITEMHEIGHT, -1, cy); 421 if (infoPtr->hwndCombo) { 422 SendMessageW (infoPtr->hwndCombo, CB_SETITEMHEIGHT, 0, cy); 423 if ( !(infoPtr->flags & CBES_EX_NOSIZELIMIT)) { 424 RECT comboRect, ourRect; 425 GetWindowRect(infoPtr->hwndCombo, &comboRect); 426 GetWindowRect(infoPtr->hwndSelf, &ourRect); 427 if (comboRect.bottom > ourRect.bottom) 428 SetWindowPos( infoPtr->hwndSelf, 0, 0, 0, ourRect.right - ourRect.left, 429 comboRect.bottom - comboRect.top, 430 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW ); 431 } 432 } 433 } 434 435 436 static void COMBOEX_SetEditText (const COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item) 437 { 438 if (!infoPtr->hwndEdit) return; 439 440 if (item->mask & CBEIF_TEXT) { 441 SendMessageW (infoPtr->hwndEdit, WM_SETTEXT, 0, (LPARAM)COMBOEX_GetText(infoPtr, item)); 442 SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, 0); 443 SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, -1); 444 } 445 } 446 447 448 static CBE_ITEMDATA *COMBOEX_FindItem(COMBOEX_INFO *infoPtr, INT_PTR index) 449 { 450 CBE_ITEMDATA *item; 451 INT i; 452 453 if ((index >= infoPtr->nb_items) || (index < -1)) 454 return NULL; 455 if (index == -1) 456 return &infoPtr->edit; 457 item = infoPtr->items; 458 i = infoPtr->nb_items - 1; 459 460 /* find the item in the list */ 461 while (item && (i > index)) { 462 item = item->next; 463 i--; 464 } 465 if (!item || (i != index)) { 466 ERR("COMBOBOXEX item structures broken. Please report!\n"); 467 return 0; 468 } 469 return item; 470 } 471 472 /* *** CBEM_xxx message support *** */ 473 474 static UINT COMBOEX_GetListboxText(COMBOEX_INFO *infoPtr, INT_PTR n, LPWSTR buf) 475 { 476 CBE_ITEMDATA *item; 477 LPCWSTR str; 478 479 item = COMBOEX_FindItem(infoPtr, n); 480 if (!item) 481 return 0; 482 483 str = COMBOEX_GetText(infoPtr, item); 484 if (!str) 485 { 486 if (buf) 487 { 488 if (infoPtr->unicode) 489 buf[0] = 0; 490 else 491 *((LPSTR)buf) = 0; 492 } 493 return 0; 494 } 495 496 if (infoPtr->unicode) 497 { 498 if (buf) 499 lstrcpyW(buf, str); 500 return lstrlenW(str); 501 } 502 else 503 { 504 UINT r; 505 r = WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)buf, 0x40000000, NULL, NULL); 506 if (r) r--; 507 return r; 508 } 509 } 510 511 512 static INT COMBOEX_DeleteItem (COMBOEX_INFO *infoPtr, INT_PTR index) 513 { 514 TRACE("(index=%ld)\n", index); 515 516 /* if item number requested does not exist then return failure */ 517 if ((index >= infoPtr->nb_items) || (index < 0)) return CB_ERR; 518 if (!COMBOEX_FindItem(infoPtr, index)) return CB_ERR; 519 520 /* doing this will result in WM_DELETEITEM being issued */ 521 SendMessageW (infoPtr->hwndCombo, CB_DELETESTRING, index, 0); 522 523 return infoPtr->nb_items; 524 } 525 526 527 static BOOL COMBOEX_GetItemW (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMW *cit) 528 { 529 INT_PTR index = cit->iItem; 530 CBE_ITEMDATA *item; 531 532 TRACE("\n"); 533 534 /* if item number requested does not exist then return failure */ 535 if ((index >= infoPtr->nb_items) || (index < -1)) return FALSE; 536 537 /* if the item is the edit control and there is no edit control, skip */ 538 if ((index == -1) && !infoPtr->hwndEdit) return FALSE; 539 540 if (!(item = COMBOEX_FindItem(infoPtr, index))) return FALSE; 541 542 COMBOEX_CopyItem (item, cit); 543 544 return TRUE; 545 } 546 547 548 static BOOL COMBOEX_GetItemA (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMA *cit) 549 { 550 COMBOBOXEXITEMW tmpcit; 551 552 TRACE("\n"); 553 554 tmpcit.mask = cit->mask; 555 tmpcit.iItem = cit->iItem; 556 tmpcit.pszText = 0; 557 if(!COMBOEX_GetItemW (infoPtr, &tmpcit)) return FALSE; 558 559 if (cit->mask & CBEIF_TEXT) 560 { 561 if (is_textW(tmpcit.pszText) && cit->pszText) 562 WideCharToMultiByte(CP_ACP, 0, tmpcit.pszText, -1, 563 cit->pszText, cit->cchTextMax, NULL, NULL); 564 else if (cit->pszText) cit->pszText[0] = 0; 565 else cit->pszText = (LPSTR)tmpcit.pszText; 566 } 567 568 if (cit->mask & CBEIF_IMAGE) 569 cit->iImage = tmpcit.iImage; 570 if (cit->mask & CBEIF_SELECTEDIMAGE) 571 cit->iSelectedImage = tmpcit.iSelectedImage; 572 if (cit->mask & CBEIF_OVERLAY) 573 cit->iOverlay = tmpcit.iOverlay; 574 if (cit->mask & CBEIF_INDENT) 575 cit->iIndent = tmpcit.iIndent; 576 if (cit->mask & CBEIF_LPARAM) 577 cit->lParam = tmpcit.lParam; 578 579 return TRUE; 580 } 581 582 583 static inline BOOL COMBOEX_HasEditChanged (COMBOEX_INFO const *infoPtr) 584 { 585 return infoPtr->hwndEdit && (infoPtr->flags & WCBE_EDITHASCHANGED) == WCBE_EDITHASCHANGED; 586 } 587 588 589 static INT COMBOEX_InsertItemW (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMW const *cit) 590 { 591 INT_PTR index; 592 CBE_ITEMDATA *item; 593 NMCOMBOBOXEXW nmcit; 594 595 TRACE("\n"); 596 597 if (TRACE_ON(comboex)) COMBOEX_DumpInput (cit); 598 599 /* get real index of item to insert */ 600 index = cit->iItem; 601 if (index == -1) index = infoPtr->nb_items; 602 if (index > infoPtr->nb_items) return -1; 603 604 /* get zero-filled space and chain it in */ 605 if(!(item = Alloc (sizeof(*item)))) return -1; 606 607 /* locate position to insert new item in */ 608 if (index == infoPtr->nb_items) { 609 /* fast path for iItem = -1 */ 610 item->next = infoPtr->items; 611 infoPtr->items = item; 612 } 613 else { 614 INT i = infoPtr->nb_items-1; 615 CBE_ITEMDATA *moving = infoPtr->items; 616 617 while ((i > index) && moving) { 618 moving = moving->next; 619 i--; 620 } 621 if (!moving) { 622 ERR("COMBOBOXEX item structures broken. Please report!\n"); 623 Free(item); 624 return -1; 625 } 626 item->next = moving->next; 627 moving->next = item; 628 } 629 630 /* fill in our hidden item structure */ 631 item->mask = cit->mask; 632 if (item->mask & CBEIF_TEXT) { 633 INT len = 0; 634 635 if (is_textW(cit->pszText)) len = strlenW (cit->pszText); 636 if (len > 0) { 637 item->pszText = Alloc ((len + 1)*sizeof(WCHAR)); 638 if (!item->pszText) { 639 Free(item); 640 return -1; 641 } 642 strcpyW (item->pszText, cit->pszText); 643 } 644 else if (cit->pszText == LPSTR_TEXTCALLBACKW) 645 item->pszText = LPSTR_TEXTCALLBACKW; 646 item->cchTextMax = cit->cchTextMax; 647 } 648 if (item->mask & CBEIF_IMAGE) 649 item->iImage = cit->iImage; 650 if (item->mask & CBEIF_SELECTEDIMAGE) 651 item->iSelectedImage = cit->iSelectedImage; 652 if (item->mask & CBEIF_OVERLAY) 653 item->iOverlay = cit->iOverlay; 654 if (item->mask & CBEIF_INDENT) 655 item->iIndent = cit->iIndent; 656 if (item->mask & CBEIF_LPARAM) 657 item->lParam = cit->lParam; 658 infoPtr->nb_items++; 659 660 if (TRACE_ON(comboex)) COMBOEX_DumpItem (item); 661 662 SendMessageW (infoPtr->hwndCombo, CB_INSERTSTRING, cit->iItem, (LPARAM)item); 663 664 memset (&nmcit.ceItem, 0, sizeof(nmcit.ceItem)); 665 nmcit.ceItem.mask=~0; 666 COMBOEX_CopyItem (item, &nmcit.ceItem); 667 COMBOEX_NotifyItem (infoPtr, CBEN_INSERTITEM, &nmcit); 668 669 return index; 670 671 } 672 673 674 static INT COMBOEX_InsertItemA (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMA const *cit) 675 { 676 COMBOBOXEXITEMW citW; 677 LPWSTR wstr = NULL; 678 INT ret; 679 680 memcpy(&citW,cit,sizeof(COMBOBOXEXITEMA)); 681 if (cit->mask & CBEIF_TEXT && is_textA(cit->pszText)) { 682 INT len = MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, NULL, 0); 683 wstr = Alloc ((len + 1)*sizeof(WCHAR)); 684 if (!wstr) return -1; 685 MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, wstr, len); 686 citW.pszText = wstr; 687 } 688 ret = COMBOEX_InsertItemW(infoPtr, &citW); 689 690 Free(wstr); 691 692 return ret; 693 } 694 695 696 static DWORD 697 COMBOEX_SetExtendedStyle (COMBOEX_INFO *infoPtr, DWORD mask, DWORD style) 698 { 699 DWORD dwTemp; 700 701 TRACE("(mask=x%08x, style=0x%08x)\n", mask, style); 702 703 dwTemp = infoPtr->dwExtStyle; 704 705 if (mask) 706 infoPtr->dwExtStyle = (infoPtr->dwExtStyle & ~mask) | style; 707 else 708 infoPtr->dwExtStyle = style; 709 710 /* see if we need to change the word break proc on the edit */ 711 if ((infoPtr->dwExtStyle ^ dwTemp) & CBES_EX_PATHWORDBREAKPROC) 712 SetPathWordBreakProc(infoPtr->hwndEdit, 713 (infoPtr->dwExtStyle & CBES_EX_PATHWORDBREAKPROC) != 0); 714 715 /* test if the control's appearance has changed */ 716 mask = CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT; 717 if ((infoPtr->dwExtStyle & mask) != (dwTemp & mask)) { 718 /* if state of EX_NOEDITIMAGE changes, invalidate all */ 719 TRACE("EX_NOEDITIMAGE state changed to %d\n", 720 infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGE); 721 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE); 722 COMBOEX_AdjustEditPos (infoPtr); 723 if (infoPtr->hwndEdit) 724 InvalidateRect (infoPtr->hwndEdit, NULL, TRUE); 725 } 726 727 return dwTemp; 728 } 729 730 731 static HIMAGELIST COMBOEX_SetImageList (COMBOEX_INFO *infoPtr, HIMAGELIST himl) 732 { 733 HIMAGELIST himlTemp = infoPtr->himl; 734 735 TRACE("\n"); 736 737 infoPtr->himl = himl; 738 739 COMBOEX_ReSize (infoPtr); 740 InvalidateRect (infoPtr->hwndCombo, NULL, TRUE); 741 742 /* reposition the Edit control based on whether icon exists */ 743 COMBOEX_AdjustEditPos (infoPtr); 744 return himlTemp; 745 } 746 747 static BOOL COMBOEX_SetItemW (COMBOEX_INFO *infoPtr, const COMBOBOXEXITEMW *cit) 748 { 749 INT_PTR index = cit->iItem; 750 CBE_ITEMDATA *item; 751 752 if (TRACE_ON(comboex)) COMBOEX_DumpInput (cit); 753 754 /* if item number requested does not exist then return failure */ 755 if ((index >= infoPtr->nb_items) || (index < -1)) return FALSE; 756 757 /* if the item is the edit control and there is no edit control, skip */ 758 if ((index == -1) && !infoPtr->hwndEdit) return FALSE; 759 760 if (!(item = COMBOEX_FindItem(infoPtr, index))) return FALSE; 761 762 /* add/change stuff to the internal item structure */ 763 item->mask |= cit->mask; 764 if (cit->mask & CBEIF_TEXT) { 765 INT len = 0; 766 767 COMBOEX_FreeText(item); 768 if (is_textW(cit->pszText)) len = strlenW(cit->pszText); 769 if (len > 0) { 770 item->pszText = Alloc ((len + 1)*sizeof(WCHAR)); 771 if (!item->pszText) return FALSE; 772 strcpyW(item->pszText, cit->pszText); 773 } else if (cit->pszText == LPSTR_TEXTCALLBACKW) 774 item->pszText = LPSTR_TEXTCALLBACKW; 775 item->cchTextMax = cit->cchTextMax; 776 } 777 if (cit->mask & CBEIF_IMAGE) 778 item->iImage = cit->iImage; 779 if (cit->mask & CBEIF_SELECTEDIMAGE) 780 item->iSelectedImage = cit->iSelectedImage; 781 if (cit->mask & CBEIF_OVERLAY) 782 item->iOverlay = cit->iOverlay; 783 if (cit->mask & CBEIF_INDENT) 784 item->iIndent = cit->iIndent; 785 if (cit->mask & CBEIF_LPARAM) 786 item->lParam = cit->lParam; 787 788 if (TRACE_ON(comboex)) COMBOEX_DumpItem (item); 789 790 /* if original request was to update edit control, do some fast foot work */ 791 if (cit->iItem == -1 && cit->mask & CBEIF_TEXT) { 792 COMBOEX_SetEditText (infoPtr, item); 793 RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE | RDW_INVALIDATE); 794 } 795 return TRUE; 796 } 797 798 static BOOL COMBOEX_SetItemA (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMA const *cit) 799 { 800 COMBOBOXEXITEMW citW; 801 LPWSTR wstr = NULL; 802 BOOL ret; 803 804 memcpy(&citW, cit, sizeof(COMBOBOXEXITEMA)); 805 if ((cit->mask & CBEIF_TEXT) && is_textA(cit->pszText)) { 806 INT len = MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, NULL, 0); 807 wstr = Alloc ((len + 1)*sizeof(WCHAR)); 808 if (!wstr) return FALSE; 809 MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, wstr, len); 810 citW.pszText = wstr; 811 } 812 ret = COMBOEX_SetItemW(infoPtr, &citW); 813 814 Free(wstr); 815 816 return ret; 817 } 818 819 820 static BOOL COMBOEX_SetUnicodeFormat (COMBOEX_INFO *infoPtr, BOOL value) 821 { 822 BOOL bTemp = infoPtr->unicode; 823 824 TRACE("to %s, was %s\n", value ? "TRUE":"FALSE", bTemp ? "TRUE":"FALSE"); 825 826 infoPtr->unicode = value; 827 828 return bTemp; 829 } 830 831 832 /* *** CB_xxx message support *** */ 833 834 static INT 835 COMBOEX_FindStringExact (const COMBOEX_INFO *infoPtr, INT start, LPCWSTR str) 836 { 837 INT i; 838 cmp_func_t cmptext = get_cmp_func(infoPtr); 839 INT count = SendMessageW (infoPtr->hwndCombo, CB_GETCOUNT, 0, 0); 840 841 /* now search from after starting loc and wrapping back to start */ 842 for(i=start+1; i<count; i++) { 843 CBE_ITEMDATA *item = get_item_data(infoPtr, i); 844 if ((LRESULT)item == CB_ERR) continue; 845 if (cmptext(COMBOEX_GetText(infoPtr, item), str) == 0) return i; 846 } 847 for(i=0; i<=start; i++) { 848 CBE_ITEMDATA *item = get_item_data(infoPtr, i); 849 if ((LRESULT)item == CB_ERR) continue; 850 if (cmptext(COMBOEX_GetText(infoPtr, item), str) == 0) return i; 851 } 852 return CB_ERR; 853 } 854 855 856 static DWORD_PTR COMBOEX_GetItemData (COMBOEX_INFO *infoPtr, INT_PTR index) 857 { 858 CBE_ITEMDATA const *item1; 859 CBE_ITEMDATA const *item2; 860 DWORD_PTR ret = 0; 861 862 item1 = get_item_data(infoPtr, index); 863 if ((item1 != NULL) && ((LRESULT)item1 != CB_ERR)) { 864 item2 = COMBOEX_FindItem (infoPtr, index); 865 if (item2 != item1) { 866 ERR("data structures damaged!\n"); 867 return CB_ERR; 868 } 869 if (item1->mask & CBEIF_LPARAM) ret = item1->lParam; 870 TRACE("returning 0x%08lx\n", ret); 871 } else { 872 ret = (DWORD_PTR)item1; 873 TRACE("non-valid result from combo, returning 0x%08lx\n", ret); 874 } 875 return ret; 876 } 877 878 879 static INT COMBOEX_SetCursel (COMBOEX_INFO *infoPtr, INT_PTR index) 880 { 881 CBE_ITEMDATA *item; 882 INT sel; 883 884 if (!(item = COMBOEX_FindItem(infoPtr, index))) 885 return SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, index, 0); 886 887 TRACE("selecting item %ld text=%s\n", index, debugstr_txt(item->pszText)); 888 infoPtr->selected = index; 889 890 sel = (INT)SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, index, 0); 891 COMBOEX_SetEditText (infoPtr, item); 892 return sel; 893 } 894 895 896 static DWORD_PTR COMBOEX_SetItemData (COMBOEX_INFO *infoPtr, INT_PTR index, DWORD_PTR data) 897 { 898 CBE_ITEMDATA *item1; 899 CBE_ITEMDATA const *item2; 900 901 item1 = get_item_data(infoPtr, index); 902 if ((item1 != NULL) && ((LRESULT)item1 != CB_ERR)) { 903 item2 = COMBOEX_FindItem (infoPtr, index); 904 if (item2 != item1) { 905 ERR("data structures damaged!\n"); 906 return CB_ERR; 907 } 908 item1->mask |= CBEIF_LPARAM; 909 item1->lParam = data; 910 TRACE("setting lparam to 0x%08lx\n", data); 911 return 0; 912 } 913 TRACE("non-valid result from combo %p\n", item1); 914 return (DWORD_PTR)item1; 915 } 916 917 918 static INT COMBOEX_SetItemHeight (COMBOEX_INFO const *infoPtr, INT index, UINT height) 919 { 920 RECT cb_wrect, cbx_wrect, cbx_crect; 921 922 /* First, lets forward the message to the normal combo control 923 just like Windows. */ 924 if (infoPtr->hwndCombo) 925 if (SendMessageW (infoPtr->hwndCombo, CB_SETITEMHEIGHT, 926 index, height) == CB_ERR) return CB_ERR; 927 928 GetWindowRect (infoPtr->hwndCombo, &cb_wrect); 929 GetWindowRect (infoPtr->hwndSelf, &cbx_wrect); 930 GetClientRect (infoPtr->hwndSelf, &cbx_crect); 931 /* the height of comboex as height of the combo + comboex border */ 932 height = cb_wrect.bottom-cb_wrect.top 933 + cbx_wrect.bottom-cbx_wrect.top 934 - (cbx_crect.bottom-cbx_crect.top); 935 TRACE("EX window=(%s), client=(%s)\n", 936 wine_dbgstr_rect(&cbx_wrect), wine_dbgstr_rect(&cbx_crect)); 937 TRACE("CB window=(%s), EX setting=(0,0)-(%d,%d)\n", 938 wine_dbgstr_rect(&cbx_wrect), cbx_wrect.right-cbx_wrect.left, height); 939 SetWindowPos (infoPtr->hwndSelf, HWND_TOP, 0, 0, 940 cbx_wrect.right-cbx_wrect.left, height, 941 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); 942 943 return 0; 944 } 945 946 947 /* *** WM_xxx message support *** */ 948 949 950 static LRESULT COMBOEX_Create (HWND hwnd, CREATESTRUCTA const *cs) 951 { 952 static const WCHAR NIL[] = { 0 }; 953 COMBOEX_INFO *infoPtr; 954 LOGFONTW mylogfont; 955 RECT win_rect; 956 INT i; 957 958 /* allocate memory for info structure */ 959 infoPtr = Alloc (sizeof(COMBOEX_INFO)); 960 if (!infoPtr) return -1; 961 962 /* initialize info structure */ 963 /* note that infoPtr is allocated zero-filled */ 964 965 infoPtr->hwndSelf = hwnd; 966 infoPtr->selected = -1; 967 968 infoPtr->unicode = IsWindowUnicode (hwnd); 969 infoPtr->hwndNotify = cs->hwndParent; 970 971 i = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY); 972 if ((i != NFR_ANSI) && (i != NFR_UNICODE)) { 973 WARN("wrong response to WM_NOTIFYFORMAT (%d), assuming ANSI\n", i); 974 i = NFR_ANSI; 975 } 976 infoPtr->NtfUnicode = (i == NFR_UNICODE); 977 978 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); 979 980 if (TRACE_ON(comboex)) { 981 RECT client, rect; 982 GetWindowRect(hwnd, &rect); 983 GetClientRect(hwnd, &client); 984 TRACE("EX window=(%s), client=(%s)\n", 985 wine_dbgstr_rect(&rect), wine_dbgstr_rect(&client)); 986 } 987 988 /* Native version of ComboEx creates the ComboBox with DROPDOWNLIST */ 989 /* specified. It then creates its own version of the EDIT control */ 990 /* and makes the ComboBox the parent. This is because a normal */ 991 /* DROPDOWNLIST does not have an EDIT control, but we need one. */ 992 /* We also need to place the edit control at the proper location */ 993 /* (allow space for the icons). */ 994 995 infoPtr->hwndCombo = CreateWindowW (WC_COMBOBOXW, NIL, 996 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | 997 CBS_NOINTEGRALHEIGHT | CBS_DROPDOWNLIST | 998 WS_CHILD | WS_VISIBLE | CBS_OWNERDRAWFIXED | 999 GetWindowLongW (hwnd, GWL_STYLE), 1000 cs->y, cs->x, cs->cx, cs->cy, hwnd, 1001 (HMENU) GetWindowLongPtrW (hwnd, GWLP_ID), 1002 (HINSTANCE)GetWindowLongPtrW (hwnd, GWLP_HINSTANCE), NULL); 1003 1004 SetWindowSubclass(infoPtr->hwndCombo, COMBOEX_ComboWndProc, COMBO_SUBCLASSID, 1005 (DWORD_PTR)hwnd); 1006 infoPtr->font = (HFONT)SendMessageW (infoPtr->hwndCombo, WM_GETFONT, 0, 0); 1007 1008 /* 1009 * Now create our own EDIT control so we can position it. 1010 * It is created only for CBS_DROPDOWN style 1011 */ 1012 if ((cs->style & CBS_DROPDOWNLIST) == CBS_DROPDOWN) { 1013 infoPtr->hwndEdit = CreateWindowExW (0, WC_EDITW, NIL, 1014 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ES_AUTOHSCROLL, 1015 0, 0, 0, 0, /* will set later */ 1016 infoPtr->hwndCombo, 1017 (HMENU) GetWindowLongPtrW (hwnd, GWLP_ID), 1018 (HINSTANCE)GetWindowLongPtrW (hwnd, GWLP_HINSTANCE), NULL); 1019 1020 SetWindowSubclass(infoPtr->hwndEdit, COMBOEX_EditWndProc, EDIT_SUBCLASSID, 1021 (DWORD_PTR)hwnd); 1022 1023 infoPtr->font = (HFONT)SendMessageW(infoPtr->hwndCombo, WM_GETFONT, 0, 0); 1024 } 1025 1026 /* 1027 * Locate the default font if necessary and then set it in 1028 * all associated controls 1029 */ 1030 if (!infoPtr->font) { 1031 SystemParametersInfoW (SPI_GETICONTITLELOGFONT, sizeof(mylogfont), 1032 &mylogfont, 0); 1033 infoPtr->font = infoPtr->defaultFont = CreateFontIndirectW (&mylogfont); 1034 } 1035 SendMessageW (infoPtr->hwndCombo, WM_SETFONT, (WPARAM)infoPtr->font, 0); 1036 if (infoPtr->hwndEdit) { 1037 SendMessageW (infoPtr->hwndEdit, WM_SETFONT, (WPARAM)infoPtr->font, 0); 1038 SendMessageW (infoPtr->hwndEdit, EM_SETMARGINS, EC_USEFONTINFO, 0); 1039 } 1040 1041 COMBOEX_ReSize (infoPtr); 1042 1043 /* Above is fairly certain, below is much less certain. */ 1044 1045 GetWindowRect(hwnd, &win_rect); 1046 1047 if (TRACE_ON(comboex)) { 1048 RECT client, rect; 1049 GetClientRect(hwnd, &client); 1050 GetWindowRect(infoPtr->hwndCombo, &rect); 1051 TRACE("EX window=(%s) client=(%s) CB wnd=(%s)\n", 1052 wine_dbgstr_rect(&win_rect), wine_dbgstr_rect(&client), 1053 wine_dbgstr_rect(&rect)); 1054 } 1055 SetWindowPos(infoPtr->hwndCombo, HWND_TOP, 0, 0, 1056 win_rect.right - win_rect.left, win_rect.bottom - win_rect.top, 1057 SWP_NOACTIVATE | SWP_NOREDRAW); 1058 1059 GetWindowRect(infoPtr->hwndCombo, &win_rect); 1060 TRACE("CB window=(%s)\n", wine_dbgstr_rect(&win_rect)); 1061 SetWindowPos(hwnd, HWND_TOP, 0, 0, 1062 win_rect.right - win_rect.left, win_rect.bottom - win_rect.top, 1063 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); 1064 1065 COMBOEX_AdjustEditPos (infoPtr); 1066 1067 return 0; 1068 } 1069 1070 1071 static LRESULT COMBOEX_Command (COMBOEX_INFO *infoPtr, WPARAM wParam) 1072 { 1073 LRESULT lret; 1074 INT command = HIWORD(wParam); 1075 CBE_ITEMDATA *item = 0; 1076 WCHAR wintext[520]; 1077 INT cursel, n; 1078 INT_PTR oldItem; 1079 NMCBEENDEDITW cbeend; 1080 DWORD oldflags; 1081 HWND parent = infoPtr->hwndNotify; 1082 1083 TRACE("for command %d\n", command); 1084 1085 switch (command) 1086 { 1087 case CBN_DROPDOWN: 1088 SetFocus (infoPtr->hwndCombo); 1089 ShowWindow (infoPtr->hwndEdit, SW_HIDE); 1090 infoPtr->flags |= WCBE_ACTEDIT; 1091 return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1092 case CBN_CLOSEUP: 1093 SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1094 ShowWindow (infoPtr->hwndEdit, SW_SHOW); 1095 InvalidateRect (infoPtr->hwndCombo, 0, TRUE); 1096 if (infoPtr->hwndEdit) InvalidateRect (infoPtr->hwndEdit, 0, TRUE); 1097 cursel = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0); 1098 if (cursel == -1) { 1099 cmp_func_t cmptext = get_cmp_func(infoPtr); 1100 /* find match from edit against those in Combobox */ 1101 GetWindowTextW (infoPtr->hwndEdit, wintext, 520); 1102 n = SendMessageW (infoPtr->hwndCombo, CB_GETCOUNT, 0, 0); 1103 for (cursel = 0; cursel < n; cursel++){ 1104 item = get_item_data(infoPtr, cursel); 1105 if ((INT_PTR)item == CB_ERR) break; 1106 if (!cmptext(COMBOEX_GetText(infoPtr, item), wintext)) break; 1107 } 1108 if ((cursel == n) || ((INT_PTR)item == CB_ERR)) { 1109 TRACE("failed to find match??? item=%p cursel=%d\n", 1110 item, cursel); 1111 if (infoPtr->hwndEdit) SetFocus(infoPtr->hwndEdit); 1112 return 0; 1113 } 1114 } 1115 else { 1116 item = get_item_data(infoPtr, cursel); 1117 if ((INT_PTR)item == CB_ERR) { 1118 TRACE("failed to find match??? item=%p cursel=%d\n", 1119 item, cursel); 1120 if (infoPtr->hwndEdit) SetFocus(infoPtr->hwndEdit); 1121 return 0; 1122 } 1123 } 1124 1125 /* Save flags for testing and reset them */ 1126 oldflags = infoPtr->flags; 1127 infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG); 1128 1129 if (oldflags & WCBE_ACTEDIT) { 1130 cbeend.fChanged = (oldflags & WCBE_EDITCHG); 1131 cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo, 1132 CB_GETCURSEL, 0, 0); 1133 cbeend.iWhy = CBENF_DROPDOWN; 1134 1135 if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, COMBOEX_GetText(infoPtr, item))) return 0; 1136 } 1137 1138 /* if selection has changed the set the new current selection */ 1139 cursel = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0); 1140 if ((oldflags & WCBE_EDITCHG) || (cursel != infoPtr->selected)) { 1141 infoPtr->selected = cursel; 1142 SendMessageW (infoPtr->hwndSelf, CB_SETCURSEL, cursel, 0); 1143 SetFocus(infoPtr->hwndCombo); 1144 } 1145 return 0; 1146 1147 case CBN_SELCHANGE: 1148 /* 1149 * CB_GETCURSEL(Combo) 1150 * CB_GETITEMDATA(Combo) < simulated by COMBOEX_FindItem 1151 * lstrlenA 1152 * WM_SETTEXT(Edit) 1153 * WM_GETTEXTLENGTH(Edit) 1154 * WM_GETTEXT(Edit) 1155 * EM_SETSEL(Edit, 0,0) 1156 * WM_GETTEXTLENGTH(Edit) 1157 * WM_GETTEXT(Edit) 1158 * EM_SETSEL(Edit, 0,len) 1159 * return WM_COMMAND to parent 1160 */ 1161 oldItem = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0); 1162 if (!(item = COMBOEX_FindItem(infoPtr, oldItem))) { 1163 ERR("item %ld not found. Problem!\n", oldItem); 1164 break; 1165 } 1166 infoPtr->selected = oldItem; 1167 COMBOEX_SetEditText (infoPtr, item); 1168 return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1169 1170 case CBN_SELENDOK: 1171 case CBN_SELENDCANCEL: 1172 /* 1173 * We have to change the handle since we are the control 1174 * issuing the message. IE4 depends on this. 1175 */ 1176 return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1177 1178 case CBN_KILLFOCUS: 1179 SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1180 if (infoPtr->flags & WCBE_ACTEDIT) { 1181 GetWindowTextW (infoPtr->hwndEdit, wintext, 260); 1182 cbeend.fChanged = (infoPtr->flags & WCBE_EDITCHG); 1183 cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo, 1184 CB_GETCURSEL, 0, 0); 1185 cbeend.iWhy = CBENF_KILLFOCUS; 1186 1187 infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG); 1188 if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, wintext)) return 0; 1189 } 1190 /* possible CB_GETCURSEL */ 1191 InvalidateRect (infoPtr->hwndCombo, 0, 0); 1192 return 0; 1193 1194 case CBN_SETFOCUS: 1195 return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1196 1197 default: 1198 /* 1199 * We have to change the handle since we are the control 1200 * issuing the message. IE4 depends on this. 1201 * We also need to set the focus back to the Edit control 1202 * after passing the command to the parent of the ComboEx. 1203 */ 1204 lret = SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf); 1205 if (infoPtr->hwndEdit) SetFocus(infoPtr->hwndEdit); 1206 return lret; 1207 } 1208 return 0; 1209 } 1210 1211 1212 static BOOL COMBOEX_WM_DeleteItem (COMBOEX_INFO *infoPtr, DELETEITEMSTRUCT const *dis) 1213 { 1214 CBE_ITEMDATA *item, *olditem; 1215 NMCOMBOBOXEXW nmcit; 1216 UINT i; 1217 1218 TRACE("CtlType=%08x, CtlID=%08x, itemID=%08x, hwnd=%p, data=%08lx\n", 1219 dis->CtlType, dis->CtlID, dis->itemID, dis->hwndItem, dis->itemData); 1220 1221 if (dis->itemID >= infoPtr->nb_items) return FALSE; 1222 1223 olditem = infoPtr->items; 1224 i = infoPtr->nb_items - 1; 1225 1226 if (i == dis->itemID) { 1227 infoPtr->items = infoPtr->items->next; 1228 } 1229 else { 1230 item = olditem; 1231 i--; 1232 1233 /* find the prior item in the list */ 1234 while (item->next && (i > dis->itemID)) { 1235 item = item->next; 1236 i--; 1237 } 1238 if (!item->next || (i != dis->itemID)) { 1239 ERR("COMBOBOXEX item structures broken. Please report!\n"); 1240 return FALSE; 1241 } 1242 olditem = item->next; 1243 item->next = item->next->next; 1244 } 1245 infoPtr->nb_items--; 1246 1247 memset (&nmcit.ceItem, 0, sizeof(nmcit.ceItem)); 1248 nmcit.ceItem.mask=~0; 1249 COMBOEX_CopyItem (olditem, &nmcit.ceItem); 1250 COMBOEX_NotifyItem (infoPtr, CBEN_DELETEITEM, &nmcit); 1251 1252 COMBOEX_FreeText(olditem); 1253 Free(olditem); 1254 1255 return TRUE; 1256 } 1257 1258 1259 static LRESULT COMBOEX_DrawItem (COMBOEX_INFO *infoPtr, DRAWITEMSTRUCT const *dis) 1260 { 1261 static const WCHAR nil[] = { 0 }; 1262 CBE_ITEMDATA *item = NULL; 1263 SIZE txtsize; 1264 RECT rect; 1265 LPCWSTR str = nil; 1266 UINT xbase, x, y; 1267 INT len; 1268 COLORREF nbkc, ntxc, bkc, txc; 1269 int drawimage, drawstate, xioff, selected; 1270 1271 TRACE("DRAWITEMSTRUCT: CtlType=0x%08x CtlID=0x%08x\n", 1272 dis->CtlType, dis->CtlID); 1273 TRACE("itemID=0x%08x itemAction=0x%08x itemState=0x%08x\n", 1274 dis->itemID, dis->itemAction, dis->itemState); 1275 TRACE("hWnd=%p hDC=%p (%s) itemData=0x%08lx\n", 1276 dis->hwndItem, dis->hDC, wine_dbgstr_rect(&dis->rcItem), dis->itemData); 1277 1278 /* MSDN says: */ 1279 /* "itemID - Specifies the menu item identifier for a menu */ 1280 /* item or the index of the item in a list box or combo box. */ 1281 /* For an empty list box or combo box, this member can be -1. */ 1282 /* This allows the application to draw only the focus */ 1283 /* rectangle at the coordinates specified by the rcItem */ 1284 /* member even though there are no items in the control. */ 1285 /* This indicates to the user whether the list box or combo */ 1286 /* box has the focus. How the bits are set in the itemAction */ 1287 /* member determines whether the rectangle is to be drawn as */ 1288 /* though the list box or combo box has the focus. */ 1289 if (dis->itemID == 0xffffffff) { 1290 if ( ( (dis->itemAction & ODA_FOCUS) && (dis->itemState & ODS_SELECTED)) || 1291 ( (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) && (dis->itemState & ODS_FOCUS) ) ) { 1292 1293 TRACE("drawing item -1 special focus, rect=(%s)\n", 1294 wine_dbgstr_rect(&dis->rcItem)); 1295 } 1296 else if ((dis->CtlType == ODT_COMBOBOX) && 1297 (dis->itemAction == ODA_DRAWENTIRE)) { 1298 /* draw of edit control data */ 1299 1300 if (TRACE_ON(comboex)) { 1301 RECT exrc, cbrc, edrc; 1302 GetWindowRect (infoPtr->hwndSelf, &exrc); 1303 GetWindowRect (infoPtr->hwndCombo, &cbrc); 1304 SetRect(&edrc, -1, -1, -1, -1); 1305 if (infoPtr->hwndEdit) GetWindowRect (infoPtr->hwndEdit, &edrc); 1306 TRACE("window rects ex=(%s), cb=(%s), ed=(%s)\n", 1307 wine_dbgstr_rect(&exrc), wine_dbgstr_rect(&cbrc), 1308 wine_dbgstr_rect(&edrc)); 1309 } 1310 } 1311 else { 1312 ERR("NOT drawing item -1 special focus, rect=(%s), action=%08x, state=%08x\n", 1313 wine_dbgstr_rect(&dis->rcItem), 1314 dis->itemAction, dis->itemState); 1315 return 0; 1316 } 1317 } 1318 1319 /* If draw item is -1 (edit control) setup the item pointer */ 1320 if (dis->itemID == 0xffffffff) { 1321 item = &infoPtr->edit; 1322 1323 if (infoPtr->hwndEdit) { 1324 /* free previous text of edit item */ 1325 COMBOEX_FreeText(item); 1326 item->mask &= ~CBEIF_TEXT; 1327 if( (len = GetWindowTextLengthW(infoPtr->hwndEdit)) ) { 1328 item->mask |= CBEIF_TEXT; 1329 item->pszText = Alloc ((len + 1)*sizeof(WCHAR)); 1330 if (item->pszText) 1331 GetWindowTextW(infoPtr->hwndEdit, item->pszText, len+1); 1332 1333 TRACE("edit control hwndEdit=%p, text len=%d str=%s\n", 1334 infoPtr->hwndEdit, len, debugstr_txt(item->pszText)); 1335 } 1336 } 1337 } 1338 1339 1340 /* if the item pointer is not set, then get the data and locate it */ 1341 if (!item) { 1342 item = get_item_data(infoPtr, dis->itemID); 1343 if (item == (CBE_ITEMDATA *)CB_ERR) { 1344 ERR("invalid item for id %d\n", dis->itemID); 1345 return 0; 1346 } 1347 } 1348 1349 if (TRACE_ON(comboex)) COMBOEX_DumpItem (item); 1350 1351 xbase = CBE_STARTOFFSET; 1352 if ((item->mask & CBEIF_INDENT) && (dis->itemState & ODS_COMBOEXLBOX)) { 1353 INT indent = item->iIndent; 1354 if (indent == I_INDENTCALLBACK) { 1355 NMCOMBOBOXEXW nmce; 1356 ZeroMemory(&nmce, sizeof(nmce)); 1357 nmce.ceItem.mask = CBEIF_INDENT; 1358 nmce.ceItem.lParam = item->lParam; 1359 nmce.ceItem.iItem = dis->itemID; 1360 COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce); 1361 if (nmce.ceItem.mask & CBEIF_DI_SETITEM) 1362 item->iIndent = nmce.ceItem.iIndent; 1363 indent = nmce.ceItem.iIndent; 1364 } 1365 xbase += (indent * CBE_INDENT); 1366 } 1367 1368 drawimage = -2; 1369 drawstate = ILD_NORMAL; 1370 selected = infoPtr->selected == dis->itemID; 1371 1372 if (item->mask & CBEIF_IMAGE) 1373 drawimage = item->iImage; 1374 if (item->mask & CBEIF_SELECTEDIMAGE && selected) 1375 drawimage = item->iSelectedImage; 1376 if (dis->itemState & ODS_COMBOEXLBOX) { 1377 /* drawing listbox entry */ 1378 if (dis->itemState & ODS_SELECTED) 1379 drawstate = ILD_SELECTED; 1380 } else { 1381 /* drawing combo/edit entry */ 1382 if (IsWindowVisible(infoPtr->hwndEdit)) { 1383 /* if we have an edit control, the slave the 1384 * selection state to the Edit focus state 1385 */ 1386 if (infoPtr->flags & WCBE_EDITFOCUSED) 1387 drawstate = ILD_SELECTED; 1388 } else 1389 /* if we don't have an edit control, use 1390 * the requested state. 1391 */ 1392 if (dis->itemState & ODS_SELECTED) 1393 drawstate = ILD_SELECTED; 1394 } 1395 1396 if (infoPtr->himl && !(infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGEINDENT)) { 1397 IMAGEINFO iinfo; 1398 iinfo.rcImage.left = iinfo.rcImage.right = 0; 1399 ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo); 1400 xioff = iinfo.rcImage.right - iinfo.rcImage.left + CBE_SEP; 1401 } else xioff = 0; 1402 1403 /* setup pointer to text to be drawn */ 1404 str = COMBOEX_GetText(infoPtr, item); 1405 if (!str) str = nil; 1406 1407 len = strlenW (str); 1408 GetTextExtentPoint32W (dis->hDC, str, len, &txtsize); 1409 1410 if (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) { 1411 int overlay = item->iOverlay; 1412 1413 if (drawimage == I_IMAGECALLBACK) { 1414 NMCOMBOBOXEXW nmce; 1415 ZeroMemory(&nmce, sizeof(nmce)); 1416 nmce.ceItem.mask = selected ? CBEIF_SELECTEDIMAGE : CBEIF_IMAGE; 1417 nmce.ceItem.lParam = item->lParam; 1418 nmce.ceItem.iItem = dis->itemID; 1419 COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce); 1420 if (!selected) { 1421 if (nmce.ceItem.mask & CBEIF_DI_SETITEM) item->iImage = nmce.ceItem.iImage; 1422 drawimage = nmce.ceItem.iImage; 1423 } else { 1424 if (nmce.ceItem.mask & CBEIF_DI_SETITEM) item->iSelectedImage = nmce.ceItem.iSelectedImage; 1425 drawimage = nmce.ceItem.iSelectedImage; 1426 } 1427 } 1428 1429 if (overlay == I_IMAGECALLBACK) { 1430 NMCOMBOBOXEXW nmce; 1431 ZeroMemory(&nmce, sizeof(nmce)); 1432 nmce.ceItem.mask = CBEIF_OVERLAY; 1433 nmce.ceItem.lParam = item->lParam; 1434 nmce.ceItem.iItem = dis->itemID; 1435 COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce); 1436 if (nmce.ceItem.mask & CBEIF_DI_SETITEM) 1437 item->iOverlay = nmce.ceItem.iOverlay; 1438 overlay = nmce.ceItem.iOverlay; 1439 } 1440 1441 if (drawimage >= 0 && 1442 !(infoPtr->dwExtStyle & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT))) { 1443 if (overlay > 0) ImageList_SetOverlayImage (infoPtr->himl, overlay, 1); 1444 ImageList_Draw (infoPtr->himl, drawimage, dis->hDC, xbase, dis->rcItem.top, 1445 drawstate | (overlay > 0 ? INDEXTOOVERLAYMASK(1) : 0)); 1446 } 1447 1448 /* now draw the text */ 1449 if (!IsWindowVisible (infoPtr->hwndEdit)) { 1450 nbkc = (dis->itemState & ODS_SELECTED) ? 1451 comctl32_color.clrHighlight : comctl32_color.clrWindow; 1452 bkc = SetBkColor (dis->hDC, nbkc); 1453 ntxc = (dis->itemState & ODS_SELECTED) ? 1454 comctl32_color.clrHighlightText : comctl32_color.clrWindowText; 1455 txc = SetTextColor (dis->hDC, ntxc); 1456 x = xbase + xioff; 1457 y = dis->rcItem.top + 1458 (dis->rcItem.bottom - dis->rcItem.top - txtsize.cy) / 2; 1459 SetRect(&rect, x, dis->rcItem.top + 1, x + txtsize.cx, dis->rcItem.bottom - 1); 1460 TRACE("drawing item %d text, rect=(%s)\n", 1461 dis->itemID, wine_dbgstr_rect(&rect)); 1462 ExtTextOutW (dis->hDC, x, y, ETO_OPAQUE | ETO_CLIPPED, 1463 &rect, str, len, 0); 1464 SetBkColor (dis->hDC, bkc); 1465 SetTextColor (dis->hDC, txc); 1466 } 1467 } 1468 1469 if (dis->itemAction & ODA_FOCUS) { 1470 rect.left = xbase + xioff - 1; 1471 rect.right = rect.left + txtsize.cx + 2; 1472 rect.top = dis->rcItem.top; 1473 rect.bottom = dis->rcItem.bottom; 1474 DrawFocusRect(dis->hDC, &rect); 1475 } 1476 1477 return 0; 1478 } 1479 1480 1481 static void COMBOEX_ResetContent (COMBOEX_INFO *infoPtr) 1482 { 1483 if (infoPtr->items) 1484 { 1485 CBE_ITEMDATA *item, *next; 1486 1487 item = infoPtr->items; 1488 while (item) { 1489 next = item->next; 1490 COMBOEX_FreeText (item); 1491 Free (item); 1492 item = next; 1493 } 1494 infoPtr->items = 0; 1495 } 1496 1497 infoPtr->selected = -1; 1498 infoPtr->nb_items = 0; 1499 } 1500 1501 1502 static LRESULT COMBOEX_Destroy (COMBOEX_INFO *infoPtr) 1503 { 1504 if (infoPtr->hwndCombo) 1505 SetWindowSubclass(infoPtr->hwndCombo, COMBOEX_ComboWndProc, COMBO_SUBCLASSID, 0); 1506 1507 if (infoPtr->hwndEdit) 1508 SetWindowSubclass(infoPtr->hwndEdit, COMBOEX_EditWndProc, EDIT_SUBCLASSID, 0); 1509 1510 COMBOEX_FreeText (&infoPtr->edit); 1511 COMBOEX_ResetContent (infoPtr); 1512 1513 if (infoPtr->defaultFont) 1514 DeleteObject (infoPtr->defaultFont); 1515 1516 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); 1517 1518 /* free comboex info data */ 1519 Free (infoPtr); 1520 1521 return 0; 1522 } 1523 1524 1525 static LRESULT COMBOEX_Enable (COMBOEX_INFO *infoPtr, BOOL enable) 1526 { 1527 TRACE("hwnd=%p, enable=%s\n", infoPtr->hwndSelf, enable ? "TRUE":"FALSE"); 1528 1529 if (infoPtr->hwndEdit) 1530 EnableWindow(infoPtr->hwndEdit, enable); 1531 1532 EnableWindow(infoPtr->hwndCombo, enable); 1533 1534 /* Force the control to repaint when the enabled state changes. */ 1535 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 1536 1537 return 1; 1538 } 1539 1540 1541 static LRESULT COMBOEX_MeasureItem (COMBOEX_INFO const *infoPtr, MEASUREITEMSTRUCT *mis) 1542 { 1543 static const WCHAR strW[] = { 'W', 0 }; 1544 SIZE mysize; 1545 HDC hdc; 1546 1547 hdc = GetDC (0); 1548 GetTextExtentPointW (hdc, strW, 1, &mysize); 1549 ReleaseDC (0, hdc); 1550 mis->itemHeight = mysize.cy + CBE_EXTRA; 1551 1552 TRACE("adjusted height hwnd=%p, height=%d\n", 1553 infoPtr->hwndSelf, mis->itemHeight); 1554 1555 return 0; 1556 } 1557 1558 1559 static LRESULT COMBOEX_NCCreate (HWND hwnd) 1560 { 1561 /* WARNING: The COMBOEX_INFO structure is not yet created */ 1562 DWORD oldstyle, newstyle; 1563 1564 oldstyle = (DWORD)GetWindowLongW (hwnd, GWL_STYLE); 1565 newstyle = oldstyle & ~(WS_VSCROLL | WS_HSCROLL | WS_BORDER); 1566 if (newstyle != oldstyle) { 1567 TRACE("req style %08x, resetting style %08x\n", 1568 oldstyle, newstyle); 1569 SetWindowLongW (hwnd, GWL_STYLE, newstyle); 1570 } 1571 return 1; 1572 } 1573 1574 1575 static LRESULT COMBOEX_NotifyFormat (COMBOEX_INFO *infoPtr, LPARAM lParam) 1576 { 1577 if (lParam == NF_REQUERY) { 1578 INT i = SendMessageW(infoPtr->hwndNotify, 1579 WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY); 1580 infoPtr->NtfUnicode = (i == NFR_UNICODE); 1581 } 1582 return infoPtr->NtfUnicode ? NFR_UNICODE : NFR_ANSI; 1583 } 1584 1585 1586 static LRESULT COMBOEX_Size (COMBOEX_INFO *infoPtr, INT width, INT height) 1587 { 1588 TRACE("(width=%d, height=%d)\n", width, height); 1589 1590 MoveWindow (infoPtr->hwndCombo, 0, 0, width, height, TRUE); 1591 1592 COMBOEX_AdjustEditPos (infoPtr); 1593 1594 return 0; 1595 } 1596 1597 static LRESULT COMBOEX_SetFont( COMBOEX_INFO *infoPtr, HFONT font, BOOL redraw ) 1598 { 1599 infoPtr->font = font; 1600 SendMessageW( infoPtr->hwndCombo, WM_SETFONT, (WPARAM)font, 0 ); 1601 if (infoPtr->hwndEdit) SendMessageW( infoPtr->hwndEdit, WM_SETFONT, (WPARAM)font, 0 ); 1602 COMBOEX_ReSize( infoPtr ); 1603 if (redraw) InvalidateRect( infoPtr->hwndCombo, NULL, TRUE ); 1604 return 0; 1605 } 1606 1607 static LRESULT COMBOEX_SetRedraw(const COMBOEX_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 1608 { 1609 LRESULT ret = DefWindowProcW( infoPtr->hwndSelf, WM_SETREDRAW, wParam, lParam ); 1610 if (wParam) RedrawWindow( infoPtr->hwndSelf, NULL, 0, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN ); 1611 return ret; 1612 } 1613 1614 1615 static LRESULT COMBOEX_WindowPosChanging (const COMBOEX_INFO *infoPtr, WINDOWPOS *wp) 1616 { 1617 RECT cbx_wrect, cbx_crect, cb_wrect; 1618 INT width, height; 1619 1620 GetWindowRect (infoPtr->hwndSelf, &cbx_wrect); 1621 GetClientRect (infoPtr->hwndSelf, &cbx_crect); 1622 GetWindowRect (infoPtr->hwndCombo, &cb_wrect); 1623 1624 /* width is winpos value + border width of comboex */ 1625 width = wp->cx 1626 + (cbx_wrect.right-cbx_wrect.left) 1627 - (cbx_crect.right-cbx_crect.left); 1628 1629 TRACE("winpos=(%d,%d %dx%d) flags=0x%08x\n", 1630 wp->x, wp->y, wp->cx, wp->cy, wp->flags); 1631 TRACE("EX window=(%s), client=(%s)\n", 1632 wine_dbgstr_rect(&cbx_wrect), wine_dbgstr_rect(&cbx_crect)); 1633 TRACE("CB window=(%s), EX setting=(0,0)-(%d,%d)\n", 1634 wine_dbgstr_rect(&cbx_wrect), width, cb_wrect.bottom-cb_wrect.top); 1635 1636 if (width) SetWindowPos (infoPtr->hwndCombo, HWND_TOP, 0, 0, 1637 width, 1638 cb_wrect.bottom-cb_wrect.top, 1639 SWP_NOACTIVATE); 1640 1641 GetWindowRect (infoPtr->hwndCombo, &cb_wrect); 1642 1643 /* height is combo window height plus border width of comboex */ 1644 height = (cb_wrect.bottom-cb_wrect.top) 1645 + (cbx_wrect.bottom-cbx_wrect.top) 1646 - (cbx_crect.bottom-cbx_crect.top); 1647 wp->cy = height; 1648 if (infoPtr->hwndEdit) { 1649 COMBOEX_AdjustEditPos (infoPtr); 1650 InvalidateRect (infoPtr->hwndCombo, 0, TRUE); 1651 } 1652 1653 return 0; 1654 } 1655 1656 static LRESULT CALLBACK 1657 COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 1658 UINT_PTR uId, DWORD_PTR ref_data) 1659 { 1660 COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr ((HWND)ref_data); 1661 NMCBEENDEDITW cbeend; 1662 WCHAR edit_text[260]; 1663 COLORREF obkc; 1664 HDC hDC; 1665 RECT rect; 1666 LRESULT lret; 1667 1668 TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx, info_ptr=%p\n", 1669 hwnd, uMsg, wParam, lParam, infoPtr); 1670 1671 if (uMsg == WM_NCDESTROY) 1672 RemoveWindowSubclass(hwnd, COMBOEX_EditWndProc, EDIT_SUBCLASSID); 1673 1674 if (!infoPtr) 1675 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1676 1677 switch (uMsg) 1678 { 1679 1680 case WM_CHAR: 1681 /* handle (ignore) the return character */ 1682 if (wParam == VK_RETURN) return 0; 1683 /* all other characters pass into the real Edit */ 1684 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1685 1686 case WM_ERASEBKGND: 1687 hDC = (HDC) wParam; 1688 obkc = SetBkColor (hDC, comctl32_color.clrWindow); 1689 GetClientRect (hwnd, &rect); 1690 TRACE("erasing (%s)\n", wine_dbgstr_rect(&rect)); 1691 ExtTextOutW (hDC, 0, 0, ETO_OPAQUE, &rect, 0, 0, 0); 1692 SetBkColor (hDC, obkc); 1693 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1694 1695 case WM_KEYDOWN: { 1696 INT_PTR oldItem, selected; 1697 CBE_ITEMDATA *item; 1698 1699 switch ((INT)wParam) 1700 { 1701 case VK_ESCAPE: 1702 TRACE("special code for VK_ESCAPE\n"); 1703 1704 GetWindowTextW (infoPtr->hwndEdit, edit_text, 260); 1705 1706 infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG); 1707 cbeend.fChanged = FALSE; 1708 cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo, 1709 CB_GETCURSEL, 0, 0); 1710 cbeend.iWhy = CBENF_ESCAPE; 1711 1712 if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text)) return 0; 1713 oldItem = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0); 1714 InvalidateRect (infoPtr->hwndCombo, 0, 0); 1715 if (!(item = COMBOEX_FindItem(infoPtr, oldItem))) { 1716 ERR("item %ld not found. Problem!\n", oldItem); 1717 break; 1718 } 1719 infoPtr->selected = oldItem; 1720 COMBOEX_SetEditText (infoPtr, item); 1721 RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE | 1722 RDW_INVALIDATE); 1723 break; 1724 1725 case VK_RETURN: 1726 TRACE("special code for VK_RETURN\n"); 1727 1728 GetWindowTextW (infoPtr->hwndEdit, edit_text, 260); 1729 1730 infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG); 1731 selected = SendMessageW (infoPtr->hwndCombo, 1732 CB_GETCURSEL, 0, 0); 1733 1734 if (selected != -1) { 1735 cmp_func_t cmptext = get_cmp_func(infoPtr); 1736 item = COMBOEX_FindItem (infoPtr, selected); 1737 TRACE("handling VK_RETURN, selected = %ld, selected_text=%s\n", 1738 selected, debugstr_txt(item->pszText)); 1739 TRACE("handling VK_RETURN, edittext=%s\n", 1740 debugstr_w(edit_text)); 1741 if (cmptext (COMBOEX_GetText(infoPtr, item), edit_text)) { 1742 /* strings not equal -- indicate edit has changed */ 1743 selected = -1; 1744 } 1745 } 1746 1747 cbeend.iNewSelection = selected; 1748 cbeend.fChanged = TRUE; 1749 cbeend.iWhy = CBENF_RETURN; 1750 if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text)) { 1751 /* abort the change, restore previous */ 1752 TRACE("Notify requested abort of change\n"); 1753 COMBOEX_SetEditText (infoPtr, &infoPtr->edit); 1754 RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE | 1755 RDW_INVALIDATE); 1756 return 0; 1757 } 1758 oldItem = SendMessageW (infoPtr->hwndCombo,CB_GETCURSEL, 0, 0); 1759 if (oldItem != -1) { 1760 /* if something is selected, then deselect it */ 1761 SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, -1, 0); 1762 } 1763 InvalidateRect (infoPtr->hwndCombo, 0, 0); 1764 SetFocus(infoPtr->hwndEdit); 1765 break; 1766 1767 case VK_UP: 1768 case VK_DOWN: 1769 { 1770 INT step = wParam == VK_DOWN ? 1 : -1; 1771 1772 oldItem = SendMessageW (infoPtr->hwndSelf, CB_GETCURSEL, 0, 0); 1773 if (oldItem >= 0 && oldItem + step >= 0) 1774 SendMessageW (infoPtr->hwndSelf, CB_SETCURSEL, oldItem + step, 0); 1775 return 0; 1776 } 1777 default: 1778 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1779 } 1780 return 0; 1781 } 1782 1783 case WM_SETFOCUS: 1784 /* remember the focus to set state of icon */ 1785 lret = DefSubclassProc(hwnd, uMsg, wParam, lParam); 1786 infoPtr->flags |= WCBE_EDITFOCUSED; 1787 return lret; 1788 1789 case WM_KILLFOCUS: 1790 /* 1791 * do NOTIFY CBEN_ENDEDIT with CBENF_KILLFOCUS 1792 */ 1793 infoPtr->flags &= ~WCBE_EDITFOCUSED; 1794 if (infoPtr->flags & WCBE_ACTEDIT) { 1795 infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG); 1796 1797 GetWindowTextW (infoPtr->hwndEdit, edit_text, 260); 1798 cbeend.fChanged = FALSE; 1799 cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo, 1800 CB_GETCURSEL, 0, 0); 1801 cbeend.iWhy = CBENF_KILLFOCUS; 1802 1803 COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text); 1804 } 1805 /* fall through */ 1806 1807 default: 1808 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1809 } 1810 } 1811 1812 1813 static LRESULT CALLBACK 1814 COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 1815 UINT_PTR uId, DWORD_PTR ref_data) 1816 { 1817 COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr ((HWND)ref_data); 1818 NMCBEENDEDITW cbeend; 1819 NMMOUSE nmmse; 1820 COLORREF obkc; 1821 HDC hDC; 1822 HWND focusedhwnd; 1823 RECT rect; 1824 POINT pt; 1825 WCHAR edit_text[260]; 1826 1827 TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx, info_ptr=%p\n", 1828 hwnd, uMsg, wParam, lParam, infoPtr); 1829 1830 if (uMsg == WM_NCDESTROY) 1831 RemoveWindowSubclass(hwnd, COMBOEX_ComboWndProc, COMBO_SUBCLASSID); 1832 1833 if (!infoPtr) 1834 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1835 1836 switch (uMsg) 1837 { 1838 case WM_DRAWITEM: 1839 /* 1840 * The only way this message should come is from the 1841 * child Listbox issuing the message. Flag this so 1842 * that ComboEx knows this is listbox. 1843 */ 1844 ((DRAWITEMSTRUCT *)lParam)->itemState |= ODS_COMBOEXLBOX; 1845 break; 1846 1847 case WM_ERASEBKGND: 1848 hDC = (HDC) wParam; 1849 obkc = SetBkColor (hDC, comctl32_color.clrWindow); 1850 GetClientRect (hwnd, &rect); 1851 TRACE("erasing (%s)\n", wine_dbgstr_rect(&rect)); 1852 ExtTextOutW (hDC, 0, 0, ETO_OPAQUE, &rect, 0, 0, 0); 1853 SetBkColor (hDC, obkc); 1854 break; 1855 1856 case WM_SETCURSOR: 1857 /* 1858 * WM_NOTIFY to comboex parent (rebar) 1859 * with NM_SETCURSOR with extra words of 0,0,0,0,0x02010001 1860 * CallWindowProc (previous) 1861 */ 1862 nmmse.dwItemSpec = 0; 1863 nmmse.dwItemData = 0; 1864 nmmse.pt.x = 0; 1865 nmmse.pt.y = 0; 1866 nmmse.dwHitInfo = lParam; 1867 COMBOEX_Notify (infoPtr, NM_SETCURSOR, (NMHDR *)&nmmse); 1868 break; 1869 1870 case WM_LBUTTONDOWN: 1871 GetClientRect (hwnd, &rect); 1872 rect.bottom = rect.top + SendMessageW(infoPtr->hwndSelf, 1873 CB_GETITEMHEIGHT, -1, 0); 1874 rect.left = rect.right - GetSystemMetrics(SM_CXVSCROLL); 1875 pt.x = (short)LOWORD(lParam); 1876 pt.y = (short)HIWORD(lParam); 1877 if (PtInRect(&rect, pt)) 1878 break; 1879 1880 infoPtr->flags |= WCBE_MOUSECAPTURED; 1881 SetCapture(hwnd); 1882 return 0; 1883 1884 case WM_LBUTTONUP: 1885 if (!(infoPtr->flags & WCBE_MOUSECAPTURED)) 1886 break; 1887 1888 ReleaseCapture(); 1889 infoPtr->flags &= ~WCBE_MOUSECAPTURED; 1890 if (infoPtr->flags & WCBE_MOUSEDRAGGED) { 1891 infoPtr->flags &= ~WCBE_MOUSEDRAGGED; 1892 } else { 1893 SendMessageW(hwnd, CB_SHOWDROPDOWN, TRUE, 0); 1894 } 1895 return 0; 1896 1897 case WM_MOUSEMOVE: 1898 if ( (infoPtr->flags & WCBE_MOUSECAPTURED) && 1899 !(infoPtr->flags & WCBE_MOUSEDRAGGED)) { 1900 GetWindowTextW (infoPtr->hwndEdit, edit_text, 260); 1901 COMBOEX_NotifyDragBegin(infoPtr, edit_text); 1902 infoPtr->flags |= WCBE_MOUSEDRAGGED; 1903 } 1904 break; 1905 1906 case WM_COMMAND: 1907 switch (HIWORD(wParam)) { 1908 1909 case EN_UPDATE: 1910 /* traces show that COMBOEX does not issue CBN_EDITUPDATE 1911 * on the EN_UPDATE 1912 */ 1913 return 0; 1914 1915 case EN_KILLFOCUS: 1916 focusedhwnd = GetFocus(); 1917 if (infoPtr->flags & WCBE_ACTEDIT) { 1918 GetWindowTextW (infoPtr->hwndEdit, edit_text, 260); 1919 cbeend.fChanged = (infoPtr->flags & WCBE_EDITCHG); 1920 cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo, 1921 CB_GETCURSEL, 0, 0); 1922 cbeend.iWhy = CBENF_KILLFOCUS; 1923 1924 infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG); 1925 if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text)) return 0; 1926 } 1927 /* possible CB_GETCURSEL */ 1928 InvalidateRect (infoPtr->hwndCombo, 0, 0); 1929 if (focusedhwnd) 1930 SendMessageW (infoPtr->hwndCombo, WM_KILLFOCUS, 1931 (WPARAM)focusedhwnd, 0); 1932 return 0; 1933 1934 case EN_SETFOCUS: { 1935 NMHDR hdr; 1936 1937 SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, 0); 1938 SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, -1); 1939 COMBOEX_Notify (infoPtr, CBEN_BEGINEDIT, &hdr); 1940 infoPtr->flags |= WCBE_ACTEDIT; 1941 infoPtr->flags &= ~WCBE_EDITCHG; /* no change yet */ 1942 return 0; 1943 } 1944 1945 case EN_CHANGE: { 1946 LPCWSTR lastwrk; 1947 cmp_func_t cmptext = get_cmp_func(infoPtr); 1948 1949 INT_PTR selected = SendMessageW (infoPtr->hwndCombo, 1950 CB_GETCURSEL, 0, 0); 1951 1952 /* lstrlenW( lastworkingURL ) */ 1953 1954 GetWindowTextW (infoPtr->hwndEdit, edit_text, 260); 1955 if (selected == -1) { 1956 lastwrk = infoPtr->edit.pszText; 1957 } 1958 else { 1959 CBE_ITEMDATA *item = COMBOEX_FindItem (infoPtr, selected); 1960 lastwrk = COMBOEX_GetText(infoPtr, item); 1961 } 1962 1963 TRACE("handling EN_CHANGE, selected = %ld, selected_text=%s\n", 1964 selected, debugstr_w(lastwrk)); 1965 TRACE("handling EN_CHANGE, edittext=%s\n", 1966 debugstr_w(edit_text)); 1967 1968 /* cmptext is between lastworkingURL and GetWindowText */ 1969 if (cmptext (lastwrk, edit_text)) { 1970 /* strings not equal -- indicate edit has changed */ 1971 infoPtr->flags |= WCBE_EDITCHG; 1972 } 1973 SendMessageW ( infoPtr->hwndNotify, WM_COMMAND, 1974 MAKEWPARAM(GetDlgCtrlID (infoPtr->hwndSelf), 1975 CBN_EDITCHANGE), 1976 (LPARAM)infoPtr->hwndSelf); 1977 return 0; 1978 } 1979 1980 case LBN_SELCHANGE: 1981 default: 1982 break; 1983 }/* fall through */ 1984 default: 1985 ; 1986 } 1987 1988 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 1989 } 1990 1991 1992 static LRESULT WINAPI 1993 COMBOEX_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1994 { 1995 COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd); 1996 1997 TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam); 1998 1999 if (!infoPtr) { 2000 if (uMsg == WM_CREATE) 2001 return COMBOEX_Create (hwnd, (LPCREATESTRUCTA)lParam); 2002 if (uMsg == WM_NCCREATE) 2003 COMBOEX_NCCreate (hwnd); 2004 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 2005 } 2006 2007 switch (uMsg) 2008 { 2009 case CBEM_DELETEITEM: 2010 return COMBOEX_DeleteItem (infoPtr, wParam); 2011 2012 case CBEM_GETCOMBOCONTROL: 2013 return (LRESULT)infoPtr->hwndCombo; 2014 2015 case CBEM_GETEDITCONTROL: 2016 return (LRESULT)infoPtr->hwndEdit; 2017 2018 case CBEM_GETEXTENDEDSTYLE: 2019 return infoPtr->dwExtStyle; 2020 2021 case CBEM_GETIMAGELIST: 2022 return (LRESULT)infoPtr->himl; 2023 2024 case CBEM_GETITEMA: 2025 return (LRESULT)COMBOEX_GetItemA (infoPtr, (COMBOBOXEXITEMA *)lParam); 2026 2027 case CBEM_GETITEMW: 2028 return (LRESULT)COMBOEX_GetItemW (infoPtr, (COMBOBOXEXITEMW *)lParam); 2029 2030 case CBEM_GETUNICODEFORMAT: 2031 return infoPtr->unicode; 2032 2033 case CBEM_HASEDITCHANGED: 2034 return COMBOEX_HasEditChanged (infoPtr); 2035 2036 case CBEM_INSERTITEMA: 2037 return COMBOEX_InsertItemA (infoPtr, (COMBOBOXEXITEMA *)lParam); 2038 2039 case CBEM_INSERTITEMW: 2040 return COMBOEX_InsertItemW (infoPtr, (COMBOBOXEXITEMW *)lParam); 2041 2042 case CBEM_SETEXSTYLE: 2043 case CBEM_SETEXTENDEDSTYLE: 2044 return COMBOEX_SetExtendedStyle (infoPtr, (DWORD)wParam, (DWORD)lParam); 2045 2046 case CBEM_SETIMAGELIST: 2047 return (LRESULT)COMBOEX_SetImageList (infoPtr, (HIMAGELIST)lParam); 2048 2049 case CBEM_SETITEMA: 2050 return COMBOEX_SetItemA (infoPtr, (COMBOBOXEXITEMA *)lParam); 2051 2052 case CBEM_SETITEMW: 2053 return COMBOEX_SetItemW (infoPtr, (COMBOBOXEXITEMW *)lParam); 2054 2055 case CBEM_SETUNICODEFORMAT: 2056 return COMBOEX_SetUnicodeFormat (infoPtr, wParam); 2057 2058 /*case CBEM_SETWINDOWTHEME: 2059 FIXME("CBEM_SETWINDOWTHEME: stub\n");*/ 2060 2061 case WM_SETTEXT: 2062 case WM_GETTEXT: 2063 case WM_GETTEXTLENGTH: 2064 return SendMessageW(infoPtr->hwndEdit, uMsg, wParam, lParam); 2065 2066 case CB_GETLBTEXT: 2067 return COMBOEX_GetListboxText(infoPtr, wParam, (LPWSTR)lParam); 2068 2069 case CB_GETLBTEXTLEN: 2070 return COMBOEX_GetListboxText(infoPtr, wParam, NULL); 2071 2072 case CB_RESETCONTENT: 2073 COMBOEX_ResetContent(infoPtr); 2074 /* fall through */ 2075 2076 /* Combo messages we are not sure if we need to process or just forward */ 2077 case CB_GETDROPPEDCONTROLRECT: 2078 case CB_GETITEMHEIGHT: 2079 case CB_GETEXTENDEDUI: 2080 case CB_LIMITTEXT: 2081 case CB_SELECTSTRING: 2082 2083 /* Combo messages OK to just forward to the regular COMBO */ 2084 case CB_GETCOUNT: 2085 case CB_GETCURSEL: 2086 case CB_GETDROPPEDSTATE: 2087 case CB_SETDROPPEDWIDTH: 2088 case CB_SETEXTENDEDUI: 2089 case CB_SHOWDROPDOWN: 2090 return SendMessageW (infoPtr->hwndCombo, uMsg, wParam, lParam); 2091 2092 /* Combo messages we need to process specially */ 2093 case CB_FINDSTRINGEXACT: 2094 return COMBOEX_FindStringExact (infoPtr, (INT)wParam, (LPCWSTR)lParam); 2095 2096 case CB_GETITEMDATA: 2097 return COMBOEX_GetItemData (infoPtr, (INT)wParam); 2098 2099 case CB_SETCURSEL: 2100 return COMBOEX_SetCursel (infoPtr, (INT)wParam); 2101 2102 case CB_SETITEMDATA: 2103 return COMBOEX_SetItemData (infoPtr, (INT)wParam, (DWORD_PTR)lParam); 2104 2105 case CB_SETITEMHEIGHT: 2106 return COMBOEX_SetItemHeight (infoPtr, (INT)wParam, (UINT)lParam); 2107 2108 2109 2110 /* Window messages passed to parent */ 2111 case WM_COMMAND: 2112 return COMBOEX_Command (infoPtr, wParam); 2113 2114 case WM_NOTIFY: 2115 if (infoPtr->NtfUnicode) 2116 return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam); 2117 else 2118 return SendMessageA (infoPtr->hwndNotify, uMsg, wParam, lParam); 2119 2120 2121 /* Window messages we need to process */ 2122 case WM_DELETEITEM: 2123 return COMBOEX_WM_DeleteItem (infoPtr, (DELETEITEMSTRUCT *)lParam); 2124 2125 case WM_DRAWITEM: 2126 return COMBOEX_DrawItem (infoPtr, (DRAWITEMSTRUCT *)lParam); 2127 2128 case WM_DESTROY: 2129 return COMBOEX_Destroy (infoPtr); 2130 2131 case WM_ENABLE: 2132 return COMBOEX_Enable (infoPtr, (BOOL)wParam); 2133 2134 case WM_MEASUREITEM: 2135 return COMBOEX_MeasureItem (infoPtr, (MEASUREITEMSTRUCT *)lParam); 2136 2137 case WM_NOTIFYFORMAT: 2138 return COMBOEX_NotifyFormat (infoPtr, lParam); 2139 2140 case WM_SIZE: 2141 return COMBOEX_Size (infoPtr, LOWORD(lParam), HIWORD(lParam)); 2142 2143 case WM_GETFONT: 2144 return (LRESULT)infoPtr->font; 2145 2146 case WM_SETFONT: 2147 return COMBOEX_SetFont( infoPtr, (HFONT)wParam, LOWORD(lParam) != 0 ); 2148 2149 case WM_SETREDRAW: 2150 return COMBOEX_SetRedraw(infoPtr, wParam, lParam); 2151 2152 case WM_WINDOWPOSCHANGING: 2153 return COMBOEX_WindowPosChanging (infoPtr, (WINDOWPOS *)lParam); 2154 2155 case WM_SETFOCUS: 2156 if (infoPtr->hwndEdit) SetFocus( infoPtr->hwndEdit ); 2157 else SetFocus( infoPtr->hwndCombo ); 2158 return 0; 2159 2160 case WM_SYSCOLORCHANGE: 2161 COMCTL32_RefreshSysColors(); 2162 return 0; 2163 2164 default: 2165 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) 2166 ERR("unknown msg %04x wp=%08lx lp=%08lx\n",uMsg,wParam,lParam); 2167 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 2168 } 2169 } 2170 2171 2172 void COMBOEX_Register (void) 2173 { 2174 WNDCLASSW wndClass; 2175 2176 ZeroMemory (&wndClass, sizeof(WNDCLASSW)); 2177 wndClass.style = CS_GLOBALCLASS; 2178 wndClass.lpfnWndProc = COMBOEX_WindowProc; 2179 wndClass.cbClsExtra = 0; 2180 wndClass.cbWndExtra = sizeof(COMBOEX_INFO *); 2181 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); 2182 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 2183 wndClass.lpszClassName = WC_COMBOBOXEXW; 2184 2185 RegisterClassW (&wndClass); 2186 } 2187 2188 2189 void COMBOEX_Unregister (void) 2190 { 2191 UnregisterClassW (WC_COMBOBOXEXW, NULL); 2192 } 2193