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