1 /* 2 * Listbox controls 3 * 4 * Copyright 1996 Alexandre Julliard 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 * 20 * NOTES 21 * 22 * This code was audited for completeness against the documented features 23 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun. 24 * 25 * Unless otherwise noted, we believe this code to be complete, as per 26 * the specification mentioned above. 27 * If you discover missing features, or bugs, please note them below. 28 * 29 * TODO: 30 * - LBS_NODATA ReactOS 31 */ 32 33 #include <user32.h> 34 35 WINE_DEFAULT_DEBUG_CHANNEL(listbox); 36 37 /* Items array granularity */ 38 #define LB_ARRAY_GRANULARITY 16 39 40 /* Scrolling timeout in ms */ 41 #define LB_SCROLL_TIMEOUT 50 42 43 /* Listbox system timer id */ 44 #define LB_TIMER_ID 2 45 46 /* flag listbox changed while setredraw false - internal style */ 47 #define LBS_DISPLAYCHANGED 0x80000000 48 49 /* Item structure */ 50 typedef struct 51 { 52 LPWSTR str; /* Item text */ 53 BOOL selected; /* Is item selected? */ 54 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */ 55 ULONG_PTR data; /* User data */ 56 } LB_ITEMDATA; 57 58 /* Listbox structure */ 59 typedef struct 60 { 61 HWND self; /* Our own window handle */ 62 HWND owner; /* Owner window to send notifications to */ 63 UINT style; /* Window style */ 64 INT width; /* Window width */ 65 INT height; /* Window height */ 66 LB_ITEMDATA *items; /* Array of items */ 67 INT nb_items; /* Number of items */ 68 INT top_item; /* Top visible item */ 69 INT selected_item; /* Selected item */ 70 INT focus_item; /* Item that has the focus */ 71 INT anchor_item; /* Anchor item for extended selection */ 72 INT item_height; /* Default item height */ 73 INT page_size; /* Items per listbox page */ 74 INT column_width; /* Column width for multi-column listboxes */ 75 INT horz_extent; /* Horizontal extent */ 76 INT horz_pos; /* Horizontal position */ 77 INT nb_tabs; /* Number of tabs in array */ 78 INT *tabs; /* Array of tabs */ 79 INT avg_char_width; /* Average width of characters */ 80 INT wheel_remain; /* Left over scroll amount */ 81 BOOL caret_on; /* Is caret on? */ 82 BOOL captured; /* Is mouse captured? */ 83 BOOL in_focus; 84 HFONT font; /* Current font */ 85 LCID locale; /* Current locale for string comparisons */ 86 LPHEADCOMBO lphc; /* ComboLBox */ 87 LONG UIState; // REACTOS 88 } LB_DESCR; 89 90 91 #define IS_OWNERDRAW(descr) \ 92 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) 93 94 #define HAS_STRINGS(descr) \ 95 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS)) 96 97 98 #define IS_MULTISELECT(descr) \ 99 ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \ 100 !((descr)->style & LBS_NOSEL)) 101 102 #define SEND_NOTIFICATION(descr,code) \ 103 (SendMessageW( (descr)->owner, WM_COMMAND, \ 104 MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) )) 105 106 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03) 107 108 /* Current timer status */ 109 typedef enum 110 { 111 LB_TIMER_NONE, 112 LB_TIMER_UP, 113 LB_TIMER_LEFT, 114 LB_TIMER_DOWN, 115 LB_TIMER_RIGHT 116 } TIMER_DIRECTION; 117 118 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE; 119 120 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect ); 121 122 /********************************************************************* 123 * listbox class descriptor 124 */ 125 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0}; 126 const struct builtin_class_descr LISTBOX_builtin_class = 127 { 128 listboxW, /* name */ 129 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */ 130 ListBoxWndProcA, /* procA */ 131 ListBoxWndProcW, /* procW */ 132 sizeof(LB_DESCR *), /* extra */ 133 IDC_ARROW, /* cursor */ 134 0 /* brush */ 135 }; 136 137 138 /********************************************************************* 139 * combolbox class descriptor 140 */ 141 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0}; 142 const struct builtin_class_descr COMBOLBOX_builtin_class = 143 { 144 combolboxW, /* name */ 145 CS_DBLCLKS | CS_SAVEBITS, /* style */ 146 ListBoxWndProcA, /* procA */ 147 ListBoxWndProcW, /* procW */ 148 sizeof(LB_DESCR *), /* extra */ 149 IDC_ARROW, /* cursor */ 150 0 /* brush */ 151 }; 152 153 154 /*********************************************************************** 155 * LISTBOX_GetCurrentPageSize 156 * 157 * Return the current page size 158 */ 159 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr ) 160 { 161 INT i, height; 162 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size; 163 for (i = descr->top_item, height = 0; i < descr->nb_items; i++) 164 { 165 if ((height += descr->items[i].height) > descr->height) break; 166 } 167 if (i == descr->top_item) return 1; 168 else return i - descr->top_item; 169 } 170 171 172 /*********************************************************************** 173 * LISTBOX_GetMaxTopIndex 174 * 175 * Return the maximum possible index for the top of the listbox. 176 */ 177 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr ) 178 { 179 INT max, page; 180 181 if (descr->style & LBS_OWNERDRAWVARIABLE) 182 { 183 page = descr->height; 184 for (max = descr->nb_items - 1; max >= 0; max--) 185 if ((page -= descr->items[max].height) < 0) break; 186 if (max < descr->nb_items - 1) max++; 187 } 188 else if (descr->style & LBS_MULTICOLUMN) 189 { 190 if ((page = descr->width / descr->column_width) < 1) page = 1; 191 max = (descr->nb_items + descr->page_size - 1) / descr->page_size; 192 max = (max - page) * descr->page_size; 193 } 194 else 195 { 196 max = descr->nb_items - descr->page_size; 197 } 198 if (max < 0) max = 0; 199 return max; 200 } 201 202 203 /*********************************************************************** 204 * LISTBOX_UpdateScroll 205 * 206 * Update the scrollbars. Should be called whenever the content 207 * of the listbox changes. 208 */ 209 static void LISTBOX_UpdateScroll( LB_DESCR *descr ) 210 { 211 SCROLLINFO info; 212 213 /* Check the listbox scroll bar flags individually before we call 214 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and 215 no WS_VSCROLL, we end up with an uninitialized, visible horizontal 216 scroll bar when we do not need one. 217 if (!(descr->style & WS_VSCROLL)) return; 218 */ 219 220 /* It is important that we check descr->style, and not wnd->dwStyle, 221 for WS_VSCROLL, as the former is exactly the one passed in 222 argument to CreateWindow. 223 In Windows (and from now on in Wine :) a listbox created 224 with such a style (no WS_SCROLL) does not update 225 the scrollbar with listbox-related data, thus letting 226 the programmer use it for his/her own purposes. */ 227 228 if (descr->style & LBS_NOREDRAW) return; 229 info.cbSize = sizeof(info); 230 231 if (descr->style & LBS_MULTICOLUMN) 232 { 233 info.nMin = 0; 234 info.nMax = (descr->nb_items - 1) / descr->page_size; 235 info.nPos = descr->top_item / descr->page_size; 236 info.nPage = descr->width / descr->column_width; 237 if (info.nPage < 1) info.nPage = 1; 238 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; 239 if (descr->style & LBS_DISABLENOSCROLL) 240 info.fMask |= SIF_DISABLENOSCROLL; 241 if (descr->style & WS_HSCROLL) 242 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); 243 info.nMax = 0; 244 info.fMask = SIF_RANGE; 245 if (descr->style & WS_VSCROLL) 246 SetScrollInfo( descr->self, SB_VERT, &info, TRUE ); 247 } 248 else 249 { 250 info.nMin = 0; 251 info.nMax = descr->nb_items - 1; 252 info.nPos = descr->top_item; 253 info.nPage = LISTBOX_GetCurrentPageSize( descr ); 254 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; 255 if (descr->style & LBS_DISABLENOSCROLL) 256 info.fMask |= SIF_DISABLENOSCROLL; 257 if (descr->style & WS_VSCROLL) 258 SetScrollInfo( descr->self, SB_VERT, &info, TRUE ); 259 260 if ((descr->style & WS_HSCROLL) && descr->horz_extent) 261 { 262 info.nPos = descr->horz_pos; 263 info.nPage = descr->width; 264 info.fMask = SIF_POS | SIF_PAGE; 265 if (descr->style & LBS_DISABLENOSCROLL) 266 info.fMask |= SIF_DISABLENOSCROLL; 267 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); 268 } 269 else 270 { 271 if (descr->style & LBS_DISABLENOSCROLL) 272 { 273 info.nMin = 0; 274 info.nMax = 0; 275 info.fMask = SIF_RANGE | SIF_DISABLENOSCROLL; 276 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); 277 } 278 else 279 { 280 ShowScrollBar( descr->self, SB_HORZ, FALSE ); 281 } 282 } 283 } 284 } 285 286 287 /*********************************************************************** 288 * LISTBOX_SetTopItem 289 * 290 * Set the top item of the listbox, scrolling up or down if necessary. 291 */ 292 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll ) 293 { 294 INT max = LISTBOX_GetMaxTopIndex( descr ); 295 296 TRACE("setting top item %d, scroll %d\n", index, scroll); 297 298 if (index > max) index = max; 299 if (index < 0) index = 0; 300 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size; 301 if (descr->top_item == index) return LB_OKAY; 302 if (scroll) 303 { 304 INT diff; 305 if (descr->style & LBS_MULTICOLUMN) 306 diff = (descr->top_item - index) / descr->page_size * descr->column_width; 307 else if (descr->style & LBS_OWNERDRAWVARIABLE) 308 { 309 INT i; 310 diff = 0; 311 if (index > descr->top_item) 312 { 313 for (i = index - 1; i >= descr->top_item; i--) 314 diff -= descr->items[i].height; 315 } 316 else 317 { 318 for (i = index; i < descr->top_item; i++) 319 diff += descr->items[i].height; 320 } 321 } 322 else 323 diff = (descr->top_item - index) * descr->item_height; 324 325 #ifdef __REACTOS__ 326 if (descr->style & LBS_MULTICOLUMN) 327 ScrollWindowEx(descr->self, diff, 0, NULL, NULL, 0, NULL, 328 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN); 329 else 330 #endif 331 ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL, 332 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 333 } 334 else 335 InvalidateRect( descr->self, NULL, TRUE ); 336 descr->top_item = index; 337 LISTBOX_UpdateScroll( descr ); 338 return LB_OKAY; 339 } 340 341 342 /*********************************************************************** 343 * LISTBOX_UpdatePage 344 * 345 * Update the page size. Should be called when the size of 346 * the client area or the item height changes. 347 */ 348 static void LISTBOX_UpdatePage( LB_DESCR *descr ) 349 { 350 INT page_size; 351 352 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1) 353 page_size = 1; 354 if (page_size == descr->page_size) return; 355 descr->page_size = page_size; 356 if (descr->style & LBS_MULTICOLUMN) 357 InvalidateRect( descr->self, NULL, TRUE ); 358 LISTBOX_SetTopItem( descr, descr->top_item, FALSE ); 359 } 360 361 362 /*********************************************************************** 363 * LISTBOX_UpdateSize 364 * 365 * Update the size of the listbox. Should be called when the size of 366 * the client area changes. 367 */ 368 static void LISTBOX_UpdateSize( LB_DESCR *descr ) 369 { 370 RECT rect; 371 LONG style = GetWindowLongPtrW( descr->self, GWL_STYLE ); 372 373 GetClientRect( descr->self, &rect ); 374 if (style & WS_HSCROLL) 375 rect.bottom += GetSystemMetrics(SM_CYHSCROLL); 376 descr->width = rect.right - rect.left; 377 descr->height = rect.bottom - rect.top; 378 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE)) 379 { 380 INT remaining; 381 RECT rect; 382 383 GetWindowRect( descr->self, &rect ); 384 if(descr->item_height != 0) 385 remaining = descr->height % descr->item_height; 386 else 387 remaining = 0; 388 if ((descr->height > descr->item_height) && remaining) 389 { 390 TRACE("[%p]: changing height %d -> %d\n", 391 descr->self, descr->height, descr->height - remaining ); 392 SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left, 393 rect.bottom - rect.top - remaining, 394 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE ); 395 return; 396 } 397 } 398 TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height ); 399 LISTBOX_UpdatePage( descr ); 400 LISTBOX_UpdateScroll( descr ); 401 402 /* Invalidate the focused item so it will be repainted correctly */ 403 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1) 404 { 405 InvalidateRect( descr->self, &rect, FALSE ); 406 } 407 } 408 409 410 /*********************************************************************** 411 * LISTBOX_GetItemRect 412 * 413 * Get the rectangle enclosing an item, in listbox client coordinates. 414 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error. 415 */ 416 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect ) 417 { 418 /* Index <= 0 is legal even on empty listboxes */ 419 if (index && (index >= descr->nb_items)) 420 { 421 memset(rect, 0, sizeof(*rect)); 422 SetLastError(ERROR_INVALID_INDEX); 423 return LB_ERR; 424 } 425 SetRect( rect, 0, 0, descr->width, descr->height ); 426 if (descr->style & LBS_MULTICOLUMN) 427 { 428 INT col = (index / descr->page_size) - 429 (descr->top_item / descr->page_size); 430 rect->left += col * descr->column_width; 431 rect->right = rect->left + descr->column_width; 432 rect->top += (index % descr->page_size) * descr->item_height; 433 rect->bottom = rect->top + descr->item_height; 434 } 435 else if (descr->style & LBS_OWNERDRAWVARIABLE) 436 { 437 INT i; 438 rect->right += descr->horz_pos; 439 if ((index >= 0) && (index < descr->nb_items)) 440 { 441 if (index < descr->top_item) 442 { 443 for (i = descr->top_item-1; i >= index; i--) 444 rect->top -= descr->items[i].height; 445 } 446 else 447 { 448 for (i = descr->top_item; i < index; i++) 449 rect->top += descr->items[i].height; 450 } 451 rect->bottom = rect->top + descr->items[index].height; 452 453 } 454 } 455 else 456 { 457 rect->top += (index - descr->top_item) * descr->item_height; 458 rect->bottom = rect->top + descr->item_height; 459 rect->right += descr->horz_pos; 460 } 461 462 TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect)); 463 464 return ((rect->left < descr->width) && (rect->right > 0) && 465 (rect->top < descr->height) && (rect->bottom > 0)); 466 } 467 468 469 /*********************************************************************** 470 * LISTBOX_GetItemFromPoint 471 * 472 * Return the item nearest from point (x,y) (in client coordinates). 473 */ 474 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y ) 475 { 476 INT index = descr->top_item; 477 478 if (!descr->nb_items) return -1; /* No items */ 479 if (descr->style & LBS_OWNERDRAWVARIABLE) 480 { 481 INT pos = 0; 482 if (y >= 0) 483 { 484 while (index < descr->nb_items) 485 { 486 if ((pos += descr->items[index].height) > y) break; 487 index++; 488 } 489 } 490 else 491 { 492 while (index > 0) 493 { 494 index--; 495 if ((pos -= descr->items[index].height) <= y) break; 496 } 497 } 498 } 499 else if (descr->style & LBS_MULTICOLUMN) 500 { 501 if (y >= descr->item_height * descr->page_size) return -1; 502 if (y >= 0) index += y / descr->item_height; 503 if (x >= 0) index += (x / descr->column_width) * descr->page_size; 504 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size; 505 } 506 else 507 { 508 index += (y / descr->item_height); 509 } 510 if (index < 0) return 0; 511 if (index >= descr->nb_items) return -1; 512 return index; 513 } 514 515 516 /*********************************************************************** 517 * LISTBOX_PaintItem 518 * 519 * Paint an item. 520 */ 521 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, 522 INT index, UINT action, BOOL ignoreFocus ) 523 { 524 LB_ITEMDATA *item = NULL; 525 if (index < descr->nb_items) item = &descr->items[index]; 526 527 if (IS_OWNERDRAW(descr)) 528 { 529 DRAWITEMSTRUCT dis; 530 RECT r; 531 HRGN hrgn; 532 533 if (!item) 534 { 535 if (action == ODA_FOCUS) 536 { // REACTOS 537 if (!(descr->UIState & UISF_HIDEFOCUS)) 538 DrawFocusRect( hdc, rect ); 539 } // 540 else 541 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items); 542 return; 543 } 544 545 /* some programs mess with the clipping region when 546 drawing the item, *and* restore the previous region 547 after they are done, so a region has better to exist 548 else everything ends clipped */ 549 GetClientRect(descr->self, &r); 550 hrgn = set_control_clipping( hdc, &r ); 551 552 dis.CtlType = ODT_LISTBOX; 553 dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID ); 554 dis.hwndItem = descr->self; 555 dis.itemAction = action; 556 dis.hDC = hdc; 557 dis.itemID = index; 558 dis.itemState = 0; 559 if (item->selected) dis.itemState |= ODS_SELECTED; 560 if (!ignoreFocus && (descr->focus_item == index) && 561 (descr->caret_on) && 562 (descr->in_focus)) dis.itemState |= ODS_FOCUS; 563 if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED; 564 dis.itemData = item->data; 565 dis.rcItem = *rect; 566 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n", 567 descr->self, index, debugstr_w(item->str), action, 568 dis.itemState, wine_dbgstr_rect(rect) ); 569 SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); 570 SelectClipRgn( hdc, hrgn ); 571 if (hrgn) DeleteObject( hrgn ); 572 } 573 else 574 { 575 COLORREF oldText = 0, oldBk = 0; 576 577 if (action == ODA_FOCUS) 578 { 579 if (!(descr->UIState & UISF_HIDEFOCUS)) // REACTOS 580 DrawFocusRect( hdc, rect ); 581 return; 582 } 583 if (item && item->selected) 584 { 585 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); 586 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 587 } 588 589 TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n", 590 descr->self, index, item ? debugstr_w(item->str) : "", action, 591 wine_dbgstr_rect(rect) ); 592 if (!item) 593 ExtTextOutW( hdc, rect->left + 1, rect->top, 594 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); 595 else if (!(descr->style & LBS_USETABSTOPS)) 596 ExtTextOutW( hdc, rect->left + 1, rect->top, 597 ETO_OPAQUE | ETO_CLIPPED, rect, item->str, 598 strlenW(item->str), NULL ); 599 else 600 { 601 /* Output empty string to paint background in the full width. */ 602 ExtTextOutW( hdc, rect->left + 1, rect->top, 603 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); 604 TabbedTextOutW( hdc, rect->left + 1 , rect->top, 605 item->str, strlenW(item->str), 606 descr->nb_tabs, descr->tabs, 0); 607 } 608 if (item && item->selected) 609 { 610 SetBkColor( hdc, oldBk ); 611 SetTextColor( hdc, oldText ); 612 } 613 if (!ignoreFocus && (descr->focus_item == index) && 614 (descr->caret_on) && 615 (descr->in_focus) && 616 !(descr->UIState & UISF_HIDEFOCUS)) DrawFocusRect( hdc, rect ); 617 } 618 } 619 620 621 /*********************************************************************** 622 * LISTBOX_SetRedraw 623 * 624 * Change the redraw flag. 625 */ 626 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on ) 627 { 628 if (on) 629 { 630 if (!(descr->style & LBS_NOREDRAW)) return; 631 descr->style &= ~LBS_NOREDRAW; 632 if (descr->style & LBS_DISPLAYCHANGED) 633 { /* page was changed while setredraw false, refresh automatically */ 634 InvalidateRect(descr->self, NULL, TRUE); 635 if ((descr->top_item + descr->page_size) > descr->nb_items) 636 { /* reset top of page if less than number of items/page */ 637 descr->top_item = descr->nb_items - descr->page_size; 638 if (descr->top_item < 0) descr->top_item = 0; 639 } 640 descr->style &= ~LBS_DISPLAYCHANGED; 641 } 642 LISTBOX_UpdateScroll( descr ); 643 } 644 else descr->style |= LBS_NOREDRAW; 645 } 646 647 648 /*********************************************************************** 649 * LISTBOX_RepaintItem 650 * 651 * Repaint a single item synchronously. 652 */ 653 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action ) 654 { 655 HDC hdc; 656 RECT rect; 657 HFONT oldFont = 0; 658 HBRUSH hbrush, oldBrush = 0; 659 660 /* Do not repaint the item if the item is not visible */ 661 if (!IsWindowVisible(descr->self)) return; 662 if (descr->style & LBS_NOREDRAW) 663 { 664 descr->style |= LBS_DISPLAYCHANGED; 665 return; 666 } 667 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return; 668 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return; 669 if (descr->font) oldFont = SelectObject( hdc, descr->font ); 670 #ifdef __REACTOS__ 671 hbrush = GetControlColor( descr->owner, descr->self, hdc, WM_CTLCOLORLISTBOX); 672 #else 673 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX, 674 (WPARAM)hdc, (LPARAM)descr->self ); 675 #endif 676 if (hbrush) oldBrush = SelectObject( hdc, hbrush ); 677 if (!IsWindowEnabled(descr->self)) 678 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); 679 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); 680 LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE ); 681 if (oldFont) SelectObject( hdc, oldFont ); 682 if (oldBrush) SelectObject( hdc, oldBrush ); 683 ReleaseDC( descr->self, hdc ); 684 } 685 686 687 /*********************************************************************** 688 * LISTBOX_DrawFocusRect 689 */ 690 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on ) 691 { 692 HDC hdc; 693 RECT rect; 694 HFONT oldFont = 0; 695 696 /* Do not repaint the item if the item is not visible */ 697 if (!IsWindowVisible(descr->self)) return; 698 699 if (descr->focus_item == -1) return; 700 if (!descr->caret_on || !descr->in_focus) return; 701 702 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return; 703 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return; 704 if (descr->font) oldFont = SelectObject( hdc, descr->font ); 705 if (!IsWindowEnabled(descr->self)) 706 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); 707 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); 708 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, !on ); 709 if (oldFont) SelectObject( hdc, oldFont ); 710 ReleaseDC( descr->self, hdc ); 711 } 712 713 714 /*********************************************************************** 715 * LISTBOX_InitStorage 716 */ 717 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items ) 718 { 719 LB_ITEMDATA *item; 720 721 nb_items += LB_ARRAY_GRANULARITY - 1; 722 nb_items -= (nb_items % LB_ARRAY_GRANULARITY); 723 if (descr->items) { 724 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); 725 item = HeapReAlloc( GetProcessHeap(), 0, descr->items, 726 nb_items * sizeof(LB_ITEMDATA)); 727 } 728 else { 729 item = HeapAlloc( GetProcessHeap(), 0, 730 nb_items * sizeof(LB_ITEMDATA)); 731 } 732 733 if (!item) 734 { 735 SEND_NOTIFICATION( descr, LBN_ERRSPACE ); 736 return LB_ERRSPACE; 737 } 738 descr->items = item; 739 return LB_OKAY; 740 } 741 742 743 /*********************************************************************** 744 * LISTBOX_SetTabStops 745 */ 746 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs ) 747 { 748 INT i; 749 750 if (!(descr->style & LBS_USETABSTOPS)) 751 { 752 SetLastError(ERROR_LB_WITHOUT_TABSTOPS); 753 return FALSE; 754 } 755 756 HeapFree( GetProcessHeap(), 0, descr->tabs ); 757 if (!(descr->nb_tabs = count)) 758 { 759 descr->tabs = NULL; 760 return TRUE; 761 } 762 if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0, 763 descr->nb_tabs * sizeof(INT) ))) 764 return FALSE; 765 memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) ); 766 767 /* convert into "dialog units"*/ 768 for (i = 0; i < descr->nb_tabs; i++) 769 descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4); 770 771 return TRUE; 772 } 773 774 775 /*********************************************************************** 776 * LISTBOX_GetText 777 */ 778 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode ) 779 { 780 DWORD len; 781 782 if ((index < 0) || (index >= descr->nb_items)) 783 { 784 SetLastError(ERROR_INVALID_INDEX); 785 return LB_ERR; 786 } 787 if (HAS_STRINGS(descr)) 788 { 789 if (!buffer) 790 { 791 len = strlenW(descr->items[index].str); 792 if( unicode ) 793 return len; 794 return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len, 795 NULL, 0, NULL, NULL ); 796 } 797 798 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str)); 799 800 _SEH2_TRY /* hide a Delphi bug that passes a read-only buffer */ 801 { 802 if(unicode) 803 { 804 strcpyW( buffer, descr->items[index].str ); 805 len = strlenW(buffer); 806 } 807 else 808 { 809 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, 810 (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1; 811 } 812 } 813 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 814 { 815 ERR( "got an invalid buffer (Delphi bug?)\n" ); 816 SetLastError( ERROR_INVALID_PARAMETER ); 817 len = LB_ERR; 818 } 819 _SEH2_END 820 } else { 821 if (buffer) 822 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data); 823 len = sizeof(DWORD); 824 } 825 return len; 826 } 827 828 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 ) 829 { 830 INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 ); 831 if (ret == CSTR_LESS_THAN) 832 return -1; 833 if (ret == CSTR_EQUAL) 834 return 0; 835 if (ret == CSTR_GREATER_THAN) 836 return 1; 837 return -1; 838 } 839 840 /*********************************************************************** 841 * LISTBOX_FindStringPos 842 * 843 * Find the nearest string located before a given string in sort order. 844 * If 'exact' is TRUE, return an error if we don't get an exact match. 845 */ 846 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact ) 847 { 848 INT index, min, max, res; 849 850 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */ 851 min = 0; 852 max = descr->nb_items; 853 while (min != max) 854 { 855 index = (min + max) / 2; 856 if (HAS_STRINGS(descr)) 857 res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str); 858 else 859 { 860 COMPAREITEMSTRUCT cis; 861 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 862 863 cis.CtlType = ODT_LISTBOX; 864 cis.CtlID = id; 865 cis.hwndItem = descr->self; 866 /* note that some application (MetaStock) expects the second item 867 * to be in the listbox */ 868 cis.itemID1 = -1; 869 cis.itemData1 = (ULONG_PTR)str; 870 cis.itemID2 = index; 871 cis.itemData2 = descr->items[index].data; 872 cis.dwLocaleId = descr->locale; 873 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis ); 874 } 875 if (!res) return index; 876 if (res < 0) max = index; 877 else min = index + 1; 878 } 879 return exact ? -1 : max; 880 } 881 882 883 /*********************************************************************** 884 * LISTBOX_FindFileStrPos 885 * 886 * Find the nearest string located before a given string in directory 887 * sort order (i.e. first files, then directories, then drives). 888 */ 889 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str ) 890 { 891 INT min, max, res; 892 893 if (!HAS_STRINGS(descr)) 894 return LISTBOX_FindStringPos( descr, str, FALSE ); 895 min = 0; 896 max = descr->nb_items; 897 while (min != max) 898 { 899 INT index = (min + max) / 2; 900 LPCWSTR p = descr->items[index].str; 901 if (*p == '[') /* drive or directory */ 902 { 903 if (*str != '[') res = -1; 904 else if (p[1] == '-') /* drive */ 905 { 906 if (str[1] == '-') res = str[2] - p[2]; 907 else res = -1; 908 } 909 else /* directory */ 910 { 911 if (str[1] == '-') res = 1; 912 else res = LISTBOX_lstrcmpiW( descr->locale, str, p ); 913 } 914 } 915 else /* filename */ 916 { 917 if (*str == '[') res = 1; 918 else res = LISTBOX_lstrcmpiW( descr->locale, str, p ); 919 } 920 if (!res) return index; 921 if (res < 0) max = index; 922 else min = index + 1; 923 } 924 return max; 925 } 926 927 928 /*********************************************************************** 929 * LISTBOX_FindString 930 * 931 * Find the item beginning with a given string. 932 */ 933 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact ) 934 { 935 INT i; 936 LB_ITEMDATA *item; 937 938 if (start >= descr->nb_items) start = -1; 939 item = descr->items + start + 1; 940 if (HAS_STRINGS(descr)) 941 { 942 if (!str || ! str[0] ) return LB_ERR; 943 if (exact) 944 { 945 for (i = start + 1; i < descr->nb_items; i++, item++) 946 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i; 947 for (i = 0, item = descr->items; i <= start; i++, item++) 948 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i; 949 } 950 else 951 { 952 /* Special case for drives and directories: ignore prefix */ 953 #define CHECK_DRIVE(item) \ 954 if ((item)->str[0] == '[') \ 955 { \ 956 if (!strncmpiW( str, (item)->str+1, len )) return i; \ 957 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \ 958 return i; \ 959 } 960 961 INT len = strlenW(str); 962 for (i = start + 1; i < descr->nb_items; i++, item++) 963 { 964 if (!strncmpiW( str, item->str, len )) return i; 965 CHECK_DRIVE(item); 966 } 967 for (i = 0, item = descr->items; i <= start; i++, item++) 968 { 969 if (!strncmpiW( str, item->str, len )) return i; 970 CHECK_DRIVE(item); 971 } 972 #undef CHECK_DRIVE 973 } 974 } 975 else 976 { 977 if (exact && (descr->style & LBS_SORT)) 978 /* If sorted, use a WM_COMPAREITEM binary search */ 979 return LISTBOX_FindStringPos( descr, str, TRUE ); 980 981 /* Otherwise use a linear search */ 982 for (i = start + 1; i < descr->nb_items; i++, item++) 983 if (item->data == (ULONG_PTR)str) return i; 984 for (i = 0, item = descr->items; i <= start; i++, item++) 985 if (item->data == (ULONG_PTR)str) return i; 986 } 987 return LB_ERR; 988 } 989 990 991 /*********************************************************************** 992 * LISTBOX_GetSelCount 993 */ 994 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr ) 995 { 996 INT i, count; 997 const LB_ITEMDATA *item = descr->items; 998 999 if (!(descr->style & LBS_MULTIPLESEL) || 1000 (descr->style & LBS_NOSEL)) 1001 return LB_ERR; 1002 for (i = count = 0; i < descr->nb_items; i++, item++) 1003 if (item->selected) count++; 1004 return count; 1005 } 1006 1007 1008 /*********************************************************************** 1009 * LISTBOX_GetSelItems 1010 */ 1011 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array ) 1012 { 1013 INT i, count; 1014 const LB_ITEMDATA *item = descr->items; 1015 1016 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; 1017 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++) 1018 if (item->selected) array[count++] = i; 1019 return count; 1020 } 1021 1022 1023 /*********************************************************************** 1024 * LISTBOX_Paint 1025 */ 1026 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc ) 1027 { 1028 INT i, col_pos = descr->page_size - 1; 1029 RECT rect; 1030 RECT focusRect = {-1, -1, -1, -1}; 1031 HFONT oldFont = 0; 1032 HBRUSH hbrush, oldBrush = 0; 1033 1034 if (descr->style & LBS_NOREDRAW) return 0; 1035 1036 SetRect( &rect, 0, 0, descr->width, descr->height ); 1037 if (descr->style & LBS_MULTICOLUMN) 1038 rect.right = rect.left + descr->column_width; 1039 else if (descr->horz_pos) 1040 { 1041 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); 1042 rect.right += descr->horz_pos; 1043 } 1044 1045 if (descr->font) oldFont = SelectObject( hdc, descr->font ); 1046 #ifdef __REACTOS__ 1047 hbrush = GetControlColor( descr->owner, descr->self, hdc, WM_CTLCOLORLISTBOX); 1048 #else 1049 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX, 1050 (WPARAM)hdc, (LPARAM)descr->self ); 1051 #endif 1052 if (hbrush) oldBrush = SelectObject( hdc, hbrush ); 1053 if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); 1054 1055 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on && 1056 (descr->in_focus)) 1057 { 1058 /* Special case for empty listbox: paint focus rect */ 1059 rect.bottom = rect.top + descr->item_height; 1060 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 1061 &rect, NULL, 0, NULL ); 1062 LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE ); 1063 rect.top = rect.bottom; 1064 } 1065 1066 /* Paint all the item, regarding the selection 1067 Focus state will be painted after */ 1068 1069 for (i = descr->top_item; i < descr->nb_items; i++) 1070 { 1071 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) 1072 rect.bottom = rect.top + descr->item_height; 1073 else 1074 rect.bottom = rect.top + descr->items[i].height; 1075 1076 /* keep the focus rect, to paint the focus item after */ 1077 if (i == descr->focus_item) 1078 focusRect = rect; 1079 1080 LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE ); 1081 rect.top = rect.bottom; 1082 1083 if ((descr->style & LBS_MULTICOLUMN) && !col_pos) 1084 { 1085 if (!IS_OWNERDRAW(descr)) 1086 { 1087 /* Clear the bottom of the column */ 1088 if (rect.top < descr->height) 1089 { 1090 rect.bottom = descr->height; 1091 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 1092 &rect, NULL, 0, NULL ); 1093 } 1094 } 1095 1096 /* Go to the next column */ 1097 rect.left += descr->column_width; 1098 rect.right += descr->column_width; 1099 rect.top = 0; 1100 col_pos = descr->page_size - 1; 1101 } 1102 else 1103 { 1104 col_pos--; 1105 if (rect.top >= descr->height) break; 1106 } 1107 } 1108 1109 /* Paint the focus item now */ 1110 if (focusRect.top != focusRect.bottom && 1111 descr->caret_on && descr->in_focus) 1112 LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE ); 1113 1114 if (!IS_OWNERDRAW(descr)) 1115 { 1116 /* Clear the remainder of the client area */ 1117 if (rect.top < descr->height) 1118 { 1119 rect.bottom = descr->height; 1120 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 1121 &rect, NULL, 0, NULL ); 1122 } 1123 if (rect.right < descr->width) 1124 { 1125 rect.left = rect.right; 1126 rect.right = descr->width; 1127 rect.top = 0; 1128 rect.bottom = descr->height; 1129 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, 1130 &rect, NULL, 0, NULL ); 1131 } 1132 } 1133 if (oldFont) SelectObject( hdc, oldFont ); 1134 if (oldBrush) SelectObject( hdc, oldBrush ); 1135 return 0; 1136 } 1137 1138 1139 /*********************************************************************** 1140 * LISTBOX_InvalidateItems 1141 * 1142 * Invalidate all items from a given item. If the specified item is not 1143 * visible, nothing happens. 1144 */ 1145 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index ) 1146 { 1147 RECT rect; 1148 1149 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1) 1150 { 1151 if (descr->style & LBS_NOREDRAW) 1152 { 1153 descr->style |= LBS_DISPLAYCHANGED; 1154 return; 1155 } 1156 rect.bottom = descr->height; 1157 InvalidateRect( descr->self, &rect, TRUE ); 1158 if (descr->style & LBS_MULTICOLUMN) 1159 { 1160 /* Repaint the other columns */ 1161 rect.left = rect.right; 1162 rect.right = descr->width; 1163 rect.top = 0; 1164 InvalidateRect( descr->self, &rect, TRUE ); 1165 } 1166 } 1167 } 1168 1169 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index ) 1170 { 1171 RECT rect; 1172 1173 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1) 1174 InvalidateRect( descr->self, &rect, TRUE ); 1175 } 1176 1177 /*********************************************************************** 1178 * LISTBOX_GetItemHeight 1179 */ 1180 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index ) 1181 { 1182 if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0) 1183 { 1184 if ((index < 0) || (index >= descr->nb_items)) 1185 { 1186 SetLastError(ERROR_INVALID_INDEX); 1187 return LB_ERR; 1188 } 1189 return descr->items[index].height; 1190 } 1191 else return descr->item_height; 1192 } 1193 1194 1195 /*********************************************************************** 1196 * LISTBOX_SetItemHeight 1197 */ 1198 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint ) 1199 { 1200 if (height > MAXBYTE) 1201 return -1; 1202 1203 if (!height) height = 1; 1204 1205 if (descr->style & LBS_OWNERDRAWVARIABLE) 1206 { 1207 if ((index < 0) || (index >= descr->nb_items)) 1208 { 1209 SetLastError(ERROR_INVALID_INDEX); 1210 return LB_ERR; 1211 } 1212 TRACE("[%p]: item %d height = %d\n", descr->self, index, height ); 1213 descr->items[index].height = height; 1214 LISTBOX_UpdateScroll( descr ); 1215 if (repaint) 1216 LISTBOX_InvalidateItems( descr, index ); 1217 } 1218 else if (height != descr->item_height) 1219 { 1220 TRACE("[%p]: new height = %d\n", descr->self, height ); 1221 descr->item_height = height; 1222 LISTBOX_UpdatePage( descr ); 1223 LISTBOX_UpdateScroll( descr ); 1224 if (repaint) 1225 InvalidateRect( descr->self, 0, TRUE ); 1226 } 1227 return LB_OKAY; 1228 } 1229 1230 1231 /*********************************************************************** 1232 * LISTBOX_SetHorizontalPos 1233 */ 1234 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos ) 1235 { 1236 INT diff; 1237 1238 if (pos > descr->horz_extent - descr->width) 1239 pos = descr->horz_extent - descr->width; 1240 if (pos < 0) pos = 0; 1241 if (!(diff = descr->horz_pos - pos)) return; 1242 TRACE("[%p]: new horz pos = %d\n", descr->self, pos ); 1243 descr->horz_pos = pos; 1244 LISTBOX_UpdateScroll( descr ); 1245 if (abs(diff) < descr->width) 1246 { 1247 RECT rect; 1248 /* Invalidate the focused item so it will be repainted correctly */ 1249 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1) 1250 InvalidateRect( descr->self, &rect, TRUE ); 1251 ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL, 1252 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 1253 } 1254 else 1255 InvalidateRect( descr->self, NULL, TRUE ); 1256 } 1257 1258 1259 /*********************************************************************** 1260 * LISTBOX_SetHorizontalExtent 1261 */ 1262 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent ) 1263 { 1264 if (descr->style & LBS_MULTICOLUMN) 1265 return LB_OKAY; 1266 if (extent == descr->horz_extent) return LB_OKAY; 1267 TRACE("[%p]: new horz extent = %d\n", descr->self, extent ); 1268 descr->horz_extent = extent; 1269 if (descr->style & WS_HSCROLL) { 1270 SCROLLINFO info; 1271 info.cbSize = sizeof(info); 1272 info.nMin = 0; 1273 info.nMax = descr->horz_extent ? descr->horz_extent - 1 : 0; 1274 info.fMask = SIF_RANGE; 1275 if (descr->style & LBS_DISABLENOSCROLL) 1276 info.fMask |= SIF_DISABLENOSCROLL; 1277 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); 1278 } 1279 if (descr->horz_pos > extent - descr->width) 1280 LISTBOX_SetHorizontalPos( descr, extent - descr->width ); 1281 return LB_OKAY; 1282 } 1283 1284 1285 /*********************************************************************** 1286 * LISTBOX_SetColumnWidth 1287 */ 1288 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width) 1289 { 1290 if (width == descr->column_width) return LB_OKAY; 1291 TRACE("[%p]: new column width = %d\n", descr->self, width ); 1292 descr->column_width = width; 1293 LISTBOX_UpdatePage( descr ); 1294 return LB_OKAY; 1295 } 1296 1297 1298 /*********************************************************************** 1299 * LISTBOX_SetFont 1300 * 1301 * Returns the item height. 1302 */ 1303 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font ) 1304 { 1305 HDC hdc; 1306 HFONT oldFont = 0; 1307 const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 1308 SIZE sz; 1309 1310 descr->font = font; 1311 1312 if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) 1313 { 1314 ERR("unable to get DC.\n" ); 1315 return 16; 1316 } 1317 if (font) oldFont = SelectObject( hdc, font ); 1318 GetTextExtentPointA( hdc, alphabet, 52, &sz); 1319 if (oldFont) SelectObject( hdc, oldFont ); 1320 ReleaseDC( descr->self, hdc ); 1321 1322 descr->avg_char_width = (sz.cx / 26 + 1) / 2; 1323 if (!IS_OWNERDRAW(descr)) 1324 LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE ); 1325 return sz.cy; 1326 } 1327 1328 1329 /*********************************************************************** 1330 * LISTBOX_MakeItemVisible 1331 * 1332 * Make sure that a given item is partially or fully visible. 1333 */ 1334 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully ) 1335 { 1336 INT top; 1337 1338 TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully); 1339 1340 if (index <= descr->top_item) top = index; 1341 else if (descr->style & LBS_MULTICOLUMN) 1342 { 1343 INT cols = descr->width; 1344 if (!fully) cols += descr->column_width - 1; 1345 if (cols >= descr->column_width) cols /= descr->column_width; 1346 else cols = 1; 1347 if (index < descr->top_item + (descr->page_size * cols)) return; 1348 top = index - descr->page_size * (cols - 1); 1349 } 1350 else if (descr->style & LBS_OWNERDRAWVARIABLE) 1351 { 1352 INT height = fully ? descr->items[index].height : 1; 1353 for (top = index; top > descr->top_item; top--) 1354 if ((height += descr->items[top-1].height) > descr->height) break; 1355 } 1356 else 1357 { 1358 if (index < descr->top_item + descr->page_size) return; 1359 if (!fully && (index == descr->top_item + descr->page_size) && 1360 (descr->height > (descr->page_size * descr->item_height))) return; 1361 top = index - descr->page_size + 1; 1362 } 1363 LISTBOX_SetTopItem( descr, top, TRUE ); 1364 } 1365 1366 /*********************************************************************** 1367 * LISTBOX_SetCaretIndex 1368 * 1369 * NOTES 1370 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned. 1371 * 1372 */ 1373 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible ) 1374 { 1375 INT oldfocus = descr->focus_item; 1376 1377 TRACE("old focus %d, index %d\n", oldfocus, index); 1378 1379 if (descr->style & LBS_NOSEL) return LB_ERR; 1380 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; 1381 if (index == oldfocus) return LB_OKAY; 1382 1383 LISTBOX_DrawFocusRect( descr, FALSE ); 1384 descr->focus_item = index; 1385 1386 LISTBOX_MakeItemVisible( descr, index, fully_visible ); 1387 LISTBOX_DrawFocusRect( descr, TRUE ); 1388 1389 return LB_OKAY; 1390 } 1391 1392 1393 /*********************************************************************** 1394 * LISTBOX_SelectItemRange 1395 * 1396 * Select a range of items. Should only be used on a MULTIPLESEL listbox. 1397 */ 1398 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first, 1399 INT last, BOOL on ) 1400 { 1401 INT i; 1402 1403 /* A few sanity checks */ 1404 1405 if (descr->style & LBS_NOSEL) return LB_ERR; 1406 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; 1407 1408 if (!descr->nb_items) return LB_OKAY; 1409 1410 if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1; 1411 if (first < 0) first = 0; 1412 if (last < first) return LB_OKAY; 1413 1414 if (on) /* Turn selection on */ 1415 { 1416 for (i = first; i <= last; i++) 1417 { 1418 if (descr->items[i].selected) continue; 1419 descr->items[i].selected = TRUE; 1420 LISTBOX_InvalidateItemRect(descr, i); 1421 } 1422 } 1423 else /* Turn selection off */ 1424 { 1425 for (i = first; i <= last; i++) 1426 { 1427 if (!descr->items[i].selected) continue; 1428 descr->items[i].selected = FALSE; 1429 LISTBOX_InvalidateItemRect(descr, i); 1430 } 1431 } 1432 return LB_OKAY; 1433 } 1434 1435 /*********************************************************************** 1436 * LISTBOX_SetSelection 1437 */ 1438 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index, 1439 BOOL on, BOOL send_notify ) 1440 { 1441 TRACE( "cur_sel=%d index=%d notify=%s\n", 1442 descr->selected_item, index, send_notify ? "YES" : "NO" ); 1443 1444 if (descr->style & LBS_NOSEL) 1445 { 1446 descr->selected_item = index; 1447 return LB_ERR; 1448 } 1449 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR; 1450 if (descr->style & LBS_MULTIPLESEL) 1451 { 1452 if (index == -1) /* Select all items */ 1453 return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on ); 1454 else /* Only one item */ 1455 return LISTBOX_SelectItemRange( descr, index, index, on ); 1456 } 1457 else 1458 { 1459 INT oldsel = descr->selected_item; 1460 if (index == oldsel) return LB_OKAY; 1461 if (oldsel != -1) descr->items[oldsel].selected = FALSE; 1462 if (index != -1) descr->items[index].selected = TRUE; 1463 if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); 1464 descr->selected_item = index; 1465 if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT ); 1466 if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr, 1467 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL ); 1468 else 1469 if( descr->lphc ) /* set selection change flag for parent combo */ 1470 descr->lphc->wState |= CBF_SELCHANGE; 1471 } 1472 return LB_OKAY; 1473 } 1474 1475 1476 /*********************************************************************** 1477 * LISTBOX_MoveCaret 1478 * 1479 * Change the caret position and extend the selection to the new caret. 1480 */ 1481 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible ) 1482 { 1483 TRACE("old focus %d, index %d\n", descr->focus_item, index); 1484 1485 if ((index < 0) || (index >= descr->nb_items)) 1486 return; 1487 1488 /* Important, repaint needs to be done in this order if 1489 you want to mimic Windows behavior: 1490 1. Remove the focus and paint the item 1491 2. Remove the selection and paint the item(s) 1492 3. Set the selection and repaint the item(s) 1493 4. Set the focus to 'index' and repaint the item */ 1494 1495 /* 1. remove the focus and repaint the item */ 1496 LISTBOX_DrawFocusRect( descr, FALSE ); 1497 1498 /* 2. then turn off the previous selection */ 1499 /* 3. repaint the new selected item */ 1500 if (descr->style & LBS_EXTENDEDSEL) 1501 { 1502 if (descr->anchor_item != -1) 1503 { 1504 INT first = min( index, descr->anchor_item ); 1505 INT last = max( index, descr->anchor_item ); 1506 if (first > 0) 1507 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE ); 1508 LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE ); 1509 LISTBOX_SelectItemRange( descr, first, last, TRUE ); 1510 } 1511 } 1512 else if (!(descr->style & LBS_MULTIPLESEL)) 1513 { 1514 /* Set selection to new caret item */ 1515 LISTBOX_SetSelection( descr, index, TRUE, FALSE ); 1516 } 1517 1518 /* 4. repaint the new item with the focus */ 1519 descr->focus_item = index; 1520 LISTBOX_MakeItemVisible( descr, index, fully_visible ); 1521 LISTBOX_DrawFocusRect( descr, TRUE ); 1522 } 1523 1524 1525 /*********************************************************************** 1526 * LISTBOX_InsertItem 1527 */ 1528 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index, 1529 LPWSTR str, ULONG_PTR data ) 1530 { 1531 LB_ITEMDATA *item; 1532 INT max_items; 1533 INT oldfocus = descr->focus_item; 1534 1535 if (index == -1) index = descr->nb_items; 1536 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR; 1537 if (!descr->items) max_items = 0; 1538 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); 1539 if (descr->nb_items == max_items) 1540 { 1541 /* We need to grow the array */ 1542 max_items += LB_ARRAY_GRANULARITY; 1543 if (descr->items) 1544 item = HeapReAlloc( GetProcessHeap(), 0, descr->items, 1545 max_items * sizeof(LB_ITEMDATA) ); 1546 else 1547 item = HeapAlloc( GetProcessHeap(), 0, 1548 max_items * sizeof(LB_ITEMDATA) ); 1549 if (!item) 1550 { 1551 SEND_NOTIFICATION( descr, LBN_ERRSPACE ); 1552 return LB_ERRSPACE; 1553 } 1554 descr->items = item; 1555 } 1556 1557 /* Insert the item structure */ 1558 1559 item = &descr->items[index]; 1560 if (index < descr->nb_items) 1561 RtlMoveMemory( item + 1, item, 1562 (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); 1563 item->str = str; 1564 item->data = data; 1565 item->height = 0; 1566 item->selected = FALSE; 1567 descr->nb_items++; 1568 1569 /* Get item height */ 1570 1571 if (descr->style & LBS_OWNERDRAWVARIABLE) 1572 { 1573 MEASUREITEMSTRUCT mis; 1574 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 1575 1576 mis.CtlType = ODT_LISTBOX; 1577 mis.CtlID = id; 1578 mis.itemID = index; 1579 mis.itemData = descr->items[index].data; 1580 mis.itemHeight = descr->item_height; 1581 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); 1582 item->height = mis.itemHeight ? mis.itemHeight : 1; 1583 TRACE("[%p]: measure item %d (%s) = %d\n", 1584 descr->self, index, str ? debugstr_w(str) : "", item->height ); 1585 } 1586 1587 /* Repaint the items */ 1588 1589 LISTBOX_UpdateScroll( descr ); 1590 LISTBOX_InvalidateItems( descr, index ); 1591 1592 /* Move selection and focused item */ 1593 /* If listbox was empty, set focus to the first item */ 1594 if (descr->nb_items == 1) 1595 LISTBOX_SetCaretIndex( descr, 0, FALSE ); 1596 /* single select don't change selection index in win31 */ 1597 else if ((ISWIN31) && !(IS_MULTISELECT(descr))) 1598 { 1599 descr->selected_item++; 1600 LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE ); 1601 } 1602 else 1603 { 1604 if (index <= descr->selected_item) 1605 { 1606 descr->selected_item++; 1607 descr->focus_item = oldfocus; /* focus not changed */ 1608 } 1609 } 1610 return LB_OKAY; 1611 } 1612 1613 1614 /*********************************************************************** 1615 * LISTBOX_InsertString 1616 */ 1617 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str ) 1618 { 1619 LPWSTR new_str = NULL; 1620 ULONG_PTR data = 0; 1621 LRESULT ret; 1622 1623 if (HAS_STRINGS(descr)) 1624 { 1625 static const WCHAR empty_stringW[] = { 0 }; 1626 if (!str) str = empty_stringW; 1627 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) ))) 1628 { 1629 SEND_NOTIFICATION( descr, LBN_ERRSPACE ); 1630 return LB_ERRSPACE; 1631 } 1632 strcpyW(new_str, str); 1633 } 1634 else data = (ULONG_PTR)str; 1635 1636 if (index == -1) index = descr->nb_items; 1637 if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0) 1638 { 1639 HeapFree( GetProcessHeap(), 0, new_str ); 1640 return ret; 1641 } 1642 1643 TRACE("[%p]: added item %d %s\n", 1644 descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" ); 1645 return index; 1646 } 1647 1648 1649 /*********************************************************************** 1650 * LISTBOX_DeleteItem 1651 * 1652 * Delete the content of an item. 'index' must be a valid index. 1653 */ 1654 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index ) 1655 { 1656 /* save the item data before it gets freed by LB_RESETCONTENT */ 1657 ULONG_PTR item_data = descr->items[index].data; 1658 LPWSTR item_str = descr->items[index].str; 1659 1660 if (!descr->nb_items) 1661 SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 ); 1662 1663 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items, 1664 * while Win95 sends it for all items with user data. 1665 * It's probably better to send it too often than not 1666 * often enough, so this is what we do here. 1667 */ 1668 if (IS_OWNERDRAW(descr) || item_data) 1669 { 1670 DELETEITEMSTRUCT dis; 1671 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 1672 1673 dis.CtlType = ODT_LISTBOX; 1674 dis.CtlID = id; 1675 dis.itemID = index; 1676 dis.hwndItem = descr->self; 1677 dis.itemData = item_data; 1678 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis ); 1679 } 1680 if (HAS_STRINGS(descr)) 1681 HeapFree( GetProcessHeap(), 0, item_str ); 1682 } 1683 1684 1685 /*********************************************************************** 1686 * LISTBOX_RemoveItem 1687 * 1688 * Remove an item from the listbox and delete its content. 1689 */ 1690 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index ) 1691 { 1692 LB_ITEMDATA *item; 1693 INT max_items; 1694 1695 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; 1696 1697 /* We need to invalidate the original rect instead of the updated one. */ 1698 LISTBOX_InvalidateItems( descr, index ); 1699 1700 descr->nb_items--; 1701 LISTBOX_DeleteItem( descr, index ); 1702 1703 if (!descr->nb_items) return LB_OKAY; 1704 1705 /* Remove the item */ 1706 1707 item = &descr->items[index]; 1708 if (index < descr->nb_items) 1709 RtlMoveMemory( item, item + 1, 1710 (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); 1711 if (descr->anchor_item == descr->nb_items) descr->anchor_item--; 1712 1713 /* Shrink the item array if possible */ 1714 1715 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA); 1716 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) 1717 { 1718 max_items -= LB_ARRAY_GRANULARITY; 1719 item = HeapReAlloc( GetProcessHeap(), 0, descr->items, 1720 max_items * sizeof(LB_ITEMDATA) ); 1721 if (item) descr->items = item; 1722 } 1723 /* Repaint the items */ 1724 1725 LISTBOX_UpdateScroll( descr ); 1726 /* if we removed the scrollbar, reset the top of the list 1727 (correct for owner-drawn ???) */ 1728 if (descr->nb_items == descr->page_size) 1729 LISTBOX_SetTopItem( descr, 0, TRUE ); 1730 1731 /* Move selection and focused item */ 1732 if (!IS_MULTISELECT(descr)) 1733 { 1734 if (index == descr->selected_item) 1735 descr->selected_item = -1; 1736 else if (index < descr->selected_item) 1737 { 1738 descr->selected_item--; 1739 if (ISWIN31) /* win 31 do not change the selected item number */ 1740 LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE); 1741 } 1742 } 1743 1744 if (descr->focus_item >= descr->nb_items) 1745 { 1746 descr->focus_item = descr->nb_items - 1; 1747 if (descr->focus_item < 0) descr->focus_item = 0; 1748 } 1749 return LB_OKAY; 1750 } 1751 1752 1753 /*********************************************************************** 1754 * LISTBOX_ResetContent 1755 */ 1756 static void LISTBOX_ResetContent( LB_DESCR *descr ) 1757 { 1758 INT i; 1759 1760 for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i); 1761 HeapFree( GetProcessHeap(), 0, descr->items ); 1762 descr->nb_items = 0; 1763 descr->top_item = 0; 1764 descr->selected_item = -1; 1765 descr->focus_item = 0; 1766 descr->anchor_item = -1; 1767 descr->items = NULL; 1768 } 1769 1770 1771 /*********************************************************************** 1772 * LISTBOX_SetCount 1773 */ 1774 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count ) 1775 { 1776 LRESULT ret; 1777 1778 if (HAS_STRINGS(descr)) 1779 { 1780 SetLastError(ERROR_SETCOUNT_ON_BAD_LB); 1781 return LB_ERR; 1782 } 1783 1784 /* FIXME: this is far from optimal... */ 1785 if (count > descr->nb_items) 1786 { 1787 while (count > descr->nb_items) 1788 if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0) 1789 return ret; 1790 } 1791 else if (count < descr->nb_items) 1792 { 1793 while (count < descr->nb_items) 1794 if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0) 1795 return ret; 1796 } 1797 1798 InvalidateRect( descr->self, NULL, TRUE ); 1799 return LB_OKAY; 1800 } 1801 1802 1803 /*********************************************************************** 1804 * LISTBOX_Directory 1805 */ 1806 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib, 1807 LPCWSTR filespec, BOOL long_names ) 1808 { 1809 HANDLE handle; 1810 LRESULT ret = LB_OKAY; 1811 WIN32_FIND_DATAW entry; 1812 int pos; 1813 LRESULT maxinsert = LB_ERR; 1814 1815 /* don't scan directory if we just want drives exclusively */ 1816 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) { 1817 /* scan directory */ 1818 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE) 1819 { 1820 int le = GetLastError(); 1821 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR; 1822 } 1823 else 1824 { 1825 do 1826 { 1827 WCHAR buffer[270]; 1828 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1829 { 1830 static const WCHAR bracketW[] = { ']',0 }; 1831 static const WCHAR dotW[] = { '.',0 }; 1832 if (!(attrib & DDL_DIRECTORY) || 1833 !strcmpW( entry.cFileName, dotW )) continue; 1834 buffer[0] = '['; 1835 if (!long_names && entry.cAlternateFileName[0]) 1836 strcpyW( buffer + 1, entry.cAlternateFileName ); 1837 else 1838 strcpyW( buffer + 1, entry.cFileName ); 1839 strcatW(buffer, bracketW); 1840 } 1841 else /* not a directory */ 1842 { 1843 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \ 1844 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE) 1845 1846 if ((attrib & DDL_EXCLUSIVE) && 1847 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS))) 1848 continue; 1849 #undef ATTRIBS 1850 if (!long_names && entry.cAlternateFileName[0]) 1851 strcpyW( buffer, entry.cAlternateFileName ); 1852 else 1853 strcpyW( buffer, entry.cFileName ); 1854 } 1855 if (!long_names) CharLowerW( buffer ); 1856 pos = LISTBOX_FindFileStrPos( descr, buffer ); 1857 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0) 1858 break; 1859 if (ret <= maxinsert) maxinsert++; else maxinsert = ret; 1860 } while (FindNextFileW( handle, &entry )); 1861 FindClose( handle ); 1862 } 1863 } 1864 if (ret >= 0) 1865 { 1866 ret = maxinsert; 1867 1868 /* scan drives */ 1869 if (attrib & DDL_DRIVES) 1870 { 1871 WCHAR buffer[] = {'[','-','a','-',']',0}; 1872 WCHAR root[] = {'A',':','\\',0}; 1873 int drive; 1874 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++) 1875 { 1876 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue; 1877 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0) 1878 break; 1879 } 1880 } 1881 } 1882 return ret; 1883 } 1884 1885 1886 /*********************************************************************** 1887 * LISTBOX_HandleVScroll 1888 */ 1889 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos ) 1890 { 1891 SCROLLINFO info; 1892 1893 if (descr->style & LBS_MULTICOLUMN) return 0; 1894 switch(scrollReq) 1895 { 1896 case SB_LINEUP: 1897 LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE ); 1898 break; 1899 case SB_LINEDOWN: 1900 LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE ); 1901 break; 1902 case SB_PAGEUP: 1903 LISTBOX_SetTopItem( descr, descr->top_item - 1904 LISTBOX_GetCurrentPageSize( descr ), TRUE ); 1905 break; 1906 case SB_PAGEDOWN: 1907 LISTBOX_SetTopItem( descr, descr->top_item + 1908 LISTBOX_GetCurrentPageSize( descr ), TRUE ); 1909 break; 1910 case SB_THUMBPOSITION: 1911 LISTBOX_SetTopItem( descr, pos, TRUE ); 1912 break; 1913 case SB_THUMBTRACK: 1914 info.cbSize = sizeof(info); 1915 info.fMask = SIF_TRACKPOS; 1916 GetScrollInfo( descr->self, SB_VERT, &info ); 1917 LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE ); 1918 break; 1919 case SB_TOP: 1920 LISTBOX_SetTopItem( descr, 0, TRUE ); 1921 break; 1922 case SB_BOTTOM: 1923 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE ); 1924 break; 1925 } 1926 return 0; 1927 } 1928 1929 1930 /*********************************************************************** 1931 * LISTBOX_HandleHScroll 1932 */ 1933 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos ) 1934 { 1935 SCROLLINFO info; 1936 INT page; 1937 1938 if (descr->style & LBS_MULTICOLUMN) 1939 { 1940 switch(scrollReq) 1941 { 1942 case SB_LINELEFT: 1943 LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size, 1944 TRUE ); 1945 break; 1946 case SB_LINERIGHT: 1947 LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size, 1948 TRUE ); 1949 break; 1950 case SB_PAGELEFT: 1951 page = descr->width / descr->column_width; 1952 if (page < 1) page = 1; 1953 LISTBOX_SetTopItem( descr, 1954 descr->top_item - page * descr->page_size, TRUE ); 1955 break; 1956 case SB_PAGERIGHT: 1957 page = descr->width / descr->column_width; 1958 if (page < 1) page = 1; 1959 LISTBOX_SetTopItem( descr, 1960 descr->top_item + page * descr->page_size, TRUE ); 1961 break; 1962 case SB_THUMBPOSITION: 1963 LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE ); 1964 break; 1965 case SB_THUMBTRACK: 1966 info.cbSize = sizeof(info); 1967 info.fMask = SIF_TRACKPOS; 1968 #ifdef __REACTOS__ 1969 GetScrollInfo( descr->self, SB_HORZ, &info ); 1970 #else 1971 GetScrollInfo( descr->self, SB_VERT, &info ); 1972 #endif 1973 LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size, 1974 TRUE ); 1975 break; 1976 case SB_LEFT: 1977 LISTBOX_SetTopItem( descr, 0, TRUE ); 1978 break; 1979 case SB_RIGHT: 1980 LISTBOX_SetTopItem( descr, descr->nb_items, TRUE ); 1981 break; 1982 } 1983 } 1984 else if (descr->horz_extent) 1985 { 1986 switch(scrollReq) 1987 { 1988 case SB_LINELEFT: 1989 LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 ); 1990 break; 1991 case SB_LINERIGHT: 1992 LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 ); 1993 break; 1994 case SB_PAGELEFT: 1995 LISTBOX_SetHorizontalPos( descr, 1996 descr->horz_pos - descr->width ); 1997 break; 1998 case SB_PAGERIGHT: 1999 LISTBOX_SetHorizontalPos( descr, 2000 descr->horz_pos + descr->width ); 2001 break; 2002 case SB_THUMBPOSITION: 2003 LISTBOX_SetHorizontalPos( descr, pos ); 2004 break; 2005 case SB_THUMBTRACK: 2006 info.cbSize = sizeof(info); 2007 info.fMask = SIF_TRACKPOS; 2008 GetScrollInfo( descr->self, SB_HORZ, &info ); 2009 LISTBOX_SetHorizontalPos( descr, info.nTrackPos ); 2010 break; 2011 case SB_LEFT: 2012 LISTBOX_SetHorizontalPos( descr, 0 ); 2013 break; 2014 case SB_RIGHT: 2015 LISTBOX_SetHorizontalPos( descr, 2016 descr->horz_extent - descr->width ); 2017 break; 2018 } 2019 } 2020 return 0; 2021 } 2022 2023 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta ) 2024 { 2025 UINT pulScrollLines = 3; 2026 2027 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); 2028 2029 /* if scrolling changes direction, ignore left overs */ 2030 if ((delta < 0 && descr->wheel_remain < 0) || 2031 (delta > 0 && descr->wheel_remain > 0)) 2032 descr->wheel_remain += delta; 2033 else 2034 descr->wheel_remain = delta; 2035 2036 if (descr->wheel_remain && pulScrollLines) 2037 { 2038 int cLineScroll; 2039 pulScrollLines = min((UINT) descr->page_size, pulScrollLines); 2040 cLineScroll = pulScrollLines * (float)descr->wheel_remain / WHEEL_DELTA; 2041 descr->wheel_remain -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines; 2042 #ifdef __REACTOS__ 2043 if (cLineScroll < 0) 2044 cLineScroll -= descr->page_size; 2045 #endif 2046 LISTBOX_SetTopItem( descr, descr->top_item - cLineScroll, TRUE ); 2047 } 2048 return 0; 2049 } 2050 2051 /*********************************************************************** 2052 * LISTBOX_HandleLButtonDown 2053 */ 2054 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y ) 2055 { 2056 INT index = LISTBOX_GetItemFromPoint( descr, x, y ); 2057 2058 TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n", 2059 descr->self, x, y, index, descr->focus_item); 2060 2061 if (!descr->caret_on && (descr->in_focus)) return 0; 2062 2063 if (!descr->in_focus) 2064 { 2065 if( !descr->lphc ) SetFocus( descr->self ); 2066 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self ); 2067 } 2068 2069 if (index == -1) return 0; 2070 2071 if (!descr->lphc) 2072 { 2073 if (descr->style & LBS_NOTIFY ) 2074 SendMessageW( descr->owner, WM_LBTRACKPOINT, index, 2075 MAKELPARAM( x, y ) ); 2076 } 2077 2078 descr->captured = TRUE; 2079 SetCapture( descr->self ); 2080 2081 if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) 2082 { 2083 /* we should perhaps make sure that all items are deselected 2084 FIXME: needed for !LBS_EXTENDEDSEL, too ? 2085 if (!(keys & (MK_SHIFT|MK_CONTROL))) 2086 LISTBOX_SetSelection( descr, -1, FALSE, FALSE); 2087 */ 2088 2089 if (!(keys & MK_SHIFT)) descr->anchor_item = index; 2090 if (keys & MK_CONTROL) 2091 { 2092 LISTBOX_SetCaretIndex( descr, index, FALSE ); 2093 LISTBOX_SetSelection( descr, index, 2094 !descr->items[index].selected, 2095 (descr->style & LBS_NOTIFY) != 0); 2096 } 2097 else 2098 { 2099 LISTBOX_MoveCaret( descr, index, FALSE ); 2100 2101 if (descr->style & LBS_EXTENDEDSEL) 2102 { 2103 LISTBOX_SetSelection( descr, index, 2104 descr->items[index].selected, 2105 (descr->style & LBS_NOTIFY) != 0 ); 2106 } 2107 else 2108 { 2109 LISTBOX_SetSelection( descr, index, 2110 !descr->items[index].selected, 2111 (descr->style & LBS_NOTIFY) != 0 ); 2112 } 2113 } 2114 } 2115 else 2116 { 2117 descr->anchor_item = index; 2118 LISTBOX_MoveCaret( descr, index, FALSE ); 2119 LISTBOX_SetSelection( descr, index, 2120 TRUE, (descr->style & LBS_NOTIFY) != 0 ); 2121 } 2122 2123 if (!descr->lphc) 2124 { // See rev 40864 use Ptr for 64 bit. 2125 if (GetWindowLongPtrW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT) 2126 { 2127 POINT pt; 2128 2129 pt.x = x; 2130 pt.y = y; 2131 2132 if (DragDetect( descr->self, pt )) 2133 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 ); 2134 } 2135 } 2136 return 0; 2137 } 2138 2139 2140 /************************************************************************* 2141 * LISTBOX_HandleLButtonDownCombo [Internal] 2142 * 2143 * Process LButtonDown message for the ComboListBox 2144 * 2145 * PARAMS 2146 * pWnd [I] The windows internal structure 2147 * pDescr [I] The ListBox internal structure 2148 * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info) 2149 * x [I] X Mouse Coordinate 2150 * y [I] Y Mouse Coordinate 2151 * 2152 * RETURNS 2153 * 0 since we are processing the WM_LBUTTONDOWN Message 2154 * 2155 * NOTES 2156 * This function is only to be used when a ListBox is a ComboListBox 2157 */ 2158 2159 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y) 2160 { 2161 RECT clientRect, screenRect; 2162 POINT mousePos; 2163 2164 mousePos.x = x; 2165 mousePos.y = y; 2166 2167 GetClientRect(descr->self, &clientRect); 2168 2169 if(PtInRect(&clientRect, mousePos)) 2170 { 2171 /* MousePos is in client, resume normal processing */ 2172 if (msg == WM_LBUTTONDOWN) 2173 { 2174 descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1; 2175 return LISTBOX_HandleLButtonDown( descr, keys, x, y); 2176 } 2177 else if (descr->style & LBS_NOTIFY) 2178 SEND_NOTIFICATION( descr, LBN_DBLCLK ); 2179 } 2180 else 2181 { 2182 POINT screenMousePos; 2183 HWND hWndOldCapture; 2184 2185 /* Check the Non-Client Area */ 2186 screenMousePos = mousePos; 2187 hWndOldCapture = GetCapture(); 2188 ReleaseCapture(); 2189 GetWindowRect(descr->self, &screenRect); 2190 ClientToScreen(descr->self, &screenMousePos); 2191 2192 if(!PtInRect(&screenRect, screenMousePos)) 2193 { 2194 LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE ); 2195 LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE ); 2196 COMBO_FlipListbox( descr->lphc, FALSE, FALSE ); 2197 } 2198 else 2199 { 2200 /* Check to see the NC is a scrollbar */ 2201 INT nHitTestType=0; 2202 LONG style = GetWindowLongPtrW( descr->self, GWL_STYLE ); 2203 /* Check Vertical scroll bar */ 2204 if (style & WS_VSCROLL) 2205 { 2206 clientRect.right += GetSystemMetrics(SM_CXVSCROLL); 2207 if (PtInRect( &clientRect, mousePos )) 2208 nHitTestType = HTVSCROLL; 2209 } 2210 /* Check horizontal scroll bar */ 2211 if (style & WS_HSCROLL) 2212 { 2213 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL); 2214 if (PtInRect( &clientRect, mousePos )) 2215 nHitTestType = HTHSCROLL; 2216 } 2217 /* Windows sends this message when a scrollbar is clicked 2218 */ 2219 2220 if(nHitTestType != 0) 2221 { 2222 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType, 2223 MAKELONG(screenMousePos.x, screenMousePos.y)); 2224 } 2225 /* Resume the Capture after scrolling is complete 2226 */ 2227 if(hWndOldCapture != 0) 2228 SetCapture(hWndOldCapture); 2229 } 2230 } 2231 return 0; 2232 } 2233 2234 /*********************************************************************** 2235 * LISTBOX_HandleLButtonUp 2236 */ 2237 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr ) 2238 { 2239 if (LISTBOX_Timer != LB_TIMER_NONE) 2240 KillSystemTimer( descr->self, LB_TIMER_ID ); 2241 LISTBOX_Timer = LB_TIMER_NONE; 2242 if (descr->captured) 2243 { 2244 descr->captured = FALSE; 2245 if (GetCapture() == descr->self) ReleaseCapture(); 2246 if ((descr->style & LBS_NOTIFY) && descr->nb_items) 2247 SEND_NOTIFICATION( descr, LBN_SELCHANGE ); 2248 } 2249 return 0; 2250 } 2251 2252 2253 /*********************************************************************** 2254 * LISTBOX_HandleTimer 2255 * 2256 * Handle scrolling upon a timer event. 2257 * Return TRUE if scrolling should continue. 2258 */ 2259 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir ) 2260 { 2261 switch(dir) 2262 { 2263 case LB_TIMER_UP: 2264 if (descr->top_item) index = descr->top_item - 1; 2265 else index = 0; 2266 break; 2267 case LB_TIMER_LEFT: 2268 if (descr->top_item) index -= descr->page_size; 2269 break; 2270 case LB_TIMER_DOWN: 2271 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr ); 2272 if (index == descr->focus_item) index++; 2273 if (index >= descr->nb_items) index = descr->nb_items - 1; 2274 break; 2275 case LB_TIMER_RIGHT: 2276 if (index + descr->page_size < descr->nb_items) 2277 index += descr->page_size; 2278 break; 2279 case LB_TIMER_NONE: 2280 break; 2281 } 2282 if (index == descr->focus_item) return FALSE; 2283 LISTBOX_MoveCaret( descr, index, FALSE ); 2284 return TRUE; 2285 } 2286 2287 2288 /*********************************************************************** 2289 * LISTBOX_HandleSystemTimer 2290 * 2291 * WM_SYSTIMER handler. 2292 */ 2293 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr ) 2294 { 2295 if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer )) 2296 { 2297 KillSystemTimer( descr->self, LB_TIMER_ID ); 2298 LISTBOX_Timer = LB_TIMER_NONE; 2299 } 2300 return 0; 2301 } 2302 2303 2304 /*********************************************************************** 2305 * LISTBOX_HandleMouseMove 2306 * 2307 * WM_MOUSEMOVE handler. 2308 */ 2309 static void LISTBOX_HandleMouseMove( LB_DESCR *descr, 2310 INT x, INT y ) 2311 { 2312 INT index; 2313 TIMER_DIRECTION dir = LB_TIMER_NONE; 2314 2315 if (!descr->captured) return; 2316 2317 if (descr->style & LBS_MULTICOLUMN) 2318 { 2319 if (y < 0) y = 0; 2320 else if (y >= descr->item_height * descr->page_size) 2321 y = descr->item_height * descr->page_size - 1; 2322 2323 if (x < 0) 2324 { 2325 dir = LB_TIMER_LEFT; 2326 x = 0; 2327 } 2328 else if (x >= descr->width) 2329 { 2330 dir = LB_TIMER_RIGHT; 2331 x = descr->width - 1; 2332 } 2333 } 2334 else 2335 { 2336 if (y < 0) dir = LB_TIMER_UP; /* above */ 2337 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */ 2338 } 2339 2340 index = LISTBOX_GetItemFromPoint( descr, x, y ); 2341 if (index == -1) index = descr->focus_item; 2342 if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE; 2343 2344 /* Start/stop the system timer */ 2345 2346 if (dir != LB_TIMER_NONE) 2347 SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL); 2348 else if (LISTBOX_Timer != LB_TIMER_NONE) 2349 KillSystemTimer( descr->self, LB_TIMER_ID ); 2350 LISTBOX_Timer = dir; 2351 } 2352 2353 2354 /*********************************************************************** 2355 * LISTBOX_HandleKeyDown 2356 */ 2357 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key ) 2358 { 2359 INT caret = -1; 2360 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */ 2361 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item)) 2362 bForceSelection = FALSE; /* only for single select list */ 2363 2364 if (descr->style & LBS_WANTKEYBOARDINPUT) 2365 { 2366 caret = SendMessageW( descr->owner, WM_VKEYTOITEM, 2367 MAKEWPARAM(LOWORD(key), descr->focus_item), 2368 (LPARAM)descr->self ); 2369 if (caret == -2) return 0; 2370 } 2371 if (caret == -1) switch(key) 2372 { 2373 case VK_LEFT: 2374 if (descr->style & LBS_MULTICOLUMN) 2375 { 2376 bForceSelection = FALSE; 2377 if (descr->focus_item >= descr->page_size) 2378 caret = descr->focus_item - descr->page_size; 2379 break; 2380 } 2381 /* fall through */ 2382 case VK_UP: 2383 caret = descr->focus_item - 1; 2384 if (caret < 0) caret = 0; 2385 break; 2386 case VK_RIGHT: 2387 if (descr->style & LBS_MULTICOLUMN) 2388 { 2389 bForceSelection = FALSE; 2390 if (descr->focus_item + descr->page_size < descr->nb_items) 2391 caret = descr->focus_item + descr->page_size; 2392 break; 2393 } 2394 /* fall through */ 2395 case VK_DOWN: 2396 caret = descr->focus_item + 1; 2397 if (caret >= descr->nb_items) caret = descr->nb_items - 1; 2398 break; 2399 2400 case VK_PRIOR: 2401 if (descr->style & LBS_MULTICOLUMN) 2402 { 2403 INT page = descr->width / descr->column_width; 2404 if (page < 1) page = 1; 2405 caret = descr->focus_item - (page * descr->page_size) + 1; 2406 } 2407 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1; 2408 if (caret < 0) caret = 0; 2409 break; 2410 case VK_NEXT: 2411 if (descr->style & LBS_MULTICOLUMN) 2412 { 2413 INT page = descr->width / descr->column_width; 2414 if (page < 1) page = 1; 2415 caret = descr->focus_item + (page * descr->page_size) - 1; 2416 } 2417 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1; 2418 if (caret >= descr->nb_items) caret = descr->nb_items - 1; 2419 break; 2420 case VK_HOME: 2421 caret = 0; 2422 break; 2423 case VK_END: 2424 caret = descr->nb_items - 1; 2425 break; 2426 case VK_SPACE: 2427 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item; 2428 else if (descr->style & LBS_MULTIPLESEL) 2429 { 2430 LISTBOX_SetSelection( descr, descr->focus_item, 2431 !descr->items[descr->focus_item].selected, 2432 (descr->style & LBS_NOTIFY) != 0 ); 2433 } 2434 break; 2435 default: 2436 bForceSelection = FALSE; 2437 } 2438 if (bForceSelection) /* focused item is used instead of key */ 2439 caret = descr->focus_item; 2440 if (caret >= 0) 2441 { 2442 if (((descr->style & LBS_EXTENDEDSEL) && 2443 !(GetKeyState( VK_SHIFT ) & 0x8000)) || 2444 !IS_MULTISELECT(descr)) 2445 descr->anchor_item = caret; 2446 LISTBOX_MoveCaret( descr, caret, TRUE ); 2447 2448 if (descr->style & LBS_MULTIPLESEL) 2449 descr->selected_item = caret; 2450 else 2451 LISTBOX_SetSelection( descr, caret, TRUE, FALSE); 2452 if (descr->style & LBS_NOTIFY) 2453 { 2454 if (descr->lphc && IsWindowVisible( descr->self )) 2455 { 2456 /* make sure that combo parent doesn't hide us */ 2457 descr->lphc->wState |= CBF_NOROLLUP; 2458 } 2459 if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE ); 2460 } 2461 } 2462 return 0; 2463 } 2464 2465 2466 /*********************************************************************** 2467 * LISTBOX_HandleChar 2468 */ 2469 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW ) 2470 { 2471 INT caret = -1; 2472 WCHAR str[2]; 2473 2474 str[0] = charW; 2475 str[1] = '\0'; 2476 2477 if (descr->style & LBS_WANTKEYBOARDINPUT) 2478 { 2479 caret = SendMessageW( descr->owner, WM_CHARTOITEM, 2480 MAKEWPARAM(charW, descr->focus_item), 2481 (LPARAM)descr->self ); 2482 if (caret == -2) return 0; 2483 } 2484 if (caret == -1) 2485 caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE); 2486 if (caret != -1) 2487 { 2488 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1) 2489 LISTBOX_SetSelection( descr, caret, TRUE, FALSE); 2490 LISTBOX_MoveCaret( descr, caret, TRUE ); 2491 if ((descr->style & LBS_NOTIFY) && descr->nb_items) 2492 SEND_NOTIFICATION( descr, LBN_SELCHANGE ); 2493 } 2494 return 0; 2495 } 2496 2497 /* ReactOS Retrieve the UI state for the control */ 2498 static BOOL LISTBOX_update_uistate(LB_DESCR *descr) 2499 { 2500 LONG prev_flags; 2501 2502 prev_flags = descr->UIState; 2503 descr->UIState = DefWindowProcW(descr->self, WM_QUERYUISTATE, 0, 0); 2504 return prev_flags != descr->UIState; 2505 } 2506 2507 2508 /*********************************************************************** 2509 * LISTBOX_Create 2510 */ 2511 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc ) 2512 { 2513 LB_DESCR *descr; 2514 MEASUREITEMSTRUCT mis; 2515 RECT rect; 2516 2517 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) ))) 2518 return FALSE; 2519 2520 GetClientRect( hwnd, &rect ); 2521 descr->self = hwnd; 2522 descr->owner = GetParent( descr->self ); 2523 descr->style = GetWindowLongPtrW( descr->self, GWL_STYLE ); 2524 descr->width = rect.right - rect.left; 2525 descr->height = rect.bottom - rect.top; 2526 descr->items = NULL; 2527 descr->nb_items = 0; 2528 descr->top_item = 0; 2529 descr->selected_item = -1; 2530 descr->focus_item = 0; 2531 descr->anchor_item = -1; 2532 descr->item_height = 1; 2533 descr->page_size = 1; 2534 descr->column_width = 150; 2535 descr->horz_extent = 0; 2536 descr->horz_pos = 0; 2537 descr->nb_tabs = 0; 2538 descr->tabs = NULL; 2539 descr->wheel_remain = 0; 2540 descr->caret_on = !lphc; 2541 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE; 2542 descr->in_focus = FALSE; 2543 descr->captured = FALSE; 2544 descr->font = 0; 2545 descr->locale = GetUserDefaultLCID(); 2546 descr->lphc = lphc; 2547 2548 if( lphc ) 2549 { 2550 TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self ); 2551 descr->owner = lphc->self; 2552 } 2553 2554 SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr ); 2555 2556 LISTBOX_update_uistate(descr); // ReactOS 2557 2558 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY; 2559 */ 2560 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL; 2561 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE; 2562 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT; 2563 2564 //// ReactOS 2565 /* A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must 2566 not have the LBS_SORT or LBS_HASSTRINGS style. */ 2567 if ( descr->style & LBS_NODATA && 2568 (!(descr->style & LBS_OWNERDRAWFIXED) || descr->style & (LBS_HASSTRINGS|LBS_SORT) ) ) 2569 descr->style &= ~LBS_NODATA; 2570 //// 2571 descr->item_height = LISTBOX_SetFont( descr, 0 ); 2572 2573 if (descr->style & LBS_OWNERDRAWFIXED) 2574 { 2575 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN)) 2576 { 2577 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */ 2578 descr->item_height = lphc->fixedOwnerDrawHeight; 2579 } 2580 else 2581 { 2582 UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); 2583 mis.CtlType = ODT_LISTBOX; 2584 mis.CtlID = id; 2585 mis.itemID = -1; 2586 mis.itemWidth = 0; 2587 mis.itemData = 0; 2588 mis.itemHeight = descr->item_height; 2589 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); 2590 descr->item_height = mis.itemHeight ? mis.itemHeight : 1; 2591 } 2592 } 2593 2594 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height); 2595 return TRUE; 2596 } 2597 2598 2599 /*********************************************************************** 2600 * LISTBOX_Destroy 2601 */ 2602 static BOOL LISTBOX_Destroy( LB_DESCR *descr ) 2603 { 2604 LISTBOX_ResetContent( descr ); 2605 SetWindowLongPtrW( descr->self, 0, 0 ); 2606 HeapFree( GetProcessHeap(), 0, descr ); 2607 return TRUE; 2608 } 2609 2610 2611 /*********************************************************************** 2612 * ListBoxWndProc_common 2613 */ 2614 LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg, 2615 WPARAM wParam, LPARAM lParam, BOOL unicode ) 2616 { 2617 LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 ); 2618 LPHEADCOMBO lphc = 0; 2619 LRESULT ret; 2620 #ifdef __REACTOS__ 2621 PWND pWnd; 2622 2623 pWnd = ValidateHwnd(hwnd); 2624 if (pWnd) 2625 { 2626 if (!pWnd->fnid) 2627 { 2628 NtUserSetWindowFNID(hwnd, FNID_LISTBOX); // Could be FNID_COMBOLBOX by class. 2629 } 2630 else 2631 { 2632 if (pWnd->fnid != FNID_LISTBOX) 2633 { 2634 ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd->fnid); 2635 return 0; 2636 } 2637 } 2638 } 2639 #endif 2640 2641 if (!descr) 2642 { 2643 if (!IsWindow(hwnd)) return 0; 2644 2645 if (msg == WM_CREATE) 2646 { 2647 CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam; 2648 if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams; 2649 if (!LISTBOX_Create( hwnd, lphc )) return -1; 2650 TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) ); 2651 return 0; 2652 } 2653 /* Ignore all other messages before we get a WM_CREATE */ 2654 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) : 2655 DefWindowProcA( hwnd, msg, wParam, lParam ); 2656 } 2657 if (descr->style & LBS_COMBOBOX) lphc = descr->lphc; 2658 2659 TRACE("[%p]: msg %s wp %08lx lp %08lx\n", 2660 descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam ); 2661 2662 switch(msg) 2663 { 2664 case LB_RESETCONTENT: 2665 LISTBOX_ResetContent( descr ); 2666 LISTBOX_UpdateScroll( descr ); 2667 InvalidateRect( descr->self, NULL, TRUE ); 2668 return 0; 2669 2670 case LB_ADDSTRING: 2671 #ifdef __REACTOS__ 2672 case LB_ADDSTRING_LOWER: 2673 case LB_ADDSTRING_UPPER: 2674 #endif 2675 { 2676 INT ret; 2677 LPWSTR textW; 2678 if(unicode || !HAS_STRINGS(descr)) 2679 textW = (LPWSTR)lParam; 2680 else 2681 { 2682 LPSTR textA = (LPSTR)lParam; 2683 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 2684 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 2685 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 2686 else 2687 return LB_ERRSPACE; 2688 } 2689 #ifdef __REACTOS__ 2690 /* in the unicode the version, the string is really overwritten 2691 during the converting case */ 2692 if (msg == LB_ADDSTRING_LOWER) 2693 strlwrW(textW); 2694 else if (msg == LB_ADDSTRING_UPPER) 2695 struprW(textW); 2696 #endif 2697 wParam = LISTBOX_FindStringPos( descr, textW, FALSE ); 2698 ret = LISTBOX_InsertString( descr, wParam, textW ); 2699 if (!unicode && HAS_STRINGS(descr)) 2700 HeapFree(GetProcessHeap(), 0, textW); 2701 return ret; 2702 } 2703 2704 case LB_INSERTSTRING: 2705 #ifdef __REACTOS__ 2706 case LB_INSERTSTRING_UPPER: 2707 case LB_INSERTSTRING_LOWER: 2708 #endif 2709 { 2710 INT ret; 2711 LPWSTR textW; 2712 if(unicode || !HAS_STRINGS(descr)) 2713 textW = (LPWSTR)lParam; 2714 else 2715 { 2716 LPSTR textA = (LPSTR)lParam; 2717 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 2718 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 2719 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 2720 else 2721 return LB_ERRSPACE; 2722 } 2723 #ifdef __REACTOS__ 2724 /* in the unicode the version, the string is really overwritten 2725 during the converting case */ 2726 if (msg == LB_INSERTSTRING_LOWER) 2727 strlwrW(textW); 2728 else if (msg == LB_INSERTSTRING_UPPER) 2729 struprW(textW); 2730 #endif 2731 ret = LISTBOX_InsertString( descr, wParam, textW ); 2732 if(!unicode && HAS_STRINGS(descr)) 2733 HeapFree(GetProcessHeap(), 0, textW); 2734 return ret; 2735 } 2736 2737 case LB_ADDFILE: 2738 { 2739 INT ret; 2740 LPWSTR textW; 2741 if(unicode || !HAS_STRINGS(descr)) 2742 textW = (LPWSTR)lParam; 2743 else 2744 { 2745 LPSTR textA = (LPSTR)lParam; 2746 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 2747 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 2748 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 2749 else 2750 return LB_ERRSPACE; 2751 } 2752 wParam = LISTBOX_FindFileStrPos( descr, textW ); 2753 ret = LISTBOX_InsertString( descr, wParam, textW ); 2754 if(!unicode && HAS_STRINGS(descr)) 2755 HeapFree(GetProcessHeap(), 0, textW); 2756 return ret; 2757 } 2758 2759 case LB_DELETESTRING: 2760 if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR) 2761 return descr->nb_items; 2762 else 2763 { 2764 SetLastError(ERROR_INVALID_INDEX); 2765 return LB_ERR; 2766 } 2767 2768 case LB_GETITEMDATA: 2769 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) 2770 { 2771 SetLastError(ERROR_INVALID_INDEX); 2772 return LB_ERR; 2773 } 2774 return descr->items[wParam].data; 2775 2776 case LB_SETITEMDATA: 2777 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) 2778 { 2779 SetLastError(ERROR_INVALID_INDEX); 2780 return LB_ERR; 2781 } 2782 descr->items[wParam].data = lParam; 2783 /* undocumented: returns TRUE, not LB_OKAY (0) */ 2784 return TRUE; 2785 2786 case LB_GETCOUNT: 2787 return descr->nb_items; 2788 2789 case LB_GETTEXT: 2790 return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode ); 2791 2792 case LB_GETTEXTLEN: 2793 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0) 2794 { 2795 SetLastError(ERROR_INVALID_INDEX); 2796 return LB_ERR; 2797 } 2798 if (!HAS_STRINGS(descr)) return sizeof(DWORD); 2799 if (unicode) return strlenW( descr->items[wParam].str ); 2800 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str, 2801 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL ); 2802 2803 case LB_GETCURSEL: 2804 if (descr->nb_items == 0) 2805 return LB_ERR; 2806 if (!IS_MULTISELECT(descr)) 2807 return descr->selected_item; 2808 if (descr->selected_item != -1) 2809 return descr->selected_item; 2810 return descr->focus_item; 2811 /* otherwise, if the user tries to move the selection with the */ 2812 /* arrow keys, we will give the application something to choke on */ 2813 case LB_GETTOPINDEX: 2814 return descr->top_item; 2815 2816 case LB_GETITEMHEIGHT: 2817 return LISTBOX_GetItemHeight( descr, wParam ); 2818 2819 case LB_SETITEMHEIGHT: 2820 return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE ); 2821 2822 case LB_ITEMFROMPOINT: 2823 { 2824 POINT pt; 2825 RECT rect; 2826 int index; 2827 BOOL hit = TRUE; 2828 2829 /* The hiword of the return value is not a client area 2830 hittest as suggested by MSDN, but rather a hittest on 2831 the returned listbox item. */ 2832 2833 if(descr->nb_items == 0) 2834 return 0x1ffff; /* win9x returns 0x10000, we copy winnt */ 2835 2836 pt.x = (short)LOWORD(lParam); 2837 pt.y = (short)HIWORD(lParam); 2838 2839 SetRect(&rect, 0, 0, descr->width, descr->height); 2840 2841 if(!PtInRect(&rect, pt)) 2842 { 2843 pt.x = min(pt.x, rect.right - 1); 2844 pt.x = max(pt.x, 0); 2845 pt.y = min(pt.y, rect.bottom - 1); 2846 pt.y = max(pt.y, 0); 2847 hit = FALSE; 2848 } 2849 2850 index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y); 2851 2852 if(index == -1) 2853 { 2854 index = descr->nb_items - 1; 2855 hit = FALSE; 2856 } 2857 return MAKELONG(index, hit ? 0 : 1); 2858 } 2859 2860 case LB_SETCARETINDEX: 2861 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR; 2862 if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR) 2863 return LB_ERR; 2864 else if (ISWIN31) 2865 return wParam; 2866 else 2867 return LB_OKAY; 2868 2869 case LB_GETCARETINDEX: 2870 return descr->focus_item; 2871 2872 case LB_SETTOPINDEX: 2873 return LISTBOX_SetTopItem( descr, wParam, TRUE ); 2874 2875 case LB_SETCOLUMNWIDTH: 2876 return LISTBOX_SetColumnWidth( descr, wParam ); 2877 2878 case LB_GETITEMRECT: 2879 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam ); 2880 2881 case LB_FINDSTRING: 2882 { 2883 INT ret; 2884 LPWSTR textW; 2885 if(unicode || !HAS_STRINGS(descr)) 2886 textW = (LPWSTR)lParam; 2887 else 2888 { 2889 LPSTR textA = (LPSTR)lParam; 2890 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 2891 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 2892 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 2893 } 2894 ret = LISTBOX_FindString( descr, wParam, textW, FALSE ); 2895 if(!unicode && HAS_STRINGS(descr)) 2896 HeapFree(GetProcessHeap(), 0, textW); 2897 return ret; 2898 } 2899 2900 case LB_FINDSTRINGEXACT: 2901 { 2902 INT ret; 2903 LPWSTR textW; 2904 if(unicode || !HAS_STRINGS(descr)) 2905 textW = (LPWSTR)lParam; 2906 else 2907 { 2908 LPSTR textA = (LPSTR)lParam; 2909 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 2910 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 2911 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 2912 } 2913 ret = LISTBOX_FindString( descr, wParam, textW, TRUE ); 2914 if(!unicode && HAS_STRINGS(descr)) 2915 HeapFree(GetProcessHeap(), 0, textW); 2916 return ret; 2917 } 2918 2919 case LB_SELECTSTRING: 2920 { 2921 INT index; 2922 LPWSTR textW; 2923 2924 if(HAS_STRINGS(descr)) 2925 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) : 2926 debugstr_a((LPSTR)lParam)); 2927 if(unicode || !HAS_STRINGS(descr)) 2928 textW = (LPWSTR)lParam; 2929 else 2930 { 2931 LPSTR textA = (LPSTR)lParam; 2932 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 2933 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 2934 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 2935 } 2936 index = LISTBOX_FindString( descr, wParam, textW, FALSE ); 2937 if(!unicode && HAS_STRINGS(descr)) 2938 HeapFree(GetProcessHeap(), 0, textW); 2939 if (index != LB_ERR) 2940 { 2941 LISTBOX_MoveCaret( descr, index, TRUE ); 2942 LISTBOX_SetSelection( descr, index, TRUE, FALSE ); 2943 } 2944 return index; 2945 } 2946 2947 case LB_GETSEL: 2948 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) 2949 return LB_ERR; 2950 return descr->items[wParam].selected; 2951 2952 case LB_SETSEL: 2953 return LISTBOX_SetSelection( descr, lParam, wParam, FALSE ); 2954 2955 case LB_SETCURSEL: 2956 if (IS_MULTISELECT(descr)) return LB_ERR; 2957 LISTBOX_SetCaretIndex( descr, wParam, TRUE ); 2958 ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE ); 2959 if (ret != LB_ERR) ret = descr->selected_item; 2960 return ret; 2961 2962 case LB_GETSELCOUNT: 2963 return LISTBOX_GetSelCount( descr ); 2964 2965 case LB_GETSELITEMS: 2966 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam ); 2967 2968 case LB_SELITEMRANGE: 2969 if (LOWORD(lParam) <= HIWORD(lParam)) 2970 return LISTBOX_SelectItemRange( descr, LOWORD(lParam), 2971 HIWORD(lParam), wParam ); 2972 else 2973 return LISTBOX_SelectItemRange( descr, HIWORD(lParam), 2974 LOWORD(lParam), wParam ); 2975 2976 case LB_SELITEMRANGEEX: 2977 if ((INT)lParam >= (INT)wParam) 2978 return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE ); 2979 else 2980 return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE); 2981 2982 case LB_GETHORIZONTALEXTENT: 2983 return descr->horz_extent; 2984 2985 case LB_SETHORIZONTALEXTENT: 2986 return LISTBOX_SetHorizontalExtent( descr, wParam ); 2987 2988 case LB_GETANCHORINDEX: 2989 return descr->anchor_item; 2990 2991 case LB_SETANCHORINDEX: 2992 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items)) 2993 { 2994 SetLastError(ERROR_INVALID_INDEX); 2995 return LB_ERR; 2996 } 2997 descr->anchor_item = (INT)wParam; 2998 return LB_OKAY; 2999 3000 case LB_DIR: 3001 { 3002 INT ret; 3003 LPWSTR textW; 3004 if(unicode) 3005 textW = (LPWSTR)lParam; 3006 else 3007 { 3008 LPSTR textA = (LPSTR)lParam; 3009 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0); 3010 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) 3011 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW); 3012 } 3013 ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR ); 3014 if(!unicode) 3015 HeapFree(GetProcessHeap(), 0, textW); 3016 return ret; 3017 } 3018 3019 case LB_GETLOCALE: 3020 return descr->locale; 3021 3022 case LB_SETLOCALE: 3023 { 3024 LCID ret; 3025 if (!IsValidLocale((LCID)wParam, LCID_INSTALLED)) 3026 return LB_ERR; 3027 ret = descr->locale; 3028 descr->locale = (LCID)wParam; 3029 return ret; 3030 } 3031 3032 case LB_INITSTORAGE: 3033 return LISTBOX_InitStorage( descr, wParam ); 3034 3035 case LB_SETCOUNT: 3036 return LISTBOX_SetCount( descr, (INT)wParam ); 3037 3038 case LB_SETTABSTOPS: 3039 return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam ); 3040 3041 case LB_CARETON: 3042 if (descr->caret_on) 3043 return LB_OKAY; 3044 descr->caret_on = TRUE; 3045 if ((descr->focus_item != -1) && (descr->in_focus)) 3046 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); 3047 return LB_OKAY; 3048 3049 case LB_CARETOFF: 3050 if (!descr->caret_on) 3051 return LB_OKAY; 3052 descr->caret_on = FALSE; 3053 if ((descr->focus_item != -1) && (descr->in_focus)) 3054 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); 3055 return LB_OKAY; 3056 3057 case LB_GETLISTBOXINFO: 3058 return descr->page_size; 3059 3060 case WM_DESTROY: 3061 return LISTBOX_Destroy( descr ); 3062 3063 case WM_ENABLE: 3064 InvalidateRect( descr->self, NULL, TRUE ); 3065 return 0; 3066 3067 case WM_SETREDRAW: 3068 LISTBOX_SetRedraw( descr, wParam != 0 ); 3069 return 0; 3070 3071 case WM_GETDLGCODE: 3072 return DLGC_WANTARROWS | DLGC_WANTCHARS; 3073 3074 case WM_PRINTCLIENT: 3075 case WM_PAINT: 3076 { 3077 PAINTSTRUCT ps; 3078 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps ); 3079 ret = LISTBOX_Paint( descr, hdc ); 3080 if( !wParam ) EndPaint( descr->self, &ps ); 3081 } 3082 return ret; 3083 case WM_SIZE: 3084 LISTBOX_UpdateSize( descr ); 3085 return 0; 3086 case WM_GETFONT: 3087 return (LRESULT)descr->font; 3088 case WM_SETFONT: 3089 LISTBOX_SetFont( descr, (HFONT)wParam ); 3090 if (lParam) InvalidateRect( descr->self, 0, TRUE ); 3091 return 0; 3092 case WM_SETFOCUS: 3093 descr->in_focus = TRUE; 3094 descr->caret_on = TRUE; 3095 if (descr->focus_item != -1) 3096 LISTBOX_DrawFocusRect( descr, TRUE ); 3097 SEND_NOTIFICATION( descr, LBN_SETFOCUS ); 3098 return 0; 3099 case WM_KILLFOCUS: 3100 LISTBOX_HandleLButtonUp( descr ); /* Release capture if we have it */ 3101 descr->in_focus = FALSE; 3102 descr->wheel_remain = 0; 3103 if ((descr->focus_item != -1) && descr->caret_on) 3104 LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); 3105 SEND_NOTIFICATION( descr, LBN_KILLFOCUS ); 3106 return 0; 3107 case WM_HSCROLL: 3108 return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) ); 3109 case WM_VSCROLL: 3110 return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) ); 3111 case WM_MOUSEWHEEL: 3112 if (wParam & (MK_SHIFT | MK_CONTROL)) 3113 return DefWindowProcW( descr->self, msg, wParam, lParam ); 3114 return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) ); 3115 case WM_LBUTTONDOWN: 3116 if (lphc) 3117 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam, 3118 (INT16)LOWORD(lParam), 3119 (INT16)HIWORD(lParam) ); 3120 return LISTBOX_HandleLButtonDown( descr, wParam, 3121 (INT16)LOWORD(lParam), 3122 (INT16)HIWORD(lParam) ); 3123 case WM_LBUTTONDBLCLK: 3124 if (lphc) 3125 return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam, 3126 (INT16)LOWORD(lParam), 3127 (INT16)HIWORD(lParam) ); 3128 if (descr->style & LBS_NOTIFY) 3129 SEND_NOTIFICATION( descr, LBN_DBLCLK ); 3130 return 0; 3131 case WM_MOUSEMOVE: 3132 if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) ) 3133 { 3134 BOOL captured = descr->captured; 3135 POINT mousePos; 3136 RECT clientRect; 3137 3138 mousePos.x = (INT16)LOWORD(lParam); 3139 mousePos.y = (INT16)HIWORD(lParam); 3140 3141 /* 3142 * If we are in a dropdown combobox, we simulate that 3143 * the mouse is captured to show the tracking of the item. 3144 */ 3145 if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos )) 3146 descr->captured = TRUE; 3147 3148 LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y); 3149 3150 descr->captured = captured; 3151 } 3152 else if (GetCapture() == descr->self) 3153 { 3154 LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam), 3155 (INT16)HIWORD(lParam) ); 3156 } 3157 return 0; 3158 case WM_LBUTTONUP: 3159 if (lphc) 3160 { 3161 POINT mousePos; 3162 RECT clientRect; 3163 3164 /* 3165 * If the mouse button "up" is not in the listbox, 3166 * we make sure there is no selection by re-selecting the 3167 * item that was selected when the listbox was made visible. 3168 */ 3169 mousePos.x = (INT16)LOWORD(lParam); 3170 mousePos.y = (INT16)HIWORD(lParam); 3171 3172 GetClientRect(descr->self, &clientRect); 3173 3174 /* 3175 * When the user clicks outside the combobox and the focus 3176 * is lost, the owning combobox will send a fake buttonup with 3177 * 0xFFFFFFF as the mouse location, we must also revert the 3178 * selection to the original selection. 3179 */ 3180 if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) ) 3181 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE ); 3182 } 3183 return LISTBOX_HandleLButtonUp( descr ); 3184 case WM_KEYDOWN: 3185 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE ) 3186 { 3187 /* for some reason Windows makes it possible to 3188 * show/hide ComboLBox by sending it WM_KEYDOWNs */ 3189 3190 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) || 3191 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED) 3192 && (wParam == VK_DOWN || wParam == VK_UP)) ) 3193 { 3194 COMBO_FlipListbox( lphc, FALSE, FALSE ); 3195 return 0; 3196 } 3197 } 3198 return LISTBOX_HandleKeyDown( descr, wParam ); 3199 case WM_CHAR: 3200 { 3201 WCHAR charW; 3202 if(unicode) 3203 charW = (WCHAR)wParam; 3204 else 3205 { 3206 CHAR charA = (CHAR)wParam; 3207 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1); 3208 } 3209 return LISTBOX_HandleChar( descr, charW ); 3210 } 3211 case WM_SYSTIMER: 3212 return LISTBOX_HandleSystemTimer( descr ); 3213 case WM_ERASEBKGND: 3214 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED)) 3215 { 3216 RECT rect; 3217 #ifdef __REACTOS__ 3218 HBRUSH hbrush = GetControlColor( descr->owner, descr->self, (HDC)wParam, WM_CTLCOLORLISTBOX); 3219 #else 3220 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX, 3221 wParam, (LPARAM)descr->self ); 3222 #endif 3223 TRACE("hbrush = %p\n", hbrush); 3224 if(!hbrush) 3225 hbrush = GetSysColorBrush(COLOR_WINDOW); 3226 if(hbrush) 3227 { 3228 GetClientRect(descr->self, &rect); 3229 FillRect((HDC)wParam, &rect, hbrush); 3230 } 3231 } 3232 return 1; 3233 case WM_DROPFILES: 3234 if( lphc ) return 0; 3235 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) : 3236 SendMessageA( descr->owner, msg, wParam, lParam ); 3237 3238 case WM_NCDESTROY: 3239 if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE ) 3240 lphc->hWndLBox = 0; 3241 #ifdef __REACTOS__ 3242 NtUserSetWindowFNID(hwnd, FNID_DESTROY); 3243 #endif 3244 break; 3245 3246 case WM_NCACTIVATE: 3247 if (lphc) return 0; 3248 break; 3249 // ReactOS 3250 case WM_UPDATEUISTATE: 3251 if (unicode) 3252 DefWindowProcW(descr->self, msg, wParam, lParam); 3253 else 3254 DefWindowProcA(descr->self, msg, wParam, lParam); 3255 3256 if (LISTBOX_update_uistate(descr)) 3257 { 3258 /* redraw text */ 3259 if (descr->focus_item != -1) 3260 LISTBOX_DrawFocusRect( descr, descr->in_focus ); 3261 } 3262 break; 3263 // 3264 default: 3265 if ((msg >= WM_USER) && (msg < 0xc000)) 3266 WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n", 3267 hwnd, msg, wParam, lParam ); 3268 } 3269 3270 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) : 3271 DefWindowProcA( hwnd, msg, wParam, lParam ); 3272 } 3273 3274 /*********************************************************************** 3275 * ListBoxWndProcA 3276 */ 3277 LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) 3278 { 3279 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE ); 3280 } 3281 3282 /*********************************************************************** 3283 * ListBoxWndProcW 3284 */ 3285 LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) 3286 { 3287 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE ); 3288 } 3289