Removed @PROGEXT@ (it was broken anyway).
[wine.git] / controls / listbox.c
blob24f6ea8722d35b0e7768b41c776c121c43c40688
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <string.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "winuser.h"
15 #include "winerror.h"
16 #include "drive.h"
17 #include "heap.h"
18 #include "spy.h"
19 #include "selectors.h"
20 #include "win.h"
21 #include "combo.h"
22 #include "debugtools.h"
23 #include "tweak.h"
25 DEFAULT_DEBUG_CHANNEL(listbox);
26 DECLARE_DEBUG_CHANNEL(combo);
28 /* Unimplemented yet:
29 * - LBS_NOSEL
30 * - LBS_USETABSTOPS
31 * - Unicode
32 * - Locale handling
35 /* Items array granularity */
36 #define LB_ARRAY_GRANULARITY 16
38 /* Scrolling timeout in ms */
39 #define LB_SCROLL_TIMEOUT 50
41 /* Listbox system timer id */
42 #define LB_TIMER_ID 2
44 /* Item structure */
45 typedef struct
47 LPSTR str; /* Item text */
48 BOOL selected; /* Is item selected? */
49 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
50 DWORD data; /* User data */
51 } LB_ITEMDATA;
53 /* Listbox structure */
54 typedef struct
56 HANDLE heap; /* Heap for this listbox */
57 HWND owner; /* Owner window to send notifications to */
58 UINT style; /* Window style */
59 INT width; /* Window width */
60 INT height; /* Window height */
61 LB_ITEMDATA *items; /* Array of items */
62 INT nb_items; /* Number of items */
63 INT top_item; /* Top visible item */
64 INT selected_item; /* Selected item */
65 INT focus_item; /* Item that has the focus */
66 INT anchor_item; /* Anchor item for extended selection */
67 INT item_height; /* Default item height */
68 INT page_size; /* Items per listbox page */
69 INT column_width; /* Column width for multi-column listboxes */
70 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
71 INT horz_pos; /* Horizontal position */
72 INT nb_tabs; /* Number of tabs in array */
73 INT *tabs; /* Array of tabs */
74 BOOL caret_on; /* Is caret on? */
75 BOOL captured; /* Is mouse captured? */
76 BOOL in_focus;
77 HFONT font; /* Current font */
78 LCID locale; /* Current locale for string comparisons */
79 LPHEADCOMBO lphc; /* ComboLBox */
80 } LB_DESCR;
83 #define IS_OWNERDRAW(descr) \
84 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
86 #define HAS_STRINGS(descr) \
87 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
90 #define IS_MULTISELECT(descr) \
91 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
93 #define SEND_NOTIFICATION(wnd,descr,code) \
94 (SendMessageA( (descr)->owner, WM_COMMAND, \
95 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
97 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
99 /* Current timer status */
100 typedef enum
102 LB_TIMER_NONE,
103 LB_TIMER_UP,
104 LB_TIMER_LEFT,
105 LB_TIMER_DOWN,
106 LB_TIMER_RIGHT
107 } TIMER_DIRECTION;
109 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
112 /***********************************************************************
113 * LISTBOX_Dump
115 void LISTBOX_Dump( WND *wnd )
117 INT i;
118 LB_ITEMDATA *item;
119 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
121 TRACE( "Listbox:\n" );
122 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
123 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
124 descr->top_item );
125 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
127 TRACE( "%4d: %-40s %d %08lx %3d\n",
128 i, item->str, item->selected, item->data, item->height );
133 /***********************************************************************
134 * LISTBOX_GetCurrentPageSize
136 * Return the current page size
138 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
140 INT i, height;
141 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
142 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
144 if ((height += descr->items[i].height) > descr->height) break;
146 if (i == descr->top_item) return 1;
147 else return i - descr->top_item;
151 /***********************************************************************
152 * LISTBOX_GetMaxTopIndex
154 * Return the maximum possible index for the top of the listbox.
156 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
158 INT max, page;
160 if (descr->style & LBS_OWNERDRAWVARIABLE)
162 page = descr->height;
163 for (max = descr->nb_items - 1; max >= 0; max--)
164 if ((page -= descr->items[max].height) < 0) break;
165 if (max < descr->nb_items - 1) max++;
167 else if (descr->style & LBS_MULTICOLUMN)
169 if ((page = descr->width / descr->column_width) < 1) page = 1;
170 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
171 max = (max - page) * descr->page_size;
173 else
175 max = descr->nb_items - descr->page_size;
177 if (max < 0) max = 0;
178 return max;
182 /***********************************************************************
183 * LISTBOX_UpdateScroll
185 * Update the scrollbars. Should be called whenever the content
186 * of the listbox changes.
188 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
190 SCROLLINFO info;
192 /* Check the listbox scroll bar flags individually before we call
193 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
194 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
195 scroll bar when we do not need one.
196 if (!(descr->style & WS_VSCROLL)) return;
199 /* It is important that we check descr->style, and not wnd->dwStyle,
200 for WS_VSCROLL, as the former is exactly the one passed in
201 argument to CreateWindow.
202 In Windows (and from now on in Wine :) a listbox created
203 with such a style (no WS_SCROLL) does not update
204 the scrollbar with listbox-related data, thus letting
205 the programmer use it for his/her own purposes. */
207 if (descr->style & LBS_NOREDRAW) return;
208 info.cbSize = sizeof(info);
210 if (descr->style & LBS_MULTICOLUMN)
212 info.nMin = 0;
213 info.nMax = (descr->nb_items - 1) / descr->page_size;
214 info.nPos = descr->top_item / descr->page_size;
215 info.nPage = descr->width / descr->column_width;
216 if (info.nPage < 1) info.nPage = 1;
217 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
218 if (descr->style & LBS_DISABLENOSCROLL)
219 info.fMask |= SIF_DISABLENOSCROLL;
220 if (descr->style & WS_HSCROLL)
221 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
222 info.nMax = 0;
223 info.fMask = SIF_RANGE;
224 if (descr->style & WS_VSCROLL)
225 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
227 else
229 info.nMin = 0;
230 info.nMax = descr->nb_items - 1;
231 info.nPos = descr->top_item;
232 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
233 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
234 if (descr->style & LBS_DISABLENOSCROLL)
235 info.fMask |= SIF_DISABLENOSCROLL;
236 if (descr->style & WS_VSCROLL)
237 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
239 if (descr->horz_extent)
241 info.nMin = 0;
242 info.nMax = descr->horz_extent - 1;
243 info.nPos = descr->horz_pos;
244 info.nPage = descr->width;
245 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
246 if (descr->style & LBS_DISABLENOSCROLL)
247 info.fMask |= SIF_DISABLENOSCROLL;
248 if (descr->style & WS_HSCROLL)
249 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
256 /***********************************************************************
257 * LISTBOX_SetTopItem
259 * Set the top item of the listbox, scrolling up or down if necessary.
261 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
262 BOOL scroll )
264 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
265 if (index > max) index = max;
266 if (index < 0) index = 0;
267 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
268 if (descr->top_item == index) return LB_OKAY;
269 if (descr->style & LBS_MULTICOLUMN)
271 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
272 if (scroll && (abs(diff) < descr->width))
273 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
274 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
276 else
277 scroll = FALSE;
279 else if (scroll)
281 INT diff;
282 if (descr->style & LBS_OWNERDRAWVARIABLE)
284 INT i;
285 diff = 0;
286 if (index > descr->top_item)
288 for (i = index - 1; i >= descr->top_item; i--)
289 diff -= descr->items[i].height;
291 else
293 for (i = index; i < descr->top_item; i++)
294 diff += descr->items[i].height;
297 else
298 diff = (descr->top_item - index) * descr->item_height;
300 if (abs(diff) < descr->height)
301 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
302 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
303 else
304 scroll = FALSE;
306 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
307 descr->top_item = index;
308 LISTBOX_UpdateScroll( wnd, descr );
309 return LB_OKAY;
313 /***********************************************************************
314 * LISTBOX_UpdatePage
316 * Update the page size. Should be called when the size of
317 * the client area or the item height changes.
319 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
321 INT page_size;
323 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
324 page_size = 1;
325 if (page_size == descr->page_size) return;
326 descr->page_size = page_size;
327 if (descr->style & LBS_MULTICOLUMN)
328 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
329 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
333 /***********************************************************************
334 * LISTBOX_UpdateSize
336 * Update the size of the listbox. Should be called when the size of
337 * the client area changes.
339 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
341 RECT rect;
343 GetClientRect( wnd->hwndSelf, &rect );
344 descr->width = rect.right - rect.left;
345 descr->height = rect.bottom - rect.top;
346 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
348 if ((descr->height > descr->item_height) &&
349 (descr->height % descr->item_height))
351 TRACE("[%04x]: changing height %d -> %d\n",
352 wnd->hwndSelf, descr->height,
353 descr->height - descr->height%descr->item_height );
354 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
355 wnd->rectWindow.right - wnd->rectWindow.left,
356 wnd->rectWindow.bottom - wnd->rectWindow.top -
357 (descr->height % descr->item_height),
358 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
359 return;
362 TRACE("[%04x]: new size = %d,%d\n",
363 wnd->hwndSelf, descr->width, descr->height );
364 LISTBOX_UpdatePage( wnd, descr );
365 LISTBOX_UpdateScroll( wnd, descr );
369 /***********************************************************************
370 * LISTBOX_GetItemRect
372 * Get the rectangle enclosing an item, in listbox client coordinates.
373 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
375 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
376 RECT *rect )
378 /* Index <= 0 is legal even on empty listboxes */
379 if (index && (index >= descr->nb_items)) return -1;
380 SetRect( rect, 0, 0, descr->width, descr->height );
381 if (descr->style & LBS_MULTICOLUMN)
383 INT col = (index / descr->page_size) -
384 (descr->top_item / descr->page_size);
385 rect->left += col * descr->column_width;
386 rect->right = rect->left + descr->column_width;
387 rect->top += (index % descr->page_size) * descr->item_height;
388 rect->bottom = rect->top + descr->item_height;
390 else if (descr->style & LBS_OWNERDRAWVARIABLE)
392 INT i;
393 rect->right += descr->horz_pos;
394 if ((index >= 0) && (index < descr->nb_items))
396 if (index < descr->top_item)
398 for (i = descr->top_item-1; i >= index; i--)
399 rect->top -= descr->items[i].height;
401 else
403 for (i = descr->top_item; i < index; i++)
404 rect->top += descr->items[i].height;
406 rect->bottom = rect->top + descr->items[index].height;
410 else
412 rect->top += (index - descr->top_item) * descr->item_height;
413 rect->bottom = rect->top + descr->item_height;
414 rect->right += descr->horz_pos;
417 return ((rect->left < descr->width) && (rect->right > 0) &&
418 (rect->top < descr->height) && (rect->bottom > 0));
422 /***********************************************************************
423 * LISTBOX_GetItemFromPoint
425 * Return the item nearest from point (x,y) (in client coordinates).
427 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
428 INT x, INT y )
430 INT index = descr->top_item;
432 if (!descr->nb_items) return -1; /* No items */
433 if (descr->style & LBS_OWNERDRAWVARIABLE)
435 INT pos = 0;
436 if (y >= 0)
438 while (index < descr->nb_items)
440 if ((pos += descr->items[index].height) > y) break;
441 index++;
444 else
446 while (index > 0)
448 index--;
449 if ((pos -= descr->items[index].height) <= y) break;
453 else if (descr->style & LBS_MULTICOLUMN)
455 if (y >= descr->item_height * descr->page_size) return -1;
456 if (y >= 0) index += y / descr->item_height;
457 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
458 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
460 else
462 index += (y / descr->item_height);
464 if (index < 0) return 0;
465 if (index >= descr->nb_items) return -1;
466 return index;
470 /***********************************************************************
471 * LISTBOX_PaintItem
473 * Paint an item.
475 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
476 const RECT *rect, INT index, UINT action )
478 LB_ITEMDATA *item = NULL;
479 if (index < descr->nb_items) item = &descr->items[index];
481 if (IS_OWNERDRAW(descr))
483 DRAWITEMSTRUCT dis;
485 if (!item)
487 if (action == ODA_FOCUS)
488 DrawFocusRect( hdc, rect );
489 else
490 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
491 return;
493 dis.CtlType = ODT_LISTBOX;
494 dis.CtlID = wnd->wIDmenu;
495 dis.hwndItem = wnd->hwndSelf;
496 dis.itemAction = action;
497 dis.hDC = hdc;
498 dis.itemID = index;
499 dis.itemState = 0;
500 if (item && item->selected) dis.itemState |= ODS_SELECTED;
501 if ((descr->focus_item == index) &&
502 (descr->caret_on) &&
503 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
504 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
505 dis.itemData = item ? item->data : 0;
506 dis.rcItem = *rect;
507 TRACE("[%04x]: drawitem %d (%s) action=%02x "
508 "state=%02x rect=%d,%d-%d,%d\n",
509 wnd->hwndSelf, index, item ? item->str : "", action,
510 dis.itemState, rect->left, rect->top,
511 rect->right, rect->bottom );
512 SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
514 else
516 COLORREF oldText = 0, oldBk = 0;
518 if (action == ODA_FOCUS)
520 DrawFocusRect( hdc, rect );
521 return;
523 if (item && item->selected)
525 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
526 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
529 TRACE("[%04x]: painting %d (%s) action=%02x "
530 "rect=%d,%d-%d,%d\n",
531 wnd->hwndSelf, index, item ? item->str : "", action,
532 rect->left, rect->top, rect->right, rect->bottom );
533 if (!item)
534 ExtTextOutA( hdc, rect->left + 1, rect->top,
535 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
536 else if (!(descr->style & LBS_USETABSTOPS))
537 ExtTextOutA( hdc, rect->left + 1, rect->top,
538 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
539 strlen(item->str), NULL );
540 else
542 /* Output empty string to paint background in the full width. */
543 ExtTextOutA( hdc, rect->left + 1, rect->top,
544 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
545 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
546 item->str, strlen(item->str),
547 descr->nb_tabs, descr->tabs, 0);
549 if (item && item->selected)
551 SetBkColor( hdc, oldBk );
552 SetTextColor( hdc, oldText );
554 if ((descr->focus_item == index) &&
555 (descr->caret_on) &&
556 (descr->in_focus)) DrawFocusRect( hdc, rect );
561 /***********************************************************************
562 * LISTBOX_SetRedraw
564 * Change the redraw flag.
566 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
568 if (on)
570 if (!(descr->style & LBS_NOREDRAW)) return;
571 descr->style &= ~LBS_NOREDRAW;
572 LISTBOX_UpdateScroll( wnd, descr );
574 else descr->style |= LBS_NOREDRAW;
578 /***********************************************************************
579 * LISTBOX_RepaintItem
581 * Repaint a single item synchronously.
583 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
584 UINT action )
586 HDC hdc;
587 RECT rect;
588 HFONT oldFont = 0;
589 HBRUSH hbrush, oldBrush = 0;
591 /* Do not repaint the item if the item is not visible */
592 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
594 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
595 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
596 if (descr->font) oldFont = SelectObject( hdc, descr->font );
597 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
598 hdc, (LPARAM)wnd->hwndSelf );
599 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
600 if (wnd->dwStyle & WS_DISABLED)
601 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
602 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
603 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
604 if (oldFont) SelectObject( hdc, oldFont );
605 if (oldBrush) SelectObject( hdc, oldBrush );
606 ReleaseDC( wnd->hwndSelf, hdc );
610 /***********************************************************************
611 * LISTBOX_InitStorage
613 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
614 DWORD bytes )
616 LB_ITEMDATA *item;
618 nb_items += LB_ARRAY_GRANULARITY - 1;
619 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
620 if (descr->items)
621 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
622 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
623 nb_items * sizeof(LB_ITEMDATA) )))
625 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
626 return LB_ERRSPACE;
628 descr->items = item;
629 return LB_OKAY;
633 /***********************************************************************
634 * LISTBOX_SetTabStops
636 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
637 LPINT tabs, BOOL short_ints )
639 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
640 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
641 if (!(descr->nb_tabs = count))
643 descr->tabs = NULL;
644 return TRUE;
646 /* FIXME: count = 1 */
647 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
648 descr->nb_tabs * sizeof(INT) )))
649 return FALSE;
650 if (short_ints)
652 INT i;
653 LPINT16 p = (LPINT16)tabs;
655 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
656 for (i = 0; i < descr->nb_tabs; i++) {
657 descr->tabs[i] = *p++<<1; /* FIXME */
658 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
660 if (TRACE_ON(listbox)) DPRINTF("\n");
662 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
663 /* FIXME: repaint the window? */
664 return TRUE;
668 /***********************************************************************
669 * LISTBOX_GetText
671 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
672 LPSTR buffer )
674 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
675 if (HAS_STRINGS(descr))
677 if (!buffer)
678 return strlen(descr->items[index].str);
679 lstrcpyA( buffer, descr->items[index].str );
680 return strlen(buffer);
681 } else {
682 if (buffer)
683 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
684 return sizeof(DWORD);
689 /***********************************************************************
690 * LISTBOX_FindStringPos
692 * Find the nearest string located before a given string in sort order.
693 * If 'exact' is TRUE, return an error if we don't get an exact match.
695 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
696 BOOL exact )
698 INT index, min, max, res = -1;
700 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
701 min = 0;
702 max = descr->nb_items;
703 while (min != max)
705 index = (min + max) / 2;
706 if (HAS_STRINGS(descr))
707 res = lstrcmpiA( descr->items[index].str, str );
708 else
710 COMPAREITEMSTRUCT cis;
712 cis.CtlType = ODT_LISTBOX;
713 cis.CtlID = wnd->wIDmenu;
714 cis.hwndItem = wnd->hwndSelf;
715 cis.itemID1 = index;
716 cis.itemData1 = descr->items[index].data;
717 cis.itemID2 = -1;
718 cis.itemData2 = (DWORD)str;
719 cis.dwLocaleId = descr->locale;
720 res = SendMessageA( descr->owner, WM_COMPAREITEM,
721 wnd->wIDmenu, (LPARAM)&cis );
723 if (!res) return index;
724 if (res > 0) max = index;
725 else min = index + 1;
727 return exact ? -1 : max;
731 /***********************************************************************
732 * LISTBOX_FindFileStrPos
734 * Find the nearest string located before a given string in directory
735 * sort order (i.e. first files, then directories, then drives).
737 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
739 INT min, max, res = -1;
741 if (!HAS_STRINGS(descr))
742 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
743 min = 0;
744 max = descr->nb_items;
745 while (min != max)
747 INT index = (min + max) / 2;
748 const char *p = descr->items[index].str;
749 if (*p == '[') /* drive or directory */
751 if (*str != '[') res = -1;
752 else if (p[1] == '-') /* drive */
754 if (str[1] == '-') res = str[2] - p[2];
755 else res = -1;
757 else /* directory */
759 if (str[1] == '-') res = 1;
760 else res = lstrcmpiA( str, p );
763 else /* filename */
765 if (*str == '[') res = 1;
766 else res = lstrcmpiA( str, p );
768 if (!res) return index;
769 if (res < 0) max = index;
770 else min = index + 1;
772 return max;
776 /***********************************************************************
777 * LISTBOX_FindString
779 * Find the item beginning with a given string.
781 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
782 LPCSTR str, BOOL exact )
784 INT i;
785 LB_ITEMDATA *item;
787 if (start >= descr->nb_items) start = -1;
788 item = descr->items + start + 1;
789 if (HAS_STRINGS(descr))
791 if (!str || ! str[0] ) return LB_ERR;
792 if (exact)
794 for (i = start + 1; i < descr->nb_items; i++, item++)
795 if (!lstrcmpiA( str, item->str )) return i;
796 for (i = 0, item = descr->items; i <= start; i++, item++)
797 if (!lstrcmpiA( str, item->str )) return i;
799 else
801 /* Special case for drives and directories: ignore prefix */
802 #define CHECK_DRIVE(item) \
803 if ((item)->str[0] == '[') \
805 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
806 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
807 return i; \
810 INT len = strlen(str);
811 for (i = start + 1; i < descr->nb_items; i++, item++)
813 if (!lstrncmpiA( str, item->str, len )) return i;
814 CHECK_DRIVE(item);
816 for (i = 0, item = descr->items; i <= start; i++, item++)
818 if (!lstrncmpiA( str, item->str, len )) return i;
819 CHECK_DRIVE(item);
821 #undef CHECK_DRIVE
824 else
826 if (exact && (descr->style & LBS_SORT))
827 /* If sorted, use a WM_COMPAREITEM binary search */
828 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
830 /* Otherwise use a linear search */
831 for (i = start + 1; i < descr->nb_items; i++, item++)
832 if (item->data == (DWORD)str) return i;
833 for (i = 0, item = descr->items; i <= start; i++, item++)
834 if (item->data == (DWORD)str) return i;
836 return LB_ERR;
840 /***********************************************************************
841 * LISTBOX_GetSelCount
843 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
845 INT i, count;
846 LB_ITEMDATA *item = descr->items;
848 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
849 for (i = count = 0; i < descr->nb_items; i++, item++)
850 if (item->selected) count++;
851 return count;
855 /***********************************************************************
856 * LISTBOX_GetSelItems16
858 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
859 LPINT16 array )
861 INT i, count;
862 LB_ITEMDATA *item = descr->items;
864 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
865 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
866 if (item->selected) array[count++] = (INT16)i;
867 return count;
871 /***********************************************************************
872 * LISTBOX_GetSelItems32
874 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
875 LPINT array )
877 INT i, count;
878 LB_ITEMDATA *item = descr->items;
880 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
881 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
882 if (item->selected) array[count++] = i;
883 return count;
887 /***********************************************************************
888 * LISTBOX_Paint
890 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
892 INT i, col_pos = descr->page_size - 1;
893 RECT rect;
894 RECT focusRect = {-1, -1, -1, -1};
895 HFONT oldFont = 0;
896 HBRUSH hbrush, oldBrush = 0;
897 INT focusItem;
899 if (descr->style & LBS_NOREDRAW) return 0;
901 SetRect( &rect, 0, 0, descr->width, descr->height );
902 if (descr->style & LBS_MULTICOLUMN)
903 rect.right = rect.left + descr->column_width;
904 else if (descr->horz_pos)
906 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
907 rect.right += descr->horz_pos;
910 if (descr->font) oldFont = SelectObject( hdc, descr->font );
911 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
912 hdc, (LPARAM)wnd->hwndSelf );
913 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
914 if (wnd->dwStyle & WS_DISABLED)
915 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
917 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
918 (descr->in_focus))
920 /* Special case for empty listbox: paint focus rect */
921 rect.bottom = rect.top + descr->item_height;
922 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
923 ODA_FOCUS );
924 rect.top = rect.bottom;
927 /* Paint all the item, regarding the selection
928 Focus state will be painted after */
929 focusItem = descr->focus_item;
930 descr->focus_item = -1;
932 for (i = descr->top_item; i < descr->nb_items; i++)
934 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
935 rect.bottom = rect.top + descr->item_height;
936 else
937 rect.bottom = rect.top + descr->items[i].height;
939 if (i == focusItem)
941 /* keep the focus rect, to paint the focus item after */
942 focusRect.left = rect.left;
943 focusRect.right = rect.right;
944 focusRect.top = rect.top;
945 focusRect.bottom = rect.bottom;
947 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
948 rect.top = rect.bottom;
950 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
952 if (!IS_OWNERDRAW(descr))
954 /* Clear the bottom of the column */
955 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
956 if (rect.top < descr->height)
958 rect.bottom = descr->height;
959 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
960 &rect, NULL, 0, NULL );
964 /* Go to the next column */
965 rect.left += descr->column_width;
966 rect.right += descr->column_width;
967 rect.top = 0;
968 col_pos = descr->page_size - 1;
970 else
972 col_pos--;
973 if (rect.top >= descr->height) break;
977 /* Paint the focus item now */
978 descr->focus_item = focusItem;
979 if (focusRect.top != focusRect.bottom && descr->caret_on)
980 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS );
982 if (!IS_OWNERDRAW(descr))
984 /* Clear the remainder of the client area */
985 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
986 if (rect.top < descr->height)
988 rect.bottom = descr->height;
989 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
990 &rect, NULL, 0, NULL );
992 if (rect.right < descr->width)
994 rect.left = rect.right;
995 rect.right = descr->width;
996 rect.top = 0;
997 rect.bottom = descr->height;
998 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
999 &rect, NULL, 0, NULL );
1002 if (oldFont) SelectObject( hdc, oldFont );
1003 if (oldBrush) SelectObject( hdc, oldBrush );
1004 return 0;
1008 /***********************************************************************
1009 * LISTBOX_InvalidateItems
1011 * Invalidate all items from a given item. If the specified item is not
1012 * visible, nothing happens.
1014 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1016 RECT rect;
1018 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1020 rect.bottom = descr->height;
1021 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1022 if (descr->style & LBS_MULTICOLUMN)
1024 /* Repaint the other columns */
1025 rect.left = rect.right;
1026 rect.right = descr->width;
1027 rect.top = 0;
1028 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1034 /***********************************************************************
1035 * LISTBOX_GetItemHeight
1037 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1039 if (descr->style & LBS_OWNERDRAWVARIABLE)
1041 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1042 return descr->items[index].height;
1044 else return descr->item_height;
1048 /***********************************************************************
1049 * LISTBOX_SetItemHeight
1051 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1052 UINT height )
1054 if (!height) height = 1;
1056 if (descr->style & LBS_OWNERDRAWVARIABLE)
1058 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1059 TRACE("[%04x]: item %d height = %d\n",
1060 wnd->hwndSelf, index, height );
1061 descr->items[index].height = height;
1062 LISTBOX_UpdateScroll( wnd, descr );
1063 LISTBOX_InvalidateItems( wnd, descr, index );
1065 else if (height != descr->item_height)
1067 TRACE("[%04x]: new height = %d\n",
1068 wnd->hwndSelf, height );
1069 descr->item_height = height;
1070 LISTBOX_UpdatePage( wnd, descr );
1071 LISTBOX_UpdateScroll( wnd, descr );
1072 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1074 return LB_OKAY;
1078 /***********************************************************************
1079 * LISTBOX_SetHorizontalPos
1081 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1083 INT diff;
1085 if (pos > descr->horz_extent - descr->width)
1086 pos = descr->horz_extent - descr->width;
1087 if (pos < 0) pos = 0;
1088 if (!(diff = descr->horz_pos - pos)) return;
1089 TRACE("[%04x]: new horz pos = %d\n",
1090 wnd->hwndSelf, pos );
1091 descr->horz_pos = pos;
1092 LISTBOX_UpdateScroll( wnd, descr );
1093 if (abs(diff) < descr->width)
1094 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1095 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1096 else
1097 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1101 /***********************************************************************
1102 * LISTBOX_SetHorizontalExtent
1104 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1105 UINT extent )
1107 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1108 return LB_OKAY;
1109 if (extent <= 0) extent = 1;
1110 if (extent == descr->horz_extent) return LB_OKAY;
1111 TRACE("[%04x]: new horz extent = %d\n",
1112 wnd->hwndSelf, extent );
1113 descr->horz_extent = extent;
1114 if (descr->horz_pos > extent - descr->width)
1115 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1116 else
1117 LISTBOX_UpdateScroll( wnd, descr );
1118 return LB_OKAY;
1122 /***********************************************************************
1123 * LISTBOX_SetColumnWidth
1125 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1127 if (width == descr->column_width) return LB_OKAY;
1128 TRACE("[%04x]: new column width = %d\n",
1129 wnd->hwndSelf, width );
1130 descr->column_width = width;
1131 LISTBOX_UpdatePage( wnd, descr );
1132 return LB_OKAY;
1136 /***********************************************************************
1137 * LISTBOX_SetFont
1139 * Returns the item height.
1141 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1143 HDC hdc;
1144 HFONT oldFont = 0;
1145 TEXTMETRICA tm;
1147 descr->font = font;
1149 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1151 ERR("unable to get DC.\n" );
1152 return 16;
1154 if (font) oldFont = SelectObject( hdc, font );
1155 GetTextMetricsA( hdc, &tm );
1156 if (oldFont) SelectObject( hdc, oldFont );
1157 ReleaseDC( wnd->hwndSelf, hdc );
1158 if (!IS_OWNERDRAW(descr))
1159 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1160 return tm.tmHeight ;
1164 /***********************************************************************
1165 * LISTBOX_MakeItemVisible
1167 * Make sure that a given item is partially or fully visible.
1169 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1170 BOOL fully )
1172 INT top;
1174 if (index <= descr->top_item) top = index;
1175 else if (descr->style & LBS_MULTICOLUMN)
1177 INT cols = descr->width;
1178 if (!fully) cols += descr->column_width - 1;
1179 if (cols >= descr->column_width) cols /= descr->column_width;
1180 else cols = 1;
1181 if (index < descr->top_item + (descr->page_size * cols)) return;
1182 top = index - descr->page_size * (cols - 1);
1184 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1186 INT height = fully ? descr->items[index].height : 1;
1187 for (top = index; top > descr->top_item; top--)
1188 if ((height += descr->items[top-1].height) > descr->height) break;
1190 else
1192 if (index < descr->top_item + descr->page_size) return;
1193 if (!fully && (index == descr->top_item + descr->page_size) &&
1194 (descr->height > (descr->page_size * descr->item_height))) return;
1195 top = index - descr->page_size + 1;
1197 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1201 /***********************************************************************
1202 * LISTBOX_SelectItemRange
1204 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1206 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1207 INT last, BOOL on )
1209 INT i;
1211 /* A few sanity checks */
1213 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1214 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1215 if (last == -1) last = descr->nb_items - 1;
1216 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1217 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1218 /* selected_item reflects last selected/unselected item on multiple sel */
1219 descr->selected_item = last;
1221 if (on) /* Turn selection on */
1223 for (i = first; i <= last; i++)
1225 if (descr->items[i].selected) continue;
1226 descr->items[i].selected = TRUE;
1227 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1230 else /* Turn selection off */
1232 for (i = first; i <= last; i++)
1234 if (!descr->items[i].selected) continue;
1235 descr->items[i].selected = FALSE;
1236 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1239 return LB_OKAY;
1243 /***********************************************************************
1244 * LISTBOX_SetCaretIndex
1246 * NOTES
1247 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1250 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1251 BOOL fully_visible )
1253 INT oldfocus = descr->focus_item;
1255 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1256 if (index == oldfocus) return LB_OKAY;
1257 descr->focus_item = index;
1258 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1259 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1261 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1262 if (descr->caret_on && (descr->in_focus))
1263 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1265 return LB_OKAY;
1269 /***********************************************************************
1270 * LISTBOX_SetSelection
1272 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1273 BOOL on, BOOL send_notify )
1275 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1277 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1278 if (descr->style & LBS_MULTIPLESEL)
1280 if (index == -1) /* Select all items */
1281 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1282 else /* Only one item */
1283 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1285 else
1287 INT oldsel = descr->selected_item;
1288 if (index == oldsel) return LB_OKAY;
1289 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1290 if (index != -1) descr->items[index].selected = TRUE;
1291 descr->selected_item = index;
1292 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1293 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1294 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1295 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1296 else
1297 if( descr->lphc ) /* set selection change flag for parent combo */
1298 descr->lphc->wState |= CBF_SELCHANGE;
1300 return LB_OKAY;
1304 /***********************************************************************
1305 * LISTBOX_MoveCaret
1307 * Change the caret position and extend the selection to the new caret.
1309 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1310 BOOL fully_visible )
1312 INT oldfocus = descr->focus_item;
1314 if ((index < 0) || (index >= descr->nb_items))
1315 return;
1317 /* Important, repaint needs to be done in this order if
1318 you want to mimic Windows behavior:
1319 1. Remove the focus and paint the item
1320 2. Remove the selection and paint the item(s)
1321 3. Set the selection and repaint the item(s)
1322 4. Set the focus to 'index' and repaint the item */
1324 /* 1. remove the focus and repaint the item */
1325 descr->focus_item = -1;
1326 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1327 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1329 /* 2. then turn off the previous selection */
1330 /* 3. repaint the new selected item */
1331 if (descr->style & LBS_EXTENDEDSEL)
1333 if (descr->anchor_item != -1)
1335 INT first = min( index, descr->anchor_item );
1336 INT last = max( index, descr->anchor_item );
1337 if (first > 0)
1338 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1339 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1340 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1343 else if (!(descr->style & LBS_MULTIPLESEL))
1345 /* Set selection to new caret item */
1346 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1349 /* 4. repaint the new item with the focus */
1350 descr->focus_item = index;
1351 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1352 if (descr->caret_on && (descr->in_focus))
1353 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1357 /***********************************************************************
1358 * LISTBOX_InsertItem
1360 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1361 LPSTR str, DWORD data )
1363 LB_ITEMDATA *item;
1364 INT max_items;
1365 INT oldfocus = descr->focus_item;
1367 if (index == -1) index = descr->nb_items;
1368 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1369 if (!descr->items) max_items = 0;
1370 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1371 if (descr->nb_items == max_items)
1373 /* We need to grow the array */
1374 max_items += LB_ARRAY_GRANULARITY;
1375 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1376 max_items * sizeof(LB_ITEMDATA) )))
1378 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1379 return LB_ERRSPACE;
1381 descr->items = item;
1384 /* Insert the item structure */
1386 item = &descr->items[index];
1387 if (index < descr->nb_items)
1388 RtlMoveMemory( item + 1, item,
1389 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1390 item->str = str;
1391 item->data = data;
1392 item->height = 0;
1393 item->selected = FALSE;
1394 descr->nb_items++;
1396 /* Get item height */
1398 if (descr->style & LBS_OWNERDRAWVARIABLE)
1400 MEASUREITEMSTRUCT mis;
1402 mis.CtlType = ODT_LISTBOX;
1403 mis.CtlID = wnd->wIDmenu;
1404 mis.itemID = index;
1405 mis.itemData = descr->items[index].data;
1406 mis.itemHeight = descr->item_height;
1407 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1408 item->height = mis.itemHeight ? mis.itemHeight : 1;
1409 TRACE("[%04x]: measure item %d (%s) = %d\n",
1410 wnd->hwndSelf, index, str ? str : "", item->height );
1413 /* Repaint the items */
1415 LISTBOX_UpdateScroll( wnd, descr );
1416 LISTBOX_InvalidateItems( wnd, descr, index );
1418 /* Move selection and focused item */
1419 /* If listbox was empty, set focus to the first item */
1420 if (descr->nb_items == 1)
1421 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1422 /* single select don't change selection index in win31 */
1423 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1425 descr->selected_item++;
1426 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1428 else
1430 if (index <= descr->selected_item)
1432 descr->selected_item++;
1433 descr->focus_item = oldfocus; /* focus not changed */
1436 return LB_OKAY;
1440 /***********************************************************************
1441 * LISTBOX_InsertString
1443 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1444 LPCSTR str )
1446 LPSTR new_str = NULL;
1447 DWORD data = 0;
1448 LRESULT ret;
1450 if (HAS_STRINGS(descr))
1452 if (!str) str="";
1453 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1455 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1456 return LB_ERRSPACE;
1459 else data = (DWORD)str;
1461 if (index == -1) index = descr->nb_items;
1462 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1464 if (new_str) HeapFree( descr->heap, 0, new_str );
1465 return ret;
1468 TRACE("[%04x]: added item %d '%s'\n",
1469 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1470 return index;
1474 /***********************************************************************
1475 * LISTBOX_DeleteItem
1477 * Delete the content of an item. 'index' must be a valid index.
1479 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1481 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1482 * while Win95 sends it for all items with user data.
1483 * It's probably better to send it too often than not
1484 * often enough, so this is what we do here.
1486 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1488 DELETEITEMSTRUCT dis;
1490 dis.CtlType = ODT_LISTBOX;
1491 dis.CtlID = wnd->wIDmenu;
1492 dis.itemID = index;
1493 dis.hwndItem = wnd->hwndSelf;
1494 dis.itemData = descr->items[index].data;
1495 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1497 if (HAS_STRINGS(descr) && descr->items[index].str)
1498 HeapFree( descr->heap, 0, descr->items[index].str );
1502 /***********************************************************************
1503 * LISTBOX_RemoveItem
1505 * Remove an item from the listbox and delete its content.
1507 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1509 LB_ITEMDATA *item;
1510 INT max_items;
1512 if (index == -1) index = descr->nb_items - 1;
1513 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1515 /* We need to invalidate the original rect instead of the updated one. */
1516 LISTBOX_InvalidateItems( wnd, descr, index );
1518 LISTBOX_DeleteItem( wnd, descr, index );
1520 /* Remove the item */
1522 item = &descr->items[index];
1523 if (index < descr->nb_items-1)
1524 RtlMoveMemory( item, item + 1,
1525 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1526 descr->nb_items--;
1527 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1529 /* Shrink the item array if possible */
1531 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1532 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1534 max_items -= LB_ARRAY_GRANULARITY;
1535 item = HeapReAlloc( descr->heap, 0, descr->items,
1536 max_items * sizeof(LB_ITEMDATA) );
1537 if (item) descr->items = item;
1539 /* Repaint the items */
1541 LISTBOX_UpdateScroll( wnd, descr );
1542 /* if we removed the scrollbar, reset the top of the list
1543 (correct for owner-drawn ???) */
1544 if (descr->nb_items == descr->page_size)
1545 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1547 /* Move selection and focused item */
1548 if (!IS_MULTISELECT(descr))
1550 if (index == descr->selected_item)
1551 descr->selected_item = -1;
1552 else if (index < descr->selected_item)
1554 descr->selected_item--;
1555 if (ISWIN31) /* win 31 do not change the selected item number */
1556 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1560 if (descr->focus_item >= descr->nb_items)
1562 descr->focus_item = descr->nb_items - 1;
1563 if (descr->focus_item < 0) descr->focus_item = 0;
1565 return LB_OKAY;
1569 /***********************************************************************
1570 * LISTBOX_ResetContent
1572 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1574 INT i;
1576 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1577 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1578 descr->nb_items = 0;
1579 descr->top_item = 0;
1580 descr->selected_item = -1;
1581 descr->focus_item = 0;
1582 descr->anchor_item = -1;
1583 descr->items = NULL;
1584 LISTBOX_UpdateScroll( wnd, descr );
1585 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1589 /***********************************************************************
1590 * LISTBOX_SetCount
1592 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1594 LRESULT ret;
1596 if (HAS_STRINGS(descr)) return LB_ERR;
1597 /* FIXME: this is far from optimal... */
1598 if (count > descr->nb_items)
1600 while (count > descr->nb_items)
1601 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1602 return ret;
1604 else if (count < descr->nb_items)
1606 while (count < descr->nb_items)
1607 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1608 return ret;
1610 return LB_OKAY;
1614 /***********************************************************************
1615 * LISTBOX_Directory
1617 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1618 LPCSTR filespec, BOOL long_names )
1620 HANDLE handle;
1621 LRESULT ret = LB_OKAY;
1622 WIN32_FIND_DATAA entry;
1623 int pos;
1625 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1627 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1629 else
1633 char buffer[270];
1634 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1636 if (!(attrib & DDL_DIRECTORY) ||
1637 !strcmp( entry.cAlternateFileName, "." )) continue;
1638 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1639 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1641 else /* not a directory */
1643 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1644 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1646 if ((attrib & DDL_EXCLUSIVE) &&
1647 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1648 continue;
1649 #undef ATTRIBS
1650 if (long_names) strcpy( buffer, entry.cFileName );
1651 else strcpy( buffer, entry.cAlternateFileName );
1653 if (!long_names) CharLowerA( buffer );
1654 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1655 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1656 break;
1657 } while (FindNextFileA( handle, &entry ));
1658 FindClose( handle );
1661 if ((ret >= 0) && (attrib & DDL_DRIVES))
1663 char buffer[] = "[-a-]";
1664 int drive;
1665 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1667 if (!DRIVE_IsValid(drive)) continue;
1668 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1669 break;
1672 return ret;
1676 /***********************************************************************
1677 * LISTBOX_HandleVScroll
1679 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1680 WPARAM wParam, LPARAM lParam )
1682 SCROLLINFO info;
1684 if (descr->style & LBS_MULTICOLUMN) return 0;
1685 switch(LOWORD(wParam))
1687 case SB_LINEUP:
1688 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1689 break;
1690 case SB_LINEDOWN:
1691 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1692 break;
1693 case SB_PAGEUP:
1694 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1695 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1696 break;
1697 case SB_PAGEDOWN:
1698 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1699 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1700 break;
1701 case SB_THUMBPOSITION:
1702 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1703 break;
1704 case SB_THUMBTRACK:
1705 info.cbSize = sizeof(info);
1706 info.fMask = SIF_TRACKPOS;
1707 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1708 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1709 break;
1710 case SB_TOP:
1711 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1712 break;
1713 case SB_BOTTOM:
1714 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1715 break;
1717 return 0;
1721 /***********************************************************************
1722 * LISTBOX_HandleHScroll
1724 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1725 WPARAM wParam, LPARAM lParam )
1727 SCROLLINFO info;
1728 INT page;
1730 if (descr->style & LBS_MULTICOLUMN)
1732 switch(LOWORD(wParam))
1734 case SB_LINELEFT:
1735 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1736 TRUE );
1737 break;
1738 case SB_LINERIGHT:
1739 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1740 TRUE );
1741 break;
1742 case SB_PAGELEFT:
1743 page = descr->width / descr->column_width;
1744 if (page < 1) page = 1;
1745 LISTBOX_SetTopItem( wnd, descr,
1746 descr->top_item - page * descr->page_size, TRUE );
1747 break;
1748 case SB_PAGERIGHT:
1749 page = descr->width / descr->column_width;
1750 if (page < 1) page = 1;
1751 LISTBOX_SetTopItem( wnd, descr,
1752 descr->top_item + page * descr->page_size, TRUE );
1753 break;
1754 case SB_THUMBPOSITION:
1755 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1756 TRUE );
1757 break;
1758 case SB_THUMBTRACK:
1759 info.cbSize = sizeof(info);
1760 info.fMask = SIF_TRACKPOS;
1761 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1762 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1763 TRUE );
1764 break;
1765 case SB_LEFT:
1766 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1767 break;
1768 case SB_RIGHT:
1769 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1770 break;
1773 else if (descr->horz_extent)
1775 switch(LOWORD(wParam))
1777 case SB_LINELEFT:
1778 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1779 break;
1780 case SB_LINERIGHT:
1781 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1782 break;
1783 case SB_PAGELEFT:
1784 LISTBOX_SetHorizontalPos( wnd, descr,
1785 descr->horz_pos - descr->width );
1786 break;
1787 case SB_PAGERIGHT:
1788 LISTBOX_SetHorizontalPos( wnd, descr,
1789 descr->horz_pos + descr->width );
1790 break;
1791 case SB_THUMBPOSITION:
1792 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1793 break;
1794 case SB_THUMBTRACK:
1795 info.cbSize = sizeof(info);
1796 info.fMask = SIF_TRACKPOS;
1797 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1798 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1799 break;
1800 case SB_LEFT:
1801 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1802 break;
1803 case SB_RIGHT:
1804 LISTBOX_SetHorizontalPos( wnd, descr,
1805 descr->horz_extent - descr->width );
1806 break;
1809 return 0;
1812 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1814 short gcWheelDelta = 0;
1815 UINT pulScrollLines = 3;
1817 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1819 gcWheelDelta -= (short) HIWORD(wParam);
1821 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1823 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1824 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1825 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1827 return 0;
1830 /***********************************************************************
1831 * LISTBOX_HandleLButtonDown
1833 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1834 WPARAM wParam, INT x, INT y )
1836 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1837 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1838 wnd->hwndSelf, x, y, index );
1839 if (!descr->caret_on && (descr->in_focus)) return 0;
1841 if (!descr->in_focus)
1843 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1844 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1845 : descr->lphc->self->hwndSelf );
1848 if (index != -1)
1850 if (descr->style & LBS_EXTENDEDSEL)
1852 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1853 if (wParam & MK_CONTROL)
1855 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1856 LISTBOX_SetSelection( wnd, descr, index,
1857 !descr->items[index].selected,
1858 (descr->style & LBS_NOTIFY) != 0);
1860 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1862 else
1864 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1865 LISTBOX_SetSelection( wnd, descr, index,
1866 (!(descr->style & LBS_MULTIPLESEL) ||
1867 !descr->items[index].selected),
1868 (descr->style & LBS_NOTIFY) != 0 );
1872 descr->captured = TRUE;
1873 SetCapture( wnd->hwndSelf );
1874 if (index != -1 && !descr->lphc)
1876 if (descr->style & LBS_NOTIFY )
1877 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1878 MAKELPARAM( x, y ) );
1879 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1881 POINT pt;
1883 pt.x = x;
1884 pt.y = y;
1886 if (DragDetect( wnd->hwndSelf, pt ))
1887 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1890 return 0;
1894 /*************************************************************************
1895 * LISTBOX_HandleLButtonDownCombo [Internal]
1897 * Process LButtonDown message for the ComboListBox
1899 * PARAMS
1900 * pWnd [I] The windows internal structure
1901 * pDescr [I] The ListBox internal structure
1902 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1903 * x [I] X Mouse Coordinate
1904 * y [I] Y Mouse Coordinate
1906 * RETURNS
1907 * 0 since we are processing the WM_LBUTTONDOWN Message
1909 * NOTES
1910 * This function is only to be used when a ListBox is a ComboListBox
1913 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1914 UINT msg, WPARAM wParam, INT x, INT y)
1916 RECT clientRect, screenRect;
1917 POINT mousePos;
1919 mousePos.x = x;
1920 mousePos.y = y;
1922 GetClientRect(pWnd->hwndSelf, &clientRect);
1924 if(PtInRect(&clientRect, mousePos))
1926 /* MousePos is in client, resume normal processing */
1927 if (msg == WM_LBUTTONDOWN)
1929 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
1930 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1932 else if (pDescr->style & LBS_NOTIFY)
1933 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1934 return 0;
1936 else
1938 POINT screenMousePos;
1939 HWND hWndOldCapture;
1941 /* Check the Non-Client Area */
1942 screenMousePos = mousePos;
1943 hWndOldCapture = GetCapture();
1944 ReleaseCapture();
1945 GetWindowRect(pWnd->hwndSelf, &screenRect);
1946 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1948 if(!PtInRect(&screenRect, screenMousePos))
1950 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
1951 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
1952 return 0;
1954 else
1956 /* Check to see the NC is a scrollbar */
1957 INT nHitTestType=0;
1958 /* Check Vertical scroll bar */
1959 if (pWnd->dwStyle & WS_VSCROLL)
1961 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1962 if (PtInRect( &clientRect, mousePos ))
1964 nHitTestType = HTVSCROLL;
1967 /* Check horizontal scroll bar */
1968 if (pWnd->dwStyle & WS_HSCROLL)
1970 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
1971 if (PtInRect( &clientRect, mousePos ))
1973 nHitTestType = HTHSCROLL;
1976 /* Windows sends this message when a scrollbar is clicked
1979 if(nHitTestType != 0)
1981 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
1982 MAKELONG(screenMousePos.x, screenMousePos.y));
1984 /* Resume the Capture after scrolling is complete
1986 if(hWndOldCapture != 0)
1988 SetCapture(hWndOldCapture);
1992 return 0;
1995 /***********************************************************************
1996 * LISTBOX_HandleLButtonUp
1998 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2000 if (LISTBOX_Timer != LB_TIMER_NONE)
2001 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2002 LISTBOX_Timer = LB_TIMER_NONE;
2003 if (descr->captured)
2005 descr->captured = FALSE;
2006 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2007 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2008 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2010 return 0;
2014 /***********************************************************************
2015 * LISTBOX_HandleTimer
2017 * Handle scrolling upon a timer event.
2018 * Return TRUE if scrolling should continue.
2020 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2021 INT index, TIMER_DIRECTION dir )
2023 switch(dir)
2025 case LB_TIMER_UP:
2026 if (descr->top_item) index = descr->top_item - 1;
2027 else index = 0;
2028 break;
2029 case LB_TIMER_LEFT:
2030 if (descr->top_item) index -= descr->page_size;
2031 break;
2032 case LB_TIMER_DOWN:
2033 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2034 if (index == descr->focus_item) index++;
2035 if (index >= descr->nb_items) index = descr->nb_items - 1;
2036 break;
2037 case LB_TIMER_RIGHT:
2038 if (index + descr->page_size < descr->nb_items)
2039 index += descr->page_size;
2040 break;
2041 case LB_TIMER_NONE:
2042 break;
2044 if (index == descr->focus_item) return FALSE;
2045 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2046 return TRUE;
2050 /***********************************************************************
2051 * LISTBOX_HandleSystemTimer
2053 * WM_SYSTIMER handler.
2055 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2057 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2059 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2060 LISTBOX_Timer = LB_TIMER_NONE;
2062 return 0;
2066 /***********************************************************************
2067 * LISTBOX_HandleMouseMove
2069 * WM_MOUSEMOVE handler.
2071 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2072 INT x, INT y )
2074 INT index;
2075 TIMER_DIRECTION dir = LB_TIMER_NONE;
2077 if (!descr->captured) return;
2079 if (descr->style & LBS_MULTICOLUMN)
2081 if (y < 0) y = 0;
2082 else if (y >= descr->item_height * descr->page_size)
2083 y = descr->item_height * descr->page_size - 1;
2085 if (x < 0)
2087 dir = LB_TIMER_LEFT;
2088 x = 0;
2090 else if (x >= descr->width)
2092 dir = LB_TIMER_RIGHT;
2093 x = descr->width - 1;
2096 else
2098 if (y < 0) dir = LB_TIMER_UP; /* above */
2099 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2102 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2103 if (index == -1) index = descr->focus_item;
2104 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2106 /* Start/stop the system timer */
2108 if (dir != LB_TIMER_NONE)
2109 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2110 else if (LISTBOX_Timer != LB_TIMER_NONE)
2111 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2112 LISTBOX_Timer = dir;
2116 /***********************************************************************
2117 * LISTBOX_HandleKeyDown
2119 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2121 INT caret = -1;
2122 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2123 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2124 bForceSelection = FALSE; /* only for single select list */
2126 if (descr->style & LBS_WANTKEYBOARDINPUT)
2128 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2129 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2130 wnd->hwndSelf );
2131 if (caret == -2) return 0;
2133 if (caret == -1) switch(wParam)
2135 case VK_LEFT:
2136 if (descr->style & LBS_MULTICOLUMN)
2138 bForceSelection = FALSE;
2139 if (descr->focus_item >= descr->page_size)
2140 caret = descr->focus_item - descr->page_size;
2141 break;
2143 /* fall through */
2144 case VK_UP:
2145 caret = descr->focus_item - 1;
2146 if (caret < 0) caret = 0;
2147 break;
2148 case VK_RIGHT:
2149 if (descr->style & LBS_MULTICOLUMN)
2151 bForceSelection = FALSE;
2152 if (descr->focus_item + descr->page_size < descr->nb_items)
2153 caret = descr->focus_item + descr->page_size;
2154 break;
2156 /* fall through */
2157 case VK_DOWN:
2158 caret = descr->focus_item + 1;
2159 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2160 break;
2162 case VK_PRIOR:
2163 if (descr->style & LBS_MULTICOLUMN)
2165 INT page = descr->width / descr->column_width;
2166 if (page < 1) page = 1;
2167 caret = descr->focus_item - (page * descr->page_size) + 1;
2169 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2170 if (caret < 0) caret = 0;
2171 break;
2172 case VK_NEXT:
2173 if (descr->style & LBS_MULTICOLUMN)
2175 INT page = descr->width / descr->column_width;
2176 if (page < 1) page = 1;
2177 caret = descr->focus_item + (page * descr->page_size) - 1;
2179 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2180 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2181 break;
2182 case VK_HOME:
2183 caret = 0;
2184 break;
2185 case VK_END:
2186 caret = descr->nb_items - 1;
2187 break;
2188 case VK_SPACE:
2189 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2190 else if (descr->style & LBS_MULTIPLESEL)
2192 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2193 !descr->items[descr->focus_item].selected,
2194 (descr->style & LBS_NOTIFY) != 0 );
2196 break;
2197 default:
2198 bForceSelection = FALSE;
2200 if (bForceSelection) /* focused item is used instead of key */
2201 caret = descr->focus_item;
2202 if (caret >= 0)
2204 if ((descr->style & LBS_EXTENDEDSEL) &&
2205 !(GetKeyState( VK_SHIFT ) & 0x8000))
2206 descr->anchor_item = caret;
2207 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2208 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2209 if (descr->style & LBS_NOTIFY)
2211 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2213 /* make sure that combo parent doesn't hide us */
2214 descr->lphc->wState |= CBF_NOROLLUP;
2216 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2219 return 0;
2223 /***********************************************************************
2224 * LISTBOX_HandleChar
2226 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2227 WPARAM wParam )
2229 INT caret = -1;
2230 char str[2];
2232 str[0] = wParam & 0xff;
2233 str[1] = '\0';
2235 if (descr->style & LBS_WANTKEYBOARDINPUT)
2237 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2238 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2239 wnd->hwndSelf );
2240 if (caret == -2) return 0;
2242 if (caret == -1)
2243 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2244 if (caret != -1)
2246 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2247 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2248 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2249 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2250 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2252 return 0;
2256 /***********************************************************************
2257 * LISTBOX_Create
2259 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2261 LB_DESCR *descr;
2262 MEASUREITEMSTRUCT mis;
2263 RECT rect;
2265 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2266 return FALSE;
2267 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2269 HeapFree( GetProcessHeap(), 0, descr );
2270 return FALSE;
2272 GetClientRect( wnd->hwndSelf, &rect );
2273 descr->owner = GetParent( wnd->hwndSelf );
2274 descr->style = wnd->dwStyle;
2275 descr->width = rect.right - rect.left;
2276 descr->height = rect.bottom - rect.top;
2277 descr->items = NULL;
2278 descr->nb_items = 0;
2279 descr->top_item = 0;
2280 descr->selected_item = -1;
2281 descr->focus_item = 0;
2282 descr->anchor_item = -1;
2283 descr->item_height = 1;
2284 descr->page_size = 1;
2285 descr->column_width = 150;
2286 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2287 descr->horz_pos = 0;
2288 descr->nb_tabs = 0;
2289 descr->tabs = NULL;
2290 descr->caret_on = lphc ? FALSE : TRUE;
2291 descr->in_focus = FALSE;
2292 descr->captured = FALSE;
2293 descr->font = 0;
2294 descr->locale = 0; /* FIXME */
2295 descr->lphc = lphc;
2297 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2298 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2300 /* Win95 document "List Box Differences" from MSDN:
2301 If a list box in a version 3.x application has either the
2302 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2303 horizontal and vertical scroll bars.
2305 descr->style |= WS_VSCROLL | WS_HSCROLL;
2308 if( lphc )
2310 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2311 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2312 descr->owner = lphc->self->hwndSelf;
2315 *(LB_DESCR **)wnd->wExtra = descr;
2317 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2319 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2320 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2321 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2322 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2324 if (descr->style & LBS_OWNERDRAWFIXED)
2326 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2328 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2329 descr->item_height = lphc->fixedOwnerDrawHeight;
2331 else
2333 mis.CtlType = ODT_LISTBOX;
2334 mis.CtlID = wnd->wIDmenu;
2335 mis.itemID = -1;
2336 mis.itemWidth = 0;
2337 mis.itemData = 0;
2338 mis.itemHeight = descr->item_height;
2339 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2340 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2344 return TRUE;
2348 /***********************************************************************
2349 * LISTBOX_Destroy
2351 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2353 LISTBOX_ResetContent( wnd, descr );
2354 HeapDestroy( descr->heap );
2355 HeapFree( GetProcessHeap(), 0, descr );
2356 wnd->wExtra[0] = 0;
2357 return TRUE;
2361 /***********************************************************************
2362 * ListBoxWndProc
2364 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2365 WPARAM wParam, LPARAM lParam )
2367 LRESULT ret;
2368 LB_DESCR *descr;
2369 HWND hwnd = wnd->hwndSelf;
2371 if (!wnd) return 0;
2372 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2374 switch (msg)
2376 case WM_CREATE:
2378 if (!LISTBOX_Create( wnd, NULL ))
2379 return -1;
2380 TRACE("creating wnd=%04x descr=%p\n",
2381 hwnd, *(LB_DESCR **)wnd->wExtra );
2382 return 0;
2384 case WM_NCCREATE:
2387 * When a listbox is not in a combobox and the look
2388 * is win95, the WS_BORDER style is replaced with
2389 * the WS_EX_CLIENTEDGE style.
2391 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2392 (wnd->dwStyle & WS_BORDER) )
2394 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2395 wnd->dwStyle &= ~ WS_BORDER;
2400 /* Ignore all other messages before we get a WM_CREATE */
2401 return DefWindowProcA( hwnd, msg, wParam, lParam );
2404 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2405 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2406 switch(msg)
2408 case LB_RESETCONTENT16:
2409 case LB_RESETCONTENT:
2410 LISTBOX_ResetContent( wnd, descr );
2411 return 0;
2413 case LB_ADDSTRING16:
2414 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2415 /* fall through */
2416 case LB_ADDSTRING:
2417 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2418 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2420 case LB_INSERTSTRING16:
2421 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2422 wParam = (INT)(INT16)wParam;
2423 /* fall through */
2424 case LB_INSERTSTRING:
2425 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2427 case LB_ADDFILE16:
2428 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2429 /* fall through */
2430 case LB_ADDFILE:
2431 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2432 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2434 case LB_DELETESTRING16:
2435 case LB_DELETESTRING:
2436 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2437 return descr->nb_items;
2438 else
2439 return LB_ERR;
2441 case LB_GETITEMDATA16:
2442 case LB_GETITEMDATA:
2443 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2444 return LB_ERR;
2445 return descr->items[wParam].data;
2447 case LB_SETITEMDATA16:
2448 case LB_SETITEMDATA:
2449 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2450 return LB_ERR;
2451 descr->items[wParam].data = (DWORD)lParam;
2452 return LB_OKAY;
2454 case LB_GETCOUNT16:
2455 case LB_GETCOUNT:
2456 return descr->nb_items;
2458 case LB_GETTEXT16:
2459 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2460 /* fall through */
2461 case LB_GETTEXT:
2462 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2464 case LB_GETTEXTLEN16:
2465 /* fall through */
2466 case LB_GETTEXTLEN:
2467 if (wParam >= descr->nb_items)
2468 return LB_ERR;
2469 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2470 : sizeof(DWORD));
2472 case LB_GETCURSEL16:
2473 case LB_GETCURSEL:
2474 if (descr->nb_items==0)
2475 return LB_ERR;
2476 if (!IS_MULTISELECT(descr))
2477 return descr->selected_item;
2478 /* else */
2479 if (descr->selected_item!=-1)
2480 return descr->selected_item;
2481 /* else */
2482 return descr->focus_item;
2483 /* otherwise, if the user tries to move the selection with the */
2484 /* arrow keys, we will give the application something to choke on */
2485 case LB_GETTOPINDEX16:
2486 case LB_GETTOPINDEX:
2487 return descr->top_item;
2489 case LB_GETITEMHEIGHT16:
2490 case LB_GETITEMHEIGHT:
2491 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2493 case LB_SETITEMHEIGHT16:
2494 lParam = LOWORD(lParam);
2495 /* fall through */
2496 case LB_SETITEMHEIGHT:
2497 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2499 case LB_ITEMFROMPOINT:
2501 POINT pt;
2502 RECT rect;
2504 pt.x = LOWORD(lParam);
2505 pt.y = HIWORD(lParam);
2506 rect.left = 0;
2507 rect.top = 0;
2508 rect.right = descr->width;
2509 rect.bottom = descr->height;
2511 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2512 !PtInRect( &rect, pt ) );
2515 case LB_SETCARETINDEX16:
2516 case LB_SETCARETINDEX:
2517 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2518 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2519 return LB_ERR;
2520 else if (ISWIN31)
2521 return wParam;
2522 else
2523 return LB_OKAY;
2525 case LB_GETCARETINDEX16:
2526 case LB_GETCARETINDEX:
2527 return descr->focus_item;
2529 case LB_SETTOPINDEX16:
2530 case LB_SETTOPINDEX:
2531 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2533 case LB_SETCOLUMNWIDTH16:
2534 case LB_SETCOLUMNWIDTH:
2535 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2537 case LB_GETITEMRECT16:
2539 RECT rect;
2540 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2541 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2543 return ret;
2545 case LB_GETITEMRECT:
2546 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2548 case LB_FINDSTRING16:
2549 wParam = (INT)(INT16)wParam;
2550 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2551 /* fall through */
2552 case LB_FINDSTRING:
2553 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2555 case LB_FINDSTRINGEXACT16:
2556 wParam = (INT)(INT16)wParam;
2557 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2558 /* fall through */
2559 case LB_FINDSTRINGEXACT:
2560 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2562 case LB_SELECTSTRING16:
2563 wParam = (INT)(INT16)wParam;
2564 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2565 /* fall through */
2566 case LB_SELECTSTRING:
2568 INT index = LISTBOX_FindString( wnd, descr, wParam,
2569 (LPCSTR)lParam, FALSE );
2570 if (index == LB_ERR)
2571 return LB_ERR;
2572 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2573 return index;
2576 case LB_GETSEL16:
2577 wParam = (INT)(INT16)wParam;
2578 /* fall through */
2579 case LB_GETSEL:
2580 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2581 return LB_ERR;
2582 return descr->items[wParam].selected;
2584 case LB_SETSEL16:
2585 lParam = (INT)(INT16)lParam;
2586 /* fall through */
2587 case LB_SETSEL:
2588 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2590 case LB_SETCURSEL16:
2591 wParam = (INT)(INT16)wParam;
2592 /* fall through */
2593 case LB_SETCURSEL:
2594 if (IS_MULTISELECT(descr)) return LB_ERR;
2595 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2596 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2598 case LB_GETSELCOUNT16:
2599 case LB_GETSELCOUNT:
2600 return LISTBOX_GetSelCount( wnd, descr );
2602 case LB_GETSELITEMS16:
2603 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2604 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2606 case LB_GETSELITEMS:
2607 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2609 case LB_SELITEMRANGE16:
2610 case LB_SELITEMRANGE:
2611 if (LOWORD(lParam) <= HIWORD(lParam))
2612 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2613 HIWORD(lParam), wParam );
2614 else
2615 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2616 LOWORD(lParam), wParam );
2618 case LB_SELITEMRANGEEX16:
2619 case LB_SELITEMRANGEEX:
2620 if ((INT)lParam >= (INT)wParam)
2621 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2622 else
2623 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2625 case LB_GETHORIZONTALEXTENT16:
2626 case LB_GETHORIZONTALEXTENT:
2627 return descr->horz_extent;
2629 case LB_SETHORIZONTALEXTENT16:
2630 case LB_SETHORIZONTALEXTENT:
2631 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2633 case LB_GETANCHORINDEX16:
2634 case LB_GETANCHORINDEX:
2635 return descr->anchor_item;
2637 case LB_SETANCHORINDEX16:
2638 wParam = (INT)(INT16)wParam;
2639 /* fall through */
2640 case LB_SETANCHORINDEX:
2641 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2642 return LB_ERR;
2643 descr->anchor_item = (INT)wParam;
2644 return LB_OKAY;
2646 case LB_DIR16:
2647 return LISTBOX_Directory( wnd, descr, wParam,
2648 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2650 case LB_DIR:
2651 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2653 case LB_GETLOCALE:
2654 return descr->locale;
2656 case LB_SETLOCALE:
2657 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2658 return LB_OKAY;
2660 case LB_INITSTORAGE:
2661 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2663 case LB_SETCOUNT:
2664 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2666 case LB_SETTABSTOPS16:
2667 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2668 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2670 case LB_SETTABSTOPS:
2671 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2673 case LB_CARETON16:
2674 case LB_CARETON:
2675 if (descr->caret_on)
2676 return LB_OKAY;
2677 descr->caret_on = TRUE;
2678 if ((descr->focus_item != -1) && (descr->in_focus))
2679 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2680 return LB_OKAY;
2682 case LB_CARETOFF16:
2683 case LB_CARETOFF:
2684 if (!descr->caret_on)
2685 return LB_OKAY;
2686 descr->caret_on = FALSE;
2687 if ((descr->focus_item != -1) && (descr->in_focus))
2688 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2689 return LB_OKAY;
2691 case WM_DESTROY:
2692 return LISTBOX_Destroy( wnd, descr );
2694 case WM_ENABLE:
2695 InvalidateRect( hwnd, NULL, TRUE );
2696 return 0;
2698 case WM_SETREDRAW:
2699 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2700 return 0;
2702 case WM_GETDLGCODE:
2703 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2705 case WM_PAINT:
2707 PAINTSTRUCT ps;
2708 HDC hdc = ( wParam ) ? ((HDC)wParam)
2709 : BeginPaint( hwnd, &ps );
2710 ret = LISTBOX_Paint( wnd, descr, hdc );
2711 if( !wParam ) EndPaint( hwnd, &ps );
2713 return ret;
2714 case WM_SIZE:
2715 LISTBOX_UpdateSize( wnd, descr );
2716 return 0;
2717 case WM_GETFONT:
2718 return descr->font;
2719 case WM_SETFONT:
2720 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2721 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2722 return 0;
2723 case WM_SETFOCUS:
2724 descr->in_focus = TRUE;
2725 descr->caret_on = TRUE;
2726 if (descr->focus_item != -1)
2727 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2728 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2729 return 0;
2730 case WM_KILLFOCUS:
2731 descr->in_focus = FALSE;
2732 if ((descr->focus_item != -1) && descr->caret_on)
2733 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2734 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2735 return 0;
2736 case WM_HSCROLL:
2737 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2738 case WM_VSCROLL:
2739 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2740 case WM_MOUSEACTIVATE:
2741 return MA_NOACTIVATE;
2742 case WM_MOUSEWHEEL:
2743 if (wParam & (MK_SHIFT | MK_CONTROL))
2744 return DefWindowProcA( hwnd, msg, wParam, lParam );
2745 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2746 case WM_LBUTTONDOWN:
2747 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2748 (INT16)LOWORD(lParam),
2749 (INT16)HIWORD(lParam) );
2750 case WM_LBUTTONDBLCLK:
2751 if (descr->style & LBS_NOTIFY)
2752 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2753 return 0;
2754 case WM_MOUSEMOVE:
2755 if (GetCapture() == hwnd)
2756 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2757 (INT16)HIWORD(lParam) );
2758 return 0;
2759 case WM_LBUTTONUP:
2760 return LISTBOX_HandleLButtonUp( wnd, descr );
2761 case WM_KEYDOWN:
2762 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2763 case WM_CHAR:
2764 return LISTBOX_HandleChar( wnd, descr, wParam );
2765 case WM_SYSTIMER:
2766 return LISTBOX_HandleSystemTimer( wnd, descr );
2767 case WM_ERASEBKGND:
2768 if (IS_OWNERDRAW(descr))
2770 RECT rect;
2771 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2772 wParam, (LPARAM)wnd->hwndSelf );
2773 GetClientRect(hwnd, &rect);
2774 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2776 return 1;
2777 case WM_DROPFILES:
2778 if( !descr->lphc )
2779 return SendMessageA( descr->owner, msg, wParam, lParam );
2780 break;
2782 case WM_DROPOBJECT:
2783 case WM_QUERYDROPOBJECT:
2784 case WM_DRAGSELECT:
2785 case WM_DRAGMOVE:
2786 if( !descr->lphc )
2788 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2789 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2790 dragInfo->pt.y );
2791 return SendMessageA( descr->owner, msg, wParam, lParam );
2793 break;
2795 default:
2796 if ((msg >= WM_USER) && (msg < 0xc000))
2797 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2798 hwnd, msg, wParam, lParam );
2799 return DefWindowProcA( hwnd, msg, wParam, lParam );
2801 return 0;
2804 /***********************************************************************
2805 * ListBoxWndProc
2807 * This is just a wrapper for the real wndproc, it only does window locking
2808 * and unlocking.
2810 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2811 WPARAM wParam, LPARAM lParam )
2813 WND* wndPtr = WIN_FindWndPtr( hwnd );
2814 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2816 WIN_ReleaseWndPtr(wndPtr);
2817 return res;
2820 /***********************************************************************
2821 * COMBO_Directory
2823 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2825 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2827 if( wnd )
2829 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2830 if( descr )
2832 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2834 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2835 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2836 WIN_ReleaseWndPtr(wnd);
2837 return lRet;
2839 WIN_ReleaseWndPtr(wnd);
2841 return CB_ERR;
2844 /***********************************************************************
2845 * ComboLBWndProc_locked
2847 * The real combo listbox wndproc, but called with locked WND struct.
2849 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2850 WPARAM wParam, LPARAM lParam )
2852 LRESULT lRet = 0;
2853 HWND hwnd = wnd->hwndSelf;
2855 if (wnd)
2857 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2859 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2860 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2862 if( descr || msg == WM_CREATE )
2864 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2866 switch( msg )
2868 case WM_CREATE:
2869 #define lpcs ((LPCREATESTRUCTA)lParam)
2870 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2871 (UINT)lpcs->lpCreateParams);
2873 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2874 #undef lpcs
2875 return LISTBOX_Create( wnd, lphc );
2876 case WM_MOUSEMOVE:
2877 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2878 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2880 POINT mousePos;
2881 BOOL captured;
2882 RECT clientRect;
2884 mousePos.x = (INT16)LOWORD(lParam);
2885 mousePos.y = (INT16)HIWORD(lParam);
2888 * If we are in a dropdown combobox, we simulate that
2889 * the mouse is captured to show the tracking of the item.
2891 GetClientRect(hwnd, &clientRect);
2893 if (PtInRect( &clientRect, mousePos ))
2895 captured = descr->captured;
2896 descr->captured = TRUE;
2898 LISTBOX_HandleMouseMove( wnd, descr,
2899 mousePos.x, mousePos.y);
2901 descr->captured = captured;
2904 else
2906 LISTBOX_HandleMouseMove( wnd, descr,
2907 mousePos.x, mousePos.y);
2910 return 0;
2913 else
2916 * If we are in Win3.1 look, go with the default behavior.
2918 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2920 case WM_LBUTTONUP:
2921 if (TWEAK_WineLook > WIN31_LOOK)
2923 POINT mousePos;
2924 RECT clientRect;
2927 * If the mouse button "up" is not in the listbox,
2928 * we make sure there is no selection by re-selecting the
2929 * item that was selected when the listbox was made visible.
2931 mousePos.x = (INT16)LOWORD(lParam);
2932 mousePos.y = (INT16)HIWORD(lParam);
2934 GetClientRect(hwnd, &clientRect);
2937 * When the user clicks outside the combobox and the focus
2938 * is lost, the owning combobox will send a fake buttonup with
2939 * 0xFFFFFFF as the mouse location, we must also revert the
2940 * selection to the original selection.
2942 if ( (lParam == 0xFFFFFFFF) ||
2943 (!PtInRect( &clientRect, mousePos )) )
2945 LISTBOX_MoveCaret( wnd,
2946 descr,
2947 lphc->droppedIndex,
2948 FALSE );
2951 return LISTBOX_HandleLButtonUp( wnd, descr );
2952 case WM_LBUTTONDBLCLK:
2953 case WM_LBUTTONDOWN:
2954 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
2955 (INT16)LOWORD(lParam),
2956 (INT16)HIWORD(lParam) );
2957 case WM_MOUSEACTIVATE:
2958 return MA_NOACTIVATE;
2959 case WM_NCACTIVATE:
2960 return FALSE;
2961 case WM_KEYDOWN:
2962 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2964 /* for some reason(?) Windows makes it possible to
2965 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2967 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2968 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2969 && (wParam == VK_DOWN || wParam == VK_UP)) )
2971 COMBO_FlipListbox( lphc, FALSE, FALSE );
2972 return 0;
2975 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2977 case LB_SETCURSEL16:
2978 case LB_SETCURSEL:
2979 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2980 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2981 return lRet;
2982 case WM_NCDESTROY:
2983 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2984 lphc->hWndLBox = 0;
2985 /* fall through */
2987 default:
2988 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2991 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2993 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2995 return lRet;
2998 /***********************************************************************
2999 * ComboLBWndProc
3001 * NOTE: in Windows, winproc address of the ComboLBox is the same
3002 * as that of the Listbox.
3004 * This is just a wrapper for the real wndproc, it only does window locking
3005 * and unlocking.
3007 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
3008 WPARAM wParam, LPARAM lParam )
3010 WND *wnd = WIN_FindWndPtr( hwnd );
3011 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3013 WIN_ReleaseWndPtr(wnd);
3014 return res;