4 * Copyright 1996 Alexandre Julliard
9 #include "wine/winuser16.h"
27 /* Items array granularity */
28 #define LB_ARRAY_GRANULARITY 16
30 /* Scrolling timeout in ms */
31 #define LB_SCROLL_TIMEOUT 50
33 /* Listbox system timer id */
39 LPSTR str; /* Item text */
40 BOOL selected; /* Is item selected? */
41 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
42 DWORD data; /* User data */
45 /* Listbox structure */
48 HANDLE heap; /* Heap for this listbox */
49 HWND owner; /* Owner window to send notifications to */
50 UINT style; /* Window style */
51 INT width; /* Window width */
52 INT height; /* Window height */
53 LB_ITEMDATA *items; /* Array of items */
54 INT nb_items; /* Number of items */
55 INT top_item; /* Top visible item */
56 INT selected_item; /* Selected item */
57 INT focus_item; /* Item that has the focus */
58 INT anchor_item; /* Anchor item for extended selection */
59 INT item_height; /* Default item height */
60 INT page_size; /* Items per listbox page */
61 INT column_width; /* Column width for multi-column listboxes */
62 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
63 INT horz_pos; /* Horizontal position */
64 INT nb_tabs; /* Number of tabs in array */
65 INT *tabs; /* Array of tabs */
66 BOOL caret_on; /* Is caret on? */
67 BOOL captured; /* Is mouse captured? */
68 HFONT font; /* Current font */
69 LCID locale; /* Current locale for string comparisons */
70 LPHEADCOMBO lphc; /* ComboLBox */
74 #define IS_OWNERDRAW(descr) \
75 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
77 #define HAS_STRINGS(descr) \
78 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
80 #define SEND_NOTIFICATION(wnd,descr,code) \
81 (SendMessageA( (descr)->owner, WM_COMMAND, \
82 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
84 /* Current timer status */
94 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
97 /***********************************************************************
100 void LISTBOX_Dump( WND *wnd )
104 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
106 DUMP( "Listbox:\n" );
107 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
108 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
110 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
112 DUMP( "%4d: %-40s %d %08lx %3d\n",
113 i, item->str, item->selected, item->data, item->height );
118 /***********************************************************************
119 * LISTBOX_GetCurrentPageSize
121 * Return the current page size
123 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
126 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
127 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
129 if ((height += descr->items[i].height) > descr->height) break;
131 if (i == descr->top_item) return 1;
132 else return i - descr->top_item;
136 /***********************************************************************
137 * LISTBOX_GetMaxTopIndex
139 * Return the maximum possible index for the top of the listbox.
141 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
145 if (descr->style & LBS_OWNERDRAWVARIABLE)
147 page = descr->height;
148 for (max = descr->nb_items - 1; max >= 0; max--)
149 if ((page -= descr->items[max].height) < 0) break;
150 if (max < descr->nb_items - 1) max++;
152 else if (descr->style & LBS_MULTICOLUMN)
154 if ((page = descr->width / descr->column_width) < 1) page = 1;
155 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
156 max = (max - page) * descr->page_size;
160 max = descr->nb_items - descr->page_size;
162 if (max < 0) max = 0;
167 /***********************************************************************
168 * LISTBOX_UpdateScroll
170 * Update the scrollbars. Should be called whenever the content
171 * of the listbox changes.
173 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
177 if (!(descr->style & WS_VSCROLL)) return;
178 /* It is important that we check descr->style, and not wnd->dwStyle,
179 for WS_VSCROLL, as the former is exactly the one passed in
180 argument to CreateWindow.
181 In Windows (and from now on in Wine :) a listbox created
182 with such a style (no WS_SCROLL) does not update
183 the scrollbar with listbox-related data, thus letting
184 the programmer use it for his/her own purposes. */
186 if (descr->style & LBS_NOREDRAW) return;
187 info.cbSize = sizeof(info);
189 if (descr->style & LBS_MULTICOLUMN)
192 info.nMax = (descr->nb_items - 1) / descr->page_size;
193 info.nPos = descr->top_item / descr->page_size;
194 info.nPage = descr->width / descr->column_width;
195 if (info.nPage < 1) info.nPage = 1;
196 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
197 if (descr->style & LBS_DISABLENOSCROLL)
198 info.fMask |= SIF_DISABLENOSCROLL;
199 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
201 info.fMask = SIF_RANGE;
202 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
207 info.nMax = descr->nb_items - 1;
208 info.nPos = descr->top_item;
209 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
210 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
211 if (descr->style & LBS_DISABLENOSCROLL)
212 info.fMask |= SIF_DISABLENOSCROLL;
213 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
215 if (descr->horz_extent)
218 info.nMax = descr->horz_extent - 1;
219 info.nPos = descr->horz_pos;
220 info.nPage = descr->width;
221 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
222 if (descr->style & LBS_DISABLENOSCROLL)
223 info.fMask |= SIF_DISABLENOSCROLL;
224 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
230 /***********************************************************************
233 * Set the top item of the listbox, scrolling up or down if necessary.
235 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
238 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
239 if (index > max) index = max;
240 if (index < 0) index = 0;
241 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
242 if (descr->top_item == index) return LB_OKAY;
243 if (descr->style & LBS_MULTICOLUMN)
245 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
246 if (scroll && (abs(diff) < descr->width))
247 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
248 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
256 if (descr->style & LBS_OWNERDRAWVARIABLE)
260 if (index > descr->top_item)
262 for (i = index - 1; i >= descr->top_item; i--)
263 diff -= descr->items[i].height;
267 for (i = index; i < descr->top_item; i++)
268 diff += descr->items[i].height;
272 diff = (descr->top_item - index) * descr->item_height;
274 if (abs(diff) < descr->height)
275 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
276 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
280 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
281 descr->top_item = index;
282 LISTBOX_UpdateScroll( wnd, descr );
287 /***********************************************************************
290 * Update the page size. Should be called when the size of
291 * the client area or the item height changes.
293 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
297 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
298 if (page_size == descr->page_size) return;
299 descr->page_size = page_size;
300 if (descr->style & LBS_MULTICOLUMN)
301 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
302 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
306 /***********************************************************************
309 * Update the size of the listbox. Should be called when the size of
310 * the client area changes.
312 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
316 GetClientRect( wnd->hwndSelf, &rect );
317 descr->width = rect.right - rect.left;
318 descr->height = rect.bottom - rect.top;
319 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
321 if ((descr->height > descr->item_height) &&
322 (descr->height % descr->item_height))
324 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
325 wnd->hwndSelf, descr->height,
326 descr->height - descr->height%descr->item_height );
327 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
328 wnd->rectWindow.right - wnd->rectWindow.left,
329 wnd->rectWindow.bottom - wnd->rectWindow.top -
330 (descr->height % descr->item_height),
331 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
335 TRACE(listbox, "[%04x]: new size = %d,%d\n",
336 wnd->hwndSelf, descr->width, descr->height );
337 LISTBOX_UpdatePage( wnd, descr );
338 LISTBOX_UpdateScroll( wnd, descr );
342 /***********************************************************************
343 * LISTBOX_GetItemRect
345 * Get the rectangle enclosing an item, in listbox client coordinates.
346 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
348 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
351 /* Index <= 0 is legal even on empty listboxes */
352 if (index && (index >= descr->nb_items)) return -1;
353 SetRect( rect, 0, 0, descr->width, descr->height );
354 if (descr->style & LBS_MULTICOLUMN)
356 INT col = (index / descr->page_size) -
357 (descr->top_item / descr->page_size);
358 rect->left += col * descr->column_width;
359 rect->right = rect->left + descr->column_width;
360 rect->top += (index % descr->page_size) * descr->item_height;
361 rect->bottom = rect->top + descr->item_height;
363 else if (descr->style & LBS_OWNERDRAWVARIABLE)
366 rect->right += descr->horz_pos;
367 if ((index >= 0) && (index < descr->nb_items))
369 if (index < descr->top_item)
371 for (i = descr->top_item-1; i >= index; i--)
372 rect->top -= descr->items[i].height;
376 for (i = descr->top_item; i < index; i++)
377 rect->top += descr->items[i].height;
379 rect->bottom = rect->top + descr->items[index].height;
385 rect->top += (index - descr->top_item) * descr->item_height;
386 rect->bottom = rect->top + descr->item_height;
387 rect->right += descr->horz_pos;
390 return ((rect->left < descr->width) && (rect->right > 0) &&
391 (rect->top < descr->height) && (rect->bottom > 0));
395 /***********************************************************************
396 * LISTBOX_GetItemFromPoint
398 * Return the item nearest from point (x,y) (in client coordinates).
400 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
403 INT index = descr->top_item;
405 if (!descr->nb_items) return -1; /* No items */
406 if (descr->style & LBS_OWNERDRAWVARIABLE)
411 while (index < descr->nb_items)
413 if ((pos += descr->items[index].height) > y) break;
422 if ((pos -= descr->items[index].height) <= y) break;
426 else if (descr->style & LBS_MULTICOLUMN)
428 if (y >= descr->item_height * descr->page_size) return -1;
429 if (y >= 0) index += y / descr->item_height;
430 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
431 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
435 index += (y / descr->item_height);
437 if (index < 0) return 0;
438 if (index >= descr->nb_items) return -1;
443 /***********************************************************************
448 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
449 const RECT *rect, INT index, UINT action )
451 LB_ITEMDATA *item = NULL;
452 if (index < descr->nb_items) item = &descr->items[index];
454 if (IS_OWNERDRAW(descr))
457 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
461 if (action == ODA_FOCUS)
462 DrawFocusRect( hdc, rect );
464 FIXME(listbox,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
467 dis.CtlType = ODT_LISTBOX;
469 dis.hwndItem = wnd->hwndSelf;
470 dis.itemAction = action;
474 if (item && item->selected) dis.itemState |= ODS_SELECTED;
475 if ((descr->focus_item == index) &&
477 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
478 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
479 dis.itemData = item ? item->data : 0;
481 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
482 "state=%02x rect=%d,%d-%d,%d\n",
483 wnd->hwndSelf, index, item ? item->str : "", action,
484 dis.itemState, rect->left, rect->top,
485 rect->right, rect->bottom );
486 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
490 COLORREF oldText = 0, oldBk = 0;
492 if (action == ODA_FOCUS)
494 DrawFocusRect( hdc, rect );
497 if (item && item->selected)
499 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
500 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
503 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
504 "rect=%d,%d-%d,%d\n",
505 wnd->hwndSelf, index, item ? item->str : "", action,
506 rect->left, rect->top, rect->right, rect->bottom );
508 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
509 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
510 else if (!(descr->style & LBS_USETABSTOPS))
511 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
512 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
513 strlen(item->str), NULL );
516 /* Output empty string to paint background in the full width. */
517 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
518 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
519 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
520 item->str, strlen(item->str),
521 descr->nb_tabs, descr->tabs, 0);
523 if (item && item->selected)
525 SetBkColor( hdc, oldBk );
526 SetTextColor( hdc, oldText );
528 if ((descr->focus_item == index) &&
530 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
535 /***********************************************************************
538 * Change the redraw flag.
540 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
544 if (!(descr->style & LBS_NOREDRAW)) return;
545 descr->style &= ~LBS_NOREDRAW;
546 LISTBOX_UpdateScroll( wnd, descr );
548 else descr->style |= LBS_NOREDRAW;
552 /***********************************************************************
553 * LISTBOX_RepaintItem
555 * Repaint a single item synchronously.
557 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
563 HBRUSH hbrush, oldBrush = 0;
565 if (descr->style & LBS_NOREDRAW) return;
566 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
567 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
568 if (descr->font) oldFont = SelectObject( hdc, descr->font );
569 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
570 hdc, (LPARAM)wnd->hwndSelf );
571 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
572 if (wnd->dwStyle & WS_DISABLED)
573 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
574 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
575 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
576 if (oldFont) SelectObject( hdc, oldFont );
577 if (oldBrush) SelectObject( hdc, oldBrush );
578 ReleaseDC( wnd->hwndSelf, hdc );
582 /***********************************************************************
583 * LISTBOX_InitStorage
585 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
590 nb_items += LB_ARRAY_GRANULARITY - 1;
591 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
593 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
594 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
595 nb_items * sizeof(LB_ITEMDATA) )))
597 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
605 /***********************************************************************
606 * LISTBOX_SetTabStops
608 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
609 LPINT tabs, BOOL short_ints )
611 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
612 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
613 if (!(descr->nb_tabs = count))
618 /* FIXME: count = 1 */
619 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
620 descr->nb_tabs * sizeof(INT) )))
625 LPINT16 p = (LPINT16)tabs;
626 dbg_decl_str(listbox, 256);
628 for (i = 0; i < descr->nb_tabs; i++) {
629 descr->tabs[i] = *p++<<1; /* FIXME */
630 if(TRACE_ON(listbox))
631 dsprintf(listbox, "%hd ", descr->tabs[i]);
633 TRACE(listbox, "[%04x]: settabstops %s\n",
634 wnd->hwndSelf, dbg_str(listbox));
636 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
637 /* FIXME: repaint the window? */
642 /***********************************************************************
645 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
648 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
649 if (HAS_STRINGS(descr))
652 return strlen(descr->items[index].str);
653 lstrcpyA( buffer, descr->items[index].str );
654 return strlen(buffer);
657 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
658 return sizeof(DWORD);
663 /***********************************************************************
664 * LISTBOX_FindStringPos
666 * Find the nearest string located before a given string in sort order.
667 * If 'exact' is TRUE, return an error if we don't get an exact match.
669 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
672 INT index, min, max, res = -1;
674 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
676 max = descr->nb_items;
679 index = (min + max) / 2;
680 if (HAS_STRINGS(descr))
681 res = lstrcmpiA( descr->items[index].str, str );
684 COMPAREITEMSTRUCT cis;
685 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
687 cis.CtlType = ODT_LISTBOX;
689 cis.hwndItem = wnd->hwndSelf;
691 cis.itemData1 = descr->items[index].data;
693 cis.itemData2 = (DWORD)str;
694 cis.dwLocaleId = descr->locale;
695 res = SendMessageA( descr->owner, WM_COMPAREITEM,
698 if (!res) return index;
699 if (res > 0) max = index;
700 else min = index + 1;
702 return exact ? -1 : max;
706 /***********************************************************************
707 * LISTBOX_FindFileStrPos
709 * Find the nearest string located before a given string in directory
710 * sort order (i.e. first files, then directories, then drives).
712 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
714 INT min, max, res = -1;
716 if (!HAS_STRINGS(descr))
717 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
719 max = descr->nb_items;
722 INT index = (min + max) / 2;
723 const char *p = descr->items[index].str;
724 if (*p == '[') /* drive or directory */
726 if (*str != '[') res = -1;
727 else if (p[1] == '-') /* drive */
729 if (str[1] == '-') res = str[2] - p[2];
734 if (str[1] == '-') res = 1;
735 else res = lstrcmpiA( str, p );
740 if (*str == '[') res = 1;
741 else res = lstrcmpiA( str, p );
743 if (!res) return index;
744 if (res < 0) max = index;
745 else min = index + 1;
751 /***********************************************************************
754 * Find the item beginning with a given string.
756 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
757 LPCSTR str, BOOL exact )
762 if (start >= descr->nb_items) start = -1;
763 item = descr->items + start + 1;
764 if (HAS_STRINGS(descr))
766 if (!str) return LB_ERR;
769 for (i = start + 1; i < descr->nb_items; i++, item++)
770 if (!lstrcmpiA( str, item->str )) return i;
771 for (i = 0, item = descr->items; i <= start; i++, item++)
772 if (!lstrcmpiA( str, item->str )) return i;
776 /* Special case for drives and directories: ignore prefix */
777 #define CHECK_DRIVE(item) \
778 if ((item)->str[0] == '[') \
780 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
781 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
785 INT len = strlen(str);
786 for (i = start + 1; i < descr->nb_items; i++, item++)
788 if (!lstrncmpiA( str, item->str, len )) return i;
791 for (i = 0, item = descr->items; i <= start; i++, item++)
793 if (!lstrncmpiA( str, item->str, len )) return i;
801 if (exact && (descr->style & LBS_SORT))
802 /* If sorted, use a WM_COMPAREITEM binary search */
803 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
805 /* Otherwise use a linear search */
806 for (i = start + 1; i < descr->nb_items; i++, item++)
807 if (item->data == (DWORD)str) return i;
808 for (i = 0, item = descr->items; i <= start; i++, item++)
809 if (item->data == (DWORD)str) return i;
815 /***********************************************************************
816 * LISTBOX_GetSelCount
818 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
821 LB_ITEMDATA *item = descr->items;
823 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
824 for (i = count = 0; i < descr->nb_items; i++, item++)
825 if (item->selected) count++;
830 /***********************************************************************
831 * LISTBOX_GetSelItems16
833 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
837 LB_ITEMDATA *item = descr->items;
839 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
840 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
841 if (item->selected) array[count++] = (INT16)i;
846 /***********************************************************************
847 * LISTBOX_GetSelItems32
849 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
853 LB_ITEMDATA *item = descr->items;
855 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
856 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
857 if (item->selected) array[count++] = i;
862 /***********************************************************************
865 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
867 INT i, col_pos = descr->page_size - 1;
870 HBRUSH hbrush, oldBrush = 0;
872 SetRect( &rect, 0, 0, descr->width, descr->height );
873 if (descr->style & LBS_NOREDRAW) return 0;
874 if (descr->style & LBS_MULTICOLUMN)
875 rect.right = rect.left + descr->column_width;
876 else if (descr->horz_pos)
878 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
879 rect.right += descr->horz_pos;
882 if (descr->font) oldFont = SelectObject( hdc, descr->font );
883 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
884 hdc, (LPARAM)wnd->hwndSelf );
885 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
886 if (wnd->dwStyle & WS_DISABLED)
887 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
889 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
890 (GetFocus() == wnd->hwndSelf))
892 /* Special case for empty listbox: paint focus rect */
893 rect.bottom = rect.top + descr->item_height;
894 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
896 rect.top = rect.bottom;
899 for (i = descr->top_item; i < descr->nb_items; i++)
901 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
902 rect.bottom = rect.top + descr->item_height;
904 rect.bottom = rect.top + descr->items[i].height;
906 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
907 rect.top = rect.bottom;
909 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
911 if (!IS_OWNERDRAW(descr))
913 /* Clear the bottom of the column */
914 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
915 if (rect.top < descr->height)
917 rect.bottom = descr->height;
918 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
919 &rect, NULL, 0, NULL );
923 /* Go to the next column */
924 rect.left += descr->column_width;
925 rect.right += descr->column_width;
927 col_pos = descr->page_size - 1;
932 if (rect.top >= descr->height) break;
936 if (!IS_OWNERDRAW(descr))
938 /* Clear the remainder of the client area */
939 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
940 if (rect.top < descr->height)
942 rect.bottom = descr->height;
943 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
944 &rect, NULL, 0, NULL );
946 if (rect.right < descr->width)
948 rect.left = rect.right;
949 rect.right = descr->width;
951 rect.bottom = descr->height;
952 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
953 &rect, NULL, 0, NULL );
956 if (oldFont) SelectObject( hdc, oldFont );
957 if (oldBrush) SelectObject( hdc, oldBrush );
962 /***********************************************************************
963 * LISTBOX_InvalidateItems
965 * Invalidate all items from a given item. If the specified item is not
966 * visible, nothing happens.
968 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
972 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
974 rect.bottom = descr->height;
975 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
976 if (descr->style & LBS_MULTICOLUMN)
978 /* Repaint the other columns */
979 rect.left = rect.right;
980 rect.right = descr->width;
982 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
988 /***********************************************************************
989 * LISTBOX_GetItemHeight
991 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
993 if (descr->style & LBS_OWNERDRAWVARIABLE)
995 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
996 return descr->items[index].height;
998 else return descr->item_height;
1002 /***********************************************************************
1003 * LISTBOX_SetItemHeight
1005 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1008 if (!height) height = 1;
1010 if (descr->style & LBS_OWNERDRAWVARIABLE)
1012 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1013 TRACE(listbox, "[%04x]: item %d height = %d\n",
1014 wnd->hwndSelf, index, height );
1015 descr->items[index].height = height;
1016 LISTBOX_UpdateScroll( wnd, descr );
1017 LISTBOX_InvalidateItems( wnd, descr, index );
1019 else if (height != descr->item_height)
1021 TRACE(listbox, "[%04x]: new height = %d\n",
1022 wnd->hwndSelf, height );
1023 descr->item_height = height;
1024 LISTBOX_UpdatePage( wnd, descr );
1025 LISTBOX_UpdateScroll( wnd, descr );
1026 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1032 /***********************************************************************
1033 * LISTBOX_SetHorizontalPos
1035 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1039 if (pos > descr->horz_extent - descr->width)
1040 pos = descr->horz_extent - descr->width;
1041 if (pos < 0) pos = 0;
1042 if (!(diff = descr->horz_pos - pos)) return;
1043 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1044 wnd->hwndSelf, pos );
1045 descr->horz_pos = pos;
1046 LISTBOX_UpdateScroll( wnd, descr );
1047 if (abs(diff) < descr->width)
1048 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1049 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1051 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1055 /***********************************************************************
1056 * LISTBOX_SetHorizontalExtent
1058 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1061 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1063 if (extent <= 0) extent = 1;
1064 if (extent == descr->horz_extent) return LB_OKAY;
1065 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1066 wnd->hwndSelf, extent );
1067 descr->horz_extent = extent;
1068 if (descr->horz_pos > extent - descr->width)
1069 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1071 LISTBOX_UpdateScroll( wnd, descr );
1076 /***********************************************************************
1077 * LISTBOX_SetColumnWidth
1079 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1081 width += 2; /* For left and right margin */
1082 if (width == descr->column_width) return LB_OKAY;
1083 TRACE(listbox, "[%04x]: new column width = %d\n",
1084 wnd->hwndSelf, width );
1085 descr->column_width = width;
1086 LISTBOX_UpdatePage( wnd, descr );
1091 /***********************************************************************
1094 * Returns the item height.
1096 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1104 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1106 ERR(listbox, "unable to get DC.\n" );
1109 if (font) oldFont = SelectObject( hdc, font );
1110 GetTextMetricsA( hdc, &tm );
1111 if (oldFont) SelectObject( hdc, oldFont );
1112 ReleaseDC( wnd->hwndSelf, hdc );
1113 if (!IS_OWNERDRAW(descr))
1114 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1115 return tm.tmHeight ;
1119 /***********************************************************************
1120 * LISTBOX_MakeItemVisible
1122 * Make sure that a given item is partially or fully visible.
1124 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1129 if (index <= descr->top_item) top = index;
1130 else if (descr->style & LBS_MULTICOLUMN)
1132 INT cols = descr->width;
1133 if (!fully) cols += descr->column_width - 1;
1134 if (cols >= descr->column_width) cols /= descr->column_width;
1136 if (index < descr->top_item + (descr->page_size * cols)) return;
1137 top = index - descr->page_size * (cols - 1);
1139 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1141 INT height = fully ? descr->items[index].height : 1;
1142 for (top = index; top > descr->top_item; top--)
1143 if ((height += descr->items[top-1].height) > descr->height) break;
1147 if (index < descr->top_item + descr->page_size) return;
1148 if (!fully && (index == descr->top_item + descr->page_size) &&
1149 (descr->height > (descr->page_size * descr->item_height))) return;
1150 top = index - descr->page_size + 1;
1152 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1156 /***********************************************************************
1157 * LISTBOX_SelectItemRange
1159 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1161 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1166 /* A few sanity checks */
1168 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1169 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1170 if (last == -1) last = descr->nb_items - 1;
1171 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1172 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1173 /* selected_item reflects last selected/unselected item on multiple sel */
1174 descr->selected_item = last;
1176 if (on) /* Turn selection on */
1178 for (i = first; i <= last; i++)
1180 if (descr->items[i].selected) continue;
1181 descr->items[i].selected = TRUE;
1182 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1185 else /* Turn selection off */
1187 for (i = first; i <= last; i++)
1189 if (!descr->items[i].selected) continue;
1190 descr->items[i].selected = FALSE;
1191 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1198 /***********************************************************************
1199 * LISTBOX_SetCaretIndex
1202 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1205 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1206 BOOL fully_visible )
1208 INT oldfocus = descr->focus_item;
1210 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1211 if (index == oldfocus) return LB_OKAY;
1212 descr->focus_item = index;
1213 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1214 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1216 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1217 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1218 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1224 /***********************************************************************
1225 * LISTBOX_SetSelection
1227 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1228 BOOL on, BOOL send_notify )
1230 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1231 if (descr->style & LBS_MULTIPLESEL)
1233 if (index == -1) /* Select all items */
1234 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1235 else /* Only one item */
1236 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1240 INT oldsel = descr->selected_item;
1241 if (index == oldsel) return LB_OKAY;
1242 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1243 if (index != -1) descr->items[index].selected = TRUE;
1244 descr->selected_item = index;
1245 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1246 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1247 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1248 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1250 if( descr->lphc ) /* set selection change flag for parent combo */
1251 descr->lphc->wState |= CBF_SELCHANGE;
1257 /***********************************************************************
1260 * Change the caret position and extend the selection to the new caret.
1262 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1263 BOOL fully_visible )
1265 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1266 if (descr->style & LBS_EXTENDEDSEL)
1268 if (descr->anchor_item != -1)
1270 INT first = MIN( descr->focus_item, descr->anchor_item );
1271 INT last = MAX( descr->focus_item, descr->anchor_item );
1273 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1274 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1275 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1278 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1280 /* Set selection to new caret item */
1281 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1286 /***********************************************************************
1287 * LISTBOX_InsertItem
1289 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1290 LPSTR str, DWORD data )
1295 if (index == -1) index = descr->nb_items;
1296 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1297 if (!descr->items) max_items = 0;
1298 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1299 if (descr->nb_items == max_items)
1301 /* We need to grow the array */
1302 max_items += LB_ARRAY_GRANULARITY;
1303 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1304 max_items * sizeof(LB_ITEMDATA) )))
1306 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1309 descr->items = item;
1312 /* Insert the item structure */
1314 item = &descr->items[index];
1315 if (index < descr->nb_items)
1316 RtlMoveMemory( item + 1, item,
1317 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1321 item->selected = FALSE;
1324 /* Get item height */
1326 if (descr->style & LBS_OWNERDRAWVARIABLE)
1328 MEASUREITEMSTRUCT mis;
1329 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1331 mis.CtlType = ODT_LISTBOX;
1334 mis.itemData = descr->items[index].data;
1335 mis.itemHeight = descr->item_height;
1336 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1337 item->height = mis.itemHeight ? mis.itemHeight : 1;
1338 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1339 wnd->hwndSelf, index, str ? str : "", item->height );
1342 /* Repaint the items */
1344 LISTBOX_UpdateScroll( wnd, descr );
1345 LISTBOX_InvalidateItems( wnd, descr, index );
1347 /* Move selection and focused item */
1349 if (index <= descr->selected_item) descr->selected_item++;
1350 if (index <= descr->focus_item)
1352 descr->focus_item++;
1353 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1356 /* If listbox was empty, set focus to the first item */
1358 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1363 /***********************************************************************
1364 * LISTBOX_InsertString
1366 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1369 LPSTR new_str = NULL;
1373 if (HAS_STRINGS(descr))
1375 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1377 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1381 else data = (DWORD)str;
1383 if (index == -1) index = descr->nb_items;
1384 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1386 if (new_str) HeapFree( descr->heap, 0, new_str );
1390 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1391 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1396 /***********************************************************************
1397 * LISTBOX_DeleteItem
1399 * Delete the content of an item. 'index' must be a valid index.
1401 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1403 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1404 * while Win95 sends it for all items with user data.
1405 * It's probably better to send it too often than not
1406 * often enough, so this is what we do here.
1408 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1410 DELETEITEMSTRUCT dis;
1411 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1413 dis.CtlType = ODT_LISTBOX;
1416 dis.hwndItem = wnd->hwndSelf;
1417 dis.itemData = descr->items[index].data;
1418 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1420 if (HAS_STRINGS(descr) && descr->items[index].str)
1421 HeapFree( descr->heap, 0, descr->items[index].str );
1425 /***********************************************************************
1426 * LISTBOX_RemoveItem
1428 * Remove an item from the listbox and delete its content.
1430 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1435 if (index == -1) index = descr->nb_items - 1;
1436 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1437 LISTBOX_DeleteItem( wnd, descr, index );
1439 /* Remove the item */
1441 item = &descr->items[index];
1442 if (index < descr->nb_items-1)
1443 RtlMoveMemory( item, item + 1,
1444 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1446 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1448 /* Shrink the item array if possible */
1450 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1451 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1453 max_items -= LB_ARRAY_GRANULARITY;
1454 item = HeapReAlloc( descr->heap, 0, descr->items,
1455 max_items * sizeof(LB_ITEMDATA) );
1456 if (item) descr->items = item;
1459 /* Repaint the items */
1461 LISTBOX_UpdateScroll( wnd, descr );
1462 LISTBOX_InvalidateItems( wnd, descr, index );
1464 /* Move selection and focused item */
1466 if (index <= descr->selected_item) descr->selected_item--;
1467 if (index <= descr->focus_item)
1469 descr->focus_item--;
1470 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1476 /***********************************************************************
1477 * LISTBOX_ResetContent
1479 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1483 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1484 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1485 descr->nb_items = 0;
1486 descr->top_item = 0;
1487 descr->selected_item = -1;
1488 descr->focus_item = 0;
1489 descr->anchor_item = -1;
1490 descr->items = NULL;
1491 LISTBOX_UpdateScroll( wnd, descr );
1492 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1496 /***********************************************************************
1499 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1503 if (HAS_STRINGS(descr)) return LB_ERR;
1504 /* FIXME: this is far from optimal... */
1505 if (count > descr->nb_items)
1507 while (count > descr->nb_items)
1508 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1511 else if (count < descr->nb_items)
1513 while (count < descr->nb_items)
1514 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1521 /***********************************************************************
1524 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1525 LPCSTR filespec, BOOL long_names )
1528 LRESULT ret = LB_OKAY;
1529 WIN32_FIND_DATAA entry;
1532 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1534 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1541 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1543 if (!(attrib & DDL_DIRECTORY) ||
1544 !strcmp( entry.cAlternateFileName, "." )) continue;
1545 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1546 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1548 else /* not a directory */
1550 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1551 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1553 if ((attrib & DDL_EXCLUSIVE) &&
1554 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1557 if (long_names) strcpy( buffer, entry.cFileName );
1558 else strcpy( buffer, entry.cAlternateFileName );
1560 if (!long_names) CharLowerA( buffer );
1561 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1562 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1564 } while (FindNextFileA( handle, &entry ));
1565 FindClose( handle );
1568 if ((ret >= 0) && (attrib & DDL_DRIVES))
1570 char buffer[] = "[-a-]";
1572 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1574 if (!DRIVE_IsValid(drive)) continue;
1575 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1583 /***********************************************************************
1584 * LISTBOX_HandleVScroll
1586 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1587 WPARAM wParam, LPARAM lParam )
1591 if (descr->style & LBS_MULTICOLUMN) return 0;
1592 switch(LOWORD(wParam))
1595 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1598 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1601 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1602 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1605 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1606 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1608 case SB_THUMBPOSITION:
1609 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1612 info.cbSize = sizeof(info);
1613 info.fMask = SIF_TRACKPOS;
1614 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1615 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1618 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1621 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1628 /***********************************************************************
1629 * LISTBOX_HandleHScroll
1631 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1632 WPARAM wParam, LPARAM lParam )
1637 if (descr->style & LBS_MULTICOLUMN)
1639 switch(LOWORD(wParam))
1642 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1646 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1650 page = descr->width / descr->column_width;
1651 if (page < 1) page = 1;
1652 LISTBOX_SetTopItem( wnd, descr,
1653 descr->top_item - page * descr->page_size, TRUE );
1656 page = descr->width / descr->column_width;
1657 if (page < 1) page = 1;
1658 LISTBOX_SetTopItem( wnd, descr,
1659 descr->top_item + page * descr->page_size, TRUE );
1661 case SB_THUMBPOSITION:
1662 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1666 info.cbSize = sizeof(info);
1667 info.fMask = SIF_TRACKPOS;
1668 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1669 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1673 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1676 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1680 else if (descr->horz_extent)
1682 switch(LOWORD(wParam))
1685 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1688 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1691 LISTBOX_SetHorizontalPos( wnd, descr,
1692 descr->horz_pos - descr->width );
1695 LISTBOX_SetHorizontalPos( wnd, descr,
1696 descr->horz_pos + descr->width );
1698 case SB_THUMBPOSITION:
1699 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1702 info.cbSize = sizeof(info);
1703 info.fMask = SIF_TRACKPOS;
1704 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1705 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1708 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1711 LISTBOX_SetHorizontalPos( wnd, descr,
1712 descr->horz_extent - descr->width );
1720 /***********************************************************************
1721 * LISTBOX_HandleLButtonDown
1723 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1724 WPARAM wParam, INT x, INT y )
1726 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1727 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1728 wnd->hwndSelf, x, y, index );
1729 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1732 if (descr->style & LBS_EXTENDEDSEL)
1734 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1735 if (wParam & MK_CONTROL)
1737 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1738 LISTBOX_SetSelection( wnd, descr, index,
1739 !descr->items[index].selected, FALSE );
1741 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1745 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1746 LISTBOX_SetSelection( wnd, descr, index,
1747 (!(descr->style & LBS_MULTIPLESEL) ||
1748 !descr->items[index].selected), FALSE );
1752 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1753 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1754 : descr->lphc->self->hwndSelf ) ;
1756 descr->captured = TRUE;
1757 SetCapture( wnd->hwndSelf );
1758 if (index != -1 && !descr->lphc)
1760 if (descr->style & LBS_NOTIFY )
1761 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1762 MAKELPARAM( x, y ) );
1763 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1765 POINT pt = { x, y };
1766 if (DragDetect( wnd->hwndSelf, pt ))
1767 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1774 /***********************************************************************
1775 * LISTBOX_HandleLButtonUp
1777 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1779 if (LISTBOX_Timer != LB_TIMER_NONE)
1780 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1781 LISTBOX_Timer = LB_TIMER_NONE;
1782 if (descr->captured)
1784 descr->captured = FALSE;
1785 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1786 if (descr->style & LBS_NOTIFY)
1787 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1793 /***********************************************************************
1794 * LISTBOX_HandleTimer
1796 * Handle scrolling upon a timer event.
1797 * Return TRUE if scrolling should continue.
1799 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1800 INT index, TIMER_DIRECTION dir )
1805 if (descr->top_item) index = descr->top_item - 1;
1809 if (descr->top_item) index -= descr->page_size;
1812 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1813 if (index == descr->focus_item) index++;
1814 if (index >= descr->nb_items) index = descr->nb_items - 1;
1816 case LB_TIMER_RIGHT:
1817 if (index + descr->page_size < descr->nb_items)
1818 index += descr->page_size;
1823 if (index == descr->focus_item) return FALSE;
1824 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1829 /***********************************************************************
1830 * LISTBOX_HandleSystemTimer
1832 * WM_SYSTIMER handler.
1834 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1836 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1838 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1839 LISTBOX_Timer = LB_TIMER_NONE;
1845 /***********************************************************************
1846 * LISTBOX_HandleMouseMove
1848 * WM_MOUSEMOVE handler.
1850 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1854 TIMER_DIRECTION dir;
1856 if (!descr->captured) return;
1858 if (descr->style & LBS_MULTICOLUMN)
1861 else if (y >= descr->item_height * descr->page_size)
1862 y = descr->item_height * descr->page_size - 1;
1866 dir = LB_TIMER_LEFT;
1869 else if (x >= descr->width)
1871 dir = LB_TIMER_RIGHT;
1872 x = descr->width - 1;
1874 else dir = LB_TIMER_NONE; /* inside */
1878 if (y < 0) dir = LB_TIMER_UP; /* above */
1879 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1880 else dir = LB_TIMER_NONE; /* inside */
1883 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1884 if (index == -1) index = descr->focus_item;
1885 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1887 /* Start/stop the system timer */
1889 if (dir != LB_TIMER_NONE)
1890 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1891 else if (LISTBOX_Timer != LB_TIMER_NONE)
1892 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1893 LISTBOX_Timer = dir;
1897 /***********************************************************************
1898 * LISTBOX_HandleKeyDown
1900 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1903 if (descr->style & LBS_WANTKEYBOARDINPUT)
1905 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1906 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1908 if (caret == -2) return 0;
1910 if (caret == -1) switch(wParam)
1913 if (descr->style & LBS_MULTICOLUMN)
1915 if (descr->focus_item >= descr->page_size)
1916 caret = descr->focus_item - descr->page_size;
1921 caret = descr->focus_item - 1;
1922 if (caret < 0) caret = 0;
1925 if (descr->style & LBS_MULTICOLUMN)
1927 if (descr->focus_item + descr->page_size < descr->nb_items)
1928 caret = descr->focus_item + descr->page_size;
1933 caret = descr->focus_item + 1;
1934 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1937 if (descr->style & LBS_MULTICOLUMN)
1939 INT page = descr->width / descr->column_width;
1940 if (page < 1) page = 1;
1941 caret = descr->focus_item - (page * descr->page_size) + 1;
1943 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1944 if (caret < 0) caret = 0;
1947 if (descr->style & LBS_MULTICOLUMN)
1949 INT page = descr->width / descr->column_width;
1950 if (page < 1) page = 1;
1951 caret = descr->focus_item + (page * descr->page_size) - 1;
1953 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1954 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1960 caret = descr->nb_items - 1;
1963 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1964 else if (descr->style & LBS_MULTIPLESEL)
1966 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1967 !descr->items[descr->focus_item].selected,
1968 (descr->style & LBS_NOTIFY) != 0 );
1970 else if (descr->selected_item == -1)
1972 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1973 (descr->style & LBS_NOTIFY) != 0 );
1979 if ((descr->style & LBS_EXTENDEDSEL) &&
1980 !(GetKeyState( VK_SHIFT ) & 0x8000))
1981 descr->anchor_item = caret;
1982 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1983 if (descr->style & LBS_NOTIFY)
1985 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1987 /* make sure that combo parent doesn't hide us */
1988 descr->lphc->wState |= CBF_NOROLLUP;
1990 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1997 /***********************************************************************
1998 * LISTBOX_HandleChar
2000 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2004 char str[2] = { wParam & 0xff, '\0' };
2006 if (descr->style & LBS_WANTKEYBOARDINPUT)
2008 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2009 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2011 if (caret == -2) return 0;
2014 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2017 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2018 if (descr->style & LBS_NOTIFY)
2019 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2025 /***********************************************************************
2028 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2031 MEASUREITEMSTRUCT mis;
2034 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2036 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2038 HeapFree( GetProcessHeap(), 0, descr );
2041 GetClientRect( wnd->hwndSelf, &rect );
2042 descr->owner = GetParent( wnd->hwndSelf );
2043 descr->style = wnd->dwStyle;
2044 descr->width = rect.right - rect.left;
2045 descr->height = rect.bottom - rect.top;
2046 descr->items = NULL;
2047 descr->nb_items = 0;
2048 descr->top_item = 0;
2049 descr->selected_item = -1;
2050 descr->focus_item = 0;
2051 descr->anchor_item = -1;
2052 descr->item_height = 1;
2053 descr->page_size = 1;
2054 descr->column_width = 150;
2055 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2056 descr->horz_pos = 0;
2059 descr->caret_on = TRUE;
2060 descr->captured = FALSE;
2062 descr->locale = 0; /* FIXME */
2067 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2068 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2069 descr->owner = lphc->self->hwndSelf;
2072 *(LB_DESCR **)wnd->wExtra = descr;
2074 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2076 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2077 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2078 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2079 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2081 if (descr->style & LBS_OWNERDRAWFIXED)
2083 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2085 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2086 descr->item_height = lphc->fixedOwnerDrawHeight;
2090 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2092 mis.CtlType = ODT_LISTBOX;
2097 mis.itemHeight = descr->item_height;
2098 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2099 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2107 /***********************************************************************
2110 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2112 LISTBOX_ResetContent( wnd, descr );
2113 HeapDestroy( descr->heap );
2114 HeapFree( GetProcessHeap(), 0, descr );
2120 /***********************************************************************
2123 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2124 WPARAM wParam, LPARAM lParam )
2128 WND *wnd = WIN_FindWndPtr( hwnd );
2132 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2134 if (msg == WM_CREATE)
2136 if (!LISTBOX_Create( wnd, NULL ))
2141 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2142 hwnd, *(LB_DESCR **)wnd->wExtra );
2146 /* Ignore all other messages before we get a WM_CREATE */
2147 retvalue = DefWindowProcA( hwnd, msg, wParam, lParam );
2151 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2152 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2155 case LB_RESETCONTENT16:
2156 case LB_RESETCONTENT:
2157 LISTBOX_ResetContent( wnd, descr );
2161 case LB_ADDSTRING16:
2162 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2165 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2166 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2169 case LB_INSERTSTRING16:
2170 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2171 wParam = (INT)(INT16)wParam;
2173 case LB_INSERTSTRING:
2174 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2178 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2181 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2182 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2185 case LB_DELETESTRING16:
2186 case LB_DELETESTRING:
2187 retvalue = LISTBOX_RemoveItem( wnd, descr, wParam );
2190 case LB_GETITEMDATA16:
2191 case LB_GETITEMDATA:
2192 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2197 retvalue = descr->items[wParam].data;
2200 case LB_SETITEMDATA16:
2201 case LB_SETITEMDATA:
2202 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2207 descr->items[wParam].data = (DWORD)lParam;
2213 retvalue = descr->nb_items;
2217 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2220 retvalue = LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2223 case LB_GETTEXTLEN16:
2226 if (wParam >= descr->nb_items)
2231 retvalue = (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2235 case LB_GETCURSEL16:
2237 if (descr->nb_items==0)
2241 retvalue = descr->selected_item;
2242 if (retvalue == -1) retvalue = descr->focus_item;
2244 /* otherwise, if the user tries to move the selection with the */
2245 /* arrow keys, we will give the application something to choke on */
2248 case LB_GETTOPINDEX16:
2249 case LB_GETTOPINDEX:
2250 retvalue = descr->top_item;
2253 case LB_GETITEMHEIGHT16:
2254 case LB_GETITEMHEIGHT:
2255 retvalue = LISTBOX_GetItemHeight( wnd, descr, wParam );
2258 case LB_SETITEMHEIGHT16:
2259 lParam = LOWORD(lParam);
2261 case LB_SETITEMHEIGHT:
2262 retvalue = LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2265 case LB_ITEMFROMPOINT:
2267 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
2268 RECT rect = { 0, 0, descr->width, descr->height };
2269 retvalue = MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2270 PtInRect( &rect, pt ) );
2274 case LB_SETCARETINDEX16:
2275 case LB_SETCARETINDEX:
2276 retvalue = LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2279 case LB_GETCARETINDEX16:
2280 case LB_GETCARETINDEX:
2281 retvalue = descr->focus_item;
2284 case LB_SETTOPINDEX16:
2285 case LB_SETTOPINDEX:
2286 retvalue = LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2289 case LB_SETCOLUMNWIDTH16:
2290 case LB_SETCOLUMNWIDTH:
2291 retvalue = LISTBOX_SetColumnWidth( wnd, descr, wParam );
2294 case LB_GETITEMRECT16:
2297 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2298 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2303 case LB_GETITEMRECT:
2304 retvalue = LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2307 case LB_FINDSTRING16:
2308 wParam = (INT)(INT16)wParam;
2309 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2312 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2315 case LB_FINDSTRINGEXACT16:
2316 wParam = (INT)(INT16)wParam;
2317 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2319 case LB_FINDSTRINGEXACT:
2320 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2323 case LB_SELECTSTRING16:
2324 wParam = (INT)(INT16)wParam;
2325 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2327 case LB_SELECTSTRING:
2329 INT index = LISTBOX_FindString( wnd, descr, wParam,
2330 (LPCSTR)lParam, FALSE );
2331 if (index == LB_ERR)
2336 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2342 wParam = (INT)(INT16)wParam;
2345 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2350 retvalue = descr->items[wParam].selected;
2354 lParam = (INT)(INT16)lParam;
2357 retvalue = LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2360 case LB_SETCURSEL16:
2361 wParam = (INT)(INT16)wParam;
2364 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2365 retvalue = LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2368 case LB_GETSELCOUNT16:
2369 case LB_GETSELCOUNT:
2370 retvalue = LISTBOX_GetSelCount( wnd, descr );
2373 case LB_GETSELITEMS16:
2374 retvalue = LISTBOX_GetSelItems16( wnd, descr, wParam,
2375 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2378 case LB_GETSELITEMS:
2379 retvalue = LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2382 case LB_SELITEMRANGE16:
2383 case LB_SELITEMRANGE:
2384 if (LOWORD(lParam) <= HIWORD(lParam))
2386 retvalue = LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2387 HIWORD(lParam), wParam );
2391 retvalue = LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2392 LOWORD(lParam), wParam );
2396 case LB_SELITEMRANGEEX16:
2397 case LB_SELITEMRANGEEX:
2398 if ((INT)lParam >= (INT)wParam)
2399 retvalue = LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2401 retvalue = LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2404 case LB_GETHORIZONTALEXTENT16:
2405 case LB_GETHORIZONTALEXTENT:
2406 retvalue = descr->horz_extent;
2409 case LB_SETHORIZONTALEXTENT16:
2410 case LB_SETHORIZONTALEXTENT:
2411 retvalue = LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2414 case LB_GETANCHORINDEX16:
2415 case LB_GETANCHORINDEX:
2416 retvalue = descr->anchor_item;
2419 case LB_SETANCHORINDEX16:
2420 wParam = (INT)(INT16)wParam;
2422 case LB_SETANCHORINDEX:
2423 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2428 descr->anchor_item = (INT)wParam;
2433 retvalue = LISTBOX_Directory( wnd, descr, wParam,
2434 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2438 retvalue = LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2442 retvalue = descr->locale;
2446 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2450 case LB_INITSTORAGE:
2451 retvalue = LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2455 retvalue = LISTBOX_SetCount( wnd, descr, (INT)wParam );
2458 case LB_SETTABSTOPS16:
2459 retvalue = LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2460 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2463 case LB_SETTABSTOPS:
2464 retvalue = LISTBOX_SetTabStops( wnd, descr, wParam,
2465 (LPINT)lParam, FALSE );
2470 if (descr->caret_on)
2475 descr->caret_on = TRUE;
2476 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2477 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2483 if (!descr->caret_on)
2488 descr->caret_on = FALSE;
2489 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2490 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2495 retvalue = LISTBOX_Destroy( wnd, descr );
2499 InvalidateRect( hwnd, NULL, TRUE );
2504 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2509 retvalue =DLGC_WANTARROWS | DLGC_WANTCHARS;
2514 HDC hdc = ( wParam ) ? ((HDC)wParam)
2515 : BeginPaint( hwnd, &ps );
2516 ret = LISTBOX_Paint( wnd, descr, hdc );
2517 if( !wParam ) EndPaint( hwnd, &ps );
2522 LISTBOX_UpdateSize( wnd, descr );
2526 retvalue =descr->font;
2529 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2530 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2534 descr->caret_on = TRUE;
2535 if (descr->focus_item != -1)
2536 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2537 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2541 if ((descr->focus_item != -1) && descr->caret_on)
2542 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2543 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2547 retvalue =LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2550 retvalue =LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2552 case WM_LBUTTONDOWN:
2553 retvalue =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2554 (INT16)LOWORD(lParam),
2555 (INT16)HIWORD(lParam) );
2557 case WM_LBUTTONDBLCLK:
2558 if (descr->style & LBS_NOTIFY)
2559 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2563 if (GetCapture() == hwnd)
2564 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2565 (INT16)HIWORD(lParam) );
2569 retvalue =LISTBOX_HandleLButtonUp( wnd, descr );
2572 retvalue =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2575 retvalue =LISTBOX_HandleChar( wnd, descr, wParam );
2578 retvalue =LISTBOX_HandleSystemTimer( wnd, descr );
2581 if (IS_OWNERDRAW(descr))
2583 RECT rect = { 0, 0, descr->width, descr->height };
2584 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2585 wParam, (LPARAM)wnd->hwndSelf );
2586 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2593 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2599 case WM_QUERYDROPOBJECT:
2604 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2605 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2607 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2613 if (TWEAK_WineLook > WIN31_LOOK)
2614 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2615 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2618 if ((msg >= WM_USER) && (msg < 0xc000))
2619 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2620 hwnd, msg, wParam, lParam );
2621 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2626 WIN_ReleaseWndPtr(wnd);
2630 /***********************************************************************
2633 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2635 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2639 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2642 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2644 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2645 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2646 WIN_ReleaseWndPtr(wnd);
2649 WIN_ReleaseWndPtr(wnd);
2654 /***********************************************************************
2657 * NOTE: in Windows, winproc address of the ComboLBox is the same
2658 * as that of the Listbox.
2660 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2661 WPARAM wParam, LPARAM lParam )
2664 WND *wnd = WIN_FindWndPtr( hwnd );
2668 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2670 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2671 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2673 if( descr || msg == WM_CREATE )
2675 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2680 #define lpcs ((LPCREATESTRUCTA)lParam)
2681 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2682 (UINT)lpcs->lpCreateParams);
2684 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2686 lRet =LISTBOX_Create( wnd, lphc );
2688 case WM_LBUTTONDOWN:
2689 lRet =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2690 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2692 /* avoid activation at all costs */
2694 case WM_MOUSEACTIVATE:
2695 lRet =MA_NOACTIVATE;
2701 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2703 /* for some reason(?) Windows makes it possible to
2704 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2706 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2707 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2708 && (wParam == VK_DOWN || wParam == VK_UP)) )
2710 COMBO_FlipListbox( lphc, FALSE );
2715 lRet =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2718 case LB_SETCURSEL16:
2720 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2721 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2724 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2729 lRet =ListBoxWndProc( hwnd, msg, wParam, lParam );
2733 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2735 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2738 WIN_ReleaseWndPtr(wnd);