4 * Copyright 1996 Alexandre Julliard
9 #include "wine/winuser16.h"
17 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(listbox)
21 DECLARE_DEBUG_CHANNEL(combo)
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:\n" );
110 TRACE( "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( "%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 ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
302 if (page_size == descr->page_size) return;
303 descr->page_size = page_size;
304 if (descr->style & LBS_MULTICOLUMN)
305 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
306 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
310 /***********************************************************************
313 * Update the size of the listbox. Should be called when the size of
314 * the client area changes.
316 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
320 GetClientRect( wnd->hwndSelf, &rect );
321 descr->width = rect.right - rect.left;
322 descr->height = rect.bottom - rect.top;
323 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
325 if ((descr->height > descr->item_height) &&
326 (descr->height % descr->item_height))
328 TRACE("[%04x]: changing height %d -> %d\n",
329 wnd->hwndSelf, descr->height,
330 descr->height - descr->height%descr->item_height );
331 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
332 wnd->rectWindow.right - wnd->rectWindow.left,
333 wnd->rectWindow.bottom - wnd->rectWindow.top -
334 (descr->height % descr->item_height),
335 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
339 TRACE("[%04x]: new size = %d,%d\n",
340 wnd->hwndSelf, descr->width, descr->height );
341 LISTBOX_UpdatePage( wnd, descr );
342 LISTBOX_UpdateScroll( wnd, descr );
346 /***********************************************************************
347 * LISTBOX_GetItemRect
349 * Get the rectangle enclosing an item, in listbox client coordinates.
350 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
352 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
355 /* Index <= 0 is legal even on empty listboxes */
356 if (index && (index >= descr->nb_items)) return -1;
357 SetRect( rect, 0, 0, descr->width, descr->height );
358 if (descr->style & LBS_MULTICOLUMN)
360 INT col = (index / descr->page_size) -
361 (descr->top_item / descr->page_size);
362 rect->left += col * descr->column_width;
363 rect->right = rect->left + descr->column_width;
364 rect->top += (index % descr->page_size) * descr->item_height;
365 rect->bottom = rect->top + descr->item_height;
367 else if (descr->style & LBS_OWNERDRAWVARIABLE)
370 rect->right += descr->horz_pos;
371 if ((index >= 0) && (index < descr->nb_items))
373 if (index < descr->top_item)
375 for (i = descr->top_item-1; i >= index; i--)
376 rect->top -= descr->items[i].height;
380 for (i = descr->top_item; i < index; i++)
381 rect->top += descr->items[i].height;
383 rect->bottom = rect->top + descr->items[index].height;
389 rect->top += (index - descr->top_item) * descr->item_height;
390 rect->bottom = rect->top + descr->item_height;
391 rect->right += descr->horz_pos;
394 return ((rect->left < descr->width) && (rect->right > 0) &&
395 (rect->top < descr->height) && (rect->bottom > 0));
399 /***********************************************************************
400 * LISTBOX_GetItemFromPoint
402 * Return the item nearest from point (x,y) (in client coordinates).
404 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
407 INT index = descr->top_item;
409 if (!descr->nb_items) return -1; /* No items */
410 if (descr->style & LBS_OWNERDRAWVARIABLE)
415 while (index < descr->nb_items)
417 if ((pos += descr->items[index].height) > y) break;
426 if ((pos -= descr->items[index].height) <= y) break;
430 else if (descr->style & LBS_MULTICOLUMN)
432 if (y >= descr->item_height * descr->page_size) return -1;
433 if (y >= 0) index += y / descr->item_height;
434 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
435 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
439 index += (y / descr->item_height);
441 if (index < 0) return 0;
442 if (index >= descr->nb_items) return -1;
447 /***********************************************************************
452 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
453 const RECT *rect, INT index, UINT action )
455 LB_ITEMDATA *item = NULL;
456 if (index < descr->nb_items) item = &descr->items[index];
458 if (IS_OWNERDRAW(descr))
461 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
465 if (action == ODA_FOCUS)
466 DrawFocusRect( hdc, rect );
468 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
471 dis.CtlType = ODT_LISTBOX;
473 dis.hwndItem = wnd->hwndSelf;
474 dis.itemAction = action;
478 if (item && item->selected) dis.itemState |= ODS_SELECTED;
479 if ((descr->focus_item == index) &&
481 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
482 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
483 dis.itemData = item ? item->data : 0;
485 TRACE("[%04x]: drawitem %d (%s) action=%02x "
486 "state=%02x rect=%d,%d-%d,%d\n",
487 wnd->hwndSelf, index, item ? item->str : "", action,
488 dis.itemState, rect->left, rect->top,
489 rect->right, rect->bottom );
490 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
494 COLORREF oldText = 0, oldBk = 0;
496 if (action == ODA_FOCUS)
498 DrawFocusRect( hdc, rect );
501 if (item && item->selected)
503 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
504 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
507 TRACE("[%04x]: painting %d (%s) action=%02x "
508 "rect=%d,%d-%d,%d\n",
509 wnd->hwndSelf, index, item ? item->str : "", action,
510 rect->left, rect->top, rect->right, rect->bottom );
512 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
513 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
514 else if (!(descr->style & LBS_USETABSTOPS))
515 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
516 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
517 strlen(item->str), NULL );
520 /* Output empty string to paint background in the full width. */
521 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
522 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
523 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
524 item->str, strlen(item->str),
525 descr->nb_tabs, descr->tabs, 0);
527 if (item && item->selected)
529 SetBkColor( hdc, oldBk );
530 SetTextColor( hdc, oldText );
532 if ((descr->focus_item == index) &&
534 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
539 /***********************************************************************
542 * Change the redraw flag.
544 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
548 if (!(descr->style & LBS_NOREDRAW)) return;
549 descr->style &= ~LBS_NOREDRAW;
550 LISTBOX_UpdateScroll( wnd, descr );
552 else descr->style |= LBS_NOREDRAW;
556 /***********************************************************************
557 * LISTBOX_RepaintItem
559 * Repaint a single item synchronously.
561 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
567 HBRUSH hbrush, oldBrush = 0;
569 /* Do not repaint the item if the item is not visible */
570 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
572 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
573 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
574 if (descr->font) oldFont = SelectObject( hdc, descr->font );
575 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
576 hdc, (LPARAM)wnd->hwndSelf );
577 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
578 if (wnd->dwStyle & WS_DISABLED)
579 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
580 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
581 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
582 if (oldFont) SelectObject( hdc, oldFont );
583 if (oldBrush) SelectObject( hdc, oldBrush );
584 ReleaseDC( wnd->hwndSelf, hdc );
588 /***********************************************************************
589 * LISTBOX_InitStorage
591 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
596 nb_items += LB_ARRAY_GRANULARITY - 1;
597 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
599 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
600 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
601 nb_items * sizeof(LB_ITEMDATA) )))
603 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
611 /***********************************************************************
612 * LISTBOX_SetTabStops
614 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
615 LPINT tabs, BOOL short_ints )
617 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
618 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
619 if (!(descr->nb_tabs = count))
624 /* FIXME: count = 1 */
625 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
626 descr->nb_tabs * sizeof(INT) )))
631 LPINT16 p = (LPINT16)tabs;
632 dbg_decl_str(listbox, 256);
634 for (i = 0; i < descr->nb_tabs; i++) {
635 descr->tabs[i] = *p++<<1; /* FIXME */
636 if(TRACE_ON(listbox))
637 dsprintf(listbox, "%hd ", descr->tabs[i]);
639 TRACE("[%04x]: settabstops %s\n",
640 wnd->hwndSelf, dbg_str(listbox));
642 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
643 /* FIXME: repaint the window? */
648 /***********************************************************************
651 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
654 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
655 if (HAS_STRINGS(descr))
658 return strlen(descr->items[index].str);
659 lstrcpyA( buffer, descr->items[index].str );
660 return strlen(buffer);
663 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
664 return sizeof(DWORD);
669 /***********************************************************************
670 * LISTBOX_FindStringPos
672 * Find the nearest string located before a given string in sort order.
673 * If 'exact' is TRUE, return an error if we don't get an exact match.
675 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
678 INT index, min, max, res = -1;
680 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
682 max = descr->nb_items;
685 index = (min + max) / 2;
686 if (HAS_STRINGS(descr))
687 res = lstrcmpiA( descr->items[index].str, str );
690 COMPAREITEMSTRUCT cis;
691 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
693 cis.CtlType = ODT_LISTBOX;
695 cis.hwndItem = wnd->hwndSelf;
697 cis.itemData1 = descr->items[index].data;
699 cis.itemData2 = (DWORD)str;
700 cis.dwLocaleId = descr->locale;
701 res = SendMessageA( descr->owner, WM_COMPAREITEM,
704 if (!res) return index;
705 if (res > 0) max = index;
706 else min = index + 1;
708 return exact ? -1 : max;
712 /***********************************************************************
713 * LISTBOX_FindFileStrPos
715 * Find the nearest string located before a given string in directory
716 * sort order (i.e. first files, then directories, then drives).
718 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
720 INT min, max, res = -1;
722 if (!HAS_STRINGS(descr))
723 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
725 max = descr->nb_items;
728 INT index = (min + max) / 2;
729 const char *p = descr->items[index].str;
730 if (*p == '[') /* drive or directory */
732 if (*str != '[') res = -1;
733 else if (p[1] == '-') /* drive */
735 if (str[1] == '-') res = str[2] - p[2];
740 if (str[1] == '-') res = 1;
741 else res = lstrcmpiA( str, p );
746 if (*str == '[') res = 1;
747 else res = lstrcmpiA( str, p );
749 if (!res) return index;
750 if (res < 0) max = index;
751 else min = index + 1;
757 /***********************************************************************
760 * Find the item beginning with a given string.
762 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
763 LPCSTR str, BOOL exact )
768 if (start >= descr->nb_items) start = -1;
769 item = descr->items + start + 1;
770 if (HAS_STRINGS(descr))
772 if (!str) return LB_ERR;
775 for (i = start + 1; i < descr->nb_items; i++, item++)
776 if (!lstrcmpiA( str, item->str )) return i;
777 for (i = 0, item = descr->items; i <= start; i++, item++)
778 if (!lstrcmpiA( str, item->str )) return i;
782 /* Special case for drives and directories: ignore prefix */
783 #define CHECK_DRIVE(item) \
784 if ((item)->str[0] == '[') \
786 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
787 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
791 INT len = strlen(str);
792 for (i = start + 1; i < descr->nb_items; i++, item++)
794 if (!lstrncmpiA( str, item->str, len )) return i;
797 for (i = 0, item = descr->items; i <= start; i++, item++)
799 if (!lstrncmpiA( str, item->str, len )) return i;
807 if (exact && (descr->style & LBS_SORT))
808 /* If sorted, use a WM_COMPAREITEM binary search */
809 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
811 /* Otherwise use a linear search */
812 for (i = start + 1; i < descr->nb_items; i++, item++)
813 if (item->data == (DWORD)str) return i;
814 for (i = 0, item = descr->items; i <= start; i++, item++)
815 if (item->data == (DWORD)str) return i;
821 /***********************************************************************
822 * LISTBOX_GetSelCount
824 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
827 LB_ITEMDATA *item = descr->items;
829 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
830 for (i = count = 0; i < descr->nb_items; i++, item++)
831 if (item->selected) count++;
836 /***********************************************************************
837 * LISTBOX_GetSelItems16
839 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
843 LB_ITEMDATA *item = descr->items;
845 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
846 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
847 if (item->selected) array[count++] = (INT16)i;
852 /***********************************************************************
853 * LISTBOX_GetSelItems32
855 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
859 LB_ITEMDATA *item = descr->items;
861 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
862 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
863 if (item->selected) array[count++] = i;
868 /***********************************************************************
871 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
873 INT i, col_pos = descr->page_size - 1;
876 HBRUSH hbrush, oldBrush = 0;
878 SetRect( &rect, 0, 0, descr->width, descr->height );
879 if (descr->style & LBS_NOREDRAW) return 0;
880 if (descr->style & LBS_MULTICOLUMN)
881 rect.right = rect.left + descr->column_width;
882 else if (descr->horz_pos)
884 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
885 rect.right += descr->horz_pos;
888 if (descr->font) oldFont = SelectObject( hdc, descr->font );
889 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
890 hdc, (LPARAM)wnd->hwndSelf );
891 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
892 if (wnd->dwStyle & WS_DISABLED)
893 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
895 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
896 (GetFocus() == wnd->hwndSelf))
898 /* Special case for empty listbox: paint focus rect */
899 rect.bottom = rect.top + descr->item_height;
900 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
902 rect.top = rect.bottom;
905 for (i = descr->top_item; i < descr->nb_items; i++)
907 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
908 rect.bottom = rect.top + descr->item_height;
910 rect.bottom = rect.top + descr->items[i].height;
912 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
913 rect.top = rect.bottom;
915 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
917 if (!IS_OWNERDRAW(descr))
919 /* Clear the bottom of the column */
920 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
921 if (rect.top < descr->height)
923 rect.bottom = descr->height;
924 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
925 &rect, NULL, 0, NULL );
929 /* Go to the next column */
930 rect.left += descr->column_width;
931 rect.right += descr->column_width;
933 col_pos = descr->page_size - 1;
938 if (rect.top >= descr->height) break;
942 if (!IS_OWNERDRAW(descr))
944 /* Clear the remainder of the client area */
945 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
946 if (rect.top < descr->height)
948 rect.bottom = descr->height;
949 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
950 &rect, NULL, 0, NULL );
952 if (rect.right < descr->width)
954 rect.left = rect.right;
955 rect.right = descr->width;
957 rect.bottom = descr->height;
958 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
959 &rect, NULL, 0, NULL );
962 if (oldFont) SelectObject( hdc, oldFont );
963 if (oldBrush) SelectObject( hdc, oldBrush );
968 /***********************************************************************
969 * LISTBOX_InvalidateItems
971 * Invalidate all items from a given item. If the specified item is not
972 * visible, nothing happens.
974 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
978 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
980 rect.bottom = descr->height;
981 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
982 if (descr->style & LBS_MULTICOLUMN)
984 /* Repaint the other columns */
985 rect.left = rect.right;
986 rect.right = descr->width;
988 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
994 /***********************************************************************
995 * LISTBOX_GetItemHeight
997 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
999 if (descr->style & LBS_OWNERDRAWVARIABLE)
1001 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1002 return descr->items[index].height;
1004 else return descr->item_height;
1008 /***********************************************************************
1009 * LISTBOX_SetItemHeight
1011 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1014 if (!height) height = 1;
1016 if (descr->style & LBS_OWNERDRAWVARIABLE)
1018 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1019 TRACE("[%04x]: item %d height = %d\n",
1020 wnd->hwndSelf, index, height );
1021 descr->items[index].height = height;
1022 LISTBOX_UpdateScroll( wnd, descr );
1023 LISTBOX_InvalidateItems( wnd, descr, index );
1025 else if (height != descr->item_height)
1027 TRACE("[%04x]: new height = %d\n",
1028 wnd->hwndSelf, height );
1029 descr->item_height = height;
1030 LISTBOX_UpdatePage( wnd, descr );
1031 LISTBOX_UpdateScroll( wnd, descr );
1032 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1038 /***********************************************************************
1039 * LISTBOX_SetHorizontalPos
1041 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1045 if (pos > descr->horz_extent - descr->width)
1046 pos = descr->horz_extent - descr->width;
1047 if (pos < 0) pos = 0;
1048 if (!(diff = descr->horz_pos - pos)) return;
1049 TRACE("[%04x]: new horz pos = %d\n",
1050 wnd->hwndSelf, pos );
1051 descr->horz_pos = pos;
1052 LISTBOX_UpdateScroll( wnd, descr );
1053 if (abs(diff) < descr->width)
1054 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1055 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1057 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1061 /***********************************************************************
1062 * LISTBOX_SetHorizontalExtent
1064 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1067 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1069 if (extent <= 0) extent = 1;
1070 if (extent == descr->horz_extent) return LB_OKAY;
1071 TRACE("[%04x]: new horz extent = %d\n",
1072 wnd->hwndSelf, extent );
1073 descr->horz_extent = extent;
1074 if (descr->horz_pos > extent - descr->width)
1075 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1077 LISTBOX_UpdateScroll( wnd, descr );
1082 /***********************************************************************
1083 * LISTBOX_SetColumnWidth
1085 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1087 width += 2; /* For left and right margin */
1088 if (width == descr->column_width) return LB_OKAY;
1089 TRACE("[%04x]: new column width = %d\n",
1090 wnd->hwndSelf, width );
1091 descr->column_width = width;
1092 LISTBOX_UpdatePage( wnd, descr );
1097 /***********************************************************************
1100 * Returns the item height.
1102 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1110 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1112 ERR("unable to get DC.\n" );
1115 if (font) oldFont = SelectObject( hdc, font );
1116 GetTextMetricsA( hdc, &tm );
1117 if (oldFont) SelectObject( hdc, oldFont );
1118 ReleaseDC( wnd->hwndSelf, hdc );
1119 if (!IS_OWNERDRAW(descr))
1120 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1121 return tm.tmHeight ;
1125 /***********************************************************************
1126 * LISTBOX_MakeItemVisible
1128 * Make sure that a given item is partially or fully visible.
1130 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1135 if (index <= descr->top_item) top = index;
1136 else if (descr->style & LBS_MULTICOLUMN)
1138 INT cols = descr->width;
1139 if (!fully) cols += descr->column_width - 1;
1140 if (cols >= descr->column_width) cols /= descr->column_width;
1142 if (index < descr->top_item + (descr->page_size * cols)) return;
1143 top = index - descr->page_size * (cols - 1);
1145 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1147 INT height = fully ? descr->items[index].height : 1;
1148 for (top = index; top > descr->top_item; top--)
1149 if ((height += descr->items[top-1].height) > descr->height) break;
1153 if (index < descr->top_item + descr->page_size) return;
1154 if (!fully && (index == descr->top_item + descr->page_size) &&
1155 (descr->height > (descr->page_size * descr->item_height))) return;
1156 top = index - descr->page_size + 1;
1158 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1162 /***********************************************************************
1163 * LISTBOX_SelectItemRange
1165 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1167 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1172 /* A few sanity checks */
1174 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1175 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1176 if (last == -1) last = descr->nb_items - 1;
1177 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1178 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1179 /* selected_item reflects last selected/unselected item on multiple sel */
1180 descr->selected_item = last;
1182 if (on) /* Turn selection on */
1184 for (i = first; i <= last; i++)
1186 if (descr->items[i].selected) continue;
1187 descr->items[i].selected = TRUE;
1188 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1191 else /* Turn selection off */
1193 for (i = first; i <= last; i++)
1195 if (!descr->items[i].selected) continue;
1196 descr->items[i].selected = FALSE;
1197 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1204 /***********************************************************************
1205 * LISTBOX_SetCaretIndex
1208 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1211 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1212 BOOL fully_visible )
1214 INT oldfocus = descr->focus_item;
1216 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1217 if (index == oldfocus) return LB_OKAY;
1218 descr->focus_item = index;
1219 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1220 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1222 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1223 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1224 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1230 /***********************************************************************
1231 * LISTBOX_SetSelection
1233 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1234 BOOL on, BOOL send_notify )
1236 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1237 if (descr->style & LBS_MULTIPLESEL)
1239 if (index == -1) /* Select all items */
1240 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1241 else /* Only one item */
1242 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1246 INT oldsel = descr->selected_item;
1247 if (index == oldsel) return LB_OKAY;
1248 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1249 if (index != -1) descr->items[index].selected = TRUE;
1250 descr->selected_item = index;
1251 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1252 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1253 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1254 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1256 if( descr->lphc ) /* set selection change flag for parent combo */
1257 descr->lphc->wState |= CBF_SELCHANGE;
1263 /***********************************************************************
1266 * Change the caret position and extend the selection to the new caret.
1268 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1269 BOOL fully_visible )
1271 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1272 if (descr->style & LBS_EXTENDEDSEL)
1274 if (descr->anchor_item != -1)
1276 INT first = MIN( descr->focus_item, descr->anchor_item );
1277 INT last = MAX( descr->focus_item, descr->anchor_item );
1279 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1280 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1281 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1284 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1286 /* Set selection to new caret item */
1287 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1292 /***********************************************************************
1293 * LISTBOX_InsertItem
1295 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1296 LPSTR str, DWORD data )
1301 if (index == -1) index = descr->nb_items;
1302 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1303 if (!descr->items) max_items = 0;
1304 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1305 if (descr->nb_items == max_items)
1307 /* We need to grow the array */
1308 max_items += LB_ARRAY_GRANULARITY;
1309 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1310 max_items * sizeof(LB_ITEMDATA) )))
1312 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1315 descr->items = item;
1318 /* Insert the item structure */
1320 item = &descr->items[index];
1321 if (index < descr->nb_items)
1322 RtlMoveMemory( item + 1, item,
1323 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1327 item->selected = FALSE;
1330 /* Get item height */
1332 if (descr->style & LBS_OWNERDRAWVARIABLE)
1334 MEASUREITEMSTRUCT mis;
1335 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1337 mis.CtlType = ODT_LISTBOX;
1340 mis.itemData = descr->items[index].data;
1341 mis.itemHeight = descr->item_height;
1342 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1343 item->height = mis.itemHeight ? mis.itemHeight : 1;
1344 TRACE("[%04x]: measure item %d (%s) = %d\n",
1345 wnd->hwndSelf, index, str ? str : "", item->height );
1348 /* Repaint the items */
1350 LISTBOX_UpdateScroll( wnd, descr );
1351 LISTBOX_InvalidateItems( wnd, descr, index );
1353 /* Move selection and focused item */
1355 if (index <= descr->selected_item) descr->selected_item++;
1356 if (index <= descr->focus_item)
1358 descr->focus_item++;
1359 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1362 /* If listbox was empty, set focus to the first item */
1364 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1369 /***********************************************************************
1370 * LISTBOX_InsertString
1372 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1375 LPSTR new_str = NULL;
1379 if (HAS_STRINGS(descr))
1381 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1383 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1387 else data = (DWORD)str;
1389 if (index == -1) index = descr->nb_items;
1390 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1392 if (new_str) HeapFree( descr->heap, 0, new_str );
1396 TRACE("[%04x]: added item %d '%s'\n",
1397 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1402 /***********************************************************************
1403 * LISTBOX_DeleteItem
1405 * Delete the content of an item. 'index' must be a valid index.
1407 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1409 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1410 * while Win95 sends it for all items with user data.
1411 * It's probably better to send it too often than not
1412 * often enough, so this is what we do here.
1414 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1416 DELETEITEMSTRUCT dis;
1417 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1419 dis.CtlType = ODT_LISTBOX;
1422 dis.hwndItem = wnd->hwndSelf;
1423 dis.itemData = descr->items[index].data;
1424 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1426 if (HAS_STRINGS(descr) && descr->items[index].str)
1427 HeapFree( descr->heap, 0, descr->items[index].str );
1431 /***********************************************************************
1432 * LISTBOX_RemoveItem
1434 * Remove an item from the listbox and delete its content.
1436 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1441 if (index == -1) index = descr->nb_items - 1;
1442 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1443 LISTBOX_DeleteItem( wnd, descr, index );
1445 /* Remove the item */
1447 item = &descr->items[index];
1448 if (index < descr->nb_items-1)
1449 RtlMoveMemory( item, item + 1,
1450 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1452 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1454 /* Shrink the item array if possible */
1456 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1457 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1459 max_items -= LB_ARRAY_GRANULARITY;
1460 item = HeapReAlloc( descr->heap, 0, descr->items,
1461 max_items * sizeof(LB_ITEMDATA) );
1462 if (item) descr->items = item;
1465 /* Repaint the items */
1467 LISTBOX_UpdateScroll( wnd, descr );
1468 LISTBOX_InvalidateItems( wnd, descr, index );
1470 /* Move selection and focused item */
1472 if (index <= descr->selected_item) descr->selected_item--;
1473 if (index <= descr->focus_item)
1475 descr->focus_item--;
1476 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1482 /***********************************************************************
1483 * LISTBOX_ResetContent
1485 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1489 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1490 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1491 descr->nb_items = 0;
1492 descr->top_item = 0;
1493 descr->selected_item = -1;
1494 descr->focus_item = 0;
1495 descr->anchor_item = -1;
1496 descr->items = NULL;
1497 LISTBOX_UpdateScroll( wnd, descr );
1498 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1502 /***********************************************************************
1505 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1509 if (HAS_STRINGS(descr)) return LB_ERR;
1510 /* FIXME: this is far from optimal... */
1511 if (count > descr->nb_items)
1513 while (count > descr->nb_items)
1514 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1517 else if (count < descr->nb_items)
1519 while (count < descr->nb_items)
1520 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1527 /***********************************************************************
1530 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1531 LPCSTR filespec, BOOL long_names )
1534 LRESULT ret = LB_OKAY;
1535 WIN32_FIND_DATAA entry;
1538 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1540 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1547 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1549 if (!(attrib & DDL_DIRECTORY) ||
1550 !strcmp( entry.cAlternateFileName, "." )) continue;
1551 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1552 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1554 else /* not a directory */
1556 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1557 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1559 if ((attrib & DDL_EXCLUSIVE) &&
1560 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1563 if (long_names) strcpy( buffer, entry.cFileName );
1564 else strcpy( buffer, entry.cAlternateFileName );
1566 if (!long_names) CharLowerA( buffer );
1567 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1568 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1570 } while (FindNextFileA( handle, &entry ));
1571 FindClose( handle );
1574 if ((ret >= 0) && (attrib & DDL_DRIVES))
1576 char buffer[] = "[-a-]";
1578 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1580 if (!DRIVE_IsValid(drive)) continue;
1581 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1589 /***********************************************************************
1590 * LISTBOX_HandleVScroll
1592 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1593 WPARAM wParam, LPARAM lParam )
1597 if (descr->style & LBS_MULTICOLUMN) return 0;
1598 switch(LOWORD(wParam))
1601 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1604 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1607 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1608 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1611 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1612 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1614 case SB_THUMBPOSITION:
1615 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1618 info.cbSize = sizeof(info);
1619 info.fMask = SIF_TRACKPOS;
1620 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1621 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1624 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1627 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1634 /***********************************************************************
1635 * LISTBOX_HandleHScroll
1637 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1638 WPARAM wParam, LPARAM lParam )
1643 if (descr->style & LBS_MULTICOLUMN)
1645 switch(LOWORD(wParam))
1648 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1652 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1656 page = descr->width / descr->column_width;
1657 if (page < 1) page = 1;
1658 LISTBOX_SetTopItem( wnd, descr,
1659 descr->top_item - page * descr->page_size, TRUE );
1662 page = descr->width / descr->column_width;
1663 if (page < 1) page = 1;
1664 LISTBOX_SetTopItem( wnd, descr,
1665 descr->top_item + page * descr->page_size, TRUE );
1667 case SB_THUMBPOSITION:
1668 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1672 info.cbSize = sizeof(info);
1673 info.fMask = SIF_TRACKPOS;
1674 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1675 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1679 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1682 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1686 else if (descr->horz_extent)
1688 switch(LOWORD(wParam))
1691 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1694 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1697 LISTBOX_SetHorizontalPos( wnd, descr,
1698 descr->horz_pos - descr->width );
1701 LISTBOX_SetHorizontalPos( wnd, descr,
1702 descr->horz_pos + descr->width );
1704 case SB_THUMBPOSITION:
1705 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1708 info.cbSize = sizeof(info);
1709 info.fMask = SIF_TRACKPOS;
1710 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1711 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1714 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1717 LISTBOX_SetHorizontalPos( wnd, descr,
1718 descr->horz_extent - descr->width );
1726 /***********************************************************************
1727 * LISTBOX_HandleLButtonDown
1729 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1730 WPARAM wParam, INT x, INT y )
1732 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1733 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1734 wnd->hwndSelf, x, y, index );
1735 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1738 if (descr->style & LBS_EXTENDEDSEL)
1740 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1741 if (wParam & MK_CONTROL)
1743 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1744 LISTBOX_SetSelection( wnd, descr, index,
1745 !descr->items[index].selected, FALSE );
1747 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1751 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1752 LISTBOX_SetSelection( wnd, descr, index,
1753 (!(descr->style & LBS_MULTIPLESEL) ||
1754 !descr->items[index].selected), FALSE );
1758 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1759 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1760 : descr->lphc->self->hwndSelf ) ;
1762 descr->captured = TRUE;
1763 SetCapture( wnd->hwndSelf );
1764 if (index != -1 && !descr->lphc)
1766 if (descr->style & LBS_NOTIFY )
1767 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1768 MAKELPARAM( x, y ) );
1769 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1776 if (DragDetect( wnd->hwndSelf, pt ))
1777 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1784 /***********************************************************************
1785 * LISTBOX_HandleLButtonUp
1787 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1789 if (LISTBOX_Timer != LB_TIMER_NONE)
1790 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1791 LISTBOX_Timer = LB_TIMER_NONE;
1792 if (descr->captured)
1794 descr->captured = FALSE;
1795 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1796 if (descr->style & LBS_NOTIFY)
1797 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1803 /***********************************************************************
1804 * LISTBOX_HandleTimer
1806 * Handle scrolling upon a timer event.
1807 * Return TRUE if scrolling should continue.
1809 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1810 INT index, TIMER_DIRECTION dir )
1815 if (descr->top_item) index = descr->top_item - 1;
1819 if (descr->top_item) index -= descr->page_size;
1822 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1823 if (index == descr->focus_item) index++;
1824 if (index >= descr->nb_items) index = descr->nb_items - 1;
1826 case LB_TIMER_RIGHT:
1827 if (index + descr->page_size < descr->nb_items)
1828 index += descr->page_size;
1833 if (index == descr->focus_item) return FALSE;
1834 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1839 /***********************************************************************
1840 * LISTBOX_HandleSystemTimer
1842 * WM_SYSTIMER handler.
1844 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1846 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1848 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1849 LISTBOX_Timer = LB_TIMER_NONE;
1855 /***********************************************************************
1856 * LISTBOX_HandleMouseMove
1858 * WM_MOUSEMOVE handler.
1860 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1864 TIMER_DIRECTION dir;
1866 if (!descr->captured) return;
1868 if (descr->style & LBS_MULTICOLUMN)
1871 else if (y >= descr->item_height * descr->page_size)
1872 y = descr->item_height * descr->page_size - 1;
1876 dir = LB_TIMER_LEFT;
1879 else if (x >= descr->width)
1881 dir = LB_TIMER_RIGHT;
1882 x = descr->width - 1;
1884 else dir = LB_TIMER_NONE; /* inside */
1888 if (y < 0) dir = LB_TIMER_UP; /* above */
1889 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1890 else dir = LB_TIMER_NONE; /* inside */
1893 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1894 if (index == -1) index = descr->focus_item;
1895 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1897 /* Start/stop the system timer */
1899 if (dir != LB_TIMER_NONE)
1900 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1901 else if (LISTBOX_Timer != LB_TIMER_NONE)
1902 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1903 LISTBOX_Timer = dir;
1907 /***********************************************************************
1908 * LISTBOX_HandleKeyDown
1910 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1913 if (descr->style & LBS_WANTKEYBOARDINPUT)
1915 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1916 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1918 if (caret == -2) return 0;
1920 if (caret == -1) switch(wParam)
1923 if (descr->style & LBS_MULTICOLUMN)
1925 if (descr->focus_item >= descr->page_size)
1926 caret = descr->focus_item - descr->page_size;
1931 caret = descr->focus_item - 1;
1932 if (caret < 0) caret = 0;
1935 if (descr->style & LBS_MULTICOLUMN)
1937 if (descr->focus_item + descr->page_size < descr->nb_items)
1938 caret = descr->focus_item + descr->page_size;
1943 caret = descr->focus_item + 1;
1944 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1947 if (descr->style & LBS_MULTICOLUMN)
1949 INT page = descr->width / descr->column_width;
1950 if (page < 1) page = 1;
1951 caret = descr->focus_item - (page * descr->page_size) + 1;
1953 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1954 if (caret < 0) caret = 0;
1957 if (descr->style & LBS_MULTICOLUMN)
1959 INT page = descr->width / descr->column_width;
1960 if (page < 1) page = 1;
1961 caret = descr->focus_item + (page * descr->page_size) - 1;
1963 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1964 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1970 caret = descr->nb_items - 1;
1973 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1974 else if (descr->style & LBS_MULTIPLESEL)
1976 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1977 !descr->items[descr->focus_item].selected,
1978 (descr->style & LBS_NOTIFY) != 0 );
1980 else if (descr->selected_item == -1)
1982 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1983 (descr->style & LBS_NOTIFY) != 0 );
1989 if ((descr->style & LBS_EXTENDEDSEL) &&
1990 !(GetKeyState( VK_SHIFT ) & 0x8000))
1991 descr->anchor_item = caret;
1992 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1993 if (descr->style & LBS_NOTIFY)
1995 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1997 /* make sure that combo parent doesn't hide us */
1998 descr->lphc->wState |= CBF_NOROLLUP;
2000 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2007 /***********************************************************************
2008 * LISTBOX_HandleChar
2010 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2016 str[0] = wParam & 0xff;
2019 if (descr->style & LBS_WANTKEYBOARDINPUT)
2021 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2022 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2024 if (caret == -2) return 0;
2027 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2030 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2031 if (descr->style & LBS_NOTIFY)
2032 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2038 /***********************************************************************
2041 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2044 MEASUREITEMSTRUCT mis;
2047 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2049 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2051 HeapFree( GetProcessHeap(), 0, descr );
2054 GetClientRect( wnd->hwndSelf, &rect );
2055 descr->owner = GetParent( wnd->hwndSelf );
2056 descr->style = wnd->dwStyle;
2057 descr->width = rect.right - rect.left;
2058 descr->height = rect.bottom - rect.top;
2059 descr->items = NULL;
2060 descr->nb_items = 0;
2061 descr->top_item = 0;
2062 descr->selected_item = -1;
2063 descr->focus_item = 0;
2064 descr->anchor_item = -1;
2065 descr->item_height = 1;
2066 descr->page_size = 1;
2067 descr->column_width = 150;
2068 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2069 descr->horz_pos = 0;
2072 descr->caret_on = TRUE;
2073 descr->captured = FALSE;
2075 descr->locale = 0; /* FIXME */
2080 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2081 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2082 descr->owner = lphc->self->hwndSelf;
2085 *(LB_DESCR **)wnd->wExtra = descr;
2087 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2089 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2090 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2091 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2092 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2094 if (descr->style & LBS_OWNERDRAWFIXED)
2096 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2098 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2099 descr->item_height = lphc->fixedOwnerDrawHeight;
2103 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2105 mis.CtlType = ODT_LISTBOX;
2110 mis.itemHeight = descr->item_height;
2111 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2112 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2120 /***********************************************************************
2123 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2125 LISTBOX_ResetContent( wnd, descr );
2126 HeapDestroy( descr->heap );
2127 HeapFree( GetProcessHeap(), 0, descr );
2133 /***********************************************************************
2136 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2137 WPARAM wParam, LPARAM lParam )
2141 HWND hwnd = wnd->hwndSelf;
2144 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2146 if (msg == WM_CREATE)
2148 if (!LISTBOX_Create( wnd, NULL ))
2150 TRACE("creating wnd=%04x descr=%p\n",
2151 hwnd, *(LB_DESCR **)wnd->wExtra );
2154 /* Ignore all other messages before we get a WM_CREATE */
2155 return DefWindowProcA( hwnd, msg, wParam, lParam );
2158 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2159 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2162 case LB_RESETCONTENT16:
2163 case LB_RESETCONTENT:
2164 LISTBOX_ResetContent( wnd, descr );
2167 case LB_ADDSTRING16:
2168 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2171 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2172 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2174 case LB_INSERTSTRING16:
2175 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2176 wParam = (INT)(INT16)wParam;
2178 case LB_INSERTSTRING:
2179 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2182 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2185 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2186 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2188 case LB_DELETESTRING16:
2189 case LB_DELETESTRING:
2190 return LISTBOX_RemoveItem( wnd, descr, wParam );
2192 case LB_GETITEMDATA16:
2193 case LB_GETITEMDATA:
2194 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2196 return descr->items[wParam].data;
2198 case LB_SETITEMDATA16:
2199 case LB_SETITEMDATA:
2200 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2202 descr->items[wParam].data = (DWORD)lParam;
2207 return descr->nb_items;
2210 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2213 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2215 case LB_GETTEXTLEN16:
2218 if (wParam >= descr->nb_items)
2220 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2223 case LB_GETCURSEL16:
2225 if (descr->nb_items==0)
2228 if (descr->selected_item!=-1)
2229 return descr->selected_item;
2231 return descr->focus_item;
2232 /* otherwise, if the user tries to move the selection with the */
2233 /* arrow keys, we will give the application something to choke on */
2234 case LB_GETTOPINDEX16:
2235 case LB_GETTOPINDEX:
2236 return descr->top_item;
2238 case LB_GETITEMHEIGHT16:
2239 case LB_GETITEMHEIGHT:
2240 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2242 case LB_SETITEMHEIGHT16:
2243 lParam = LOWORD(lParam);
2245 case LB_SETITEMHEIGHT:
2246 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2248 case LB_ITEMFROMPOINT:
2253 pt.x = LOWORD(lParam);
2254 pt.y = HIWORD(lParam);
2257 rect.right = descr->width;
2258 rect.bottom = descr->height;
2260 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2261 !PtInRect( &rect, pt ) );
2264 case LB_SETCARETINDEX16:
2265 case LB_SETCARETINDEX:
2266 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2268 case LB_GETCARETINDEX16:
2269 case LB_GETCARETINDEX:
2270 return descr->focus_item;
2272 case LB_SETTOPINDEX16:
2273 case LB_SETTOPINDEX:
2274 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2276 case LB_SETCOLUMNWIDTH16:
2277 case LB_SETCOLUMNWIDTH:
2278 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2280 case LB_GETITEMRECT16:
2283 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2284 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2288 case LB_GETITEMRECT:
2289 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2291 case LB_FINDSTRING16:
2292 wParam = (INT)(INT16)wParam;
2293 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2296 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2298 case LB_FINDSTRINGEXACT16:
2299 wParam = (INT)(INT16)wParam;
2300 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2302 case LB_FINDSTRINGEXACT:
2303 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2305 case LB_SELECTSTRING16:
2306 wParam = (INT)(INT16)wParam;
2307 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2309 case LB_SELECTSTRING:
2311 INT index = LISTBOX_FindString( wnd, descr, wParam,
2312 (LPCSTR)lParam, FALSE );
2313 if (index == LB_ERR)
2315 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2320 wParam = (INT)(INT16)wParam;
2323 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2325 return descr->items[wParam].selected;
2328 lParam = (INT)(INT16)lParam;
2331 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2333 case LB_SETCURSEL16:
2334 wParam = (INT)(INT16)wParam;
2337 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2338 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2340 case LB_GETSELCOUNT16:
2341 case LB_GETSELCOUNT:
2342 return LISTBOX_GetSelCount( wnd, descr );
2344 case LB_GETSELITEMS16:
2345 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2346 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2348 case LB_GETSELITEMS:
2349 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2351 case LB_SELITEMRANGE16:
2352 case LB_SELITEMRANGE:
2353 if (LOWORD(lParam) <= HIWORD(lParam))
2354 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2355 HIWORD(lParam), wParam );
2357 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2358 LOWORD(lParam), wParam );
2360 case LB_SELITEMRANGEEX16:
2361 case LB_SELITEMRANGEEX:
2362 if ((INT)lParam >= (INT)wParam)
2363 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2365 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2367 case LB_GETHORIZONTALEXTENT16:
2368 case LB_GETHORIZONTALEXTENT:
2369 return descr->horz_extent;
2371 case LB_SETHORIZONTALEXTENT16:
2372 case LB_SETHORIZONTALEXTENT:
2373 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2375 case LB_GETANCHORINDEX16:
2376 case LB_GETANCHORINDEX:
2377 return descr->anchor_item;
2379 case LB_SETANCHORINDEX16:
2380 wParam = (INT)(INT16)wParam;
2382 case LB_SETANCHORINDEX:
2383 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2385 descr->anchor_item = (INT)wParam;
2389 return LISTBOX_Directory( wnd, descr, wParam,
2390 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2393 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2396 return descr->locale;
2399 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2402 case LB_INITSTORAGE:
2403 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2406 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2408 case LB_SETTABSTOPS16:
2409 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2410 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2412 case LB_SETTABSTOPS:
2413 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2417 if (descr->caret_on)
2419 descr->caret_on = TRUE;
2420 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2421 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2426 if (!descr->caret_on)
2428 descr->caret_on = FALSE;
2429 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2430 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2434 return LISTBOX_Destroy( wnd, descr );
2437 InvalidateRect( hwnd, NULL, TRUE );
2441 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2445 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2450 HDC hdc = ( wParam ) ? ((HDC)wParam)
2451 : BeginPaint( hwnd, &ps );
2452 ret = LISTBOX_Paint( wnd, descr, hdc );
2453 if( !wParam ) EndPaint( hwnd, &ps );
2457 LISTBOX_UpdateSize( wnd, descr );
2462 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2463 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2466 descr->caret_on = TRUE;
2467 if (descr->focus_item != -1)
2468 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2469 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2472 if ((descr->focus_item != -1) && descr->caret_on)
2473 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2474 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2477 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2479 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2480 case WM_LBUTTONDOWN:
2481 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2482 (INT16)LOWORD(lParam),
2483 (INT16)HIWORD(lParam) );
2484 case WM_LBUTTONDBLCLK:
2485 if (descr->style & LBS_NOTIFY)
2486 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2489 if (GetCapture() == hwnd)
2490 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2491 (INT16)HIWORD(lParam) );
2494 return LISTBOX_HandleLButtonUp( wnd, descr );
2496 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2498 return LISTBOX_HandleChar( wnd, descr, wParam );
2500 return LISTBOX_HandleSystemTimer( wnd, descr );
2502 if (IS_OWNERDRAW(descr))
2505 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2506 wParam, (LPARAM)wnd->hwndSelf );
2507 GetClientRect(hwnd, &rect);
2508 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2513 return SendMessageA( descr->owner, msg, wParam, lParam );
2517 case WM_QUERYDROPOBJECT:
2522 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2523 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2525 return SendMessageA( descr->owner, msg, wParam, lParam );
2530 if (TWEAK_WineLook > WIN31_LOOK)
2531 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2532 return DefWindowProcA( hwnd, msg, wParam, lParam );
2534 if ((msg >= WM_USER) && (msg < 0xc000))
2535 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2536 hwnd, msg, wParam, lParam );
2537 return DefWindowProcA( hwnd, msg, wParam, lParam );
2542 /***********************************************************************
2545 * This is just a wrapper for the real wndproc, it only does window locking
2548 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2549 WPARAM wParam, LPARAM lParam )
2551 WND* wndPtr = WIN_FindWndPtr( hwnd );
2552 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2554 WIN_ReleaseWndPtr(wndPtr);
2558 /***********************************************************************
2561 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2563 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2567 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2570 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2572 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2573 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2574 WIN_ReleaseWndPtr(wnd);
2577 WIN_ReleaseWndPtr(wnd);
2582 /***********************************************************************
2583 * ComboLBWndProc_locked
2585 * The real combo listbox wndproc, but called with locked WND struct.
2587 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2588 WPARAM wParam, LPARAM lParam )
2591 HWND hwnd = wnd->hwndSelf;
2595 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2597 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2598 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2600 if( descr || msg == WM_CREATE )
2602 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2607 #define lpcs ((LPCREATESTRUCTA)lParam)
2608 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2609 (UINT)lpcs->lpCreateParams);
2611 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2613 return LISTBOX_Create( wnd, lphc );
2614 case WM_LBUTTONDOWN:
2615 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2616 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2617 /* avoid activation at all costs */
2619 case WM_MOUSEACTIVATE:
2620 return MA_NOACTIVATE;
2624 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2626 /* for some reason(?) Windows makes it possible to
2627 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2629 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2630 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2631 && (wParam == VK_DOWN || wParam == VK_UP)) )
2633 COMBO_FlipListbox( lphc, FALSE );
2637 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2639 case LB_SETCURSEL16:
2641 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2642 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2645 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2650 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2653 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2655 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2660 /***********************************************************************
2663 * NOTE: in Windows, winproc address of the ComboLBox is the same
2664 * as that of the Listbox.
2666 * This is just a wrapper for the real wndproc, it only does window locking
2669 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2670 WPARAM wParam, LPARAM lParam )
2672 WND *wnd = WIN_FindWndPtr( hwnd );
2673 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2675 WIN_ReleaseWndPtr(wnd);