4 * Copyright 1996 Alexandre Julliard
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
20 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(listbox);
24 DECLARE_DEBUG_CHANNEL(combo);
31 * Probably needs improvement:
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;
114 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
115 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
117 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
120 /*********************************************************************
121 * listbox class descriptor
123 const struct builtin_class_descr LISTBOX_builtin_class =
125 "ListBox", /* name */
126 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
127 ListBoxWndProcA, /* procA */
128 NULL, /* procW (FIXME) */
129 sizeof(LB_DESCR *), /* extra */
130 IDC_ARROWA, /* cursor */
135 /*********************************************************************
136 * combolbox class descriptor
138 const struct builtin_class_descr COMBOLBOX_builtin_class =
140 "ComboLBox", /* name */
141 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
142 ComboLBWndProcA, /* procA */
143 NULL, /* procW (FIXME) */
144 sizeof(LB_DESCR *), /* extra */
145 IDC_ARROWA, /* cursor */
150 /***********************************************************************
153 void LISTBOX_Dump( WND *wnd )
157 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
159 TRACE( "Listbox:\n" );
160 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
161 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
163 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
165 TRACE( "%4d: %-40s %d %08lx %3d\n",
166 i, item->str, item->selected, item->data, item->height );
171 /***********************************************************************
172 * LISTBOX_GetCurrentPageSize
174 * Return the current page size
176 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
179 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
180 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
182 if ((height += descr->items[i].height) > descr->height) break;
184 if (i == descr->top_item) return 1;
185 else return i - descr->top_item;
189 /***********************************************************************
190 * LISTBOX_GetMaxTopIndex
192 * Return the maximum possible index for the top of the listbox.
194 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
198 if (descr->style & LBS_OWNERDRAWVARIABLE)
200 page = descr->height;
201 for (max = descr->nb_items - 1; max >= 0; max--)
202 if ((page -= descr->items[max].height) < 0) break;
203 if (max < descr->nb_items - 1) max++;
205 else if (descr->style & LBS_MULTICOLUMN)
207 if ((page = descr->width / descr->column_width) < 1) page = 1;
208 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
209 max = (max - page) * descr->page_size;
213 max = descr->nb_items - descr->page_size;
215 if (max < 0) max = 0;
220 /***********************************************************************
221 * LISTBOX_UpdateScroll
223 * Update the scrollbars. Should be called whenever the content
224 * of the listbox changes.
226 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
230 /* Check the listbox scroll bar flags individually before we call
231 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
232 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
233 scroll bar when we do not need one.
234 if (!(descr->style & WS_VSCROLL)) return;
237 /* It is important that we check descr->style, and not wnd->dwStyle,
238 for WS_VSCROLL, as the former is exactly the one passed in
239 argument to CreateWindow.
240 In Windows (and from now on in Wine :) a listbox created
241 with such a style (no WS_SCROLL) does not update
242 the scrollbar with listbox-related data, thus letting
243 the programmer use it for his/her own purposes. */
245 if (descr->style & LBS_NOREDRAW) return;
246 info.cbSize = sizeof(info);
248 if (descr->style & LBS_MULTICOLUMN)
251 info.nMax = (descr->nb_items - 1) / descr->page_size;
252 info.nPos = descr->top_item / descr->page_size;
253 info.nPage = descr->width / descr->column_width;
254 if (info.nPage < 1) info.nPage = 1;
255 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
256 if (descr->style & LBS_DISABLENOSCROLL)
257 info.fMask |= SIF_DISABLENOSCROLL;
258 if (descr->style & WS_HSCROLL)
259 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
261 info.fMask = SIF_RANGE;
262 if (descr->style & WS_VSCROLL)
263 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
268 info.nMax = descr->nb_items - 1;
269 info.nPos = descr->top_item;
270 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
271 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
272 if (descr->style & LBS_DISABLENOSCROLL)
273 info.fMask |= SIF_DISABLENOSCROLL;
274 if (descr->style & WS_VSCROLL)
275 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
277 if (descr->horz_extent)
280 info.nMax = descr->horz_extent - 1;
281 info.nPos = descr->horz_pos;
282 info.nPage = descr->width;
283 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
284 if (descr->style & LBS_DISABLENOSCROLL)
285 info.fMask |= SIF_DISABLENOSCROLL;
286 if (descr->style & WS_HSCROLL)
287 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
294 /***********************************************************************
297 * Set the top item of the listbox, scrolling up or down if necessary.
299 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
302 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
303 if (index > max) index = max;
304 if (index < 0) index = 0;
305 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
306 if (descr->top_item == index) return LB_OKAY;
307 if (descr->style & LBS_MULTICOLUMN)
309 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
310 if (scroll && (abs(diff) < descr->width))
311 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
312 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
320 if (descr->style & LBS_OWNERDRAWVARIABLE)
324 if (index > descr->top_item)
326 for (i = index - 1; i >= descr->top_item; i--)
327 diff -= descr->items[i].height;
331 for (i = index; i < descr->top_item; i++)
332 diff += descr->items[i].height;
336 diff = (descr->top_item - index) * descr->item_height;
338 if (abs(diff) < descr->height)
339 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
340 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
344 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
345 descr->top_item = index;
346 LISTBOX_UpdateScroll( wnd, descr );
351 /***********************************************************************
354 * Update the page size. Should be called when the size of
355 * the client area or the item height changes.
357 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
361 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
363 if (page_size == descr->page_size) return;
364 descr->page_size = page_size;
365 if (descr->style & LBS_MULTICOLUMN)
366 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
367 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
371 /***********************************************************************
374 * Update the size of the listbox. Should be called when the size of
375 * the client area changes.
377 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
381 GetClientRect( wnd->hwndSelf, &rect );
382 descr->width = rect.right - rect.left;
383 descr->height = rect.bottom - rect.top;
384 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
388 if(descr->item_height != 0)
389 remaining = descr->height % descr->item_height;
392 if ((descr->height > descr->item_height) && remaining)
394 if (!(wnd->flags & WIN_ISWIN32))
395 { /* give a margin for error to 16 bits programs - if we need
396 less than the height of the nonclient area, round to the
397 *next* number of items */
398 int ncheight = wnd->rectWindow.bottom - wnd->rectWindow.top - descr->height;
399 if ((descr->item_height - remaining) <= ncheight)
400 remaining = remaining - descr->item_height;
402 TRACE("[%04x]: changing height %d -> %d\n",
403 wnd->hwndSelf, descr->height,
404 descr->height - remaining );
405 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
406 wnd->rectWindow.right - wnd->rectWindow.left,
407 wnd->rectWindow.bottom - wnd->rectWindow.top - remaining,
408 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
412 TRACE("[%04x]: new size = %d,%d\n",
413 wnd->hwndSelf, descr->width, descr->height );
414 LISTBOX_UpdatePage( wnd, descr );
415 LISTBOX_UpdateScroll( wnd, descr );
417 /* Invalidate the focused item so it will be repainted correctly */
418 if (1==LISTBOX_GetItemRect( wnd, descr, descr->focus_item, &rect ))
420 InvalidateRect( wnd->hwndSelf, &rect, FALSE );
425 /***********************************************************************
426 * LISTBOX_GetItemRect
428 * Get the rectangle enclosing an item, in listbox client coordinates.
429 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
431 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
434 /* Index <= 0 is legal even on empty listboxes */
435 if (index && (index >= descr->nb_items)) return -1;
436 SetRect( rect, 0, 0, descr->width, descr->height );
437 if (descr->style & LBS_MULTICOLUMN)
439 INT col = (index / descr->page_size) -
440 (descr->top_item / descr->page_size);
441 rect->left += col * descr->column_width;
442 rect->right = rect->left + descr->column_width;
443 rect->top += (index % descr->page_size) * descr->item_height;
444 rect->bottom = rect->top + descr->item_height;
446 else if (descr->style & LBS_OWNERDRAWVARIABLE)
449 rect->right += descr->horz_pos;
450 if ((index >= 0) && (index < descr->nb_items))
452 if (index < descr->top_item)
454 for (i = descr->top_item-1; i >= index; i--)
455 rect->top -= descr->items[i].height;
459 for (i = descr->top_item; i < index; i++)
460 rect->top += descr->items[i].height;
462 rect->bottom = rect->top + descr->items[index].height;
468 rect->top += (index - descr->top_item) * descr->item_height;
469 rect->bottom = rect->top + descr->item_height;
470 rect->right += descr->horz_pos;
473 return ((rect->left < descr->width) && (rect->right > 0) &&
474 (rect->top < descr->height) && (rect->bottom > 0));
478 /***********************************************************************
479 * LISTBOX_GetItemFromPoint
481 * Return the item nearest from point (x,y) (in client coordinates).
483 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
486 INT index = descr->top_item;
488 if (!descr->nb_items) return -1; /* No items */
489 if (descr->style & LBS_OWNERDRAWVARIABLE)
494 while (index < descr->nb_items)
496 if ((pos += descr->items[index].height) > y) break;
505 if ((pos -= descr->items[index].height) <= y) break;
509 else if (descr->style & LBS_MULTICOLUMN)
511 if (y >= descr->item_height * descr->page_size) return -1;
512 if (y >= 0) index += y / descr->item_height;
513 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
514 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
518 index += (y / descr->item_height);
520 if (index < 0) return 0;
521 if (index >= descr->nb_items) return -1;
526 /***********************************************************************
531 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
532 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
534 LB_ITEMDATA *item = NULL;
535 if (index < descr->nb_items) item = &descr->items[index];
537 if (IS_OWNERDRAW(descr))
545 if (action == ODA_FOCUS)
546 DrawFocusRect( hdc, rect );
548 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
552 /* some programs mess with the clipping region when
553 drawing the item, *and* restore the previous region
554 after they are done, so a region has better to exist
555 else everything ends clipped */
556 GetClientRect(wnd->hwndSelf, &r);
557 hrgn = CreateRectRgnIndirect(&r);
558 SelectClipRgn( hdc, hrgn);
559 DeleteObject( hrgn );
561 dis.CtlType = ODT_LISTBOX;
562 dis.CtlID = wnd->wIDmenu;
563 dis.hwndItem = wnd->hwndSelf;
564 dis.itemAction = action;
568 if (item && item->selected) dis.itemState |= ODS_SELECTED;
569 if (!ignoreFocus && (descr->focus_item == index) &&
571 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
572 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
573 dis.itemData = item ? item->data : 0;
575 TRACE("[%04x]: drawitem %d (%s) action=%02x "
576 "state=%02x rect=%d,%d-%d,%d\n",
577 wnd->hwndSelf, index, item ? item->str : "", action,
578 dis.itemState, rect->left, rect->top,
579 rect->right, rect->bottom );
580 SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
584 COLORREF oldText = 0, oldBk = 0;
586 if (action == ODA_FOCUS)
588 DrawFocusRect( hdc, rect );
591 if (item && item->selected)
593 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
594 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
597 TRACE("[%04x]: painting %d (%s) action=%02x "
598 "rect=%d,%d-%d,%d\n",
599 wnd->hwndSelf, index, item ? item->str : "", action,
600 rect->left, rect->top, rect->right, rect->bottom );
602 ExtTextOutA( hdc, rect->left + 1, rect->top,
603 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
604 else if (!(descr->style & LBS_USETABSTOPS))
605 ExtTextOutA( hdc, rect->left + 1, rect->top,
606 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
607 strlen(item->str), NULL );
610 /* Output empty string to paint background in the full width. */
611 ExtTextOutA( hdc, rect->left + 1, rect->top,
612 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
613 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
614 item->str, strlen(item->str),
615 descr->nb_tabs, descr->tabs, 0);
617 if (item && item->selected)
619 SetBkColor( hdc, oldBk );
620 SetTextColor( hdc, oldText );
622 if (!ignoreFocus && (descr->focus_item == index) &&
624 (descr->in_focus)) DrawFocusRect( hdc, rect );
629 /***********************************************************************
632 * Change the redraw flag.
634 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
638 if (!(descr->style & LBS_NOREDRAW)) return;
639 descr->style &= ~LBS_NOREDRAW;
640 if (descr->style & LBS_DISPLAYCHANGED)
641 { /* page was changed while setredraw false, refresh automatically */
642 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
643 if ((descr->top_item + descr->page_size) > descr->nb_items)
644 { /* reset top of page if less than number of items/page */
645 descr->top_item = descr->nb_items - descr->page_size;
646 if (descr->top_item < 0) descr->top_item = 0;
648 descr->style &= ~LBS_DISPLAYCHANGED;
650 LISTBOX_UpdateScroll( wnd, descr );
652 else descr->style |= LBS_NOREDRAW;
656 /***********************************************************************
657 * LISTBOX_RepaintItem
659 * Repaint a single item synchronously.
661 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
667 HBRUSH hbrush, oldBrush = 0;
669 /* Do not repaint the item if the item is not visible */
670 if (!IsWindowVisible(wnd->hwndSelf)) return;
671 if (descr->style & LBS_NOREDRAW)
673 descr->style |= LBS_DISPLAYCHANGED;
676 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
677 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
678 if (descr->font) oldFont = SelectObject( hdc, descr->font );
679 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
680 hdc, (LPARAM)wnd->hwndSelf );
681 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
682 if (wnd->dwStyle & WS_DISABLED)
683 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
684 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
685 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action, FALSE );
686 if (oldFont) SelectObject( hdc, oldFont );
687 if (oldBrush) SelectObject( hdc, oldBrush );
688 ReleaseDC( wnd->hwndSelf, hdc );
692 /***********************************************************************
693 * LISTBOX_InitStorage
695 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
700 nb_items += LB_ARRAY_GRANULARITY - 1;
701 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
703 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
704 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
705 nb_items * sizeof(LB_ITEMDATA) )))
707 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
715 /***********************************************************************
716 * LISTBOX_SetTabStops
718 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
719 LPINT tabs, BOOL short_ints )
721 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
722 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
723 if (!(descr->nb_tabs = count))
728 /* FIXME: count = 1 */
729 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
730 descr->nb_tabs * sizeof(INT) )))
735 LPINT16 p = (LPINT16)tabs;
737 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
738 for (i = 0; i < descr->nb_tabs; i++) {
739 descr->tabs[i] = *p++<<1; /* FIXME */
740 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
742 if (TRACE_ON(listbox)) DPRINTF("\n");
744 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
745 /* FIXME: repaint the window? */
750 /***********************************************************************
753 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
756 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
757 if (HAS_STRINGS(descr))
760 return strlen(descr->items[index].str);
761 strcpy( buffer, descr->items[index].str );
762 return strlen(buffer);
765 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
766 return sizeof(DWORD);
771 /***********************************************************************
772 * LISTBOX_FindStringPos
774 * Find the nearest string located before a given string in sort order.
775 * If 'exact' is TRUE, return an error if we don't get an exact match.
777 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
780 INT index, min, max, res = -1;
782 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
784 max = descr->nb_items;
787 index = (min + max) / 2;
788 if (HAS_STRINGS(descr))
789 res = lstrcmpiA( descr->items[index].str, str );
792 COMPAREITEMSTRUCT cis;
794 cis.CtlType = ODT_LISTBOX;
795 cis.CtlID = wnd->wIDmenu;
796 cis.hwndItem = wnd->hwndSelf;
798 cis.itemData1 = descr->items[index].data;
800 cis.itemData2 = (DWORD)str;
801 cis.dwLocaleId = descr->locale;
802 res = SendMessageA( descr->owner, WM_COMPAREITEM,
803 wnd->wIDmenu, (LPARAM)&cis );
805 if (!res) return index;
806 if (res > 0) max = index;
807 else min = index + 1;
809 return exact ? -1 : max;
813 /***********************************************************************
814 * LISTBOX_FindFileStrPos
816 * Find the nearest string located before a given string in directory
817 * sort order (i.e. first files, then directories, then drives).
819 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
821 INT min, max, res = -1;
823 if (!HAS_STRINGS(descr))
824 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
826 max = descr->nb_items;
829 INT index = (min + max) / 2;
830 const char *p = descr->items[index].str;
831 if (*p == '[') /* drive or directory */
833 if (*str != '[') res = -1;
834 else if (p[1] == '-') /* drive */
836 if (str[1] == '-') res = str[2] - p[2];
841 if (str[1] == '-') res = 1;
842 else res = lstrcmpiA( str, p );
847 if (*str == '[') res = 1;
848 else res = lstrcmpiA( str, p );
850 if (!res) return index;
851 if (res < 0) max = index;
852 else min = index + 1;
858 /***********************************************************************
861 * Find the item beginning with a given string.
863 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
864 LPCSTR str, BOOL exact )
869 if (start >= descr->nb_items) start = -1;
870 item = descr->items + start + 1;
871 if (HAS_STRINGS(descr))
873 if (!str || ! str[0] ) return LB_ERR;
876 for (i = start + 1; i < descr->nb_items; i++, item++)
877 if (!lstrcmpiA( str, item->str )) return i;
878 for (i = 0, item = descr->items; i <= start; i++, item++)
879 if (!lstrcmpiA( str, item->str )) return i;
883 /* Special case for drives and directories: ignore prefix */
884 #define CHECK_DRIVE(item) \
885 if ((item)->str[0] == '[') \
887 if (!strncasecmp( str, (item)->str+1, len )) return i; \
888 if (((item)->str[1] == '-') && !strncasecmp(str,(item)->str+2,len)) \
892 INT len = strlen(str);
893 for (i = start + 1; i < descr->nb_items; i++, item++)
895 if (!strncasecmp( str, item->str, len )) return i;
898 for (i = 0, item = descr->items; i <= start; i++, item++)
900 if (!strncasecmp( str, item->str, len )) return i;
908 if (exact && (descr->style & LBS_SORT))
909 /* If sorted, use a WM_COMPAREITEM binary search */
910 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
912 /* Otherwise use a linear search */
913 for (i = start + 1; i < descr->nb_items; i++, item++)
914 if (item->data == (DWORD)str) return i;
915 for (i = 0, item = descr->items; i <= start; i++, item++)
916 if (item->data == (DWORD)str) return i;
922 /***********************************************************************
923 * LISTBOX_GetSelCount
925 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
928 LB_ITEMDATA *item = descr->items;
930 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
931 for (i = count = 0; i < descr->nb_items; i++, item++)
932 if (item->selected) count++;
937 /***********************************************************************
938 * LISTBOX_GetSelItems16
940 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
944 LB_ITEMDATA *item = descr->items;
946 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
947 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
948 if (item->selected) array[count++] = (INT16)i;
953 /***********************************************************************
954 * LISTBOX_GetSelItems32
956 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
960 LB_ITEMDATA *item = descr->items;
962 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
963 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
964 if (item->selected) array[count++] = i;
969 /***********************************************************************
972 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
974 INT i, col_pos = descr->page_size - 1;
976 RECT focusRect = {-1, -1, -1, -1};
978 HBRUSH hbrush, oldBrush = 0;
980 if (descr->style & LBS_NOREDRAW) return 0;
982 SetRect( &rect, 0, 0, descr->width, descr->height );
983 if (descr->style & LBS_MULTICOLUMN)
984 rect.right = rect.left + descr->column_width;
985 else if (descr->horz_pos)
987 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
988 rect.right += descr->horz_pos;
991 if (descr->font) oldFont = SelectObject( hdc, descr->font );
992 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
993 hdc, (LPARAM)wnd->hwndSelf );
994 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
995 if (wnd->dwStyle & WS_DISABLED)
996 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
998 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1001 /* Special case for empty listbox: paint focus rect */
1002 rect.bottom = rect.top + descr->item_height;
1003 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
1005 rect.top = rect.bottom;
1008 /* Paint all the item, regarding the selection
1009 Focus state will be painted after */
1011 for (i = descr->top_item; i < descr->nb_items; i++)
1013 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1014 rect.bottom = rect.top + descr->item_height;
1016 rect.bottom = rect.top + descr->items[i].height;
1018 if (i == descr->focus_item)
1020 /* keep the focus rect, to paint the focus item after */
1021 focusRect.left = rect.left;
1022 focusRect.right = rect.right;
1023 focusRect.top = rect.top;
1024 focusRect.bottom = rect.bottom;
1026 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1027 rect.top = rect.bottom;
1029 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1031 if (!IS_OWNERDRAW(descr))
1033 /* Clear the bottom of the column */
1034 if (rect.top < descr->height)
1036 rect.bottom = descr->height;
1037 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1038 &rect, NULL, 0, NULL );
1042 /* Go to the next column */
1043 rect.left += descr->column_width;
1044 rect.right += descr->column_width;
1046 col_pos = descr->page_size - 1;
1051 if (rect.top >= descr->height) break;
1055 /* Paint the focus item now */
1056 if (focusRect.top != focusRect.bottom && descr->caret_on)
1057 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1059 if (!IS_OWNERDRAW(descr))
1061 /* Clear the remainder of the client area */
1062 if (rect.top < descr->height)
1064 rect.bottom = descr->height;
1065 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1066 &rect, NULL, 0, NULL );
1068 if (rect.right < descr->width)
1070 rect.left = rect.right;
1071 rect.right = descr->width;
1073 rect.bottom = descr->height;
1074 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1075 &rect, NULL, 0, NULL );
1078 if (oldFont) SelectObject( hdc, oldFont );
1079 if (oldBrush) SelectObject( hdc, oldBrush );
1084 /***********************************************************************
1085 * LISTBOX_InvalidateItems
1087 * Invalidate all items from a given item. If the specified item is not
1088 * visible, nothing happens.
1090 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1094 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1096 if (descr->style & LBS_NOREDRAW)
1098 descr->style |= LBS_DISPLAYCHANGED;
1101 rect.bottom = descr->height;
1102 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1103 if (descr->style & LBS_MULTICOLUMN)
1105 /* Repaint the other columns */
1106 rect.left = rect.right;
1107 rect.right = descr->width;
1109 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1115 /***********************************************************************
1116 * LISTBOX_GetItemHeight
1118 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1120 if (descr->style & LBS_OWNERDRAWVARIABLE)
1122 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1123 return descr->items[index].height;
1125 else return descr->item_height;
1129 /***********************************************************************
1130 * LISTBOX_SetItemHeight
1132 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1135 if (!height) height = 1;
1137 if (descr->style & LBS_OWNERDRAWVARIABLE)
1139 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1140 TRACE("[%04x]: item %d height = %d\n",
1141 wnd->hwndSelf, index, height );
1142 descr->items[index].height = height;
1143 LISTBOX_UpdateScroll( wnd, descr );
1144 LISTBOX_InvalidateItems( wnd, descr, index );
1146 else if (height != descr->item_height)
1148 TRACE("[%04x]: new height = %d\n",
1149 wnd->hwndSelf, height );
1150 descr->item_height = height;
1151 LISTBOX_UpdatePage( wnd, descr );
1152 LISTBOX_UpdateScroll( wnd, descr );
1153 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1159 /***********************************************************************
1160 * LISTBOX_SetHorizontalPos
1162 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1166 if (pos > descr->horz_extent - descr->width)
1167 pos = descr->horz_extent - descr->width;
1168 if (pos < 0) pos = 0;
1169 if (!(diff = descr->horz_pos - pos)) return;
1170 TRACE("[%04x]: new horz pos = %d\n",
1171 wnd->hwndSelf, pos );
1172 descr->horz_pos = pos;
1173 LISTBOX_UpdateScroll( wnd, descr );
1174 if (abs(diff) < descr->width)
1175 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1176 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1178 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1182 /***********************************************************************
1183 * LISTBOX_SetHorizontalExtent
1185 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1188 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1190 if (extent <= 0) extent = 1;
1191 if (extent == descr->horz_extent) return LB_OKAY;
1192 TRACE("[%04x]: new horz extent = %d\n",
1193 wnd->hwndSelf, extent );
1194 descr->horz_extent = extent;
1195 if (descr->horz_pos > extent - descr->width)
1196 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1198 LISTBOX_UpdateScroll( wnd, descr );
1203 /***********************************************************************
1204 * LISTBOX_SetColumnWidth
1206 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1208 if (width == descr->column_width) return LB_OKAY;
1209 TRACE("[%04x]: new column width = %d\n",
1210 wnd->hwndSelf, width );
1211 descr->column_width = width;
1212 LISTBOX_UpdatePage( wnd, descr );
1217 /***********************************************************************
1220 * Returns the item height.
1222 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1230 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1232 ERR("unable to get DC.\n" );
1235 if (font) oldFont = SelectObject( hdc, font );
1236 GetTextMetricsA( hdc, &tm );
1237 if (oldFont) SelectObject( hdc, oldFont );
1238 ReleaseDC( wnd->hwndSelf, hdc );
1239 if (!IS_OWNERDRAW(descr))
1240 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1241 return tm.tmHeight ;
1245 /***********************************************************************
1246 * LISTBOX_MakeItemVisible
1248 * Make sure that a given item is partially or fully visible.
1250 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1255 if (index <= descr->top_item) top = index;
1256 else if (descr->style & LBS_MULTICOLUMN)
1258 INT cols = descr->width;
1259 if (!fully) cols += descr->column_width - 1;
1260 if (cols >= descr->column_width) cols /= descr->column_width;
1262 if (index < descr->top_item + (descr->page_size * cols)) return;
1263 top = index - descr->page_size * (cols - 1);
1265 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1267 INT height = fully ? descr->items[index].height : 1;
1268 for (top = index; top > descr->top_item; top--)
1269 if ((height += descr->items[top-1].height) > descr->height) break;
1273 if (index < descr->top_item + descr->page_size) return;
1274 if (!fully && (index == descr->top_item + descr->page_size) &&
1275 (descr->height > (descr->page_size * descr->item_height))) return;
1276 top = index - descr->page_size + 1;
1278 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1281 /***********************************************************************
1282 * LISTBOX_SetCaretIndex
1285 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1288 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1289 BOOL fully_visible )
1291 INT oldfocus = descr->focus_item;
1293 if (descr->style & LBS_NOSEL) return LB_ERR;
1294 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1295 if (index == oldfocus) return LB_OKAY;
1296 descr->focus_item = index;
1297 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1298 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1300 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1301 if (descr->caret_on && (descr->in_focus))
1302 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1308 /***********************************************************************
1309 * LISTBOX_SelectItemRange
1311 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1313 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1318 /* A few sanity checks */
1320 if (descr->style & LBS_NOSEL) return LB_ERR;
1321 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1322 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1323 if (last == -1) last = descr->nb_items - 1;
1324 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1325 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1326 /* selected_item reflects last selected/unselected item on multiple sel */
1327 descr->selected_item = last;
1329 if (on) /* Turn selection on */
1331 for (i = first; i <= last; i++)
1333 if (descr->items[i].selected) continue;
1334 descr->items[i].selected = TRUE;
1335 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1337 LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1339 else /* Turn selection off */
1341 for (i = first; i <= last; i++)
1343 if (!descr->items[i].selected) continue;
1344 descr->items[i].selected = FALSE;
1345 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1351 /***********************************************************************
1352 * LISTBOX_SetSelection
1354 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1355 BOOL on, BOOL send_notify )
1357 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1359 if (descr->style & LBS_NOSEL) return LB_ERR;
1360 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1361 if (descr->style & LBS_MULTIPLESEL)
1363 if (index == -1) /* Select all items */
1364 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1365 else /* Only one item */
1366 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1370 INT oldsel = descr->selected_item;
1371 if (index == oldsel) return LB_OKAY;
1372 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1373 if (index != -1) descr->items[index].selected = TRUE;
1374 descr->selected_item = index;
1375 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1376 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1377 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1378 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1380 if( descr->lphc ) /* set selection change flag for parent combo */
1381 descr->lphc->wState |= CBF_SELCHANGE;
1387 /***********************************************************************
1390 * Change the caret position and extend the selection to the new caret.
1392 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1393 BOOL fully_visible )
1395 INT oldfocus = descr->focus_item;
1397 if ((index < 0) || (index >= descr->nb_items))
1400 /* Important, repaint needs to be done in this order if
1401 you want to mimic Windows behavior:
1402 1. Remove the focus and paint the item
1403 2. Remove the selection and paint the item(s)
1404 3. Set the selection and repaint the item(s)
1405 4. Set the focus to 'index' and repaint the item */
1407 /* 1. remove the focus and repaint the item */
1408 descr->focus_item = -1;
1409 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1410 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1412 /* 2. then turn off the previous selection */
1413 /* 3. repaint the new selected item */
1414 if (descr->style & LBS_EXTENDEDSEL)
1416 if (descr->anchor_item != -1)
1418 INT first = min( index, descr->anchor_item );
1419 INT last = max( index, descr->anchor_item );
1421 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1422 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1423 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1426 else if (!(descr->style & LBS_MULTIPLESEL))
1428 /* Set selection to new caret item */
1429 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1432 /* 4. repaint the new item with the focus */
1433 descr->focus_item = index;
1434 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1435 if (descr->caret_on && (descr->in_focus))
1436 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1440 /***********************************************************************
1441 * LISTBOX_InsertItem
1443 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1444 LPSTR str, DWORD data )
1448 INT oldfocus = descr->focus_item;
1450 if (index == -1) index = descr->nb_items;
1451 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1452 if (!descr->items) max_items = 0;
1453 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1454 if (descr->nb_items == max_items)
1456 /* We need to grow the array */
1457 max_items += LB_ARRAY_GRANULARITY;
1458 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1459 max_items * sizeof(LB_ITEMDATA) )))
1461 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1464 descr->items = item;
1467 /* Insert the item structure */
1469 item = &descr->items[index];
1470 if (index < descr->nb_items)
1471 RtlMoveMemory( item + 1, item,
1472 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1476 item->selected = FALSE;
1479 /* Get item height */
1481 if (descr->style & LBS_OWNERDRAWVARIABLE)
1483 MEASUREITEMSTRUCT mis;
1485 mis.CtlType = ODT_LISTBOX;
1486 mis.CtlID = wnd->wIDmenu;
1488 mis.itemData = descr->items[index].data;
1489 mis.itemHeight = descr->item_height;
1490 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1491 item->height = mis.itemHeight ? mis.itemHeight : 1;
1492 TRACE("[%04x]: measure item %d (%s) = %d\n",
1493 wnd->hwndSelf, index, str ? str : "", item->height );
1496 /* Repaint the items */
1498 LISTBOX_UpdateScroll( wnd, descr );
1499 LISTBOX_InvalidateItems( wnd, descr, index );
1501 /* Move selection and focused item */
1502 /* If listbox was empty, set focus to the first item */
1503 if (descr->nb_items == 1)
1504 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1505 /* single select don't change selection index in win31 */
1506 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1508 descr->selected_item++;
1509 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1513 if (index <= descr->selected_item)
1515 descr->selected_item++;
1516 descr->focus_item = oldfocus; /* focus not changed */
1523 /***********************************************************************
1524 * LISTBOX_InsertString
1526 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1529 LPSTR new_str = NULL;
1533 if (HAS_STRINGS(descr))
1536 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1538 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1542 else data = (DWORD)str;
1544 if (index == -1) index = descr->nb_items;
1545 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1547 if (new_str) HeapFree( descr->heap, 0, new_str );
1551 TRACE("[%04x]: added item %d '%s'\n",
1552 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1557 /***********************************************************************
1558 * LISTBOX_DeleteItem
1560 * Delete the content of an item. 'index' must be a valid index.
1562 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1564 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1565 * while Win95 sends it for all items with user data.
1566 * It's probably better to send it too often than not
1567 * often enough, so this is what we do here.
1569 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1571 DELETEITEMSTRUCT dis;
1573 dis.CtlType = ODT_LISTBOX;
1574 dis.CtlID = wnd->wIDmenu;
1576 dis.hwndItem = wnd->hwndSelf;
1577 dis.itemData = descr->items[index].data;
1578 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1580 if (HAS_STRINGS(descr) && descr->items[index].str)
1581 HeapFree( descr->heap, 0, descr->items[index].str );
1585 /***********************************************************************
1586 * LISTBOX_RemoveItem
1588 * Remove an item from the listbox and delete its content.
1590 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1595 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1596 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1598 /* We need to invalidate the original rect instead of the updated one. */
1599 LISTBOX_InvalidateItems( wnd, descr, index );
1601 LISTBOX_DeleteItem( wnd, descr, index );
1603 /* Remove the item */
1605 item = &descr->items[index];
1606 if (index < descr->nb_items-1)
1607 RtlMoveMemory( item, item + 1,
1608 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1610 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1612 /* Shrink the item array if possible */
1614 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1615 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1617 max_items -= LB_ARRAY_GRANULARITY;
1618 item = HeapReAlloc( descr->heap, 0, descr->items,
1619 max_items * sizeof(LB_ITEMDATA) );
1620 if (item) descr->items = item;
1622 /* Repaint the items */
1624 LISTBOX_UpdateScroll( wnd, descr );
1625 /* if we removed the scrollbar, reset the top of the list
1626 (correct for owner-drawn ???) */
1627 if (descr->nb_items == descr->page_size)
1628 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1630 /* Move selection and focused item */
1631 if (!IS_MULTISELECT(descr))
1633 if (index == descr->selected_item)
1634 descr->selected_item = -1;
1635 else if (index < descr->selected_item)
1637 descr->selected_item--;
1638 if (ISWIN31) /* win 31 do not change the selected item number */
1639 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1643 if (descr->focus_item >= descr->nb_items)
1645 descr->focus_item = descr->nb_items - 1;
1646 if (descr->focus_item < 0) descr->focus_item = 0;
1652 /***********************************************************************
1653 * LISTBOX_ResetContent
1655 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1659 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1660 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1661 descr->nb_items = 0;
1662 descr->top_item = 0;
1663 descr->selected_item = -1;
1664 descr->focus_item = 0;
1665 descr->anchor_item = -1;
1666 descr->items = NULL;
1670 /***********************************************************************
1673 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1677 if (HAS_STRINGS(descr)) return LB_ERR;
1678 /* FIXME: this is far from optimal... */
1679 if (count > descr->nb_items)
1681 while (count > descr->nb_items)
1682 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1685 else if (count < descr->nb_items)
1687 while (count < descr->nb_items)
1688 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1695 /***********************************************************************
1698 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1699 LPCSTR filespec, BOOL long_names )
1702 LRESULT ret = LB_OKAY;
1703 WIN32_FIND_DATAA entry;
1706 /* don't scan directory if we just want drives exclusively */
1707 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1708 /* scan directory */
1709 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1711 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1718 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1720 if (!(attrib & DDL_DIRECTORY) ||
1721 !strcmp( entry.cAlternateFileName, "." )) continue;
1722 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1723 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1725 else /* not a directory */
1727 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1728 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1730 if ((attrib & DDL_EXCLUSIVE) &&
1731 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1734 if (long_names) strcpy( buffer, entry.cFileName );
1735 else strcpy( buffer, entry.cAlternateFileName );
1737 if (!long_names) CharLowerA( buffer );
1738 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1739 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1741 } while (FindNextFileA( handle, &entry ));
1742 FindClose( handle );
1747 if ((ret >= 0) && (attrib & DDL_DRIVES))
1749 char buffer[] = "[-a-]";
1750 char root[] = "A:\\";
1752 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1754 if (GetDriveTypeA(root) <= DRIVE_NO_ROOT_DIR) continue;
1755 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1763 /***********************************************************************
1764 * LISTBOX_HandleVScroll
1766 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1767 WPARAM wParam, LPARAM lParam )
1771 if (descr->style & LBS_MULTICOLUMN) return 0;
1772 switch(LOWORD(wParam))
1775 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1778 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1781 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1782 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1785 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1786 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1788 case SB_THUMBPOSITION:
1789 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1792 info.cbSize = sizeof(info);
1793 info.fMask = SIF_TRACKPOS;
1794 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1795 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1798 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1801 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1808 /***********************************************************************
1809 * LISTBOX_HandleHScroll
1811 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1812 WPARAM wParam, LPARAM lParam )
1817 if (descr->style & LBS_MULTICOLUMN)
1819 switch(LOWORD(wParam))
1822 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1826 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1830 page = descr->width / descr->column_width;
1831 if (page < 1) page = 1;
1832 LISTBOX_SetTopItem( wnd, descr,
1833 descr->top_item - page * descr->page_size, TRUE );
1836 page = descr->width / descr->column_width;
1837 if (page < 1) page = 1;
1838 LISTBOX_SetTopItem( wnd, descr,
1839 descr->top_item + page * descr->page_size, TRUE );
1841 case SB_THUMBPOSITION:
1842 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1846 info.cbSize = sizeof(info);
1847 info.fMask = SIF_TRACKPOS;
1848 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1849 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1853 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1856 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1860 else if (descr->horz_extent)
1862 switch(LOWORD(wParam))
1865 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1868 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1871 LISTBOX_SetHorizontalPos( wnd, descr,
1872 descr->horz_pos - descr->width );
1875 LISTBOX_SetHorizontalPos( wnd, descr,
1876 descr->horz_pos + descr->width );
1878 case SB_THUMBPOSITION:
1879 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1882 info.cbSize = sizeof(info);
1883 info.fMask = SIF_TRACKPOS;
1884 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1885 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1888 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1891 LISTBOX_SetHorizontalPos( wnd, descr,
1892 descr->horz_extent - descr->width );
1899 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1901 short gcWheelDelta = 0;
1902 UINT pulScrollLines = 3;
1904 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1906 gcWheelDelta -= (short) HIWORD(wParam);
1908 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1910 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1911 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1912 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1917 /***********************************************************************
1918 * LISTBOX_HandleLButtonDown
1920 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1921 WPARAM wParam, INT x, INT y )
1923 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1924 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1925 wnd->hwndSelf, x, y, index );
1926 if (!descr->caret_on && (descr->in_focus)) return 0;
1928 if (!descr->in_focus)
1930 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1931 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1932 : descr->lphc->self->hwndSelf );
1935 if (index == -1) return 0;
1937 if (descr->style & LBS_EXTENDEDSEL)
1939 /* we should perhaps make sure that all items are deselected
1940 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1941 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1942 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1945 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1946 if (wParam & MK_CONTROL)
1948 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1949 LISTBOX_SetSelection( wnd, descr, index,
1950 !descr->items[index].selected,
1951 (descr->style & LBS_NOTIFY) != 0);
1953 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1957 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1958 LISTBOX_SetSelection( wnd, descr, index,
1959 (!(descr->style & LBS_MULTIPLESEL) ||
1960 !descr->items[index].selected),
1961 (descr->style & LBS_NOTIFY) != 0 );
1964 descr->captured = TRUE;
1965 SetCapture( wnd->hwndSelf );
1969 if (descr->style & LBS_NOTIFY )
1970 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1971 MAKELPARAM( x, y ) );
1972 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1979 if (DragDetect( wnd->hwndSelf, pt ))
1980 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1987 /*************************************************************************
1988 * LISTBOX_HandleLButtonDownCombo [Internal]
1990 * Process LButtonDown message for the ComboListBox
1993 * pWnd [I] The windows internal structure
1994 * pDescr [I] The ListBox internal structure
1995 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1996 * x [I] X Mouse Coordinate
1997 * y [I] Y Mouse Coordinate
2000 * 0 since we are processing the WM_LBUTTONDOWN Message
2003 * This function is only to be used when a ListBox is a ComboListBox
2006 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
2007 UINT msg, WPARAM wParam, INT x, INT y)
2009 RECT clientRect, screenRect;
2015 GetClientRect(pWnd->hwndSelf, &clientRect);
2017 if(PtInRect(&clientRect, mousePos))
2019 /* MousePos is in client, resume normal processing */
2020 if (msg == WM_LBUTTONDOWN)
2022 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2023 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
2025 else if (pDescr->style & LBS_NOTIFY)
2026 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
2031 POINT screenMousePos;
2032 HWND hWndOldCapture;
2034 /* Check the Non-Client Area */
2035 screenMousePos = mousePos;
2036 hWndOldCapture = GetCapture();
2038 GetWindowRect(pWnd->hwndSelf, &screenRect);
2039 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
2041 if(!PtInRect(&screenRect, screenMousePos))
2043 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2044 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2049 /* Check to see the NC is a scrollbar */
2051 /* Check Vertical scroll bar */
2052 if (pWnd->dwStyle & WS_VSCROLL)
2054 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2055 if (PtInRect( &clientRect, mousePos ))
2057 nHitTestType = HTVSCROLL;
2060 /* Check horizontal scroll bar */
2061 if (pWnd->dwStyle & WS_HSCROLL)
2063 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2064 if (PtInRect( &clientRect, mousePos ))
2066 nHitTestType = HTHSCROLL;
2069 /* Windows sends this message when a scrollbar is clicked
2072 if(nHitTestType != 0)
2074 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2075 MAKELONG(screenMousePos.x, screenMousePos.y));
2077 /* Resume the Capture after scrolling is complete
2079 if(hWndOldCapture != 0)
2081 SetCapture(hWndOldCapture);
2088 /***********************************************************************
2089 * LISTBOX_HandleLButtonUp
2091 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2093 if (LISTBOX_Timer != LB_TIMER_NONE)
2094 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2095 LISTBOX_Timer = LB_TIMER_NONE;
2096 if (descr->captured)
2098 descr->captured = FALSE;
2099 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2100 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2101 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2107 /***********************************************************************
2108 * LISTBOX_HandleTimer
2110 * Handle scrolling upon a timer event.
2111 * Return TRUE if scrolling should continue.
2113 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2114 INT index, TIMER_DIRECTION dir )
2119 if (descr->top_item) index = descr->top_item - 1;
2123 if (descr->top_item) index -= descr->page_size;
2126 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2127 if (index == descr->focus_item) index++;
2128 if (index >= descr->nb_items) index = descr->nb_items - 1;
2130 case LB_TIMER_RIGHT:
2131 if (index + descr->page_size < descr->nb_items)
2132 index += descr->page_size;
2137 if (index == descr->focus_item) return FALSE;
2138 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2143 /***********************************************************************
2144 * LISTBOX_HandleSystemTimer
2146 * WM_SYSTIMER handler.
2148 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2150 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2152 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2153 LISTBOX_Timer = LB_TIMER_NONE;
2159 /***********************************************************************
2160 * LISTBOX_HandleMouseMove
2162 * WM_MOUSEMOVE handler.
2164 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2168 TIMER_DIRECTION dir = LB_TIMER_NONE;
2170 if (!descr->captured) return;
2172 if (descr->style & LBS_MULTICOLUMN)
2175 else if (y >= descr->item_height * descr->page_size)
2176 y = descr->item_height * descr->page_size - 1;
2180 dir = LB_TIMER_LEFT;
2183 else if (x >= descr->width)
2185 dir = LB_TIMER_RIGHT;
2186 x = descr->width - 1;
2191 if (y < 0) dir = LB_TIMER_UP; /* above */
2192 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2195 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2196 if (index == -1) index = descr->focus_item;
2197 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2199 /* Start/stop the system timer */
2201 if (dir != LB_TIMER_NONE)
2202 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2203 else if (LISTBOX_Timer != LB_TIMER_NONE)
2204 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2205 LISTBOX_Timer = dir;
2209 /***********************************************************************
2210 * LISTBOX_HandleKeyDown
2212 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2215 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2216 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2217 bForceSelection = FALSE; /* only for single select list */
2219 if (descr->style & LBS_WANTKEYBOARDINPUT)
2221 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2222 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2224 if (caret == -2) return 0;
2226 if (caret == -1) switch(wParam)
2229 if (descr->style & LBS_MULTICOLUMN)
2231 bForceSelection = FALSE;
2232 if (descr->focus_item >= descr->page_size)
2233 caret = descr->focus_item - descr->page_size;
2238 caret = descr->focus_item - 1;
2239 if (caret < 0) caret = 0;
2242 if (descr->style & LBS_MULTICOLUMN)
2244 bForceSelection = FALSE;
2245 if (descr->focus_item + descr->page_size < descr->nb_items)
2246 caret = descr->focus_item + descr->page_size;
2251 caret = descr->focus_item + 1;
2252 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2256 if (descr->style & LBS_MULTICOLUMN)
2258 INT page = descr->width / descr->column_width;
2259 if (page < 1) page = 1;
2260 caret = descr->focus_item - (page * descr->page_size) + 1;
2262 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2263 if (caret < 0) caret = 0;
2266 if (descr->style & LBS_MULTICOLUMN)
2268 INT page = descr->width / descr->column_width;
2269 if (page < 1) page = 1;
2270 caret = descr->focus_item + (page * descr->page_size) - 1;
2272 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2273 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2279 caret = descr->nb_items - 1;
2282 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2283 else if (descr->style & LBS_MULTIPLESEL)
2285 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2286 !descr->items[descr->focus_item].selected,
2287 (descr->style & LBS_NOTIFY) != 0 );
2291 bForceSelection = FALSE;
2293 if (bForceSelection) /* focused item is used instead of key */
2294 caret = descr->focus_item;
2297 if ((descr->style & LBS_EXTENDEDSEL) &&
2298 !(GetKeyState( VK_SHIFT ) & 0x8000))
2299 descr->anchor_item = caret;
2300 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2301 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2302 if (descr->style & LBS_NOTIFY)
2304 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2306 /* make sure that combo parent doesn't hide us */
2307 descr->lphc->wState |= CBF_NOROLLUP;
2309 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2316 /***********************************************************************
2317 * LISTBOX_HandleChar
2319 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2325 str[0] = wParam & 0xff;
2328 if (descr->style & LBS_WANTKEYBOARDINPUT)
2330 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2331 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2333 if (caret == -2) return 0;
2336 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2339 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2340 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2341 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2342 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2343 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2349 /***********************************************************************
2352 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2355 MEASUREITEMSTRUCT mis;
2358 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2360 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2362 HeapFree( GetProcessHeap(), 0, descr );
2365 GetClientRect( wnd->hwndSelf, &rect );
2366 descr->owner = GetParent( wnd->hwndSelf );
2367 descr->style = wnd->dwStyle;
2368 descr->width = rect.right - rect.left;
2369 descr->height = rect.bottom - rect.top;
2370 descr->items = NULL;
2371 descr->nb_items = 0;
2372 descr->top_item = 0;
2373 descr->selected_item = -1;
2374 descr->focus_item = 0;
2375 descr->anchor_item = -1;
2376 descr->item_height = 1;
2377 descr->page_size = 1;
2378 descr->column_width = 150;
2379 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2380 descr->horz_pos = 0;
2383 descr->caret_on = lphc ? FALSE : TRUE;
2384 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2385 descr->in_focus = FALSE;
2386 descr->captured = FALSE;
2388 descr->locale = 0; /* FIXME */
2391 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2392 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2394 /* Win95 document "List Box Differences" from MSDN:
2395 If a list box in a version 3.x application has either the
2396 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2397 horizontal and vertical scroll bars.
2399 descr->style |= WS_VSCROLL | WS_HSCROLL;
2404 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2405 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2406 descr->owner = lphc->self->hwndSelf;
2409 *(LB_DESCR **)wnd->wExtra = descr;
2411 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2413 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2414 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2415 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2416 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2418 if (descr->style & LBS_OWNERDRAWFIXED)
2420 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2422 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2423 descr->item_height = lphc->fixedOwnerDrawHeight;
2427 mis.CtlType = ODT_LISTBOX;
2428 mis.CtlID = wnd->wIDmenu;
2432 mis.itemHeight = descr->item_height;
2433 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2434 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2438 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2443 /***********************************************************************
2446 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2448 LISTBOX_ResetContent( wnd, descr );
2449 HeapDestroy( descr->heap );
2450 HeapFree( GetProcessHeap(), 0, descr );
2456 /***********************************************************************
2459 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2460 WPARAM wParam, LPARAM lParam )
2464 HWND hwnd = wnd->hwndSelf;
2467 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2473 if (!LISTBOX_Create( wnd, NULL ))
2475 TRACE("creating wnd=%04x descr=%p\n",
2476 hwnd, *(LB_DESCR **)wnd->wExtra );
2482 * When a listbox is not in a combobox and the look
2483 * is win95, the WS_BORDER style is replaced with
2484 * the WS_EX_CLIENTEDGE style.
2486 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2487 (wnd->dwStyle & WS_BORDER) )
2489 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2490 wnd->dwStyle &= ~ WS_BORDER;
2495 /* Ignore all other messages before we get a WM_CREATE */
2496 return DefWindowProcA( hwnd, msg, wParam, lParam );
2499 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2500 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2503 case LB_RESETCONTENT16:
2504 case LB_RESETCONTENT:
2505 LISTBOX_ResetContent( wnd, descr );
2506 LISTBOX_UpdateScroll( wnd, descr );
2507 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
2510 case LB_ADDSTRING16:
2511 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2514 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2515 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2517 case LB_INSERTSTRING16:
2518 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2519 wParam = (INT)(INT16)wParam;
2521 case LB_INSERTSTRING:
2522 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2525 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2528 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2529 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2531 case LB_DELETESTRING16:
2532 case LB_DELETESTRING:
2533 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2534 return descr->nb_items;
2538 case LB_GETITEMDATA16:
2539 case LB_GETITEMDATA:
2540 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2542 return descr->items[wParam].data;
2544 case LB_SETITEMDATA16:
2545 case LB_SETITEMDATA:
2546 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2548 descr->items[wParam].data = (DWORD)lParam;
2553 return descr->nb_items;
2556 lParam = (LPARAM)MapSL(lParam);
2559 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2561 case LB_GETTEXTLEN16:
2564 if (wParam >= descr->nb_items)
2566 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2569 case LB_GETCURSEL16:
2571 if (descr->nb_items==0)
2573 if (!IS_MULTISELECT(descr))
2574 return descr->selected_item;
2576 if (descr->selected_item!=-1)
2577 return descr->selected_item;
2579 return descr->focus_item;
2580 /* otherwise, if the user tries to move the selection with the */
2581 /* arrow keys, we will give the application something to choke on */
2582 case LB_GETTOPINDEX16:
2583 case LB_GETTOPINDEX:
2584 return descr->top_item;
2586 case LB_GETITEMHEIGHT16:
2587 case LB_GETITEMHEIGHT:
2588 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2590 case LB_SETITEMHEIGHT16:
2591 lParam = LOWORD(lParam);
2593 case LB_SETITEMHEIGHT:
2594 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2596 case LB_ITEMFROMPOINT:
2601 pt.x = LOWORD(lParam);
2602 pt.y = HIWORD(lParam);
2605 rect.right = descr->width;
2606 rect.bottom = descr->height;
2608 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2609 !PtInRect( &rect, pt ) );
2612 case LB_SETCARETINDEX16:
2613 case LB_SETCARETINDEX:
2614 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2615 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2622 case LB_GETCARETINDEX16:
2623 case LB_GETCARETINDEX:
2624 return descr->focus_item;
2626 case LB_SETTOPINDEX16:
2627 case LB_SETTOPINDEX:
2628 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2630 case LB_SETCOLUMNWIDTH16:
2631 case LB_SETCOLUMNWIDTH:
2632 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2634 case LB_GETITEMRECT16:
2637 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2638 CONV_RECT32TO16( &rect, MapSL(lParam) );
2642 case LB_GETITEMRECT:
2643 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2645 case LB_FINDSTRING16:
2646 wParam = (INT)(INT16)wParam;
2647 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2650 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2652 case LB_FINDSTRINGEXACT16:
2653 wParam = (INT)(INT16)wParam;
2654 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2656 case LB_FINDSTRINGEXACT:
2657 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2659 case LB_SELECTSTRING16:
2660 wParam = (INT)(INT16)wParam;
2661 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2663 case LB_SELECTSTRING:
2665 INT index = LISTBOX_FindString( wnd, descr, wParam,
2666 (LPCSTR)lParam, FALSE );
2667 if (index == LB_ERR)
2669 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2674 wParam = (INT)(INT16)wParam;
2677 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2679 return descr->items[wParam].selected;
2682 lParam = (INT)(INT16)lParam;
2685 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2687 case LB_SETCURSEL16:
2688 wParam = (INT)(INT16)wParam;
2691 if (IS_MULTISELECT(descr)) return LB_ERR;
2692 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2693 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2695 case LB_GETSELCOUNT16:
2696 case LB_GETSELCOUNT:
2697 return LISTBOX_GetSelCount( wnd, descr );
2699 case LB_GETSELITEMS16:
2700 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2701 (LPINT16)MapSL(lParam) );
2703 case LB_GETSELITEMS:
2704 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2706 case LB_SELITEMRANGE16:
2707 case LB_SELITEMRANGE:
2708 if (LOWORD(lParam) <= HIWORD(lParam))
2709 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2710 HIWORD(lParam), wParam );
2712 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2713 LOWORD(lParam), wParam );
2715 case LB_SELITEMRANGEEX16:
2716 case LB_SELITEMRANGEEX:
2717 if ((INT)lParam >= (INT)wParam)
2718 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2720 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2722 case LB_GETHORIZONTALEXTENT16:
2723 case LB_GETHORIZONTALEXTENT:
2724 return descr->horz_extent;
2726 case LB_SETHORIZONTALEXTENT16:
2727 case LB_SETHORIZONTALEXTENT:
2728 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2730 case LB_GETANCHORINDEX16:
2731 case LB_GETANCHORINDEX:
2732 return descr->anchor_item;
2734 case LB_SETANCHORINDEX16:
2735 wParam = (INT)(INT16)wParam;
2737 case LB_SETANCHORINDEX:
2738 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2740 descr->anchor_item = (INT)wParam;
2744 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2745 * be set automatically (this is different in Win32) */
2746 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2747 return LISTBOX_Directory( wnd, descr, wParam, MapSL(lParam), FALSE );
2750 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2753 return descr->locale;
2756 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2759 case LB_INITSTORAGE:
2760 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2763 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2765 case LB_SETTABSTOPS16:
2766 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2768 case LB_SETTABSTOPS:
2769 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2773 if (descr->caret_on)
2775 descr->caret_on = TRUE;
2776 if ((descr->focus_item != -1) && (descr->in_focus))
2777 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2782 if (!descr->caret_on)
2784 descr->caret_on = FALSE;
2785 if ((descr->focus_item != -1) && (descr->in_focus))
2786 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2790 return LISTBOX_Destroy( wnd, descr );
2793 InvalidateRect( hwnd, NULL, TRUE );
2797 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2801 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2806 HDC hdc = ( wParam ) ? ((HDC)wParam)
2807 : BeginPaint( hwnd, &ps );
2808 ret = LISTBOX_Paint( wnd, descr, hdc );
2809 if( !wParam ) EndPaint( hwnd, &ps );
2813 LISTBOX_UpdateSize( wnd, descr );
2818 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2819 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2822 descr->in_focus = TRUE;
2823 descr->caret_on = TRUE;
2824 if (descr->focus_item != -1)
2825 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2826 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2829 descr->in_focus = FALSE;
2830 if ((descr->focus_item != -1) && descr->caret_on)
2831 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2832 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2835 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2837 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2838 case WM_MOUSEACTIVATE:
2839 return MA_NOACTIVATE;
2841 if (wParam & (MK_SHIFT | MK_CONTROL))
2842 return DefWindowProcA( hwnd, msg, wParam, lParam );
2843 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2844 case WM_LBUTTONDOWN:
2845 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2846 (INT16)LOWORD(lParam),
2847 (INT16)HIWORD(lParam) );
2848 case WM_LBUTTONDBLCLK:
2849 if (descr->style & LBS_NOTIFY)
2850 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2853 if (GetCapture() == hwnd)
2854 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2855 (INT16)HIWORD(lParam) );
2858 return LISTBOX_HandleLButtonUp( wnd, descr );
2860 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2862 return LISTBOX_HandleChar( wnd, descr, wParam );
2864 return LISTBOX_HandleSystemTimer( wnd, descr );
2866 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2869 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2870 wParam, (LPARAM)wnd->hwndSelf );
2871 GetClientRect(hwnd, &rect);
2872 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2877 return SendMessageA( descr->owner, msg, wParam, lParam );
2881 case WM_QUERYDROPOBJECT:
2886 LPDRAGINFO16 dragInfo = MapSL( lParam );
2887 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2889 return SendMessageA( descr->owner, msg, wParam, lParam );
2894 if ((msg >= WM_USER) && (msg < 0xc000))
2895 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2896 hwnd, msg, wParam, lParam );
2897 return DefWindowProcA( hwnd, msg, wParam, lParam );
2902 /***********************************************************************
2905 * This is just a wrapper for the real wndproc, it only does window locking
2908 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
2910 WND* wndPtr = WIN_FindWndPtr( hwnd );
2911 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2913 WIN_ReleaseWndPtr(wndPtr);
2917 /***********************************************************************
2920 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2922 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2926 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2929 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2931 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2932 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2933 WIN_ReleaseWndPtr(wnd);
2936 WIN_ReleaseWndPtr(wnd);
2941 /***********************************************************************
2942 * ComboLBWndProc_locked
2944 * The real combo listbox wndproc, but called with locked WND struct.
2946 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2947 WPARAM wParam, LPARAM lParam )
2950 HWND hwnd = wnd->hwndSelf;
2954 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2956 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2957 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2959 if( descr || msg == WM_CREATE )
2961 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2966 #define lpcs ((LPCREATESTRUCTA)lParam)
2967 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2968 (UINT)lpcs->lpCreateParams);
2970 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2972 return LISTBOX_Create( wnd, lphc );
2974 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2975 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2981 mousePos.x = (INT16)LOWORD(lParam);
2982 mousePos.y = (INT16)HIWORD(lParam);
2985 * If we are in a dropdown combobox, we simulate that
2986 * the mouse is captured to show the tracking of the item.
2988 GetClientRect(hwnd, &clientRect);
2990 if (PtInRect( &clientRect, mousePos ))
2992 captured = descr->captured;
2993 descr->captured = TRUE;
2995 LISTBOX_HandleMouseMove( wnd, descr,
2996 mousePos.x, mousePos.y);
2998 descr->captured = captured;
3003 LISTBOX_HandleMouseMove( wnd, descr,
3004 mousePos.x, mousePos.y);
3013 * If we are in Win3.1 look, go with the default behavior.
3015 return ListBoxWndProcA( hwnd, msg, wParam, lParam );
3018 if (TWEAK_WineLook > WIN31_LOOK)
3024 * If the mouse button "up" is not in the listbox,
3025 * we make sure there is no selection by re-selecting the
3026 * item that was selected when the listbox was made visible.
3028 mousePos.x = (INT16)LOWORD(lParam);
3029 mousePos.y = (INT16)HIWORD(lParam);
3031 GetClientRect(hwnd, &clientRect);
3034 * When the user clicks outside the combobox and the focus
3035 * is lost, the owning combobox will send a fake buttonup with
3036 * 0xFFFFFFF as the mouse location, we must also revert the
3037 * selection to the original selection.
3039 if ( (lParam == 0xFFFFFFFF) ||
3040 (!PtInRect( &clientRect, mousePos )) )
3042 LISTBOX_MoveCaret( wnd,
3048 return LISTBOX_HandleLButtonUp( wnd, descr );
3049 case WM_LBUTTONDBLCLK:
3050 case WM_LBUTTONDOWN:
3051 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
3052 (INT16)LOWORD(lParam),
3053 (INT16)HIWORD(lParam) );
3054 case WM_MOUSEACTIVATE:
3055 return MA_NOACTIVATE;
3059 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3061 /* for some reason(?) Windows makes it possible to
3062 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3064 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3065 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3066 && (wParam == VK_DOWN || wParam == VK_UP)) )
3068 COMBO_FlipListbox( lphc, FALSE, FALSE );
3072 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3074 case LB_SETCURSEL16:
3076 lRet = ListBoxWndProcA( hwnd, msg, wParam, lParam );
3077 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3080 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3085 return ListBoxWndProcA( hwnd, msg, wParam, lParam );
3088 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3090 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3095 /***********************************************************************
3098 * NOTE: in Windows, winproc address of the ComboLBox is the same
3099 * as that of the Listbox.
3101 * This is just a wrapper for the real wndproc, it only does window locking
3104 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg,
3105 WPARAM wParam, LPARAM lParam )
3107 WND *wnd = WIN_FindWndPtr( hwnd );
3108 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3110 WIN_ReleaseWndPtr(wnd);