4 * Copyright 1996 Alexandre Julliard
8 #include "wine/winuser16.h"
26 /* Items array granularity */
27 #define LB_ARRAY_GRANULARITY 16
29 /* Scrolling timeout in ms */
30 #define LB_SCROLL_TIMEOUT 50
32 /* Listbox system timer id */
38 LPSTR str; /* Item text */
39 BOOL32 selected; /* Is item selected? */
40 UINT32 height; /* Item height (only for OWNERDRAWVARIABLE) */
41 DWORD data; /* User data */
44 /* Listbox structure */
47 HANDLE32 heap; /* Heap for this listbox */
48 HWND32 owner; /* Owner window to send notifications to */
49 UINT32 style; /* Window style */
50 INT32 width; /* Window width */
51 INT32 height; /* Window height */
52 LB_ITEMDATA *items; /* Array of items */
53 INT32 nb_items; /* Number of items */
54 INT32 top_item; /* Top visible item */
55 INT32 selected_item; /* Selected item */
56 INT32 focus_item; /* Item that has the focus */
57 INT32 anchor_item; /* Anchor item for extended selection */
58 INT32 item_height; /* Default item height */
59 INT32 page_size; /* Items per listbox page */
60 INT32 column_width; /* Column width for multi-column listboxes */
61 INT32 horz_extent; /* Horizontal extent (0 if no hscroll) */
62 INT32 horz_pos; /* Horizontal position */
63 INT32 nb_tabs; /* Number of tabs in array */
64 INT32 *tabs; /* Array of tabs */
65 BOOL32 caret_on; /* Is caret on? */
66 HFONT32 font; /* Current font */
67 LCID locale; /* Current locale for string comparisons */
68 LPHEADCOMBO lphc; /* ComboLBox */
72 #define IS_OWNERDRAW(descr) \
73 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
75 #define HAS_STRINGS(descr) \
76 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
78 #define SEND_NOTIFICATION(wnd,descr,code) \
79 (SendMessage32A( (descr)->owner, WM_COMMAND, \
80 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
82 /* Current timer status */
92 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
95 /***********************************************************************
98 void LISTBOX_Dump( WND *wnd )
102 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
104 DUMP( "Listbox:\n" );
105 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
106 wnd->hwndSelf, (UINT32)descr, descr->heap, descr->nb_items,
108 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
110 DUMP( "%4d: %-40s %d %08lx %3d\n",
111 i, item->str, item->selected, item->data, item->height );
116 /***********************************************************************
117 * LISTBOX_GetCurrentPageSize
119 * Return the current page size
121 static INT32 LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
124 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
125 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
127 if ((height += descr->items[i].height) > descr->height) break;
129 if (i == descr->top_item) return 1;
130 else return i - descr->top_item;
134 /***********************************************************************
135 * LISTBOX_GetMaxTopIndex
137 * Return the maximum possible index for the top of the listbox.
139 static INT32 LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
143 if (descr->style & LBS_OWNERDRAWVARIABLE)
145 page = descr->height;
146 for (max = descr->nb_items - 1; max >= 0; max--)
147 if ((page -= descr->items[max].height) < 0) break;
148 if (max < descr->nb_items - 1) max++;
150 else if (descr->style & LBS_MULTICOLUMN)
152 if ((page = descr->width / descr->column_width) < 1) page = 1;
153 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
154 max = (max - page) * descr->page_size;
158 max = descr->nb_items - descr->page_size;
160 if (max < 0) max = 0;
165 /***********************************************************************
166 * LISTBOX_UpdateScroll
168 * Update the scrollbars. Should be called whenever the content
169 * of the listbox changes.
171 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
175 if (!(descr->style & WS_VSCROLL)) return;
176 /* It is important that we check descr->style, and not wnd->dwStyle,
177 for WS_VSCROLL, as the former is exactly the one passed in
178 argument to CreateWindow.
179 In Windows (and from now on in Wine :) a listbox created
180 with such a style (no WS_SCROLL) does not update
181 the scrollbar with listbox-related data, thus letting
182 the programmer use it for his/her own purposes. */
184 if (descr->style & LBS_NOREDRAW) return;
185 info.cbSize = sizeof(info);
187 if (descr->style & LBS_MULTICOLUMN)
190 info.nMax = (descr->nb_items - 1) / descr->page_size;
191 info.nPos = descr->top_item / descr->page_size;
192 info.nPage = descr->width / descr->column_width;
193 if (info.nPage < 1) info.nPage = 1;
194 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
195 if (descr->style & LBS_DISABLENOSCROLL)
196 info.fMask |= SIF_DISABLENOSCROLL;
197 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
199 info.fMask = SIF_RANGE;
200 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
205 info.nMax = descr->nb_items - 1;
206 info.nPos = descr->top_item;
207 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
208 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
209 if (descr->style & LBS_DISABLENOSCROLL)
210 info.fMask |= SIF_DISABLENOSCROLL;
211 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
213 if (descr->horz_extent)
216 info.nMax = descr->horz_extent - 1;
217 info.nPos = descr->horz_pos;
218 info.nPage = descr->width;
219 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
220 if (descr->style & LBS_DISABLENOSCROLL)
221 info.fMask |= SIF_DISABLENOSCROLL;
222 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
228 /***********************************************************************
231 * Set the top item of the listbox, scrolling up or down if necessary.
233 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index,
236 INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr );
237 if (index > max) index = max;
238 if (index < 0) index = 0;
239 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
240 if (descr->top_item == index) return LB_OKAY;
241 if (descr->style & LBS_MULTICOLUMN)
243 INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width;
244 if (scroll && (abs(diff) < descr->width))
245 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
246 SW_INVALIDATE | SW_ERASE );
253 if (descr->style & LBS_OWNERDRAWVARIABLE)
257 if (index > descr->top_item)
259 for (i = index - 1; i >= descr->top_item; i--)
260 diff -= descr->items[i].height;
264 for (i = index; i < descr->top_item; i++)
265 diff += descr->items[i].height;
269 diff = (descr->top_item - index) * descr->item_height;
271 if (abs(diff) < descr->height)
272 ScrollWindowEx32( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
273 SW_INVALIDATE | SW_ERASE );
277 if (!scroll) InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
278 descr->top_item = index;
279 LISTBOX_UpdateScroll( wnd, descr );
284 /***********************************************************************
287 * Update the page size. Should be called when the size of
288 * the client area or the item height changes.
290 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
294 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
295 if (page_size == descr->page_size) return;
296 descr->page_size = page_size;
297 if (descr->style & LBS_MULTICOLUMN)
298 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
299 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
303 /***********************************************************************
306 * Update the size of the listbox. Should be called when the size of
307 * the client area changes.
309 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
313 GetClientRect32( wnd->hwndSelf, &rect );
314 descr->width = rect.right - rect.left;
315 descr->height = rect.bottom - rect.top;
316 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
318 if ((descr->height > descr->item_height) &&
319 (descr->height % descr->item_height))
321 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
322 wnd->hwndSelf, descr->height,
323 descr->height - descr->height%descr->item_height );
324 SetWindowPos32( wnd->hwndSelf, 0, 0, 0,
325 wnd->rectWindow.right - wnd->rectWindow.left,
326 wnd->rectWindow.bottom - wnd->rectWindow.top -
327 (descr->height % descr->item_height),
328 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
332 TRACE(listbox, "[%04x]: new size = %d,%d\n",
333 wnd->hwndSelf, descr->width, descr->height );
334 LISTBOX_UpdatePage( wnd, descr );
335 LISTBOX_UpdateScroll( wnd, descr );
339 /***********************************************************************
340 * LISTBOX_GetItemRect
342 * Get the rectangle enclosing an item, in listbox client coordinates.
343 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
345 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT32 index,
348 /* Index <= 0 is legal even on empty listboxes */
349 if (index && (index >= descr->nb_items)) return -1;
350 SetRect32( rect, 0, 0, descr->width, descr->height );
351 if (descr->style & LBS_MULTICOLUMN)
353 INT32 col = (index / descr->page_size) -
354 (descr->top_item / descr->page_size);
355 rect->left += col * descr->column_width;
356 rect->right = rect->left + descr->column_width;
357 rect->top += (index % descr->page_size) * descr->item_height;
358 rect->bottom = rect->top + descr->item_height;
360 else if (descr->style & LBS_OWNERDRAWVARIABLE)
363 rect->right += descr->horz_pos;
364 if ((index >= 0) && (index < descr->nb_items))
366 if (index < descr->top_item)
368 for (i = descr->top_item-1; i >= index; i--)
369 rect->top -= descr->items[i].height;
373 for (i = descr->top_item; i < index; i++)
374 rect->top += descr->items[i].height;
376 rect->bottom = rect->top + descr->items[index].height;
382 rect->top += (index - descr->top_item) * descr->item_height;
383 rect->bottom = rect->top + descr->item_height;
384 rect->right += descr->horz_pos;
387 return ((rect->left < descr->width) && (rect->right > 0) &&
388 (rect->top < descr->height) && (rect->bottom > 0));
392 /***********************************************************************
393 * LISTBOX_GetItemFromPoint
395 * Return the item nearest from point (x,y) (in client coordinates).
397 static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
400 INT32 index = descr->top_item;
402 if (!descr->nb_items) return -1; /* No items */
403 if (descr->style & LBS_OWNERDRAWVARIABLE)
408 while (index < descr->nb_items)
410 if ((pos += descr->items[index].height) > y) break;
419 if ((pos -= descr->items[index].height) <= y) break;
423 else if (descr->style & LBS_MULTICOLUMN)
425 if (y >= descr->item_height * descr->page_size) return -1;
426 if (y >= 0) index += y / descr->item_height;
427 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
428 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
432 index += (y / descr->item_height);
434 if (index < 0) return 0;
435 if (index >= descr->nb_items) return -1;
440 /***********************************************************************
445 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc,
446 const RECT32 *rect, INT32 index, UINT32 action )
448 LB_ITEMDATA *item = NULL;
449 if (index < descr->nb_items) item = &descr->items[index];
451 if (IS_OWNERDRAW(descr))
453 DRAWITEMSTRUCT32 dis;
454 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
458 if (action == ODA_FOCUS)
459 DrawFocusRect32( hdc, rect );
461 FIXME(listbox,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
464 dis.CtlType = ODT_LISTBOX;
466 dis.hwndItem = wnd->hwndSelf;
467 dis.itemAction = action;
471 if (item && item->selected) dis.itemState |= ODS_SELECTED;
472 if ((descr->focus_item == index) &&
474 (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
475 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
476 dis.itemData = item ? item->data : 0;
478 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
479 "state=%02x rect=%d,%d-%d,%d\n",
480 wnd->hwndSelf, index, item ? item->str : "", action,
481 dis.itemState, rect->left, rect->top,
482 rect->right, rect->bottom );
483 SendMessage32A(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
487 COLORREF oldText = 0, oldBk = 0;
489 if (action == ODA_FOCUS)
491 DrawFocusRect32( hdc, rect );
494 if (item && item->selected)
496 oldBk = SetBkColor32( hdc, GetSysColor32( COLOR_HIGHLIGHT ) );
497 oldText = SetTextColor32( hdc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
500 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
501 "rect=%d,%d-%d,%d\n",
502 wnd->hwndSelf, index, item ? item->str : "", action,
503 rect->left, rect->top, rect->right, rect->bottom );
505 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
506 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
507 else if (!(descr->style & LBS_USETABSTOPS))
508 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
509 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
510 strlen(item->str), NULL );
513 /* Output empty string to paint background in the full width. */
514 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
515 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
516 TabbedTextOut32A( hdc, rect->left + 1 , rect->top + 1,
517 item->str, strlen(item->str),
518 descr->nb_tabs, descr->tabs, 0);
520 if (item && item->selected)
522 SetBkColor32( hdc, oldBk );
523 SetTextColor32( hdc, oldText );
525 if ((descr->focus_item == index) &&
527 (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect );
532 /***********************************************************************
535 * Change the redraw flag.
537 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on )
541 if (!(descr->style & LBS_NOREDRAW)) return;
542 descr->style &= ~LBS_NOREDRAW;
543 LISTBOX_UpdateScroll( wnd, descr );
545 else descr->style |= LBS_NOREDRAW;
549 /***********************************************************************
550 * LISTBOX_RepaintItem
552 * Repaint a single item synchronously.
554 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index,
560 HBRUSH32 hbrush, oldBrush = 0;
562 if (descr->style & LBS_NOREDRAW) return;
563 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
564 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE ))) return;
565 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
566 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
567 hdc, (LPARAM)wnd->hwndSelf );
568 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
569 if (wnd->dwStyle & WS_DISABLED)
570 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
571 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
572 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
573 if (oldFont) SelectObject32( hdc, oldFont );
574 if (oldBrush) SelectObject32( hdc, oldBrush );
575 ReleaseDC32( wnd->hwndSelf, hdc );
579 /***********************************************************************
580 * LISTBOX_InitStorage
582 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items,
587 nb_items += LB_ARRAY_GRANULARITY - 1;
588 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
590 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
591 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
592 nb_items * sizeof(LB_ITEMDATA) )))
594 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
602 /***********************************************************************
603 * LISTBOX_SetTabStops
605 static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count,
606 LPINT32 tabs, BOOL32 short_ints )
608 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
609 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
610 if (!(descr->nb_tabs = count))
615 /* FIXME: count = 1 */
616 if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0,
617 descr->nb_tabs * sizeof(INT32) )))
622 LPINT16 p = (LPINT16)tabs;
623 dbg_decl_str(listbox, 256);
625 for (i = 0; i < descr->nb_tabs; i++) {
626 descr->tabs[i] = *p++<<1; /* FIXME */
627 if(TRACE_ON(listbox))
628 dsprintf(listbox, "%hd ", descr->tabs[i]);
630 TRACE(listbox, "[%04x]: settabstops %s\n",
631 wnd->hwndSelf, dbg_str(listbox));
633 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) );
634 /* FIXME: repaint the window? */
639 /***********************************************************************
642 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index,
645 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
646 if (HAS_STRINGS(descr))
649 return strlen(descr->items[index].str);
650 lstrcpy32A( buffer, descr->items[index].str );
651 return strlen(buffer);
654 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
655 return sizeof(DWORD);
660 /***********************************************************************
661 * LISTBOX_FindStringPos
663 * Find the nearest string located before a given string in sort order.
664 * If 'exact' is TRUE, return an error if we don't get an exact match.
666 static INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
669 INT32 index, min, max, res = -1;
671 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
673 max = descr->nb_items;
676 index = (min + max) / 2;
677 if (HAS_STRINGS(descr))
678 res = lstrcmpi32A( descr->items[index].str, str );
681 COMPAREITEMSTRUCT32 cis;
682 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
684 cis.CtlType = ODT_LISTBOX;
686 cis.hwndItem = wnd->hwndSelf;
688 cis.itemData1 = descr->items[index].data;
690 cis.itemData2 = (DWORD)str;
691 cis.dwLocaleId = descr->locale;
692 res = SendMessage32A( descr->owner, WM_COMPAREITEM,
695 if (!res) return index;
696 if (res > 0) max = index;
697 else min = index + 1;
699 return exact ? -1 : max;
703 /***********************************************************************
704 * LISTBOX_FindFileStrPos
706 * Find the nearest string located before a given string in directory
707 * sort order (i.e. first files, then directories, then drives).
709 static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
711 INT32 min, max, res = -1;
713 if (!HAS_STRINGS(descr))
714 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
716 max = descr->nb_items;
719 INT32 index = (min + max) / 2;
720 const char *p = descr->items[index].str;
721 if (*p == '[') /* drive or directory */
723 if (*str != '[') res = -1;
724 else if (p[1] == '-') /* drive */
726 if (str[1] == '-') res = str[2] - p[2];
731 if (str[1] == '-') res = 1;
732 else res = lstrcmpi32A( str, p );
737 if (*str == '[') res = 1;
738 else res = lstrcmpi32A( str, p );
740 if (!res) return index;
741 if (res < 0) max = index;
742 else min = index + 1;
748 /***********************************************************************
751 * Find the item beginning with a given string.
753 static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start,
754 LPCSTR str, BOOL32 exact )
759 if (start >= descr->nb_items) start = -1;
760 item = descr->items + start + 1;
761 if (HAS_STRINGS(descr))
763 if (!str) return LB_ERR;
766 for (i = start + 1; i < descr->nb_items; i++, item++)
767 if (!lstrcmpi32A( str, item->str )) return i;
768 for (i = 0, item = descr->items; i <= start; i++, item++)
769 if (!lstrcmpi32A( str, item->str )) return i;
773 /* Special case for drives and directories: ignore prefix */
774 #define CHECK_DRIVE(item) \
775 if ((item)->str[0] == '[') \
777 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
778 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
782 INT32 len = strlen(str);
783 for (i = start + 1; i < descr->nb_items; i++, item++)
785 if (!lstrncmpi32A( str, item->str, len )) return i;
788 for (i = 0, item = descr->items; i <= start; i++, item++)
790 if (!lstrncmpi32A( str, item->str, len )) return i;
798 if (exact && (descr->style & LBS_SORT))
799 /* If sorted, use a WM_COMPAREITEM binary search */
800 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
802 /* Otherwise use a linear search */
803 for (i = start + 1; i < descr->nb_items; i++, item++)
804 if (item->data == (DWORD)str) return i;
805 for (i = 0, item = descr->items; i <= start; i++, item++)
806 if (item->data == (DWORD)str) return i;
812 /***********************************************************************
813 * LISTBOX_GetSelCount
815 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
818 LB_ITEMDATA *item = descr->items;
820 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
821 for (i = count = 0; i < descr->nb_items; i++, item++)
822 if (item->selected) count++;
827 /***********************************************************************
828 * LISTBOX_GetSelItems16
830 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
834 LB_ITEMDATA *item = descr->items;
836 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
837 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
838 if (item->selected) array[count++] = (INT16)i;
843 /***********************************************************************
844 * LISTBOX_GetSelItems32
846 static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max,
850 LB_ITEMDATA *item = descr->items;
852 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
853 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
854 if (item->selected) array[count++] = i;
859 /***********************************************************************
862 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc )
864 INT32 i, col_pos = descr->page_size - 1;
867 HBRUSH32 hbrush, oldBrush = 0;
869 SetRect32( &rect, 0, 0, descr->width, descr->height );
870 if (descr->style & LBS_NOREDRAW) return 0;
871 if (descr->style & LBS_MULTICOLUMN)
872 rect.right = rect.left + descr->column_width;
873 else if (descr->horz_pos)
875 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
876 rect.right += descr->horz_pos;
879 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
880 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
881 hdc, (LPARAM)wnd->hwndSelf );
882 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
883 if (wnd->dwStyle & WS_DISABLED)
884 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
886 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
887 (GetFocus32() == wnd->hwndSelf))
889 /* Special case for empty listbox: paint focus rect */
890 rect.bottom = rect.top + descr->item_height;
891 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
893 rect.top = rect.bottom;
896 for (i = descr->top_item; i < descr->nb_items; i++)
898 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
899 rect.bottom = rect.top + descr->item_height;
901 rect.bottom = rect.top + descr->items[i].height;
903 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
904 rect.top = rect.bottom;
906 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
908 if (!IS_OWNERDRAW(descr))
910 /* Clear the bottom of the column */
911 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
912 if (rect.top < descr->height)
914 rect.bottom = descr->height;
915 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
916 &rect, NULL, 0, NULL );
920 /* Go to the next column */
921 rect.left += descr->column_width;
922 rect.right += descr->column_width;
924 col_pos = descr->page_size - 1;
929 if (rect.top >= descr->height) break;
933 if (!IS_OWNERDRAW(descr))
935 /* Clear the remainder of the client area */
936 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
937 if (rect.top < descr->height)
939 rect.bottom = descr->height;
940 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
941 &rect, NULL, 0, NULL );
943 if (rect.right < descr->width)
945 rect.left = rect.right;
946 rect.right = descr->width;
948 rect.bottom = descr->height;
949 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
950 &rect, NULL, 0, NULL );
953 if (oldFont) SelectObject32( hdc, oldFont );
954 if (oldBrush) SelectObject32( hdc, oldBrush );
959 /***********************************************************************
960 * LISTBOX_InvalidateItems
962 * Invalidate all items from a given item. If the specified item is not
963 * visible, nothing happens.
965 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index )
969 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
971 rect.bottom = descr->height;
972 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
973 if (descr->style & LBS_MULTICOLUMN)
975 /* Repaint the other columns */
976 rect.left = rect.right;
977 rect.right = descr->width;
979 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
985 /***********************************************************************
986 * LISTBOX_GetItemHeight
988 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index )
990 if (descr->style & LBS_OWNERDRAWVARIABLE)
992 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
993 return descr->items[index].height;
995 else return descr->item_height;
999 /***********************************************************************
1000 * LISTBOX_SetItemHeight
1002 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index,
1005 if (!height) height = 1;
1007 if (descr->style & LBS_OWNERDRAWVARIABLE)
1009 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1010 TRACE(listbox, "[%04x]: item %d height = %d\n",
1011 wnd->hwndSelf, index, height );
1012 descr->items[index].height = height;
1013 LISTBOX_UpdateScroll( wnd, descr );
1014 LISTBOX_InvalidateItems( wnd, descr, index );
1016 else if (height != descr->item_height)
1018 TRACE(listbox, "[%04x]: new height = %d\n",
1019 wnd->hwndSelf, height );
1020 descr->item_height = height;
1021 LISTBOX_UpdatePage( wnd, descr );
1022 LISTBOX_UpdateScroll( wnd, descr );
1023 InvalidateRect32( wnd->hwndSelf, 0, TRUE );
1029 /***********************************************************************
1030 * LISTBOX_SetHorizontalPos
1032 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos )
1036 if (pos > descr->horz_extent - descr->width)
1037 pos = descr->horz_extent - descr->width;
1038 if (pos < 0) pos = 0;
1039 if (!(diff = descr->horz_pos - pos)) return;
1040 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1041 wnd->hwndSelf, pos );
1042 descr->horz_pos = pos;
1043 LISTBOX_UpdateScroll( wnd, descr );
1044 if (abs(diff) < descr->width)
1045 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1046 SW_INVALIDATE | SW_ERASE );
1048 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1052 /***********************************************************************
1053 * LISTBOX_SetHorizontalExtent
1055 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1058 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1060 if (extent <= 0) extent = 1;
1061 if (extent == descr->horz_extent) return LB_OKAY;
1062 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1063 wnd->hwndSelf, extent );
1064 descr->horz_extent = extent;
1065 if (descr->horz_pos > extent - descr->width)
1066 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1068 LISTBOX_UpdateScroll( wnd, descr );
1073 /***********************************************************************
1074 * LISTBOX_SetColumnWidth
1076 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width)
1078 width += 2; /* For left and right margin */
1079 if (width == descr->column_width) return LB_OKAY;
1080 TRACE(listbox, "[%04x]: new column width = %d\n",
1081 wnd->hwndSelf, width );
1082 descr->column_width = width;
1083 LISTBOX_UpdatePage( wnd, descr );
1088 /***********************************************************************
1091 * Returns the item height.
1093 static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font )
1096 HFONT32 oldFont = 0;
1101 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
1103 ERR(listbox, "unable to get DC.\n" );
1106 if (font) oldFont = SelectObject32( hdc, font );
1107 GetTextMetrics32A( hdc, &tm );
1108 if (oldFont) SelectObject32( hdc, oldFont );
1109 ReleaseDC32( wnd->hwndSelf, hdc );
1110 if (!IS_OWNERDRAW(descr))
1111 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1112 return tm.tmHeight ;
1116 /***********************************************************************
1117 * LISTBOX_MakeItemVisible
1119 * Make sure that a given item is partially or fully visible.
1121 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index,
1126 if (index <= descr->top_item) top = index;
1127 else if (descr->style & LBS_MULTICOLUMN)
1129 INT32 cols = descr->width;
1130 if (!fully) cols += descr->column_width - 1;
1131 if (cols >= descr->column_width) cols /= descr->column_width;
1133 if (index < descr->top_item + (descr->page_size * cols)) return;
1134 top = index - descr->page_size * (cols - 1);
1136 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1138 INT32 height = fully ? descr->items[index].height : 1;
1139 for (top = index; top > descr->top_item; top--)
1140 if ((height += descr->items[top-1].height) > descr->height) break;
1144 if (index < descr->top_item + descr->page_size) return;
1145 if (!fully && (index == descr->top_item + descr->page_size) &&
1146 (descr->height > (descr->page_size * descr->item_height))) return;
1147 top = index - descr->page_size + 1;
1149 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1153 /***********************************************************************
1154 * LISTBOX_SelectItemRange
1156 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1158 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first,
1159 INT32 last, BOOL32 on )
1163 /* A few sanity checks */
1165 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1166 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1167 if (last == -1) last = descr->nb_items - 1;
1168 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1169 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1170 /* selected_item reflects last selected/unselected item on multiple sel */
1171 descr->selected_item = last;
1173 if (on) /* Turn selection on */
1175 for (i = first; i <= last; i++)
1177 if (descr->items[i].selected) continue;
1178 descr->items[i].selected = TRUE;
1179 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1182 else /* Turn selection off */
1184 for (i = first; i <= last; i++)
1186 if (!descr->items[i].selected) continue;
1187 descr->items[i].selected = FALSE;
1188 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1195 /***********************************************************************
1196 * LISTBOX_SetCaretIndex
1199 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1202 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index,
1203 BOOL32 fully_visible )
1205 INT32 oldfocus = descr->focus_item;
1207 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1208 if (index == oldfocus) return LB_OKAY;
1209 descr->focus_item = index;
1210 if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1211 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1213 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1214 if (descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1215 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1221 /***********************************************************************
1222 * LISTBOX_SetSelection
1224 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index,
1225 BOOL32 on, BOOL32 send_notify )
1227 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1228 if (descr->style & LBS_MULTIPLESEL)
1230 if (index == -1) /* Select all items */
1231 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1232 else /* Only one item */
1233 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1237 INT32 oldsel = descr->selected_item;
1238 if (index == oldsel) return LB_OKAY;
1239 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1240 if (index != -1) descr->items[index].selected = TRUE;
1241 descr->selected_item = index;
1242 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1243 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1244 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1245 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1247 if( descr->lphc ) /* set selection change flag for parent combo */
1248 descr->lphc->wState |= CBF_SELCHANGE;
1254 /***********************************************************************
1257 * Change the caret position and extend the selection to the new caret.
1259 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index,
1260 BOOL32 fully_visible )
1262 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1263 if (descr->style & LBS_EXTENDEDSEL)
1265 if (descr->anchor_item != -1)
1267 INT32 first = MIN( descr->focus_item, descr->anchor_item );
1268 INT32 last = MAX( descr->focus_item, descr->anchor_item );
1270 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1271 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1272 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1275 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1277 /* Set selection to new caret item */
1278 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1283 /***********************************************************************
1284 * LISTBOX_InsertItem
1286 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index,
1287 LPSTR str, DWORD data )
1292 if (index == -1) index = descr->nb_items;
1293 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1294 if (!descr->items) max_items = 0;
1295 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1296 if (descr->nb_items == max_items)
1298 /* We need to grow the array */
1299 max_items += LB_ARRAY_GRANULARITY;
1300 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1301 max_items * sizeof(LB_ITEMDATA) )))
1303 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1306 descr->items = item;
1309 /* Insert the item structure */
1311 item = &descr->items[index];
1312 if (index < descr->nb_items)
1313 RtlMoveMemory( item + 1, item,
1314 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1318 item->selected = FALSE;
1321 /* Get item height */
1323 if (descr->style & LBS_OWNERDRAWVARIABLE)
1325 MEASUREITEMSTRUCT32 mis;
1326 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1328 mis.CtlType = ODT_LISTBOX;
1331 mis.itemData = descr->items[index].data;
1332 mis.itemHeight = descr->item_height;
1333 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1334 item->height = mis.itemHeight ? mis.itemHeight : 1;
1335 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1336 wnd->hwndSelf, index, str ? str : "", item->height );
1339 /* Repaint the items */
1341 LISTBOX_UpdateScroll( wnd, descr );
1342 LISTBOX_InvalidateItems( wnd, descr, index );
1344 /* Move selection and focused item */
1346 if (index <= descr->selected_item) descr->selected_item++;
1347 if (index <= descr->focus_item)
1349 descr->focus_item++;
1350 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1353 /* If listbox was empty, set focus to the first item */
1355 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1360 /***********************************************************************
1361 * LISTBOX_InsertString
1363 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index,
1366 LPSTR new_str = NULL;
1370 if (HAS_STRINGS(descr))
1372 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1374 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1378 else data = (DWORD)str;
1380 if (index == -1) index = descr->nb_items;
1381 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1383 if (new_str) HeapFree( descr->heap, 0, new_str );
1387 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1388 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1393 /***********************************************************************
1394 * LISTBOX_DeleteItem
1396 * Delete the content of an item. 'index' must be a valid index.
1398 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index )
1400 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1401 * while Win95 sends it for all items with user data.
1402 * It's probably better to send it too often than not
1403 * often enough, so this is what we do here.
1405 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1407 DELETEITEMSTRUCT32 dis;
1408 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1410 dis.CtlType = ODT_LISTBOX;
1413 dis.hwndItem = wnd->hwndSelf;
1414 dis.itemData = descr->items[index].data;
1415 SendMessage32A( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1417 if (HAS_STRINGS(descr) && descr->items[index].str)
1418 HeapFree( descr->heap, 0, descr->items[index].str );
1422 /***********************************************************************
1423 * LISTBOX_RemoveItem
1425 * Remove an item from the listbox and delete its content.
1427 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index )
1432 if (index == -1) index = descr->nb_items - 1;
1433 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1434 LISTBOX_DeleteItem( wnd, descr, index );
1436 /* Remove the item */
1438 item = &descr->items[index];
1439 if (index < descr->nb_items-1)
1440 RtlMoveMemory( item, item + 1,
1441 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1443 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1445 /* Shrink the item array if possible */
1447 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1448 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1450 max_items -= LB_ARRAY_GRANULARITY;
1451 item = HeapReAlloc( descr->heap, 0, descr->items,
1452 max_items * sizeof(LB_ITEMDATA) );
1453 if (item) descr->items = item;
1456 /* Repaint the items */
1458 LISTBOX_UpdateScroll( wnd, descr );
1459 LISTBOX_InvalidateItems( wnd, descr, index );
1461 /* Move selection and focused item */
1463 if (index <= descr->selected_item) descr->selected_item--;
1464 if (index <= descr->focus_item)
1466 descr->focus_item--;
1467 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1473 /***********************************************************************
1474 * LISTBOX_ResetContent
1476 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1480 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1481 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1482 descr->nb_items = 0;
1483 descr->top_item = 0;
1484 descr->selected_item = -1;
1485 descr->focus_item = 0;
1486 descr->anchor_item = -1;
1487 descr->items = NULL;
1488 LISTBOX_UpdateScroll( wnd, descr );
1489 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1493 /***********************************************************************
1496 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count )
1500 if (HAS_STRINGS(descr)) return LB_ERR;
1501 /* FIXME: this is far from optimal... */
1502 if (count > descr->nb_items)
1504 while (count > descr->nb_items)
1505 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1508 else if (count < descr->nb_items)
1510 while (count < descr->nb_items)
1511 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1518 /***********************************************************************
1521 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib,
1522 LPCSTR filespec, BOOL32 long_names )
1525 LRESULT ret = LB_OKAY;
1526 WIN32_FIND_DATA32A entry;
1529 if ((handle = FindFirstFile32A(filespec,&entry)) == INVALID_HANDLE_VALUE32)
1531 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1538 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1540 if (!(attrib & DDL_DIRECTORY) ||
1541 !strcmp( entry.cAlternateFileName, "." )) continue;
1542 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1543 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1545 else /* not a directory */
1547 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1548 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1550 if ((attrib & DDL_EXCLUSIVE) &&
1551 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1554 if (long_names) strcpy( buffer, entry.cFileName );
1555 else strcpy( buffer, entry.cAlternateFileName );
1557 if (!long_names) CharLower32A( buffer );
1558 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1559 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1561 } while (FindNextFile32A( handle, &entry ));
1562 FindClose32( handle );
1565 if ((ret >= 0) && (attrib & DDL_DRIVES))
1567 char buffer[] = "[-a-]";
1569 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1571 if (!DRIVE_IsValid(drive)) continue;
1572 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1580 /***********************************************************************
1581 * LISTBOX_HandleVScroll
1583 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1584 WPARAM32 wParam, LPARAM lParam )
1588 if (descr->style & LBS_MULTICOLUMN) return 0;
1589 switch(LOWORD(wParam))
1592 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1595 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1598 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1599 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1602 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1603 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1605 case SB_THUMBPOSITION:
1606 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1609 info.cbSize = sizeof(info);
1610 info.fMask = SIF_TRACKPOS;
1611 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1612 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1615 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1618 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1625 /***********************************************************************
1626 * LISTBOX_HandleHScroll
1628 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1629 WPARAM32 wParam, LPARAM lParam )
1634 if (descr->style & LBS_MULTICOLUMN)
1636 switch(LOWORD(wParam))
1639 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1643 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1647 page = descr->width / descr->column_width;
1648 if (page < 1) page = 1;
1649 LISTBOX_SetTopItem( wnd, descr,
1650 descr->top_item - page * descr->page_size, TRUE );
1653 page = descr->width / descr->column_width;
1654 if (page < 1) page = 1;
1655 LISTBOX_SetTopItem( wnd, descr,
1656 descr->top_item + page * descr->page_size, TRUE );
1658 case SB_THUMBPOSITION:
1659 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1663 info.cbSize = sizeof(info);
1664 info.fMask = SIF_TRACKPOS;
1665 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1666 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1670 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1673 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1677 else if (descr->horz_extent)
1679 switch(LOWORD(wParam))
1682 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1685 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1688 LISTBOX_SetHorizontalPos( wnd, descr,
1689 descr->horz_pos - descr->width );
1692 LISTBOX_SetHorizontalPos( wnd, descr,
1693 descr->horz_pos + descr->width );
1695 case SB_THUMBPOSITION:
1696 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1699 info.cbSize = sizeof(info);
1700 info.fMask = SIF_TRACKPOS;
1701 GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info );
1702 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1705 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1708 LISTBOX_SetHorizontalPos( wnd, descr,
1709 descr->horz_extent - descr->width );
1717 /***********************************************************************
1718 * LISTBOX_HandleLButtonDown
1720 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1721 WPARAM32 wParam, INT32 x, INT32 y )
1723 INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1724 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1725 wnd->hwndSelf, x, y, index );
1726 if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0;
1729 if (descr->style & LBS_EXTENDEDSEL)
1731 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1732 if (wParam & MK_CONTROL)
1734 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1735 LISTBOX_SetSelection( wnd, descr, index,
1736 !descr->items[index].selected, FALSE );
1738 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1742 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1743 LISTBOX_SetSelection( wnd, descr, index,
1744 (!(descr->style & LBS_MULTIPLESEL) ||
1745 !descr->items[index].selected), FALSE );
1749 if( !descr->lphc ) SetFocus32( wnd->hwndSelf );
1750 else SetFocus32( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1751 : descr->lphc->self->hwndSelf ) ;
1753 SetCapture32( wnd->hwndSelf );
1754 if (index != -1 && !descr->lphc)
1756 if (descr->style & LBS_NOTIFY )
1757 SendMessage32A( descr->owner, WM_LBTRACKPOINT, index,
1758 MAKELPARAM( x, y ) );
1759 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1761 POINT32 pt = { x, y };
1762 if (DragDetect32( wnd->hwndSelf, pt ))
1763 SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 );
1770 /***********************************************************************
1771 * LISTBOX_HandleLButtonUp
1773 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1775 if (LISTBOX_Timer != LB_TIMER_NONE)
1776 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1777 LISTBOX_Timer = LB_TIMER_NONE;
1778 if (GetCapture32() == wnd->hwndSelf)
1781 if (descr->style & LBS_NOTIFY)
1782 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1788 /***********************************************************************
1789 * LISTBOX_HandleTimer
1791 * Handle scrolling upon a timer event.
1792 * Return TRUE if scrolling should continue.
1794 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1795 INT32 index, TIMER_DIRECTION dir )
1800 if (descr->top_item) index = descr->top_item - 1;
1804 if (descr->top_item) index -= descr->page_size;
1807 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1808 if (index == descr->focus_item) index++;
1809 if (index >= descr->nb_items) index = descr->nb_items - 1;
1811 case LB_TIMER_RIGHT:
1812 if (index + descr->page_size < descr->nb_items)
1813 index += descr->page_size;
1818 if (index == descr->focus_item) return FALSE;
1819 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1824 /***********************************************************************
1825 * LISTBOX_HandleSystemTimer
1827 * WM_SYSTIMER handler.
1829 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1831 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1833 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1834 LISTBOX_Timer = LB_TIMER_NONE;
1840 /***********************************************************************
1841 * LISTBOX_HandleMouseMove
1843 * WM_MOUSEMOVE handler.
1845 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1849 TIMER_DIRECTION dir;
1851 if (descr->style & LBS_MULTICOLUMN)
1854 else if (y >= descr->item_height * descr->page_size)
1855 y = descr->item_height * descr->page_size - 1;
1859 dir = LB_TIMER_LEFT;
1862 else if (x >= descr->width)
1864 dir = LB_TIMER_RIGHT;
1865 x = descr->width - 1;
1867 else dir = LB_TIMER_NONE; /* inside */
1871 if (y < 0) dir = LB_TIMER_UP; /* above */
1872 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1873 else dir = LB_TIMER_NONE; /* inside */
1876 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1877 if (index == -1) index = descr->focus_item;
1878 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1880 /* Start/stop the system timer */
1882 if (dir != LB_TIMER_NONE)
1883 SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1884 else if (LISTBOX_Timer != LB_TIMER_NONE)
1885 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1886 LISTBOX_Timer = dir;
1890 /***********************************************************************
1891 * LISTBOX_HandleKeyDown
1893 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM32 wParam )
1896 if (descr->style & LBS_WANTKEYBOARDINPUT)
1898 caret = SendMessage32A( descr->owner, WM_VKEYTOITEM,
1899 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1901 if (caret == -2) return 0;
1903 if (caret == -1) switch(wParam)
1906 if (descr->style & LBS_MULTICOLUMN)
1908 if (descr->focus_item >= descr->page_size)
1909 caret = descr->focus_item - descr->page_size;
1914 caret = descr->focus_item - 1;
1915 if (caret < 0) caret = 0;
1918 if (descr->style & LBS_MULTICOLUMN)
1920 if (descr->focus_item + descr->page_size < descr->nb_items)
1921 caret = descr->focus_item + descr->page_size;
1926 caret = descr->focus_item + 1;
1927 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1930 if (descr->style & LBS_MULTICOLUMN)
1932 INT32 page = descr->width / descr->column_width;
1933 if (page < 1) page = 1;
1934 caret = descr->focus_item - (page * descr->page_size) + 1;
1936 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1937 if (caret < 0) caret = 0;
1940 if (descr->style & LBS_MULTICOLUMN)
1942 INT32 page = descr->width / descr->column_width;
1943 if (page < 1) page = 1;
1944 caret = descr->focus_item + (page * descr->page_size) - 1;
1946 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1947 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1953 caret = descr->nb_items - 1;
1956 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1957 else if (descr->style & LBS_MULTIPLESEL)
1959 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1960 !descr->items[descr->focus_item].selected,
1961 (descr->style & LBS_NOTIFY) != 0 );
1963 else if (descr->selected_item == -1)
1965 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1966 (descr->style & LBS_NOTIFY) != 0 );
1972 if ((descr->style & LBS_EXTENDEDSEL) &&
1973 !(GetKeyState32( VK_SHIFT ) & 0x8000))
1974 descr->anchor_item = caret;
1975 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1976 if (descr->style & LBS_NOTIFY)
1978 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1980 /* make sure that combo parent doesn't hide us */
1981 descr->lphc->wState |= CBF_NOROLLUP;
1983 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1990 /***********************************************************************
1991 * LISTBOX_HandleChar
1993 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1997 char str[2] = { wParam & 0xff, '\0' };
1999 if (descr->style & LBS_WANTKEYBOARDINPUT)
2001 caret = SendMessage32A( descr->owner, WM_CHARTOITEM,
2002 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2004 if (caret == -2) return 0;
2007 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2010 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2011 if (descr->style & LBS_NOTIFY)
2012 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2018 /***********************************************************************
2021 static BOOL32 LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2024 MEASUREITEMSTRUCT32 mis;
2027 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2029 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2031 HeapFree( GetProcessHeap(), 0, descr );
2034 GetClientRect32( wnd->hwndSelf, &rect );
2035 descr->owner = GetParent32( wnd->hwndSelf );
2036 descr->style = wnd->dwStyle;
2037 descr->width = rect.right - rect.left;
2038 descr->height = rect.bottom - rect.top;
2039 descr->items = NULL;
2040 descr->nb_items = 0;
2041 descr->top_item = 0;
2042 descr->selected_item = -1;
2043 descr->focus_item = 0;
2044 descr->anchor_item = -1;
2045 descr->item_height = 1;
2046 descr->page_size = 1;
2047 descr->column_width = 150;
2048 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2049 descr->horz_pos = 0;
2052 descr->caret_on = TRUE;
2054 descr->locale = 0; /* FIXME */
2059 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2060 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2061 descr->owner = lphc->self->hwndSelf;
2064 *(LB_DESCR **)wnd->wExtra = descr;
2066 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2068 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2069 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2070 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2071 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2073 if (descr->style & LBS_OWNERDRAWFIXED)
2075 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2077 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2078 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2082 UINT32 id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2084 mis.CtlType = ODT_LISTBOX;
2089 mis.itemHeight = descr->item_height;
2090 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2091 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2099 /***********************************************************************
2102 static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2104 LISTBOX_ResetContent( wnd, descr );
2105 HeapDestroy( descr->heap );
2106 HeapFree( GetProcessHeap(), 0, descr );
2112 /***********************************************************************
2115 LRESULT WINAPI ListBoxWndProc( HWND32 hwnd, UINT32 msg,
2116 WPARAM32 wParam, LPARAM lParam )
2120 WND *wnd = WIN_FindWndPtr( hwnd );
2123 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2125 if (msg == WM_CREATE)
2127 if (!LISTBOX_Create( wnd, NULL )) return -1;
2128 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2129 hwnd, *(LB_DESCR **)wnd->wExtra );
2132 /* Ignore all other messages before we get a WM_CREATE */
2133 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2136 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2137 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2140 case LB_RESETCONTENT16:
2141 case LB_RESETCONTENT32:
2142 LISTBOX_ResetContent( wnd, descr );
2145 case LB_ADDSTRING16:
2146 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2148 case LB_ADDSTRING32:
2149 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2150 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2152 case LB_INSERTSTRING16:
2153 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2154 wParam = (INT32)(INT16)wParam;
2156 case LB_INSERTSTRING32:
2157 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2160 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2163 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2164 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2166 case LB_DELETESTRING16:
2167 case LB_DELETESTRING32:
2168 return LISTBOX_RemoveItem( wnd, descr, wParam );
2170 case LB_GETITEMDATA16:
2171 case LB_GETITEMDATA32:
2172 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2174 return descr->items[wParam].data;
2176 case LB_SETITEMDATA16:
2177 case LB_SETITEMDATA32:
2178 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2180 descr->items[wParam].data = (DWORD)lParam;
2185 return descr->nb_items;
2188 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2191 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2193 case LB_GETTEXTLEN16:
2195 case LB_GETTEXTLEN32:
2196 if (wParam >= descr->nb_items) return LB_ERR;
2197 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2200 case LB_GETCURSEL16:
2201 case LB_GETCURSEL32:
2202 return descr->selected_item;
2204 case LB_GETTOPINDEX16:
2205 case LB_GETTOPINDEX32:
2206 return descr->top_item;
2208 case LB_GETITEMHEIGHT16:
2209 case LB_GETITEMHEIGHT32:
2210 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2212 case LB_SETITEMHEIGHT16:
2213 lParam = LOWORD(lParam);
2215 case LB_SETITEMHEIGHT32:
2216 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2218 case LB_ITEMFROMPOINT32:
2220 POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
2221 RECT32 rect = { 0, 0, descr->width, descr->height };
2222 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2223 PtInRect32( &rect, pt ) );
2226 case LB_SETCARETINDEX16:
2227 case LB_SETCARETINDEX32:
2228 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2230 case LB_GETCARETINDEX16:
2231 case LB_GETCARETINDEX32:
2232 return descr->focus_item;
2234 case LB_SETTOPINDEX16:
2235 case LB_SETTOPINDEX32:
2236 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2238 case LB_SETCOLUMNWIDTH16:
2239 case LB_SETCOLUMNWIDTH32:
2240 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2242 case LB_GETITEMRECT16:
2245 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2246 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2250 case LB_GETITEMRECT32:
2251 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam );
2253 case LB_FINDSTRING16:
2254 wParam = (INT32)(INT16)wParam;
2255 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2257 case LB_FINDSTRING32:
2258 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2260 case LB_FINDSTRINGEXACT16:
2261 wParam = (INT32)(INT16)wParam;
2262 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2264 case LB_FINDSTRINGEXACT32:
2265 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2267 case LB_SELECTSTRING16:
2268 wParam = (INT32)(INT16)wParam;
2269 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2271 case LB_SELECTSTRING32:
2273 INT32 index = LISTBOX_FindString( wnd, descr, wParam,
2274 (LPCSTR)lParam, FALSE );
2275 if (index == LB_ERR) return LB_ERR;
2276 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2281 wParam = (INT32)(INT16)wParam;
2284 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2286 return descr->items[wParam].selected;
2289 lParam = (INT32)(INT16)lParam;
2292 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2294 case LB_SETCURSEL16:
2295 wParam = (INT32)(INT16)wParam;
2297 case LB_SETCURSEL32:
2298 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2299 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2301 case LB_GETSELCOUNT16:
2302 case LB_GETSELCOUNT32:
2303 return LISTBOX_GetSelCount( wnd, descr );
2305 case LB_GETSELITEMS16:
2306 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2307 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2309 case LB_GETSELITEMS32:
2310 return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam );
2312 case LB_SELITEMRANGE16:
2313 case LB_SELITEMRANGE32:
2314 if (LOWORD(lParam) <= HIWORD(lParam))
2315 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2316 HIWORD(lParam), wParam );
2318 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2319 LOWORD(lParam), wParam );
2321 case LB_SELITEMRANGEEX16:
2322 case LB_SELITEMRANGEEX32:
2323 if ((INT32)lParam >= (INT32)wParam)
2324 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2326 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2328 case LB_GETHORIZONTALEXTENT16:
2329 case LB_GETHORIZONTALEXTENT32:
2330 return descr->horz_extent;
2332 case LB_SETHORIZONTALEXTENT16:
2333 case LB_SETHORIZONTALEXTENT32:
2334 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2336 case LB_GETANCHORINDEX16:
2337 case LB_GETANCHORINDEX32:
2338 return descr->anchor_item;
2340 case LB_SETANCHORINDEX16:
2341 wParam = (INT32)(INT16)wParam;
2343 case LB_SETANCHORINDEX32:
2344 if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items))
2346 descr->anchor_item = (INT32)wParam;
2350 return LISTBOX_Directory( wnd, descr, wParam,
2351 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2354 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2356 case LB_GETLOCALE32:
2357 return descr->locale;
2359 case LB_SETLOCALE32:
2360 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2363 case LB_INITSTORAGE32:
2364 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2367 return LISTBOX_SetCount( wnd, descr, (INT32)wParam );
2369 case LB_SETTABSTOPS16:
2370 return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam,
2371 (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE );
2373 case LB_SETTABSTOPS32:
2374 return LISTBOX_SetTabStops( wnd, descr, wParam,
2375 (LPINT32)lParam, FALSE );
2379 if (descr->caret_on) return LB_OKAY;
2380 descr->caret_on = TRUE;
2381 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2382 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2387 if (!descr->caret_on) return LB_OKAY;
2388 descr->caret_on = FALSE;
2389 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2390 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2394 return LISTBOX_Destroy( wnd, descr );
2397 InvalidateRect32( hwnd, NULL, TRUE );
2401 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2405 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2410 HDC32 hdc = ( wParam ) ? ((HDC32)wParam)
2411 : BeginPaint32( hwnd, &ps );
2412 ret = LISTBOX_Paint( wnd, descr, hdc );
2413 if( !wParam ) EndPaint32( hwnd, &ps );
2418 LISTBOX_UpdateSize( wnd, descr );
2425 LISTBOX_SetFont( wnd, descr, (HFONT32)wParam );
2426 if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE );
2430 descr->caret_on = TRUE;
2431 if (descr->focus_item != -1)
2432 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2433 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2437 if ((descr->focus_item != -1) && descr->caret_on)
2438 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2439 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2443 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2446 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2448 case WM_LBUTTONDOWN:
2449 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2450 (INT16)LOWORD(lParam),
2451 (INT16)HIWORD(lParam) );
2453 case WM_LBUTTONDBLCLK:
2454 if (descr->style & LBS_NOTIFY)
2455 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2459 if (GetCapture32() == hwnd)
2460 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2461 (INT16)HIWORD(lParam) );
2465 return LISTBOX_HandleLButtonUp( wnd, descr );
2468 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2471 return LISTBOX_HandleChar( wnd, descr, wParam );
2474 return LISTBOX_HandleSystemTimer( wnd, descr );
2477 if (IS_OWNERDRAW(descr))
2479 RECT32 rect = { 0, 0, descr->width, descr->height };
2480 HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
2481 wParam, (LPARAM)wnd->hwndSelf );
2482 if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush );
2488 return SendMessage32A( descr->owner, msg, wParam, lParam );
2492 case WM_QUERYDROPOBJECT:
2497 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2498 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2500 return SendMessage32A( descr->owner, msg, wParam, lParam );
2505 if (TWEAK_WineLook > WIN31_LOOK)
2506 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2507 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2510 if ((msg >= WM_USER) && (msg < 0xc000))
2511 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2512 hwnd, msg, wParam, lParam );
2513 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2518 /***********************************************************************
2521 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT32 attrib, LPSTR dir, BOOL32 bLong)
2523 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2527 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2530 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2532 RedrawWindow32( lphc->self->hwndSelf, NULL, 0,
2533 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2540 /***********************************************************************
2543 * NOTE: in Windows, winproc address of the ComboLBox is the same
2544 * as that of the Listbox.
2546 LRESULT WINAPI ComboLBWndProc( HWND32 hwnd, UINT32 msg,
2547 WPARAM32 wParam, LPARAM lParam )
2550 WND *wnd = WIN_FindWndPtr( hwnd );
2554 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2556 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2557 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2559 if( descr || msg == WM_CREATE )
2561 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2566 #define lpcs ((LPCREATESTRUCT32A)lParam)
2567 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2568 (UINT32)lpcs->lpCreateParams);
2570 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2572 return LISTBOX_Create( wnd, lphc );
2574 case WM_LBUTTONDOWN:
2575 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2576 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2578 /* avoid activation at all costs */
2580 case WM_MOUSEACTIVATE:
2581 return MA_NOACTIVATE;
2587 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2589 /* for some reason(?) Windows makes it possible to
2590 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2592 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2593 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2594 && (wParam == VK_DOWN || wParam == VK_UP)) )
2596 COMBO_FlipListbox( lphc, FALSE );
2600 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2602 case LB_SETCURSEL16:
2603 case LB_SETCURSEL32:
2604 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2605 return (lRet == LB_ERR) ? lRet : descr->selected_item;
2608 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2613 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2616 lRet = DefWindowProc32A( hwnd, msg, wParam, lParam );
2618 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );