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