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