4 * Copyright 1996 Alexandre Julliard
9 #include "wine/winuser16.h"
17 #include "debugtools.h"
20 DECLARE_DEBUG_CHANNEL(combo)
21 DECLARE_DEBUG_CHANNEL(listbox)
30 /* Items array granularity */
31 #define LB_ARRAY_GRANULARITY 16
33 /* Scrolling timeout in ms */
34 #define LB_SCROLL_TIMEOUT 50
36 /* Listbox system timer id */
42 LPSTR str; /* Item text */
43 BOOL selected; /* Is item selected? */
44 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
45 DWORD data; /* User data */
48 /* Listbox structure */
51 HANDLE heap; /* Heap for this listbox */
52 HWND owner; /* Owner window to send notifications to */
53 UINT style; /* Window style */
54 INT width; /* Window width */
55 INT height; /* Window height */
56 LB_ITEMDATA *items; /* Array of items */
57 INT nb_items; /* Number of items */
58 INT top_item; /* Top visible item */
59 INT selected_item; /* Selected item */
60 INT focus_item; /* Item that has the focus */
61 INT anchor_item; /* Anchor item for extended selection */
62 INT item_height; /* Default item height */
63 INT page_size; /* Items per listbox page */
64 INT column_width; /* Column width for multi-column listboxes */
65 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
66 INT horz_pos; /* Horizontal position */
67 INT nb_tabs; /* Number of tabs in array */
68 INT *tabs; /* Array of tabs */
69 BOOL caret_on; /* Is caret on? */
70 BOOL captured; /* Is mouse captured? */
71 HFONT font; /* Current font */
72 LCID locale; /* Current locale for string comparisons */
73 LPHEADCOMBO lphc; /* ComboLBox */
77 #define IS_OWNERDRAW(descr) \
78 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
80 #define HAS_STRINGS(descr) \
81 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
83 #define SEND_NOTIFICATION(wnd,descr,code) \
84 (SendMessageA( (descr)->owner, WM_COMMAND, \
85 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
87 /* Current timer status */
97 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
100 /***********************************************************************
103 void LISTBOX_Dump( WND *wnd )
107 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
109 TRACE_(listbox)( "Listbox:\n" );
110 TRACE_(listbox)( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
111 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
113 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
115 TRACE_(listbox)( "%4d: %-40s %d %08lx %3d\n",
116 i, item->str, item->selected, item->data, item->height );
121 /***********************************************************************
122 * LISTBOX_GetCurrentPageSize
124 * Return the current page size
126 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
129 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
130 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
132 if ((height += descr->items[i].height) > descr->height) break;
134 if (i == descr->top_item) return 1;
135 else return i - descr->top_item;
139 /***********************************************************************
140 * LISTBOX_GetMaxTopIndex
142 * Return the maximum possible index for the top of the listbox.
144 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
148 if (descr->style & LBS_OWNERDRAWVARIABLE)
150 page = descr->height;
151 for (max = descr->nb_items - 1; max >= 0; max--)
152 if ((page -= descr->items[max].height) < 0) break;
153 if (max < descr->nb_items - 1) max++;
155 else if (descr->style & LBS_MULTICOLUMN)
157 if ((page = descr->width / descr->column_width) < 1) page = 1;
158 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
159 max = (max - page) * descr->page_size;
163 max = descr->nb_items - descr->page_size;
165 if (max < 0) max = 0;
170 /***********************************************************************
171 * LISTBOX_UpdateScroll
173 * Update the scrollbars. Should be called whenever the content
174 * of the listbox changes.
176 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
180 if (!(descr->style & WS_VSCROLL)) return;
181 /* It is important that we check descr->style, and not wnd->dwStyle,
182 for WS_VSCROLL, as the former is exactly the one passed in
183 argument to CreateWindow.
184 In Windows (and from now on in Wine :) a listbox created
185 with such a style (no WS_SCROLL) does not update
186 the scrollbar with listbox-related data, thus letting
187 the programmer use it for his/her own purposes. */
189 if (descr->style & LBS_NOREDRAW) return;
190 info.cbSize = sizeof(info);
192 if (descr->style & LBS_MULTICOLUMN)
195 info.nMax = (descr->nb_items - 1) / descr->page_size;
196 info.nPos = descr->top_item / descr->page_size;
197 info.nPage = descr->width / descr->column_width;
198 if (info.nPage < 1) info.nPage = 1;
199 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
200 if (descr->style & LBS_DISABLENOSCROLL)
201 info.fMask |= SIF_DISABLENOSCROLL;
202 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
204 info.fMask = SIF_RANGE;
205 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
210 info.nMax = descr->nb_items - 1;
211 info.nPos = descr->top_item;
212 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
213 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
214 if (descr->style & LBS_DISABLENOSCROLL)
215 info.fMask |= SIF_DISABLENOSCROLL;
216 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
218 if (descr->horz_extent)
221 info.nMax = descr->horz_extent - 1;
222 info.nPos = descr->horz_pos;
223 info.nPage = descr->width;
224 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
225 if (descr->style & LBS_DISABLENOSCROLL)
226 info.fMask |= SIF_DISABLENOSCROLL;
227 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
233 /***********************************************************************
236 * Set the top item of the listbox, scrolling up or down if necessary.
238 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
241 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
242 if (index > max) index = max;
243 if (index < 0) index = 0;
244 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
245 if (descr->top_item == index) return LB_OKAY;
246 if (descr->style & LBS_MULTICOLUMN)
248 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
249 if (scroll && (abs(diff) < descr->width))
250 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
251 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
259 if (descr->style & LBS_OWNERDRAWVARIABLE)
263 if (index > descr->top_item)
265 for (i = index - 1; i >= descr->top_item; i--)
266 diff -= descr->items[i].height;
270 for (i = index; i < descr->top_item; i++)
271 diff += descr->items[i].height;
275 diff = (descr->top_item - index) * descr->item_height;
277 if (abs(diff) < descr->height)
278 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
279 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
283 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
284 descr->top_item = index;
285 LISTBOX_UpdateScroll( wnd, descr );
290 /***********************************************************************
293 * Update the page size. Should be called when the size of
294 * the client area or the item height changes.
296 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
300 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
301 if (page_size == descr->page_size) return;
302 descr->page_size = page_size;
303 if (descr->style & LBS_MULTICOLUMN)
304 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
305 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
309 /***********************************************************************
312 * Update the size of the listbox. Should be called when the size of
313 * the client area changes.
315 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
319 GetClientRect( wnd->hwndSelf, &rect );
320 descr->width = rect.right - rect.left;
321 descr->height = rect.bottom - rect.top;
322 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
324 if ((descr->height > descr->item_height) &&
325 (descr->height % descr->item_height))
327 TRACE_(listbox)("[%04x]: changing height %d -> %d\n",
328 wnd->hwndSelf, descr->height,
329 descr->height - descr->height%descr->item_height );
330 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
331 wnd->rectWindow.right - wnd->rectWindow.left,
332 wnd->rectWindow.bottom - wnd->rectWindow.top -
333 (descr->height % descr->item_height),
334 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
338 TRACE_(listbox)("[%04x]: new size = %d,%d\n",
339 wnd->hwndSelf, descr->width, descr->height );
340 LISTBOX_UpdatePage( wnd, descr );
341 LISTBOX_UpdateScroll( wnd, descr );
345 /***********************************************************************
346 * LISTBOX_GetItemRect
348 * Get the rectangle enclosing an item, in listbox client coordinates.
349 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
351 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
354 /* Index <= 0 is legal even on empty listboxes */
355 if (index && (index >= descr->nb_items)) return -1;
356 SetRect( rect, 0, 0, descr->width, descr->height );
357 if (descr->style & LBS_MULTICOLUMN)
359 INT col = (index / descr->page_size) -
360 (descr->top_item / descr->page_size);
361 rect->left += col * descr->column_width;
362 rect->right = rect->left + descr->column_width;
363 rect->top += (index % descr->page_size) * descr->item_height;
364 rect->bottom = rect->top + descr->item_height;
366 else if (descr->style & LBS_OWNERDRAWVARIABLE)
369 rect->right += descr->horz_pos;
370 if ((index >= 0) && (index < descr->nb_items))
372 if (index < descr->top_item)
374 for (i = descr->top_item-1; i >= index; i--)
375 rect->top -= descr->items[i].height;
379 for (i = descr->top_item; i < index; i++)
380 rect->top += descr->items[i].height;
382 rect->bottom = rect->top + descr->items[index].height;
388 rect->top += (index - descr->top_item) * descr->item_height;
389 rect->bottom = rect->top + descr->item_height;
390 rect->right += descr->horz_pos;
393 return ((rect->left < descr->width) && (rect->right > 0) &&
394 (rect->top < descr->height) && (rect->bottom > 0));
398 /***********************************************************************
399 * LISTBOX_GetItemFromPoint
401 * Return the item nearest from point (x,y) (in client coordinates).
403 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
406 INT index = descr->top_item;
408 if (!descr->nb_items) return -1; /* No items */
409 if (descr->style & LBS_OWNERDRAWVARIABLE)
414 while (index < descr->nb_items)
416 if ((pos += descr->items[index].height) > y) break;
425 if ((pos -= descr->items[index].height) <= y) break;
429 else if (descr->style & LBS_MULTICOLUMN)
431 if (y >= descr->item_height * descr->page_size) return -1;
432 if (y >= 0) index += y / descr->item_height;
433 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
434 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
438 index += (y / descr->item_height);
440 if (index < 0) return 0;
441 if (index >= descr->nb_items) return -1;
446 /***********************************************************************
451 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
452 const RECT *rect, INT index, UINT action )
454 LB_ITEMDATA *item = NULL;
455 if (index < descr->nb_items) item = &descr->items[index];
457 if (IS_OWNERDRAW(descr))
460 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
464 if (action == ODA_FOCUS)
465 DrawFocusRect( hdc, rect );
467 FIXME_(listbox)("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
470 dis.CtlType = ODT_LISTBOX;
472 dis.hwndItem = wnd->hwndSelf;
473 dis.itemAction = action;
477 if (item && item->selected) dis.itemState |= ODS_SELECTED;
478 if ((descr->focus_item == index) &&
480 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
481 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
482 dis.itemData = item ? item->data : 0;
484 TRACE_(listbox)("[%04x]: drawitem %d (%s) action=%02x "
485 "state=%02x rect=%d,%d-%d,%d\n",
486 wnd->hwndSelf, index, item ? item->str : "", action,
487 dis.itemState, rect->left, rect->top,
488 rect->right, rect->bottom );
489 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
493 COLORREF oldText = 0, oldBk = 0;
495 if (action == ODA_FOCUS)
497 DrawFocusRect( hdc, rect );
500 if (item && item->selected)
502 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
503 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
506 TRACE_(listbox)("[%04x]: painting %d (%s) action=%02x "
507 "rect=%d,%d-%d,%d\n",
508 wnd->hwndSelf, index, item ? item->str : "", action,
509 rect->left, rect->top, rect->right, rect->bottom );
511 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
512 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
513 else if (!(descr->style & LBS_USETABSTOPS))
514 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
515 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
516 strlen(item->str), NULL );
519 /* Output empty string to paint background in the full width. */
520 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
521 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
522 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
523 item->str, strlen(item->str),
524 descr->nb_tabs, descr->tabs, 0);
526 if (item && item->selected)
528 SetBkColor( hdc, oldBk );
529 SetTextColor( hdc, oldText );
531 if ((descr->focus_item == index) &&
533 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
538 /***********************************************************************
541 * Change the redraw flag.
543 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
547 if (!(descr->style & LBS_NOREDRAW)) return;
548 descr->style &= ~LBS_NOREDRAW;
549 LISTBOX_UpdateScroll( wnd, descr );
551 else descr->style |= LBS_NOREDRAW;
555 /***********************************************************************
556 * LISTBOX_RepaintItem
558 * Repaint a single item synchronously.
560 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
566 HBRUSH hbrush, oldBrush = 0;
568 if (descr->style & LBS_NOREDRAW) return;
569 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
570 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
571 if (descr->font) oldFont = SelectObject( hdc, descr->font );
572 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
573 hdc, (LPARAM)wnd->hwndSelf );
574 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
575 if (wnd->dwStyle & WS_DISABLED)
576 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
577 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
578 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
579 if (oldFont) SelectObject( hdc, oldFont );
580 if (oldBrush) SelectObject( hdc, oldBrush );
581 ReleaseDC( wnd->hwndSelf, hdc );
585 /***********************************************************************
586 * LISTBOX_InitStorage
588 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
593 nb_items += LB_ARRAY_GRANULARITY - 1;
594 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
596 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
597 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
598 nb_items * sizeof(LB_ITEMDATA) )))
600 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
608 /***********************************************************************
609 * LISTBOX_SetTabStops
611 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
612 LPINT tabs, BOOL short_ints )
614 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
615 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
616 if (!(descr->nb_tabs = count))
621 /* FIXME: count = 1 */
622 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
623 descr->nb_tabs * sizeof(INT) )))
628 LPINT16 p = (LPINT16)tabs;
629 dbg_decl_str(listbox, 256);
631 for (i = 0; i < descr->nb_tabs; i++) {
632 descr->tabs[i] = *p++<<1; /* FIXME */
633 if(TRACE_ON(listbox))
634 dsprintf(listbox, "%hd ", descr->tabs[i]);
636 TRACE_(listbox)("[%04x]: settabstops %s\n",
637 wnd->hwndSelf, dbg_str(listbox));
639 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
640 /* FIXME: repaint the window? */
645 /***********************************************************************
648 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
651 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
652 if (HAS_STRINGS(descr))
655 return strlen(descr->items[index].str);
656 lstrcpyA( buffer, descr->items[index].str );
657 return strlen(buffer);
660 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
661 return sizeof(DWORD);
666 /***********************************************************************
667 * LISTBOX_FindStringPos
669 * Find the nearest string located before a given string in sort order.
670 * If 'exact' is TRUE, return an error if we don't get an exact match.
672 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
675 INT index, min, max, res = -1;
677 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
679 max = descr->nb_items;
682 index = (min + max) / 2;
683 if (HAS_STRINGS(descr))
684 res = lstrcmpiA( descr->items[index].str, str );
687 COMPAREITEMSTRUCT cis;
688 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
690 cis.CtlType = ODT_LISTBOX;
692 cis.hwndItem = wnd->hwndSelf;
694 cis.itemData1 = descr->items[index].data;
696 cis.itemData2 = (DWORD)str;
697 cis.dwLocaleId = descr->locale;
698 res = SendMessageA( descr->owner, WM_COMPAREITEM,
701 if (!res) return index;
702 if (res > 0) max = index;
703 else min = index + 1;
705 return exact ? -1 : max;
709 /***********************************************************************
710 * LISTBOX_FindFileStrPos
712 * Find the nearest string located before a given string in directory
713 * sort order (i.e. first files, then directories, then drives).
715 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
717 INT min, max, res = -1;
719 if (!HAS_STRINGS(descr))
720 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
722 max = descr->nb_items;
725 INT index = (min + max) / 2;
726 const char *p = descr->items[index].str;
727 if (*p == '[') /* drive or directory */
729 if (*str != '[') res = -1;
730 else if (p[1] == '-') /* drive */
732 if (str[1] == '-') res = str[2] - p[2];
737 if (str[1] == '-') res = 1;
738 else res = lstrcmpiA( str, p );
743 if (*str == '[') res = 1;
744 else res = lstrcmpiA( str, p );
746 if (!res) return index;
747 if (res < 0) max = index;
748 else min = index + 1;
754 /***********************************************************************
757 * Find the item beginning with a given string.
759 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
760 LPCSTR str, BOOL exact )
765 if (start >= descr->nb_items) start = -1;
766 item = descr->items + start + 1;
767 if (HAS_STRINGS(descr))
769 if (!str) return LB_ERR;
772 for (i = start + 1; i < descr->nb_items; i++, item++)
773 if (!lstrcmpiA( str, item->str )) return i;
774 for (i = 0, item = descr->items; i <= start; i++, item++)
775 if (!lstrcmpiA( str, item->str )) return i;
779 /* Special case for drives and directories: ignore prefix */
780 #define CHECK_DRIVE(item) \
781 if ((item)->str[0] == '[') \
783 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
784 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
788 INT len = strlen(str);
789 for (i = start + 1; i < descr->nb_items; i++, item++)
791 if (!lstrncmpiA( str, item->str, len )) return i;
794 for (i = 0, item = descr->items; i <= start; i++, item++)
796 if (!lstrncmpiA( str, item->str, len )) return i;
804 if (exact && (descr->style & LBS_SORT))
805 /* If sorted, use a WM_COMPAREITEM binary search */
806 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
808 /* Otherwise use a linear search */
809 for (i = start + 1; i < descr->nb_items; i++, item++)
810 if (item->data == (DWORD)str) return i;
811 for (i = 0, item = descr->items; i <= start; i++, item++)
812 if (item->data == (DWORD)str) return i;
818 /***********************************************************************
819 * LISTBOX_GetSelCount
821 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
824 LB_ITEMDATA *item = descr->items;
826 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
827 for (i = count = 0; i < descr->nb_items; i++, item++)
828 if (item->selected) count++;
833 /***********************************************************************
834 * LISTBOX_GetSelItems16
836 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
840 LB_ITEMDATA *item = descr->items;
842 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
843 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
844 if (item->selected) array[count++] = (INT16)i;
849 /***********************************************************************
850 * LISTBOX_GetSelItems32
852 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
856 LB_ITEMDATA *item = descr->items;
858 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
859 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
860 if (item->selected) array[count++] = i;
865 /***********************************************************************
868 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
870 INT i, col_pos = descr->page_size - 1;
873 HBRUSH hbrush, oldBrush = 0;
875 SetRect( &rect, 0, 0, descr->width, descr->height );
876 if (descr->style & LBS_NOREDRAW) return 0;
877 if (descr->style & LBS_MULTICOLUMN)
878 rect.right = rect.left + descr->column_width;
879 else if (descr->horz_pos)
881 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
882 rect.right += descr->horz_pos;
885 if (descr->font) oldFont = SelectObject( hdc, descr->font );
886 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
887 hdc, (LPARAM)wnd->hwndSelf );
888 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
889 if (wnd->dwStyle & WS_DISABLED)
890 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
892 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
893 (GetFocus() == wnd->hwndSelf))
895 /* Special case for empty listbox: paint focus rect */
896 rect.bottom = rect.top + descr->item_height;
897 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
899 rect.top = rect.bottom;
902 for (i = descr->top_item; i < descr->nb_items; i++)
904 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
905 rect.bottom = rect.top + descr->item_height;
907 rect.bottom = rect.top + descr->items[i].height;
909 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
910 rect.top = rect.bottom;
912 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
914 if (!IS_OWNERDRAW(descr))
916 /* Clear the bottom of the column */
917 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
918 if (rect.top < descr->height)
920 rect.bottom = descr->height;
921 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
922 &rect, NULL, 0, NULL );
926 /* Go to the next column */
927 rect.left += descr->column_width;
928 rect.right += descr->column_width;
930 col_pos = descr->page_size - 1;
935 if (rect.top >= descr->height) break;
939 if (!IS_OWNERDRAW(descr))
941 /* Clear the remainder of the client area */
942 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
943 if (rect.top < descr->height)
945 rect.bottom = descr->height;
946 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
947 &rect, NULL, 0, NULL );
949 if (rect.right < descr->width)
951 rect.left = rect.right;
952 rect.right = descr->width;
954 rect.bottom = descr->height;
955 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
956 &rect, NULL, 0, NULL );
959 if (oldFont) SelectObject( hdc, oldFont );
960 if (oldBrush) SelectObject( hdc, oldBrush );
965 /***********************************************************************
966 * LISTBOX_InvalidateItems
968 * Invalidate all items from a given item. If the specified item is not
969 * visible, nothing happens.
971 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
975 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
977 rect.bottom = descr->height;
978 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
979 if (descr->style & LBS_MULTICOLUMN)
981 /* Repaint the other columns */
982 rect.left = rect.right;
983 rect.right = descr->width;
985 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
991 /***********************************************************************
992 * LISTBOX_GetItemHeight
994 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
996 if (descr->style & LBS_OWNERDRAWVARIABLE)
998 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
999 return descr->items[index].height;
1001 else return descr->item_height;
1005 /***********************************************************************
1006 * LISTBOX_SetItemHeight
1008 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1011 if (!height) height = 1;
1013 if (descr->style & LBS_OWNERDRAWVARIABLE)
1015 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1016 TRACE_(listbox)("[%04x]: item %d height = %d\n",
1017 wnd->hwndSelf, index, height );
1018 descr->items[index].height = height;
1019 LISTBOX_UpdateScroll( wnd, descr );
1020 LISTBOX_InvalidateItems( wnd, descr, index );
1022 else if (height != descr->item_height)
1024 TRACE_(listbox)("[%04x]: new height = %d\n",
1025 wnd->hwndSelf, height );
1026 descr->item_height = height;
1027 LISTBOX_UpdatePage( wnd, descr );
1028 LISTBOX_UpdateScroll( wnd, descr );
1029 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1035 /***********************************************************************
1036 * LISTBOX_SetHorizontalPos
1038 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1042 if (pos > descr->horz_extent - descr->width)
1043 pos = descr->horz_extent - descr->width;
1044 if (pos < 0) pos = 0;
1045 if (!(diff = descr->horz_pos - pos)) return;
1046 TRACE_(listbox)("[%04x]: new horz pos = %d\n",
1047 wnd->hwndSelf, pos );
1048 descr->horz_pos = pos;
1049 LISTBOX_UpdateScroll( wnd, descr );
1050 if (abs(diff) < descr->width)
1051 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1052 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1054 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1058 /***********************************************************************
1059 * LISTBOX_SetHorizontalExtent
1061 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1064 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1066 if (extent <= 0) extent = 1;
1067 if (extent == descr->horz_extent) return LB_OKAY;
1068 TRACE_(listbox)("[%04x]: new horz extent = %d\n",
1069 wnd->hwndSelf, extent );
1070 descr->horz_extent = extent;
1071 if (descr->horz_pos > extent - descr->width)
1072 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1074 LISTBOX_UpdateScroll( wnd, descr );
1079 /***********************************************************************
1080 * LISTBOX_SetColumnWidth
1082 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1084 width += 2; /* For left and right margin */
1085 if (width == descr->column_width) return LB_OKAY;
1086 TRACE_(listbox)("[%04x]: new column width = %d\n",
1087 wnd->hwndSelf, width );
1088 descr->column_width = width;
1089 LISTBOX_UpdatePage( wnd, descr );
1094 /***********************************************************************
1097 * Returns the item height.
1099 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1107 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1109 ERR_(listbox)("unable to get DC.\n" );
1112 if (font) oldFont = SelectObject( hdc, font );
1113 GetTextMetricsA( hdc, &tm );
1114 if (oldFont) SelectObject( hdc, oldFont );
1115 ReleaseDC( wnd->hwndSelf, hdc );
1116 if (!IS_OWNERDRAW(descr))
1117 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1118 return tm.tmHeight ;
1122 /***********************************************************************
1123 * LISTBOX_MakeItemVisible
1125 * Make sure that a given item is partially or fully visible.
1127 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1132 if (index <= descr->top_item) top = index;
1133 else if (descr->style & LBS_MULTICOLUMN)
1135 INT cols = descr->width;
1136 if (!fully) cols += descr->column_width - 1;
1137 if (cols >= descr->column_width) cols /= descr->column_width;
1139 if (index < descr->top_item + (descr->page_size * cols)) return;
1140 top = index - descr->page_size * (cols - 1);
1142 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1144 INT height = fully ? descr->items[index].height : 1;
1145 for (top = index; top > descr->top_item; top--)
1146 if ((height += descr->items[top-1].height) > descr->height) break;
1150 if (index < descr->top_item + descr->page_size) return;
1151 if (!fully && (index == descr->top_item + descr->page_size) &&
1152 (descr->height > (descr->page_size * descr->item_height))) return;
1153 top = index - descr->page_size + 1;
1155 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1159 /***********************************************************************
1160 * LISTBOX_SelectItemRange
1162 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1164 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1169 /* A few sanity checks */
1171 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1172 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1173 if (last == -1) last = descr->nb_items - 1;
1174 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1175 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1176 /* selected_item reflects last selected/unselected item on multiple sel */
1177 descr->selected_item = last;
1179 if (on) /* Turn selection on */
1181 for (i = first; i <= last; i++)
1183 if (descr->items[i].selected) continue;
1184 descr->items[i].selected = TRUE;
1185 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1188 else /* Turn selection off */
1190 for (i = first; i <= last; i++)
1192 if (!descr->items[i].selected) continue;
1193 descr->items[i].selected = FALSE;
1194 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1201 /***********************************************************************
1202 * LISTBOX_SetCaretIndex
1205 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1208 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1209 BOOL fully_visible )
1211 INT oldfocus = descr->focus_item;
1213 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1214 if (index == oldfocus) return LB_OKAY;
1215 descr->focus_item = index;
1216 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1217 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1219 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1220 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1221 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1227 /***********************************************************************
1228 * LISTBOX_SetSelection
1230 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1231 BOOL on, BOOL send_notify )
1233 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1234 if (descr->style & LBS_MULTIPLESEL)
1236 if (index == -1) /* Select all items */
1237 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1238 else /* Only one item */
1239 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1243 INT oldsel = descr->selected_item;
1244 if (index == oldsel) return LB_OKAY;
1245 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1246 if (index != -1) descr->items[index].selected = TRUE;
1247 descr->selected_item = index;
1248 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1249 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1250 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1251 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1253 if( descr->lphc ) /* set selection change flag for parent combo */
1254 descr->lphc->wState |= CBF_SELCHANGE;
1260 /***********************************************************************
1263 * Change the caret position and extend the selection to the new caret.
1265 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1266 BOOL fully_visible )
1268 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1269 if (descr->style & LBS_EXTENDEDSEL)
1271 if (descr->anchor_item != -1)
1273 INT first = MIN( descr->focus_item, descr->anchor_item );
1274 INT last = MAX( descr->focus_item, descr->anchor_item );
1276 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1277 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1278 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1281 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1283 /* Set selection to new caret item */
1284 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1289 /***********************************************************************
1290 * LISTBOX_InsertItem
1292 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1293 LPSTR str, DWORD data )
1298 if (index == -1) index = descr->nb_items;
1299 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1300 if (!descr->items) max_items = 0;
1301 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1302 if (descr->nb_items == max_items)
1304 /* We need to grow the array */
1305 max_items += LB_ARRAY_GRANULARITY;
1306 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1307 max_items * sizeof(LB_ITEMDATA) )))
1309 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1312 descr->items = item;
1315 /* Insert the item structure */
1317 item = &descr->items[index];
1318 if (index < descr->nb_items)
1319 RtlMoveMemory( item + 1, item,
1320 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1324 item->selected = FALSE;
1327 /* Get item height */
1329 if (descr->style & LBS_OWNERDRAWVARIABLE)
1331 MEASUREITEMSTRUCT mis;
1332 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1334 mis.CtlType = ODT_LISTBOX;
1337 mis.itemData = descr->items[index].data;
1338 mis.itemHeight = descr->item_height;
1339 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1340 item->height = mis.itemHeight ? mis.itemHeight : 1;
1341 TRACE_(listbox)("[%04x]: measure item %d (%s) = %d\n",
1342 wnd->hwndSelf, index, str ? str : "", item->height );
1345 /* Repaint the items */
1347 LISTBOX_UpdateScroll( wnd, descr );
1348 LISTBOX_InvalidateItems( wnd, descr, index );
1350 /* Move selection and focused item */
1352 if (index <= descr->selected_item) descr->selected_item++;
1353 if (index <= descr->focus_item)
1355 descr->focus_item++;
1356 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1359 /* If listbox was empty, set focus to the first item */
1361 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1366 /***********************************************************************
1367 * LISTBOX_InsertString
1369 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1372 LPSTR new_str = NULL;
1376 if (HAS_STRINGS(descr))
1378 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1380 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1384 else data = (DWORD)str;
1386 if (index == -1) index = descr->nb_items;
1387 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1389 if (new_str) HeapFree( descr->heap, 0, new_str );
1393 TRACE_(listbox)("[%04x]: added item %d '%s'\n",
1394 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1399 /***********************************************************************
1400 * LISTBOX_DeleteItem
1402 * Delete the content of an item. 'index' must be a valid index.
1404 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1406 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1407 * while Win95 sends it for all items with user data.
1408 * It's probably better to send it too often than not
1409 * often enough, so this is what we do here.
1411 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1413 DELETEITEMSTRUCT dis;
1414 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1416 dis.CtlType = ODT_LISTBOX;
1419 dis.hwndItem = wnd->hwndSelf;
1420 dis.itemData = descr->items[index].data;
1421 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1423 if (HAS_STRINGS(descr) && descr->items[index].str)
1424 HeapFree( descr->heap, 0, descr->items[index].str );
1428 /***********************************************************************
1429 * LISTBOX_RemoveItem
1431 * Remove an item from the listbox and delete its content.
1433 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1438 if (index == -1) index = descr->nb_items - 1;
1439 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1440 LISTBOX_DeleteItem( wnd, descr, index );
1442 /* Remove the item */
1444 item = &descr->items[index];
1445 if (index < descr->nb_items-1)
1446 RtlMoveMemory( item, item + 1,
1447 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1449 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1451 /* Shrink the item array if possible */
1453 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1454 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1456 max_items -= LB_ARRAY_GRANULARITY;
1457 item = HeapReAlloc( descr->heap, 0, descr->items,
1458 max_items * sizeof(LB_ITEMDATA) );
1459 if (item) descr->items = item;
1462 /* Repaint the items */
1464 LISTBOX_UpdateScroll( wnd, descr );
1465 LISTBOX_InvalidateItems( wnd, descr, index );
1467 /* Move selection and focused item */
1469 if (index <= descr->selected_item) descr->selected_item--;
1470 if (index <= descr->focus_item)
1472 descr->focus_item--;
1473 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1479 /***********************************************************************
1480 * LISTBOX_ResetContent
1482 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1486 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1487 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1488 descr->nb_items = 0;
1489 descr->top_item = 0;
1490 descr->selected_item = -1;
1491 descr->focus_item = 0;
1492 descr->anchor_item = -1;
1493 descr->items = NULL;
1494 LISTBOX_UpdateScroll( wnd, descr );
1495 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1499 /***********************************************************************
1502 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1506 if (HAS_STRINGS(descr)) return LB_ERR;
1507 /* FIXME: this is far from optimal... */
1508 if (count > descr->nb_items)
1510 while (count > descr->nb_items)
1511 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1514 else if (count < descr->nb_items)
1516 while (count < descr->nb_items)
1517 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1524 /***********************************************************************
1527 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1528 LPCSTR filespec, BOOL long_names )
1531 LRESULT ret = LB_OKAY;
1532 WIN32_FIND_DATAA entry;
1535 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1537 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1544 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1546 if (!(attrib & DDL_DIRECTORY) ||
1547 !strcmp( entry.cAlternateFileName, "." )) continue;
1548 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1549 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1551 else /* not a directory */
1553 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1554 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1556 if ((attrib & DDL_EXCLUSIVE) &&
1557 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1560 if (long_names) strcpy( buffer, entry.cFileName );
1561 else strcpy( buffer, entry.cAlternateFileName );
1563 if (!long_names) CharLowerA( buffer );
1564 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1565 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1567 } while (FindNextFileA( handle, &entry ));
1568 FindClose( handle );
1571 if ((ret >= 0) && (attrib & DDL_DRIVES))
1573 char buffer[] = "[-a-]";
1575 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1577 if (!DRIVE_IsValid(drive)) continue;
1578 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1586 /***********************************************************************
1587 * LISTBOX_HandleVScroll
1589 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1590 WPARAM wParam, LPARAM lParam )
1594 if (descr->style & LBS_MULTICOLUMN) return 0;
1595 switch(LOWORD(wParam))
1598 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1601 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1604 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1605 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1608 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1609 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1611 case SB_THUMBPOSITION:
1612 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1615 info.cbSize = sizeof(info);
1616 info.fMask = SIF_TRACKPOS;
1617 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1618 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1621 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1624 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1631 /***********************************************************************
1632 * LISTBOX_HandleHScroll
1634 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1635 WPARAM wParam, LPARAM lParam )
1640 if (descr->style & LBS_MULTICOLUMN)
1642 switch(LOWORD(wParam))
1645 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1649 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1653 page = descr->width / descr->column_width;
1654 if (page < 1) page = 1;
1655 LISTBOX_SetTopItem( wnd, descr,
1656 descr->top_item - page * descr->page_size, TRUE );
1659 page = descr->width / descr->column_width;
1660 if (page < 1) page = 1;
1661 LISTBOX_SetTopItem( wnd, descr,
1662 descr->top_item + page * descr->page_size, TRUE );
1664 case SB_THUMBPOSITION:
1665 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1669 info.cbSize = sizeof(info);
1670 info.fMask = SIF_TRACKPOS;
1671 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1672 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1676 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1679 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1683 else if (descr->horz_extent)
1685 switch(LOWORD(wParam))
1688 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1691 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1694 LISTBOX_SetHorizontalPos( wnd, descr,
1695 descr->horz_pos - descr->width );
1698 LISTBOX_SetHorizontalPos( wnd, descr,
1699 descr->horz_pos + descr->width );
1701 case SB_THUMBPOSITION:
1702 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1705 info.cbSize = sizeof(info);
1706 info.fMask = SIF_TRACKPOS;
1707 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1708 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1711 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1714 LISTBOX_SetHorizontalPos( wnd, descr,
1715 descr->horz_extent - descr->width );
1723 /***********************************************************************
1724 * LISTBOX_HandleLButtonDown
1726 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1727 WPARAM wParam, INT x, INT y )
1729 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1730 TRACE_(listbox)("[%04x]: lbuttondown %d,%d item %d\n",
1731 wnd->hwndSelf, x, y, index );
1732 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1735 if (descr->style & LBS_EXTENDEDSEL)
1737 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1738 if (wParam & MK_CONTROL)
1740 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1741 LISTBOX_SetSelection( wnd, descr, index,
1742 !descr->items[index].selected, FALSE );
1744 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1748 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1749 LISTBOX_SetSelection( wnd, descr, index,
1750 (!(descr->style & LBS_MULTIPLESEL) ||
1751 !descr->items[index].selected), FALSE );
1755 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1756 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1757 : descr->lphc->self->hwndSelf ) ;
1759 descr->captured = TRUE;
1760 SetCapture( wnd->hwndSelf );
1761 if (index != -1 && !descr->lphc)
1763 if (descr->style & LBS_NOTIFY )
1764 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1765 MAKELPARAM( x, y ) );
1766 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1773 if (DragDetect( wnd->hwndSelf, pt ))
1774 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1781 /***********************************************************************
1782 * LISTBOX_HandleLButtonUp
1784 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1786 if (LISTBOX_Timer != LB_TIMER_NONE)
1787 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1788 LISTBOX_Timer = LB_TIMER_NONE;
1789 if (descr->captured)
1791 descr->captured = FALSE;
1792 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1793 if (descr->style & LBS_NOTIFY)
1794 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1800 /***********************************************************************
1801 * LISTBOX_HandleTimer
1803 * Handle scrolling upon a timer event.
1804 * Return TRUE if scrolling should continue.
1806 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1807 INT index, TIMER_DIRECTION dir )
1812 if (descr->top_item) index = descr->top_item - 1;
1816 if (descr->top_item) index -= descr->page_size;
1819 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1820 if (index == descr->focus_item) index++;
1821 if (index >= descr->nb_items) index = descr->nb_items - 1;
1823 case LB_TIMER_RIGHT:
1824 if (index + descr->page_size < descr->nb_items)
1825 index += descr->page_size;
1830 if (index == descr->focus_item) return FALSE;
1831 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1836 /***********************************************************************
1837 * LISTBOX_HandleSystemTimer
1839 * WM_SYSTIMER handler.
1841 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1843 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1845 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1846 LISTBOX_Timer = LB_TIMER_NONE;
1852 /***********************************************************************
1853 * LISTBOX_HandleMouseMove
1855 * WM_MOUSEMOVE handler.
1857 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1861 TIMER_DIRECTION dir;
1863 if (!descr->captured) return;
1865 if (descr->style & LBS_MULTICOLUMN)
1868 else if (y >= descr->item_height * descr->page_size)
1869 y = descr->item_height * descr->page_size - 1;
1873 dir = LB_TIMER_LEFT;
1876 else if (x >= descr->width)
1878 dir = LB_TIMER_RIGHT;
1879 x = descr->width - 1;
1881 else dir = LB_TIMER_NONE; /* inside */
1885 if (y < 0) dir = LB_TIMER_UP; /* above */
1886 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1887 else dir = LB_TIMER_NONE; /* inside */
1890 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1891 if (index == -1) index = descr->focus_item;
1892 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1894 /* Start/stop the system timer */
1896 if (dir != LB_TIMER_NONE)
1897 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1898 else if (LISTBOX_Timer != LB_TIMER_NONE)
1899 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1900 LISTBOX_Timer = dir;
1904 /***********************************************************************
1905 * LISTBOX_HandleKeyDown
1907 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1910 if (descr->style & LBS_WANTKEYBOARDINPUT)
1912 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1913 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1915 if (caret == -2) return 0;
1917 if (caret == -1) switch(wParam)
1920 if (descr->style & LBS_MULTICOLUMN)
1922 if (descr->focus_item >= descr->page_size)
1923 caret = descr->focus_item - descr->page_size;
1928 caret = descr->focus_item - 1;
1929 if (caret < 0) caret = 0;
1932 if (descr->style & LBS_MULTICOLUMN)
1934 if (descr->focus_item + descr->page_size < descr->nb_items)
1935 caret = descr->focus_item + descr->page_size;
1940 caret = descr->focus_item + 1;
1941 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1944 if (descr->style & LBS_MULTICOLUMN)
1946 INT page = descr->width / descr->column_width;
1947 if (page < 1) page = 1;
1948 caret = descr->focus_item - (page * descr->page_size) + 1;
1950 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1951 if (caret < 0) caret = 0;
1954 if (descr->style & LBS_MULTICOLUMN)
1956 INT page = descr->width / descr->column_width;
1957 if (page < 1) page = 1;
1958 caret = descr->focus_item + (page * descr->page_size) - 1;
1960 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1961 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1967 caret = descr->nb_items - 1;
1970 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1971 else if (descr->style & LBS_MULTIPLESEL)
1973 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1974 !descr->items[descr->focus_item].selected,
1975 (descr->style & LBS_NOTIFY) != 0 );
1977 else if (descr->selected_item == -1)
1979 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1980 (descr->style & LBS_NOTIFY) != 0 );
1986 if ((descr->style & LBS_EXTENDEDSEL) &&
1987 !(GetKeyState( VK_SHIFT ) & 0x8000))
1988 descr->anchor_item = caret;
1989 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1990 if (descr->style & LBS_NOTIFY)
1992 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1994 /* make sure that combo parent doesn't hide us */
1995 descr->lphc->wState |= CBF_NOROLLUP;
1997 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2004 /***********************************************************************
2005 * LISTBOX_HandleChar
2007 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2013 str[0] = wParam & 0xff;
2016 if (descr->style & LBS_WANTKEYBOARDINPUT)
2018 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2019 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2021 if (caret == -2) return 0;
2024 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2027 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2028 if (descr->style & LBS_NOTIFY)
2029 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2035 /***********************************************************************
2038 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2041 MEASUREITEMSTRUCT mis;
2044 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2046 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2048 HeapFree( GetProcessHeap(), 0, descr );
2051 GetClientRect( wnd->hwndSelf, &rect );
2052 descr->owner = GetParent( wnd->hwndSelf );
2053 descr->style = wnd->dwStyle;
2054 descr->width = rect.right - rect.left;
2055 descr->height = rect.bottom - rect.top;
2056 descr->items = NULL;
2057 descr->nb_items = 0;
2058 descr->top_item = 0;
2059 descr->selected_item = -1;
2060 descr->focus_item = 0;
2061 descr->anchor_item = -1;
2062 descr->item_height = 1;
2063 descr->page_size = 1;
2064 descr->column_width = 150;
2065 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2066 descr->horz_pos = 0;
2069 descr->caret_on = TRUE;
2070 descr->captured = FALSE;
2072 descr->locale = 0; /* FIXME */
2077 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2078 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2079 descr->owner = lphc->self->hwndSelf;
2082 *(LB_DESCR **)wnd->wExtra = descr;
2084 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2086 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2087 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2088 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2089 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2091 if (descr->style & LBS_OWNERDRAWFIXED)
2093 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2095 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2096 descr->item_height = lphc->fixedOwnerDrawHeight;
2100 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2102 mis.CtlType = ODT_LISTBOX;
2107 mis.itemHeight = descr->item_height;
2108 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2109 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2117 /***********************************************************************
2120 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2122 LISTBOX_ResetContent( wnd, descr );
2123 HeapDestroy( descr->heap );
2124 HeapFree( GetProcessHeap(), 0, descr );
2130 /***********************************************************************
2133 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2134 WPARAM wParam, LPARAM lParam )
2138 HWND hwnd = wnd->hwndSelf;
2141 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2143 if (msg == WM_CREATE)
2145 if (!LISTBOX_Create( wnd, NULL ))
2147 TRACE_(listbox)("creating wnd=%04x descr=%p\n",
2148 hwnd, *(LB_DESCR **)wnd->wExtra );
2151 /* Ignore all other messages before we get a WM_CREATE */
2152 return DefWindowProcA( hwnd, msg, wParam, lParam );
2155 TRACE_(listbox)("[%04x]: msg %s wp %08x lp %08lx\n",
2156 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2159 case LB_RESETCONTENT16:
2160 case LB_RESETCONTENT:
2161 LISTBOX_ResetContent( wnd, descr );
2164 case LB_ADDSTRING16:
2165 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2168 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2169 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2171 case LB_INSERTSTRING16:
2172 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2173 wParam = (INT)(INT16)wParam;
2175 case LB_INSERTSTRING:
2176 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2179 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2182 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2183 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2185 case LB_DELETESTRING16:
2186 case LB_DELETESTRING:
2187 return LISTBOX_RemoveItem( wnd, descr, wParam );
2189 case LB_GETITEMDATA16:
2190 case LB_GETITEMDATA:
2191 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2193 return descr->items[wParam].data;
2195 case LB_SETITEMDATA16:
2196 case LB_SETITEMDATA:
2197 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2199 descr->items[wParam].data = (DWORD)lParam;
2204 return descr->nb_items;
2207 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2210 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2212 case LB_GETTEXTLEN16:
2215 if (wParam >= descr->nb_items)
2217 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2220 case LB_GETCURSEL16:
2222 if (descr->nb_items==0)
2225 if (descr->selected_item!=-1)
2226 return descr->selected_item;
2228 return descr->focus_item;
2229 /* otherwise, if the user tries to move the selection with the */
2230 /* arrow keys, we will give the application something to choke on */
2231 case LB_GETTOPINDEX16:
2232 case LB_GETTOPINDEX:
2233 return descr->top_item;
2235 case LB_GETITEMHEIGHT16:
2236 case LB_GETITEMHEIGHT:
2237 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2239 case LB_SETITEMHEIGHT16:
2240 lParam = LOWORD(lParam);
2242 case LB_SETITEMHEIGHT:
2243 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2245 case LB_ITEMFROMPOINT:
2250 pt.x = LOWORD(lParam);
2251 pt.y = HIWORD(lParam);
2254 rect.right = descr->width;
2255 rect.bottom = descr->height;
2257 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2258 PtInRect( &rect, pt ) );
2261 case LB_SETCARETINDEX16:
2262 case LB_SETCARETINDEX:
2263 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2265 case LB_GETCARETINDEX16:
2266 case LB_GETCARETINDEX:
2267 return descr->focus_item;
2269 case LB_SETTOPINDEX16:
2270 case LB_SETTOPINDEX:
2271 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2273 case LB_SETCOLUMNWIDTH16:
2274 case LB_SETCOLUMNWIDTH:
2275 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2277 case LB_GETITEMRECT16:
2280 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2281 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2285 case LB_GETITEMRECT:
2286 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2288 case LB_FINDSTRING16:
2289 wParam = (INT)(INT16)wParam;
2290 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2293 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2295 case LB_FINDSTRINGEXACT16:
2296 wParam = (INT)(INT16)wParam;
2297 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2299 case LB_FINDSTRINGEXACT:
2300 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2302 case LB_SELECTSTRING16:
2303 wParam = (INT)(INT16)wParam;
2304 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2306 case LB_SELECTSTRING:
2308 INT index = LISTBOX_FindString( wnd, descr, wParam,
2309 (LPCSTR)lParam, FALSE );
2310 if (index == LB_ERR)
2312 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2317 wParam = (INT)(INT16)wParam;
2320 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2322 return descr->items[wParam].selected;
2325 lParam = (INT)(INT16)lParam;
2328 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2330 case LB_SETCURSEL16:
2331 wParam = (INT)(INT16)wParam;
2334 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2335 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2337 case LB_GETSELCOUNT16:
2338 case LB_GETSELCOUNT:
2339 return LISTBOX_GetSelCount( wnd, descr );
2341 case LB_GETSELITEMS16:
2342 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2343 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2345 case LB_GETSELITEMS:
2346 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2348 case LB_SELITEMRANGE16:
2349 case LB_SELITEMRANGE:
2350 if (LOWORD(lParam) <= HIWORD(lParam))
2351 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2352 HIWORD(lParam), wParam );
2354 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2355 LOWORD(lParam), wParam );
2357 case LB_SELITEMRANGEEX16:
2358 case LB_SELITEMRANGEEX:
2359 if ((INT)lParam >= (INT)wParam)
2360 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2362 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2364 case LB_GETHORIZONTALEXTENT16:
2365 case LB_GETHORIZONTALEXTENT:
2366 return descr->horz_extent;
2368 case LB_SETHORIZONTALEXTENT16:
2369 case LB_SETHORIZONTALEXTENT:
2370 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2372 case LB_GETANCHORINDEX16:
2373 case LB_GETANCHORINDEX:
2374 return descr->anchor_item;
2376 case LB_SETANCHORINDEX16:
2377 wParam = (INT)(INT16)wParam;
2379 case LB_SETANCHORINDEX:
2380 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2382 descr->anchor_item = (INT)wParam;
2386 return LISTBOX_Directory( wnd, descr, wParam,
2387 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2390 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2393 return descr->locale;
2396 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2399 case LB_INITSTORAGE:
2400 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2403 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2405 case LB_SETTABSTOPS16:
2406 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2407 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2409 case LB_SETTABSTOPS:
2410 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2414 if (descr->caret_on)
2416 descr->caret_on = TRUE;
2417 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2418 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2423 if (!descr->caret_on)
2425 descr->caret_on = FALSE;
2426 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2427 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2431 return LISTBOX_Destroy( wnd, descr );
2434 InvalidateRect( hwnd, NULL, TRUE );
2438 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2442 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2447 HDC hdc = ( wParam ) ? ((HDC)wParam)
2448 : BeginPaint( hwnd, &ps );
2449 ret = LISTBOX_Paint( wnd, descr, hdc );
2450 if( !wParam ) EndPaint( hwnd, &ps );
2454 LISTBOX_UpdateSize( wnd, descr );
2459 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2460 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2463 descr->caret_on = TRUE;
2464 if (descr->focus_item != -1)
2465 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2466 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2469 if ((descr->focus_item != -1) && descr->caret_on)
2470 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2471 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2474 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2476 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2477 case WM_LBUTTONDOWN:
2478 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2479 (INT16)LOWORD(lParam),
2480 (INT16)HIWORD(lParam) );
2481 case WM_LBUTTONDBLCLK:
2482 if (descr->style & LBS_NOTIFY)
2483 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2486 if (GetCapture() == hwnd)
2487 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2488 (INT16)HIWORD(lParam) );
2491 return LISTBOX_HandleLButtonUp( wnd, descr );
2493 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2495 return LISTBOX_HandleChar( wnd, descr, wParam );
2497 return LISTBOX_HandleSystemTimer( wnd, descr );
2499 if (IS_OWNERDRAW(descr))
2502 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2503 wParam, (LPARAM)wnd->hwndSelf );
2504 GetClientRect(hwnd, &rect);
2505 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2510 return SendMessageA( descr->owner, msg, wParam, lParam );
2514 case WM_QUERYDROPOBJECT:
2519 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2520 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2522 return SendMessageA( descr->owner, msg, wParam, lParam );
2527 if (TWEAK_WineLook > WIN31_LOOK)
2528 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2529 return DefWindowProcA( hwnd, msg, wParam, lParam );
2531 if ((msg >= WM_USER) && (msg < 0xc000))
2532 WARN_(listbox)("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2533 hwnd, msg, wParam, lParam );
2534 return DefWindowProcA( hwnd, msg, wParam, lParam );
2539 /***********************************************************************
2542 * This is just a wrapper for the real wndproc, it only does window locking
2545 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2546 WPARAM wParam, LPARAM lParam )
2548 WND* wndPtr = WIN_FindWndPtr( hwnd );
2549 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2551 WIN_ReleaseWndPtr(wndPtr);
2555 /***********************************************************************
2558 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2560 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2564 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2567 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2569 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2570 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2571 WIN_ReleaseWndPtr(wnd);
2574 WIN_ReleaseWndPtr(wnd);
2579 /***********************************************************************
2580 * ComboLBWndProc_locked
2582 * The real combo listbox wndproc, but called with locked WND struct.
2584 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2585 WPARAM wParam, LPARAM lParam )
2588 HWND hwnd = wnd->hwndSelf;
2592 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2594 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2595 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2597 if( descr || msg == WM_CREATE )
2599 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2604 #define lpcs ((LPCREATESTRUCTA)lParam)
2605 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2606 (UINT)lpcs->lpCreateParams);
2608 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2610 return LISTBOX_Create( wnd, lphc );
2611 case WM_LBUTTONDOWN:
2612 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2613 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2614 /* avoid activation at all costs */
2616 case WM_MOUSEACTIVATE:
2617 return MA_NOACTIVATE;
2621 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2623 /* for some reason(?) Windows makes it possible to
2624 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2626 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2627 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2628 && (wParam == VK_DOWN || wParam == VK_UP)) )
2630 COMBO_FlipListbox( lphc, FALSE );
2634 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2636 case LB_SETCURSEL16:
2638 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2639 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2642 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2647 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2650 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2652 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2657 /***********************************************************************
2660 * NOTE: in Windows, winproc address of the ComboLBox is the same
2661 * as that of the Listbox.
2663 * This is just a wrapper for the real wndproc, it only does window locking
2666 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2667 WPARAM wParam, LPARAM lParam )
2669 WND *wnd = WIN_FindWndPtr( hwnd );
2670 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2672 WIN_ReleaseWndPtr(wnd);