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 HFONT font; /* Current font */
68 LCID locale; /* Current locale for string comparisons */
69 LPHEADCOMBO lphc; /* ComboLBox */
73 #define IS_OWNERDRAW(descr) \
74 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
76 #define HAS_STRINGS(descr) \
77 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
79 #define SEND_NOTIFICATION(wnd,descr,code) \
80 (SendMessageA( (descr)->owner, WM_COMMAND, \
81 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
83 /* Current timer status */
93 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
96 /***********************************************************************
99 void LISTBOX_Dump( WND *wnd )
103 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
105 DUMP( "Listbox:\n" );
106 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
107 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
109 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
111 DUMP( "%4d: %-40s %d %08lx %3d\n",
112 i, item->str, item->selected, item->data, item->height );
117 /***********************************************************************
118 * LISTBOX_GetCurrentPageSize
120 * Return the current page size
122 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
125 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
126 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
128 if ((height += descr->items[i].height) > descr->height) break;
130 if (i == descr->top_item) return 1;
131 else return i - descr->top_item;
135 /***********************************************************************
136 * LISTBOX_GetMaxTopIndex
138 * Return the maximum possible index for the top of the listbox.
140 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
144 if (descr->style & LBS_OWNERDRAWVARIABLE)
146 page = descr->height;
147 for (max = descr->nb_items - 1; max >= 0; max--)
148 if ((page -= descr->items[max].height) < 0) break;
149 if (max < descr->nb_items - 1) max++;
151 else if (descr->style & LBS_MULTICOLUMN)
153 if ((page = descr->width / descr->column_width) < 1) page = 1;
154 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
155 max = (max - page) * descr->page_size;
159 max = descr->nb_items - descr->page_size;
161 if (max < 0) max = 0;
166 /***********************************************************************
167 * LISTBOX_UpdateScroll
169 * Update the scrollbars. Should be called whenever the content
170 * of the listbox changes.
172 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
176 if (!(descr->style & WS_VSCROLL)) return;
177 /* It is important that we check descr->style, and not wnd->dwStyle,
178 for WS_VSCROLL, as the former is exactly the one passed in
179 argument to CreateWindow.
180 In Windows (and from now on in Wine :) a listbox created
181 with such a style (no WS_SCROLL) does not update
182 the scrollbar with listbox-related data, thus letting
183 the programmer use it for his/her own purposes. */
185 if (descr->style & LBS_NOREDRAW) return;
186 info.cbSize = sizeof(info);
188 if (descr->style & LBS_MULTICOLUMN)
191 info.nMax = (descr->nb_items - 1) / descr->page_size;
192 info.nPos = descr->top_item / descr->page_size;
193 info.nPage = descr->width / descr->column_width;
194 if (info.nPage < 1) info.nPage = 1;
195 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
196 if (descr->style & LBS_DISABLENOSCROLL)
197 info.fMask |= SIF_DISABLENOSCROLL;
198 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
200 info.fMask = SIF_RANGE;
201 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
206 info.nMax = descr->nb_items - 1;
207 info.nPos = descr->top_item;
208 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
209 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
210 if (descr->style & LBS_DISABLENOSCROLL)
211 info.fMask |= SIF_DISABLENOSCROLL;
212 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
214 if (descr->horz_extent)
217 info.nMax = descr->horz_extent - 1;
218 info.nPos = descr->horz_pos;
219 info.nPage = descr->width;
220 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
221 if (descr->style & LBS_DISABLENOSCROLL)
222 info.fMask |= SIF_DISABLENOSCROLL;
223 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
229 /***********************************************************************
232 * Set the top item of the listbox, scrolling up or down if necessary.
234 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
237 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
238 if (index > max) index = max;
239 if (index < 0) index = 0;
240 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
241 if (descr->top_item == index) return LB_OKAY;
242 if (descr->style & LBS_MULTICOLUMN)
244 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
245 if (scroll && (abs(diff) < descr->width))
246 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
247 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
255 if (descr->style & LBS_OWNERDRAWVARIABLE)
259 if (index > descr->top_item)
261 for (i = index - 1; i >= descr->top_item; i--)
262 diff -= descr->items[i].height;
266 for (i = index; i < descr->top_item; i++)
267 diff += descr->items[i].height;
271 diff = (descr->top_item - index) * descr->item_height;
273 if (abs(diff) < descr->height)
274 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
275 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
279 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
280 descr->top_item = index;
281 LISTBOX_UpdateScroll( wnd, descr );
286 /***********************************************************************
289 * Update the page size. Should be called when the size of
290 * the client area or the item height changes.
292 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
296 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
297 if (page_size == descr->page_size) return;
298 descr->page_size = page_size;
299 if (descr->style & LBS_MULTICOLUMN)
300 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
301 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
305 /***********************************************************************
308 * Update the size of the listbox. Should be called when the size of
309 * the client area changes.
311 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
315 GetClientRect( wnd->hwndSelf, &rect );
316 descr->width = rect.right - rect.left;
317 descr->height = rect.bottom - rect.top;
318 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
320 if ((descr->height > descr->item_height) &&
321 (descr->height % descr->item_height))
323 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
324 wnd->hwndSelf, descr->height,
325 descr->height - descr->height%descr->item_height );
326 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
327 wnd->rectWindow.right - wnd->rectWindow.left,
328 wnd->rectWindow.bottom - wnd->rectWindow.top -
329 (descr->height % descr->item_height),
330 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
334 TRACE(listbox, "[%04x]: new size = %d,%d\n",
335 wnd->hwndSelf, descr->width, descr->height );
336 LISTBOX_UpdatePage( wnd, descr );
337 LISTBOX_UpdateScroll( wnd, descr );
341 /***********************************************************************
342 * LISTBOX_GetItemRect
344 * Get the rectangle enclosing an item, in listbox client coordinates.
345 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
347 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
350 /* Index <= 0 is legal even on empty listboxes */
351 if (index && (index >= descr->nb_items)) return -1;
352 SetRect( rect, 0, 0, descr->width, descr->height );
353 if (descr->style & LBS_MULTICOLUMN)
355 INT col = (index / descr->page_size) -
356 (descr->top_item / descr->page_size);
357 rect->left += col * descr->column_width;
358 rect->right = rect->left + descr->column_width;
359 rect->top += (index % descr->page_size) * descr->item_height;
360 rect->bottom = rect->top + descr->item_height;
362 else if (descr->style & LBS_OWNERDRAWVARIABLE)
365 rect->right += descr->horz_pos;
366 if ((index >= 0) && (index < descr->nb_items))
368 if (index < descr->top_item)
370 for (i = descr->top_item-1; i >= index; i--)
371 rect->top -= descr->items[i].height;
375 for (i = descr->top_item; i < index; i++)
376 rect->top += descr->items[i].height;
378 rect->bottom = rect->top + descr->items[index].height;
384 rect->top += (index - descr->top_item) * descr->item_height;
385 rect->bottom = rect->top + descr->item_height;
386 rect->right += descr->horz_pos;
389 return ((rect->left < descr->width) && (rect->right > 0) &&
390 (rect->top < descr->height) && (rect->bottom > 0));
394 /***********************************************************************
395 * LISTBOX_GetItemFromPoint
397 * Return the item nearest from point (x,y) (in client coordinates).
399 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
402 INT index = descr->top_item;
404 if (!descr->nb_items) return -1; /* No items */
405 if (descr->style & LBS_OWNERDRAWVARIABLE)
410 while (index < descr->nb_items)
412 if ((pos += descr->items[index].height) > y) break;
421 if ((pos -= descr->items[index].height) <= y) break;
425 else if (descr->style & LBS_MULTICOLUMN)
427 if (y >= descr->item_height * descr->page_size) return -1;
428 if (y >= 0) index += y / descr->item_height;
429 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
430 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
434 index += (y / descr->item_height);
436 if (index < 0) return 0;
437 if (index >= descr->nb_items) return -1;
442 /***********************************************************************
447 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
448 const RECT *rect, INT index, UINT action )
450 LB_ITEMDATA *item = NULL;
451 if (index < descr->nb_items) item = &descr->items[index];
453 if (IS_OWNERDRAW(descr))
456 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
460 if (action == ODA_FOCUS)
461 DrawFocusRect( hdc, rect );
463 FIXME(listbox,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
466 dis.CtlType = ODT_LISTBOX;
468 dis.hwndItem = wnd->hwndSelf;
469 dis.itemAction = action;
473 if (item && item->selected) dis.itemState |= ODS_SELECTED;
474 if ((descr->focus_item == index) &&
476 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
477 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
478 dis.itemData = item ? item->data : 0;
480 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
481 "state=%02x rect=%d,%d-%d,%d\n",
482 wnd->hwndSelf, index, item ? item->str : "", action,
483 dis.itemState, rect->left, rect->top,
484 rect->right, rect->bottom );
485 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
489 COLORREF oldText = 0, oldBk = 0;
491 if (action == ODA_FOCUS)
493 DrawFocusRect( hdc, rect );
496 if (item && item->selected)
498 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
499 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
502 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
503 "rect=%d,%d-%d,%d\n",
504 wnd->hwndSelf, index, item ? item->str : "", action,
505 rect->left, rect->top, rect->right, rect->bottom );
507 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
508 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
509 else if (!(descr->style & LBS_USETABSTOPS))
510 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
511 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
512 strlen(item->str), NULL );
515 /* Output empty string to paint background in the full width. */
516 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
517 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
518 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
519 item->str, strlen(item->str),
520 descr->nb_tabs, descr->tabs, 0);
522 if (item && item->selected)
524 SetBkColor( hdc, oldBk );
525 SetTextColor( hdc, oldText );
527 if ((descr->focus_item == index) &&
529 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
534 /***********************************************************************
537 * Change the redraw flag.
539 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
543 if (!(descr->style & LBS_NOREDRAW)) return;
544 descr->style &= ~LBS_NOREDRAW;
545 LISTBOX_UpdateScroll( wnd, descr );
547 else descr->style |= LBS_NOREDRAW;
551 /***********************************************************************
552 * LISTBOX_RepaintItem
554 * Repaint a single item synchronously.
556 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
562 HBRUSH hbrush, oldBrush = 0;
564 if (descr->style & LBS_NOREDRAW) return;
565 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
566 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
567 if (descr->font) oldFont = SelectObject( hdc, descr->font );
568 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
569 hdc, (LPARAM)wnd->hwndSelf );
570 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
571 if (wnd->dwStyle & WS_DISABLED)
572 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
573 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
574 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
575 if (oldFont) SelectObject( hdc, oldFont );
576 if (oldBrush) SelectObject( hdc, oldBrush );
577 ReleaseDC( wnd->hwndSelf, hdc );
581 /***********************************************************************
582 * LISTBOX_InitStorage
584 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
589 nb_items += LB_ARRAY_GRANULARITY - 1;
590 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
592 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
593 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
594 nb_items * sizeof(LB_ITEMDATA) )))
596 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
604 /***********************************************************************
605 * LISTBOX_SetTabStops
607 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
608 LPINT tabs, BOOL short_ints )
610 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
611 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
612 if (!(descr->nb_tabs = count))
617 /* FIXME: count = 1 */
618 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
619 descr->nb_tabs * sizeof(INT) )))
624 LPINT16 p = (LPINT16)tabs;
625 dbg_decl_str(listbox, 256);
627 for (i = 0; i < descr->nb_tabs; i++) {
628 descr->tabs[i] = *p++<<1; /* FIXME */
629 if(TRACE_ON(listbox))
630 dsprintf(listbox, "%hd ", descr->tabs[i]);
632 TRACE(listbox, "[%04x]: settabstops %s\n",
633 wnd->hwndSelf, dbg_str(listbox));
635 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
636 /* FIXME: repaint the window? */
641 /***********************************************************************
644 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
647 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
648 if (HAS_STRINGS(descr))
651 return strlen(descr->items[index].str);
652 lstrcpyA( buffer, descr->items[index].str );
653 return strlen(buffer);
656 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
657 return sizeof(DWORD);
662 /***********************************************************************
663 * LISTBOX_FindStringPos
665 * Find the nearest string located before a given string in sort order.
666 * If 'exact' is TRUE, return an error if we don't get an exact match.
668 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
671 INT index, min, max, res = -1;
673 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
675 max = descr->nb_items;
678 index = (min + max) / 2;
679 if (HAS_STRINGS(descr))
680 res = lstrcmpiA( descr->items[index].str, str );
683 COMPAREITEMSTRUCT cis;
684 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
686 cis.CtlType = ODT_LISTBOX;
688 cis.hwndItem = wnd->hwndSelf;
690 cis.itemData1 = descr->items[index].data;
692 cis.itemData2 = (DWORD)str;
693 cis.dwLocaleId = descr->locale;
694 res = SendMessageA( descr->owner, WM_COMPAREITEM,
697 if (!res) return index;
698 if (res > 0) max = index;
699 else min = index + 1;
701 return exact ? -1 : max;
705 /***********************************************************************
706 * LISTBOX_FindFileStrPos
708 * Find the nearest string located before a given string in directory
709 * sort order (i.e. first files, then directories, then drives).
711 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
713 INT min, max, res = -1;
715 if (!HAS_STRINGS(descr))
716 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
718 max = descr->nb_items;
721 INT index = (min + max) / 2;
722 const char *p = descr->items[index].str;
723 if (*p == '[') /* drive or directory */
725 if (*str != '[') res = -1;
726 else if (p[1] == '-') /* drive */
728 if (str[1] == '-') res = str[2] - p[2];
733 if (str[1] == '-') res = 1;
734 else res = lstrcmpiA( str, p );
739 if (*str == '[') res = 1;
740 else res = lstrcmpiA( str, p );
742 if (!res) return index;
743 if (res < 0) max = index;
744 else min = index + 1;
750 /***********************************************************************
753 * Find the item beginning with a given string.
755 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
756 LPCSTR str, BOOL exact )
761 if (start >= descr->nb_items) start = -1;
762 item = descr->items + start + 1;
763 if (HAS_STRINGS(descr))
765 if (!str) return LB_ERR;
768 for (i = start + 1; i < descr->nb_items; i++, item++)
769 if (!lstrcmpiA( str, item->str )) return i;
770 for (i = 0, item = descr->items; i <= start; i++, item++)
771 if (!lstrcmpiA( str, item->str )) return i;
775 /* Special case for drives and directories: ignore prefix */
776 #define CHECK_DRIVE(item) \
777 if ((item)->str[0] == '[') \
779 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
780 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
784 INT len = strlen(str);
785 for (i = start + 1; i < descr->nb_items; i++, item++)
787 if (!lstrncmpiA( str, item->str, len )) return i;
790 for (i = 0, item = descr->items; i <= start; i++, item++)
792 if (!lstrncmpiA( str, item->str, len )) return i;
800 if (exact && (descr->style & LBS_SORT))
801 /* If sorted, use a WM_COMPAREITEM binary search */
802 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
804 /* Otherwise use a linear search */
805 for (i = start + 1; i < descr->nb_items; i++, item++)
806 if (item->data == (DWORD)str) return i;
807 for (i = 0, item = descr->items; i <= start; i++, item++)
808 if (item->data == (DWORD)str) return i;
814 /***********************************************************************
815 * LISTBOX_GetSelCount
817 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
820 LB_ITEMDATA *item = descr->items;
822 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
823 for (i = count = 0; i < descr->nb_items; i++, item++)
824 if (item->selected) count++;
829 /***********************************************************************
830 * LISTBOX_GetSelItems16
832 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
836 LB_ITEMDATA *item = descr->items;
838 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
839 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
840 if (item->selected) array[count++] = (INT16)i;
845 /***********************************************************************
846 * LISTBOX_GetSelItems32
848 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
852 LB_ITEMDATA *item = descr->items;
854 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
855 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
856 if (item->selected) array[count++] = i;
861 /***********************************************************************
864 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
866 INT i, col_pos = descr->page_size - 1;
869 HBRUSH hbrush, oldBrush = 0;
871 SetRect( &rect, 0, 0, descr->width, descr->height );
872 if (descr->style & LBS_NOREDRAW) return 0;
873 if (descr->style & LBS_MULTICOLUMN)
874 rect.right = rect.left + descr->column_width;
875 else if (descr->horz_pos)
877 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
878 rect.right += descr->horz_pos;
881 if (descr->font) oldFont = SelectObject( hdc, descr->font );
882 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
883 hdc, (LPARAM)wnd->hwndSelf );
884 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
885 if (wnd->dwStyle & WS_DISABLED)
886 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
888 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
889 (GetFocus() == wnd->hwndSelf))
891 /* Special case for empty listbox: paint focus rect */
892 rect.bottom = rect.top + descr->item_height;
893 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
895 rect.top = rect.bottom;
898 for (i = descr->top_item; i < descr->nb_items; i++)
900 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
901 rect.bottom = rect.top + descr->item_height;
903 rect.bottom = rect.top + descr->items[i].height;
905 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
906 rect.top = rect.bottom;
908 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
910 if (!IS_OWNERDRAW(descr))
912 /* Clear the bottom of the column */
913 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
914 if (rect.top < descr->height)
916 rect.bottom = descr->height;
917 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
918 &rect, NULL, 0, NULL );
922 /* Go to the next column */
923 rect.left += descr->column_width;
924 rect.right += descr->column_width;
926 col_pos = descr->page_size - 1;
931 if (rect.top >= descr->height) break;
935 if (!IS_OWNERDRAW(descr))
937 /* Clear the remainder of the client area */
938 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
939 if (rect.top < descr->height)
941 rect.bottom = descr->height;
942 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
943 &rect, NULL, 0, NULL );
945 if (rect.right < descr->width)
947 rect.left = rect.right;
948 rect.right = descr->width;
950 rect.bottom = descr->height;
951 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
952 &rect, NULL, 0, NULL );
955 if (oldFont) SelectObject( hdc, oldFont );
956 if (oldBrush) SelectObject( hdc, oldBrush );
961 /***********************************************************************
962 * LISTBOX_InvalidateItems
964 * Invalidate all items from a given item. If the specified item is not
965 * visible, nothing happens.
967 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
971 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
973 rect.bottom = descr->height;
974 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
975 if (descr->style & LBS_MULTICOLUMN)
977 /* Repaint the other columns */
978 rect.left = rect.right;
979 rect.right = descr->width;
981 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
987 /***********************************************************************
988 * LISTBOX_GetItemHeight
990 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
992 if (descr->style & LBS_OWNERDRAWVARIABLE)
994 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
995 return descr->items[index].height;
997 else return descr->item_height;
1001 /***********************************************************************
1002 * LISTBOX_SetItemHeight
1004 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1007 if (!height) height = 1;
1009 if (descr->style & LBS_OWNERDRAWVARIABLE)
1011 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1012 TRACE(listbox, "[%04x]: item %d height = %d\n",
1013 wnd->hwndSelf, index, height );
1014 descr->items[index].height = height;
1015 LISTBOX_UpdateScroll( wnd, descr );
1016 LISTBOX_InvalidateItems( wnd, descr, index );
1018 else if (height != descr->item_height)
1020 TRACE(listbox, "[%04x]: new height = %d\n",
1021 wnd->hwndSelf, height );
1022 descr->item_height = height;
1023 LISTBOX_UpdatePage( wnd, descr );
1024 LISTBOX_UpdateScroll( wnd, descr );
1025 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1031 /***********************************************************************
1032 * LISTBOX_SetHorizontalPos
1034 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1038 if (pos > descr->horz_extent - descr->width)
1039 pos = descr->horz_extent - descr->width;
1040 if (pos < 0) pos = 0;
1041 if (!(diff = descr->horz_pos - pos)) return;
1042 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1043 wnd->hwndSelf, pos );
1044 descr->horz_pos = pos;
1045 LISTBOX_UpdateScroll( wnd, descr );
1046 if (abs(diff) < descr->width)
1047 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1048 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1050 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1054 /***********************************************************************
1055 * LISTBOX_SetHorizontalExtent
1057 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1060 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1062 if (extent <= 0) extent = 1;
1063 if (extent == descr->horz_extent) return LB_OKAY;
1064 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1065 wnd->hwndSelf, extent );
1066 descr->horz_extent = extent;
1067 if (descr->horz_pos > extent - descr->width)
1068 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1070 LISTBOX_UpdateScroll( wnd, descr );
1075 /***********************************************************************
1076 * LISTBOX_SetColumnWidth
1078 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1080 width += 2; /* For left and right margin */
1081 if (width == descr->column_width) return LB_OKAY;
1082 TRACE(listbox, "[%04x]: new column width = %d\n",
1083 wnd->hwndSelf, width );
1084 descr->column_width = width;
1085 LISTBOX_UpdatePage( wnd, descr );
1090 /***********************************************************************
1093 * Returns the item height.
1095 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1103 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1105 ERR(listbox, "unable to get DC.\n" );
1108 if (font) oldFont = SelectObject( hdc, font );
1109 GetTextMetricsA( hdc, &tm );
1110 if (oldFont) SelectObject( hdc, oldFont );
1111 ReleaseDC( wnd->hwndSelf, hdc );
1112 if (!IS_OWNERDRAW(descr))
1113 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1114 return tm.tmHeight ;
1118 /***********************************************************************
1119 * LISTBOX_MakeItemVisible
1121 * Make sure that a given item is partially or fully visible.
1123 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1128 if (index <= descr->top_item) top = index;
1129 else if (descr->style & LBS_MULTICOLUMN)
1131 INT cols = descr->width;
1132 if (!fully) cols += descr->column_width - 1;
1133 if (cols >= descr->column_width) cols /= descr->column_width;
1135 if (index < descr->top_item + (descr->page_size * cols)) return;
1136 top = index - descr->page_size * (cols - 1);
1138 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1140 INT height = fully ? descr->items[index].height : 1;
1141 for (top = index; top > descr->top_item; top--)
1142 if ((height += descr->items[top-1].height) > descr->height) break;
1146 if (index < descr->top_item + descr->page_size) return;
1147 if (!fully && (index == descr->top_item + descr->page_size) &&
1148 (descr->height > (descr->page_size * descr->item_height))) return;
1149 top = index - descr->page_size + 1;
1151 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1155 /***********************************************************************
1156 * LISTBOX_SelectItemRange
1158 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1160 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1165 /* A few sanity checks */
1167 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1168 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1169 if (last == -1) last = descr->nb_items - 1;
1170 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1171 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1172 /* selected_item reflects last selected/unselected item on multiple sel */
1173 descr->selected_item = last;
1175 if (on) /* Turn selection on */
1177 for (i = first; i <= last; i++)
1179 if (descr->items[i].selected) continue;
1180 descr->items[i].selected = TRUE;
1181 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1184 else /* Turn selection off */
1186 for (i = first; i <= last; i++)
1188 if (!descr->items[i].selected) continue;
1189 descr->items[i].selected = FALSE;
1190 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1197 /***********************************************************************
1198 * LISTBOX_SetCaretIndex
1201 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1204 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1205 BOOL fully_visible )
1207 INT oldfocus = descr->focus_item;
1209 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1210 if (index == oldfocus) return LB_OKAY;
1211 descr->focus_item = index;
1212 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1213 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1215 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1216 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1217 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1223 /***********************************************************************
1224 * LISTBOX_SetSelection
1226 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1227 BOOL on, BOOL send_notify )
1229 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1230 if (descr->style & LBS_MULTIPLESEL)
1232 if (index == -1) /* Select all items */
1233 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1234 else /* Only one item */
1235 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1239 INT oldsel = descr->selected_item;
1240 if (index == oldsel) return LB_OKAY;
1241 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1242 if (index != -1) descr->items[index].selected = TRUE;
1243 descr->selected_item = index;
1244 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1245 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1246 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1247 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1249 if( descr->lphc ) /* set selection change flag for parent combo */
1250 descr->lphc->wState |= CBF_SELCHANGE;
1256 /***********************************************************************
1259 * Change the caret position and extend the selection to the new caret.
1261 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1262 BOOL fully_visible )
1264 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1265 if (descr->style & LBS_EXTENDEDSEL)
1267 if (descr->anchor_item != -1)
1269 INT first = MIN( descr->focus_item, descr->anchor_item );
1270 INT last = MAX( descr->focus_item, descr->anchor_item );
1272 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1273 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1274 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1277 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1279 /* Set selection to new caret item */
1280 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1285 /***********************************************************************
1286 * LISTBOX_InsertItem
1288 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1289 LPSTR str, DWORD data )
1294 if (index == -1) index = descr->nb_items;
1295 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1296 if (!descr->items) max_items = 0;
1297 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1298 if (descr->nb_items == max_items)
1300 /* We need to grow the array */
1301 max_items += LB_ARRAY_GRANULARITY;
1302 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1303 max_items * sizeof(LB_ITEMDATA) )))
1305 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1308 descr->items = item;
1311 /* Insert the item structure */
1313 item = &descr->items[index];
1314 if (index < descr->nb_items)
1315 RtlMoveMemory( item + 1, item,
1316 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1320 item->selected = FALSE;
1323 /* Get item height */
1325 if (descr->style & LBS_OWNERDRAWVARIABLE)
1327 MEASUREITEMSTRUCT mis;
1328 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1330 mis.CtlType = ODT_LISTBOX;
1333 mis.itemData = descr->items[index].data;
1334 mis.itemHeight = descr->item_height;
1335 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1336 item->height = mis.itemHeight ? mis.itemHeight : 1;
1337 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1338 wnd->hwndSelf, index, str ? str : "", item->height );
1341 /* Repaint the items */
1343 LISTBOX_UpdateScroll( wnd, descr );
1344 LISTBOX_InvalidateItems( wnd, descr, index );
1346 /* Move selection and focused item */
1348 if (index <= descr->selected_item) descr->selected_item++;
1349 if (index <= descr->focus_item)
1351 descr->focus_item++;
1352 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1355 /* If listbox was empty, set focus to the first item */
1357 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1362 /***********************************************************************
1363 * LISTBOX_InsertString
1365 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1368 LPSTR new_str = NULL;
1372 if (HAS_STRINGS(descr))
1374 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1376 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1380 else data = (DWORD)str;
1382 if (index == -1) index = descr->nb_items;
1383 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1385 if (new_str) HeapFree( descr->heap, 0, new_str );
1389 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1390 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1395 /***********************************************************************
1396 * LISTBOX_DeleteItem
1398 * Delete the content of an item. 'index' must be a valid index.
1400 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1402 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1403 * while Win95 sends it for all items with user data.
1404 * It's probably better to send it too often than not
1405 * often enough, so this is what we do here.
1407 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1409 DELETEITEMSTRUCT dis;
1410 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1412 dis.CtlType = ODT_LISTBOX;
1415 dis.hwndItem = wnd->hwndSelf;
1416 dis.itemData = descr->items[index].data;
1417 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1419 if (HAS_STRINGS(descr) && descr->items[index].str)
1420 HeapFree( descr->heap, 0, descr->items[index].str );
1424 /***********************************************************************
1425 * LISTBOX_RemoveItem
1427 * Remove an item from the listbox and delete its content.
1429 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1434 if (index == -1) index = descr->nb_items - 1;
1435 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1436 LISTBOX_DeleteItem( wnd, descr, index );
1438 /* Remove the item */
1440 item = &descr->items[index];
1441 if (index < descr->nb_items-1)
1442 RtlMoveMemory( item, item + 1,
1443 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1445 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1447 /* Shrink the item array if possible */
1449 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1450 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1452 max_items -= LB_ARRAY_GRANULARITY;
1453 item = HeapReAlloc( descr->heap, 0, descr->items,
1454 max_items * sizeof(LB_ITEMDATA) );
1455 if (item) descr->items = item;
1458 /* Repaint the items */
1460 LISTBOX_UpdateScroll( wnd, descr );
1461 LISTBOX_InvalidateItems( wnd, descr, index );
1463 /* Move selection and focused item */
1465 if (index <= descr->selected_item) descr->selected_item--;
1466 if (index <= descr->focus_item)
1468 descr->focus_item--;
1469 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1475 /***********************************************************************
1476 * LISTBOX_ResetContent
1478 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1482 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1483 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1484 descr->nb_items = 0;
1485 descr->top_item = 0;
1486 descr->selected_item = -1;
1487 descr->focus_item = 0;
1488 descr->anchor_item = -1;
1489 descr->items = NULL;
1490 LISTBOX_UpdateScroll( wnd, descr );
1491 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1495 /***********************************************************************
1498 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1502 if (HAS_STRINGS(descr)) return LB_ERR;
1503 /* FIXME: this is far from optimal... */
1504 if (count > descr->nb_items)
1506 while (count > descr->nb_items)
1507 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1510 else if (count < descr->nb_items)
1512 while (count < descr->nb_items)
1513 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1520 /***********************************************************************
1523 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1524 LPCSTR filespec, BOOL long_names )
1527 LRESULT ret = LB_OKAY;
1528 WIN32_FIND_DATAA entry;
1531 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1533 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1540 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1542 if (!(attrib & DDL_DIRECTORY) ||
1543 !strcmp( entry.cAlternateFileName, "." )) continue;
1544 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1545 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1547 else /* not a directory */
1549 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1550 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1552 if ((attrib & DDL_EXCLUSIVE) &&
1553 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1556 if (long_names) strcpy( buffer, entry.cFileName );
1557 else strcpy( buffer, entry.cAlternateFileName );
1559 if (!long_names) CharLowerA( buffer );
1560 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1561 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1563 } while (FindNextFileA( handle, &entry ));
1564 FindClose( handle );
1567 if ((ret >= 0) && (attrib & DDL_DRIVES))
1569 char buffer[] = "[-a-]";
1571 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1573 if (!DRIVE_IsValid(drive)) continue;
1574 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1582 /***********************************************************************
1583 * LISTBOX_HandleVScroll
1585 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1586 WPARAM wParam, LPARAM lParam )
1590 if (descr->style & LBS_MULTICOLUMN) return 0;
1591 switch(LOWORD(wParam))
1594 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1597 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1600 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1601 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1604 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1605 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1607 case SB_THUMBPOSITION:
1608 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1611 info.cbSize = sizeof(info);
1612 info.fMask = SIF_TRACKPOS;
1613 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1614 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1617 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1620 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1627 /***********************************************************************
1628 * LISTBOX_HandleHScroll
1630 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1631 WPARAM wParam, LPARAM lParam )
1636 if (descr->style & LBS_MULTICOLUMN)
1638 switch(LOWORD(wParam))
1641 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1645 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1649 page = descr->width / descr->column_width;
1650 if (page < 1) page = 1;
1651 LISTBOX_SetTopItem( wnd, descr,
1652 descr->top_item - page * descr->page_size, TRUE );
1655 page = descr->width / descr->column_width;
1656 if (page < 1) page = 1;
1657 LISTBOX_SetTopItem( wnd, descr,
1658 descr->top_item + page * descr->page_size, TRUE );
1660 case SB_THUMBPOSITION:
1661 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1665 info.cbSize = sizeof(info);
1666 info.fMask = SIF_TRACKPOS;
1667 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1668 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1672 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1675 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1679 else if (descr->horz_extent)
1681 switch(LOWORD(wParam))
1684 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1687 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1690 LISTBOX_SetHorizontalPos( wnd, descr,
1691 descr->horz_pos - descr->width );
1694 LISTBOX_SetHorizontalPos( wnd, descr,
1695 descr->horz_pos + descr->width );
1697 case SB_THUMBPOSITION:
1698 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1701 info.cbSize = sizeof(info);
1702 info.fMask = SIF_TRACKPOS;
1703 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1704 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1707 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1710 LISTBOX_SetHorizontalPos( wnd, descr,
1711 descr->horz_extent - descr->width );
1719 /***********************************************************************
1720 * LISTBOX_HandleLButtonDown
1722 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1723 WPARAM wParam, INT x, INT y )
1725 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1726 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1727 wnd->hwndSelf, x, y, index );
1728 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1731 if (descr->style & LBS_EXTENDEDSEL)
1733 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1734 if (wParam & MK_CONTROL)
1736 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1737 LISTBOX_SetSelection( wnd, descr, index,
1738 !descr->items[index].selected, FALSE );
1740 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1744 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1745 LISTBOX_SetSelection( wnd, descr, index,
1746 (!(descr->style & LBS_MULTIPLESEL) ||
1747 !descr->items[index].selected), FALSE );
1751 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1752 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1753 : descr->lphc->self->hwndSelf ) ;
1755 SetCapture( wnd->hwndSelf );
1756 if (index != -1 && !descr->lphc)
1758 if (descr->style & LBS_NOTIFY )
1759 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1760 MAKELPARAM( x, y ) );
1761 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1763 POINT pt = { x, y };
1764 if (DragDetect( wnd->hwndSelf, pt ))
1765 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1772 /***********************************************************************
1773 * LISTBOX_HandleLButtonUp
1775 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1777 if (LISTBOX_Timer != LB_TIMER_NONE)
1778 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1779 LISTBOX_Timer = LB_TIMER_NONE;
1780 if (GetCapture() == wnd->hwndSelf)
1783 if (descr->style & LBS_NOTIFY)
1784 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1790 /***********************************************************************
1791 * LISTBOX_HandleTimer
1793 * Handle scrolling upon a timer event.
1794 * Return TRUE if scrolling should continue.
1796 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1797 INT index, TIMER_DIRECTION dir )
1802 if (descr->top_item) index = descr->top_item - 1;
1806 if (descr->top_item) index -= descr->page_size;
1809 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1810 if (index == descr->focus_item) index++;
1811 if (index >= descr->nb_items) index = descr->nb_items - 1;
1813 case LB_TIMER_RIGHT:
1814 if (index + descr->page_size < descr->nb_items)
1815 index += descr->page_size;
1820 if (index == descr->focus_item) return FALSE;
1821 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1826 /***********************************************************************
1827 * LISTBOX_HandleSystemTimer
1829 * WM_SYSTIMER handler.
1831 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1833 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1835 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1836 LISTBOX_Timer = LB_TIMER_NONE;
1842 /***********************************************************************
1843 * LISTBOX_HandleMouseMove
1845 * WM_MOUSEMOVE handler.
1847 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1851 TIMER_DIRECTION dir;
1853 if (descr->style & LBS_MULTICOLUMN)
1856 else if (y >= descr->item_height * descr->page_size)
1857 y = descr->item_height * descr->page_size - 1;
1861 dir = LB_TIMER_LEFT;
1864 else if (x >= descr->width)
1866 dir = LB_TIMER_RIGHT;
1867 x = descr->width - 1;
1869 else dir = LB_TIMER_NONE; /* inside */
1873 if (y < 0) dir = LB_TIMER_UP; /* above */
1874 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1875 else dir = LB_TIMER_NONE; /* inside */
1878 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1879 if (index == -1) index = descr->focus_item;
1880 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1882 /* Start/stop the system timer */
1884 if (dir != LB_TIMER_NONE)
1885 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1886 else if (LISTBOX_Timer != LB_TIMER_NONE)
1887 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1888 LISTBOX_Timer = dir;
1892 /***********************************************************************
1893 * LISTBOX_HandleKeyDown
1895 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1898 if (descr->style & LBS_WANTKEYBOARDINPUT)
1900 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1901 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1903 if (caret == -2) return 0;
1905 if (caret == -1) switch(wParam)
1908 if (descr->style & LBS_MULTICOLUMN)
1910 if (descr->focus_item >= descr->page_size)
1911 caret = descr->focus_item - descr->page_size;
1916 caret = descr->focus_item - 1;
1917 if (caret < 0) caret = 0;
1920 if (descr->style & LBS_MULTICOLUMN)
1922 if (descr->focus_item + descr->page_size < descr->nb_items)
1923 caret = descr->focus_item + descr->page_size;
1928 caret = descr->focus_item + 1;
1929 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1932 if (descr->style & LBS_MULTICOLUMN)
1934 INT page = descr->width / descr->column_width;
1935 if (page < 1) page = 1;
1936 caret = descr->focus_item - (page * descr->page_size) + 1;
1938 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1939 if (caret < 0) caret = 0;
1942 if (descr->style & LBS_MULTICOLUMN)
1944 INT page = descr->width / descr->column_width;
1945 if (page < 1) page = 1;
1946 caret = descr->focus_item + (page * descr->page_size) - 1;
1948 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1949 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1955 caret = descr->nb_items - 1;
1958 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1959 else if (descr->style & LBS_MULTIPLESEL)
1961 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1962 !descr->items[descr->focus_item].selected,
1963 (descr->style & LBS_NOTIFY) != 0 );
1965 else if (descr->selected_item == -1)
1967 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1968 (descr->style & LBS_NOTIFY) != 0 );
1974 if ((descr->style & LBS_EXTENDEDSEL) &&
1975 !(GetKeyState( VK_SHIFT ) & 0x8000))
1976 descr->anchor_item = caret;
1977 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1978 if (descr->style & LBS_NOTIFY)
1980 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1982 /* make sure that combo parent doesn't hide us */
1983 descr->lphc->wState |= CBF_NOROLLUP;
1985 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1992 /***********************************************************************
1993 * LISTBOX_HandleChar
1995 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1999 char str[2] = { wParam & 0xff, '\0' };
2001 if (descr->style & LBS_WANTKEYBOARDINPUT)
2003 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2004 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2006 if (caret == -2) return 0;
2009 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2012 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2013 if (descr->style & LBS_NOTIFY)
2014 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2020 /***********************************************************************
2023 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2026 MEASUREITEMSTRUCT mis;
2029 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2031 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2033 HeapFree( GetProcessHeap(), 0, descr );
2036 GetClientRect( wnd->hwndSelf, &rect );
2037 descr->owner = GetParent( wnd->hwndSelf );
2038 descr->style = wnd->dwStyle;
2039 descr->width = rect.right - rect.left;
2040 descr->height = rect.bottom - rect.top;
2041 descr->items = NULL;
2042 descr->nb_items = 0;
2043 descr->top_item = 0;
2044 descr->selected_item = -1;
2045 descr->focus_item = 0;
2046 descr->anchor_item = -1;
2047 descr->item_height = 1;
2048 descr->page_size = 1;
2049 descr->column_width = 150;
2050 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2051 descr->horz_pos = 0;
2054 descr->caret_on = TRUE;
2056 descr->locale = 0; /* FIXME */
2061 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2062 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2063 descr->owner = lphc->self->hwndSelf;
2066 *(LB_DESCR **)wnd->wExtra = descr;
2068 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2070 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2071 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2072 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2073 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2075 if (descr->style & LBS_OWNERDRAWFIXED)
2077 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2079 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2080 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2084 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2086 mis.CtlType = ODT_LISTBOX;
2091 mis.itemHeight = descr->item_height;
2092 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2093 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2101 /***********************************************************************
2104 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2106 LISTBOX_ResetContent( wnd, descr );
2107 HeapDestroy( descr->heap );
2108 HeapFree( GetProcessHeap(), 0, descr );
2114 /***********************************************************************
2117 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2118 WPARAM wParam, LPARAM lParam )
2122 WND *wnd = WIN_FindWndPtr( hwnd );
2126 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2128 if (msg == WM_CREATE)
2130 if (!LISTBOX_Create( wnd, NULL ))
2135 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2136 hwnd, *(LB_DESCR **)wnd->wExtra );
2140 /* Ignore all other messages before we get a WM_CREATE */
2141 retvalue = DefWindowProcA( hwnd, msg, wParam, lParam );
2145 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2146 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2149 case LB_RESETCONTENT16:
2150 case LB_RESETCONTENT:
2151 LISTBOX_ResetContent( wnd, descr );
2155 case LB_ADDSTRING16:
2156 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2159 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2160 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2163 case LB_INSERTSTRING16:
2164 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2165 wParam = (INT)(INT16)wParam;
2167 case LB_INSERTSTRING:
2168 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2172 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2175 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2176 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2179 case LB_DELETESTRING16:
2180 case LB_DELETESTRING:
2181 retvalue = LISTBOX_RemoveItem( wnd, descr, wParam );
2184 case LB_GETITEMDATA16:
2185 case LB_GETITEMDATA:
2186 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2191 retvalue = descr->items[wParam].data;
2194 case LB_SETITEMDATA16:
2195 case LB_SETITEMDATA:
2196 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2201 descr->items[wParam].data = (DWORD)lParam;
2207 retvalue = descr->nb_items;
2211 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2214 retvalue = LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2217 case LB_GETTEXTLEN16:
2220 if (wParam >= descr->nb_items)
2225 retvalue = (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2229 case LB_GETCURSEL16:
2231 if (descr->nb_items==0)
2235 retvalue = descr->selected_item;
2236 if (retvalue == -1) retvalue = descr->focus_item;
2238 /* otherwise, if the user tries to move the selection with the */
2239 /* arrow keys, we will give the application something to choke on */
2242 case LB_GETTOPINDEX16:
2243 case LB_GETTOPINDEX:
2244 retvalue = descr->top_item;
2247 case LB_GETITEMHEIGHT16:
2248 case LB_GETITEMHEIGHT:
2249 retvalue = LISTBOX_GetItemHeight( wnd, descr, wParam );
2252 case LB_SETITEMHEIGHT16:
2253 lParam = LOWORD(lParam);
2255 case LB_SETITEMHEIGHT:
2256 retvalue = LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2259 case LB_ITEMFROMPOINT:
2261 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
2262 RECT rect = { 0, 0, descr->width, descr->height };
2263 retvalue = MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2264 PtInRect( &rect, pt ) );
2268 case LB_SETCARETINDEX16:
2269 case LB_SETCARETINDEX:
2270 retvalue = LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2273 case LB_GETCARETINDEX16:
2274 case LB_GETCARETINDEX:
2275 retvalue = descr->focus_item;
2278 case LB_SETTOPINDEX16:
2279 case LB_SETTOPINDEX:
2280 retvalue = LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2283 case LB_SETCOLUMNWIDTH16:
2284 case LB_SETCOLUMNWIDTH:
2285 retvalue = LISTBOX_SetColumnWidth( wnd, descr, wParam );
2288 case LB_GETITEMRECT16:
2291 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2292 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2297 case LB_GETITEMRECT:
2298 retvalue = LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2301 case LB_FINDSTRING16:
2302 wParam = (INT)(INT16)wParam;
2303 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2306 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2309 case LB_FINDSTRINGEXACT16:
2310 wParam = (INT)(INT16)wParam;
2311 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2313 case LB_FINDSTRINGEXACT:
2314 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2317 case LB_SELECTSTRING16:
2318 wParam = (INT)(INT16)wParam;
2319 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2321 case LB_SELECTSTRING:
2323 INT index = LISTBOX_FindString( wnd, descr, wParam,
2324 (LPCSTR)lParam, FALSE );
2325 if (index == LB_ERR)
2330 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2336 wParam = (INT)(INT16)wParam;
2339 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2344 retvalue = descr->items[wParam].selected;
2348 lParam = (INT)(INT16)lParam;
2351 retvalue = LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2354 case LB_SETCURSEL16:
2355 wParam = (INT)(INT16)wParam;
2358 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2359 retvalue = LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2362 case LB_GETSELCOUNT16:
2363 case LB_GETSELCOUNT:
2364 retvalue = LISTBOX_GetSelCount( wnd, descr );
2367 case LB_GETSELITEMS16:
2368 retvalue = LISTBOX_GetSelItems16( wnd, descr, wParam,
2369 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2372 case LB_GETSELITEMS:
2373 retvalue = LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2376 case LB_SELITEMRANGE16:
2377 case LB_SELITEMRANGE:
2378 if (LOWORD(lParam) <= HIWORD(lParam))
2380 retvalue = LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2381 HIWORD(lParam), wParam );
2385 retvalue = LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2386 LOWORD(lParam), wParam );
2390 case LB_SELITEMRANGEEX16:
2391 case LB_SELITEMRANGEEX:
2392 if ((INT)lParam >= (INT)wParam)
2393 retvalue = LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2395 retvalue = LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2398 case LB_GETHORIZONTALEXTENT16:
2399 case LB_GETHORIZONTALEXTENT:
2400 retvalue = descr->horz_extent;
2403 case LB_SETHORIZONTALEXTENT16:
2404 case LB_SETHORIZONTALEXTENT:
2405 retvalue = LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2408 case LB_GETANCHORINDEX16:
2409 case LB_GETANCHORINDEX:
2410 retvalue = descr->anchor_item;
2413 case LB_SETANCHORINDEX16:
2414 wParam = (INT)(INT16)wParam;
2416 case LB_SETANCHORINDEX:
2417 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2422 descr->anchor_item = (INT)wParam;
2427 retvalue = LISTBOX_Directory( wnd, descr, wParam,
2428 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2432 retvalue = LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2436 retvalue = descr->locale;
2440 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2444 case LB_INITSTORAGE:
2445 retvalue = LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2449 retvalue = LISTBOX_SetCount( wnd, descr, (INT)wParam );
2452 case LB_SETTABSTOPS16:
2453 retvalue = LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2454 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2457 case LB_SETTABSTOPS:
2458 retvalue = LISTBOX_SetTabStops( wnd, descr, wParam,
2459 (LPINT)lParam, FALSE );
2464 if (descr->caret_on)
2469 descr->caret_on = TRUE;
2470 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2471 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2477 if (!descr->caret_on)
2482 descr->caret_on = FALSE;
2483 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2484 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2489 retvalue = LISTBOX_Destroy( wnd, descr );
2493 InvalidateRect( hwnd, NULL, TRUE );
2498 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2503 retvalue =DLGC_WANTARROWS | DLGC_WANTCHARS;
2508 HDC hdc = ( wParam ) ? ((HDC)wParam)
2509 : BeginPaint( hwnd, &ps );
2510 ret = LISTBOX_Paint( wnd, descr, hdc );
2511 if( !wParam ) EndPaint( hwnd, &ps );
2516 LISTBOX_UpdateSize( wnd, descr );
2520 retvalue =descr->font;
2523 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2524 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2528 descr->caret_on = TRUE;
2529 if (descr->focus_item != -1)
2530 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2531 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2535 if ((descr->focus_item != -1) && descr->caret_on)
2536 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2537 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2541 retvalue =LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2544 retvalue =LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2546 case WM_LBUTTONDOWN:
2547 retvalue =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2548 (INT16)LOWORD(lParam),
2549 (INT16)HIWORD(lParam) );
2551 case WM_LBUTTONDBLCLK:
2552 if (descr->style & LBS_NOTIFY)
2553 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2557 if (GetCapture() == hwnd)
2558 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2559 (INT16)HIWORD(lParam) );
2563 retvalue =LISTBOX_HandleLButtonUp( wnd, descr );
2566 retvalue =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2569 retvalue =LISTBOX_HandleChar( wnd, descr, wParam );
2572 retvalue =LISTBOX_HandleSystemTimer( wnd, descr );
2575 if (IS_OWNERDRAW(descr))
2577 RECT rect = { 0, 0, descr->width, descr->height };
2578 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2579 wParam, (LPARAM)wnd->hwndSelf );
2580 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2587 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2593 case WM_QUERYDROPOBJECT:
2598 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2599 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2601 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2607 if (TWEAK_WineLook > WIN31_LOOK)
2608 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2609 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2612 if ((msg >= WM_USER) && (msg < 0xc000))
2613 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2614 hwnd, msg, wParam, lParam );
2615 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2620 WIN_ReleaseWndPtr(wnd);
2624 /***********************************************************************
2627 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2629 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2633 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2636 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2638 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2639 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2640 WIN_ReleaseWndPtr(wnd);
2643 WIN_ReleaseWndPtr(wnd);
2648 /***********************************************************************
2651 * NOTE: in Windows, winproc address of the ComboLBox is the same
2652 * as that of the Listbox.
2654 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2655 WPARAM wParam, LPARAM lParam )
2658 WND *wnd = WIN_FindWndPtr( hwnd );
2662 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2664 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2665 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2667 if( descr || msg == WM_CREATE )
2669 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2674 #define lpcs ((LPCREATESTRUCTA)lParam)
2675 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2676 (UINT)lpcs->lpCreateParams);
2678 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2680 lRet =LISTBOX_Create( wnd, lphc );
2682 case WM_LBUTTONDOWN:
2683 lRet =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2684 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2686 /* avoid activation at all costs */
2688 case WM_MOUSEACTIVATE:
2689 lRet =MA_NOACTIVATE;
2695 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2697 /* for some reason(?) Windows makes it possible to
2698 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2700 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2701 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2702 && (wParam == VK_DOWN || wParam == VK_UP)) )
2704 COMBO_FlipListbox( lphc, FALSE );
2709 lRet =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2712 case LB_SETCURSEL16:
2714 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2715 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2718 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2723 lRet =ListBoxWndProc( hwnd, msg, wParam, lParam );
2727 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2729 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2732 WIN_ReleaseWndPtr(wnd);