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