4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
19 #include "selectors.h"
22 #include "debugtools.h"
25 DEFAULT_DEBUG_CHANNEL(listbox);
26 DECLARE_DEBUG_CHANNEL(combo);
35 /* Items array granularity */
36 #define LB_ARRAY_GRANULARITY 16
38 /* Scrolling timeout in ms */
39 #define LB_SCROLL_TIMEOUT 50
41 /* Listbox system timer id */
44 /* flag listbox changed while setredraw false - internal style */
45 #define LBS_DISPLAYCHANGED 0x80000000
50 LPSTR str; /* Item text */
51 BOOL selected; /* Is item selected? */
52 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
53 DWORD data; /* User data */
56 /* Listbox structure */
59 HANDLE heap; /* Heap for this listbox */
60 HWND owner; /* Owner window to send notifications to */
61 UINT style; /* Window style */
62 INT width; /* Window width */
63 INT height; /* Window height */
64 LB_ITEMDATA *items; /* Array of items */
65 INT nb_items; /* Number of items */
66 INT top_item; /* Top visible item */
67 INT selected_item; /* Selected item */
68 INT focus_item; /* Item that has the focus */
69 INT anchor_item; /* Anchor item for extended selection */
70 INT item_height; /* Default item height */
71 INT page_size; /* Items per listbox page */
72 INT column_width; /* Column width for multi-column listboxes */
73 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
74 INT horz_pos; /* Horizontal position */
75 INT nb_tabs; /* Number of tabs in array */
76 INT *tabs; /* Array of tabs */
77 BOOL caret_on; /* Is caret on? */
78 BOOL captured; /* Is mouse captured? */
80 HFONT font; /* Current font */
81 LCID locale; /* Current locale for string comparisons */
82 LPHEADCOMBO lphc; /* ComboLBox */
86 #define IS_OWNERDRAW(descr) \
87 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
89 #define HAS_STRINGS(descr) \
90 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
93 #define IS_MULTISELECT(descr) \
94 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
96 #define SEND_NOTIFICATION(wnd,descr,code) \
97 (SendMessageA( (descr)->owner, WM_COMMAND, \
98 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
100 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
102 /* Current timer status */
112 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
115 /***********************************************************************
118 void LISTBOX_Dump( WND *wnd )
122 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
124 TRACE( "Listbox:\n" );
125 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
126 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
128 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
130 TRACE( "%4d: %-40s %d %08lx %3d\n",
131 i, item->str, item->selected, item->data, item->height );
136 /***********************************************************************
137 * LISTBOX_GetCurrentPageSize
139 * Return the current page size
141 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
144 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
145 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
147 if ((height += descr->items[i].height) > descr->height) break;
149 if (i == descr->top_item) return 1;
150 else return i - descr->top_item;
154 /***********************************************************************
155 * LISTBOX_GetMaxTopIndex
157 * Return the maximum possible index for the top of the listbox.
159 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
163 if (descr->style & LBS_OWNERDRAWVARIABLE)
165 page = descr->height;
166 for (max = descr->nb_items - 1; max >= 0; max--)
167 if ((page -= descr->items[max].height) < 0) break;
168 if (max < descr->nb_items - 1) max++;
170 else if (descr->style & LBS_MULTICOLUMN)
172 if ((page = descr->width / descr->column_width) < 1) page = 1;
173 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
174 max = (max - page) * descr->page_size;
178 max = descr->nb_items - descr->page_size;
180 if (max < 0) max = 0;
185 /***********************************************************************
186 * LISTBOX_UpdateScroll
188 * Update the scrollbars. Should be called whenever the content
189 * of the listbox changes.
191 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
195 /* Check the listbox scroll bar flags individually before we call
196 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
197 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
198 scroll bar when we do not need one.
199 if (!(descr->style & WS_VSCROLL)) return;
202 /* It is important that we check descr->style, and not wnd->dwStyle,
203 for WS_VSCROLL, as the former is exactly the one passed in
204 argument to CreateWindow.
205 In Windows (and from now on in Wine :) a listbox created
206 with such a style (no WS_SCROLL) does not update
207 the scrollbar with listbox-related data, thus letting
208 the programmer use it for his/her own purposes. */
210 if (descr->style & LBS_NOREDRAW) return;
211 info.cbSize = sizeof(info);
213 if (descr->style & LBS_MULTICOLUMN)
216 info.nMax = (descr->nb_items - 1) / descr->page_size;
217 info.nPos = descr->top_item / descr->page_size;
218 info.nPage = descr->width / descr->column_width;
219 if (info.nPage < 1) info.nPage = 1;
220 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
221 if (descr->style & LBS_DISABLENOSCROLL)
222 info.fMask |= SIF_DISABLENOSCROLL;
223 if (descr->style & WS_HSCROLL)
224 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
226 info.fMask = SIF_RANGE;
227 if (descr->style & WS_VSCROLL)
228 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
233 info.nMax = descr->nb_items - 1;
234 info.nPos = descr->top_item;
235 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
236 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
237 if (descr->style & LBS_DISABLENOSCROLL)
238 info.fMask |= SIF_DISABLENOSCROLL;
239 if (descr->style & WS_VSCROLL)
240 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
242 if (descr->horz_extent)
245 info.nMax = descr->horz_extent - 1;
246 info.nPos = descr->horz_pos;
247 info.nPage = descr->width;
248 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
249 if (descr->style & LBS_DISABLENOSCROLL)
250 info.fMask |= SIF_DISABLENOSCROLL;
251 if (descr->style & WS_HSCROLL)
252 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
259 /***********************************************************************
262 * Set the top item of the listbox, scrolling up or down if necessary.
264 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
267 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
268 if (index > max) index = max;
269 if (index < 0) index = 0;
270 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
271 if (descr->top_item == index) return LB_OKAY;
272 if (descr->style & LBS_MULTICOLUMN)
274 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
275 if (scroll && (abs(diff) < descr->width))
276 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
277 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
285 if (descr->style & LBS_OWNERDRAWVARIABLE)
289 if (index > descr->top_item)
291 for (i = index - 1; i >= descr->top_item; i--)
292 diff -= descr->items[i].height;
296 for (i = index; i < descr->top_item; i++)
297 diff += descr->items[i].height;
301 diff = (descr->top_item - index) * descr->item_height;
303 if (abs(diff) < descr->height)
304 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
305 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
309 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
310 descr->top_item = index;
311 LISTBOX_UpdateScroll( wnd, descr );
316 /***********************************************************************
319 * Update the page size. Should be called when the size of
320 * the client area or the item height changes.
322 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
326 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
328 if (page_size == descr->page_size) return;
329 descr->page_size = page_size;
330 if (descr->style & LBS_MULTICOLUMN)
331 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
332 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
336 /***********************************************************************
339 * Update the size of the listbox. Should be called when the size of
340 * the client area changes.
342 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
346 GetClientRect( wnd->hwndSelf, &rect );
347 descr->width = rect.right - rect.left;
348 descr->height = rect.bottom - rect.top;
349 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
351 if ((descr->height > descr->item_height) &&
352 (descr->height % descr->item_height))
354 TRACE("[%04x]: changing height %d -> %d\n",
355 wnd->hwndSelf, descr->height,
356 descr->height - descr->height%descr->item_height );
357 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
358 wnd->rectWindow.right - wnd->rectWindow.left,
359 wnd->rectWindow.bottom - wnd->rectWindow.top -
360 (descr->height % descr->item_height),
361 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
365 TRACE("[%04x]: new size = %d,%d\n",
366 wnd->hwndSelf, descr->width, descr->height );
367 LISTBOX_UpdatePage( wnd, descr );
368 LISTBOX_UpdateScroll( wnd, descr );
372 /***********************************************************************
373 * LISTBOX_GetItemRect
375 * Get the rectangle enclosing an item, in listbox client coordinates.
376 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
378 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
381 /* Index <= 0 is legal even on empty listboxes */
382 if (index && (index >= descr->nb_items)) return -1;
383 SetRect( rect, 0, 0, descr->width, descr->height );
384 if (descr->style & LBS_MULTICOLUMN)
386 INT col = (index / descr->page_size) -
387 (descr->top_item / descr->page_size);
388 rect->left += col * descr->column_width;
389 rect->right = rect->left + descr->column_width;
390 rect->top += (index % descr->page_size) * descr->item_height;
391 rect->bottom = rect->top + descr->item_height;
393 else if (descr->style & LBS_OWNERDRAWVARIABLE)
396 rect->right += descr->horz_pos;
397 if ((index >= 0) && (index < descr->nb_items))
399 if (index < descr->top_item)
401 for (i = descr->top_item-1; i >= index; i--)
402 rect->top -= descr->items[i].height;
406 for (i = descr->top_item; i < index; i++)
407 rect->top += descr->items[i].height;
409 rect->bottom = rect->top + descr->items[index].height;
415 rect->top += (index - descr->top_item) * descr->item_height;
416 rect->bottom = rect->top + descr->item_height;
417 rect->right += descr->horz_pos;
420 return ((rect->left < descr->width) && (rect->right > 0) &&
421 (rect->top < descr->height) && (rect->bottom > 0));
425 /***********************************************************************
426 * LISTBOX_GetItemFromPoint
428 * Return the item nearest from point (x,y) (in client coordinates).
430 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
433 INT index = descr->top_item;
435 if (!descr->nb_items) return -1; /* No items */
436 if (descr->style & LBS_OWNERDRAWVARIABLE)
441 while (index < descr->nb_items)
443 if ((pos += descr->items[index].height) > y) break;
452 if ((pos -= descr->items[index].height) <= y) break;
456 else if (descr->style & LBS_MULTICOLUMN)
458 if (y >= descr->item_height * descr->page_size) return -1;
459 if (y >= 0) index += y / descr->item_height;
460 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
461 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
465 index += (y / descr->item_height);
467 if (index < 0) return 0;
468 if (index >= descr->nb_items) return -1;
473 /***********************************************************************
478 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
479 const RECT *rect, INT index, UINT action )
481 LB_ITEMDATA *item = NULL;
482 if (index < descr->nb_items) item = &descr->items[index];
484 if (IS_OWNERDRAW(descr))
492 if (action == ODA_FOCUS)
493 DrawFocusRect( hdc, rect );
495 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
499 /* some programs mess with the clipping region when
500 drawing the item, *and* restore the previous region
501 after they are done, so a region has better to exist
502 else everything ends clipped */
503 GetClientRect(wnd->hwndSelf, &r);
504 hrgn = CreateRectRgnIndirect(&r);
505 SelectClipRgn( hdc, hrgn);
506 DeleteObject( hrgn );
508 dis.CtlType = ODT_LISTBOX;
509 dis.CtlID = wnd->wIDmenu;
510 dis.hwndItem = wnd->hwndSelf;
511 dis.itemAction = action;
515 if (item && item->selected) dis.itemState |= ODS_SELECTED;
516 if ((descr->focus_item == index) &&
518 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
519 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
520 dis.itemData = item ? item->data : 0;
522 TRACE("[%04x]: drawitem %d (%s) action=%02x "
523 "state=%02x rect=%d,%d-%d,%d\n",
524 wnd->hwndSelf, index, item ? item->str : "", action,
525 dis.itemState, rect->left, rect->top,
526 rect->right, rect->bottom );
527 SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
531 COLORREF oldText = 0, oldBk = 0;
533 if (action == ODA_FOCUS)
535 DrawFocusRect( hdc, rect );
538 if (item && item->selected)
540 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
541 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
544 TRACE("[%04x]: painting %d (%s) action=%02x "
545 "rect=%d,%d-%d,%d\n",
546 wnd->hwndSelf, index, item ? item->str : "", action,
547 rect->left, rect->top, rect->right, rect->bottom );
549 ExtTextOutA( hdc, rect->left + 1, rect->top,
550 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
551 else if (!(descr->style & LBS_USETABSTOPS))
552 ExtTextOutA( hdc, rect->left + 1, rect->top,
553 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
554 strlen(item->str), NULL );
557 /* Output empty string to paint background in the full width. */
558 ExtTextOutA( hdc, rect->left + 1, rect->top,
559 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
560 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
561 item->str, strlen(item->str),
562 descr->nb_tabs, descr->tabs, 0);
564 if (item && item->selected)
566 SetBkColor( hdc, oldBk );
567 SetTextColor( hdc, oldText );
569 if ((descr->focus_item == index) &&
571 (descr->in_focus)) DrawFocusRect( hdc, rect );
576 /***********************************************************************
579 * Change the redraw flag.
581 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
585 if (!(descr->style & LBS_NOREDRAW)) return;
586 descr->style &= ~LBS_NOREDRAW;
587 if (descr->style & LBS_DISPLAYCHANGED)
588 { /* page was changed while setredraw false, refresh automatically */
589 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
590 if ((descr->top_item + descr->page_size) > descr->nb_items)
591 { /* reset top of page if less than number of items/page */
592 descr->top_item = descr->nb_items - descr->page_size;
593 if (descr->top_item < 0) descr->top_item = 0;
595 descr->style &= ~LBS_DISPLAYCHANGED;
597 LISTBOX_UpdateScroll( wnd, descr );
599 else descr->style |= LBS_NOREDRAW;
603 /***********************************************************************
604 * LISTBOX_RepaintItem
606 * Repaint a single item synchronously.
608 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
614 HBRUSH hbrush, oldBrush = 0;
616 /* Do not repaint the item if the item is not visible */
617 if (!IsWindowVisible(wnd->hwndSelf)) return;
618 if (descr->style & LBS_NOREDRAW)
620 descr->style |= LBS_DISPLAYCHANGED;
623 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
624 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
625 if (descr->font) oldFont = SelectObject( hdc, descr->font );
626 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
627 hdc, (LPARAM)wnd->hwndSelf );
628 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
629 if (wnd->dwStyle & WS_DISABLED)
630 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
631 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
632 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
633 if (oldFont) SelectObject( hdc, oldFont );
634 if (oldBrush) SelectObject( hdc, oldBrush );
635 ReleaseDC( wnd->hwndSelf, hdc );
639 /***********************************************************************
640 * LISTBOX_InitStorage
642 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
647 nb_items += LB_ARRAY_GRANULARITY - 1;
648 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
650 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
651 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
652 nb_items * sizeof(LB_ITEMDATA) )))
654 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
662 /***********************************************************************
663 * LISTBOX_SetTabStops
665 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
666 LPINT tabs, BOOL short_ints )
668 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
669 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
670 if (!(descr->nb_tabs = count))
675 /* FIXME: count = 1 */
676 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
677 descr->nb_tabs * sizeof(INT) )))
682 LPINT16 p = (LPINT16)tabs;
684 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
685 for (i = 0; i < descr->nb_tabs; i++) {
686 descr->tabs[i] = *p++<<1; /* FIXME */
687 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
689 if (TRACE_ON(listbox)) DPRINTF("\n");
691 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
692 /* FIXME: repaint the window? */
697 /***********************************************************************
700 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
703 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
704 if (HAS_STRINGS(descr))
707 return strlen(descr->items[index].str);
708 lstrcpyA( buffer, descr->items[index].str );
709 return strlen(buffer);
712 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
713 return sizeof(DWORD);
718 /***********************************************************************
719 * LISTBOX_FindStringPos
721 * Find the nearest string located before a given string in sort order.
722 * If 'exact' is TRUE, return an error if we don't get an exact match.
724 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
727 INT index, min, max, res = -1;
729 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
731 max = descr->nb_items;
734 index = (min + max) / 2;
735 if (HAS_STRINGS(descr))
736 res = lstrcmpiA( descr->items[index].str, str );
739 COMPAREITEMSTRUCT cis;
741 cis.CtlType = ODT_LISTBOX;
742 cis.CtlID = wnd->wIDmenu;
743 cis.hwndItem = wnd->hwndSelf;
745 cis.itemData1 = descr->items[index].data;
747 cis.itemData2 = (DWORD)str;
748 cis.dwLocaleId = descr->locale;
749 res = SendMessageA( descr->owner, WM_COMPAREITEM,
750 wnd->wIDmenu, (LPARAM)&cis );
752 if (!res) return index;
753 if (res > 0) max = index;
754 else min = index + 1;
756 return exact ? -1 : max;
760 /***********************************************************************
761 * LISTBOX_FindFileStrPos
763 * Find the nearest string located before a given string in directory
764 * sort order (i.e. first files, then directories, then drives).
766 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
768 INT min, max, res = -1;
770 if (!HAS_STRINGS(descr))
771 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
773 max = descr->nb_items;
776 INT index = (min + max) / 2;
777 const char *p = descr->items[index].str;
778 if (*p == '[') /* drive or directory */
780 if (*str != '[') res = -1;
781 else if (p[1] == '-') /* drive */
783 if (str[1] == '-') res = str[2] - p[2];
788 if (str[1] == '-') res = 1;
789 else res = lstrcmpiA( str, p );
794 if (*str == '[') res = 1;
795 else res = lstrcmpiA( str, p );
797 if (!res) return index;
798 if (res < 0) max = index;
799 else min = index + 1;
805 /***********************************************************************
808 * Find the item beginning with a given string.
810 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
811 LPCSTR str, BOOL exact )
816 if (start >= descr->nb_items) start = -1;
817 item = descr->items + start + 1;
818 if (HAS_STRINGS(descr))
820 if (!str || ! str[0] ) return LB_ERR;
823 for (i = start + 1; i < descr->nb_items; i++, item++)
824 if (!lstrcmpiA( str, item->str )) return i;
825 for (i = 0, item = descr->items; i <= start; i++, item++)
826 if (!lstrcmpiA( str, item->str )) return i;
830 /* Special case for drives and directories: ignore prefix */
831 #define CHECK_DRIVE(item) \
832 if ((item)->str[0] == '[') \
834 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
835 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
839 INT len = strlen(str);
840 for (i = start + 1; i < descr->nb_items; i++, item++)
842 if (!lstrncmpiA( str, item->str, len )) return i;
845 for (i = 0, item = descr->items; i <= start; i++, item++)
847 if (!lstrncmpiA( str, item->str, len )) return i;
855 if (exact && (descr->style & LBS_SORT))
856 /* If sorted, use a WM_COMPAREITEM binary search */
857 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
859 /* Otherwise use a linear search */
860 for (i = start + 1; i < descr->nb_items; i++, item++)
861 if (item->data == (DWORD)str) return i;
862 for (i = 0, item = descr->items; i <= start; i++, item++)
863 if (item->data == (DWORD)str) return i;
869 /***********************************************************************
870 * LISTBOX_GetSelCount
872 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
875 LB_ITEMDATA *item = descr->items;
877 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
878 for (i = count = 0; i < descr->nb_items; i++, item++)
879 if (item->selected) count++;
884 /***********************************************************************
885 * LISTBOX_GetSelItems16
887 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
891 LB_ITEMDATA *item = descr->items;
893 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
894 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
895 if (item->selected) array[count++] = (INT16)i;
900 /***********************************************************************
901 * LISTBOX_GetSelItems32
903 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
907 LB_ITEMDATA *item = descr->items;
909 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
910 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
911 if (item->selected) array[count++] = i;
916 /***********************************************************************
919 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
921 INT i, col_pos = descr->page_size - 1;
923 RECT focusRect = {-1, -1, -1, -1};
925 HBRUSH hbrush, oldBrush = 0;
928 if (descr->style & LBS_NOREDRAW) return 0;
930 SetRect( &rect, 0, 0, descr->width, descr->height );
931 if (descr->style & LBS_MULTICOLUMN)
932 rect.right = rect.left + descr->column_width;
933 else if (descr->horz_pos)
935 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
936 rect.right += descr->horz_pos;
939 if (descr->font) oldFont = SelectObject( hdc, descr->font );
940 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
941 hdc, (LPARAM)wnd->hwndSelf );
942 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
943 if (wnd->dwStyle & WS_DISABLED)
944 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
946 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
949 /* Special case for empty listbox: paint focus rect */
950 rect.bottom = rect.top + descr->item_height;
951 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
953 rect.top = rect.bottom;
956 /* Paint all the item, regarding the selection
957 Focus state will be painted after */
958 focusItem = descr->focus_item;
959 descr->focus_item = -1;
961 for (i = descr->top_item; i < descr->nb_items; i++)
963 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
964 rect.bottom = rect.top + descr->item_height;
966 rect.bottom = rect.top + descr->items[i].height;
970 /* keep the focus rect, to paint the focus item after */
971 focusRect.left = rect.left;
972 focusRect.right = rect.right;
973 focusRect.top = rect.top;
974 focusRect.bottom = rect.bottom;
976 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
977 rect.top = rect.bottom;
979 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
981 if (!IS_OWNERDRAW(descr))
983 /* Clear the bottom of the column */
984 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
985 if (rect.top < descr->height)
987 rect.bottom = descr->height;
988 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
989 &rect, NULL, 0, NULL );
993 /* Go to the next column */
994 rect.left += descr->column_width;
995 rect.right += descr->column_width;
997 col_pos = descr->page_size - 1;
1002 if (rect.top >= descr->height) break;
1006 /* Paint the focus item now */
1007 descr->focus_item = focusItem;
1008 if (focusRect.top != focusRect.bottom && descr->caret_on)
1009 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS );
1011 if (!IS_OWNERDRAW(descr))
1013 /* Clear the remainder of the client area */
1014 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
1015 if (rect.top < descr->height)
1017 rect.bottom = descr->height;
1018 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1019 &rect, NULL, 0, NULL );
1021 if (rect.right < descr->width)
1023 rect.left = rect.right;
1024 rect.right = descr->width;
1026 rect.bottom = descr->height;
1027 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1028 &rect, NULL, 0, NULL );
1031 if (oldFont) SelectObject( hdc, oldFont );
1032 if (oldBrush) SelectObject( hdc, oldBrush );
1037 /***********************************************************************
1038 * LISTBOX_InvalidateItems
1040 * Invalidate all items from a given item. If the specified item is not
1041 * visible, nothing happens.
1043 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1047 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1049 if (descr->style & LBS_NOREDRAW)
1051 descr->style |= LBS_DISPLAYCHANGED;
1054 rect.bottom = descr->height;
1055 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1056 if (descr->style & LBS_MULTICOLUMN)
1058 /* Repaint the other columns */
1059 rect.left = rect.right;
1060 rect.right = descr->width;
1062 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1068 /***********************************************************************
1069 * LISTBOX_GetItemHeight
1071 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1073 if (descr->style & LBS_OWNERDRAWVARIABLE)
1075 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1076 return descr->items[index].height;
1078 else return descr->item_height;
1082 /***********************************************************************
1083 * LISTBOX_SetItemHeight
1085 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1088 if (!height) height = 1;
1090 if (descr->style & LBS_OWNERDRAWVARIABLE)
1092 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1093 TRACE("[%04x]: item %d height = %d\n",
1094 wnd->hwndSelf, index, height );
1095 descr->items[index].height = height;
1096 LISTBOX_UpdateScroll( wnd, descr );
1097 LISTBOX_InvalidateItems( wnd, descr, index );
1099 else if (height != descr->item_height)
1101 TRACE("[%04x]: new height = %d\n",
1102 wnd->hwndSelf, height );
1103 descr->item_height = height;
1104 LISTBOX_UpdatePage( wnd, descr );
1105 LISTBOX_UpdateScroll( wnd, descr );
1106 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1112 /***********************************************************************
1113 * LISTBOX_SetHorizontalPos
1115 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1119 if (pos > descr->horz_extent - descr->width)
1120 pos = descr->horz_extent - descr->width;
1121 if (pos < 0) pos = 0;
1122 if (!(diff = descr->horz_pos - pos)) return;
1123 TRACE("[%04x]: new horz pos = %d\n",
1124 wnd->hwndSelf, pos );
1125 descr->horz_pos = pos;
1126 LISTBOX_UpdateScroll( wnd, descr );
1127 if (abs(diff) < descr->width)
1128 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1129 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1131 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1135 /***********************************************************************
1136 * LISTBOX_SetHorizontalExtent
1138 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1141 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1143 if (extent <= 0) extent = 1;
1144 if (extent == descr->horz_extent) return LB_OKAY;
1145 TRACE("[%04x]: new horz extent = %d\n",
1146 wnd->hwndSelf, extent );
1147 descr->horz_extent = extent;
1148 if (descr->horz_pos > extent - descr->width)
1149 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1151 LISTBOX_UpdateScroll( wnd, descr );
1156 /***********************************************************************
1157 * LISTBOX_SetColumnWidth
1159 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1161 if (width == descr->column_width) return LB_OKAY;
1162 TRACE("[%04x]: new column width = %d\n",
1163 wnd->hwndSelf, width );
1164 descr->column_width = width;
1165 LISTBOX_UpdatePage( wnd, descr );
1170 /***********************************************************************
1173 * Returns the item height.
1175 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1183 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1185 ERR("unable to get DC.\n" );
1188 if (font) oldFont = SelectObject( hdc, font );
1189 GetTextMetricsA( hdc, &tm );
1190 if (oldFont) SelectObject( hdc, oldFont );
1191 ReleaseDC( wnd->hwndSelf, hdc );
1192 if (!IS_OWNERDRAW(descr))
1193 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1194 return tm.tmHeight ;
1198 /***********************************************************************
1199 * LISTBOX_MakeItemVisible
1201 * Make sure that a given item is partially or fully visible.
1203 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1208 if (index <= descr->top_item) top = index;
1209 else if (descr->style & LBS_MULTICOLUMN)
1211 INT cols = descr->width;
1212 if (!fully) cols += descr->column_width - 1;
1213 if (cols >= descr->column_width) cols /= descr->column_width;
1215 if (index < descr->top_item + (descr->page_size * cols)) return;
1216 top = index - descr->page_size * (cols - 1);
1218 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1220 INT height = fully ? descr->items[index].height : 1;
1221 for (top = index; top > descr->top_item; top--)
1222 if ((height += descr->items[top-1].height) > descr->height) break;
1226 if (index < descr->top_item + descr->page_size) return;
1227 if (!fully && (index == descr->top_item + descr->page_size) &&
1228 (descr->height > (descr->page_size * descr->item_height))) return;
1229 top = index - descr->page_size + 1;
1231 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1234 /***********************************************************************
1235 * LISTBOX_SetCaretIndex
1238 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1241 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1242 BOOL fully_visible )
1244 INT oldfocus = descr->focus_item;
1246 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1247 if (index == oldfocus) return LB_OKAY;
1248 descr->focus_item = index;
1249 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1250 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1252 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1253 if (descr->caret_on && (descr->in_focus))
1254 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1260 /***********************************************************************
1261 * LISTBOX_SelectItemRange
1263 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1265 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1270 /* A few sanity checks */
1272 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1273 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1274 if (last == -1) last = descr->nb_items - 1;
1275 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1276 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1277 /* selected_item reflects last selected/unselected item on multiple sel */
1278 descr->selected_item = last;
1280 if (on) /* Turn selection on */
1282 for (i = first; i <= last; i++)
1284 if (descr->items[i].selected) continue;
1285 descr->items[i].selected = TRUE;
1286 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1288 LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1290 else /* Turn selection off */
1292 for (i = first; i <= last; i++)
1294 if (!descr->items[i].selected) continue;
1295 descr->items[i].selected = FALSE;
1296 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1302 /***********************************************************************
1303 * LISTBOX_SetSelection
1305 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1306 BOOL on, BOOL send_notify )
1308 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1310 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1311 if (descr->style & LBS_MULTIPLESEL)
1313 if (index == -1) /* Select all items */
1314 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1315 else /* Only one item */
1316 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1320 INT oldsel = descr->selected_item;
1321 if (index == oldsel) return LB_OKAY;
1322 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1323 if (index != -1) descr->items[index].selected = TRUE;
1324 descr->selected_item = index;
1325 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1326 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1327 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1328 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1330 if( descr->lphc ) /* set selection change flag for parent combo */
1331 descr->lphc->wState |= CBF_SELCHANGE;
1337 /***********************************************************************
1340 * Change the caret position and extend the selection to the new caret.
1342 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1343 BOOL fully_visible )
1345 INT oldfocus = descr->focus_item;
1347 if ((index < 0) || (index >= descr->nb_items))
1350 /* Important, repaint needs to be done in this order if
1351 you want to mimic Windows behavior:
1352 1. Remove the focus and paint the item
1353 2. Remove the selection and paint the item(s)
1354 3. Set the selection and repaint the item(s)
1355 4. Set the focus to 'index' and repaint the item */
1357 /* 1. remove the focus and repaint the item */
1358 descr->focus_item = -1;
1359 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1360 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1362 /* 2. then turn off the previous selection */
1363 /* 3. repaint the new selected item */
1364 if (descr->style & LBS_EXTENDEDSEL)
1366 if (descr->anchor_item != -1)
1368 INT first = min( index, descr->anchor_item );
1369 INT last = max( index, descr->anchor_item );
1371 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1372 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1373 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1376 else if (!(descr->style & LBS_MULTIPLESEL))
1378 /* Set selection to new caret item */
1379 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1382 /* 4. repaint the new item with the focus */
1383 descr->focus_item = index;
1384 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1385 if (descr->caret_on && (descr->in_focus))
1386 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1390 /***********************************************************************
1391 * LISTBOX_InsertItem
1393 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1394 LPSTR str, DWORD data )
1398 INT oldfocus = descr->focus_item;
1400 if (index == -1) index = descr->nb_items;
1401 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1402 if (!descr->items) max_items = 0;
1403 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1404 if (descr->nb_items == max_items)
1406 /* We need to grow the array */
1407 max_items += LB_ARRAY_GRANULARITY;
1408 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1409 max_items * sizeof(LB_ITEMDATA) )))
1411 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1414 descr->items = item;
1417 /* Insert the item structure */
1419 item = &descr->items[index];
1420 if (index < descr->nb_items)
1421 RtlMoveMemory( item + 1, item,
1422 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1426 item->selected = FALSE;
1429 /* Get item height */
1431 if (descr->style & LBS_OWNERDRAWVARIABLE)
1433 MEASUREITEMSTRUCT mis;
1435 mis.CtlType = ODT_LISTBOX;
1436 mis.CtlID = wnd->wIDmenu;
1438 mis.itemData = descr->items[index].data;
1439 mis.itemHeight = descr->item_height;
1440 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1441 item->height = mis.itemHeight ? mis.itemHeight : 1;
1442 TRACE("[%04x]: measure item %d (%s) = %d\n",
1443 wnd->hwndSelf, index, str ? str : "", item->height );
1446 /* Repaint the items */
1448 LISTBOX_UpdateScroll( wnd, descr );
1449 LISTBOX_InvalidateItems( wnd, descr, index );
1451 /* Move selection and focused item */
1452 /* If listbox was empty, set focus to the first item */
1453 if (descr->nb_items == 1)
1454 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1455 /* single select don't change selection index in win31 */
1456 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1458 descr->selected_item++;
1459 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1463 if (index <= descr->selected_item)
1465 descr->selected_item++;
1466 descr->focus_item = oldfocus; /* focus not changed */
1473 /***********************************************************************
1474 * LISTBOX_InsertString
1476 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1479 LPSTR new_str = NULL;
1483 if (HAS_STRINGS(descr))
1486 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1488 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1492 else data = (DWORD)str;
1494 if (index == -1) index = descr->nb_items;
1495 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1497 if (new_str) HeapFree( descr->heap, 0, new_str );
1501 TRACE("[%04x]: added item %d '%s'\n",
1502 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1507 /***********************************************************************
1508 * LISTBOX_DeleteItem
1510 * Delete the content of an item. 'index' must be a valid index.
1512 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1514 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1515 * while Win95 sends it for all items with user data.
1516 * It's probably better to send it too often than not
1517 * often enough, so this is what we do here.
1519 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1521 DELETEITEMSTRUCT dis;
1523 dis.CtlType = ODT_LISTBOX;
1524 dis.CtlID = wnd->wIDmenu;
1526 dis.hwndItem = wnd->hwndSelf;
1527 dis.itemData = descr->items[index].data;
1528 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1530 if (HAS_STRINGS(descr) && descr->items[index].str)
1531 HeapFree( descr->heap, 0, descr->items[index].str );
1535 /***********************************************************************
1536 * LISTBOX_RemoveItem
1538 * Remove an item from the listbox and delete its content.
1540 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1545 if (index == -1) index = descr->nb_items - 1;
1546 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1548 /* We need to invalidate the original rect instead of the updated one. */
1549 LISTBOX_InvalidateItems( wnd, descr, index );
1551 LISTBOX_DeleteItem( wnd, descr, index );
1553 /* Remove the item */
1555 item = &descr->items[index];
1556 if (index < descr->nb_items-1)
1557 RtlMoveMemory( item, item + 1,
1558 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1560 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1562 /* Shrink the item array if possible */
1564 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1565 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1567 max_items -= LB_ARRAY_GRANULARITY;
1568 item = HeapReAlloc( descr->heap, 0, descr->items,
1569 max_items * sizeof(LB_ITEMDATA) );
1570 if (item) descr->items = item;
1572 /* Repaint the items */
1574 LISTBOX_UpdateScroll( wnd, descr );
1575 /* if we removed the scrollbar, reset the top of the list
1576 (correct for owner-drawn ???) */
1577 if (descr->nb_items == descr->page_size)
1578 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1580 /* Move selection and focused item */
1581 if (!IS_MULTISELECT(descr))
1583 if (index == descr->selected_item)
1584 descr->selected_item = -1;
1585 else if (index < descr->selected_item)
1587 descr->selected_item--;
1588 if (ISWIN31) /* win 31 do not change the selected item number */
1589 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1593 if (descr->focus_item >= descr->nb_items)
1595 descr->focus_item = descr->nb_items - 1;
1596 if (descr->focus_item < 0) descr->focus_item = 0;
1602 /***********************************************************************
1603 * LISTBOX_ResetContent
1605 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1609 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1610 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1611 descr->nb_items = 0;
1612 descr->top_item = 0;
1613 descr->selected_item = -1;
1614 descr->focus_item = 0;
1615 descr->anchor_item = -1;
1616 descr->items = NULL;
1617 LISTBOX_UpdateScroll( wnd, descr );
1618 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1622 /***********************************************************************
1625 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1629 if (HAS_STRINGS(descr)) return LB_ERR;
1630 /* FIXME: this is far from optimal... */
1631 if (count > descr->nb_items)
1633 while (count > descr->nb_items)
1634 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1637 else if (count < descr->nb_items)
1639 while (count < descr->nb_items)
1640 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1647 /***********************************************************************
1650 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1651 LPCSTR filespec, BOOL long_names )
1654 LRESULT ret = LB_OKAY;
1655 WIN32_FIND_DATAA entry;
1658 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1660 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1667 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1669 if (!(attrib & DDL_DIRECTORY) ||
1670 !strcmp( entry.cAlternateFileName, "." )) continue;
1671 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1672 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1674 else /* not a directory */
1676 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1677 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1679 if ((attrib & DDL_EXCLUSIVE) &&
1680 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1683 if (long_names) strcpy( buffer, entry.cFileName );
1684 else strcpy( buffer, entry.cAlternateFileName );
1686 if (!long_names) CharLowerA( buffer );
1687 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1688 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1690 } while (FindNextFileA( handle, &entry ));
1691 FindClose( handle );
1694 if ((ret >= 0) && (attrib & DDL_DRIVES))
1696 char buffer[] = "[-a-]";
1698 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1700 if (!DRIVE_IsValid(drive)) continue;
1701 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1709 /***********************************************************************
1710 * LISTBOX_HandleVScroll
1712 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1713 WPARAM wParam, LPARAM lParam )
1717 if (descr->style & LBS_MULTICOLUMN) return 0;
1718 switch(LOWORD(wParam))
1721 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1724 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1727 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1728 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1731 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1732 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1734 case SB_THUMBPOSITION:
1735 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1738 info.cbSize = sizeof(info);
1739 info.fMask = SIF_TRACKPOS;
1740 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1741 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1744 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1747 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1754 /***********************************************************************
1755 * LISTBOX_HandleHScroll
1757 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1758 WPARAM wParam, LPARAM lParam )
1763 if (descr->style & LBS_MULTICOLUMN)
1765 switch(LOWORD(wParam))
1768 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1772 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1776 page = descr->width / descr->column_width;
1777 if (page < 1) page = 1;
1778 LISTBOX_SetTopItem( wnd, descr,
1779 descr->top_item - page * descr->page_size, TRUE );
1782 page = descr->width / descr->column_width;
1783 if (page < 1) page = 1;
1784 LISTBOX_SetTopItem( wnd, descr,
1785 descr->top_item + page * descr->page_size, TRUE );
1787 case SB_THUMBPOSITION:
1788 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1792 info.cbSize = sizeof(info);
1793 info.fMask = SIF_TRACKPOS;
1794 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1795 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1799 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1802 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1806 else if (descr->horz_extent)
1808 switch(LOWORD(wParam))
1811 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1814 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1817 LISTBOX_SetHorizontalPos( wnd, descr,
1818 descr->horz_pos - descr->width );
1821 LISTBOX_SetHorizontalPos( wnd, descr,
1822 descr->horz_pos + descr->width );
1824 case SB_THUMBPOSITION:
1825 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1828 info.cbSize = sizeof(info);
1829 info.fMask = SIF_TRACKPOS;
1830 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1831 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1834 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1837 LISTBOX_SetHorizontalPos( wnd, descr,
1838 descr->horz_extent - descr->width );
1845 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1847 short gcWheelDelta = 0;
1848 UINT pulScrollLines = 3;
1850 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1852 gcWheelDelta -= (short) HIWORD(wParam);
1854 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1856 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1857 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1858 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1863 /***********************************************************************
1864 * LISTBOX_HandleLButtonDown
1866 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1867 WPARAM wParam, INT x, INT y )
1869 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1870 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1871 wnd->hwndSelf, x, y, index );
1872 if (!descr->caret_on && (descr->in_focus)) return 0;
1874 if (!descr->in_focus)
1876 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1877 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1878 : descr->lphc->self->hwndSelf );
1883 if (descr->style & LBS_EXTENDEDSEL)
1885 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1886 if (wParam & MK_CONTROL)
1888 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1889 LISTBOX_SetSelection( wnd, descr, index,
1890 !descr->items[index].selected,
1891 (descr->style & LBS_NOTIFY) != 0);
1893 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1897 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1898 LISTBOX_SetSelection( wnd, descr, index,
1899 (!(descr->style & LBS_MULTIPLESEL) ||
1900 !descr->items[index].selected),
1901 (descr->style & LBS_NOTIFY) != 0 );
1905 descr->captured = TRUE;
1906 SetCapture( wnd->hwndSelf );
1907 if (index != -1 && !descr->lphc)
1909 if (descr->style & LBS_NOTIFY )
1910 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1911 MAKELPARAM( x, y ) );
1912 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1919 if (DragDetect( wnd->hwndSelf, pt ))
1920 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1927 /*************************************************************************
1928 * LISTBOX_HandleLButtonDownCombo [Internal]
1930 * Process LButtonDown message for the ComboListBox
1933 * pWnd [I] The windows internal structure
1934 * pDescr [I] The ListBox internal structure
1935 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1936 * x [I] X Mouse Coordinate
1937 * y [I] Y Mouse Coordinate
1940 * 0 since we are processing the WM_LBUTTONDOWN Message
1943 * This function is only to be used when a ListBox is a ComboListBox
1946 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1947 UINT msg, WPARAM wParam, INT x, INT y)
1949 RECT clientRect, screenRect;
1955 GetClientRect(pWnd->hwndSelf, &clientRect);
1957 if(PtInRect(&clientRect, mousePos))
1959 /* MousePos is in client, resume normal processing */
1960 if (msg == WM_LBUTTONDOWN)
1962 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
1963 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1965 else if (pDescr->style & LBS_NOTIFY)
1966 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1971 POINT screenMousePos;
1972 HWND hWndOldCapture;
1974 /* Check the Non-Client Area */
1975 screenMousePos = mousePos;
1976 hWndOldCapture = GetCapture();
1978 GetWindowRect(pWnd->hwndSelf, &screenRect);
1979 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1981 if(!PtInRect(&screenRect, screenMousePos))
1983 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
1984 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
1989 /* Check to see the NC is a scrollbar */
1991 /* Check Vertical scroll bar */
1992 if (pWnd->dwStyle & WS_VSCROLL)
1994 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1995 if (PtInRect( &clientRect, mousePos ))
1997 nHitTestType = HTVSCROLL;
2000 /* Check horizontal scroll bar */
2001 if (pWnd->dwStyle & WS_HSCROLL)
2003 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2004 if (PtInRect( &clientRect, mousePos ))
2006 nHitTestType = HTHSCROLL;
2009 /* Windows sends this message when a scrollbar is clicked
2012 if(nHitTestType != 0)
2014 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2015 MAKELONG(screenMousePos.x, screenMousePos.y));
2017 /* Resume the Capture after scrolling is complete
2019 if(hWndOldCapture != 0)
2021 SetCapture(hWndOldCapture);
2028 /***********************************************************************
2029 * LISTBOX_HandleLButtonUp
2031 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2033 if (LISTBOX_Timer != LB_TIMER_NONE)
2034 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2035 LISTBOX_Timer = LB_TIMER_NONE;
2036 if (descr->captured)
2038 descr->captured = FALSE;
2039 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2040 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2041 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2047 /***********************************************************************
2048 * LISTBOX_HandleTimer
2050 * Handle scrolling upon a timer event.
2051 * Return TRUE if scrolling should continue.
2053 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2054 INT index, TIMER_DIRECTION dir )
2059 if (descr->top_item) index = descr->top_item - 1;
2063 if (descr->top_item) index -= descr->page_size;
2066 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2067 if (index == descr->focus_item) index++;
2068 if (index >= descr->nb_items) index = descr->nb_items - 1;
2070 case LB_TIMER_RIGHT:
2071 if (index + descr->page_size < descr->nb_items)
2072 index += descr->page_size;
2077 if (index == descr->focus_item) return FALSE;
2078 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2083 /***********************************************************************
2084 * LISTBOX_HandleSystemTimer
2086 * WM_SYSTIMER handler.
2088 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2090 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2092 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2093 LISTBOX_Timer = LB_TIMER_NONE;
2099 /***********************************************************************
2100 * LISTBOX_HandleMouseMove
2102 * WM_MOUSEMOVE handler.
2104 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2108 TIMER_DIRECTION dir = LB_TIMER_NONE;
2110 if (!descr->captured) return;
2112 if (descr->style & LBS_MULTICOLUMN)
2115 else if (y >= descr->item_height * descr->page_size)
2116 y = descr->item_height * descr->page_size - 1;
2120 dir = LB_TIMER_LEFT;
2123 else if (x >= descr->width)
2125 dir = LB_TIMER_RIGHT;
2126 x = descr->width - 1;
2131 if (y < 0) dir = LB_TIMER_UP; /* above */
2132 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2135 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2136 if (index == -1) index = descr->focus_item;
2137 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2139 /* Start/stop the system timer */
2141 if (dir != LB_TIMER_NONE)
2142 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2143 else if (LISTBOX_Timer != LB_TIMER_NONE)
2144 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2145 LISTBOX_Timer = dir;
2149 /***********************************************************************
2150 * LISTBOX_HandleKeyDown
2152 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2155 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2156 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2157 bForceSelection = FALSE; /* only for single select list */
2159 if (descr->style & LBS_WANTKEYBOARDINPUT)
2161 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2162 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2164 if (caret == -2) return 0;
2166 if (caret == -1) switch(wParam)
2169 if (descr->style & LBS_MULTICOLUMN)
2171 bForceSelection = FALSE;
2172 if (descr->focus_item >= descr->page_size)
2173 caret = descr->focus_item - descr->page_size;
2178 caret = descr->focus_item - 1;
2179 if (caret < 0) caret = 0;
2182 if (descr->style & LBS_MULTICOLUMN)
2184 bForceSelection = FALSE;
2185 if (descr->focus_item + descr->page_size < descr->nb_items)
2186 caret = descr->focus_item + descr->page_size;
2191 caret = descr->focus_item + 1;
2192 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2196 if (descr->style & LBS_MULTICOLUMN)
2198 INT page = descr->width / descr->column_width;
2199 if (page < 1) page = 1;
2200 caret = descr->focus_item - (page * descr->page_size) + 1;
2202 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2203 if (caret < 0) caret = 0;
2206 if (descr->style & LBS_MULTICOLUMN)
2208 INT page = descr->width / descr->column_width;
2209 if (page < 1) page = 1;
2210 caret = descr->focus_item + (page * descr->page_size) - 1;
2212 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2213 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2219 caret = descr->nb_items - 1;
2222 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2223 else if (descr->style & LBS_MULTIPLESEL)
2225 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2226 !descr->items[descr->focus_item].selected,
2227 (descr->style & LBS_NOTIFY) != 0 );
2231 bForceSelection = FALSE;
2233 if (bForceSelection) /* focused item is used instead of key */
2234 caret = descr->focus_item;
2237 if ((descr->style & LBS_EXTENDEDSEL) &&
2238 !(GetKeyState( VK_SHIFT ) & 0x8000))
2239 descr->anchor_item = caret;
2240 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2241 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2242 if (descr->style & LBS_NOTIFY)
2244 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2246 /* make sure that combo parent doesn't hide us */
2247 descr->lphc->wState |= CBF_NOROLLUP;
2249 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2256 /***********************************************************************
2257 * LISTBOX_HandleChar
2259 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2265 str[0] = wParam & 0xff;
2268 if (descr->style & LBS_WANTKEYBOARDINPUT)
2270 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2271 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2273 if (caret == -2) return 0;
2276 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2279 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2280 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2281 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2282 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2283 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2289 /***********************************************************************
2292 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2295 MEASUREITEMSTRUCT mis;
2298 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2300 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2302 HeapFree( GetProcessHeap(), 0, descr );
2305 GetClientRect( wnd->hwndSelf, &rect );
2306 descr->owner = GetParent( wnd->hwndSelf );
2307 descr->style = wnd->dwStyle;
2308 descr->width = rect.right - rect.left;
2309 descr->height = rect.bottom - rect.top;
2310 descr->items = NULL;
2311 descr->nb_items = 0;
2312 descr->top_item = 0;
2313 descr->selected_item = -1;
2314 descr->focus_item = 0;
2315 descr->anchor_item = -1;
2316 descr->item_height = 1;
2317 descr->page_size = 1;
2318 descr->column_width = 150;
2319 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2320 descr->horz_pos = 0;
2323 descr->caret_on = lphc ? FALSE : TRUE;
2324 descr->in_focus = FALSE;
2325 descr->captured = FALSE;
2327 descr->locale = 0; /* FIXME */
2330 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2331 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2333 /* Win95 document "List Box Differences" from MSDN:
2334 If a list box in a version 3.x application has either the
2335 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2336 horizontal and vertical scroll bars.
2338 descr->style |= WS_VSCROLL | WS_HSCROLL;
2343 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2344 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2345 descr->owner = lphc->self->hwndSelf;
2348 *(LB_DESCR **)wnd->wExtra = descr;
2350 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2352 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2353 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2354 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2355 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2357 if (descr->style & LBS_OWNERDRAWFIXED)
2359 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2361 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2362 descr->item_height = lphc->fixedOwnerDrawHeight;
2366 mis.CtlType = ODT_LISTBOX;
2367 mis.CtlID = wnd->wIDmenu;
2371 mis.itemHeight = descr->item_height;
2372 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2373 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2381 /***********************************************************************
2384 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2386 LISTBOX_ResetContent( wnd, descr );
2387 HeapDestroy( descr->heap );
2388 HeapFree( GetProcessHeap(), 0, descr );
2394 /***********************************************************************
2397 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2398 WPARAM wParam, LPARAM lParam )
2402 HWND hwnd = wnd->hwndSelf;
2405 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2411 if (!LISTBOX_Create( wnd, NULL ))
2413 TRACE("creating wnd=%04x descr=%p\n",
2414 hwnd, *(LB_DESCR **)wnd->wExtra );
2420 * When a listbox is not in a combobox and the look
2421 * is win95, the WS_BORDER style is replaced with
2422 * the WS_EX_CLIENTEDGE style.
2424 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2425 (wnd->dwStyle & WS_BORDER) )
2427 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2428 wnd->dwStyle &= ~ WS_BORDER;
2433 /* Ignore all other messages before we get a WM_CREATE */
2434 return DefWindowProcA( hwnd, msg, wParam, lParam );
2437 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2438 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2441 case LB_RESETCONTENT16:
2442 case LB_RESETCONTENT:
2443 LISTBOX_ResetContent( wnd, descr );
2446 case LB_ADDSTRING16:
2447 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2450 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2451 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2453 case LB_INSERTSTRING16:
2454 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2455 wParam = (INT)(INT16)wParam;
2457 case LB_INSERTSTRING:
2458 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2461 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2464 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2465 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2467 case LB_DELETESTRING16:
2468 case LB_DELETESTRING:
2469 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2470 return descr->nb_items;
2474 case LB_GETITEMDATA16:
2475 case LB_GETITEMDATA:
2476 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2478 return descr->items[wParam].data;
2480 case LB_SETITEMDATA16:
2481 case LB_SETITEMDATA:
2482 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2484 descr->items[wParam].data = (DWORD)lParam;
2489 return descr->nb_items;
2492 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2495 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2497 case LB_GETTEXTLEN16:
2500 if (wParam >= descr->nb_items)
2502 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2505 case LB_GETCURSEL16:
2507 if (descr->nb_items==0)
2509 if (!IS_MULTISELECT(descr))
2510 return descr->selected_item;
2512 if (descr->selected_item!=-1)
2513 return descr->selected_item;
2515 return descr->focus_item;
2516 /* otherwise, if the user tries to move the selection with the */
2517 /* arrow keys, we will give the application something to choke on */
2518 case LB_GETTOPINDEX16:
2519 case LB_GETTOPINDEX:
2520 return descr->top_item;
2522 case LB_GETITEMHEIGHT16:
2523 case LB_GETITEMHEIGHT:
2524 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2526 case LB_SETITEMHEIGHT16:
2527 lParam = LOWORD(lParam);
2529 case LB_SETITEMHEIGHT:
2530 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2532 case LB_ITEMFROMPOINT:
2537 pt.x = LOWORD(lParam);
2538 pt.y = HIWORD(lParam);
2541 rect.right = descr->width;
2542 rect.bottom = descr->height;
2544 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2545 !PtInRect( &rect, pt ) );
2548 case LB_SETCARETINDEX16:
2549 case LB_SETCARETINDEX:
2550 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2551 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2558 case LB_GETCARETINDEX16:
2559 case LB_GETCARETINDEX:
2560 return descr->focus_item;
2562 case LB_SETTOPINDEX16:
2563 case LB_SETTOPINDEX:
2564 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2566 case LB_SETCOLUMNWIDTH16:
2567 case LB_SETCOLUMNWIDTH:
2568 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2570 case LB_GETITEMRECT16:
2573 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2574 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2578 case LB_GETITEMRECT:
2579 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2581 case LB_FINDSTRING16:
2582 wParam = (INT)(INT16)wParam;
2583 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2586 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2588 case LB_FINDSTRINGEXACT16:
2589 wParam = (INT)(INT16)wParam;
2590 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2592 case LB_FINDSTRINGEXACT:
2593 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2595 case LB_SELECTSTRING16:
2596 wParam = (INT)(INT16)wParam;
2597 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2599 case LB_SELECTSTRING:
2601 INT index = LISTBOX_FindString( wnd, descr, wParam,
2602 (LPCSTR)lParam, FALSE );
2603 if (index == LB_ERR)
2605 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2610 wParam = (INT)(INT16)wParam;
2613 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2615 return descr->items[wParam].selected;
2618 lParam = (INT)(INT16)lParam;
2621 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2623 case LB_SETCURSEL16:
2624 wParam = (INT)(INT16)wParam;
2627 if (IS_MULTISELECT(descr)) return LB_ERR;
2628 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2629 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2631 case LB_GETSELCOUNT16:
2632 case LB_GETSELCOUNT:
2633 return LISTBOX_GetSelCount( wnd, descr );
2635 case LB_GETSELITEMS16:
2636 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2637 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2639 case LB_GETSELITEMS:
2640 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2642 case LB_SELITEMRANGE16:
2643 case LB_SELITEMRANGE:
2644 if (LOWORD(lParam) <= HIWORD(lParam))
2645 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2646 HIWORD(lParam), wParam );
2648 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2649 LOWORD(lParam), wParam );
2651 case LB_SELITEMRANGEEX16:
2652 case LB_SELITEMRANGEEX:
2653 if ((INT)lParam >= (INT)wParam)
2654 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2656 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2658 case LB_GETHORIZONTALEXTENT16:
2659 case LB_GETHORIZONTALEXTENT:
2660 return descr->horz_extent;
2662 case LB_SETHORIZONTALEXTENT16:
2663 case LB_SETHORIZONTALEXTENT:
2664 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2666 case LB_GETANCHORINDEX16:
2667 case LB_GETANCHORINDEX:
2668 return descr->anchor_item;
2670 case LB_SETANCHORINDEX16:
2671 wParam = (INT)(INT16)wParam;
2673 case LB_SETANCHORINDEX:
2674 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2676 descr->anchor_item = (INT)wParam;
2680 return LISTBOX_Directory( wnd, descr, wParam,
2681 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2684 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2687 return descr->locale;
2690 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2693 case LB_INITSTORAGE:
2694 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2697 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2699 case LB_SETTABSTOPS16:
2700 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2701 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2703 case LB_SETTABSTOPS:
2704 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2708 if (descr->caret_on)
2710 descr->caret_on = TRUE;
2711 if ((descr->focus_item != -1) && (descr->in_focus))
2712 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2717 if (!descr->caret_on)
2719 descr->caret_on = FALSE;
2720 if ((descr->focus_item != -1) && (descr->in_focus))
2721 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2725 return LISTBOX_Destroy( wnd, descr );
2728 InvalidateRect( hwnd, NULL, TRUE );
2732 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2736 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2741 HDC hdc = ( wParam ) ? ((HDC)wParam)
2742 : BeginPaint( hwnd, &ps );
2743 ret = LISTBOX_Paint( wnd, descr, hdc );
2744 if( !wParam ) EndPaint( hwnd, &ps );
2748 LISTBOX_UpdateSize( wnd, descr );
2753 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2754 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2757 descr->in_focus = TRUE;
2758 descr->caret_on = TRUE;
2759 if (descr->focus_item != -1)
2760 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2761 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2764 descr->in_focus = FALSE;
2765 if ((descr->focus_item != -1) && descr->caret_on)
2766 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2767 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2770 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2772 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2773 case WM_MOUSEACTIVATE:
2774 return MA_NOACTIVATE;
2776 if (wParam & (MK_SHIFT | MK_CONTROL))
2777 return DefWindowProcA( hwnd, msg, wParam, lParam );
2778 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2779 case WM_LBUTTONDOWN:
2780 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2781 (INT16)LOWORD(lParam),
2782 (INT16)HIWORD(lParam) );
2783 case WM_LBUTTONDBLCLK:
2784 if (descr->style & LBS_NOTIFY)
2785 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2788 if (GetCapture() == hwnd)
2789 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2790 (INT16)HIWORD(lParam) );
2793 return LISTBOX_HandleLButtonUp( wnd, descr );
2795 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2797 return LISTBOX_HandleChar( wnd, descr, wParam );
2799 return LISTBOX_HandleSystemTimer( wnd, descr );
2801 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2804 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2805 wParam, (LPARAM)wnd->hwndSelf );
2806 GetClientRect(hwnd, &rect);
2807 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2812 return SendMessageA( descr->owner, msg, wParam, lParam );
2816 case WM_QUERYDROPOBJECT:
2821 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2822 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2824 return SendMessageA( descr->owner, msg, wParam, lParam );
2829 if ((msg >= WM_USER) && (msg < 0xc000))
2830 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2831 hwnd, msg, wParam, lParam );
2832 return DefWindowProcA( hwnd, msg, wParam, lParam );
2837 /***********************************************************************
2840 * This is just a wrapper for the real wndproc, it only does window locking
2843 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2844 WPARAM wParam, LPARAM lParam )
2846 WND* wndPtr = WIN_FindWndPtr( hwnd );
2847 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2849 WIN_ReleaseWndPtr(wndPtr);
2853 /***********************************************************************
2856 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2858 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2862 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2865 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2867 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2868 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2869 WIN_ReleaseWndPtr(wnd);
2872 WIN_ReleaseWndPtr(wnd);
2877 /***********************************************************************
2878 * ComboLBWndProc_locked
2880 * The real combo listbox wndproc, but called with locked WND struct.
2882 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2883 WPARAM wParam, LPARAM lParam )
2886 HWND hwnd = wnd->hwndSelf;
2890 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2892 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2893 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2895 if( descr || msg == WM_CREATE )
2897 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2902 #define lpcs ((LPCREATESTRUCTA)lParam)
2903 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2904 (UINT)lpcs->lpCreateParams);
2906 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2908 return LISTBOX_Create( wnd, lphc );
2910 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2911 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2917 mousePos.x = (INT16)LOWORD(lParam);
2918 mousePos.y = (INT16)HIWORD(lParam);
2921 * If we are in a dropdown combobox, we simulate that
2922 * the mouse is captured to show the tracking of the item.
2924 GetClientRect(hwnd, &clientRect);
2926 if (PtInRect( &clientRect, mousePos ))
2928 captured = descr->captured;
2929 descr->captured = TRUE;
2931 LISTBOX_HandleMouseMove( wnd, descr,
2932 mousePos.x, mousePos.y);
2934 descr->captured = captured;
2939 LISTBOX_HandleMouseMove( wnd, descr,
2940 mousePos.x, mousePos.y);
2949 * If we are in Win3.1 look, go with the default behavior.
2951 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2954 if (TWEAK_WineLook > WIN31_LOOK)
2960 * If the mouse button "up" is not in the listbox,
2961 * we make sure there is no selection by re-selecting the
2962 * item that was selected when the listbox was made visible.
2964 mousePos.x = (INT16)LOWORD(lParam);
2965 mousePos.y = (INT16)HIWORD(lParam);
2967 GetClientRect(hwnd, &clientRect);
2970 * When the user clicks outside the combobox and the focus
2971 * is lost, the owning combobox will send a fake buttonup with
2972 * 0xFFFFFFF as the mouse location, we must also revert the
2973 * selection to the original selection.
2975 if ( (lParam == 0xFFFFFFFF) ||
2976 (!PtInRect( &clientRect, mousePos )) )
2978 LISTBOX_MoveCaret( wnd,
2984 return LISTBOX_HandleLButtonUp( wnd, descr );
2985 case WM_LBUTTONDBLCLK:
2986 case WM_LBUTTONDOWN:
2987 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
2988 (INT16)LOWORD(lParam),
2989 (INT16)HIWORD(lParam) );
2990 case WM_MOUSEACTIVATE:
2991 return MA_NOACTIVATE;
2995 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2997 /* for some reason(?) Windows makes it possible to
2998 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3000 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3001 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3002 && (wParam == VK_DOWN || wParam == VK_UP)) )
3004 COMBO_FlipListbox( lphc, FALSE, FALSE );
3008 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3010 case LB_SETCURSEL16:
3012 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
3013 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3016 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3021 return ListBoxWndProc( hwnd, msg, wParam, lParam );
3024 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3026 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3031 /***********************************************************************
3034 * NOTE: in Windows, winproc address of the ComboLBox is the same
3035 * as that of the Listbox.
3037 * This is just a wrapper for the real wndproc, it only does window locking
3040 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
3041 WPARAM wParam, LPARAM lParam )
3043 WND *wnd = WIN_FindWndPtr( hwnd );
3044 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3046 WIN_ReleaseWndPtr(wnd);